Merge branch 'project/gltf_development' of https://github.com/secondlife/viewer

# Conflicts:
#	autobuild.xml
#	indra/newview/app_settings/settings.xml
#	indra/newview/llinventorymodel.cpp
#	indra/newview/llviewermenu.cpp
#	indra/newview/llviewerregion.cpp
#	indra/newview/llvovolume.cpp
#	indra/newview/skins/default/xui/en/notifications.xml
master
Ansariel 2024-05-23 01:48:45 +02:00
commit c203bb650a
51 changed files with 2724 additions and 436 deletions

View File

@ -1121,6 +1121,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>havok-source</key>
<map>
<key>platforms</key>

View File

@ -2,6 +2,8 @@
## Tiling
The southwest corner of a region with PBR materials should exactly match up with the bottom left corner of the material texture(s).
If two adjacent regions have the same PBR terrain settings, then:
- There should not be seams between the two regions at their shared border

View File

@ -126,7 +126,7 @@ if(WINDOWS)
elseif (MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1930) # Visual Studio 2019
set(MSVC_VER 140)
set(MSVC_TOOLSET_VER 142)
elseif (MSVC_VERSION GREATER_EQUAL 1930 AND MSVC_VERSION LESS 1940) # Visual Studio 2022
elseif (MSVC_VERSION GREATER_EQUAL 1930 AND MSVC_VERSION LESS 1950) # Visual Studio 2022
set(MSVC_VER 140)
set(MSVC_TOOLSET_VER 143)
else (MSVC80)

7
indra/cmake/GLM.cmake Normal file
View File

@ -0,0 +1,7 @@
# -*- cmake -*-
include(Prebuilt)
add_library( ll::glm INTERFACE IMPORTED )
use_system_binary( glm )
use_prebuilt_binary(glm)

View File

@ -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));

View File

@ -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: |

View File

@ -371,6 +371,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" }
};

View File

@ -84,6 +84,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));
@ -154,9 +156,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

View File

@ -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

View File

@ -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;

View File

@ -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
////////////////////////////////////

View File

@ -193,11 +193,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;

View File

@ -8,6 +8,7 @@ include(LLCoreHttp)
include(LLPhysicsExtensions)
include(LLPrimitive)
include(GLH)
include(GLM)
include(TinyGLTF)
include(ColladaDom)

View File

@ -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

View File

@ -84,7 +84,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.

View File

@ -24323,6 +24323,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<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>
<key>FSNetMapPhantomOpacity</key>
<map>

View File

