mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +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;
|
uint32_t currentAnimationId = 0;
|
||||||
int currentSequenceIndex = -1; // Index into M2Model::sequences
|
int currentSequenceIndex = -1; // Index into M2Model::sequences
|
||||||
float animationTime = 0.0f;
|
float animationTime = 0.0f;
|
||||||
|
float globalSequenceTime = 0.0f; // Separate timer for global sequences (accumulates without wrapping at sequence duration)
|
||||||
bool animationLoop = true;
|
bool animationLoop = true;
|
||||||
bool isDead = false; // Prevents movement while in death state
|
bool isDead = false; // Prevents movement while in death state
|
||||||
std::vector<glm::mat4> boneMatrices; // Current bone transforms
|
std::vector<glm::mat4> boneMatrices; // Current bone transforms
|
||||||
|
|
@ -206,8 +207,8 @@ private:
|
||||||
void calculateBindPose(M2ModelGPU& gpuModel);
|
void calculateBindPose(M2ModelGPU& gpuModel);
|
||||||
void updateAnimation(CharacterInstance& instance, float deltaTime);
|
void updateAnimation(CharacterInstance& instance, float deltaTime);
|
||||||
void calculateBoneMatrices(CharacterInstance& instance);
|
void calculateBoneMatrices(CharacterInstance& instance);
|
||||||
glm::mat4 getBoneTransform(const pipeline::M2Bone& bone, float time, int sequenceIndex,
|
glm::mat4 getBoneTransform(const pipeline::M2Bone& bone, float animTime, float globalSeqTime,
|
||||||
const std::vector<uint32_t>& globalSeqDurations);
|
int sequenceIndex, const std::vector<uint32_t>& globalSeqDurations);
|
||||||
glm::mat4 getModelMatrix(const CharacterInstance& instance) const;
|
glm::mat4 getModelMatrix(const CharacterInstance& instance) const;
|
||||||
void destroyModelGPU(M2ModelGPU& gpuModel, bool defer = false);
|
void destroyModelGPU(M2ModelGPU& gpuModel, bool defer = false);
|
||||||
void destroyInstanceBones(CharacterInstance& inst, 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);
|
float distSq = glm::distance2(inst.position, cameraPos);
|
||||||
if (distSq >= animUpdateRadiusSq) continue;
|
if (distSq >= animUpdateRadiusSq) continue;
|
||||||
|
|
||||||
|
// Advance global sequence timer (accumulates independently of animation wrapping)
|
||||||
|
inst.globalSequenceTime += deltaTime * 1000.0f;
|
||||||
|
|
||||||
// Always advance animation time (cheap)
|
// Always advance animation time (cheap)
|
||||||
if (inst.cachedModel && !inst.cachedModel->data.sequences.empty()) {
|
if (inst.cachedModel && !inst.cachedModel->data.sequences.empty()) {
|
||||||
if (inst.currentSequenceIndex < 0) {
|
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.
|
// 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,
|
static void resolveTrackTime(const pipeline::M2AnimationTrack& track,
|
||||||
int seqIdx, float time,
|
int seqIdx, float animTime, float globalSeqTime,
|
||||||
const std::vector<uint32_t>& globalSeqDurations,
|
const std::vector<uint32_t>& globalSeqDurations,
|
||||||
int& outSeqIdx, float& outTime) {
|
int& outSeqIdx, float& outTime) {
|
||||||
if (track.globalSequence >= 0 &&
|
if (track.globalSequence >= 0 &&
|
||||||
|
|
@ -1861,14 +1866,14 @@ static void resolveTrackTime(const pipeline::M2AnimationTrack& track,
|
||||||
outSeqIdx = 0;
|
outSeqIdx = 0;
|
||||||
float dur = static_cast<float>(globalSeqDurations[track.globalSequence]);
|
float dur = static_cast<float>(globalSeqDurations[track.globalSequence]);
|
||||||
if (dur > 0.0f) {
|
if (dur > 0.0f) {
|
||||||
outTime = std::fmod(time, dur);
|
outTime = std::fmod(globalSeqTime, dur);
|
||||||
if (outTime < 0.0f) outTime += dur;
|
if (outTime < 0.0f) outTime += dur;
|
||||||
} else {
|
} else {
|
||||||
outTime = 0.0f;
|
outTime = 0.0f;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outSeqIdx = seqIdx;
|
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)
|
// Local transform includes pivot bracket: T(pivot)*T*R*S*T(-pivot)
|
||||||
// At rest this is identity, so no separate bind pose is needed
|
// 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
|
// Compose with parent
|
||||||
if (bone.parentBone >= 0 && static_cast<size_t>(bone.parentBone) < numBones) {
|
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,
|
glm::mat4 CharacterRenderer::getBoneTransform(const pipeline::M2Bone& bone, float animTime, float globalSeqTime,
|
||||||
const std::vector<uint32_t>& globalSeqDurations) {
|
int sequenceIndex, const std::vector<uint32_t>& globalSeqDurations) {
|
||||||
// Resolve global sequences: bones with globalSequence >= 0 use sequence 0
|
// Resolve global sequences: bones with globalSequence >= 0 use sequence 0
|
||||||
// with time wrapped at the global sequence duration, independent of the
|
// with time wrapped at the global sequence duration, independent of the
|
||||||
// character's current animation.
|
// character's current animation.
|
||||||
int tSeq, rSeq, sSeq;
|
int tSeq, rSeq, sSeq;
|
||||||
float tTime, rTime, sTime;
|
float tTime, rTime, sTime;
|
||||||
resolveTrackTime(bone.translation, sequenceIndex, time, globalSeqDurations, tSeq, tTime);
|
resolveTrackTime(bone.translation, sequenceIndex, animTime, globalSeqTime, globalSeqDurations, tSeq, tTime);
|
||||||
resolveTrackTime(bone.rotation, sequenceIndex, time, globalSeqDurations, rSeq, rTime);
|
resolveTrackTime(bone.rotation, sequenceIndex, animTime, globalSeqTime, globalSeqDurations, rSeq, rTime);
|
||||||
resolveTrackTime(bone.scale, sequenceIndex, time, globalSeqDurations, sSeq, sTime);
|
resolveTrackTime(bone.scale, sequenceIndex, animTime, globalSeqTime, globalSeqDurations, sSeq, sTime);
|
||||||
|
|
||||||
glm::vec3 translation = interpolateVec3(bone.translation, tSeq, tTime, glm::vec3(0.0f));
|
glm::vec3 translation = interpolateVec3(bone.translation, tSeq, tTime, glm::vec3(0.0f));
|
||||||
glm::quat rotation = interpolateQuat(bone.rotation, rSeq, rTime);
|
glm::quat rotation = interpolateQuat(bone.rotation, rSeq, rTime);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue