@@ -153,11 +153,17 @@ void main() {
153153 }
154154 #endif
155155
156- // Add velocity deltas to local velocity
157- localVelocity += volDeltaLocal + folDeltaLocal;
156+ // Gravity and FOL contribute to base velocity (persisted, subject to dampen/drag).
157+ vec3 gravityLocal = rotationByQuaternions(gravityDelta, quaternionConjugate(worldRotation));
158+ localVelocity += folDeltaLocal + gravityLocal;
158159
159160 // =====================================================
160161 // Step 2: Dampen (Limit Velocity)
162+ // Two-velocity system:
163+ // base velocity (persisted) = startVelocity + FOL + gravity
164+ // animated velocity (per-frame) = VOL
165+ // Dampen uses total (base + animated) to judge overspeed,
166+ // but only permanently modifies base velocity.
161167 // =====================================================
162168 #ifdef RENDERER_LVL_MODULE_ENABLED
163169 float limitRand = a_Random2.w;
@@ -166,28 +172,29 @@ void main() {
166172 float effectiveDampen = 1.0 - pow (1.0 - dampen, dt * 30.0 );
167173
168174 if (renderer_LVLSpace == 0 ) {
169- localVelocity = applyLVLSpeedLimitTF(localVelocity, normalizedAge, limitRand, effectiveDampen);
175+ vec3 totalLocal = localVelocity + volDeltaLocal;
176+ vec3 dampenedTotal = applyLVLSpeedLimitTF(totalLocal, normalizedAge, limitRand, effectiveDampen);
177+ localVelocity = dampenedTotal - volDeltaLocal;
170178 } else {
171- // World space: exclude animatedVelocity from permanent dampen.
172- vec3 animatedWorld = volDeltaWorld + folDeltaWorld + gravityDelta;
173- vec3 localWorld = rotationByQuaternions(localVelocity, worldRotation);
174- vec3 totalWorld = localWorld + animatedWorld;
179+ vec3 animatedWorld = volDeltaWorld;
180+ vec3 baseWorld = rotationByQuaternions(localVelocity, worldRotation);
181+ vec3 totalWorld = baseWorld + animatedWorld;
175182 vec3 dampenedTotal = applyLVLSpeedLimitTF(totalWorld, normalizedAge, limitRand, effectiveDampen);
176- // Delta applies only to the local velocity portion
177- vec3 dampenedLocal = dampenedTotal - animatedWorld;
178- localVelocity = rotationByQuaternions(dampenedLocal, quaternionConjugate(worldRotation));
183+ vec3 dampenedBase = dampenedTotal - animatedWorld;
184+ localVelocity = rotationByQuaternions(dampenedBase, quaternionConjugate(worldRotation));
179185 }
180186 #endif
181187
182188 // =====================================================
183189 // Step 3: Drag
190+ // Drag also uses total velocity (base + animated), only modifies base.
184191 // =====================================================
185192 #ifdef RENDERER_LVL_MODULE_ENABLED
186193 {
187194 float dragCoeff = evaluateLVLDrag(normalizedAge, a_Random0.x);
188195 if (dragCoeff > 0.0 ) {
189- vec3 combinedVel = localVelocity;
190- float velMagSqr = dot (combinedVel, combinedVel );
196+ vec3 totalVel = localVelocity + volDeltaLocal ;
197+ float velMagSqr = dot (totalVel, totalVel );
191198 float velMag = sqrt (velMagSqr);
192199
193200 float drag = dragCoeff;
@@ -204,16 +211,19 @@ void main() {
204211
205212 if (velMag > 0.0 ) {
206213 float newVelMag = max (0.0 , velMag - drag * dt);
207- localVelocity = localVelocity * (newVelMag / velMag);
214+ vec3 dampenedTotal = totalVel * (newVelMag / velMag);
215+ localVelocity = dampenedTotal - volDeltaLocal;
208216 }
209217 }
210218 }
211219 #endif
212220
213221 // =====================================================
214222 // Step 4: Integrate position
223+ // localVelocity (base, includes gravity+FOL) is persisted in TF buffer.
224+ // VOL is added for integration only (not persisted).
215225 // =====================================================
216- vec3 worldVelocity = rotationByQuaternions(localVelocity, worldRotation) + volDeltaWorld + folDeltaWorld + gravityDelta ;
226+ vec3 worldVelocity = rotationByQuaternions(localVelocity + volDeltaLocal , worldRotation) + volDeltaWorld + folDeltaWorld;
217227 worldPosition += worldVelocity * dt;
218228
219229 v_TFPosition = worldPosition;
0 commit comments