summaryrefslogtreecommitdiff
path: root/indra/newview/gltf
diff options
context:
space:
mode:
authorDave Parks <davep@lindenlab.com>2024-05-28 09:45:40 -0500
committerGitHub <noreply@github.com>2024-05-28 09:45:40 -0500
commit2f4120038429c6aff865f153f708ceefb60d67f4 (patch)
tree85e63488eb66760996fd2e0c55c40fdc7b5b38b3 /indra/newview/gltf
parentdb627bc3544b828982a2a366972e0adf86e42f75 (diff)
Remove tinygltf dependency from LL::GLTF (#1541)
* #1535 Image loading/saving support in boost::json driven GLTF parser * #1536 GLB Support in boost::json drvien GLTF parser
Diffstat (limited to 'indra/newview/gltf')
-rw-r--r--indra/newview/gltf/README.md30
-rw-r--r--indra/newview/gltf/accessor.cpp158
-rw-r--r--indra/newview/gltf/accessor.h49
-rw-r--r--indra/newview/gltf/animation.cpp104
-rw-r--r--indra/newview/gltf/animation.h36
-rw-r--r--indra/newview/gltf/asset.cpp862
-rw-r--r--indra/newview/gltf/asset.h67
-rw-r--r--indra/newview/gltf/buffer_util.h100
-rw-r--r--indra/newview/gltf/common.h1
-rw-r--r--indra/newview/gltf/primitive.cpp86
-rw-r--r--indra/newview/gltf/primitive.h24
11 files changed, 628 insertions, 889 deletions
diff --git a/indra/newview/gltf/README.md b/indra/newview/gltf/README.md
index 8e7df0a439..a2d43be1d6 100644
--- a/indra/newview/gltf/README.md
+++ b/indra/newview/gltf/README.md
@@ -1,13 +1,13 @@
# Linden Lab GLTF Implementation
-
-Currently in prototype stage. Much functionality is missing (blend shapes,
+
+Currently in prototype stage. Much functionality is missing (blend shapes,
multiple texture coordinates, etc).
GLTF Specification can be found here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html.
If this implementation disagrees with the GLTF Specification, the specification is correct.
Class structure and naming should match the GLTF Specification as closely as possible while
-conforming to the LL coding standards. All code in headers should be contained in the
+conforming to the LL coding standards. All code in headers should be contained in the
LL::GLTF namespace.
The implementation serves both the client and the server.
@@ -18,7 +18,7 @@ The implementation serves both the client and the server.
- The implementation MUST use the same indexing scheme as the GLTF specification. Do not store pointers where the
- GLTF specification stores indices, store indices.
- Limit dependencies on llcommon as much as possible. Prefer std::, boost::, and (soon) glm:: over LL facsimiles.
-- Usage of LLSD is forbidden in the LL::GLTF namespace.
+- Usage of LLSD is forbidden in the LL::GLTF namespace.
- Use "using namespace" liberally in .cpp files, but never in .h files.
- "using Foo = Bar" is permissible in .h files within the LL::GLTF namespace.
@@ -69,14 +69,14 @@ Parameters to "write" and "copy" MUST be ordered "src" before "dst"
so the code reads as "write src to dst" and "copy src to dst".
When reading string constants from GLTF json (i.e. "OPAQUE", "TRIANGLES"), these
-strings should be converted to enums inside operator=. It is permissible to
+strings should be converted to enums inside operator=. It is permissible to
store the original strings during prototyping to aid in development, but eventually
we'll purge these strings from the implementation. However, implementations MUST
preserve any and all "name" members.
"write" and "copy" implementations MUST be stored in buffer_util.h.
As implementers encounter new data types, you'll see compiler errors
-pointing at templates in buffer_util.h. See vec3 as a known good
+pointing at templates in buffer_util.h. See vec3 as a known good
example of how to add support for a new type (there are bad examples, so beware):
```
@@ -120,29 +120,29 @@ inline bool write(const vec3& src, Value& dst)
Speed is important, but so is safety. In writers, try to avoid redundant copies
(prefer resize over push_back, convert dst to an empty array and fill it, don't
-make an array on the stack and copy it into dst).
+make an array on the stack and copy it into dst).
-boost::json WILL throw exceptions if you call as_foo() on a mismatched type but
-WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo
+boost::json WILL throw exceptions if you call as_foo() on a mismatched type but
+WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo
before calling as_foo or get_foo. DO NOT add exception handlers. If boost throws
an exception in serialization, the fix is to add type checks. If we see a large
number of crash reports from boost::json exceptions, each of those reports
-indicates a place where we're missing "is_foo" checks. They are gold. Do not
+indicates a place where we're missing "is_foo" checks. They are gold. Do not
bury them with an exception handler.
-DO NOT rely on existing type conversion tools in the LL codebase -- LL data models
-conflict with the GLTF specification so we MUST provide conversions independent of
+DO NOT rely on existing type conversion tools in the LL codebase -- LL data models
+conflict with the GLTF specification so we MUST provide conversions independent of
our existing implementations.
### JSON Serialization ###
-NEVER include buffer_util.h from a header.
+NEVER include buffer_util.h from a header.
Loading from and saving to disk (import/export) is currently done using tinygltf, but this is not a long term
solution. Eventually the implementation should rely solely on boost::json for reading and writing .gltf
-files and should handle .bin files natively.
+files and should handle .bin files natively.
When serializing Images and Buffers to the server, clients MUST store a single UUID "uri" field and nothing else.
The server MUST reject any data that violates this requirement.
@@ -152,5 +152,5 @@ Servers MAY reject Assets that contain Buffers with unreferenced data.
... to be continued.
-
+
diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp
index 369ff4f240..0619c617e2 100644
--- a/indra/newview/gltf/accessor.cpp
+++ b/indra/newview/gltf/accessor.cpp
@@ -28,6 +28,7 @@
#include "asset.h"
#include "buffer_util.h"
+#include "llfilesystem.h"
using namespace LL::GLTF;
using namespace boost::json;
@@ -107,6 +108,8 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
mData.erase(mData.begin() + offset, mData.begin() + offset + length);
+ mByteLength = mData.size();
+
for (BufferView& view : asset.mBufferViews)
{
if (view.mBuffer == idx)
@@ -119,6 +122,95 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
}
}
+bool Buffer::prep(Asset& asset)
+{
+ // PRECONDITION: mByteLength must not be 0
+ llassert(mByteLength != 0);
+
+ LLUUID id;
+ if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
+ { // loaded from an asset, fetch the buffer data from the asset store
+ LLFileSystem file(id, LLAssetType::AT_GLTF_BIN, LLFileSystem::READ);
+
+ mData.resize(file.getSize());
+ if (!file.read((U8*)mData.data(), mData.size()))
+ {
+ LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL;
+ return false;
+ }
+ }
+ else if (mUri.find("data:") == 0)
+ { // loaded from a data URI, load the texture from the data
+ LL_WARNS() << "Data URIs not yet supported" << LL_ENDL;
+ return false;
+ }
+ else if (!asset.mFilename.empty() &&
+ !mUri.empty()) // <-- uri could be empty if we're loading from .glb
+ {
+ std::string dir = gDirUtilp->getDirName(asset.mFilename);
+ std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
+
+ std::ifstream file(bin_file, std::ios::binary);
+ if (!file.is_open())
+ {
+ LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
+ return false;
+ }
+
+ file.seekg(0, std::ios::end);
+ if (mByteLength > file.tellg())
+ {
+ LL_WARNS("GLTF") << "Unexpected file size: " << bin_file << " is " << file.tellg() << " bytes, expected " << mByteLength << LL_ENDL;
+ return false;
+ }
+ file.seekg(0, std::ios::beg);
+
+ mData.resize(mByteLength);
+ file.read((char*)mData.data(), mData.size());
+ }
+
+ // POSTCONDITION: on success, mData.size == mByteLength
+ llassert(mData.size() == mByteLength);
+ return true;
+}
+
+bool Buffer::save(Asset& asset, const std::string& folder)
+{
+ if (mUri.substr(0, 5) == "data:")
+ {
+ LL_WARNS("GLTF") << "Data URIs not yet supported" << LL_ENDL;
+ return false;
+ }
+
+ std::string bin_file = folder + gDirUtilp->getDirDelimiter();
+
+ if (mUri.empty())
+ {
+ if (mName.empty())
+ {
+ S32 idx = this - &asset.mBuffers[0];
+ mUri = llformat("buffer_%d.bin", idx);
+ }
+ else
+ {
+ mUri = mName + ".bin";
+ }
+ }
+
+ bin_file += mUri;
+
+ std::ofstream file(bin_file, std::ios::binary);
+ if (!file.is_open())
+ {
+ LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
+ return false;
+ }
+
+ file.write((char*)mData.data(), mData.size());
+
+ return true;
+}
+
void Buffer::serialize(object& dst) const
{
write(mName, "name", dst);
@@ -132,23 +224,15 @@ const Buffer& Buffer::operator=(const Value& src)
{
copy(src, "name", mName);
copy(src, "uri", mUri);
-
- // NOTE: DO NOT attempt to handle the uri here.
+ copy(src, "byteLength", mByteLength);
+
+ // NOTE: DO NOT attempt to handle the uri here.
// The uri is a reference to a file that is not loaded until
// after the json document is parsed
}
return *this;
}
-const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
-{
- mData = src.data;
- mName = src.name;
- mUri = src.uri;
- return *this;
-}
-
-
void BufferView::serialize(object& dst) const
{
write_always(mBuffer, "buffer", dst);
@@ -173,43 +257,6 @@ const BufferView& BufferView::operator=(const Value& src)
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;
-}
-
-Accessor::Type tinygltf_type_to_enum(S32 type)
-{
- switch (type)
- {
- case TINYGLTF_TYPE_SCALAR:
- return Accessor::Type::SCALAR;
- case TINYGLTF_TYPE_VEC2:
- return Accessor::Type::VEC2;
- case TINYGLTF_TYPE_VEC3:
- return Accessor::Type::VEC3;
- case TINYGLTF_TYPE_VEC4:
- return Accessor::Type::VEC4;
- case TINYGLTF_TYPE_MAT2:
- return Accessor::Type::MAT2;
- case TINYGLTF_TYPE_MAT3:
- return Accessor::Type::MAT3;
- case TINYGLTF_TYPE_MAT4:
- return Accessor::Type::MAT4;
- }
-
- LL_WARNS("GLTF") << "Unknown tinygltf accessor type: " << type << LL_ENDL;
- llassert(false);
-
- return Accessor::Type::SCALAR;
-}
-
void Accessor::serialize(object& dst) const
{
write(mName, "name", dst);
@@ -240,18 +287,3 @@ const Accessor& Accessor::operator=(const Value& src)
return *this;
}
-const Accessor& Accessor::operator=(const tinygltf::Accessor& src)
-{
- mBufferView = src.bufferView;
- mByteOffset = src.byteOffset;
- mComponentType = src.componentType;
- mCount = src.count;
- mType = tinygltf_type_to_enum(src.type);
- mNormalized = src.normalized;
- mName = src.name;
- mMax = src.maxValues;
- mMin = src.minValues;
-
- return *this;
-}
-
diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h
index 3bbc5216bd..ec68c5f624 100644
--- a/indra/newview/gltf/accessor.h
+++ b/indra/newview/gltf/accessor.h
@@ -26,7 +26,6 @@
* $/LicenseInfo$
*/
-#include "../lltinygltfhelper.h"
#include "llstrider.h"
#include "boost/json.hpp"
@@ -51,9 +50,12 @@ namespace LL
// also updates all buffer views in given asset that reference this buffer
void erase(Asset& asset, S32 offset, S32 length);
+ bool prep(Asset& asset);
+
void serialize(boost::json::object& obj) const;
const Buffer& operator=(const Value& value);
- const Buffer& operator=(const tinygltf::Buffer& src);
+
+ bool save(Asset& asset, const std::string& folder);
};
class BufferView
@@ -69,37 +71,44 @@ namespace LL
void serialize(boost::json::object& obj) const;
const BufferView& operator=(const Value& value);
- const BufferView& operator=(const tinygltf::BufferView& src);
};
-
+
class Accessor
{
public:
- S32 mBufferView = INVALID_INDEX;
- S32 mByteOffset = 0;
- S32 mComponentType = 0;
- S32 mCount = 0;
- std::vector<double> mMax;
- std::vector<double> mMin;
+ enum class Type : U8
+ {
+ SCALAR,
+ VEC2,
+ VEC3,
+ VEC4,
+ MAT2,
+ MAT3,
+ MAT4
+ };
- enum class Type : S32
+ enum class ComponentType : U32
{
- 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
+ BYTE = 5120,
+ UNSIGNED_BYTE = 5121,
+ SHORT = 5122,
+ UNSIGNED_SHORT = 5123,
+ UNSIGNED_INT = 5125,
+ FLOAT = 5126
};
+ std::vector<double> mMax;
+ std::vector<double> mMin;
+ std::string mName;
+ S32 mBufferView = INVALID_INDEX;
+ S32 mByteOffset = 0;
+ ComponentType mComponentType = ComponentType::BYTE;
+ S32 mCount = 0;
Type mType = Type::SCALAR;
bool mNormalized = false;
- std::string mName;
void serialize(boost::json::object& obj) const;
const Accessor& operator=(const Value& value);
- const Accessor& operator=(const tinygltf::Accessor& src);
};
// convert from "SCALAR", "VEC2", etc to Accessor::Type
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp
index 8a542fb315..f18bba788c 100644
--- a/indra/newview/gltf/animation.cpp
+++ b/indra/newview/gltf/animation.cpp
@@ -32,7 +32,7 @@
using namespace LL::GLTF;
using namespace boost::json;
-void Animation::allocateGLResources(Asset& asset)
+bool Animation::prep(Asset& asset)
{
if (!mSamplers.empty())
{
@@ -40,7 +40,10 @@ void Animation::allocateGLResources(Asset& asset)
mMaxTime = -FLT_MAX;
for (auto& sampler : mSamplers)
{
- sampler.allocateGLResources(asset);
+ if (!sampler.prep(asset))
+ {
+ return false;
+ }
mMinTime = llmin(sampler.mMinTime, mMinTime);
mMaxTime = llmax(sampler.mMaxTime, mMaxTime);
}
@@ -52,13 +55,21 @@ void Animation::allocateGLResources(Asset& asset)
for (auto& channel : mRotationChannels)
{
- channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
+ if (!channel.prep(asset, mSamplers[channel.mSampler]))
+ {
+ return false;
+ }
}
for (auto& channel : mTranslationChannels)
{
- channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
+ if (!channel.prep(asset, mSamplers[channel.mSampler]))
+ {
+ return false;
+ }
}
+
+ return true;
}
void Animation::update(Asset& asset, F32 dt)
@@ -85,7 +96,7 @@ void Animation::apply(Asset& asset, float time)
}
};
-void Animation::Sampler::allocateGLResources(Asset& asset)
+bool Animation::Sampler::prep(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mInput];
mMinTime = accessor.mMin[0];
@@ -95,6 +106,8 @@ void Animation::Sampler::allocateGLResources(Asset& asset)
LLStrider<F32> frame_times = mFrameTimes.data();
copy(asset, accessor, frame_times);
+
+ return true;
}
@@ -120,16 +133,6 @@ const Animation::Sampler& Animation::Sampler::operator=(const Value& src)
return *this;
}
-
-const Animation::Sampler& Animation::Sampler::operator=(const tinygltf::AnimationSampler& src)
-{
- mInput = src.input;
- mOutput = src.output;
- mInterpolation = src.interpolation;
-
- return *this;
-}
-
bool Animation::Channel::Target::operator==(const Channel::Target& rhs) const
{
return mNode == rhs.mNode && mPath == rhs.mPath;
@@ -172,17 +175,6 @@ const Animation::Channel& Animation::Channel::operator=(const Value& src)
return *this;
}
-const Animation::Channel& Animation::Channel::operator=(const tinygltf::AnimationChannel& src)
-{
- mSampler = src.sampler;
-
- mTarget.mNode = src.target_node;
- mTarget.mPath = src.target_path;
-
- return *this;
-}
-
-
void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
{
LL_PROFILE_ZONE_SCOPED;
@@ -223,11 +215,13 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F
}
}
-void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
+bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mRotations);
+
+ return true;
}
void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@@ -254,11 +248,13 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
}
}
-void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
+bool Animation::TranslationChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mTranslations);
+
+ return true;
}
void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@@ -286,11 +282,13 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti
}
}
-void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
+bool Animation::ScaleChannel::prep(Asset& asset, Animation::Sampler& sampler)
{
Accessor& accessor = asset.mAccessors[sampler.mOutput];
copy(asset, accessor, mScales);
+
+ return true;
}
void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
@@ -364,47 +362,15 @@ const Animation& Animation::operator=(const Value& src)
return *this;
}
-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)
+bool Skin::prep(Asset& asset)
{
if (mInverseBindMatrices != INVALID_INDEX)
{
Accessor& accessor = asset.mAccessors[mInverseBindMatrices];
copy(asset, accessor, mInverseBindMatricesData);
}
+
+ return true;
}
const Skin& Skin::operator=(const Value& src)
@@ -419,16 +385,6 @@ const Skin& Skin::operator=(const Value& src)
return *this;
}
-const Skin& Skin::operator=(const tinygltf::Skin& src)
-{
- mName = src.name;
- mSkeleton = src.skeleton;
- mInverseBindMatrices = src.inverseBindMatrices;
- mJoints = src.joints;
-
- return *this;
-}
-
void Skin::serialize(object& obj) const
{
write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX);
diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h
index 53c11d4669..d5426fd4ce 100644
--- a/indra/newview/gltf/animation.h
+++ b/indra/newview/gltf/animation.h
@@ -49,12 +49,10 @@ namespace LL
S32 mOutput = INVALID_INDEX;
std::string mInterpolation;
- void allocateGLResources(Asset& asset);
+ bool prep(Asset& asset);
void serialize(boost::json::object& dst) const;
const Sampler& operator=(const Value& value);
- const Sampler& operator=(const tinygltf::AnimationSampler& src);
-
// get the frame index and time for the specified time
// asset -- the asset to reference for Accessors
@@ -85,7 +83,6 @@ namespace LL
void serialize(boost::json::object& dst) const;
const Channel& operator=(const Value& value);
- const Channel& operator=(const tinygltf::AnimationChannel& src);
};
class RotationChannel : public Channel
@@ -96,16 +93,10 @@ namespace LL
std::vector<quat> 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);
+ bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@@ -118,16 +109,10 @@ namespace LL
std::vector<vec3> 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);
+ bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@@ -140,16 +125,10 @@ namespace LL
std::vector<vec3> 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);
+ bool prep(Asset& asset, Sampler& sampler);
void apply(Asset& asset, Sampler& sampler, F32 time);
};
@@ -160,7 +139,7 @@ namespace LL
// 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;
@@ -170,9 +149,8 @@ namespace LL
void serialize(boost::json::object& dst) const;
const Animation& operator=(const Value& value);
- const Animation& operator=(const tinygltf::Animation& src);
-
- void allocateGLResources(Asset& asset);
+
+ bool prep(Asset& asset);
void update(Asset& asset, float dt);
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index c64d48662c..485984fac1 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -33,6 +33,8 @@
#include "../llviewertexturelist.h"
#include "../pipeline.h"
#include "buffer_util.h"
+#include <boost/url.hpp>
+#include "llimagejpeg.h"
using namespace LL::GLTF;
using namespace boost::json;
@@ -75,268 +77,14 @@ namespace LL
return "OPAQUE";
}
}
-
- template <typename T, typename U>
- void copy(const std::vector<T>& src, std::vector<U>& dst)
- {
- dst.resize(src.size());
- for (U32 i = 0; i < src.size(); ++i)
- {
- copy(src[i], dst[i]);
- }
- }
-
- void copy(const Node& src, tinygltf::Node& dst)
- {
- if (src.mMatrixValid)
- {
- if (src.mMatrix != glm::identity<mat4>())
- {
- dst.matrix.resize(16);
- const F32* m = glm::value_ptr(src.mMatrix);
- for (U32 i = 0; i < 16; ++i)
- {
- dst.matrix[i] = m[i];
- }
- }
- }
- else if (src.mTRSValid)
- {
- if (src.mRotation != glm::identity<quat>())
- {
- dst.rotation.resize(4);
- dst.rotation[0] = src.mRotation.x;
- dst.rotation[1] = src.mRotation.y;
- dst.rotation[2] = src.mRotation.z;
- dst.rotation[3] = src.mRotation.w;
- }
-
- if (src.mTranslation != vec3(0.f, 0.f, 0.f))
- {
- dst.translation.resize(3);
- dst.translation[0] = src.mTranslation.x;
- dst.translation[1] = src.mTranslation.y;
- dst.translation[2] = src.mTranslation.z;
- }
-
- if (src.mScale != vec3(1.f, 1.f, 1.f))
- {
- dst.scale.resize(3);
- dst.scale[0] = src.mScale.x;
- dst.scale[1] = src.mScale.y;
- dst.scale[2] = src.mScale.z;
- }
- }
-
- dst.children = src.mChildren;
- dst.mesh = src.mMesh;
- dst.skin = src.mSkin;
- dst.name = src.mName;
- }
-
- void copy(const Scene& src, tinygltf::Scene& dst)
- {
- dst.nodes = src.mNodes;
- dst.name = src.mName;
- }
-
- void copy(const Primitive& src, tinygltf::Primitive& dst)
- {
- for (auto& attrib : src.mAttributes)
- {
- dst.attributes[attrib.first] = attrib.second;
- }
- dst.indices = src.mIndices;
- dst.material = src.mMaterial;
- dst.mode = src.mMode;
- }
-
- void copy(const Mesh& src, tinygltf::Mesh& mesh)
- {
- copy(src.mPrimitives, mesh.primitives);
- mesh.weights = src.mWeights;
- mesh.name = src.mName;
- }
-
- void copy(const Material::TextureInfo& src, tinygltf::TextureInfo& dst)
- {
- dst.index = src.mIndex;
- dst.texCoord = src.mTexCoord;
- }
-
- void copy(const Material::OcclusionTextureInfo& src, tinygltf::OcclusionTextureInfo& dst)
- {
- dst.index = src.mIndex;
- dst.texCoord = src.mTexCoord;
- dst.strength = src.mStrength;
- }
-
- void copy(const Material::NormalTextureInfo& src, tinygltf::NormalTextureInfo& dst)
- {
- dst.index = src.mIndex;
- dst.texCoord = src.mTexCoord;
- dst.scale = src.mScale;
- }
-
- void copy(const Material::PbrMetallicRoughness& src, tinygltf::PbrMetallicRoughness& dst)
- {
- dst.baseColorFactor = { src.mBaseColorFactor.r, src.mBaseColorFactor.g, src.mBaseColorFactor.b, src.mBaseColorFactor.a };
- copy(src.mBaseColorTexture, dst.baseColorTexture);
- dst.metallicFactor = src.mMetallicFactor;
- dst.roughnessFactor = src.mRoughnessFactor;
- copy(src.mMetallicRoughnessTexture, dst.metallicRoughnessTexture);
- }
-
- void copy(const Material& src, tinygltf::Material& material)
- {
- material.name = src.mName;
-
- material.emissiveFactor = { src.mEmissiveFactor.r, src.mEmissiveFactor.g, src.mEmissiveFactor.b };
- copy(src.mPbrMetallicRoughness, material.pbrMetallicRoughness);
- copy(src.mNormalTexture, material.normalTexture);
- copy(src.mEmissiveTexture, material.emissiveTexture);
- }
-
- void copy(const Texture& src, tinygltf::Texture& texture)
- {
- texture.sampler = src.mSampler;
- texture.source = src.mSource;
- texture.name = src.mName;
- }
-
- void copy(const Sampler& src, tinygltf::Sampler& sampler)
- {
- sampler.magFilter = src.mMagFilter;
- sampler.minFilter = src.mMinFilter;
- sampler.wrapS = src.mWrapS;
- sampler.wrapT = src.mWrapT;
- sampler.name = src.mName;
- }
-
- void copy(const Skin& src, tinygltf::Skin& skin)
- {
- skin.joints = src.mJoints;
- skin.inverseBindMatrices = src.mInverseBindMatrices;
- skin.skeleton = src.mSkeleton;
- skin.name = src.mName;
- }
-
- void copy(const Accessor& src, tinygltf::Accessor& accessor)
- {
- accessor.bufferView = src.mBufferView;
- accessor.byteOffset = src.mByteOffset;
- accessor.componentType = src.mComponentType;
- accessor.minValues = src.mMin;
- accessor.maxValues = src.mMax;
-
- accessor.count = src.mCount;
- accessor.type = (S32) src.mType;
- accessor.normalized = src.mNormalized;
- accessor.name = src.mName;
- }
-
- void copy(const Animation::Sampler& src, tinygltf::AnimationSampler& sampler)
- {
- sampler.input = src.mInput;
- sampler.output = src.mOutput;
- sampler.interpolation = src.mInterpolation;
- }
-
- void copy(const Animation::Channel& src, tinygltf::AnimationChannel& channel)
- {
- channel.sampler = src.mSampler;
- channel.target_node = src.mTarget.mNode;
- channel.target_path = src.mTarget.mPath;
- }
-
- void copy(const Animation& src, tinygltf::Animation& animation)
- {
- animation.name = src.mName;
-
- copy(src.mSamplers, animation.samplers);
-
- U32 channel_count = src.mRotationChannels.size() + src.mTranslationChannels.size() + src.mScaleChannels.size();
-
- animation.channels.resize(channel_count);
-
- U32 idx = 0;
- for (U32 i = 0; i < src.mTranslationChannels.size(); ++i)
- {
- copy(src.mTranslationChannels[i], animation.channels[idx++]);
- }
-
- for (U32 i = 0; i < src.mRotationChannels.size(); ++i)
- {
- copy(src.mRotationChannels[i], animation.channels[idx++]);
- }
-
- for (U32 i = 0; i < src.mScaleChannels.size(); ++i)
- {
- copy(src.mScaleChannels[i], animation.channels[idx++]);
- }
- }
-
- void copy(const Buffer& src, tinygltf::Buffer& buffer)
- {
- buffer.uri = src.mUri;
- buffer.data = src.mData;
- buffer.name = src.mName;
- }
-
- void copy(const BufferView& src, tinygltf::BufferView& bufferView)
- {
- bufferView.buffer = src.mBuffer;
- bufferView.byteOffset = src.mByteOffset;
- bufferView.byteLength = src.mByteLength;
- bufferView.byteStride = src.mByteStride;
- bufferView.target = src.mTarget;
- bufferView.name = src.mName;
- }
-
- void copy(const Image& src, tinygltf::Image& image)
- {
- image.name = src.mName;
- image.width = src.mWidth;
- image.height = src.mHeight;
- image.component = src.mComponent;
- image.bits = src.mBits;
- image.pixel_type = src.mPixelType;
-
- image.image = src.mData;
- image.bufferView = src.mBufferView;
- image.mimeType = src.mMimeType;
- image.uri = src.mUri;
- }
-
- void copy(const Asset & src, tinygltf::Model& dst)
- {
- dst.defaultScene = src.mDefaultScene;
- dst.asset.copyright = src.mCopyright;
- dst.asset.version = src.mVersion;
- dst.asset.minVersion = src.mMinVersion;
- dst.asset.generator = "Linden Lab Experimental GLTF Export";
-
- // NOTE: extras are lost in the conversion for now
-
- copy(src.mScenes, dst.scenes);
- copy(src.mNodes, dst.nodes);
- copy(src.mMeshes, dst.meshes);
- copy(src.mMaterials, dst.materials);
- copy(src.mBuffers, dst.buffers);
- copy(src.mBufferViews, dst.bufferViews);
- copy(src.mTextures, dst.textures);
- copy(src.mSamplers, dst.samplers);
- copy(src.mImages, dst.images);
- copy(src.mAccessors, dst.accessors);
- copy(src.mAnimations, dst.animations);
- copy(src.mSkins, dst.skins);
- }
}
}
+
+
void Scene::updateTransforms(Asset& asset)
{
mat4 identity = glm::identity<mat4>();
-
+
for (auto& nodeIndex : mNodes)
{
Node& node = asset.mNodes[nodeIndex];
@@ -368,9 +116,9 @@ void Node::updateTransforms(Asset& asset, const mat4& parentMatrix)
{
makeMatrixValid();
mAssetMatrix = parentMatrix * mMatrix;
-
+
mAssetMatrixInv = glm::inverse(mAssetMatrix);
-
+
S32 my_index = this - &asset.mNodes[0];
for (auto& childIndex : mChildren)
@@ -419,7 +167,6 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
{
if (node.mMesh != INVALID_INDEX)
{
-
bool newHit = false;
LLMatrix4a ami;
@@ -452,7 +199,7 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
LLMatrix4a am;
am.loadu(glm::value_ptr(node.mAssetMatrix));
// shorten line segment on hit
- am.affineTransform(p, asset_end);
+ am.affineTransform(p, asset_end);
// transform results back to asset space
if (intersection)
@@ -521,7 +268,7 @@ void Node::makeTRSValid()
vec3 skew;
vec4 perspective;
glm::decompose(mMatrix, mScale, mRotation, mTranslation, skew, perspective);
-
+
mTRSValid = true;
}
@@ -577,59 +324,6 @@ const Node& Node::operator=(const Value& src)
{
mTRSValid = true;
}
-
- return *this;
-}
-
-const Node& Node::operator=(const tinygltf::Node& src)
-{
- F32* dstMatrix = glm::value_ptr(mMatrix);
-
- if (src.matrix.size() == 16)
- {
- // Node has a transformation matrix, just copy it
- for (U32 i = 0; i < 16; ++i)
- {
- 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
- if (src.rotation.size() == 4)
- {
- mRotation = quat((F32)src.rotation[3], (F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2]);
- }
-
- if (src.translation.size() == 3)
- {
- mTranslation = vec3((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]);
- }
-
- if (src.scale.size() == 3)
- {
- mScale = vec3((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]);
- }
- else
- {
- mScale = vec3(1.f, 1.f, 1.f);
- }
-
- mTRSValid = true;
- }
- else
- {
- // node specifies no transformation, set to identity
- mMatrix = glm::identity<mat4>();
- mMatrixValid = true;
- }
-
- mChildren = src.children;
- mMesh = src.mesh;
- mSkin = src.skin;
- mName = src.name;
return *this;
}
@@ -662,22 +356,6 @@ const Image& Image::operator=(const Value& src)
return *this;
}
-const Image& Image::operator=(const tinygltf::Image& src)
-{
- mName = src.name;
- mWidth = src.width;
- mHeight = src.height;
- mComponent = src.component;
- mBits = src.bits;
- mPixelType = src.pixel_type;
- mUri = src.uri;
- mBufferView = src.bufferView;
- mMimeType = src.mimeType;
- mData = src.image;
- return *this;
-}
-
-
void Asset::render(bool opaque, bool rigged)
{
if (rigged)
@@ -726,14 +404,8 @@ void Asset::render(bool opaque, bool rigged)
continue;
}
- if (mMaterials[primitive.mMaterial].mMaterial.notNull())
- {
- material.mMaterial->bind();
- }
- else
- {
- material.bind(*this);
- }
+ material.bind(*this);
+
cull = !material.mDoubleSided;
}
else
@@ -792,45 +464,50 @@ void Asset::update()
}
}
-void Asset::allocateGLResources(const std::string& filename, const tinygltf::Model& model)
+bool Asset::prep()
{
- // do images first as materials may depend on images
- for (auto& image : mImages)
+ // do buffers first as other resources depend on them
+ for (auto& buffer : mBuffers)
{
- image.allocateGLResources();
+ if (!buffer.prep(*this))
+ {
+ return false;
+ }
}
-
- // do materials before meshes as meshes may depend on materials
- if (!filename.empty())
+ for (auto& image : mImages)
{
- for (U32 i = 0; i < mMaterials.size(); ++i)
+ if (!image.prep(*this))
{
- // HACK: local preview mode, load material from model for now
- mMaterials[i].allocateGLResources(*this);
- LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true);
+ return false;
}
}
for (auto& mesh : mMeshes)
{
- mesh.allocateGLResources(*this);
+ if (!mesh.prep(*this))
+ {
+ return false;
+ }
}
for (auto& animation : mAnimations)
{
- animation.allocateGLResources(*this);
+ if (!animation.prep(*this))
+ {
+ return false;
+ }
}
for (auto& skin : mSkins)
{
- skin.allocateGLResources(*this);
+ if (!skin.prep(*this))
+ {
+ return false;
+ }
}
-}
-Asset::Asset(const tinygltf::Model& src)
-{
- *this = src;
+ return true;
}
Asset::Asset(const Value& src)
@@ -838,90 +515,141 @@ Asset::Asset(const Value& src)
*this = src;
}
-const Asset& Asset::operator=(const tinygltf::Model& src)
+bool Asset::load(std::string_view filename)
{
- mVersion = src.asset.version;
- mMinVersion = src.asset.minVersion;
- mGenerator = src.asset.generator;
- mCopyright = src.asset.copyright;
+ mFilename = filename;
+ std::string ext = gDirUtilp->getExtension(mFilename);
- // note: extras are lost in the conversion for now
-
- mDefaultScene = src.defaultScene;
-
- mScenes.resize(src.scenes.size());
- for (U32 i = 0; i < src.scenes.size(); ++i)
+ std::ifstream file(filename.data(), std::ios::binary);
+ if (file.is_open())
{
- mScenes[i] = src.scenes[i];
- }
+ std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+ file.close();
- mNodes.resize(src.nodes.size());
- for (U32 i = 0; i < src.nodes.size(); ++i)
- {
- mNodes[i] = src.nodes[i];
+ if (ext == "gltf")
+ {
+ Value val = parse(str);
+ *this = val;
+ return prep();
+ }
+ else if (ext == "glb")
+ {
+ return loadBinary(str);
+ }
+ else
+ {
+ LL_WARNS() << "Unsupported file type: " << ext << LL_ENDL;
+ return false;
+ }
}
-
- mMeshes.resize(src.meshes.size());
- for (U32 i = 0; i < src.meshes.size(); ++i)
+ else
{
- mMeshes[i] = src.meshes[i];
+ LL_WARNS() << "Failed to open file: " << filename << LL_ENDL;
+ return false;
}
- mMaterials.resize(src.materials.size());
- for (U32 i = 0; i < src.materials.size(); ++i)
- {
- mMaterials[i] = src.materials[i];
- }
+ return false;
+}
- mBuffers.resize(src.buffers.size());
- for (U32 i = 0; i < src.buffers.size(); ++i)
- {
- mBuffers[i] = src.buffers[i];
- }
+bool Asset::loadBinary(const std::string& data)
+{
+ // load from binary gltf
+ const U8* ptr = (const U8*)data.data();
+ const U8* end = ptr + data.size();
- mBufferViews.resize(src.bufferViews.size());
- for (U32 i = 0; i < src.bufferViews.size(); ++i)
+ if (end - ptr < 12)
{
- mBufferViews[i] = src.bufferViews[i];
+ LL_WARNS("GLTF") << "GLB file too short" << LL_ENDL;
+ return false;
}
- mTextures.resize(src.textures.size());
- for (U32 i = 0; i < src.textures.size(); ++i)
+ U32 magic = *(U32*)ptr;
+ ptr += 4;
+
+ if (magic != 0x46546C67)
{
- mTextures[i] = src.textures[i];
+ LL_WARNS("GLTF") << "Invalid GLB magic" << LL_ENDL;
+ return false;
}
- mSamplers.resize(src.samplers.size());
- for (U32 i = 0; i < src.samplers.size(); ++i)
+ U32 version = *(U32*)ptr;
+ ptr += 4;
+
+ if (version != 2)
{
- mSamplers[i] = src.samplers[i];
+ LL_WARNS("GLTF") << "Unsupported GLB version" << LL_ENDL;
+ return false;
}
- mImages.resize(src.images.size());
- for (U32 i = 0; i < src.images.size(); ++i)
+ U32 length = *(U32*)ptr;
+ ptr += 4;
+
+ if (length != data.size())
{
- mImages[i] = src.images[i];
+ LL_WARNS("GLTF") << "GLB length mismatch" << LL_ENDL;
+ return false;
}
- mAccessors.resize(src.accessors.size());
- for (U32 i = 0; i < src.accessors.size(); ++i)
+ U32 chunkLength = *(U32*)ptr;
+ ptr += 4;
+
+ if (end - ptr < chunkLength + 8)
{
- mAccessors[i] = src.accessors[i];
+ LL_WARNS("GLTF") << "GLB chunk too short" << LL_ENDL;
+ return false;
}
- mAnimations.resize(src.animations.size());
- for (U32 i = 0; i < src.animations.size(); ++i)
+ U32 chunkType = *(U32*)ptr;
+ ptr += 4;
+
+ if (chunkType != 0x4E4F534A)
{
- mAnimations[i] = src.animations[i];
+ LL_WARNS("GLTF") << "Invalid GLB chunk type" << LL_ENDL;
+ return false;
}
- mSkins.resize(src.skins.size());
- for (U32 i = 0; i < src.skins.size(); ++i)
+ Value val = parse(std::string_view((const char*)ptr, chunkLength));
+ *this = val;
+
+ if (mBuffers.size() > 0 && mBuffers[0].mUri.empty())
{
- mSkins[i] = src.skins[i];
+ // load binary chunk
+ ptr += chunkLength;
+
+ if (end - ptr < 8)
+ {
+ LL_WARNS("GLTF") << "GLB chunk too short" << LL_ENDL;
+ return false;
+ }
+
+ chunkLength = *(U32*)ptr;
+ ptr += 4;
+
+ chunkType = *(U32*)ptr;
+ ptr += 4;
+
+ if (chunkType != 0x004E4942)
+ {
+ LL_WARNS("GLTF") << "Invalid GLB chunk type" << LL_ENDL;
+ return false;
+ }
+
+ auto& buffer = mBuffers[0];
+
+ if (ptr + buffer.mByteLength <= end)
+ {
+ buffer.mData.resize(buffer.mByteLength);
+ memcpy(buffer.mData.data(), ptr, buffer.mByteLength);
+ ptr += buffer.mByteLength;
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Buffer too short" << LL_ENDL;
+ return false;
+ }
}
-
- return *this;
+
+ return prep();
}
const Asset& Asset::operator=(const Value& src)
@@ -943,7 +671,7 @@ const Asset& Asset::operator=(const Value& src)
copy(asset, "extras", mExtras);
}
- copy(obj, "defaultScene", mDefaultScene);
+ copy(obj, "scene", mScene);
copy(obj, "scenes", mScenes);
copy(obj, "nodes", mNodes);
copy(obj, "meshes", mMeshes);
@@ -961,18 +689,17 @@ const Asset& Asset::operator=(const Value& src)
return *this;
}
-void Asset::save(tinygltf::Model& dst)
-{
- LL::GLTF::copy(*this, dst);
-}
-
void Asset::serialize(object& dst) const
{
- write(mVersion, "version", dst);
- write(mMinVersion, "minVersion", dst, std::string());
- write(mGenerator, "generator", dst);
- write(mDefaultScene, "defaultScene", dst, 0);
-
+ static const std::string sGenerator = "Linden Lab GLTF Prototype v0.1";
+
+ dst["asset"] = object{};
+ object& asset = dst["asset"].get_object();
+
+ write(mVersion, "version", asset);
+ write(mMinVersion, "minVersion", asset, std::string());
+ write(sGenerator, "generator", asset);
+ write(mScene, "scene", dst, INVALID_INDEX);
write(mScenes, "scenes", dst);
write(mNodes, "nodes", dst);
write(mMeshes, "meshes", dst);
@@ -987,16 +714,39 @@ void Asset::serialize(object& dst) const
write(mSkins, "skins", dst);
}
-void Asset::decompose(const std::string& filename)
+bool Asset::save(const std::string& filename)
{
// get folder path
std::string folder = gDirUtilp->getDirName(filename);
- // decompose images
+ // save images
for (auto& image : mImages)
{
- image.decompose(*this, folder);
+ if (!image.save(*this, folder))
+ {
+ return false;
+ }
+ }
+
+ // save buffers
+ // NOTE: save buffers after saving images as saving images
+ // may remove image data from buffers
+ for (auto& buffer : mBuffers)
+ {
+ if (!buffer.save(*this, folder))
+ {
+ return false;
+ }
}
+
+ // save .gltf
+ object obj;
+ serialize(obj);
+ std::string buffer = boost::json::serialize(obj, {});
+ std::ofstream file(filename, std::ios::binary);
+ file.write(buffer.c_str(), buffer.size());
+
+ return true;
}
void Asset::eraseBufferView(S32 bufferView)
@@ -1023,13 +773,63 @@ void Asset::eraseBufferView(S32 bufferView)
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
-void Image::allocateGLResources()
+bool Image::prep(Asset& asset)
{
LLUUID id;
- if (LLUUID::parseUUID(mUri, &id) && id.notNull())
- {
+ if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
+ { // loaded from an asset, fetch the texture from the asset system
mTexture = fetch_texture(id);
}
+ else if (mUri.find("data:") == 0)
+ { // embedded in a data URI, load the texture from the URI
+ LL_WARNS() << "Data URIs not yet supported" << LL_ENDL;
+ return false;
+ }
+ else if (mBufferView != INVALID_INDEX)
+ { // embedded in a buffer, load the texture from the buffer
+ BufferView& bufferView = asset.mBufferViews[mBufferView];
+ Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ U8* data = buffer.mData.data() + bufferView.mByteOffset;
+
+ mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
+
+ if (mTexture.isNull())
+ {
+ LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;
+ LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
+ LL_WARNS("GLTF") << " mimeType: " << mMimeType << LL_ENDL;
+
+ return false;
+ }
+ }
+ else if (!asset.mFilename.empty() && !mUri.empty())
+ { // loaded locally and not embedded, load the texture as a local preview
+ std::string dir = gDirUtilp->getDirName(asset.mFilename);
+ std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
+
+ LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
+ if (tracking_id.notNull())
+ {
+ LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
+ mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
+ LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
+ LL_WARNS("GLTF") << " file: " << img_file << LL_ENDL;
+
+ return false;
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load image: " << mName << LL_ENDL;
+ return false;
+ }
+
+ return true;
}
@@ -1046,7 +846,6 @@ void Image::clearData(Asset& asset)
asset.eraseBufferView(mBufferView);
}
- mData.clear();
mBufferView = INVALID_INDEX;
mWidth = -1;
mHeight = -1;
@@ -1056,9 +855,15 @@ void Image::clearData(Asset& asset)
mMimeType = "";
}
-void Image::decompose(Asset& asset, const std::string& folder)
+bool Image::save(Asset& asset, const std::string& folder)
{
+ // NOTE: this *MUST* be a lossless save
+ // Artists use this to save their work repeatedly, so
+ // adding any compression artifacts here will degrade
+ // images over time.
std::string name = mName;
+ std::string error;
+ const std::string& delim = gDirUtilp->getDirDelimiter();
if (name.empty())
{
S32 idx = this - asset.mImages.data();
@@ -1067,10 +872,11 @@ void Image::decompose(Asset& asset, const std::string& folder)
if (mBufferView != INVALID_INDEX)
{
- // save original image
+ // we have the bytes of the original image, save that out in its
+ // original format
BufferView& bufferView = asset.mBufferViews[mBufferView];
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
-
+
std::string extension;
if (mMimeType == "image/jpeg")
@@ -1083,37 +889,76 @@ void Image::decompose(Asset& asset, const std::string& folder)
}
else
{
+ error = "Unknown mime type, saved as .bin";
extension = ".bin";
}
- std::string filename = folder + "/" + name + "." + extension;
+ std::string filename = folder + delim + name + extension;
// set URI to non-j2c file for now, but later we'll want to reference the j2c hash
- mUri = name + "." + extension;
+ mUri = name + extension;
std::ofstream file(filename, std::ios::binary);
file.write((const char*)buffer.mData.data() + bufferView.mByteOffset, bufferView.mByteLength);
}
-
-#if 0
- if (!mData.empty())
+ else if (mTexture.notNull())
{
- // save j2c image
- std::string filename = folder + "/" + name + ".j2c";
-
- LLPointer<LLImageRaw> raw = new LLImageRaw(mWidth, mHeight, mComponent);
- U8* data = raw->allocateData();
- llassert_always(mData.size() == raw->getDataSize());
- memcpy(data, mData.data(), mData.size());
-
- LLViewerTextureList::createUploadFile(raw, filename, 4096);
+ auto bitmapmgr = LLLocalBitmapMgr::getInstance();
+ if (bitmapmgr->isLocal(mTexture->getID()))
+ {
+ LLUUID tracking_id = bitmapmgr->getTrackingID(mTexture->getID());
+ if (tracking_id.notNull())
+ { // copy original file to destination folder
+ std::string source = bitmapmgr->getFilename(tracking_id);
+ if (gDirUtilp->fileExists(source))
+ {
+ std::string filename = gDirUtilp->getBaseFileName(source);
+ std::string dest = folder + delim + filename;
- mData.clear();
+ LLFile::copy(source, dest);
+ mUri = filename;
+ }
+ else
+ {
+ error = "File not found: " + source;
+ }
+ }
+ else
+ {
+ error = "Local image missing.";
+ }
+ }
+ else if (!mUri.empty())
+ {
+ std::string from_dir = gDirUtilp->getDirName(asset.mFilename);
+ std::string base_filename = gDirUtilp->getBaseFileName(mUri);
+ std::string filename = from_dir + delim + base_filename;
+ if (gDirUtilp->fileExists(filename))
+ {
+ std::string dest = folder + delim + base_filename;
+ LLFile::copy(filename, dest);
+ mUri = base_filename;
+ }
+ else
+ {
+ error = "Original image file not found: " + filename;
+ }
+ }
+ else
+ {
+ error = "Image is not a local image and has no uri, cannot save.";
+ }
}
-#endif
+ if (!error.empty())
+ {
+ LL_WARNS("GLTF") << "Failed to save " << name << ": " << error << LL_ENDL;
+ return false;
+ }
clearData(asset);
+
+ return true;
}
void Material::TextureInfo::serialize(object& dst) const
@@ -1143,13 +988,6 @@ bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const
return !(*this == rhs);
}
-const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::TextureInfo& src)
-{
- mIndex = src.index;
- mTexCoord = src.texCoord;
- return *this;
-}
-
void Material::OcclusionTextureInfo::serialize(object& dst) const
{
write(mIndex, "index", dst, INVALID_INDEX);
@@ -1169,14 +1007,6 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(
return *this;
}
-const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const tinygltf::OcclusionTextureInfo& src)
-{
- mIndex = src.index;
- mTexCoord = src.texCoord;
- mStrength = src.strength;
- return *this;
-}
-
void Material::NormalTextureInfo::serialize(object& dst) const
{
write(mIndex, "index", dst, INVALID_INDEX);
@@ -1195,13 +1025,6 @@ const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const
return *this;
}
-const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const tinygltf::NormalTextureInfo& src)
-{
- mIndex = src.index;
- mTexCoord = src.texCoord;
- mScale = src.scale;
- return *this;
-}
const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const Value& src)
{
@@ -1240,21 +1063,6 @@ bool Material::PbrMetallicRoughness::operator!=(const Material::PbrMetallicRough
return !(*this == rhs);
}
-const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const tinygltf::PbrMetallicRoughness& src)
-{
- if (src.baseColorFactor.size() == 4)
- {
- mBaseColorFactor = vec4(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]);
- }
-
- mBaseColorTexture = src.baseColorTexture;
- mMetallicFactor = src.metallicFactor;
- mRoughnessFactor = src.roughnessFactor;
- mMetallicRoughnessTexture = src.metallicRoughnessTexture;
-
- return *this;
-}
-
static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback)
{
if (info.mIndex != INVALID_INDEX)
@@ -1312,10 +1120,10 @@ void Material::bind(Asset& asset)
if (!LLPipeline::sShadowRender)
{
- bindTexture(asset, LLShaderMgr::BUMP_MAP, mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep);
+ bindTexture(asset, LLShaderMgr::BUMP_MAP, mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep);
bindTexture(asset, LLShaderMgr::SPECULAR_MAP, mPbrMetallicRoughness.mMetallicRoughnessTexture, LLViewerFetchedTexture::sWhiteImagep);
bindTexture(asset, LLShaderMgr::EMISSIVE_MAP, mEmissiveTexture, LLViewerFetchedTexture::sWhiteImagep);
-
+
// NOTE: base color factor is baked into vertex stream
shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, mPbrMetallicRoughness.mRoughnessFactor);
@@ -1370,35 +1178,6 @@ const Material& Material::operator=(const Value& src)
}
-const Material& Material::operator=(const tinygltf::Material& src)
-{
- mName = src.name;
-
- if (src.emissiveFactor.size() == 3)
- {
- mEmissiveFactor = vec3(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]);
- }
-
- mPbrMetallicRoughness = src.pbrMetallicRoughness;
- mNormalTexture = src.normalTexture;
- mOcclusionTexture = src.occlusionTexture;
- mEmissiveTexture = src.emissiveTexture;
-
- mAlphaMode = gltf_alpha_mode_to_enum(src.alphaMode);
- mAlphaCutoff = src.alphaCutoff;
- mDoubleSided = src.doubleSided;
-
- return *this;
-}
-
-void Material::allocateGLResources(Asset& asset)
-{
- // HACK: allocate an LLFetchedGLTFMaterial for now
- // later we'll render directly from the GLTF Images
- // and BufferViews
- mMaterial = new LLFetchedGLTFMaterial();
-}
-
void Mesh::serialize(object& dst) const
{
write(mPrimitives, "primitives", dst);
@@ -1418,26 +1197,18 @@ const Mesh& Mesh::operator=(const Value& src)
return *this;
}
-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)
+bool Mesh::prep(Asset& asset)
{
for (auto& primitive : mPrimitives)
{
- primitive.allocateGLResources(asset);
+ if (!primitive.prep(asset))
+ {
+ return false;
+ }
}
+
+ return true;
}
void Scene::serialize(object& dst) const
@@ -1450,14 +1221,6 @@ const Scene& Scene::operator=(const Value& src)
{
copy(src, "nodes", mNodes);
copy(src, "name", mName);
-
- return *this;
-}
-
-const Scene& Scene::operator=(const tinygltf::Scene& src)
-{
- mNodes = src.nodes;
- mName = src.name;
return *this;
}
@@ -1481,16 +1244,6 @@ const Texture& Texture::operator=(const Value& src)
return *this;
}
-const Texture& Texture::operator=(const tinygltf::Texture& src)
-{
- mSampler = src.sampler;
- mSource = src.source;
- mName = src.name;
-
- return *this;
-}
-
-
void Sampler::serialize(object& dst) const
{
write(mMagFilter, "magFilter", dst, LINEAR);
@@ -1511,17 +1264,6 @@ const Sampler& Sampler::operator=(const Value& src)
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
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index 990e1e41a9..761e746aa1 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -28,12 +28,12 @@
#include "llvertexbuffer.h"
#include "llvolumeoctree.h"
-#include "../lltinygltfhelper.h"
#include "accessor.h"
#include "primitive.h"
#include "animation.h"
#include "boost/json.hpp"
#include "common.h"
+#include "../llviewertexture.h"
extern F32SecondsImplicit gFrameTimeSeconds;
@@ -69,7 +69,6 @@ namespace LL
bool operator==(const TextureInfo& rhs) const;
bool operator!=(const TextureInfo& rhs) const;
- const TextureInfo& operator=(const tinygltf::TextureInfo& src);
const TextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -79,7 +78,6 @@ namespace LL
public:
F32 mScale = 1.0f;
- const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src);
const NormalTextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -89,7 +87,6 @@ namespace LL
public:
F32 mStrength = 1.0f;
- const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src);
const OcclusionTextureInfo& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -105,22 +102,16 @@ namespace LL
bool operator==(const PbrMetallicRoughness& rhs) const;
bool operator!=(const PbrMetallicRoughness& rhs) const;
- const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src);
const PbrMetallicRoughness& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
- // use LLFetchedGLTFMaterial for now, but eventually we'll want to use
- // a more flexible GLTF material implementation instead of the fixed packing
- // version we use for sharable GLTF material assets
- LLPointer<LLFetchedGLTFMaterial> mMaterial;
PbrMetallicRoughness mPbrMetallicRoughness;
NormalTextureInfo mNormalTexture;
OcclusionTextureInfo mOcclusionTexture;
TextureInfo mEmissiveTexture;
-
std::string mName;
vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f);
AlphaMode mAlphaMode = AlphaMode::OPAQUE;
@@ -129,11 +120,8 @@ namespace LL
// bind for rendering
void bind(Asset& asset);
- const Material& operator=(const tinygltf::Material& src);
const Material& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
-
- void allocateGLResources(Asset& asset);
};
class Mesh
@@ -143,11 +131,10 @@ namespace LL
std::vector<double> mWeights;
std::string mName;
- const Mesh& operator=(const tinygltf::Mesh& src);
const Mesh& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
- void allocateGLResources(Asset& asset);
+ bool prep(Asset& asset);
};
class Node
@@ -178,7 +165,6 @@ namespace LL
std::string mName;
- const Node& operator=(const tinygltf::Node& src);
const Node& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@@ -217,10 +203,9 @@ namespace LL
std::string mName;
std::vector<mat4> mInverseBindMatricesData;
- void allocateGLResources(Asset& asset);
+ bool prep(Asset& asset);
void uploadMatrixPalette(Asset& asset, Node& node);
- const Skin& operator=(const tinygltf::Skin& src);
const Skin& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -231,7 +216,6 @@ namespace LL
std::vector<S32> mNodes;
std::string mName;
- const Scene& operator=(const tinygltf::Scene& src);
const Scene& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
@@ -246,7 +230,6 @@ namespace LL
S32 mSource = INVALID_INDEX;
std::string mName;
- const Texture& operator=(const tinygltf::Texture& src);
const Texture& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -260,7 +243,6 @@ namespace LL
S32 mWrapT = REPEAT;
std::string mName;
- const Sampler& operator=(const tinygltf::Sampler& src);
const Sampler& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
};
@@ -274,7 +256,6 @@ namespace LL
S32 mBufferView = INVALID_INDEX;
- std::vector<U8> mData;
S32 mWidth = -1;
S32 mHeight = -1;
S32 mComponent = -1;
@@ -283,19 +264,20 @@ namespace LL
LLPointer<LLViewerFetchedTexture> mTexture;
- const Image& operator=(const tinygltf::Image& src);
const Image& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
- // save image clear local data, and set uri
- void decompose(Asset& asset, const std::string& filename);
+ // save image to disk
+ // may remove image data from bufferviews and convert to
+ // file uri if necessary
+ bool save(Asset& asset, const std::string& filename);
// erase the buffer view associated with this image
- // free any associated resources
+ // free any associated GLTF resources
// preserve only uri and name
void clearData(Asset& asset);
- void allocateGLResources();
+ bool prep(Asset& asset);
};
// C++ representation of a GLTF Asset
@@ -322,16 +304,20 @@ namespace LL
std::string mMinVersion;
std::string mCopyright;
- S32 mDefaultScene = INVALID_INDEX;
+ S32 mScene = INVALID_INDEX;
Value mExtras;
U32 mPendingBuffers = 0;
+ // local file this asset was loaded from (if any)
+ std::string mFilename;
+
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
- // prepare the asset for rendering
- void allocateGLResources(const std::string& filename = "", const tinygltf::Model& model = tinygltf::Model());
+
+ // prepare for first time use
+ bool prep();
// Called periodically (typically once per frame)
// Any ongoing work (such as animations) should be handled here
@@ -361,18 +347,25 @@ namespace LL
);
Asset() = default;
- Asset(const tinygltf::Model& src);
Asset(const Value& src);
- const Asset& operator=(const tinygltf::Model& src);
+ // load from given file
+ // accepts .gltf and .glb files
+ // Any existing data will be lost
+ // returns result of prep() on success
+ bool load(std::string_view filename);
+
+ // load .glb contents from memory
+ // data - binary contents of .glb file
+ // returns result of prep() on success
+ bool loadBinary(const std::string& data);
+
const Asset& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
- // save the asset to a tinygltf model
- void save(tinygltf::Model& dst);
-
- // decompose the asset to the given .gltf file
- void decompose(const std::string& filename);
+ // save the asset to the given .gltf file
+ // saves images and bins alongside the gltf file
+ bool save(const std::string& filename);
// remove the bufferview at the given index
// updates all bufferview indices in this Asset as needed
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index b0fbc8524d..72e69c9599 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -31,7 +31,7 @@
// whenever we add support for more types
#ifdef _MSC_VER
-#define LL_FUNCSIG __FUNCSIG__
+#define LL_FUNCSIG __FUNCSIG__
#else
#define LL_FUNCSIG __PRETTY_FUNCTION__
#endif
@@ -353,37 +353,29 @@ namespace LL
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;
+ switch (accessor.mComponentType)
+ {
+ case Accessor::ComponentType::FLOAT:
+ copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::UNSIGNED_INT:
+ copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::SHORT:
+ copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::UNSIGNED_SHORT:
+ copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::BYTE:
+ copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::UNSIGNED_BYTE:
+ copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
+ break;
+ default:
+ LL_ERRS("GLTF") << "Invalid component type" << LL_ENDL;
+ break;
}
}
@@ -400,7 +392,7 @@ namespace LL
//=========================================================================================================
// boost::json copying utilities
// ========================================================================================================
-
+
//====================== unspecialized base template, single value ===========================
// to/from Value
@@ -528,7 +520,7 @@ namespace LL
}
return false;
}
-
+
template<typename T>
inline bool write(const std::unordered_map<std::string, T>& src, string_view member, boost::json::object& dst, const std::unordered_map<std::string, T>& default_value = std::unordered_map<std::string, T>())
{
@@ -571,6 +563,44 @@ namespace LL
return false;
}
+ // Accessor::ComponentType
+ template<>
+ inline bool copy(const Value& src, Accessor::ComponentType& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = (Accessor::ComponentType)src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const Accessor::ComponentType& src, Value& dst)
+ {
+ dst = (S32)src;
+ return true;
+ }
+
+ //Primitive::Mode
+ template<>
+ inline bool copy(const Value& src, Primitive::Mode& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = (Primitive::Mode)src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const Primitive::Mode& src, Value& dst)
+ {
+ dst = (S32)src;
+ return true;
+ }
+
// vec4
template<>
inline bool copy(const Value& src, vec4& dst)
@@ -881,7 +911,7 @@ namespace LL
return true;
}
- //
+ //
// ========================================================================================================
}
diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h
index 859e202738..59f2ba38db 100644
--- a/indra/newview/gltf/common.h
+++ b/indra/newview/gltf/common.h
@@ -60,6 +60,7 @@ namespace LL
constexpr S32 MIRRORED_REPEAT = 33648;
constexpr S32 REPEAT = 10497;
+
class Asset;
}
}
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index 1bde7327e6..886b8f79c9 100644
--- a/indra/newview/gltf/primitive.cpp
+++ b/indra/newview/gltf/primitive.cpp
@@ -9,7 +9,7 @@
* 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.
+ * 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
@@ -30,12 +30,10 @@
#include "buffer_util.h"
#include "../llviewershadermgr.h"
-#include "../lltinygltfhelper.h"
-
using namespace LL::GLTF;
using namespace boost::json;
-void Primitive::allocateGLResources(Asset& asset)
+bool Primitive::prep(Asset& asset)
{
// allocate vertex buffer
// We diverge from the intent of the GLTF format here to work with our existing render pipeline
@@ -85,10 +83,19 @@ void Primitive::allocateGLResources(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mIndices];
copy(asset, accessor, mIndexArray);
+
+ for (auto& idx : mIndexArray)
+ {
+ if (idx >= mPositions.size())
+ {
+ LL_WARNS("GLTF") << "Invalid index array" << LL_ENDL;
+ return false;
+ }
+ }
}
U32 mask = ATTRIBUTE_MASK;
-
+
if (!mWeights.empty())
{
mask |= LLVertexBuffer::MAP_WEIGHT4;
@@ -130,7 +137,7 @@ void Primitive::allocateGLResources(Asset& asset)
{
mColors.resize(mPositions.size(), LLColor4U::white);
}
-
+
// bake material basecolor into color array
if (mMaterial != INVALID_INDEX)
{
@@ -148,7 +155,7 @@ void Primitive::allocateGLResources(Asset& asset)
{
mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
}
-
+
mVertexBuffer->setNormalData(mNormals.data());
if (mTangents.empty())
@@ -175,10 +182,12 @@ void Primitive::allocateGLResources(Asset& asset)
mVertexBuffer->setWeight4Data(weight_data.data());
}
-
+
createOctree();
-
+
mVertexBuffer->unbind();
+
+ return true;
}
void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
@@ -224,7 +233,7 @@ void Primitive::createOctree()
F32 scaler = 0.25f;
- if (mMode == TINYGLTF_MODE_TRIANGLES)
+ if (mMode == Mode::TRIANGLES)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() / 3;
// Initialize all the triangles we need
@@ -241,14 +250,14 @@ void Primitive::createOctree()
const LLVector4a& v0 = mPositions[i0];
const LLVector4a& v1 = mPositions[i1];
const LLVector4a& v2 = mPositions[i2];
-
+
initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2);
-
+
//insert
mOctree->insert(tri);
}
}
- else if (mMode == TINYGLTF_MODE_TRIANGLE_STRIP)
+ else if (mMode == Mode::TRIANGLE_STRIP)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
// Initialize all the triangles we need
@@ -272,7 +281,7 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
- else if (mMode == TINYGLTF_MODE_TRIANGLE_FAN)
+ else if (mMode == Mode::TRIANGLE_FAN)
{
const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
// Initialize all the triangles we need
@@ -296,14 +305,14 @@ void Primitive::createOctree()
mOctree->insert(tri);
}
}
- else if (mMode == TINYGLTF_MODE_POINTS ||
- mMode == TINYGLTF_MODE_LINE ||
- mMode == TINYGLTF_MODE_LINE_LOOP ||
- mMode == TINYGLTF_MODE_LINE_STRIP)
+ else if (mMode == Mode::POINTS ||
+ mMode == Mode::LINES ||
+ mMode == Mode::LINE_LOOP ||
+ mMode == Mode::LINE_STRIP)
{
// nothing to do, no volume... maybe add some collision geometry around these primitive types?
}
-
+
else
{
LL_ERRS() << "Unsupported Primitive mode" << LL_ENDL;
@@ -357,23 +366,23 @@ Primitive::~Primitive()
mOctree = nullptr;
}
-U32 gltf_mode_to_gl_mode(U32 mode)
+LLRender::eGeomModes gltf_mode_to_gl_mode(Primitive::Mode mode)
{
switch (mode)
{
- case TINYGLTF_MODE_POINTS:
+ case Primitive::Mode::POINTS:
return LLRender::POINTS;
- case TINYGLTF_MODE_LINE:
+ case Primitive::Mode::LINES:
return LLRender::LINES;
- case TINYGLTF_MODE_LINE_LOOP:
+ case Primitive::Mode::LINE_LOOP:
return LLRender::LINE_LOOP;
- case TINYGLTF_MODE_LINE_STRIP:
+ case Primitive::Mode::LINE_STRIP:
return LLRender::LINE_STRIP;
- case TINYGLTF_MODE_TRIANGLES:
+ case Primitive::Mode::TRIANGLES:
return LLRender::TRIANGLES;
- case TINYGLTF_MODE_TRIANGLE_STRIP:
+ case Primitive::Mode::TRIANGLE_STRIP:
return LLRender::TRIANGLE_STRIP;
- case TINYGLTF_MODE_TRIANGLE_FAN:
+ case Primitive::Mode::TRIANGLE_FAN:
return LLRender::TRIANGLE_FAN;
default:
return LLRender::TRIANGLES;
@@ -383,7 +392,7 @@ U32 gltf_mode_to_gl_mode(U32 mode)
void Primitive::serialize(boost::json::object& dst) const
{
write(mMaterial, "material", dst, -1);
- write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES);
+ write(mMode, "mode", dst, Primitive::Mode::TRIANGLES);
write(mIndices, "indices", dst, INVALID_INDEX);
write(mAttributes, "attributes", dst);
}
@@ -402,24 +411,3 @@ const Primitive& Primitive::operator=(const Value& src)
return *this;
}
-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;
- }
-
- mGLMode = gltf_mode_to_gl_mode(mMode);
-
- return *this;
-}
diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h
index 18aadce808..9914ff3b08 100644
--- a/indra/newview/gltf/primitive.h
+++ b/indra/newview/gltf/primitive.h
@@ -48,6 +48,17 @@ namespace LL
class Primitive
{
public:
+ enum class Mode : U8
+ {
+ POINTS,
+ LINES,
+ LINE_LOOP,
+ LINE_STRIP,
+ TRIANGLES,
+ TRIANGLE_STRIP,
+ TRIANGLE_FAN
+ };
+
~Primitive();
// GPU copy of mesh data
@@ -66,10 +77,10 @@ namespace LL
// raycast acceleration structure
LLPointer<LLVolumeOctree> mOctree;
std::vector<LLVolumeTriangle> mOctreeTriangles;
-
+
S32 mMaterial = -1;
- S32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles
- U32 mGLMode = LLRender::TRIANGLES;
+ Mode mMode = Mode::TRIANGLES; // default to triangles
+ LLRender::eGeomModes mGLMode = LLRender::TRIANGLES; // for use with LLRender
S32 mIndices = -1;
std::unordered_map<std::string, S32> mAttributes;
@@ -77,7 +88,7 @@ namespace LL
// must be called before buffer is unmapped and after buffer is populated with good data
void createOctree();
- //get the LLVolumeTriangle that intersects with the given line segment at the point
+ //get the LLVolumeTriangle that intersects with the given line segment at the point
//closest to start. Moves end to the point of intersection. Returns nullptr if no intersection.
//Line segment must be in the same coordinate frame as this Primitive
const LLVolumeTriangle* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
@@ -86,12 +97,11 @@ namespace LL
LLVector4a* normal = NULL, // return the surface normal at the intersection point
LLVector4a* tangent = NULL // return the surface tangent at the intersection point
);
-
+
void serialize(boost::json::object& obj) const;
const Primitive& operator=(const Value& src);
- const Primitive& operator=(const tinygltf::Primitive& src);
- void allocateGLResources(Asset& asset);
+ bool prep(Asset& asset);
};
}
}