diff options
author | RunitaiLinden <davep@lindenlab.com> | 2024-04-24 09:51:15 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-24 09:51:15 -0500 |
commit | cadc1a02cc7289dabd368dd1a1d237c042e9f82e (patch) | |
tree | 7493c7a7a8a8b395bc9fc98a1a9dd018098c4147 /indra | |
parent | f1b7e806eb30a343876036eff5caef7c03309aa6 (diff) |
1285 GLTF Animation Prototype
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llcommon/llstrider.h | 2 | ||||
-rw-r--r-- | indra/llmath/llvolumeoctree.h | 2 | ||||
-rw-r--r-- | indra/llrender/llvertexbuffer.cpp | 43 | ||||
-rw-r--r-- | indra/llrender/llvertexbuffer.h | 8 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 5 | ||||
-rw-r--r-- | indra/newview/gltf/accessor.cpp | 66 | ||||
-rw-r--r-- | indra/newview/gltf/accessor.h | 95 | ||||
-rw-r--r-- | indra/newview/gltf/animation.cpp | 287 | ||||
-rw-r--r-- | indra/newview/gltf/animation.h | 181 | ||||
-rw-r--r-- | indra/newview/gltf/asset.cpp | 375 | ||||
-rw-r--r-- | indra/newview/gltf/asset.h | 295 | ||||
-rw-r--r-- | indra/newview/gltf/buffer_util.h | 402 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.cpp | 488 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.h | 57 | ||||
-rw-r--r-- | indra/newview/gltfscenemanager.cpp | 23 | ||||
-rw-r--r-- | indra/newview/gltfscenemanager.h | 5 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 1 | ||||
-rw-r--r-- | indra/newview/lldrawpoolalpha.cpp | 9 | ||||
-rw-r--r-- | indra/newview/lldrawpoolpbropaque.cpp | 1 | ||||
-rw-r--r-- | indra/newview/pipeline.cpp | 4 |
20 files changed, 1702 insertions, 647 deletions
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h index ed9284d2c5..e7522484e6 100644 --- a/indra/llcommon/llstrider.h +++ b/indra/llcommon/llstrider.h @@ -37,8 +37,8 @@ template <class Object> class LLStrider }; U32 mSkip; public: - LLStrider() { mObjectp = NULL; mSkip = sizeof(Object); } + LLStrider(Object* first) { mObjectp = first; mSkip = sizeof(Object); } ~LLStrider() { } const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;} diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index d6f536b9ca..0bbb793896 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -62,7 +62,7 @@ public: LL_ALIGN_16(LLVector4a mPositionGroup); const LLVector4a* mV[3]; - U16 mIndex[3]; + U32 mIndex[3]; F32 mRadius; mutable S32 mBinIndex; diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index dda3c1532d..1d81c3778b 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -741,8 +741,8 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi llassert(mGLBuffer == sGLRenderBuffer); llassert(mGLIndices == sGLRenderIndices); gGL.syncMatrices(); - glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT, - (GLvoid*) (indices_offset * sizeof(U16))); + glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType, + (GLvoid*) (indices_offset * (size_t) mIndicesStride)); } void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const @@ -1139,7 +1139,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count) } // flush the given byte range -// target -- "targret" parameter for glBufferSubData +// target -- "target" parameter for glBufferSubData // start -- first byte to copy // end -- last byte to copy (NOT last byte + 1) // data -- mMappedData or mMappedIndexData @@ -1301,6 +1301,8 @@ bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector4a>& strider, U32 index, } bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, U32 index, S32 count) { + llassert(mIndicesStride == 2); // cannot access 32-bit indices with U16 strider + llassert(mIndicesType == GL_UNSIGNED_SHORT); return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index, count); } bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, U32 index, S32 count) @@ -1507,4 +1509,39 @@ void LLVertexBuffer::setColorData(const LLColor4U* data) flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data); } +void LLVertexBuffer::setNormalData(const LLVector4a* data) +{ + llassert(sGLRenderBuffer == mGLBuffer); + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data); +} + +void LLVertexBuffer::setTangentData(const LLVector4a* data) +{ + llassert(sGLRenderBuffer == mGLBuffer); + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data); +} + +void LLVertexBuffer::setWeight4Data(const LLVector4a* data) +{ + llassert(sGLRenderBuffer == mGLBuffer); + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data); +} + +void LLVertexBuffer::setIndexData(const U16* data) +{ + llassert(sGLRenderIndices == mGLIndices); + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data); +} + +void LLVertexBuffer::setIndexData(const U32* data) +{ + llassert(sGLRenderIndices == mGLIndices); + if (mIndicesType != GL_UNSIGNED_INT) + { // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices + mIndicesType = GL_UNSIGNED_INT; + mIndicesStride = 4; + mNumIndices /= 2; + } + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data); +} diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index cc59e322ed..669d89aabf 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -190,9 +190,13 @@ public: bool getClothWeightStrider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1); void setPositionData(const LLVector4a* data); + void setNormalData(const LLVector4a* data); + void setTangentData(const LLVector4a* data); + void setWeight4Data(const LLVector4a* data); void setTexCoordData(const LLVector2* data); void setColorData(const LLColor4U* data); - + void setIndexData(const U16* data); + void setIndexData(const U32* data); U32 getNumVerts() const { return mNumVerts; } U32 getNumIndices() const { return mNumIndices; } @@ -224,6 +228,8 @@ protected: U32 mGLIndices = 0; // GL IBO handle U32 mNumVerts = 0; // Number of vertices allocated U32 mNumIndices = 0; // Number of indices allocated + U32 mIndicesType = GL_UNSIGNED_SHORT; // type of indices in index buffer + U32 mIndicesStride = 2; // size of each index in bytes U32 mOffsets[TYPE_MAX]; // byte offsets into mMappedData of each attribute U8* mMappedData = nullptr; // pointer to currently mapped data (NULL if unmapped) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ed617ba70e..b15ee235bd 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -76,7 +76,9 @@ endif (NOT HAVOK_TPV) set(viewer_SOURCE_FILES gltfscenemanager.cpp gltf/asset.cpp + gltf/accessor.cpp gltf/primitive.cpp + gltf/animation.cpp groupchatlistener.cpp llaccountingcostmanager.cpp llaisapi.cpp @@ -733,7 +735,10 @@ set(viewer_HEADER_FILES gltfscenemanager.h groupchatlistener.h gltf/asset.h + gltf/accessor.h + gltf/buffer_util.h gltf/primitive.h + gltf/animation.h llaccountingcost.h llaccountingcostmanager.h llaisapi.h diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp new file mode 100644 index 0000000000..55d36b7a32 --- /dev/null +++ b/indra/newview/gltf/accessor.cpp @@ -0,0 +1,66 @@ +/** + * @file accessor.cpp + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "../llviewerprecompiledheaders.h" + +#include "asset.h" + +using namespace LL::GLTF; + +const Buffer& Buffer::operator=(const tinygltf::Buffer& src) +{ + mData = src.data; + mName = src.name; + mUri = src.uri; + return *this; +} + +const BufferView& BufferView::operator=(const tinygltf::BufferView& src) +{ + mBuffer = src.buffer; + mByteLength = src.byteLength; + mByteOffset = src.byteOffset; + mByteStride = src.byteStride; + mTarget = src.target; + mName = src.name; + return *this; +} + +const Accessor& Accessor::operator=(const tinygltf::Accessor& src) +{ + mBufferView = src.bufferView; + mByteOffset = src.byteOffset; + mComponentType = src.componentType; + mCount = src.count; + mType = src.type; + mNormalized = src.normalized; + mName = src.name; + mMax = src.maxValues; + mMin = src.minValues; + + return *this; +} + diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h new file mode 100644 index 0000000000..9b8265d8da --- /dev/null +++ b/indra/newview/gltf/accessor.h @@ -0,0 +1,95 @@ +#pragma once + +/** + * @file asset.h + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "../lltinygltfhelper.h" +#include "llstrider.h" + +// LL GLTF Implementation +namespace LL +{ + namespace GLTF + { + class Asset; + + constexpr S32 INVALID_INDEX = -1; + + class Buffer + { + public: + std::vector<U8> mData; + std::string mName; + std::string mUri; + + const Buffer& operator=(const tinygltf::Buffer& src); + }; + + class BufferView + { + public: + S32 mBuffer = INVALID_INDEX; + S32 mByteLength; + S32 mByteOffset; + S32 mByteStride; + S32 mTarget; + S32 mComponentType; + + std::string mName; + + const BufferView& operator=(const tinygltf::BufferView& src); + + }; + + class Accessor + { + public: + S32 mBufferView = INVALID_INDEX; + S32 mByteOffset; + S32 mComponentType; + S32 mCount; + std::vector<double> mMax; + std::vector<double> mMin; + + enum class Type : S32 + { + SCALAR = TINYGLTF_TYPE_SCALAR, + VEC2 = TINYGLTF_TYPE_VEC2, + VEC3 = TINYGLTF_TYPE_VEC3, + VEC4 = TINYGLTF_TYPE_VEC4, + MAT2 = TINYGLTF_TYPE_MAT2, + MAT3 = TINYGLTF_TYPE_MAT3, + MAT4 = TINYGLTF_TYPE_MAT4 + }; + + S32 mType; + bool mNormalized; + std::string mName; + + const Accessor& operator=(const tinygltf::Accessor& src); + }; + } +} diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp new file mode 100644 index 0000000000..da6d02b356 --- /dev/null +++ b/indra/newview/gltf/animation.cpp @@ -0,0 +1,287 @@ +/** + * @file animation.cpp + * @brief LL GLTF Animation Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "../llviewerprecompiledheaders.h" + +#include "asset.h" +#include "buffer_util.h" + +using namespace LL::GLTF; + +void Animation::allocateGLResources(Asset& asset) +{ + if (!mSamplers.empty()) + { + mMinTime = FLT_MAX; + mMaxTime = -FLT_MAX; + for (auto& sampler : mSamplers) + { + sampler.allocateGLResources(asset); + mMinTime = llmin(sampler.mMinTime, mMinTime); + mMaxTime = llmax(sampler.mMaxTime, mMaxTime); + } + } + else + { + mMinTime = mMaxTime = 0.f; + } + + for (auto& channel : mRotationChannels) + { + channel.allocateGLResources(asset, mSamplers[channel.mSampler]); + } + + for (auto& channel : mTranslationChannels) + { + channel.allocateGLResources(asset, mSamplers[channel.mSampler]); + } +} + +void Animation::update(Asset& asset, F32 dt) +{ + mTime += dt; + + apply(asset, mTime); +} + +void Animation::apply(Asset& asset, float time) +{ + // 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); + } + + for (auto& channel : mTranslationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } +}; + + +void Animation::Sampler::allocateGLResources(Asset& asset) +{ + Accessor& accessor = asset.mAccessors[mInput]; + mMinTime = accessor.mMin[0]; + mMaxTime = accessor.mMax[0]; + + mFrameTimes.resize(accessor.mCount); + + LLStrider<F32> frame_times = mFrameTimes.data(); + copy(asset, accessor, frame_times); +} + +void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t) +{ + if (time < mMinTime) + { + frameIndex = 0; + t = 0.0f; + return; + } + + if (mFrameTimes.size() > 1) + { + if (time > mMaxTime) + { + frameIndex = mFrameTimes.size() - 2; + t = 1.0f; + return; + } + + frameIndex = mFrameTimes.size() - 2; + t = 1.f; + + for (U32 i = 0; i < mFrameTimes.size() - 1; i++) + { + if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) + { + frameIndex = i; + t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); + return; + } + } + } + else + { + frameIndex = 0; + t = 0.0f; + } +} + +void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +{ + Accessor& accessor = asset.mAccessors[sampler.mOutput]; + + copy(asset, accessor, mRotations); +} + +void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) +{ + U32 frameIndex; + F32 t; + + Node& node = asset.mNodes[mTarget.mNode]; + + sampler.getFrameInfo(asset, time, frameIndex, t); + + if (sampler.mFrameTimes.size() == 1) + { + node.setRotation(mRotations[0]); + } + else + { + // interpolate + LLQuaternion q0(mRotations[frameIndex].get_value()); + LLQuaternion q1(mRotations[frameIndex + 1].get_value()); + + LLQuaternion qf = slerp(t, q0, q1); + + qf.normalize(); + node.setRotation(glh::quaternionf(qf.mQ)); + } +} + +void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +{ + Accessor& accessor = asset.mAccessors[sampler.mOutput]; + + copy(asset, accessor, mTranslations); +} + +void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time) +{ + U32 frameIndex; + F32 t; + + Node& node = asset.mNodes[mTarget.mNode]; + + sampler.getFrameInfo(asset, time, frameIndex, t); + + if (sampler.mFrameTimes.size() == 1) + { + node.setTranslation(mTranslations[0]); + } + else + { + // interpolate + const glh::vec3f& v0 = mTranslations[frameIndex]; + const glh::vec3f& v1 = mTranslations[frameIndex + 1]; + + glh::vec3f vf = v0 + t * (v1 - v0); + + node.setTranslation(vf); + } +} + +void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +{ + Accessor& accessor = asset.mAccessors[sampler.mOutput]; + + copy(asset, accessor, mScales); +} + +void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) +{ + U32 frameIndex; + F32 t; + + Node& node = asset.mNodes[mTarget.mNode]; + + sampler.getFrameInfo(asset, time, frameIndex, t); + + if (sampler.mFrameTimes.size() == 1) + { + node.setScale(mScales[0]); + } + else + { + // interpolate + const glh::vec3f& v0 = mScales[frameIndex]; + const glh::vec3f& v1 = mScales[frameIndex + 1]; + + glh::vec3f vf = v0 + t * (v1 - v0); + + node.setScale(vf); + } +} + +const Animation& Animation::operator=(const tinygltf::Animation& src) +{ + mName = src.name; + + mSamplers.resize(src.samplers.size()); + for (U32 i = 0; i < src.samplers.size(); ++i) + { + mSamplers[i] = src.samplers[i]; + } + + for (U32 i = 0; i < src.channels.size(); ++i) + { + if (src.channels[i].target_path == "rotation") + { + mRotationChannels.push_back(RotationChannel()); + mRotationChannels.back() = src.channels[i]; + } + + if (src.channels[i].target_path == "translation") + { + mTranslationChannels.push_back(TranslationChannel()); + mTranslationChannels.back() = src.channels[i]; + } + + if (src.channels[i].target_path == "scale") + { + mScaleChannels.push_back(ScaleChannel()); + mScaleChannels.back() = src.channels[i]; + } + } + + return *this; +} + +void Skin::allocateGLResources(Asset& asset) +{ + if (mInverseBindMatrices != INVALID_INDEX) + { + Accessor& accessor = asset.mAccessors[mInverseBindMatrices]; + copy(asset, accessor, mInverseBindMatricesData); + } +} + +const Skin& Skin::operator=(const tinygltf::Skin& src) +{ + mName = src.name; + mSkeleton = src.skeleton; + mInverseBindMatrices = src.inverseBindMatrices; + mJoints = src.joints; + + return *this; +} + diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h new file mode 100644 index 0000000000..869eae963a --- /dev/null +++ b/indra/newview/gltf/animation.h @@ -0,0 +1,181 @@ +#pragma once + +/** + * @file animation.h + * @brief LL GLTF Animation Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "accessor.h" + +// LL GLTF Implementation +namespace LL +{ + namespace GLTF + { + class Asset; + + class Animation + { + public: + class Sampler + { + public: + std::vector<F32> mFrameTimes; + + F32 mMinTime = -FLT_MAX; + F32 mMaxTime = FLT_MAX; + + S32 mInput = INVALID_INDEX; + S32 mOutput = INVALID_INDEX; + std::string mInterpolation; + + void allocateGLResources(Asset& asset); + + const Sampler& operator=(const tinygltf::AnimationSampler& src) + { + mInput = src.input; + mOutput = src.output; + mInterpolation = src.interpolation; + + return *this; + } + + // get the frame index and time for the specified time + // asset -- the asset to reference for Accessors + // time -- the animation time to get the frame info for + // frameIndex -- index of the closest frame that precedes the specified time + // t - interpolant value between the frameIndex and the next frame + void getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t); + }; + + class Channel + { + public: + class Target + { + public: + S32 mNode = INVALID_INDEX; + std::string mPath; + }; + + S32 mSampler = INVALID_INDEX; + Target mTarget; + std::string mTargetPath; + std::string mName; + + const Channel& operator=(const tinygltf::AnimationChannel& src) + { + mSampler = src.sampler; + + mTarget.mNode = src.target_node; + mTarget.mPath = src.target_path; + + return *this; + } + + }; + + class RotationChannel : public Channel + { + public: + std::vector<glh::quaternionf> mRotations; + + const RotationChannel& operator=(const tinygltf::AnimationChannel& src) + { + Channel::operator=(src); + return *this; + } + + // prepare data needed for rendering + // asset -- asset to reference for Accessors + // sampler -- Sampler associated with this channel + void allocateGLResources(Asset& asset, Sampler& sampler); + + void apply(Asset& asset, Sampler& sampler, F32 time); + }; + + class TranslationChannel : public Channel + { + public: + std::vector<glh::vec3f> mTranslations; + + const TranslationChannel& operator=(const tinygltf::AnimationChannel& src) + { + Channel::operator=(src); + return *this; + } + + // prepare data needed for rendering + // asset -- asset to reference for Accessors + // sampler -- Sampler associated with this channel + void allocateGLResources(Asset& asset, Sampler& sampler); + + void apply(Asset& asset, Sampler& sampler, F32 time); + }; + + class ScaleChannel : public Channel + { + public: + std::vector<glh::vec3f> mScales; + + const ScaleChannel& operator=(const tinygltf::AnimationChannel& src) + { + Channel::operator=(src); + return *this; + } + + // prepare data needed for rendering + // asset -- asset to reference for Accessors + // sampler -- Sampler associated with this channel + void allocateGLResources(Asset& asset, Sampler& sampler); + + void apply(Asset& asset, Sampler& sampler, F32 time); + }; + + std::string mName; + std::vector<Sampler> mSamplers; + + // min/max time values for all samplers combined + F32 mMinTime = 0.f; + F32 mMaxTime = 0.f; + + // current time of the animation + F32 mTime = 0.f; + + std::vector<RotationChannel> mRotationChannels; + std::vector<TranslationChannel> mTranslationChannels; + std::vector<ScaleChannel> mScaleChannels; + + const Animation& operator=(const tinygltf::Animation& src); + + void allocateGLResources(Asset& asset); + + void update(Asset& asset, float dt); + + // apply this animation at the specified time + void apply(Asset& asset, F32 time); + }; + + } +} diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index 7181c5fa53..313e82bf01 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -28,6 +28,8 @@ #include "asset.h" #include "llvolumeoctree.h" +#include "../llviewershadermgr.h" +#include "../llviewercontrol.h" using namespace LL::GLTF; @@ -66,6 +68,7 @@ LLMatrix4a inverse(const LLMatrix4a& mat); void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix) { + makeMatrixValid(); matMul(mMatrix, parentMatrix, mAssetMatrix); mAssetMatrixInv = inverse(mAssetMatrix); @@ -99,7 +102,7 @@ void Asset::updateRenderTransforms(const LLMatrix4a& modelview) // use mAssetMatrix to update render transforms from node list for (auto& node : mNodes) { - if (node.mMesh != INVALID_INDEX) + //if (node.mMesh != INVALID_INDEX) { matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); } @@ -211,6 +214,67 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, return node_hit; } + +void Node::makeMatrixValid() +{ + if (!mMatrixValid && mTRSValid) + { + glh::matrix4f rot; + mRotation.get_value(rot); + + glh::matrix4f trans; + trans.set_translate(mTranslation); + + glh::matrix4f sc; + sc.set_scale(mScale); + + glh::matrix4f t; + //t = sc * rot * trans; + //t = trans * rot * sc; // best so far, still wrong on negative scale + //t = sc * trans * rot; + t = trans * sc * rot; + + mMatrix.loadu(t.m); + mMatrixValid = true; + } +} + +void Node::makeTRSValid() +{ + if (!mTRSValid && mMatrixValid) + { + glh::matrix4f t(mMatrix.getF32ptr()); + + glh::vec4f p = t.get_column(3); + mTranslation.set_value(p.v[0], p.v[1], p.v[2]); + + mScale.set_value(t.get_column(0).length(), t.get_column(1).length(), t.get_column(2).length()); + mRotation.set_value(t); + mTRSValid = true; + } +} + +void Node::setRotation(const glh::quaternionf& q) +{ + makeTRSValid(); + mRotation = q; + mMatrixValid = false; +} + +void Node::setTranslation(const glh::vec3f& t) +{ + makeTRSValid(); + mTranslation = t; + mMatrixValid = false; +} + +void Node::setScale(const glh::vec3f& s) +{ + makeTRSValid(); + mScale = s; + mMatrixValid = false; +} + const Node& Node::operator=(const tinygltf::Node& src) { F32* dstMatrix = mMatrix.getF32ptr(); @@ -222,48 +286,33 @@ const Node& Node::operator=(const tinygltf::Node& src) { dstMatrix[i] = (F32)src.matrix[i]; } + + mMatrixValid = true; } else if (!src.rotation.empty() || !src.translation.empty() || !src.scale.empty()) { // node has rotation/translation/scale, convert to matrix - glh::quaternionf rotation; if (src.rotation.size() == 4) { - rotation = glh::quaternionf((F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2], (F32)src.rotation[3]); + mRotation = glh::quaternionf((F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2], (F32)src.rotation[3]); } - glh::vec3f translation; if (src.translation.size() == 3) { - translation = glh::vec3f((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]); + mTranslation = glh::vec3f((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]); } glh::vec3f scale; if (src.scale.size() == 3) { - scale = glh::vec3f((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]); + mScale = glh::vec3f((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]); } else { - scale.set_value(1.f, 1.f, 1.f); + mScale.set_value(1.f, 1.f, 1.f); } - glh::matrix4f rot; - rotation.get_value(rot); - - glh::matrix4f trans; - trans.set_translate(translation); - - glh::matrix4f sc; - sc.set_scale(scale); - - glh::matrix4f t; - //t = sc * rot * trans; - //t = trans * rot * sc; // best so far, still wrong on negative scale - //t = sc * trans * rot; - t = trans * sc * rot; - - mMatrix.loadu(t.m); + mTRSValid = true; } else { @@ -273,21 +322,50 @@ const Node& Node::operator=(const tinygltf::Node& src) mChildren = src.children; mMesh = src.mesh; + mSkin = src.skin; mName = src.name; return *this; } -void Asset::render(bool opaque) +void Asset::render(bool opaque, bool rigged) { + if (rigged) + { + gGL.loadIdentity(); + } + for (auto& node : mNodes) { + if (node.mSkin != INVALID_INDEX) + { + if (rigged) + { + Skin& skin = mSkins[node.mSkin]; + skin.uploadMatrixPalette(*this, node); + } + else + { + //skip static nodes if we're rendering rigged + continue; + } + } + else if (rigged) + { + // skip rigged nodes if we're not rendering rigged + continue; + } + + if (node.mMesh != INVALID_INDEX) { Mesh& mesh = mMeshes[node.mMesh]; for (auto& primitive : mesh.mPrimitives) { - gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + if (!rigged) + { + gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + } bool cull = true; if (primitive.mMaterial != INVALID_INDEX) { @@ -336,4 +414,251 @@ void Asset::renderTransparent() render(false); } +void Asset::update() +{ + F32 dt = gFrameTimeSeconds - mLastUpdateTime; + + if (dt > 0.f) + { + mLastUpdateTime = gFrameTimeSeconds; + if (mAnimations.size() > 0) + { + static LLCachedControl<U32> anim_idx(gSavedSettings, "GLTFAnimationIndex", 0); + static LLCachedControl<F32> anim_speed(gSavedSettings, "GLTFAnimationSpeed", 1.f); + + U32 idx = llclamp(anim_idx(), 0U, mAnimations.size() - 1); + mAnimations[idx].update(*this, dt*anim_speed); + } + + updateTransforms(); + } +} + +void Asset::allocateGLResources(const std::string& filename, const tinygltf::Model& model) +{ + // do images first as materials may depend on images + for (auto& image : mImages) + { + image.allocateGLResources(); + } + + // do materials before meshes as meshes may depend on materials + for (U32 i = 0; i < mMaterials.size(); ++i) + { + mMaterials[i].allocateGLResources(*this); + LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); + } + + for (auto& mesh : mMeshes) + { + mesh.allocateGLResources(*this); + } + + for (auto& animation : mAnimations) + { + animation.allocateGLResources(*this); + } + + for (auto& skin : mSkins) + { + skin.allocateGLResources(*this); + } +} + +const Asset& Asset::operator=(const tinygltf::Model& src) +{ + mScenes.resize(src.scenes.size()); + for (U32 i = 0; i < src.scenes.size(); ++i) + { + mScenes[i] = src.scenes[i]; + } + + mNodes.resize(src.nodes.size()); + for (U32 i = 0; i < src.nodes.size(); ++i) + { + mNodes[i] = src.nodes[i]; + } + + mMeshes.resize(src.meshes.size()); + for (U32 i = 0; i < src.meshes.size(); ++i) + { + mMeshes[i] = src.meshes[i]; + } + + mMaterials.resize(src.materials.size()); + for (U32 i = 0; i < src.materials.size(); ++i) + { + mMaterials[i] = src.materials[i]; + } + + mBuffers.resize(src.buffers.size()); + for (U32 i = 0; i < src.buffers.size(); ++i) + { + mBuffers[i] = src.buffers[i]; + } + + mBufferViews.resize(src.bufferViews.size()); + for (U32 i = 0; i < src.bufferViews.size(); ++i) + { + mBufferViews[i] = src.bufferViews[i]; + } + + mTextures.resize(src.textures.size()); + for (U32 i = 0; i < src.textures.size(); ++i) + { + mTextures[i] = src.textures[i]; + } + + mSamplers.resize(src.samplers.size()); + for (U32 i = 0; i < src.samplers.size(); ++i) + { + mSamplers[i] = src.samplers[i]; + } + + mImages.resize(src.images.size()); + for (U32 i = 0; i < src.images.size(); ++i) + { + mImages[i] = src.images[i]; + } + + mAccessors.resize(src.accessors.size()); + for (U32 i = 0; i < src.accessors.size(); ++i) + { + mAccessors[i] = src.accessors[i]; + } + + mAnimations.resize(src.animations.size()); + for (U32 i = 0; i < src.animations.size(); ++i) + { + mAnimations[i] = src.animations[i]; + } + + mSkins.resize(src.skins.size()); + for (U32 i = 0; i < src.skins.size(); ++i) + { + mSkins[i] = src.skins[i]; + } + + return *this; +} + +const Material& Material::operator=(const tinygltf::Material& src) +{ + mName = src.name; + return *this; +} + +void Material::allocateGLResources(Asset& asset) +{ + // allocate material + mMaterial = new LLFetchedGLTFMaterial(); +} + +const Mesh& Mesh::operator=(const tinygltf::Mesh& src) +{ + mPrimitives.resize(src.primitives.size()); + for (U32 i = 0; i < src.primitives.size(); ++i) + { + mPrimitives[i] = src.primitives[i]; + } + + mWeights = src.weights; + mName = src.name; + + return *this; +} + +void Mesh::allocateGLResources(Asset& asset) +{ + for (auto& primitive : mPrimitives) + { + primitive.allocateGLResources(asset); + } +} + +const Scene& Scene::operator=(const tinygltf::Scene& src) +{ + mNodes = src.nodes; + mName = src.name; + + return *this; +} + +const Texture& Texture::operator=(const tinygltf::Texture& src) +{ + mSampler = src.sampler; + mSource = src.source; + mName = src.name; + + return *this; +} + +const Sampler& Sampler::operator=(const tinygltf::Sampler& src) +{ + mMagFilter = src.magFilter; + mMinFilter = src.minFilter; + mWrapS = src.wrapS; + mWrapT = src.wrapT; + mName = src.name; + + return *this; +} + +void Skin::uploadMatrixPalette(Asset& asset, Node& node) +{ + // prepare matrix palette + + // modelview will be applied by the shader, so assume matrix palette is in asset space + std::vector<glh::matrix4f> t_mp; + + t_mp.resize(mJoints.size()); + + for (U32 i = 0; i < mJoints.size(); ++i) + { + Node& joint = asset.mNodes[mJoints[i]]; + + //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr()); + //t_mp[i] = t_mp[i] * mInverseBindMatricesData[i]; + + //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr()); + //t_mp[i] = mInverseBindMatricesData[i] * t_mp[i]; + + t_mp[i].set_value(joint.mRenderMatrix.getF32ptr()); + t_mp[i] = t_mp[i] * mInverseBindMatricesData[i]; + + } + + std::vector<F32> glmp; + + glmp.resize(mJoints.size() * 12); + + F32* mp = glmp.data(); + + for (U32 i = 0; i < mJoints.size(); ++i) + { + F32* m = (F32*)t_mp[i].m; + + 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]; + } + + LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX, + mJoints.size(), + FALSE, + (GLfloat*)glmp.data()); +} diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index acf9ba77df..6e576a1ffe 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -29,86 +29,19 @@ #include "llvertexbuffer.h" #include "llvolumeoctree.h" #include "../lltinygltfhelper.h" +#include "accessor.h" #include "primitive.h" +#include "animation.h" + +extern F32SecondsImplicit gFrameTimeSeconds; // LL GLTF Implementation namespace LL { namespace GLTF { - constexpr S32 INVALID_INDEX = -1; - class Asset; - class Buffer - { - public: - std::vector<U8> mData; - std::string mName; - std::string mUri; - - const Buffer& operator=(const tinygltf::Buffer& src) - { - mData = src.data; - mName = src.name; - mUri = src.uri; - return *this; - } - }; - - class BufferView - { - public: - S32 mBuffer = INVALID_INDEX; - S32 mByteLength; - S32 mByteOffset; - S32 mByteStride; - S32 mTarget; - S32 mComponentType; - - std::string mName; - - const BufferView& operator=(const tinygltf::BufferView& src) - { - mBuffer = src.buffer; - mByteLength = src.byteLength; - mByteOffset = src.byteOffset; - mByteStride = src.byteStride; - mTarget = src.target; - mName = src.name; - return *this; - } - }; - - class Accessor - { - public: - S32 mBufferView = INVALID_INDEX; - S32 mByteOffset; - S32 mComponentType; - S32 mCount; - std::vector<double> mMax; - std::vector<double> mMin; - S32 mType; - bool mNormalized; - std::string mName; - - const Accessor& operator=(const tinygltf::Accessor& src) - { - mBufferView = src.bufferView; - mByteOffset = src.byteOffset; - mComponentType = src.componentType; - mCount = src.count; - mType = src.type; - mNormalized = src.normalized; - mName = src.name; - mMax = src.maxValues; - mMin = src.maxValues; - - return *this; - } - }; - class Material { public: @@ -118,17 +51,9 @@ namespace LL LLPointer<LLFetchedGLTFMaterial> mMaterial; std::string mName; - const Material& operator=(const tinygltf::Material& src) - { - mName = src.name; - return *this; - } - - void allocateGLResources(Asset& asset) - { - // allocate material - mMaterial = new LLFetchedGLTFMaterial(); - } + const Material& operator=(const tinygltf::Material& src); + + void allocateGLResources(Asset& asset); }; class Mesh @@ -138,28 +63,9 @@ namespace LL std::vector<double> mWeights; std::string mName; - const Mesh& operator=(const tinygltf::Mesh& src) - { - mPrimitives.resize(src.primitives.size()); - for (U32 i = 0; i < src.primitives.size(); ++i) - { - mPrimitives[i] = src.primitives[i]; - } - - mWeights = src.weights; - mName = src.name; - - return *this; - } - - void allocateGLResources(Asset& asset) - { - for (auto& primitive : mPrimitives) - { - primitive.allocateGLResources(asset); - } - } - + const Mesh& operator=(const tinygltf::Mesh& src); + + void allocateGLResources(Asset& asset); }; class Node @@ -170,10 +76,24 @@ namespace LL LLMatrix4a mAssetMatrix; //transform from local to asset space LLMatrix4a mAssetMatrixInv; //transform from asset to local space + glh::vec3f mTranslation; + glh::quaternionf mRotation; + glh::vec3f mScale; + + // if true, mMatrix is valid and up to date + bool mMatrixValid = false; + + // if true, translation/rotation/scale are valid and up to date + bool mTRSValid = false; + + bool mNeedsApplyMatrix = false; + std::vector<S32> mChildren; S32 mParent = INVALID_INDEX; S32 mMesh = INVALID_INDEX; + S32 mSkin = INVALID_INDEX; + std::string mName; const Node& operator=(const tinygltf::Node& src); @@ -184,26 +104,51 @@ namespace LL // update mAssetMatrix and mAssetMatrixInv void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix); - + + // ensure mMatrix is valid -- if mMatrixValid is false and mTRSValid is true, will update mMatrix to match Translation/Rotation/Scale + void makeMatrixValid(); + + // ensure Translation/Rotation/Scale are valid -- if mTRSValid is false and mMatrixValid is true, will update Translation/Rotation/Scale to match mMatrix + void makeTRSValid(); + + // Set rotation of this node + // SIDE EFFECT: invalidates mMatrix + void setRotation(const glh::quaternionf& rotation); + + // Set translation of this node + // SIDE EFFECT: invalidates mMatrix + void setTranslation(const glh::vec3f& translation); + + // Set scale of this node + // SIDE EFFECT: invalidates mMatrix + void setScale(const glh::vec3f& scale); }; - class Scene + class Skin { public: - std::vector<S32> mNodes; + S32 mInverseBindMatrices = INVALID_INDEX; + S32 mSkeleton = INVALID_INDEX; + std::vector<S32> mJoints; std::string mName; + std::vector<glh::matrix4f> mInverseBindMatricesData; - const Scene& operator=(const tinygltf::Scene& src) - { - mNodes = src.nodes; - mName = src.name; + void allocateGLResources(Asset& asset); + void uploadMatrixPalette(Asset& asset, Node& node); - return *this; - } + const Skin& operator=(const tinygltf::Skin& src); + }; + + class Scene + { + public: + std::vector<S32> mNodes; + std::string mName; + const Scene& operator=(const tinygltf::Scene& src); + void updateTransforms(Asset& asset); void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); - }; class Texture @@ -213,14 +158,7 @@ namespace LL S32 mSource = INVALID_INDEX; std::string mName; - const Texture& operator=(const tinygltf::Texture& src) - { - mSampler = src.sampler; - mSource = src.source; - mName = src.name; - - return *this; - } + const Texture& operator=(const tinygltf::Texture& src); }; class Sampler @@ -232,16 +170,7 @@ namespace LL S32 mWrapT; std::string mName; - const Sampler& operator=(const tinygltf::Sampler& src) - { - mMagFilter = src.magFilter; - mMinFilter = src.minFilter; - mWrapS = src.wrapS; - mWrapT = src.wrapT; - mName = src.name; - - return *this; - } + const Sampler& operator=(const tinygltf::Sampler& src); }; class Image @@ -292,27 +221,21 @@ namespace LL std::vector<Sampler> mSamplers; std::vector<Image> mImages; std::vector<Accessor> mAccessors; + std::vector<Animation> mAnimations; + std::vector<Skin> mSkins; - void allocateGLResources(const std::string& filename, const tinygltf::Model& model) - { - // do images first as materials may depend on images - for (auto& image : mImages) - { - image.allocateGLResources(); - } - - // do materials before meshes as meshes may depend on materials - for (U32 i = 0; i < mMaterials.size(); ++i) - { - mMaterials[i].allocateGLResources(*this); - LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); - } - - for (auto& mesh : mMeshes) - { - mesh.allocateGLResources(*this); - } - } + // the last time update() was called according to gFrameTimeSeconds + F32 mLastUpdateTime = gFrameTimeSeconds; + + // prepare the asset for rendering + void allocateGLResources(const std::string& filename, const tinygltf::Model& model); + + // Called periodically (typically once per frame) + // Any ongoing work (such as animations) should be handled here + // NOT guaranteed to be called every frame + // MAY be called more than once per frame + // Upon return, all Node Matrix transforms should be up to date + void update(); // update asset-to-node and node-to-asset transforms void updateTransforms(); @@ -320,7 +243,7 @@ namespace LL // update node render transforms void updateRenderTransforms(const LLMatrix4a& modelview); - void render(bool opaque); + void render(bool opaque, bool rigged = false); void renderOpaque(); void renderTransparent(); @@ -334,70 +257,8 @@ namespace LL S32* primitive_hitp = nullptr // return the index of the primitive that was hit ); - const Asset& operator=(const tinygltf::Model& src) - { - mScenes.resize(src.scenes.size()); - for (U32 i = 0; i < src.scenes.size(); ++i) - { - mScenes[i] = src.scenes[i]; - } - - mNodes.resize(src.nodes.size()); - for (U32 i = 0; i < src.nodes.size(); ++i) - { - mNodes[i] = src.nodes[i]; - } - - mMeshes.resize(src.meshes.size()); - for (U32 i = 0; i < src.meshes.size(); ++i) - { - mMeshes[i] = src.meshes[i]; - } - - mMaterials.resize(src.materials.size()); - for (U32 i = 0; i < src.materials.size(); ++i) - { - mMaterials[i] = src.materials[i]; - } - - mBuffers.resize(src.buffers.size()); - for (U32 i = 0; i < src.buffers.size(); ++i) - { - mBuffers[i] = src.buffers[i]; - } - - mBufferViews.resize(src.bufferViews.size()); - for (U32 i = 0; i < src.bufferViews.size(); ++i) - { - mBufferViews[i] = src.bufferViews[i]; - } - - mTextures.resize(src.textures.size()); - for (U32 i = 0; i < src.textures.size(); ++i) - { - mTextures[i] = src.textures[i]; - } - - mSamplers.resize(src.samplers.size()); - for (U32 i = 0; i < src.samplers.size(); ++i) - { - mSamplers[i] = src.samplers[i]; - } - - mImages.resize(src.images.size()); - for (U32 i = 0; i < src.images.size(); ++i) - { - mImages[i] = src.images[i]; - } - - mAccessors.resize(src.accessors.size()); - for (U32 i = 0; i < src.accessors.size(); ++i) - { - mAccessors[i] = src.accessors[i]; - } - - return *this; - } + const Asset& operator=(const tinygltf::Model& src); + }; } } diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h new file mode 100644 index 0000000000..4e6f5901e7 --- /dev/null +++ b/indra/newview/gltf/buffer_util.h @@ -0,0 +1,402 @@ +#pragma once + +/** + * @file buffer_util.inl + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// inline template implementations for copying data out of GLTF buffers +// DO NOT include from header files to avoid the need to rebuild the whole project +// whenever we add support for more types + +#ifdef _MSC_VER +#define LL_FUNCSIG __FUNCSIG__ +#else +#define LL_FUNCSIG __PRETTY_FUNCTION__ +#endif + +namespace LL +{ + namespace GLTF + { + // copy one Scalar from src to dst + template<class S, class T> + static void copyScalar(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + // copy one vec2 from src to dst + template<class S, class T> + static void copyVec2(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + // copy one vec3 from src to dst + template<class S, class T> + static void copyVec3(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + // copy one vec4 from src to dst + template<class S, class T> + static void copyVec4(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + // copy one vec2 from src to dst + template<class S, class T> + static void copyMat2(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + // copy one vec3 from src to dst + template<class S, class T> + static void copyMat3(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + // copy one vec4 from src to dst + template<class S, class T> + static void copyMat4(S* src, T& dst) + { + LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; + } + + //========================================================================================================= + // concrete implementations for different types of source and destination + //========================================================================================================= + +// suppress unused function warning -- clang complains here but these specializations are definitely used +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + + template<> + void copyScalar<F32, F32>(F32* src, F32& dst) + { + dst = *src; + } + + template<> + void copyScalar<U32, U32>(U32* src, U32& dst) + { + dst = *src; + } + + template<> + void copyScalar<U32, U16>(U32* src, U16& dst) + { + dst = *src; + } + + template<> + void copyScalar<U16, U16>(U16* src, U16& dst) + { + dst = *src; + } + + template<> + void copyScalar<U16, U32>(U16* src, U32& dst) + { + dst = *src; + } + + template<> + void copyScalar<U8, U16>(U8* src, U16& dst) + { + dst = *src; + } + + template<> + void copyScalar<U8, U32>(U8* src, U32& dst) + { + dst = *src; + } + + template<> + void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst) + { + dst.set(src[0], src[1]); + } + + template<> + void copyVec3<F32, glh::vec3f>(F32* src, glh::vec3f& dst) + { + dst.set_value(src[0], src[1], src[2]); + } + + template<> + void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) + { + dst.load3(src); + } + + template<> + void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst) + { + dst.set(src[0], src[1], src[2], 255); + } + + template<> + void copyVec4<U8, LLColor4U>(U8* src, LLColor4U& dst) + { + dst.set(src[0], src[1], src[2], src[3]); + } + + template<> + void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst) + { + dst.set(src[0], src[1], src[2], src[3]); + } + + template<> + void copyVec4<F32, LLColor4U>(F32* src, LLColor4U& dst) + { + dst.set(src[0]*255, src[1]*255, src[2]*255, src[3]*255); + } + + template<> + void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst) + { + dst.loadua(src); + } + + template<> + void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst) + { + dst.set(src[0], src[1], src[2], src[3]); + } + + template<> + void copyVec4<U8, LLVector4a>(U8* src, LLVector4a& dst) + { + dst.set(src[0], src[1], src[2], src[3]); + } + + template<> + void copyVec4<F32, glh::quaternionf>(F32* src, glh::quaternionf& dst) + { + dst.set_value(src); + } + + template<> + void copyMat4<F32, glh::matrix4f>(F32* src, glh::matrix4f& dst) + { + dst.set_value(src); + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + //========================================================================================================= + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyScalar(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyScalar(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyVec2(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyVec3(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyVec4(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyMat2(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyMat2(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyMat3(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyMat3(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy + template<class S, class T> + static void copyMat4(S* src, LLStrider<T> dst, S32 stride, S32 count) + { + for (S32 i = 0; i < count; ++i) + { + copyMat4(src, *dst); + dst++; + src = (S*)((U8*)src + stride); + } + } + + template<class S, class T> + static void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) + { + if (accessor.mType == (S32)Accessor::Type::SCALAR) + { + S32 stride = byteStride == 0 ? sizeof(S) * 1 : byteStride; + copyScalar((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == (S32)Accessor::Type::VEC2) + { + S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride; + copyVec2((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == (S32)Accessor::Type::VEC3) + { + S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride; + copyVec3((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == (S32)Accessor::Type::VEC4) + { + S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; + copyVec4((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == (S32)Accessor::Type::MAT2) + { + S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; + copyMat2((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == (S32)Accessor::Type::MAT3) + { + S32 stride = byteStride == 0 ? sizeof(S) * 9 : byteStride; + copyMat3((S*)src, dst, stride, accessor.mCount); + } + else if (accessor.mType == (S32)Accessor::Type::MAT4) + { + S32 stride = byteStride == 0 ? sizeof(S) * 16 : byteStride; + copyMat4((S*)src, dst, stride, accessor.mCount); + } + else + { + LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL; + } + } + + // copy data from accessor to strider + template<class T> + static void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst) + { + const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; + const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; + + if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT) + { + LL::GLTF::copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + LL::GLTF::copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + LL::GLTF::copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) + { + LL::GLTF::copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_SHORT) + { + LL::GLTF::copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_BYTE) + { + LL::GLTF::copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride); + } + else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) + { + LL::GLTF::copy(asset, accessor, (const F64*)src, dst, bufferView.mByteStride); + } + else + { + LL_ERRS("GLTF") << "Unsupported component type" << LL_ENDL; + } + } + + // copy data from accessor to vector + template<class T> + static void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst) + { + dst.resize(accessor.mCount); + LLStrider<T> strider = dst.data(); + copy(asset, accessor, strider); + } + } +} + diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index b5d59e1b24..b57a0af18d 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -27,163 +27,12 @@ #include "../llviewerprecompiledheaders.h" #include "asset.h" +#include "buffer_util.h" + #include "../lltinygltfhelper.h" using namespace LL::GLTF; -#ifdef _MSC_VER -#define LL_FUNCSIG __FUNCSIG__ -#else -#define LL_FUNCSIG __PRETTY_FUNCTION__ -#endif - -// copy one vec3 from src to dst -template<class S, class T> -void copyVec2(S* src, T& dst) -{ - LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; -} - -// copy one vec3 from src to dst -template<class S, class T> -void copyVec3(S* src, T& dst) -{ - LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; -} - -// copy one vec4 from src to dst -template<class S, class T> -void copyVec4(S* src, T& dst) -{ - LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; -} - -template<> -void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst) -{ - dst.set(src[0], src[1]); -} - -template<> -void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) -{ - dst.load3(src); -} - -template<> -void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst) -{ - dst.set(src[0], src[1], src[2], 255); -} - -template<> -void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst) -{ - dst.loadua(src); -} - -// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy -template<class S, class T> -void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count) -{ - for (S32 i = 0; i < count; ++i) - { - copyVec2(src, *dst); - dst++; - src = (S*)((U8*)src + stride); - } -} - -// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy -template<class S, class T> -void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count) -{ - for (S32 i = 0; i < count; ++i) - { - copyVec3(src, *dst); - dst++; - src = (S*)((U8*)src + stride); - } -} - -// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy -template<class S, class T> -void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count) -{ - for (S32 i = 0; i < count; ++i) - { - copyVec3(src, *dst); - dst++; - src = (S*)((U8*)src + stride); - } -} - -template<class S, class T> -void copyAttributeArray(Asset& asset, const Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) -{ - if (accessor.mType == TINYGLTF_TYPE_VEC2) - { - S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride; - copyVec2((S*)src, dst, stride, accessor.mCount); - } - else if (accessor.mType == TINYGLTF_TYPE_VEC3) - { - S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride; - copyVec3((S*)src, dst, stride, accessor.mCount); - } - else if (accessor.mType == TINYGLTF_TYPE_VEC4) - { - S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride; - copyVec4((S*)src, dst, stride, accessor.mCount); - } - else - { - LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL; - } -} - -template <class T> -void Primitive::copyAttribute(Asset& asset, S32 accessorIdx, LLStrider<T>& dst) -{ - const Accessor& accessor = asset.mAccessors[accessorIdx]; - const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; - const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; - const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; - - if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT) - { - copyAttributeArray(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) - { - copyAttributeArray(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) - { - copyAttributeArray(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) - { - copyAttributeArray(asset, accessor, (const U8*)src, dst, bufferView.mByteStride); - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_SHORT) - { - copyAttributeArray(asset, accessor, (const S16*)src, dst, bufferView.mByteStride); - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_BYTE) - { - copyAttributeArray(asset, accessor, (const S8*)src, dst, bufferView.mByteStride); - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) - { - copyAttributeArray(asset, accessor, (const F64*)src, dst, bufferView.mByteStride); - } - else - { - LL_ERRS("GLTF") << "Unsupported component type" << LL_ENDL; - } -} - void Primitive::allocateGLResources(Asset& asset) { // allocate vertex buffer @@ -192,219 +41,138 @@ void Primitive::allocateGLResources(Asset& asset) // For our engine, though, it's better to rearrange the buffers at load time into a layout that's more consistent. // The GLTF native approach undoubtedly works well if you can count on VAOs, but VAOs perform much worse with our scenes. - // get the number of vertices - U32 numVertices = 0; - if (!mAttributes.empty()) - { - auto it = mAttributes.begin(); - const Accessor& accessor = asset.mAccessors[it->second]; - numVertices = accessor.mCount; - } - - // get the number of indices - U32 numIndices = 0; - if (mIndices != INVALID_INDEX) - { - const Accessor& accessor = asset.mAccessors[mIndices]; - numIndices = accessor.mCount; - } - - // create vertex buffer - mVertexBuffer = new LLVertexBuffer(ATTRIBUTE_MASK); - mVertexBuffer->allocateBuffer(numVertices, numIndices); - - bool needs_color = true; - bool needs_texcoord = true; - bool needs_normal = true; - bool needs_tangent = true; - // load vertex data for (auto& it : mAttributes) { const std::string& attribName = it.first; + Accessor& accessor = asset.mAccessors[it.second]; // load vertex data if (attribName == "POSITION") { - // load position data - LLStrider<LLVector4a> dst; - mVertexBuffer->getVertexStrider(dst); - - copyAttribute(asset, it.second, dst); + copy(asset, accessor, mPositions); } else if (attribName == "NORMAL") { - needs_normal = false; - // load normal data - LLStrider<LLVector4a> dst; - mVertexBuffer->getNormalStrider(dst); - - copyAttribute(asset, it.second, dst); + copy(asset, accessor, mNormals); } else if (attribName == "TANGENT") { - needs_tangent = false; - // load tangent data - - LLStrider<LLVector4a> dst; - mVertexBuffer->getTangentStrider(dst); - - copyAttribute(asset, it.second, dst); + copy(asset, accessor, mTangents); } else if (attribName == "COLOR_0") { - needs_color = false; - // load color data - - LLStrider<LLColor4U> dst; - mVertexBuffer->getColorStrider(dst); - - copyAttribute(asset, it.second, dst); + copy(asset, accessor, mColors); } else if (attribName == "TEXCOORD_0") { - needs_texcoord = false; - // load texcoord data - LLStrider<LLVector2> dst; - mVertexBuffer->getTexCoord0Strider(dst); - - LLStrider<LLVector2> tc = dst; - copyAttribute(asset, it.second, dst); - - // convert to OpenGL coordinate space - for (U32 i = 0; i < numVertices; ++i) - { - tc->mV[1] = 1.0f - tc->mV[1];; - tc++; - } + copy(asset, accessor, mTexCoords); + } + else if (attribName == "JOINTS_0") + { + copy(asset, accessor, mJoints); + } + else if (attribName == "WEIGHTS_0") + { + copy(asset, accessor, mWeights); } } // copy index buffer if (mIndices != INVALID_INDEX) { - const Accessor& accessor = asset.mAccessors[mIndices]; - const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; - const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + Accessor& accessor = asset.mAccessors[mIndices]; + copy(asset, accessor, mIndexArray); + } - const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; + U32 mask = ATTRIBUTE_MASK; + + if (!mWeights.empty()) + { + mask |= LLVertexBuffer::MAP_WEIGHT4; + } - LLStrider<U16> dst; - mVertexBuffer->getIndexStrider(dst); - mIndexArray.resize(numIndices); + mVertexBuffer = new LLVertexBuffer(mask); + mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices - if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) - { - for (U32 i = 0; i < numIndices; ++i) - { - *(dst++) = (U16) * (U32*)src; - src += sizeof(U32); - } - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) - { - for (U32 i = 0; i < numIndices; ++i) - { - *(dst++) = *(U16*)src; - src += sizeof(U16); - } - } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) - { - for (U32 i = 0; i < numIndices; ++i) - { - *(dst++) = *(U8*)src; - src += sizeof(U8); - } - } - else - { - LL_ERRS("GLTF") << "Unsupported component type for indices" << LL_ENDL; - } + mVertexBuffer->setBuffer(); + mVertexBuffer->setPositionData(mPositions.data()); - U16* idx = (U16*)mVertexBuffer->getMappedIndices(); - for (U32 i = 0; i < numIndices; ++i) - { - mIndexArray[i] = idx[i]; - } + if (!mIndexArray.empty()) + { + mVertexBuffer->setIndexData(mIndexArray.data()); } - // fill in default values for missing attributes - if (needs_color) - { // set default color - LLStrider<LLColor4U> dst; - mVertexBuffer->getColorStrider(dst); - for (U32 i = 0; i < numVertices; ++i) - { - *(dst++) = LLColor4U(255, 255, 255, 255); - } + if (mTexCoords.empty()) + { + mTexCoords.resize(mPositions.size()); + } + + // flip texcoord y, upload, then flip back (keep the off-spec data in vram only) + for (auto& tc : mTexCoords) + { + tc[1] = 1.f - tc[1]; } + mVertexBuffer->setTexCoordData(mTexCoords.data()); + for (auto& tc : mTexCoords) + { + tc[1] = 1.f - tc[1]; + } + + if (mColors.empty()) + { + mColors.resize(mPositions.size(), LLColor4U::white); + } + // bake material basecolor into color array if (mMaterial != INVALID_INDEX) { const Material& material = asset.mMaterials[mMaterial]; LLColor4 baseColor = material.mMaterial->mBaseColor; - LLStrider<LLColor4U> dst; - mVertexBuffer->getColorStrider(dst); - - for (U32 i = 0; i < numVertices; ++i) + for (auto& dst : mColors) { - LLColor4 col = *dst; - *dst = LLColor4U(baseColor * col); - dst++; + dst = LLColor4U(baseColor * LLColor4(dst)); } } - if (needs_texcoord) - { // set default texcoord - LLStrider<LLVector2> dst; - mVertexBuffer->getTexCoord0Strider(dst); - for (U32 i = 0; i < numVertices; ++i) - { - *(dst++) = LLVector2(0.0f, 0.0f); - } - } + mVertexBuffer->setColorData(mColors.data()); - if (needs_normal) - { // set default normal - LLStrider<LLVector4a> dst; - mVertexBuffer->getNormalStrider(dst); - for (U32 i = 0; i < numVertices; ++i) - { - *(dst++) = LLVector4a(0.0f, 0.0f, 1.0f, 0.0f); - } + if (mNormals.empty()) + { + mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0)); } + + mVertexBuffer->setNormalData(mNormals.data()); - if (needs_tangent) - { // TODO: generate tangents if needed - LLStrider<LLVector4a> dst; - mVertexBuffer->getTangentStrider(dst); - for (U32 i = 0; i < numVertices; ++i) - { - *(dst++) = LLVector4a(1.0f, 0.0f, 0.0f, 1.0f); - } + if (mTangents.empty()) + { + // TODO: generate tangents if needed + mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1)); } - mPositions.resize(numVertices); - mTexCoords.resize(numVertices); - mNormals.resize(numVertices); - mTangents.resize(numVertices); + mVertexBuffer->setTangentData(mTangents.data()); - LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); - LLVector2* tc = (LLVector2*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TEXCOORD0)); - LLVector4a* norm = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_NORMAL)); - LLVector4a* tangent = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_TANGENT)); - for (U32 i = 0; i < numVertices; ++i) + if (!mWeights.empty()) { - mPositions[i] = pos[i]; - mTexCoords[i] = tc[i]; - mNormals[i] = norm[i]; - mTangents[i] = tangent[i]; + std::vector<LLVector4a> weight_data; + weight_data.resize(mWeights.size()); + + F32 max_weight = 1.f - FLT_EPSILON*100.f; + LLVector4a maxw(max_weight, max_weight, max_weight, max_weight); + for (U32 i = 0; i < mWeights.size(); ++i) + { + LLVector4a& w = weight_data[i]; + w.setMin(mWeights[i], maxw); + w.add(mJoints[i]); + }; + + mVertexBuffer->setWeight4Data(weight_data.data()); } + createOctree(); - mVertexBuffer->unmapBuffer(); + mVertexBuffer->unbind(); } void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2) @@ -456,20 +224,17 @@ void Primitive::createOctree() // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); - LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); - U16* indices = (U16*)mVertexBuffer->getMappedIndices(); - for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) { //for each triangle const U32 index = triangle_index * 3; LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; - S32 i0 = indices[index]; - S32 i1 = indices[index + 1]; - S32 i2 = indices[index + 2]; + S32 i0 = mIndexArray[index]; + S32 i1 = mIndexArray[index + 1]; + S32 i2 = mIndexArray[index + 2]; - const LLVector4a& v0 = pos[i0]; - const LLVector4a& v1 = pos[i1]; - const LLVector4a& v2 = pos[i2]; + const LLVector4a& v0 = mPositions[i0]; + const LLVector4a& v1 = mPositions[i1]; + const LLVector4a& v2 = mPositions[i2]; initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2); @@ -483,20 +248,17 @@ void Primitive::createOctree() // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); - LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); - U16* indices = (U16*)mVertexBuffer->getMappedIndices(); - for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) { //for each triangle const U32 index = triangle_index + 2; LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; - S32 i0 = indices[index]; - S32 i1 = indices[index - 1]; - S32 i2 = indices[index - 2]; + S32 i0 = mIndexArray[index]; + S32 i1 = mIndexArray[index - 1]; + S32 i2 = mIndexArray[index - 2]; - const LLVector4a& v0 = pos[i0]; - const LLVector4a& v1 = pos[i1]; - const LLVector4a& v2 = pos[i2]; + const LLVector4a& v0 = mPositions[i0]; + const LLVector4a& v1 = mPositions[i1]; + const LLVector4a& v2 = mPositions[i2]; initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2); @@ -510,20 +272,17 @@ void Primitive::createOctree() // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); - LLVector4a* pos = (LLVector4a*)(mVertexBuffer->getMappedData() + mVertexBuffer->getOffset(LLVertexBuffer::TYPE_VERTEX)); - U16* indices = (U16*)mVertexBuffer->getMappedIndices(); - for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) { //for each triangle const U32 index = triangle_index + 2; LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; - S32 i0 = indices[0]; - S32 i1 = indices[index - 1]; - S32 i2 = indices[index - 2]; + S32 i0 = mIndexArray[0]; + S32 i1 = mIndexArray[index - 1]; + S32 i2 = mIndexArray[index - 2]; - const LLVector4a& v0 = pos[i0]; - const LLVector4a& v1 = pos[i1]; - const LLVector4a& v2 = pos[i2]; + const LLVector4a& v0 = mPositions[i0]; + const LLVector4a& v1 = mPositions[i1]; + const LLVector4a& v2 = mPositions[i2]; initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2); @@ -571,7 +330,7 @@ const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, face.mTexCoords = mTexCoords.data(); face.mNormals = mNormals.data(); face.mTangents = mTangents.data(); - face.mIndices = mIndexArray.data(); + face.mIndices = nullptr; // unreferenced face.mNumIndices = mIndexArray.size(); face.mNumVertices = mPositions.size(); @@ -592,3 +351,50 @@ Primitive::~Primitive() mOctree = nullptr; } + +const Primitive& Primitive::operator=(const tinygltf::Primitive& src) +{ + // load material + mMaterial = src.material; + + // load mode + mMode = src.mode; + + // load indices + mIndices = src.indices; + + // load attributes + for (auto& it : src.attributes) + { + mAttributes[it.first] = it.second; + } + + switch (mMode) + { + case TINYGLTF_MODE_POINTS: + mGLMode = LLRender::POINTS; + break; + case TINYGLTF_MODE_LINE: + mGLMode = LLRender::LINES; + break; + case TINYGLTF_MODE_LINE_LOOP: + mGLMode = LLRender::LINE_LOOP; + break; + case TINYGLTF_MODE_LINE_STRIP: + mGLMode = LLRender::LINE_STRIP; + break; + case TINYGLTF_MODE_TRIANGLES: + mGLMode = LLRender::TRIANGLES; + break; + case TINYGLTF_MODE_TRIANGLE_STRIP: + mGLMode = LLRender::TRIANGLE_STRIP; + break; + case TINYGLTF_MODE_TRIANGLE_FAN: + mGLMode = LLRender::TRIANGLE_FAN; + break; + default: + mGLMode = GL_TRIANGLES; + } + + return *this; +} diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index 7c47d9dac5..07e8e7deb2 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -56,7 +56,10 @@ namespace LL std::vector<LLVector4a> mNormals; std::vector<LLVector4a> mTangents; std::vector<LLVector4a> mPositions; - std::vector<U16> mIndexArray; + std::vector<LLVector4a> mJoints; + std::vector<LLVector4a> mWeights; + std::vector<LLColor4U> mColors; + std::vector<U32> mIndexArray; // raycast acceleration structure LLPointer<LLVolumeOctree> mOctree; @@ -68,11 +71,6 @@ namespace LL S32 mIndices = -1; std::unordered_map<std::string, int> mAttributes; - // copy the attribute in the given BufferView to the given destination - // assumes destination has enough storage for the attribute - template<class T> - void copyAttribute(Asset& asset, S32 bufferViewIdx, LLStrider<T>& dst); - // create octree based on vertex buffer // must be called before buffer is unmapped and after buffer is populated with good data void createOctree(); @@ -87,52 +85,7 @@ namespace LL LLVector4a* tangent = NULL // return the surface tangent at the intersection point ); - const Primitive& operator=(const tinygltf::Primitive& src) - { - // load material - mMaterial = src.material; - - // load mode - mMode = src.mode; - - // load indices - mIndices = src.indices; - - // load attributes - for (auto& it : src.attributes) - { - mAttributes[it.first] = it.second; - } - - switch (mMode) - { - case TINYGLTF_MODE_POINTS: - mGLMode = LLRender::POINTS; - break; - case TINYGLTF_MODE_LINE: - mGLMode = LLRender::LINES; - break; - case TINYGLTF_MODE_LINE_LOOP: - mGLMode = LLRender::LINE_LOOP; - break; - case TINYGLTF_MODE_LINE_STRIP: - mGLMode = LLRender::LINE_STRIP; - break; - case TINYGLTF_MODE_TRIANGLES: - mGLMode = LLRender::TRIANGLES; - break; - case TINYGLTF_MODE_TRIANGLE_STRIP: - mGLMode = LLRender::TRIANGLE_STRIP; - break; - case TINYGLTF_MODE_TRIANGLE_FAN: - mGLMode = LLRender::TRIANGLE_FAN; - break; - default: - mGLMode = GL_TRIANGLES; - } - - return *this; - } + const Primitive& operator=(const tinygltf::Primitive& src); void allocateGLResources(Asset& asset); }; diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 8273c707f9..4e3439ea5c 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -82,6 +82,7 @@ void GLTFSceneManager::load(const std::string& filename) LLPointer<Asset> asset = new Asset(); *asset = model; + gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions asset->allocateGLResources(filename, model); asset->updateTransforms(); @@ -114,7 +115,25 @@ void GLTFSceneManager::renderAlpha() render(false); } -void GLTFSceneManager::render(bool opaque) +void GLTFSceneManager::update() +{ + for (U32 i = 0; i < mObjects.size(); ++i) + { + if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr) + { + mObjects.erase(mObjects.begin() + i); + --i; + continue; + } + + Asset* asset = mObjects[i]->mGLTFAsset; + + asset->update(); + + } +} + +void GLTFSceneManager::render(bool opaque, bool rigged) { // for debugging, just render the whole scene as opaque // by traversing the whole scenegraph @@ -144,7 +163,7 @@ void GLTFSceneManager::render(bool opaque) matMul(mat, modelview, modelview); asset->updateRenderTransforms(modelview); - asset->render(opaque); + asset->render(opaque, rigged); gGL.popMatrix(); } diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h index 4e9013834b..d286f335e4 100644 --- a/indra/newview/gltfscenemanager.h +++ b/indra/newview/gltfscenemanager.h @@ -36,9 +36,12 @@ namespace LL public: ~GLTFSceneManager(); // load GLTF file from disk + void load(); // open filepicker to choose asset void load(const std::string& filename); // load asset from filename - void render(bool opaque); + + void update(); + void render(bool opaque, bool rigged = false); void renderOpaque(); void renderAlpha(); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 46b95601af..c4c7d5675f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4857,6 +4857,7 @@ void LLAppViewer::idle() if (!(logoutRequestSent() && hasSavedFinalSnapshot())) { gObjectList.update(gAgent); + LL::GLTFSceneManager::instance().update(); } } diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 81014e9d6e..9418cead76 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -261,10 +261,13 @@ void LLDrawPoolAlpha::forwardRender(bool rigged) mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // } gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); - if (write_depth) - { // draw GLTF scene to depth buffer + if (rigged) + { // draw GLTF scene to depth buffer before rigged alpha gPipeline.bindDeferredShader(gDeferredPBRAlphaProgram); - LL::GLTFSceneManager::instance().renderAlpha(); + LL::GLTFSceneManager::instance().render(false, false); + + gPipeline.bindDeferredShader(*gDeferredPBRAlphaProgram.mRiggedVariant); + LL::GLTFSceneManager::instance().render(false, true); } // If the face is more than 90% transparent, then don't update the Depth buffer for Dof diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp index a32382af92..a32b6b1687 100644 --- a/indra/newview/lldrawpoolpbropaque.cpp +++ b/indra/newview/lldrawpoolpbropaque.cpp @@ -61,6 +61,7 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass) gDeferredPBROpaqueProgram.bind(true); + LL::GLTFSceneManager::instance().render(true, true); pushRiggedGLTFBatches(mRenderType + 1); } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index fd8a2e3ffc..341c9706f8 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -6593,6 +6593,10 @@ void LLPipeline::renderGLTFObjects(U32 type, bool texture, bool rigged) { LL::GLTFSceneManager::instance().renderOpaque(); } + else + { + LL::GLTFSceneManager::instance().render(true, true); + } } // Currently only used for shadows -Cosmic,2023-04-19 |