mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-14 16:33:52 +00:00
fix(rendering): use separate timer for global sequence bones
Global sequence bones (hair, cape, physics) need time values spanning their full duration (up to ~968733ms), but animationTime wraps at the current animation's sequence duration (~2000ms for walk). This caused vertex spikes projecting from fingers/neck/ponytail as bones got stuck in the first ~2s of their loop. Add a separate globalSequenceTime accumulator that is not wrapped at the animation duration.
This commit is contained in:
parent
f520511139
commit
bde9bd20d8
2 changed files with 18 additions and 11 deletions
|
|
@ -158,6 +158,7 @@ private:
|
|||
uint32_t currentAnimationId = 0;
|
||||
int currentSequenceIndex = -1; // Index into M2Model::sequences
|
||||
float animationTime = 0.0f;
|
||||
float globalSequenceTime = 0.0f; // Separate timer for global sequences (accumulates without wrapping at sequence duration)
|
||||
bool animationLoop = true;
|
||||
bool isDead = false; // Prevents movement while in death state
|
||||
std::vector<glm::mat4> boneMatrices; // Current bone transforms
|
||||
|
|
@ -206,8 +207,8 @@ private:
|
|||
void calculateBindPose(M2ModelGPU& gpuModel);
|
||||
void updateAnimation(CharacterInstance& instance, float deltaTime);
|
||||
void calculateBoneMatrices(CharacterInstance& instance);
|
||||
glm::mat4 getBoneTransform(const pipeline::M2Bone& bone, float time, int sequenceIndex,
|
||||
const std::vector<uint32_t>& globalSeqDurations);
|
||||
glm::mat4 getBoneTransform(const pipeline::M2Bone& bone, float animTime, float globalSeqTime,
|
||||
int sequenceIndex, const std::vector<uint32_t>& globalSeqDurations);
|
||||
glm::mat4 getModelMatrix(const CharacterInstance& instance) const;
|
||||
void destroyModelGPU(M2ModelGPU& gpuModel, bool defer = false);
|
||||
void destroyInstanceBones(CharacterInstance& inst, bool defer = false);
|
||||
|
|
|
|||
|
|
@ -1690,6 +1690,9 @@ void CharacterRenderer::update(float deltaTime, const glm::vec3& cameraPos) {
|
|||
float distSq = glm::distance2(inst.position, cameraPos);
|
||||
if (distSq >= animUpdateRadiusSq) continue;
|
||||
|
||||
// Advance global sequence timer (accumulates independently of animation wrapping)
|
||||
inst.globalSequenceTime += deltaTime * 1000.0f;
|
||||
|
||||
// Always advance animation time (cheap)
|
||||
if (inst.cachedModel && !inst.cachedModel->data.sequences.empty()) {
|
||||
if (inst.currentSequenceIndex < 0) {
|
||||
|
|
@ -1852,8 +1855,10 @@ int CharacterRenderer::findKeyframeIndex(const std::vector<uint32_t>& timestamps
|
|||
}
|
||||
|
||||
// Resolve sequence index and time for a track, handling global sequences.
|
||||
// globalSeqTime is a separate accumulating timer that is NOT wrapped at the
|
||||
// current animation's sequence duration, so global sequences get full range.
|
||||
static void resolveTrackTime(const pipeline::M2AnimationTrack& track,
|
||||
int seqIdx, float time,
|
||||
int seqIdx, float animTime, float globalSeqTime,
|
||||
const std::vector<uint32_t>& globalSeqDurations,
|
||||
int& outSeqIdx, float& outTime) {
|
||||
if (track.globalSequence >= 0 &&
|
||||
|
|
@ -1861,14 +1866,14 @@ static void resolveTrackTime(const pipeline::M2AnimationTrack& track,
|
|||
outSeqIdx = 0;
|
||||
float dur = static_cast<float>(globalSeqDurations[track.globalSequence]);
|
||||
if (dur > 0.0f) {
|
||||
outTime = std::fmod(time, dur);
|
||||
outTime = std::fmod(globalSeqTime, dur);
|
||||
if (outTime < 0.0f) outTime += dur;
|
||||
} else {
|
||||
outTime = 0.0f;
|
||||
}
|
||||
} else {
|
||||
outSeqIdx = seqIdx;
|
||||
outTime = time;
|
||||
outTime = animTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1959,7 +1964,8 @@ void CharacterRenderer::calculateBoneMatrices(CharacterInstance& instance) {
|
|||
|
||||
// Local transform includes pivot bracket: T(pivot)*T*R*S*T(-pivot)
|
||||
// At rest this is identity, so no separate bind pose is needed
|
||||
glm::mat4 localTransform = getBoneTransform(bone, instance.animationTime, instance.currentSequenceIndex, gsd);
|
||||
glm::mat4 localTransform = getBoneTransform(bone, instance.animationTime, instance.globalSequenceTime,
|
||||
instance.currentSequenceIndex, gsd);
|
||||
|
||||
// Compose with parent
|
||||
if (bone.parentBone >= 0 && static_cast<size_t>(bone.parentBone) < numBones) {
|
||||
|
|
@ -1970,16 +1976,16 @@ void CharacterRenderer::calculateBoneMatrices(CharacterInstance& instance) {
|
|||
}
|
||||
}
|
||||
|
||||
glm::mat4 CharacterRenderer::getBoneTransform(const pipeline::M2Bone& bone, float time, int sequenceIndex,
|
||||
const std::vector<uint32_t>& globalSeqDurations) {
|
||||
glm::mat4 CharacterRenderer::getBoneTransform(const pipeline::M2Bone& bone, float animTime, float globalSeqTime,
|
||||
int sequenceIndex, const std::vector<uint32_t>& globalSeqDurations) {
|
||||
// Resolve global sequences: bones with globalSequence >= 0 use sequence 0
|
||||
// with time wrapped at the global sequence duration, independent of the
|
||||
// character's current animation.
|
||||
int tSeq, rSeq, sSeq;
|
||||
float tTime, rTime, sTime;
|
||||
resolveTrackTime(bone.translation, sequenceIndex, time, globalSeqDurations, tSeq, tTime);
|
||||
resolveTrackTime(bone.rotation, sequenceIndex, time, globalSeqDurations, rSeq, rTime);
|
||||
resolveTrackTime(bone.scale, sequenceIndex, time, globalSeqDurations, sSeq, sTime);
|
||||
resolveTrackTime(bone.translation, sequenceIndex, animTime, globalSeqTime, globalSeqDurations, tSeq, tTime);
|
||||
resolveTrackTime(bone.rotation, sequenceIndex, animTime, globalSeqTime, globalSeqDurations, rSeq, rTime);
|
||||
resolveTrackTime(bone.scale, sequenceIndex, animTime, globalSeqTime, globalSeqDurations, sSeq, sTime);
|
||||
|
||||
glm::vec3 translation = interpolateVec3(bone.translation, tSeq, tTime, glm::vec3(0.0f));
|
||||
glm::quat rotation = interpolateQuat(bone.rotation, rSeq, rTime);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue