forked from LeenkxTeam/LNXSDK
		
	Update leenkx/Sources/iron/object/ParticleSystem.hx
This commit is contained in:
		@ -8,6 +8,8 @@ import kha.arrays.Float32Array;
 | 
				
			|||||||
import iron.data.Data;
 | 
					import iron.data.Data;
 | 
				
			||||||
import iron.data.ParticleData;
 | 
					import iron.data.ParticleData;
 | 
				
			||||||
import iron.data.SceneFormat;
 | 
					import iron.data.SceneFormat;
 | 
				
			||||||
 | 
					import iron.data.Geometry;
 | 
				
			||||||
 | 
					import iron.data.MeshData;
 | 
				
			||||||
import iron.system.Time;
 | 
					import iron.system.Time;
 | 
				
			||||||
import iron.math.Mat4;
 | 
					import iron.math.Mat4;
 | 
				
			||||||
import iron.math.Quat;
 | 
					import iron.math.Quat;
 | 
				
			||||||
@ -17,6 +19,7 @@ import iron.math.Vec4;
 | 
				
			|||||||
class ParticleSystem {
 | 
					class ParticleSystem {
 | 
				
			||||||
	public var data: ParticleData;
 | 
						public var data: ParticleData;
 | 
				
			||||||
	public var speed = 1.0;
 | 
						public var speed = 1.0;
 | 
				
			||||||
 | 
						public var dynamicEmitter: Bool = true;
 | 
				
			||||||
	var currentSpeed = 0.0;
 | 
						var currentSpeed = 0.0;
 | 
				
			||||||
	var particles: Array<Particle>;
 | 
						var particles: Array<Particle>;
 | 
				
			||||||
	var ready: Bool;
 | 
						var ready: Bool;
 | 
				
			||||||
@ -52,6 +55,12 @@ class ParticleSystem {
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	var random = 0.0;
 | 
						var random = 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var tmpV4 = new Vec4();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var instancedData: Float32Array = null;
 | 
				
			||||||
 | 
						var lastSpawnedCount: Int = 0;
 | 
				
			||||||
 | 
						var hasUniqueGeom: Bool = false; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function new(sceneName: String, pref: TParticleReference) {
 | 
						public function new(sceneName: String, pref: TParticleReference) {
 | 
				
			||||||
		seed = pref.seed;
 | 
							seed = pref.seed;
 | 
				
			||||||
		currentSpeed = speed;
 | 
							currentSpeed = speed;
 | 
				
			||||||
@ -62,6 +71,11 @@ class ParticleSystem {
 | 
				
			|||||||
		Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
 | 
							Data.getParticle(sceneName, pref.particle, function(b: ParticleData) {
 | 
				
			||||||
			data = b;
 | 
								data = b;
 | 
				
			||||||
			r = data.raw;
 | 
								r = data.raw;
 | 
				
			||||||
 | 
								if (r.dynamic_emitter != null){
 | 
				
			||||||
 | 
									dynamicEmitter = r.dynamic_emitter; 
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									dynamicEmitter = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if (Scene.active.raw.gravity != null) {
 | 
								if (Scene.active.raw.gravity != null) {
 | 
				
			||||||
				gx = Scene.active.raw.gravity[0] * r.weight_gravity;
 | 
									gx = Scene.active.raw.gravity[0] * r.weight_gravity;
 | 
				
			||||||
				gy = Scene.active.raw.gravity[1] * r.weight_gravity;
 | 
									gy = Scene.active.raw.gravity[1] * r.weight_gravity;
 | 
				
			||||||
@ -98,6 +112,8 @@ class ParticleSystem {
 | 
				
			|||||||
		lap = 0;
 | 
							lap = 0;
 | 
				
			||||||
		lapTime = 0;
 | 
							lapTime = 0;
 | 
				
			||||||
		speed = currentSpeed;
 | 
							speed = currentSpeed;
 | 
				
			||||||
 | 
							lastSpawnedCount = 0;
 | 
				
			||||||
 | 
							instancedData = null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function pause() {
 | 
						public function pause() {
 | 
				
			||||||
@ -130,8 +146,13 @@ class ParticleSystem {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Copy owner world transform but discard scale
 | 
							// Copy owner world transform but discard scale
 | 
				
			||||||
		owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
 | 
							owner.transform.world.decompose(ownerLoc, ownerRot, ownerScl);
 | 
				
			||||||
		object.transform.loc = ownerLoc;
 | 
							if (dynamicEmitter) {
 | 
				
			||||||
		object.transform.rot = ownerRot;
 | 
								object.transform.loc.x = 0; object.transform.loc.y = 0; object.transform.loc.z = 0;
 | 
				
			||||||
 | 
								object.transform.rot = new Quat();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								object.transform.loc = ownerLoc;
 | 
				
			||||||
 | 
								object.transform.rot = ownerRot;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Set particle size per particle system
 | 
							// Set particle size per particle system
 | 
				
			||||||
		object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1);
 | 
							object.transform.scale = new Vec4(r.particle_size, r.particle_size, r.particle_size, 1);
 | 
				
			||||||
@ -158,13 +179,18 @@ class ParticleSystem {
 | 
				
			|||||||
		if (lap > prevLap && !r.loop) {
 | 
							if (lap > prevLap && !r.loop) {
 | 
				
			||||||
			end();
 | 
								end();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (lap > prevLap && r.loop) {
 | 
				
			||||||
 | 
								lastSpawnedCount = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		updateGpu(object, owner);
 | 
							updateGpu(object, owner);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function getData(): Mat4 {
 | 
						public function getData(): Mat4 {
 | 
				
			||||||
		var hair = r.type == 1;
 | 
							var hair = r.type == 1;
 | 
				
			||||||
		m._00 = animtime;
 | 
							// Store loop flag in the sign: positive -> loop, negative -> no loop
 | 
				
			||||||
 | 
							m._00 = r.loop ? animtime : -animtime;
 | 
				
			||||||
		m._01 = hair ? 1 / particles.length : spawnRate;
 | 
							m._01 = hair ? 1 / particles.length : spawnRate;
 | 
				
			||||||
		m._02 = hair ? 1 : lifetime;
 | 
							m._02 = hair ? 1 : lifetime;
 | 
				
			||||||
		m._03 = particles.length;
 | 
							m._03 = particles.length;
 | 
				
			||||||
@ -187,17 +213,26 @@ class ParticleSystem {
 | 
				
			|||||||
		return r.size_random;
 | 
							return r.size_random;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function getRandom(): FastFloat {
 | 
						public inline function getRandom(): FastFloat {
 | 
				
			||||||
		return random;
 | 
							return random;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function getSize(): FastFloat {
 | 
						public inline function getSize(): FastFloat {
 | 
				
			||||||
		return r.particle_size;
 | 
							return r.particle_size;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function updateGpu(object: MeshObject, owner: MeshObject) {
 | 
						function updateGpu(object: MeshObject, owner: MeshObject) {
 | 
				
			||||||
		if (!object.data.geom.instanced) setupGeomGpu(object, owner);
 | 
							if (dynamicEmitter) {
 | 
				
			||||||
		// GPU particles transform is attached to owner object
 | 
								if (!hasUniqueGeom) ensureUniqueGeom(object);
 | 
				
			||||||
 | 
								var needSetup = instancedData == null || object.data.geom.instancedVB == null;
 | 
				
			||||||
 | 
								if (needSetup) setupGeomGpuDynamic(object, owner);
 | 
				
			||||||
 | 
								updateSpawnedInstances(object, owner);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								if (!hasUniqueGeom) ensureUniqueGeom(object);
 | 
				
			||||||
 | 
								if (!object.data.geom.instanced) setupGeomGpu(object, owner);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// GPU particles transform is attached to owner object in static mode
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function setupGeomGpu(object: MeshObject, owner: MeshObject) {
 | 
						function setupGeomGpu(object: MeshObject, owner: MeshObject) {
 | 
				
			||||||
@ -258,18 +293,134 @@ class ParticleSystem {
 | 
				
			|||||||
		object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
 | 
							object.data.geom.setupInstanced(instancedData, 1, Usage.StaticUsage);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function fhash(n: Int): Float {
 | 
						// allocate instanced VB once for this object 
 | 
				
			||||||
		var s = n + 1.0;
 | 
						function setupGeomGpuDynamic(object: MeshObject, owner: MeshObject) {
 | 
				
			||||||
		s *= 9301.0 % s;
 | 
							if (instancedData == null) instancedData = new Float32Array(particles.length * 3);
 | 
				
			||||||
		s = (s * 9301.0 + 49297.0) % 233280.0;
 | 
							lastSpawnedCount = 0; 
 | 
				
			||||||
		return s / 233280.0;
 | 
							// Create instanced VB once if missing (seed with our instancedData)
 | 
				
			||||||
 | 
							if (object.data.geom.instancedVB == null) {
 | 
				
			||||||
 | 
								object.data.geom.setupInstanced(instancedData, 1, Usage.DynamicUsage);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function ensureUniqueGeom(object: MeshObject) {
 | 
				
			||||||
 | 
							if (hasUniqueGeom) return;
 | 
				
			||||||
 | 
							var newData: MeshData = null;
 | 
				
			||||||
 | 
							new MeshData(object.data.raw, function(dat: MeshData) {
 | 
				
			||||||
 | 
								dat.scalePos = object.data.scalePos;
 | 
				
			||||||
 | 
								dat.scaleTex = object.data.scaleTex;
 | 
				
			||||||
 | 
								dat.format = object.data.format;
 | 
				
			||||||
 | 
								newData = dat;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							if (newData != null) object.setData(newData);
 | 
				
			||||||
 | 
							hasUniqueGeom = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function updateSpawnedInstances(object: MeshObject, owner: MeshObject) {
 | 
				
			||||||
 | 
							if (instancedData == null) return;
 | 
				
			||||||
 | 
							var targetCount = count;
 | 
				
			||||||
 | 
							if (targetCount > particles.length) targetCount = particles.length;
 | 
				
			||||||
 | 
							if (targetCount <= lastSpawnedCount) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var normFactor = 1 / 32767;
 | 
				
			||||||
 | 
							var scalePosOwner = owner.data.scalePos;
 | 
				
			||||||
 | 
							var scalePosParticle = object.data.scalePos;
 | 
				
			||||||
 | 
							var particleSize = r.particle_size;
 | 
				
			||||||
 | 
							var base = 1.0 / (particleSize * scalePosParticle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (r.emit_from) {
 | 
				
			||||||
 | 
								case 0: // Vert
 | 
				
			||||||
 | 
									var pa = owner.data.geom.positions;
 | 
				
			||||||
 | 
									var osx = owner.transform.scale.x;
 | 
				
			||||||
 | 
									var osy = owner.transform.scale.y;
 | 
				
			||||||
 | 
									var osz = owner.transform.scale.z;
 | 
				
			||||||
 | 
									var pCount = Std.int(pa.values.length / pa.size);
 | 
				
			||||||
 | 
									for (idx in lastSpawnedCount...targetCount) {
 | 
				
			||||||
 | 
										var j = Std.int(fhash(idx) * pCount);
 | 
				
			||||||
 | 
										var lx = pa.values[j * pa.size    ] * normFactor * scalePosOwner * osx;
 | 
				
			||||||
 | 
										var ly = pa.values[j * pa.size + 1] * normFactor * scalePosOwner * osy;
 | 
				
			||||||
 | 
										var lz = pa.values[j * pa.size + 2] * normFactor * scalePosOwner * osz;
 | 
				
			||||||
 | 
										tmpV4.x = lx; tmpV4.y = ly; tmpV4.z = lz; tmpV4.w = 1;
 | 
				
			||||||
 | 
										tmpV4.applyQuat(ownerRot);
 | 
				
			||||||
 | 
										var o = idx * 3;
 | 
				
			||||||
 | 
										instancedData.set(o    , (tmpV4.x + ownerLoc.x) * base);
 | 
				
			||||||
 | 
										instancedData.set(o + 1, (tmpV4.y + ownerLoc.y) * base);
 | 
				
			||||||
 | 
										instancedData.set(o + 2, (tmpV4.z + ownerLoc.z) * base);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 1: // Face
 | 
				
			||||||
 | 
									var positions = owner.data.geom.positions.values;
 | 
				
			||||||
 | 
									var osx1 = owner.transform.scale.x;
 | 
				
			||||||
 | 
									var osy1 = owner.transform.scale.y;
 | 
				
			||||||
 | 
									var osz1 = owner.transform.scale.z;
 | 
				
			||||||
 | 
									for (idx in lastSpawnedCount...targetCount) {
 | 
				
			||||||
 | 
										var ia = owner.data.geom.indices[Std.random(owner.data.geom.indices.length)];
 | 
				
			||||||
 | 
										var faceIndex = Std.random(Std.int(ia.length / 3));
 | 
				
			||||||
 | 
										var i0 = ia[faceIndex * 3 + 0];
 | 
				
			||||||
 | 
										var i1 = ia[faceIndex * 3 + 1];
 | 
				
			||||||
 | 
										var i2 = ia[faceIndex * 3 + 2];
 | 
				
			||||||
 | 
										var v0x = positions[i0 * 4    ], v0y = positions[i0 * 4 + 1], v0z = positions[i0 * 4 + 2];
 | 
				
			||||||
 | 
										var v1x = positions[i1 * 4    ], v1y = positions[i1 * 4 + 1], v1z = positions[i1 * 4 + 2];
 | 
				
			||||||
 | 
										var v2x = positions[i2 * 4    ], v2y = positions[i2 * 4 + 1], v2z = positions[i2 * 4 + 2];
 | 
				
			||||||
 | 
										var rx = Math.random(); var ry = Math.random(); if (rx + ry > 1) { rx = 1 - rx; ry = 1 - ry; }
 | 
				
			||||||
 | 
										var pxs = v0x + rx * (v1x - v0x) + ry * (v2x - v0x);
 | 
				
			||||||
 | 
										var pys = v0y + rx * (v1y - v0y) + ry * (v2y - v0y);
 | 
				
			||||||
 | 
										var pzs = v0z + rx * (v1z - v0z) + ry * (v2z - v0z);
 | 
				
			||||||
 | 
										var px = pxs * normFactor * scalePosOwner * osx1;
 | 
				
			||||||
 | 
										var py = pys * normFactor * scalePosOwner * osy1;
 | 
				
			||||||
 | 
										var pz = pzs * normFactor * scalePosOwner * osz1;
 | 
				
			||||||
 | 
										tmpV4.x = px; tmpV4.y = py; tmpV4.z = pz; tmpV4.w = 1;
 | 
				
			||||||
 | 
										tmpV4.applyQuat(ownerRot);
 | 
				
			||||||
 | 
										var o1 = idx * 3;
 | 
				
			||||||
 | 
										instancedData.set(o1    , (tmpV4.x + ownerLoc.x) * base);
 | 
				
			||||||
 | 
										instancedData.set(o1 + 1, (tmpV4.y + ownerLoc.y) * base);
 | 
				
			||||||
 | 
										instancedData.set(o1 + 2, (tmpV4.z + ownerLoc.z) * base);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case 2: // Volume
 | 
				
			||||||
 | 
									var dim = object.transform.dim;
 | 
				
			||||||
 | 
									for (idx in lastSpawnedCount...targetCount) {
 | 
				
			||||||
 | 
										tmpV4.x = (Math.random() * 2.0 - 1.0) * (dim.x * 0.5);
 | 
				
			||||||
 | 
										tmpV4.y = (Math.random() * 2.0 - 1.0) * (dim.y * 0.5);
 | 
				
			||||||
 | 
										tmpV4.z = (Math.random() * 2.0 - 1.0) * (dim.z * 0.5);
 | 
				
			||||||
 | 
										tmpV4.w = 1;
 | 
				
			||||||
 | 
										tmpV4.applyQuat(ownerRot);
 | 
				
			||||||
 | 
										var o2 = idx * 3;
 | 
				
			||||||
 | 
										instancedData.set(o2    , (tmpV4.x + ownerLoc.x) * base);
 | 
				
			||||||
 | 
										instancedData.set(o2 + 1, (tmpV4.y + ownerLoc.y) * base);
 | 
				
			||||||
 | 
										instancedData.set(o2 + 2, (tmpV4.z + ownerLoc.z) * base);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Upload full active range [0..targetCount) to this object's instanced VB
 | 
				
			||||||
 | 
							var geom = object.data.geom;
 | 
				
			||||||
 | 
							if (geom.instancedVB == null) {
 | 
				
			||||||
 | 
								geom.setupInstanced(instancedData, 1, Usage.DynamicUsage);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							var vb = geom.instancedVB.lock();
 | 
				
			||||||
 | 
							var totalFloats = targetCount * 3; // xyz per instance
 | 
				
			||||||
 | 
							var i = 0;
 | 
				
			||||||
 | 
							while (i < totalFloats) {
 | 
				
			||||||
 | 
								vb.setFloat32(i * 4, instancedData[i]);
 | 
				
			||||||
 | 
								i++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							geom.instancedVB.unlock();
 | 
				
			||||||
 | 
							geom.instanceCount = targetCount;
 | 
				
			||||||
 | 
							lastSpawnedCount = targetCount;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline function fhash(n: Int): Float {
 | 
				
			||||||
 | 
					    var s = n + 1.0;
 | 
				
			||||||
 | 
					    s *= 9301.0 % s;
 | 
				
			||||||
 | 
					    s = (s * 9301.0 + 49297.0) % 233280.0;
 | 
				
			||||||
 | 
					    return s / 233280.0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public function remove() {}
 | 
						public function remove() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Generates a random point in the triangle with vertex positions abc.
 | 
							Generates a random point in the triangle with vertex positions abc.
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		Please note that the given position vectors are changed in-place by this
 | 
							Please note that the given position vectors are changed in-place by this
 | 
				
			||||||
		function and can be considered garbage afterwards, so make sure to clone
 | 
							function and can be considered garbage afterwards, so make sure to clone
 | 
				
			||||||
		them first if needed.
 | 
							them first if needed.
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user