| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
- // All rights reserved.
- // Code licensed under the BSD License.
- // http://www.anki3d.org/LICENSE
- #include <AnKi/Scene/Components/SkinComponent.h>
- #include <AnKi/Scene/SceneNode.h>
- #include <AnKi/Scene/SceneGraph.h>
- #include <AnKi/Resource/SkeletonResource.h>
- #include <AnKi/Resource/AnimationResource.h>
- #include <AnKi/Resource/ResourceManager.h>
- #include <AnKi/Util/BitSet.h>
- namespace anki {
- SkinComponent::SkinComponent(SceneNode* node)
- : SceneComponent(node, kClassType)
- {
- }
- SkinComponent::~SkinComponent()
- {
- }
- SkinComponent& SkinComponent::setSkeletonFilename(CString fname)
- {
- SkeletonResourcePtr rsrc;
- const Error err = ResourceManager::getSingleton().loadResource(fname, rsrc);
- if(err)
- {
- ANKI_SCENE_LOGE("Failed to load skeleton");
- }
- else
- {
- m_resourceDirty = true;
- m_skeleton = std::move(rsrc);
- // Cleanup
- m_boneTrfs[0].destroy();
- m_boneTrfs[1].destroy();
- m_animationTrfs.destroy();
- GpuSceneBuffer::getSingleton().deferredFree(m_gpuSceneBoneTransforms);
- // Create
- const U32 boneCount = m_skeleton->getBones().getSize();
- m_boneTrfs[0].resize(boneCount, Mat3x4::getIdentity());
- m_boneTrfs[1].resize(boneCount, Mat3x4::getIdentity());
- m_animationTrfs.resize(boneCount, Trf{Vec3(0.0f), Quat::getIdentity(), 1.0f});
- m_gpuSceneBoneTransforms = GpuSceneBuffer::getSingleton().allocate(sizeof(Mat4) * boneCount * 2, 4);
- }
- return *this;
- }
- CString SkinComponent::getSkeletonFilename() const
- {
- return (m_skeleton) ? m_skeleton->getFilename() : "*Error*";
- }
- void SkinComponent::playAnimation(U32 trackIdx, AnimationResourcePtr anim, const AnimationPlayInfo& info)
- {
- const Second animDuration = anim->getDuration();
- Track& track = m_tracks[trackIdx];
- track.m_anim = anim;
- track.m_absoluteStartTime = m_absoluteTime + info.m_startTime;
- track.m_relativeTimePassed = 0.0;
- if(info.m_repeatTimes > 0.0)
- {
- track.m_blendInTime = min(animDuration * info.m_repeatTimes, info.m_blendInTime);
- track.m_blendOutTime = min(animDuration * info.m_repeatTimes, info.m_blendOutTime);
- }
- else
- {
- track.m_blendInTime = info.m_blendInTime;
- track.m_blendOutTime = 0.0; // Irrelevant
- }
- track.m_repeatTimes = info.m_repeatTimes;
- track.m_animationSpeedScale = max(0.1f, info.m_animationSpeedScale);
- }
- void SkinComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
- {
- updated = false;
- if(!m_skeleton.isCreated())
- {
- return;
- }
- // Always update two frames because of the bone transforms buffer
- const Bool updatedLastFrame = m_updatedLastFrame;
- const Bool resourceDirty = m_resourceDirty;
- m_resourceDirty = false;
- Bool animationRun = false;
- const Second dt = info.m_dt;
- Vec4 minExtend(kMaxF32, kMaxF32, kMaxF32, 0.0f);
- Vec4 maxExtend(kMinF32, kMinF32, kMinF32, 0.0f);
- BitSet<128> bonesAnimated(false);
- for(Track& track : m_tracks)
- {
- if(!track.m_anim.isCreated())
- {
- continue;
- }
- if(track.m_absoluteStartTime > m_absoluteTime)
- {
- // Hasn't started yet
- continue;
- }
- const Second clipDuration = track.m_anim->getDuration();
- const Second animationDuration = track.m_repeatTimes * clipDuration;
- if(track.m_repeatTimes > 0.0 && track.m_relativeTimePassed > animationDuration)
- {
- // Animation finished
- continue;
- }
- animationRun = true;
- const Second animTime = track.m_relativeTimePassed;
- track.m_relativeTimePassed += dt * Second(track.m_animationSpeedScale);
- // Iterate the animation channels and interpolate
- for(U32 i = 0; i < track.m_anim->getChannels().getSize(); ++i)
- {
- const AnimationChannel& channel = track.m_anim->getChannels()[i];
- const Bone* bone = m_skeleton->tryFindBone(channel.m_name.toCString());
- if(!bone)
- {
- ANKI_SCENE_LOGW("Animation is referencing unknown bone \"%s\"", &channel.m_name[0]);
- continue;
- }
- const U32 boneIdx = bone->getIndex();
- // Interpolate
- Vec3 position;
- Quat rotation;
- F32 scale;
- track.m_anim->interpolate(i, animTime, position, rotation, scale);
- // Blend with previous track
- if(bonesAnimated.get(boneIdx) && (track.m_blendInTime > 0.0 || track.m_blendOutTime > 0.0))
- {
- F32 blendInFactor;
- if(track.m_blendInTime > 0.0)
- {
- blendInFactor = min(1.0f, F32(animTime / track.m_blendInTime));
- }
- else
- {
- blendInFactor = 1.0f;
- }
- F32 blendOutFactor;
- if(track.m_blendOutTime > 0.0)
- {
- blendOutFactor = min(1.0f, F32((animationDuration - animTime) / track.m_blendOutTime));
- }
- else
- {
- blendOutFactor = 1.0f;
- }
- const F32 factor = blendInFactor * blendOutFactor;
- if(factor < 1.0f)
- {
- const Trf& prevTrf = m_animationTrfs[boneIdx];
- position = linearInterpolate(prevTrf.m_translation, position, factor);
- rotation = prevTrf.m_rotation.slerp(rotation, factor);
- scale = linearInterpolate(prevTrf.m_scale, scale, factor);
- }
- }
- // Store
- bonesAnimated.set(boneIdx);
- m_animationTrfs[boneIdx] = {position, rotation, scale};
- }
- }
- if(updatedLastFrame || resourceDirty || animationRun)
- {
- m_prevBoneTrfs = m_crntBoneTrfs;
- m_crntBoneTrfs = m_crntBoneTrfs ^ 1;
- // Walk the bone hierarchy to add additional transforms
- visitBones(m_skeleton->getRootBone(), Mat3x4::getIdentity(), bonesAnimated, minExtend, maxExtend);
- const Vec4 e(kEpsilonf, kEpsilonf, kEpsilonf, 0.0f);
- m_boneBoundingVolume.setMin(minExtend - e);
- m_boneBoundingVolume.setMax(maxExtend + e);
- // Update the GPU scene
- const U32 boneCount = m_skeleton->getBones().getSize();
- DynamicArray<Mat3x4, MemoryPoolPtrWrapper<StackMemoryPool>> trfs(info.m_framePool);
- trfs.resize(boneCount * 2);
- for(U32 i = 0; i < boneCount; ++i)
- {
- trfs[i * 2 + 0] = getBoneTransforms()[i];
- trfs[i * 2 + 1] = getPreviousFrameBoneTransforms()[i];
- }
- GpuSceneMicroPatcher::getSingleton().newCopy(*info.m_framePool, m_gpuSceneBoneTransforms, trfs.getSizeInBytes(), trfs.getBegin());
- }
- else
- {
- m_prevBoneTrfs = m_crntBoneTrfs;
- }
- m_absoluteTime += dt;
- updated = updatedLastFrame || resourceDirty || animationRun;
- m_updatedLastFrame = resourceDirty || animationRun;
- }
- void SkinComponent::visitBones(const Bone& bone, const Mat3x4& parentTrf, const BitSet<128>& bonesAnimated, Vec4& minExtend, Vec4& maxExtend)
- {
- Mat3x4 outMat;
- if(bonesAnimated.get(bone.getIndex()))
- {
- const Trf& t = m_animationTrfs[bone.getIndex()];
- outMat = parentTrf.combineTransformations(Mat3x4(t.m_translation.xyz(), Mat3(t.m_rotation), Vec3(t.m_scale)));
- }
- else
- {
- outMat = parentTrf.combineTransformations(bone.getTransform());
- }
- m_boneTrfs[m_crntBoneTrfs][bone.getIndex()] = outMat.combineTransformations(bone.getVertexTransform());
- // Update volume
- const Vec3 bonePos = outMat * Vec4(0.0f, 0.0f, 0.0f, 1.0f);
- minExtend = minExtend.min(bonePos.xyz0());
- maxExtend = maxExtend.max(bonePos.xyz0());
- for(const Bone* child : bone.getChildren())
- {
- visitBones(*child, outMat, bonesAnimated, minExtend, maxExtend);
- }
- }
- } // end namespace anki
|