diff options
Diffstat (limited to 'indra/newview')
27 files changed, 2560 insertions, 389 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b2cb58d0df..c7999fc2e9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15738,5 +15738,16 @@ <key>Value</key> <integer>0</integer> </map> + <key>GLTFEnabled</key> + <map> + <key>Comment</key> + <string>Enable GLTF support. Set by SimulatorFeatures</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> </map> </llsd> 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 74f4b246a5..990e1e41a9 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); diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 70e9fb299f..5413a7d021 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -39,7 +39,14 @@ #include "gltf/asset.h" #include "pipeline.h" #include "llviewershadermgr.h" +#include "llviewertexturelist.h" +#include "llimagej2c.h" +#include "llfloaterperms.h" +#include "llagentbenefits.h" +#include "llfilesystem.h" +#include "boost/json.hpp" +#define GLTF_SIM_SUPPORT 1 using namespace LL; @@ -126,6 +133,170 @@ void GLTFSceneManager::decomposeSelection() } } +void GLTFSceneManager::uploadSelection() +{ + if (mUploadingAsset) + { // upload already in progress + LLNotificationsUtil::add("GLTFUploadInProgress"); + return; + } + + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + if (obj && obj->mGLTFAsset) + { + // make a copy of the asset prior to uploading + mUploadingAsset = std::make_shared<Asset>(); + mUploadingObject = obj; + *mUploadingAsset = *obj->mGLTFAsset; + + GLTF::Asset& asset = *mUploadingAsset; + + for (auto& image : asset.mImages) + { + if (!image.mData.empty()) + { + mPendingImageUploads++; + + LLPointer<LLImageRaw> raw = new LLImageRaw(image.mWidth, image.mHeight, image.mComponent); + U8* data = raw->allocateData(); + llassert_always(image.mData.size() == raw->getDataSize()); + memcpy(data, image.mData.data(), image.mData.size()); + + // for GLTF native content, store image in GLTF orientation + raw->verticalFlip(); + + LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw); + + std::string buffer; + buffer.assign((const char*)j2c->getData(), j2c->getDataSize()); + + LLUUID asset_id = LLUUID::generateNewID(); + + std::string name; + S32 idx = (S32)(&image - &asset.mImages[0]); + + if (image.mName.empty()) + { + + name = llformat("Image_%d", idx); + } + else + { + name = image.mName; + } + + LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason) + { + // TODO: handle failure + mPendingImageUploads--; + return false; + }; + + + LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response) + { + if (mUploadingAsset && mUploadingAsset->mImages.size() > idx) + { + mUploadingAsset->mImages[idx].mUri = assetId.asString(); + mPendingImageUploads--; + } + }; + + S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c); + + LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>( + buffer, + asset_id, + name, + name, + 0, + LLFolderType::FT_TEXTURE, + LLInventoryType::IT_TEXTURE, + LLAssetType::AT_TEXTURE, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost, + false, + finish, + failure)); + + upload_new_resource(uploadInfo); + + image.clearData(asset); + } + } + + // upload .bin + for (auto& bin : asset.mBuffers) + { + mPendingBinaryUploads++; + + S32 idx = (S32)(&bin - &asset.mBuffers[0]); + + std::string buffer; + buffer.assign((const char*)bin.mData.data(), bin.mData.size()); + + LLUUID asset_id = LLUUID::generateNewID(); + + LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason) + { + // TODO: handle failure + mPendingBinaryUploads--; + mUploadingAsset = nullptr; + mUploadingObject = nullptr; + LL_WARNS("GLTF") << "Failed to upload GLTF binary: " << reason << LL_ENDL; + LL_WARNS("GLTF") << response << LL_ENDL; + return false; + }; + + LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx](LLUUID assetId, LLSD response) + { + if (mUploadingAsset && mUploadingAsset->mBuffers.size() > idx) + { + mUploadingAsset->mBuffers[idx].mUri = assetId.asString(); + mPendingBinaryUploads--; + + // HACK: save buffer to cache to emulate a successful download + LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE); + auto& data = mUploadingAsset->mBuffers[idx].mData; + + cache.write((const U8*)data.data(), data.size()); + } + }; +#if GLTF_SIM_SUPPORT + S32 expected_upload_cost = 1; + + LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>( + buffer, + asset_id, + "", + "", + 0, + LLFolderType::FT_NONE, + LLInventoryType::IT_GLTF_BIN, + LLAssetType::AT_GLTF_BIN, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost, + false, + finish, + failure)); + + upload_new_resource(uploadInfo); +#else + // dummy finish + finish(LLUUID::generateNewID(), LLSD()); +#endif + } + } + else + { + LLNotificationsUtil::add("GLTFUploadSelection"); + } +} + void GLTFSceneManager::decomposeSelection(const std::string& filename) { LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); @@ -176,7 +347,7 @@ void GLTFSceneManager::load(const std::string& filename) if (obj) { // assign to self avatar obj->mGLTFAsset = asset; - + obj->markForUpdate(); if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end()) { mObjects.push_back(obj); @@ -199,6 +370,109 @@ void GLTFSceneManager::renderAlpha() render(false); } +void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id) +{ + llassert(obj->getVolume()->getParams().getSculptID() == gltf_id); + llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF); + + obj->ref(); + gAssetStorage->getAssetData(gltf_id, LLAssetType::AT_GLTF, onGLTFLoadComplete, obj); +} + +//static +void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status) +{ + LLViewerObject* obj = (LLViewerObject*)user_data; + llassert(asset_type == LLAssetType::AT_GLTF_BIN); + + if (status == LL_ERR_NOERR) + { + if (obj) + { + // find the Buffer with the given id in the asset + if (obj->mGLTFAsset) + { + for (auto& buffer : obj->mGLTFAsset->mBuffers) + { + LLUUID buffer_id; + if (LLUUID::parseUUID(buffer.mUri, &buffer_id) && buffer_id == id) + { + LLFileSystem file(id, asset_type, LLFileSystem::READ); + + buffer.mData.resize(file.getSize()); + file.read((U8*)buffer.mData.data(), buffer.mData.size()); + + obj->mGLTFAsset->mPendingBuffers--; + + if (obj->mGLTFAsset->mPendingBuffers == 0) + { + obj->mGLTFAsset->allocateGLResources(); + GLTFSceneManager& mgr = GLTFSceneManager::instance(); + if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end()) + { + GLTFSceneManager::instance().mObjects.push_back(obj); + } + } + } + } + } + + + } + } + else + { + LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL; + obj->unref(); + } +} + +//static +void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status) +{ + LLViewerObject* obj = (LLViewerObject*)user_data; + llassert(asset_type == LLAssetType::AT_GLTF); + + if (status == LL_ERR_NOERR) + { + if (obj) + { + LLFileSystem file(id, asset_type, LLFileSystem::READ); + std::string data; + data.resize(file.getSize()); + file.read((U8*)data.data(), data.size()); + + boost::json::value json = boost::json::parse(data); + + std::shared_ptr<Asset> asset = std::make_shared<Asset>(json); + obj->mGLTFAsset = asset; + + for (auto& buffer : asset->mBuffers) + { + // for now just assume the buffer is already in the asset cache + LLUUID buffer_id; + if (LLUUID::parseUUID(buffer.mUri, &buffer_id)) + { + asset->mPendingBuffers++; + + gAssetStorage->getAssetData(buffer_id, LLAssetType::AT_GLTF_BIN, onGLTFBinLoadComplete, obj); + } + else + { + LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL; + obj->unref(); + return; + } + } + } + } + else + { + LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL; + obj->unref(); + } +} + void GLTFSceneManager::update() { for (U32 i = 0; i < mObjects.size(); ++i) @@ -212,6 +486,107 @@ void GLTFSceneManager::update() mObjects[i]->mGLTFAsset->update(); } + + // process pending uploads + if (mUploadingAsset && !mGLTFUploadPending) + { + if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0) + { + std::string filename(gDirUtilp->getTempDir() + "/upload.gltf"); +#if 0 + tinygltf::Model model; + mUploadingAsset->save(model); + + tinygltf::TinyGLTF writer; + + writer.WriteGltfSceneToFile(&model, filename, false, false, true, false); +#else + boost::json::object obj; + mUploadingAsset->serialize(obj); + std::string json = boost::json::serialize(obj, {}); + + { + std::ofstream o(filename); + o << json; + } +#endif + + std::ifstream t(filename); + std::stringstream str; + str << t.rdbuf(); + + std::string buffer = str.str(); + + LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason) + { + // TODO: handle failure + LL_WARNS("GLTF") << "Failed to upload GLTF json: " << reason << LL_ENDL; + LL_WARNS("GLTF") << response << LL_ENDL; + + mUploadingAsset = nullptr; + mUploadingObject = nullptr; + mGLTFUploadPending = false; + return false; + }; + + LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response) + { + LLAppViewer::instance()->postToMainCoro( + [=]() + { + if (mUploadingAsset) + { + // HACK: save buffer to cache to emulate a successful upload + LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE); + + LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL; + cache.write((const U8 *) buffer.c_str(), buffer.size()); + + mUploadingAsset = nullptr; + } + + if (mUploadingObject) + { + mUploadingObject->mGLTFAsset = nullptr; + mUploadingObject->setGLTFAsset(assetId); + mUploadingObject->markForUpdate(); + mUploadingObject = nullptr; + } + + mGLTFUploadPending = false; + }); + }; + +#if GLTF_SIM_SUPPORT + S32 expected_upload_cost = 1; + LLUUID asset_id = LLUUID::generateNewID(); + + mGLTFUploadPending = true; + + LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>( + buffer, + asset_id, + "", + "", + 0, + LLFolderType::FT_NONE, + LLInventoryType::IT_GLTF, + LLAssetType::AT_GLTF, + LLFloaterPerms::getNextOwnerPerms("Uploads"), + LLFloaterPerms::getGroupPerms("Uploads"), + LLFloaterPerms::getEveryonePerms("Uploads"), + expected_upload_cost, + false, + finish, + failure)); + + upload_new_resource(uploadInfo); +#else + // dummy finish + finish(LLUUID::generateNewID(), LLSD()); +#endif + } + } } void GLTFSceneManager::render(bool opaque, bool rigged) @@ -219,7 +594,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged) // for debugging, just render the whole scene as opaque // by traversing the whole scenegraph // Assumes camera transform is already set and - // appropriate shader is already bound + // appropriate shader is already boundd gGL.matrixMode(LLRender::MM_MODELVIEW); @@ -243,7 +618,8 @@ void GLTFSceneManager::render(bool opaque, bool rigged) matMul(mat, modelview, modelview); - asset->updateRenderTransforms(modelview); + mat4 mdv = glm::make_mat4(modelview.getF32ptr()); + asset->updateRenderTransforms(mdv); asset->render(opaque, rigged); gGL.popMatrix(); @@ -411,12 +787,16 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) // get raycast in asset space LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform(); - LLVector4a start; - LLVector4a end; + vec4 start; + vec4 end; - agent_to_asset.affineTransform(gDebugRaycastStart, start); - agent_to_asset.affineTransform(gDebugRaycastEnd, end); + LLVector4a t; + agent_to_asset.affineTransform(gDebugRaycastStart, t); + start = glm::make_vec4(t.getF32ptr()); + agent_to_asset.affineTransform(gDebugRaycastEnd, t); + end = glm::make_vec4(t.getF32ptr()); + start.w = end.w = 1.0; for (auto& node : asset->mNodes) { @@ -424,7 +804,7 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) if (node.mMesh != INVALID_INDEX) { - gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix)); // draw bounding box of mesh primitives if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES)) @@ -442,24 +822,24 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) } } -#if 0 +#if 1 if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) { gGL.flush(); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // convert raycast to node local space - LLVector4a local_start; - LLVector4a local_end; - - node.mAssetMatrixInv.affineTransform(start, local_start); - node.mAssetMatrixInv.affineTransform(end, local_end); + vec4 local_start = node.mAssetMatrixInv * start; + vec4 local_end = node.mAssetMatrixInv * end; for (auto& primitive : mesh.mPrimitives) { if (primitive.mOctree.notNull()) { - renderOctreeRaycast(local_start, local_end, primitive.mOctree); + LLVector4a s, e; + s.load3(glm::value_ptr(local_start)); + e.load3(glm::value_ptr(local_end)); + renderOctreeRaycast(s, e, primitive.mOctree); } } @@ -499,18 +879,18 @@ void GLTFSceneManager::renderDebug() continue; } - LLMatrix4a mat = obj->getGLTFAssetToAgentTransform(); + mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr()); - LLMatrix4a modelview; - modelview.loadu(gGLModelView); + mat4 modelview = glm::make_mat4(gGLModelView); - matMul(mat, modelview, modelview); + + modelview = modelview * mat; Asset* asset = obj->mGLTFAsset.get(); for (auto& node : asset->mNodes) { - matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); + node.mRenderMatrix = modelview * node.mAssetMatrix; } } @@ -523,13 +903,6 @@ void GLTFSceneManager::renderDebug() Asset* asset = obj->mGLTFAsset.get(); - LLMatrix4a mat = obj->getGLTFAssetToAgentTransform(); - - LLMatrix4a modelview; - modelview.loadu(gGLModelView); - - matMul(mat, modelview, modelview); - renderAssetDebug(obj, asset); } @@ -551,21 +924,20 @@ void GLTFSceneManager::renderDebug() continue; } - LLMatrix4a mat = obj->getGLTFAssetToAgentTransform(); + mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr()); - LLMatrix4a modelview; - modelview.loadu(gGLModelView); + mat4 modelview = glm::make_mat4(gGLModelView); - matMul(mat, modelview, modelview); + modelview = modelview * mat; Asset* asset = obj->mGLTFAsset.get(); for (auto& node : asset->mNodes) { // force update all mRenderMatrix, not just nodes with meshes - matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); + node.mRenderMatrix = modelview * node.mAssetMatrix; - gGL.loadMatrix(node.mRenderMatrix.getF32ptr()); + gGL.loadMatrix(glm::value_ptr(node.mRenderMatrix)); // render x-axis red, y-axis green, z-axis blue gGL.color4f(1.f, 0.f, 0.f, 0.5f); gGL.begin(LLRender::LINES); @@ -595,7 +967,9 @@ void GLTFSceneManager::renderDebug() { Node& child = asset->mNodes[child_idx]; gGL.vertex3f(0.f, 0.f, 0.f); - gGL.vertex3fv(child.mMatrix.getTranslation().getF32ptr()); + + + gGL.vertex3fv(glm::value_ptr(child.mMatrix[3])); } gGL.end(); gGL.flush(); @@ -628,8 +1002,7 @@ void GLTFSceneManager::renderDebug() gGL.color3f(1, 0, 1); drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f)); - gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix); - + gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix)); auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0); @@ -643,3 +1016,5 @@ void GLTFSceneManager::renderDebug() gDebugProgram.unbind(); } + + diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h index 5ff7c1a447..5434f6ddb2 100644 --- a/indra/newview/gltfscenemanager.h +++ b/indra/newview/gltfscenemanager.h @@ -54,6 +54,7 @@ namespace LL void save(const std::string& filename); // save selected asset to filename (suitable for use in external programs) void decomposeSelection(); // open file picker and choose a location to decompose to void decomposeSelection(const std::string& filename); // decompose selected asset into simulator-ready .gltf, .bin, and .j2c files + void uploadSelection(); // decompose selected asset and upload to simulator void update(); void render(bool opaque, bool rigged = false); @@ -77,7 +78,18 @@ namespace LL void renderDebug(); + void addGLTFObject(LLViewerObject* object, LLUUID gltf_id); + static void onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status); + static void onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status); + std::vector<LLPointer<LLViewerObject>> mObjects; + + std::shared_ptr<GLTF::Asset> mUploadingAsset; + bool mGLTFUploadPending = false; + LLPointer<LLViewerObject> mUploadingObject; + U32 mPendingImageUploads = 0; + U32 mPendingBinaryUploads = 0; + U32 mPendingGLTFUploads = 0; }; } diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp index 6e45e2a0f3..ee4f1b0fe5 100644 --- a/indra/newview/llfetchedgltfmaterial.cpp +++ b/indra/newview/llfetchedgltfmaterial.cpp @@ -142,7 +142,6 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex) mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed); shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed); } - } LLViewerFetchedTexture* fetch_texture(const LLUUID& id) diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index ca800be580..ba763780ed 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1637,23 +1637,29 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) { session_id = message_data["asset_id"].asUUID(); } - LLIMProcessing::processNewMessage( - message_data["from_agent_id"].asUUID(), - from_group, - message_data["to_agent_id"].asUUID(), - message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE, - dialog, - session_id, - static_cast<U32>(message_data["timestamp"].asInteger()), - message_data["from_agent_name"].asString(), - message_data["message"].asString(), - static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland - message_data["region_id"].asUUID(), - position, - bin_bucket.data(), - bin_bucket.size(), - sender, - message_data["asset_id"].asUUID()); + + LLAppViewer::instance()->postToMainCoro([=]() + { + std::vector<U8> local_bin_bucket = bin_bucket; + LLHost local_sender = sender; + LLIMProcessing::processNewMessage( + message_data["from_agent_id"].asUUID(), + from_group, + message_data["to_agent_id"].asUUID(), + message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE, + dialog, + session_id, + static_cast<U32>(message_data["timestamp"].asInteger()), + message_data["from_agent_name"].asString(), + message_data["message"].asString(), + static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland + message_data["region_id"].asUUID(), + position, + local_bin_bucket.data(), + local_bin_bucket.size(), + local_sender, + message_data["asset_id"].asUUID()); + }); } } diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index f0e024605c..9090718691 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1400,7 +1400,9 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) return mask; } - if (item->getType() == LLAssetType::AT_MESH) + if (item->getType() == LLAssetType::AT_MESH || + item->getType() == LLAssetType::AT_GLTF || + item->getType() == LLAssetType::AT_GLTF_BIN) { return mask; } diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index 3e3347ff33..f4c618c08d 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -89,6 +89,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); addEntry(LLViewerAssetType::AT_SETTINGS, new ViewerAssetEntry(DAD_SETTINGS)); addEntry(LLViewerAssetType::AT_MATERIAL, new ViewerAssetEntry(DAD_MATERIAL)); + addEntry(LLViewerAssetType::AT_GLTF, new ViewerAssetEntry(DAD_GLTF)); + addEntry(LLViewerAssetType::AT_GLTF_BIN, new ViewerAssetEntry(DAD_GLTF_BIN)); }; EDragAndDropType LLViewerAssetType::lookupDragAndDropType(EType asset_type) diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 7712c16c16..17d440d8c9 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -299,10 +299,18 @@ void LLResourceUploadInfo::assignDefaults() mDescription = "(No Description)"; } + if (mAssetType == LLAssetType::AT_GLTF || + mAssetType == LLAssetType::AT_GLTF_BIN) + { + mFolderId = LLUUID::null; + } + else + { mFolderId = gInventory.findUserDefinedCategoryUUIDForType( (mDestinationFolderType == LLFolderType::FT_NONE) ? (LLFolderType::EType)mAssetType : mDestinationFolderType); } +} std::string LLResourceUploadInfo::getDisplayName() const { diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index f7d9c12e66..23931ecf03 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2674,7 +2674,13 @@ void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url) { if (initializeMedia(mimeType)) { - loadURI(); + ref(); + LLAppViewer::instance()->postToMainCoro([this]() + { + loadURI(); + unref(); + }); + } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 1a0437ed92..fd16b07985 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -139,6 +139,7 @@ #include "boost/unordered_map.hpp" #include <boost/regex.hpp> #include <boost/algorithm/string.hpp> +#include <boost/json.hpp> #include "llcleanup.h" #include "llviewershadermgr.h" #include "gltfscenemanager.h" @@ -3317,6 +3318,13 @@ bool enable_os_exception() #endif } + +bool enable_gltf() +{ + static LLCachedControl<bool> enablegltf(gSavedSettings, "GLTFEnabled", false); + return enablegltf; +} + class LLSelfRemoveAllAttachments : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -8080,7 +8088,6 @@ class LLAdvancedClickGLTFOpen: public view_listener_t { bool handleEvent(const LLSD& userdata) { - // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools) LL::GLTFSceneManager::instance().load(); return true; } @@ -8090,7 +8097,6 @@ class LLAdvancedClickGLTFSaveAs : public view_listener_t { bool handleEvent(const LLSD& userdata) { - // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools) LL::GLTFSceneManager::instance().saveAs(); return true; } @@ -8100,12 +8106,39 @@ class LLAdvancedClickGLTFDecompose : public view_listener_t { bool handleEvent(const LLSD& userdata) { - // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools) LL::GLTFSceneManager::instance().decomposeSelection(); return true; } }; +class LLAdvancedClickGLTFUpload: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LL::GLTFSceneManager::instance().uploadSelection(); + return true; + } +}; + +class LLAdvancedClickResizeWindow : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + S32 w = 0; + S32 h = 0; + + sscanf(userdata.asString().c_str(), "%dx%d", &w, &h); + + if (w > 0 && h > 0) + { + gViewerWindow->getWindow()->setSize(LLCoordWindow(w, h)); + } + + return true; + } +}; + + // these are used in the gl menus to set control values that require shader recompilation class LLToggleShaderControl : public view_listener_t { @@ -9761,6 +9794,8 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen"); view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs"); view_listener_t::addMenu(new LLAdvancedClickGLTFDecompose(), "Advanced.ClickGLTFDecompose"); + view_listener_t::addMenu(new LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload"); + view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow"); view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache"); view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); @@ -10065,6 +10100,7 @@ void initialize_menus() commit.add("Pathfinding.Characters.Select", boost::bind(&LLFloaterPathfindingCharacters::openCharactersWithSelectedObjects)); enable.add("EnableSelectInPathfindingCharacters", boost::bind(&enable_object_select_in_pathfinding_characters)); enable.add("Advanced.EnableErrorOSException", boost::bind(&enable_os_exception)); + enable.add("EnableGLTF", boost::bind(&enable_gltf)); view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible"); view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel"); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 7b60dc754f..654f2aa1de 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4296,7 +4296,9 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const LLMatrix4a asset_to_agent = getGLTFAssetToAgentTransform(); LLMatrix4a node_to_agent; - matMul(node.mAssetMatrix, asset_to_agent, node_to_agent); + LLMatrix4a am; + am.loadu(glm::value_ptr(node.mAssetMatrix)); + matMul(am, asset_to_agent, node_to_agent); mat = node_to_agent; } @@ -4307,6 +4309,7 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const return mat; } + void LLViewerObject::getGLTFNodeTransformAgent(S32 node_index, LLVector3* position, LLQuaternion* rotation, LLVector3* scale) const { LLMatrix4a node_to_agent = getGLTFNodeTransformAgent(node_index); @@ -4354,7 +4357,9 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion if (node.mParent != -1) { auto& parent = mGLTFAsset->mNodes[node.mParent]; - matMul(agent_to_asset, parent.mAssetMatrixInv, agent_to_node); + LLMatrix4a ami; + ami.loadu(glm::value_ptr(parent.mAssetMatrixInv)); + matMul(agent_to_asset, ami, agent_to_node); } LLQuaternion agent_to_node_rot(agent_to_node.asMatrix4()); @@ -4366,9 +4371,13 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion LLVector3 pos; LLQuaternion rot; LLVector3 scale; - decomposeMatrix(node.mMatrix, pos, rot, scale); + LLMatrix4a mat; + mat.loadu(glm::value_ptr(node.mMatrix)); + decomposeMatrix(mat, pos, rot, scale); + + mat.asMatrix4().initAll(scale, new_rot, pos); - node.mMatrix.asMatrix4().initAll(scale, new_rot, pos); + node.mMatrix = glm::make_mat4(mat.getF32ptr()); mGLTFAsset->updateTransforms(); } @@ -4382,7 +4391,9 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset) LLMatrix4a agent_to_asset = getAgentToGLTFAssetTransform(); LLMatrix4a agent_to_node; - matMul(agent_to_asset, node.mAssetMatrixInv, agent_to_node); + LLMatrix4a ami; + ami.loadu(glm::value_ptr(node.mAssetMatrixInv)); + matMul(agent_to_asset, ami, agent_to_node); LLVector4a origin = LLVector4a::getZero(); LLVector4a offset_v; @@ -4399,7 +4410,12 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset) trans.setIdentity(); trans.mMatrix[3] = offset_v; - matMul(trans, node.mMatrix, node.mMatrix); + LLMatrix4a mat; + mat.loadu(glm::value_ptr(node.mMatrix)); + + matMul(trans, mat, mat); + + node.mMatrix = glm::make_mat4(mat.getF32ptr()); // TODO -- only update transforms for this node and its children (or use a dirty flag) mGLTFAsset->updateTransforms(); @@ -7490,6 +7506,23 @@ void LLViewerObject::shrinkWrap() } } +void LLViewerObject::setGLTFAsset(const LLUUID& id) +{ + //get the sculpt params and set the sculpt type and id + auto* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_SCULPT); + + LLSculptParams* sculpt_params = (LLSculptParams*)param->data; + sculpt_params->setSculptTexture(id, LL_SCULPT_TYPE_GLTF); + + setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, true, true); + + // Update the volume + LLVolumeParams volume_params; + volume_params.setSculptID(id, LL_SCULPT_TYPE_GLTF); + updateVolume(volume_params); +} + + class ObjectPhysicsProperties : public LLHTTPNode { public: diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 32e1a495c4..81b4952a39 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -1,3 +1,4 @@ + /** * @file llviewerobject.h * @brief Description of LLViewerObject class, which is the base class for most objects in the viewer. @@ -742,6 +743,11 @@ public: F32 mPhysicsDensity; F32 mPhysicsRestitution; + // set the GLTF asset for this LLViewerObject to the specified asset id + // id MUST be for a GLTF asset (LLAssetType::AT_GLTF) + // will relesae any currently held references to a GLTF asset on id change + void setGLTFAsset(const LLUUID& id); + // Associated GLTF Asset std::shared_ptr<LL::GLTF::Asset> mGLTFAsset; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 5c44913b29..bf4bdfd252 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2462,24 +2462,14 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features) gSavedSettings.setS32("max_texture_dimension_Y", 1024); } - if (features.has("PBRTerrainEnabled")) + if (features.has("GLTFEnabled")) { - bool enabled = features["PBRTerrainEnabled"]; - gSavedSettings.setBOOL("RenderTerrainPBREnabled", enabled); + bool enabled = features["GLTFEnabled"]; + gSavedSettings.setBOOL("GLTFEnabled", enabled); } else { - gSavedSettings.setBOOL("RenderTerrainPBREnabled", false); - } - - if (features.has("PBRMaterialSwatchEnabled")) - { - bool enabled = features["PBRMaterialSwatchEnabled"]; - gSavedSettings.setBOOL("UIPreviewMaterial", enabled); - } - else - { - gSavedSettings.setBOOL("UIPreviewMaterial", false); + gSavedSettings.setBOOL("GLTFEnabled", false); } }; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 8108aea06f..8e91354bb0 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -89,6 +89,7 @@ #include "llsculptidsize.h" #include "llavatarappearancedefines.h" #include "llgltfmateriallist.h" +#include "gltfscenemanager.h" const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; const F32 FORCE_CULL_AREA = 8.f; @@ -1133,6 +1134,11 @@ bool LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo } } + if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF) + { // notify GLTFSceneManager about new GLTF object + LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID()); + } + return true; } else if (NO_LOD == lod) @@ -1407,6 +1413,12 @@ bool LLVOVolume::calcLOD() return false; } + if (mGLTFAsset != nullptr) + { + // do not calculate LOD for GLTF objects + return false; + } + S32 cur_detail = 0; F32 radius; @@ -5623,7 +5635,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LLVOVolume* vobj = drawablep->getVOVolume(); - if (!vobj || vobj->isDead()) + if (!vobj || vobj->isDead() || vobj->mGLTFAsset) { continue; } diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index a37a1be0f4..8c9561fe64 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2866,21 +2866,69 @@ function="World.EnvPreset" <menu_item_call label="Open..." name="Open..."> + <menu_item_call.on_enable + function="EnableGLTF"/> <menu_item_call.on_click function="Advanced.ClickGLTFOpen" /> </menu_item_call> <menu_item_call label="Save As..." name="Save As..."> + <menu_item_call.on_enable + function="EnableGLTF"/> <menu_item_call.on_click function="Advanced.ClickGLTFSaveAs" /> </menu_item_call> <menu_item_call label="Decompose..." name="Decompose..."> + <menu_item_call.on_enable + function="EnableGLTF"/> <menu_item_call.on_click function="Advanced.ClickGLTFDecompose" /> </menu_item_call> + <menu_item_call + label="Upload..." + name="Upload..."> + <menu_item_call.on_enable + function="EnableGLTF"/> + <menu_item_call.on_click + function="Advanced.ClickGLTFUpload" /> + </menu_item_call> + </menu> + <menu + create_jump_keys="true" + label="Video" + name="Video" + tear_off="true"> + <menu_item_call + label="1080x1920" + name="1080x1920"> + <menu_item_call.on_click + function="Advanced.ClickResizeWindow" + parameter="1080x1920"/> + </menu_item_call> + <menu_item_call + label="1920x1080" + name="1920x1080"> + <menu_item_call.on_click + function="Advanced.ClickResizeWindow" + parameter="1920x1080"/> + </menu_item_call> + <menu_item_call + label="1280x720" + name="1280x720"> + <menu_item_call.on_click + function="Advanced.ClickResizeWindow" + parameter="1280x720"/> + </menu_item_call> + <menu_item_call + label="720x1280" + name="720x1280"> + <menu_item_call.on_click + function="Advanced.ClickResizeWindow" + parameter="720x1280"/> + </menu_item_call> </menu> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index a0f58550cc..e3b9f744e9 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12481,4 +12481,26 @@ are wearing now. yestext="OK"/> </notification> + <notification + icon="alertmodal.tga" + name="GLTFUploadSelection" + type="alert"> + You must select an object that has local-only GLTF asset associated with it. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="GLTFUploadInProgress" + type="alert"> + Upload is currently in progress. Please try again later. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + </notifications> |