1285 GLTF Animation Prototype

master
RunitaiLinden 2024-04-24 09:51:15 -05:00 committed by GitHub
parent f1b7e806eb
commit cadc1a02cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1710 additions and 655 deletions

View File

@ -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;}

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);
};
}
}

View File

@ -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;
}

View File

@ -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);
};
}
}

View File

@ -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());
}

View File

@ -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,7 +104,39 @@ 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 Skin
{
public:
S32 mInverseBindMatrices = INVALID_INDEX;
S32 mSkeleton = INVALID_INDEX;
std::vector<S32> mJoints;
std::string mName;
std::vector<glh::matrix4f> mInverseBindMatricesData;
void allocateGLResources(Asset& asset);
void uploadMatrixPalette(Asset& asset, Node& node);
const Skin& operator=(const tinygltf::Skin& src);
};
class Scene
@ -193,17 +145,10 @@ namespace LL
std::vector<S32> mNodes;
std::string mName;
const Scene& operator=(const tinygltf::Scene& src)
{
mNodes = src.nodes;
mName = src.name;
return *this;
}
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();
}
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
// 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);
}
}
// 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);
};
}
}

View File

@ -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);
}
}
}

View File

@ -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];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
LLStrider<U16> dst;
mVertexBuffer->getIndexStrider(dst);
mIndexArray.resize(numIndices);
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;
}
U16* idx = (U16*)mVertexBuffer->getMappedIndices();
for (U32 i = 0; i < numIndices; ++i)
{
mIndexArray[i] = idx[i];
}
Accessor& accessor = asset.mAccessors[mIndices];
copy(asset, accessor, mIndexArray);
}
// 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);
}
U32 mask = ATTRIBUTE_MASK;
if (!mWeights.empty())
{
mask |= LLVertexBuffer::MAP_WEIGHT4;
}
mVertexBuffer = new LLVertexBuffer(mask);
mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices
mVertexBuffer->setBuffer();
mVertexBuffer->setPositionData(mPositions.data());
if (!mIndexArray.empty())
{
mVertexBuffer->setIndexData(mIndexArray.data());
}
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 (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);
}
}
mPositions.resize(numVertices);
mTexCoords.resize(numVertices);
mNormals.resize(numVertices);
mTangents.resize(numVertices);
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 (mNormals.empty())
{
mPositions[i] = pos[i];
mTexCoords[i] = tc[i];
mNormals[i] = norm[i];
mTangents[i] = tangent[i];
mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
}
mVertexBuffer->setNormalData(mNormals.data());
if (mTangents.empty())
{
// TODO: generate tangents if needed
mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1));
}
mVertexBuffer->setTangentData(mTangents.data());
if (!mWeights.empty())
{
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;
}

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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();

View File

@ -4857,6 +4857,7 @@ void LLAppViewer::idle()
if (!(logoutRequestSent() && hasSavedFinalSnapshot()))
{
gObjectList.update(gAgent);
LL::GLTFSceneManager::instance().update();
}
}

View File

@ -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

View File

@ -61,6 +61,7 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass)
gDeferredPBROpaqueProgram.bind(true);
LL::GLTFSceneManager::instance().render(true, true);
pushRiggedGLTFBatches(mRenderType + 1);
}

View File

@ -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