From c16ed19fa0826f9b617a6ec5b4ef50c60d9810e9 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Tue, 25 Mar 2025 02:06:07 -0400 Subject: [PATCH 1/3] Track particle offsets when using multiple successive subgeometries. For instance, if the first subgeometry has particles 0-199, the second subgeometry's particles will start counting from 200. --- away3d/animators/ParticleAnimationSet.hx | 10 +++++++++- away3d/animators/ParticleAnimator.hx | 1 + away3d/animators/data/AnimationSubGeometry.hx | 2 ++ away3d/animators/states/ParticleStateBase.hx | 12 +++++++++--- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/away3d/animators/ParticleAnimationSet.hx b/away3d/animators/ParticleAnimationSet.hx index f65fcf9b..a8698fb8 100644 --- a/away3d/animators/ParticleAnimationSet.hx +++ b/away3d/animators/ParticleAnimationSet.hx @@ -274,6 +274,7 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet var subGeometry:ISubGeometry; var subMesh:SubMesh; var localNode:ParticleNodeBase; + var particleOffset:Int = 0; for (i in 0...mesh.subMeshes.length) { subMesh = mesh.subMeshes[i]; @@ -283,6 +284,7 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet if (animationSubGeometry != null) { subMesh.animationSubGeometry = animationSubGeometry; + particleOffset += animationSubGeometry.numAnimationParticles; continue; } } @@ -295,8 +297,12 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet //create the vertexData vector that will be used for local node data animationSubGeometry.createVertexData(subGeometry.numVertices, _totalLenOfOneVertex); + + particleOffset += animationSubGeometry.animationParticles.length; } + animationSubGeometry.animationParticleOffset = particleOffset; + if (newAnimationSubGeometry == false) return; @@ -370,8 +376,10 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet } //store particle properties if they need to be retreived for dynamic local nodes - if (_localDynamicNodes.length > 0) + if (_localDynamicNodes.length > 0) { animationSubGeometry.animationParticles.push(new ParticleAnimationData(i, particleProperties.startTime, particleProperties.duration, particleProperties.delay, particle)); + animationSubGeometry.numAnimationParticles++; + } animationSubGeometry.numProcessedVertices += numVertices; diff --git a/away3d/animators/ParticleAnimator.hx b/away3d/animators/ParticleAnimator.hx index d605aa10..ce33695f 100644 --- a/away3d/animators/ParticleAnimator.hx +++ b/away3d/animators/ParticleAnimator.hx @@ -157,6 +157,7 @@ class ParticleAnimator extends AnimatorBase implements IAnimator animatorSubGeometry.createVertexData(subGeometry.numVertices, _totalLenOfOneVertex); //pass the particles data to the animator subGeometry + animatorSubGeometry.animationParticleOffset = subMesh.animationSubGeometry.animationParticleOffset; animatorSubGeometry.animationParticles = subMesh.animationSubGeometry.animationParticles; } } \ No newline at end of file diff --git a/away3d/animators/data/AnimationSubGeometry.hx b/away3d/animators/data/AnimationSubGeometry.hx index 00ccfc19..e5e89200 100644 --- a/away3d/animators/data/AnimationSubGeometry.hx +++ b/away3d/animators/data/AnimationSubGeometry.hx @@ -30,6 +30,8 @@ class AnimationSubGeometry public var previousTime:Float = Math.NEGATIVE_INFINITY; + public var animationParticleOffset:Int = 0; + public var numAnimationParticles:Int = 0; public var animationParticles:Vector = new Vector(); public function new() diff --git a/away3d/animators/states/ParticleStateBase.hx b/away3d/animators/states/ParticleStateBase.hx index 43d4be03..871aa8aa 100644 --- a/away3d/animators/states/ParticleStateBase.hx +++ b/away3d/animators/states/ParticleStateBase.hx @@ -60,13 +60,19 @@ class ParticleStateBase extends AnimationStateBase var animationParticle:ParticleAnimationData = null; // var numParticles:uint = _positions.length/dataLength; - var numParticles:Int = _dynamicProperties.length; - var i:Int = 0; + var particleOffset:Int = animationSubGeometry.animationParticleOffset; + var numParticles:Int = animationParticles.length; + if(numParticles > _dynamicProperties.length - particleOffset) { + numParticles = _dynamicProperties.length - particleOffset; + } + + var i:Int = particleOffset; var j:Int = 0; var k:Int = 0; //loop through all particles - while (i < numParticles) { + while (i < particleOffset + numParticles) { + data = _dynamicProperties[i]; //loop through each particle data for the current particle while (j < numParticles && (animationParticle = animationParticles[j]).index == i) { data = _dynamicProperties[i]; From d6a8416cd1d95372127314360bd66334ca9d5e3e Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Tue, 25 Mar 2025 02:12:13 -0400 Subject: [PATCH 2/3] Avoid generating redundant geometry. `generateAnimationSubGeometries()` seemed to assume `animationParticles` was empty, without checking. If it was called twice, it would generate and append its data twice. I'm fairly sure this didn't cause any user-visible errors, it just wasted time and memory. --- away3d/animators/ParticleAnimationSet.hx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/away3d/animators/ParticleAnimationSet.hx b/away3d/animators/ParticleAnimationSet.hx index a8698fb8..06ddd060 100644 --- a/away3d/animators/ParticleAnimationSet.hx +++ b/away3d/animators/ParticleAnimationSet.hx @@ -350,6 +350,12 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet break; } } + + if(j < animationSubGeometry.animationParticleOffset + animationSubGeometry.numAnimationParticles) { + j++; + continue; + } + numVertices = particle.numVertices; vertexData = animationSubGeometry.vertexData; vertexLength = numVertices*_totalLenOfOneVertex; From 77e109ec42e4a135228d085590c8729707fcf762 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 27 Jul 2025 22:10:54 -0400 Subject: [PATCH 3/3] Calculate particle offsets more reliably. The previous implementation assumed `generateAnimationSubGeometries()` would be called multiple times. However, it turns out that in some cases (perhaps when the mesh is too far away to be rendered), it's only called once. In that case, the calculation needs to happen at the end of the one call. But also, if it IS called multiple times, the second half of the function needs access to the up-to-date offsets, so I also recount them about halfway through. --- away3d/animators/ParticleAnimationSet.hx | 32 +++++++++++++------ away3d/animators/data/AnimationSubGeometry.hx | 1 - 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/away3d/animators/ParticleAnimationSet.hx b/away3d/animators/ParticleAnimationSet.hx index 06ddd060..d28eec79 100644 --- a/away3d/animators/ParticleAnimationSet.hx +++ b/away3d/animators/ParticleAnimationSet.hx @@ -274,7 +274,6 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet var subGeometry:ISubGeometry; var subMesh:SubMesh; var localNode:ParticleNodeBase; - var particleOffset:Int = 0; for (i in 0...mesh.subMeshes.length) { subMesh = mesh.subMeshes[i]; @@ -284,7 +283,6 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet if (animationSubGeometry != null) { subMesh.animationSubGeometry = animationSubGeometry; - particleOffset += animationSubGeometry.numAnimationParticles; continue; } } @@ -297,11 +295,9 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet //create the vertexData vector that will be used for local node data animationSubGeometry.createVertexData(subGeometry.numVertices, _totalLenOfOneVertex); - - particleOffset += animationSubGeometry.animationParticles.length; } - animationSubGeometry.animationParticleOffset = particleOffset; + calculateParticleOffsets(mesh); if (newAnimationSubGeometry == false) return; @@ -351,7 +347,7 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet } } - if(j < animationSubGeometry.animationParticleOffset + animationSubGeometry.numAnimationParticles) { + if(j < animationSubGeometry.animationParticleOffset + animationSubGeometry.animationParticles.length) { j++; continue; } @@ -378,14 +374,11 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet counterForVertex += _totalLenOfOneVertex; } - } //store particle properties if they need to be retreived for dynamic local nodes - if (_localDynamicNodes.length > 0) { + if (_localDynamicNodes.length > 0) animationSubGeometry.animationParticles.push(new ParticleAnimationData(i, particleProperties.startTime, particleProperties.duration, particleProperties.delay, particle)); - animationSubGeometry.numAnimationParticles++; - } animationSubGeometry.numProcessedVertices += numVertices; @@ -396,5 +389,24 @@ class ParticleAnimationSet extends AnimationSetBase implements IAnimationSet //next particle i++; } + + calculateParticleOffsets(mesh); + } + + /** + * Will throw an error if a sub-mesh lacks an AnimationSubGeometry; only + * call this after creating them all. + */ + private function calculateParticleOffsets(mesh:Mesh):Void + { + var particleOffset:Int = 0; + for (subMesh in mesh.subMeshes) { + var subGeometry:AnimationSubGeometry = mesh.shareAnimationGeometry + ? _animationSubGeometries[subMesh.subGeometry] + : subMesh.animationSubGeometry; + + subGeometry.animationParticleOffset = particleOffset; + particleOffset += subGeometry.animationParticles.length; + } } } \ No newline at end of file diff --git a/away3d/animators/data/AnimationSubGeometry.hx b/away3d/animators/data/AnimationSubGeometry.hx index e5e89200..0664de0b 100644 --- a/away3d/animators/data/AnimationSubGeometry.hx +++ b/away3d/animators/data/AnimationSubGeometry.hx @@ -31,7 +31,6 @@ class AnimationSubGeometry public var previousTime:Float = Math.NEGATIVE_INFINITY; public var animationParticleOffset:Int = 0; - public var numAnimationParticles:Int = 0; public var animationParticles:Vector = new Vector(); public function new()