diff options
Diffstat (limited to 'indra/newview')
27 files changed, 2567 insertions, 396 deletions
| diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c3f9a64fd6..34c1c07cd3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -17955,5 +17955,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 5a62313705..1d707cbeba 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -32,9 +32,16 @@  #include "accessor.h"  #include "primitive.h"  #include "animation.h" +#include "boost/json.hpp" +#include "common.h"  extern F32SecondsImplicit		gFrameTimeSeconds; +// wingdi defines OPAQUE, which conflicts with our enum +#if defined(OPAQUE) +#undef OPAQUE +#endif +  // LL GLTF Implementation  namespace LL  { @@ -45,13 +52,26 @@ namespace LL          class Material          {          public: + +            enum class AlphaMode +            { +                OPAQUE, +                MASK, +                BLEND +            }; +              class TextureInfo              {              public:                  S32 mIndex = INVALID_INDEX;                  S32 mTexCoord = 0; +                bool operator==(const TextureInfo& rhs) const; +                bool operator!=(const TextureInfo& rhs) const; +                                  const TextureInfo& operator=(const tinygltf::TextureInfo& src); +                const TextureInfo& operator=(const Value& src); +                void serialize(boost::json::object& dst) const;              };              class NormalTextureInfo : public TextureInfo @@ -60,6 +80,8 @@ namespace LL                  F32 mScale = 1.0f;                  const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src); +                const NormalTextureInfo& operator=(const Value& src); +                void serialize(boost::json::object& dst) const;              };              class OcclusionTextureInfo : public TextureInfo @@ -68,17 +90,24 @@ namespace LL                  F32 mStrength = 1.0f;                  const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src); +                const OcclusionTextureInfo& operator=(const Value& src); +                void serialize(boost::json::object& dst) const;              };              class PbrMetallicRoughness              {              public: -                glh::vec4f mBaseColorFactor = glh::vec4f(1.f,1.f,1.f,1.f); +                vec4 mBaseColorFactor = vec4(1.f,1.f,1.f,1.f);                  TextureInfo mBaseColorTexture;                  F32 mMetallicFactor = 1.0f;                  F32 mRoughnessFactor = 1.0f;                  TextureInfo mMetallicRoughnessTexture; + +                bool operator==(const PbrMetallicRoughness& rhs) const; +                bool operator!=(const PbrMetallicRoughness& rhs) const;                  const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src); +                const PbrMetallicRoughness& operator=(const Value& src); +                void serialize(boost::json::object& dst) const;              }; @@ -93,13 +122,16 @@ namespace LL              std::string mName; -            glh::vec3f mEmissiveFactor = glh::vec3f(0.f, 0.f, 0.f); -            std::string mAlphaMode = "OPAQUE"; +            vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f); +            AlphaMode mAlphaMode = AlphaMode::OPAQUE;              F32 mAlphaCutoff = 0.5f;              bool mDoubleSided = false; - +            // bind for rendering +            void bind(Asset& asset);              const Material& operator=(const tinygltf::Material& src); +            const Material& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;              void allocateGLResources(Asset& asset);          }; @@ -112,6 +144,8 @@ namespace LL              std::string mName;              const Mesh& operator=(const tinygltf::Mesh& src); +            const Mesh& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;              void allocateGLResources(Asset& asset);          }; @@ -119,14 +153,14 @@ namespace LL          class Node          {          public: -            LLMatrix4a mMatrix; //local transform -            LLMatrix4a mRenderMatrix; //transform for rendering -            LLMatrix4a mAssetMatrix; //transform from local to asset space -            LLMatrix4a mAssetMatrixInv; //transform from asset to local space +            mat4 mMatrix = glm::identity<mat4>(); //local transform +            mat4 mRenderMatrix; //transform for rendering +            mat4 mAssetMatrix; //transform from local to asset space +            mat4 mAssetMatrixInv; //transform from asset to local space -            glh::vec3f mTranslation; -            glh::quaternionf mRotation; -            glh::vec3f mScale; +            vec3  mTranslation = vec3(0,0,0); +            quat mRotation = glm::identity<quat>(); +            vec3 mScale = vec3(1.f,1.f,1.f);              // if true, mMatrix is valid and up to date              bool mMatrixValid = false; @@ -145,13 +179,15 @@ namespace LL              std::string mName;              const Node& operator=(const tinygltf::Node& src); +            const Node& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;              // Set mRenderMatrix to a transform that can be used for the current render pass              // modelview -- parent's render matrix -            void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); +            void updateRenderTransforms(Asset& asset, const mat4& modelview);              // update mAssetMatrix and mAssetMatrixInv -            void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix); +            void updateTransforms(Asset& asset, const mat4& parentMatrix);              // ensure mMatrix is valid -- if mMatrixValid is false and mTRSValid is true, will update mMatrix to match Translation/Rotation/Scale              void makeMatrixValid(); @@ -161,15 +197,15 @@ namespace LL              // Set rotation of this node              // SIDE EFFECT: invalidates mMatrix -            void setRotation(const glh::quaternionf& rotation); +            void setRotation(const quat& rotation);              // Set translation of this node              // SIDE EFFECT: invalidates mMatrix -            void setTranslation(const glh::vec3f& translation); +            void setTranslation(const vec3& translation);              // Set scale of this node              // SIDE EFFECT: invalidates mMatrix -            void setScale(const glh::vec3f& scale); +            void setScale(const vec3& scale);          };          class Skin @@ -179,12 +215,14 @@ namespace LL              S32 mSkeleton = INVALID_INDEX;              std::vector<S32> mJoints;              std::string mName; -            std::vector<glh::matrix4f> mInverseBindMatricesData; +            std::vector<mat4> mInverseBindMatricesData;              void allocateGLResources(Asset& asset);              void uploadMatrixPalette(Asset& asset, Node& node);              const Skin& operator=(const tinygltf::Skin& src); +            const Skin& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;          };          class Scene @@ -194,9 +232,11 @@ namespace LL              std::string mName;              const Scene& operator=(const tinygltf::Scene& src); -             +            const Scene& operator=(const Value& src); +            void serialize(boost::json::object& dst) const; +              void updateTransforms(Asset& asset); -            void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview); +            void updateRenderTransforms(Asset& asset, const mat4& modelview);          };          class Texture @@ -207,18 +247,22 @@ namespace LL              std::string mName;              const Texture& operator=(const tinygltf::Texture& src); +            const Texture& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;          };          class Sampler          {          public: -            S32 mMagFilter; -            S32 mMinFilter; -            S32 mWrapS; -            S32 mWrapT; +            S32 mMagFilter = LINEAR; +            S32 mMinFilter = LINEAR_MIPMAP_LINEAR; +            S32 mWrapS = REPEAT; +            S32 mWrapT = REPEAT;              std::string mName;              const Sampler& operator=(const tinygltf::Sampler& src); +            const Sampler& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;          };          class Image @@ -231,43 +275,35 @@ namespace LL              S32 mBufferView = INVALID_INDEX;              std::vector<U8> mData; -            S32 mWidth; -            S32 mHeight; -            S32 mComponent; -            S32 mBits; -            S32 mPixelType; +            S32 mWidth = -1; +            S32 mHeight = -1; +            S32 mComponent = -1; +            S32 mBits = -1; +            S32 mPixelType = -1;              LLPointer<LLViewerFetchedTexture> mTexture; -            const Image& operator=(const tinygltf::Image& src) -            { -                mName = src.name; -                mUri = src.uri; -                mMimeType = src.mimeType; -                mData = src.image; -                mWidth = src.width; -                mHeight = src.height; -                mComponent = src.component; -                mBits = src.bits; -                mBufferView = src.bufferView; -                mPixelType = src.pixel_type; -                return *this; -            } - +            const Image& operator=(const tinygltf::Image& src); +            const Image& operator=(const Value& src); +            void serialize(boost::json::object& dst) const; +                          // save image clear local data, and set uri              void decompose(Asset& asset, const std::string& filename); -            void allocateGLResources() -            { -                // allocate texture +            // erase the buffer view associated with this image +            // free any associated resources +            // preserve only uri and name +            void clearData(Asset& asset); -            } +            void allocateGLResources();          };          // C++ representation of a GLTF Asset          class Asset          {          public: + +            static const std::string minVersion_default;              std::vector<Scene> mScenes;              std::vector<Node> mNodes;              std::vector<Mesh> mMeshes; @@ -287,14 +323,15 @@ namespace LL              std::string mCopyright;              S32 mDefaultScene = INVALID_INDEX; -            tinygltf::Value mExtras; +            Value mExtras; +            U32 mPendingBuffers = 0;              // the last time update() was called according to gFrameTimeSeconds              F32 mLastUpdateTime = gFrameTimeSeconds;              // prepare the asset for rendering -            void allocateGLResources(const std::string& filename, const tinygltf::Model& model); +            void allocateGLResources(const std::string& filename = "", const tinygltf::Model& model = tinygltf::Model());              // Called periodically (typically once per frame)              // Any ongoing work (such as animations) should be handled here @@ -307,7 +344,7 @@ namespace LL              void updateTransforms();              // update node render transforms -            void updateRenderTransforms(const LLMatrix4a& modelview); +            void updateRenderTransforms(const mat4& modelview);              void render(bool opaque, bool rigged = false);              void renderOpaque(); @@ -323,7 +360,13 @@ namespace LL                  S32* primitive_hitp = nullptr           // return the index of the primitive that was hit              ); +            Asset() = default; +            Asset(const tinygltf::Model& src); +            Asset(const Value& src); +              const Asset& operator=(const tinygltf::Model& src); +            const Asset& operator=(const Value& src); +            void serialize(boost::json::object& dst) const;              // save the asset to a tinygltf model              void save(tinygltf::Model& dst); @@ -335,5 +378,8 @@ namespace LL              // updates all bufferview indices in this Asset as needed              void eraseBufferView(S32 bufferView);          }; + +        Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode); +        std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode);      }  } diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h index 4e6f5901e7..b0fbc8524d 100644 --- a/indra/newview/gltf/buffer_util.h +++ b/indra/newview/gltf/buffer_util.h @@ -36,55 +36,60 @@  #define LL_FUNCSIG __PRETTY_FUNCTION__  #endif +#include "accessor.h" +  namespace LL  {      namespace GLTF      { + +        using string_view = boost::json::string_view; +          // copy one Scalar from src to dst          template<class S, class T> -        static void copyScalar(S* src, T& dst) +        inline void copyScalar(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          }          // copy one vec2 from src to dst          template<class S, class T> -        static void copyVec2(S* src, T& dst) +        inline void copyVec2(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          }          // copy one vec3 from src to dst          template<class S, class T> -        static void copyVec3(S* src, T& dst) +        inline void copyVec3(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          }          // copy one vec4 from src to dst          template<class S, class T> -        static void copyVec4(S* src, T& dst) +        inline void copyVec4(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          } -        // copy one vec2 from src to dst +        // copy one mat2 from src to dst          template<class S, class T> -        static void copyMat2(S* src, T& dst) +        inline void copyMat2(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          } -        // copy one vec3 from src to dst +        // copy one mat3 from src to dst          template<class S, class T> -        static void copyMat3(S* src, T& dst) +        inline void copyMat3(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          } -        // copy one vec4 from src to dst +        // copy one mat4 from src to dst          template<class S, class T> -        static void copyMat4(S* src, T& dst) +        inline void copyMat4(S* src, T& dst)          {              LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;          } @@ -93,135 +98,128 @@ namespace LL          // concrete implementations for different types of source and destination          //========================================================================================================= -// suppress unused function warning -- clang complains here but these specializations are definitely used -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#endif -          template<> -        void copyScalar<F32, F32>(F32* src, F32& dst) +        inline void copyScalar<F32, F32>(F32* src, F32& dst)          {              dst = *src;          }          template<> -        void copyScalar<U32, U32>(U32* src, U32& dst) +        inline void copyScalar<U32, U32>(U32* src, U32& dst)          {              dst = *src;          }          template<> -        void copyScalar<U32, U16>(U32* src, U16& dst) +        inline void copyScalar<U32, U16>(U32* src, U16& dst)          {              dst = *src;          }          template<> -        void copyScalar<U16, U16>(U16* src, U16& dst) +        inline void copyScalar<U16, U16>(U16* src, U16& dst)          {              dst = *src;          }          template<> -        void copyScalar<U16, U32>(U16* src, U32& dst) +        inline void copyScalar<U16, U32>(U16* src, U32& dst)          {              dst = *src;          }          template<> -        void copyScalar<U8, U16>(U8* src, U16& dst) +        inline void copyScalar<U8, U16>(U8* src, U16& dst)          {              dst = *src;          }          template<> -        void copyScalar<U8, U32>(U8* src, U32& dst) +        inline void copyScalar<U8, U32>(U8* src, U32& dst)          {              dst = *src;          }          template<> -        void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst) +        inline void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst)          {              dst.set(src[0], src[1]);          }          template<> -        void copyVec3<F32, glh::vec3f>(F32* src, glh::vec3f& dst) +        inline void copyVec3<F32, vec3>(F32* src, vec3& dst)          { -            dst.set_value(src[0], src[1], src[2]); +            dst = vec3(src[0], src[1], src[2]);          }          template<> -        void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst) +        inline void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst)          {              dst.load3(src);          }          template<> -        void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst) +        inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)          {              dst.set(src[0], src[1], src[2], 255);          }          template<> -        void copyVec4<U8, LLColor4U>(U8* src, LLColor4U& dst) +        inline void copyVec4<U8, LLColor4U>(U8* src, LLColor4U& dst)          {              dst.set(src[0], src[1], src[2], src[3]);          }          template<> -        void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst) +        inline void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst)          {              dst.set(src[0], src[1], src[2], src[3]);          }          template<> -        void copyVec4<F32, LLColor4U>(F32* src, LLColor4U& dst) +        inline void copyVec4<F32, LLColor4U>(F32* src, LLColor4U& dst)          {              dst.set(src[0]*255, src[1]*255, src[2]*255, src[3]*255);          }          template<> -        void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst) +        inline void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst)          {              dst.loadua(src);          }          template<> -        void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst) +        inline void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst)          {              dst.set(src[0], src[1], src[2], src[3]);          }          template<> -        void copyVec4<U8, LLVector4a>(U8* src, LLVector4a& dst) +        inline void copyVec4<U8, LLVector4a>(U8* src, LLVector4a& dst)          {              dst.set(src[0], src[1], src[2], src[3]);          }          template<> -        void copyVec4<F32, glh::quaternionf>(F32* src, glh::quaternionf& dst) +        inline void copyVec4<F32, quat>(F32* src, quat& dst)          { -            dst.set_value(src); +            dst.x = src[0]; +            dst.y = src[1]; +            dst.z = src[2]; +            dst.w = src[3];          }          template<> -        void copyMat4<F32, glh::matrix4f>(F32* src, glh::matrix4f& dst) +        inline void copyMat4<F32, mat4>(F32* src, mat4& dst)          { -            dst.set_value(src); +            dst = glm::make_mat4(src);          } -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -          //=========================================================================================================          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyScalar(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyScalar(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -233,7 +231,7 @@ namespace LL          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -245,7 +243,7 @@ namespace LL          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -257,7 +255,7 @@ namespace LL          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -269,7 +267,7 @@ namespace LL          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyMat2(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyMat2(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -281,7 +279,7 @@ namespace LL          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyMat3(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyMat3(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -293,7 +291,7 @@ namespace LL          // copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy          template<class S, class T> -        static void copyMat4(S* src, LLStrider<T> dst, S32 stride, S32 count) +        inline void copyMat4(S* src, LLStrider<T> dst, S32 stride, S32 count)          {              for (S32 i = 0; i < count; ++i)              { @@ -304,39 +302,39 @@ namespace LL          }          template<class S, class T> -        static void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride) +        inline void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride)          { -            if (accessor.mType == (S32)Accessor::Type::SCALAR) +            if (accessor.mType == Accessor::Type::SCALAR)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 1 : byteStride;                  copyScalar((S*)src, dst, stride, accessor.mCount);              } -            else if (accessor.mType == (S32)Accessor::Type::VEC2) +            else if (accessor.mType == Accessor::Type::VEC2)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride;                  copyVec2((S*)src, dst, stride, accessor.mCount);              } -            else if (accessor.mType == (S32)Accessor::Type::VEC3) +            else if (accessor.mType == Accessor::Type::VEC3)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride;                  copyVec3((S*)src, dst, stride, accessor.mCount);              } -            else if (accessor.mType == (S32)Accessor::Type::VEC4) +            else if (accessor.mType == Accessor::Type::VEC4)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;                  copyVec4((S*)src, dst, stride, accessor.mCount);              } -            else if (accessor.mType == (S32)Accessor::Type::MAT2) +            else if (accessor.mType == Accessor::Type::MAT2)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;                  copyMat2((S*)src, dst, stride, accessor.mCount);              } -            else if (accessor.mType == (S32)Accessor::Type::MAT3) +            else if (accessor.mType == Accessor::Type::MAT3)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 9 : byteStride;                  copyMat3((S*)src, dst, stride, accessor.mCount);              } -            else if (accessor.mType == (S32)Accessor::Type::MAT4) +            else if (accessor.mType == Accessor::Type::MAT4)              {                  S32 stride = byteStride == 0 ? sizeof(S) * 16 : byteStride;                  copyMat4((S*)src, dst, stride, accessor.mCount); @@ -349,7 +347,7 @@ namespace LL          // copy data from accessor to strider          template<class T> -        static void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst) +        inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)          {              const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];              const Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; @@ -391,12 +389,504 @@ namespace LL          // copy data from accessor to vector          template<class T> -        static void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst) +        inline void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst)          {              dst.resize(accessor.mCount);              LLStrider<T> strider = dst.data();              copy(asset, accessor, strider);          } + + +        //========================================================================================================= +        // boost::json copying utilities +        // ======================================================================================================== +         +        //====================== unspecialized base template, single value =========================== + +        // to/from Value +        template<typename T> +        inline bool copy(const Value& src, T& dst) +        { +            dst = src; +            return true; +        } + +        template<typename T> +        inline bool write(const T& src, Value& dst) +        { +            dst = boost::json::object(); +            src.serialize(dst.as_object()); +            return true; +        } + +        template<typename T> +        inline bool copy(const Value& src, std::unordered_map<std::string, T>& dst) +        { +            if (src.is_object()) +            { +                const boost::json::object& obj = src.as_object(); +                for (const auto& [key, value] : obj) +                { +                    copy<T>(value, dst[key]); +                } +                return true; +            } +            return false; +        } + +        template<typename T> +        inline bool write(const std::unordered_map<std::string, T>& src, Value& dst) +        { +            boost::json::object obj; +            for (const auto& [key, value] : src) +            { +                Value v; +                if (write<T>(value, v)) +                { +                    obj[key] = v; +                } +                else +                { +                    return false; +                } +            } +            dst = obj; +            return true; +        } + +        // to/from array +        template<typename T> +        inline bool copy(const Value& src, std::vector<T>& dst) +        { +            if (src.is_array()) +            { +                const boost::json::array& arr = src.get_array(); +                dst.resize(arr.size()); +                for (size_t i = 0; i < arr.size(); ++i) +                { +                    copy(arr[i], dst[i]); +                } +                return true; +            } + +            return false; +        } + +        template<typename T> +        inline bool write(const std::vector<T>& src, Value& dst) +        { +            boost::json::array arr; +            for (const T& t : src) +            { +                Value v; +                if (write(t, v)) +                { +                    arr.push_back(v); +                } +                else +                { +                    return false; +                } +            } +            dst = arr; +            return true; +        } + +        // to/from object member +        template<typename T> +        inline bool copy(const boost::json::object& src, string_view member, T& dst) +        { +            auto it = src.find(member); +            if (it != src.end()) +            { +                return copy(it->value(), dst); +            } +            return false; +        } + +        // always write a member to an object without checking default +        template<typename T> +        inline bool write_always(const T& src, string_view member, boost::json::object& dst) +        { +            Value& v = dst[member]; +            if (!write(src, v)) +            { +                dst.erase(member); +                return false; +            } +            return true; +        } + +        // conditionally write a member to an object if the member +        // is not the default value +        template<typename T> +        inline bool write(const T& src, string_view member, boost::json::object& dst, const T& default_value = T()) +        { +            if (src != default_value) +            { +                return write_always(src, member, dst); +            } +            return false; +        } +         +        template<typename T> +        inline bool write(const std::unordered_map<std::string, T>& src, string_view member, boost::json::object& dst, const std::unordered_map<std::string, T>& default_value = std::unordered_map<std::string, T>()) +        { +            if (!src.empty()) +            { +                Value v; +                if (write<T>(src, v)) +                { +                    dst[member] = v; +                    return true; +                } +            } +            return false; +        } + +        template<typename T> +        inline bool write(const std::vector<T>& src, string_view member, boost::json::object& dst, const std::vector<T>& deafault_value = std::vector<T>()) +        { +            if (!src.empty()) +            { +                Value v; +                if (write(src, v)) +                { +                    dst[member] = v; +                    return true; +                } +            } +            return false; +        } + +        template<typename T> +        inline bool copy(const Value& src, string_view member, T& dst) +        { +            if (src.is_object()) +            { +                const boost::json::object& obj = src.as_object(); +                return copy(obj, member, dst); +            } + +            return false; +        } + +        // vec4 +        template<> +        inline bool copy(const Value& src, vec4& dst) +        { +            if (src.is_array()) +            { +                const boost::json::array& arr = src.as_array(); +                if (arr.size() == 4) +                { +                    if (arr[0].is_double() && +                        arr[1].is_double() && +                        arr[2].is_double() && +                        arr[3].is_double()) +                    { +                        dst = vec4(arr[0].get_double(), arr[1].get_double(), arr[2].get_double(), arr[3].get_double()); +                        return true; +                    } +                } +            } +            return false; +        } + +        template<> +        inline bool write(const vec4& src, Value& dst) +        { +            dst = boost::json::array(); +            boost::json::array& arr = dst.get_array(); +            arr.resize(4); +            arr[0] = src.x; +            arr[1] = src.y; +            arr[2] = src.z; +            arr[3] = src.w; +            return true; +        } + +        // quat +        template<> +        inline bool copy(const Value& src, quat& dst) +        { +            if (src.is_array()) +            { +                const boost::json::array& arr = src.as_array(); +                if (arr.size() == 4) +                { +                    if (arr[0].is_double() && +                        arr[1].is_double() && +                        arr[2].is_double() && +                        arr[3].is_double()) +                    { +                        dst.x = arr[0].get_double(); +                        dst.y = arr[1].get_double(); +                        dst.z = arr[2].get_double(); +                        dst.w = arr[3].get_double(); +                        return true; +                    } +                } +            } +            return false; +        } + +        template<> +        inline bool write(const quat& src, Value& dst) +        { +            dst = boost::json::array(); +            boost::json::array& arr = dst.get_array(); +            arr.resize(4); +            arr[0] = src.x; +            arr[1] = src.y; +            arr[2] = src.z; +            arr[3] = src.w; +            return true; +        } + + +        // vec3 +        template<> +        inline bool copy(const Value& src, vec3& dst) +        { +            if (src.is_array()) +            { +                const boost::json::array& arr = src.as_array(); +                if (arr.size() == 3) +                { +                    if (arr[0].is_double() && +                        arr[1].is_double() && +                        arr[2].is_double()) +                    { +                        dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double()); +                    } +                    return true; +                } +            } +            return false; +        } + +        template<> +        inline bool write(const vec3& src, Value& dst) +        { +            dst = boost::json::array(); +            boost::json::array& arr = dst.as_array(); +            arr.resize(3); +            arr[0] = src.x; +            arr[1] = src.y; +            arr[2] = src.z; +            return true; +        } + +        // bool +        template<> +        inline bool copy(const Value& src, bool& dst) +        { +            if (src.is_bool()) +            { +                dst = src.get_bool(); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const bool& src, Value& dst) +        { +            dst = src; +            return true; +        } + +        // F32 +        template<> +        inline bool copy(const Value& src, F32& dst) +        { +            if (src.is_double()) +            { +                dst = src.get_double(); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const F32& src, Value& dst) +        { +            dst = src; +            return true; +        } + + +        // U32 +        template<> +        inline bool copy(const Value& src, U32& dst) +        { +            if (src.is_int64()) +            { +                dst = src.get_int64(); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const U32& src, Value& dst) +        { +            dst = src; +            return true; +        } + +        // F64 +        template<> +        inline bool copy(const Value& src, F64& dst) +        { +            if (src.is_double()) +            { +                dst = src.get_double(); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const F64& src, Value& dst) +        { +            dst = src; +            return true; +        } + +        // Accessor::Type +        template<> +        inline bool copy(const Value& src, Accessor::Type& dst) +        { +            if (src.is_string()) +            { +                dst = gltf_type_to_enum(src.get_string().c_str()); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const Accessor::Type& src, Value& dst) +        { +            dst = enum_to_gltf_type(src); +            return true; +        } + +        // S32 +        template<> +        inline bool copy(const Value& src, S32& dst) +        { +            if (src.is_int64()) +            { +                dst = src.get_int64(); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const S32& src, Value& dst) +        { +            dst = src; +            return true; +        } + + +        // std::string +        template<> +        inline bool copy(const Value& src, std::string& dst) +        { +            if (src.is_string()) +            { +                dst = src.get_string().c_str(); +                return true; +            } +            return false; +        } + +        template<> +        inline bool write(const std::string& src, Value& dst) +        { +            dst = src; +            return true; +        } + +        // mat4 +        template<> +        inline bool copy(const Value& src, mat4& dst) +        { +            if (src.is_array()) +            { +                const boost::json::array& arr = src.get_array(); +                if (arr.size() == 16) +                { +                    // populate a temporary local in case +                    // we hit an error in the middle of the array +                    // (don't partially write a matrix) +                    mat4 t; +                    F32* p = glm::value_ptr(t); + +                    for (U32 i = 0; i < arr.size(); ++i) +                    { +                        if (arr[i].is_double()) +                        { +                            p[i] = arr[i].get_double(); +                        } +                        else +                        { +                            return false; +                        } +                    } + +                    dst = t; +                    return true; +                } +            } + +            return false; +        } + +        template<> +        inline bool write(const mat4& src, Value& dst) +        { +            dst = boost::json::array(); +            boost::json::array& arr = dst.get_array(); +            arr.resize(16); +            const F32* p = glm::value_ptr(src); +            for (U32 i = 0; i < 16; ++i) +            { +                arr[i] = p[i]; +            } +            return true; +        } + +        // Material::AlphaMode +        template<> +        inline bool copy(const Value& src, Material::AlphaMode& dst) +        { +            if (src.is_string()) +            { +                dst = gltf_alpha_mode_to_enum(src.get_string().c_str()); +                return true; +            } +            return true; +        } + +        template<> +        inline bool write(const Material::AlphaMode& src, Value& dst) +        { +            dst = enum_to_gltf_alpha_mode(src); +            return true; +        } + +        //  +        // ======================================================================================================== +      }  } + + + diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h new file mode 100644 index 0000000000..859e202738 --- /dev/null +++ b/indra/newview/gltf/common.h @@ -0,0 +1,66 @@ +#pragma once + +/** + * @file common.h + * @brief LL GLTF Implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#define GLM_ENABLE_EXPERIMENTAL 1 + +#include "glm/vec2.hpp" +#include "glm/vec3.hpp" +#include "glm/vec4.hpp" +#include "glm/mat4x4.hpp" +#include "glm/gtc/type_ptr.hpp" +#include "glm/ext/quaternion_float.hpp" +#include "glm/gtx/quaternion.hpp" +#include "glm/gtx/matrix_decompose.hpp" + +// Common types and constants used in the GLTF implementation +namespace LL +{ +    namespace GLTF +    { +        using Value = boost::json::value; + +        using mat4 = glm::mat4; +        using vec4 = glm::vec4; +        using vec3 = glm::vec3; +        using vec2 = glm::vec2; +        using quat = glm::quat; + +        constexpr S32 LINEAR = 9729; +        constexpr S32 NEAREST = 9728; +        constexpr S32 NEAREST_MIPMAP_NEAREST = 9984; +        constexpr S32 LINEAR_MIPMAP_NEAREST = 9985; +        constexpr S32 NEAREST_MIPMAP_LINEAR = 9986; +        constexpr S32 LINEAR_MIPMAP_LINEAR = 9987; +        constexpr S32 CLAMP_TO_EDGE = 33071; +        constexpr S32 MIRRORED_REPEAT = 33648; +        constexpr S32 REPEAT = 10497; + +        class Asset; +    } +} + diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index b57a0af18d..1bde7327e6 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -28,10 +28,12 @@  #include "asset.h"  #include "buffer_util.h" +#include "../llviewershadermgr.h"  #include "../lltinygltfhelper.h"  using namespace LL::GLTF; +using namespace boost::json;  void Primitive::allocateGLResources(Asset& asset)  { @@ -92,6 +94,10 @@ void Primitive::allocateGLResources(Asset& asset)          mask |= LLVertexBuffer::MAP_WEIGHT4;      } +    if (LLGLSLShader::sCurBoundShaderPtr == nullptr) +    { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer +        gDebugProgram.bind(); +    }      mVertexBuffer = new LLVertexBuffer(mask);      mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices @@ -129,7 +135,7 @@ void Primitive::allocateGLResources(Asset& asset)      if (mMaterial != INVALID_INDEX)      {          const Material& material = asset.mMaterials[mMaterial]; -        LLColor4 baseColor = material.mMaterial->mBaseColor; +        LLColor4 baseColor(glm::value_ptr(material.mPbrMetallicRoughness.mBaseColorFactor));          for (auto& dst : mColors)          {              dst = LLColor4U(baseColor * LLColor4(dst)); @@ -351,6 +357,50 @@ Primitive::~Primitive()      mOctree = nullptr;  } +U32 gltf_mode_to_gl_mode(U32 mode) +{ +    switch (mode) +    { +    case TINYGLTF_MODE_POINTS: +        return LLRender::POINTS; +    case TINYGLTF_MODE_LINE: +        return LLRender::LINES; +    case TINYGLTF_MODE_LINE_LOOP: +        return LLRender::LINE_LOOP; +    case TINYGLTF_MODE_LINE_STRIP: +        return LLRender::LINE_STRIP; +    case TINYGLTF_MODE_TRIANGLES: +        return LLRender::TRIANGLES; +    case TINYGLTF_MODE_TRIANGLE_STRIP: +        return LLRender::TRIANGLE_STRIP; +    case TINYGLTF_MODE_TRIANGLE_FAN: +        return LLRender::TRIANGLE_FAN; +    default: +        return LLRender::TRIANGLES; +    } +} + +void Primitive::serialize(boost::json::object& dst) const +{ +    write(mMaterial, "material", dst, -1); +    write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES); +    write(mIndices, "indices", dst, INVALID_INDEX); +    write(mAttributes, "attributes", dst); +} + +const Primitive& Primitive::operator=(const Value& src) +{ +    if (src.is_object()) +    { +        copy(src, "material", mMaterial); +        copy(src, "mode", mMode); +        copy(src, "indices", mIndices); +        copy(src, "attributes", mAttributes); + +        mGLMode = gltf_mode_to_gl_mode(mMode); +    } +    return *this; +}  const Primitive& Primitive::operator=(const tinygltf::Primitive& src)  { @@ -369,32 +419,7 @@ const Primitive& Primitive::operator=(const tinygltf::Primitive& src)          mAttributes[it.first] = it.second;      } -    switch (mMode) -    { -    case TINYGLTF_MODE_POINTS: -        mGLMode = LLRender::POINTS; -        break; -    case TINYGLTF_MODE_LINE: -        mGLMode = LLRender::LINES; -        break; -    case TINYGLTF_MODE_LINE_LOOP: -        mGLMode = LLRender::LINE_LOOP; -        break; -    case TINYGLTF_MODE_LINE_STRIP: -        mGLMode = LLRender::LINE_STRIP; -        break; -    case TINYGLTF_MODE_TRIANGLES: -        mGLMode = LLRender::TRIANGLES; -        break; -    case TINYGLTF_MODE_TRIANGLE_STRIP: -        mGLMode = LLRender::TRIANGLE_STRIP; -        break; -    case TINYGLTF_MODE_TRIANGLE_FAN: -        mGLMode = LLRender::TRIANGLE_FAN; -        break; -    default: -        mGLMode = GL_TRIANGLES; -    } +    mGLMode = gltf_mode_to_gl_mode(mMode);      return *this;  } diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index 07e8e7deb2..18aadce808 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -28,12 +28,14 @@  #include "llvertexbuffer.h"  #include "llvolumeoctree.h" +#include "boost/json.hpp"  // LL GLTF Implementation  namespace LL  {      namespace GLTF      { +        using Value = boost::json::value;          class Asset;          constexpr U32 ATTRIBUTE_MASK = @@ -66,10 +68,10 @@ namespace LL              std::vector<LLVolumeTriangle> mOctreeTriangles;              S32 mMaterial = -1; -            U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles +            S32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles              U32 mGLMode = LLRender::TRIANGLES;              S32 mIndices = -1; -            std::unordered_map<std::string, int> mAttributes; +            std::unordered_map<std::string, S32> mAttributes;              // create octree based on vertex buffer              // must be called before buffer is unmapped and after buffer is populated with good data @@ -85,6 +87,8 @@ namespace LL                  LLVector4a* tangent = NULL             // return the surface tangent at the intersection point              ); +            void serialize(boost::json::object& obj) const; +            const Primitive& operator=(const Value& src);              const Primitive& operator=(const tinygltf::Primitive& src);              void allocateGLResources(Asset& asset); diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 8bbcef1594..ae4d252d34 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,20 +787,24 @@ 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)      {          Mesh& mesh = asset->mMeshes[node.mMesh];          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(); - -        LLMatrix4a modelview; -        modelview.loadu(gGLModelView); +        mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr()); -        matMul(mat, modelview, modelview); +        mat4 modelview = glm::make_mat4(gGLModelView); +         +        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,9 +1002,8 @@ 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);              drawBoxOutline(listener->mBounds[0], listener->mBounds[1]); @@ -643,3 +1016,5 @@ void GLTFSceneManager::renderDebug()      gDebugProgram.unbind();  } + + diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h index 8f29f34c30..a6754d0484 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 44705acb07..ba270949cc 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 b8bef6361f..198fdad616 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 481086f760..b6a9a7c160 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 68ed7633b9..d11cdae482 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -299,9 +299,17 @@ void LLResourceUploadInfo::assignDefaults()          mDescription = "(No Description)";      } -    mFolderId = gInventory.findUserDefinedCategoryUUIDForType( -        (mDestinationFolderType == LLFolderType::FT_NONE) ? -        (LLFolderType::EType)mAssetType : mDestinationFolderType); +    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 f32bbdf3c2..b74fde48c3 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 5f2eafd62e..0b1c6c1329 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) @@ -8047,7 +8055,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;      } @@ -8057,7 +8064,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;      } @@ -8067,12 +8073,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  { @@ -9728,6 +9761,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"); @@ -10032,6 +10067,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 d8f522163c..16d874717a 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4303,7 +4303,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;      } @@ -4314,6 +4316,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); @@ -4361,7 +4364,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()); @@ -4373,9 +4378,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();      } @@ -4389,7 +4398,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; @@ -4406,7 +4417,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(); @@ -7497,6 +7513,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 4da8b25357..f29de6bfc6 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 6668384f52..2cdba5dee1 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2466,24 +2466,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 a4835849eb..46df5e206f 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 9697774fd9..e7898ef146 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2852,21 +2852,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 4ed7c5250a..e6c06d54ce 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12439,4 +12439,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> | 
