diff options
| -rw-r--r-- | indra/llmath/llvolumeoctree.h | 6 | ||||
| -rw-r--r-- | indra/newview/gltf/accessor.cpp | 18 | ||||
| -rw-r--r-- | indra/newview/gltf/accessor.h | 4 | ||||
| -rw-r--r-- | indra/newview/gltf/animation.h | 2 | ||||
| -rw-r--r-- | indra/newview/gltf/asset.cpp | 434 | ||||
| -rw-r--r-- | indra/newview/gltf/asset.h | 81 | ||||
| -rw-r--r-- | indra/newview/gltfscenemanager.cpp | 107 | ||||
| -rw-r--r-- | indra/newview/gltfscenemanager.h | 15 | ||||
| -rw-r--r-- | indra/newview/llfilepicker.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/lltinygltfhelper.cpp | 37 | ||||
| -rw-r--r-- | indra/newview/lltinygltfhelper.h | 1 | ||||
| -rw-r--r-- | indra/newview/llviewermenu.cpp | 26 | ||||
| -rw-r--r-- | indra/newview/llviewerobject.cpp | 11 | ||||
| -rw-r--r-- | indra/newview/llviewerobject.h | 11 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/menu_viewer.xml | 30 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 13 | 
16 files changed, 757 insertions, 41 deletions
| diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index 0bbb793896..1df2bf5390 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -48,12 +48,6 @@ public:  		*this = rhs;  	} -	const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs) -	{ -		LL_ERRS() << "Illegal operation!" << LL_ENDL; -		return *this; -	} -  	~LLVolumeTriangle()  	{ diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp index 55d36b7a32..9bfdc2afa6 100644 --- a/indra/newview/gltf/accessor.cpp +++ b/indra/newview/gltf/accessor.cpp @@ -30,6 +30,24 @@  using namespace LL::GLTF; +void Buffer::erase(Asset& asset, S32 offset, S32 length) +{ +    S32 idx = this - &asset.mBuffers[0]; + +    mData.erase(mData.begin() + offset, mData.begin() + offset + length); + +    for (BufferView& view : asset.mBufferViews) +    { +        if (view.mBuffer == idx) +        { +            if (view.mByteOffset >= offset) +            { +                view.mByteOffset -= length; +            } +        } +    } +} +  const Buffer& Buffer::operator=(const tinygltf::Buffer& src)  {      mData = src.data; diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h index 9b8265d8da..6849cd8609 100644 --- a/indra/newview/gltf/accessor.h +++ b/indra/newview/gltf/accessor.h @@ -45,6 +45,10 @@ namespace LL              std::string mName;              std::string mUri; +            // 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); +              const Buffer& operator=(const tinygltf::Buffer& src);          }; diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h index 869eae963a..cc2045ebb2 100644 --- a/indra/newview/gltf/animation.h +++ b/indra/newview/gltf/animation.h @@ -81,8 +81,6 @@ namespace LL                  S32 mSampler = INVALID_INDEX;                  Target mTarget; -                std::string mTargetPath; -                std::string mName;                  const Channel& operator=(const tinygltf::AnimationChannel& src)                  { diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index 313e82bf01..42f064699c 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -30,9 +30,271 @@  #include "llvolumeoctree.h"  #include "../llviewershadermgr.h"  #include "../llviewercontrol.h" +#include "../llviewertexturelist.h"  using namespace LL::GLTF; +namespace LL +{ +    namespace GLTF +    { +        template <typename T, typename U> +        void copy(const std::vector<T>& src, std::vector<U>& dst) +        { +            dst.resize(src.size()); +            for (U32 i = 0; i < src.size(); ++i) +            { +                copy(src[i], dst[i]); +            } +        } + +        void copy(const Node& src, tinygltf::Node& dst) +        { +            if (src.mMatrixValid) +            { +                if (!src.mMatrix.asMatrix4().isIdentity()) +                { +                    dst.matrix.resize(16); +                    for (U32 i = 0; i < 16; ++i) +                    { +                        dst.matrix[i] = src.mMatrix.getF32ptr()[i]; +                    } +                } +            } +            else if (src.mTRSValid) +            { +                if (!src.mRotation.equals(glh::quaternionf::identity(), FLT_EPSILON)) +                { +                    dst.rotation.resize(4); +                    for (U32 i = 0; i < 4; ++i) +                    { +                        dst.rotation[i] = src.mRotation.get_value()[i]; +                    } +                }    +                 +                if (src.mTranslation != glh::vec3f(0.f, 0.f, 0.f)) +                { +                    dst.translation.resize(3); +                    for (U32 i = 0; i < 3; ++i) +                    { +                        dst.translation[i] = src.mTranslation.v[i]; +                    } +                } +                 +                if (src.mScale != glh::vec3f(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.children = src.mChildren; +            dst.mesh = src.mMesh; +            dst.skin = src.mSkin; +            dst.name = src.mName; +        } + +        void copy(const Scene& src, tinygltf::Scene& dst) +        { +            dst.nodes = src.mNodes; +            dst.name = src.mName; +        } + +        void copy(const Primitive& src, tinygltf::Primitive& dst) +        { +            for (auto& attrib : src.mAttributes) +            { +                dst.attributes[attrib.first] = attrib.second; +            } +            dst.indices = src.mIndices; +            dst.material = src.mMaterial; +            dst.mode = src.mMode; +        } + +        void copy(const Mesh& src, tinygltf::Mesh& mesh) +        { +            copy(src.mPrimitives, mesh.primitives); +            mesh.weights = src.mWeights; +            mesh.name = src.mName; +        } + +        void copy(const Material::TextureInfo& src, tinygltf::TextureInfo& dst) +        { +            dst.index = src.mIndex; +            dst.texCoord = src.mTexCoord; +        } + +        void copy(const Material::OcclusionTextureInfo& src, tinygltf::OcclusionTextureInfo& dst) +        { +            dst.index = src.mIndex; +            dst.texCoord = src.mTexCoord; +            dst.strength = src.mStrength; +        } + +        void copy(const Material::NormalTextureInfo& src, tinygltf::NormalTextureInfo& dst) +        { +            dst.index = src.mIndex; +            dst.texCoord = src.mTexCoord; +            dst.scale = src.mScale; +        } + +        void copy(const Material::PbrMetallicRoughness& src, tinygltf::PbrMetallicRoughness& dst) +        { +            dst.baseColorFactor = { src.mBaseColorFactor.v[0], src.mBaseColorFactor.v[1], src.mBaseColorFactor.v[2], src.mBaseColorFactor.v[3] }; +            copy(src.mBaseColorTexture, dst.baseColorTexture); +            dst.metallicFactor = src.mMetallicFactor; +            dst.roughnessFactor = src.mRoughnessFactor; +            copy(src.mMetallicRoughnessTexture, dst.metallicRoughnessTexture); +        } + +        void copy(const Material& src, tinygltf::Material& material) +        { +            material.name = src.mName; + +            material.emissiveFactor = { src.mEmissiveFactor.v[0], src.mEmissiveFactor.v[1], src.mEmissiveFactor.v[2] }; +            copy(src.mPbrMetallicRoughness, material.pbrMetallicRoughness); +            copy(src.mNormalTexture, material.normalTexture); +            copy(src.mEmissiveTexture, material.emissiveTexture); +        } + +        void copy(const Texture& src, tinygltf::Texture& texture) +        { +            texture.sampler = src.mSampler; +            texture.source = src.mSource; +            texture.name = src.mName; +        } + +        void copy(const Sampler& src, tinygltf::Sampler& sampler) +        { +            sampler.magFilter = src.mMagFilter; +            sampler.minFilter = src.mMinFilter; +            sampler.wrapS = src.mWrapS; +            sampler.wrapT = src.mWrapT; +            sampler.name = src.mName; +        } + +        void copy(const Skin& src, tinygltf::Skin& skin) +        { +            skin.joints = src.mJoints; +            skin.inverseBindMatrices = src.mInverseBindMatrices; +            skin.skeleton = src.mSkeleton; +            skin.name = src.mName; +        } + +        void copy(const Accessor& src, tinygltf::Accessor& accessor) +        { +            accessor.bufferView = src.mBufferView; +            accessor.byteOffset = src.mByteOffset; +            accessor.componentType = src.mComponentType; +            accessor.minValues = src.mMin; +            accessor.maxValues = src.mMax; +             +            accessor.count = src.mCount; +            accessor.type = src.mType; +            accessor.normalized = src.mNormalized; +            accessor.name = src.mName; +        } + +        void copy(const Animation::Sampler& src, tinygltf::AnimationSampler& sampler) +        { +            sampler.input = src.mInput; +            sampler.output = src.mOutput; +            sampler.interpolation = src.mInterpolation; +        } + +        void copy(const Animation::Channel& src, tinygltf::AnimationChannel& channel) +        { +            channel.sampler = src.mSampler; +            channel.target_node = src.mTarget.mNode; +            channel.target_path = src.mTarget.mPath; +        } + +        void copy(const Animation& src, tinygltf::Animation& animation) +        { +            animation.name = src.mName; + +            copy(src.mSamplers, animation.samplers); + +            U32 channel_count = src.mRotationChannels.size() + src.mTranslationChannels.size() + src.mScaleChannels.size(); + +            animation.channels.resize(channel_count); + +            U32 idx = 0; +            for (U32 i = 0; i < src.mTranslationChannels.size(); ++i) +            { +                copy(src.mTranslationChannels[i], animation.channels[idx++]); +            } + +            for (U32 i = 0; i < src.mRotationChannels.size(); ++i) +            { +                copy(src.mRotationChannels[i], animation.channels[idx++]); +            } + +            for (U32 i = 0; i < src.mScaleChannels.size(); ++i) +            { +                copy(src.mScaleChannels[i], animation.channels[idx++]); +            } +        } + +        void copy(const Buffer& src, tinygltf::Buffer& buffer) +        { +            buffer.uri = src.mUri; +            buffer.data = src.mData; +            buffer.name = src.mName; +        } + +        void copy(const BufferView& src, tinygltf::BufferView& bufferView) +        { +            bufferView.buffer = src.mBuffer; +            bufferView.byteOffset = src.mByteOffset; +            bufferView.byteLength = src.mByteLength; +            bufferView.byteStride = src.mByteStride; +            bufferView.target = src.mTarget; +            bufferView.name = src.mName; +        } + +        void copy(const Image& src, tinygltf::Image& image) +        { +            image.name = src.mName; +            image.width = src.mWidth; +            image.height = src.mHeight; +            image.component = src.mComponent; +            image.bits = src.mBits; +            image.pixel_type = src.mPixelType; + +            image.image = src.mData; +            image.bufferView = src.mBufferView; +            image.mimeType = src.mMimeType; +            image.uri = src.mUri; +        } + +        void copy(const Asset & src, tinygltf::Model& dst) +        { +            dst.defaultScene = src.mDefaultScene; +            dst.asset.copyright = src.mCopyright; +            dst.asset.version = src.mVersion; +            dst.asset.minVersion = src.mMinVersion; +            dst.asset.generator = "Linden Lab Experimental GLTF Export"; +            dst.asset.extras = src.mExtras; + +            copy(src.mScenes, dst.scenes); +            copy(src.mNodes, dst.nodes); +            copy(src.mMeshes, dst.meshes); +            copy(src.mMaterials, dst.materials); +            copy(src.mBuffers, dst.buffers); +            copy(src.mBufferViews, dst.bufferViews); +            copy(src.mTextures, dst.textures); +            copy(src.mSamplers, dst.samplers); +            copy(src.mImages, dst.images); +            copy(src.mAccessors, dst.accessors); +            copy(src.mAnimations, dst.animations); +            copy(src.mSkins, dst.skins); +        } +    } +}  void Scene::updateTransforms(Asset& asset)  {      LLMatrix4a identity; @@ -237,6 +499,8 @@ void Node::makeMatrixValid()          mMatrix.loadu(t.m);          mMatrixValid = true;      } + +    llassert(mMatrixValid);  }  void Node::makeTRSValid() @@ -252,6 +516,8 @@ void Node::makeTRSValid()          mRotation.set_value(t);          mTRSValid = true;      } + +    llassert(mTRSValid);  }  void Node::setRotation(const glh::quaternionf& q) @@ -318,6 +584,7 @@ const Node& Node::operator=(const tinygltf::Node& src)      {          // node specifies no transformation, set to identity          mMatrix.setIdentity(); +        mMatrixValid = true;      }      mChildren = src.children; @@ -467,6 +734,15 @@ void Asset::allocateGLResources(const std::string& filename, const tinygltf::Mod  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; + +    mDefaultScene = src.defaultScene; +     +      mScenes.resize(src.scenes.size());      for (U32 i = 0; i < src.scenes.size(); ++i)      { @@ -542,9 +818,167 @@ const Asset& Asset::operator=(const tinygltf::Model& src)      return *this;  } +void Asset::save(tinygltf::Model& dst) +{ +    LL::GLTF::copy(*this, dst); +} + +void Asset::decompose(const std::string& filename) +{ +    // get folder path +    std::string folder = gDirUtilp->getDirName(filename); + +    // decompose images +    for (auto& image : mImages) +    { +        image.decompose(*this, folder); +    } +} + +void Asset::eraseBufferView(S32 bufferView) +{ +    mBufferViews.erase(mBufferViews.begin() + bufferView); + +    for (auto& accessor : mAccessors) +    { +        if (accessor.mBufferView > bufferView) +        { +            accessor.mBufferView--; +        } +    } + +    for (auto& image : mImages) +    { +        if (image.mBufferView > bufferView) +        { +            image.mBufferView--; +        } +    } + +} + +void Image::decompose(Asset& asset, const std::string& folder) +{ +    std::string name = mName; +    if (name.empty()) +    { +        S32 idx = this - asset.mImages.data(); +        name = llformat("image_%d", idx); +    } + +    if (mBufferView != INVALID_INDEX) +    { +        // save original image +        BufferView& bufferView = asset.mBufferViews[mBufferView]; +        Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; +         +        std::string extension; + +        if (mMimeType == "image/jpeg") +        { +            extension = ".jpg"; +        } +        else if (mMimeType == "image/png") +        { +            extension = ".png"; +        } +        else +        { +            extension = ".bin"; +        } + +        std::string filename = folder + "/" + name + "." + extension; + +        // set URI to non-j2c file for now, but later we'll want to reference the j2c hash +        mUri = name + "." + extension; + +        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 (!mData.empty()) +    { +        // save j2c image +        std::string filename = folder + "/" + name + ".j2c"; + +        LLPointer<LLImageRaw> raw = new LLImageRaw(mWidth, mHeight, mComponent); +        U8* data = raw->allocateData(); +        llassert(mData.size() == raw->getDataSize()); +        memcpy(data, mData.data(), mData.size()); + +        LLViewerTextureList::createUploadFile(raw, filename, 4096); + +        mData.clear(); +    } + +    mWidth = -1; +    mHeight = -1; +    mComponent = -1; +    mBits = -1; +    mPixelType = -1; +    mMimeType = ""; + +} + +const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::TextureInfo& src) +{ +    mIndex = src.index; +    mTexCoord = src.texCoord; +    return *this; +} + +const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const tinygltf::OcclusionTextureInfo& src) +{ +    mIndex = src.index; +    mTexCoord = src.texCoord; +    mStrength = src.strength; +    return *this; +} + +const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const tinygltf::NormalTextureInfo& src) +{ +    mIndex = src.index; +    mTexCoord = src.texCoord; +    mScale = src.scale; +    return *this; +} + +const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const tinygltf::PbrMetallicRoughness& src) +{ +    if (src.baseColorFactor.size() == 4) +    { +        mBaseColorFactor.set_value(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]); +    } +     +    mBaseColorTexture = src.baseColorTexture; +    mMetallicFactor = src.metallicFactor; +    mRoughnessFactor = src.roughnessFactor; +    mMetallicRoughnessTexture = src.metallicRoughnessTexture; + +    return *this; +}  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]); +    } + +    mPbrMetallicRoughness = src.pbrMetallicRoughness; +    mNormalTexture = src.normalTexture; +    mOcclusionTexture = src.occlusionTexture; +    mEmissiveTexture = src.emissiveTexture; + +    mAlphaMode = src.alphaMode; +    mAlphaCutoff = src.alphaCutoff; +    mDoubleSided = src.doubleSided; +      return *this;  } diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index 6e576a1ffe..5a62313705 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -45,11 +45,59 @@ namespace LL          class Material          {          public: +            class TextureInfo +            { +            public: +                S32 mIndex = INVALID_INDEX; +                S32 mTexCoord = 0; + +                const TextureInfo& operator=(const tinygltf::TextureInfo& src); +            }; + +            class NormalTextureInfo : public TextureInfo +            { +            public: +                F32 mScale = 1.0f; + +                const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src); +            }; + +            class OcclusionTextureInfo : public TextureInfo +            { +            public: +                F32 mStrength = 1.0f; + +                const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src); +            }; + +            class PbrMetallicRoughness +            { +            public: +                glh::vec4f mBaseColorFactor = glh::vec4f(1.f,1.f,1.f,1.f); +                TextureInfo mBaseColorTexture; +                F32 mMetallicFactor = 1.0f; +                F32 mRoughnessFactor = 1.0f; +                TextureInfo mMetallicRoughnessTexture; +                const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src); +            }; + +              // use LLFetchedGLTFMaterial for now, but eventually we'll want to use              // a more flexible GLTF material implementation instead of the fixed packing              // version we use for sharable GLTF material assets              LLPointer<LLFetchedGLTFMaterial> mMaterial; +            PbrMetallicRoughness mPbrMetallicRoughness; +            NormalTextureInfo mNormalTexture; +            OcclusionTextureInfo mOcclusionTexture; +            TextureInfo mEmissiveTexture; + +              std::string mName; +            glh::vec3f mEmissiveFactor = glh::vec3f(0.f, 0.f, 0.f); +            std::string mAlphaMode = "OPAQUE"; +            F32 mAlphaCutoff = 0.5f; +            bool mDoubleSided = false; +              const Material& operator=(const tinygltf::Material& src); @@ -179,11 +227,16 @@ namespace LL              std::string mName;              std::string mUri;              std::string mMimeType; + +            S32 mBufferView = INVALID_INDEX; +              std::vector<U8> mData;              S32 mWidth;              S32 mHeight;              S32 mComponent;              S32 mBits; +            S32 mPixelType; +              LLPointer<LLViewerFetchedTexture> mTexture;              const Image& operator=(const tinygltf::Image& src) @@ -196,10 +249,14 @@ namespace LL                  mHeight = src.height;                  mComponent = src.component;                  mBits = src.bits; - +                mBufferView = src.bufferView; +                mPixelType = src.pixel_type;                  return *this;              } +            // save image clear local data, and set uri +            void decompose(Asset& asset, const std::string& filename); +              void allocateGLResources()              {                  // allocate texture @@ -208,7 +265,7 @@ namespace LL          };          // C++ representation of a GLTF Asset -        class Asset : public LLRefCount +        class Asset          {          public:              std::vector<Scene> mScenes; @@ -224,6 +281,15 @@ namespace LL              std::vector<Animation> mAnimations;              std::vector<Skin> mSkins; +            std::string mVersion; +            std::string mGenerator; +            std::string mMinVersion; +            std::string mCopyright; + +            S32 mDefaultScene = INVALID_INDEX; +            tinygltf::Value mExtras; + +              // the last time update() was called according to gFrameTimeSeconds              F32 mLastUpdateTime = gFrameTimeSeconds; @@ -258,7 +324,16 @@ namespace LL              );              const Asset& operator=(const tinygltf::Model& src); -             + +            // save the asset to a tinygltf model +            void save(tinygltf::Model& dst); + +            // decompose the asset to the given .gltf file +            void decompose(const std::string& filename); + +            // remove the bufferview at the given index +            // updates all bufferview indices in this Asset as needed +            void eraseBufferView(S32 bufferView);          };      }  } diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index 4e3439ea5c..6533f283e2 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -66,11 +66,95 @@ void GLTFSceneManager::load()                  }              },              LLFilePicker::FFLOAD_GLTF, -            true); +            false);      }      else      { -        LLNotificationsUtil::add("GLTFPreviewSelection"); +        LLNotificationsUtil::add("GLTFOpenSelection"); +    } +} + +void GLTFSceneManager::saveAs() +{ +    LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); +    if (obj && obj->mGLTFAsset) +    { +        LLFilePickerReplyThread::startPicker( +            [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) +            { +                if (LLAppViewer::instance()->quitRequested()) +                { +                    return; +                } +                if (filenames.size() > 0) +                { +                    GLTFSceneManager::instance().save(filenames[0]); +                } +            }, +            LLFilePicker::FFSAVE_GLTF, +            "scene.gltf"); +    } +    else +    { +        LLNotificationsUtil::add("GLTFSaveSelection"); +    } +} + +void GLTFSceneManager::decomposeSelection() +{ +    LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); +    if (obj && obj->mGLTFAsset) +    { +        LLFilePickerReplyThread::startPicker( +            [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) +            { +                if (LLAppViewer::instance()->quitRequested()) +                { +                    return; +                } +                if (filenames.size() > 0) +                { +                    GLTFSceneManager::instance().decomposeSelection(filenames[0]); +                } +            }, +            LLFilePicker::FFSAVE_GLTF, +            "scene.gltf"); +    } +    else +    { +        LLNotificationsUtil::add("GLTFSaveSelection"); +    } +} + +void GLTFSceneManager::decomposeSelection(const std::string& filename) +{ +    LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); +    if (obj && obj->mGLTFAsset) +    { +        // copy asset out for decomposition +        Asset asset = *obj->mGLTFAsset; + +        // decompose the asset into component parts +        asset.decompose(filename); + +        // copy decomposed asset into tinygltf for serialization +        tinygltf::Model model; +        asset.save(model); + +        LLTinyGLTFHelper::saveModel(filename, model); +    } +} + +void GLTFSceneManager::save(const std::string& filename) +{ +    LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); +    if (obj && obj->mGLTFAsset) +    { +        Asset* asset = obj->mGLTFAsset.get(); +        tinygltf::Model model; +        asset->save(model); + +        LLTinyGLTFHelper::saveModel(filename, model);      }  } @@ -79,7 +163,7 @@ void GLTFSceneManager::load(const std::string& filename)      tinygltf::Model model;      LLTinyGLTFHelper::loadModel(filename, model); -    LLPointer<Asset> asset = new Asset(); +    std::shared_ptr<Asset> asset = std::make_shared<Asset>();      *asset = model;      gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions @@ -126,10 +210,7 @@ void GLTFSceneManager::update()              continue;          } -        Asset* asset = mObjects[i]->mGLTFAsset; - -        asset->update(); -      +        mObjects[i]->mGLTFAsset->update();      }  } @@ -151,7 +232,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged)              continue;          } -        Asset* asset = mObjects[i]->mGLTFAsset; +        Asset* asset = mObjects[i]->mGLTFAsset.get();          gGL.pushMatrix(); @@ -298,7 +379,7 @@ LLDrawable* GLTFSceneManager::lineSegmentIntersect(const LLVector4a& start, cons          }          // temporary debug -- always double check objects that have GLTF scenes hanging off of them even if the ray doesn't intersect the object bounds -        if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset, start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent)) +        if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset.get(), start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent))          {              local_end = position;              if (intersection) @@ -425,7 +506,7 @@ void GLTFSceneManager::renderDebug()          matMul(mat, modelview, modelview); -        Asset* asset = obj->mGLTFAsset; +        Asset* asset = obj->mGLTFAsset.get();          for (auto& node : asset->mNodes)          { @@ -440,7 +521,7 @@ void GLTFSceneManager::renderDebug()              continue;          } -        Asset* asset = obj->mGLTFAsset; +        Asset* asset = obj->mGLTFAsset.get();          LLMatrix4a mat = obj->getGLTFAssetToAgentTransform(); @@ -477,7 +558,7 @@ void GLTFSceneManager::renderDebug()                  matMul(mat, modelview, modelview); -                Asset* asset = obj->mGLTFAsset; +                Asset* asset = obj->mGLTFAsset.get();                  for (auto& node : asset->mNodes)                  { @@ -538,7 +619,7 @@ void GLTFSceneManager::renderDebug()          if (drawable)          {              gGL.pushMatrix(); -            Asset* asset = drawable->getVObj()->mGLTFAsset; +            Asset* asset = drawable->getVObj()->mGLTFAsset.get();              Node* node = &asset->mNodes[node_hit];              Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit]; diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h index d286f335e4..e380be7e3c 100644 --- a/indra/newview/gltfscenemanager.h +++ b/indra/newview/gltfscenemanager.h @@ -28,6 +28,16 @@  #include "llsingleton.h"  #include "llviewerobject.h" +class LLVOVolume; +class LLDrawable; + +namespace LL +{ +    namespace GLTF +    { +        class Asset; +    } +}  namespace LL  { @@ -40,6 +50,11 @@ namespace LL          void load(); // open filepicker to choose asset          void load(const std::string& filename); // load asset from filename +        void saveAs(); // open filepicker and choose file to save selected asset to +        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 update();          void render(bool opaque, bool rigged = false);          void renderOpaque(); diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index b82172c506..23d9b25344 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -790,7 +790,7 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension,          case LLFilePicker::FFSAVE_GLTF:              type = "\?\?\?\?";              creator = "\?\?\?\?"; -            extension = "glb"; +            extension = "glb,gltf";              break;          case LLFilePicker::FFSAVE_XML: diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp index 49c35165e6..c8a9a36ad1 100644 --- a/indra/newview/lltinygltfhelper.cpp +++ b/indra/newview/lltinygltfhelper.cpp @@ -248,6 +248,43 @@ bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& m      return false;  } +bool LLTinyGLTFHelper::saveModel(const std::string& filename, tinygltf::Model& model_in) +{ +    std::string exten = gDirUtilp->getExtension(filename); + +    bool success = false; + +    if (exten == "gltf" || exten == "glb") +    { +        tinygltf::TinyGLTF writer; + +        std::string filename_lc = filename; +        LLStringUtil::toLower(filename_lc); + +         +        bool embed_images = false; +        bool embed_buffers = false; +        bool pretty_print = true; +        bool write_binary = false; +         + +        if (std::string::npos == filename_lc.rfind(".gltf")) +        {  // file is binary +            embed_images = embed_buffers = write_binary = true; +        } + +        success = writer.WriteGltfSceneToFile(&model_in, filename, embed_images, embed_buffers, pretty_print, write_binary); + +        if (!success) +        { +            LL_WARNS("GLTF") << "Failed to save" << LL_ENDL; +            return false; +        } +    } + +    return success; +} +  bool LLTinyGLTFHelper::getMaterialFromModel(      const std::string& filename,      const tinygltf::Model& model_in, diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/lltinygltfhelper.h index da505b41e9..a259609404 100644 --- a/indra/newview/lltinygltfhelper.h +++ b/indra/newview/lltinygltfhelper.h @@ -42,6 +42,7 @@ namespace LLTinyGLTFHelper      LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, bool flip = true);      bool loadModel(const std::string& filename, tinygltf::Model& model_out); +    bool saveModel(const std::string& filename, tinygltf::Model& model_in);      bool getMaterialFromModel(          const std::string& filename, diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index c1a8c5df9e..631e1b57d9 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -7980,7 +7980,7 @@ class LLAdvancedClickHDRIPreview: public view_listener_t  }; -class LLAdvancedClickGLTFScenePreview : public view_listener_t +class LLAdvancedClickGLTFOpen: public view_listener_t  {      bool handleEvent(const LLSD& userdata)      { @@ -7990,6 +7990,26 @@ class LLAdvancedClickGLTFScenePreview : public view_listener_t      }  }; +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; +    } +}; + +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; +    } +}; +  // these are used in the gl menus to set control values that require shader recompilation  class LLToggleShaderControl : public view_listener_t  { @@ -9637,7 +9657,9 @@ void initialize_menus()  	view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile");  	view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark");      view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview"); -    view_listener_t::addMenu(new LLAdvancedClickGLTFScenePreview(), "Advanced.ClickGLTFScenePreview"); +    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 LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");      view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index b66d4b1dab..cc6f4403dd 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -107,6 +107,7 @@  #include "llmeshrepository.h"  #include "llgltfmateriallist.h"  #include "llgl.h" +#include "gltf/asset.h"  //#define DEBUG_UPDATE_TYPE @@ -4379,7 +4380,7 @@ LLMatrix4a LLViewerObject::getGLTFAssetToAgentTransform() const      LLMatrix4 root;      root.initScale(getScale());      root.rotate(getRenderRotation()); -    root.translate(getPositionAgent()); +    root.translate(getRenderPosition());      LLMatrix4a mat;      mat.loadu((F32*)root.mMatrix); @@ -4403,7 +4404,7 @@ LLMatrix4a LLViewerObject::getAgentToGLTFAssetTransform() const      scale.mV[1] = 1.f / scale.mV[1];      scale.mV[2] = 1.f / scale.mV[2]; -    root.translate(-getPositionAgent()); +    root.translate(-getRenderPosition());      root.rotate(~getRenderRotation());      LLMatrix4 scale_mat; @@ -4420,7 +4421,7 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const  {      LLMatrix4a mat; -    if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) +    if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())      {          auto& node = mGLTFAsset->mNodes[node_index]; @@ -4474,7 +4475,7 @@ void decomposeMatrix(const LLMatrix4a& mat, LLVector3& position, LLQuaternion& r  void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion& rotation)  { -    if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) +    if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())      {          auto& node = mGLTFAsset->mNodes[node_index]; @@ -4506,7 +4507,7 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion  void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)  { -    if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) +    if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())      {          auto& node = mGLTFAsset->mNodes[node_index]; diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 64c1ee6633..e314992532 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -45,7 +45,14 @@  #include "llbbox.h"  #include "llrigginginfo.h"  #include "llreflectionmap.h" -#include "gltf/asset.h" + +namespace LL +{ +    namespace GLTF +    { +        class Asset; +    } +}  class LLAgent;			// TODO: Get rid of this.  class LLAudioSource; @@ -736,7 +743,7 @@ public:  	F32             mPhysicsRestitution;      // Associated GLTF Asset -    LLPointer<LL::GLTF::Asset> mGLTFAsset; +    std::shared_ptr<LL::GLTF::Asset> mGLTFAsset;  	// Pipeline classes  	LLPointer<LLDrawable> mDrawable; diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 644553e64c..6238efe688 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2844,6 +2844,30 @@ function="World.EnvPreset"                  parameter="mem_leaking" />                 </menu_item_call>          </menu> +      <menu +         create_jump_keys="true" +         label="GLTF" +         name="GLTF" +         tear_off="true"> +        <menu_item_call +         label="Open..." +         name="Open..."> +          <menu_item_call.on_click +           function="Advanced.ClickGLTFOpen" /> +        </menu_item_call> +        <menu_item_call +         label="Save As..." +         name="Save As..."> +          <menu_item_call.on_click +           function="Advanced.ClickGLTFSaveAs" /> +        </menu_item_call> +        <menu_item_call +         label="Decompose..." +         name="Decompose..."> +          <menu_item_call.on_click +           function="Advanced.ClickGLTFDecompose" /> +        </menu_item_call> +      </menu>          <menu           create_jump_keys="true"           label="Render Tests" @@ -2896,12 +2920,6 @@ function="World.EnvPreset"              <menu_item_call.on_click               function="Advanced.ClickHDRIPreview" />            </menu_item_call> -          <menu_item_call -           label="GLTF Scene Preview" -           name="GLTF Scene Preview"> -            <menu_item_call.on_click -             function="Advanced.ClickGLTFScenePreview" /> -          </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 ad1f12002e..7d7ea986dd 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12408,7 +12408,7 @@ are wearing now.    <notification     icon="alertmodal.tga" -   name="GLTFPreviewSelection" +   name="GLTFOpenSelection"     type="alert">      You must select an object to act as a handle to the GLTF asset you are previewing.      <tag>fail</tag> @@ -12417,4 +12417,15 @@ are wearing now.        yestext="OK"/>    </notification> +  <notification +   icon="alertmodal.tga" +   name="GLTFSaveSelection" +   type="alert"> +    You must select an object that has a GLTF asset associated with it. +    <tag>fail</tag> +    <usetemplate +      name="okbutton" +      yestext="OK"/> +  </notification> +  </notifications> | 
