|
import type { Mesh, Scene } from '@babylonjs/core' |
|
import { Animation, Color3, Color4, CubicEase, GlowLayer, Matrix, MeshBuilder, ParticleSystem, StandardMaterial, Texture, TransformNode, Vector3 } from '@babylonjs/core' |
|
|
|
export class LaserCutter { |
|
scene: Scene |
|
pivot: Mesh |
|
beam: Mesh |
|
sparkEmitter: Mesh |
|
sparks: ParticleSystem |
|
|
|
constructor(scene: Scene) { |
|
this.scene = scene |
|
|
|
const pivot = this.createLasePivot(scene) |
|
const sparkEmitter = this.createSparkEmitter(scene) |
|
const beam = this.createBeam(pivot.position, sparkEmitter.position, scene) |
|
const sparks = this.createSparks(sparkEmitter, scene) |
|
const gl = new GlowLayer('GlowLayer', scene) |
|
gl.addIncludedOnlyMesh(beam) |
|
gl.intensity = 2 |
|
|
|
this.pivot = pivot |
|
this.sparkEmitter = sparkEmitter |
|
this.beam = beam |
|
this.sparks = sparks |
|
} |
|
|
|
async cut(path: Vector3[]) { |
|
this.beam.isVisible = true |
|
this.sparks.start() |
|
for (let i = 1; i < path.length; i++) |
|
await this.move(path[i - 1], path[i]) |
|
this.sparks.stop() |
|
this.beam.isVisible = false |
|
} |
|
|
|
private async move(from: Vector3, to: Vector3) { |
|
const { scene } = this |
|
const SPEED = 1.0 |
|
const frameRate = 1 / (to.subtract(from).length() / SPEED) |
|
const anim = new Animation('Move', 'position', frameRate, Animation.ANIMATIONTYPE_VECTOR3, Animation.ANIMATIONLOOPMODE_CONSTANT) |
|
anim.setKeys([ |
|
{ frame: 0, value: from }, |
|
{ frame: 1, value: to }, |
|
]) |
|
anim.setEasingFunction(new CubicEase()) |
|
const tn = new TransformNode('Target') |
|
tn.animations.push(anim) |
|
const animatable = scene.beginAnimation(tn, 0, 1) |
|
const trackTarget = () => { |
|
this.attachTo(tn.position) |
|
} |
|
const observer = scene.onBeforeRenderObservable.add(trackTarget) |
|
await animatable.waitAsync() |
|
observer?.remove() |
|
} |
|
|
|
private attachTo(target: Vector3) { |
|
const start = this.pivot.getAbsolutePosition() |
|
const direction = target.subtract(start).normalize() |
|
const offset = direction.negate().scale(0.01) |
|
this.sparkEmitter.setAbsolutePosition(target.add(offset)) |
|
|
|
this.createBeam(this.pivot.getAbsolutePosition(), this.sparkEmitter.getAbsolutePosition(), this.scene, this.beam) |
|
} |
|
|
|
private createLasePivot(scene: Scene) { |
|
const laserPivot = MeshBuilder.CreateSphere('Target', { diameter: 0.1 }, scene) |
|
laserPivot.position = new Vector3(0, 0, 0) |
|
laserPivot.isVisible = false |
|
return laserPivot |
|
} |
|
|
|
private createSparkEmitter(scene: Scene) { |
|
const sparkEmitter = MeshBuilder.CreateSphere('Spark', { diameter: 0.1, segments: 32 }, scene) |
|
sparkEmitter.position = new Vector3(0, 0, 0) |
|
sparkEmitter.isVisible = false |
|
return sparkEmitter |
|
} |
|
|
|
private createBeam(start: Vector3, end: Vector3, scene: Scene, mesh?: Mesh) { |
|
const beam = MeshBuilder.CreateTube('Laser', { path: [start, end], radius: 0.05, instance: mesh }, scene) |
|
beam.bakeTransformIntoVertices(Matrix.Translation(0, 0, 0)) |
|
const laserBeamMtl = new StandardMaterial('Emissive') |
|
laserBeamMtl.emissiveColor = new Color3(1, 0, 0) |
|
beam.material = laserBeamMtl |
|
return beam |
|
} |
|
|
|
private createSparks(emitter: Mesh, scene: Scene) { |
|
const sparks = new ParticleSystem('Sparks', 1000, scene) |
|
sparks.createPointEmitter(new Vector3(1, 1, 1), Vector3.Up()) |
|
sparks.particleTexture = new Texture('textures/flare.png') |
|
sparks.emitter = emitter |
|
sparks.emitRate = 160 |
|
sparks.color1 = new Color4(1, 0.12, 0) |
|
sparks.color2 = new Color4(1, 0.37, 0) |
|
sparks.maxSize = 0.15 |
|
sparks.minSize = 0.1 |
|
sparks.minEmitPower = 2 |
|
sparks.maxEmitPower = 6 |
|
sparks.maxLifeTime = 3 |
|
sparks.minLifeTime = 0.5 |
|
sparks.gravity = new Vector3(0, -3, 0) |
|
return sparks |
|
} |
|
} |
|
|