diff options
author | Dave Parks <davep@lindenlab.com> | 2024-06-21 13:13:08 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-21 13:13:08 -0500 |
commit | 80ea30af1a8b38360f71c29cc45872c4399dab0d (patch) | |
tree | db07a706a4c896b11b8abca8a7d8815797a8d0be /indra | |
parent | 9fb9e8f33cb33a1535f43b4be030009c192ea92b (diff) |
#1769 gltf optimization pass (#1816)
#1814 and #1517 Fix mirror update rate and occlusion culling
Diffstat (limited to 'indra')
29 files changed, 985 insertions, 485 deletions
diff --git a/indra/llcommon/llprofilercategories.h b/indra/llcommon/llprofilercategories.h index 0de343d020..1c4f0f5624 100644 --- a/indra/llcommon/llprofilercategories.h +++ b/indra/llcommon/llprofilercategories.h @@ -67,6 +67,7 @@ #define LL_PROFILER_CATEGORY_ENABLE_VERTEX 1 #define LL_PROFILER_CATEGORY_ENABLE_VOLUME 1 #define LL_PROFILER_CATEGORY_ENABLE_WIN32 1 +#define LL_PROFILER_CATEGORY_ENABLE_GLTF 1 #define LL_PROFILER_CATEGORY_ENABLE_VOICE 1 #if LL_PROFILER_CATEGORY_ENABLE_APP @@ -277,12 +278,19 @@ #define LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32 #endif +#if LL_PROFILER_CATEGORY_ENABLE_GLTF + #define LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF LL_PROFILE_ZONE_NAMED + #define LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF LL_PROFILE_ZONE_SCOPED +#else + #define LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF(name) + #define LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF +#endif #if LL_PROFILER_CATEGORY_ENABLE_VOICE -#define LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE LL_PROFILE_ZONE_NAMED -#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE LL_PROFILE_ZONE_SCOPED + #define LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE LL_PROFILE_ZONE_NAMED + #define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE LL_PROFILE_ZONE_SCOPED #else -#define LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE(name) -#define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE + #define LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE(name) + #define LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE #endif #endif // LL_PROFILER_CATEGORIES_H diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index e22df46b28..25e4a88f28 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -54,6 +54,8 @@ using std::string; GLuint LLGLSLShader::sCurBoundShader = 0; LLGLSLShader* LLGLSLShader::sCurBoundShaderPtr = NULL; S32 LLGLSLShader::sIndexedTextureChannels = 0; +U32 LLGLSLShader::sMaxGLTFMaterials = 0; +U32 LLGLSLShader::sMaxGLTFNodes = 0; bool LLGLSLShader::sProfileEnabled = false; std::set<LLGLSLShader*> LLGLSLShader::sInstances; LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines; @@ -978,7 +980,9 @@ bool LLGLSLShader::mapUniforms() const char* ubo_names[] = { "ReflectionProbes", // UB_REFLECTION_PROBES - "GLTFJoints", // UB_GLTF_JOINTS + "GLTFJoints", // UB_GLTF_JOINTS + "GLTFNodes", // UB_GLTF_NODES + "GLTFMaterials", // UB_GLTF_MATERIALS }; llassert(LL_ARRAY_SIZE(ubo_names) == NUM_UNIFORM_BLOCKS); diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index cc2ba0fcff..86e5625dca 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -147,8 +147,10 @@ public: enum UniformBlock : GLuint { - UB_REFLECTION_PROBES, - UB_GLTF_JOINTS, + UB_REFLECTION_PROBES, // "ReflectionProbes" + UB_GLTF_JOINTS, // "GLTFJoints" + UB_GLTF_NODES, // "GLTFNodes" + UB_GLTF_MATERIALS, // "GLTFMaterials" NUM_UNIFORM_BLOCKS }; @@ -163,6 +165,9 @@ public: static LLGLSLShader* sCurBoundShaderPtr; static S32 sIndexedTextureChannels; + static U32 sMaxGLTFMaterials; + static U32 sMaxGLTFNodes; + static void initProfile(); static void finishProfile(bool emit_report = true); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index cfefde3acc..a0209fab43 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -991,6 +991,8 @@ void LLRender::syncLightState() void LLRender::syncMatrices() { STOP_GLERROR; + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; + static const U32 name[] = { LLShaderMgr::MODELVIEW_MATRIX, diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 574cf55a0e..a8e9f20b40 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1185,6 +1185,8 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("normal_texcoord"); // (GLTF) mReservedUniforms.push_back("metallic_roughness_texcoord"); // (GLTF) mReservedUniforms.push_back("occlusion_texcoord"); // (GLTF) + mReservedUniforms.push_back("gltf_node_id"); // (GLTF) + mReservedUniforms.push_back("gltf_material_id"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index c00aff3a9a..fe6137c448 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -63,6 +63,8 @@ public: NORMAL_TEXCOORD, // "normal_texcoord" (GLTF) METALLIC_ROUGHNESS_TEXCOORD, // "metallic_roughness_texcoord" (GLTF) OCCLUSION_TEXCOORD, // "occlusion_texcoord" (GLTF) + GLTF_NODE_ID, // "gltf_node_id" (GLTF) + GLTF_MATERIAL_ID, // "gltf_material_id" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index f82ec30242..2eb7c21f77 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -806,6 +806,13 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi STOP_GLERROR; } +void LLVertexBuffer::drawRangeFast(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const +{ + glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType, + (GLvoid*)(indices_offset * (size_t)mIndicesStride)); +} + + void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const { drawRange(mode, 0, mNumVerts-1, count, indices_offset); @@ -1062,12 +1069,6 @@ bool LLVertexBuffer::updateNumVerts(U32 nverts) bool success = true; - if (nverts > 65536) - { - LL_WARNS() << "Vertex buffer overflow!" << LL_ENDL; - nverts = 65536; - } - U32 needed_size = calcOffsets(mTypeMask, mOffsets, nverts); if (needed_size != mSize) @@ -1210,7 +1211,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count) // end -- last byte to copy (NOT last byte + 1) // data -- data to be flushed // dst -- mMappedData or mMappedIndexData -static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) +void LLVertexBuffer::flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) { #if LL_DARWIN LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb memcpy"); @@ -1218,6 +1219,8 @@ static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst) // copy into mapped buffer memcpy(dst+start, data, end-start+1); #else + llassert(target == GL_ARRAY_BUFFER ? sGLRenderBuffer == mGLBuffer : sGLRenderIndices == mGLIndices); + // skip mapped data and stream to GPU via glBufferSubData if (end != 0) { @@ -1627,81 +1630,51 @@ void LLVertexBuffer::setupVertexBuffer() void LLVertexBuffer::setPositionData(const LLVector4a* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData); } void LLVertexBuffer::setTexCoord0Data(const LLVector2* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data, mMappedData); } void LLVertexBuffer::setTexCoord1Data(const LLVector2* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD1], mOffsets[TYPE_TEXCOORD1] + sTypeSize[TYPE_TEXCOORD1] * getNumVerts() - 1, (U8*)data, mMappedData); } void LLVertexBuffer::setColorData(const LLColor4U* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setNormalData(const LLVector4a* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setTangentData(const LLVector4a* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setWeight4Data(const LLVector4a* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setJointData(const U64* data) { -#if !LL_DARWIN - llassert(sGLRenderBuffer == mGLBuffer); -#endif flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_JOINT], mOffsets[TYPE_JOINT] + sTypeSize[TYPE_JOINT] * getNumVerts() - 1, (U8*) data, mMappedData); } void LLVertexBuffer::setIndexData(const U16* data) { -#if !LL_DARWIN - llassert(sGLRenderIndices == mGLIndices); -#endif flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data, mMappedIndexData); } void LLVertexBuffer::setIndexData(const U32* data) { -#if !LL_DARWIN - llassert(sGLRenderIndices == mGLIndices); -#endif if (mIndicesType != GL_UNSIGNED_INT) { // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices mIndicesType = GL_UNSIGNED_INT; @@ -1711,3 +1684,62 @@ void LLVertexBuffer::setIndexData(const U32* data) flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data, mMappedIndexData); } +void LLVertexBuffer::setPositionData(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, offset * sizeof(LLVector4a), (offset + count) * sizeof(LLVector4a) - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setNormalData(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL] + offset * sTypeSize[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + (offset + count) * sTypeSize[TYPE_NORMAL] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setTexCoord0Data(const LLVector2* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0] + offset * sTypeSize[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + (offset + count) * sTypeSize[TYPE_TEXCOORD0] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setTexCoord1Data(const LLVector2* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD1] + offset * sTypeSize[TYPE_TEXCOORD1], mOffsets[TYPE_TEXCOORD1] + (offset + count) * sTypeSize[TYPE_TEXCOORD1] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setColorData(const LLColor4U* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR] + offset * sTypeSize[TYPE_COLOR], mOffsets[TYPE_COLOR] + (offset + count) * sTypeSize[TYPE_COLOR] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setTangentData(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT] + offset * sTypeSize[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + (offset + count) * sTypeSize[TYPE_TANGENT] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setWeight4Data(const LLVector4a* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4] + offset * sTypeSize[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + (offset + count) * sTypeSize[TYPE_WEIGHT4] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setJointData(const U64* data, U32 offset, U32 count) +{ + flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_JOINT] + offset * sTypeSize[TYPE_JOINT], mOffsets[TYPE_JOINT] + (offset + count) * sTypeSize[TYPE_JOINT] - 1, (U8*)data, mMappedData); +} + +void LLVertexBuffer::setIndexData(const U16* data, U32 offset, U32 count) +{ + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, offset * sizeof(U16), (offset + count) * sizeof(U16) - 1, (U8*)data, mMappedIndexData); +} + +void LLVertexBuffer::setIndexData(const U32* data, U32 offset, U32 count) +{ + if (mIndicesType != GL_UNSIGNED_INT) + { // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices + mIndicesType = GL_UNSIGNED_INT; + mIndicesStride = 4; + mNumIndices /= 2; + } + flush_vbo(GL_ELEMENT_ARRAY_BUFFER, offset * sizeof(U32), (offset + count) * sizeof(U32) - 1, (U8*)data, mMappedIndexData); +} + + + + diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 601096abf9..49500e28ce 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -202,6 +202,18 @@ public: void setIndexData(const U16* data); void setIndexData(const U32* data); + void setPositionData(const LLVector4a* data, U32 offset, U32 count); + void setNormalData(const LLVector4a* data, U32 offset, U32 count); + void setTangentData(const LLVector4a* data, U32 offset, U32 count); + void setWeight4Data(const LLVector4a* data, U32 offset, U32 count); + void setJointData(const U64* data, U32 offset, U32 count); + void setTexCoord0Data(const LLVector2* data, U32 offset, U32 count); + void setTexCoord1Data(const LLVector2* data, U32 offset, U32 count); + void setColorData(const LLColor4U* data, U32 offset, U32 count); + void setIndexData(const U16* data, U32 offset, U32 count); + void setIndexData(const U32* data, U32 offset, U32 count); + + U32 getNumVerts() const { return mNumVerts; } U32 getNumIndices() const { return mNumIndices; } @@ -219,6 +231,10 @@ public: void drawArrays(U32 mode, U32 offset, U32 count) const; void drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const; + // draw without syncing matrices. If you're positive there have been no matrix + // since the last call to syncMatrices, this is much faster than drawRange + void drawRangeFast(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const; + //for debugging, validate data in given range is valid bool validateRange(U32 start, U32 end, U32 count, U32 offset) const; @@ -256,6 +272,8 @@ private: friend class LLNavShapeVBOManager; friend class LLNavMeshVBOManager; + void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst); + LLVertexBuffer(U32 typemask, U32 usage) : LLVertexBuffer(typemask) {} diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl index 789c00259b..ac4ff50552 100644 --- a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl +++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl @@ -28,18 +28,40 @@ // GLTF pbrMetallicRoughness implementation +uniform int gltf_material_id; + +vec3 emissiveColor = vec3(0,0,0); +float metallicFactor = 1.0; +float roughnessFactor = 1.0; +float minimum_alpha = -1.0; + +layout (std140) uniform GLTFMaterials +{ + // see pbrmetallicroughnessV.glsl for packing + vec4 gltf_material_data[MAX_UBO_VEC4S]; +}; + +void unpackMaterial() +{ + if (gltf_material_id > -1) + { + int idx = gltf_material_id*12; + emissiveColor = gltf_material_data[idx+10].rgb; + roughnessFactor = gltf_material_data[idx+11].g; + metallicFactor = gltf_material_data[idx+11].b; + minimum_alpha -= gltf_material_data[idx+11].a; + } +} // ================================== // needed by all variants // ================================== uniform sampler2D diffuseMap; //always in sRGB space uniform sampler2D emissiveMap; -uniform vec3 emissiveColor; in vec3 vary_position; in vec4 vertex_color; in vec2 base_color_uv; in vec2 emissive_uv; -uniform float minimum_alpha; void mirrorClip(vec3 pos); vec3 linear_to_srgb(vec3 c); @@ -54,8 +76,6 @@ vec3 srgb_to_linear(vec3 c); uniform sampler2D normalMap; uniform sampler2D metallicRoughnessMap; uniform sampler2D occlusionMap; -uniform float metallicFactor; -uniform float roughnessFactor; in vec3 vary_normal; in vec3 vary_tangent; flat in float vary_sign; @@ -154,7 +174,7 @@ out vec4 frag_data[4]; void main() { - + unpackMaterial(); // ================================== // all variants // mirror clip @@ -165,6 +185,10 @@ void main() vec3 pos = vary_position; mirrorClip(pos); +#ifdef ALPHA_BLEND + //waterClip(pos); +#endif + vec4 basecolor = texture(diffuseMap, base_color_uv.xy).rgba; basecolor.rgb = srgb_to_linear(basecolor.rgb); basecolor *= vertex_color; diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl index aac3dc917f..6a628bc852 100644 --- a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl +++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl @@ -26,19 +26,95 @@ // GLTF pbrMetallicRoughness implementation uniform mat4 modelview_matrix; - -#ifdef HAS_SKIN uniform mat4 projection_matrix; -#else -uniform mat3 normal_matrix; -uniform mat4 modelview_projection_matrix; + +#ifdef MULTI_UV +in vec2 texcoord1; +int base_color_texcoord = 0; +int emissive_texcoord = 0; +#ifndef UNLIT +int normal_texcoord = 0; +int metallic_roughness_texcoord = 0; +int occlusion_texcoord = 0; +#endif +#endif + +uniform int gltf_material_id; + +layout (std140) uniform GLTFMaterials +{ + // index by gltf_material_id*12 + + // [gltf_material_id + [0-1]] - base color transform + // [gltf_material_id + [2-3]] - normal transform + // [gltf_material_id + [4-5]] - metallic roughness transform + // [gltf_material_id + [6-7]] - emissive transform + // [gltf_material_id + [8-9]] - occlusion transform + // [gltf_material_id + 10] - emissive factor + // [gltf_material_id + 11] - .r unused, .g roughness, .b metalness, .a minimum alpha + + // Transforms are packed as follows + // packed[0] = vec4(scale.x, scale.y, rotation, offset.x) + // packed[1] = vec4(mScale.y, texcoord, 0, 0) + vec4 gltf_material_data[MAX_UBO_VEC4S]; +}; + +vec4[2] texture_base_color_transform; +vec4[2] texture_normal_transform; +vec4[2] texture_metallic_roughness_transform; +vec4[2] texture_emissive_transform; +vec4[2] texture_occlusion_transform; + +void unpackTextureTransforms() +{ + if (gltf_material_id != -1) + { + int idx = gltf_material_id*12; + + texture_base_color_transform[0] = gltf_material_data[idx+0]; + texture_base_color_transform[1] = gltf_material_data[idx+1]; + + texture_normal_transform[0] = gltf_material_data[idx+2]; + texture_normal_transform[1] = gltf_material_data[idx+3]; + + texture_metallic_roughness_transform[0] = gltf_material_data[idx+4]; + texture_metallic_roughness_transform[1] = gltf_material_data[idx+5]; + + texture_emissive_transform[0] = gltf_material_data[idx+6]; + texture_emissive_transform[1] = gltf_material_data[idx+7]; + + texture_occlusion_transform[0] = gltf_material_data[idx+8]; + texture_occlusion_transform[1] = gltf_material_data[idx+9]; + +#ifdef MULTI_UV + base_color_texcoord = int(gltf_material_data[idx+1].g); + emissive_texcoord = int(gltf_material_data[idx+7].g); +#ifndef UNLIT + normal_texcoord = int(gltf_material_data[idx+3].g); + metallic_roughness_texcoord = int(gltf_material_data[idx+5].g); + occlusion_texcoord = int(gltf_material_data[idx+9].g); #endif +#endif + } + else + { + texture_base_color_transform[0] = vec4(1.0, 1.0, 0.0, 0.0); + texture_base_color_transform[1] = vec4(0.0, 0.0, 0.0, 0.0); + + texture_normal_transform[0] = vec4(1.0, 1.0, 0.0, 0.0); + texture_normal_transform[1] = vec4(0.0, 0.0, 0.0, 0.0); + + texture_metallic_roughness_transform[0] = vec4(1.0, 1.0, 0.0, 0.0); + texture_metallic_roughness_transform[1] = vec4(0.0, 0.0, 0.0, 0.0); + + texture_emissive_transform[0] = vec4(1.0, 1.0, 0.0, 0.0); + texture_emissive_transform[1] = vec4(0.0, 0.0, 0.0, 0.0); + + texture_occlusion_transform[0] = vec4(1.0, 1.0, 0.0, 0.0); + texture_occlusion_transform[1] = vec4(0.0, 0.0, 0.0, 0.0); + } +} -uniform vec4[2] texture_base_color_transform; -uniform vec4[2] texture_normal_transform; -uniform vec4[2] texture_metallic_roughness_transform; -uniform vec4[2] texture_emissive_transform; -uniform vec4[2] texture_occlusion_transform; in vec3 position; in vec4 diffuse_color; @@ -59,17 +135,6 @@ flat out float vary_sign; out vec3 vary_normal; #endif -#ifdef MULTI_UV -in vec2 texcoord1; -uniform int base_color_texcoord; -uniform int emissive_texcoord; -#ifndef UNLIT -uniform int normal_texcoord; -uniform int metallic_roughness_texcoord; -uniform int occlusion_texcoord; -#endif -#endif - vec2 gltf_texture_transform(vec2 texcoord, vec4[2] p) { texcoord.y = 1.0 - texcoord.y; @@ -124,23 +189,22 @@ vec3 gltf_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[ } #endif - - #ifdef ALPHA_BLEND out vec3 vary_fragcoord; #endif #ifdef HAS_SKIN -in uvec4 joint; -in vec4 weight4; layout (std140) uniform GLTFJoints { - // list of OBBs for user override probes - mat3x4 gltf_joints[MAX_JOINTS_PER_GLTF_OBJECT]; + mat3x4 gltf_joints[MAX_NODES_PER_GLTF_OBJECT]; }; -mat4 getGLTFSkinTransform() + +in uvec4 joint; +in vec4 weight4; + +mat4 getGLTFTransform() { int i; @@ -169,21 +233,37 @@ mat4 getGLTFSkinTransform() ret[3] = vec4(trans, 1.0); return ret; +} -#ifdef IS_AMD_CARD - // If it's AMD make sure the GLSL compiler sees the arrays referenced once by static index. Otherwise it seems to optimise the storage awawy which leads to unfun crashes and artifacts. - mat3x4 dummy1 = gltf_joints[0]; - mat3x4 dummy2 = gltf_joints[MAX_JOINTS_PER_GLTF_OBJECT-1]; -#endif +#else + +layout (std140) uniform GLTFNodes +{ + mat3x4 gltf_nodes[MAX_NODES_PER_GLTF_OBJECT]; +}; + +uniform int gltf_node_id = 0; + +mat4 getGLTFTransform() +{ + mat4 ret; + mat3x4 src = gltf_nodes[gltf_node_id]; + + ret[0] = vec4(src[0].xyz, 0); + ret[1] = vec4(src[1].xyz, 0); + ret[2] = vec4(src[2].xyz, 0); + + ret[3] = vec4(src[0].w, src[1].w, src[2].w, 1); + return ret; } #endif void main() { -#ifdef HAS_SKIN - mat4 mat = getGLTFSkinTransform(); + unpackTextureTransforms(); + mat4 mat = getGLTFTransform(); mat = modelview_matrix * mat; @@ -193,13 +273,6 @@ void main() vec4 vert = projection_matrix * vec4(pos, 1.0); gl_Position = vert; -#else - vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz; - //transform vertex - vec4 vert = modelview_projection_matrix * vec4(position.xyz, 1.0); - gl_Position = vert; -#endif - vec2 bcuv; vec2 emuv; @@ -237,13 +310,8 @@ void main() #endif #ifndef UNLIT -#ifdef HAS_SKIN vec3 n = (mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz; vec3 t = (mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz; -#else //HAS_SKIN - vec3 n = normal_matrix * normal; - vec3 t = normal_matrix * tangent.xyz; -#endif n = normalize(n); vary_tangent = normalize(gltf_tangent_space_transform(vec4(t, tangent.w), n, texture_normal_transform)); diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 8950770172..03849a0326 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -111,7 +111,7 @@ RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 4 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 // diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 8c71235f37..7aa8504eaa 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -109,7 +109,7 @@ RenderReflectionProbeLevel 1 0 RenderMirrors 1 0 RenderHeroProbeResolution 1 256 RenderHeroProbeDistance 1 4 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 // diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp index 8b85eba3e5..3dff67d746 100644 --- a/indra/newview/gltf/animation.cpp +++ b/indra/newview/gltf/animation.cpp @@ -70,6 +70,14 @@ bool Animation::prep(Asset& asset) } } + for (auto& channel : mScaleChannels) + { + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } + } + return true; } @@ -82,18 +90,37 @@ void Animation::update(Asset& asset, F32 dt) void Animation::apply(Asset& asset, float time) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // convert time to animation loop time time = fmod(time, mMaxTime - mMinTime) + mMinTime; // apply each channel - for (auto& channel : mRotationChannels) { - channel.apply(asset, mSamplers[channel.mSampler], time); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - rotation"); + + for (auto& channel : mRotationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - translation"); + + for (auto& channel : mTranslationChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } } - for (auto& channel : mTranslationChannels) { - channel.apply(asset, mSamplers[channel.mSampler], time); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfanim - scale"); + + for (auto& channel : mScaleChannels) + { + channel.apply(asset, mSamplers[channel.mSampler], time); + } } }; @@ -178,7 +205,8 @@ const Animation::Channel& Animation::Channel::operator=(const Value& src) void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t) { - LL_PROFILE_ZONE_SCOPED; + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + llassert(mFrameTimes.size() > 1); // if there is only one frame, there is no need to interpolate if (time < mMinTime) { @@ -187,32 +215,33 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F return; } - if (mFrameTimes.size() > 1) + frameIndex = U32(mFrameTimes.size()) - 2; + t = 1.f; + + if (time > mMaxTime) { - llassert(mFrameTimes.size() <= size_t(U32_MAX)); - frameIndex = U32(mFrameTimes.size()) - 2; - t = 1.f; + return; + } - if (time > mMaxTime) - { - return; - } + if (time < mLastFrameTime) + { + mLastFrameIndex = 0; + } - for (U32 i = 0; i < (U32)mFrameTimes.size() - 1; i++) + mLastFrameTime = time; + + U32 idx = mLastFrameIndex; + + for (U32 i = idx; i < (U32)mFrameTimes.size() - 1; i++) + { + if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) { - if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) - { - frameIndex = i; - t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); - return; - } + frameIndex = i; + t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]); + mLastFrameIndex = frameIndex; + return; } } - else - { - frameIndex = 0; - t = 0.0f; - } } bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler) @@ -231,14 +260,14 @@ void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setRotation(mRotations[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t); @@ -264,14 +293,14 @@ void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 ti Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setTranslation(mTranslations[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate const vec3& v0 = mTranslations[frameIndex]; const vec3& v1 = mTranslations[frameIndex + 1]; @@ -298,14 +327,14 @@ void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) Node& node = asset.mNodes[mTarget.mNode]; - sampler.getFrameInfo(asset, time, frameIndex, t); - - if (sampler.mFrameTimes.size() == 1) + if (sampler.mFrameTimes.size() < 2) { node.setScale(mScales[0]); } else { + sampler.getFrameInfo(asset, time, frameIndex, t); + // interpolate const vec3& v0 = mScales[frameIndex]; const vec3& v1 = mScales[frameIndex + 1]; @@ -373,6 +402,7 @@ Skin::~Skin() void Skin::uploadMatrixPalette(Asset& asset) { // prepare matrix palette + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount(); diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h index d5426fd4ce..ab8839470a 100644 --- a/indra/newview/gltf/animation.h +++ b/indra/newview/gltf/animation.h @@ -49,6 +49,9 @@ namespace LL S32 mOutput = INVALID_INDEX; std::string mInterpolation; + F32 mLastFrameTime = 0.f; + U32 mLastFrameIndex = 0; + bool prep(Asset& asset); void serialize(boost::json::object& dst) const; diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index 21be69aae2..a454e68a92 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -35,6 +35,7 @@ #include "buffer_util.h" #include <boost/url.hpp> #include "llimagejpeg.h" +#include "../llskinningutil.h" using namespace LL::GLTF; using namespace boost::json; @@ -86,7 +87,6 @@ namespace LL } } - void Scene::updateTransforms(Asset& asset) { mat4 identity = glm::identity<mat4>(); @@ -98,26 +98,6 @@ void Scene::updateTransforms(Asset& asset) } } -void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview) -{ - for (auto& nodeIndex : mNodes) - { - Node& node = asset.mNodes[nodeIndex]; - node.updateRenderTransforms(asset, modelview); - } -} - -void Node::updateRenderTransforms(Asset& asset, const mat4& modelview) -{ - mRenderMatrix = modelview * mMatrix; - - for (auto& childIndex : mChildren) - { - Node& child = asset.mNodes[childIndex]; - child.updateRenderTransforms(asset, mRenderMatrix); - } -} - void Node::updateTransforms(Asset& asset, const mat4& parentMatrix) { makeMatrixValid(); @@ -137,19 +117,119 @@ void Node::updateTransforms(Asset& asset, const mat4& parentMatrix) void Asset::updateTransforms() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; for (auto& scene : mScenes) { scene.updateTransforms(*this); } + + uploadTransforms(); } -void Asset::updateRenderTransforms(const mat4& modelview) +void Asset::uploadTransforms() { - // use mAssetMatrix to update render transforms from node list - for (auto& node : mNodes) + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // prepare matrix palette + U32 max_nodes = LLSkinningUtil::getMaxGLTFJointCount(); + + size_t node_count = llmin<size_t>(max_nodes, mNodes.size()); + + std::vector<mat4> t_mp; + + t_mp.resize(node_count); + + for (U32 i = 0; i < node_count; ++i) { - node.mRenderMatrix = modelview * node.mAssetMatrix; + Node& node = mNodes[i]; + // build matrix palette in asset space + t_mp[i] = node.mAssetMatrix; } + + std::vector<F32> glmp; + + glmp.resize(node_count * 12); + + F32* mp = glmp.data(); + + for (U32 i = 0; i < node_count; ++i) + { + F32* m = glm::value_ptr(t_mp[i]); + + U32 idx = i * 12; + + mp[idx + 0] = m[0]; + mp[idx + 1] = m[1]; + mp[idx + 2] = m[2]; + mp[idx + 3] = m[12]; + + mp[idx + 4] = m[4]; + mp[idx + 5] = m[5]; + mp[idx + 6] = m[6]; + mp[idx + 7] = m[13]; + + mp[idx + 8] = m[8]; + mp[idx + 9] = m[9]; + mp[idx + 10] = m[10]; + mp[idx + 11] = m[14]; + } + + if (mNodesUBO == 0) + { + glGenBuffers(1, &mNodesUBO); + } + + glBindBuffer(GL_UNIFORM_BUFFER, mNodesUBO); + glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +void Asset::uploadMaterials() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + // see pbrmetallicroughnessV.glsl for the layout of the material UBO + std::vector<vec4> md; + + U32 material_size = sizeof(vec4) * 12; + U32 max_materials = gGLManager.mMaxUniformBlockSize / material_size; + + U32 mat_count = (U32)mMaterials.size(); + mat_count = llmin(mat_count, max_materials); + + md.resize(mat_count * 12); + + for (U32 i = 0; i < mat_count*12; i += 12) + { + Material& material = mMaterials[i/12]; + + // add texture transforms and UV indices + material.mPbrMetallicRoughness.mBaseColorTexture.mTextureTransform.getPacked(&md[i+0]); + md[i + 1].g = (F32)material.mPbrMetallicRoughness.mBaseColorTexture.getTexCoord(); + material.mNormalTexture.mTextureTransform.getPacked(&md[i + 2]); + md[i + 3].g = (F32)material.mNormalTexture.getTexCoord(); + material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTextureTransform.getPacked(&md[i+4]); + md[i + 5].g = (F32)material.mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord(); + material.mEmissiveTexture.mTextureTransform.getPacked(&md[i + 6]); + md[i + 7].g = (F32)material.mEmissiveTexture.getTexCoord(); + material.mOcclusionTexture.mTextureTransform.getPacked(&md[i + 8]); + md[i + 9].g = (F32)material.mOcclusionTexture.getTexCoord(); + + // add material properties + F32 min_alpha = material.mAlphaMode == Material::AlphaMode::MASK ? material.mAlphaCutoff : -1.0f; + md[i + 10] = vec4(material.mEmissiveFactor, 1.f); + md[i + 11] = vec4(0.f, + material.mPbrMetallicRoughness.mRoughnessFactor, + material.mPbrMetallicRoughness.mMetallicFactor, + min_alpha); + } + + if (mMaterialsUBO == 0) + { + glGenBuffers(1, &mMaterialsUBO); + } + + glBindBuffer(GL_UNIFORM_BUFFER, mMaterialsUBO); + glBufferData(GL_UNIFORM_BUFFER, md.size() * sizeof(vec4), md.data(), GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); } S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, @@ -363,6 +443,7 @@ const Image& Image::operator=(const Value& src) void Asset::update() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; F32 dt = gFrameTimeSeconds - mLastUpdateTime; if (dt > 0.f) @@ -383,11 +464,27 @@ void Asset::update() { skin.uploadMatrixPalette(*this); } + + uploadMaterials(); + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltf - addTextureStats"); + + for (auto& image : mImages) + { + if (image.mTexture.notNull()) + { // HACK - force texture to be loaded full rez + // TODO: calculate actual vsize + image.mTexture->addTextureStats(2048.f * 2048.f); + } + } + } } } bool Asset::prep() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; // check required extensions and fail if not supported bool unsupported = false; for (auto& extension : mExtensionsRequired) @@ -445,6 +542,127 @@ bool Asset::prep() } } + // prepare vertex buffers + + // material count is number of materials + 1 for default material + U32 mat_count = (U32) mMaterials.size() + 1; + + if (LLGLSLShader::sCurBoundShaderPtr == nullptr) + { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer + gDebugProgram.bind(); + } + + for (S32 double_sided = 0; double_sided < 2; ++double_sided) + { + RenderData& rd = mRenderData[double_sided]; + for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i) + { + rd.mBatches[i].resize(mat_count); + } + + // for each material + for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id) + { + // for each shader variant + U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; + U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 }; + + S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided; + if (ds_mat != double_sided) + { + continue; + } + + for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant) + { + U32 attribute_mask = 0; + // for each mesh + for (auto& mesh : mMeshes) + { + // for each primitive + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) + { + // accumulate vertex and index counts + primitive.mVertexOffset = vertex_count[variant]; + primitive.mIndexOffset = index_count[variant]; + + vertex_count[variant] += primitive.getVertexCount(); + index_count[variant] += primitive.getIndexCount(); + + // all primitives of a given variant and material should all have the same attribute mask + llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask); + attribute_mask |= primitive.mAttributeMask; + } + } + } + + // allocate vertex buffer and pack it + if (vertex_count[variant] > 0) + { + U32 mat_idx = mat_id + 1; + LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask); + + rd.mBatches[variant][mat_idx].mVertexBuffer = vb; + vb->allocateBuffer(vertex_count[variant], + index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used + vb->setBuffer(); + + for (auto& mesh : mMeshes) + { + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant) + { + primitive.upload(vb); + } + } + } + + vb->unmapBuffer(); + + vb->unbind(); + } + } + } + } + + // sanity check that all primitives have a vertex buffer + for (auto& mesh : mMeshes) + { + for (auto& primitive : mesh.mPrimitives) + { + llassert(primitive.mVertexBuffer.notNull()); + } + } + + // build render batches + for (S32 node_id = 0; node_id < mNodes.size(); ++node_id) + { + Node& node = mNodes[node_id]; + + if (node.mMesh != INVALID_INDEX) + { + auto& mesh = mMeshes[node.mMesh]; + + S32 mat_idx = mesh.mPrimitives[0].mMaterial + 1; + + S32 double_sided = mat_idx == 0 ? 0 : mMaterials[mat_idx - 1].mDoubleSided; + + for (S32 j = 0; j < mesh.mPrimitives.size(); ++j) + { + auto& primitive = mesh.mPrimitives[j]; + + S32 variant = primitive.mShaderVariant; + + RenderData& rd = mRenderData[double_sided]; + RenderBatch& rb = rd.mBatches[variant][mat_idx]; + + rb.mPrimitives.push_back({ j, node_id }); + } + } + } return true; } @@ -455,6 +673,7 @@ Asset::Asset(const Value& src) bool Asset::load(std::string_view filename) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; mFilename = filename; std::string ext = gDirUtilp->getExtension(mFilename); @@ -903,14 +1122,14 @@ bool Image::save(Asset& asset, const std::string& folder) return true; } -void Material::TextureInfo::serialize(object& dst) const +void TextureInfo::serialize(object& dst) const { write(mIndex, "index", dst, INVALID_INDEX); write(mTexCoord, "texCoord", dst, 0); write_extensions(dst, &mTextureTransform, "KHR_texture_transform"); } -S32 Material::TextureInfo::getTexCoord() const +S32 TextureInfo::getTexCoord() const { if (mTextureTransform.mPresent && mTextureTransform.mTexCoord != INVALID_INDEX) { @@ -928,7 +1147,7 @@ bool Material::isMultiUV() const mEmissiveTexture.getTexCoord() != 0; } -const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) +const TextureInfo& TextureInfo::operator=(const Value& src) { if (src.is_object()) { @@ -940,23 +1159,23 @@ const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) return *this; } -bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const +bool TextureInfo::operator==(const TextureInfo& rhs) const { return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord; } -bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const +bool TextureInfo::operator!=(const TextureInfo& rhs) const { return !(*this == rhs); } -void Material::OcclusionTextureInfo::serialize(object& dst) const +void OcclusionTextureInfo::serialize(object& dst) const { TextureInfo::serialize(dst); write(mStrength, "strength", dst, 1.f); } -const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src) +const OcclusionTextureInfo& OcclusionTextureInfo::operator=(const Value& src) { TextureInfo::operator=(src); @@ -968,13 +1187,13 @@ const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=( return *this; } -void Material::NormalTextureInfo::serialize(object& dst) const +void NormalTextureInfo::serialize(object& dst) const { TextureInfo::serialize(dst); write(mScale, "scale", dst, 1.f); } -const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src) +const NormalTextureInfo& NormalTextureInfo::operator=(const Value& src) { TextureInfo::operator=(src); if (src.is_object()) @@ -1035,18 +1254,12 @@ void Material::Unlit::serialize(object& dst) const // no members and object has already been created, nothing to do } -void TextureTransform::getPacked(F32* packed) const +void TextureTransform::getPacked(vec4* packed) const { - packed[0] = mScale.x; - packed[1] = mScale.y; - packed[2] = mRotation; - packed[3] = mOffset.x; - packed[4] = mOffset.y; - - packed[5] = packed[6] = packed[7] = 0.f; + packed[0] = vec4(mScale.x, mScale.y, mRotation, mOffset.x); + packed[1] = vec4(mOffset.y, 0.f, 0.f, 0.f); } - const TextureTransform& TextureTransform::operator=(const Value& src) { mPresent = true; diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index ea3f7d480a..27821659db 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -34,6 +34,7 @@ #include "boost/json.hpp" #include "common.h" #include "../llviewertexture.h" +#include "llglslshader.h" extern F32SecondsImplicit gFrameTimeSeconds; @@ -65,14 +66,51 @@ namespace LL vec2 mScale = vec2(1.f, 1.f); S32 mTexCoord = INVALID_INDEX; - // get the texture transform as a packed array of floats - // dst MUST point to at least 8 floats - void getPacked(F32* dst) const; + // get the texture transform as a packed array of vec4's + // dst MUST point to at least 2 vec4's + void getPacked(vec4* dst) const; const TextureTransform& operator=(const Value& src); void serialize(boost::json::object& dst) const; }; + class TextureInfo + { + public: + S32 mIndex = INVALID_INDEX; + S32 mTexCoord = 0; + + TextureTransform mTextureTransform; + + bool operator==(const TextureInfo& rhs) const; + bool operator!=(const TextureInfo& rhs) const; + + // get the UV channel that should be used for sampling this texture + // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord + S32 getTexCoord() const; + + const TextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class NormalTextureInfo : public TextureInfo + { + public: + F32 mScale = 1.0f; + + const NormalTextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + class OcclusionTextureInfo : public TextureInfo + { + public: + F32 mStrength = 1.0f; + + const OcclusionTextureInfo& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + class Material { public: @@ -91,42 +129,6 @@ namespace LL BLEND }; - class TextureInfo - { - public: - S32 mIndex = INVALID_INDEX; - S32 mTexCoord = 0; - - TextureTransform mTextureTransform; - - bool operator==(const TextureInfo& rhs) const; - bool operator!=(const TextureInfo& rhs) const; - - // get the UV channel that should be used for sampling this texture - // returns mTextureTransform.mTexCoord if present and valid, otherwise mTexCoord - S32 getTexCoord() const; - - const TextureInfo& operator=(const Value& src); - void serialize(boost::json::object& dst) const; - }; - - class NormalTextureInfo : public TextureInfo - { - public: - F32 mScale = 1.0f; - - const NormalTextureInfo& operator=(const Value& src); - void serialize(boost::json::object& dst) const; - }; - - class OcclusionTextureInfo : public TextureInfo - { - public: - F32 mStrength = 1.0f; - - const OcclusionTextureInfo& operator=(const Value& src); - void serialize(boost::json::object& dst) const; - }; class PbrMetallicRoughness { @@ -179,7 +181,6 @@ namespace LL { public: 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 @@ -206,10 +207,6 @@ namespace LL 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 mat4& modelview); - // update mAssetMatrix and mAssetMatrixInv void updateTransforms(Asset& asset, const mat4& parentMatrix); @@ -322,6 +319,31 @@ namespace LL bool prep(Asset& asset); }; + // Render Batch -- vertex buffer and list of primitives to render using + // said vertex buffer + class RenderBatch + { + public: + struct PrimitiveData + { + S32 mPrimitiveIndex = INVALID_INDEX; + S32 mNodeIndex = INVALID_INDEX; + }; + + LLPointer<LLVertexBuffer> mVertexBuffer; + std::vector<PrimitiveData> mPrimitives; + }; + + class RenderData + { + public: + // list of render batches + // indexed by [material index + 1](0 is reserved for default material) + // there should be exactly one render batch per material per variant + std::vector<RenderBatch> mBatches[LLGLSLShader::NUM_GLTF_VARIANTS]; + }; + + // C++ representation of a GLTF Asset class Asset { @@ -359,6 +381,16 @@ namespace LL // the last time update() was called according to gFrameTimeSeconds F32 mLastUpdateTime = gFrameTimeSeconds; + // data used for rendering + // 0 - single sided + // 1 - double sided + RenderData mRenderData[2]; + + // UBO for storing node transforms + U32 mNodesUBO = 0; + + // UBO for storing material data + U32 mMaterialsUBO = 0; // prepare for first time use bool prep(); @@ -373,8 +405,11 @@ namespace LL // update asset-to-node and node-to-asset transforms void updateTransforms(); - // update node render transforms - void updateRenderTransforms(const mat4& modelview); + // upload matrices to UBO + void uploadTransforms(); + + // upload materils to UBO + void uploadMaterials(); // return the index of the node that the line segment intersects with, or -1 if no hit // input and output values must be in this asset's local coordinate frame diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h index 4f660d7cfc..b9698d4017 100644 --- a/indra/newview/gltf/common.h +++ b/indra/newview/gltf/common.h @@ -64,6 +64,9 @@ namespace LL class Asset; class Material; + class TextureInfo; + class NormalTextureInfo; + class OcclusionTextureInfo; class Mesh; class Node; class Scene; @@ -78,6 +81,17 @@ namespace LL class Accessor; class BufferView; class Buffer; + + enum class TextureType : U8 + { + BASE_COLOR = 0, + NORMAL, + METALLIC_ROUGHNESS, + OCCLUSION, + EMISSIVE + }; + + constexpr U32 TEXTURE_TYPE_COUNT = 5; } } diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index 2280c7004e..e1579374d4 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -380,11 +380,22 @@ bool Primitive::prep(Asset& asset) } } } + else + { //everything must be indexed at runtime + mIndexArray.resize(mPositions.size()); + for (U32 i = 0; i < mPositions.size(); ++i) + { + mIndexArray[i] = i; + } + } U32 mask = LLVertexBuffer::MAP_VERTEX; + mShaderVariant = 0; + if (!mWeights.empty()) { + mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED; mask |= LLVertexBuffer::MAP_WEIGHT4; mask |= LLVertexBuffer::MAP_JOINT; } @@ -406,9 +417,6 @@ bool Primitive::prep(Asset& asset) mColors.resize(mPositions.size(), LLColor4U::white); } - mShaderVariant = 0; - - // TODO: support colorless vertex buffers mask |= LLVertexBuffer::MAP_COLOR; bool unlit = false; @@ -506,69 +514,79 @@ bool Primitive::prep(Asset& asset) mask |= LLVertexBuffer::MAP_TANGENT; } - if (LLGLSLShader::sCurBoundShaderPtr == nullptr) - { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer - gDebugProgram.bind(); + mAttributeMask = mask; + + if (mMaterial != INVALID_INDEX) + { + Material& material = asset.mMaterials[mMaterial]; + if (material.mAlphaMode == Material::AlphaMode::BLEND) + { + mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + } } - mVertexBuffer = new LLVertexBuffer(mask); + createOctree(); + + return true; +} + +void Primitive::upload(LLVertexBuffer* buffer) +{ + mVertexBuffer = buffer; // we store these buffer sizes as S32 elsewhere llassert(mPositions.size() <= size_t(S32_MAX)); llassert(mIndexArray.size() <= size_t(S32_MAX / 2)); - mVertexBuffer->allocateBuffer(U32(mPositions.size()), U32(mIndexArray.size() * 2)); // double the size of the index buffer for 32-bit indices - mVertexBuffer->setBuffer(); - mVertexBuffer->setPositionData(mPositions.data()); - mVertexBuffer->setColorData(mColors.data()); + llassert(mVertexBuffer != nullptr); + + // assert that buffer can hold this primitive + llassert(mVertexBuffer->getNumVerts() >= mPositions.size() + mVertexOffset); + llassert(mVertexBuffer->getNumIndices() >= mIndexArray.size() + mIndexOffset); + llassert(mVertexBuffer->getTypeMask() == mAttributeMask); + + U32 offset = mVertexOffset; + U32 count = getVertexCount(); + + mVertexBuffer->setPositionData(mPositions.data(), offset, count); + mVertexBuffer->setColorData(mColors.data(), offset, count); if (!mNormals.empty()) { - mVertexBuffer->setNormalData(mNormals.data()); + mVertexBuffer->setNormalData(mNormals.data(), offset, count); } if (!mTangents.empty()) { - mVertexBuffer->setTangentData(mTangents.data()); + mVertexBuffer->setTangentData(mTangents.data(), offset, count); } if (!mWeights.empty()) { - mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED; - mVertexBuffer->setWeight4Data(mWeights.data()); - mVertexBuffer->setJointData(mJoints.data()); + mVertexBuffer->setWeight4Data(mWeights.data(), offset, count); + mVertexBuffer->setJointData(mJoints.data(), offset, count); } // flip texcoord y, upload, then flip back (keep the off-spec data in vram only) vertical_flip(mTexCoords0); - mVertexBuffer->setTexCoord0Data(mTexCoords0.data()); + mVertexBuffer->setTexCoord0Data(mTexCoords0.data(), offset, count); vertical_flip(mTexCoords0); if (!mTexCoords1.empty()) { vertical_flip(mTexCoords1); - mVertexBuffer->setTexCoord1Data(mTexCoords1.data()); + mVertexBuffer->setTexCoord1Data(mTexCoords1.data(), offset, count); vertical_flip(mTexCoords1); } - if (!mIndexArray.empty()) { - mVertexBuffer->setIndexData(mIndexArray.data()); - } - - createOctree(); - - mVertexBuffer->unbind(); - - if (mMaterial != INVALID_INDEX) - { - Material& material = asset.mMaterials[mMaterial]; - if (material.mAlphaMode == Material::AlphaMode::BLEND) + std::vector<U32> index_array; + index_array.resize(mIndexArray.size()); + for (U32 i = 0; i < mIndexArray.size(); ++i) { - mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + index_array[i] = mIndexArray[i] + mVertexOffset; } + mVertexBuffer->setIndexData(index_array.data(), mIndexOffset, getIndexCount()); } - - return true; } void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2) @@ -616,7 +634,7 @@ void Primitive::createOctree() if (mMode == Mode::TRIANGLES) { - const U32 num_triangles = mVertexBuffer->getNumIndices() / 3; + const U32 num_triangles = getIndexCount() / 3; // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); @@ -640,7 +658,7 @@ void Primitive::createOctree() } else if (mMode == Mode::TRIANGLE_STRIP) { - const U32 num_triangles = mVertexBuffer->getNumIndices() - 2; + const U32 num_triangles = getIndexCount() - 2; // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); @@ -664,7 +682,7 @@ void Primitive::createOctree() } else if (mMode == Mode::TRIANGLE_FAN) { - const U32 num_triangles = mVertexBuffer->getNumIndices() - 2; + const U32 num_triangles = getIndexCount() - 2; // Initialize all the triangles we need mOctreeTriangles.resize(num_triangles); diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index 7cc05cf831..304eb26432 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -54,10 +54,7 @@ namespace LL ~Primitive(); - // GPU copy of mesh data - LLPointer<LLVertexBuffer> mVertexBuffer; - - // CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code + // CPU copy of mesh data std::vector<LLVector2> mTexCoords0; std::vector<LLVector2> mTexCoords1; std::vector<LLVector4a> mNormals; @@ -80,6 +77,17 @@ namespace LL // shader variant according to LLGLSLShader::GLTFVariant flags U8 mShaderVariant = 0; + // vertex attribute mask + U32 mAttributeMask = 0; + + // backpointer to vertex buffer (owned by Asset) + LLPointer<LLVertexBuffer> mVertexBuffer; + U32 mVertexOffset = 0; + U32 mIndexOffset = 0; + + U32 getVertexCount() const { return (U32) mPositions.size(); } + U32 getIndexCount() const { return (U32) mIndexArray.size(); } + std::unordered_map<std::string, S32> mAttributes; // create octree based on vertex buffer @@ -100,6 +108,11 @@ namespace LL const Primitive& operator=(const Value& src); bool prep(Asset& asset); + + // upload geometry to given vertex buffer + // asserts that buffer is bound + // asserts that buffer is valid for this primitive + void upload(LLVertexBuffer* buffer); }; } } diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index e01aec0497..7b2de4d6de 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -437,6 +437,8 @@ void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType a void GLTFSceneManager::update() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; + for (U32 i = 0; i < mObjects.size(); ++i) { if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr) @@ -552,6 +554,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged, bool unlit) void GLTFSceneManager::render(U8 variant) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; // just render the whole scene by traversing the whole scenegraph // Assumes camera transform is already set and appropriate shader is already bound. // Eventually we'll want a smarter render pipe that has pre-sorted the scene graph @@ -563,8 +566,6 @@ void GLTFSceneManager::render(U8 variant) render((U8) (variant | LLGLSLShader::GLTFVariant::MULTI_UV)); } - gGL.matrixMode(LLRender::MM_MODELVIEW); - bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED; for (U32 i = 0; i < mObjects.size(); ++i) @@ -581,19 +582,11 @@ void GLTFSceneManager::render(U8 variant) LLMatrix4a mat = mObjects[i]->getGLTFAssetToAgentTransform(); - LLMatrix4a modelview; - modelview.loadu(gGLModelView); - - matMul(mat, modelview, modelview); - - mat4 mdv = glm::make_mat4(modelview.getF32ptr()); - asset->updateRenderTransforms(mdv); + // provide a modelview matrix that goes from asset to camera space + // (matrix palettes are in asset space) + gGL.loadMatrix(gGLModelView); + gGL.multMatrix(mat.getF32ptr()); - if (rigged) - { // provide a modelview matrix that goes from asset to camera space for rigged render passes - // (matrix palettes are in asset space) - gGL.loadMatrix(glm::value_ptr(mdv)); - } render(*asset, variant); gGL.popMatrix(); @@ -602,176 +595,185 @@ void GLTFSceneManager::render(U8 variant) void GLTFSceneManager::render(Asset& asset, U8 variant) { - bool opaque = !(variant & LLGLSLShader::GLTFVariant::ALPHA_BLEND); - bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED; + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; - if (opaque) + for (U32 ds = 0; ds < 2; ++ds) { - gGLTFPBRMetallicRoughnessProgram.bind(variant); - } - else - { // alpha shaders need all the shadow map setup etc - gPipeline.bindDeferredShader(gGLTFPBRMetallicRoughnessProgram.mGLTFVariants[variant]); - } + RenderData& rd = asset.mRenderData[ds]; + auto& batches = rd.mBatches[variant]; - for (auto& node : asset.mNodes) - { - if (node.mSkin != INVALID_INDEX) + if (batches.empty()) { - if (rigged) - { - Skin& skin = asset.mSkins[node.mSkin]; - glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_JOINTS, skin.mUBO); - } + return; } - if (node.mMesh != INVALID_INDEX) + LLGLDisable cull_face(ds == 1 ? GL_CULL_FACE : 0); + + bool opaque = !(variant & LLGLSLShader::GLTFVariant::ALPHA_BLEND); + bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED; + + bool shader_bound = false; + + for (U32 i = 0; i < batches.size(); ++i) { - Mesh& mesh = asset.mMeshes[node.mMesh]; - for (auto& primitive : mesh.mPrimitives) + if (batches[i].mPrimitives.empty() || batches[i].mVertexBuffer.isNull()) { - if (primitive.mShaderVariant != variant) + continue; + } + + if (!shader_bound) + { // don't bind the shader until we know we have somthing to render + if (opaque) { - continue; + gGLTFPBRMetallicRoughnessProgram.bind(variant); + } + else + { // alpha shaders need all the shadow map setup etc + gPipeline.bindDeferredShader(gGLTFPBRMetallicRoughnessProgram.mGLTFVariants[variant]); } if (!rigged) { - gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix)); + glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_NODES, asset.mNodesUBO); } - bool cull = true; - if (primitive.mMaterial != INVALID_INDEX) - { - Material& material = asset.mMaterials[primitive.mMaterial]; - bind(asset, material); - cull = !material.mDoubleSided; - } - else + glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_MATERIALS, asset.mMaterialsUBO); + + for (U32 i = 0; i < TEXTURE_TYPE_COUNT; ++i) { - LLFetchedGLTFMaterial::sDefault.bind(); + mLastTexture[i] = -2; } - LLGLDisable cull_face(!cull ? GL_CULL_FACE : 0); + gGL.syncMatrices(); + shader_bound = true; + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfdc - set vb"); + batches[i].mVertexBuffer->setBuffer(); + } + + S32 mat_idx = i - 1; + if (mat_idx != INVALID_INDEX) + { + Material& material = asset.mMaterials[mat_idx]; + bind(asset, material); + } + else + { + LLFetchedGLTFMaterial::sDefault.bind(); + LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::GLTF_MATERIAL_ID, -1); + } + + for (auto& pdata : batches[i].mPrimitives) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("GLTF draw call"); + Node& node = asset.mNodes[pdata.mNodeIndex]; + Mesh& mesh = asset.mMeshes[node.mMesh]; + Primitive& primitive = mesh.mPrimitives[pdata.mPrimitiveIndex]; - primitive.mVertexBuffer->setBuffer(); - if (primitive.mVertexBuffer->getNumIndices() > 0) + if (rigged) { - primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0); + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfdc - bind skin"); + llassert(node.mSkin != INVALID_INDEX); + Skin& skin = asset.mSkins[node.mSkin]; + glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_JOINTS, skin.mUBO); } else { - primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts()); + LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::GLTF_NODE_ID, pdata.mNodeIndex); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gltfdc - push vb"); + + primitive.mVertexBuffer->drawRangeFast(primitive.mGLMode, primitive.mVertexOffset, primitive.mVertexOffset + primitive.getVertexCount() - 1, primitive.getIndexCount(), primitive.mIndexOffset); } } } } } -static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback) +void GLTFSceneManager::bindTexture(Asset& asset, TextureType texture_type, TextureInfo& info, LLViewerTexture* fallback) { - if (info.mIndex != INVALID_INDEX) + U8 type_idx = (U8)texture_type; + + if (info.mIndex == mLastTexture[type_idx]) + { //already bound + return; + } + + S32 uniform[] = { - Texture& texture = asset.mTextures[info.mIndex]; + LLShaderMgr::DIFFUSE_MAP, + LLShaderMgr::NORMAL_MAP, + LLShaderMgr::METALLIC_ROUGHNESS_MAP, + LLShaderMgr::OCCLUSION_MAP, + LLShaderMgr::EMISSIVE_MAP + }; - LLViewerTexture* tex = asset.mImages[texture.mSource].mTexture; - if (tex) + S32 channel = LLGLSLShader::sCurBoundShaderPtr->getTextureChannel(uniform[(U8)type_idx]); + + if (channel > -1) + { + glActiveTexture(GL_TEXTURE0 + channel); + + if (info.mIndex != INVALID_INDEX) { - tex->addTextureStats(2048.f * 2048.f); - S32 channel = LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex); + Texture& texture = asset.mTextures[info.mIndex]; + + LLViewerTexture* tex = asset.mImages[texture.mSource].mTexture; + if (tex) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_GLTF("gl bind texture"); + glBindTexture(GL_TEXTURE_2D, tex->getTexName()); - if (channel != -1 && texture.mSampler != -1) - { // set sampler state - Sampler& sampler = asset.mSamplers[texture.mSampler]; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sampler.mWrapS); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, sampler.mWrapT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sampler.mMagFilter); + if (channel != -1 && texture.mSampler != -1) + { // set sampler state + Sampler& sampler = asset.mSamplers[texture.mSampler]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, sampler.mWrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, sampler.mWrapT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sampler.mMagFilter); - // NOTE: do not set min filter. Always respect client preference for min filter + // NOTE: do not set min filter. Always respect client preference for min filter + } + else + { + // set default sampler state + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } } else { - // set default sampler state - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, fallback->getTexName()); } } else { - LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback); + glBindTexture(GL_TEXTURE_2D, fallback->getTexName()); } } - else - { - LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback); - } } void GLTFSceneManager::bind(Asset& asset, Material& material) { - // bind for rendering (derived from LLFetchedGLTFMaterial::bind) - // glTF 2.0 Specification 3.9.4. Alpha Coverage - // mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK - F32 min_alpha = -1.0; - + LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF; LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; - if (!LLPipeline::sShadowRender || (material.mAlphaMode == Material::AlphaMode::BLEND)) - { - if (material.mAlphaMode == Material::AlphaMode::MASK) - { - // dividing the alpha cutoff by transparency here allows the shader to compare against - // the alpha value of the texture without needing the transparency value - if (material.mPbrMetallicRoughness.mBaseColorFactor.a > 0.f) - { - min_alpha = material.mAlphaCutoff / material.mPbrMetallicRoughness.mBaseColorFactor.a; - } - else - { - min_alpha = 1024.f; - } - } - shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha); - } - - bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep); - - F32 tf[8]; - material.mPbrMetallicRoughness.mBaseColorTexture.mTextureTransform.getPacked(tf); - shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, tf); - shader->uniform1i(LLShaderMgr::BASE_COLOR_TEXCOORD, material.mPbrMetallicRoughness.mBaseColorTexture.getTexCoord()); + bindTexture(asset, TextureType::BASE_COLOR, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep); if (!LLPipeline::sShadowRender) { - bindTexture(asset, LLShaderMgr::NORMAL_MAP, material.mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep); - bindTexture(asset, LLShaderMgr::METALLIC_ROUGHNESS_MAP, material.mPbrMetallicRoughness.mMetallicRoughnessTexture, LLViewerFetchedTexture::sWhiteImagep); - bindTexture(asset, LLShaderMgr::OCCLUSION_MAP, material.mOcclusionTexture, LLViewerFetchedTexture::sWhiteImagep); - bindTexture(asset, LLShaderMgr::EMISSIVE_MAP, material.mEmissiveTexture, LLViewerFetchedTexture::sWhiteImagep); - - // NOTE: base color factor is baked into vertex stream - - shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, material.mPbrMetallicRoughness.mRoughnessFactor); - shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, material.mPbrMetallicRoughness.mMetallicFactor); - shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(material.mEmissiveFactor)); - - material.mNormalTexture.mTextureTransform.getPacked(tf); - shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, tf); - shader->uniform1i(LLShaderMgr::NORMAL_TEXCOORD, material.mNormalTexture.getTexCoord()); - - material.mPbrMetallicRoughness.mMetallicRoughnessTexture.mTextureTransform.getPacked(tf); - shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, tf); - shader->uniform1i(LLShaderMgr::METALLIC_ROUGHNESS_TEXCOORD, material.mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord()); - - material.mOcclusionTexture.mTextureTransform.getPacked(tf); - shader->uniform4fv(LLShaderMgr::TEXTURE_OCCLUSION_TRANSFORM, 2, tf); - shader->uniform1i(LLShaderMgr::OCCLUSION_TEXCOORD, material.mOcclusionTexture.getTexCoord()); - - material.mEmissiveTexture.mTextureTransform.getPacked(tf); - shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, tf); - shader->uniform1i(LLShaderMgr::EMISSIVE_TEXCOORD, material.mEmissiveTexture.getTexCoord()); + bindTexture(asset, TextureType::NORMAL, material.mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep); + bindTexture(asset, TextureType::METALLIC_ROUGHNESS, material.mPbrMetallicRoughness.mMetallicRoughnessTexture, LLViewerFetchedTexture::sWhiteImagep); + bindTexture(asset, TextureType::OCCLUSION, material.mOcclusionTexture, LLViewerFetchedTexture::sWhiteImagep); + bindTexture(asset, TextureType::EMISSIVE, material.mEmissiveTexture, LLViewerFetchedTexture::sWhiteImagep); } + + shader->uniform1i(LLShaderMgr::GLTF_MATERIAL_ID, &material - &asset.mMaterials[0]); } LLMatrix4a inverse(const LLMatrix4a& mat) @@ -931,10 +933,11 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) // assumes modelview matrix is already set gGL.pushMatrix(); - // get raycast in asset space LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform(); + gGL.multMatrix(agent_to_asset.getF32ptr()); + vec4 start; vec4 end; @@ -952,7 +955,8 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) if (node.mMesh != INVALID_INDEX) { - gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix)); + gGL.pushMatrix(); + gGL.multMatrix((F32*)glm::value_ptr(node.mAssetMatrix)); // draw bounding box of mesh primitives if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES)) @@ -995,6 +999,7 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } #endif + gGL.popMatrix(); } } @@ -1013,35 +1018,15 @@ void GLTFSceneManager::renderDebug() gDebugProgram.bind(); + gGL.pushMatrix(); + gGL.loadMatrix(gGLModelView); + LLGLDisable cullface(GL_CULL_FACE); LLGLEnable blend(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gPipeline.disableLights(); - // force update all mRenderMatrix, not just nodes with meshes - for (auto& obj : mObjects) - { - if (obj->isDead() || obj->mGLTFAsset == nullptr) - { - continue; - } - - mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr()); - - mat4 modelview = glm::make_mat4(gGLModelView); - - - modelview = modelview * mat; - - Asset* asset = obj->mGLTFAsset.get(); - - for (auto& node : asset->mNodes) - { - node.mRenderMatrix = modelview * node.mAssetMatrix; - } - } - for (auto& obj : mObjects) { if (obj->isDead() || obj->mGLTFAsset == nullptr) @@ -1062,9 +1047,6 @@ void GLTFSceneManager::renderDebug() LLGLDepthTest depth(GL_TRUE, i == 0 ? GL_FALSE : GL_TRUE, i == 0 ? GL_GREATER : GL_LEQUAL); LLGLState blend(GL_BLEND, i == 0 ? GL_TRUE : GL_FALSE); - - gGL.pushMatrix(); - for (auto& obj : mObjects) { if (obj->isDead() || obj->mGLTFAsset == nullptr) @@ -1072,20 +1054,16 @@ void GLTFSceneManager::renderDebug() continue; } - mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr()); + gGL.pushMatrix(); - mat4 modelview = glm::make_mat4(gGLModelView); - - modelview = modelview * mat; + gGL.multMatrix(obj->getGLTFAssetToAgentTransform().getF32ptr()); Asset* asset = obj->mGLTFAsset.get(); for (auto& node : asset->mNodes) { - // force update all mRenderMatrix, not just nodes with meshes - node.mRenderMatrix = modelview * node.mAssetMatrix; - - gGL.loadMatrix(glm::value_ptr(node.mRenderMatrix)); + gGL.pushMatrix(); + gGL.multMatrix(glm::value_ptr(node.mAssetMatrix)); // render x-axis red, y-axis green, z-axis blue gGL.color4f(1.f, 0.f, 0.f, 0.5f); gGL.begin(LLRender::LINES); @@ -1121,12 +1099,12 @@ void GLTFSceneManager::renderDebug() } gGL.end(); gGL.flush(); + gGL.popMatrix(); } - } - gGL.popMatrix(); + gGL.popMatrix(); + } } - } @@ -1140,28 +1118,36 @@ void GLTFSceneManager::renderDebug() if (drawable) { - gGL.pushMatrix(); - Asset* asset = drawable->getVObj()->mGLTFAsset.get(); - Node* node = &asset->mNodes[node_hit]; - Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit]; - gGL.flush(); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - gGL.color3f(1, 0, 1); - drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f)); + LLViewerObject* obj = drawable->getVObj(); + if (obj) + { + gGL.pushMatrix(); + gGL.multMatrix(obj->getGLTFAssetToAgentTransform().getF32ptr()); + Asset* asset = obj->mGLTFAsset.get(); + Node* node = &asset->mNodes[node_hit]; + Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit]; - gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix)); + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + gGL.color3f(1, 0, 1); + drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f)); + gGL.multMatrix(glm::value_ptr(node->mAssetMatrix)); - auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0); - drawBoxOutline(listener->mBounds[0], listener->mBounds[1]); + auto* listener = (LLVolumeOctreeListener*)primitive->mOctree->getListener(0); + drawBoxOutline(listener->mBounds[0], listener->mBounds[1]); - gGL.flush(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - gGL.popMatrix(); + gGL.flush(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.popMatrix(); + } } } + + gGL.popMatrix(); gDebugProgram.unbind(); + } diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h index 7da413e8b2..853bca59d0 100644 --- a/indra/newview/gltfscenemanager.h +++ b/indra/newview/gltfscenemanager.h @@ -59,7 +59,7 @@ namespace LL // bind the given material for rendering void bind(LL::GLTF::Asset& asset, LL::GLTF::Material& material); - + void bindTexture(LL::GLTF::Asset& asset, LL::GLTF::TextureType texture_type, LL::GLTF::TextureInfo& info, LLViewerTexture* fallback); void renderOpaque(); void renderAlpha(); @@ -94,6 +94,11 @@ namespace LL U32 mPendingGLTFUploads = 0; U32 mJointUBO = 0; + + + // render loop state + S32 mLastTexture[GLTF::TEXTURE_TYPE_COUNT] = { -2, -2, -2, -2, -2 }; + }; } diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 93fea899f3..34da5b29d4 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -257,7 +257,7 @@ void LLDrawPoolAlpha::forwardRender(bool rigged) mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // } gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); - if (rigged) + if (rigged && mType == LLDrawPool::POOL_ALPHA_POST_WATER) { // draw GLTF scene to depth buffer before rigged alpha LL::GLTFSceneManager::instance().render(false, false); LL::GLTFSceneManager::instance().render(false, true); diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp index 5eb10fe335..4ea23412e6 100644 --- a/indra/newview/lldrawpoolpbropaque.cpp +++ b/indra/newview/lldrawpoolpbropaque.cpp @@ -54,7 +54,10 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass) { llassert(!LLPipeline::sRenderingHUDs); - LL::GLTFSceneManager::instance().renderOpaque(); + if (mRenderType == LLPipeline::RENDER_TYPE_PASS_GLTF_PBR_ALPHA_MASK) + { + LL::GLTFSceneManager::instance().renderOpaque(); + } gDeferredPBROpaqueProgram.bind(); pushGLTFBatches(mRenderType); diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp index f544b70329..66ccdd2b32 100644 --- a/indra/newview/llheroprobemanager.cpp +++ b/indra/newview/llheroprobemanager.cpp @@ -188,18 +188,8 @@ void LLHeroProbeManager::update() LLVector3(0, 0, -1) }; - // Iterate through each face of the cube - for (int i = 0; i < 6; i++) - { - float cube_facing = fmax(-1, fmin(1.0f, cameraDirection * cubeFaces[i])); - - cube_facing = 1 - cube_facing; - - mFaceUpdateList[i] = ceilf(cube_facing * gPipeline.RenderHeroProbeConservativeUpdateMultiplier); - } - - mProbes[0]->mOrigin = probe_pos; + mProbes[0]->mRadius = mNearestHero->getScale().magVec() * 0.5f; } else { @@ -220,9 +210,10 @@ void LLHeroProbeManager::renderProbes() static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1); static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3); + static LLCachedControl<S32> sUpdateRate(gSavedSettings, "RenderHeroProbeUpdateRate", 0); F32 near_clip = 0.01f; - if (mNearestHero != nullptr && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0) && + if (mNearestHero != nullptr && !gTeleportDisplay && !gDisconnected && !LLAppViewer::instance()->logoutRequestSent()) { LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime"); @@ -232,20 +223,36 @@ void LLHeroProbeManager::renderProbes() gPipeline.mReflectionMapManager.mRadiancePass = true; mRenderingMirror = true; - doOcclusion(); + S32 rate = sUpdateRate; - for (U32 j = 0; j < mProbes.size(); j++) + // rate must be divisor of 6 (1, 2, 3, or 6) + if (rate < 1) + { + rate = 1; + } + else if (rate > 3) { + rate = 6; + } + + S32 face = gFrameCount % 6; + + if (!mProbes.empty() && !mProbes[0].isNull() && !mProbes[0]->mOccluded) + { + LL_PROFILE_ZONE_NUM(gFrameCount % rate); + LL_PROFILE_ZONE_NUM(rate); + for (U32 i = 0; i < 6; ++i) { - if (mFaceUpdateList[i] > 0 && mCurrentProbeUpdateFrame % mFaceUpdateList[i] == 0) - { - updateProbeFace(mProbes[j], i, mNearestHero->getReflectionProbeIsDynamic() && sDetail > 0, near_clip); - mCurrentProbeUpdateFrame = 0; + if ((gFrameCount % rate) == (i % rate)) + { // update 6/rate faces per frame + LL_PROFILE_ZONE_NUM(i); + updateProbeFace(mProbes[0], i, mNearestHero->getReflectionProbeIsDynamic() && sDetail > 0, near_clip); } } - generateRadiance(mProbes[j]); + generateRadiance(mProbes[0]); } + mRenderingMirror = false; gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass; @@ -253,8 +260,6 @@ void LLHeroProbeManager::renderProbes() mProbes[0]->mViewerObject = mNearestHero; mProbes[0]->autoAdjustOrigin(); } - - mCurrentProbeUpdateFrame++; } // Do the reflection map update render passes. @@ -388,6 +393,7 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, bool // Useful when we may not always be rendering a full set of faces of the probe. void LLHeroProbeManager::generateRadiance(LLReflectionMap* probe) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY; S32 sourceIdx = mReflectionProbeCount; // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes. @@ -594,7 +600,7 @@ void LLHeroProbeManager::doOcclusion() for (auto& probe : mProbes) { - if (probe != nullptr && probe != mDefaultProbe) + if (probe != nullptr) { probe->doOcclusion(eye); } diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h index c8d505f4c3..28852770c3 100644 --- a/indra/newview/llheroprobemanager.h +++ b/indra/newview/llheroprobemanager.h @@ -147,9 +147,6 @@ private: bool mReset = false; bool mRenderingMirror = false; - std::map<int, int> mFaceUpdateList; - - U32 mCurrentProbeUpdateFrame = 0; std::vector<LLPointer<LLVOVolume>> mHeroVOList; LLPointer<LLVOVolume> mNearestHero; diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 37fbfccbbb..12d0aa4f8e 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -259,7 +259,18 @@ static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool variant.mDefines = shader.mDefines; // NOTE: Must come before addPermutation - variant.addPermutation("MAX_JOINTS_PER_GLTF_OBJECT", std::to_string(LLSkinningUtil::getMaxGLTFJointCount())); + U32 node_size = 16 * 3; + U32 max_nodes = gGLManager.mMaxUniformBlockSize / node_size; + variant.addPermutation("MAX_NODES_PER_GLTF_OBJECT", std::to_string(max_nodes)); + + U32 material_size = 16 * 12; + U32 max_materials = gGLManager.mMaxUniformBlockSize / material_size; + LLGLSLShader::sMaxGLTFMaterials = max_materials; + + variant.addPermutation("MAX_MATERIALS_PER_GLTF_OBJECT", std::to_string(max_materials)); + + U32 max_vec4s = gGLManager.mMaxUniformBlockSize / 16; + variant.addPermutation("MAX_UBO_VEC4S", std::to_string(max_vec4s)); if (rigged) { diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index f9ff8217af..6c9c4751d7 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -2436,6 +2436,7 @@ void LLPipeline::doOcclusion(LLCamera& camera) mCubeVB->setBuffer(); mReflectionMapManager.doOcclusion(); + mHeroProbeManager.doOcclusion(); gOcclusionCubeProgram.unbind(); gGL.setColorMask(true, true); diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index a80b1e1c13..7bc81a1f79 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -864,21 +864,21 @@ name="HeroProbeUpdateRate" width="150"> <combo_box.item - label="Every Frame" - name="0" - value="1"/> + label="Low" + name="6" + value="6"/> + <combo_box.item + label="Medium" + name="3" + value="3"/> <combo_box.item - label="Every 2nd Frame" + label="High" name="1" value="2"/> <combo_box.item - label="Every 3rd Frame" - name="2" - value="3"/> - <combo_box.item - label="Every 4th Frame" - name="3" - value="4"/> + label="Ultra" + name="0" + value="1"/> </combo_box> <!-- End of mirror settings --> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 0e7c522f74..4a043bbbc9 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2866,8 +2866,8 @@ function="World.EnvPreset" <menu_item_call label="Open..." name="Open..."> - <menu_item_call.on_enable - function="EnableGLTF"/> + <!--<menu_item_call.on_enable + function="EnableGLTF"/>--> <menu_item_call.on_click function="Advanced.ClickGLTFOpen" /> </menu_item_call> |