diff options
author | Dave Parks <davep@lindenlab.com> | 2024-05-20 13:22:55 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-20 13:22:55 -0500 |
commit | 03c4458bdcc6821a3047f93b729d412e274ab9af (patch) | |
tree | ecc314de3aa32161e8ac5b1a554e9e7d2a608dc3 /indra/newview/gltf | |
parent | eab232d3ed49bfb1f873e332ff57ec8c311c163b (diff) |
#1392 GLTF Upload (#1394)
* #1392 WIP -- Functional texture upload, stubbed out .bin upload.
* #1392 GLTF Upload WIP -- Emulates successful upload
Successfully uploads texture
Emulates successful .gltf and .bin upload by injecting into local asset cache.
Emulates rez from inventory by setting sculpt ID of selected object
Currently fails in tinygltf parsing due to missing .bin
* Add missing notification
* Build fix
* #1392 Add boost::json .gltf reading support.
* #1392 boost::json GLTF writing prototype
* Create gltf/README.md
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* #1392 Add ability to render directly from LL::GLTF::Material
* Fix for mac build
* Mac build fix
* #1392 AssetType and Inventory Type plumbing
* #1392 More sane error handling and scheduling of uploads.
* #1392 Actually attempt to upload glbin
* Mac build fix, upload nudge
* Mac build fix
* Fix glTF asset uploads to server
* Mac build fix (inline not static)
* More consistent inline
* Add glm, mac nudge.
* #1392 For consistency with spec, start using glm over glh:: and LLFoo
* Another attempt at placating Mac builds
* Another Mac nudge
* Mac build take 23
* #1392 Prune LLMatrix4a from GLTF namespace.
* #1392 Fix for orientation being off (glm::quat is wxyz, not xyzw)
* #1392 WIP -- Actually send the sculpt type and id, nudge readme and alpha rendering
* #1392 Working download!
* #1394 Add support for GLTFEnabled SimulatorFeature
* #1392 Review feedback
---------
Co-authored-by: Pepper Linden <3782201+rohvani@users.noreply.github.com>
Diffstat (limited to 'indra/newview/gltf')
-rw-r--r-- | indra/newview/gltf/README.md | 156 | ||||
-rw-r--r-- | indra/newview/gltf/accessor.cpp | 175 | ||||
-rw-r--r-- | indra/newview/gltf/accessor.h | 38 | ||||
-rw-r--r-- | indra/newview/gltf/animation.cpp | 175 | ||||
-rw-r--r-- | indra/newview/gltf/animation.h | 49 | ||||
-rw-r--r-- | indra/newview/gltf/asset.cpp | 725 | ||||
-rw-r--r-- | indra/newview/gltf/asset.h | 146 | ||||
-rw-r--r-- | indra/newview/gltf/buffer_util.h | 608 | ||||
-rw-r--r-- | indra/newview/gltf/common.h | 66 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.cpp | 79 | ||||
-rw-r--r-- | indra/newview/gltf/primitive.h | 8 |
11 files changed, 1914 insertions, 311 deletions
diff --git a/indra/newview/gltf/README.md b/indra/newview/gltf/README.md new file mode 100644 index 0000000000..8e7df0a439 --- /dev/null +++ b/indra/newview/gltf/README.md @@ -0,0 +1,156 @@ +# Linden Lab GLTF Implementation + +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 +LL::GLTF namespace. + +The implementation serves both the client and the server. + +## Design Principles + +- The implementation MUST be capable of round-trip serialization with no data loss beyond F64 to F32 conversions. +- 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. +- 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. + +## Loading, Copying, and Serialization +Each class should provide two functions (Primitive shown for example): + +``` +// Serialize to the provided json object. +// "obj" should be "this" in json form on return +// Do not serialize default values +void serialize(boost::json::object& obj) const; + +// Initialize from a provided json value +const Primitive& operator=(const Value& src); +``` + +"serialize" implementations should use "write": + +``` +void Primitive::serialize(boost::json::object& dst) const +{ + write(mMaterial, "material", dst, -1); + write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES); + write(mIndices, "indices", dst, INVALID_INDEX); + write(mAttributes, "attributes", dst); +} +``` + +And operator= implementations should use "copy": + +``` +const Primitive& Primitive::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "material", mMaterial); + copy(src, "mode", mMode); + copy(src, "indices", mIndices); + copy(src, "attributes", mAttributes); + + mGLMode = gltf_mode_to_gl_mode(mMode); + } + return *this; +} +``` + +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 +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 +example of how to add support for a new type (there are bad examples, so beware): + +``` +// vec3 +template<> +inline bool copy(const Value& src, vec3& dst) +{ + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 3) + { + if (arr[0].is_double() && + arr[1].is_double() && + arr[2].is_double()) + { + dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double()); + } + return true; + } + } + return false; +} + +template<> +inline bool write(const vec3& src, Value& dst) +{ + dst = boost::json::array(); + boost::json::array& arr = dst.as_array(); + arr.resize(3); + arr[0] = src.x; + arr[1] = src.y; + arr[2] = src.z; + return true; +} + +``` + +"write" MUST return true if ANY data was written +"copy" MUST return true if ANY data was copied + +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). + +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 +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 +our existing implementations. + +### JSON Serialization ### + + + +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. + +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. + +Clients MUST remove any Images from Buffers prior to upload to the server. +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 9bfdc2afa6..369ff4f240 100644 --- a/indra/newview/gltf/accessor.cpp +++ b/indra/newview/gltf/accessor.cpp @@ -27,8 +27,79 @@ #include "../llviewerprecompiledheaders.h" #include "asset.h" +#include "buffer_util.h" using namespace LL::GLTF; +using namespace boost::json; + +namespace LL +{ + namespace GLTF + { + Accessor::Type gltf_type_to_enum(const std::string& type) + { + if (type == "SCALAR") + { + return Accessor::Type::SCALAR; + } + else if (type == "VEC2") + { + return Accessor::Type::VEC2; + } + else if (type == "VEC3") + { + return Accessor::Type::VEC3; + } + else if (type == "VEC4") + { + return Accessor::Type::VEC4; + } + else if (type == "MAT2") + { + return Accessor::Type::MAT2; + } + else if (type == "MAT3") + { + return Accessor::Type::MAT3; + } + else if (type == "MAT4") + { + return Accessor::Type::MAT4; + } + + LL_WARNS("GLTF") << "Unknown accessor type: " << type << LL_ENDL; + llassert(false); + + return Accessor::Type::SCALAR; + } + + std::string enum_to_gltf_type(Accessor::Type type) + { + switch (type) + { + case Accessor::Type::SCALAR: + return "SCALAR"; + case Accessor::Type::VEC2: + return "VEC2"; + case Accessor::Type::VEC3: + return "VEC3"; + case Accessor::Type::VEC4: + return "VEC4"; + case Accessor::Type::MAT2: + return "MAT2"; + case Accessor::Type::MAT3: + return "MAT3"; + case Accessor::Type::MAT4: + return "MAT4"; + } + + LL_WARNS("GLTF") << "Unknown accessor type: " << (S32)type << LL_ENDL; + llassert(false); + + return "SCALAR"; + } + } +} void Buffer::erase(Asset& asset, S32 offset, S32 length) { @@ -48,6 +119,27 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length) } } +void Buffer::serialize(object& dst) const +{ + write(mName, "name", dst); + write(mUri, "uri", dst); + write_always(mByteLength, "byteLength", dst); +}; + +const Buffer& Buffer::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "uri", mUri); + + // 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; @@ -56,6 +148,31 @@ const Buffer& Buffer::operator=(const tinygltf::Buffer& src) return *this; } + +void BufferView::serialize(object& dst) const +{ + write_always(mBuffer, "buffer", dst); + write_always(mByteLength, "byteLength", dst); + write(mByteOffset, "byteOffset", dst, 0); + write(mByteStride, "byteStride", dst, 0); + write(mTarget, "target", dst, -1); + write(mName, "name", dst); +} + +const BufferView& BufferView::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "buffer", mBuffer); + copy(src, "byteLength", mByteLength); + copy(src, "byteOffset", mByteOffset); + copy(src, "byteStride", mByteStride); + copy(src, "target", mTarget); + copy(src, "name", mName); + } + return *this; +} + const BufferView& BufferView::operator=(const tinygltf::BufferView& src) { mBuffer = src.buffer; @@ -67,13 +184,69 @@ const BufferView& BufferView::operator=(const tinygltf::BufferView& src) 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); + write(mBufferView, "bufferView", dst, INVALID_INDEX); + write(mByteOffset, "byteOffset", dst, 0); + write_always(mComponentType, "componentType", dst); + write_always(mCount, "count", dst); + write_always(enum_to_gltf_type(mType), "type", dst); + write(mNormalized, "normalized", dst, false); + write(mMax, "max", dst); + write(mMin, "min", dst); +} + +const Accessor& Accessor::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "bufferView", mBufferView); + copy(src, "byteOffset", mByteOffset); + copy(src, "componentType", mComponentType); + copy(src, "count", mCount); + copy(src, "type", mType); + copy(src, "normalized", mNormalized); + copy(src, "max", mMax); + copy(src, "min", mMin); + } + 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; + mType = tinygltf_type_to_enum(src.type); mNormalized = src.normalized; mName = src.name; mMax = src.maxValues; diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h index 6849cd8609..3bbc5216bd 100644 --- a/indra/newview/gltf/accessor.h +++ b/indra/newview/gltf/accessor.h @@ -28,14 +28,15 @@ #include "../lltinygltfhelper.h" #include "llstrider.h" +#include "boost/json.hpp" + +#include "common.h" // LL GLTF Implementation namespace LL { namespace GLTF { - class Asset; - constexpr S32 INVALID_INDEX = -1; class Buffer @@ -44,11 +45,14 @@ namespace LL std::vector<U8> mData; std::string mName; std::string mUri; + S32 mByteLength = 0; // erase the given range from this buffer. // also updates all buffer views in given asset that reference this buffer void erase(Asset& asset, S32 offset, S32 length); + void serialize(boost::json::object& obj) const; + const Buffer& operator=(const Value& value); const Buffer& operator=(const tinygltf::Buffer& src); }; @@ -56,25 +60,25 @@ namespace LL { public: S32 mBuffer = INVALID_INDEX; - S32 mByteLength; - S32 mByteOffset; - S32 mByteStride; - S32 mTarget; - S32 mComponentType; + S32 mByteLength = 0; + S32 mByteOffset = 0; + S32 mByteStride = 0; + S32 mTarget = -1; std::string mName; + 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; - S32 mComponentType; - S32 mCount; + S32 mByteOffset = 0; + S32 mComponentType = 0; + S32 mCount = 0; std::vector<double> mMax; std::vector<double> mMin; @@ -89,11 +93,19 @@ namespace LL MAT4 = TINYGLTF_TYPE_MAT4 }; - S32 mType; - bool mNormalized; + 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 + Accessor::Type gltf_type_to_enum(const std::string& type); + + // convert from Accessor::Type to "SCALAR", "VEC2", etc + std::string enum_to_gltf_type(Accessor::Type type); } } diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp index da6d02b356..8a542fb315 100644 --- a/indra/newview/gltf/animation.cpp +++ b/indra/newview/gltf/animation.cpp @@ -30,6 +30,7 @@ #include "buffer_util.h" using namespace LL::GLTF; +using namespace boost::json; void Animation::allocateGLResources(Asset& asset) { @@ -84,7 +85,6 @@ void Animation::apply(Asset& asset, float time) } }; - void Animation::Sampler::allocateGLResources(Asset& asset) { Accessor& accessor = asset.mAccessors[mInput]; @@ -97,8 +97,96 @@ void Animation::Sampler::allocateGLResources(Asset& asset) copy(asset, accessor, frame_times); } + +void Animation::Sampler::serialize(object& obj) const +{ + write(mInput, "input", obj, INVALID_INDEX); + write(mOutput, "output", obj, INVALID_INDEX); + write(mInterpolation, "interpolation", obj, std::string("LINEAR")); + write(mMinTime, "min_time", obj); + write(mMaxTime, "max_time", obj); +} + +const Animation::Sampler& Animation::Sampler::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "input", mInput); + copy(src, "output", mOutput); + copy(src, "interpolation", mInterpolation); + copy(src, "min_time", mMinTime); + copy(src, "max_time", mMaxTime); + } + 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; +} + +bool Animation::Channel::Target::operator!=(const Channel::Target& rhs) const +{ + return !(*this == rhs); +} + +void Animation::Channel::Target::serialize(object& obj) const +{ + write(mNode, "node", obj, INVALID_INDEX); + write(mPath, "path", obj); +} + +const Animation::Channel::Target& Animation::Channel::Target::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "node", mNode); + copy(src, "path", mPath); + } + return *this; +} + +void Animation::Channel::serialize(object& obj) const +{ + write(mSampler, "sampler", obj, INVALID_INDEX); + write(mTarget, "target", obj); +} + +const Animation::Channel& Animation::Channel::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "sampler", mSampler); + copy(src, "target", mTarget); + } + 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; + if (time < mMinTime) { frameIndex = 0; @@ -158,13 +246,11 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) else { // interpolate - LLQuaternion q0(mRotations[frameIndex].get_value()); - LLQuaternion q1(mRotations[frameIndex + 1].get_value()); + quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t); - LLQuaternion qf = slerp(t, q0, q1); + qf = glm::normalize(qf); - qf.normalize(); - node.setRotation(glh::quaternionf(qf.mQ)); + node.setRotation(qf); } } @@ -191,10 +277,10 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti else { // interpolate - const glh::vec3f& v0 = mTranslations[frameIndex]; - const glh::vec3f& v1 = mTranslations[frameIndex + 1]; + const vec3& v0 = mTranslations[frameIndex]; + const vec3& v1 = mTranslations[frameIndex + 1]; - glh::vec3f vf = v0 + t * (v1 - v0); + vec3 vf = v0 + t * (v1 - v0); node.setTranslation(vf); } @@ -223,15 +309,61 @@ void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) else { // interpolate - const glh::vec3f& v0 = mScales[frameIndex]; - const glh::vec3f& v1 = mScales[frameIndex + 1]; + const vec3& v0 = mScales[frameIndex]; + const vec3& v1 = mScales[frameIndex + 1]; - glh::vec3f vf = v0 + t * (v1 - v0); + vec3 vf = v0 + t * (v1 - v0); node.setScale(vf); } } +void Animation::serialize(object& obj) const +{ + write(mName, "name", obj); + write(mSamplers, "samplers", obj); + + std::vector<Channel> channels; + channels.insert(channels.end(), mRotationChannels.begin(), mRotationChannels.end()); + channels.insert(channels.end(), mTranslationChannels.begin(), mTranslationChannels.end()); + channels.insert(channels.end(), mScaleChannels.begin(), mScaleChannels.end()); + + write(channels, "channels", obj); +} + +const Animation& Animation::operator=(const Value& src) +{ + if (src.is_object()) + { + const object& obj = src.as_object(); + + copy(obj, "name", mName); + copy(obj, "samplers", mSamplers); + + // make a temporory copy of generic channels + std::vector<Channel> channels; + copy(obj, "channels", channels); + + // break up into channel specific implementations + for (auto& channel: channels) + { + if (channel.mTarget.mPath == "rotation") + { + mRotationChannels.push_back(channel); + } + else if (channel.mTarget.mPath == "translation") + { + mTranslationChannels.push_back(channel); + } + else if (channel.mTarget.mPath == "scale") + { + mScaleChannels.push_back(channel); + } + } + } + return *this; +} + const Animation& Animation::operator=(const tinygltf::Animation& src) { mName = src.name; @@ -275,6 +407,18 @@ void Skin::allocateGLResources(Asset& asset) } } +const Skin& Skin::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "skeleton", mSkeleton); + copy(src, "inverseBindMatrices", mInverseBindMatrices); + copy(src, "joints", mJoints); + } + return *this; +} + const Skin& Skin::operator=(const tinygltf::Skin& src) { mName = src.name; @@ -285,3 +429,10 @@ const Skin& Skin::operator=(const tinygltf::Skin& src) return *this; } +void Skin::serialize(object& obj) const +{ + write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX); + write(mJoints, "joints", obj); + write(mName, "name", obj); + write(mSkeleton, "skeleton", obj, INVALID_INDEX); +} diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h index cc2045ebb2..53c11d4669 100644 --- a/indra/newview/gltf/animation.h +++ b/indra/newview/gltf/animation.h @@ -27,7 +27,6 @@ */ #include "accessor.h" - // LL GLTF Implementation namespace LL { @@ -52,14 +51,10 @@ namespace LL void allocateGLResources(Asset& asset); - const Sampler& operator=(const tinygltf::AnimationSampler& src) - { - mInput = src.input; - mOutput = src.output; - mInterpolation = src.interpolation; - - return *this; - } + 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 @@ -77,27 +72,29 @@ namespace LL public: S32 mNode = INVALID_INDEX; std::string mPath; + + bool operator==(const Target& other) const; + bool operator!=(const Target& other) const; + + void serialize(boost::json::object& dst) const; + const Target& operator=(const Value& value); }; S32 mSampler = INVALID_INDEX; Target mTarget; - const Channel& operator=(const tinygltf::AnimationChannel& src) - { - mSampler = src.sampler; - - mTarget.mNode = src.target_node; - mTarget.mPath = src.target_path; - - return *this; - } - + void serialize(boost::json::object& dst) const; + const Channel& operator=(const Value& value); + const Channel& operator=(const tinygltf::AnimationChannel& src); }; class RotationChannel : public Channel { public: - std::vector<glh::quaternionf> mRotations; + RotationChannel() = default; + RotationChannel(const Channel& channel) : Channel(channel) {} + + std::vector<quat> mRotations; const RotationChannel& operator=(const tinygltf::AnimationChannel& src) { @@ -116,7 +113,10 @@ namespace LL class TranslationChannel : public Channel { public: - std::vector<glh::vec3f> mTranslations; + TranslationChannel() = default; + TranslationChannel(const Channel& channel) : Channel(channel) {} + + std::vector<vec3> mTranslations; const TranslationChannel& operator=(const tinygltf::AnimationChannel& src) { @@ -135,7 +135,10 @@ namespace LL class ScaleChannel : public Channel { public: - std::vector<glh::vec3f> mScales; + ScaleChannel() = default; + ScaleChannel(const Channel& channel) : Channel(channel) {} + + std::vector<vec3> mScales; const ScaleChannel& operator=(const tinygltf::AnimationChannel& src) { @@ -165,6 +168,8 @@ namespace LL std::vector<TranslationChannel> mTranslationChannels; std::vector<ScaleChannel> mScaleChannels; + void serialize(boost::json::object& dst) const; + const Animation& operator=(const Value& value); const Animation& operator=(const tinygltf::Animation& src); void allocateGLResources(Asset& asset); diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index 5f2217a075..c64d48662c 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -31,13 +31,51 @@ #include "../llviewershadermgr.h" #include "../llviewercontrol.h" #include "../llviewertexturelist.h" +#include "../pipeline.h" +#include "buffer_util.h" using namespace LL::GLTF; +using namespace boost::json; namespace LL { namespace GLTF { + Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode) + { + if (alpha_mode == "OPAQUE") + { + return Material::AlphaMode::OPAQUE; + } + else if (alpha_mode == "MASK") + { + return Material::AlphaMode::MASK; + } + else if (alpha_mode == "BLEND") + { + return Material::AlphaMode::BLEND; + } + else + { + return Material::AlphaMode::OPAQUE; + } + } + + std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode) + { + switch (alpha_mode) + { + case Material::AlphaMode::OPAQUE: + return "OPAQUE"; + case Material::AlphaMode::MASK: + return "MASK"; + case Material::AlphaMode::BLEND: + return "BLEND"; + default: + return "OPAQUE"; + } + } + template <typename T, typename U> void copy(const std::vector<T>& src, std::vector<U>& dst) { @@ -45,49 +83,48 @@ namespace LL 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.asMatrix4().isIdentity()) + 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] = src.mMatrix.getF32ptr()[i]; + dst.matrix[i] = m[i]; } } } else if (src.mTRSValid) { - if (!src.mRotation.equals(glh::quaternionf::identity(), FLT_EPSILON)) + if (src.mRotation != glm::identity<quat>()) { dst.rotation.resize(4); - for (U32 i = 0; i < 4; ++i) - { - dst.rotation[i] = src.mRotation.get_value()[i]; - } - } + 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 != glh::vec3f(0.f, 0.f, 0.f)) + if (src.mTranslation != vec3(0.f, 0.f, 0.f)) { dst.translation.resize(3); - for (U32 i = 0; i < 3; ++i) - { - dst.translation[i] = src.mTranslation.v[i]; - } + dst.translation[0] = src.mTranslation.x; + dst.translation[1] = src.mTranslation.y; + dst.translation[2] = src.mTranslation.z; } - if (src.mScale != glh::vec3f(1.f, 1.f, 1.f)) + if (src.mScale != vec3(1.f, 1.f, 1.f)) { dst.scale.resize(3); - for (U32 i = 0; i < 3; ++i) - { - dst.scale[i] = src.mScale.v[i]; - } + dst.scale[0] = src.mScale.x; + dst.scale[1] = src.mScale.y; + dst.scale[2] = src.mScale.z; } } @@ -143,7 +180,7 @@ namespace LL void copy(const Material::PbrMetallicRoughness& src, tinygltf::PbrMetallicRoughness& dst) { - dst.baseColorFactor = { src.mBaseColorFactor.v[0], src.mBaseColorFactor.v[1], src.mBaseColorFactor.v[2], src.mBaseColorFactor.v[3] }; + 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; @@ -154,7 +191,7 @@ namespace LL { material.name = src.mName; - material.emissiveFactor = { src.mEmissiveFactor.v[0], src.mEmissiveFactor.v[1], src.mEmissiveFactor.v[2] }; + 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); @@ -193,7 +230,7 @@ namespace LL accessor.maxValues = src.mMax; accessor.count = src.mCount; - accessor.type = src.mType; + accessor.type = (S32) src.mType; accessor.normalized = src.mNormalized; accessor.name = src.mName; } @@ -278,7 +315,8 @@ namespace LL dst.asset.version = src.mVersion; dst.asset.minVersion = src.mMinVersion; dst.asset.generator = "Linden Lab Experimental GLTF Export"; - dst.asset.extras = src.mExtras; + + // NOTE: extras are lost in the conversion for now copy(src.mScenes, dst.scenes); copy(src.mNodes, dst.nodes); @@ -297,8 +335,8 @@ namespace LL } void Scene::updateTransforms(Asset& asset) { - LLMatrix4a identity; - identity.setIdentity(); + mat4 identity = glm::identity<mat4>(); + for (auto& nodeIndex : mNodes) { Node& node = asset.mNodes[nodeIndex]; @@ -306,7 +344,7 @@ void Scene::updateTransforms(Asset& asset) } } -void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview) { for (auto& nodeIndex : mNodes) { @@ -315,9 +353,9 @@ void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) } } -void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +void Node::updateRenderTransforms(Asset& asset, const mat4& modelview) { - matMul(mMatrix, modelview, mRenderMatrix); + mRenderMatrix = modelview * mMatrix; for (auto& childIndex : mChildren) { @@ -326,13 +364,12 @@ void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) } } -LLMatrix4a inverse(const LLMatrix4a& mat); - -void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix) +void Node::updateTransforms(Asset& asset, const mat4& parentMatrix) { makeMatrixValid(); - matMul(mMatrix, parentMatrix, mAssetMatrix); - mAssetMatrixInv = inverse(mAssetMatrix); + mAssetMatrix = parentMatrix * mMatrix; + + mAssetMatrixInv = glm::inverse(mAssetMatrix); S32 my_index = this - &asset.mNodes[0]; @@ -352,26 +389,13 @@ void Asset::updateTransforms() } } -void Asset::updateRenderTransforms(const LLMatrix4a& modelview) +void Asset::updateRenderTransforms(const mat4& modelview) { -#if 0 - // traverse hierarchy and update render transforms from scratch - for (auto& scene : mScenes) - { - scene.updateRenderTransforms(*this, modelview); - } -#else // use mAssetMatrix to update render transforms from node list for (auto& node : mNodes) { - //if (node.mMesh != INVALID_INDEX) - { - matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); - } + node.mRenderMatrix = modelview * node.mAssetMatrix; } - -#endif - } S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, @@ -398,9 +422,11 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, bool newHit = false; + LLMatrix4a ami; + ami.loadu(glm::value_ptr(node.mAssetMatrixInv)); // transform start and end to this node's local space - node.mAssetMatrixInv.affineTransform(start, local_start); - node.mAssetMatrixInv.affineTransform(asset_end, local_end); + ami.affineTransform(start, local_start); + ami.affineTransform(asset_end, local_end); Mesh& mesh = mMeshes[node.mMesh]; for (auto& primitive : mesh.mPrimitives) @@ -423,8 +449,10 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, if (newHit) { + LLMatrix4a am; + am.loadu(glm::value_ptr(node.mAssetMatrix)); // shorten line segment on hit - node.mAssetMatrix.affineTransform(p, asset_end); + am.affineTransform(p, asset_end); // transform results back to asset space if (intersection) @@ -434,12 +462,10 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, if (normal || tangent) { - LLMatrix4 normalMatrix(node.mAssetMatrixInv.getF32ptr()); - - normalMatrix.transpose(); + mat4 normalMatrix = glm::transpose(node.mAssetMatrixInv); LLMatrix4a norm_mat; - norm_mat.loadu((F32*)normalMatrix.mMatrix); + norm_mat.loadu(glm::value_ptr(normalMatrix)); if (normal) { @@ -481,22 +507,7 @@ 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); + mMatrix = glm::recompose(mScale, mRotation, mTranslation, vec3(0,0,0), vec4(0,0,0,1)); mMatrixValid = true; } @@ -507,43 +518,72 @@ 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); + vec3 skew; + vec4 perspective; + glm::decompose(mMatrix, mScale, mRotation, mTranslation, skew, perspective); + mTRSValid = true; } llassert(mTRSValid); } -void Node::setRotation(const glh::quaternionf& q) +void Node::setRotation(const quat& q) { makeTRSValid(); mRotation = q; mMatrixValid = false; } -void Node::setTranslation(const glh::vec3f& t) +void Node::setTranslation(const vec3& t) { makeTRSValid(); mTranslation = t; mMatrixValid = false; } -void Node::setScale(const glh::vec3f& s) +void Node::setScale(const vec3& s) { makeTRSValid(); mScale = s; mMatrixValid = false; } +void Node::serialize(object& dst) const +{ + write(mName, "name", dst); + write(mMatrix, "matrix", dst, glm::identity<mat4>()); + write(mRotation, "rotation", dst); + write(mTranslation, "translation", dst); + write(mScale, "scale", dst, vec3(1.f,1.f,1.f)); + write(mChildren, "children", dst); + write(mMesh, "mesh", dst, INVALID_INDEX); + write(mSkin, "skin", dst, INVALID_INDEX); +} + +const Node& Node::operator=(const Value& src) +{ + copy(src, "name", mName); + mMatrixValid = copy(src, "matrix", mMatrix); + + copy(src, "rotation", mRotation); + copy(src, "translation", mTranslation); + copy(src, "scale", mScale); + copy(src, "children", mChildren); + copy(src, "mesh", mMesh); + copy(src, "skin", mSkin); + + if (!mMatrixValid) + { + mTRSValid = true; + } + + return *this; +} + const Node& Node::operator=(const tinygltf::Node& src) { - F32* dstMatrix = mMatrix.getF32ptr(); + F32* dstMatrix = glm::value_ptr(mMatrix); if (src.matrix.size() == 16) { @@ -560,22 +600,21 @@ const Node& Node::operator=(const tinygltf::Node& src) // node has rotation/translation/scale, convert to matrix if (src.rotation.size() == 4) { - mRotation = glh::quaternionf((F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2], (F32)src.rotation[3]); + mRotation = quat((F32)src.rotation[3], (F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2]); } if (src.translation.size() == 3) { - mTranslation = glh::vec3f((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]); + mTranslation = vec3((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]); } - glh::vec3f scale; if (src.scale.size() == 3) { - mScale = glh::vec3f((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]); + mScale = vec3((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]); } else { - mScale.set_value(1.f, 1.f, 1.f); + mScale = vec3(1.f, 1.f, 1.f); } mTRSValid = true; @@ -583,7 +622,7 @@ const Node& Node::operator=(const tinygltf::Node& src) else { // node specifies no transformation, set to identity - mMatrix.setIdentity(); + mMatrix = glm::identity<mat4>(); mMatrixValid = true; } @@ -595,6 +634,50 @@ const Node& Node::operator=(const tinygltf::Node& src) return *this; } +void Image::serialize(object& dst) const +{ + write(mUri, "uri", dst); + write(mMimeType, "mimeType", dst); + write(mBufferView, "bufferView", dst, INVALID_INDEX); + write(mName, "name", dst); + write(mWidth, "width", dst, -1); + write(mHeight, "height", dst, -1); + write(mComponent, "component", dst, -1); + write(mBits, "bits", dst, -1); + write(mPixelType, "pixelType", dst, -1); +} + +const Image& Image::operator=(const Value& src) +{ + copy(src, "uri", mUri); + copy(src, "mimeType", mMimeType); + copy(src, "bufferView", mBufferView); + copy(src, "name", mName); + copy(src, "width", mWidth); + copy(src, "height", mHeight); + copy(src, "component", mComponent); + copy(src, "bits", mBits); + copy(src, "pixelType", mPixelType); + + 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) @@ -623,7 +706,6 @@ void Asset::render(bool opaque, bool rigged) continue; } - if (node.mMesh != INVALID_INDEX) { Mesh& mesh = mMeshes[node.mMesh]; @@ -631,19 +713,28 @@ void Asset::render(bool opaque, bool rigged) { if (!rigged) { - gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix)); } bool cull = true; if (primitive.mMaterial != INVALID_INDEX) { Material& material = mMaterials[primitive.mMaterial]; + bool mat_opaque = material.mAlphaMode != Material::AlphaMode::BLEND; - if ((material.mMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_BLEND) == opaque) + if (mat_opaque != opaque) { continue; } - material.mMaterial->bind(); - cull = !material.mMaterial->mDoubleSided; + + if (mMaterials[primitive.mMaterial].mMaterial.notNull()) + { + material.mMaterial->bind(); + } + else + { + material.bind(*this); + } + cull = !material.mDoubleSided; } else { @@ -709,11 +800,16 @@ void Asset::allocateGLResources(const std::string& filename, const tinygltf::Mod image.allocateGLResources(); } + // do materials before meshes as meshes may depend on materials - for (U32 i = 0; i < mMaterials.size(); ++i) + if (!filename.empty()) { - mMaterials[i].allocateGLResources(*this); - LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); + for (U32 i = 0; i < mMaterials.size(); ++i) + { + // 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); + } } for (auto& mesh : mMeshes) @@ -732,16 +828,26 @@ void Asset::allocateGLResources(const std::string& filename, const tinygltf::Mod } } +Asset::Asset(const tinygltf::Model& src) +{ + *this = src; +} + +Asset::Asset(const Value& src) +{ + *this = src; +} + const Asset& Asset::operator=(const tinygltf::Model& src) { mVersion = src.asset.version; mMinVersion = src.asset.minVersion; mGenerator = src.asset.generator; mCopyright = src.asset.copyright; - mExtras = src.asset.extras; + + // 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) @@ -818,11 +924,69 @@ const Asset& Asset::operator=(const tinygltf::Model& src) return *this; } +const Asset& Asset::operator=(const Value& src) +{ + if (src.is_object()) + { + const object& obj = src.as_object(); + + const auto it = obj.find("asset"); + + if (it != obj.end()) + { + const Value& asset = it->value(); + + copy(asset, "version", mVersion); + copy(asset, "minVersion", mMinVersion); + copy(asset, "generator", mGenerator); + copy(asset, "copyright", mCopyright); + copy(asset, "extras", mExtras); + } + + copy(obj, "defaultScene", mDefaultScene); + copy(obj, "scenes", mScenes); + copy(obj, "nodes", mNodes); + copy(obj, "meshes", mMeshes); + copy(obj, "materials", mMaterials); + copy(obj, "buffers", mBuffers); + copy(obj, "bufferViews", mBufferViews); + copy(obj, "textures", mTextures); + copy(obj, "samplers", mSamplers); + copy(obj, "images", mImages); + copy(obj, "accessors", mAccessors); + copy(obj, "animations", mAnimations); + copy(obj, "skins", mSkins); + } + + 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); + + write(mScenes, "scenes", dst); + write(mNodes, "nodes", dst); + write(mMeshes, "meshes", dst); + write(mMaterials, "materials", dst); + write(mBuffers, "buffers", dst); + write(mBufferViews, "bufferViews", dst); + write(mTextures, "textures", dst); + write(mSamplers, "samplers", dst); + write(mImages, "images", dst); + write(mAccessors, "accessors", dst); + write(mAnimations, "animations", dst); + write(mSkins, "skins", dst); +} + void Asset::decompose(const std::string& filename) { // get folder path @@ -857,6 +1021,41 @@ void Asset::eraseBufferView(S32 bufferView) } +LLViewerFetchedTexture* fetch_texture(const LLUUID& id); + +void Image::allocateGLResources() +{ + LLUUID id; + if (LLUUID::parseUUID(mUri, &id) && id.notNull()) + { + mTexture = fetch_texture(id); + } +} + + +void Image::clearData(Asset& asset) +{ + if (mBufferView != INVALID_INDEX) + { + // remove data from buffer + BufferView& bufferView = asset.mBufferViews[mBufferView]; + Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + + buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength); + + asset.eraseBufferView(mBufferView); + } + + mData.clear(); + mBufferView = INVALID_INDEX; + mWidth = -1; + mHeight = -1; + mComponent = -1; + mBits = -1; + mPixelType = -1; + mMimeType = ""; +} + void Image::decompose(Asset& asset, const std::string& folder) { std::string name = mName; @@ -894,12 +1093,9 @@ void Image::decompose(Asset& asset, const std::string& folder) std::ofstream file(filename, std::ios::binary); file.write((const char*)buffer.mData.data() + bufferView.mByteOffset, bufferView.mByteLength); - - buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength); - - asset.eraseBufferView(mBufferView); } +#if 0 if (!mData.empty()) { // save j2c image @@ -907,21 +1103,44 @@ void Image::decompose(Asset& asset, const std::string& folder) LLPointer<LLImageRaw> raw = new LLImageRaw(mWidth, mHeight, mComponent); U8* data = raw->allocateData(); - llassert(mData.size() == raw->getDataSize()); + llassert_always(mData.size() == raw->getDataSize()); memcpy(data, mData.data(), mData.size()); LLViewerTextureList::createUploadFile(raw, filename, 4096); mData.clear(); } +#endif - mWidth = -1; - mHeight = -1; - mComponent = -1; - mBits = -1; - mPixelType = -1; - mMimeType = ""; + clearData(asset); +} + +void Material::TextureInfo::serialize(object& dst) const +{ + write(mIndex, "index", dst, INVALID_INDEX); + write(mTexCoord, "texCoord", dst, 0); +} + +const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "index", mIndex); + copy(src, "texCoord", mTexCoord); + } + + return *this; +} + +bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const +{ + return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord; +} + +bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const +{ + return !(*this == rhs); } const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::TextureInfo& src) @@ -931,6 +1150,25 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::Te return *this; } +void Material::OcclusionTextureInfo::serialize(object& dst) const +{ + write(mIndex, "index", dst, INVALID_INDEX); + write(mTexCoord, "texCoord", dst, 0); + write(mStrength, "strength", dst, 1.f); +} + +const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "index", mIndex); + copy(src, "texCoord", mTexCoord); + copy(src, "strength", mStrength); + } + + return *this; +} + const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const tinygltf::OcclusionTextureInfo& src) { mIndex = src.index; @@ -939,6 +1177,24 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=( return *this; } +void Material::NormalTextureInfo::serialize(object& dst) const +{ + write(mIndex, "index", dst, INVALID_INDEX); + write(mTexCoord, "texCoord", dst, 0); + write(mScale, "scale", dst, 1.f); +} + +const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "index", mIndex); + copy(src, "texCoord", mTexCoord); + copy(src, "scale", mScale); + } + + return *this; +} const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const tinygltf::NormalTextureInfo& src) { mIndex = src.index; @@ -947,11 +1203,48 @@ const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const return *this; } +const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "baseColorFactor", mBaseColorFactor); + copy(src, "baseColorTexture", mBaseColorTexture); + copy(src, "metallicFactor", mMetallicFactor); + copy(src, "roughnessFactor", mRoughnessFactor); + copy(src, "metallicRoughnessTexture", mMetallicRoughnessTexture); + } + + return *this; +} + +void Material::PbrMetallicRoughness::serialize(object& dst) const +{ + write(mBaseColorFactor, "baseColorFactor", dst, vec4(1.f, 1.f, 1.f, 1.f)); + write(mBaseColorTexture, "baseColorTexture", dst); + write(mMetallicFactor, "metallicFactor", dst, 1.f); + write(mRoughnessFactor, "roughnessFactor", dst, 1.f); + write(mMetallicRoughnessTexture, "metallicRoughnessTexture", dst); +} + +bool Material::PbrMetallicRoughness::operator==(const Material::PbrMetallicRoughness& rhs) const +{ + return mBaseColorFactor == rhs.mBaseColorFactor && + mBaseColorTexture == rhs.mBaseColorTexture && + mMetallicFactor == rhs.mMetallicFactor && + mRoughnessFactor == rhs.mRoughnessFactor && + mMetallicRoughnessTexture == rhs.mMetallicRoughnessTexture; +} + +bool Material::PbrMetallicRoughness::operator!=(const Material::PbrMetallicRoughness& rhs) const +{ + return !(*this == rhs); +} + const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const tinygltf::PbrMetallicRoughness& src) { if (src.baseColorFactor.size() == 4) { - mBaseColorFactor.set_value(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]); + mBaseColorFactor = vec4(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]); } mBaseColorTexture = src.baseColorTexture; @@ -961,13 +1254,129 @@ const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=( return *this; } + +static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback) +{ + if (info.mIndex != INVALID_INDEX) + { + LLViewerTexture* tex = asset.mImages[asset.mTextures[info.mIndex].mSource].mTexture; + if (tex) + { + tex->addTextureStats(2048.f * 2048.f); + LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex); + } + else + { + LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback); + } + } + else + { + LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback); + } +} + +void Material::bind(Asset& asset) +{ + // bind for rendering (derived from LLFetchedGLTFMaterial::bind) + // glTF 2.0 Specification 3.9.4. Alpha Coverage + // mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK + F32 min_alpha = -1.0; + + LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; + + if (!LLPipeline::sShadowRender || (mAlphaMode == Material::AlphaMode::BLEND)) + { + if (mAlphaMode == Material::AlphaMode::MASK) + { + // dividing the alpha cutoff by transparency here allows the shader to compare against + // the alpha value of the texture without needing the transparency value + if (mPbrMetallicRoughness.mBaseColorFactor.a > 0.f) + { + min_alpha = mAlphaCutoff / mPbrMetallicRoughness.mBaseColorFactor.a; + } + else + { + min_alpha = 1024.f; + } + } + shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha); + } + + bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep); + + F32 base_color_packed[8]; + //mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed); + LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed); + shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed); + + if (!LLPipeline::sShadowRender) + { + 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); + shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, mPbrMetallicRoughness.mMetallicFactor); + shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(mEmissiveFactor)); + + F32 normal_packed[8]; + //mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed); + LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed); + shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, (F32*)normal_packed); + + F32 metallic_roughness_packed[8]; + //mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed); + LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed); + shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, (F32*)metallic_roughness_packed); + + F32 emissive_packed[8]; + //mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed); + LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed); + shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed); + } +} + +void Material::serialize(object& dst) const +{ + write(mName, "name", dst); + write(mEmissiveFactor, "emissiveFactor", dst, vec3(0.f, 0.f, 0.f)); + write(mPbrMetallicRoughness, "pbrMetallicRoughness", dst); + write(mNormalTexture, "normalTexture", dst); + write(mOcclusionTexture, "occlusionTexture", dst); + write(mEmissiveTexture, "emissiveTexture", dst); + write(mAlphaMode, "alphaMode", dst, Material::AlphaMode::OPAQUE); + write(mAlphaCutoff, "alphaCutoff", dst, 0.5f); + write(mDoubleSided, "doubleSided", dst, false); +} + +const Material& Material::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "emissiveFactor", mEmissiveFactor); + copy(src, "pbrMetallicRoughness", mPbrMetallicRoughness); + copy(src, "normalTexture", mNormalTexture); + copy(src, "occlusionTexture", mOcclusionTexture); + copy(src, "emissiveTexture", mEmissiveTexture); + copy(src, "alphaMode", mAlphaMode); + copy(src, "alphaCutoff", mAlphaCutoff); + copy(src, "doubleSided", mDoubleSided); + } + return *this; +} + + const Material& Material::operator=(const tinygltf::Material& src) { mName = src.name; if (src.emissiveFactor.size() == 3) { - mEmissiveFactor.set_value(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]); + mEmissiveFactor = vec3(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]); } mPbrMetallicRoughness = src.pbrMetallicRoughness; @@ -975,7 +1384,7 @@ const Material& Material::operator=(const tinygltf::Material& src) mOcclusionTexture = src.occlusionTexture; mEmissiveTexture = src.emissiveTexture; - mAlphaMode = src.alphaMode; + mAlphaMode = gltf_alpha_mode_to_enum(src.alphaMode); mAlphaCutoff = src.alphaCutoff; mDoubleSided = src.doubleSided; @@ -984,10 +1393,31 @@ const Material& Material::operator=(const tinygltf::Material& src) void Material::allocateGLResources(Asset& asset) { - // allocate material + // 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); + write(mWeights, "weights", dst); + write(mName, "name", dst); +} + +const Mesh& Mesh::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "primitives", mPrimitives); + copy(src, "weights", mWeights); + copy(src, "name", mName); + } + + return *this; + +} const Mesh& Mesh::operator=(const tinygltf::Mesh& src) { mPrimitives.resize(src.primitives.size()); @@ -1010,6 +1440,20 @@ void Mesh::allocateGLResources(Asset& asset) } } +void Scene::serialize(object& dst) const +{ + write(mNodes, "nodes", dst); + write(mName, "name", dst); +} + +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; @@ -1018,6 +1462,25 @@ const Scene& Scene::operator=(const tinygltf::Scene& src) return *this; } +void Texture::serialize(object& dst) const +{ + write(mSampler, "sampler", dst, INVALID_INDEX); + write(mSource, "source", dst, INVALID_INDEX); + write(mName, "name", dst); +} + +const Texture& Texture::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "sampler", mSampler); + copy(src, "source", mSource); + copy(src, "name", mName); + } + + return *this; +} + const Texture& Texture::operator=(const tinygltf::Texture& src) { mSampler = src.sampler; @@ -1027,6 +1490,27 @@ const Texture& Texture::operator=(const tinygltf::Texture& src) return *this; } + +void Sampler::serialize(object& dst) const +{ + write(mMagFilter, "magFilter", dst, LINEAR); + write(mMinFilter, "minFilter", dst, LINEAR_MIPMAP_LINEAR); + write(mWrapS, "wrapS", dst, REPEAT); + write(mWrapT, "wrapT", dst, REPEAT); + write(mName, "name", dst); +} + +const Sampler& Sampler::operator=(const Value& src) +{ + copy(src, "magFilter", mMagFilter); + copy(src, "minFilter", mMinFilter); + copy(src, "wrapS", mWrapS); + copy(src, "wrapT", mWrapT); + copy(src, "name", mName); + + return *this; +} + const Sampler& Sampler::operator=(const tinygltf::Sampler& src) { mMagFilter = src.magFilter; @@ -1043,23 +1527,14 @@ 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; + std::vector<mat4> 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]; - + t_mp[i] = joint.mRenderMatrix * mInverseBindMatricesData[i]; } std::vector<F32> glmp; @@ -1070,7 +1545,7 @@ void Skin::uploadMatrixPalette(Asset& asset, Node& node) for (U32 i = 0; i < mJoints.size(); ++i) { - F32* m = (F32*)t_mp[i].m; + F32* m = glm::value_ptr(t_mp[i]); U32 idx = i * 12; diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index 5a62313705..1d707cbeba 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -32,9 +32,16 @@ #include "accessor.h" #include "primitive.h" #include "animation.h" +#include "boost/json.hpp" +#include "common.h" extern F32SecondsImplicit gFrameTimeSeconds; +// wingdi defines OPAQUE, which conflicts with our enum +#if defined(OPAQUE) +#undef OPAQUE +#endif + // LL GLTF Implementation namespace LL { @@ -45,13 +52,26 @@ namespace LL class Material { public: + + enum class AlphaMode + { + OPAQUE, + MASK, + BLEND + }; + class TextureInfo { public: S32 mIndex = INVALID_INDEX; S32 mTexCoord = 0; + 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; }; class NormalTextureInfo : public TextureInfo @@ -60,6 +80,8 @@ namespace LL F32 mScale = 1.0f; const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src); + const NormalTextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; }; class OcclusionTextureInfo : public TextureInfo @@ -68,17 +90,24 @@ namespace LL F32 mStrength = 1.0f; const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src); + const OcclusionTextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; }; class PbrMetallicRoughness { public: - glh::vec4f mBaseColorFactor = glh::vec4f(1.f,1.f,1.f,1.f); + vec4 mBaseColorFactor = vec4(1.f,1.f,1.f,1.f); TextureInfo mBaseColorTexture; F32 mMetallicFactor = 1.0f; F32 mRoughnessFactor = 1.0f; TextureInfo mMetallicRoughnessTexture; + + 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; }; @@ -93,13 +122,16 @@ namespace LL std::string mName; - glh::vec3f mEmissiveFactor = glh::vec3f(0.f, 0.f, 0.f); - std::string mAlphaMode = "OPAQUE"; + vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f); + AlphaMode mAlphaMode = AlphaMode::OPAQUE; F32 mAlphaCutoff = 0.5f; bool mDoubleSided = false; - + // 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); }; @@ -112,6 +144,8 @@ namespace LL 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); }; @@ -119,14 +153,14 @@ namespace LL class Node { public: - LLMatrix4a mMatrix; //local transform - LLMatrix4a mRenderMatrix; //transform for rendering - LLMatrix4a mAssetMatrix; //transform from local to asset space - LLMatrix4a mAssetMatrixInv; //transform from asset to local space + mat4 mMatrix = glm::identity<mat4>(); //local transform + mat4 mRenderMatrix; //transform for rendering + mat4 mAssetMatrix; //transform from local to asset space + mat4 mAssetMatrixInv; //transform from asset to local space - glh::vec3f mTranslation; - glh::quaternionf mRotation; - glh::vec3f mScale; + vec3 mTranslation = vec3(0,0,0); + quat mRotation = glm::identity<quat>(); + vec3 mScale = vec3(1.f,1.f,1.f); // if true, mMatrix is valid and up to date bool mMatrixValid = false; @@ -145,13 +179,15 @@ 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; // Set mRenderMatrix to a transform that can be used for the current render pass // modelview -- parent's render matrix - void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); + void updateRenderTransforms(Asset& asset, const mat4& modelview); // update mAssetMatrix and mAssetMatrixInv - void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix); + void updateTransforms(Asset& asset, const mat4& parentMatrix); // ensure mMatrix is valid -- if mMatrixValid is false and mTRSValid is true, will update mMatrix to match Translation/Rotation/Scale void makeMatrixValid(); @@ -161,15 +197,15 @@ namespace LL // Set rotation of this node // SIDE EFFECT: invalidates mMatrix - void setRotation(const glh::quaternionf& rotation); + void setRotation(const quat& rotation); // Set translation of this node // SIDE EFFECT: invalidates mMatrix - void setTranslation(const glh::vec3f& translation); + void setTranslation(const vec3& translation); // Set scale of this node // SIDE EFFECT: invalidates mMatrix - void setScale(const glh::vec3f& scale); + void setScale(const vec3& scale); }; class Skin @@ -179,12 +215,14 @@ namespace LL S32 mSkeleton = INVALID_INDEX; std::vector<S32> mJoints; std::string mName; - std::vector<glh::matrix4f> mInverseBindMatricesData; + std::vector<mat4> mInverseBindMatricesData; void allocateGLResources(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; }; class Scene @@ -194,9 +232,11 @@ namespace LL std::string mName; const Scene& operator=(const tinygltf::Scene& src); - + const Scene& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + void updateTransforms(Asset& asset); - void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); + void updateRenderTransforms(Asset& asset, const mat4& modelview); }; class Texture @@ -207,18 +247,22 @@ namespace LL std::string mName; const Texture& operator=(const tinygltf::Texture& src); + const Texture& operator=(const Value& src); + void serialize(boost::json::object& dst) const; }; class Sampler { public: - S32 mMagFilter; - S32 mMinFilter; - S32 mWrapS; - S32 mWrapT; + S32 mMagFilter = LINEAR; + S32 mMinFilter = LINEAR_MIPMAP_LINEAR; + S32 mWrapS = REPEAT; + 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; }; class Image @@ -231,43 +275,35 @@ namespace LL S32 mBufferView = INVALID_INDEX; std::vector<U8> mData; - S32 mWidth; - S32 mHeight; - S32 mComponent; - S32 mBits; - S32 mPixelType; + S32 mWidth = -1; + S32 mHeight = -1; + S32 mComponent = -1; + S32 mBits = -1; + S32 mPixelType = -1; LLPointer<LLViewerFetchedTexture> mTexture; - const Image& operator=(const tinygltf::Image& src) - { - mName = src.name; - mUri = src.uri; - mMimeType = src.mimeType; - mData = src.image; - mWidth = src.width; - mHeight = src.height; - mComponent = src.component; - mBits = src.bits; - mBufferView = src.bufferView; - mPixelType = src.pixel_type; - return *this; - } - + 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); - void allocateGLResources() - { - // allocate texture + // erase the buffer view associated with this image + // free any associated resources + // preserve only uri and name + void clearData(Asset& asset); - } + void allocateGLResources(); }; // C++ representation of a GLTF Asset class Asset { public: + + static const std::string minVersion_default; std::vector<Scene> mScenes; std::vector<Node> mNodes; std::vector<Mesh> mMeshes; @@ -287,14 +323,15 @@ namespace LL std::string mCopyright; S32 mDefaultScene = INVALID_INDEX; - tinygltf::Value mExtras; + Value mExtras; + U32 mPendingBuffers = 0; // 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); + void allocateGLResources(const std::string& filename = "", const tinygltf::Model& model = tinygltf::Model()); // Called periodically (typically once per frame) // Any ongoing work (such as animations) should be handled here @@ -307,7 +344,7 @@ namespace LL void updateTransforms(); // update node render transforms - void updateRenderTransforms(const LLMatrix4a& modelview); + void updateRenderTransforms(const mat4& modelview); void render(bool opaque, bool rigged = false); void renderOpaque(); @@ -323,7 +360,13 @@ namespace LL S32* primitive_hitp = nullptr // return the index of the primitive that was hit ); + Asset() = default; + Asset(const tinygltf::Model& src); + Asset(const Value& src); + const Asset& operator=(const tinygltf::Model& src); + 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); @@ -335,5 +378,8 @@ namespace LL // updates all bufferview indices in this Asset as needed void eraseBufferView(S32 bufferView); }; + + Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode); + std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode); } } diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h index 4e6f5901e7..b0fbc8524d 100644 --- a/indra/newview/gltf/buffer_util.h +++ b/indra/newview/gltf/buffer_util.h @@ -36,55 +36,60 @@ #define LL_FUNCSIG __PRETTY_FUNCTION__ #endif +#include "accessor.h" + namespace LL { namespace GLTF { + + using string_view = boost::json::string_view; + // copy one Scalar from src to dst template<class S, class T> - static void copyScalar(S* src, T& dst) + inline 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) + inline 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) + inline 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) + inline void copyVec4(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } - // copy one vec2 from src to dst + // copy one mat2 from src to dst template<class S, class T> - static void copyMat2(S* src, T& dst) + inline void copyMat2(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } - // copy one vec3 from src to dst + // copy one mat3 from src to dst template<class S, class T> - static void copyMat3(S* src, T& dst) + inline void copyMat3(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } - // copy one vec4 from src to dst + // copy one mat4 from src to dst template<class S, class T> - static void copyMat4(S* src, T& dst) + inline void copyMat4(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } @@ -93,135 +98,128 @@ namespace LL // 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) + inline void copyScalar<F32, F32>(F32* src, F32& dst) { dst = *src; } template<> - void copyScalar<U32, U32>(U32* src, U32& dst) + inline void copyScalar<U32, U32>(U32* src, U32& dst) { dst = *src; } template<> - void copyScalar<U32, U16>(U32* src, U16& dst) + inline void copyScalar<U32, U16>(U32* src, U16& dst) { dst = *src; } template<> - void copyScalar<U16, U16>(U16* src, U16& dst) + inline void copyScalar<U16, U16>(U16* src, U16& dst) { dst = *src; } template<> - void copyScalar<U16, U32>(U16* src, U32& dst) + inline void copyScalar<U16, U32>(U16* src, U32& dst) { dst = *src; } template<> - void copyScalar<U8, U16>(U8* src, U16& dst) + inline void copyScalar<U8, U16>(U8* src, U16& dst) { dst = *src; } template<> - void copyScalar<U8, U32>(U8* src, U32& dst) + inline void copyScalar<U8, U32>(U8* src, U32& dst) { dst = *src; } template<> - void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst) + inline 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) + inline void copyVec3<F32, vec3>(F32* src, vec3& dst) { - dst.set_value(src[0], src[1], src[2]); + dst = vec3(src[0], src[1], src[2]); } template<> - void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) + inline void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) { dst.load3(src); } template<> - void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst) + inline 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) + inline 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) + inline 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) + inline 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) + inline void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst) { dst.loadua(src); } template<> - void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst) + inline 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) + inline 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) + inline void copyVec4<F32, quat>(F32* src, quat& dst) { - dst.set_value(src); + dst.x = src[0]; + dst.y = src[1]; + dst.z = src[2]; + dst.w = src[3]; } template<> - void copyMat4<F32, glh::matrix4f>(F32* src, glh::matrix4f& dst) + inline void copyMat4<F32, mat4>(F32* src, mat4& dst) { - dst.set_value(src); + dst = glm::make_mat4(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) + inline void copyScalar(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -233,7 +231,7 @@ namespace LL // 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) + inline void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -245,7 +243,7 @@ namespace LL // 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) + inline void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -257,7 +255,7 @@ namespace LL // 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) + inline void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -269,7 +267,7 @@ namespace LL // 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) + inline void copyMat2(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -281,7 +279,7 @@ namespace LL // 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) + inline void copyMat3(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -293,7 +291,7 @@ namespace LL // 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) + inline void copyMat4(S* src, LLStrider<T> dst, S32 stride, S32 count) { for (S32 i = 0; i < count; ++i) { @@ -304,39 +302,39 @@ namespace LL } template<class S, class T> - static void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) + inline void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) { - if (accessor.mType == (S32)Accessor::Type::SCALAR) + if (accessor.mType == 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) + else if (accessor.mType == 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) + else if (accessor.mType == 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) + else if (accessor.mType == 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) + else if (accessor.mType == 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) + else if (accessor.mType == 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) + else if (accessor.mType == Accessor::Type::MAT4) { S32 stride = byteStride == 0 ? sizeof(S) * 16 : byteStride; copyMat4((S*)src, dst, stride, accessor.mCount); @@ -349,7 +347,7 @@ namespace LL // copy data from accessor to strider template<class T> - static void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst) + inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst) { const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView]; const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; @@ -391,12 +389,504 @@ namespace LL // copy data from accessor to vector template<class T> - static void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst) + inline void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst) { dst.resize(accessor.mCount); LLStrider<T> strider = dst.data(); copy(asset, accessor, strider); } + + + //========================================================================================================= + // boost::json copying utilities + // ======================================================================================================== + + //====================== unspecialized base template, single value =========================== + + // to/from Value + template<typename T> + inline bool copy(const Value& src, T& dst) + { + dst = src; + return true; + } + + template<typename T> + inline bool write(const T& src, Value& dst) + { + dst = boost::json::object(); + src.serialize(dst.as_object()); + return true; + } + + template<typename T> + inline bool copy(const Value& src, std::unordered_map<std::string, T>& dst) + { + if (src.is_object()) + { + const boost::json::object& obj = src.as_object(); + for (const auto& [key, value] : obj) + { + copy<T>(value, dst[key]); + } + return true; + } + return false; + } + + template<typename T> + inline bool write(const std::unordered_map<std::string, T>& src, Value& dst) + { + boost::json::object obj; + for (const auto& [key, value] : src) + { + Value v; + if (write<T>(value, v)) + { + obj[key] = v; + } + else + { + return false; + } + } + dst = obj; + return true; + } + + // to/from array + template<typename T> + inline bool copy(const Value& src, std::vector<T>& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.get_array(); + dst.resize(arr.size()); + for (size_t i = 0; i < arr.size(); ++i) + { + copy(arr[i], dst[i]); + } + return true; + } + + return false; + } + + template<typename T> + inline bool write(const std::vector<T>& src, Value& dst) + { + boost::json::array arr; + for (const T& t : src) + { + Value v; + if (write(t, v)) + { + arr.push_back(v); + } + else + { + return false; + } + } + dst = arr; + return true; + } + + // to/from object member + template<typename T> + inline bool copy(const boost::json::object& src, string_view member, T& dst) + { + auto it = src.find(member); + if (it != src.end()) + { + return copy(it->value(), dst); + } + return false; + } + + // always write a member to an object without checking default + template<typename T> + inline bool write_always(const T& src, string_view member, boost::json::object& dst) + { + Value& v = dst[member]; + if (!write(src, v)) + { + dst.erase(member); + return false; + } + return true; + } + + // conditionally write a member to an object if the member + // is not the default value + template<typename T> + inline bool write(const T& src, string_view member, boost::json::object& dst, const T& default_value = T()) + { + if (src != default_value) + { + return write_always(src, member, dst); + } + 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>()) + { + if (!src.empty()) + { + Value v; + if (write<T>(src, v)) + { + dst[member] = v; + return true; + } + } + return false; + } + + template<typename T> + inline bool write(const std::vector<T>& src, string_view member, boost::json::object& dst, const std::vector<T>& deafault_value = std::vector<T>()) + { + if (!src.empty()) + { + Value v; + if (write(src, v)) + { + dst[member] = v; + return true; + } + } + return false; + } + + template<typename T> + inline bool copy(const Value& src, string_view member, T& dst) + { + if (src.is_object()) + { + const boost::json::object& obj = src.as_object(); + return copy(obj, member, dst); + } + + return false; + } + + // vec4 + template<> + inline bool copy(const Value& src, vec4& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 4) + { + if (arr[0].is_double() && + arr[1].is_double() && + arr[2].is_double() && + arr[3].is_double()) + { + dst = vec4(arr[0].get_double(), arr[1].get_double(), arr[2].get_double(), arr[3].get_double()); + return true; + } + } + } + return false; + } + + template<> + inline bool write(const vec4& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.get_array(); + arr.resize(4); + arr[0] = src.x; + arr[1] = src.y; + arr[2] = src.z; + arr[3] = src.w; + return true; + } + + // quat + template<> + inline bool copy(const Value& src, quat& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 4) + { + if (arr[0].is_double() && + arr[1].is_double() && + arr[2].is_double() && + arr[3].is_double()) + { + dst.x = arr[0].get_double(); + dst.y = arr[1].get_double(); + dst.z = arr[2].get_double(); + dst.w = arr[3].get_double(); + return true; + } + } + } + return false; + } + + template<> + inline bool write(const quat& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.get_array(); + arr.resize(4); + arr[0] = src.x; + arr[1] = src.y; + arr[2] = src.z; + arr[3] = src.w; + return true; + } + + + // vec3 + template<> + inline bool copy(const Value& src, vec3& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 3) + { + if (arr[0].is_double() && + arr[1].is_double() && + arr[2].is_double()) + { + dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double()); + } + return true; + } + } + return false; + } + + template<> + inline bool write(const vec3& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.as_array(); + arr.resize(3); + arr[0] = src.x; + arr[1] = src.y; + arr[2] = src.z; + return true; + } + + // bool + template<> + inline bool copy(const Value& src, bool& dst) + { + if (src.is_bool()) + { + dst = src.get_bool(); + return true; + } + return false; + } + + template<> + inline bool write(const bool& src, Value& dst) + { + dst = src; + return true; + } + + // F32 + template<> + inline bool copy(const Value& src, F32& dst) + { + if (src.is_double()) + { + dst = src.get_double(); + return true; + } + return false; + } + + template<> + inline bool write(const F32& src, Value& dst) + { + dst = src; + return true; + } + + + // U32 + template<> + inline bool copy(const Value& src, U32& dst) + { + if (src.is_int64()) + { + dst = src.get_int64(); + return true; + } + return false; + } + + template<> + inline bool write(const U32& src, Value& dst) + { + dst = src; + return true; + } + + // F64 + template<> + inline bool copy(const Value& src, F64& dst) + { + if (src.is_double()) + { + dst = src.get_double(); + return true; + } + return false; + } + + template<> + inline bool write(const F64& src, Value& dst) + { + dst = src; + return true; + } + + // Accessor::Type + template<> + inline bool copy(const Value& src, Accessor::Type& dst) + { + if (src.is_string()) + { + dst = gltf_type_to_enum(src.get_string().c_str()); + return true; + } + return false; + } + + template<> + inline bool write(const Accessor::Type& src, Value& dst) + { + dst = enum_to_gltf_type(src); + return true; + } + + // S32 + template<> + inline bool copy(const Value& src, S32& dst) + { + if (src.is_int64()) + { + dst = src.get_int64(); + return true; + } + return false; + } + + template<> + inline bool write(const S32& src, Value& dst) + { + dst = src; + return true; + } + + + // std::string + template<> + inline bool copy(const Value& src, std::string& dst) + { + if (src.is_string()) + { + dst = src.get_string().c_str(); + return true; + } + return false; + } + + template<> + inline bool write(const std::string& src, Value& dst) + { + dst = src; + return true; + } + + // mat4 + template<> + inline bool copy(const Value& src, mat4& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.get_array(); + if (arr.size() == 16) + { + // populate a temporary local in case + // we hit an error in the middle of the array + // (don't partially write a matrix) + mat4 t; + F32* p = glm::value_ptr(t); + + for (U32 i = 0; i < arr.size(); ++i) + { + if (arr[i].is_double()) + { + p[i] = arr[i].get_double(); + } + else + { + return false; + } + } + + dst = t; + return true; + } + } + + return false; + } + + template<> + inline bool write(const mat4& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.get_array(); + arr.resize(16); + const F32* p = glm::value_ptr(src); + for (U32 i = 0; i < 16; ++i) + { + arr[i] = p[i]; + } + return true; + } + + // Material::AlphaMode + template<> + inline bool copy(const Value& src, Material::AlphaMode& dst) + { + if (src.is_string()) + { + dst = gltf_alpha_mode_to_enum(src.get_string().c_str()); + return true; + } + return true; + } + + template<> + inline bool write(const Material::AlphaMode& src, Value& dst) + { + dst = enum_to_gltf_alpha_mode(src); + return true; + } + + // + // ======================================================================================================== + } } + + + diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h new file mode 100644 index 0000000000..859e202738 --- /dev/null +++ b/indra/newview/gltf/common.h @@ -0,0 +1,66 @@ +#pragma once + +/** + * @file common.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$ + */ + +#define GLM_ENABLE_EXPERIMENTAL 1 + +#include "glm/vec2.hpp" +#include "glm/vec3.hpp" +#include "glm/vec4.hpp" +#include "glm/mat4x4.hpp" +#include "glm/gtc/type_ptr.hpp" +#include "glm/ext/quaternion_float.hpp" +#include "glm/gtx/quaternion.hpp" +#include "glm/gtx/matrix_decompose.hpp" + +// Common types and constants used in the GLTF implementation +namespace LL +{ + namespace GLTF + { + using Value = boost::json::value; + + using mat4 = glm::mat4; + using vec4 = glm::vec4; + using vec3 = glm::vec3; + using vec2 = glm::vec2; + using quat = glm::quat; + + constexpr S32 LINEAR = 9729; + constexpr S32 NEAREST = 9728; + constexpr S32 NEAREST_MIPMAP_NEAREST = 9984; + constexpr S32 LINEAR_MIPMAP_NEAREST = 9985; + constexpr S32 NEAREST_MIPMAP_LINEAR = 9986; + constexpr S32 LINEAR_MIPMAP_LINEAR = 9987; + constexpr S32 CLAMP_TO_EDGE = 33071; + 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 b57a0af18d..1bde7327e6 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -28,10 +28,12 @@ #include "asset.h" #include "buffer_util.h" +#include "../llviewershadermgr.h" #include "../lltinygltfhelper.h" using namespace LL::GLTF; +using namespace boost::json; void Primitive::allocateGLResources(Asset& asset) { @@ -92,6 +94,10 @@ void Primitive::allocateGLResources(Asset& asset) mask |= LLVertexBuffer::MAP_WEIGHT4; } + if (LLGLSLShader::sCurBoundShaderPtr == nullptr) + { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer + gDebugProgram.bind(); + } mVertexBuffer = new LLVertexBuffer(mask); mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices @@ -129,7 +135,7 @@ void Primitive::allocateGLResources(Asset& asset) if (mMaterial != INVALID_INDEX) { const Material& material = asset.mMaterials[mMaterial]; - LLColor4 baseColor = material.mMaterial->mBaseColor; + LLColor4 baseColor(glm::value_ptr(material.mPbrMetallicRoughness.mBaseColorFactor)); for (auto& dst : mColors) { dst = LLColor4U(baseColor * LLColor4(dst)); @@ -351,6 +357,50 @@ Primitive::~Primitive() mOctree = nullptr; } +U32 gltf_mode_to_gl_mode(U32 mode) +{ + switch (mode) + { + case TINYGLTF_MODE_POINTS: + return LLRender::POINTS; + case TINYGLTF_MODE_LINE: + return LLRender::LINES; + case TINYGLTF_MODE_LINE_LOOP: + return LLRender::LINE_LOOP; + case TINYGLTF_MODE_LINE_STRIP: + return LLRender::LINE_STRIP; + case TINYGLTF_MODE_TRIANGLES: + return LLRender::TRIANGLES; + case TINYGLTF_MODE_TRIANGLE_STRIP: + return LLRender::TRIANGLE_STRIP; + case TINYGLTF_MODE_TRIANGLE_FAN: + return LLRender::TRIANGLE_FAN; + default: + return LLRender::TRIANGLES; + } +} + +void Primitive::serialize(boost::json::object& dst) const +{ + write(mMaterial, "material", dst, -1); + write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES); + write(mIndices, "indices", dst, INVALID_INDEX); + write(mAttributes, "attributes", dst); +} + +const Primitive& Primitive::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "material", mMaterial); + copy(src, "mode", mMode); + copy(src, "indices", mIndices); + copy(src, "attributes", mAttributes); + + mGLMode = gltf_mode_to_gl_mode(mMode); + } + return *this; +} const Primitive& Primitive::operator=(const tinygltf::Primitive& src) { @@ -369,32 +419,7 @@ const Primitive& Primitive::operator=(const tinygltf::Primitive& src) 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; - } + mGLMode = gltf_mode_to_gl_mode(mMode); return *this; } diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index 07e8e7deb2..18aadce808 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -28,12 +28,14 @@ #include "llvertexbuffer.h" #include "llvolumeoctree.h" +#include "boost/json.hpp" // LL GLTF Implementation namespace LL { namespace GLTF { + using Value = boost::json::value; class Asset; constexpr U32 ATTRIBUTE_MASK = @@ -66,10 +68,10 @@ namespace LL std::vector<LLVolumeTriangle> mOctreeTriangles; S32 mMaterial = -1; - U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles + S32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles U32 mGLMode = LLRender::TRIANGLES; S32 mIndices = -1; - std::unordered_map<std::string, int> mAttributes; + std::unordered_map<std::string, S32> mAttributes; // create octree based on vertex buffer // must be called before buffer is unmapped and after buffer is populated with good data @@ -85,6 +87,8 @@ namespace LL 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); |