diff --git a/leenkx/Sources/iron/object/ParticleSystem.hx b/leenkx/Sources/iron/object/ParticleSystem.hx index 5c94f08..ca9fef5 100644 --- a/leenkx/Sources/iron/object/ParticleSystem.hx +++ b/leenkx/Sources/iron/object/ParticleSystem.hx @@ -2,6 +2,7 @@ package iron.object; #if lnx_particles +import kha.FastFloat; import kha.graphics4.Usage; import kha.arrays.Float32Array; import iron.data.Data; @@ -16,39 +17,45 @@ import iron.math.Vec4; class ParticleSystem { public var data: ParticleData; public var speed = 1.0; + var currentSpeed = 0.0; var particles: Array; var ready: Bool; - public var frameRate = 24; - public var lifetime = 0.0; - public var animtime = 0.0; - public var time = 0.0; - public var spawnRate = 0.0; + var frameRate = 24; + var lifetime = 0.0; + var animtime = 0.0; + var time = 0.0; + var spawnRate = 0.0; + var looptime = 0.0; var seed = 0; - public var r: TParticleData; - public var gx: Float; - public var gy: Float; - public var gz: Float; - public var alignx: Float; - public var aligny: Float; - public var alignz: Float; + var r: TParticleData; + var gx: Float; + var gy: Float; + var gz: Float; + var alignx: Float; + var aligny: Float; + var alignz: Float; var dimx: Float; var dimy: Float; var tilesx: Int; var tilesy: Int; var tilesFramerate: Int; - public var count = 0; - public var lap = 0; - public var lapTime = 0.0; + var count = 0; + var lap = 0; + var lapTime = 0.0; var m = Mat4.identity(); var ownerLoc = new Vec4(); var ownerRot = new Quat(); var ownerScl = new Vec4(); + + var random = 0.0; public function new(sceneName: String, pref: TParticleReference) { seed = pref.seed; + currentSpeed = speed; + speed = 0; particles = []; ready = false; @@ -65,34 +72,61 @@ class ParticleSystem { gy = 0; gz = -9.81 * r.weight_gravity; } - alignx = r.object_align_factor[0] / 2; - aligny = r.object_align_factor[1] / 2; - alignz = r.object_align_factor[2] / 2; + alignx = r.object_align_factor[0]; + aligny = r.object_align_factor[1]; + alignz = r.object_align_factor[2]; + looptime = (r.frame_end - r.frame_start) / frameRate; lifetime = r.lifetime / frameRate; - animtime = (r.frame_end - r.frame_start) / frameRate; + animtime = r.loop ? looptime : looptime + lifetime; spawnRate = ((r.frame_end - r.frame_start) / r.count) / frameRate; for (i in 0...r.count) { - var particle = new Particle(i); - particle.sr = 1 - Math.random() * r.size_random; - particles.push(particle); + particles.push(new Particle(i)); } ready = true; + if (r.auto_start){ + start(); + } }); } + public function start() { + if (r.is_unique) random = Math.random(); + lifetime = r.lifetime / frameRate; + time = 0; + lap = 0; + lapTime = 0; + speed = currentSpeed; + } + public function pause() { - lifetime = 0; + speed = 0; } public function resume() { lifetime = r.lifetime / frameRate; + speed = currentSpeed; } + + + // TODO: interrupt smoothly + public function stop() { + end(); + } + + function end() { + lifetime = 0; + speed = 0; + lap = 0; + } + public function update(object: MeshObject, owner: MeshObject) { if (!ready || object == null || speed == 0.0) return; + var prevLap = lap; + // Copy owner world transform but discard scale owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl); object.transform.loc = ownerLoc; @@ -115,17 +149,21 @@ class ParticleSystem { } // Animate - time += Time.delta * speed; + time += Time.renderDelta * speed; lap = Std.int(time / animtime); lapTime = time - lap * animtime; count = Std.int(lapTime / spawnRate); + if (lap > prevLap && !r.loop) { + end(); + } + updateGpu(object, owner); } public function getData(): Mat4 { var hair = r.type == 1; - m._00 = r.loop ? animtime : -animtime; + m._00 = animtime; m._01 = hair ? 1 / particles.length : spawnRate; m._02 = hair ? 1 : lifetime; m._03 = particles.length; @@ -133,9 +171,9 @@ class ParticleSystem { m._11 = hair ? 0 : aligny; m._12 = hair ? 0 : alignz; m._13 = hair ? 0 : r.factor_random; - m._20 = hair ? 0 : gx * r.mass; - m._21 = hair ? 0 : gy * r.mass; - m._22 = hair ? 0 : gz * r.mass; + m._20 = hair ? 0 : gx; + m._21 = hair ? 0 : gy; + m._22 = hair ? 0 : gz; m._23 = hair ? 0 : r.lifetime_random; m._30 = tilesx; m._31 = tilesy; @@ -144,13 +182,25 @@ class ParticleSystem { return m; } + public function getSizeRandom(): FastFloat { + return r.size_random; + } + + public function getRandom(): FastFloat { + return random; + } + + public function getSize(): FastFloat { + return r.particle_size; + } + function updateGpu(object: MeshObject, owner: MeshObject) { if (!object.data.geom.instanced) setupGeomGpu(object, owner); // GPU particles transform is attached to owner object } - public function setupGeomGpu(object: MeshObject, owner: MeshObject) { - var instancedData = new Float32Array(particles.length * 6); + function setupGeomGpu(object: MeshObject, owner: MeshObject) { + var instancedData = new Float32Array(particles.length * 3); var i = 0; var normFactor = 1 / 32767; // pa.values are not normalized @@ -169,10 +219,6 @@ class ParticleSystem { instancedData.set(i, pa.values[j * pa.size ] * normFactor * scaleFactor.x); i++; instancedData.set(i, pa.values[j * pa.size + 1] * normFactor * scaleFactor.y); i++; instancedData.set(i, pa.values[j * pa.size + 2] * normFactor * scaleFactor.z); i++; - - instancedData.set(i, p.sr); i++; - instancedData.set(i, p.sr); i++; - instancedData.set(i, p.sr); i++; } case 1: // Face @@ -196,10 +242,6 @@ class ParticleSystem { instancedData.set(i, pos.x * normFactor * scaleFactor.x); i++; instancedData.set(i, pos.y * normFactor * scaleFactor.y); i++; instancedData.set(i, pos.z * normFactor * scaleFactor.z); i++; - - instancedData.set(i, p.sr); i++; - instancedData.set(i, p.sr); i++; - instancedData.set(i, p.sr); i++; } case 2: // Volume @@ -210,13 +252,9 @@ class ParticleSystem { instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.x); i++; instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.y); i++; instancedData.set(i, (Math.random() * 2.0 - 1.0) * scaleFactorVolume.z); i++; - - instancedData.set(i, p.sr); i++; - instancedData.set(i, p.sr); i++; - instancedData.set(i, p.sr); i++; } } - object.data.geom.setupInstanced(instancedData, 3, Usage.StaticUsage); + object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage); } function fhash(n: Int): Float { @@ -255,10 +293,11 @@ class ParticleSystem { class Particle { public var i: Int; - public var px = 0.0; - public var py = 0.0; - public var pz = 0.0; - public var sr = 1.0; // Size random + + public var x = 0.0; + public var y = 0.0; + public var z = 0.0; + public var cameraDistance: Float; public function new(i: Int) {