diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 9ceedb62..c5ef43b2 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -1880,7 +1880,15 @@ static void resolveTrackTime(const pipeline::M2AnimationTrack& track, // Global sequence: always use sub-array 0, wrap time at global duration outSeqIdx = 0; float dur = static_cast(globalSeqDurations[track.globalSequence]); - outTime = (dur > 0.0f) ? std::fmod(time, dur) : 0.0f; + if (dur > 0.0f) { + // Use iterative subtraction instead of fmod() to preserve precision + outTime = time; + while (outTime >= dur) { + outTime -= dur; + } + } else { + outTime = 0.0f; + } } else { outSeqIdx = seqIdx; outTime = time; @@ -2070,8 +2078,9 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm:: for (size_t idx : particleOnlyInstanceIndices_) { if (idx >= instances.size()) continue; auto& instance = instances[idx]; - if (instance.animTime > 3333.0f) { - instance.animTime = std::fmod(instance.animTime, 3333.0f); + // Use iterative subtraction instead of fmod() to preserve precision + while (instance.animTime > 3333.0f) { + instance.animTime -= 3333.0f; } } @@ -2114,7 +2123,11 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm:: instance.animTime = 0.0f; instance.variationTimer = 4000.0f + static_cast(rand() % 6000); } else { - instance.animTime = std::fmod(instance.animTime, std::max(1.0f, instance.animDuration)); + // Use iterative subtraction instead of fmod() to preserve precision + float duration = std::max(1.0f, instance.animDuration); + while (instance.animTime >= duration) { + instance.animTime -= duration; + } } } @@ -3452,8 +3465,12 @@ void M2Renderer::renderM2Particles(VkCommandBuffer cmd, VkDescriptorSet perFrame if ((em.flags & kParticleFlagTiled) && totalTiles > 1) { float animSeconds = inst.animTime / 1000.0f; uint32_t animFrame = static_cast(std::floor(animSeconds * totalTiles)) % totalTiles; - tileIndex = std::fmod(p.tileIndex + static_cast(animFrame), - static_cast(totalTiles)); + tileIndex = p.tileIndex + static_cast(animFrame); + float tilesFloat = static_cast(totalTiles); + // Wrap tile index within totalTiles range + while (tileIndex >= tilesFloat) { + tileIndex -= tilesFloat; + } } group.vertexData.push_back(tileIndex); totalParticles++; diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index c06e5bed..7618a345 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -2008,7 +2008,12 @@ void Renderer::updateCharacterAnimation() { // Rider bob: sinusoidal motion synced to mount's run animation (only used in fallback positioning) mountBob = 0.0f; if (moving && haveMountState && curMountDur > 1.0f) { - float norm = std::fmod(curMountTime, curMountDur) / curMountDur; + // Wrap mount time preserving precision via subtraction instead of fmod + float wrappedTime = curMountTime; + while (wrappedTime >= curMountDur) { + wrappedTime -= curMountDur; + } + float norm = wrappedTime / curMountDur; // One bounce per stride cycle float bobSpeed = taxiFlight_ ? 2.0f : 1.0f; mountBob = std::sin(norm * 2.0f * 3.14159f * bobSpeed) * 0.12f; @@ -2580,8 +2585,13 @@ bool Renderer::shouldTriggerFootstepEvent(uint32_t animationId, float animationT return false; } - float norm = std::fmod(animationTimeMs, animationDurationMs) / animationDurationMs; - if (norm < 0.0f) norm += 1.0f; + // Wrap animation time preserving precision via subtraction instead of fmod + float wrappedTime = animationTimeMs; + while (wrappedTime >= animationDurationMs) { + wrappedTime -= animationDurationMs; + } + if (wrappedTime < 0.0f) wrappedTime += animationDurationMs; + float norm = wrappedTime / animationDurationMs; if (animationId != footstepLastAnimationId) { footstepLastAnimationId = animationId; @@ -2875,8 +2885,13 @@ void Renderer::update(float deltaTime) { float animTimeMs = 0.0f, animDurationMs = 0.0f; if (characterRenderer->getAnimationState(mountInstanceId_, animId, animTimeMs, animDurationMs) && animDurationMs > 1.0f && cameraController->isMoving()) { - float norm = std::fmod(animTimeMs, animDurationMs) / animDurationMs; - if (norm < 0.0f) norm += 1.0f; + // Wrap animation time preserving precision via subtraction instead of fmod + float wrappedTime = animTimeMs; + while (wrappedTime >= animDurationMs) { + wrappedTime -= animDurationMs; + } + if (wrappedTime < 0.0f) wrappedTime += animDurationMs; + float norm = wrappedTime / animDurationMs; if (animId != mountFootstepLastAnimId) { mountFootstepLastAnimId = animId;