diff options
Diffstat (limited to 'indra/newview/gltf/animation.cpp')
-rw-r--r-- | indra/newview/gltf/animation.cpp | 372 |
1 files changed, 287 insertions, 85 deletions
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp index da6d02b356..31549986af 100644 --- a/indra/newview/gltf/animation.cpp +++ b/indra/newview/gltf/animation.cpp @@ -28,10 +28,12 @@ #include "asset.h" #include "buffer_util.h" +#include "../llskinningutil.h" using namespace LL::GLTF; +using namespace boost::json; -void Animation::allocateGLResources(Asset& asset) +bool Animation::prep(Asset& asset) { if (!mSamplers.empty()) { @@ -39,7 +41,10 @@ void Animation::allocateGLResources(Asset& asset) mMaxTime = -FLT_MAX; for (auto& sampler : mSamplers) { - sampler.allocateGLResources(asset); + if (!sampler.prep(asset)) + { + return false; + } mMinTime = llmin(sampler.mMinTime, mMinTime); mMaxTime = llmax(sampler.mMaxTime, mMaxTime); } @@ -51,13 +56,29 @@ void Animation::allocateGLResources(Asset& asset) for (auto& channel : mRotationChannels) { - channel.allocateGLResources(asset, mSamplers[channel.mSampler]); + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } } for (auto& channel : mTranslationChannels) { - channel.allocateGLResources(asset, mSamplers[channel.mSampler]); + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } + } + + for (auto& channel : mScaleChannels) + { + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } } + + return true; } void Animation::update(Asset& asset, F32 dt) @@ -69,36 +90,124 @@ void Animation::update(Asset& asset, F32 dt) void Animation::apply(Asset& asset, float time) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // convert time to animation loop time time = fmod(time, mMaxTime - mMinTime) + mMinTime; // apply each channel - for (auto& channel : mRotationChannels) { - channel.apply(asset, mSamplers[channel.mSampler], time); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - rotation"); + + for (auto& channel : mRotationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } } - for (auto& channel : mTranslationChannels) { - channel.apply(asset, mSamplers[channel.mSampler], time); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - translation"); + + for (auto& channel : mTranslationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } } -}; + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - scale"); + + for (auto& channel : mScaleChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } + } +}; -void Animation::Sampler::allocateGLResources(Asset& asset) +bool Animation::Sampler::prep(Asset& asset) { Accessor& accessor = asset.mAccessors[mInput]; - mMinTime = accessor.mMin[0]; - mMaxTime = accessor.mMax[0]; + mMinTime = (F32)accessor.mMin[0]; + mMaxTime = (F32)accessor.mMax[0]; mFrameTimes.resize(accessor.mCount); LLStrider<F32> frame_times = mFrameTimes.data(); copy(asset, accessor, frame_times); + + return true; +} + + +void Animation::Sampler::serialize(object& obj) const +{ + write(mInput, "input", obj, INVALID_INDEX); + write(mOutput, "output", obj, INVALID_INDEX); + write(mInterpolation, "interpolation", obj, std::string("LINEAR")); + write(mMinTime, "min_time", obj); + write(mMaxTime, "max_time", obj); +} + +const Animation::Sampler& Animation::Sampler::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "input", mInput); + copy(src, "output", mOutput); + copy(src, "interpolation", mInterpolation); + copy(src, "min_time", mMinTime); + copy(src, "max_time", mMaxTime); + } + return *this; +} + +bool Animation::Channel::Target::operator==(const Channel::Target& rhs) const +{ + return mNode == rhs.mNode && mPath == rhs.mPath; +} + +bool Animation::Channel::Target::operator!=(const Channel::Target& rhs) const +{ + return !(*this == rhs); +} + +void Animation::Channel::Target::serialize(object& obj) const +{ + write(mNode, "node", obj, INVALID_INDEX); + write(mPath, "path", obj); +} + +const Animation::Channel::Target& Animation::Channel::Target::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "node", mNode); + copy(src, "path", mPath); + } + return *this; +} + +void Animation::Channel::serialize(object& obj) const +{ + write(mSampler, "sampler", obj, INVALID_INDEX); + write(mTarget, "target", obj); +} + +const Animation::Channel& Animation::Channel::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "sampler", mSampler); + copy(src, "target", mTarget); + } + return *this; } void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + llassert(mFrameTimes.size() > 1); // if there is only one frame, there is no need to interpolate + if (time < mMinTime) { frameIndex = 0; @@ -106,40 +215,42 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F return; } - if (mFrameTimes.size() > 1) + frameIndex = U32(mFrameTimes.size()) - 2; + t = 1.f; + + if (time > mMaxTime) { - if (time > mMaxTime) - { - frameIndex = mFrameTimes.size() - 2; - t = 1.0f; - return; - } + return; + } + + if (time < mLastFrameTime) + { + mLastFrameIndex = 0; + } + + mLastFrameTime = time; - frameIndex = mFrameTimes.size() - 2; - t = 1.f; + U32 idx = mLastFrameIndex; - for (U32 i = 0; i < mFrameTimes.size() - 1; i++) + for (U32 i = idx; i < (U32)mFrameTimes.size() - 1; i++) + { + if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) { - if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) - { - frameIndex = i; - t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); - return; - } + frameIndex = i; + t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); + mLastFrameIndex = frameIndex; + return; } } - else - { - frameIndex = 0; - t = 0.0f; - } } -void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler) { Accessor& accessor = asset.mAccessors[sampler.mOutput]; copy(asset, accessor, mRotations); + + return true; } void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) @@ -149,30 +260,30 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setRotation(mRotations[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate - LLQuaternion q0(mRotations[frameIndex].get_value()); - LLQuaternion q1(mRotations[frameIndex + 1].get_value()); + quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t); - LLQuaternion qf = slerp(t, q0, q1); + qf = glm::normalize(qf); - qf.normalize(); - node.setRotation(glh::quaternionf(qf.mQ)); + node.setRotation(qf); } } -void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +bool Animation::TranslationChannel::prep(Asset& asset, Animation::Sampler& sampler) { Accessor& accessor = asset.mAccessors[sampler.mOutput]; copy(asset, accessor, mTranslations); + + return true; } void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time) @@ -182,29 +293,31 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setTranslation(mTranslations[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate - const glh::vec3f& v0 = mTranslations[frameIndex]; - const glh::vec3f& v1 = mTranslations[frameIndex + 1]; + const vec3& v0 = mTranslations[frameIndex]; + const vec3& v1 = mTranslations[frameIndex + 1]; - glh::vec3f vf = v0 + t * (v1 - v0); + vec3 vf = v0 + t * (v1 - v0); node.setTranslation(vf); } } -void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +bool Animation::ScaleChannel::prep(Asset& asset, Animation::Sampler& sampler) { Accessor& accessor = asset.mAccessors[sampler.mOutput]; copy(asset, accessor, mScales); + + return true; } void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) @@ -214,74 +327,163 @@ void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setScale(mScales[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate - const glh::vec3f& v0 = mScales[frameIndex]; - const glh::vec3f& v1 = mScales[frameIndex + 1]; + const vec3& v0 = mScales[frameIndex]; + const vec3& v1 = mScales[frameIndex + 1]; - glh::vec3f vf = v0 + t * (v1 - v0); + vec3 vf = v0 + t * (v1 - v0); node.setScale(vf); } } -const Animation& Animation::operator=(const tinygltf::Animation& src) +void Animation::serialize(object& obj) const { - mName = src.name; + write(mName, "name", obj); + write(mSamplers, "samplers", obj); - mSamplers.resize(src.samplers.size()); - for (U32 i = 0; i < src.samplers.size(); ++i) - { - mSamplers[i] = src.samplers[i]; - } + std::vector<Channel> channels; + channels.insert(channels.end(), mRotationChannels.begin(), mRotationChannels.end()); + channels.insert(channels.end(), mTranslationChannels.begin(), mTranslationChannels.end()); + channels.insert(channels.end(), mScaleChannels.begin(), mScaleChannels.end()); - for (U32 i = 0; i < src.channels.size(); ++i) + write(channels, "channels", obj); +} + +const Animation& Animation::operator=(const Value& src) +{ + if (src.is_object()) { - if (src.channels[i].target_path == "rotation") - { - mRotationChannels.push_back(RotationChannel()); - mRotationChannels.back() = src.channels[i]; - } + const object& obj = src.as_object(); - if (src.channels[i].target_path == "translation") - { - mTranslationChannels.push_back(TranslationChannel()); - mTranslationChannels.back() = src.channels[i]; - } + copy(obj, "name", mName); + copy(obj, "samplers", mSamplers); + + // make a temporory copy of generic channels + std::vector<Channel> channels; + copy(obj, "channels", channels); - if (src.channels[i].target_path == "scale") + // break up into channel specific implementations + for (auto& channel: channels) { - mScaleChannels.push_back(ScaleChannel()); - mScaleChannels.back() = src.channels[i]; + if (channel.mTarget.mPath == "rotation") + { + mRotationChannels.push_back(channel); + } + else if (channel.mTarget.mPath == "translation") + { + mTranslationChannels.push_back(channel); + } + else if (channel.mTarget.mPath == "scale") + { + mScaleChannels.push_back(channel); + } } } - return *this; } -void Skin::allocateGLResources(Asset& asset) +Skin::~Skin() +{ + if (mUBO) + { + glDeleteBuffers(1, &mUBO); + } +} + +void Skin::uploadMatrixPalette(Asset& asset) +{ + // prepare matrix palette + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + + U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount(); + + if (mUBO == 0) + { + glGenBuffers(1, &mUBO); + } + + size_t joint_count = llmin<size_t>(max_joints, mJoints.size()); + + std::vector<mat4> t_mp; + + t_mp.resize(joint_count); + + for (U32 i = 0; i < joint_count; ++i) + { + Node& joint = asset.mNodes[mJoints[i]]; + // build matrix palette in asset space + t_mp[i] = joint.mAssetMatrix * mInverseBindMatricesData[i]; + } + + std::vector<F32> glmp; + + glmp.resize(joint_count * 12); + + F32* mp = glmp.data(); + + for (U32 i = 0; i < joint_count; ++i) + { + F32* m = glm::value_ptr(t_mp[i]); + + U32 idx = i * 12; + + mp[idx + 0] = m[0]; + mp[idx + 1] = m[1]; + mp[idx + 2] = m[2]; + mp[idx + 3] = m[12]; + + mp[idx + 4] = m[4]; + mp[idx + 5] = m[5]; + mp[idx + 6] = m[6]; + mp[idx + 7] = m[13]; + + mp[idx + 8] = m[8]; + mp[idx + 9] = m[9]; + mp[idx + 10] = m[10]; + mp[idx + 11] = m[14]; + } + + glBindBuffer(GL_UNIFORM_BUFFER, mUBO); + glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +bool Skin::prep(Asset& asset) { if (mInverseBindMatrices != INVALID_INDEX) { Accessor& accessor = asset.mAccessors[mInverseBindMatrices]; copy(asset, accessor, mInverseBindMatricesData); } + + return true; } -const Skin& Skin::operator=(const tinygltf::Skin& src) +const Skin& Skin::operator=(const Value& src) { - mName = src.name; - mSkeleton = src.skeleton; - mInverseBindMatrices = src.inverseBindMatrices; - mJoints = src.joints; - + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "skeleton", mSkeleton); + copy(src, "inverseBindMatrices", mInverseBindMatrices); + copy(src, "joints", mJoints); + } return *this; } +void Skin::serialize(object& obj) const +{ + write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX); + write(mJoints, "joints", obj); + write(mName, "name", obj); + write(mSkeleton, "skeleton", obj, INVALID_INDEX); +} |