Merge remote-tracking branch 'origin/project/gltf_development' into brad/merge-maint-a-to-dev
commit
9f6849e081
|
|
@ -743,6 +743,48 @@
|
|||
<key>description</key>
|
||||
<string>glh - is a platform-indepenedent C++ OpenGL helper library</string>
|
||||
</map>
|
||||
<key>glm</key>
|
||||
<map>
|
||||
<key>canonical_repo</key>
|
||||
<string>https://github.com/secondlife/3p-glm</string>
|
||||
<key>copyright</key>
|
||||
<string>Copyright (c) 2005 - G-Truc Creation</string>
|
||||
<key>description</key>
|
||||
<string>OpenGL Mathematics</string>
|
||||
<key>license</key>
|
||||
<string>MIT</string>
|
||||
<key>license_file</key>
|
||||
<string>LICENSES/glm_license.txt</string>
|
||||
<key>name</key>
|
||||
<string>glm</string>
|
||||
<key>platforms</key>
|
||||
<map>
|
||||
<key>common</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>353ae3a560143732503a8e14499ae12db1e66056</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>sha1</string>
|
||||
<key>url</key>
|
||||
<string>https://github.com/secondlife/3p-glm/releases/download/v1.0.1-r1/glm-v1.0.1-common-9066386153.tar.zst</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>common</string>
|
||||
</map>
|
||||
</map>
|
||||
<key>source_type</key>
|
||||
<string>git</string>
|
||||
<key>vcs_branch</key>
|
||||
<string>refs/tags/v1.0.1-r1</string>
|
||||
<key>vcs_revision</key>
|
||||
<string>399cd5ba57a9267a560ce07e50a0f8c5fe3dc66f</string>
|
||||
<key>vcs_url</key>
|
||||
<string>git://github.com/secondlife/3p-glm.git</string>
|
||||
<key>version</key>
|
||||
<string>v1.0.1</string>
|
||||
</map>
|
||||
<key>gstreamer</key>
|
||||
<map>
|
||||
<key>platforms</key>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# -*- cmake -*-
|
||||
include(Prebuilt)
|
||||
|
||||
add_library( ll::glm INTERFACE IMPORTED )
|
||||
|
||||
use_system_binary( glm )
|
||||
use_prebuilt_binary(glm)
|
||||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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: |
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ include(LLCoreHttp)
|
|||
include(LLPhysicsExtensions)
|
||||
include(LLPrimitive)
|
||||
include(GLH)
|
||||
include(GLM)
|
||||
include(TinyGLTF)
|
||||
|
||||
set(llprimitive_SOURCE_FILES
|
||||
|
|
|
|||
|
|
@ -5,21 +5,21 @@
|
|||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, 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$
|
||||
*/
|
||||
|
|
@ -195,7 +195,7 @@ public:
|
|||
void writeToModel(tinygltf::Model& model, S32 mat_index) const;
|
||||
|
||||
virtual void applyOverride(const LLGLTFMaterial& override_mat);
|
||||
|
||||
|
||||
// apply the given LLSD override data
|
||||
void applyOverrideLLSD(const LLSD& data);
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -15738,5 +15738,16 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>GLTFEnabled</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Enable GLTF support. Set by SimulatorFeatures</string>
|
||||
<key>Persist</key>
|
||||
<integer>0</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
</map>
|
||||
</llsd>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
//
|
||||
// ========================================================================================================
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,14 @@
|
|||
#include "gltf/asset.h"
|
||||
#include "pipeline.h"
|
||||
#include "llviewershadermgr.h"
|
||||
#include "llviewertexturelist.h"
|
||||
#include "llimagej2c.h"
|
||||
#include "llfloaterperms.h"
|
||||
#include "llagentbenefits.h"
|
||||
#include "llfilesystem.h"
|
||||
#include "boost/json.hpp"
|
||||
|
||||
#define GLTF_SIM_SUPPORT 1
|
||||
|
||||
using namespace LL;
|
||||
|
||||
|
|
@ -126,6 +133,170 @@ void GLTFSceneManager::decomposeSelection()
|
|||
}
|
||||
}
|
||||
|
||||
void GLTFSceneManager::uploadSelection()
|
||||
{
|
||||
if (mUploadingAsset)
|
||||
{ // upload already in progress
|
||||
LLNotificationsUtil::add("GLTFUploadInProgress");
|
||||
return;
|
||||
}
|
||||
|
||||
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
|
||||
if (obj && obj->mGLTFAsset)
|
||||
{
|
||||
// make a copy of the asset prior to uploading
|
||||
mUploadingAsset = std::make_shared<Asset>();
|
||||
mUploadingObject = obj;
|
||||
*mUploadingAsset = *obj->mGLTFAsset;
|
||||
|
||||
GLTF::Asset& asset = *mUploadingAsset;
|
||||
|
||||
for (auto& image : asset.mImages)
|
||||
{
|
||||
if (!image.mData.empty())
|
||||
{
|
||||
mPendingImageUploads++;
|
||||
|
||||
LLPointer<LLImageRaw> raw = new LLImageRaw(image.mWidth, image.mHeight, image.mComponent);
|
||||
U8* data = raw->allocateData();
|
||||
llassert_always(image.mData.size() == raw->getDataSize());
|
||||
memcpy(data, image.mData.data(), image.mData.size());
|
||||
|
||||
// for GLTF native content, store image in GLTF orientation
|
||||
raw->verticalFlip();
|
||||
|
||||
LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
|
||||
|
||||
std::string buffer;
|
||||
buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
|
||||
|
||||
LLUUID asset_id = LLUUID::generateNewID();
|
||||
|
||||
std::string name;
|
||||
S32 idx = (S32)(&image - &asset.mImages[0]);
|
||||
|
||||
if (image.mName.empty())
|
||||
{
|
||||
|
||||
name = llformat("Image_%d", idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = image.mName;
|
||||
}
|
||||
|
||||
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
|
||||
{
|
||||
// TODO: handle failure
|
||||
mPendingImageUploads--;
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
|
||||
{
|
||||
if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
|
||||
{
|
||||
mUploadingAsset->mImages[idx].mUri = assetId.asString();
|
||||
mPendingImageUploads--;
|
||||
}
|
||||
};
|
||||
|
||||
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
|
||||
|
||||
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
|
||||
buffer,
|
||||
asset_id,
|
||||
name,
|
||||
name,
|
||||
0,
|
||||
LLFolderType::FT_TEXTURE,
|
||||
LLInventoryType::IT_TEXTURE,
|
||||
LLAssetType::AT_TEXTURE,
|
||||
LLFloaterPerms::getNextOwnerPerms("Uploads"),
|
||||
LLFloaterPerms::getGroupPerms("Uploads"),
|
||||
LLFloaterPerms::getEveryonePerms("Uploads"),
|
||||
expected_upload_cost,
|
||||
false,
|
||||
finish,
|
||||
failure));
|
||||
|
||||
upload_new_resource(uploadInfo);
|
||||
|
||||
image.clearData(asset);
|
||||
}
|
||||
}
|
||||
|
||||
// upload .bin
|
||||
for (auto& bin : asset.mBuffers)
|
||||
{
|
||||
mPendingBinaryUploads++;
|
||||
|
||||
S32 idx = (S32)(&bin - &asset.mBuffers[0]);
|
||||
|
||||
std::string buffer;
|
||||
buffer.assign((const char*)bin.mData.data(), bin.mData.size());
|
||||
|
||||
LLUUID asset_id = LLUUID::generateNewID();
|
||||
|
||||
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
|
||||
{
|
||||
// TODO: handle failure
|
||||
mPendingBinaryUploads--;
|
||||
mUploadingAsset = nullptr;
|
||||
mUploadingObject = nullptr;
|
||||
LL_WARNS("GLTF") << "Failed to upload GLTF binary: " << reason << LL_ENDL;
|
||||
LL_WARNS("GLTF") << response << LL_ENDL;
|
||||
return false;
|
||||
};
|
||||
|
||||
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx](LLUUID assetId, LLSD response)
|
||||
{
|
||||
if (mUploadingAsset && mUploadingAsset->mBuffers.size() > idx)
|
||||
{
|
||||
mUploadingAsset->mBuffers[idx].mUri = assetId.asString();
|
||||
mPendingBinaryUploads--;
|
||||
|
||||
// HACK: save buffer to cache to emulate a successful download
|
||||
LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE);
|
||||
auto& data = mUploadingAsset->mBuffers[idx].mData;
|
||||
|
||||
cache.write((const U8*)data.data(), data.size());
|
||||
}
|
||||
};
|
||||
#if GLTF_SIM_SUPPORT
|
||||
S32 expected_upload_cost = 1;
|
||||
|
||||
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
|
||||
buffer,
|
||||
asset_id,
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
LLFolderType::FT_NONE,
|
||||
LLInventoryType::IT_GLTF_BIN,
|
||||
LLAssetType::AT_GLTF_BIN,
|
||||
LLFloaterPerms::getNextOwnerPerms("Uploads"),
|
||||
LLFloaterPerms::getGroupPerms("Uploads"),
|
||||
LLFloaterPerms::getEveryonePerms("Uploads"),
|
||||
expected_upload_cost,
|
||||
false,
|
||||
finish,
|
||||
failure));
|
||||
|
||||
upload_new_resource(uploadInfo);
|
||||
#else
|
||||
// dummy finish
|
||||
finish(LLUUID::generateNewID(), LLSD());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LLNotificationsUtil::add("GLTFUploadSelection");
|
||||
}
|
||||
}
|
||||
|
||||
void GLTFSceneManager::decomposeSelection(const std::string& filename)
|
||||
{
|
||||
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
|
||||
|
|
@ -176,7 +347,7 @@ void GLTFSceneManager::load(const std::string& filename)
|
|||
if (obj)
|
||||
{ // assign to self avatar
|
||||
obj->mGLTFAsset = asset;
|
||||
|
||||
obj->markForUpdate();
|
||||
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
|
||||
{
|
||||
mObjects.push_back(obj);
|
||||
|
|
@ -199,6 +370,109 @@ void GLTFSceneManager::renderAlpha()
|
|||
render(false);
|
||||
}
|
||||
|
||||
void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id)
|
||||
{
|
||||
llassert(obj->getVolume()->getParams().getSculptID() == gltf_id);
|
||||
llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
|
||||
|
||||
obj->ref();
|
||||
gAssetStorage->getAssetData(gltf_id, LLAssetType::AT_GLTF, onGLTFLoadComplete, obj);
|
||||
}
|
||||
|
||||
//static
|
||||
void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
|
||||
{
|
||||
LLViewerObject* obj = (LLViewerObject*)user_data;
|
||||
llassert(asset_type == LLAssetType::AT_GLTF_BIN);
|
||||
|
||||
if (status == LL_ERR_NOERR)
|
||||
{
|
||||
if (obj)
|
||||
{
|
||||
// find the Buffer with the given id in the asset
|
||||
if (obj->mGLTFAsset)
|
||||
{
|
||||
for (auto& buffer : obj->mGLTFAsset->mBuffers)
|
||||
{
|
||||
LLUUID buffer_id;
|
||||
if (LLUUID::parseUUID(buffer.mUri, &buffer_id) && buffer_id == id)
|
||||
{
|
||||
LLFileSystem file(id, asset_type, LLFileSystem::READ);
|
||||
|
||||
buffer.mData.resize(file.getSize());
|
||||
file.read((U8*)buffer.mData.data(), buffer.mData.size());
|
||||
|
||||
obj->mGLTFAsset->mPendingBuffers--;
|
||||
|
||||
if (obj->mGLTFAsset->mPendingBuffers == 0)
|
||||
{
|
||||
obj->mGLTFAsset->allocateGLResources();
|
||||
GLTFSceneManager& mgr = GLTFSceneManager::instance();
|
||||
if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
|
||||
{
|
||||
GLTFSceneManager::instance().mObjects.push_back(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
|
||||
obj->unref();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
|
||||
{
|
||||
LLViewerObject* obj = (LLViewerObject*)user_data;
|
||||
llassert(asset_type == LLAssetType::AT_GLTF);
|
||||
|
||||
if (status == LL_ERR_NOERR)
|
||||
{
|
||||
if (obj)
|
||||
{
|
||||
LLFileSystem file(id, asset_type, LLFileSystem::READ);
|
||||
std::string data;
|
||||
data.resize(file.getSize());
|
||||
file.read((U8*)data.data(), data.size());
|
||||
|
||||
boost::json::value json = boost::json::parse(data);
|
||||
|
||||
std::shared_ptr<Asset> asset = std::make_shared<Asset>(json);
|
||||
obj->mGLTFAsset = asset;
|
||||
|
||||
for (auto& buffer : asset->mBuffers)
|
||||
{
|
||||
// for now just assume the buffer is already in the asset cache
|
||||
LLUUID buffer_id;
|
||||
if (LLUUID::parseUUID(buffer.mUri, &buffer_id))
|
||||
{
|
||||
asset->mPendingBuffers++;
|
||||
|
||||
gAssetStorage->getAssetData(buffer_id, LLAssetType::AT_GLTF_BIN, onGLTFBinLoadComplete, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL;
|
||||
obj->unref();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
|
||||
obj->unref();
|
||||
}
|
||||
}
|
||||
|
||||
void GLTFSceneManager::update()
|
||||
{
|
||||
for (U32 i = 0; i < mObjects.size(); ++i)
|
||||
|
|
@ -212,6 +486,107 @@ void GLTFSceneManager::update()
|
|||
|
||||
mObjects[i]->mGLTFAsset->update();
|
||||
}
|
||||
|
||||
// process pending uploads
|
||||
if (mUploadingAsset && !mGLTFUploadPending)
|
||||
{
|
||||
if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0)
|
||||
{
|
||||
std::string filename(gDirUtilp->getTempDir() + "/upload.gltf");
|
||||
#if 0
|
||||
tinygltf::Model model;
|
||||
mUploadingAsset->save(model);
|
||||
|
||||
tinygltf::TinyGLTF writer;
|
||||
|
||||
writer.WriteGltfSceneToFile(&model, filename, false, false, true, false);
|
||||
#else
|
||||
boost::json::object obj;
|
||||
mUploadingAsset->serialize(obj);
|
||||
std::string json = boost::json::serialize(obj, {});
|
||||
|
||||
{
|
||||
std::ofstream o(filename);
|
||||
o << json;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::ifstream t(filename);
|
||||
std::stringstream str;
|
||||
str << t.rdbuf();
|
||||
|
||||
std::string buffer = str.str();
|
||||
|
||||
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
|
||||
{
|
||||
// TODO: handle failure
|
||||
LL_WARNS("GLTF") << "Failed to upload GLTF json: " << reason << LL_ENDL;
|
||||
LL_WARNS("GLTF") << response << LL_ENDL;
|
||||
|
||||
mUploadingAsset = nullptr;
|
||||
mUploadingObject = nullptr;
|
||||
mGLTFUploadPending = false;
|
||||
return false;
|
||||
};
|
||||
|
||||
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response)
|
||||
{
|
||||
LLAppViewer::instance()->postToMainCoro(
|
||||
[=]()
|
||||
{
|
||||
if (mUploadingAsset)
|
||||
{
|
||||
// HACK: save buffer to cache to emulate a successful upload
|
||||
LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE);
|
||||
|
||||
LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL;
|
||||
cache.write((const U8 *) buffer.c_str(), buffer.size());
|
||||
|
||||
mUploadingAsset = nullptr;
|
||||
}
|
||||
|
||||
if (mUploadingObject)
|
||||
{
|
||||
mUploadingObject->mGLTFAsset = nullptr;
|
||||
mUploadingObject->setGLTFAsset(assetId);
|
||||
mUploadingObject->markForUpdate();
|
||||
mUploadingObject = nullptr;
|
||||
}
|
||||
|
||||
mGLTFUploadPending = false;
|
||||
});
|
||||
};
|
||||
|
||||
#if GLTF_SIM_SUPPORT
|
||||
S32 expected_upload_cost = 1;
|
||||
LLUUID asset_id = LLUUID::generateNewID();
|
||||
|
||||
mGLTFUploadPending = true;
|
||||
|
||||
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
|
||||
buffer,
|
||||
asset_id,
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
LLFolderType::FT_NONE,
|
||||
LLInventoryType::IT_GLTF,
|
||||
LLAssetType::AT_GLTF,
|
||||
LLFloaterPerms::getNextOwnerPerms("Uploads"),
|
||||
LLFloaterPerms::getGroupPerms("Uploads"),
|
||||
LLFloaterPerms::getEveryonePerms("Uploads"),
|
||||
expected_upload_cost,
|
||||
false,
|
||||
finish,
|
||||
failure));
|
||||
|
||||
upload_new_resource(uploadInfo);
|
||||
#else
|
||||
// dummy finish
|
||||
finish(LLUUID::generateNewID(), LLSD());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLTFSceneManager::render(bool opaque, bool rigged)
|
||||
|
|
@ -219,7 +594,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
|
|||
// for debugging, just render the whole scene as opaque
|
||||
// by traversing the whole scenegraph
|
||||
// Assumes camera transform is already set and
|
||||
// appropriate shader is already bound
|
||||
// appropriate shader is already boundd
|
||||
|
||||
gGL.matrixMode(LLRender::MM_MODELVIEW);
|
||||
|
||||
|
|
@ -243,7 +618,8 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
|
|||
|
||||
matMul(mat, modelview, modelview);
|
||||
|
||||
asset->updateRenderTransforms(modelview);
|
||||
mat4 mdv = glm::make_mat4(modelview.getF32ptr());
|
||||
asset->updateRenderTransforms(mdv);
|
||||
asset->render(opaque, rigged);
|
||||
|
||||
gGL.popMatrix();
|
||||
|
|
@ -411,12 +787,16 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
|
|||
// get raycast in asset space
|
||||
LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform();
|
||||
|
||||
LLVector4a start;
|
||||
LLVector4a end;
|
||||
vec4 start;
|
||||
vec4 end;
|
||||
|
||||
agent_to_asset.affineTransform(gDebugRaycastStart, start);
|
||||
agent_to_asset.affineTransform(gDebugRaycastEnd, end);
|
||||
LLVector4a t;
|
||||
agent_to_asset.affineTransform(gDebugRaycastStart, t);
|
||||
start = glm::make_vec4(t.getF32ptr());
|
||||
agent_to_asset.affineTransform(gDebugRaycastEnd, t);
|
||||
end = glm::make_vec4(t.getF32ptr());
|
||||
|
||||
start.w = end.w = 1.0;
|
||||
|
||||
for (auto& node : asset->mNodes)
|
||||
{
|
||||
|
|
@ -424,7 +804,7 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
|
|||
|
||||
if (node.mMesh != INVALID_INDEX)
|
||||
{
|
||||
gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix);
|
||||
gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
|
||||
|
||||
// draw bounding box of mesh primitives
|
||||
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
|
||||
|
|
@ -442,24 +822,24 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
|
||||
{
|
||||
gGL.flush();
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
|
||||
// convert raycast to node local space
|
||||
LLVector4a local_start;
|
||||
LLVector4a local_end;
|
||||
|
||||
node.mAssetMatrixInv.affineTransform(start, local_start);
|
||||
node.mAssetMatrixInv.affineTransform(end, local_end);
|
||||
vec4 local_start = node.mAssetMatrixInv * start;
|
||||
vec4 local_end = node.mAssetMatrixInv * end;
|
||||
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
{
|
||||
if (primitive.mOctree.notNull())
|
||||
{
|
||||
renderOctreeRaycast(local_start, local_end, primitive.mOctree);
|
||||
LLVector4a s, e;
|
||||
s.load3(glm::value_ptr(local_start));
|
||||
e.load3(glm::value_ptr(local_end));
|
||||
renderOctreeRaycast(s, e, primitive.mOctree);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -499,18 +879,18 @@ void GLTFSceneManager::renderDebug()
|
|||
continue;
|
||||
}
|
||||
|
||||
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
|
||||
mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
|
||||
|
||||
LLMatrix4a modelview;
|
||||
modelview.loadu(gGLModelView);
|
||||
mat4 modelview = glm::make_mat4(gGLModelView);
|
||||
|
||||
matMul(mat, modelview, modelview);
|
||||
|
||||
modelview = modelview * mat;
|
||||
|
||||
Asset* asset = obj->mGLTFAsset.get();
|
||||
|
||||
for (auto& node : asset->mNodes)
|
||||
{
|
||||
matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
|
||||
node.mRenderMatrix = modelview * node.mAssetMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -523,13 +903,6 @@ void GLTFSceneManager::renderDebug()
|
|||
|
||||
Asset* asset = obj->mGLTFAsset.get();
|
||||
|
||||
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
|
||||
|
||||
LLMatrix4a modelview;
|
||||
modelview.loadu(gGLModelView);
|
||||
|
||||
matMul(mat, modelview, modelview);
|
||||
|
||||
renderAssetDebug(obj, asset);
|
||||
}
|
||||
|
||||
|
|
@ -551,21 +924,20 @@ void GLTFSceneManager::renderDebug()
|
|||
continue;
|
||||
}
|
||||
|
||||
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
|
||||
mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
|
||||
|
||||
LLMatrix4a modelview;
|
||||
modelview.loadu(gGLModelView);
|
||||
mat4 modelview = glm::make_mat4(gGLModelView);
|
||||
|
||||
matMul(mat, modelview, modelview);
|
||||
modelview = modelview * mat;
|
||||
|
||||
Asset* asset = obj->mGLTFAsset.get();
|
||||
|
||||
for (auto& node : asset->mNodes)
|
||||
{
|
||||
// force update all mRenderMatrix, not just nodes with meshes
|
||||
matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
|
||||
node.mRenderMatrix = modelview * node.mAssetMatrix;
|
||||
|
||||
gGL.loadMatrix(node.mRenderMatrix.getF32ptr());
|
||||
gGL.loadMatrix(glm::value_ptr(node.mRenderMatrix));
|
||||
// render x-axis red, y-axis green, z-axis blue
|
||||
gGL.color4f(1.f, 0.f, 0.f, 0.5f);
|
||||
gGL.begin(LLRender::LINES);
|
||||
|
|
@ -595,7 +967,9 @@ void GLTFSceneManager::renderDebug()
|
|||
{
|
||||
Node& child = asset->mNodes[child_idx];
|
||||
gGL.vertex3f(0.f, 0.f, 0.f);
|
||||
gGL.vertex3fv(child.mMatrix.getTranslation().getF32ptr());
|
||||
|
||||
|
||||
gGL.vertex3fv(glm::value_ptr(child.mMatrix[3]));
|
||||
}
|
||||
gGL.end();
|
||||
gGL.flush();
|
||||
|
|
@ -628,8 +1002,7 @@ void GLTFSceneManager::renderDebug()
|
|||
gGL.color3f(1, 0, 1);
|
||||
drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f));
|
||||
|
||||
gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix);
|
||||
|
||||
gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix));
|
||||
|
||||
|
||||
auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0);
|
||||
|
|
@ -643,3 +1016,5 @@ void GLTFSceneManager::renderDebug()
|
|||
gDebugProgram.unbind();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -299,10 +299,18 @@ void LLResourceUploadInfo::assignDefaults()
|
|||
mDescription = "(No Description)";
|
||||
}
|
||||
|
||||
if (mAssetType == LLAssetType::AT_GLTF ||
|
||||
mAssetType == LLAssetType::AT_GLTF_BIN)
|
||||
{
|
||||
mFolderId = LLUUID::null;
|
||||
}
|
||||
else
|
||||
{
|
||||
mFolderId = gInventory.findUserDefinedCategoryUUIDForType(
|
||||
(mDestinationFolderType == LLFolderType::FT_NONE) ?
|
||||
(LLFolderType::EType)mAssetType : mDestinationFolderType);
|
||||
}
|
||||
}
|
||||
|
||||
std::string LLResourceUploadInfo::getDisplayName() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2674,7 +2674,13 @@ void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url)
|
|||
{
|
||||
if (initializeMedia(mimeType))
|
||||
{
|
||||
loadURI();
|
||||
ref();
|
||||
LLAppViewer::instance()->postToMainCoro([this]()
|
||||
{
|
||||
loadURI();
|
||||
unref();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@
|
|||
#include "boost/unordered_map.hpp"
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include "llcleanup.h"
|
||||
#include "llviewershadermgr.h"
|
||||
#include "gltfscenemanager.h"
|
||||
|
|
@ -3317,6 +3318,13 @@ bool enable_os_exception()
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool enable_gltf()
|
||||
{
|
||||
static LLCachedControl<bool> enablegltf(gSavedSettings, "GLTFEnabled", false);
|
||||
return enablegltf;
|
||||
}
|
||||
|
||||
class LLSelfRemoveAllAttachments : public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
|
|
@ -8080,7 +8088,6 @@ class LLAdvancedClickGLTFOpen: public view_listener_t
|
|||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
// open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
|
||||
LL::GLTFSceneManager::instance().load();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -8090,7 +8097,6 @@ class LLAdvancedClickGLTFSaveAs : public view_listener_t
|
|||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
// open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
|
||||
LL::GLTFSceneManager::instance().saveAs();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -8100,12 +8106,39 @@ class LLAdvancedClickGLTFDecompose : public view_listener_t
|
|||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
// open personal lighting floater when previewing an HDRI (keeps HDRI from implicitly unloading when opening build tools)
|
||||
LL::GLTFSceneManager::instance().decomposeSelection();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class LLAdvancedClickGLTFUpload: public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
LL::GLTFSceneManager::instance().uploadSelection();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class LLAdvancedClickResizeWindow : public view_listener_t
|
||||
{
|
||||
bool handleEvent(const LLSD& userdata)
|
||||
{
|
||||
S32 w = 0;
|
||||
S32 h = 0;
|
||||
|
||||
sscanf(userdata.asString().c_str(), "%dx%d", &w, &h);
|
||||
|
||||
if (w > 0 && h > 0)
|
||||
{
|
||||
gViewerWindow->getWindow()->setSize(LLCoordWindow(w, h));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// these are used in the gl menus to set control values that require shader recompilation
|
||||
class LLToggleShaderControl : public view_listener_t
|
||||
{
|
||||
|
|
@ -9761,6 +9794,8 @@ void initialize_menus()
|
|||
view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen");
|
||||
view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs");
|
||||
view_listener_t::addMenu(new LLAdvancedClickGLTFDecompose(), "Advanced.ClickGLTFDecompose");
|
||||
view_listener_t::addMenu(new LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload");
|
||||
view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow");
|
||||
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
|
||||
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
|
||||
|
||||
|
|
@ -10065,6 +10100,7 @@ void initialize_menus()
|
|||
commit.add("Pathfinding.Characters.Select", boost::bind(&LLFloaterPathfindingCharacters::openCharactersWithSelectedObjects));
|
||||
enable.add("EnableSelectInPathfindingCharacters", boost::bind(&enable_object_select_in_pathfinding_characters));
|
||||
enable.add("Advanced.EnableErrorOSException", boost::bind(&enable_os_exception));
|
||||
enable.add("EnableGLTF", boost::bind(&enable_gltf));
|
||||
|
||||
view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible");
|
||||
view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel");
|
||||
|
|
|
|||
|
|
@ -4296,7 +4296,9 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const
|
|||
|
||||
LLMatrix4a asset_to_agent = getGLTFAssetToAgentTransform();
|
||||
LLMatrix4a node_to_agent;
|
||||
matMul(node.mAssetMatrix, asset_to_agent, node_to_agent);
|
||||
LLMatrix4a am;
|
||||
am.loadu(glm::value_ptr(node.mAssetMatrix));
|
||||
matMul(am, asset_to_agent, node_to_agent);
|
||||
|
||||
mat = node_to_agent;
|
||||
}
|
||||
|
|
@ -4307,6 +4309,7 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const
|
|||
|
||||
return mat;
|
||||
}
|
||||
|
||||
void LLViewerObject::getGLTFNodeTransformAgent(S32 node_index, LLVector3* position, LLQuaternion* rotation, LLVector3* scale) const
|
||||
{
|
||||
LLMatrix4a node_to_agent = getGLTFNodeTransformAgent(node_index);
|
||||
|
|
@ -4354,7 +4357,9 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion
|
|||
if (node.mParent != -1)
|
||||
{
|
||||
auto& parent = mGLTFAsset->mNodes[node.mParent];
|
||||
matMul(agent_to_asset, parent.mAssetMatrixInv, agent_to_node);
|
||||
LLMatrix4a ami;
|
||||
ami.loadu(glm::value_ptr(parent.mAssetMatrixInv));
|
||||
matMul(agent_to_asset, ami, agent_to_node);
|
||||
}
|
||||
|
||||
LLQuaternion agent_to_node_rot(agent_to_node.asMatrix4());
|
||||
|
|
@ -4366,9 +4371,13 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion
|
|||
LLVector3 pos;
|
||||
LLQuaternion rot;
|
||||
LLVector3 scale;
|
||||
decomposeMatrix(node.mMatrix, pos, rot, scale);
|
||||
LLMatrix4a mat;
|
||||
mat.loadu(glm::value_ptr(node.mMatrix));
|
||||
decomposeMatrix(mat, pos, rot, scale);
|
||||
|
||||
node.mMatrix.asMatrix4().initAll(scale, new_rot, pos);
|
||||
mat.asMatrix4().initAll(scale, new_rot, pos);
|
||||
|
||||
node.mMatrix = glm::make_mat4(mat.getF32ptr());
|
||||
|
||||
mGLTFAsset->updateTransforms();
|
||||
}
|
||||
|
|
@ -4382,7 +4391,9 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
|
|||
|
||||
LLMatrix4a agent_to_asset = getAgentToGLTFAssetTransform();
|
||||
LLMatrix4a agent_to_node;
|
||||
matMul(agent_to_asset, node.mAssetMatrixInv, agent_to_node);
|
||||
LLMatrix4a ami;
|
||||
ami.loadu(glm::value_ptr(node.mAssetMatrixInv));
|
||||
matMul(agent_to_asset, ami, agent_to_node);
|
||||
|
||||
LLVector4a origin = LLVector4a::getZero();
|
||||
LLVector4a offset_v;
|
||||
|
|
@ -4399,7 +4410,12 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
|
|||
trans.setIdentity();
|
||||
trans.mMatrix[3] = offset_v;
|
||||
|
||||
matMul(trans, node.mMatrix, node.mMatrix);
|
||||
LLMatrix4a mat;
|
||||
mat.loadu(glm::value_ptr(node.mMatrix));
|
||||
|
||||
matMul(trans, mat, mat);
|
||||
|
||||
node.mMatrix = glm::make_mat4(mat.getF32ptr());
|
||||
|
||||
// TODO -- only update transforms for this node and its children (or use a dirty flag)
|
||||
mGLTFAsset->updateTransforms();
|
||||
|
|
@ -7490,6 +7506,23 @@ void LLViewerObject::shrinkWrap()
|
|||
}
|
||||
}
|
||||
|
||||
void LLViewerObject::setGLTFAsset(const LLUUID& id)
|
||||
{
|
||||
//get the sculpt params and set the sculpt type and id
|
||||
auto* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_SCULPT);
|
||||
|
||||
LLSculptParams* sculpt_params = (LLSculptParams*)param->data;
|
||||
sculpt_params->setSculptTexture(id, LL_SCULPT_TYPE_GLTF);
|
||||
|
||||
setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, true, true);
|
||||
|
||||
// Update the volume
|
||||
LLVolumeParams volume_params;
|
||||
volume_params.setSculptID(id, LL_SCULPT_TYPE_GLTF);
|
||||
updateVolume(volume_params);
|
||||
}
|
||||
|
||||
|
||||
class ObjectPhysicsProperties : public LLHTTPNode
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -2462,24 +2462,14 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
|
|||
gSavedSettings.setS32("max_texture_dimension_Y", 1024);
|
||||
}
|
||||
|
||||
if (features.has("PBRTerrainEnabled"))
|
||||
if (features.has("GLTFEnabled"))
|
||||
{
|
||||
bool enabled = features["PBRTerrainEnabled"];
|
||||
gSavedSettings.setBOOL("RenderTerrainPBREnabled", enabled);
|
||||
bool enabled = features["GLTFEnabled"];
|
||||
gSavedSettings.setBOOL("GLTFEnabled", enabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
gSavedSettings.setBOOL("RenderTerrainPBREnabled", false);
|
||||
}
|
||||
|
||||
if (features.has("PBRMaterialSwatchEnabled"))
|
||||
{
|
||||
bool enabled = features["PBRMaterialSwatchEnabled"];
|
||||
gSavedSettings.setBOOL("UIPreviewMaterial", enabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
gSavedSettings.setBOOL("UIPreviewMaterial", false);
|
||||
gSavedSettings.setBOOL("GLTFEnabled", false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
#include "llsculptidsize.h"
|
||||
#include "llavatarappearancedefines.h"
|
||||
#include "llgltfmateriallist.h"
|
||||
#include "gltfscenemanager.h"
|
||||
|
||||
const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
|
||||
const F32 FORCE_CULL_AREA = 8.f;
|
||||
|
|
@ -1133,6 +1134,11 @@ bool LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo
|
|||
}
|
||||
}
|
||||
|
||||
if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF)
|
||||
{ // notify GLTFSceneManager about new GLTF object
|
||||
LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (NO_LOD == lod)
|
||||
|
|
@ -1407,6 +1413,12 @@ bool LLVOVolume::calcLOD()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mGLTFAsset != nullptr)
|
||||
{
|
||||
// do not calculate LOD for GLTF objects
|
||||
return false;
|
||||
}
|
||||
|
||||
S32 cur_detail = 0;
|
||||
|
||||
F32 radius;
|
||||
|
|
@ -5623,7 +5635,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
|
|||
|
||||
LLVOVolume* vobj = drawablep->getVOVolume();
|
||||
|
||||
if (!vobj || vobj->isDead())
|
||||
if (!vobj || vobj->isDead() || vobj->mGLTFAsset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2866,21 +2866,69 @@ function="World.EnvPreset"
|
|||
<menu_item_call
|
||||
label="Open..."
|
||||
name="Open...">
|
||||
<menu_item_call.on_enable
|
||||
function="EnableGLTF"/>
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickGLTFOpen" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Save As..."
|
||||
name="Save As...">
|
||||
<menu_item_call.on_enable
|
||||
function="EnableGLTF"/>
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickGLTFSaveAs" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Decompose..."
|
||||
name="Decompose...">
|
||||
<menu_item_call.on_enable
|
||||
function="EnableGLTF"/>
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickGLTFDecompose" />
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="Upload..."
|
||||
name="Upload...">
|
||||
<menu_item_call.on_enable
|
||||
function="EnableGLTF"/>
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickGLTFUpload" />
|
||||
</menu_item_call>
|
||||
</menu>
|
||||
<menu
|
||||
create_jump_keys="true"
|
||||
label="Video"
|
||||
name="Video"
|
||||
tear_off="true">
|
||||
<menu_item_call
|
||||
label="1080x1920"
|
||||
name="1080x1920">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickResizeWindow"
|
||||
parameter="1080x1920"/>
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="1920x1080"
|
||||
name="1920x1080">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickResizeWindow"
|
||||
parameter="1920x1080"/>
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="1280x720"
|
||||
name="1280x720">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickResizeWindow"
|
||||
parameter="1280x720"/>
|
||||
</menu_item_call>
|
||||
<menu_item_call
|
||||
label="720x1280"
|
||||
name="720x1280">
|
||||
<menu_item_call.on_click
|
||||
function="Advanced.ClickResizeWindow"
|
||||
parameter="720x1280"/>
|
||||
</menu_item_call>
|
||||
</menu>
|
||||
<menu
|
||||
create_jump_keys="true"
|
||||
|
|
|
|||
|
|
@ -12481,4 +12481,26 @@ are wearing now.
|
|||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="GLTFUploadSelection"
|
||||
type="alert">
|
||||
You must select an object that has local-only GLTF asset associated with it.
|
||||
<tag>fail</tag>
|
||||
<usetemplate
|
||||
name="okbutton"
|
||||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
<notification
|
||||
icon="alertmodal.tga"
|
||||
name="GLTFUploadInProgress"
|
||||
type="alert">
|
||||
Upload is currently in progress. Please try again later.
|
||||
<tag>fail</tag>
|
||||
<usetemplate
|
||||
name="okbutton"
|
||||
yestext="OK"/>
|
||||
</notification>
|
||||
|
||||
</notifications>
|
||||
|
|
|
|||
Loading…
Reference in New Issue