summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorRunitaiLinden <davep@lindenlab.com>2024-04-24 09:51:15 -0500
committerGitHub <noreply@github.com>2024-04-24 09:51:15 -0500
commitcadc1a02cc7289dabd368dd1a1d237c042e9f82e (patch)
tree7493c7a7a8a8b395bc9fc98a1a9dd018098c4147 /indra
parentf1b7e806eb30a343876036eff5caef7c03309aa6 (diff)
1285 GLTF Animation Prototype
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/llstrider.h2
-rw-r--r--indra/llmath/llvolumeoctree.h2
-rw-r--r--indra/llrender/llvertexbuffer.cpp43
-rw-r--r--indra/llrender/llvertexbuffer.h8
-rw-r--r--indra/newview/CMakeLists.txt5
-rw-r--r--indra/newview/gltf/accessor.cpp66
-rw-r--r--indra/newview/gltf/accessor.h95
-rw-r--r--indra/newview/gltf/animation.cpp287
-rw-r--r--indra/newview/gltf/animation.h181
-rw-r--r--indra/newview/gltf/asset.cpp375
-rw-r--r--indra/newview/gltf/asset.h295
-rw-r--r--indra/newview/gltf/buffer_util.h402
-rw-r--r--indra/newview/gltf/primitive.cpp488
-rw-r--r--indra/newview/gltf/primitive.h57
-rw-r--r--indra/newview/gltfscenemanager.cpp23
-rw-r--r--indra/newview/gltfscenemanager.h5
-rw-r--r--indra/newview/llappviewer.cpp1
-rw-r--r--indra/newview/lldrawpoolalpha.cpp9
-rw-r--r--indra/newview/lldrawpoolpbropaque.cpp1
-rw-r--r--indra/newview/pipeline.cpp4
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