| 548 | } |
| 549 | |
| 550 | static void ApplyMorphAnimation(RigInstance* instance, RigPlayer* player, float blend_weight) |
| 551 | { |
| 552 | const dmRigDDF::RigAnimation* animation = player->m_Animation; |
| 553 | // No m_Playing check: UpdatePlayer may clear it when a ONCE clip ends in the same DoAnimate pass; |
| 554 | // morph weights must still be sampled for that final frame (same as bone tracks). |
| 555 | if (!animation || instance->m_MorphSlots.Empty()) |
| 556 | { |
| 557 | return; |
| 558 | } |
| 559 | |
| 560 | uint32_t mw_count = animation->m_MorphWeightTracks.m_Count; |
| 561 | const dmRigDDF::MorphWeightTrack* mw_tracks = animation->m_MorphWeightTracks.m_Data; |
| 562 | |
| 563 | float duration = GetCursorDuration(player, animation); |
| 564 | float t = CursorToTime(player->m_Cursor, duration, player->m_Backwards, player->m_Playback == dmRig::PLAYBACK_ONCE_PINGPONG); |
| 565 | float sample_rate = animation->m_SampleRate; |
| 566 | |
| 567 | for (uint32_t ti = 0; ti < mw_count; ++ti) |
| 568 | { |
| 569 | const dmRigDDF::MorphWeightTrack* track = &mw_tracks[ti]; |
| 570 | MorphWeightSlot* slot = GetMorphWeightSlot(instance, track->m_ModelId); |
| 571 | if (!slot) |
| 572 | { |
| 573 | continue; |
| 574 | } |
| 575 | |
| 576 | if (track->m_MorphCount != slot->m_MorphCount) |
| 577 | { |
| 578 | continue; |
| 579 | } |
| 580 | |
| 581 | SampleMorphWeightTrack(track, t, sample_rate, instance->m_MorphScratch.Begin()); |
| 582 | float* dest = instance->m_MorphWeightsBuffer.Begin() + slot->m_BufferOffset; |
| 583 | const float* sampled = instance->m_MorphScratch.Begin(); |
| 584 | for (uint32_t d = 0; d < slot->m_MorphCount; ++d) |
| 585 | { |
| 586 | dest[d] = dest[d] + blend_weight * (sampled[d] - dest[d]); |
| 587 | } |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | static void ApplyAnimation(RigInstance* instance, RigPlayer* player, dmArray<BonePose>& pose, dmArray<IKAnimation>& ik_animation, float blend_weight) |
| 592 | { |
no test coverage detected