1285 GLTF Animation Prototype
parent
f1b7e806eb
commit
cadc1a02cc
|
|
@ -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;}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -4857,6 +4857,7 @@ void LLAppViewer::idle()
|
|||
if (!(logoutRequestSent() && hasSavedFinalSnapshot()))
|
||||
{
|
||||
gObjectList.update(gAgent);
|
||||
LL::GLTFSceneManager::instance().update();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass)
|
|||
|
||||
|
||||
gDeferredPBROpaqueProgram.bind(true);
|
||||
LL::GLTFSceneManager::instance().render(true, true);
|
||||
pushRiggedGLTFBatches(mRenderType + 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue