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