summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/gltf/README.md156
-rw-r--r--indra/newview/gltf/accessor.cpp175
-rw-r--r--indra/newview/gltf/accessor.h38
-rw-r--r--indra/newview/gltf/animation.cpp175
-rw-r--r--indra/newview/gltf/animation.h49
-rw-r--r--indra/newview/gltf/asset.cpp725
-rw-r--r--indra/newview/gltf/asset.h142
-rw-r--r--indra/newview/gltf/buffer_util.h608
-rw-r--r--indra/newview/gltf/common.h66
-rw-r--r--indra/newview/gltf/primitive.cpp79
-rw-r--r--indra/newview/gltf/primitive.h8
-rw-r--r--indra/newview/gltfscenemanager.cpp447
-rw-r--r--indra/newview/gltfscenemanager.h12
-rw-r--r--indra/newview/llfetchedgltfmaterial.cpp1
-rw-r--r--indra/newview/llimprocessing.cpp40
-rw-r--r--indra/newview/llinventorymodel.cpp4
-rw-r--r--indra/newview/llviewerassettype.cpp2
-rw-r--r--indra/newview/llviewerassetupload.cpp8
-rw-r--r--indra/newview/llviewermedia.cpp8
-rw-r--r--indra/newview/llviewermenu.cpp42
-rw-r--r--indra/newview/llviewerobject.cpp45
-rw-r--r--indra/newview/llviewerobject.h6
-rwxr-xr-xindra/newview/llviewerregion.cpp18
-rw-r--r--indra/newview/llvovolume.cpp14
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml48
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml22
27 files changed, 2560 insertions, 389 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index b2cb58d0df..c7999fc2e9 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -15738,5 +15738,16 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>GLTFEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable GLTF support. Set by SimulatorFeatures</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
</map>
</llsd>
diff --git a/indra/newview/gltf/README.md b/indra/newview/gltf/README.md
new file mode 100644
index 0000000000..8e7df0a439
--- /dev/null
+++ b/indra/newview/gltf/README.md
@@ -0,0 +1,156 @@
+# Linden Lab GLTF Implementation
+
+Currently in prototype stage. Much functionality is missing (blend shapes,
+multiple texture coordinates, etc).
+
+GLTF Specification can be found here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html.
+If this implementation disagrees with the GLTF Specification, the specification is correct.
+
+Class structure and naming should match the GLTF Specification as closely as possible while
+conforming to the LL coding standards. All code in headers should be contained in the
+LL::GLTF namespace.
+
+The implementation serves both the client and the server.
+
+## Design Principles
+
+- The implementation MUST be capable of round-trip serialization with no data loss beyond F64 to F32 conversions.
+- The implementation MUST use the same indexing scheme as the GLTF specification. Do not store pointers where the
+- GLTF specification stores indices, store indices.
+- Limit dependencies on llcommon as much as possible. Prefer std::, boost::, and (soon) glm:: over LL facsimiles.
+- Usage of LLSD is forbidden in the LL::GLTF namespace.
+- Use "using namespace" liberally in .cpp files, but never in .h files.
+- "using Foo = Bar" is permissible in .h files within the LL::GLTF namespace.
+
+## Loading, Copying, and Serialization
+Each class should provide two functions (Primitive shown for example):
+
+```
+// Serialize to the provided json object.
+// "obj" should be "this" in json form on return
+// Do not serialize default values
+void serialize(boost::json::object& obj) const;
+
+// Initialize from a provided json value
+const Primitive& operator=(const Value& src);
+```
+
+"serialize" implementations should use "write":
+
+```
+void Primitive::serialize(boost::json::object& dst) const
+{
+ write(mMaterial, "material", dst, -1);
+ write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES);
+ write(mIndices, "indices", dst, INVALID_INDEX);
+ write(mAttributes, "attributes", dst);
+}
+```
+
+And operator= implementations should use "copy":
+
+```
+const Primitive& Primitive::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "material", mMaterial);
+ copy(src, "mode", mMode);
+ copy(src, "indices", mIndices);
+ copy(src, "attributes", mAttributes);
+
+ mGLMode = gltf_mode_to_gl_mode(mMode);
+ }
+ return *this;
+}
+```
+
+Parameters to "write" and "copy" MUST be ordered "src" before "dst"
+so the code reads as "write src to dst" and "copy src to dst".
+
+When reading string constants from GLTF json (i.e. "OPAQUE", "TRIANGLES"), these
+strings should be converted to enums inside operator=. It is permissible to
+store the original strings during prototyping to aid in development, but eventually
+we'll purge these strings from the implementation. However, implementations MUST
+preserve any and all "name" members.
+
+"write" and "copy" implementations MUST be stored in buffer_util.h.
+As implementers encounter new data types, you'll see compiler errors
+pointing at templates in buffer_util.h. See vec3 as a known good
+example of how to add support for a new type (there are bad examples, so beware):
+
+```
+// vec3
+template<>
+inline bool copy(const Value& src, vec3& dst)
+{
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 3)
+ {
+ if (arr[0].is_double() &&
+ arr[1].is_double() &&
+ arr[2].is_double())
+ {
+ dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double());
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+template<>
+inline bool write(const vec3& src, Value& dst)
+{
+ dst = boost::json::array();
+ boost::json::array& arr = dst.as_array();
+ arr.resize(3);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ return true;
+}
+
+```
+
+"write" MUST return true if ANY data was written
+"copy" MUST return true if ANY data was copied
+
+Speed is important, but so is safety. In writers, try to avoid redundant copies
+(prefer resize over push_back, convert dst to an empty array and fill it, don't
+make an array on the stack and copy it into dst).
+
+boost::json WILL throw exceptions if you call as_foo() on a mismatched type but
+WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo
+before calling as_foo or get_foo. DO NOT add exception handlers. If boost throws
+an exception in serialization, the fix is to add type checks. If we see a large
+number of crash reports from boost::json exceptions, each of those reports
+indicates a place where we're missing "is_foo" checks. They are gold. Do not
+bury them with an exception handler.
+
+DO NOT rely on existing type conversion tools in the LL codebase -- LL data models
+conflict with the GLTF specification so we MUST provide conversions independent of
+our existing implementations.
+
+### JSON Serialization ###
+
+
+
+NEVER include buffer_util.h from a header.
+
+Loading from and saving to disk (import/export) is currently done using tinygltf, but this is not a long term
+solution. Eventually the implementation should rely solely on boost::json for reading and writing .gltf
+files and should handle .bin files natively.
+
+When serializing Images and Buffers to the server, clients MUST store a single UUID "uri" field and nothing else.
+The server MUST reject any data that violates this requirement.
+
+Clients MUST remove any Images from Buffers prior to upload to the server.
+Servers MAY reject Assets that contain Buffers with unreferenced data.
+
+... to be continued.
+
+
+
diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp
index 9bfdc2afa6..369ff4f240 100644
--- a/indra/newview/gltf/accessor.cpp
+++ b/indra/newview/gltf/accessor.cpp
@@ -27,8 +27,79 @@
#include "../llviewerprecompiledheaders.h"
#include "asset.h"
+#include "buffer_util.h"
using namespace LL::GLTF;
+using namespace boost::json;
+
+namespace LL
+{
+ namespace GLTF
+ {
+ Accessor::Type gltf_type_to_enum(const std::string& type)
+ {
+ if (type == "SCALAR")
+ {
+ return Accessor::Type::SCALAR;
+ }
+ else if (type == "VEC2")
+ {
+ return Accessor::Type::VEC2;
+ }
+ else if (type == "VEC3")
+ {
+ return Accessor::Type::VEC3;
+ }
+ else if (type == "VEC4")
+ {
+ return Accessor::Type::VEC4;
+ }
+ else if (type == "MAT2")
+ {
+ return Accessor::Type::MAT2;
+ }
+ else if (type == "MAT3")
+ {
+ return Accessor::Type::MAT3;
+ }
+ else if (type == "MAT4")
+ {
+ return Accessor::Type::MAT4;
+ }
+
+ LL_WARNS("GLTF") << "Unknown accessor type: " << type << LL_ENDL;
+ llassert(false);
+
+ return Accessor::Type::SCALAR;
+ }
+
+ std::string enum_to_gltf_type(Accessor::Type type)
+ {
+ switch (type)
+ {
+ case Accessor::Type::SCALAR:
+ return "SCALAR";
+ case Accessor::Type::VEC2:
+ return "VEC2";
+ case Accessor::Type::VEC3:
+ return "VEC3";
+ case Accessor::Type::VEC4:
+ return "VEC4";
+ case Accessor::Type::MAT2:
+ return "MAT2";
+ case Accessor::Type::MAT3:
+ return "MAT3";
+ case Accessor::Type::MAT4:
+ return "MAT4";
+ }
+
+ LL_WARNS("GLTF") << "Unknown accessor type: " << (S32)type << LL_ENDL;
+ llassert(false);
+
+ return "SCALAR";
+ }
+ }
+}
void Buffer::erase(Asset& asset, S32 offset, S32 length)
{
@@ -48,6 +119,27 @@ void Buffer::erase(Asset& asset, S32 offset, S32 length)
}
}
+void Buffer::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mUri, "uri", dst);
+ write_always(mByteLength, "byteLength", dst);
+};
+
+const Buffer& Buffer::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "uri", mUri);
+
+ // NOTE: DO NOT attempt to handle the uri here.
+ // The uri is a reference to a file that is not loaded until
+ // after the json document is parsed
+ }
+ return *this;
+}
+
const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
{
mData = src.data;
@@ -56,6 +148,31 @@ const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
return *this;
}
+
+void BufferView::serialize(object& dst) const
+{
+ write_always(mBuffer, "buffer", dst);
+ write_always(mByteLength, "byteLength", dst);
+ write(mByteOffset, "byteOffset", dst, 0);
+ write(mByteStride, "byteStride", dst, 0);
+ write(mTarget, "target", dst, -1);
+ write(mName, "name", dst);
+}
+
+const BufferView& BufferView::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "buffer", mBuffer);
+ copy(src, "byteLength", mByteLength);
+ copy(src, "byteOffset", mByteOffset);
+ copy(src, "byteStride", mByteStride);
+ copy(src, "target", mTarget);
+ copy(src, "name", mName);
+ }
+ return *this;
+}
+
const BufferView& BufferView::operator=(const tinygltf::BufferView& src)
{
mBuffer = src.buffer;
@@ -67,13 +184,69 @@ const BufferView& BufferView::operator=(const tinygltf::BufferView& src)
return *this;
}
+Accessor::Type tinygltf_type_to_enum(S32 type)
+{
+ switch (type)
+ {
+ case TINYGLTF_TYPE_SCALAR:
+ return Accessor::Type::SCALAR;
+ case TINYGLTF_TYPE_VEC2:
+ return Accessor::Type::VEC2;
+ case TINYGLTF_TYPE_VEC3:
+ return Accessor::Type::VEC3;
+ case TINYGLTF_TYPE_VEC4:
+ return Accessor::Type::VEC4;
+ case TINYGLTF_TYPE_MAT2:
+ return Accessor::Type::MAT2;
+ case TINYGLTF_TYPE_MAT3:
+ return Accessor::Type::MAT3;
+ case TINYGLTF_TYPE_MAT4:
+ return Accessor::Type::MAT4;
+ }
+
+ LL_WARNS("GLTF") << "Unknown tinygltf accessor type: " << type << LL_ENDL;
+ llassert(false);
+
+ return Accessor::Type::SCALAR;
+}
+
+void Accessor::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mBufferView, "bufferView", dst, INVALID_INDEX);
+ write(mByteOffset, "byteOffset", dst, 0);
+ write_always(mComponentType, "componentType", dst);
+ write_always(mCount, "count", dst);
+ write_always(enum_to_gltf_type(mType), "type", dst);
+ write(mNormalized, "normalized", dst, false);
+ write(mMax, "max", dst);
+ write(mMin, "min", dst);
+}
+
+const Accessor& Accessor::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "bufferView", mBufferView);
+ copy(src, "byteOffset", mByteOffset);
+ copy(src, "componentType", mComponentType);
+ copy(src, "count", mCount);
+ copy(src, "type", mType);
+ copy(src, "normalized", mNormalized);
+ copy(src, "max", mMax);
+ copy(src, "min", mMin);
+ }
+ return *this;
+}
+
const Accessor& Accessor::operator=(const tinygltf::Accessor& src)
{
mBufferView = src.bufferView;
mByteOffset = src.byteOffset;
mComponentType = src.componentType;
mCount = src.count;
- mType = src.type;
+ mType = tinygltf_type_to_enum(src.type);
mNormalized = src.normalized;
mName = src.name;
mMax = src.maxValues;
diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h
index 6849cd8609..3bbc5216bd 100644
--- a/indra/newview/gltf/accessor.h
+++ b/indra/newview/gltf/accessor.h
@@ -28,14 +28,15 @@
#include "../lltinygltfhelper.h"
#include "llstrider.h"
+#include "boost/json.hpp"
+
+#include "common.h"
// LL GLTF Implementation
namespace LL
{
namespace GLTF
{
- class Asset;
-
constexpr S32 INVALID_INDEX = -1;
class Buffer
@@ -44,11 +45,14 @@ namespace LL
std::vector<U8> mData;
std::string mName;
std::string mUri;
+ S32 mByteLength = 0;
// erase the given range from this buffer.
// also updates all buffer views in given asset that reference this buffer
void erase(Asset& asset, S32 offset, S32 length);
+ void serialize(boost::json::object& obj) const;
+ const Buffer& operator=(const Value& value);
const Buffer& operator=(const tinygltf::Buffer& src);
};
@@ -56,25 +60,25 @@ namespace LL
{
public:
S32 mBuffer = INVALID_INDEX;
- S32 mByteLength;
- S32 mByteOffset;
- S32 mByteStride;
- S32 mTarget;
- S32 mComponentType;
+ S32 mByteLength = 0;
+ S32 mByteOffset = 0;
+ S32 mByteStride = 0;
+ S32 mTarget = -1;
std::string mName;
+ void serialize(boost::json::object& obj) const;
+ const BufferView& operator=(const Value& value);
const BufferView& operator=(const tinygltf::BufferView& src);
-
};
class Accessor
{
public:
S32 mBufferView = INVALID_INDEX;
- S32 mByteOffset;
- S32 mComponentType;
- S32 mCount;
+ S32 mByteOffset = 0;
+ S32 mComponentType = 0;
+ S32 mCount = 0;
std::vector<double> mMax;
std::vector<double> mMin;
@@ -89,11 +93,19 @@ namespace LL
MAT4 = TINYGLTF_TYPE_MAT4
};
- S32 mType;
- bool mNormalized;
+ Type mType = Type::SCALAR;
+ bool mNormalized = false;
std::string mName;
+ void serialize(boost::json::object& obj) const;
+ const Accessor& operator=(const Value& value);
const Accessor& operator=(const tinygltf::Accessor& src);
};
+
+ // convert from "SCALAR", "VEC2", etc to Accessor::Type
+ Accessor::Type gltf_type_to_enum(const std::string& type);
+
+ // convert from Accessor::Type to "SCALAR", "VEC2", etc
+ std::string enum_to_gltf_type(Accessor::Type type);
}
}
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp
index da6d02b356..8a542fb315 100644
--- a/indra/newview/gltf/animation.cpp
+++ b/indra/newview/gltf/animation.cpp
@@ -30,6 +30,7 @@
#include "buffer_util.h"
using namespace LL::GLTF;
+using namespace boost::json;
void Animation::allocateGLResources(Asset& asset)
{
@@ -84,7 +85,6 @@ void Animation::apply(Asset& asset, float time)
}
};
-
void Animation::Sampler::allocateGLResources(Asset& asset)
{
Accessor& accessor = asset.mAccessors[mInput];
@@ -97,8 +97,96 @@ void Animation::Sampler::allocateGLResources(Asset& asset)
copy(asset, accessor, frame_times);
}
+
+void Animation::Sampler::serialize(object& obj) const
+{
+ write(mInput, "input", obj, INVALID_INDEX);
+ write(mOutput, "output", obj, INVALID_INDEX);
+ write(mInterpolation, "interpolation", obj, std::string("LINEAR"));
+ write(mMinTime, "min_time", obj);
+ write(mMaxTime, "max_time", obj);
+}
+
+const Animation::Sampler& Animation::Sampler::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "input", mInput);
+ copy(src, "output", mOutput);
+ copy(src, "interpolation", mInterpolation);
+ copy(src, "min_time", mMinTime);
+ copy(src, "max_time", mMaxTime);
+ }
+ return *this;
+}
+
+
+const Animation::Sampler& Animation::Sampler::operator=(const tinygltf::AnimationSampler& src)
+{
+ mInput = src.input;
+ mOutput = src.output;
+ mInterpolation = src.interpolation;
+
+ return *this;
+}
+
+bool Animation::Channel::Target::operator==(const Channel::Target& rhs) const
+{
+ return mNode == rhs.mNode && mPath == rhs.mPath;
+}
+
+bool Animation::Channel::Target::operator!=(const Channel::Target& rhs) const
+{
+ return !(*this == rhs);
+}
+
+void Animation::Channel::Target::serialize(object& obj) const
+{
+ write(mNode, "node", obj, INVALID_INDEX);
+ write(mPath, "path", obj);
+}
+
+const Animation::Channel::Target& Animation::Channel::Target::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "node", mNode);
+ copy(src, "path", mPath);
+ }
+ return *this;
+}
+
+void Animation::Channel::serialize(object& obj) const
+{
+ write(mSampler, "sampler", obj, INVALID_INDEX);
+ write(mTarget, "target", obj);
+}
+
+const Animation::Channel& Animation::Channel::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "sampler", mSampler);
+ copy(src, "target", mTarget);
+ }
+ return *this;
+}
+
+const Animation::Channel& Animation::Channel::operator=(const tinygltf::AnimationChannel& src)
+{
+ mSampler = src.sampler;
+
+ mTarget.mNode = src.target_node;
+ mTarget.mPath = src.target_path;
+
+ return *this;
+}
+
+
void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
{
+ LL_PROFILE_ZONE_SCOPED;
+
if (time < mMinTime)
{
frameIndex = 0;
@@ -158,13 +246,11 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
else
{
// interpolate
- LLQuaternion q0(mRotations[frameIndex].get_value());
- LLQuaternion q1(mRotations[frameIndex + 1].get_value());
+ quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t);
- LLQuaternion qf = slerp(t, q0, q1);
+ qf = glm::normalize(qf);
- qf.normalize();
- node.setRotation(glh::quaternionf(qf.mQ));
+ node.setRotation(qf);
}
}
@@ -191,10 +277,10 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti
else
{
// interpolate
- const glh::vec3f& v0 = mTranslations[frameIndex];
- const glh::vec3f& v1 = mTranslations[frameIndex + 1];
+ const vec3& v0 = mTranslations[frameIndex];
+ const vec3& v1 = mTranslations[frameIndex + 1];
- glh::vec3f vf = v0 + t * (v1 - v0);
+ vec3 vf = v0 + t * (v1 - v0);
node.setTranslation(vf);
}
@@ -223,15 +309,61 @@ void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
else
{
// interpolate
- const glh::vec3f& v0 = mScales[frameIndex];
- const glh::vec3f& v1 = mScales[frameIndex + 1];
+ const vec3& v0 = mScales[frameIndex];
+ const vec3& v1 = mScales[frameIndex + 1];
- glh::vec3f vf = v0 + t * (v1 - v0);
+ vec3 vf = v0 + t * (v1 - v0);
node.setScale(vf);
}
}
+void Animation::serialize(object& obj) const
+{
+ write(mName, "name", obj);
+ write(mSamplers, "samplers", obj);
+
+ std::vector<Channel> channels;
+ channels.insert(channels.end(), mRotationChannels.begin(), mRotationChannels.end());
+ channels.insert(channels.end(), mTranslationChannels.begin(), mTranslationChannels.end());
+ channels.insert(channels.end(), mScaleChannels.begin(), mScaleChannels.end());
+
+ write(channels, "channels", obj);
+}
+
+const Animation& Animation::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ const object& obj = src.as_object();
+
+ copy(obj, "name", mName);
+ copy(obj, "samplers", mSamplers);
+
+ // make a temporory copy of generic channels
+ std::vector<Channel> channels;
+ copy(obj, "channels", channels);
+
+ // break up into channel specific implementations
+ for (auto& channel: channels)
+ {
+ if (channel.mTarget.mPath == "rotation")
+ {
+ mRotationChannels.push_back(channel);
+ }
+ else if (channel.mTarget.mPath == "translation")
+ {
+ mTranslationChannels.push_back(channel);
+ }
+ else if (channel.mTarget.mPath == "scale")
+ {
+ mScaleChannels.push_back(channel);
+ }
+ }
+ }
+ return *this;
+}
+
const Animation& Animation::operator=(const tinygltf::Animation& src)
{
mName = src.name;
@@ -275,6 +407,18 @@ void Skin::allocateGLResources(Asset& asset)
}
}
+const Skin& Skin::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "skeleton", mSkeleton);
+ copy(src, "inverseBindMatrices", mInverseBindMatrices);
+ copy(src, "joints", mJoints);
+ }
+ return *this;
+}
+
const Skin& Skin::operator=(const tinygltf::Skin& src)
{
mName = src.name;
@@ -285,3 +429,10 @@ const Skin& Skin::operator=(const tinygltf::Skin& src)
return *this;
}
+void Skin::serialize(object& obj) const
+{
+ write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX);
+ write(mJoints, "joints", obj);
+ write(mName, "name", obj);
+ write(mSkeleton, "skeleton", obj, INVALID_INDEX);
+}
diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h
index cc2045ebb2..53c11d4669 100644
--- a/indra/newview/gltf/animation.h
+++ b/indra/newview/gltf/animation.h
@@ -27,7 +27,6 @@
*/
#include "accessor.h"
-
// LL GLTF Implementation
namespace LL
{
@@ -52,14 +51,10 @@ namespace LL
void allocateGLResources(Asset& asset);
- const Sampler& operator=(const tinygltf::AnimationSampler& src)
- {
- mInput = src.input;
- mOutput = src.output;
- mInterpolation = src.interpolation;
-
- return *this;
- }
+ void serialize(boost::json::object& dst) const;
+ const Sampler& operator=(const Value& value);
+ const Sampler& operator=(const tinygltf::AnimationSampler& src);
+
// get the frame index and time for the specified time
// asset -- the asset to reference for Accessors
@@ -77,27 +72,29 @@ namespace LL
public:
S32 mNode = INVALID_INDEX;
std::string mPath;
+
+ bool operator==(const Target& other) const;
+ bool operator!=(const Target& other) const;
+
+ void serialize(boost::json::object& dst) const;
+ const Target& operator=(const Value& value);
};
S32 mSampler = INVALID_INDEX;
Target mTarget;
- const Channel& operator=(const tinygltf::AnimationChannel& src)
- {
- mSampler = src.sampler;
-
- mTarget.mNode = src.target_node;
- mTarget.mPath = src.target_path;
-
- return *this;
- }
-
+ void serialize(boost::json::object& dst) const;
+ const Channel& operator=(const Value& value);
+ const Channel& operator=(const tinygltf::AnimationChannel& src);
};
class RotationChannel : public Channel
{
public:
- std::vector<glh::quaternionf> mRotations;
+ RotationChannel() = default;
+ RotationChannel(const Channel& channel) : Channel(channel) {}
+
+ std::vector<quat> mRotations;
const RotationChannel& operator=(const tinygltf::AnimationChannel& src)
{
@@ -116,7 +113,10 @@ namespace LL
class TranslationChannel : public Channel
{
public:
- std::vector<glh::vec3f> mTranslations;
+ TranslationChannel() = default;
+ TranslationChannel(const Channel& channel) : Channel(channel) {}
+
+ std::vector<vec3> mTranslations;
const TranslationChannel& operator=(const tinygltf::AnimationChannel& src)
{
@@ -135,7 +135,10 @@ namespace LL
class ScaleChannel : public Channel
{
public:
- std::vector<glh::vec3f> mScales;
+ ScaleChannel() = default;
+ ScaleChannel(const Channel& channel) : Channel(channel) {}
+
+ std::vector<vec3> mScales;
const ScaleChannel& operator=(const tinygltf::AnimationChannel& src)
{
@@ -165,6 +168,8 @@ namespace LL
std::vector<TranslationChannel> mTranslationChannels;
std::vector<ScaleChannel> mScaleChannels;
+ void serialize(boost::json::object& dst) const;
+ const Animation& operator=(const Value& value);
const Animation& operator=(const tinygltf::Animation& src);
void allocateGLResources(Asset& asset);
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index 5f2217a075..c64d48662c 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -31,13 +31,51 @@
#include "../llviewershadermgr.h"
#include "../llviewercontrol.h"
#include "../llviewertexturelist.h"
+#include "../pipeline.h"
+#include "buffer_util.h"
using namespace LL::GLTF;
+using namespace boost::json;
namespace LL
{
namespace GLTF
{
+ Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
+ {
+ if (alpha_mode == "OPAQUE")
+ {
+ return Material::AlphaMode::OPAQUE;
+ }
+ else if (alpha_mode == "MASK")
+ {
+ return Material::AlphaMode::MASK;
+ }
+ else if (alpha_mode == "BLEND")
+ {
+ return Material::AlphaMode::BLEND;
+ }
+ else
+ {
+ return Material::AlphaMode::OPAQUE;
+ }
+ }
+
+ std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode)
+ {
+ switch (alpha_mode)
+ {
+ case Material::AlphaMode::OPAQUE:
+ return "OPAQUE";
+ case Material::AlphaMode::MASK:
+ return "MASK";
+ case Material::AlphaMode::BLEND:
+ return "BLEND";
+ default:
+ return "OPAQUE";
+ }
+ }
+
template <typename T, typename U>
void copy(const std::vector<T>& src, std::vector<U>& dst)
{
@@ -45,49 +83,48 @@ namespace LL
for (U32 i = 0; i < src.size(); ++i)
{
copy(src[i], dst[i]);
- }
+ }
}
void copy(const Node& src, tinygltf::Node& dst)
{
if (src.mMatrixValid)
{
- if (!src.mMatrix.asMatrix4().isIdentity())
+ if (src.mMatrix != glm::identity<mat4>())
{
dst.matrix.resize(16);
+ const F32* m = glm::value_ptr(src.mMatrix);
for (U32 i = 0; i < 16; ++i)
{
- dst.matrix[i] = src.mMatrix.getF32ptr()[i];
+ dst.matrix[i] = m[i];
}
}
}
else if (src.mTRSValid)
{
- if (!src.mRotation.equals(glh::quaternionf::identity(), FLT_EPSILON))
+ if (src.mRotation != glm::identity<quat>())
{
dst.rotation.resize(4);
- for (U32 i = 0; i < 4; ++i)
- {
- dst.rotation[i] = src.mRotation.get_value()[i];
- }
- }
+ dst.rotation[0] = src.mRotation.x;
+ dst.rotation[1] = src.mRotation.y;
+ dst.rotation[2] = src.mRotation.z;
+ dst.rotation[3] = src.mRotation.w;
+ }
- if (src.mTranslation != glh::vec3f(0.f, 0.f, 0.f))
+ if (src.mTranslation != vec3(0.f, 0.f, 0.f))
{
dst.translation.resize(3);
- for (U32 i = 0; i < 3; ++i)
- {
- dst.translation[i] = src.mTranslation.v[i];
- }
+ dst.translation[0] = src.mTranslation.x;
+ dst.translation[1] = src.mTranslation.y;
+ dst.translation[2] = src.mTranslation.z;
}
- if (src.mScale != glh::vec3f(1.f, 1.f, 1.f))
+ if (src.mScale != vec3(1.f, 1.f, 1.f))
{
dst.scale.resize(3);
- for (U32 i = 0; i < 3; ++i)
- {
- dst.scale[i] = src.mScale.v[i];
- }
+ dst.scale[0] = src.mScale.x;
+ dst.scale[1] = src.mScale.y;
+ dst.scale[2] = src.mScale.z;
}
}
@@ -143,7 +180,7 @@ namespace LL
void copy(const Material::PbrMetallicRoughness& src, tinygltf::PbrMetallicRoughness& dst)
{
- dst.baseColorFactor = { src.mBaseColorFactor.v[0], src.mBaseColorFactor.v[1], src.mBaseColorFactor.v[2], src.mBaseColorFactor.v[3] };
+ dst.baseColorFactor = { src.mBaseColorFactor.r, src.mBaseColorFactor.g, src.mBaseColorFactor.b, src.mBaseColorFactor.a };
copy(src.mBaseColorTexture, dst.baseColorTexture);
dst.metallicFactor = src.mMetallicFactor;
dst.roughnessFactor = src.mRoughnessFactor;
@@ -154,7 +191,7 @@ namespace LL
{
material.name = src.mName;
- material.emissiveFactor = { src.mEmissiveFactor.v[0], src.mEmissiveFactor.v[1], src.mEmissiveFactor.v[2] };
+ material.emissiveFactor = { src.mEmissiveFactor.r, src.mEmissiveFactor.g, src.mEmissiveFactor.b };
copy(src.mPbrMetallicRoughness, material.pbrMetallicRoughness);
copy(src.mNormalTexture, material.normalTexture);
copy(src.mEmissiveTexture, material.emissiveTexture);
@@ -193,7 +230,7 @@ namespace LL
accessor.maxValues = src.mMax;
accessor.count = src.mCount;
- accessor.type = src.mType;
+ accessor.type = (S32) src.mType;
accessor.normalized = src.mNormalized;
accessor.name = src.mName;
}
@@ -278,7 +315,8 @@ namespace LL
dst.asset.version = src.mVersion;
dst.asset.minVersion = src.mMinVersion;
dst.asset.generator = "Linden Lab Experimental GLTF Export";
- dst.asset.extras = src.mExtras;
+
+ // NOTE: extras are lost in the conversion for now
copy(src.mScenes, dst.scenes);
copy(src.mNodes, dst.nodes);
@@ -297,8 +335,8 @@ namespace LL
}
void Scene::updateTransforms(Asset& asset)
{
- LLMatrix4a identity;
- identity.setIdentity();
+ mat4 identity = glm::identity<mat4>();
+
for (auto& nodeIndex : mNodes)
{
Node& node = asset.mNodes[nodeIndex];
@@ -306,7 +344,7 @@ void Scene::updateTransforms(Asset& asset)
}
}
-void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview)
+void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview)
{
for (auto& nodeIndex : mNodes)
{
@@ -315,9 +353,9 @@ void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview)
}
}
-void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview)
+void Node::updateRenderTransforms(Asset& asset, const mat4& modelview)
{
- matMul(mMatrix, modelview, mRenderMatrix);
+ mRenderMatrix = modelview * mMatrix;
for (auto& childIndex : mChildren)
{
@@ -326,13 +364,12 @@ void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview)
}
}
-LLMatrix4a inverse(const LLMatrix4a& mat);
-
-void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix)
+void Node::updateTransforms(Asset& asset, const mat4& parentMatrix)
{
makeMatrixValid();
- matMul(mMatrix, parentMatrix, mAssetMatrix);
- mAssetMatrixInv = inverse(mAssetMatrix);
+ mAssetMatrix = parentMatrix * mMatrix;
+
+ mAssetMatrixInv = glm::inverse(mAssetMatrix);
S32 my_index = this - &asset.mNodes[0];
@@ -352,26 +389,13 @@ void Asset::updateTransforms()
}
}
-void Asset::updateRenderTransforms(const LLMatrix4a& modelview)
+void Asset::updateRenderTransforms(const mat4& modelview)
{
-#if 0
- // traverse hierarchy and update render transforms from scratch
- for (auto& scene : mScenes)
- {
- scene.updateRenderTransforms(*this, modelview);
- }
-#else
// use mAssetMatrix to update render transforms from node list
for (auto& node : mNodes)
{
- //if (node.mMesh != INVALID_INDEX)
- {
- matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
- }
+ node.mRenderMatrix = modelview * node.mAssetMatrix;
}
-
-#endif
-
}
S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
@@ -398,9 +422,11 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
bool newHit = false;
+ LLMatrix4a ami;
+ ami.loadu(glm::value_ptr(node.mAssetMatrixInv));
// transform start and end to this node's local space
- node.mAssetMatrixInv.affineTransform(start, local_start);
- node.mAssetMatrixInv.affineTransform(asset_end, local_end);
+ ami.affineTransform(start, local_start);
+ ami.affineTransform(asset_end, local_end);
Mesh& mesh = mMeshes[node.mMesh];
for (auto& primitive : mesh.mPrimitives)
@@ -423,8 +449,10 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
if (newHit)
{
+ LLMatrix4a am;
+ am.loadu(glm::value_ptr(node.mAssetMatrix));
// shorten line segment on hit
- node.mAssetMatrix.affineTransform(p, asset_end);
+ am.affineTransform(p, asset_end);
// transform results back to asset space
if (intersection)
@@ -434,12 +462,10 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
if (normal || tangent)
{
- LLMatrix4 normalMatrix(node.mAssetMatrixInv.getF32ptr());
-
- normalMatrix.transpose();
+ mat4 normalMatrix = glm::transpose(node.mAssetMatrixInv);
LLMatrix4a norm_mat;
- norm_mat.loadu((F32*)normalMatrix.mMatrix);
+ norm_mat.loadu(glm::value_ptr(normalMatrix));
if (normal)
{
@@ -481,22 +507,7 @@ void Node::makeMatrixValid()
{
if (!mMatrixValid && mTRSValid)
{
- glh::matrix4f rot;
- mRotation.get_value(rot);
-
- glh::matrix4f trans;
- trans.set_translate(mTranslation);
-
- glh::matrix4f sc;
- sc.set_scale(mScale);
-
- glh::matrix4f t;
- //t = sc * rot * trans;
- //t = trans * rot * sc; // best so far, still wrong on negative scale
- //t = sc * trans * rot;
- t = trans * sc * rot;
-
- mMatrix.loadu(t.m);
+ mMatrix = glm::recompose(mScale, mRotation, mTranslation, vec3(0,0,0), vec4(0,0,0,1));
mMatrixValid = true;
}
@@ -507,43 +518,72 @@ void Node::makeTRSValid()
{
if (!mTRSValid && mMatrixValid)
{
- glh::matrix4f t(mMatrix.getF32ptr());
-
- glh::vec4f p = t.get_column(3);
- mTranslation.set_value(p.v[0], p.v[1], p.v[2]);
-
- mScale.set_value(t.get_column(0).length(), t.get_column(1).length(), t.get_column(2).length());
- mRotation.set_value(t);
+ vec3 skew;
+ vec4 perspective;
+ glm::decompose(mMatrix, mScale, mRotation, mTranslation, skew, perspective);
+
mTRSValid = true;
}
llassert(mTRSValid);
}
-void Node::setRotation(const glh::quaternionf& q)
+void Node::setRotation(const quat& q)
{
makeTRSValid();
mRotation = q;
mMatrixValid = false;
}
-void Node::setTranslation(const glh::vec3f& t)
+void Node::setTranslation(const vec3& t)
{
makeTRSValid();
mTranslation = t;
mMatrixValid = false;
}
-void Node::setScale(const glh::vec3f& s)
+void Node::setScale(const vec3& s)
{
makeTRSValid();
mScale = s;
mMatrixValid = false;
}
+void Node::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mMatrix, "matrix", dst, glm::identity<mat4>());
+ write(mRotation, "rotation", dst);
+ write(mTranslation, "translation", dst);
+ write(mScale, "scale", dst, vec3(1.f,1.f,1.f));
+ write(mChildren, "children", dst);
+ write(mMesh, "mesh", dst, INVALID_INDEX);
+ write(mSkin, "skin", dst, INVALID_INDEX);
+}
+
+const Node& Node::operator=(const Value& src)
+{
+ copy(src, "name", mName);
+ mMatrixValid = copy(src, "matrix", mMatrix);
+
+ copy(src, "rotation", mRotation);
+ copy(src, "translation", mTranslation);
+ copy(src, "scale", mScale);
+ copy(src, "children", mChildren);
+ copy(src, "mesh", mMesh);
+ copy(src, "skin", mSkin);
+
+ if (!mMatrixValid)
+ {
+ mTRSValid = true;
+ }
+
+ return *this;
+}
+
const Node& Node::operator=(const tinygltf::Node& src)
{
- F32* dstMatrix = mMatrix.getF32ptr();
+ F32* dstMatrix = glm::value_ptr(mMatrix);
if (src.matrix.size() == 16)
{
@@ -560,22 +600,21 @@ const Node& Node::operator=(const tinygltf::Node& src)
// node has rotation/translation/scale, convert to matrix
if (src.rotation.size() == 4)
{
- mRotation = glh::quaternionf((F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2], (F32)src.rotation[3]);
+ mRotation = quat((F32)src.rotation[3], (F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2]);
}
if (src.translation.size() == 3)
{
- mTranslation = glh::vec3f((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]);
+ mTranslation = vec3((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]);
}
- glh::vec3f scale;
if (src.scale.size() == 3)
{
- mScale = glh::vec3f((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]);
+ mScale = vec3((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]);
}
else
{
- mScale.set_value(1.f, 1.f, 1.f);
+ mScale = vec3(1.f, 1.f, 1.f);
}
mTRSValid = true;
@@ -583,7 +622,7 @@ const Node& Node::operator=(const tinygltf::Node& src)
else
{
// node specifies no transformation, set to identity
- mMatrix.setIdentity();
+ mMatrix = glm::identity<mat4>();
mMatrixValid = true;
}
@@ -595,6 +634,50 @@ const Node& Node::operator=(const tinygltf::Node& src)
return *this;
}
+void Image::serialize(object& dst) const
+{
+ write(mUri, "uri", dst);
+ write(mMimeType, "mimeType", dst);
+ write(mBufferView, "bufferView", dst, INVALID_INDEX);
+ write(mName, "name", dst);
+ write(mWidth, "width", dst, -1);
+ write(mHeight, "height", dst, -1);
+ write(mComponent, "component", dst, -1);
+ write(mBits, "bits", dst, -1);
+ write(mPixelType, "pixelType", dst, -1);
+}
+
+const Image& Image::operator=(const Value& src)
+{
+ copy(src, "uri", mUri);
+ copy(src, "mimeType", mMimeType);
+ copy(src, "bufferView", mBufferView);
+ copy(src, "name", mName);
+ copy(src, "width", mWidth);
+ copy(src, "height", mHeight);
+ copy(src, "component", mComponent);
+ copy(src, "bits", mBits);
+ copy(src, "pixelType", mPixelType);
+
+ return *this;
+}
+
+const Image& Image::operator=(const tinygltf::Image& src)
+{
+ mName = src.name;
+ mWidth = src.width;
+ mHeight = src.height;
+ mComponent = src.component;
+ mBits = src.bits;
+ mPixelType = src.pixel_type;
+ mUri = src.uri;
+ mBufferView = src.bufferView;
+ mMimeType = src.mimeType;
+ mData = src.image;
+ return *this;
+}
+
+
void Asset::render(bool opaque, bool rigged)
{
if (rigged)
@@ -623,7 +706,6 @@ void Asset::render(bool opaque, bool rigged)
continue;
}
-
if (node.mMesh != INVALID_INDEX)
{
Mesh& mesh = mMeshes[node.mMesh];
@@ -631,19 +713,28 @@ void Asset::render(bool opaque, bool rigged)
{
if (!rigged)
{
- gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix);
+ gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
}
bool cull = true;
if (primitive.mMaterial != INVALID_INDEX)
{
Material& material = mMaterials[primitive.mMaterial];
+ bool mat_opaque = material.mAlphaMode != Material::AlphaMode::BLEND;
- if ((material.mMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_BLEND) == opaque)
+ if (mat_opaque != opaque)
{
continue;
}
- material.mMaterial->bind();
- cull = !material.mMaterial->mDoubleSided;
+
+ if (mMaterials[primitive.mMaterial].mMaterial.notNull())
+ {
+ material.mMaterial->bind();
+ }
+ else
+ {
+ material.bind(*this);
+ }
+ cull = !material.mDoubleSided;
}
else
{
@@ -709,11 +800,16 @@ void Asset::allocateGLResources(const std::string& filename, const tinygltf::Mod
image.allocateGLResources();
}
+
// do materials before meshes as meshes may depend on materials
- for (U32 i = 0; i < mMaterials.size(); ++i)
+ if (!filename.empty())
{
- mMaterials[i].allocateGLResources(*this);
- LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true);
+ for (U32 i = 0; i < mMaterials.size(); ++i)
+ {
+ // HACK: local preview mode, load material from model for now
+ mMaterials[i].allocateGLResources(*this);
+ LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true);
+ }
}
for (auto& mesh : mMeshes)
@@ -732,16 +828,26 @@ void Asset::allocateGLResources(const std::string& filename, const tinygltf::Mod
}
}
+Asset::Asset(const tinygltf::Model& src)
+{
+ *this = src;
+}
+
+Asset::Asset(const Value& src)
+{
+ *this = src;
+}
+
const Asset& Asset::operator=(const tinygltf::Model& src)
{
mVersion = src.asset.version;
mMinVersion = src.asset.minVersion;
mGenerator = src.asset.generator;
mCopyright = src.asset.copyright;
- mExtras = src.asset.extras;
+
+ // note: extras are lost in the conversion for now
mDefaultScene = src.defaultScene;
-
mScenes.resize(src.scenes.size());
for (U32 i = 0; i < src.scenes.size(); ++i)
@@ -818,11 +924,69 @@ const Asset& Asset::operator=(const tinygltf::Model& src)
return *this;
}
+const Asset& Asset::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ const object& obj = src.as_object();
+
+ const auto it = obj.find("asset");
+
+ if (it != obj.end())
+ {
+ const Value& asset = it->value();
+
+ copy(asset, "version", mVersion);
+ copy(asset, "minVersion", mMinVersion);
+ copy(asset, "generator", mGenerator);
+ copy(asset, "copyright", mCopyright);
+ copy(asset, "extras", mExtras);
+ }
+
+ copy(obj, "defaultScene", mDefaultScene);
+ copy(obj, "scenes", mScenes);
+ copy(obj, "nodes", mNodes);
+ copy(obj, "meshes", mMeshes);
+ copy(obj, "materials", mMaterials);
+ copy(obj, "buffers", mBuffers);
+ copy(obj, "bufferViews", mBufferViews);
+ copy(obj, "textures", mTextures);
+ copy(obj, "samplers", mSamplers);
+ copy(obj, "images", mImages);
+ copy(obj, "accessors", mAccessors);
+ copy(obj, "animations", mAnimations);
+ copy(obj, "skins", mSkins);
+ }
+
+ return *this;
+}
+
void Asset::save(tinygltf::Model& dst)
{
LL::GLTF::copy(*this, dst);
}
+void Asset::serialize(object& dst) const
+{
+ write(mVersion, "version", dst);
+ write(mMinVersion, "minVersion", dst, std::string());
+ write(mGenerator, "generator", dst);
+ write(mDefaultScene, "defaultScene", dst, 0);
+
+ write(mScenes, "scenes", dst);
+ write(mNodes, "nodes", dst);
+ write(mMeshes, "meshes", dst);
+ write(mMaterials, "materials", dst);
+ write(mBuffers, "buffers", dst);
+ write(mBufferViews, "bufferViews", dst);
+ write(mTextures, "textures", dst);
+ write(mSamplers, "samplers", dst);
+ write(mImages, "images", dst);
+ write(mAccessors, "accessors", dst);
+ write(mAnimations, "animations", dst);
+ write(mSkins, "skins", dst);
+}
+
void Asset::decompose(const std::string& filename)
{
// get folder path
@@ -857,6 +1021,41 @@ void Asset::eraseBufferView(S32 bufferView)
}
+LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
+
+void Image::allocateGLResources()
+{
+ LLUUID id;
+ if (LLUUID::parseUUID(mUri, &id) && id.notNull())
+ {
+ mTexture = fetch_texture(id);
+ }
+}
+
+
+void Image::clearData(Asset& asset)
+{
+ if (mBufferView != INVALID_INDEX)
+ {
+ // remove data from buffer
+ BufferView& bufferView = asset.mBufferViews[mBufferView];
+ Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength);
+
+ asset.eraseBufferView(mBufferView);
+ }
+
+ mData.clear();
+ mBufferView = INVALID_INDEX;
+ mWidth = -1;
+ mHeight = -1;
+ mComponent = -1;
+ mBits = -1;
+ mPixelType = -1;
+ mMimeType = "";
+}
+
void Image::decompose(Asset& asset, const std::string& folder)
{
std::string name = mName;
@@ -894,12 +1093,9 @@ void Image::decompose(Asset& asset, const std::string& folder)
std::ofstream file(filename, std::ios::binary);
file.write((const char*)buffer.mData.data() + bufferView.mByteOffset, bufferView.mByteLength);
-
- buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength);
-
- asset.eraseBufferView(mBufferView);
}
+#if 0
if (!mData.empty())
{
// save j2c image
@@ -907,21 +1103,44 @@ void Image::decompose(Asset& asset, const std::string& folder)
LLPointer<LLImageRaw> raw = new LLImageRaw(mWidth, mHeight, mComponent);
U8* data = raw->allocateData();
- llassert(mData.size() == raw->getDataSize());
+ llassert_always(mData.size() == raw->getDataSize());
memcpy(data, mData.data(), mData.size());
LLViewerTextureList::createUploadFile(raw, filename, 4096);
mData.clear();
}
+#endif
- mWidth = -1;
- mHeight = -1;
- mComponent = -1;
- mBits = -1;
- mPixelType = -1;
- mMimeType = "";
+ clearData(asset);
+}
+
+void Material::TextureInfo::serialize(object& dst) const
+{
+ write(mIndex, "index", dst, INVALID_INDEX);
+ write(mTexCoord, "texCoord", dst, 0);
+}
+
+const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "index", mIndex);
+ copy(src, "texCoord", mTexCoord);
+ }
+
+ return *this;
+}
+
+bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const
+{
+ return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord;
+}
+
+bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const
+{
+ return !(*this == rhs);
}
const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::TextureInfo& src)
@@ -931,6 +1150,25 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const tinygltf::Te
return *this;
}
+void Material::OcclusionTextureInfo::serialize(object& dst) const
+{
+ write(mIndex, "index", dst, INVALID_INDEX);
+ write(mTexCoord, "texCoord", dst, 0);
+ write(mStrength, "strength", dst, 1.f);
+}
+
+const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "index", mIndex);
+ copy(src, "texCoord", mTexCoord);
+ copy(src, "strength", mStrength);
+ }
+
+ return *this;
+}
+
const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const tinygltf::OcclusionTextureInfo& src)
{
mIndex = src.index;
@@ -939,6 +1177,24 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(
return *this;
}
+void Material::NormalTextureInfo::serialize(object& dst) const
+{
+ write(mIndex, "index", dst, INVALID_INDEX);
+ write(mTexCoord, "texCoord", dst, 0);
+ write(mScale, "scale", dst, 1.f);
+}
+
+const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "index", mIndex);
+ copy(src, "texCoord", mTexCoord);
+ copy(src, "scale", mScale);
+ }
+
+ return *this;
+}
const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const tinygltf::NormalTextureInfo& src)
{
mIndex = src.index;
@@ -947,11 +1203,48 @@ const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const
return *this;
}
+const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "baseColorFactor", mBaseColorFactor);
+ copy(src, "baseColorTexture", mBaseColorTexture);
+ copy(src, "metallicFactor", mMetallicFactor);
+ copy(src, "roughnessFactor", mRoughnessFactor);
+ copy(src, "metallicRoughnessTexture", mMetallicRoughnessTexture);
+ }
+
+ return *this;
+}
+
+void Material::PbrMetallicRoughness::serialize(object& dst) const
+{
+ write(mBaseColorFactor, "baseColorFactor", dst, vec4(1.f, 1.f, 1.f, 1.f));
+ write(mBaseColorTexture, "baseColorTexture", dst);
+ write(mMetallicFactor, "metallicFactor", dst, 1.f);
+ write(mRoughnessFactor, "roughnessFactor", dst, 1.f);
+ write(mMetallicRoughnessTexture, "metallicRoughnessTexture", dst);
+}
+
+bool Material::PbrMetallicRoughness::operator==(const Material::PbrMetallicRoughness& rhs) const
+{
+ return mBaseColorFactor == rhs.mBaseColorFactor &&
+ mBaseColorTexture == rhs.mBaseColorTexture &&
+ mMetallicFactor == rhs.mMetallicFactor &&
+ mRoughnessFactor == rhs.mRoughnessFactor &&
+ mMetallicRoughnessTexture == rhs.mMetallicRoughnessTexture;
+}
+
+bool Material::PbrMetallicRoughness::operator!=(const Material::PbrMetallicRoughness& rhs) const
+{
+ return !(*this == rhs);
+}
+
const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const tinygltf::PbrMetallicRoughness& src)
{
if (src.baseColorFactor.size() == 4)
{
- mBaseColorFactor.set_value(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]);
+ mBaseColorFactor = vec4(src.baseColorFactor[0], src.baseColorFactor[1], src.baseColorFactor[2], src.baseColorFactor[3]);
}
mBaseColorTexture = src.baseColorTexture;
@@ -961,13 +1254,129 @@ const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(
return *this;
}
+
+static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback)
+{
+ if (info.mIndex != INVALID_INDEX)
+ {
+ LLViewerTexture* tex = asset.mImages[asset.mTextures[info.mIndex].mSource].mTexture;
+ if (tex)
+ {
+ tex->addTextureStats(2048.f * 2048.f);
+ LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex);
+ }
+ else
+ {
+ LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback);
+ }
+ }
+ else
+ {
+ LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback);
+ }
+}
+
+void Material::bind(Asset& asset)
+{
+ // bind for rendering (derived from LLFetchedGLTFMaterial::bind)
+ // glTF 2.0 Specification 3.9.4. Alpha Coverage
+ // mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK
+ F32 min_alpha = -1.0;
+
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+
+ if (!LLPipeline::sShadowRender || (mAlphaMode == Material::AlphaMode::BLEND))
+ {
+ if (mAlphaMode == Material::AlphaMode::MASK)
+ {
+ // dividing the alpha cutoff by transparency here allows the shader to compare against
+ // the alpha value of the texture without needing the transparency value
+ if (mPbrMetallicRoughness.mBaseColorFactor.a > 0.f)
+ {
+ min_alpha = mAlphaCutoff / mPbrMetallicRoughness.mBaseColorFactor.a;
+ }
+ else
+ {
+ min_alpha = 1024.f;
+ }
+ }
+ shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha);
+ }
+
+ bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep);
+
+ F32 base_color_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed);
+
+ if (!LLPipeline::sShadowRender)
+ {
+ bindTexture(asset, LLShaderMgr::BUMP_MAP, mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep);
+ bindTexture(asset, LLShaderMgr::SPECULAR_MAP, mPbrMetallicRoughness.mMetallicRoughnessTexture, LLViewerFetchedTexture::sWhiteImagep);
+ bindTexture(asset, LLShaderMgr::EMISSIVE_MAP, mEmissiveTexture, LLViewerFetchedTexture::sWhiteImagep);
+
+ // NOTE: base color factor is baked into vertex stream
+
+ shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, mPbrMetallicRoughness.mRoughnessFactor);
+ shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, mPbrMetallicRoughness.mMetallicFactor);
+ shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(mEmissiveFactor));
+
+ F32 normal_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, (F32*)normal_packed);
+
+ F32 metallic_roughness_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, (F32*)metallic_roughness_packed);
+
+ F32 emissive_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed);
+ }
+}
+
+void Material::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mEmissiveFactor, "emissiveFactor", dst, vec3(0.f, 0.f, 0.f));
+ write(mPbrMetallicRoughness, "pbrMetallicRoughness", dst);
+ write(mNormalTexture, "normalTexture", dst);
+ write(mOcclusionTexture, "occlusionTexture", dst);
+ write(mEmissiveTexture, "emissiveTexture", dst);
+ write(mAlphaMode, "alphaMode", dst, Material::AlphaMode::OPAQUE);
+ write(mAlphaCutoff, "alphaCutoff", dst, 0.5f);
+ write(mDoubleSided, "doubleSided", dst, false);
+}
+
+const Material& Material::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "emissiveFactor", mEmissiveFactor);
+ copy(src, "pbrMetallicRoughness", mPbrMetallicRoughness);
+ copy(src, "normalTexture", mNormalTexture);
+ copy(src, "occlusionTexture", mOcclusionTexture);
+ copy(src, "emissiveTexture", mEmissiveTexture);
+ copy(src, "alphaMode", mAlphaMode);
+ copy(src, "alphaCutoff", mAlphaCutoff);
+ copy(src, "doubleSided", mDoubleSided);
+ }
+ return *this;
+}
+
+
const Material& Material::operator=(const tinygltf::Material& src)
{
mName = src.name;
if (src.emissiveFactor.size() == 3)
{
- mEmissiveFactor.set_value(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]);
+ mEmissiveFactor = vec3(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]);
}
mPbrMetallicRoughness = src.pbrMetallicRoughness;
@@ -975,7 +1384,7 @@ const Material& Material::operator=(const tinygltf::Material& src)
mOcclusionTexture = src.occlusionTexture;
mEmissiveTexture = src.emissiveTexture;
- mAlphaMode = src.alphaMode;
+ mAlphaMode = gltf_alpha_mode_to_enum(src.alphaMode);
mAlphaCutoff = src.alphaCutoff;
mDoubleSided = src.doubleSided;
@@ -984,10 +1393,31 @@ const Material& Material::operator=(const tinygltf::Material& src)
void Material::allocateGLResources(Asset& asset)
{
- // allocate material
+ // HACK: allocate an LLFetchedGLTFMaterial for now
+ // later we'll render directly from the GLTF Images
+ // and BufferViews
mMaterial = new LLFetchedGLTFMaterial();
}
+void Mesh::serialize(object& dst) const
+{
+ write(mPrimitives, "primitives", dst);
+ write(mWeights, "weights", dst);
+ write(mName, "name", dst);
+}
+
+const Mesh& Mesh::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "primitives", mPrimitives);
+ copy(src, "weights", mWeights);
+ copy(src, "name", mName);
+ }
+
+ return *this;
+
+}
const Mesh& Mesh::operator=(const tinygltf::Mesh& src)
{
mPrimitives.resize(src.primitives.size());
@@ -1010,6 +1440,20 @@ void Mesh::allocateGLResources(Asset& asset)
}
}
+void Scene::serialize(object& dst) const
+{
+ write(mNodes, "nodes", dst);
+ write(mName, "name", dst);
+}
+
+const Scene& Scene::operator=(const Value& src)
+{
+ copy(src, "nodes", mNodes);
+ copy(src, "name", mName);
+
+ return *this;
+}
+
const Scene& Scene::operator=(const tinygltf::Scene& src)
{
mNodes = src.nodes;
@@ -1018,6 +1462,25 @@ const Scene& Scene::operator=(const tinygltf::Scene& src)
return *this;
}
+void Texture::serialize(object& dst) const
+{
+ write(mSampler, "sampler", dst, INVALID_INDEX);
+ write(mSource, "source", dst, INVALID_INDEX);
+ write(mName, "name", dst);
+}
+
+const Texture& Texture::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "sampler", mSampler);
+ copy(src, "source", mSource);
+ copy(src, "name", mName);
+ }
+
+ return *this;
+}
+
const Texture& Texture::operator=(const tinygltf::Texture& src)
{
mSampler = src.sampler;
@@ -1027,6 +1490,27 @@ const Texture& Texture::operator=(const tinygltf::Texture& src)
return *this;
}
+
+void Sampler::serialize(object& dst) const
+{
+ write(mMagFilter, "magFilter", dst, LINEAR);
+ write(mMinFilter, "minFilter", dst, LINEAR_MIPMAP_LINEAR);
+ write(mWrapS, "wrapS", dst, REPEAT);
+ write(mWrapT, "wrapT", dst, REPEAT);
+ write(mName, "name", dst);
+}
+
+const Sampler& Sampler::operator=(const Value& src)
+{
+ copy(src, "magFilter", mMagFilter);
+ copy(src, "minFilter", mMinFilter);
+ copy(src, "wrapS", mWrapS);
+ copy(src, "wrapT", mWrapT);
+ copy(src, "name", mName);
+
+ return *this;
+}
+
const Sampler& Sampler::operator=(const tinygltf::Sampler& src)
{
mMagFilter = src.magFilter;
@@ -1043,23 +1527,14 @@ void Skin::uploadMatrixPalette(Asset& asset, Node& node)
// prepare matrix palette
// modelview will be applied by the shader, so assume matrix palette is in asset space
- std::vector<glh::matrix4f> t_mp;
+ std::vector<mat4> t_mp;
t_mp.resize(mJoints.size());
for (U32 i = 0; i < mJoints.size(); ++i)
{
Node& joint = asset.mNodes[mJoints[i]];
-
- //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr());
- //t_mp[i] = t_mp[i] * mInverseBindMatricesData[i];
-
- //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr());
- //t_mp[i] = mInverseBindMatricesData[i] * t_mp[i];
-
- t_mp[i].set_value(joint.mRenderMatrix.getF32ptr());
- t_mp[i] = t_mp[i] * mInverseBindMatricesData[i];
-
+ t_mp[i] = joint.mRenderMatrix * mInverseBindMatricesData[i];
}
std::vector<F32> glmp;
@@ -1070,7 +1545,7 @@ void Skin::uploadMatrixPalette(Asset& asset, Node& node)
for (U32 i = 0; i < mJoints.size(); ++i)
{
- F32* m = (F32*)t_mp[i].m;
+ F32* m = glm::value_ptr(t_mp[i]);
U32 idx = i * 12;
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index 74f4b246a5..990e1e41a9 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -32,9 +32,16 @@
#include "accessor.h"
#include "primitive.h"
#include "animation.h"
+#include "boost/json.hpp"
+#include "common.h"
extern F32SecondsImplicit gFrameTimeSeconds;
+// wingdi defines OPAQUE, which conflicts with our enum
+#if defined(OPAQUE)
+#undef OPAQUE
+#endif
+
// LL GLTF Implementation
namespace LL
{
@@ -45,13 +52,26 @@ namespace LL
class Material
{
public:
+
+ enum class AlphaMode
+ {
+ OPAQUE,
+ MASK,
+ BLEND
+ };
+
class TextureInfo
{
public:
S32 mIndex = INVALID_INDEX;
S32 mTexCoord = 0;
+ bool operator==(const TextureInfo& rhs) const;
+ bool operator!=(const TextureInfo& rhs) const;
+
const TextureInfo& operator=(const tinygltf::TextureInfo& src);
+ const TextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
class NormalTextureInfo : public TextureInfo
@@ -60,6 +80,8 @@ namespace LL
F32 mScale = 1.0f;
const NormalTextureInfo& operator=(const tinygltf::NormalTextureInfo& src);
+ const NormalTextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
class OcclusionTextureInfo : public TextureInfo
@@ -68,17 +90,24 @@ namespace LL
F32 mStrength = 1.0f;
const OcclusionTextureInfo& operator=(const tinygltf::OcclusionTextureInfo& src);
+ const OcclusionTextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
class PbrMetallicRoughness
{
public:
- glh::vec4f mBaseColorFactor = glh::vec4f(1.f,1.f,1.f,1.f);
+ vec4 mBaseColorFactor = vec4(1.f,1.f,1.f,1.f);
TextureInfo mBaseColorTexture;
F32 mMetallicFactor = 1.0f;
F32 mRoughnessFactor = 1.0f;
TextureInfo mMetallicRoughnessTexture;
+
+ bool operator==(const PbrMetallicRoughness& rhs) const;
+ bool operator!=(const PbrMetallicRoughness& rhs) const;
const PbrMetallicRoughness& operator=(const tinygltf::PbrMetallicRoughness& src);
+ const PbrMetallicRoughness& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
@@ -93,13 +122,16 @@ namespace LL
std::string mName;
- glh::vec3f mEmissiveFactor = glh::vec3f(0.f, 0.f, 0.f);
- std::string mAlphaMode = "OPAQUE";
+ vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f);
+ AlphaMode mAlphaMode = AlphaMode::OPAQUE;
F32 mAlphaCutoff = 0.5f;
bool mDoubleSided = false;
-
+ // bind for rendering
+ void bind(Asset& asset);
const Material& operator=(const tinygltf::Material& src);
+ const Material& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
void allocateGLResources(Asset& asset);
};
@@ -112,6 +144,8 @@ namespace LL
std::string mName;
const Mesh& operator=(const tinygltf::Mesh& src);
+ const Mesh& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
void allocateGLResources(Asset& asset);
};
@@ -119,14 +153,14 @@ namespace LL
class Node
{
public:
- LLMatrix4a mMatrix; //local transform
- LLMatrix4a mRenderMatrix; //transform for rendering
- LLMatrix4a mAssetMatrix; //transform from local to asset space
- LLMatrix4a mAssetMatrixInv; //transform from asset to local space
+ mat4 mMatrix = glm::identity<mat4>(); //local transform
+ mat4 mRenderMatrix; //transform for rendering
+ mat4 mAssetMatrix; //transform from local to asset space
+ mat4 mAssetMatrixInv; //transform from asset to local space
- glh::vec3f mTranslation;
- glh::quaternionf mRotation;
- glh::vec3f mScale;
+ vec3 mTranslation = vec3(0,0,0);
+ quat mRotation = glm::identity<quat>();
+ vec3 mScale = vec3(1.f,1.f,1.f);
// if true, mMatrix is valid and up to date
bool mMatrixValid = false;
@@ -145,13 +179,15 @@ namespace LL
std::string mName;
const Node& operator=(const tinygltf::Node& src);
+ const Node& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
// Set mRenderMatrix to a transform that can be used for the current render pass
// modelview -- parent's render matrix
- void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview);
+ void updateRenderTransforms(Asset& asset, const mat4& modelview);
// update mAssetMatrix and mAssetMatrixInv
- void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix);
+ void updateTransforms(Asset& asset, const mat4& parentMatrix);
// ensure mMatrix is valid -- if mMatrixValid is false and mTRSValid is true, will update mMatrix to match Translation/Rotation/Scale
void makeMatrixValid();
@@ -161,15 +197,15 @@ namespace LL
// Set rotation of this node
// SIDE EFFECT: invalidates mMatrix
- void setRotation(const glh::quaternionf& rotation);
+ void setRotation(const quat& rotation);
// Set translation of this node
// SIDE EFFECT: invalidates mMatrix
- void setTranslation(const glh::vec3f& translation);
+ void setTranslation(const vec3& translation);
// Set scale of this node
// SIDE EFFECT: invalidates mMatrix
- void setScale(const glh::vec3f& scale);
+ void setScale(const vec3& scale);
};
class Skin
@@ -179,12 +215,14 @@ namespace LL
S32 mSkeleton = INVALID_INDEX;
std::vector<S32> mJoints;
std::string mName;
- std::vector<glh::matrix4f> mInverseBindMatricesData;
+ std::vector<mat4> mInverseBindMatricesData;
void allocateGLResources(Asset& asset);
void uploadMatrixPalette(Asset& asset, Node& node);
const Skin& operator=(const tinygltf::Skin& src);
+ const Skin& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
class Scene
@@ -194,9 +232,11 @@ namespace LL
std::string mName;
const Scene& operator=(const tinygltf::Scene& src);
+ const Scene& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
void updateTransforms(Asset& asset);
- void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview);
+ void updateRenderTransforms(Asset& asset, const mat4& modelview);
};
class Texture
@@ -207,18 +247,22 @@ namespace LL
std::string mName;
const Texture& operator=(const tinygltf::Texture& src);
+ const Texture& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
class Sampler
{
public:
- S32 mMagFilter;
- S32 mMinFilter;
- S32 mWrapS;
- S32 mWrapT;
+ S32 mMagFilter = LINEAR;
+ S32 mMinFilter = LINEAR_MIPMAP_LINEAR;
+ S32 mWrapS = REPEAT;
+ S32 mWrapT = REPEAT;
std::string mName;
const Sampler& operator=(const tinygltf::Sampler& src);
+ const Sampler& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
};
class Image
@@ -231,43 +275,35 @@ namespace LL
S32 mBufferView = INVALID_INDEX;
std::vector<U8> mData;
- S32 mWidth;
- S32 mHeight;
- S32 mComponent;
- S32 mBits;
- S32 mPixelType;
+ S32 mWidth = -1;
+ S32 mHeight = -1;
+ S32 mComponent = -1;
+ S32 mBits = -1;
+ S32 mPixelType = -1;
LLPointer<LLViewerFetchedTexture> mTexture;
- const Image& operator=(const tinygltf::Image& src)
- {
- mName = src.name;
- mUri = src.uri;
- mMimeType = src.mimeType;
- mData = src.image;
- mWidth = src.width;
- mHeight = src.height;
- mComponent = src.component;
- mBits = src.bits;
- mBufferView = src.bufferView;
- mPixelType = src.pixel_type;
- return *this;
- }
+ const Image& operator=(const tinygltf::Image& src);
+ const Image& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
// save image clear local data, and set uri
void decompose(Asset& asset, const std::string& filename);
- void allocateGLResources()
- {
- // allocate texture
+ // erase the buffer view associated with this image
+ // free any associated resources
+ // preserve only uri and name
+ void clearData(Asset& asset);
- }
+ void allocateGLResources();
};
// C++ representation of a GLTF Asset
class Asset
{
public:
+
+ static const std::string minVersion_default;
std::vector<Scene> mScenes;
std::vector<Node> mNodes;
std::vector<Mesh> mMeshes;
@@ -287,14 +323,15 @@ namespace LL
std::string mCopyright;
S32 mDefaultScene = INVALID_INDEX;
- tinygltf::Value mExtras;
+ Value mExtras;
+ U32 mPendingBuffers = 0;
// the last time update() was called according to gFrameTimeSeconds
F32 mLastUpdateTime = gFrameTimeSeconds;
// prepare the asset for rendering
- void allocateGLResources(const std::string& filename, const tinygltf::Model& model);
+ void allocateGLResources(const std::string& filename = "", const tinygltf::Model& model = tinygltf::Model());
// Called periodically (typically once per frame)
// Any ongoing work (such as animations) should be handled here
@@ -307,7 +344,7 @@ namespace LL
void updateTransforms();
// update node render transforms
- void updateRenderTransforms(const LLMatrix4a& modelview);
+ void updateRenderTransforms(const mat4& modelview);
void render(bool opaque, bool rigged = false);
void renderOpaque();
@@ -323,7 +360,13 @@ namespace LL
S32* primitive_hitp = nullptr // return the index of the primitive that was hit
);
+ Asset() = default;
+ Asset(const tinygltf::Model& src);
+ Asset(const Value& src);
+
const Asset& operator=(const tinygltf::Model& src);
+ const Asset& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
// save the asset to a tinygltf model
void save(tinygltf::Model& dst);
@@ -335,5 +378,8 @@ namespace LL
// updates all bufferview indices in this Asset as needed
void eraseBufferView(S32 bufferView);
};
+
+ Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode);
+ std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode);
}
}
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index 4e6f5901e7..b0fbc8524d 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -36,55 +36,60 @@
#define LL_FUNCSIG __PRETTY_FUNCTION__
#endif
+#include "accessor.h"
+
namespace LL
{
namespace GLTF
{
+
+ using string_view = boost::json::string_view;
+
// copy one Scalar from src to dst
template<class S, class T>
- static void copyScalar(S* src, T& dst)
+ inline void copyScalar(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
// copy one vec2 from src to dst
template<class S, class T>
- static void copyVec2(S* src, T& dst)
+ inline void copyVec2(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
// copy one vec3 from src to dst
template<class S, class T>
- static void copyVec3(S* src, T& dst)
+ inline void copyVec3(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
// copy one vec4 from src to dst
template<class S, class T>
- static void copyVec4(S* src, T& dst)
+ inline void copyVec4(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
- // copy one vec2 from src to dst
+ // copy one mat2 from src to dst
template<class S, class T>
- static void copyMat2(S* src, T& dst)
+ inline void copyMat2(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
- // copy one vec3 from src to dst
+ // copy one mat3 from src to dst
template<class S, class T>
- static void copyMat3(S* src, T& dst)
+ inline void copyMat3(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
- // copy one vec4 from src to dst
+ // copy one mat4 from src to dst
template<class S, class T>
- static void copyMat4(S* src, T& dst)
+ inline void copyMat4(S* src, T& dst)
{
LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
}
@@ -93,135 +98,128 @@ namespace LL
// concrete implementations for different types of source and destination
//=========================================================================================================
-// suppress unused function warning -- clang complains here but these specializations are definitely used
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-#endif
-
template<>
- void copyScalar<F32, F32>(F32* src, F32& dst)
+ inline void copyScalar<F32, F32>(F32* src, F32& dst)
{
dst = *src;
}
template<>
- void copyScalar<U32, U32>(U32* src, U32& dst)
+ inline void copyScalar<U32, U32>(U32* src, U32& dst)
{
dst = *src;
}
template<>
- void copyScalar<U32, U16>(U32* src, U16& dst)
+ inline void copyScalar<U32, U16>(U32* src, U16& dst)
{
dst = *src;
}
template<>
- void copyScalar<U16, U16>(U16* src, U16& dst)
+ inline void copyScalar<U16, U16>(U16* src, U16& dst)
{
dst = *src;
}
template<>
- void copyScalar<U16, U32>(U16* src, U32& dst)
+ inline void copyScalar<U16, U32>(U16* src, U32& dst)
{
dst = *src;
}
template<>
- void copyScalar<U8, U16>(U8* src, U16& dst)
+ inline void copyScalar<U8, U16>(U8* src, U16& dst)
{
dst = *src;
}
template<>
- void copyScalar<U8, U32>(U8* src, U32& dst)
+ inline void copyScalar<U8, U32>(U8* src, U32& dst)
{
dst = *src;
}
template<>
- void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst)
+ inline void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst)
{
dst.set(src[0], src[1]);
}
template<>
- void copyVec3<F32, glh::vec3f>(F32* src, glh::vec3f& dst)
+ inline void copyVec3<F32, vec3>(F32* src, vec3& dst)
{
- dst.set_value(src[0], src[1], src[2]);
+ dst = vec3(src[0], src[1], src[2]);
}
template<>
- void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst)
+ inline void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst)
{
dst.load3(src);
}
template<>
- void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
+ inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
{
dst.set(src[0], src[1], src[2], 255);
}
template<>
- void copyVec4<U8, LLColor4U>(U8* src, LLColor4U& dst)
+ inline void copyVec4<U8, LLColor4U>(U8* src, LLColor4U& dst)
{
dst.set(src[0], src[1], src[2], src[3]);
}
template<>
- void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst)
+ inline void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst)
{
dst.set(src[0], src[1], src[2], src[3]);
}
template<>
- void copyVec4<F32, LLColor4U>(F32* src, LLColor4U& dst)
+ inline void copyVec4<F32, LLColor4U>(F32* src, LLColor4U& dst)
{
dst.set(src[0]*255, src[1]*255, src[2]*255, src[3]*255);
}
template<>
- void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst)
+ inline void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst)
{
dst.loadua(src);
}
template<>
- void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst)
+ inline void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst)
{
dst.set(src[0], src[1], src[2], src[3]);
}
template<>
- void copyVec4<U8, LLVector4a>(U8* src, LLVector4a& dst)
+ inline void copyVec4<U8, LLVector4a>(U8* src, LLVector4a& dst)
{
dst.set(src[0], src[1], src[2], src[3]);
}
template<>
- void copyVec4<F32, glh::quaternionf>(F32* src, glh::quaternionf& dst)
+ inline void copyVec4<F32, quat>(F32* src, quat& dst)
{
- dst.set_value(src);
+ dst.x = src[0];
+ dst.y = src[1];
+ dst.z = src[2];
+ dst.w = src[3];
}
template<>
- void copyMat4<F32, glh::matrix4f>(F32* src, glh::matrix4f& dst)
+ inline void copyMat4<F32, mat4>(F32* src, mat4& dst)
{
- dst.set_value(src);
+ dst = glm::make_mat4(src);
}
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
//=========================================================================================================
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyScalar(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyScalar(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -233,7 +231,7 @@ namespace LL
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyVec2(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -245,7 +243,7 @@ namespace LL
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyVec3(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -257,7 +255,7 @@ namespace LL
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyVec4(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -269,7 +267,7 @@ namespace LL
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyMat2(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyMat2(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -281,7 +279,7 @@ namespace LL
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyMat3(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyMat3(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -293,7 +291,7 @@ namespace LL
// copy from src to dst, stride is the number of bytes between each element in src, count is number of elements to copy
template<class S, class T>
- static void copyMat4(S* src, LLStrider<T> dst, S32 stride, S32 count)
+ inline void copyMat4(S* src, LLStrider<T> dst, S32 stride, S32 count)
{
for (S32 i = 0; i < count; ++i)
{
@@ -304,39 +302,39 @@ namespace LL
}
template<class S, class T>
- static void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride)
+ inline void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride)
{
- if (accessor.mType == (S32)Accessor::Type::SCALAR)
+ if (accessor.mType == Accessor::Type::SCALAR)
{
S32 stride = byteStride == 0 ? sizeof(S) * 1 : byteStride;
copyScalar((S*)src, dst, stride, accessor.mCount);
}
- else if (accessor.mType == (S32)Accessor::Type::VEC2)
+ else if (accessor.mType == Accessor::Type::VEC2)
{
S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride;
copyVec2((S*)src, dst, stride, accessor.mCount);
}
- else if (accessor.mType == (S32)Accessor::Type::VEC3)
+ else if (accessor.mType == Accessor::Type::VEC3)
{
S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride;
copyVec3((S*)src, dst, stride, accessor.mCount);
}
- else if (accessor.mType == (S32)Accessor::Type::VEC4)
+ else if (accessor.mType == Accessor::Type::VEC4)
{
S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;
copyVec4((S*)src, dst, stride, accessor.mCount);
}
- else if (accessor.mType == (S32)Accessor::Type::MAT2)
+ else if (accessor.mType == Accessor::Type::MAT2)
{
S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;
copyMat2((S*)src, dst, stride, accessor.mCount);
}
- else if (accessor.mType == (S32)Accessor::Type::MAT3)
+ else if (accessor.mType == Accessor::Type::MAT3)
{
S32 stride = byteStride == 0 ? sizeof(S) * 9 : byteStride;
copyMat3((S*)src, dst, stride, accessor.mCount);
}
- else if (accessor.mType == (S32)Accessor::Type::MAT4)
+ else if (accessor.mType == Accessor::Type::MAT4)
{
S32 stride = byteStride == 0 ? sizeof(S) * 16 : byteStride;
copyMat4((S*)src, dst, stride, accessor.mCount);
@@ -349,7 +347,7 @@ namespace LL
// copy data from accessor to strider
template<class T>
- static void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
+ inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
{
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
@@ -391,12 +389,504 @@ namespace LL
// copy data from accessor to vector
template<class T>
- static void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst)
+ inline void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst)
{
dst.resize(accessor.mCount);
LLStrider<T> strider = dst.data();
copy(asset, accessor, strider);
}
+
+
+ //=========================================================================================================
+ // boost::json copying utilities
+ // ========================================================================================================
+
+ //====================== unspecialized base template, single value ===========================
+
+ // to/from Value
+ template<typename T>
+ inline bool copy(const Value& src, T& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ template<typename T>
+ inline bool write(const T& src, Value& dst)
+ {
+ dst = boost::json::object();
+ src.serialize(dst.as_object());
+ return true;
+ }
+
+ template<typename T>
+ inline bool copy(const Value& src, std::unordered_map<std::string, T>& dst)
+ {
+ if (src.is_object())
+ {
+ const boost::json::object& obj = src.as_object();
+ for (const auto& [key, value] : obj)
+ {
+ copy<T>(value, dst[key]);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::unordered_map<std::string, T>& src, Value& dst)
+ {
+ boost::json::object obj;
+ for (const auto& [key, value] : src)
+ {
+ Value v;
+ if (write<T>(value, v))
+ {
+ obj[key] = v;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ dst = obj;
+ return true;
+ }
+
+ // to/from array
+ template<typename T>
+ inline bool copy(const Value& src, std::vector<T>& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.get_array();
+ dst.resize(arr.size());
+ for (size_t i = 0; i < arr.size(); ++i)
+ {
+ copy(arr[i], dst[i]);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::vector<T>& src, Value& dst)
+ {
+ boost::json::array arr;
+ for (const T& t : src)
+ {
+ Value v;
+ if (write(t, v))
+ {
+ arr.push_back(v);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ dst = arr;
+ return true;
+ }
+
+ // to/from object member
+ template<typename T>
+ inline bool copy(const boost::json::object& src, string_view member, T& dst)
+ {
+ auto it = src.find(member);
+ if (it != src.end())
+ {
+ return copy(it->value(), dst);
+ }
+ return false;
+ }
+
+ // always write a member to an object without checking default
+ template<typename T>
+ inline bool write_always(const T& src, string_view member, boost::json::object& dst)
+ {
+ Value& v = dst[member];
+ if (!write(src, v))
+ {
+ dst.erase(member);
+ return false;
+ }
+ return true;
+ }
+
+ // conditionally write a member to an object if the member
+ // is not the default value
+ template<typename T>
+ inline bool write(const T& src, string_view member, boost::json::object& dst, const T& default_value = T())
+ {
+ if (src != default_value)
+ {
+ return write_always(src, member, dst);
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::unordered_map<std::string, T>& src, string_view member, boost::json::object& dst, const std::unordered_map<std::string, T>& default_value = std::unordered_map<std::string, T>())
+ {
+ if (!src.empty())
+ {
+ Value v;
+ if (write<T>(src, v))
+ {
+ dst[member] = v;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::vector<T>& src, string_view member, boost::json::object& dst, const std::vector<T>& deafault_value = std::vector<T>())
+ {
+ if (!src.empty())
+ {
+ Value v;
+ if (write(src, v))
+ {
+ dst[member] = v;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool copy(const Value& src, string_view member, T& dst)
+ {
+ if (src.is_object())
+ {
+ const boost::json::object& obj = src.as_object();
+ return copy(obj, member, dst);
+ }
+
+ return false;
+ }
+
+ // vec4
+ template<>
+ inline bool copy(const Value& src, vec4& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 4)
+ {
+ if (arr[0].is_double() &&
+ arr[1].is_double() &&
+ arr[2].is_double() &&
+ arr[3].is_double())
+ {
+ dst = vec4(arr[0].get_double(), arr[1].get_double(), arr[2].get_double(), arr[3].get_double());
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const vec4& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.get_array();
+ arr.resize(4);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ arr[3] = src.w;
+ return true;
+ }
+
+ // quat
+ template<>
+ inline bool copy(const Value& src, quat& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 4)
+ {
+ if (arr[0].is_double() &&
+ arr[1].is_double() &&
+ arr[2].is_double() &&
+ arr[3].is_double())
+ {
+ dst.x = arr[0].get_double();
+ dst.y = arr[1].get_double();
+ dst.z = arr[2].get_double();
+ dst.w = arr[3].get_double();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const quat& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.get_array();
+ arr.resize(4);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ arr[3] = src.w;
+ return true;
+ }
+
+
+ // vec3
+ template<>
+ inline bool copy(const Value& src, vec3& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 3)
+ {
+ if (arr[0].is_double() &&
+ arr[1].is_double() &&
+ arr[2].is_double())
+ {
+ dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const vec3& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.as_array();
+ arr.resize(3);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ return true;
+ }
+
+ // bool
+ template<>
+ inline bool copy(const Value& src, bool& dst)
+ {
+ if (src.is_bool())
+ {
+ dst = src.get_bool();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const bool& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // F32
+ template<>
+ inline bool copy(const Value& src, F32& dst)
+ {
+ if (src.is_double())
+ {
+ dst = src.get_double();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const F32& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+
+ // U32
+ template<>
+ inline bool copy(const Value& src, U32& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const U32& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // F64
+ template<>
+ inline bool copy(const Value& src, F64& dst)
+ {
+ if (src.is_double())
+ {
+ dst = src.get_double();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const F64& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // Accessor::Type
+ template<>
+ inline bool copy(const Value& src, Accessor::Type& dst)
+ {
+ if (src.is_string())
+ {
+ dst = gltf_type_to_enum(src.get_string().c_str());
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const Accessor::Type& src, Value& dst)
+ {
+ dst = enum_to_gltf_type(src);
+ return true;
+ }
+
+ // S32
+ template<>
+ inline bool copy(const Value& src, S32& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const S32& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+
+ // std::string
+ template<>
+ inline bool copy(const Value& src, std::string& dst)
+ {
+ if (src.is_string())
+ {
+ dst = src.get_string().c_str();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const std::string& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // mat4
+ template<>
+ inline bool copy(const Value& src, mat4& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.get_array();
+ if (arr.size() == 16)
+ {
+ // populate a temporary local in case
+ // we hit an error in the middle of the array
+ // (don't partially write a matrix)
+ mat4 t;
+ F32* p = glm::value_ptr(t);
+
+ for (U32 i = 0; i < arr.size(); ++i)
+ {
+ if (arr[i].is_double())
+ {
+ p[i] = arr[i].get_double();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ dst = t;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ template<>
+ inline bool write(const mat4& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.get_array();
+ arr.resize(16);
+ const F32* p = glm::value_ptr(src);
+ for (U32 i = 0; i < 16; ++i)
+ {
+ arr[i] = p[i];
+ }
+ return true;
+ }
+
+ // Material::AlphaMode
+ template<>
+ inline bool copy(const Value& src, Material::AlphaMode& dst)
+ {
+ if (src.is_string())
+ {
+ dst = gltf_alpha_mode_to_enum(src.get_string().c_str());
+ return true;
+ }
+ return true;
+ }
+
+ template<>
+ inline bool write(const Material::AlphaMode& src, Value& dst)
+ {
+ dst = enum_to_gltf_alpha_mode(src);
+ return true;
+ }
+
+ //
+ // ========================================================================================================
+
}
}
+
+
+
diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h
new file mode 100644
index 0000000000..859e202738
--- /dev/null
+++ b/indra/newview/gltf/common.h
@@ -0,0 +1,66 @@
+#pragma once
+
+/**
+ * @file common.h
+ * @brief LL GLTF Implementation
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#define GLM_ENABLE_EXPERIMENTAL 1
+
+#include "glm/vec2.hpp"
+#include "glm/vec3.hpp"
+#include "glm/vec4.hpp"
+#include "glm/mat4x4.hpp"
+#include "glm/gtc/type_ptr.hpp"
+#include "glm/ext/quaternion_float.hpp"
+#include "glm/gtx/quaternion.hpp"
+#include "glm/gtx/matrix_decompose.hpp"
+
+// Common types and constants used in the GLTF implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ using Value = boost::json::value;
+
+ using mat4 = glm::mat4;
+ using vec4 = glm::vec4;
+ using vec3 = glm::vec3;
+ using vec2 = glm::vec2;
+ using quat = glm::quat;
+
+ constexpr S32 LINEAR = 9729;
+ constexpr S32 NEAREST = 9728;
+ constexpr S32 NEAREST_MIPMAP_NEAREST = 9984;
+ constexpr S32 LINEAR_MIPMAP_NEAREST = 9985;
+ constexpr S32 NEAREST_MIPMAP_LINEAR = 9986;
+ constexpr S32 LINEAR_MIPMAP_LINEAR = 9987;
+ constexpr S32 CLAMP_TO_EDGE = 33071;
+ constexpr S32 MIRRORED_REPEAT = 33648;
+ constexpr S32 REPEAT = 10497;
+
+ class Asset;
+ }
+}
+
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
index b57a0af18d..1bde7327e6 100644
--- a/indra/newview/gltf/primitive.cpp
+++ b/indra/newview/gltf/primitive.cpp
@@ -28,10 +28,12 @@
#include "asset.h"
#include "buffer_util.h"
+#include "../llviewershadermgr.h"
#include "../lltinygltfhelper.h"
using namespace LL::GLTF;
+using namespace boost::json;
void Primitive::allocateGLResources(Asset& asset)
{
@@ -92,6 +94,10 @@ void Primitive::allocateGLResources(Asset& asset)
mask |= LLVertexBuffer::MAP_WEIGHT4;
}
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
+ }
mVertexBuffer = new LLVertexBuffer(mask);
mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices
@@ -129,7 +135,7 @@ void Primitive::allocateGLResources(Asset& asset)
if (mMaterial != INVALID_INDEX)
{
const Material& material = asset.mMaterials[mMaterial];
- LLColor4 baseColor = material.mMaterial->mBaseColor;
+ LLColor4 baseColor(glm::value_ptr(material.mPbrMetallicRoughness.mBaseColorFactor));
for (auto& dst : mColors)
{
dst = LLColor4U(baseColor * LLColor4(dst));
@@ -351,6 +357,50 @@ Primitive::~Primitive()
mOctree = nullptr;
}
+U32 gltf_mode_to_gl_mode(U32 mode)
+{
+ switch (mode)
+ {
+ case TINYGLTF_MODE_POINTS:
+ return LLRender::POINTS;
+ case TINYGLTF_MODE_LINE:
+ return LLRender::LINES;
+ case TINYGLTF_MODE_LINE_LOOP:
+ return LLRender::LINE_LOOP;
+ case TINYGLTF_MODE_LINE_STRIP:
+ return LLRender::LINE_STRIP;
+ case TINYGLTF_MODE_TRIANGLES:
+ return LLRender::TRIANGLES;
+ case TINYGLTF_MODE_TRIANGLE_STRIP:
+ return LLRender::TRIANGLE_STRIP;
+ case TINYGLTF_MODE_TRIANGLE_FAN:
+ return LLRender::TRIANGLE_FAN;
+ default:
+ return LLRender::TRIANGLES;
+ }
+}
+
+void Primitive::serialize(boost::json::object& dst) const
+{
+ write(mMaterial, "material", dst, -1);
+ write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES);
+ write(mIndices, "indices", dst, INVALID_INDEX);
+ write(mAttributes, "attributes", dst);
+}
+
+const Primitive& Primitive::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "material", mMaterial);
+ copy(src, "mode", mMode);
+ copy(src, "indices", mIndices);
+ copy(src, "attributes", mAttributes);
+
+ mGLMode = gltf_mode_to_gl_mode(mMode);
+ }
+ return *this;
+}
const Primitive& Primitive::operator=(const tinygltf::Primitive& src)
{
@@ -369,32 +419,7 @@ const Primitive& Primitive::operator=(const tinygltf::Primitive& src)
mAttributes[it.first] = it.second;
}
- switch (mMode)
- {
- case TINYGLTF_MODE_POINTS:
- mGLMode = LLRender::POINTS;
- break;
- case TINYGLTF_MODE_LINE:
- mGLMode = LLRender::LINES;
- break;
- case TINYGLTF_MODE_LINE_LOOP:
- mGLMode = LLRender::LINE_LOOP;
- break;
- case TINYGLTF_MODE_LINE_STRIP:
- mGLMode = LLRender::LINE_STRIP;
- break;
- case TINYGLTF_MODE_TRIANGLES:
- mGLMode = LLRender::TRIANGLES;
- break;
- case TINYGLTF_MODE_TRIANGLE_STRIP:
- mGLMode = LLRender::TRIANGLE_STRIP;
- break;
- case TINYGLTF_MODE_TRIANGLE_FAN:
- mGLMode = LLRender::TRIANGLE_FAN;
- break;
- default:
- mGLMode = GL_TRIANGLES;
- }
+ mGLMode = gltf_mode_to_gl_mode(mMode);
return *this;
}
diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h
index 07e8e7deb2..18aadce808 100644
--- a/indra/newview/gltf/primitive.h
+++ b/indra/newview/gltf/primitive.h
@@ -28,12 +28,14 @@
#include "llvertexbuffer.h"
#include "llvolumeoctree.h"
+#include "boost/json.hpp"
// LL GLTF Implementation
namespace LL
{
namespace GLTF
{
+ using Value = boost::json::value;
class Asset;
constexpr U32 ATTRIBUTE_MASK =
@@ -66,10 +68,10 @@ namespace LL
std::vector<LLVolumeTriangle> mOctreeTriangles;
S32 mMaterial = -1;
- U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles
+ S32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles
U32 mGLMode = LLRender::TRIANGLES;
S32 mIndices = -1;
- std::unordered_map<std::string, int> mAttributes;
+ std::unordered_map<std::string, S32> mAttributes;
// create octree based on vertex buffer
// must be called before buffer is unmapped and after buffer is populated with good data
@@ -85,6 +87,8 @@ namespace LL
LLVector4a* tangent = NULL // return the surface tangent at the intersection point
);
+ void serialize(boost::json::object& obj) const;
+ const Primitive& operator=(const Value& src);
const Primitive& operator=(const tinygltf::Primitive& src);
void allocateGLResources(Asset& asset);
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index 70e9fb299f..5413a7d021 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -39,7 +39,14 @@
#include "gltf/asset.h"
#include "pipeline.h"
#include "llviewershadermgr.h"
+#include "llviewertexturelist.h"
+#include "llimagej2c.h"
+#include "llfloaterperms.h"
+#include "llagentbenefits.h"
+#include "llfilesystem.h"
+#include "boost/json.hpp"
+#define GLTF_SIM_SUPPORT 1
using namespace LL;
@@ -126,6 +133,170 @@ void GLTFSceneManager::decomposeSelection()
}
}
+void GLTFSceneManager::uploadSelection()
+{
+ if (mUploadingAsset)
+ { // upload already in progress
+ LLNotificationsUtil::add("GLTFUploadInProgress");
+ return;
+ }
+
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset)
+ {
+ // make a copy of the asset prior to uploading
+ mUploadingAsset = std::make_shared<Asset>();
+ mUploadingObject = obj;
+ *mUploadingAsset = *obj->mGLTFAsset;
+
+ GLTF::Asset& asset = *mUploadingAsset;
+
+ for (auto& image : asset.mImages)
+ {
+ if (!image.mData.empty())
+ {
+ mPendingImageUploads++;
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw(image.mWidth, image.mHeight, image.mComponent);
+ U8* data = raw->allocateData();
+ llassert_always(image.mData.size() == raw->getDataSize());
+ memcpy(data, image.mData.data(), image.mData.size());
+
+ // for GLTF native content, store image in GLTF orientation
+ raw->verticalFlip();
+
+ LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
+
+ std::string buffer;
+ buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
+
+ LLUUID asset_id = LLUUID::generateNewID();
+
+ std::string name;
+ S32 idx = (S32)(&image - &asset.mImages[0]);
+
+ if (image.mName.empty())
+ {
+
+ name = llformat("Image_%d", idx);
+ }
+ else
+ {
+ name = image.mName;
+ }
+
+ LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
+ {
+ // TODO: handle failure
+ mPendingImageUploads--;
+ return false;
+ };
+
+
+ LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
+ {
+ if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
+ {
+ mUploadingAsset->mImages[idx].mUri = assetId.asString();
+ mPendingImageUploads--;
+ }
+ };
+
+ S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
+
+ LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
+ buffer,
+ asset_id,
+ name,
+ name,
+ 0,
+ LLFolderType::FT_TEXTURE,
+ LLInventoryType::IT_TEXTURE,
+ LLAssetType::AT_TEXTURE,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost,
+ false,
+ finish,
+ failure));
+
+ upload_new_resource(uploadInfo);
+
+ image.clearData(asset);
+ }
+ }
+
+ // upload .bin
+ for (auto& bin : asset.mBuffers)
+ {
+ mPendingBinaryUploads++;
+
+ S32 idx = (S32)(&bin - &asset.mBuffers[0]);
+
+ std::string buffer;
+ buffer.assign((const char*)bin.mData.data(), bin.mData.size());
+
+ LLUUID asset_id = LLUUID::generateNewID();
+
+ LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
+ {
+ // TODO: handle failure
+ mPendingBinaryUploads--;
+ mUploadingAsset = nullptr;
+ mUploadingObject = nullptr;
+ LL_WARNS("GLTF") << "Failed to upload GLTF binary: " << reason << LL_ENDL;
+ LL_WARNS("GLTF") << response << LL_ENDL;
+ return false;
+ };
+
+ LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx](LLUUID assetId, LLSD response)
+ {
+ if (mUploadingAsset && mUploadingAsset->mBuffers.size() > idx)
+ {
+ mUploadingAsset->mBuffers[idx].mUri = assetId.asString();
+ mPendingBinaryUploads--;
+
+ // HACK: save buffer to cache to emulate a successful download
+ LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE);
+ auto& data = mUploadingAsset->mBuffers[idx].mData;
+
+ cache.write((const U8*)data.data(), data.size());
+ }
+ };
+#if GLTF_SIM_SUPPORT
+ S32 expected_upload_cost = 1;
+
+ LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
+ buffer,
+ asset_id,
+ "",
+ "",
+ 0,
+ LLFolderType::FT_NONE,
+ LLInventoryType::IT_GLTF_BIN,
+ LLAssetType::AT_GLTF_BIN,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost,
+ false,
+ finish,
+ failure));
+
+ upload_new_resource(uploadInfo);
+#else
+ // dummy finish
+ finish(LLUUID::generateNewID(), LLSD());
+#endif
+ }
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFUploadSelection");
+ }
+}
+
void GLTFSceneManager::decomposeSelection(const std::string& filename)
{
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
@@ -176,7 +347,7 @@ void GLTFSceneManager::load(const std::string& filename)
if (obj)
{ // assign to self avatar
obj->mGLTFAsset = asset;
-
+ obj->markForUpdate();
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
{
mObjects.push_back(obj);
@@ -199,6 +370,109 @@ void GLTFSceneManager::renderAlpha()
render(false);
}
+void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id)
+{
+ llassert(obj->getVolume()->getParams().getSculptID() == gltf_id);
+ llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
+
+ obj->ref();
+ gAssetStorage->getAssetData(gltf_id, LLAssetType::AT_GLTF, onGLTFLoadComplete, obj);
+}
+
+//static
+void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
+{
+ LLViewerObject* obj = (LLViewerObject*)user_data;
+ llassert(asset_type == LLAssetType::AT_GLTF_BIN);
+
+ if (status == LL_ERR_NOERR)
+ {
+ if (obj)
+ {
+ // find the Buffer with the given id in the asset
+ if (obj->mGLTFAsset)
+ {
+ for (auto& buffer : obj->mGLTFAsset->mBuffers)
+ {
+ LLUUID buffer_id;
+ if (LLUUID::parseUUID(buffer.mUri, &buffer_id) && buffer_id == id)
+ {
+ LLFileSystem file(id, asset_type, LLFileSystem::READ);
+
+ buffer.mData.resize(file.getSize());
+ file.read((U8*)buffer.mData.data(), buffer.mData.size());
+
+ obj->mGLTFAsset->mPendingBuffers--;
+
+ if (obj->mGLTFAsset->mPendingBuffers == 0)
+ {
+ obj->mGLTFAsset->allocateGLResources();
+ GLTFSceneManager& mgr = GLTFSceneManager::instance();
+ if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
+ {
+ GLTFSceneManager::instance().mObjects.push_back(obj);
+ }
+ }
+ }
+ }
+ }
+
+
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
+ obj->unref();
+ }
+}
+
+//static
+void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
+{
+ LLViewerObject* obj = (LLViewerObject*)user_data;
+ llassert(asset_type == LLAssetType::AT_GLTF);
+
+ if (status == LL_ERR_NOERR)
+ {
+ if (obj)
+ {
+ LLFileSystem file(id, asset_type, LLFileSystem::READ);
+ std::string data;
+ data.resize(file.getSize());
+ file.read((U8*)data.data(), data.size());
+
+ boost::json::value json = boost::json::parse(data);
+
+ std::shared_ptr<Asset> asset = std::make_shared<Asset>(json);
+ obj->mGLTFAsset = asset;
+
+ for (auto& buffer : asset->mBuffers)
+ {
+ // for now just assume the buffer is already in the asset cache
+ LLUUID buffer_id;
+ if (LLUUID::parseUUID(buffer.mUri, &buffer_id))
+ {
+ asset->mPendingBuffers++;
+
+ gAssetStorage->getAssetData(buffer_id, LLAssetType::AT_GLTF_BIN, onGLTFBinLoadComplete, obj);
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL;
+ obj->unref();
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
+ obj->unref();
+ }
+}
+
void GLTFSceneManager::update()
{
for (U32 i = 0; i < mObjects.size(); ++i)
@@ -212,6 +486,107 @@ void GLTFSceneManager::update()
mObjects[i]->mGLTFAsset->update();
}
+
+ // process pending uploads
+ if (mUploadingAsset && !mGLTFUploadPending)
+ {
+ if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0)
+ {
+ std::string filename(gDirUtilp->getTempDir() + "/upload.gltf");
+#if 0
+ tinygltf::Model model;
+ mUploadingAsset->save(model);
+
+ tinygltf::TinyGLTF writer;
+
+ writer.WriteGltfSceneToFile(&model, filename, false, false, true, false);
+#else
+ boost::json::object obj;
+ mUploadingAsset->serialize(obj);
+ std::string json = boost::json::serialize(obj, {});
+
+ {
+ std::ofstream o(filename);
+ o << json;
+ }
+#endif
+
+ std::ifstream t(filename);
+ std::stringstream str;
+ str << t.rdbuf();
+
+ std::string buffer = str.str();
+
+ LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
+ {
+ // TODO: handle failure
+ LL_WARNS("GLTF") << "Failed to upload GLTF json: " << reason << LL_ENDL;
+ LL_WARNS("GLTF") << response << LL_ENDL;
+
+ mUploadingAsset = nullptr;
+ mUploadingObject = nullptr;
+ mGLTFUploadPending = false;
+ return false;
+ };
+
+ LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response)
+ {
+ LLAppViewer::instance()->postToMainCoro(
+ [=]()
+ {
+ if (mUploadingAsset)
+ {
+ // HACK: save buffer to cache to emulate a successful upload
+ LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE);
+
+ LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL;
+ cache.write((const U8 *) buffer.c_str(), buffer.size());
+
+ mUploadingAsset = nullptr;
+ }
+
+ if (mUploadingObject)
+ {
+ mUploadingObject->mGLTFAsset = nullptr;
+ mUploadingObject->setGLTFAsset(assetId);
+ mUploadingObject->markForUpdate();
+ mUploadingObject = nullptr;
+ }
+
+ mGLTFUploadPending = false;
+ });
+ };
+
+#if GLTF_SIM_SUPPORT
+ S32 expected_upload_cost = 1;
+ LLUUID asset_id = LLUUID::generateNewID();
+
+ mGLTFUploadPending = true;
+
+ LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
+ buffer,
+ asset_id,
+ "",
+ "",
+ 0,
+ LLFolderType::FT_NONE,
+ LLInventoryType::IT_GLTF,
+ LLAssetType::AT_GLTF,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost,
+ false,
+ finish,
+ failure));
+
+ upload_new_resource(uploadInfo);
+#else
+ // dummy finish
+ finish(LLUUID::generateNewID(), LLSD());
+#endif
+ }
+ }
}
void GLTFSceneManager::render(bool opaque, bool rigged)
@@ -219,7 +594,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
// for debugging, just render the whole scene as opaque
// by traversing the whole scenegraph
// Assumes camera transform is already set and
- // appropriate shader is already bound
+ // appropriate shader is already boundd
gGL.matrixMode(LLRender::MM_MODELVIEW);
@@ -243,7 +618,8 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
matMul(mat, modelview, modelview);
- asset->updateRenderTransforms(modelview);
+ mat4 mdv = glm::make_mat4(modelview.getF32ptr());
+ asset->updateRenderTransforms(mdv);
asset->render(opaque, rigged);
gGL.popMatrix();
@@ -411,12 +787,16 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
// get raycast in asset space
LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform();
- LLVector4a start;
- LLVector4a end;
+ vec4 start;
+ vec4 end;
- agent_to_asset.affineTransform(gDebugRaycastStart, start);
- agent_to_asset.affineTransform(gDebugRaycastEnd, end);
+ LLVector4a t;
+ agent_to_asset.affineTransform(gDebugRaycastStart, t);
+ start = glm::make_vec4(t.getF32ptr());
+ agent_to_asset.affineTransform(gDebugRaycastEnd, t);
+ end = glm::make_vec4(t.getF32ptr());
+ start.w = end.w = 1.0;
for (auto& node : asset->mNodes)
{
@@ -424,7 +804,7 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
if (node.mMesh != INVALID_INDEX)
{
- gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix);
+ gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
// draw bounding box of mesh primitives
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
@@ -442,24 +822,24 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
}
}
-#if 0
+#if 1
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
{
gGL.flush();
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// convert raycast to node local space
- LLVector4a local_start;
- LLVector4a local_end;
-
- node.mAssetMatrixInv.affineTransform(start, local_start);
- node.mAssetMatrixInv.affineTransform(end, local_end);
+ vec4 local_start = node.mAssetMatrixInv * start;
+ vec4 local_end = node.mAssetMatrixInv * end;
for (auto& primitive : mesh.mPrimitives)
{
if (primitive.mOctree.notNull())
{
- renderOctreeRaycast(local_start, local_end, primitive.mOctree);
+ LLVector4a s, e;
+ s.load3(glm::value_ptr(local_start));
+ e.load3(glm::value_ptr(local_end));
+ renderOctreeRaycast(s, e, primitive.mOctree);
}
}
@@ -499,18 +879,18 @@ void GLTFSceneManager::renderDebug()
continue;
}
- LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
+ mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
- LLMatrix4a modelview;
- modelview.loadu(gGLModelView);
+ mat4 modelview = glm::make_mat4(gGLModelView);
- matMul(mat, modelview, modelview);
+
+ modelview = modelview * mat;
Asset* asset = obj->mGLTFAsset.get();
for (auto& node : asset->mNodes)
{
- matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
+ node.mRenderMatrix = modelview * node.mAssetMatrix;
}
}
@@ -523,13 +903,6 @@ void GLTFSceneManager::renderDebug()
Asset* asset = obj->mGLTFAsset.get();
- LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
-
- LLMatrix4a modelview;
- modelview.loadu(gGLModelView);
-
- matMul(mat, modelview, modelview);
-
renderAssetDebug(obj, asset);
}
@@ -551,21 +924,20 @@ void GLTFSceneManager::renderDebug()
continue;
}
- LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
+ mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
- LLMatrix4a modelview;
- modelview.loadu(gGLModelView);
+ mat4 modelview = glm::make_mat4(gGLModelView);
- matMul(mat, modelview, modelview);
+ modelview = modelview * mat;
Asset* asset = obj->mGLTFAsset.get();
for (auto& node : asset->mNodes)
{
// force update all mRenderMatrix, not just nodes with meshes
- matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
+ node.mRenderMatrix = modelview * node.mAssetMatrix;
- gGL.loadMatrix(node.mRenderMatrix.getF32ptr());
+ gGL.loadMatrix(glm::value_ptr(node.mRenderMatrix));
// render x-axis red, y-axis green, z-axis blue
gGL.color4f(1.f, 0.f, 0.f, 0.5f);
gGL.begin(LLRender::LINES);
@@ -595,7 +967,9 @@ void GLTFSceneManager::renderDebug()
{
Node& child = asset->mNodes[child_idx];
gGL.vertex3f(0.f, 0.f, 0.f);
- gGL.vertex3fv(child.mMatrix.getTranslation().getF32ptr());
+
+
+ gGL.vertex3fv(glm::value_ptr(child.mMatrix[3]));
}
gGL.end();
gGL.flush();
@@ -628,8 +1002,7 @@ void GLTFSceneManager::renderDebug()
gGL.color3f(1, 0, 1);
drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f));
- gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix);
-
+ gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix));
auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0);
@@ -643,3 +1016,5 @@ void GLTFSceneManager::renderDebug()
gDebugProgram.unbind();
}
+
+
diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h
index 5ff7c1a447..5434f6ddb2 100644
--- a/indra/newview/gltfscenemanager.h
+++ b/indra/newview/gltfscenemanager.h
@@ -54,6 +54,7 @@ namespace LL
void save(const std::string& filename); // save selected asset to filename (suitable for use in external programs)
void decomposeSelection(); // open file picker and choose a location to decompose to
void decomposeSelection(const std::string& filename); // decompose selected asset into simulator-ready .gltf, .bin, and .j2c files
+ void uploadSelection(); // decompose selected asset and upload to simulator
void update();
void render(bool opaque, bool rigged = false);
@@ -77,7 +78,18 @@ namespace LL
void renderDebug();
+ void addGLTFObject(LLViewerObject* object, LLUUID gltf_id);
+ static void onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status);
+ static void onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status);
+
std::vector<LLPointer<LLViewerObject>> mObjects;
+
+ std::shared_ptr<GLTF::Asset> mUploadingAsset;
+ bool mGLTFUploadPending = false;
+ LLPointer<LLViewerObject> mUploadingObject;
+ U32 mPendingImageUploads = 0;
+ U32 mPendingBinaryUploads = 0;
+ U32 mPendingGLTFUploads = 0;
};
}
diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp
index 6e45e2a0f3..ee4f1b0fe5 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -142,7 +142,6 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed);
}
-
}
LLViewerFetchedTexture* fetch_texture(const LLUUID& id)
diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp
index ca800be580..ba763780ed 100644
--- a/indra/newview/llimprocessing.cpp
+++ b/indra/newview/llimprocessing.cpp
@@ -1637,23 +1637,29 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url)
{
session_id = message_data["asset_id"].asUUID();
}
- LLIMProcessing::processNewMessage(
- message_data["from_agent_id"].asUUID(),
- from_group,
- message_data["to_agent_id"].asUUID(),
- message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE,
- dialog,
- session_id,
- static_cast<U32>(message_data["timestamp"].asInteger()),
- message_data["from_agent_name"].asString(),
- message_data["message"].asString(),
- static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland
- message_data["region_id"].asUUID(),
- position,
- bin_bucket.data(),
- bin_bucket.size(),
- sender,
- message_data["asset_id"].asUUID());
+
+ LLAppViewer::instance()->postToMainCoro([=]()
+ {
+ std::vector<U8> local_bin_bucket = bin_bucket;
+ LLHost local_sender = sender;
+ LLIMProcessing::processNewMessage(
+ message_data["from_agent_id"].asUUID(),
+ from_group,
+ message_data["to_agent_id"].asUUID(),
+ message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE,
+ dialog,
+ session_id,
+ static_cast<U32>(message_data["timestamp"].asInteger()),
+ message_data["from_agent_name"].asString(),
+ message_data["message"].asString(),
+ static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland
+ message_data["region_id"].asUUID(),
+ position,
+ local_bin_bucket.data(),
+ local_bin_bucket.size(),
+ local_sender,
+ message_data["asset_id"].asUUID());
+ });
}
}
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index f0e024605c..9090718691 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1400,7 +1400,9 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)
return mask;
}
- if (item->getType() == LLAssetType::AT_MESH)
+ if (item->getType() == LLAssetType::AT_MESH ||
+ item->getType() == LLAssetType::AT_GLTF ||
+ item->getType() == LLAssetType::AT_GLTF_BIN)
{
return mask;
}
diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp
index 3e3347ff33..f4c618c08d 100644
--- a/indra/newview/llviewerassettype.cpp
+++ b/indra/newview/llviewerassettype.cpp
@@ -89,6 +89,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary()
addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE));
addEntry(LLViewerAssetType::AT_SETTINGS, new ViewerAssetEntry(DAD_SETTINGS));
addEntry(LLViewerAssetType::AT_MATERIAL, new ViewerAssetEntry(DAD_MATERIAL));
+ addEntry(LLViewerAssetType::AT_GLTF, new ViewerAssetEntry(DAD_GLTF));
+ addEntry(LLViewerAssetType::AT_GLTF_BIN, new ViewerAssetEntry(DAD_GLTF_BIN));
};
EDragAndDropType LLViewerAssetType::lookupDragAndDropType(EType asset_type)
diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp
index 7712c16c16..17d440d8c9 100644
--- a/indra/newview/llviewerassetupload.cpp
+++ b/indra/newview/llviewerassetupload.cpp
@@ -299,10 +299,18 @@ void LLResourceUploadInfo::assignDefaults()
mDescription = "(No Description)";
}
+ if (mAssetType == LLAssetType::AT_GLTF ||
+ mAssetType == LLAssetType::AT_GLTF_BIN)
+ {
+ mFolderId = LLUUID::null;
+ }
+ else
+ {
mFolderId = gInventory.findUserDefinedCategoryUUIDForType(
(mDestinationFolderType == LLFolderType::FT_NONE) ?
(LLFolderType::EType)mAssetType : mDestinationFolderType);
}
+}
std::string LLResourceUploadInfo::getDisplayName() const
{
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index f7d9c12e66..23931ecf03 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -2674,7 +2674,13 @@ void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url)
{
if (initializeMedia(mimeType))
{
- loadURI();
+ ref();
+ LLAppViewer::instance()->postToMainCoro([this]()
+ {
+ loadURI();
+ unref();
+ });
+
}
}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 1a0437ed92..fd16b07985 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -139,6 +139,7 @@
#include "boost/unordered_map.hpp"
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/json.hpp>
#include "llcleanup.h"
#include "llviewershadermgr.h"
#include "gltfscenemanager.h"
@@ -3317,6 +3318,13 @@ bool enable_os_exception()
#endif
}
+
+bool enable_gltf()
+{
+ static LLCachedControl<bool> enablegltf(gSavedSettings, "GLTFEnabled", false);
+ return enablegltf;
+}
+
class LLSelfRemoveAllAttachments : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -8080,7 +8088,6 @@ class LLAdvancedClickGLTFOpen: public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
LL::GLTFSceneManager::instance().load();
return true;
}
@@ -8090,7 +8097,6 @@ class LLAdvancedClickGLTFSaveAs : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
LL::GLTFSceneManager::instance().saveAs();
return true;
}
@@ -8100,12 +8106,39 @@ class LLAdvancedClickGLTFDecompose : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- // open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
LL::GLTFSceneManager::instance().decomposeSelection();
return true;
}
};
+class LLAdvancedClickGLTFUpload: public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LL::GLTFSceneManager::instance().uploadSelection();
+ return true;
+ }
+};
+
+class LLAdvancedClickResizeWindow : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ S32 w = 0;
+ S32 h = 0;
+
+ sscanf(userdata.asString().c_str(), "%dx%d", &w, &h);
+
+ if (w > 0 && h > 0)
+ {
+ gViewerWindow->getWindow()->setSize(LLCoordWindow(w, h));
+ }
+
+ return true;
+ }
+};
+
+
// these are used in the gl menus to set control values that require shader recompilation
class LLToggleShaderControl : public view_listener_t
{
@@ -9761,6 +9794,8 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen");
view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs");
view_listener_t::addMenu(new LLAdvancedClickGLTFDecompose(), "Advanced.ClickGLTFDecompose");
+ view_listener_t::addMenu(new LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload");
+ view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
@@ -10065,6 +10100,7 @@ void initialize_menus()
commit.add("Pathfinding.Characters.Select", boost::bind(&LLFloaterPathfindingCharacters::openCharactersWithSelectedObjects));
enable.add("EnableSelectInPathfindingCharacters", boost::bind(&enable_object_select_in_pathfinding_characters));
enable.add("Advanced.EnableErrorOSException", boost::bind(&enable_os_exception));
+ enable.add("EnableGLTF", boost::bind(&enable_gltf));
view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible");
view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel");
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 7b60dc754f..654f2aa1de 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -4296,7 +4296,9 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const
LLMatrix4a asset_to_agent = getGLTFAssetToAgentTransform();
LLMatrix4a node_to_agent;
- matMul(node.mAssetMatrix, asset_to_agent, node_to_agent);
+ LLMatrix4a am;
+ am.loadu(glm::value_ptr(node.mAssetMatrix));
+ matMul(am, asset_to_agent, node_to_agent);
mat = node_to_agent;
}
@@ -4307,6 +4309,7 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const
return mat;
}
+
void LLViewerObject::getGLTFNodeTransformAgent(S32 node_index, LLVector3* position, LLQuaternion* rotation, LLVector3* scale) const
{
LLMatrix4a node_to_agent = getGLTFNodeTransformAgent(node_index);
@@ -4354,7 +4357,9 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion
if (node.mParent != -1)
{
auto& parent = mGLTFAsset->mNodes[node.mParent];
- matMul(agent_to_asset, parent.mAssetMatrixInv, agent_to_node);
+ LLMatrix4a ami;
+ ami.loadu(glm::value_ptr(parent.mAssetMatrixInv));
+ matMul(agent_to_asset, ami, agent_to_node);
}
LLQuaternion agent_to_node_rot(agent_to_node.asMatrix4());
@@ -4366,9 +4371,13 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion
LLVector3 pos;
LLQuaternion rot;
LLVector3 scale;
- decomposeMatrix(node.mMatrix, pos, rot, scale);
+ LLMatrix4a mat;
+ mat.loadu(glm::value_ptr(node.mMatrix));
+ decomposeMatrix(mat, pos, rot, scale);
+
+ mat.asMatrix4().initAll(scale, new_rot, pos);
- node.mMatrix.asMatrix4().initAll(scale, new_rot, pos);
+ node.mMatrix = glm::make_mat4(mat.getF32ptr());
mGLTFAsset->updateTransforms();
}
@@ -4382,7 +4391,9 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
LLMatrix4a agent_to_asset = getAgentToGLTFAssetTransform();
LLMatrix4a agent_to_node;
- matMul(agent_to_asset, node.mAssetMatrixInv, agent_to_node);
+ LLMatrix4a ami;
+ ami.loadu(glm::value_ptr(node.mAssetMatrixInv));
+ matMul(agent_to_asset, ami, agent_to_node);
LLVector4a origin = LLVector4a::getZero();
LLVector4a offset_v;
@@ -4399,7 +4410,12 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
trans.setIdentity();
trans.mMatrix[3] = offset_v;
- matMul(trans, node.mMatrix, node.mMatrix);
+ LLMatrix4a mat;
+ mat.loadu(glm::value_ptr(node.mMatrix));
+
+ matMul(trans, mat, mat);
+
+ node.mMatrix = glm::make_mat4(mat.getF32ptr());
// TODO -- only update transforms for this node and its children (or use a dirty flag)
mGLTFAsset->updateTransforms();
@@ -7490,6 +7506,23 @@ void LLViewerObject::shrinkWrap()
}
}
+void LLViewerObject::setGLTFAsset(const LLUUID& id)
+{
+ //get the sculpt params and set the sculpt type and id
+ auto* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_SCULPT);
+
+ LLSculptParams* sculpt_params = (LLSculptParams*)param->data;
+ sculpt_params->setSculptTexture(id, LL_SCULPT_TYPE_GLTF);
+
+ setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, true, true);
+
+ // Update the volume
+ LLVolumeParams volume_params;
+ volume_params.setSculptID(id, LL_SCULPT_TYPE_GLTF);
+ updateVolume(volume_params);
+}
+
+
class ObjectPhysicsProperties : public LLHTTPNode
{
public:
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 32e1a495c4..81b4952a39 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -1,3 +1,4 @@
+
/**
* @file llviewerobject.h
* @brief Description of LLViewerObject class, which is the base class for most objects in the viewer.
@@ -742,6 +743,11 @@ public:
F32 mPhysicsDensity;
F32 mPhysicsRestitution;
+ // set the GLTF asset for this LLViewerObject to the specified asset id
+ // id MUST be for a GLTF asset (LLAssetType::AT_GLTF)
+ // will relesae any currently held references to a GLTF asset on id change
+ void setGLTFAsset(const LLUUID& id);
+
// Associated GLTF Asset
std::shared_ptr<LL::GLTF::Asset> mGLTFAsset;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 5c44913b29..bf4bdfd252 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -2462,24 +2462,14 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
gSavedSettings.setS32("max_texture_dimension_Y", 1024);
}
- if (features.has("PBRTerrainEnabled"))
+ if (features.has("GLTFEnabled"))
{
- bool enabled = features["PBRTerrainEnabled"];
- gSavedSettings.setBOOL("RenderTerrainPBREnabled", enabled);
+ bool enabled = features["GLTFEnabled"];
+ gSavedSettings.setBOOL("GLTFEnabled", enabled);
}
else
{
- gSavedSettings.setBOOL("RenderTerrainPBREnabled", false);
- }
-
- if (features.has("PBRMaterialSwatchEnabled"))
- {
- bool enabled = features["PBRMaterialSwatchEnabled"];
- gSavedSettings.setBOOL("UIPreviewMaterial", enabled);
- }
- else
- {
- gSavedSettings.setBOOL("UIPreviewMaterial", false);
+ gSavedSettings.setBOOL("GLTFEnabled", false);
}
};
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 8108aea06f..8e91354bb0 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -89,6 +89,7 @@
#include "llsculptidsize.h"
#include "llavatarappearancedefines.h"
#include "llgltfmateriallist.h"
+#include "gltfscenemanager.h"
const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
const F32 FORCE_CULL_AREA = 8.f;
@@ -1133,6 +1134,11 @@ bool LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
}
}
+ if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF)
+ { // notify GLTFSceneManager about new GLTF object
+ LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID());
+ }
+
return true;
}
else if (NO_LOD == lod)
@@ -1407,6 +1413,12 @@ bool LLVOVolume::calcLOD()
return false;
}
+ if (mGLTFAsset != nullptr)
+ {
+ // do not calculate LOD for GLTF objects
+ return false;
+ }
+
S32 cur_detail = 0;
F32 radius;
@@ -5623,7 +5635,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
LLVOVolume* vobj = drawablep->getVOVolume();
- if (!vobj || vobj->isDead())
+ if (!vobj || vobj->isDead() || vobj->mGLTFAsset)
{
continue;
}
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index a37a1be0f4..8c9561fe64 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2866,21 +2866,69 @@ function="World.EnvPreset"
<menu_item_call
label="Open..."
name="Open...">
+ <menu_item_call.on_enable
+ function="EnableGLTF"/>
<menu_item_call.on_click
function="Advanced.ClickGLTFOpen" />
</menu_item_call>
<menu_item_call
label="Save As..."
name="Save As...">
+ <menu_item_call.on_enable
+ function="EnableGLTF"/>
<menu_item_call.on_click
function="Advanced.ClickGLTFSaveAs" />
</menu_item_call>
<menu_item_call
label="Decompose..."
name="Decompose...">
+ <menu_item_call.on_enable
+ function="EnableGLTF"/>
<menu_item_call.on_click
function="Advanced.ClickGLTFDecompose" />
</menu_item_call>
+ <menu_item_call
+ label="Upload..."
+ name="Upload...">
+ <menu_item_call.on_enable
+ function="EnableGLTF"/>
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFUpload" />
+ </menu_item_call>
+ </menu>
+ <menu
+ create_jump_keys="true"
+ label="Video"
+ name="Video"
+ tear_off="true">
+ <menu_item_call
+ label="1080x1920"
+ name="1080x1920">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="1080x1920"/>
+ </menu_item_call>
+ <menu_item_call
+ label="1920x1080"
+ name="1920x1080">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="1920x1080"/>
+ </menu_item_call>
+ <menu_item_call
+ label="1280x720"
+ name="1280x720">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="1280x720"/>
+ </menu_item_call>
+ <menu_item_call
+ label="720x1280"
+ name="720x1280">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="720x1280"/>
+ </menu_item_call>
</menu>
<menu
create_jump_keys="true"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index a0f58550cc..e3b9f744e9 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -12481,4 +12481,26 @@ are wearing now.
yestext="OK"/>
</notification>
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFUploadSelection"
+ type="alert">
+ You must select an object that has local-only GLTF asset associated with it.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFUploadInProgress"
+ type="alert">
+ Upload is currently in progress. Please try again later.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
</notifications>