@ -35,7 +35,7 @@ uniform float minimum_alpha;
void main()
{
float alpha = texture(diffuseMap,vary_texcoord0.xy).a;
float alpha = texture(diffuseMap,vary_texcoord0.xy).a * vertex_color.a;
if (alpha < minimum_alpha)
{

View File

@ -69,12 +69,16 @@ void main()
mirrorClip(vary_position);
vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba;
basecolor.rgb = srgb_to_linear(basecolor.rgb);
basecolor *= vertex_color;
if (basecolor.a < minimum_alpha)
{
discard;
}
vec3 col = vertex_color.rgb * srgb_to_linear(basecolor.rgb);
vec3 col = basecolor.rgb;
// from mikktspace.com
vec3 vNt = texture(bumpMap, normal_texcoord.xy).xyz*2.0-1.0;
@ -108,7 +112,7 @@ void main()
//emissive = tnorm*0.5+0.5;
// See: C++: addDeferredAttachments(), GLSL: softenLightF
frag_data[0] = max(vec4(col, 0.0), vec4(0)); // Diffuse
frag_data[1] = max(vec4(spec.rgb,vertex_color.a), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
frag_data[1] = max(vec4(spec.rgb,0.0), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
frag_data[2] = vec4(tnorm, GBUFFER_FLAG_HAS_PBR); // normal, environment intensity, flags
frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive
}

View File

@ -195,18 +195,18 @@ void main()
PBRMix mix = init_pbr_mix();
PBRMix mix2;
TerrainCoord terrain_texcoord;
switch (tm.type & MIX_X)
{
case MIX_X:
#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
TerrainCoord terrain_texcoord;
terrain_texcoord[0].xy = vary_coords[0].xy;
terrain_texcoord[0].zw = vary_coords[0].zw;
terrain_texcoord[1].xy = vary_coords[1].xy;
terrain_texcoord[1].zw = vary_coords[1].zw;
terrain_texcoord[2].xy = vary_coords[2].xy;
#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
TerrainCoord terrain_texcoord = vary_coords[0].xy;
terrain_texcoord = vary_coords[0].xy;
#endif
mix2 = terrain_sample_and_multiply_pbr(
terrain_texcoord
@ -242,14 +242,13 @@ void main()
{
case MIX_Y:
#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
TerrainCoord terrain_texcoord;
terrain_texcoord[0].xy = vary_coords[2].zw;
terrain_texcoord[0].zw = vary_coords[3].xy;
terrain_texcoord[1].xy = vary_coords[3].zw;
terrain_texcoord[1].zw = vary_coords[4].xy;
terrain_texcoord[2].xy = vary_coords[4].zw;
#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
TerrainCoord terrain_texcoord = vary_coords[0].zw;
terrain_texcoord = vary_coords[0].zw;
#endif
mix2 = terrain_sample_and_multiply_pbr(
terrain_texcoord
@ -285,14 +284,13 @@ void main()
{
case MIX_Z:
#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
TerrainCoord terrain_texcoord;
terrain_texcoord[0].xy = vary_coords[5].xy;
terrain_texcoord[0].zw = vary_coords[5].zw;
terrain_texcoord[1].xy = vary_coords[6].xy;
terrain_texcoord[1].zw = vary_coords[6].zw;
terrain_texcoord[2].xy = vary_coords[7].xy;
#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
TerrainCoord terrain_texcoord = vary_coords[1].xy;
terrain_texcoord = vary_coords[1].xy;
#endif
mix2 = terrain_sample_and_multiply_pbr(
terrain_texcoord
@ -328,14 +326,13 @@ void main()
{
case MIX_W:
#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
TerrainCoord terrain_texcoord;
terrain_texcoord[0].xy = vary_coords[7].zw;
terrain_texcoord[0].zw = vary_coords[8].xy;
terrain_texcoord[1].xy = vary_coords[8].zw;
terrain_texcoord[1].zw = vary_coords[9].xy;
terrain_texcoord[2].xy = vary_coords[9].zw;
#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
TerrainCoord terrain_texcoord = vary_coords[1].zw;
terrain_texcoord = vary_coords[1].zw;
#endif
mix2 = terrain_sample_and_multiply_pbr(
terrain_texcoord

View File

@ -84,11 +84,9 @@ vec2 terrain_texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform)
{
vec2 texcoord = vertex_texcoord;
texcoord.y = 1.0 - texcoord.y;
//texcoord.y = -texcoord.y;
texcoord.y = -texcoord.y;
texcoord = khr_texture_transform(texcoord, khr_gltf_transform[0].xy, khr_gltf_transform[0].z, khr_gltf_transform[1].xy);
texcoord.y = 1.0 - texcoord.y;
//texcoord.y = -texcoord.y;
texcoord.y = -texcoord.y;
return texcoord;
}

View File

@ -98,7 +98,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
haze_glow = max(haze_glow, .001); // set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
haze_glow *= glow.x;
// higher glow.x gives dimmer glow (because next step is 1 / "angle")
haze_glow = clamp(pow(haze_glow, glow.z), -10, 10);
haze_glow = clamp(pow(haze_glow, glow.z), -100000, 100000);
// glow.z should be negative, so we're doing a sort of (1 / "angle") function
// add "minimum anti-solar illumination"

View File

@ -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.

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;
}
//
// ========================================================================================================
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -39,7 +39,14 @@
#include "gltf/asset.h"
#include "pipeline.h"
#include "llviewershadermgr.h"
#include "llviewertexturelist.h"
#include "llimagej2c.h"
#include "llfloaterperms.h"
#include "llagentbenefits.h"
#include "llfilesystem.h"
#include "boost/json.hpp"
#define GLTF_SIM_SUPPORT 1
using namespace LL;
@ -126,6 +133,170 @@ void GLTFSceneManager::decomposeSelection()
}
}
void GLTFSceneManager::uploadSelection()
{
if (mUploadingAsset)
{ // upload already in progress
LLNotificationsUtil::add("GLTFUploadInProgress");
return;
}
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
if (obj && obj->mGLTFAsset)
{
// make a copy of the asset prior to uploading
mUploadingAsset = std::make_shared<Asset>();
mUploadingObject = obj;
*mUploadingAsset = *obj->mGLTFAsset;
GLTF::Asset& asset = *mUploadingAsset;
for (auto& image : asset.mImages)
{
if (!image.mData.empty())
{
mPendingImageUploads++;
LLPointer<LLImageRaw> raw = new LLImageRaw(image.mWidth, image.mHeight, image.mComponent);
U8* data = raw->allocateData();
llassert_always(image.mData.size() == raw->getDataSize());
memcpy(data, image.mData.data(), image.mData.size());
// for GLTF native content, store image in GLTF orientation
raw->verticalFlip();
LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
std::string buffer;
buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
LLUUID asset_id = LLUUID::generateNewID();
std::string name;
S32 idx = (S32)(&image - &asset.mImages[0]);
if (image.mName.empty())
{
name = llformat("Image_%d", idx);
}
else
{
name = image.mName;
}
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
// TODO: handle failure
mPendingImageUploads--;
return false;
};
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
{
if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
{
mUploadingAsset->mImages[idx].mUri = assetId.asString();
mPendingImageUploads--;
}
};
S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
name,
name,
0,
LLFolderType::FT_TEXTURE,
LLInventoryType::IT_TEXTURE,
LLAssetType::AT_TEXTURE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
upload_new_resource(uploadInfo);
image.clearData(asset);
}
}
// upload .bin
for (auto& bin : asset.mBuffers)
{
mPendingBinaryUploads++;
S32 idx = (S32)(&bin - &asset.mBuffers[0]);
std::string buffer;
buffer.assign((const char*)bin.mData.data(), bin.mData.size());
LLUUID asset_id = LLUUID::generateNewID();
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
// TODO: handle failure
mPendingBinaryUploads--;
mUploadingAsset = nullptr;
mUploadingObject = nullptr;
LL_WARNS("GLTF") << "Failed to upload GLTF binary: " << reason << LL_ENDL;
LL_WARNS("GLTF") << response << LL_ENDL;
return false;
};
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx](LLUUID assetId, LLSD response)
{
if (mUploadingAsset && mUploadingAsset->mBuffers.size() > idx)
{
mUploadingAsset->mBuffers[idx].mUri = assetId.asString();
mPendingBinaryUploads--;
// HACK: save buffer to cache to emulate a successful download
LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE);
auto& data = mUploadingAsset->mBuffers[idx].mData;
cache.write((const U8*)data.data(), data.size());
}
};
#if GLTF_SIM_SUPPORT
S32 expected_upload_cost = 1;
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
"",
"",
0,
LLFolderType::FT_NONE,
LLInventoryType::IT_GLTF_BIN,
LLAssetType::AT_GLTF_BIN,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
upload_new_resource(uploadInfo);
#else
// dummy finish
finish(LLUUID::generateNewID(), LLSD());
#endif
}
}
else
{
LLNotificationsUtil::add("GLTFUploadSelection");
}
}
void GLTFSceneManager::decomposeSelection(const std::string& filename)
{
LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
@ -176,7 +347,7 @@ void GLTFSceneManager::load(const std::string& filename)
if (obj)
{ // assign to self avatar
obj->mGLTFAsset = asset;
obj->markForUpdate();
if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
{
mObjects.push_back(obj);
@ -199,6 +370,109 @@ void GLTFSceneManager::renderAlpha()
render(false);
}
void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id)
{
llassert(obj->getVolume()->getParams().getSculptID() == gltf_id);
llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
obj->ref();
gAssetStorage->getAssetData(gltf_id, LLAssetType::AT_GLTF, onGLTFLoadComplete, obj);
}
//static
void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
{
LLViewerObject* obj = (LLViewerObject*)user_data;
llassert(asset_type == LLAssetType::AT_GLTF_BIN);
if (status == LL_ERR_NOERR)
{
if (obj)
{
// find the Buffer with the given id in the asset
if (obj->mGLTFAsset)
{
for (auto& buffer : obj->mGLTFAsset->mBuffers)
{
LLUUID buffer_id;
if (LLUUID::parseUUID(buffer.mUri, &buffer_id) && buffer_id == id)
{
LLFileSystem file(id, asset_type, LLFileSystem::READ);
buffer.mData.resize(file.getSize());
file.read((U8*)buffer.mData.data(), buffer.mData.size());
obj->mGLTFAsset->mPendingBuffers--;
if (obj->mGLTFAsset->mPendingBuffers == 0)
{
obj->mGLTFAsset->allocateGLResources();
GLTFSceneManager& mgr = GLTFSceneManager::instance();
if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
{
GLTFSceneManager::instance().mObjects.push_back(obj);
}
}
}
}
}
}
}
else
{
LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
obj->unref();
}
}
//static
void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
{
LLViewerObject* obj = (LLViewerObject*)user_data;
llassert(asset_type == LLAssetType::AT_GLTF);
if (status == LL_ERR_NOERR)
{
if (obj)
{
LLFileSystem file(id, asset_type, LLFileSystem::READ);
std::string data;
data.resize(file.getSize());
file.read((U8*)data.data(), data.size());
boost::json::value json = boost::json::parse(data);
std::shared_ptr<Asset> asset = std::make_shared<Asset>(json);
obj->mGLTFAsset = asset;
for (auto& buffer : asset->mBuffers)
{
// for now just assume the buffer is already in the asset cache
LLUUID buffer_id;
if (LLUUID::parseUUID(buffer.mUri, &buffer_id))
{
asset->mPendingBuffers++;
gAssetStorage->getAssetData(buffer_id, LLAssetType::AT_GLTF_BIN, onGLTFBinLoadComplete, obj);
}
else
{
LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL;
obj->unref();
return;
}
}
}
}
else
{
LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
obj->unref();
}
}
void GLTFSceneManager::update()
{
for (U32 i = 0; i < mObjects.size(); ++i)
@ -212,6 +486,107 @@ void GLTFSceneManager::update()
mObjects[i]->mGLTFAsset->update();
}
// process pending uploads
if (mUploadingAsset && !mGLTFUploadPending)
{
if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0)
{
std::string filename(gDirUtilp->getTempDir() + "/upload.gltf");
#if 0
tinygltf::Model model;
mUploadingAsset->save(model);
tinygltf::TinyGLTF writer;
writer.WriteGltfSceneToFile(&model, filename, false, false, true, false);
#else
boost::json::object obj;
mUploadingAsset->serialize(obj);
std::string json = boost::json::serialize(obj, {});
{
std::ofstream o(filename);
o << json;
}
#endif
std::ifstream t(filename);
std::stringstream str;
str << t.rdbuf();
std::string buffer = str.str();
LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
{
// TODO: handle failure
LL_WARNS("GLTF") << "Failed to upload GLTF json: " << reason << LL_ENDL;
LL_WARNS("GLTF") << response << LL_ENDL;
mUploadingAsset = nullptr;
mUploadingObject = nullptr;
mGLTFUploadPending = false;
return false;
};
LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response)
{
LLAppViewer::instance()->postToMainCoro(
[=]()
{
if (mUploadingAsset)
{
// HACK: save buffer to cache to emulate a successful upload
LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE);
LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL;
cache.write((const U8 *) buffer.c_str(), buffer.size());
mUploadingAsset = nullptr;
}
if (mUploadingObject)
{
mUploadingObject->mGLTFAsset = nullptr;
mUploadingObject->setGLTFAsset(assetId);
mUploadingObject->markForUpdate();
mUploadingObject = nullptr;
}
mGLTFUploadPending = false;
});
};
#if GLTF_SIM_SUPPORT
S32 expected_upload_cost = 1;
LLUUID asset_id = LLUUID::generateNewID();
mGLTFUploadPending = true;
LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
buffer,
asset_id,
"",
"",
0,
LLFolderType::FT_NONE,
LLInventoryType::IT_GLTF,
LLAssetType::AT_GLTF,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
false,
finish,
failure));
upload_new_resource(uploadInfo);
#else
// dummy finish
finish(LLUUID::generateNewID(), LLSD());
#endif
}
}
}
void GLTFSceneManager::render(bool opaque, bool rigged)
@ -219,7 +594,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
// for debugging, just render the whole scene as opaque
// by traversing the whole scenegraph
// Assumes camera transform is already set and
// appropriate shader is already bound
// appropriate shader is already boundd
gGL.matrixMode(LLRender::MM_MODELVIEW);
@ -243,7 +618,8 @@ void GLTFSceneManager::render(bool opaque, bool rigged)
matMul(mat, modelview, modelview);
asset->updateRenderTransforms(modelview);
mat4 mdv = glm::make_mat4(modelview.getF32ptr());
asset->updateRenderTransforms(mdv);
asset->render(opaque, rigged);
gGL.popMatrix();
@ -411,20 +787,24 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
// get raycast in asset space
LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform();
LLVector4a start;
LLVector4a end;
vec4 start;
vec4 end;
agent_to_asset.affineTransform(gDebugRaycastStart, start);
agent_to_asset.affineTransform(gDebugRaycastEnd, end);
LLVector4a t;
agent_to_asset.affineTransform(gDebugRaycastStart, t);
start = glm::make_vec4(t.getF32ptr());
agent_to_asset.affineTransform(gDebugRaycastEnd, t);
end = glm::make_vec4(t.getF32ptr());
start.w = end.w = 1.0;
for (auto& node : asset->mNodes)
{
Mesh& mesh = asset->mMeshes[node.mMesh];
if (node.mMesh != INVALID_INDEX)
{
gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix);
gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
// draw bounding box of mesh primitives
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
@ -442,24 +822,24 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset)
}
}
#if 0
#if 1
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
{
gGL.flush();
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// convert raycast to node local space
LLVector4a local_start;
LLVector4a local_end;
node.mAssetMatrixInv.affineTransform(start, local_start);
node.mAssetMatrixInv.affineTransform(end, local_end);
vec4 local_start = node.mAssetMatrixInv * start;
vec4 local_end = node.mAssetMatrixInv * end;
for (auto& primitive : mesh.mPrimitives)
{
if (primitive.mOctree.notNull())
{
renderOctreeRaycast(local_start, local_end, primitive.mOctree);
LLVector4a s, e;
s.load3(glm::value_ptr(local_start));
e.load3(glm::value_ptr(local_end));
renderOctreeRaycast(s, e, primitive.mOctree);
}
}
@ -499,18 +879,18 @@ void GLTFSceneManager::renderDebug()
continue;
}
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
LLMatrix4a modelview;
modelview.loadu(gGLModelView);
matMul(mat, modelview, modelview);
mat4 modelview = glm::make_mat4(gGLModelView);
modelview = modelview * mat;
Asset* asset = obj->mGLTFAsset.get();
for (auto& node : asset->mNodes)
{
matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
node.mRenderMatrix = modelview * node.mAssetMatrix;
}
}
@ -523,13 +903,6 @@ void GLTFSceneManager::renderDebug()
Asset* asset = obj->mGLTFAsset.get();
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
LLMatrix4a modelview;
modelview.loadu(gGLModelView);
matMul(mat, modelview, modelview);
renderAssetDebug(obj, asset);
}
@ -551,21 +924,20 @@ void GLTFSceneManager::renderDebug()
continue;
}
LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
LLMatrix4a modelview;
modelview.loadu(gGLModelView);
mat4 modelview = glm::make_mat4(gGLModelView);
matMul(mat, modelview, modelview);
modelview = modelview * mat;
Asset* asset = obj->mGLTFAsset.get();
for (auto& node : asset->mNodes)
{
// force update all mRenderMatrix, not just nodes with meshes
matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
node.mRenderMatrix = modelview * node.mAssetMatrix;
gGL.loadMatrix(node.mRenderMatrix.getF32ptr());
gGL.loadMatrix(glm::value_ptr(node.mRenderMatrix));
// render x-axis red, y-axis green, z-axis blue
gGL.color4f(1.f, 0.f, 0.f, 0.5f);
gGL.begin(LLRender::LINES);
@ -595,7 +967,9 @@ void GLTFSceneManager::renderDebug()
{
Node& child = asset->mNodes[child_idx];
gGL.vertex3f(0.f, 0.f, 0.f);
gGL.vertex3fv(child.mMatrix.getTranslation().getF32ptr());
gGL.vertex3fv(glm::value_ptr(child.mMatrix[3]));
}
gGL.end();
gGL.flush();
@ -628,9 +1002,8 @@ void GLTFSceneManager::renderDebug()
gGL.color3f(1, 0, 1);
drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f));
gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix);
gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix));
auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0);
drawBoxOutline(listener->mBounds[0], listener->mBounds[1]);
@ -643,3 +1016,5 @@ void GLTFSceneManager::renderDebug()
gDebugProgram.unbind();
}

View File

@ -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;
};
}

View File

@ -455,7 +455,6 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
llassert(shader);
// Like for PBR materials, PBR terrain texture transforms are defined by
// the KHR_texture_transform spec, but with the following notable
@ -470,7 +469,6 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
// i.e. this isn't fully compliant with KHR_texture_transform, but is
// compliant when all texture infos used by a material have the same
// texture transform.
LLGLTFMaterial::TextureTransform::PackTight transforms_packed[terrain_material_count];
for (U32 i = 0; i < terrain_material_count; ++i)
{

View File

@ -1328,11 +1328,11 @@ bool LLFace::getGeometryVolume(const LLVolume& volume,
{
color = tep->getGLTFRenderMaterial()->mBaseColor;
}
if (rebuild_color)
if (rebuild_color)
{ //decide if shiny goes in alpha channel of color
if (tep &&
!isInAlphaPool()) // <--- alpha channel MUST contain transparency, not shiny
!isInAlphaPool() && tep->getGLTFRenderMaterial() == nullptr) // <--- alpha channel MUST contain transparency, not shiny
{
LLMaterial* mat = tep->getMaterialParams().get();

View File

@ -77,16 +77,7 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
{
if (mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
{
// dividing the alpha cutoff by transparency here allows the shader to compare against
// the alpha value of the texture without needing the transparency value
if (mBaseColor.mV[3] > 0.f)
{
min_alpha = mAlphaCutoff / mBaseColor.mV[3];
}
else
{
min_alpha = 1024.f;
}
min_alpha = mAlphaCutoff;
}
shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha);
}
@ -151,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)

View File

@ -2379,23 +2379,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());
});
}
}

View File

@ -1551,6 +1551,13 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)
return mask;
}
if (item->getType() == LLAssetType::AT_MESH ||
item->getType() == LLAssetType::AT_GLTF ||
item->getType() == LLAssetType::AT_GLTF_BIN)
{
return mask;
}
LLPointer<LLViewerInventoryItem> old_item = getItem(item->getUUID());
LLPointer<LLViewerInventoryItem> new_item;
if(old_item)

View File

@ -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)

View File

@ -305,9 +305,17 @@ void LLResourceUploadInfo::assignDefaults()
mDescription = "(No Description)";
}
mFolderId = gInventory.findUserDefinedCategoryUUIDForType(
(mDestinationFolderType == LLFolderType::FT_NONE) ?
(LLFolderType::EType)mAssetType : mDestinationFolderType);
if (mAssetType == LLAssetType::AT_GLTF ||
mAssetType == LLAssetType::AT_GLTF_BIN)
{
mFolderId = LLUUID::null;
}
else
{
mFolderId = gInventory.findUserDefinedCategoryUUIDForType(
(mDestinationFolderType == LLFolderType::FT_NONE) ?
(LLFolderType::EType)mAssetType : mDestinationFolderType);
}
}
std::string LLResourceUploadInfo::getDisplayName() const

View File

@ -2733,7 +2733,13 @@ void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url)
{
if (initializeMedia(mimeType))
{
loadURI();
ref();
LLAppViewer::instance()->postToMainCoro([this]()
{
loadURI();
unref();
});
}
}

View File

@ -143,6 +143,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"
@ -4008,6 +4009,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)
@ -10045,7 +10053,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;
}
@ -10055,7 +10062,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;
}
@ -10065,12 +10071,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
{
@ -12527,6 +12560,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");
//[FIX FIRE-1927 - enable DoubleClickTeleport shortcut : SJ]
@ -12891,6 +12926,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));
enable.add("EnableBridgeFunction", boost::bind(&enable_bridge_function)); // <FS:CR>
view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible");

View File

@ -4466,7 +4466,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;
}
@ -4477,6 +4479,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);
@ -4524,7 +4527,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());
@ -4536,9 +4541,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();
}
@ -4552,7 +4561,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;
@ -4569,7 +4580,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();
@ -7690,6 +7706,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:

View File

@ -1,3 +1,4 @@
/**
* @file llviewerobject.h
* @brief Description of LLViewerObject class, which is the base class for most objects in the viewer.
@ -759,6 +760,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;

View File

@ -2639,24 +2639,14 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
gSavedSettings.setBOOL("RenderMirrors", mirrors_enabled);
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);
}
};

View File

@ -89,6 +89,7 @@
#include "llsculptidsize.h"
#include "llavatarappearancedefines.h"
#include "llgltfmateriallist.h"
#include "gltfscenemanager.h"
// [RLVa:KB] - Checked: RLVa-2.0.0
#include "rlvactions.h"
#include "rlvlocks.h"
@ -1290,6 +1291,11 @@ bool LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
}
}
if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF)
{ // notify GLTFSceneManager about new GLTF object
LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID());
}
return true;
}
else if (NO_LOD == lod)
@ -1564,6 +1570,12 @@ bool LLVOVolume::calcLOD()
return false;
}
if (mGLTFAsset != nullptr)
{
// do not calculate LOD for GLTF objects
return false;
}
S32 cur_detail = 0;
F32 radius;
@ -5921,7 +5933,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
LLVOVolume* vobj = drawablep->getVOVolume();
if (!vobj || vobj->isDead())
if (!vobj || vobj->isDead() || vobj->mGLTFAsset)
{
continue;
}

View File

@ -474,6 +474,13 @@
<menu_item_call label="Öffnen..." name="Open..."/>
<menu_item_call label="Speichern unter..." name="Save As..."/>
<menu_item_call label="Trennen..." name="Decompose..."/>
<menu_item_call label="Hochladen..." name="Upload..."/>
</menu>
<menu label="Auflösung" name="Video">
<menu_item_call label="1080x1920" name="1080x1920"/>
<menu_item_call label="1920x1080" name="1920x1080"/>
<menu_item_call label="1280x720" name="1280x720"/>
<menu_item_call label="720x1280" name="720x1280"/>
</menu>
<menu label="Render-Tests" name="Render Tests">
<menu_item_check label="Kamera-Versatz" name="Camera Offset"/>

View File

@ -5082,6 +5082,14 @@ Möchten Sie fortfahren?
Sie müssen ein Objekt auswählen, das mit einem GLTF-Gegenstand verknüpft ist.
<usetemplate name="okbutton" yestext="OK"/>
</notification>
<notification name="GLTFUploadSelection">
Sie müssen ein Objekt auswählen, das nur mit einem lokalen GLTF-Gegenstand verknüpft ist.
<usetemplate name="okbutton" yestext="OK"/>
</notification>
<notification name="GLTFUploadInProgress">
Upload wird gerade durchgeführt. Bitte versuchen Sie es später erneut.
<usetemplate name="okbutton" yestext="OK"/>
</notification>
<notification name="NoValidEnvSettingFound">
Keine gültige Einstellung für die Umgebung ausgewählt.

View File

@ -4120,21 +4120,69 @@
<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"

View File

@ -14367,6 +14367,28 @@ This will replace the items in the selected outfit with the items you are wearin
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>
<notification
icon="alertmodal.tga"