diff options
Diffstat (limited to 'indra/newview')
108 files changed, 7414 insertions, 1584 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index e56534b84d..7568c08430 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -502,6 +502,7 @@ set(viewer_SOURCE_FILES llpathfindingobject.cpp llpathfindingobjectlist.cpp llpathfindingpathtool.cpp + llpbrterrainfeatures.cpp llpersistentnotificationstorage.cpp llphysicsmotion.cpp llphysicsshapebuilderutil.cpp @@ -1148,6 +1149,7 @@ set(viewer_HEADER_FILES llpathfindingobject.h llpathfindingobjectlist.h llpathfindingpathtool.h + llpbrterrainfeatures.h llpersistentnotificationstorage.h llphysicsmotion.h llphysicsshapebuilderutil.h diff --git a/indra/newview/app_settings/keywords_lsl_default.xml b/indra/newview/app_settings/keywords_lsl_default.xml index 893b017367..f99b86bd39 100644 --- a/indra/newview/app_settings/keywords_lsl_default.xml +++ b/indra/newview/app_settings/keywords_lsl_default.xml @@ -1945,6 +1945,15 @@ <key>tooltip</key> <string/> </map> + <key>INVENTORY_SETTING</key> + <map> + <key>type</key> + <string>integer</string> + <key>value</key> + <integer>56</integer> + <key>tooltip</key> + <string/> + </map> <key>INVENTORY_SOUND</key> <map> <key>type</key> diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 482012cdd6..51b5c66384 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -71,7 +71,7 @@ <string>Inventory</string> <string>SceneLoadTiming</string> <string>Avatar</string> - <string>Voice</string> + <string>Voice</string> --> </array> </map> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 587ae3c7c1..5f44d24a63 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -379,7 +379,7 @@ <key>Value</key> <integer>0</integer> </map> - <key>AutoAcceptNewInventory</key> + <key>AutoAcceptNewInventory</key> <map> <key>Comment</key> <string>Automatically accept new notecards/textures/landmarks</string> @@ -9253,17 +9253,6 @@ <key>Value</key> <integer>0</integer> </map> - <key>RenderTerrainPBRForce</key> - <map> - <key>Comment</key> - <string>Force-load PBR terrain if enabled</string> - <key>Persist</key> - <integer>0</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> <key>RenderTerrainPBRDetail</key> <map> <key>Comment</key> @@ -9308,6 +9297,17 @@ <key>Value</key> <real>8.0</real> </map> + <key>RenderTerrainPBRTransformsEnabled</key> + <map> + <key>Comment</key> + <string>EXPERIMENTAL: Enable PBR Terrain texture transforms.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RenderTerrainPBRNormalsEnabled</key> <map> <key>Comment</key> @@ -14634,6 +14634,226 @@ <key>Value</key> <string>00000000-0000-0000-0000-000000000000</string> </map> + <key>LocalTerrainTransform1ScaleU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset1 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform1ScaleV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset1 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform1Rotation</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset1 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform1OffsetU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset1 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform1OffsetV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset1 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform2ScaleU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset2 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform2ScaleV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset2 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform2Rotation</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset2 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform2OffsetU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset2 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform2OffsetV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset2 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform3ScaleU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset3 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform3ScaleV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset3 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform3Rotation</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset3 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform3OffsetU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset3 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform3OffsetV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset3 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform4ScaleU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset4 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform4ScaleV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset4 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>LocalTerrainTransform4Rotation</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset4 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform4OffsetU</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset4 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>LocalTerrainTransform4OffsetV</key> + <map> + <key>Comment</key> + <string>KHR texture transform component if LocalTerrainAsset4 is set</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> <key>PathfindingRetrieveNeighboringRegion</key> <map> <key>Comment</key> @@ -15518,5 +15738,16 @@ <key>Value</key> <integer>0</integer> </map> + <key>GLTFEnabled</key> + <map> + <key>Comment</key> + <string>Enable GLTF support. Set to true by simulator if the simulator you are connected to supports GLTF Asset upload. WARNING: Manually setting this to true will enable buttons that can drain your L$ balance by implicitly uploading textures without asking.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> </map> </llsd> diff --git a/indra/newview/app_settings/shaders/class1/deferred/aoUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/aoUtil.glsl index 49470f0e39..ce018623a8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/aoUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/aoUtil.glsl @@ -23,8 +23,7 @@ * $/LicenseInfo$ */ -uniform sampler2D noiseMap; -uniform sampler2D normalMap; +uniform sampler2D noiseMap; uniform sampler2D depthMap; uniform float ssao_radius; diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl index 438e1d1b33..37dcbbd328 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl @@ -27,8 +27,6 @@ out vec4 frag_color; -uniform sampler2D diffuseMap; - void main() { frag_color = vec4(1,1,1,1); diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl index d7d98477c0..23a3ca4911 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl @@ -27,7 +27,6 @@ out vec4 frag_color; -uniform sampler2D normalMap; uniform sampler2D lightMap; uniform float dist_factor; diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl index 01543732d0..ab0e4fd4d8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl @@ -50,7 +50,6 @@ SOFTWARE. uniform sampler2D normalMap; uniform sampler2D depthMap; -uniform sampler2D emissiveRect; uniform sampler2D projectionMap; // rgba uniform sampler2D brdfLut; @@ -487,6 +486,43 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, return clamp(color, vec3(0), vec3(10)); } +vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, + float perceptualRoughness, + float metallic, + vec3 n, // normal + vec3 p, // pixel position + vec3 v, // view vector (negative normalized pixel position) + vec3 lp, // light position + vec3 ld, // light direction (for spotlights) + vec3 lightColor, + float lightSize, float falloff, float is_pointlight, float ambiance) +{ + vec3 color = vec3(0,0,0); + + vec3 lv = lp.xyz - p; + + float lightDist = length(lv); + + float dist = lightDist / lightSize; + if (dist <= 1.0) + { + lv /= lightDist; + + float dist_atten = calcLegacyDistanceAttenuation(dist, falloff); + + // spotlight coefficient. + float spot = max(dot(-ld, lv), is_pointlight); + // spot*spot => GL_SPOT_EXPONENT=2 + float spot_atten = spot*spot; + + vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials + + color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv); + } + + return color; +} + void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor) { vec3 f0 = vec3(0.04); diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl index d0fc362db9..ae179d3f37 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl @@ -51,8 +51,6 @@ uniform vec4[2] texture_emissive_transform; out vec3 vary_fragcoord; -uniform float near_clip; - in vec3 position; in vec4 diffuse_color; in vec3 normal; @@ -88,7 +86,7 @@ void main() #endif gl_Position = vert; - vary_fragcoord.xyz = vert.xyz + vec3(0,0,near_clip); + vary_fragcoord.xyz = vert.xyz; base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0); normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl index 71f4a93369..2cb7ff196b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl @@ -31,7 +31,7 @@ #define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3 #if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 -#define TerrainCoord vec4[2] +#define TerrainCoord vec4[3] #elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 #define TerrainCoord vec2 #endif @@ -131,12 +131,16 @@ uniform vec3[4] emissiveColors; uniform vec4 minimum_alphas; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlphaCutoff() #if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 +in vec4[10] vary_coords; +#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 in vec4[2] vary_coords; #endif in vec3 vary_position; in vec3 vary_normal; -in vec3 vary_tangent; +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) +in vec3 vary_tangents[4]; flat in float vary_sign; +#endif in vec4 vary_texcoord0; in vec4 vary_texcoord1; @@ -144,17 +148,26 @@ void mirrorClip(vec3 position); float terrain_mix(TerrainMix tm, vec4 tms4); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) +// from mikktspace.com +vec3 mikktspace(vec3 vNt, vec3 vT) +{ + vec3 vN = vary_normal; + + vec3 vB = vary_sign * cross(vN, vT); + vec3 tnorm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); + + tnorm *= gl_FrontFacing ? 1.0 : -1.0; + + return tnorm; +} +#endif + void main() { // Make sure we clip the terrain if we're in a mirror. mirrorClip(vary_position); -#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 - TerrainCoord terrain_texcoord = vary_coords; -#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 - TerrainCoord terrain_texcoord = vary_texcoord0.xy; -#endif - float alpha1 = texture(alpha_ramp, vary_texcoord0.zw).a; float alpha2 = texture(alpha_ramp,vary_texcoord1.xy).a; float alphaFinal = texture(alpha_ramp, vary_texcoord1.zw).a; @@ -182,9 +195,19 @@ void main() PBRMix pbr_mix = init_pbr_mix(); PBRMix mix2; + TerrainCoord terrain_texcoord; switch (tm.type & MIX_X) { case MIX_X: +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + 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 + terrain_texcoord = vary_coords[0].xy; +#endif mix2 = terrain_sample_and_multiply_pbr( terrain_texcoord , detail_0_base_color @@ -207,6 +230,9 @@ void main() , emissiveColors[0] #endif ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix2.vNt = mikktspace(mix2.vNt, vary_tangents[0]); +#endif pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.x); break; default: @@ -215,6 +241,15 @@ void main() switch (tm.type & MIX_Y) { case MIX_Y: +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + 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 + terrain_texcoord = vary_coords[0].zw; +#endif mix2 = terrain_sample_and_multiply_pbr( terrain_texcoord , detail_1_base_color @@ -237,6 +272,9 @@ void main() , emissiveColors[1] #endif ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix2.vNt = mikktspace(mix2.vNt, vary_tangents[1]); +#endif pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.y); break; default: @@ -245,6 +283,15 @@ void main() switch (tm.type & MIX_Z) { case MIX_Z: +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + 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 + terrain_texcoord = vary_coords[1].xy; +#endif mix2 = terrain_sample_and_multiply_pbr( terrain_texcoord , detail_2_base_color @@ -267,6 +314,9 @@ void main() , emissiveColors[2] #endif ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix2.vNt = mikktspace(mix2.vNt, vary_tangents[2]); +#endif pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.z); break; default: @@ -275,6 +325,15 @@ void main() switch (tm.type & MIX_W) { case MIX_W: +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 + 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 + terrain_texcoord = vary_coords[1].zw; +#endif mix2 = terrain_sample_and_multiply_pbr( terrain_texcoord , detail_3_base_color @@ -297,6 +356,9 @@ void main() , emissiveColors[3] #endif ); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + mix2.vNt = mikktspace(mix2.vNt, vary_tangents[3]); +#endif pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.w); break; default: @@ -311,19 +373,11 @@ void main() float base_color_factor_alpha = terrain_mix(tm, vec4(baseColorFactors[0].z, baseColorFactors[1].z, baseColorFactors[2].z, baseColorFactors[3].z)); #if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) - // from mikktspace.com - vec3 vNt = pbr_mix.vNt; - vec3 vN = vary_normal; - vec3 vT = vary_tangent.xyz; - - vec3 vB = vary_sign * cross(vN, vT); - vec3 tnorm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); - - tnorm *= gl_FrontFacing ? 1.0 : -1.0; + vec3 tnorm = normalize(pbr_mix.vNt); #else vec3 tnorm = vary_normal; - tnorm *= gl_FrontFacing ? 1.0 : -1.0; #endif + tnorm *= gl_FrontFacing ? 1.0 : -1.0; #if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE) diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl index c44b60b6e5..1ae9efe544 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl @@ -233,17 +233,12 @@ float terrain_mix(TerrainMix tm, vec4 tms4) // Triplanar mapping // Pre-transformed texture coordinates for each axial uv slice (Packing: xy, yz, (-x)z, unused) -#define TerrainCoord vec4[2] +#define TerrainCoord vec4[3] -vec2 _t_uv(vec2 uv_unflipped, float sign_or_zero) +// If sign_or_zero is positive, use uv_unflippped, otherwise use uv_flipped +vec2 _t_uv(vec2 uv_unflipped, vec2 uv_flipped, float sign_or_zero) { - // Handle case where sign is 0 - float sign = (2.0*sign_or_zero) + 1.0; - sign /= abs(sign); - // If the vertex normal is negative, flip the texture back - // right-side up. - vec2 uv = uv_unflipped * vec2(sign, 1); - return uv; + return mix(uv_flipped, uv_unflipped, max(0.0, sign_or_zero)); } vec3 _t_normal_post_1(vec3 vNt0, float sign_or_zero) @@ -298,9 +293,9 @@ PBRMix terrain_sample_pbr( { PBRMix mix = init_pbr_mix(); -#define get_uv_x() _t_uv(terrain_coord[0].zw, sign(vary_vertex_normal.x)) -#define get_uv_y() _t_uv(terrain_coord[1].xy, sign(vary_vertex_normal.y)) -#define get_uv_z() _t_uv(terrain_coord[0].xy, sign(vary_vertex_normal.z)) +#define get_uv_x() _t_uv(terrain_coord[0].zw, terrain_coord[1].zw, sign(vary_vertex_normal.x)) +#define get_uv_y() _t_uv(terrain_coord[1].xy, terrain_coord[2].xy, sign(vary_vertex_normal.y)) +#define get_uv_z() _t_uv(terrain_coord[0].xy, vec2(0), sign(vary_vertex_normal.z)) switch (tw.type & SAMPLE_X) { case SAMPLE_X: diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl index ed52297314..f8e826bbdb 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl @@ -23,6 +23,11 @@ * $/LicenseInfo$ */ +#define TERRAIN_PBR_DETAIL_EMISSIVE 0 +#define TERRAIN_PBR_DETAIL_OCCLUSION -1 +#define TERRAIN_PBR_DETAIL_NORMAL -2 +#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3 + uniform mat3 normal_matrix; uniform mat4 texture_matrix0; uniform mat4 modelview_matrix; @@ -34,21 +39,25 @@ in vec4 tangent; in vec4 diffuse_color; in vec2 texcoord1; -#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 -out vec4[2] vary_coords; -#endif out vec3 vary_vertex_normal; // Used by pbrterrainUtilF.glsl out vec3 vary_normal; -out vec3 vary_tangent; +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) +out vec3 vary_tangents[4]; flat out float vary_sign; +#endif out vec4 vary_texcoord0; out vec4 vary_texcoord1; +#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 +out vec4[10] vary_coords; +#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 +out vec4[2] vary_coords; +#endif out vec3 vary_position; -// *HACK: tangent_space_transform should use texture_normal_transform, or maybe -// we shouldn't use tangent_space_transform at all. See the call to -// tangent_space_transform below. -uniform vec4[2] texture_base_color_transform; +// *HACK: Each material uses only one texture transform, but the KHR texture +// transform spec allows handling texture transforms separately for each +// individual texture info. +uniform vec4[5] terrain_texture_transforms; vec2 terrain_texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform); vec3 terrain_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform); @@ -63,31 +72,101 @@ void main() vary_vertex_normal = normal; vec3 t = normal_matrix * tangent.xyz; - vary_tangent = normalize(t); - // *TODO: Decide if we want this. It may be better to just calculate the - // tangents on-the-fly in the fragment shader, due to the subtleties of the - // effect of triplanar mapping on UVs. - // *HACK: Should be using texture_normal_transform here. The KHR texture - // transform spec requires handling texture transforms separately for each - // individual texture. - vary_tangent = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, texture_base_color_transform)); +#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL) + { + vec4[2] ttt; + // material 1 + ttt[0].xyz = terrain_texture_transforms[0].xyz; + ttt[1].x = terrain_texture_transforms[0].w; + ttt[1].y = terrain_texture_transforms[1].x; + vary_tangents[0] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt)); + // material 2 + ttt[0].xyz = terrain_texture_transforms[1].yzw; + ttt[1].xy = terrain_texture_transforms[2].xy; + vary_tangents[1] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt)); + // material 3 + ttt[0].xy = terrain_texture_transforms[2].zw; + ttt[0].z = terrain_texture_transforms[3].x; + ttt[1].xy = terrain_texture_transforms[3].yz; + vary_tangents[2] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt)); + // material 4 + ttt[0].x = terrain_texture_transforms[3].w; + ttt[0].yz = terrain_texture_transforms[4].xy; + ttt[1].xy = terrain_texture_transforms[4].zw; + vary_tangents[3] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt)); + } + vary_sign = tangent.w; +#endif vary_normal = normalize(n); // Transform and pass tex coords - // *HACK: texture_base_color_transform is used for all of these here, but - // the KHR texture transform spec requires handling texture transforms - // separately for each individual texture. + { + vec4[2] ttt; #if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3 - // xy - vary_coords[0].xy = terrain_texture_transform(position.xy, texture_base_color_transform); - // yz - vary_coords[0].zw = terrain_texture_transform(position.yz, texture_base_color_transform); - // (-x)z - vary_coords[1].xy = terrain_texture_transform(position.xz * vec2(-1, 1), texture_base_color_transform); +// Don't care about upside-down (transform_xy_flipped()) +#define transform_xy() terrain_texture_transform(position.xy, ttt) +#define transform_yz() terrain_texture_transform(position.yz, ttt) +#define transform_negx_z() terrain_texture_transform(position.xz * vec2(-1, 1), ttt) +#define transform_yz_flipped() terrain_texture_transform(position.yz * vec2(-1, 1), ttt) +#define transform_negx_z_flipped() terrain_texture_transform(position.xz, ttt) + // material 1 + ttt[0].xyz = terrain_texture_transforms[0].xyz; + ttt[1].x = terrain_texture_transforms[0].w; + ttt[1].y = terrain_texture_transforms[1].x; + vary_coords[0].xy = transform_xy(); + vary_coords[0].zw = transform_yz(); + vary_coords[1].xy = transform_negx_z(); + vary_coords[1].zw = transform_yz_flipped(); + vary_coords[2].xy = transform_negx_z_flipped(); + // material 2 + ttt[0].xyz = terrain_texture_transforms[1].yzw; + ttt[1].xy = terrain_texture_transforms[2].xy; + vary_coords[2].zw = transform_xy(); + vary_coords[3].xy = transform_yz(); + vary_coords[3].zw = transform_negx_z(); + vary_coords[4].xy = transform_yz_flipped(); + vary_coords[4].zw = transform_negx_z_flipped(); + // material 3 + ttt[0].xy = terrain_texture_transforms[2].zw; + ttt[0].z = terrain_texture_transforms[3].x; + ttt[1].xy = terrain_texture_transforms[3].yz; + vary_coords[5].xy = transform_xy(); + vary_coords[5].zw = transform_yz(); + vary_coords[6].xy = transform_negx_z(); + vary_coords[6].zw = transform_yz_flipped(); + vary_coords[7].xy = transform_negx_z_flipped(); + // material 4 + ttt[0].x = terrain_texture_transforms[3].w; + ttt[0].yz = terrain_texture_transforms[4].xy; + ttt[1].xy = terrain_texture_transforms[4].zw; + vary_coords[7].zw = transform_xy(); + vary_coords[8].xy = transform_yz(); + vary_coords[8].zw = transform_negx_z(); + vary_coords[9].xy = transform_yz_flipped(); + vary_coords[9].zw = transform_negx_z_flipped(); #elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1 - vary_texcoord0.xy = terrain_texture_transform(position.xy, texture_base_color_transform); + // material 1 + ttt[0].xyz = terrain_texture_transforms[0].xyz; + ttt[1].x = terrain_texture_transforms[0].w; + ttt[1].y = terrain_texture_transforms[1].x; + vary_coords[0].xy = terrain_texture_transform(position.xy, ttt); + // material 2 + ttt[0].xyz = terrain_texture_transforms[1].yzw; + ttt[1].xy = terrain_texture_transforms[2].xy; + vary_coords[0].zw = terrain_texture_transform(position.xy, ttt); + // material 3 + ttt[0].xy = terrain_texture_transforms[2].zw; + ttt[0].z = terrain_texture_transforms[3].x; + ttt[1].xy = terrain_texture_transforms[3].yz; + vary_coords[1].xy = terrain_texture_transform(position.xy, ttt); + // material 4 + ttt[0].x = terrain_texture_transforms[3].w; + ttt[0].yz = terrain_texture_transforms[4].xy; + ttt[1].xy = terrain_texture_transforms[4].zw; + vary_coords[1].zw = terrain_texture_transform(position.xy, ttt); #endif + } vec4 tc = vec4(texcoord1,0,1); vary_texcoord0.zw = tc.xy; diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl index 9db8f461dd..f208ac746b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl @@ -27,8 +27,6 @@ out vec4 frag_color; -uniform sampler2D diffuseMap; - in vec4 post_pos; in float target_pos_x; in vec4 vertex_color; diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl index 16cc7cfbbc..6f7bd2bf3c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl @@ -24,7 +24,6 @@ */ uniform sampler2D normalMap; -uniform sampler2D depthMap; #if defined(SUN_SHADOW) uniform sampler2DShadow shadowMap0; diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl index d6fe211910..1fd31e0546 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl @@ -57,7 +57,7 @@ void main() outColor.a = 0.0; // yes, downstream atmospherics - frag_data[0] = outColor; + frag_data[0] = max(outColor, vec4(0)); frag_data[1] = vec4(0.0,0.0,0.0,-1.0); vec3 nvn = normalize(vary_normal); frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS); diff --git a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl index 6ba57f7543..767416d564 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl @@ -48,6 +48,7 @@ vec2 khr_texture_transform(vec2 texcoord, vec2 scale, float rotation, vec2 offse return (transform * vec3(texcoord, 1)).xy; } +// A texture transform function for PBR materials applied to shape prims/Collada model prims // vertex_texcoord - The UV texture coordinates sampled from the vertex at // runtime. Per SL convention, this is in a right-handed UV coordinate // system. Collada models also have right-handed UVs. @@ -134,7 +135,7 @@ vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] kh return (weights.x * vertex_binormal.xyz) + (weights.y * vertex_tangent.xyz); } -// Similar to tangent_space_transform but no no texture animation support. +// Similar to tangent_space_transform but no texture animation support. vec3 terrain_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform) { // Immediately convert to left-handed coordinate system ((0,1) -> (0, -1)) diff --git a/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl b/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl index 7e1d906878..d7f6d20547 100644 --- a/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl +++ b/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl @@ -23,8 +23,6 @@ * $/LicenseInfo$ */ - uniform sampler2D exposureMap; - vec3 srgb_to_linear(vec3 cs) { vec3 low_range = cs / vec3(12.92); diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl new file mode 100644 index 0000000000..789c00259b --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl @@ -0,0 +1,301 @@ +/** + * @file pbrmetallicroughnessF.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + + +// GLTF pbrMetallicRoughness implementation + + +// ================================== +// 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); +vec3 srgb_to_linear(vec3 c); +// ================================== + + +// ================================== +// needed by all lit variants +// ================================== +#ifndef UNLIT +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; +in vec2 normal_uv; +in vec2 metallic_roughness_uv; +in vec2 occlusion_uv; +#endif +// ================================== + + +// ================================== +// needed by all alpha variants +// ================================== +#ifdef ALPHA_BLEND +in vec3 vary_fragcoord; +uniform vec4 clipPlane; +uniform float clipSign; +void waterClip(vec3 pos); +void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive); +vec4 applySkyAndWaterFog(vec3 pos, vec3 additive, vec3 atten, vec4 color); +#endif +// ================================== + + +// ================================== +// needed by lit alpha +// ================================== +#if defined(ALPHA_BLEND) && !defined(UNLIT) + +#ifdef HAS_SUN_SHADOW +uniform sampler2D lightMap; +uniform vec2 screen_res; +#endif + +// Lights +// See: LLRender::syncLightState() +uniform vec4 light_position[8]; +uniform vec3 light_direction[8]; // spot direction +uniform vec4 light_attenuation[8]; // linear, quadratic, is omni, unused, See: LLPipeline::setupHWLights() and syncLightState() +uniform vec3 light_diffuse[8]; +uniform vec2 light_deferred_attenuation[8]; // light size and falloff + +uniform int sun_up_factor; +uniform vec3 sun_dir; +uniform vec3 moon_dir; + +void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float nh, out float nl, out float nv, out float vh, out float lightDist); +float calcLegacyDistanceAttenuation(float distance, float falloff); +float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); +void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, + vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear); + +void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor); + +vec3 pbrBaseLight(vec3 diffuseColor, + vec3 specularColor, + float metallic, + vec3 pos, + vec3 norm, + float perceptualRoughness, + vec3 light_dir, + vec3 sunlit, + float scol, + vec3 radiance, + vec3 irradiance, + vec3 colorEmissive, + float ao, + vec3 additive, + vec3 atten); + +vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, + float perceptualRoughness, + float metallic, + vec3 n, // normal + vec3 p, // pixel position + vec3 v, // view vector (negative normalized pixel position) + vec3 lp, // light position + vec3 ld, // light direction (for spotlights) + vec3 lightColor, + float lightSize, float falloff, float is_pointlight, float ambiance); + +#endif +// ================================== + + +// ================================== +// output definition +// ================================== +#if defined(ALPHA_BLEND) || defined(UNLIT) +out vec4 frag_color; +#else +out vec4 frag_data[4]; +#endif +// ================================== + + +void main() +{ + +// ================================== +// all variants +// mirror clip +// base color +// masking +// emissive +// ================================== + vec3 pos = vary_position; + mirrorClip(pos); + + vec4 basecolor = texture(diffuseMap, base_color_uv.xy).rgba; + basecolor.rgb = srgb_to_linear(basecolor.rgb); + basecolor *= vertex_color; + + if (basecolor.a < minimum_alpha) + { + discard; + } + + vec3 emissive = emissiveColor; + emissive *= srgb_to_linear(texture(emissiveMap, emissive_uv.xy).rgb); +// ================================== + +// ================================== +// all lit variants +// prepare norm +// prepare orm +// ================================== +#ifndef UNLIT + // from mikktspace.com + vec3 vNt = texture(normalMap, normal_uv.xy).xyz*2.0-1.0; + float sign = vary_sign; + vec3 vN = vary_normal; + vec3 vT = vary_tangent.xyz; + + vec3 vB = sign * cross(vN, vT); + vec3 norm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); + norm *= gl_FrontFacing ? 1.0 : -1.0; + + // RGB = Occlusion, Roughness, Metal + // default values, see LLViewerTexture::sDefaultPBRORMImagep + // occlusion 1.0 + // roughness 0.0 + // metal 0.0 + vec3 orm = texture(metallicRoughnessMap, metallic_roughness_uv.xy).rgb; + orm.r = texture(occlusionMap, occlusion_uv.xy).r; + orm.g *= roughnessFactor; + orm.b *= metallicFactor; +#endif +// ================================== + +// ================================== +// non alpha output +// ================================== +#ifndef ALPHA_BLEND +#ifdef UNLIT + vec4 color = basecolor; + color.rgb += emissive.rgb; + frag_color = color; +#else + frag_data[0] = max(vec4(basecolor.rgb, 0.0), vec4(0)); + frag_data[1] = max(vec4(orm.rgb,0.0), vec4(0)); + frag_data[2] = vec4(norm, GBUFFER_FLAG_HAS_PBR); + frag_data[3] = max(vec4(emissive,0), vec4(0)); +#endif +#endif + + +// ================================== +// alpha implementation +// ================================== +#ifdef ALPHA_BLEND + + float scol = 1.0; + vec3 sunlit; + vec3 amblit; + vec3 additive; + vec3 atten; + + vec3 light_dir; + +#ifdef UNLIT + light_dir = vec3(0,0,1); + vec3 norm = vec3(0,0,1); +#else + light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir; +#endif + + calcAtmosphericVarsLinear(pos.xyz, norm, light_dir, sunlit, amblit, additive, atten); + +#ifndef UNLIT + vec3 sunlit_linear = srgb_to_linear(sunlit); + + vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5; + +#ifdef HAS_SUN_SHADOW + scol = sampleDirectionalShadow(pos.xyz, norm.xyz, frag); +#endif + + float perceptualRoughness = orm.g * roughnessFactor; + float metallic = orm.b * metallicFactor; + + // PBR IBL + float gloss = 1.0 - perceptualRoughness; + vec3 irradiance = vec3(0); + vec3 radiance = vec3(0); + sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true, amblit); + + vec3 diffuseColor; + vec3 specularColor; + calcDiffuseSpecular(basecolor.rgb, metallic, diffuseColor, specularColor); + + vec3 v = -normalize(pos.xyz); + + vec3 color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, emissive, orm.r, additive, atten); + + vec3 light = vec3(0); + + // Punctual lights +#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); + + LIGHT_LOOP(1) + LIGHT_LOOP(2) + LIGHT_LOOP(3) + LIGHT_LOOP(4) + LIGHT_LOOP(5) + LIGHT_LOOP(6) + LIGHT_LOOP(7) + + color.rgb += light.rgb; + + color.rgb = applySkyAndWaterFog(pos.xyz, additive, atten, vec4(color, 1.0)).rgb; + + float a = basecolor.a*vertex_color.a; + + frag_color = max(vec4(color.rgb,a), vec4(0)); +#else // UNLIT + vec4 color = basecolor; + color.rgb += emissive.rgb; + frag_color = color; +#endif +#endif // ALPHA_BLEND +} + diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl new file mode 100644 index 0000000000..aac3dc917f --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl @@ -0,0 +1,262 @@ +/** + * @file pbrmetallicroughnessV.glsl + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// GLTF pbrMetallicRoughness implementation + +uniform mat4 modelview_matrix; + +#ifdef HAS_SKIN +uniform mat4 projection_matrix; +#else +uniform mat3 normal_matrix; +uniform mat4 modelview_projection_matrix; +#endif + +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; +in vec2 texcoord0; +out vec2 base_color_uv; +out vec2 emissive_uv; +out vec4 vertex_color; +out vec3 vary_position; + +#ifndef UNLIT +in vec3 normal; +in vec4 tangent; +out vec2 normal_uv; +out vec2 metallic_roughness_uv; +out vec2 occlusion_uv; +out vec3 vary_tangent; +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; + + vec2 Scale = p[0].xy; + float Rotation = -p[0].z; + vec2 Offset = vec2(p[0].w, p[1].x); + + mat3 translation = mat3(1,0,0, 0,1,0, Offset.x, Offset.y, 1); + mat3 rotation = mat3( + cos(Rotation), sin(Rotation), 0, + -sin(Rotation), cos(Rotation), 0, + 0, 0, 1); + + mat3 scale = mat3(Scale.x,0,0, 0,Scale.y,0, 0,0,1); + + mat3 matrix = translation * rotation * scale; + + vec2 uvTransformed = ( matrix * vec3(texcoord.xy, 1) ).xy; + + uvTransformed.y = 1.0 - uvTransformed.y; + + return uvTransformed; +} + +#ifndef UNLIT +vec3 gltf_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform) +{ //derived from tangent_space_transform in textureUtilV.glsl + vec2 weights = vec2(0, 1); + + // Convert to left-handed coordinate system + weights.y = -weights.y; + + // Apply KHR_texture_transform (rotation only) + float khr_rotation = khr_gltf_transform[0].z; + mat2 khr_rotation_mat = mat2( + cos(khr_rotation),-sin(khr_rotation), + sin(khr_rotation), cos(khr_rotation) + ); + weights = khr_rotation_mat * weights; + + // Convert back to right-handed coordinate system + weights.y = -weights.y; + + // Similar to the MikkTSpace-compatible method of extracting the binormal + // from the normal and tangent, as seen in the fragment shader + vec3 vertex_binormal = vertex_tangent.w * cross(vertex_normal, vertex_tangent.xyz); + + return (weights.x * vertex_binormal.xyz) + (weights.y * vertex_tangent.xyz); + + return vertex_tangent.xyz; +} +#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]; +}; + +mat4 getGLTFSkinTransform() +{ + int i; + + vec4 w = weight4; + + uint i1 = joint.x; + uint i2 = joint.y; + uint i3 = joint.z; + uint i4 = joint.w; + + mat3 mat = mat3(gltf_joints[i1])*w.x; + mat += mat3(gltf_joints[i2])*w.y; + mat += mat3(gltf_joints[i3])*w.z; + mat += mat3(gltf_joints[i4])*w.w; + + vec3 trans = vec3(gltf_joints[i1][0].w,gltf_joints[i1][1].w,gltf_joints[i1][2].w)*w.x; + trans += vec3(gltf_joints[i2][0].w,gltf_joints[i2][1].w,gltf_joints[i2][2].w)*w.y; + trans += vec3(gltf_joints[i3][0].w,gltf_joints[i3][1].w,gltf_joints[i3][2].w)*w.z; + trans += vec3(gltf_joints[i4][0].w,gltf_joints[i4][1].w,gltf_joints[i4][2].w)*w.w; + + mat4 ret; + + ret[0] = vec4(mat[0], 0); + ret[1] = vec4(mat[1], 0); + ret[2] = vec4(mat[2], 0); + 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 + +} + +#endif + +void main() +{ +#ifdef HAS_SKIN + mat4 mat = getGLTFSkinTransform(); + + mat = modelview_matrix * mat; + + vec3 pos = (mat*vec4(position.xyz,1.0)).xyz; + vary_position = pos; + + 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; + +#ifdef MULTI_UV + vec2 uv[2]; + uv[0] = texcoord0; + uv[1] = texcoord1; + + bcuv = uv[base_color_texcoord]; + emuv = uv[emissive_texcoord]; +#else + bcuv = texcoord0; + emuv = texcoord0; +#endif + + base_color_uv = gltf_texture_transform(bcuv, texture_base_color_transform); + emissive_uv = gltf_texture_transform(emuv, texture_emissive_transform); + +#ifndef UNLIT + vec2 normuv; + vec2 rmuv; + vec2 ouv; +#ifdef MULTI_UV + normuv = uv[normal_texcoord]; + rmuv = uv[metallic_roughness_texcoord]; + ouv = uv[occlusion_texcoord]; +#else + normuv = texcoord0; + rmuv = texcoord0; + ouv = texcoord0; +#endif + normal_uv = gltf_texture_transform(normuv, texture_normal_transform); + metallic_roughness_uv = gltf_texture_transform(rmuv, texture_metallic_roughness_transform); + occlusion_uv = gltf_texture_transform(ouv, texture_occlusion_transform); +#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)); + vary_sign = tangent.w; + vary_normal = n; +#endif + + vertex_color = diffuse_color; +#ifdef ALPHA_BLEND + vary_fragcoord = vert.xyz; +#endif +} + + + + diff --git a/indra/newview/app_settings/shaders/class1/interface/copyF.glsl b/indra/newview/app_settings/shaders/class1/interface/copyF.glsl index edaa2488f0..094d147e86 100644 --- a/indra/newview/app_settings/shaders/class1/interface/copyF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/copyF.glsl @@ -25,7 +25,10 @@ in vec2 tc; +#if defined(COPY_DEPTH) uniform sampler2D depthMap; +#endif + uniform sampler2D diffuseMap; out vec4 frag_color; diff --git a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl index 17774adbf5..f4a8051427 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl @@ -118,7 +118,7 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, vec3 v, // surface point to camera vec3 l); //surface point to light -vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, +vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, float metallic, vec3 n, // normal @@ -127,33 +127,7 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, vec3 lp, // light position vec3 ld, // light direction (for spotlights) vec3 lightColor, - float lightSize, float falloff, float is_pointlight, float ambiance) -{ - vec3 color = vec3(0,0,0); - - vec3 lv = lp.xyz - p; - - float lightDist = length(lv); - - float dist = lightDist / lightSize; - if (dist <= 1.0) - { - lv /= lightDist; - - float dist_atten = calcLegacyDistanceAttenuation(dist, falloff); - - // spotlight coefficient. - float spot = max(dot(-ld, lv), is_pointlight); - // spot*spot => GL_SPOT_EXPONENT=2 - float spot_atten = spot*spot; - - vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials - - color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv); - } - - return color; -} + float lightSize, float falloff, float is_pointlight, float ambiance); void main() { @@ -230,7 +204,7 @@ void main() vec3 light = vec3(0); // Punctual lights -#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); +#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); LIGHT_LOOP(1) LIGHT_LOOP(2) diff --git a/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl index 87977eb28c..7b82aa1a0d 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl @@ -25,8 +25,6 @@ out vec4 frag_color; -uniform sampler2D normalMap; - // Inputs uniform vec3 sun_dir; uniform vec3 moon_dir; diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl index d3e19cf4a8..26ab0406f6 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl @@ -77,7 +77,6 @@ uniform float is_mirror; uniform vec3 sun_dir; uniform vec3 moon_dir; -in vec2 vary_fragcoord; uniform mat4 proj_mat; uniform mat4 inv_proj; diff --git a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl index ac3fec23f6..4ed778371f 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl @@ -27,7 +27,6 @@ out vec4 frag_color; -uniform sampler2D depthMap; uniform sampler2D diffuseRect; uniform sampler2D specularRect; uniform sampler2D emissiveRect; // PBR linear packed Occlusion, Roughness, Metal. See: pbropaqueF.glsl diff --git a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl index e419525bd5..6c13757149 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl @@ -29,10 +29,8 @@ out vec4 frag_color; uniform sampler2D diffuseRect; uniform sampler2D specularRect; -uniform sampler2D normalMap; uniform sampler2D emissiveRect; // PBR linear packed Occlusion, Roughness, Metal. See: pbropaqueF.glsl uniform sampler2D lightFunc; -uniform sampler2D depthMap; uniform vec3 env_mat[3]; uniform float sun_wash; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 529d1cba6b..ca88fe7482 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -29,7 +29,6 @@ out vec4 frag_color; uniform sampler2D diffuseRect; uniform sampler2D specularRect; -uniform sampler2D normalMap; uniform sampler2D emissiveRect; // PBR linear packed Occlusion, Roughness, Metal. See: pbropaqueF.glsl const float M_PI = 3.14159265; @@ -38,7 +37,6 @@ const float M_PI = 3.14159265; uniform sampler2D lightMap; #endif -uniform sampler2D depthMap; uniform sampler2D lightFunc; uniform float blur_size; diff --git a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl index 092b0c3c08..bc4d36d10d 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl @@ -29,12 +29,9 @@ out vec4 frag_color; uniform sampler2D diffuseRect; uniform sampler2D specularRect; -uniform sampler2D depthMap; -uniform sampler2D normalMap; uniform sampler2D emissiveRect; // PBR linear packed Occlusion, Roughness, Metal. See: pbropaqueF.glsl uniform samplerCube environmentMap; uniform sampler2D lightMap; -uniform sampler2D projectionMap; // rgba uniform sampler2D lightFunc; uniform mat4 proj_mat; //screen space to light space diff --git a/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl index a5a37d80dd..2bf785e773 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl @@ -28,8 +28,6 @@ out vec4 frag_color; // Inputs in vec4 vary_fragcoord; -uniform sampler2D normalMap; - vec4 getPositionWithDepth(vec2 pos_screen, float depth); float getDepth(vec2 pos_screen); diff --git a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl index 728d70ebb2..1c02dc764d 100644 --- a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl @@ -25,7 +25,6 @@ out vec4 frag_color; -uniform sampler2D diffuseMap; uniform sampler2D bumpMap; #ifdef TRANSPARENT_WATER diff --git a/indra/newview/gltf/README.md b/indra/newview/gltf/README.md new file mode 100644 index 0000000000..a2d43be1d6 --- /dev/null +++ b/indra/newview/gltf/README.md @@ -0,0 +1,156 @@ +# Linden Lab GLTF Implementation + +Currently in prototype stage. Much functionality is missing (blend shapes, +multiple texture coordinates, etc). + +GLTF Specification can be found here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html. +If this implementation disagrees with the GLTF Specification, the specification is correct. + +Class structure and naming should match the GLTF Specification as closely as possible while +conforming to the LL coding standards. All code in headers should be contained in the +LL::GLTF namespace. + +The implementation serves both the client and the server. + +## Design Principles + +- The implementation MUST be capable of round-trip serialization with no data loss beyond F64 to F32 conversions. +- The implementation MUST use the same indexing scheme as the GLTF specification. Do not store pointers where the +- GLTF specification stores indices, store indices. +- Limit dependencies on llcommon as much as possible. Prefer std::, boost::, and (soon) glm:: over LL facsimiles. +- Usage of LLSD is forbidden in the LL::GLTF namespace. +- Use "using namespace" liberally in .cpp files, but never in .h files. +- "using Foo = Bar" is permissible in .h files within the LL::GLTF namespace. + +## Loading, Copying, and Serialization +Each class should provide two functions (Primitive shown for example): + +``` +// Serialize to the provided json object. +// "obj" should be "this" in json form on return +// Do not serialize default values +void serialize(boost::json::object& obj) const; + +// Initialize from a provided json value +const Primitive& operator=(const Value& src); +``` + +"serialize" implementations should use "write": + +``` +void Primitive::serialize(boost::json::object& dst) const +{ + write(mMaterial, "material", dst, -1); + write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES); + write(mIndices, "indices", dst, INVALID_INDEX); + write(mAttributes, "attributes", dst); +} +``` + +And operator= implementations should use "copy": + +``` +const Primitive& Primitive::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "material", mMaterial); + copy(src, "mode", mMode); + copy(src, "indices", mIndices); + copy(src, "attributes", mAttributes); + + mGLMode = gltf_mode_to_gl_mode(mMode); + } + return *this; +} +``` + +Parameters to "write" and "copy" MUST be ordered "src" before "dst" +so the code reads as "write src to dst" and "copy src to dst". + +When reading string constants from GLTF json (i.e. "OPAQUE", "TRIANGLES"), these +strings should be converted to enums inside operator=. It is permissible to +store the original strings during prototyping to aid in development, but eventually +we'll purge these strings from the implementation. However, implementations MUST +preserve any and all "name" members. + +"write" and "copy" implementations MUST be stored in buffer_util.h. +As implementers encounter new data types, you'll see compiler errors +pointing at templates in buffer_util.h. See vec3 as a known good +example of how to add support for a new type (there are bad examples, so beware): + +``` +// vec3 +template<> +inline bool copy(const Value& src, vec3& dst) +{ + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 3) + { + if (arr[0].is_double() && + arr[1].is_double() && + arr[2].is_double()) + { + dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double()); + } + return true; + } + } + return false; +} + +template<> +inline bool write(const vec3& src, Value& dst) +{ + dst = boost::json::array(); + boost::json::array& arr = dst.as_array(); + arr.resize(3); + arr[0] = src.x; + arr[1] = src.y; + arr[2] = src.z; + return true; +} + +``` + +"write" MUST return true if ANY data was written +"copy" MUST return true if ANY data was copied + +Speed is important, but so is safety. In writers, try to avoid redundant copies +(prefer resize over push_back, convert dst to an empty array and fill it, don't +make an array on the stack and copy it into dst). + +boost::json WILL throw exceptions if you call as_foo() on a mismatched type but +WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo +before calling as_foo or get_foo. DO NOT add exception handlers. If boost throws +an exception in serialization, the fix is to add type checks. If we see a large +number of crash reports from boost::json exceptions, each of those reports +indicates a place where we're missing "is_foo" checks. They are gold. Do not +bury them with an exception handler. + +DO NOT rely on existing type conversion tools in the LL codebase -- LL data models +conflict with the GLTF specification so we MUST provide conversions independent of +our existing implementations. + +### JSON Serialization ### + + + +NEVER include buffer_util.h from a header. + +Loading from and saving to disk (import/export) is currently done using tinygltf, but this is not a long term +solution. Eventually the implementation should rely solely on boost::json for reading and writing .gltf +files and should handle .bin files natively. + +When serializing Images and Buffers to the server, clients MUST store a single UUID "uri" field and nothing else. +The server MUST reject any data that violates this requirement. + +Clients MUST remove any Images from Buffers prior to upload to the server. +Servers MAY reject Assets that contain Buffers with unreferenced data. + +... to be continued. + + + diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp index 8559656b40..2ef9237f2d 100644 --- a/indra/newview/gltf/accessor.cpp +++ b/indra/newview/gltf/accessor.cpp @@ -27,40 +27,272 @@ #include "../llviewerprecompiledheaders.h" #include "asset.h" +#include "buffer_util.h" +#include "llfilesystem.h" using namespace LL::GLTF; +using namespace boost::json; -const Buffer& Buffer::operator=(const tinygltf::Buffer& src) +namespace LL { - mData = src.data; - mName = src.name; - mUri = src.uri; + 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) +{ + S32 idx = this - &asset.mBuffers[0]; + + mData.erase(mData.begin() + offset, mData.begin() + offset + length); + + llassert(mData.size() <= size_t(INT_MAX)); + mByteLength = S32(mData.size()); + + for (BufferView& view : asset.mBufferViews) + { + if (view.mBuffer == idx) + { + if (view.mByteOffset >= offset) + { + view.mByteOffset -= length; + } + } + } +} + +bool Buffer::prep(Asset& asset) +{ + if (mByteLength == 0) + { + return false; + } + + LLUUID id; + if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull()) + { // loaded from an asset, fetch the buffer data from the asset store + LLFileSystem file(id, LLAssetType::AT_GLTF_BIN, LLFileSystem::READ); + + if (mByteLength > file.getSize()) + { + LL_WARNS("GLTF") << "Unexpected glbin size: " << id << " is " << file.getSize() << " bytes, expected " << mByteLength << LL_ENDL; + return false; + } + + mData.resize(mByteLength); + if (!file.read((U8*)mData.data(), mByteLength)) + { + LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL; + return false; + } + } + else if (mUri.find("data:") == 0) + { // loaded from a data URI, load the texture from the data + LL_WARNS() << "Data URIs not yet supported" << LL_ENDL; + return false; + } + else if (!asset.mFilename.empty() && + !mUri.empty()) // <-- uri could be empty if we're loading from .glb + { + std::string dir = gDirUtilp->getDirName(asset.mFilename); + std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri; + + std::ifstream file(bin_file, std::ios::binary); + if (!file.is_open()) + { + LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL; + return false; + } + + file.seekg(0, std::ios::end); + if (mByteLength > file.tellg()) + { + LL_WARNS("GLTF") << "Unexpected file size: " << bin_file << " is " << file.tellg() << " bytes, expected " << mByteLength << LL_ENDL; + return false; + } + file.seekg(0, std::ios::beg); + + mData.resize(mByteLength); + file.read((char*)mData.data(), mData.size()); + } + + // POSTCONDITION: on success, mData.size == mByteLength + llassert(mData.size() == mByteLength); + return true; +} + +bool Buffer::save(Asset& asset, const std::string& folder) +{ + if (mUri.substr(0, 5) == "data:") + { + LL_WARNS("GLTF") << "Data URIs not yet supported" << LL_ENDL; + return false; + } + + std::string bin_file = folder + gDirUtilp->getDirDelimiter(); + + if (mUri.empty()) + { + if (mName.empty()) + { + S32 idx = this - &asset.mBuffers[0]; + mUri = llformat("buffer_%d.bin", idx); + } + else + { + mUri = mName + ".bin"; + } + } + + bin_file += mUri; + + std::ofstream file(bin_file, std::ios::binary); + if (!file.is_open()) + { + LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL; + return false; + } + + file.write((char*)mData.data(), mData.size()); + + return true; +} + +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); + copy(src, "byteLength", mByteLength); + + // 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 BufferView& BufferView::operator=(const tinygltf::BufferView& src) +void BufferView::serialize(object& dst) const { - mBuffer = src.buffer; - mByteLength = (S32)src.byteLength; - mByteOffset = (S32)src.byteOffset; - mByteStride = (S32)src.byteStride; - mTarget = src.target; - mName = src.name; + 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 Accessor& Accessor::operator=(const tinygltf::Accessor& src) +void Accessor::serialize(object& dst) const { - mBufferView = src.bufferView; - mByteOffset = (S32)src.byteOffset; - mComponentType = src.componentType; - mCount = (S32)src.count; - mType = src.type; - mNormalized = src.normalized; - mName = src.name; - mMax = src.maxValues; - mMin = src.minValues; + 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; } diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h index 6e8871ef61..ec68c5f624 100644 --- a/indra/newview/gltf/accessor.h +++ b/indra/newview/gltf/accessor.h @@ -26,16 +26,16 @@ * $/LicenseInfo$ */ -#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,52 +44,77 @@ 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); + + bool prep(Asset& asset); - const Buffer& operator=(const tinygltf::Buffer& src); + void serialize(boost::json::object& obj) const; + const Buffer& operator=(const Value& value); + + bool save(Asset& asset, const std::string& folder); }; class BufferView { 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; - const BufferView& operator=(const tinygltf::BufferView& src); - + void serialize(boost::json::object& obj) const; + const BufferView& operator=(const Value& value); }; class Accessor { public: - S32 mBufferView = INVALID_INDEX; - S32 mByteOffset; - S32 mComponentType; - S32 mCount; - std::vector<double> mMax; - std::vector<double> mMin; + enum class Type : U8 + { + SCALAR, + VEC2, + VEC3, + VEC4, + MAT2, + MAT3, + MAT4 + }; - enum class Type : S32 + enum class ComponentType : U32 { - SCALAR = TINYGLTF_TYPE_SCALAR, - VEC2 = TINYGLTF_TYPE_VEC2, - VEC3 = TINYGLTF_TYPE_VEC3, - VEC4 = TINYGLTF_TYPE_VEC4, - MAT2 = TINYGLTF_TYPE_MAT2, - MAT3 = TINYGLTF_TYPE_MAT3, - MAT4 = TINYGLTF_TYPE_MAT4 + BYTE = 5120, + UNSIGNED_BYTE = 5121, + SHORT = 5122, + UNSIGNED_SHORT = 5123, + UNSIGNED_INT = 5125, + FLOAT = 5126 }; - S32 mType; - bool mNormalized; + std::vector<double> mMax; + std::vector<double> mMin; std::string mName; + S32 mBufferView = INVALID_INDEX; + S32 mByteOffset = 0; + ComponentType mComponentType = ComponentType::BYTE; + S32 mCount = 0; + Type mType = Type::SCALAR; + bool mNormalized = false; - const Accessor& operator=(const tinygltf::Accessor& src); + void serialize(boost::json::object& obj) const; + const Accessor& operator=(const Value& value); }; + + // convert from "SCALAR", "VEC2", etc to Accessor::Type + Accessor::Type gltf_type_to_enum(const std::string& type); + + // convert from Accessor::Type to "SCALAR", "VEC2", etc + std::string enum_to_gltf_type(Accessor::Type type); } } diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp index d6a899ad4c..8b85eba3e5 100644 --- a/indra/newview/gltf/animation.cpp +++ b/indra/newview/gltf/animation.cpp @@ -28,10 +28,12 @@ #include "asset.h" #include "buffer_util.h" +#include "../llskinningutil.h" using namespace LL::GLTF; +using namespace boost::json; -void Animation::allocateGLResources(Asset& asset) +bool Animation::prep(Asset& asset) { if (!mSamplers.empty()) { @@ -39,7 +41,10 @@ void Animation::allocateGLResources(Asset& asset) mMaxTime = -FLT_MAX; for (auto& sampler : mSamplers) { - sampler.allocateGLResources(asset); + if (!sampler.prep(asset)) + { + return false; + } mMinTime = llmin(sampler.mMinTime, mMinTime); mMaxTime = llmax(sampler.mMaxTime, mMaxTime); } @@ -51,13 +56,21 @@ void Animation::allocateGLResources(Asset& asset) for (auto& channel : mRotationChannels) { - channel.allocateGLResources(asset, mSamplers[channel.mSampler]); + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } } for (auto& channel : mTranslationChannels) { - channel.allocateGLResources(asset, mSamplers[channel.mSampler]); + if (!channel.prep(asset, mSamplers[channel.mSampler])) + { + return false; + } } + + return true; } void Animation::update(Asset& asset, F32 dt) @@ -84,8 +97,7 @@ void Animation::apply(Asset& asset, float time) } }; - -void Animation::Sampler::allocateGLResources(Asset& asset) +bool Animation::Sampler::prep(Asset& asset) { Accessor& accessor = asset.mAccessors[mInput]; mMinTime = accessor.mMin[0]; @@ -95,10 +107,79 @@ void Animation::Sampler::allocateGLResources(Asset& asset) LLStrider<F32> frame_times = mFrameTimes.data(); copy(asset, accessor, frame_times); + + return true; +} + + +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; +} + +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; } void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t) { + LL_PROFILE_ZONE_SCOPED; + if (time < mMinTime) { frameIndex = 0; @@ -108,16 +189,15 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F if (mFrameTimes.size() > 1) { + llassert(mFrameTimes.size() <= size_t(U32_MAX)); + frameIndex = U32(mFrameTimes.size()) - 2; + t = 1.f; + if (time > mMaxTime) { - frameIndex = (U32)mFrameTimes.size() - 2; - t = 1.0f; return; } - frameIndex = (U32)mFrameTimes.size() - 2; - t = 1.f; - for (U32 i = 0; i < (U32)mFrameTimes.size() - 1; i++) { if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1]) @@ -135,11 +215,13 @@ void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F } } -void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler) { Accessor& accessor = asset.mAccessors[sampler.mOutput]; copy(asset, accessor, mRotations); + + return true; } void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time) @@ -158,21 +240,21 @@ 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); } } -void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +bool Animation::TranslationChannel::prep(Asset& asset, Animation::Sampler& sampler) { Accessor& accessor = asset.mAccessors[sampler.mOutput]; copy(asset, accessor, mTranslations); + + return true; } void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time) @@ -191,20 +273,22 @@ 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); } } -void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler) +bool Animation::ScaleChannel::prep(Asset& asset, Animation::Sampler& sampler) { Accessor& accessor = asset.mAccessors[sampler.mOutput]; copy(asset, accessor, mScales); + + return true; } void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time) @@ -223,65 +307,153 @@ 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); } } -const Animation& Animation::operator=(const tinygltf::Animation& src) +void Animation::serialize(object& obj) const { - mName = src.name; + write(mName, "name", obj); + write(mSamplers, "samplers", obj); - mSamplers.resize(src.samplers.size()); - for (U32 i = 0; i < src.samplers.size(); ++i) - { - mSamplers[i] = src.samplers[i]; - } + 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); +} - for (U32 i = 0; i < src.channels.size(); ++i) +const Animation& Animation::operator=(const Value& src) +{ + if (src.is_object()) { - if (src.channels[i].target_path == "rotation") - { - mRotationChannels.push_back(RotationChannel()); - mRotationChannels.back() = src.channels[i]; - } + const object& obj = src.as_object(); - if (src.channels[i].target_path == "translation") - { - mTranslationChannels.push_back(TranslationChannel()); - mTranslationChannels.back() = src.channels[i]; - } + copy(obj, "name", mName); + copy(obj, "samplers", mSamplers); + + // make a temporory copy of generic channels + std::vector<Channel> channels; + copy(obj, "channels", channels); - if (src.channels[i].target_path == "scale") + // break up into channel specific implementations + for (auto& channel: channels) { - mScaleChannels.push_back(ScaleChannel()); - mScaleChannels.back() = src.channels[i]; + 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; } -void Skin::allocateGLResources(Asset& asset) +Skin::~Skin() +{ + if (mUBO) + { + glDeleteBuffers(1, &mUBO); + } +} + +void Skin::uploadMatrixPalette(Asset& asset) +{ + // prepare matrix palette + + U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount(); + + if (mUBO == 0) + { + glGenBuffers(1, &mUBO); + } + + size_t joint_count = llmin<size_t>(max_joints, mJoints.size()); + + std::vector<mat4> t_mp; + + t_mp.resize(joint_count); + + for (U32 i = 0; i < joint_count; ++i) + { + Node& joint = asset.mNodes[mJoints[i]]; + // build matrix palette in asset space + t_mp[i] = joint.mAssetMatrix * mInverseBindMatricesData[i]; + } + + std::vector<F32> glmp; + + glmp.resize(joint_count * 12); + + F32* mp = glmp.data(); + + for (U32 i = 0; i < joint_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]; + } + + glBindBuffer(GL_UNIFORM_BUFFER, mUBO); + glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +bool Skin::prep(Asset& asset) { if (mInverseBindMatrices != INVALID_INDEX) { Accessor& accessor = asset.mAccessors[mInverseBindMatrices]; copy(asset, accessor, mInverseBindMatricesData); } + + return true; } -const Skin& Skin::operator=(const tinygltf::Skin& src) +const Skin& Skin::operator=(const Value& src) { - mName = src.name; - mSkeleton = src.skeleton; - mInverseBindMatrices = src.inverseBindMatrices; - mJoints = src.joints; - + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "skeleton", mSkeleton); + copy(src, "inverseBindMatrices", mInverseBindMatrices); + copy(src, "joints", mJoints); + } return *this; } +void Skin::serialize(object& obj) const +{ + write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX); + write(mJoints, "joints", obj); + write(mName, "name", obj); + write(mSkeleton, "skeleton", obj, INVALID_INDEX); +} diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h index 66ccd14c5c..d5426fd4ce 100644 --- a/indra/newview/gltf/animation.h +++ b/indra/newview/gltf/animation.h @@ -27,7 +27,6 @@ */ #include "accessor.h" - // LL GLTF Implementation namespace LL { @@ -50,16 +49,10 @@ namespace LL S32 mOutput = INVALID_INDEX; std::string mInterpolation; - void allocateGLResources(Asset& asset); - - const Sampler& operator=(const tinygltf::AnimationSampler& src) - { - mInput = src.input; - mOutput = src.output; - mInterpolation = src.interpolation; + bool prep(Asset& asset); - return *this; - } + void serialize(boost::json::object& dst) const; + const Sampler& operator=(const Value& value); // get the frame index and time for the specified time // asset -- the asset to reference for Accessors @@ -77,40 +70,33 @@ 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; - std::string mTargetPath; - std::string mName; - - 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); }; class RotationChannel : public Channel { public: - std::vector<glh::quaternionf> mRotations; + RotationChannel() = default; + RotationChannel(const Channel& channel) : Channel(channel) {} - const RotationChannel& operator=(const tinygltf::AnimationChannel& src) - { - Channel::operator=(src); - return *this; - } + std::vector<quat> mRotations; // prepare data needed for rendering // asset -- asset to reference for Accessors // sampler -- Sampler associated with this channel - void allocateGLResources(Asset& asset, Sampler& sampler); + bool prep(Asset& asset, Sampler& sampler); void apply(Asset& asset, Sampler& sampler, F32 time); }; @@ -118,18 +104,15 @@ namespace LL class TranslationChannel : public Channel { public: - std::vector<glh::vec3f> mTranslations; + TranslationChannel() = default; + TranslationChannel(const Channel& channel) : Channel(channel) {} - const TranslationChannel& operator=(const tinygltf::AnimationChannel& src) - { - Channel::operator=(src); - return *this; - } + std::vector<vec3> mTranslations; // prepare data needed for rendering // asset -- asset to reference for Accessors // sampler -- Sampler associated with this channel - void allocateGLResources(Asset& asset, Sampler& sampler); + bool prep(Asset& asset, Sampler& sampler); void apply(Asset& asset, Sampler& sampler, F32 time); }; @@ -137,18 +120,15 @@ namespace LL class ScaleChannel : public Channel { public: - std::vector<glh::vec3f> mScales; + ScaleChannel() = default; + ScaleChannel(const Channel& channel) : Channel(channel) {} - const ScaleChannel& operator=(const tinygltf::AnimationChannel& src) - { - Channel::operator=(src); - return *this; - } + std::vector<vec3> mScales; // prepare data needed for rendering // asset -- asset to reference for Accessors // sampler -- Sampler associated with this channel - void allocateGLResources(Asset& asset, Sampler& sampler); + bool prep(Asset& asset, Sampler& sampler); void apply(Asset& asset, Sampler& sampler, F32 time); }; @@ -167,9 +147,10 @@ namespace LL std::vector<TranslationChannel> mTranslationChannels; std::vector<ScaleChannel> mScaleChannels; - const Animation& operator=(const tinygltf::Animation& src); + void serialize(boost::json::object& dst) const; + const Animation& operator=(const Value& value); - void allocateGLResources(Asset& asset); + bool prep(Asset& asset); void update(Asset& asset, float dt); diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp index 973a460b73..21be69aae2 100644 --- a/indra/newview/gltf/asset.cpp +++ b/indra/newview/gltf/asset.cpp @@ -30,13 +30,67 @@ #include "llvolumeoctree.h" #include "../llviewershadermgr.h" #include "../llviewercontrol.h" +#include "../llviewertexturelist.h" +#include "../pipeline.h" +#include "buffer_util.h" +#include <boost/url.hpp> +#include "llimagejpeg.h" using namespace LL::GLTF; +using namespace boost::json; + + +namespace LL +{ + namespace GLTF + { + static std::unordered_set<std::string> ExtensionsSupported = { + "KHR_materials_unlit", + "KHR_texture_transform" + }; + + Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode) + { + if (alpha_mode == "OPAQUE") + { + return Material::AlphaMode::OPAQUE; + } + else if (alpha_mode == "MASK") + { + return Material::AlphaMode::MASK; + } + else if (alpha_mode == "BLEND") + { + return Material::AlphaMode::BLEND; + } + else + { + return Material::AlphaMode::OPAQUE; + } + } + + std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode) + { + switch (alpha_mode) + { + case Material::AlphaMode::OPAQUE: + return "OPAQUE"; + case Material::AlphaMode::MASK: + return "MASK"; + case Material::AlphaMode::BLEND: + return "BLEND"; + default: + return "OPAQUE"; + } + } + } +} + void Scene::updateTransforms(Asset& asset) { - LLMatrix4a identity; - identity.setIdentity(); + mat4 identity = glm::identity<mat4>(); + for (auto& nodeIndex : mNodes) { Node& node = asset.mNodes[nodeIndex]; @@ -44,7 +98,7 @@ void Scene::updateTransforms(Asset& asset) } } -void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview) { for (auto& nodeIndex : mNodes) { @@ -53,9 +107,9 @@ void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) } } -void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) +void Node::updateRenderTransforms(Asset& asset, const mat4& modelview) { - matMul(mMatrix, modelview, mRenderMatrix); + mRenderMatrix = modelview * mMatrix; for (auto& childIndex : mChildren) { @@ -64,13 +118,12 @@ void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview) } } -LLMatrix4a inverse(const LLMatrix4a& mat); - -void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix) +void Node::updateTransforms(Asset& asset, const mat4& parentMatrix) { makeMatrixValid(); - matMul(mMatrix, parentMatrix, mAssetMatrix); - mAssetMatrixInv = inverse(mAssetMatrix); + mAssetMatrix = parentMatrix * mMatrix; + + mAssetMatrixInv = glm::inverse(mAssetMatrix); S32 my_index = this - &asset.mNodes[0]; @@ -90,26 +143,13 @@ void Asset::updateTransforms() } } -void Asset::updateRenderTransforms(const LLMatrix4a& modelview) +void Asset::updateRenderTransforms(const mat4& modelview) { -#if 0 - // traverse hierarchy and update render transforms from scratch - for (auto& scene : mScenes) - { - scene.updateRenderTransforms(*this, modelview); - } -#else // use mAssetMatrix to update render transforms from node list for (auto& node : mNodes) { - //if (node.mMesh != INVALID_INDEX) - { - matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); - } + node.mRenderMatrix = modelview * node.mAssetMatrix; } - -#endif - } S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, @@ -133,12 +173,13 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, { if (node.mMesh != INVALID_INDEX) { - bool newHit = false; + LLMatrix4a ami; + ami.loadu(glm::value_ptr(node.mAssetMatrixInv)); // transform start and end to this node's local space - node.mAssetMatrixInv.affineTransform(start, local_start); - node.mAssetMatrixInv.affineTransform(asset_end, local_end); + ami.affineTransform(start, local_start); + ami.affineTransform(asset_end, local_end); Mesh& mesh = mMeshes[node.mMesh]; for (auto& primitive : mesh.mPrimitives) @@ -161,8 +202,10 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, if (newHit) { + LLMatrix4a am; + am.loadu(glm::value_ptr(node.mAssetMatrix)); // shorten line segment on hit - node.mAssetMatrix.affineTransform(p, asset_end); + am.affineTransform(p, asset_end); // transform results back to asset space if (intersection) @@ -172,12 +215,10 @@ S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, if (normal || tangent) { - LLMatrix4 normalMatrix(node.mAssetMatrixInv.getF32ptr()); - - normalMatrix.transpose(); + mat4 normalMatrix = glm::transpose(node.mAssetMatrixInv); LLMatrix4a norm_mat; - norm_mat.loadu((F32*)normalMatrix.mMatrix); + norm_mat.loadu(glm::value_ptr(normalMatrix)); if (normal) { @@ -219,446 +260,933 @@ void Node::makeMatrixValid() { if (!mMatrixValid && mTRSValid) { - glh::matrix4f rot; - mRotation.get_value(rot); - - glh::matrix4f trans; - trans.set_translate(mTranslation); - - glh::matrix4f sc; - sc.set_scale(mScale); - - glh::matrix4f t; - //t = sc * rot * trans; - //t = trans * rot * sc; // best so far, still wrong on negative scale - //t = sc * trans * rot; - t = trans * sc * rot; - - mMatrix.loadu(t.m); + mMatrix = glm::recompose(mScale, mRotation, mTranslation, vec3(0,0,0), vec4(0,0,0,1)); mMatrixValid = true; } + + llassert(mMatrixValid); } void Node::makeTRSValid() { if (!mTRSValid && mMatrixValid) { - glh::matrix4f t(mMatrix.getF32ptr()); + vec3 skew; + vec4 perspective; + glm::decompose(mMatrix, mScale, mRotation, mTranslation, skew, perspective); - glh::vec4f p = t.get_column(3); - mTranslation.set_value(p.v[0], p.v[1], p.v[2]); - - mScale.set_value(t.get_column(0).length(), t.get_column(1).length(), t.get_column(2).length()); - mRotation.set_value(t); mTRSValid = true; } + + llassert(mTRSValid); } -void Node::setRotation(const glh::quaternionf& q) +void Node::setRotation(const quat& q) { makeTRSValid(); mRotation = q; mMatrixValid = false; } -void Node::setTranslation(const glh::vec3f& t) +void Node::setTranslation(const vec3& t) { makeTRSValid(); mTranslation = t; mMatrixValid = false; } -void Node::setScale(const glh::vec3f& s) +void Node::setScale(const vec3& s) { makeTRSValid(); mScale = s; mMatrixValid = false; } -const Node& Node::operator=(const tinygltf::Node& src) +void Node::serialize(object& dst) const +{ + write(mName, "name", dst); + write(mMatrix, "matrix", dst, glm::identity<mat4>()); + write(mRotation, "rotation", dst, glm::identity<quat>()); + write(mTranslation, "translation", dst, glm::vec3(0.f, 0.f, 0.f)); + write(mScale, "scale", dst, vec3(1.f,1.f,1.f)); + write(mChildren, "children", dst); + write(mMesh, "mesh", dst, INVALID_INDEX); + write(mSkin, "skin", dst, INVALID_INDEX); +} + +const Node& Node::operator=(const Value& src) { - F32* dstMatrix = mMatrix.getF32ptr(); + copy(src, "name", mName); + mMatrixValid = copy(src, "matrix", mMatrix); + copy(src, "rotation", mRotation); + copy(src, "translation", mTranslation); + copy(src, "scale", mScale); + copy(src, "children", mChildren); + copy(src, "mesh", mMesh); + copy(src, "skin", mSkin); + + if (!mMatrixValid) + { + mTRSValid = true; + } + + return *this; +} - if (src.matrix.size() == 16) +void Image::serialize(object& dst) const +{ + write(mUri, "uri", dst); + write(mMimeType, "mimeType", dst); + write(mBufferView, "bufferView", dst, INVALID_INDEX); + write(mName, "name", dst); + write(mWidth, "width", dst, -1); + write(mHeight, "height", dst, -1); + write(mComponent, "component", dst, -1); + write(mBits, "bits", dst, -1); + write(mPixelType, "pixelType", dst, -1); +} + +const Image& Image::operator=(const Value& src) +{ + copy(src, "uri", mUri); + copy(src, "mimeType", mMimeType); + copy(src, "bufferView", mBufferView); + copy(src, "name", mName); + copy(src, "width", mWidth); + copy(src, "height", mHeight); + copy(src, "component", mComponent); + copy(src, "bits", mBits); + copy(src, "pixelType", mPixelType); + + return *this; +} + +void Asset::update() +{ + F32 dt = gFrameTimeSeconds - mLastUpdateTime; + + if (dt > 0.f) { - // Node has a transformation matrix, just copy it - for (U32 i = 0; i < 16; ++i) + mLastUpdateTime = gFrameTimeSeconds; + if (mAnimations.size() > 0) { - dstMatrix[i] = (F32)src.matrix[i]; + static LLCachedControl<U32> anim_idx(gSavedSettings, "GLTFAnimationIndex", 0); + static LLCachedControl<F32> anim_speed(gSavedSettings, "GLTFAnimationSpeed", 1.f); + + U32 idx = llclamp(anim_idx(), 0U, mAnimations.size() - 1); + mAnimations[idx].update(*this, dt*anim_speed); } - mMatrixValid = true; + updateTransforms(); + + for (auto& skin : mSkins) + { + skin.uploadMatrixPalette(*this); + } } - else if (!src.rotation.empty() || !src.translation.empty() || !src.scale.empty()) +} + +bool Asset::prep() +{ + // check required extensions and fail if not supported + bool unsupported = false; + for (auto& extension : mExtensionsRequired) { - // node has rotation/translation/scale, convert to matrix - if (src.rotation.size() == 4) + if (ExtensionsSupported.find(extension) == ExtensionsSupported.end()) { - mRotation = glh::quaternionf((F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2], (F32)src.rotation[3]); + LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL; + unsupported = true; } + } - if (src.translation.size() == 3) + if (unsupported) + { + return false; + } + + // do buffers first as other resources depend on them + for (auto& buffer : mBuffers) + { + if (!buffer.prep(*this)) { - mTranslation = glh::vec3f((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]); + return false; } + } - glh::vec3f scale; - if (src.scale.size() == 3) + for (auto& image : mImages) + { + if (!image.prep(*this)) { - mScale = glh::vec3f((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]); + return false; } - else + } + + for (auto& mesh : mMeshes) + { + if (!mesh.prep(*this)) { - mScale.set_value(1.f, 1.f, 1.f); + return false; } + } - mTRSValid = true; + for (auto& animation : mAnimations) + { + if (!animation.prep(*this)) + { + return false; + } } - else + + for (auto& skin : mSkins) { - // node specifies no transformation, set to identity - mMatrix.setIdentity(); + if (!skin.prep(*this)) + { + return false; + } } - mChildren = src.children; - mMesh = src.mesh; - mSkin = src.skin; - mName = src.name; + return true; +} - return *this; +Asset::Asset(const Value& src) +{ + *this = src; } -void Asset::render(bool opaque, bool rigged) +bool Asset::load(std::string_view filename) { - if (rigged) - { - gGL.loadIdentity(); - } + mFilename = filename; + std::string ext = gDirUtilp->getExtension(mFilename); - for (auto& node : mNodes) + std::ifstream file(filename.data(), std::ios::binary); + if (file.is_open()) { - if (node.mSkin != INVALID_INDEX) + std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); + file.close(); + + if (ext == "gltf") { - if (rigged) - { - Skin& skin = mSkins[node.mSkin]; - skin.uploadMatrixPalette(*this, node); - } - else - { - //skip static nodes if we're rendering rigged - continue; - } + Value val = parse(str); + *this = val; + return prep(); + } + else if (ext == "glb") + { + return loadBinary(str); } - else if (rigged) + else { - // skip rigged nodes if we're not rendering rigged - continue; + LL_WARNS() << "Unsupported file type: " << ext << LL_ENDL; + return false; } + } + else + { + LL_WARNS() << "Failed to open file: " << filename << LL_ENDL; + return false; + } + + return false; +} +bool Asset::loadBinary(const std::string& data) +{ + // load from binary gltf + const U8* ptr = (const U8*)data.data(); + const U8* end = ptr + data.size(); - if (node.mMesh != INVALID_INDEX) + if (end - ptr < 12) + { + LL_WARNS("GLTF") << "GLB file too short" << LL_ENDL; + return false; + } + + U32 magic = *(U32*)ptr; + ptr += 4; + + if (magic != 0x46546C67) + { + LL_WARNS("GLTF") << "Invalid GLB magic" << LL_ENDL; + return false; + } + + U32 version = *(U32*)ptr; + ptr += 4; + + if (version != 2) + { + LL_WARNS("GLTF") << "Unsupported GLB version" << LL_ENDL; + return false; + } + + U32 length = *(U32*)ptr; + ptr += 4; + + if (length != data.size()) + { + LL_WARNS("GLTF") << "GLB length mismatch" << LL_ENDL; + return false; + } + + U32 chunkLength = *(U32*)ptr; + ptr += 4; + + if (end - ptr < chunkLength + 8) + { + LL_WARNS("GLTF") << "GLB chunk too short" << LL_ENDL; + return false; + } + + U32 chunkType = *(U32*)ptr; + ptr += 4; + + if (chunkType != 0x4E4F534A) + { + LL_WARNS("GLTF") << "Invalid GLB chunk type" << LL_ENDL; + return false; + } + + Value val = parse(std::string_view((const char*)ptr, chunkLength)); + *this = val; + + if (mBuffers.size() > 0 && mBuffers[0].mUri.empty()) + { + // load binary chunk + ptr += chunkLength; + + if (end - ptr < 8) { - Mesh& mesh = mMeshes[node.mMesh]; - for (auto& primitive : mesh.mPrimitives) - { - if (!rigged) - { - gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); - } - bool cull = true; - if (primitive.mMaterial != INVALID_INDEX) - { - Material& material = mMaterials[primitive.mMaterial]; + LL_WARNS("GLTF") << "GLB chunk too short" << LL_ENDL; + return false; + } - if ((material.mMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_BLEND) == opaque) - { - continue; - } - material.mMaterial->bind(); - cull = !material.mMaterial->mDoubleSided; - } - else - { - if (!opaque) - { - continue; - } - LLFetchedGLTFMaterial::sDefault.bind(); - } + chunkLength = *(U32*)ptr; + ptr += 4; - LLGLDisable cull_face(!cull ? GL_CULL_FACE : 0); + chunkType = *(U32*)ptr; + ptr += 4; - primitive.mVertexBuffer->setBuffer(); - if (primitive.mVertexBuffer->getNumIndices() > 0) - { - primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0); - } - else - { - primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts()); - } + if (chunkType != 0x004E4942) + { + LL_WARNS("GLTF") << "Invalid GLB chunk type" << LL_ENDL; + return false; + } - } + auto& buffer = mBuffers[0]; + + if (ptr + buffer.mByteLength <= end) + { + buffer.mData.resize(buffer.mByteLength); + memcpy(buffer.mData.data(), ptr, buffer.mByteLength); + ptr += buffer.mByteLength; + } + else + { + LL_WARNS("GLTF") << "Buffer too short" << LL_ENDL; + return false; } } -} -void Asset::renderOpaque() -{ - render(true); + return prep(); } -void Asset::renderTransparent() +const Asset& Asset::operator=(const Value& src) { - render(false); -} + if (src.is_object()) + { + const object& obj = src.as_object(); -void Asset::update() -{ - F32 dt = gFrameTimeSeconds - mLastUpdateTime; + const auto it = obj.find("asset"); - if (dt > 0.f) - { - mLastUpdateTime = gFrameTimeSeconds; - if (mAnimations.size() > 0) + if (it != obj.end()) { - static LLCachedControl<U32> anim_idx(gSavedSettings, "GLTFAnimationIndex", 0); - static LLCachedControl<F32> anim_speed(gSavedSettings, "GLTFAnimationSpeed", 1.f); + const Value& asset = it->value(); - U32 idx = llclamp(anim_idx(), 0U, mAnimations.size() - 1); - mAnimations[idx].update(*this, dt*anim_speed); + copy(asset, "version", mVersion); + copy(asset, "minVersion", mMinVersion); + copy(asset, "generator", mGenerator); + copy(asset, "copyright", mCopyright); + copy(asset, "extras", mExtras); } - updateTransforms(); + copy(obj, "scene", mScene); + copy(obj, "scenes", mScenes); + copy(obj, "nodes", mNodes); + copy(obj, "meshes", mMeshes); + copy(obj, "materials", mMaterials); + copy(obj, "buffers", mBuffers); + copy(obj, "bufferViews", mBufferViews); + copy(obj, "textures", mTextures); + copy(obj, "samplers", mSamplers); + copy(obj, "images", mImages); + copy(obj, "accessors", mAccessors); + copy(obj, "animations", mAnimations); + copy(obj, "skins", mSkins); + copy(obj, "extensionsUsed", mExtensionsUsed); + copy(obj, "extensionsRequired", mExtensionsRequired); } + + return *this; +} + +void Asset::serialize(object& dst) const +{ + static const std::string sGenerator = "Linden Lab GLTF Prototype v0.1"; + + dst["asset"] = object{}; + object& asset = dst["asset"].get_object(); + + write(mVersion, "version", asset); + write(mMinVersion, "minVersion", asset, std::string()); + write(sGenerator, "generator", asset); + write(mScene, "scene", dst, INVALID_INDEX); + write(mScenes, "scenes", dst); + write(mNodes, "nodes", dst); + write(mMeshes, "meshes", dst); + write(mMaterials, "materials", dst); + write(mBuffers, "buffers", dst); + write(mBufferViews, "bufferViews", dst); + write(mTextures, "textures", dst); + write(mSamplers, "samplers", dst); + write(mImages, "images", dst); + write(mAccessors, "accessors", dst); + write(mAnimations, "animations", dst); + write(mSkins, "skins", dst); + write(mExtensionsUsed, "extensionsUsed", dst); + write(mExtensionsRequired, "extensionsRequired", dst); } -void Asset::allocateGLResources(const std::string& filename, const tinygltf::Model& model) +bool Asset::save(const std::string& filename) { - // do images first as materials may depend on images + // get folder path + std::string folder = gDirUtilp->getDirName(filename); + + // save images for (auto& image : mImages) { - image.allocateGLResources(); + if (!image.save(*this, folder)) + { + return false; + } } - // do materials before meshes as meshes may depend on materials - for (U32 i = 0; i < mMaterials.size(); ++i) + // save buffers + // NOTE: save buffers after saving images as saving images + // may remove image data from buffers + for (auto& buffer : mBuffers) { - mMaterials[i].allocateGLResources(*this); - LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true); + if (!buffer.save(*this, folder)) + { + return false; + } } - for (auto& mesh : mMeshes) - { - mesh.allocateGLResources(*this); - } + // save .gltf + object obj; + serialize(obj); + std::string buffer = boost::json::serialize(obj, {}); + std::ofstream file(filename, std::ios::binary); + file.write(buffer.c_str(), buffer.size()); - for (auto& animation : mAnimations) + return true; +} + +void Asset::eraseBufferView(S32 bufferView) +{ + mBufferViews.erase(mBufferViews.begin() + bufferView); + + for (auto& accessor : mAccessors) { - animation.allocateGLResources(*this); + if (accessor.mBufferView > bufferView) + { + accessor.mBufferView--; + } } - for (auto& skin : mSkins) + for (auto& image : mImages) { - skin.allocateGLResources(*this); + if (image.mBufferView > bufferView) + { + image.mBufferView--; + } } + } -const Asset& Asset::operator=(const tinygltf::Model& src) +LLViewerFetchedTexture* fetch_texture(const LLUUID& id); + +bool Image::prep(Asset& asset) { - mScenes.resize(src.scenes.size()); - for (U32 i = 0; i < src.scenes.size(); ++i) - { - mScenes[i] = src.scenes[i]; + LLUUID id; + if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull()) + { // loaded from an asset, fetch the texture from the asset system + mTexture = fetch_texture(id); } - - mNodes.resize(src.nodes.size()); - for (U32 i = 0; i < src.nodes.size(); ++i) - { - mNodes[i] = src.nodes[i]; + else if (mUri.find("data:") == 0) + { // embedded in a data URI, load the texture from the URI + LL_WARNS() << "Data URIs not yet supported" << LL_ENDL; + return false; } + else if (mBufferView != INVALID_INDEX) + { // embedded in a buffer, load the texture from the buffer + BufferView& bufferView = asset.mBufferViews[mBufferView]; + Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; - mMeshes.resize(src.meshes.size()); - for (U32 i = 0; i < src.meshes.size(); ++i) - { - mMeshes[i] = src.meshes[i]; - } + U8* data = buffer.mData.data() + bufferView.mByteOffset; - mMaterials.resize(src.materials.size()); - for (U32 i = 0; i < src.materials.size(); ++i) - { - mMaterials[i] = src.materials[i]; + mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType); + + if (mTexture.isNull()) + { + LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL; + LL_WARNS("GLTF") << " image: " << mName << LL_ENDL; + LL_WARNS("GLTF") << " mimeType: " << mMimeType << LL_ENDL; + + return false; + } } + else if (!asset.mFilename.empty() && !mUri.empty()) + { // loaded locally and not embedded, load the texture as a local preview + std::string dir = gDirUtilp->getDirName(asset.mFilename); + std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri; + + LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file); + if (tracking_id.notNull()) + { + LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id); + mTexture = LLViewerTextureManager::getFetchedTexture(world_id); + } + else + { + LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL; + LL_WARNS("GLTF") << " image: " << mName << LL_ENDL; + LL_WARNS("GLTF") << " file: " << img_file << LL_ENDL; - mBuffers.resize(src.buffers.size()); - for (U32 i = 0; i < src.buffers.size(); ++i) + return false; + } + } + else { - mBuffers[i] = src.buffers[i]; + LL_WARNS("GLTF") << "Failed to load image: " << mName << LL_ENDL; + return false; } - mBufferViews.resize(src.bufferViews.size()); - for (U32 i = 0; i < src.bufferViews.size(); ++i) + return true; +} + + +void Image::clearData(Asset& asset) +{ + if (mBufferView != INVALID_INDEX) { - mBufferViews[i] = src.bufferViews[i]; + // remove data from buffer + BufferView& bufferView = asset.mBufferViews[mBufferView]; + Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + + buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength); + + asset.eraseBufferView(mBufferView); } - mTextures.resize(src.textures.size()); - for (U32 i = 0; i < src.textures.size(); ++i) + mBufferView = INVALID_INDEX; + mWidth = -1; + mHeight = -1; + mComponent = -1; + mBits = -1; + mPixelType = -1; + mMimeType = ""; +} + +bool Image::save(Asset& asset, const std::string& folder) +{ + // NOTE: this *MUST* be a lossless save + // Artists use this to save their work repeatedly, so + // adding any compression artifacts here will degrade + // images over time. + std::string name = mName; + std::string error; + const std::string& delim = gDirUtilp->getDirDelimiter(); + if (name.empty()) { - mTextures[i] = src.textures[i]; + S32 idx = this - asset.mImages.data(); + name = llformat("image_%d", idx); } - mSamplers.resize(src.samplers.size()); - for (U32 i = 0; i < src.samplers.size(); ++i) + if (mBufferView != INVALID_INDEX) { - mSamplers[i] = src.samplers[i]; - } + // we have the bytes of the original image, save that out in its + // original format + BufferView& bufferView = asset.mBufferViews[mBufferView]; + Buffer& buffer = asset.mBuffers[bufferView.mBuffer]; + + std::string extension; + + if (mMimeType == "image/jpeg") + { + extension = ".jpg"; + } + else if (mMimeType == "image/png") + { + extension = ".png"; + } + else + { + error = "Unknown mime type, saved as .bin"; + extension = ".bin"; + } + + std::string filename = folder + delim + name + extension; + + // set URI to non-j2c file for now, but later we'll want to reference the j2c hash + mUri = name + extension; - mImages.resize(src.images.size()); - for (U32 i = 0; i < src.images.size(); ++i) + std::ofstream file(filename, std::ios::binary); + file.write((const char*)buffer.mData.data() + bufferView.mByteOffset, bufferView.mByteLength); + } + else if (mTexture.notNull()) { - mImages[i] = src.images[i]; + auto bitmapmgr = LLLocalBitmapMgr::getInstance(); + if (bitmapmgr->isLocal(mTexture->getID())) + { + LLUUID tracking_id = bitmapmgr->getTrackingID(mTexture->getID()); + if (tracking_id.notNull()) + { // copy original file to destination folder + std::string source = bitmapmgr->getFilename(tracking_id); + if (gDirUtilp->fileExists(source)) + { + std::string filename = gDirUtilp->getBaseFileName(source); + std::string dest = folder + delim + filename; + + LLFile::copy(source, dest); + mUri = filename; + } + else + { + error = "File not found: " + source; + } + } + else + { + error = "Local image missing."; + } + } + else if (!mUri.empty()) + { + std::string from_dir = gDirUtilp->getDirName(asset.mFilename); + std::string base_filename = gDirUtilp->getBaseFileName(mUri); + std::string filename = from_dir + delim + base_filename; + if (gDirUtilp->fileExists(filename)) + { + std::string dest = folder + delim + base_filename; + LLFile::copy(filename, dest); + mUri = base_filename; + } + else + { + error = "Original image file not found: " + filename; + } + } + else + { + error = "Image is not a local image and has no uri, cannot save."; + } } - mAccessors.resize(src.accessors.size()); - for (U32 i = 0; i < src.accessors.size(); ++i) + if (!error.empty()) { - mAccessors[i] = src.accessors[i]; + LL_WARNS("GLTF") << "Failed to save " << name << ": " << error << LL_ENDL; + return false; } - mAnimations.resize(src.animations.size()); - for (U32 i = 0; i < src.animations.size(); ++i) + clearData(asset); + + return true; +} + +void Material::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 +{ + if (mTextureTransform.mPresent && mTextureTransform.mTexCoord != INVALID_INDEX) { - mAnimations[i] = src.animations[i]; + return mTextureTransform.mTexCoord; } + return mTexCoord; +} - mSkins.resize(src.skins.size()); - for (U32 i = 0; i < src.skins.size(); ++i) +bool Material::isMultiUV() const +{ + return mPbrMetallicRoughness.mBaseColorTexture.getTexCoord() != 0 || + mPbrMetallicRoughness.mMetallicRoughnessTexture.getTexCoord() != 0 || + mNormalTexture.getTexCoord() != 0 || + mOcclusionTexture.getTexCoord() != 0 || + mEmissiveTexture.getTexCoord() != 0; +} + +const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src) +{ + if (src.is_object()) { - mSkins[i] = src.skins[i]; + copy(src, "index", mIndex); + copy(src, "texCoord", mTexCoord); + copy_extensions(src, "KHR_texture_transform", &mTextureTransform); } return *this; } -const Material& Material::operator=(const tinygltf::Material& src) +bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const { - mName = src.name; - return *this; + return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord; +} + +bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const +{ + return !(*this == rhs); } -void Material::allocateGLResources(Asset& asset) +void Material::OcclusionTextureInfo::serialize(object& dst) const { - // allocate material - mMaterial = new LLFetchedGLTFMaterial(); + TextureInfo::serialize(dst); + write(mStrength, "strength", dst, 1.f); } -const Mesh& Mesh::operator=(const tinygltf::Mesh& src) +const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src) { - mPrimitives.resize(src.primitives.size()); - for (U32 i = 0; i < src.primitives.size(); ++i) + TextureInfo::operator=(src); + + if (src.is_object()) { - mPrimitives[i] = src.primitives[i]; + copy(src, "strength", mStrength); } - mWeights = src.weights; - mName = src.name; - return *this; } -void Mesh::allocateGLResources(Asset& asset) +void Material::NormalTextureInfo::serialize(object& dst) const { - for (auto& primitive : mPrimitives) + TextureInfo::serialize(dst); + write(mScale, "scale", dst, 1.f); +} + +const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src) +{ + TextureInfo::operator=(src); + if (src.is_object()) { - primitive.allocateGLResources(asset); + copy(src, "index", mIndex); + copy(src, "texCoord", mTexCoord); + copy(src, "scale", mScale); } + + return *this; } -const Scene& Scene::operator=(const tinygltf::Scene& src) +const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const Value& src) { - mNodes = src.nodes; - mName = src.name; + if (src.is_object()) + { + copy(src, "baseColorFactor", mBaseColorFactor); + copy(src, "baseColorTexture", mBaseColorTexture); + copy(src, "metallicFactor", mMetallicFactor); + copy(src, "roughnessFactor", mRoughnessFactor); + copy(src, "metallicRoughnessTexture", mMetallicRoughnessTexture); + } return *this; } -const Texture& Texture::operator=(const tinygltf::Texture& src) +void Material::PbrMetallicRoughness::serialize(object& dst) const { - mSampler = src.sampler; - mSource = src.source; - mName = src.name; + write(mBaseColorFactor, "baseColorFactor", dst, vec4(1.f, 1.f, 1.f, 1.f)); + write(mBaseColorTexture, "baseColorTexture", dst); + write(mMetallicFactor, "metallicFactor", dst, 1.f); + write(mRoughnessFactor, "roughnessFactor", dst, 1.f); + write(mMetallicRoughnessTexture, "metallicRoughnessTexture", dst); +} - return *this; +bool Material::PbrMetallicRoughness::operator==(const Material::PbrMetallicRoughness& rhs) const +{ + return mBaseColorFactor == rhs.mBaseColorFactor && + mBaseColorTexture == rhs.mBaseColorTexture && + mMetallicFactor == rhs.mMetallicFactor && + mRoughnessFactor == rhs.mRoughnessFactor && + mMetallicRoughnessTexture == rhs.mMetallicRoughnessTexture; } -const Sampler& Sampler::operator=(const tinygltf::Sampler& src) +bool Material::PbrMetallicRoughness::operator!=(const Material::PbrMetallicRoughness& rhs) const { - mMagFilter = src.magFilter; - mMinFilter = src.minFilter; - mWrapS = src.wrapS; - mWrapT = src.wrapT; - mName = src.name; + return !(*this == rhs); +} +const Material::Unlit& Material::Unlit::operator=(const Value& src) +{ + mPresent = true; return *this; } -void Skin::uploadMatrixPalette(Asset& asset, Node& node) +void Material::Unlit::serialize(object& dst) const { - // prepare matrix palette + // no members and object has already been created, nothing to do +} - // modelview will be applied by the shader, so assume matrix palette is in asset space - std::vector<glh::matrix4f> t_mp; +void TextureTransform::getPacked(F32* 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; +} - t_mp.resize(mJoints.size()); - for (U32 i = 0; i < mJoints.size(); ++i) +const TextureTransform& TextureTransform::operator=(const Value& src) +{ + mPresent = true; + if (src.is_object()) { - Node& joint = asset.mNodes[mJoints[i]]; + copy(src, "offset", mOffset); + copy(src, "rotation", mRotation); + copy(src, "scale", mScale); + copy(src, "texCoord", mTexCoord); + } - //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr()); - //t_mp[i] = t_mp[i] * mInverseBindMatricesData[i]; + return *this; +} + +void TextureTransform::serialize(object& dst) const +{ + write(mOffset, "offset", dst, vec2(0.f, 0.f)); + write(mRotation, "rotation", dst, 0.f); + write(mScale, "scale", dst, vec2(1.f, 1.f)); + write(mTexCoord, "texCoord", dst, -1); +} - //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr()); - //t_mp[i] = mInverseBindMatricesData[i] * t_mp[i]; - t_mp[i].set_value(joint.mRenderMatrix.getF32ptr()); - t_mp[i] = t_mp[i] * mInverseBindMatricesData[i]; +void Material::serialize(object& dst) const +{ + write(mName, "name", dst); + write(mEmissiveFactor, "emissiveFactor", dst, vec3(0.f, 0.f, 0.f)); + write(mPbrMetallicRoughness, "pbrMetallicRoughness", dst); + write(mNormalTexture, "normalTexture", dst); + write(mOcclusionTexture, "occlusionTexture", dst); + write(mEmissiveTexture, "emissiveTexture", dst); + write(mAlphaMode, "alphaMode", dst, Material::AlphaMode::OPAQUE); + write(mAlphaCutoff, "alphaCutoff", dst, 0.5f); + write(mDoubleSided, "doubleSided", dst, false); + write_extensions(dst, &mUnlit, "KHR_materials_unlit"); +} +const Material& Material::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "name", mName); + copy(src, "emissiveFactor", mEmissiveFactor); + copy(src, "pbrMetallicRoughness", mPbrMetallicRoughness); + copy(src, "normalTexture", mNormalTexture); + copy(src, "occlusionTexture", mOcclusionTexture); + copy(src, "emissiveTexture", mEmissiveTexture); + copy(src, "alphaMode", mAlphaMode); + copy(src, "alphaCutoff", mAlphaCutoff); + copy(src, "doubleSided", mDoubleSided); + copy_extensions(src, + "KHR_materials_unlit", &mUnlit ); } + return *this; +} - std::vector<F32> glmp; - glmp.resize(mJoints.size() * 12); +void Mesh::serialize(object& dst) const +{ + write(mPrimitives, "primitives", dst); + write(mWeights, "weights", dst); + write(mName, "name", dst); +} - F32* mp = glmp.data(); +const Mesh& Mesh::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "primitives", mPrimitives); + copy(src, "weights", mWeights); + copy(src, "name", mName); + } - for (U32 i = 0; i < mJoints.size(); ++i) + return *this; +} + +bool Mesh::prep(Asset& asset) +{ + for (auto& primitive : mPrimitives) { - F32* m = (F32*)t_mp[i].m; + if (!primitive.prep(asset)) + { + return false; + } + } + + return true; +} + +void Scene::serialize(object& dst) const +{ + write(mNodes, "nodes", dst); + write(mName, "name", dst); +} - U32 idx = i * 12; +const Scene& Scene::operator=(const Value& src) +{ + copy(src, "nodes", mNodes); + copy(src, "name", mName); - mp[idx + 0] = m[0]; - mp[idx + 1] = m[1]; - mp[idx + 2] = m[2]; - mp[idx + 3] = m[12]; + return *this; +} - mp[idx + 4] = m[4]; - mp[idx + 5] = m[5]; - mp[idx + 6] = m[6]; - mp[idx + 7] = m[13]; +void Texture::serialize(object& dst) const +{ + write(mSampler, "sampler", dst, INVALID_INDEX); + write(mSource, "source", dst, INVALID_INDEX); + write(mName, "name", dst); +} - mp[idx + 8] = m[8]; - mp[idx + 9] = m[9]; - mp[idx + 10] = m[10]; - mp[idx + 11] = m[14]; +const Texture& Texture::operator=(const Value& src) +{ + if (src.is_object()) + { + copy(src, "sampler", mSampler); + copy(src, "source", mSource); + copy(src, "name", mName); } - LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX, - (U32)mJoints.size(), - GL_FALSE, - (GLfloat*)glmp.data()); + return *this; +} + +void Sampler::serialize(object& dst) const +{ + write(mMagFilter, "magFilter", dst, LINEAR); + write(mMinFilter, "minFilter", dst, LINEAR_MIPMAP_LINEAR); + write(mWrapS, "wrapS", dst, REPEAT); + write(mWrapT, "wrapT", dst, REPEAT); + write(mName, "name", dst); } +const Sampler& Sampler::operator=(const Value& src) +{ + copy(src, "magFilter", mMagFilter); + copy(src, "minFilter", mMinFilter); + copy(src, "wrapS", mWrapS); + copy(src, "wrapT", mWrapT); + copy(src, "name", mName); + + return *this; +} + + diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h index 5ceac74a8a..ea3f7d480a 100644 --- a/indra/newview/gltf/asset.h +++ b/indra/newview/gltf/asset.h @@ -28,13 +28,20 @@ #include "llvertexbuffer.h" #include "llvolumeoctree.h" -#include "../lltinygltfhelper.h" #include "accessor.h" #include "primitive.h" #include "animation.h" +#include "boost/json.hpp" +#include "common.h" +#include "../llviewertexture.h" extern F32SecondsImplicit gFrameTimeSeconds; +// wingdi defines OPAQUE, which conflicts with our enum +#if defined(OPAQUE) +#undef OPAQUE +#endif + // LL GLTF Implementation namespace LL { @@ -42,18 +49,117 @@ namespace LL { class Asset; + class Extension + { + public: + // true if this extension is present in the gltf file + // otherwise false + bool mPresent = false; + }; + + class TextureTransform : public Extension // KHR_texture_transform implementation + { + public: + vec2 mOffset = vec2(0.f, 0.f); + F32 mRotation = 0.f; + 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; + + const TextureTransform& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + class Material { public: - // use LLFetchedGLTFMaterial for now, but eventually we'll want to use - // a more flexible GLTF material implementation instead of the fixed packing - // version we use for sharable GLTF material assets - LLPointer<LLFetchedGLTFMaterial> mMaterial; + + class Unlit : public Extension // KHR_materials_unlit implementation + { + public: + const Unlit& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + }; + + enum class AlphaMode + { + OPAQUE, + MASK, + 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 + { + public: + 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 Value& src); + void serialize(boost::json::object& dst) const; + }; + + + PbrMetallicRoughness mPbrMetallicRoughness; + NormalTextureInfo mNormalTexture; + OcclusionTextureInfo mOcclusionTexture; + TextureInfo mEmissiveTexture; + std::string mName; + vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f); + AlphaMode mAlphaMode = AlphaMode::OPAQUE; + F32 mAlphaCutoff = 0.5f; + bool mDoubleSided = false; + Unlit mUnlit; - const Material& operator=(const tinygltf::Material& src); + bool isMultiUV() const; - void allocateGLResources(Asset& asset); + const Material& operator=(const Value& src); + void serialize(boost::json::object& dst) const; }; class Mesh @@ -63,22 +169,23 @@ namespace LL std::vector<double> mWeights; 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); + bool prep(Asset& asset); }; 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; @@ -96,14 +203,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(); @@ -113,30 +221,35 @@ 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 { public: + ~Skin(); + S32 mInverseBindMatrices = INVALID_INDEX; S32 mSkeleton = INVALID_INDEX; + + U32 mUBO = 0; 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); + bool prep(Asset& asset); + void uploadMatrixPalette(Asset& asset); - const Skin& operator=(const tinygltf::Skin& src); + const Skin& operator=(const Value& src); + void serialize(boost::json::object& dst) const; }; class Scene @@ -145,10 +258,11 @@ namespace LL std::vector<S32> mNodes; 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 @@ -158,19 +272,21 @@ namespace LL S32 mSource = INVALID_INDEX; 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 @@ -179,38 +295,39 @@ namespace LL std::string mName; std::string mUri; std::string mMimeType; - std::vector<U8> mData; - S32 mWidth; - S32 mHeight; - S32 mComponent; - S32 mBits; + + S32 mBufferView = INVALID_INDEX; + + 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; - - return *this; - } - - void allocateGLResources() - { - // allocate texture + const Image& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + + // save image to disk + // may remove image data from bufferviews and convert to + // file uri if necessary + bool save(Asset& asset, const std::string& filename); + + // erase the buffer view associated with this image + // free any associated GLTF resources + // preserve only uri and name + void clearData(Asset& asset); - } + bool prep(Asset& asset); }; // C++ representation of a GLTF Asset - class Asset : public LLRefCount + class Asset { public: + + static const std::string minVersion_default; std::vector<Scene> mScenes; std::vector<Node> mNodes; std::vector<Mesh> mMeshes; @@ -223,12 +340,28 @@ namespace LL std::vector<Accessor> mAccessors; std::vector<Animation> mAnimations; std::vector<Skin> mSkins; + std::vector<std::string> mExtensionsUsed; + std::vector<std::string> mExtensionsRequired; + + std::string mVersion; + std::string mGenerator; + std::string mMinVersion; + std::string mCopyright; + + S32 mScene = INVALID_INDEX; + Value mExtras; + + U32 mPendingBuffers = 0; + + // local file this asset was loaded from (if any) + std::string mFilename; // 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); + + // prepare for first time use + bool prep(); // Called periodically (typically once per frame) // Any ongoing work (such as animations) should be handled here @@ -241,11 +374,7 @@ namespace LL void updateTransforms(); // update node render transforms - void updateRenderTransforms(const LLMatrix4a& modelview); - - void render(bool opaque, bool rigged = false); - void renderOpaque(); - void renderTransparent(); + void updateRenderTransforms(const mat4& modelview); // 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 @@ -257,8 +386,37 @@ namespace LL S32* primitive_hitp = nullptr // return the index of the primitive that was hit ); - const Asset& operator=(const tinygltf::Model& src); + Asset() = default; + Asset(const Value& src); + // load from given file + // accepts .gltf and .glb files + // Any existing data will be lost + // returns result of prep() on success + bool load(std::string_view filename); + + // load .glb contents from memory + // data - binary contents of .glb file + // returns result of prep() on success + bool loadBinary(const std::string& data); + + const Asset& operator=(const Value& src); + void serialize(boost::json::object& dst) const; + + // save the asset to the given .gltf file + // saves images and bins alongside the gltf file + bool save(const std::string& filename); + + // remove the bufferview at the given index + // updates all bufferview indices in this Asset as needed + void eraseBufferView(S32 bufferView); + + // return true if this Asset has been loaded as a local preview + // Local previews may be uploaded or exported to disk + bool isLocalPreview() { return !mFilename.empty(); } }; + + Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode); + std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode); } } diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h index a448f7a484..40f9448aaf 100644 --- a/indra/newview/gltf/buffer_util.h +++ b/indra/newview/gltf/buffer_util.h @@ -36,55 +36,60 @@ #define LL_FUNCSIG __PRETTY_FUNCTION__ #endif +#include "accessor.h" + namespace LL { namespace GLTF { + + using string_view = boost::json::string_view; + // copy one Scalar from src to dst template<class S, class T> - static void copyScalar(S* src, T& dst) + inline void copyScalar(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one vec2 from src to dst template<class S, class T> - static void copyVec2(S* src, T& dst) + inline void copyVec2(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one vec3 from src to dst template<class S, class T> - static void copyVec3(S* src, T& dst) + inline void copyVec3(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } // copy one vec4 from src to dst template<class S, class T> - static void copyVec4(S* src, T& dst) + inline void copyVec4(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } - // copy one vec2 from src to dst + // copy one mat2 from src to dst template<class S, class T> - static void copyMat2(S* src, T& dst) + inline void copyMat2(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } - // copy one vec3 from src to dst + // copy one mat3 from src to dst template<class S, class T> - static void copyMat3(S* src, T& dst) + inline void copyMat3(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } - // copy one vec4 from src to dst + // copy one mat4 from src to dst template<class S, class T> - static void copyMat4(S* src, T& dst) + inline void copyMat4(S* src, T& dst) { LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL; } @@ -93,135 +98,138 @@ 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, U64>(U16* src, U64& dst) + { + U16* data = (U16*)&dst; + data[0] = src[0]; + data[1] = src[1]; + data[2] = src[2]; + data[3] = src[3]; + } + + template<> + 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 +241,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 +253,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 +265,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 +277,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 +289,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 +301,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 +312,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,54 +357,701 @@ 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]; const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset; - if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_FLOAT) + switch (accessor.mComponentType) { - LL::GLTF::copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); + case Accessor::ComponentType::FLOAT: + copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride); + break; + case Accessor::ComponentType::UNSIGNED_INT: + copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); + break; + case Accessor::ComponentType::SHORT: + copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride); + break; + case Accessor::ComponentType::UNSIGNED_SHORT: + copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); + break; + case Accessor::ComponentType::BYTE: + copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride); + break; + case Accessor::ComponentType::UNSIGNED_BYTE: + copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride); + break; + default: + LL_ERRS("GLTF") << "Invalid component type" << LL_ENDL; + break; } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + } + + // copy data from accessor to vector + template<class T> + 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()) { - LL::GLTF::copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride); + const boost::json::object& obj = src.as_object(); + for (const auto& [key, value] : obj) + { + copy<T>(value, dst[key]); + } + return true; } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + 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) { - LL::GLTF::copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride); + Value v; + if (write<T>(value, v)) + { + obj[key] = v; + } + else + { + return false; + } } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) + dst = obj; + return true; + } + + // to/from array + template<typename T> + inline bool copy(const Value& src, std::vector<T>& dst) + { + if (src.is_array()) { - LL::GLTF::copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride); + 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; } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_SHORT) + + return false; + } + + template<typename T> + inline bool write(const std::vector<T>& src, Value& dst) + { + boost::json::array arr; + for (const T& t : src) { - LL::GLTF::copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride); + Value v; + if (write(t, v)) + { + arr.push_back(v); + } + else + { + return false; + } } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_BYTE) + 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()) { - LL::GLTF::copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride); + return copy(it->value(), dst); } - else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) + 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)) { - LL::GLTF::copy(asset, accessor, (const F64*)src, dst, bufferView.mByteStride); + dst.erase(member); + return false; } - else + return true; + } + + + // to/from extension + + // for internal use only, use copy_extensions instead + template<typename T> + inline bool _copy_extension(const boost::json::object& extensions, std::string_view member, T* dst) + { + if (extensions.contains(member)) { - LL_ERRS("GLTF") << "Unsupported component type" << LL_ENDL; + return copy(extensions.at(member), *dst); } + + return false; } - // copy data from accessor to vector - template<class T> - static void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst) + // Copy all extensions from src.extensions to provided destinations + // Usage: + // copy_extensions(src, + // "KHR_materials_unlit", &mUnlit, + // "KHR_materials_pbrSpecularGlossiness", &mPbrSpecularGlossiness); + // returns true if any of the extensions are copied + template<class... Types> + inline bool copy_extensions(const boost::json::value& src, Types... args) { - dst.resize(accessor.mCount); - LLStrider<T> strider = dst.data(); - copy(asset, accessor, strider); + // extract the extensions object (don't assume it exists and verify that it is an object) + if (src.is_object()) + { + boost::json::object obj = src.get_object(); + if (obj.contains("extensions")) + { + const boost::json::value& extensions = obj.at("extensions"); + if (extensions.is_object()) + { + const boost::json::object& ext_obj = extensions.as_object(); + bool success = false; + // copy each extension, return true if any of them succeed, do not short circuit on success + U32 count = sizeof...(args); + for (U32 i = 0; i < count; i += 2) + { + if (_copy_extension(ext_obj, args...)) + { + success = true; + } + } + return success; + } + } + } + + return false; } + + // internal use aonly, use write_extensions instead + template<typename T> + inline bool _write_extension(boost::json::object& extensions, const T* src, string_view member) + { + if (src->mPresent) + { + Value v; + if (write(*src, v)) + { + extensions[member] = v; + return true; + } + } + return false; + } + + // Write all extensions to dst.extensions + // Usage: + // write_extensions(dst, + // mUnlit, "KHR_materials_unlit", + // mPbrSpecularGlossiness, "KHR_materials_pbrSpecularGlossiness"); + // returns true if any of the extensions are written + template<class... Types> + inline bool write_extensions(boost::json::object& dst, Types... args) + { + bool success = false; + + boost::json::object extensions; + U32 count = sizeof...(args) - 1; + + for (U32 i = 0; i < count; i += 2) + { + if (_write_extension(extensions, args...)) + { + success = true; + } + } + + if (success) + { + dst["extensions"] = extensions; + } + + return success; + } + + // 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; + } + + // Accessor::ComponentType + template<> + inline bool copy(const Value& src, Accessor::ComponentType& dst) + { + if (src.is_int64()) + { + dst = (Accessor::ComponentType)src.get_int64(); + return true; + } + return false; + } + + template<> + inline bool write(const Accessor::ComponentType& src, Value& dst) + { + dst = (S32)src; + return true; + } + + //Primitive::Mode + template<> + inline bool copy(const Value& src, Primitive::Mode& dst) + { + if (src.is_int64()) + { + dst = (Primitive::Mode)src.get_int64(); + return true; + } + return false; + } + + template<> + inline bool write(const Primitive::Mode& src, Value& dst) + { + dst = (S32)src; + return true; + } + + // 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) + { + vec4 v; + std::error_code ec; + + v.x = arr[0].to_number<F32>(ec); if (ec) return false; + v.y = arr[1].to_number<F32>(ec); if (ec) return false; + v.z = arr[2].to_number<F32>(ec); if (ec) return false; + v.w = arr[3].to_number<F32>(ec); if (ec) return false; + + dst = v; + + 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) + { + std::error_code ec; + dst.x = arr[0].to_number<F32>(ec); if (ec) return false; + dst.y = arr[1].to_number<F32>(ec); if (ec) return false; + dst.z = arr[2].to_number<F32>(ec); if (ec) return false; + dst.w = arr[3].to_number<F32>(ec); if (ec) return false; + + 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) + { + std::error_code ec; + vec3 t; + t.x = arr[0].to_number<F32>(ec); if (ec) return false; + t.y = arr[1].to_number<F32>(ec); if (ec) return false; + t.z = arr[2].to_number<F32>(ec); if (ec) return false; + + dst = t; + 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; + } + + // vec2 + template<> + inline bool copy(const Value& src, vec2& dst) + { + if (src.is_array()) + { + const boost::json::array& arr = src.as_array(); + if (arr.size() == 2) + { + std::error_code ec; + vec2 t; + t.x = arr[0].to_number<F32>(ec); if (ec) return false; + t.y = arr[1].to_number<F32>(ec); if (ec) return false; + + dst = t; + return true; + } + } + return false; + } + + template<> + inline bool write(const vec2& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.as_array(); + arr.resize(2); + arr[0] = src.x; + arr[1] = src.y; + + 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) + { + std::error_code ec; + F32 t = src.to_number<F32>(ec); if (ec) return false; + dst = t; + return true; + } + + 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) + { + std::error_code ec; + F64 t = src.to_number<F64>(ec); if (ec) return false; + dst = t; + return true; + } + + 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) + { + std::error_code ec; + p[i] = arr[i].to_number<F32>(ec); + if (ec) + { + return false; + } + } + + dst = t; + return true; + } + } + + return false; + } + + template<> + inline bool write(const mat4& src, Value& dst) + { + dst = boost::json::array(); + boost::json::array& arr = dst.get_array(); + arr.resize(16); + const F32* p = glm::value_ptr(src); + for (U32 i = 0; i < 16; ++i) + { + arr[i] = p[i]; + } + return true; + } + + // Material::AlphaMode + template<> + inline bool copy(const Value& src, Material::AlphaMode& dst) + { + if (src.is_string()) + { + dst = gltf_alpha_mode_to_enum(src.get_string().c_str()); + return true; + } + return true; + } + + template<> + inline bool write(const Material::AlphaMode& src, Value& dst) + { + dst = enum_to_gltf_alpha_mode(src); + return true; + } + + // + // ======================================================================================================== + } } + + + diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h new file mode 100644 index 0000000000..4f660d7cfc --- /dev/null +++ b/indra/newview/gltf/common.h @@ -0,0 +1,83 @@ +#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" +#include <boost/json.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; + class Material; + class Mesh; + class Node; + class Scene; + class Texture; + class Sampler; + class Image; + class Animation; + class Skin; + class Camera; + class Light; + class Primitive; + class Accessor; + class BufferView; + class Buffer; + } +} + diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp index f1f0cf48f6..2280c7004e 100644 --- a/indra/newview/gltf/primitive.cpp +++ b/indra/newview/gltf/primitive.cpp @@ -28,12 +28,295 @@ #include "asset.h" #include "buffer_util.h" +#include "../llviewershadermgr.h" + +#include "mikktspace/mikktspace.hh" + +#include "meshoptimizer/meshoptimizer.h" -#include "../lltinygltfhelper.h" using namespace LL::GLTF; +using namespace boost::json; + -void Primitive::allocateGLResources(Asset& asset) +// Mesh data useful for Mikktspace tangent generation (and flat normal generation) +struct MikktMesh +{ + std::vector<LLVector3> p; //positions + std::vector<LLVector3> n; //normals + std::vector<LLVector4> t; //tangents + std::vector<LLVector2> tc0; //texcoords 0 + std::vector<LLVector2> tc1; //texcoords 1 + std::vector<LLColor4U> c; //colors + std::vector<LLVector4> w; //weights + std::vector<U64> j; //joints + + // initialize from src primitive and make an unrolled triangle list + // returns false if the Primitive cannot be converted to a triangle list + bool copy(const Primitive* prim) + { + bool indexed = !prim->mIndexArray.empty(); + size_t vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size(); + + size_t triangle_count = 0; + + if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP || + prim->mMode == Primitive::Mode::TRIANGLE_FAN) + { + triangle_count = vert_count - 2; + } + else if (prim->mMode == Primitive::Mode::TRIANGLES) + { + triangle_count = vert_count / 3; + } + else + { + LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32)prim->mMode << LL_ENDL; + return false; + } + + vert_count = triangle_count * 3; + llassert(vert_count <= size_t(U32_MAX)); // triangle_count will also naturally be under the limit + + p.resize(vert_count); + n.resize(vert_count); + tc0.resize(vert_count); + c.resize(vert_count); + + bool has_normals = !prim->mNormals.empty(); + if (has_normals) + { + n.resize(vert_count); + } + bool has_tangents = !prim->mTangents.empty(); + if (has_tangents) + { + t.resize(vert_count); + } + + bool rigged = !prim->mWeights.empty(); + if (rigged) + { + w.resize(vert_count); + j.resize(vert_count); + } + + bool multi_uv = !prim->mTexCoords1.empty(); + if (multi_uv) + { + tc1.resize(vert_count); + } + + for (U32 tri_idx = 0; tri_idx < U32(triangle_count); ++tri_idx) + { + U32 idx[3]; + + if (prim->mMode == Primitive::Mode::TRIANGLES) + { + idx[0] = tri_idx * 3; + idx[1] = tri_idx * 3 + 1; + idx[2] = tri_idx * 3 + 2; + } + else if (prim->mMode == Primitive::Mode::TRIANGLE_STRIP) + { + idx[0] = tri_idx; + idx[1] = tri_idx + 1; + idx[2] = tri_idx + 2; + + if (tri_idx % 2 != 0) + { + std::swap(idx[1], idx[2]); + } + } + else if (prim->mMode == Primitive::Mode::TRIANGLE_FAN) + { + idx[0] = 0; + idx[1] = tri_idx + 1; + idx[2] = tri_idx + 2; + } + + if (indexed) + { + idx[0] = prim->mIndexArray[idx[0]]; + idx[1] = prim->mIndexArray[idx[1]]; + idx[2] = prim->mIndexArray[idx[2]]; + } + + for (U32 v = 0; v < 3; ++v) + { + U32 i = tri_idx * 3 + v; + p[i].set(prim->mPositions[idx[v]].getF32ptr()); + tc0[i].set(prim->mTexCoords0[idx[v]]); + c[i] = prim->mColors[idx[v]]; + + if (multi_uv) + { + tc1[i].set(prim->mTexCoords1[idx[v]]); + } + + if (has_normals) + { + n[i].set(prim->mNormals[idx[v]].getF32ptr()); + } + + if (rigged) + { + w[i].set(prim->mWeights[idx[v]].getF32ptr()); + j[i] = prim->mJoints[idx[v]]; + } + } + } + + return true; + } + + void genNormals() + { + size_t tri_count = p.size() / 3; + for (size_t i = 0; i < tri_count; ++i) + { + LLVector3 v0 = p[i * 3]; + LLVector3 v1 = p[i * 3 + 1]; + LLVector3 v2 = p[i * 3 + 2]; + + LLVector3 normal = (v1 - v0) % (v2 - v0); + normal.normalize(); + + n[i * 3] = normal; + n[i * 3 + 1] = normal; + n[i * 3 + 2] = normal; + } + } + + void genTangents() + { + t.resize(p.size()); + mikk::Mikktspace ctx(*this); + ctx.genTangSpace(); + } + + // write to target primitive as an indexed triangle list + // Only modifies runtime data, does not modify the original GLTF data + void write(Primitive* prim) const + { + //re-weld + std::vector<meshopt_Stream> mos = + { + { &p[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &n[0], sizeof(LLVector3), sizeof(LLVector3) }, + { &t[0], sizeof(LLVector4), sizeof(LLVector4) }, + { &tc0[0], sizeof(LLVector2), sizeof(LLVector2) }, + { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) } + }; + + if (!w.empty()) + { + mos.push_back({ &w[0], sizeof(LLVector4), sizeof(LLVector4) }); + mos.push_back({ &j[0], sizeof(U64), sizeof(U64) }); + } + + if (!tc1.empty()) + { + mos.push_back({ &tc1[0], sizeof(LLVector2), sizeof(LLVector2) }); + } + + std::vector<U32> remap; + remap.resize(p.size()); + + size_t stream_count = mos.size(); + + size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos.data(), stream_count); + + prim->mTexCoords0.resize(vert_count); + prim->mNormals.resize(vert_count); + prim->mTangents.resize(vert_count); + prim->mPositions.resize(vert_count); + prim->mColors.resize(vert_count); + if (!w.empty()) + { + prim->mWeights.resize(vert_count); + prim->mJoints.resize(vert_count); + } + if (!tc1.empty()) + { + prim->mTexCoords1.resize(vert_count); + } + + prim->mIndexArray.resize(remap.size()); + + for (int i = 0; i < remap.size(); ++i) + { + U32 src_idx = i; + U32 dst_idx = remap[i]; + + prim->mIndexArray[i] = dst_idx; + + prim->mPositions[dst_idx].load3(p[src_idx].mV); + prim->mNormals[dst_idx].load3(n[src_idx].mV); + prim->mTexCoords0[dst_idx] = tc0[src_idx]; + prim->mTangents[dst_idx].loadua(t[src_idx].mV); + prim->mColors[dst_idx] = c[src_idx]; + + if (!w.empty()) + { + prim->mWeights[dst_idx].loadua(w[src_idx].mV); + prim->mJoints[dst_idx] = j[src_idx]; + } + + if (!tc1.empty()) + { + prim->mTexCoords1[dst_idx] = tc1[src_idx]; + } + } + + prim->mGLMode = LLRender::TRIANGLES; + } + + uint32_t GetNumFaces() + { + return uint32_t(p.size()/3); + } + + uint32_t GetNumVerticesOfFace(const uint32_t face_num) + { + return 3; + } + + mikk::float3 GetPosition(const uint32_t face_num, const uint32_t vert_num) + { + F32* v = p[face_num * 3 + vert_num].mV; + return mikk::float3(v); + } + + mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num) + { + F32* uv = tc0[face_num * 3 + vert_num].mV; + return mikk::float3(uv[0], 1.f-uv[1], 1.0f); + } + + mikk::float3 GetNormal(const uint32_t face_num, const uint32_t vert_num) + { + F32* normal = n[face_num * 3 + vert_num].mV; + return mikk::float3(normal); + } + + void SetTangentSpace(const uint32_t face_num, const uint32_t vert_num, mikk::float3 T, bool orientation) + { + S32 i = face_num * 3 + vert_num; + t[i].set(T.x, T.y, T.z, orientation ? 1.0f : -1.0f); + } +}; + + +static void vertical_flip(std::vector<LLVector2>& texcoords) +{ + for (auto& tc : texcoords) + { + tc[1] = 1.f - tc[1]; + } +} + +bool Primitive::prep(Asset& asset) { // allocate vertex buffer // We diverge from the intent of the GLTF format here to work with our existing render pipeline @@ -66,7 +349,11 @@ void Primitive::allocateGLResources(Asset& asset) } else if (attribName == "TEXCOORD_0") { - copy(asset, accessor, mTexCoords); + copy(asset, accessor, mTexCoords0); + } + else if (attribName == "TEXCOORD_1") + { + copy(asset, accessor, mTexCoords1); } else if (attribName == "JOINTS_0") { @@ -83,41 +370,35 @@ void Primitive::allocateGLResources(Asset& asset) { Accessor& accessor = asset.mAccessors[mIndices]; copy(asset, accessor, mIndexArray); + + for (auto& idx : mIndexArray) + { + if (idx >= mPositions.size()) + { + LL_WARNS("GLTF") << "Invalid index array" << LL_ENDL; + return false; + } + } } - U32 mask = ATTRIBUTE_MASK; + U32 mask = LLVertexBuffer::MAP_VERTEX; if (!mWeights.empty()) { mask |= LLVertexBuffer::MAP_WEIGHT4; + mask |= LLVertexBuffer::MAP_JOINT; } - mVertexBuffer = new LLVertexBuffer(mask); - 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()); - - if (!mIndexArray.empty()) + if (mTexCoords0.empty()) { - mVertexBuffer->setIndexData(mIndexArray.data()); + mTexCoords0.resize(mPositions.size()); } - if (mTexCoords.empty()) - { - mTexCoords.resize(mPositions.size()); - } + mask |= LLVertexBuffer::MAP_TEXCOORD0; - // flip texcoord y, upload, then flip back (keep the off-spec data in vram only) - for (auto& tc : mTexCoords) + if (!mTexCoords1.empty()) { - tc[1] = 1.f - tc[1]; - } - mVertexBuffer->setTexCoordData(mTexCoords.data()); - - for (auto& tc : mTexCoords) - { - tc[1] = 1.f - tc[1]; + mask |= LLVertexBuffer::MAP_TEXCOORD1; } if (mColors.empty()) @@ -125,54 +406,169 @@ void Primitive::allocateGLResources(Asset& asset) mColors.resize(mPositions.size(), LLColor4U::white); } + mShaderVariant = 0; + + // TODO: support colorless vertex buffers + mask |= LLVertexBuffer::MAP_COLOR; + + bool unlit = false; + // bake material basecolor into color array 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)); } - } - mVertexBuffer->setColorData(mColors.data()); + if (material.mUnlit.mPresent) + { // material uses KHR_materials_unlit + mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT; + unlit = true; + } + + if (material.isMultiUV()) + { + mShaderVariant |= LLGLSLShader::GLTFVariant::MULTI_UV; + } + } - if (mNormals.empty()) + if (mNormals.empty() && !unlit) { - mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0)); + mTangents.clear(); + + if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP) + { //no normals and no surfaces, this primitive is unlit + mTangents.clear(); + mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT; + unlit = true; + } + else + { + // unroll into non-indexed array of flat shaded triangles + MikktMesh data; + if (!data.copy(this)) + { + return false; + } + + data.genNormals(); + data.genTangents(); + data.write(this); + } + } + + if (mTangents.empty() && !unlit) + { // NOTE: must be done last because tangent generation rewrites the other arrays + // adapted from usage of Mikktspace in llvolume.cpp + if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP) + { + // for points and lines, just make sure tangent is perpendicular to normal + mTangents.resize(mNormals.size()); + LLVector4a up(0.f, 0.f, 1.f, 0.f); + LLVector4a left(1.f, 0.f, 0.f, 0.f); + for (U32 i = 0; i < mNormals.size(); ++i) + { + if (fabsf(mNormals[i].getF32ptr()[2]) < 0.999f) + { + mTangents[i] = up.cross3(mNormals[i]); + } + else + { + mTangents[i] = left.cross3(mNormals[i]); + } + + mTangents[i].getF32ptr()[3] = 1.f; + } + } + else + { + MikktMesh data; + if (!data.copy(this)) + { + return false; + } + + data.genTangents(); + data.write(this); + } } - mVertexBuffer->setNormalData(mNormals.data()); + if (!mNormals.empty()) + { + mask |= LLVertexBuffer::MAP_NORMAL; + } - if (mTangents.empty()) + if (!mTangents.empty()) { - // TODO: generate tangents if needed - mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1)); + mask |= LLVertexBuffer::MAP_TANGENT; + } + + if (LLGLSLShader::sCurBoundShaderPtr == nullptr) + { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer + gDebugProgram.bind(); } - mVertexBuffer->setTangentData(mTangents.data()); + mVertexBuffer = new LLVertexBuffer(mask); + // 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()); + + if (!mNormals.empty()) + { + mVertexBuffer->setNormalData(mNormals.data()); + } + if (!mTangents.empty()) + { + mVertexBuffer->setTangentData(mTangents.data()); + } if (!mWeights.empty()) { - std::vector<LLVector4a> weight_data; - weight_data.resize(mWeights.size()); + mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED; + mVertexBuffer->setWeight4Data(mWeights.data()); + mVertexBuffer->setJointData(mJoints.data()); + } - F32 max_weight = 1.f - FLT_EPSILON*100.f; - LLVector4a maxw(max_weight, max_weight, max_weight, max_weight); - for (U32 i = 0; i < mWeights.size(); ++i) - { - LLVector4a& w = weight_data[i]; - w.setMin(mWeights[i], maxw); - w.add(mJoints[i]); - }; + // flip texcoord y, upload, then flip back (keep the off-spec data in vram only) + vertical_flip(mTexCoords0); + mVertexBuffer->setTexCoord0Data(mTexCoords0.data()); + vertical_flip(mTexCoords0); - mVertexBuffer->setWeight4Data(weight_data.data()); + if (!mTexCoords1.empty()) + { + vertical_flip(mTexCoords1); + mVertexBuffer->setTexCoord1Data(mTexCoords1.data()); + 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) + { + mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + } + } + + return true; } void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2) @@ -218,7 +614,7 @@ void Primitive::createOctree() F32 scaler = 0.25f; - if (mMode == TINYGLTF_MODE_TRIANGLES) + if (mMode == Mode::TRIANGLES) { const U32 num_triangles = mVertexBuffer->getNumIndices() / 3; // Initialize all the triangles we need @@ -242,7 +638,7 @@ void Primitive::createOctree() mOctree->insert(tri); } } - else if (mMode == TINYGLTF_MODE_TRIANGLE_STRIP) + else if (mMode == Mode::TRIANGLE_STRIP) { const U32 num_triangles = mVertexBuffer->getNumIndices() - 2; // Initialize all the triangles we need @@ -266,7 +662,7 @@ void Primitive::createOctree() mOctree->insert(tri); } } - else if (mMode == TINYGLTF_MODE_TRIANGLE_FAN) + else if (mMode == Mode::TRIANGLE_FAN) { const U32 num_triangles = mVertexBuffer->getNumIndices() - 2; // Initialize all the triangles we need @@ -290,10 +686,10 @@ void Primitive::createOctree() mOctree->insert(tri); } } - else if (mMode == TINYGLTF_MODE_POINTS || - mMode == TINYGLTF_MODE_LINE || - mMode == TINYGLTF_MODE_LINE_LOOP || - mMode == TINYGLTF_MODE_LINE_STRIP) + else if (mMode == Mode::POINTS || + mMode == Mode::LINES || + mMode == Mode::LINE_LOOP || + mMode == Mode::LINE_STRIP) { // nothing to do, no volume... maybe add some collision geometry around these primitive types? } @@ -327,13 +723,13 @@ const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, //create a proxy LLVolumeFace for the raycast LLVolumeFace face; face.mPositions = mPositions.data(); - face.mTexCoords = mTexCoords.data(); + face.mTexCoords = mTexCoords0.data(); face.mNormals = mNormals.data(); face.mTangents = mTangents.data(); face.mIndices = nullptr; // unreferenced - face.mNumIndices = (S32)mIndexArray.size(); - face.mNumVertices = (S32)mPositions.size(); + face.mNumIndices = S32(mIndexArray.size()); + face.mNumVertices = S32(mPositions.size()); LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out); intersect.traverse(mOctree); @@ -351,50 +747,48 @@ Primitive::~Primitive() mOctree = nullptr; } - -const Primitive& Primitive::operator=(const tinygltf::Primitive& src) +LLRender::eGeomModes gltf_mode_to_gl_mode(Primitive::Mode mode) { - // load material - mMaterial = src.material; - - // load mode - mMode = src.mode; - - // load indices - mIndices = src.indices; - - // load attributes - for (auto& it : src.attributes) - { - 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; + switch (mode) + { + case Primitive::Mode::POINTS: + return LLRender::POINTS; + case Primitive::Mode::LINES: + return LLRender::LINES; + case Primitive::Mode::LINE_LOOP: + return LLRender::LINE_LOOP; + case Primitive::Mode::LINE_STRIP: + return LLRender::LINE_STRIP; + case Primitive::Mode::TRIANGLES: + return LLRender::TRIANGLES; + case Primitive::Mode::TRIANGLE_STRIP: + return LLRender::TRIANGLE_STRIP; + case Primitive::Mode::TRIANGLE_FAN: + return LLRender::TRIANGLE_FAN; default: - mGLMode = GL_TRIANGLES; + return LLRender::TRIANGLES; } +} + +void Primitive::serialize(boost::json::object& dst) const +{ + write(mMaterial, "material", dst, -1); + write(mMode, "mode", dst, Primitive::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; } + diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h index 09ab3d9ead..7cc05cf831 100644 --- a/indra/newview/gltf/primitive.h +++ b/indra/newview/gltf/primitive.h @@ -28,35 +28,42 @@ #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 = - LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_TANGENT | - LLVertexBuffer::MAP_COLOR; - class Primitive { public: + enum class Mode : U8 + { + POINTS, + LINES, + LINE_LOOP, + LINE_STRIP, + TRIANGLES, + TRIANGLE_STRIP, + TRIANGLE_FAN + }; + ~Primitive(); // GPU copy of mesh data LLPointer<LLVertexBuffer> mVertexBuffer; - // CPU copy of mesh data - std::vector<LLVector2> mTexCoords; + // CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code + std::vector<LLVector2> mTexCoords0; + std::vector<LLVector2> mTexCoords1; std::vector<LLVector4a> mNormals; std::vector<LLVector4a> mTangents; std::vector<LLVector4a> mPositions; - std::vector<LLVector4a> mJoints; + std::vector<U64> mJoints; std::vector<LLVector4a> mWeights; std::vector<LLColor4U> mColors; std::vector<U32> mIndexArray; @@ -66,10 +73,14 @@ namespace LL std::vector<LLVolumeTriangle> mOctreeTriangles; S32 mMaterial = -1; - U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles - U32 mGLMode = LLRender::TRIANGLES; + Mode mMode = Mode::TRIANGLES; // default to triangles + LLRender::eGeomModes mGLMode = LLRender::TRIANGLES; // for use with LLRender S32 mIndices = -1; - std::unordered_map<std::string, int> mAttributes; + + // shader variant according to LLGLSLShader::GLTFVariant flags + U8 mShaderVariant = 0; + + 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,9 +96,10 @@ namespace LL LLVector4a* tangent = NULL // return the surface tangent at the intersection point ); - const Primitive& operator=(const tinygltf::Primitive& src); + void serialize(boost::json::object& obj) const; + const Primitive& operator=(const Value& src); - void allocateGLResources(Asset& asset); + bool prep(Asset& asset); }; } } diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ce9e9e10f2..e01aec0497 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -39,7 +39,14 @@ #include "gltf/asset.h" #include "pipeline.h" #include "llviewershadermgr.h" +#include "llviewertexturelist.h" +#include "llimagej2c.h" +#include "llfloaterperms.h" +#include "llagentbenefits.h" +#include "llfilesystem.h" +#include "boost/json.hpp" +#define GLTF_SIM_SUPPORT 1 using namespace LL; @@ -66,40 +73,255 @@ void GLTFSceneManager::load() } }, LLFilePicker::FFLOAD_GLTF, - true); + false); } else { - LLNotificationsUtil::add("GLTFPreviewSelection"); + LLNotificationsUtil::add("GLTFOpenSelection"); } } -void GLTFSceneManager::load(const std::string& filename) +void GLTFSceneManager::saveAs() { - tinygltf::Model model; - LLTinyGLTFHelper::loadModel(filename, model); - - LLPointer<Asset> asset = new Asset(); - *asset = model; + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + if (obj && obj->mGLTFAsset) + { + LLFilePickerReplyThread::startPicker( + [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter) + { + if (LLAppViewer::instance()->quitRequested()) + { + return; + } + if (filenames.size() > 0) + { + GLTFSceneManager::instance().save(filenames[0]); + } + }, + LLFilePicker::FFSAVE_GLTF, + "scene.gltf"); + } + else + { + LLNotificationsUtil::add("GLTFSaveSelection"); + } +} - gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions - asset->allocateGLResources(filename, model); - asset->updateTransforms(); +void GLTFSceneManager::uploadSelection() +{ + if (mUploadingAsset) + { // upload already in progress + LLNotificationsUtil::add("GLTFUploadInProgress"); + return; + } - // hang the asset off the currently selected object, or off of the avatar if no object is selected 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; - if (obj) - { // assign to self avatar - obj->mGLTFAsset = asset; + GLTF::Asset& asset = *mUploadingAsset; + + for (auto& image : asset.mImages) + { + if (image.mTexture.notNull()) + { + mPendingImageUploads++; + + LLPointer<LLImageRaw> raw; + + if (image.mBufferView != INVALID_INDEX) + { + BufferView& view = asset.mBufferViews[image.mBufferView]; + Buffer& buffer = asset.mBuffers[view.mBuffer]; + + raw = LLViewerTextureManager::getRawImageFromMemory(buffer.mData.data() + view.mByteOffset, view.mByteLength, image.mMimeType); + + image.clearData(asset); + } + else + { + raw = image.mTexture->getCachedRawImage(); + } + + if (raw.notNull()) + { + 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); + } + } + } + + // 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; + + llassert(data.size() <= size_t(S32_MAX)); + cache.write((const U8 *) data.data(), S32(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"); + } +} - if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end()) +void GLTFSceneManager::save(const std::string& filename) +{ + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + if (obj && obj->mGLTFAsset) + { + Asset* asset = obj->mGLTFAsset.get(); + if (!asset->save(filename)) { - mObjects.push_back(obj); + LLNotificationsUtil::add("GLTFSaveFailed"); } } } +void GLTFSceneManager::load(const std::string& filename) +{ + std::shared_ptr<Asset> asset = std::make_shared<Asset>(); + + if (asset->load(filename)) + { + gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions + asset->updateTransforms(); + + // hang the asset off the currently selected object, or off of the avatar if no object is selected + LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject(); + + 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); + } + } + } + else + { + LLNotificationsUtil::add("GLTFLoadFailed"); + } +} + GLTFSceneManager::~GLTFSceneManager() { mObjects.clear(); @@ -115,6 +337,104 @@ 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) + { + obj->mGLTFAsset->mPendingBuffers--; + + + if (obj->mGLTFAsset->mPendingBuffers == 0) + { + if (obj->mGLTFAsset->prep()) + { + 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 prepare GLTF asset: " << id << LL_ENDL; + obj->mGLTFAsset = nullptr; + } + } + } + } + } + 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; + S32 file_size = file.getSize(); + data.resize(file_size); + file.read((U8*)data.data(), file_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) @@ -126,22 +446,127 @@ void GLTFSceneManager::update() continue; } - Asset* asset = mObjects[i]->mGLTFAsset; + mObjects[i]->mGLTFAsset->update(); + } - asset->update(); + // process pending uploads + if (mUploadingAsset && !mGLTFUploadPending) + { + if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0) + { + boost::json::object obj; + mUploadingAsset->serialize(obj); + std::string buffer = boost::json::serialize(obj, {}); + 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; + llassert(buffer.size() <= size_t(S32_MAX)); + cache.write((const U8 *) buffer.c_str(), S32(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) +void GLTFSceneManager::render(bool opaque, bool rigged, bool unlit) { - // 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 + U8 variant = 0; + if (rigged) + { + variant |= LLGLSLShader::GLTFVariant::RIGGED; + } + if (!opaque) + { + variant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND; + } + if (unlit) + { + variant |= LLGLSLShader::GLTFVariant::UNLIT; + } + + render(variant); +} + +void GLTFSceneManager::render(U8 variant) +{ + // 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 + // into buckets by material and shader. + + // HACK -- implicitly render multi-uv variant + if (!(variant & LLGLSLShader::GLTFVariant::MULTI_UV)) + { + 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) { if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr) @@ -151,8 +576,7 @@ void GLTFSceneManager::render(bool opaque, bool rigged) continue; } - Asset* asset = mObjects[i]->mGLTFAsset; - + Asset* asset = mObjects[i]->mGLTFAsset.get(); gGL.pushMatrix(); LLMatrix4a mat = mObjects[i]->getGLTFAssetToAgentTransform(); @@ -162,13 +586,194 @@ void GLTFSceneManager::render(bool opaque, bool rigged) matMul(mat, modelview, modelview); - asset->updateRenderTransforms(modelview); - asset->render(opaque, rigged); + mat4 mdv = glm::make_mat4(modelview.getF32ptr()); + asset->updateRenderTransforms(mdv); + + 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(); } } +void GLTFSceneManager::render(Asset& asset, U8 variant) +{ + bool opaque = !(variant & LLGLSLShader::GLTFVariant::ALPHA_BLEND); + bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED; + + if (opaque) + { + gGLTFPBRMetallicRoughnessProgram.bind(variant); + } + else + { // alpha shaders need all the shadow map setup etc + gPipeline.bindDeferredShader(gGLTFPBRMetallicRoughnessProgram.mGLTFVariants[variant]); + } + + for (auto& node : asset.mNodes) + { + if (node.mSkin != INVALID_INDEX) + { + if (rigged) + { + Skin& skin = asset.mSkins[node.mSkin]; + glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_JOINTS, skin.mUBO); + } + } + + if (node.mMesh != INVALID_INDEX) + { + Mesh& mesh = asset.mMeshes[node.mMesh]; + for (auto& primitive : mesh.mPrimitives) + { + if (primitive.mShaderVariant != variant) + { + continue; + } + + if (!rigged) + { + gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix)); + } + bool cull = true; + if (primitive.mMaterial != INVALID_INDEX) + { + Material& material = asset.mMaterials[primitive.mMaterial]; + bind(asset, material); + + cull = !material.mDoubleSided; + } + else + { + LLFetchedGLTFMaterial::sDefault.bind(); + } + + LLGLDisable cull_face(!cull ? GL_CULL_FACE : 0); + + primitive.mVertexBuffer->setBuffer(); + if (primitive.mVertexBuffer->getNumIndices() > 0) + { + primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0); + } + else + { + primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts()); + } + } + } + } +} + +static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback) +{ + if (info.mIndex != INVALID_INDEX) + { + Texture& texture = asset.mTextures[info.mIndex]; + + LLViewerTexture* tex = asset.mImages[texture.mSource].mTexture; + if (tex) + { + tex->addTextureStats(2048.f * 2048.f); + S32 channel = LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex); + + 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 + } + 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 + { + LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback); + } + } + 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; + + 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()); + + 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()); + } +} + LLMatrix4a inverse(const LLMatrix4a& mat) { glh::matrix4f m((F32*)mat.mMatrix); @@ -298,7 +903,7 @@ LLDrawable* GLTFSceneManager::lineSegmentIntersect(const LLVector4a& start, cons } // temporary debug -- always double check objects that have GLTF scenes hanging off of them even if the ray doesn't intersect the object bounds - if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset, start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent)) + if (lineSegmentIntersect((LLVOVolume*) mObjects[i].get(), mObjects[i]->mGLTFAsset.get(), start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent)) { local_end = position; if (intersection) @@ -330,12 +935,16 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) // get raycast in asset space LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform(); - LLVector4a start; - LLVector4a end; + vec4 start; + vec4 end; - agent_to_asset.affineTransform(gDebugRaycastStart, start); - agent_to_asset.affineTransform(gDebugRaycastEnd, end); + LLVector4a t; + agent_to_asset.affineTransform(gDebugRaycastStart, t); + start = glm::make_vec4(t.getF32ptr()); + agent_to_asset.affineTransform(gDebugRaycastEnd, t); + end = glm::make_vec4(t.getF32ptr()); + start.w = end.w = 1.0; for (auto& node : asset->mNodes) { @@ -343,7 +952,7 @@ void renderAssetDebug(LLViewerObject* obj, Asset* asset) if (node.mMesh != INVALID_INDEX) { - gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix); + gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix)); // draw bounding box of mesh primitives if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES)) @@ -361,24 +970,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); } } @@ -418,18 +1027,18 @@ void GLTFSceneManager::renderDebug() continue; } - LLMatrix4a mat = obj->getGLTFAssetToAgentTransform(); + mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr()); - LLMatrix4a modelview; - modelview.loadu(gGLModelView); + mat4 modelview = glm::make_mat4(gGLModelView); - matMul(mat, modelview, modelview); - Asset* asset = obj->mGLTFAsset; + modelview = modelview * mat; + + Asset* asset = obj->mGLTFAsset.get(); for (auto& node : asset->mNodes) { - matMul(node.mAssetMatrix, modelview, node.mRenderMatrix); + node.mRenderMatrix = modelview * node.mAssetMatrix; } } @@ -440,14 +1049,7 @@ void GLTFSceneManager::renderDebug() continue; } - Asset* asset = obj->mGLTFAsset; - - LLMatrix4a mat = obj->getGLTFAssetToAgentTransform(); - - LLMatrix4a modelview; - modelview.loadu(gGLModelView); - - matMul(mat, modelview, modelview); + Asset* asset = obj->mGLTFAsset.get(); renderAssetDebug(obj, asset); } @@ -470,21 +1072,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; + 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); @@ -514,7 +1115,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(); @@ -538,7 +1141,7 @@ void GLTFSceneManager::renderDebug() if (drawable) { gGL.pushMatrix(); - Asset* asset = drawable->getVObj()->mGLTFAsset; + Asset* asset = drawable->getVObj()->mGLTFAsset.get(); Node* node = &asset->mNodes[node_hit]; Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit]; @@ -547,8 +1150,7 @@ void GLTFSceneManager::renderDebug() gGL.color3f(1, 0, 1); drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f)); - gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix); - + gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix)); auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0); @@ -562,3 +1164,5 @@ void GLTFSceneManager::renderDebug() gDebugProgram.unbind(); } + + diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h index 34b9b46df5..7da413e8b2 100644 --- a/indra/newview/gltfscenemanager.h +++ b/indra/newview/gltfscenemanager.h @@ -28,6 +28,10 @@ #include "llsingleton.h" #include "llviewerobject.h" +#include "gltf/common.h" + +class LLVOVolume; +class LLDrawable; namespace LL { @@ -40,8 +44,22 @@ namespace LL void load(); // open filepicker to choose asset void load(const std::string& filename); // load asset from filename + void saveAs(); // open filepicker and choose file to save selected asset to + void save(const std::string& filename); // save selected asset to filename (suitable for use in external programs) + void uploadSelection(); // decompose selected asset and upload to simulator + void update(); - void render(bool opaque, bool rigged = false); + void render(bool opaque, bool rigged = false, bool unlit = false); + + // render the given variant of all assets + // variant - bitmask according to LLGLSLShader::GLTFVariant flags + void render(U8 variant); + + void render(LL::GLTF::Asset& asset, U8 variant); + + // bind the given material for rendering + void bind(LL::GLTF::Asset& asset, LL::GLTF::Material& material); + void renderOpaque(); void renderAlpha(); @@ -57,12 +75,25 @@ namespace LL LLVector4a* normal, // return the surface normal at the intersection point LLVector4a* tangent); // return the surface tangent at the intersection point - bool lineSegmentIntersect(LLVOVolume* obj, GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32* face_hitp, S32* primitive_hitp, + bool lineSegmentIntersect(LLVOVolume* obj, LL::GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32* face_hitp, S32* primitive_hitp, LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); 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; + + U32 mJointUBO = 0; }; } diff --git a/indra/newview/installers/windows/FILES_ARE_UNICODE_UTF-16LE.txt b/indra/newview/installers/windows/FILES_ARE_UNICODE_UTF-16LE.txt index 185c0180fb..30f9349111 100644 --- a/indra/newview/installers/windows/FILES_ARE_UNICODE_UTF-16LE.txt +++ b/indra/newview/installers/windows/FILES_ARE_UNICODE_UTF-16LE.txt @@ -1,6 +1,6 @@ -The language files in this directory are Unicode (Little-Endian) format, also known as UTF-16 LE.
-
-This is the format required for NSIS Unicode. See http://www.scratchpaper.com/ for details.
-
-James Cook
-September 2008
+The language files in this directory are Unicode (Little-Endian) format, also known as UTF-16 LE. + +This is the format required for NSIS Unicode. See http://www.scratchpaper.com/ for details. + +James Cook +September 2008 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index aa79a43f2e..3b3345e8b2 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5195,6 +5195,11 @@ void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp) } } +void LLAppViewer::postToMainCoro(const LL::WorkQueue::Work& work) +{ + gMainloopWork.post(work); +} + void LLAppViewer::idleNameCache() { // Neither old nor new name cache can function before agent has a region diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 4245e3da97..a9fcd82ba6 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -226,6 +226,9 @@ public: void updateNameLookupUrl(const LLViewerRegion* regionp); + // post given work to the "mainloop" work queue for handling on the main thread + void postToMainCoro(const LL::WorkQueue::Work& work); + protected: virtual bool initWindow(); // Initialize the viewer's window. virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp index ec9cf2c8b7..b0befa62e6 100644 --- a/indra/newview/llavatarrenderinfoaccountant.cpp +++ b/indra/newview/llavatarrenderinfoaccountant.cpp @@ -236,8 +236,6 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U !avatar->isControlAvatar() && // Not part of an animated object avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region { - avatar->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date - LLSD info = LLSD::emptyMap(); U32 avatar_complexity = avatar->getVisualComplexity(); if (avatar_complexity > 0) diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 92be1bdf93..93fea899f3 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -93,7 +93,7 @@ S32 LLDrawPoolAlpha::getNumPostDeferredPasses() } // set some common parameters on the given shader to prepare for alpha rendering -static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool deferredEnvironment, F32 water_sign) +static void prepare_alpha_shader(LLGLSLShader* shader, bool deferredEnvironment, F32 water_sign) { static LLCachedControl<F32> displayGamma(gSavedSettings, "RenderDeferredDisplayGamma"); F32 gamma = displayGamma; @@ -132,15 +132,11 @@ static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool d { shader->setMinimumAlpha(MINIMUM_ALPHA); } - if (textureGamma) - { - shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); - } //also prepare rigged variant if (shader->mRiggedVariant && shader->mRiggedVariant != shader) { - prepare_alpha_shader(shader->mRiggedVariant, textureGamma, deferredEnvironment, water_sign); + prepare_alpha_shader(shader->mRiggedVariant, deferredEnvironment, water_sign); } } @@ -171,36 +167,36 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) llassert(LLPipeline::sRenderDeferred); emissive_shader = &gDeferredEmissiveProgram; - prepare_alpha_shader(emissive_shader, true, false, water_sign); + prepare_alpha_shader(emissive_shader, false, water_sign); pbr_emissive_shader = &gPBRGlowProgram; - prepare_alpha_shader(pbr_emissive_shader, true, false, water_sign); + prepare_alpha_shader(pbr_emissive_shader, false, water_sign); fullbright_shader = (LLPipeline::sImpostorRender) ? &gDeferredFullbrightAlphaMaskProgram : (LLPipeline::sRenderingHUDs) ? &gHUDFullbrightAlphaMaskAlphaProgram : &gDeferredFullbrightAlphaMaskAlphaProgram; - prepare_alpha_shader(fullbright_shader, true, true, water_sign); + prepare_alpha_shader(fullbright_shader, true, water_sign); simple_shader = (LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram : (LLPipeline::sRenderingHUDs) ? &gHUDAlphaProgram : &gDeferredAlphaProgram; - prepare_alpha_shader(simple_shader, false, true, water_sign); //prime simple shader (loads shadow relevant uniforms) + prepare_alpha_shader(simple_shader, true, water_sign); //prime simple shader (loads shadow relevant uniforms) LLGLSLShader* materialShader = gDeferredMaterialProgram; for (int i = 0; i < LLMaterial::SHADER_COUNT*2; ++i) { - prepare_alpha_shader(&materialShader[i], false, true, water_sign); + prepare_alpha_shader(&materialShader[i], true, water_sign); } pbr_shader = (LLPipeline::sRenderingHUDs) ? &gHUDPBRAlphaProgram : &gDeferredPBRAlphaProgram; - prepare_alpha_shader(pbr_shader, false, true, water_sign); + prepare_alpha_shader(pbr_shader, true, water_sign); // explicitly unbind here so render loop doesn't make assumptions about the last shader // already being setup for rendering @@ -263,11 +259,10 @@ void LLDrawPoolAlpha::forwardRender(bool rigged) if (rigged) { // draw GLTF scene to depth buffer before rigged alpha - gPipeline.bindDeferredShader(gDeferredPBRAlphaProgram); LL::GLTFSceneManager::instance().render(false, false); - - gPipeline.bindDeferredShader(*gDeferredPBRAlphaProgram.mRiggedVariant); LL::GLTFSceneManager::instance().render(false, true); + LL::GLTFSceneManager::instance().render(false, false, true); + LL::GLTFSceneManager::instance().render(false, true, true); } // If the face is more than 90% transparent, then don't update the Depth buffer for Dof diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 25bce7bced..9afc705d3e 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -555,7 +555,7 @@ void LLDrawPoolAvatar::beginDeferredImpostor() sVertexProgram = &gDeferredImpostorProgram; specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP); - normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL); + normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::NORMAL_MAP); sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); sVertexProgram->bind(); sVertexProgram->setMinimumAlpha(0.01f); @@ -566,7 +566,7 @@ void LLDrawPoolAvatar::endDeferredImpostor() LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR sShaderLevel = mShaderLevel; - sVertexProgram->disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL); + sVertexProgram->disableTexture(LLViewerShaderMgr::NORMAL_MAP); sVertexProgram->disableTexture(LLViewerShaderMgr::SPECULAR_MAP); sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); gPipeline.unbindDeferredShader(*sVertexProgram); diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 62afd05f86..055f99d764 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -699,6 +699,8 @@ void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id, F32 void LLBumpImageList::updateImages() { + llassert(LLCoros::on_main_thread_main_coro()); // This code is not thread safe + for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); ) { LLViewerTexture* image = iter->second; diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp index 47b99c7a14..5eb10fe335 100644 --- a/indra/newview/lldrawpoolpbropaque.cpp +++ b/indra/newview/lldrawpoolpbropaque.cpp @@ -54,11 +54,10 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass) { llassert(!LLPipeline::sRenderingHUDs); - gDeferredPBROpaqueProgram.bind(); - LL::GLTFSceneManager::instance().renderOpaque(); - pushGLTFBatches(mRenderType); + gDeferredPBROpaqueProgram.bind(); + pushGLTFBatches(mRenderType); gDeferredPBROpaqueProgram.bind(true); LL::GLTFSceneManager::instance().render(true, true); diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp index 2b0ae260dc..836a90adab 100644 --- a/indra/newview/lldrawpoolsimple.cpp +++ b/indra/newview/lldrawpoolsimple.cpp @@ -36,43 +36,12 @@ #include "llspatialpartition.h" #include "llviewershadermgr.h" #include "llrender.h" +#include "gltfscenemanager.h" static LLTrace::BlockTimerStatHandle FTM_RENDER_SIMPLE_DEFERRED("Deferred Simple"); static LLTrace::BlockTimerStatHandle FTM_RENDER_GRASS_DEFERRED("Deferred Grass"); -static void setup_simple_shader(LLGLSLShader* shader) -{ - shader->bind(); -} - -static void setup_glow_shader(LLGLSLShader* shader) -{ - setup_simple_shader(shader); - if (LLPipeline::sRenderDeferred && !LLPipeline::sRenderingHUDs) - { - shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f); - } - else - { - shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.f); - } -} - -static void setup_fullbright_shader(LLGLSLShader* shader) -{ - setup_glow_shader(shader); - - S32 channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP); - if (channel > -1) - { - gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap); - } - - shader->uniform1f(LLViewerShaderMgr::FULLBRIGHT, 1.f); -} - - void LLDrawPoolGlow::renderPostDeferred(S32 pass) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; @@ -89,12 +58,12 @@ void LLDrawPoolGlow::renderPostDeferred(S32 pass) gGL.setColorMask(false, true); //first pass -- static objects - setup_glow_shader(shader); + shader->bind(); pushBatches(LLRenderPass::PASS_GLOW, true, true); // second pass -- rigged objects shader = shader->mRiggedVariant; - setup_glow_shader(shader); + shader->bind(); pushRiggedBatches(LLRenderPass::PASS_GLOW_RIGGED, true, true); gGL.setColorMask(true, false); @@ -133,11 +102,11 @@ void LLDrawPoolSimple::renderDeferred(S32 pass) LLGLDisable blend(GL_BLEND); //render static - setup_simple_shader(&gDeferredDiffuseProgram); + gDeferredDiffuseProgram.bind(); pushBatches(LLRenderPass::PASS_SIMPLE, true, true); //render rigged - setup_simple_shader(gDeferredDiffuseProgram.mRiggedVariant); + gDeferredDiffuseProgram.bind(true); pushRiggedBatches(LLRenderPass::PASS_SIMPLE_RIGGED, true, true); } @@ -150,11 +119,11 @@ void LLDrawPoolAlphaMask::renderDeferred(S32 pass) LLGLSLShader* shader = &gDeferredDiffuseAlphaMaskProgram; //render static - setup_simple_shader(shader); + shader->bind(); pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, true, true); //render rigged - setup_simple_shader(shader->mRiggedVariant); + shader->bind(true); pushRiggedMaskBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, true, true); } @@ -201,13 +170,13 @@ void LLDrawPoolFullbright::renderPostDeferred(S32 pass) gGL.setSceneBlendType(LLRender::BT_ALPHA); // render static - setup_fullbright_shader(shader); + shader->bind(); pushBatches(LLRenderPass::PASS_FULLBRIGHT, true, true); if (!LLPipeline::sRenderingHUDs) { // render rigged - setup_fullbright_shader(shader->mRiggedVariant); + shader->bind(true); pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_RIGGED, true, true); } } @@ -216,6 +185,10 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_FULLBRIGHT); + // render unrigged unlit GLTF + LL::GLTFSceneManager::instance().render(true, false, true); + LL::GLTFSceneManager::instance().render(true, true, true); + LLGLSLShader* shader = nullptr; if (LLPipeline::sRenderingHUDs) { @@ -229,13 +202,13 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass) LLGLDisable blend(GL_BLEND); // render static - setup_fullbright_shader(shader); + shader->bind(); pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, true, true); if (!LLPipeline::sRenderingHUDs) { // render rigged - setup_fullbright_shader(shader->mRiggedVariant); + shader->bind(true); pushRiggedMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, true, true); } } diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 8acaa79bc0..afc5cc9d4e 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -202,7 +202,7 @@ void LLDrawPoolTerrain::drawLoop() void LLDrawPoolTerrain::renderFullShader() { - const bool use_local_materials = gLocalTerrainMaterials.materialsReady(true, false); + const bool use_local_materials = gLocalTerrainMaterials.makeMaterialsReady(true, false); // Hack! Get the region that this draw pool is rendering from! LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion(); LLVLComposition *compp = regionp->getComposition(); @@ -330,7 +330,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials) // Hack! Get the region that this draw pool is rendering from! LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion(); LLVLComposition *compp = regionp->getComposition(); - LLPointer<LLFetchedGLTFMaterial> (*fetched_materials)[LLVLComposition::ASSET_COUNT] = &compp->mDetailMaterials; + LLPointer<LLFetchedGLTFMaterial> (*fetched_materials)[LLVLComposition::ASSET_COUNT] = &compp->mDetailRenderMaterials; constexpr U32 terrain_material_count = LLVLComposition::ASSET_COUNT; #ifdef SHOW_ASSERT @@ -341,7 +341,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials) if (local_materials) { // Override region terrain with the global local override terrain - fetched_materials = &gLocalTerrainMaterials.mDetailMaterials; + fetched_materials = &gLocalTerrainMaterials.mDetailRenderMaterials; } const LLGLTFMaterial* materials[terrain_material_count]; for (U32 i = 0; i < terrain_material_count; ++i) @@ -432,14 +432,50 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials) LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(shader); - LLGLTFMaterial::TextureTransform base_color_transform; - base_color_transform.mScale = LLVector2(sPBRDetailScale, sPBRDetailScale); - // *TODO: mOffset and mRotation left at defaults for now. (per-material texture transforms are implemented in another branch) - F32 base_color_packed[8]; - base_color_transform.getPacked(base_color_packed); - // *HACK: Use the same texture repeats for all PBR terrain textures for now - // (not compliant with KHR texture transform spec) - shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed); + // Like for PBR materials, PBR terrain texture transforms are defined by + // the KHR_texture_transform spec, but with the following notable + // differences: + // 1) The PBR UV origin is defined as the Southwest corner of the region, + // with positive U facing East and positive V facing South. + // 2) There is an additional scaling factor RenderTerrainPBRScale. If + // we've done our math right, RenderTerrainPBRScale should not affect the + // overall behavior of KHR_texture_transform + // 3) There is only one texture transform per material, whereas + // KHR_texture_transform supports one texture transform per texture info. + // 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) + { + const LLFetchedGLTFMaterial* fetched_material = (*fetched_materials)[i].get(); + LLGLTFMaterial::TextureTransform transform; + if (fetched_material) + { + transform = fetched_material->mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]; +#ifdef SHOW_ASSERT + // Assert condition where the contents of the texture transforms + // differ per texture info - we currently don't support this case. + for (U32 ti = 1; ti < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++ti) + { + llassert(fetched_material->mTextureTransform[0] == fetched_material->mTextureTransform[ti]); + } +#endif + } + // *NOTE: Notice here we are combining the scale from + // RenderTerrainPBRScale into the KHR_texture_transform. This only + // works if the scale is uniform and no other transforms are + // applied to the terrain UVs. + transform.mScale.mV[VX] *= sPBRDetailScale; + transform.mScale.mV[VY] *= sPBRDetailScale; + + transform.getPackedTight(transforms_packed[i]); + } + const U32 transform_param_count = LLGLTFMaterial::TextureTransform::PACK_TIGHT_SIZE * terrain_material_count; + constexpr U32 vec4_size = 4; + const U32 transform_vec4_count = (transform_param_count + (vec4_size - 1)) / vec4_size; + llassert(transform_vec4_count == 5); // If false, need to update shader + shader->uniform4fv(LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS, transform_vec4_count, (F32*)transforms_packed); LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater(); diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 0a581e4694..fe6cd4e37d 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -217,8 +217,8 @@ bool LLViewerDynamicTexture::updateAllInstances() LLViewerDynamicTexture *dynamicTexture = *iter; if (dynamicTexture->needsRender()) { - llassert(dynamicTexture->getFullWidth() <= (S32)LLPipeline::MAX_BAKE_WIDTH); - llassert(dynamicTexture->getFullHeight() <= (S32)LLPipeline::MAX_BAKE_WIDTH); + llassert(dynamicTexture->getFullWidth() <= S32(LLPipeline::MAX_BAKE_WIDTH)); + llassert(dynamicTexture->getFullHeight() <= S32(LLPipeline::MAX_BAKE_WIDTH)); glClear(GL_DEPTH_BUFFER_BIT); diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 21bb3bac1d..8884905cf0 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -1680,15 +1680,15 @@ void LLEnvironment::update(const LLViewerCamera * cam) end_shaders = LLViewerShaderMgr::instance()->endShaders(); for (shaders_iter = LLViewerShaderMgr::instance()->beginShaders(); shaders_iter != end_shaders; ++shaders_iter) { - if ((shaders_iter->mProgramObject != 0) - && (gPipeline.canUseWindLightShaders() - || shaders_iter->mShaderGroup == LLGLSLShader::SG_WATER)) + shaders_iter->mUniformsDirty = true; + if (shaders_iter->mRiggedVariant) { - shaders_iter->mUniformsDirty = true; - if (shaders_iter->mRiggedVariant) - { - shaders_iter->mRiggedVariant->mUniformsDirty = true; - } + shaders_iter->mRiggedVariant->mUniformsDirty = true; + } + + for (auto& variant : shaders_iter->mGLTFVariants) + { + variant.mUniformsDirty = true; } } } diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 9a75128eb3..0e8e64af69 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -628,13 +628,6 @@ void LLFace::renderOneWireframe(const LLColor4 &color, F32 fogCfx, bool wirefram { LLGLDisable depth(wireframe_selection ? 0 : GL_BLEND); - //LLGLEnable stencil(wireframe_selection ? 0 : GL_STENCIL_TEST); - - if (!wireframe_selection) - { //modify wireframe into outline selection mode - glStencilFunc(GL_NOTEQUAL, 2, 0xffff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - } LLGLEnable offset(GL_POLYGON_OFFSET_LINE); glPolygonOffset(3.f, 3.f); diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp index 05aea3d914..c2821d56d6 100644 --- a/indra/newview/llfetchedgltfmaterial.cpp +++ b/indra/newview/llfetchedgltfmaterial.cpp @@ -142,7 +142,6 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex) mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed); shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed); } - } LLViewerFetchedTexture* fetch_texture(const LLUUID& id) diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 20b4b05a99..0afb275d13 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -515,11 +515,11 @@ bool LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename, case FFSAVE_GLTF: if (filename.empty()) { - wcsncpy( mFilesW,L"untitled.glb", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ + wcsncpy( mFilesW,L"untitled.gltf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/ } - mOFN.lpstrDefExt = L"glb"; + mOFN.lpstrDefExt = L"gltf"; mOFN.lpstrFilter = - L"glTF Asset File (*.gltf *.glb)\0*.gltf;*.glb\0" \ + L"glTF Asset File (*.gltf)\0*.gltf\0" \ L"\0"; break; case FFSAVE_XML: @@ -790,7 +790,7 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension, case LLFilePicker::FFSAVE_GLTF: type = "\?\?\?\?"; creator = "\?\?\?\?"; - extension = "glb"; + extension = "gltf"; break; case LLFilePicker::FFSAVE_XML: diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 2c58042663..c019bd047d 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -68,6 +68,7 @@ #include "llnamelistctrl.h" #include "llnotifications.h" #include "llnotificationsutil.h" +#include "llpbrterrainfeatures.h" #include "llregioninfomodel.h" #include "llscrolllistitem.h" #include "llsliderctrl.h" @@ -263,7 +264,16 @@ bool LLFloaterRegionInfo::postBuild() panel = new LLPanelRegionTerrainInfo; mInfoPanels.push_back(panel); - panel->buildFromFile("panel_region_terrain.xml"); + static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false); + static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false); + if (!feature_pbr_terrain_transforms_enabled || !feature_pbr_terrain_enabled) + { + panel->buildFromFile("panel_region_terrain.xml"); + } + else + { + panel->buildFromFile("panel_region_terrain_texture_transform.xml"); + } mTab->addTabPanel(panel); mEnvironmentPanel = new LLPanelRegionEnvironment; @@ -346,7 +356,7 @@ void LLFloaterRegionInfo::requestRegionInfo() { tab->getChild<LLPanel>("General")->setCtrlsEnabled(false); tab->getChild<LLPanel>("Debug")->setCtrlsEnabled(false); - tab->getChild<LLPanel>("Terrain")->setCtrlsEnabled(false); + tab->getChild<LLPanel>("Terrain")->setAllChildrenEnabled(false, true); tab->getChild<LLPanel>("Estate")->setCtrlsEnabled(false); tab->getChild<LLPanel>("Access")->setCtrlsEnabled(false); } @@ -543,7 +553,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) panel->getChild<LLUICtrl>("terrain_raise_spin")->setValue(region_info.mTerrainRaiseLimit); panel->getChild<LLUICtrl>("terrain_lower_spin")->setValue(region_info.mTerrainLowerLimit); - panel->setCtrlsEnabled(allow_modify); + panel->setAllChildrenEnabled(allow_modify, true); if (floater->getVisible()) { @@ -554,6 +564,20 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) } // static +void LLFloaterRegionInfo::sRefreshFromRegion(LLViewerRegion* region) +{ + if (region != gAgent.getRegion()) { return; } + + LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info"); + if (!floater) { return; } + + if (floater->getVisible() && region == gAgent.getRegion()) + { + floater->refreshFromRegion(region); + } +} + +// static LLPanelEstateInfo* LLFloaterRegionInfo::getPanelEstate() { LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info"); @@ -644,7 +668,7 @@ void LLFloaterRegionInfo::disableTabCtrls() tab->getChild<LLPanel>("General")->setCtrlsEnabled(false); tab->getChild<LLPanel>("Debug")->setCtrlsEnabled(false); - tab->getChild<LLPanel>("Terrain")->setCtrlsEnabled(false); + tab->getChild<LLPanel>("Terrain")->setAllChildrenEnabled(false, true); tab->getChild<LLPanel>("panel_env_info")->setCtrlsEnabled(false); tab->getChild<LLPanel>("Estate")->setCtrlsEnabled(false); tab->getChild<LLPanel>("Access")->setCtrlsEnabled(false); @@ -825,6 +849,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name) getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this)); } +template<typename CTRL> +void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name) +{ + initCtrl(name); + ctrl = findChild<CTRL>(name); +} + void LLPanelRegionInfo::onClickManageTelehub() { LLFloaterReg::hideInstance("region_info"); @@ -1494,11 +1525,17 @@ LLPanelRegionTerrainInfo::LLPanelRegionTerrainInfo() const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures(); for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) { + mTextureDetailCtrl[i] = nullptr; + mMaterialDetailCtrl[i] = nullptr; + mLastSetTextures[i] = default_textures[i]; - } - for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) - { mLastSetMaterials[i] = BLANK_MATERIAL_ASSET_ID; + + mMaterialScaleUCtrl[i] = nullptr; + mMaterialScaleVCtrl[i] = nullptr; + mMaterialRotationCtrl[i] = nullptr; + mMaterialOffsetUCtrl[i] = nullptr; + mMaterialOffsetVCtrl[i] = nullptr; } } @@ -1519,15 +1556,18 @@ bool LLPanelRegionTerrainInfo::postBuild() for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) { - buffer = llformat("texture_detail_%d", i); - initCtrl(buffer); - mTextureDetailCtrl[i] = findChild<LLTextureCtrl>(buffer); - } - for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) - { - buffer = llformat("material_detail_%d", i); - initCtrl(buffer); - mMaterialDetailCtrl[i] = findChild<LLTextureCtrl>(buffer); + initAndSetCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i)); + if (mTextureDetailCtrl[i]) + { + mTextureDetailCtrl[i]->setBakeTextureEnabled(false); + } + initAndSetCtrl(mMaterialDetailCtrl[i], llformat("material_detail_%d", i)); + + initAndSetCtrl(mMaterialScaleUCtrl[i], llformat("terrain%dScaleU", i)); + initAndSetCtrl(mMaterialScaleVCtrl[i], llformat("terrain%dScaleV", i)); + initAndSetCtrl(mMaterialRotationCtrl[i], llformat("terrain%dRotation", i)); + initAndSetCtrl(mMaterialOffsetUCtrl[i], llformat("terrain%dOffsetU", i)); + initAndSetCtrl(mMaterialOffsetVCtrl[i], llformat("terrain%dOffsetV", i)); } for(S32 i = 0; i < CORNER_COUNT; ++i) @@ -1579,6 +1619,17 @@ void LLPanelRegionTerrainInfo::updateForMaterialType() } } + // Toggle visibility of terrain tabs + LLTabContainer* terrain_tabs = findChild<LLTabContainer>("terrain_tabs"); + if (terrain_tabs) + { + LLPanel* pbr_terrain_repeats_tab = findChild<LLPanel>("terrain_transform_panel"); + if (pbr_terrain_repeats_tab) + { + terrain_tabs->setTabVisibility(pbr_terrain_repeats_tab, show_material_controls); + } + } + // Toggle visibility of labels LLUICtrl* texture_label = findChild<LLUICtrl>("detail_texture_text"); if (texture_label) { texture_label->setVisible(show_texture_controls); } @@ -1606,7 +1657,7 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) || (region && (region->getOwner() == gAgent.getID())); bool owner_or_god_or_manager = owner_or_god || (region && region->isEstateManager()); - setCtrlsEnabled(owner_or_god_or_manager); + setAllChildrenEnabled(owner_or_god_or_manager, true); getChildView("apply_btn")->setEnabled(false); @@ -1618,8 +1669,8 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false); - const bool textures_ready = compp->texturesReady(false, false); - const bool materials_ready = feature_pbr_terrain_enabled && compp->materialsReady(false, false); + const bool textures_ready = compp->makeTexturesReady(false, false); + const bool materials_ready = feature_pbr_terrain_enabled && compp->makeMaterialsReady(false, false); bool set_texture_swatches; bool set_material_swatches; @@ -1707,6 +1758,21 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) } } + for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + if (!mMaterialScaleUCtrl[i] || !mMaterialScaleVCtrl[i] || !mMaterialRotationCtrl[i] || !mMaterialOffsetUCtrl[i] || !mMaterialOffsetVCtrl[i]) { continue; } + const LLGLTFMaterial* mat_override = compp->getMaterialOverride(i); + if (!mat_override) { mat_override = &LLGLTFMaterial::sDefault; } + + // Assume all texture transforms have the same value + const LLGLTFMaterial::TextureTransform& transform = mat_override->mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]; + mMaterialScaleUCtrl[i]->setValue(transform.mScale.mV[VX]); + mMaterialScaleVCtrl[i]->setValue(transform.mScale.mV[VY]); + mMaterialRotationCtrl[i]->setValue(transform.mRotation * RAD_TO_DEG); + mMaterialOffsetUCtrl[i]->setValue(transform.mOffset.mV[VX]); + mMaterialOffsetVCtrl[i]->setValue(transform.mOffset.mV[VY]); + } + std::string buffer; for(S32 i = 0; i < CORNER_COUNT; ++i) { @@ -1736,7 +1802,14 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) // virtual bool LLPanelRegionTerrainInfo::sendUpdate() { - LL_INFOS() << "LLPanelRegionTerrainInfo::sendUpdate" << LL_ENDL; + LL_INFOS() << __FUNCTION__ << LL_ENDL; + + LLUICtrl* apply_btn = getChild<LLUICtrl>("apply_btn"); + if (apply_btn && !apply_btn->getEnabled()) + { + LL_WARNS() << "Duplicate update, ignored" << LL_ENDL; + return false; + } // Make sure user hasn't chosen wacky textures. if (!validateTextureSizes()) @@ -1837,6 +1910,51 @@ bool LLPanelRegionTerrainInfo::sendUpdate() sendEstateOwnerMessage(msg, "texturecommit", invoice, strings); + // ======================================== + // POST to ModifyRegion endpoint, if enabled + + static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false); + if (material_type == LLTerrainMaterials::Type::PBR && feature_pbr_terrain_transforms_enabled) + { + LLTerrainMaterials composition; + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + LLPointer<LLGLTFMaterial> mat_override = new LLGLTFMaterial(); + + const bool transform_controls_valid = mMaterialScaleUCtrl[i] && mMaterialScaleVCtrl[i] && mMaterialRotationCtrl[i] && mMaterialOffsetUCtrl[i] && mMaterialOffsetVCtrl[i]; + if (transform_controls_valid) + { + // Set texture transforms for all texture infos to the same value, + // because the PBR terrain shader doesn't currently support + // different transforms per texture info. See also + // LLDrawPoolTerrain::renderFullShaderPBR . + for (U32 tt = 0; tt < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++tt) + { + LLGLTFMaterial::TextureTransform& transform = mat_override->mTextureTransform[tt]; + transform.mScale.mV[VX] = mMaterialScaleUCtrl[i]->getValue().asReal(); + transform.mScale.mV[VY] = mMaterialScaleVCtrl[i]->getValue().asReal(); + transform.mRotation = mMaterialRotationCtrl[i]->getValue().asReal() * DEG_TO_RAD; + transform.mOffset.mV[VX] = mMaterialOffsetUCtrl[i]->getValue().asReal(); + transform.mOffset.mV[VY] = mMaterialOffsetVCtrl[i]->getValue().asReal(); + } + } + + if (*mat_override == LLGLTFMaterial::sDefault) { mat_override = nullptr; } + composition.setMaterialOverride(i, mat_override.get()); + } + + // queueModify leads to a few messages being sent back and forth: + // viewer: POST ModifyRegion + // simulator: RegionHandshake + // viewer: GET ModifyRegion + LLViewerRegion* region = gAgent.getRegion(); + llassert(region); + if (region) + { + LLPBRTerrainFeatures::queueModify(*region, composition); + } + } + return true; } diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 4b81a26210..5623bc20cb 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -85,6 +85,7 @@ public: // get and process region info if necessary. static void processRegionInfo(LLMessageSystem* msg); + static void sRefreshFromRegion(LLViewerRegion* region); static const LLUUID& getLastInvoice() { return sRequestInvoice; } static void nextInvoice() { sRequestInvoice.generate(); } @@ -156,6 +157,7 @@ public: protected: void initCtrl(const std::string& name); + template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, const std::string& name); // Returns true if update sent and apply button should be // disabled. @@ -277,8 +279,15 @@ private: LLCheckBoxCtrl* mMaterialTypeCtrl = nullptr; LLTextureCtrl* mTextureDetailCtrl[LLTerrainMaterials::ASSET_COUNT]; LLTextureCtrl* mMaterialDetailCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLUUID mLastSetTextures[LLTerrainMaterials::ASSET_COUNT]; LLUUID mLastSetMaterials[LLTerrainMaterials::ASSET_COUNT]; + + LLSpinCtrl* mMaterialScaleUCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLSpinCtrl* mMaterialScaleVCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLSpinCtrl* mMaterialRotationCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLSpinCtrl* mMaterialOffsetUCtrl[LLTerrainMaterials::ASSET_COUNT]; + LLSpinCtrl* mMaterialOffsetVCtrl[LLTerrainMaterials::ASSET_COUNT]; }; ///////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index f6ebc8eebc..08f8918e5d 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -1009,7 +1009,7 @@ F32 gpu_benchmark() gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER)); gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER)); gBenchmarkProgram.mShaderLevel = 1; - if (!gBenchmarkProgram.createShader(NULL, NULL)) + if (!gBenchmarkProgram.createShader()) { return -1.f; } diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp index 9d9332312b..f544b70329 100644 --- a/indra/newview/llheroprobemanager.cpp +++ b/indra/newview/llheroprobemanager.cpp @@ -196,7 +196,7 @@ void LLHeroProbeManager::update() cube_facing = 1 - cube_facing; mFaceUpdateList[i] = ceilf(cube_facing * gPipeline.RenderHeroProbeConservativeUpdateMultiplier); - } + } mProbes[0]->mOrigin = probe_pos; @@ -208,7 +208,7 @@ void LLHeroProbeManager::update() mHeroProbeStrength = 1; } -} + } void LLHeroProbeManager::renderProbes() { @@ -230,7 +230,7 @@ void LLHeroProbeManager::renderProbes() bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass(); gPipeline.mReflectionMapManager.mRadiancePass = true; - mRenderingMirror = true; + mRenderingMirror = true; doOcclusion(); @@ -359,7 +359,8 @@ void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, bool res /= 2; - S32 mip = i - ((S32)mMipChain.size() - mips); + llassert(mMipChain.size() <= size_t(S32_MAX)); + GLint mip = i - (S32(mMipChain.size()) - mips); if (mip >= 0) { @@ -487,7 +488,8 @@ void LLHeroProbeManager::updateUniforms() mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius; } - mHeroData.heroMipCount = (GLint)mMipChain.size(); + llassert(mMipChain.size() <= size_t(S32_MAX)); + mHeroData.heroMipCount = S32(mMipChain.size()); } void LLHeroProbeManager::renderDebug() diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index d4c92ea5ae..e2e83ef42b 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1637,23 +1637,29 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) { session_id = message_data["asset_id"].asUUID(); } - LLIMProcessing::processNewMessage( - message_data["from_agent_id"].asUUID(), - from_group, - message_data["to_agent_id"].asUUID(), - message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE, - dialog, - session_id, - static_cast<U32>(message_data["timestamp"].asInteger()), - message_data["from_agent_name"].asString(), - message_data["message"].asString(), - static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland - message_data["region_id"].asUUID(), - position, - bin_bucket.data(), - static_cast<S32>(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(), + S32(local_bin_bucket.size()), + local_sender, + message_data["asset_id"].asUUID()); + }); } } diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index c7e0e9d702..d57cb13362 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -889,11 +889,11 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( else if (root_id.notNull()) { cat_array_t* cats = get_ptr_in_map(mParentChildCategoryTree, root_id); - if (cats) + if(cats) { for (auto& p_cat : *cats) { - if (p_cat && p_cat->getPreferredType() == preferred_type) + if(p_cat && p_cat->getPreferredType() == preferred_type) { const LLUUID& folder_id = p_cat->getUUID(); if (rv.isNull() || folder_id < rv) @@ -1389,7 +1389,9 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) return mask; } - if (item->getType() == LLAssetType::AT_MESH) + if (item->getType() == LLAssetType::AT_MESH || + item->getType() == LLAssetType::AT_GLTF || + item->getType() == LLAssetType::AT_GLTF_BIN) { return mask; } diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index 9cbbda0b79..6ab5e05b7d 100644 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -1091,8 +1091,9 @@ bool LLLocalBitmapMgr::checkTextureDimensions(std::string filename) return false; } - S32 max_width = gSavedSettings.getS32("max_texture_dimension_X"); - S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y"); + // allow loading up to 4x max rez but implicitly downrez to max rez before upload + S32 max_width = gSavedSettings.getS32("max_texture_dimension_X")*4; + S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y")*4; if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height)) { @@ -1137,6 +1138,20 @@ void LLLocalBitmapMgr::delUnit(LLUUID tracking_id) } } +LLUUID LLLocalBitmapMgr::getTrackingID(const LLUUID& world_id) const +{ + for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++) + { + LLLocalBitmap* unit = *iter; + if (unit->getWorldID() == world_id) + { + return unit->getTrackingID(); + } + } + + return LLUUID::null; +} + LLUUID LLLocalBitmapMgr::getWorldID(const LLUUID &tracking_id) const { LLUUID world_id = LLUUID::null; diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h index e0cd9d172f..e169f96e70 100644 --- a/indra/newview/lllocalbitmaps.h +++ b/indra/newview/lllocalbitmaps.h @@ -135,6 +135,7 @@ public: void delUnit(LLUUID tracking_id); bool checkTextureDimensions(std::string filename); + LLUUID getTrackingID(const LLUUID& world_id) const; LLUUID getWorldID(const LLUUID &tracking_id) const; bool isLocal(const LLUUID& world_id) const; std::string getFilename(const LLUUID &tracking_id) const; diff --git a/indra/newview/lllocalgltfmaterials.cpp b/indra/newview/lllocalgltfmaterials.cpp index 84b58b9e71..fab18f2d26 100644 --- a/indra/newview/lllocalgltfmaterials.cpp +++ b/indra/newview/lllocalgltfmaterials.cpp @@ -177,6 +177,8 @@ bool LLLocalGLTFMaterial::updateSelf() } } + materialBegin(); + materialComplete(true); updated = true; } @@ -201,6 +203,8 @@ bool LLLocalGLTFMaterial::updateSelf() LLNotificationsUtil::add("LocalBitmapsUpdateFailedFinal", notif_args); mLinkStatus = LS_BROKEN; + materialBegin(); + materialComplete(false); } } } @@ -218,6 +222,8 @@ bool LLLocalGLTFMaterial::updateSelf() LLNotificationsUtil::add("LocalBitmapsUpdateFileNotFound", notif_args); mLinkStatus = LS_BROKEN; + materialBegin(); + materialComplete(false); } } diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index d6538d5407..c11a98be50 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -1738,11 +1738,6 @@ void LLManipTranslate::highlightIntersection(LLVector3 normal, shader->bind(); } - if (shader) - { - shader->bind(); - } - //draw volume/plane intersections { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 226c32c396..89af765bb7 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -1805,7 +1805,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild<LLUICtrl>("shinyOffsetV")->setValue(offset_y); getChild<LLUICtrl>("glossiness")->setValue(material->getSpecularLightExponent()); getChild<LLUICtrl>("environment")->setValue(material->getEnvironmentIntensity()); - getChild<LLUICtrl>("mirror")->setValue(material->getEnvironmentIntensity()); updateShinyControls(!material->getSpecularID().isNull(), true); } diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 908979bcad..16c38bf1f0 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -442,9 +442,6 @@ void LLPanelVolume::getState( ) update_type = "Dynamic Mirror"; } - getChildView("Probe Ambiance")->setEnabled(!is_mirror); - getChildView("Probe Near Clip")->setEnabled(!is_mirror); - getChild<LLComboBox>("Probe Volume Type", true)->setValue(volume_type); getChild<LLSpinCtrl>("Probe Ambiance", true)->setValue(volobjp->getReflectionProbeAmbiance()); getChild<LLSpinCtrl>("Probe Near Clip", true)->setValue(volobjp->getReflectionProbeNearClip()); diff --git a/indra/newview/llpbrterrainfeatures.cpp b/indra/newview/llpbrterrainfeatures.cpp new file mode 100644 index 0000000000..bb771c6963 --- /dev/null +++ b/indra/newview/llpbrterrainfeatures.cpp @@ -0,0 +1,198 @@ +/** + * @file llpbrterrainfeatures.cpp + * + * $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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpbrterrainfeatures.h" + +#include "llappviewer.h" +#include "llgltfmaterial.h" +#include "llviewerregion.h" +#include "llvlcomposition.h" + +LLPBRTerrainFeatures gPBRTerrainFeatures; + +// static +void LLPBRTerrainFeatures::queueQuery(LLViewerRegion& region, void(*done_callback)(LLUUID, bool, const LLModifyRegion&)) +{ + llassert(on_main_thread()); + llassert(LLCoros::on_main_coro()); + + LLUUID region_id = region.getRegionID(); + + LLCoros::instance().launch("queryRegionCoro", + std::bind(&LLPBRTerrainFeatures::queryRegionCoro, + region.getCapability("ModifyRegion"), + region_id, + done_callback)); +} + +// static +void LLPBRTerrainFeatures::queueModify(LLViewerRegion& region, const LLModifyRegion& composition) +{ + llassert(on_main_thread()); + llassert(LLCoros::on_main_coro()); + + LLSD updates = LLSD::emptyMap(); + + LLSD override_updates = LLSD::emptyArray(); + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + const LLGLTFMaterial* material_override = composition.getMaterialOverride(i); + LLSD override_update; + if (material_override) + { + LLGLTFMaterial::sDefault.getOverrideLLSD(*material_override, override_update); + } + else + { + override_update = LLSD::emptyMap(); + } + override_updates.append(override_update); + } + updates["overrides"] = override_updates; + + LLCoros::instance().launch("modifyRegionCoro", + std::bind(&LLPBRTerrainFeatures::modifyRegionCoro, + region.getCapability("ModifyRegion"), + updates, + nullptr)); +} + +// static +void LLPBRTerrainFeatures::queryRegionCoro(std::string cap_url, LLUUID region_id, void(*done_callback)(LLUUID, bool, const LLModifyRegion&) ) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("queryRegionCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders; + + httpOpts->setFollowRedirects(true); + + LL_DEBUGS("GLTF") << "Querying features via ModifyRegion endpoint" << LL_ENDL; + + LLSD result = httpAdapter->getAndSuspend(httpRequest, cap_url, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + bool success = true; + if (!status || !result["success"].asBoolean()) + { + if (result["message"].isUndefined()) + { + LL_WARNS("PBRTerrain") << "Failed to query PBR terrain features." << LL_ENDL; + } + else + { + LL_WARNS("PBRTerrain") << "Failed to query PBR terrain features: " << result["message"] << LL_ENDL; + } + success = false; + } + + LLTerrainMaterials* composition = new LLTerrainMaterials(); + + if (success) + { + const LLSD& overrides = result["overrides"]; + if (!overrides.isArray() || overrides.size() < LLTerrainMaterials::ASSET_COUNT) + { + LL_WARNS("PBRTerrain") << "Invalid composition format: Missing/invalid overrides" << LL_ENDL; + success = false; + } + else + { + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + const LLSD& override_llsd = overrides[i]; + LLPointer<LLGLTFMaterial> material_override = new LLGLTFMaterial(); + material_override->applyOverrideLLSD(override_llsd); + if (*material_override == LLGLTFMaterial::sDefault) + { + material_override = nullptr; + } + composition->setMaterialOverride(i, material_override.get()); + } + } + } + + if (done_callback) + { + LLAppViewer::instance()->postToMainCoro([=]() + { + done_callback(region_id, success, *composition); + delete composition; + }); + } + else + { + delete composition; + } +} + +// static +void LLPBRTerrainFeatures::modifyRegionCoro(std::string cap_url, LLSD updates, void(*done_callback)(bool) ) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("modifyRegionCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + LLCore::HttpHeaders::ptr_t httpHeaders; + + httpOpts->setFollowRedirects(true); + + LL_DEBUGS("GLTF") << "Applying features via ModifyRegion endpoint: " << updates << LL_ENDL; + + LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, updates, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + bool success = true; + if (!status || !result["success"].asBoolean()) + { + if (result["message"].isUndefined()) + { + LL_WARNS("PBRTerrain") << "Failed to modify PBR terrain features." << LL_ENDL; + } + else + { + LL_WARNS("PBRTerrain") << "Failed to modify PBR terrain features: " << result["message"] << LL_ENDL; + } + success = false; + } + + if (done_callback) + { + LLAppViewer::instance()->postToMainCoro([=]() + { + done_callback(success); + }); + } +} + diff --git a/indra/newview/llpbrterrainfeatures.h b/indra/newview/llpbrterrainfeatures.h new file mode 100644 index 0000000000..f29d4ebf50 --- /dev/null +++ b/indra/newview/llpbrterrainfeatures.h @@ -0,0 +1,48 @@ +/** + * @file llpbrterrainfeatures.h + * + * $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$ + */ + + +#pragma once + +#include <string> + +class LLViewerRegion; +class LLMessageSystem; +class LLModifyRegion; + +// Queries/modifies PBR terrain repeats, possibly other features in the future +class LLPBRTerrainFeatures +{ +public: + static void queueQuery(LLViewerRegion& region, void(*done_callback)(LLUUID, bool, const LLModifyRegion&)); + static void queueModify(LLViewerRegion& region, const LLModifyRegion& composition); + +private: + static void queryRegionCoro(std::string cap_url, LLUUID region_id, void(*done_callback)(LLUUID, bool, const LLModifyRegion&) ); + static void modifyRegionCoro(std::string cap_url, LLSD updates, void(*done_callback)(bool) ); +}; + +extern LLPBRTerrainFeatures gPBRTerrainFeatures; + diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index 554dbe96db..cb1ab0dac2 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -1266,7 +1266,7 @@ void LLReflectionMapManager::setUniforms() { updateUniforms(); } - glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO); + glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_REFLECTION_PROBES, mUBO); } diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index 4ce45608df..c619b63ef5 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -97,7 +97,6 @@ static std::string click_action_to_string_value(U8 click_action) default: return "Touch"; } - return "Touch"; } // Default constructor diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index 52bd6a0315..9b4ed4c946 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -97,6 +97,12 @@ U32 LLSkinningUtil::getMeshJointCount(const LLMeshSkinInfo *skin) return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size()); } +S32 LLSkinningUtil::getMaxGLTFJointCount() +{ + // this is the maximum number of 3x4 matrices than can fit in a UBO + return gGLManager.mMaxUniformBlockSize / 48; +} + void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin) { if (skin->mInvalidJointsScrubbed) diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h index bd2f8ea04e..aa0c0075af 100644 --- a/indra/newview/llskinningutil.h +++ b/indra/newview/llskinningutil.h @@ -40,6 +40,7 @@ class LLJointRiggingInfoTab; namespace LLSkinningUtil { S32 getMaxJointCount(); + S32 getMaxGLTFJointCount(); U32 getMeshJointCount(const LLMeshSkinInfo *skin); void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin); void initSkinningMatrixPalette(LLMatrix4a* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 77e615b01c..dd005874a5 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -330,10 +330,18 @@ void update_texture_fetch() void set_flags_and_update_appearance() { - LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); - LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op); + // this may be called from a coroutine but has many side effects + // in non-thread-safe classes, post to main loop + auto work = []() + { + LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); + LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op); + + LLInventoryModelBackgroundFetch::instance().start(); + }; + + LLAppViewer::instance()->postToMainCoro(work); - LLInventoryModelBackgroundFetch::instance().start(); } // Returns false to skip other idle processing. Should only return diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index d0dcc8d51f..81a70a81cf 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -638,7 +638,7 @@ bool LLFloaterTexturePicker::postBuild() getChild<LLComboBox>("l_bake_use_texture_combo_box")->setCommitCallback(onBakeTextureSelect, this); - setBakeTextureEnabled(true); + setBakeTextureEnabled(mInventoryPickType != PICK_MATERIAL); return true; } @@ -1775,7 +1775,7 @@ void LLTextureCtrl::onVisibilityChange(bool new_visibility) } } -void LLTextureCtrl::setVisible( bool visible ) +void LLTextureCtrl::setVisible(bool visible ) { if( !visible ) { @@ -1882,7 +1882,7 @@ void LLTextureCtrl::showPicker(bool take_focus) { texture_floaterp->setSetImageAssetIDCallback(boost::bind(&LLTextureCtrl::setImageAssetID, this, _1)); - texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled); + texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled && mInventoryPickType != PICK_MATERIAL); } LLFloater* root_floater = gFloaterView->getParentFloater(this); @@ -2112,7 +2112,7 @@ void LLTextureCtrl::setBakeTextureEnabled(bool enabled) LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); if (floaterp) { - floaterp->setBakeTextureEnabled(enabled); + floaterp->setBakeTextureEnabled(enabled && mInventoryPickType != PICK_MATERIAL); } } diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp index 58ce23a23a..168708ca37 100644 --- a/indra/newview/lltinygltfhelper.cpp +++ b/indra/newview/lltinygltfhelper.cpp @@ -251,6 +251,43 @@ bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& m return false; } +bool LLTinyGLTFHelper::saveModel(const std::string& filename, tinygltf::Model& model_in) +{ + std::string exten = gDirUtilp->getExtension(filename); + + bool success = false; + + if (exten == "gltf" || exten == "glb") + { + tinygltf::TinyGLTF writer; + + std::string filename_lc = filename; + LLStringUtil::toLower(filename_lc); + + + bool embed_images = false; + bool embed_buffers = false; + bool pretty_print = true; + bool write_binary = false; + + + if (std::string::npos == filename_lc.rfind(".gltf")) + { // file is binary + embed_images = embed_buffers = write_binary = true; + } + + success = writer.WriteGltfSceneToFile(&model_in, filename, embed_images, embed_buffers, pretty_print, write_binary); + + if (!success) + { + LL_WARNS("GLTF") << "Failed to save" << LL_ENDL; + return false; + } + } + + return success; +} + bool LLTinyGLTFHelper::getMaterialFromModel( const std::string& filename, const tinygltf::Model& model_in, diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/lltinygltfhelper.h index da505b41e9..a259609404 100644 --- a/indra/newview/lltinygltfhelper.h +++ b/indra/newview/lltinygltfhelper.h @@ -42,6 +42,7 @@ namespace LLTinyGLTFHelper LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, bool flip = true); bool loadModel(const std::string& filename, tinygltf::Model& model_out); + bool saveModel(const std::string& filename, tinygltf::Model& model_in); bool getMaterialFromModel( const std::string& filename, diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index 56263f1afe..a28bbb3bf1 100644 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -489,10 +489,17 @@ void draw_shockwave(F32 center_z, F32 t, S32 steps, LLColor4 color) gGL.end(); } + void LLTracker::drawBeacon(LLVector3 pos_agent, std::string direction, LLColor4 fogged_color, F32 dist) { - const U32 BEACON_VERTS = 256; - F32 step; + const F32 MAX_HEIGHT = 5020.f; + const U32 BEACON_ROWS = 256; + + U32 nRows; + F32 height; + F32 rowHeight; + + LLColor4 c_col, col_next, col_edge, col_edge_next; gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); @@ -501,59 +508,99 @@ void LLTracker::drawBeacon(LLVector3 pos_agent, std::string direction, LLColor4 { gGL.translatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]); draw_shockwave(1024.f, gRenderStartTime.getElapsedTimeF32(), 32, fogged_color); - step = (5020.0f - pos_agent.mV[2]) / BEACON_VERTS; + height = MAX_HEIGHT - pos_agent.mV[2]; } else { gGL.translatef(pos_agent.mV[0], pos_agent.mV[1], 0); - step = pos_agent.mV[2] / BEACON_VERTS; + height = pos_agent.mV[2]; } - gGL.color4fv(fogged_color.mV); + nRows = ceil((BEACON_ROWS * height) / MAX_HEIGHT); + if(nRows<2) nRows=2; + rowHeight = height / nRows; - LLVector3 x_axis = LLViewerCamera::getInstance()->getLeftAxis(); - F32 t = gRenderStartTime.getElapsedTimeF32(); + gGL.color4fv(fogged_color.mV); - for (U32 i = 0; i < BEACON_VERTS; i++) - { - F32 x = x_axis.mV[0]; - F32 y = x_axis.mV[1]; - - F32 z = i * step; - F32 z_next = (i+1)*step; - - bool tracking_avatar = getTrackingStatus() == TRACKING_AVATAR; - F32 a = pulse_func(t, z, tracking_avatar, direction); - F32 an = pulse_func(t, z_next, tracking_avatar, direction); - - LLColor4 c_col = fogged_color + LLColor4(a,a,a,a); - LLColor4 col_next = fogged_color + LLColor4(an,an,an,an); - LLColor4 col_edge = fogged_color * LLColor4(a,a,a,0.0f); - LLColor4 col_edge_next = fogged_color * LLColor4(an,an,an,0.0f); - - a *= 2.f; - a += 1.0f; - - an *= 2.f; - an += 1.0f; - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.color4fv(col_edge.mV); - gGL.vertex3f(-x*a, -y*a, z); - gGL.color4fv(col_edge_next.mV); - gGL.vertex3f(-x*an, -y*an, z_next); - - gGL.color4fv(c_col.mV); - gGL.vertex3f(0, 0, z); - gGL.color4fv(col_next.mV); - gGL.vertex3f(0, 0, z_next); - - gGL.color4fv(col_edge.mV); - gGL.vertex3f(x*a,y*a,z); - gGL.color4fv(col_edge_next.mV); - gGL.vertex3f(x*an,y*an,z_next); - gGL.end(); + LLVector3 x_axis = LLViewerCamera::getInstance()->getLeftAxis(); + F32 t = gRenderStartTime.getElapsedTimeF32(); + + F32 x = x_axis.mV[0]; + F32 y = x_axis.mV[1]; + F32 z = 0.f; + F32 z_next; + + F32 a,an; + F32 xa,xan; + F32 ya,yan; + + bool tracking_avatar = getTrackingStatus() == TRACKING_AVATAR; + + gGL.begin(LLRender::TRIANGLES); + + for (U32 i = 0; i < nRows; i++) + { + z_next = z + rowHeight; + + a = pulse_func(t, z, tracking_avatar, direction); + an = pulse_func(t, z_next, tracking_avatar, direction); + + c_col = fogged_color + LLColor4(a, a, a, a); + col_next = fogged_color + LLColor4(an, an, an, an); + col_edge = fogged_color * LLColor4(a, a, a, 0.0f); + col_edge_next = fogged_color * LLColor4(an, an, an, 0.0f); + + a = a + a + 1.f; + an = an + an + 1.f; + + xa = x*a; + ya = y*a; + xan = x*an; + yan = y*an; + + gGL.color4fv(col_edge.mV); + gGL.vertex3f(-xa, -ya, z); + + gGL.color4fv(col_next.mV); + gGL.vertex3f(0, 0, z_next); + + gGL.color4fv(col_edge_next.mV); + gGL.vertex3f(-xan, -yan, z_next); + + + gGL.color4fv(col_edge.mV); + gGL.vertex3f(-xa, -ya, z); + + gGL.color4fv(c_col.mV); + gGL.vertex3f(0, 0, z); + + gGL.color4fv(col_next.mV); + gGL.vertex3f(0, 0, z_next); + + + gGL.color4fv(c_col.mV); + gGL.vertex3f(0, 0, z); + + gGL.color4fv(col_edge_next.mV); + gGL.vertex3f(xan, yan, z_next); + + gGL.color4fv(col_next.mV); + gGL.vertex3f(0, 0, z_next); + + + gGL.color4fv(c_col.mV); + gGL.vertex3f(0, 0, z); + + gGL.color4fv(col_edge.mV); + gGL.vertex3f(xa, ya, z); + + gGL.color4fv(col_edge_next.mV); + gGL.vertex3f(xan, yan, z_next); + + z += rowHeight; } + + gGL.end(); gGL.popMatrix(); } diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index c3dc07f357..a571b5544b 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -135,7 +135,7 @@ LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() std::string channel = getChannel(); static const boost::regex is_test_channel("\\bTest\\b"); - static const boost::regex is_beta_channel("\\bBeta\\b"); + static const boost::regex is_beta_channel("\\b(Beta|Develop)\\b"); // Develop is an alias for Beta static const boost::regex is_project_channel("\\bProject\\b"); static const boost::regex is_release_channel("\\bRelease\\b"); diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index 3e3347ff33..f4c618c08d 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -89,6 +89,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); addEntry(LLViewerAssetType::AT_SETTINGS, new ViewerAssetEntry(DAD_SETTINGS)); addEntry(LLViewerAssetType::AT_MATERIAL, new ViewerAssetEntry(DAD_MATERIAL)); + addEntry(LLViewerAssetType::AT_GLTF, new ViewerAssetEntry(DAD_GLTF)); + addEntry(LLViewerAssetType::AT_GLTF_BIN, new ViewerAssetEntry(DAD_GLTF_BIN)); }; EDragAndDropType LLViewerAssetType::lookupDragAndDropType(EType asset_type) diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index 1b044a3572..337c18f218 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -299,10 +299,18 @@ void LLResourceUploadInfo::assignDefaults() mDescription = "(No Description)"; } + if (mAssetType == LLAssetType::AT_GLTF || + mAssetType == LLAssetType::AT_GLTF_BIN) + { + mFolderId = LLUUID::null; + } + else + { mFolderId = gInventory.findUserDefinedCategoryUUIDForType( (mDestinationFolderType == LLFolderType::FT_NONE) ? (LLFolderType::EType)mAssetType : mDestinationFolderType); } +} std::string LLResourceUploadInfo::getDisplayName() const { diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 667e22e661..efed045b91 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -694,6 +694,28 @@ void handleLocalTerrainChanged(const LLSD& newValue) const auto setting = gSavedSettings.getString(std::string("LocalTerrainAsset") + std::to_string(i + 1)); const LLUUID materialID(setting); gLocalTerrainMaterials.setDetailAssetID(i, materialID); + + // *NOTE: The GLTF spec allows for different texture infos to have their texture transforms set independently, but as a simplification, this debug setting only updates all the transforms in-sync (i.e. only one texture transform per terrain material). + LLGLTFMaterial::TextureTransform transform; + const std::string prefix = std::string("LocalTerrainTransform") + std::to_string(i + 1); + transform.mScale.mV[VX] = gSavedSettings.getF32(prefix + "ScaleU"); + transform.mScale.mV[VY] = gSavedSettings.getF32(prefix + "ScaleV"); + transform.mRotation = gSavedSettings.getF32(prefix + "Rotation") * DEG_TO_RAD; + transform.mOffset.mV[VX] = gSavedSettings.getF32(prefix + "OffsetU"); + transform.mOffset.mV[VY] = gSavedSettings.getF32(prefix + "OffsetV"); + LLPointer<LLGLTFMaterial> mat_override = new LLGLTFMaterial(); + for (U32 info = 0; info < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++info) + { + mat_override->mTextureTransform[info] = transform; + } + if (*mat_override == LLGLTFMaterial::sDefault) + { + gLocalTerrainMaterials.setMaterialOverride(i, nullptr); + } + else + { + gLocalTerrainMaterials.setMaterialOverride(i, mat_override); + } } } //////////////////////////////////////////////////////////////////////////// @@ -879,10 +901,25 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorFarAwayDistance", handleUserImpostorDistanceChanged); setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); - setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset1", handleLocalTerrainChanged); - setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset2", handleLocalTerrainChanged); - setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset3", handleLocalTerrainChanged); - setting_setup_signal_listener(gSavedSettings, "LocalTerrainAsset4", handleLocalTerrainChanged); + { + const char* transform_suffixes[] = { + "ScaleU", + "ScaleV", + "Rotation", + "OffsetU", + "OffsetV" + }; + for (U32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + const auto asset_setting_name = std::string("LocalTerrainAsset") + std::to_string(i + 1); + setting_setup_signal_listener(gSavedSettings, asset_setting_name, handleLocalTerrainChanged); + for (const char* ts : transform_suffixes) + { + const auto transform_setting_name = std::string("LocalTerrainTransform") + std::to_string(i + 1) + ts; + setting_setup_signal_listener(gSavedSettings, transform_setting_name, handleLocalTerrainChanged); + } + } + } setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged); } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index efe57661a9..23931ecf03 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1667,7 +1667,7 @@ void LLViewerMediaImpl::destroyMediaSource() cancelMimeTypeProbe(); { - LLMutexLock lock(&mLock); // Delay tear-down while bg thread is updating + LLCoros::LockType lock(mLock); // Delay tear-down while bg thread is updating if(mMediaSource) { mMediaSource->setDeleteOK(true) ; @@ -2674,7 +2674,13 @@ void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url) { if (initializeMedia(mimeType)) { - loadURI(); + ref(); + LLAppViewer::instance()->postToMainCoro([this]() + { + loadURI(); + unref(); + }); + } } @@ -2968,7 +2974,7 @@ bool LLViewerMediaImpl::preMediaTexUpdate(LLViewerMediaTexture*& media_tex, U8*& void LLViewerMediaImpl::doMediaTexUpdate(LLViewerMediaTexture* media_tex, U8* data, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, bool sync) { LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; - LLMutexLock lock(&mLock); // don't allow media source tear-down during update + LLCoros::LockType lock(mLock); // don't allow media source tear-down during update // wrap "data" in an LLImageRaw but do NOT make a copy LLPointer<LLImageRaw> raw = new LLImageRaw(data, media_tex->getWidth(), media_tex->getHeight(), media_tex->getComponents(), true); diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index c8780f1c10..5753615a43 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -182,7 +182,7 @@ private: // Implementation functions not exported into header file class LLViewerMediaImpl - : public LLMouseHandler, public LLRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler + : public LLMouseHandler, public LLThreadSafeRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler { LOG_CLASS(LLViewerMediaImpl); public: @@ -432,7 +432,7 @@ private: private: // a single media url with some data and an impl. std::shared_ptr<LLPluginClassMedia> mMediaSource; - LLMutex mLock; + LLCoros::Mutex mLock; F64 mZoomFactor; LLUUID mTextureId; bool mMovieImageHasMips; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2074bda097..7e58a604f6 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -139,9 +139,11 @@ #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" +#include "gltf/asset.h" using namespace LLAvatarAppearanceDefines; @@ -3317,6 +3319,40 @@ bool enable_os_exception() #endif } + +bool enable_gltf() +{ + static LLCachedControl<bool> enablegltf(gSavedSettings, "GLTFEnabled", false); + return enablegltf; +} + +bool enable_gltf_save_as() +{ + if (enable_gltf()) + { + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject(); + if (obj) + { + if (obj->mGLTFAsset && obj->mGLTFAsset->isLocalPreview()) + { + return true; + } + + LLPermissions* permissions = LLSelectMgr::getInstance()->findObjectPermissions(obj); + if (permissions) + { + return permissions->allowExportBy(gAgent.getID()); + } + } + } + return false; +} + +bool enable_gltf_upload() +{ + return enable_gltf_save_as(); +} + class LLSelfRemoveAllAttachments : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -8076,16 +8112,52 @@ class LLAdvancedClickHDRIPreview: public view_listener_t }; -class LLAdvancedClickGLTFScenePreview : public view_listener_t +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; } }; +class LLAdvancedClickGLTFSaveAs : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LL::GLTFSceneManager::instance().saveAs(); + 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 { @@ -9738,7 +9810,10 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile"); view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark"); view_listener_t::addMenu(new LLAdvancedClickHDRIPreview(), "Advanced.ClickHDRIPreview"); - view_listener_t::addMenu(new LLAdvancedClickGLTFScenePreview(), "Advanced.ClickGLTFScenePreview"); + view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen"); + view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs"); + 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"); @@ -10043,6 +10118,9 @@ 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("EnableGLTFSaveAs", boost::bind(&enable_gltf_save_as)); + enable.add("EnableGLTFUpload", boost::bind(&enable_gltf_upload)); view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible"); view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 76fc629b33..5a32f9654d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -507,192 +507,6 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data) } } -// S32 exported_object_count = 0; -// S32 exported_image_count = 0; -// S32 current_object_count = 0; -// S32 current_image_count = 0; - -// extern LLNotifyBox *gExporterNotify; -// extern LLUUID gExporterRequestID; -// extern std::string gExportDirectory; - -// extern LLUploadDialog *gExportDialog; - -// std::string gExportedFile; - -// std::map<LLUUID, std::string> gImageChecksums; - -// void export_complete() -// { -// LLUploadDialog::modalUploadFinished(); -// gExporterRequestID.setNull(); -// gExportDirectory = ""; - -// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */ -// fseek(fXML, 0, SEEK_END); -// long length = ftell(fXML); -// fseek(fXML, 0, SEEK_SET); -// U8 *buffer = new U8[length + 1]; -// size_t nread = fread(buffer, 1, length, fXML); -// if (nread < (size_t) length) -// { -// LL_WARNS("Messaging") << "Short read" << LL_ENDL; -// } -// buffer[nread] = '\0'; -// fclose(fXML); - -// char *pos = (char *)buffer; -// while ((pos = strstr(pos+1, "<sl:image ")) != 0) -// { -// char *pos_check = strstr(pos, "checksum=\""); - -// if (pos_check) -// { -// char *pos_uuid = strstr(pos_check, "\">"); - -// if (pos_uuid) -// { -// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */ -// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */ -// image_uuid_str[UUID_STR_SIZE-1] = 0; - -// LLUUID image_uuid(image_uuid_str); - -// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL; - -// std::map<LLUUID, std::string>::iterator itor = gImageChecksums.find(image_uuid); -// if (itor != gImageChecksums.end()) -// { -// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL; -// if (!itor->second.empty()) -// { -// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */ -// } -// } -// } -// } -// } - -// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */ -// if (fwrite(buffer, 1, length, fXMLOut) != length) -// { -// LL_WARNS("Messaging") << "Short write" << LL_ENDL; -// } -// fclose(fXMLOut); - -// delete [] buffer; -// } - - -// void exported_item_complete(const LLTSCode status, void *user_data) -// { -// //std::string *filename = (std::string *)user_data; - -// if (status < LLTS_OK) -// { -// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL; -// } -// else -// { -// ++current_object_count; -// if (current_image_count == exported_image_count && current_object_count == exported_object_count) -// { -// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL; - -// export_complete(); -// } -// else -// { -// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); -// } -// } -// } - -// struct exported_image_info -// { -// LLUUID image_id; -// std::string filename; -// U32 image_num; -// }; - -// void exported_j2c_complete(const LLTSCode status, void *user_data) -// { -// exported_image_info *info = (exported_image_info *)user_data; -// LLUUID image_id = info->image_id; -// U32 image_num = info->image_num; -// std::string filename = info->filename; -// delete info; - -// if (status < LLTS_OK) -// { -// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL; -// } -// else -// { -// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ -// if (fIn) -// { -// LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C; -// LLPointer<LLImageTGA> TargaUtility = new LLImageTGA; - -// fseek(fIn, 0, SEEK_END); -// S32 length = ftell(fIn); -// fseek(fIn, 0, SEEK_SET); -// U8 *buffer = ImageUtility->allocateData(length); -// if (fread(buffer, 1, length, fIn) != length) -// { -// LL_WARNS("Messaging") << "Short read" << LL_ENDL; -// } -// fclose(fIn); -// LLFile::remove(filename); - -// // Convert to TGA -// LLPointer<LLImageRaw> image = new LLImageRaw(); - -// ImageUtility->updateData(); -// ImageUtility->decode(image, 100000.0f); - -// TargaUtility->encode(image); -// U8 *data = TargaUtility->getData(); -// S32 data_size = TargaUtility->getDataSize(); - -// std::string file_path = gDirUtilp->getDirName(filename); - -// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename; -// //S32 name_len = output_file.length(); -// //strcpy(&output_file[name_len-3], "tga"); -// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */ -// char md5_hash_string[33]; /* Flawfinder: ignore */ -// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */ -// if (fOut) -// { -// if (fwrite(data, 1, data_size, fOut) != data_size) -// { -// LL_WARNS("Messaging") << "Short write" << LL_ENDL; -// } -// fseek(fOut, 0, SEEK_SET); -// fclose(fOut); -// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */ -// LLMD5 my_md5_hash(fOut); -// my_md5_hash.hex_digest(md5_hash_string); -// } - -// gImageChecksums.insert(std::pair<LLUUID, std::string>(image_id, md5_hash_string)); -// } -// } - -// ++current_image_count; -// if (current_image_count == exported_image_count && current_object_count == exported_object_count) -// { -// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL; -// export_complete(); -// } -// else -// { -// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); -// } -//} - void process_derez_ack(LLMessageSystem*, void**) { if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount(); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 88e68a929f..8af0057c88 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -107,6 +107,7 @@ #include "llmeshrepository.h" #include "llgltfmateriallist.h" #include "llgl.h" +#include "gltf/asset.h" //#define DEBUG_UPDATE_TYPE @@ -4248,7 +4249,7 @@ LLMatrix4a LLViewerObject::getGLTFAssetToAgentTransform() const LLMatrix4 root; root.initScale(getScale()); root.rotate(getRenderRotation()); - root.translate(getPositionAgent()); + root.translate(getRenderPosition()); LLMatrix4a mat; mat.loadu((F32*)root.mMatrix); @@ -4272,7 +4273,7 @@ LLMatrix4a LLViewerObject::getAgentToGLTFAssetTransform() const scale.mV[1] = 1.f / scale.mV[1]; scale.mV[2] = 1.f / scale.mV[2]; - root.translate(-getPositionAgent()); + root.translate(-getRenderPosition()); root.rotate(~getRenderRotation()); LLMatrix4 scale_mat; @@ -4289,13 +4290,15 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const { LLMatrix4a mat; - if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) + if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) { auto& node = mGLTFAsset->mNodes[node_index]; 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; } @@ -4306,6 +4309,7 @@ LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const return mat; } + void LLViewerObject::getGLTFNodeTransformAgent(S32 node_index, LLVector3* position, LLQuaternion* rotation, LLVector3* scale) const { LLMatrix4a node_to_agent = getGLTFNodeTransformAgent(node_index); @@ -4343,7 +4347,7 @@ void decomposeMatrix(const LLMatrix4a& mat, LLVector3& position, LLQuaternion& r void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion& rotation) { - if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) + if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) { auto& node = mGLTFAsset->mNodes[node_index]; @@ -4353,7 +4357,9 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion if (node.mParent != -1) { auto& parent = mGLTFAsset->mNodes[node.mParent]; - matMul(agent_to_asset, parent.mAssetMatrixInv, agent_to_node); + LLMatrix4a ami; + ami.loadu(glm::value_ptr(parent.mAssetMatrixInv)); + matMul(agent_to_asset, ami, agent_to_node); } LLQuaternion agent_to_node_rot(agent_to_node.asMatrix4()); @@ -4365,9 +4371,13 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion LLVector3 pos; LLQuaternion rot; LLVector3 scale; - decomposeMatrix(node.mMatrix, pos, rot, scale); + LLMatrix4a mat; + mat.loadu(glm::value_ptr(node.mMatrix)); + decomposeMatrix(mat, pos, rot, scale); + + mat.asMatrix4().initAll(scale, new_rot, pos); - node.mMatrix.asMatrix4().initAll(scale, new_rot, pos); + node.mMatrix = glm::make_mat4(mat.getF32ptr()); mGLTFAsset->updateTransforms(); } @@ -4375,13 +4385,15 @@ void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset) { - if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) + if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size()) { auto& node = mGLTFAsset->mNodes[node_index]; 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; @@ -4398,7 +4410,12 @@ void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset) trans.setIdentity(); trans.mMatrix[3] = offset_v; - matMul(trans, node.mMatrix, node.mMatrix); + LLMatrix4a mat; + mat.loadu(glm::value_ptr(node.mMatrix)); + + matMul(trans, mat, mat); + + node.mMatrix = glm::make_mat4(mat.getF32ptr()); // TODO -- only update transforms for this node and its children (or use a dirty flag) mGLTFAsset->updateTransforms(); @@ -7489,6 +7506,23 @@ void LLViewerObject::shrinkWrap() } } +void LLViewerObject::setGLTFAsset(const LLUUID& id) +{ + //get the sculpt params and set the sculpt type and id + auto* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_SCULPT); + + LLSculptParams* sculpt_params = (LLSculptParams*)param->data; + sculpt_params->setSculptTexture(id, LL_SCULPT_TYPE_GLTF); + + setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, true, true); + + // Update the volume + LLVolumeParams volume_params; + volume_params.setSculptID(id, LL_SCULPT_TYPE_GLTF); + updateVolume(volume_params); +} + + class ObjectPhysicsProperties : public LLHTTPNode { public: diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 3f12fb5a61..09584d22a8 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -1,3 +1,4 @@ + /** * @file llviewerobject.h * @brief Description of LLViewerObject class, which is the base class for most objects in the viewer. @@ -45,7 +46,14 @@ #include "llbbox.h" #include "llrigginginfo.h" #include "llreflectionmap.h" -#include "gltf/asset.h" + +namespace LL +{ + namespace GLTF + { + class Asset; + } +} class LLAgent; // TODO: Get rid of this. class LLAudioSource; @@ -736,8 +744,13 @@ 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 - LLPointer<LL::GLTF::Asset> mGLTFAsset; + std::shared_ptr<LL::GLTF::Asset> mGLTFAsset; // Pipeline classes LLPointer<LLDrawable> mDrawable; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 662bfaa747..b5b127b27a 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -55,6 +55,7 @@ #include "llfloaterregioninfo.h" #include "llgltfmateriallist.h" #include "llhttpnode.h" +#include "llpbrterrainfeatures.h" #include "llregioninfomodel.h" #include "llsdutil.h" #include "llstartup.h" @@ -2480,13 +2481,30 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features) { gSavedSettings.setBOOL("UIPreviewMaterial", false); } + + if (features.has("GLTFEnabled")) + { + bool enabled = features["GLTFEnabled"]; + gSavedSettings.setBOOL("GLTFEnabled", enabled); + } + else + { + gSavedSettings.setBOOL("GLTFEnabled", false); + } + + if (features.has("PBRTerrainTransformsEnabled")) + { + bool enabled = features["PBRTerrainTransformsEnabled"]; + gSavedSettings.setBOOL("RenderTerrainPBRTransformsEnabled", enabled); + } + else + { + gSavedSettings.setBOOL("RenderTerrainPBRTransformsEnabled", false); + } }; - auto workqueue = LL::WorkQueue::getInstance("mainloop"); - if (workqueue) - { - LL::WorkQueue::postMaybe(workqueue, work); - } + + LLAppViewer::instance()->postToMainCoro(work); } //this is called when the parent is not cacheable. @@ -3124,6 +3142,17 @@ void LLViewerRegion::unpackRegionHandshake() { compp->setParamsReady(); } + + LLPBRTerrainFeatures::queueQuery(*this, [](LLUUID region_id, bool success, const LLModifyRegion& composition_changes) + { + if (!success) { return; } + LLViewerRegion* region = LLWorld::getInstance()->getRegionFromID(region_id); + if (!region) { return; } + LLVLComposition* compp = region->getComposition(); + if (!compp) { return; } + compp->apply(composition_changes); + LLFloaterRegionInfo::sRefreshFromRegion(region); + }); } @@ -3221,6 +3250,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("MapLayerGod"); capabilityNames.append("MeshUploadFlag"); capabilityNames.append("ModifyMaterialParams"); + capabilityNames.append("ModifyRegion"); capabilityNames.append("NavMeshGenerationStatus"); capabilityNames.append("NewFileAgentInventory"); capabilityNames.append("ObjectAnimation"); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 9958526a2b..37fbfccbbb 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -228,6 +228,9 @@ LLGLSLShader gDeferredPBRAlphaProgram; LLGLSLShader gDeferredSkinnedPBRAlphaProgram; LLGLSLShader gDeferredPBRTerrainProgram; +LLGLSLShader gGLTFPBRMetallicRoughnessProgram; + + //helper for making a rigged variant of a given shader static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader) { @@ -235,13 +238,99 @@ static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader riggedShader.mFeatures = shader.mFeatures; riggedShader.mFeatures.hasObjectSkinning = true; riggedShader.mDefines = shader.mDefines; // NOTE: Must come before addPermutation + riggedShader.addPermutation("HAS_SKIN", "1"); riggedShader.mShaderFiles = shader.mShaderFiles; riggedShader.mShaderLevel = shader.mShaderLevel; riggedShader.mShaderGroup = shader.mShaderGroup; shader.mRiggedVariant = &riggedShader; - return riggedShader.createShader(NULL, NULL); + return riggedShader.createShader(); +} + + +static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool alpha_blend, bool rigged, bool unlit, bool multi_uv, bool use_sun_shadow) +{ + variant.mName = shader.mName.c_str(); + variant.mFeatures = shader.mFeatures; + variant.mShaderFiles = shader.mShaderFiles; + variant.mShaderLevel = shader.mShaderLevel; + variant.mShaderGroup = shader.mShaderGroup; + + variant.mDefines = shader.mDefines; // NOTE: Must come before addPermutation + + variant.addPermutation("MAX_JOINTS_PER_GLTF_OBJECT", std::to_string(LLSkinningUtil::getMaxGLTFJointCount())); + + if (rigged) + { + variant.addPermutation("HAS_SKIN", "1"); + } + + if (unlit) + { + variant.addPermutation("UNLIT", "1"); + } + + if (multi_uv) + { + variant.addPermutation("MULTI_UV", "1"); + } + + if (alpha_blend) + { + variant.addPermutation("ALPHA_BLEND", "1"); + + variant.mFeatures.calculatesLighting = false; + variant.mFeatures.hasLighting = false; + variant.mFeatures.isAlphaLighting = true; + variant.mFeatures.hasSrgb = true; + variant.mFeatures.calculatesAtmospherics = true; + variant.mFeatures.hasAtmospherics = true; + variant.mFeatures.hasGamma = true; + variant.mFeatures.hasShadows = use_sun_shadow; + variant.mFeatures.isDeferred = true; // include deferredUtils + variant.mFeatures.hasReflectionProbes = true; + + if (use_sun_shadow) + { + variant.addPermutation("HAS_SUN_SHADOW", "1"); + } + + bool success = variant.createShader(); + llassert(success); + + // Alpha Shader Hack + // See: LLRender::syncMatrices() + variant.mFeatures.calculatesLighting = true; + variant.mFeatures.hasLighting = true; + + return success; + } + else + { + return variant.createShader(); + } +} + +static bool make_gltf_variants(LLGLSLShader& shader, bool use_sun_shadow) +{ + shader.mFeatures.mGLTF = true; + shader.mGLTFVariants.resize(LLGLSLShader::NUM_GLTF_VARIANTS); + + for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i) + { + bool alpha_blend = i & LLGLSLShader::GLTFVariant::ALPHA_BLEND; + bool rigged = i & LLGLSLShader::GLTFVariant::RIGGED; + bool unlit = i & LLGLSLShader::GLTFVariant::UNLIT; + bool multi_uv = i & LLGLSLShader::GLTFVariant::MULTI_UV; + + if (!make_gltf_variant(shader, shader.mGLTFVariants[i], alpha_blend, rigged, unlit, multi_uv, use_sun_shadow)) + { + return false; + } + } + + return true; } #ifdef SHOW_ASSERT @@ -329,6 +418,7 @@ void LLViewerShaderMgr::finalizeShaderList() mShaderList.push_back(&gDeferredDiffuseProgram); mShaderList.push_back(&gDeferredBumpProgram); mShaderList.push_back(&gDeferredPBROpaqueProgram); + mShaderList.push_back(&gGLTFPBRMetallicRoughnessProgram); mShaderList.push_back(&gDeferredAvatarProgram); mShaderList.push_back(&gDeferredTerrainProgram); mShaderList.push_back(&gDeferredPBRTerrainProgram); @@ -425,8 +515,8 @@ void LLViewerShaderMgr::setShaders() // when using indexed texture rendering, leave some texture units available for shadow and reflection maps static LLCachedControl<S32> reserved_texture_units(gSavedSettings, "RenderReservedTextureIndices", 14); - LLGLSLShader::sIndexedTextureChannels = - llclamp<S32>(max_texture_index, 1, gGLManager.mNumTextureImageUnits-reserved_texture_units); + LLGLSLShader::sIndexedTextureChannels = 4; + //llclamp<S32>(max_texture_index, 1, gGLManager.mNumTextureImageUnits-reserved_texture_units); reentrance = true; @@ -792,7 +882,7 @@ bool LLViewerShaderMgr::loadShadersWater() gWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; gWaterProgram.mShaderLevel = mShaderLevel[SHADER_WATER]; - success = gWaterProgram.createShader(NULL, NULL); + success = gWaterProgram.createShader(); llassert(success); } @@ -822,7 +912,7 @@ bool LLViewerShaderMgr::loadShadersWater() } gWaterEdgeProgram.mShaderGroup = LLGLSLShader::SG_WATER; gWaterEdgeProgram.mShaderLevel = mShaderLevel[SHADER_WATER]; - success = gWaterEdgeProgram.createShader(NULL, NULL); + success = gWaterEdgeProgram.createShader(); llassert(success); } @@ -842,7 +932,7 @@ bool LLViewerShaderMgr::loadShadersWater() { gUnderWaterProgram.addPermutation("TRANSPARENT_WATER", "1"); } - success = gUnderWaterProgram.createShader(NULL, NULL); + success = gUnderWaterProgram.createShader(); llassert(success); } @@ -891,7 +981,7 @@ bool LLViewerShaderMgr::loadShadersEffects() gGlowProgram.mShaderFiles.push_back(make_pair("effects/glowV.glsl", GL_VERTEX_SHADER)); gGlowProgram.mShaderFiles.push_back(make_pair("effects/glowF.glsl", GL_FRAGMENT_SHADER)); gGlowProgram.mShaderLevel = mShaderLevel[SHADER_EFFECT]; - success = gGlowProgram.createShader(NULL, NULL); + success = gGlowProgram.createShader(); if (!success) { LLPipeline::sRenderGlow = false; @@ -914,7 +1004,7 @@ bool LLViewerShaderMgr::loadShadersEffects() gGlowExtractProgram.addPermutation("HAS_NOISE", "1"); } - success = gGlowExtractProgram.createShader(NULL, NULL); + success = gGlowExtractProgram.createShader(); if (!success) { LLPipeline::sRenderGlow = false; @@ -1019,6 +1109,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHUDPBROpaqueProgram.unload(); gPBRGlowProgram.unload(); gDeferredPBROpaqueProgram.unload(); + gGLTFPBRMetallicRoughnessProgram.unload(); gDeferredSkinnedPBROpaqueProgram.unload(); gDeferredPBRAlphaProgram.unload(); gDeferredSkinnedPBRAlphaProgram.unload(); @@ -1036,7 +1127,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredHighlightProgram.mShaderFiles.push_back(make_pair("interface/highlightV.glsl", GL_VERTEX_SHADER)); gDeferredHighlightProgram.mShaderFiles.push_back(make_pair("deferred/highlightF.glsl", GL_FRAGMENT_SHADER)); gDeferredHighlightProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gDeferredHighlightProgram.createShader(NULL, NULL); + success = gDeferredHighlightProgram.createShader(); } if (success) @@ -1049,7 +1140,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredDiffuseProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels; gDeferredDiffuseProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredDiffuseProgram, gDeferredSkinnedDiffuseProgram); - success = success && gDeferredDiffuseProgram.createShader(NULL, NULL); + success = success && gDeferredDiffuseProgram.createShader(); } if (success) @@ -1061,7 +1152,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredDiffuseAlphaMaskProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels; gDeferredDiffuseAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredDiffuseAlphaMaskProgram, gDeferredSkinnedDiffuseAlphaMaskProgram); - success = success && gDeferredDiffuseAlphaMaskProgram.createShader(NULL, NULL); + success = success && gDeferredDiffuseAlphaMaskProgram.createShader(); } if (success) @@ -1071,7 +1162,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseV.glsl", GL_VERTEX_SHADER)); gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseAlphaMaskF.glsl", GL_FRAGMENT_SHADER)); gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredNonIndexedDiffuseAlphaMaskProgram.createShader(NULL, NULL); + success = gDeferredNonIndexedDiffuseAlphaMaskProgram.createShader(); llassert(success); } @@ -1082,7 +1173,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("deferred/diffuseNoColorV.glsl", GL_VERTEX_SHADER)); gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("deferred/diffuseAlphaMaskNoColorF.glsl", GL_FRAGMENT_SHADER)); gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.createShader(NULL, NULL); + success = gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.createShader(); llassert(success); } @@ -1094,7 +1185,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER)); gDeferredBumpProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredBumpProgram, gDeferredSkinnedBumpProgram); - success = success && gDeferredBumpProgram.createShader(NULL, NULL); + success = success && gDeferredBumpProgram.createShader(); llassert(success); } @@ -1176,7 +1267,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredMaterialProgram[i].mRiggedVariant = &gDeferredMaterialProgram[i + 0x10]; } - success = gDeferredMaterialProgram[i].createShader(NULL, NULL); + success = gDeferredMaterialProgram[i].createShader(); llassert(success); } } @@ -1204,13 +1295,29 @@ bool LLViewerShaderMgr::loadShadersDeferred() success = make_rigged_variant(gDeferredPBROpaqueProgram, gDeferredSkinnedPBROpaqueProgram); if (success) { - success = gDeferredPBROpaqueProgram.createShader(NULL, NULL); + success = gDeferredPBROpaqueProgram.createShader(); } llassert(success); } if (success) { + gGLTFPBRMetallicRoughnessProgram.mName = "GLTF PBR Metallic Roughness Shader"; + gGLTFPBRMetallicRoughnessProgram.mFeatures.hasSrgb = true; + + gGLTFPBRMetallicRoughnessProgram.mShaderFiles.clear(); + gGLTFPBRMetallicRoughnessProgram.mShaderFiles.push_back(make_pair("gltf/pbrmetallicroughnessV.glsl", GL_VERTEX_SHADER)); + gGLTFPBRMetallicRoughnessProgram.mShaderFiles.push_back(make_pair("gltf/pbrmetallicroughnessF.glsl", GL_FRAGMENT_SHADER)); + gGLTFPBRMetallicRoughnessProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; + gGLTFPBRMetallicRoughnessProgram.clearPermutations(); + + success = make_gltf_variants(gGLTFPBRMetallicRoughnessProgram, use_sun_shadow); + + llassert(success); + } + + if (success) + { gPBRGlowProgram.mName = " PBR Glow Shader"; gPBRGlowProgram.mFeatures.hasSrgb = true; gPBRGlowProgram.mShaderFiles.clear(); @@ -1221,7 +1328,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() success = make_rigged_variant(gPBRGlowProgram, gPBRGlowSkinnedProgram); if (success) { - success = gPBRGlowProgram.createShader(NULL, NULL); + success = gPBRGlowProgram.createShader(); } llassert(success); } @@ -1237,7 +1344,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHUDPBROpaqueProgram.clearPermutations(); gHUDPBROpaqueProgram.addPermutation("IS_HUD", "1"); - success = gHUDPBROpaqueProgram.createShader(NULL, NULL); + success = gHUDPBROpaqueProgram.createShader(); llassert(success); } @@ -1282,7 +1389,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() success = make_rigged_variant(*shader, gDeferredSkinnedPBRAlphaProgram); if (success) { - success = shader->createShader(NULL, NULL); + success = shader->createShader(); } llassert(success); @@ -1311,7 +1418,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() shader->addPermutation("IS_HUD", "1"); shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = shader->createShader(NULL, NULL); + success = shader->createShader(); llassert(success); } @@ -1325,7 +1432,6 @@ bool LLViewerShaderMgr::loadShadersDeferred() (mapping == 1 ? "flat" : "triplanar")); gDeferredPBRTerrainProgram.mFeatures.hasSrgb = true; gDeferredPBRTerrainProgram.mFeatures.isAlphaLighting = true; - gDeferredPBRTerrainProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels gDeferredPBRTerrainProgram.mFeatures.calculatesAtmospherics = true; gDeferredPBRTerrainProgram.mFeatures.hasAtmospherics = true; gDeferredPBRTerrainProgram.mFeatures.hasGamma = true; @@ -1338,7 +1444,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredPBRTerrainProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredPBRTerrainProgram.addPermutation("TERRAIN_PBR_DETAIL", llformat("%d", detail)); gDeferredPBRTerrainProgram.addPermutation("TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT", llformat("%d", mapping)); - success = gDeferredPBRTerrainProgram.createShader(NULL, NULL); + success = gDeferredPBRTerrainProgram.createShader(); llassert(success); } @@ -1349,7 +1455,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredTreeProgram.mShaderFiles.push_back(make_pair("deferred/treeV.glsl", GL_VERTEX_SHADER)); gDeferredTreeProgram.mShaderFiles.push_back(make_pair("deferred/treeF.glsl", GL_FRAGMENT_SHADER)); gDeferredTreeProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredTreeProgram.createShader(NULL, NULL); + success = gDeferredTreeProgram.createShader(); } if (success) @@ -1360,7 +1466,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredTreeShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredTreeShadowProgram.mRiggedVariant = &gDeferredSkinnedTreeShadowProgram; - success = gDeferredTreeShadowProgram.createShader(NULL, NULL); + success = gDeferredTreeShadowProgram.createShader(); llassert(success); } @@ -1372,7 +1478,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredSkinnedTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowSkinnedV.glsl", GL_VERTEX_SHADER)); gDeferredSkinnedTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredSkinnedTreeShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredSkinnedTreeShadowProgram.createShader(NULL, NULL); + success = gDeferredSkinnedTreeShadowProgram.createShader(); llassert(success); } @@ -1384,7 +1490,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredImpostorProgram.mShaderFiles.push_back(make_pair("deferred/impostorV.glsl", GL_VERTEX_SHADER)); gDeferredImpostorProgram.mShaderFiles.push_back(make_pair("deferred/impostorF.glsl", GL_FRAGMENT_SHADER)); gDeferredImpostorProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredImpostorProgram.createShader(NULL, NULL); + success = gDeferredImpostorProgram.createShader(); llassert(success); } @@ -1402,7 +1508,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredLightProgram.clearPermutations(); - success = gDeferredLightProgram.createShader(NULL, NULL); + success = gDeferredLightProgram.createShader(); llassert(success); } @@ -1422,7 +1528,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredMultiLightProgram[i].mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredMultiLightProgram[i].addPermutation("LIGHT_COUNT", llformat("%d", i+1)); - success = gDeferredMultiLightProgram[i].createShader(NULL, NULL); + success = gDeferredMultiLightProgram[i].createShader(); llassert(success); } } @@ -1440,7 +1546,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/spotLightF.glsl", GL_FRAGMENT_SHADER)); gDeferredSpotLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredSpotLightProgram.createShader(NULL, NULL); + success = gDeferredSpotLightProgram.createShader(); llassert(success); } @@ -1458,7 +1564,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/spotLightF.glsl", GL_FRAGMENT_SHADER)); gDeferredMultiSpotLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredMultiSpotLightProgram.createShader(NULL, NULL); + success = gDeferredMultiSpotLightProgram.createShader(); llassert(success); } @@ -1492,7 +1598,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredSunProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER)); gDeferredSunProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredSunProgram.createShader(NULL, NULL); + success = gDeferredSunProgram.createShader(); llassert(success); } @@ -1506,7 +1612,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair("deferred/blurLightF.glsl", GL_FRAGMENT_SHADER)); gDeferredBlurLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredBlurLightProgram.createShader(NULL, NULL); + success = gDeferredBlurLightProgram.createShader(); llassert(success); } @@ -1539,7 +1645,6 @@ bool LLViewerShaderMgr::loadShadersDeferred() shader->mFeatures.calculatesLighting = false; shader->mFeatures.hasLighting = false; shader->mFeatures.isAlphaLighting = true; - shader->mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels shader->mFeatures.hasSrgb = true; shader->mFeatures.calculatesAtmospherics = true; shader->mFeatures.hasAtmospherics = true; @@ -1573,7 +1678,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = shader->createShader(NULL, NULL); + success = shader->createShader(); llassert(success); // Hack @@ -1632,7 +1737,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() { shader->mRiggedVariant = shaders[1]; } - success = shader->createShader(NULL, NULL); + success = shader->createShader(); llassert(success); // End Hack @@ -1647,7 +1752,6 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarEyesProgram.mFeatures.calculatesAtmospherics = true; gDeferredAvatarEyesProgram.mFeatures.hasGamma = true; gDeferredAvatarEyesProgram.mFeatures.hasAtmospherics = true; - gDeferredAvatarEyesProgram.mFeatures.disableTextureIndex = true; gDeferredAvatarEyesProgram.mFeatures.hasSrgb = true; gDeferredAvatarEyesProgram.mFeatures.hasShadows = true; @@ -1655,7 +1759,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarEyesProgram.mShaderFiles.push_back(make_pair("deferred/avatarEyesV.glsl", GL_VERTEX_SHADER)); gDeferredAvatarEyesProgram.mShaderFiles.push_back(make_pair("deferred/diffuseF.glsl", GL_FRAGMENT_SHADER)); gDeferredAvatarEyesProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredAvatarEyesProgram.createShader(NULL, NULL); + success = gDeferredAvatarEyesProgram.createShader(); llassert(success); } @@ -1672,7 +1776,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredFullbrightProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightF.glsl", GL_FRAGMENT_SHADER)); gDeferredFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredFullbrightProgram, gDeferredSkinnedFullbrightProgram); - success = gDeferredFullbrightProgram.createShader(NULL, NULL); + success = gDeferredFullbrightProgram.createShader(); llassert(success); } @@ -1690,7 +1794,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHUDFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gHUDFullbrightProgram.clearPermutations(); gHUDFullbrightProgram.addPermutation("IS_HUD", "1"); - success = gHUDFullbrightProgram.createShader(NULL, NULL); + success = gHUDFullbrightProgram.createShader(); llassert(success); } @@ -1709,7 +1813,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredFullbrightAlphaMaskProgram.addPermutation("HAS_ALPHA_MASK","1"); gDeferredFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredFullbrightAlphaMaskProgram, gDeferredSkinnedFullbrightAlphaMaskProgram); - success = success && gDeferredFullbrightAlphaMaskProgram.createShader(NULL, NULL); + success = success && gDeferredFullbrightAlphaMaskProgram.createShader(); llassert(success); } @@ -1728,7 +1832,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHUDFullbrightAlphaMaskProgram.addPermutation("HAS_ALPHA_MASK", "1"); gHUDFullbrightAlphaMaskProgram.addPermutation("IS_HUD", "1"); gHUDFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gHUDFullbrightAlphaMaskProgram.createShader(NULL, NULL); + success = gHUDFullbrightAlphaMaskProgram.createShader(); llassert(success); } @@ -1749,7 +1853,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredFullbrightAlphaMaskAlphaProgram.addPermutation("IS_ALPHA", "1"); gDeferredFullbrightAlphaMaskAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredFullbrightAlphaMaskAlphaProgram, gDeferredSkinnedFullbrightAlphaMaskAlphaProgram); - success = success && gDeferredFullbrightAlphaMaskAlphaProgram.createShader(NULL, NULL); + success = success && gDeferredFullbrightAlphaMaskAlphaProgram.createShader(); llassert(success); } @@ -1770,7 +1874,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHUDFullbrightAlphaMaskAlphaProgram.addPermutation("IS_ALPHA", "1"); gHUDFullbrightAlphaMaskAlphaProgram.addPermutation("IS_HUD", "1"); gHUDFullbrightAlphaMaskAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = success && gHUDFullbrightAlphaMaskAlphaProgram.createShader(NULL, NULL); + success = success && gHUDFullbrightAlphaMaskAlphaProgram.createShader(); llassert(success); } @@ -1788,7 +1892,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredFullbrightShinyProgram.mFeatures.hasReflectionProbes = true; success = make_rigged_variant(gDeferredFullbrightShinyProgram, gDeferredSkinnedFullbrightShinyProgram); - success = success && gDeferredFullbrightShinyProgram.createShader(NULL, NULL); + success = success && gDeferredFullbrightShinyProgram.createShader(); llassert(success); } @@ -1807,7 +1911,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHUDFullbrightShinyProgram.mFeatures.hasReflectionProbes = true; gHUDFullbrightShinyProgram.clearPermutations(); gHUDFullbrightShinyProgram.addPermutation("IS_HUD", "1"); - success = gHUDFullbrightShinyProgram.createShader(NULL, NULL); + success = gHUDFullbrightShinyProgram.createShader(); llassert(success); } @@ -1823,7 +1927,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredEmissiveProgram.mShaderFiles.push_back(make_pair("deferred/emissiveF.glsl", GL_FRAGMENT_SHADER)); gDeferredEmissiveProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredEmissiveProgram, gDeferredSkinnedEmissiveProgram); - success = success && gDeferredEmissiveProgram.createShader(NULL, NULL); + success = success && gDeferredEmissiveProgram.createShader(); llassert(success); } @@ -1856,7 +1960,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredSoftenProgram.addPermutation("HAS_SSAO", "1"); } - success = gDeferredSoftenProgram.createShader(NULL, NULL); + success = gDeferredSoftenProgram.createShader(); llassert(success); } @@ -1878,7 +1982,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHazeProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gHazeProgram.createShader(NULL, NULL); + success = gHazeProgram.createShader(); llassert(success); } @@ -1902,7 +2006,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gHazeWaterProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gHazeWaterProgram.createShader(NULL, NULL); + success = gHazeWaterProgram.createShader(); llassert(success); } @@ -1915,7 +2019,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredShadowProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredShadowProgram.mRiggedVariant = &gDeferredSkinnedShadowProgram; - success = gDeferredShadowProgram.createShader(NULL, NULL); + success = gDeferredShadowProgram.createShader(); llassert(success); } @@ -1930,7 +2034,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredSkinnedShadowProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredSkinnedShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; // gDeferredSkinnedShadowProgram.addPermutation("DEPTH_CLAMP", "1"); // disable depth clamp for now - success = gDeferredSkinnedShadowProgram.createShader(NULL, NULL); + success = gDeferredSkinnedShadowProgram.createShader(); llassert(success); } @@ -1944,7 +2048,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredShadowCubeProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER)); // gDeferredShadowCubeProgram.addPermutation("DEPTH_CLAMP", "1"); gDeferredShadowCubeProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredShadowCubeProgram.createShader(NULL, NULL); + success = gDeferredShadowCubeProgram.createShader(); llassert(success); } @@ -1962,7 +2066,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredShadowFullbrightAlphaMaskProgram.addPermutation("IS_FULLBRIGHT", "1"); gDeferredShadowFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredShadowFullbrightAlphaMaskProgram, gDeferredSkinnedShadowFullbrightAlphaMaskProgram); - success = success && gDeferredShadowFullbrightAlphaMaskProgram.createShader(NULL, NULL); + success = success && gDeferredShadowFullbrightAlphaMaskProgram.createShader(); llassert(success); } @@ -1976,7 +2080,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredShadowAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/shadowAlphaMaskF.glsl", GL_FRAGMENT_SHADER)); gDeferredShadowAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = make_rigged_variant(gDeferredShadowAlphaMaskProgram, gDeferredSkinnedShadowAlphaMaskProgram); - success = success && gDeferredShadowAlphaMaskProgram.createShader(NULL, NULL); + success = success && gDeferredShadowAlphaMaskProgram.createShader(); llassert(success); } @@ -1990,7 +2094,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredShadowGLTFAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredShadowGLTFAlphaMaskProgram.clearPermutations(); success = make_rigged_variant(gDeferredShadowGLTFAlphaMaskProgram, gDeferredSkinnedShadowGLTFAlphaMaskProgram); - success = success && gDeferredShadowGLTFAlphaMaskProgram.createShader(NULL, NULL); + success = success && gDeferredShadowGLTFAlphaMaskProgram.createShader(); llassert(success); } @@ -2003,7 +2107,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredShadowGLTFAlphaBlendProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredShadowGLTFAlphaBlendProgram.clearPermutations(); success = make_rigged_variant(gDeferredShadowGLTFAlphaBlendProgram, gDeferredSkinnedShadowGLTFAlphaBlendProgram); - success = success && gDeferredShadowGLTFAlphaBlendProgram.createShader(NULL, NULL); + success = success && gDeferredShadowGLTFAlphaBlendProgram.createShader(); llassert(success); } @@ -2016,7 +2120,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarShadowV.glsl", GL_VERTEX_SHADER)); gDeferredAvatarShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarShadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredAvatarShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredAvatarShadowProgram.createShader(NULL, NULL); + success = gDeferredAvatarShadowProgram.createShader(); llassert(success); } @@ -2028,7 +2132,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarAlphaShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaShadowV.glsl", GL_VERTEX_SHADER)); gDeferredAvatarAlphaShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaShadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredAvatarAlphaShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredAvatarAlphaShadowProgram.createShader(NULL, NULL); + success = gDeferredAvatarAlphaShadowProgram.createShader(); llassert(success); } if (success) @@ -2039,7 +2143,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarAlphaMaskShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaShadowV.glsl", GL_VERTEX_SHADER)); gDeferredAvatarAlphaMaskShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaMaskShadowF.glsl", GL_FRAGMENT_SHADER)); gDeferredAvatarAlphaMaskShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredAvatarAlphaMaskShadowProgram.createShader(NULL, NULL); + success = gDeferredAvatarAlphaMaskShadowProgram.createShader(); llassert(success); } @@ -2048,7 +2152,6 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredTerrainProgram.mName = "Deferred Terrain Shader"; gDeferredTerrainProgram.mFeatures.hasSrgb = true; gDeferredTerrainProgram.mFeatures.isAlphaLighting = true; - gDeferredTerrainProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels gDeferredTerrainProgram.mFeatures.calculatesAtmospherics = true; gDeferredTerrainProgram.mFeatures.hasAtmospherics = true; gDeferredTerrainProgram.mFeatures.hasGamma = true; @@ -2057,7 +2160,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredTerrainProgram.mShaderFiles.push_back(make_pair("deferred/terrainV.glsl", GL_VERTEX_SHADER)); gDeferredTerrainProgram.mShaderFiles.push_back(make_pair("deferred/terrainF.glsl", GL_FRAGMENT_SHADER)); gDeferredTerrainProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredTerrainProgram.createShader(NULL, NULL); + success = gDeferredTerrainProgram.createShader(); llassert(success); } @@ -2069,7 +2172,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarProgram.mShaderFiles.push_back(make_pair("deferred/avatarV.glsl", GL_VERTEX_SHADER)); gDeferredAvatarProgram.mShaderFiles.push_back(make_pair("deferred/avatarF.glsl", GL_FRAGMENT_SHADER)); gDeferredAvatarProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredAvatarProgram.createShader(NULL, NULL); + success = gDeferredAvatarProgram.createShader(); llassert(success); } @@ -2080,7 +2183,6 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarAlphaProgram.mFeatures.calculatesLighting = false; gDeferredAvatarAlphaProgram.mFeatures.hasLighting = false; gDeferredAvatarAlphaProgram.mFeatures.isAlphaLighting = true; - gDeferredAvatarAlphaProgram.mFeatures.disableTextureIndex = true; gDeferredAvatarAlphaProgram.mFeatures.hasSrgb = true; gDeferredAvatarAlphaProgram.mFeatures.calculatesAtmospherics = true; gDeferredAvatarAlphaProgram.mFeatures.hasAtmospherics = true; @@ -2103,7 +2205,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredAvatarAlphaProgram.createShader(NULL, NULL); + success = gDeferredAvatarAlphaProgram.createShader(); llassert(success); gDeferredAvatarAlphaProgram.mFeatures.calculatesLighting = true; @@ -2121,7 +2223,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gExposureProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gExposureProgram.mShaderFiles.push_back(make_pair("deferred/exposureF.glsl", GL_FRAGMENT_SHADER)); gExposureProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gExposureProgram.createShader(NULL, NULL); + success = gExposureProgram.createShader(); llassert(success); } @@ -2135,7 +2237,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gExposureProgramNoFade.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gExposureProgramNoFade.mShaderFiles.push_back(make_pair("deferred/exposureF.glsl", GL_FRAGMENT_SHADER)); gExposureProgramNoFade.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gExposureProgramNoFade.createShader(NULL, NULL); + success = gExposureProgramNoFade.createShader(); llassert(success); } @@ -2147,7 +2249,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gLuminanceProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gLuminanceProgram.mShaderFiles.push_back(make_pair("deferred/luminanceF.glsl", GL_FRAGMENT_SHADER)); gLuminanceProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gLuminanceProgram.createShader(NULL, NULL); + success = gLuminanceProgram.createShader(); llassert(success); } @@ -2161,7 +2263,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gDeferredPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredGammaCorrect.glsl", GL_FRAGMENT_SHADER)); gDeferredPostGammaCorrectProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredPostGammaCorrectProgram.createShader(NULL, NULL); + success = gDeferredPostGammaCorrectProgram.createShader(); llassert(success); } @@ -2176,7 +2278,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gNoPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gNoPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredGammaCorrect.glsl", GL_FRAGMENT_SHADER)); gNoPostGammaCorrectProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gNoPostGammaCorrectProgram.createShader(NULL, NULL); + success = gNoPostGammaCorrectProgram.createShader(); llassert(success); } @@ -2191,7 +2293,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gLegacyPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gLegacyPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredGammaCorrect.glsl", GL_FRAGMENT_SHADER)); gLegacyPostGammaCorrectProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gLegacyPostGammaCorrectProgram.createShader(NULL, NULL); + success = gLegacyPostGammaCorrectProgram.createShader(); llassert(success); } @@ -2204,7 +2306,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gFXAAProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER)); gFXAAProgram.mShaderFiles.push_back(make_pair("deferred/fxaaF.glsl", GL_FRAGMENT_SHADER)); gFXAAProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gFXAAProgram.createShader(NULL, NULL); + success = gFXAAProgram.createShader(); llassert(success); } @@ -2216,7 +2318,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredF.glsl", GL_FRAGMENT_SHADER)); gDeferredPostProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredPostProgram.createShader(NULL, NULL); + success = gDeferredPostProgram.createShader(); llassert(success); } @@ -2228,7 +2330,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredCoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gDeferredCoFProgram.mShaderFiles.push_back(make_pair("deferred/cofF.glsl", GL_FRAGMENT_SHADER)); gDeferredCoFProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredCoFProgram.createShader(NULL, NULL); + success = gDeferredCoFProgram.createShader(); llassert(success); } @@ -2240,7 +2342,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredDoFCombineProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gDeferredDoFCombineProgram.mShaderFiles.push_back(make_pair("deferred/dofCombineF.glsl", GL_FRAGMENT_SHADER)); gDeferredDoFCombineProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredDoFCombineProgram.createShader(NULL, NULL); + success = gDeferredDoFCombineProgram.createShader(); llassert(success); } @@ -2252,7 +2354,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoDoFF.glsl", GL_FRAGMENT_SHADER)); gDeferredPostNoDoFProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredPostNoDoFProgram.createShader(NULL, NULL); + success = gDeferredPostNoDoFProgram.createShader(); llassert(success); } @@ -2272,7 +2374,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gEnvironmentMapProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gEnvironmentMapProgram.mShaderGroup = LLGLSLShader::SG_SKY; - success = gEnvironmentMapProgram.createShader(NULL, NULL); + success = gEnvironmentMapProgram.createShader(); llassert(success); } @@ -2290,7 +2392,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredWLSkyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredWLSkyProgram.mShaderGroup = LLGLSLShader::SG_SKY; - success = gDeferredWLSkyProgram.createShader(NULL, NULL); + success = gDeferredWLSkyProgram.createShader(); llassert(success); } @@ -2308,7 +2410,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredWLCloudProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredWLCloudProgram.mShaderGroup = LLGLSLShader::SG_SKY; gDeferredWLCloudProgram.addConstant( LLGLSLShader::SHADER_CONST_CLOUD_MOON_DEPTH ); // SL-14113 - success = gDeferredWLCloudProgram.createShader(NULL, NULL); + success = gDeferredWLCloudProgram.createShader(); llassert(success); } @@ -2319,14 +2421,13 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredWLSunProgram.mFeatures.hasAtmospherics = true; gDeferredWLSunProgram.mFeatures.hasGamma = true; gDeferredWLSunProgram.mFeatures.hasAtmospherics = true; - gDeferredWLSunProgram.mFeatures.disableTextureIndex = true; gDeferredWLSunProgram.mFeatures.hasSrgb = true; gDeferredWLSunProgram.mShaderFiles.clear(); gDeferredWLSunProgram.mShaderFiles.push_back(make_pair("deferred/sunDiscV.glsl", GL_VERTEX_SHADER)); gDeferredWLSunProgram.mShaderFiles.push_back(make_pair("deferred/sunDiscF.glsl", GL_FRAGMENT_SHADER)); gDeferredWLSunProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredWLSunProgram.mShaderGroup = LLGLSLShader::SG_SKY; - success = gDeferredWLSunProgram.createShader(NULL, NULL); + success = gDeferredWLSunProgram.createShader(); llassert(success); } @@ -2338,7 +2439,6 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredWLMoonProgram.mFeatures.hasGamma = true; gDeferredWLMoonProgram.mFeatures.hasAtmospherics = true; gDeferredWLMoonProgram.mFeatures.hasSrgb = true; - gDeferredWLMoonProgram.mFeatures.disableTextureIndex = true; gDeferredWLMoonProgram.mShaderFiles.clear(); gDeferredWLMoonProgram.mShaderFiles.push_back(make_pair("deferred/moonV.glsl", GL_VERTEX_SHADER)); @@ -2346,7 +2446,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredWLMoonProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredWLMoonProgram.mShaderGroup = LLGLSLShader::SG_SKY; gDeferredWLMoonProgram.addConstant( LLGLSLShader::SHADER_CONST_CLOUD_MOON_DEPTH ); // SL-14113 - success = gDeferredWLMoonProgram.createShader(NULL, NULL); + success = gDeferredWLMoonProgram.createShader(); llassert(success); } @@ -2359,7 +2459,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredStarProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredStarProgram.mShaderGroup = LLGLSLShader::SG_SKY; gDeferredStarProgram.addConstant( LLGLSLShader::SHADER_CONST_STAR_DEPTH ); // SL-14113 - success = gDeferredStarProgram.createShader(NULL, NULL); + success = gDeferredStarProgram.createShader(); llassert(success); } @@ -2371,7 +2471,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gNormalMapGenProgram.mShaderFiles.push_back(make_pair("deferred/normgenF.glsl", GL_FRAGMENT_SHADER)); gNormalMapGenProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gNormalMapGenProgram.mShaderGroup = LLGLSLShader::SG_SKY; - success = gNormalMapGenProgram.createShader(NULL, NULL); + success = gNormalMapGenProgram.createShader(); } if (success) @@ -2381,7 +2481,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredGenBrdfLutProgram.mShaderFiles.push_back(make_pair("deferred/genbrdflutV.glsl", GL_VERTEX_SHADER)); gDeferredGenBrdfLutProgram.mShaderFiles.push_back(make_pair("deferred/genbrdflutF.glsl", GL_FRAGMENT_SHADER)); gDeferredGenBrdfLutProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredGenBrdfLutProgram.createShader(NULL, NULL); + success = gDeferredGenBrdfLutProgram.createShader(); } if (success) { @@ -2392,7 +2492,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gPostScreenSpaceReflectionProgram.mFeatures.hasScreenSpaceReflections = true; gPostScreenSpaceReflectionProgram.mFeatures.isDeferred = true; gPostScreenSpaceReflectionProgram.mShaderLevel = 3; - success = gPostScreenSpaceReflectionProgram.createShader(NULL, NULL); + success = gPostScreenSpaceReflectionProgram.createShader(); } if (success) { @@ -2401,7 +2501,7 @@ bool LLViewerShaderMgr::loadShadersDeferred() gDeferredBufferVisualProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER)); gDeferredBufferVisualProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredVisualizeBuffers.glsl", GL_FRAGMENT_SHADER)); gDeferredBufferVisualProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - success = gDeferredBufferVisualProgram.createShader(NULL, NULL); + success = gDeferredBufferVisualProgram.createShader(); } return success; @@ -2420,7 +2520,7 @@ bool LLViewerShaderMgr::loadShadersObject() gObjectBumpProgram.mShaderFiles.push_back(make_pair("objects/bumpF.glsl", GL_FRAGMENT_SHADER)); gObjectBumpProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT]; success = make_rigged_variant(gObjectBumpProgram, gSkinnedObjectBumpProgram); - success = success && gObjectBumpProgram.createShader(NULL, NULL); + success = success && gObjectBumpProgram.createShader(); if (success) { //lldrawpoolbump assumes "texture0" has channel 0 and "texture1" has channel 1 LLGLSLShader* shader[] = { &gObjectBumpProgram, &gSkinnedObjectBumpProgram }; @@ -2442,37 +2542,34 @@ bool LLViewerShaderMgr::loadShadersObject() gObjectAlphaMaskNoColorProgram.mFeatures.hasGamma = true; gObjectAlphaMaskNoColorProgram.mFeatures.hasAtmospherics = true; gObjectAlphaMaskNoColorProgram.mFeatures.hasLighting = true; - gObjectAlphaMaskNoColorProgram.mFeatures.disableTextureIndex = true; gObjectAlphaMaskNoColorProgram.mFeatures.hasAlphaMask = true; gObjectAlphaMaskNoColorProgram.mShaderFiles.clear(); gObjectAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("objects/simpleNoColorV.glsl", GL_VERTEX_SHADER)); gObjectAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER)); gObjectAlphaMaskNoColorProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT]; - success = gObjectAlphaMaskNoColorProgram.createShader(NULL, NULL); + success = gObjectAlphaMaskNoColorProgram.createShader(); } if (success) { gImpostorProgram.mName = "Impostor Shader"; - gImpostorProgram.mFeatures.disableTextureIndex = true; gImpostorProgram.mFeatures.hasSrgb = true; gImpostorProgram.mShaderFiles.clear(); gImpostorProgram.mShaderFiles.push_back(make_pair("objects/impostorV.glsl", GL_VERTEX_SHADER)); gImpostorProgram.mShaderFiles.push_back(make_pair("objects/impostorF.glsl", GL_FRAGMENT_SHADER)); gImpostorProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT]; - success = gImpostorProgram.createShader(NULL, NULL); + success = gImpostorProgram.createShader(); } if (success) { gObjectPreviewProgram.mName = "Object Preview Shader"; - gObjectPreviewProgram.mFeatures.disableTextureIndex = true; gObjectPreviewProgram.mShaderFiles.clear(); gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewV.glsl", GL_VERTEX_SHADER)); gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewF.glsl", GL_FRAGMENT_SHADER)); gObjectPreviewProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT]; success = make_rigged_variant(gObjectPreviewProgram, gSkinnedObjectPreviewProgram); - success = gObjectPreviewProgram.createShader(NULL, NULL); + success = gObjectPreviewProgram.createShader(); gObjectPreviewProgram.mFeatures.hasLighting = true; gSkinnedObjectPreviewProgram.mFeatures.hasLighting = true; } @@ -2485,13 +2582,11 @@ bool LLViewerShaderMgr::loadShadersObject() gPhysicsPreviewProgram.mFeatures.hasGamma = false; gPhysicsPreviewProgram.mFeatures.hasAtmospherics = false; gPhysicsPreviewProgram.mFeatures.hasLighting = false; - gPhysicsPreviewProgram.mFeatures.mIndexedTextureChannels = 0; - gPhysicsPreviewProgram.mFeatures.disableTextureIndex = true; gPhysicsPreviewProgram.mShaderFiles.clear(); gPhysicsPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewPhysicsV.glsl", GL_VERTEX_SHADER)); gPhysicsPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewPhysicsF.glsl", GL_FRAGMENT_SHADER)); gPhysicsPreviewProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT]; - success = gPhysicsPreviewProgram.createShader(NULL, NULL); + success = gPhysicsPreviewProgram.createShader(); gPhysicsPreviewProgram.mFeatures.hasLighting = false; } @@ -2527,12 +2622,11 @@ bool LLViewerShaderMgr::loadShadersAvatar() gAvatarProgram.mFeatures.hasAtmospherics = true; gAvatarProgram.mFeatures.hasLighting = true; gAvatarProgram.mFeatures.hasAlphaMask = true; - gAvatarProgram.mFeatures.disableTextureIndex = true; gAvatarProgram.mShaderFiles.clear(); gAvatarProgram.mShaderFiles.push_back(make_pair("avatar/avatarV.glsl", GL_VERTEX_SHADER)); gAvatarProgram.mShaderFiles.push_back(make_pair("avatar/avatarF.glsl", GL_FRAGMENT_SHADER)); gAvatarProgram.mShaderLevel = mShaderLevel[SHADER_AVATAR]; - success = gAvatarProgram.createShader(NULL, NULL); + success = gAvatarProgram.createShader(); /// Keep track of avatar levels if (gAvatarProgram.mShaderLevel != mShaderLevel[SHADER_AVATAR]) @@ -2551,12 +2645,11 @@ bool LLViewerShaderMgr::loadShadersAvatar() gAvatarEyeballProgram.mFeatures.hasAtmospherics = true; gAvatarEyeballProgram.mFeatures.hasLighting = true; gAvatarEyeballProgram.mFeatures.hasAlphaMask = true; - gAvatarEyeballProgram.mFeatures.disableTextureIndex = true; gAvatarEyeballProgram.mShaderFiles.clear(); gAvatarEyeballProgram.mShaderFiles.push_back(make_pair("avatar/eyeballV.glsl", GL_VERTEX_SHADER)); gAvatarEyeballProgram.mShaderFiles.push_back(make_pair("avatar/eyeballF.glsl", GL_FRAGMENT_SHADER)); gAvatarEyeballProgram.mShaderLevel = mShaderLevel[SHADER_AVATAR]; - success = gAvatarEyeballProgram.createShader(NULL, NULL); + success = gAvatarEyeballProgram.createShader(); } if( !success ) @@ -2582,7 +2675,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gHighlightProgram.mShaderFiles.push_back(make_pair("interface/highlightF.glsl", GL_FRAGMENT_SHADER)); gHighlightProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; success = make_rigged_variant(gHighlightProgram, gSkinnedHighlightProgram); - success = success && gHighlightProgram.createShader(NULL, NULL); + success = success && gHighlightProgram.createShader(); } if (success) @@ -2592,7 +2685,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gHighlightNormalProgram.mShaderFiles.push_back(make_pair("interface/highlightNormV.glsl", GL_VERTEX_SHADER)); gHighlightNormalProgram.mShaderFiles.push_back(make_pair("interface/highlightF.glsl", GL_FRAGMENT_SHADER)); gHighlightNormalProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gHighlightNormalProgram.createShader(NULL, NULL); + success = gHighlightNormalProgram.createShader(); } if (success) @@ -2602,7 +2695,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gHighlightSpecularProgram.mShaderFiles.push_back(make_pair("interface/highlightSpecV.glsl", GL_VERTEX_SHADER)); gHighlightSpecularProgram.mShaderFiles.push_back(make_pair("interface/highlightF.glsl", GL_FRAGMENT_SHADER)); gHighlightSpecularProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gHighlightSpecularProgram.createShader(NULL, NULL); + success = gHighlightSpecularProgram.createShader(); } if (success) @@ -2612,7 +2705,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gUIProgram.mShaderFiles.push_back(make_pair("interface/uiV.glsl", GL_VERTEX_SHADER)); gUIProgram.mShaderFiles.push_back(make_pair("interface/uiF.glsl", GL_FRAGMENT_SHADER)); gUIProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gUIProgram.createShader(NULL, NULL); + success = gUIProgram.createShader(); } if (success) @@ -2622,7 +2715,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gPathfindingProgram.mShaderFiles.push_back(make_pair("interface/pathfindingV.glsl", GL_VERTEX_SHADER)); gPathfindingProgram.mShaderFiles.push_back(make_pair("interface/pathfindingF.glsl", GL_FRAGMENT_SHADER)); gPathfindingProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gPathfindingProgram.createShader(NULL, NULL); + success = gPathfindingProgram.createShader(); } if (success) @@ -2632,7 +2725,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gPathfindingNoNormalsProgram.mShaderFiles.push_back(make_pair("interface/pathfindingNoNormalV.glsl", GL_VERTEX_SHADER)); gPathfindingNoNormalsProgram.mShaderFiles.push_back(make_pair("interface/pathfindingF.glsl", GL_FRAGMENT_SHADER)); gPathfindingNoNormalsProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gPathfindingNoNormalsProgram.createShader(NULL, NULL); + success = gPathfindingNoNormalsProgram.createShader(); } if (success) @@ -2642,7 +2735,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gGlowCombineProgram.mShaderFiles.push_back(make_pair("interface/glowcombineV.glsl", GL_VERTEX_SHADER)); gGlowCombineProgram.mShaderFiles.push_back(make_pair("interface/glowcombineF.glsl", GL_FRAGMENT_SHADER)); gGlowCombineProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gGlowCombineProgram.createShader(NULL, NULL); + success = gGlowCombineProgram.createShader(); if (success) { gGlowCombineProgram.bind(); @@ -2659,7 +2752,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gGlowCombineFXAAProgram.mShaderFiles.push_back(make_pair("interface/glowcombineFXAAV.glsl", GL_VERTEX_SHADER)); gGlowCombineFXAAProgram.mShaderFiles.push_back(make_pair("interface/glowcombineFXAAF.glsl", GL_FRAGMENT_SHADER)); gGlowCombineFXAAProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gGlowCombineFXAAProgram.createShader(NULL, NULL); + success = gGlowCombineFXAAProgram.createShader(); if (success) { gGlowCombineFXAAProgram.bind(); @@ -2677,7 +2770,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gTwoTextureCompareProgram.mShaderFiles.push_back(make_pair("interface/twotexturecompareV.glsl", GL_VERTEX_SHADER)); gTwoTextureCompareProgram.mShaderFiles.push_back(make_pair("interface/twotexturecompareF.glsl", GL_FRAGMENT_SHADER)); gTwoTextureCompareProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gTwoTextureCompareProgram.createShader(NULL, NULL); + success = gTwoTextureCompareProgram.createShader(); if (success) { gTwoTextureCompareProgram.bind(); @@ -2694,7 +2787,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gOneTextureFilterProgram.mShaderFiles.push_back(make_pair("interface/onetexturefilterV.glsl", GL_VERTEX_SHADER)); gOneTextureFilterProgram.mShaderFiles.push_back(make_pair("interface/onetexturefilterF.glsl", GL_FRAGMENT_SHADER)); gOneTextureFilterProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gOneTextureFilterProgram.createShader(NULL, NULL); + success = gOneTextureFilterProgram.createShader(); if (success) { gOneTextureFilterProgram.bind(); @@ -2710,7 +2803,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gSolidColorProgram.mShaderFiles.push_back(make_pair("interface/solidcolorV.glsl", GL_VERTEX_SHADER)); gSolidColorProgram.mShaderFiles.push_back(make_pair("interface/solidcolorF.glsl", GL_FRAGMENT_SHADER)); gSolidColorProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gSolidColorProgram.createShader(NULL, NULL); + success = gSolidColorProgram.createShader(); if (success) { gSolidColorProgram.bind(); @@ -2727,7 +2820,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER)); gOcclusionProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; gOcclusionProgram.mRiggedVariant = &gSkinnedOcclusionProgram; - success = gOcclusionProgram.createShader(NULL, NULL); + success = gOcclusionProgram.createShader(); } if (success) @@ -2738,7 +2831,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gSkinnedOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionSkinnedV.glsl", GL_VERTEX_SHADER)); gSkinnedOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER)); gSkinnedOcclusionProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gSkinnedOcclusionProgram.createShader(NULL, NULL); + success = gSkinnedOcclusionProgram.createShader(); } if (success) @@ -2748,7 +2841,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gOcclusionCubeProgram.mShaderFiles.push_back(make_pair("interface/occlusionCubeV.glsl", GL_VERTEX_SHADER)); gOcclusionCubeProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER)); gOcclusionCubeProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gOcclusionCubeProgram.createShader(NULL, NULL); + success = gOcclusionCubeProgram.createShader(); } if (success) @@ -2760,7 +2853,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gDebugProgram.mRiggedVariant = &gSkinnedDebugProgram; gDebugProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; success = make_rigged_variant(gDebugProgram, gSkinnedDebugProgram); - success = success && gDebugProgram.createShader(NULL, NULL); + success = success && gDebugProgram.createShader(); } if (success) @@ -2786,7 +2879,7 @@ bool LLViewerShaderMgr::loadShadersInterface() shader.addPermutation("HAS_ATTRIBUTE_TANGENT", "1"); } success = make_rigged_variant(shader, skinned_shader); - success = success && shader.createShader(NULL, NULL); + success = success && shader.createShader(); } } @@ -2797,7 +2890,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gClipProgram.mShaderFiles.push_back(make_pair("interface/clipV.glsl", GL_VERTEX_SHADER)); gClipProgram.mShaderFiles.push_back(make_pair("interface/clipF.glsl", GL_FRAGMENT_SHADER)); gClipProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gClipProgram.createShader(NULL, NULL); + success = gClipProgram.createShader(); } if (success) @@ -2807,7 +2900,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gBenchmarkProgram.mShaderFiles.push_back(make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER)); gBenchmarkProgram.mShaderFiles.push_back(make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER)); gBenchmarkProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gBenchmarkProgram.createShader(NULL, NULL); + success = gBenchmarkProgram.createShader(); } if (success) @@ -2823,7 +2916,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gReflectionProbeDisplayProgram.mShaderFiles.push_back(make_pair("interface/reflectionprobeV.glsl", GL_VERTEX_SHADER)); gReflectionProbeDisplayProgram.mShaderFiles.push_back(make_pair("interface/reflectionprobeF.glsl", GL_FRAGMENT_SHADER)); gReflectionProbeDisplayProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gReflectionProbeDisplayProgram.createShader(NULL, NULL); + success = gReflectionProbeDisplayProgram.createShader(); } if (success) @@ -2833,7 +2926,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gCopyProgram.mShaderFiles.push_back(make_pair("interface/copyV.glsl", GL_VERTEX_SHADER)); gCopyProgram.mShaderFiles.push_back(make_pair("interface/copyF.glsl", GL_FRAGMENT_SHADER)); gCopyProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gCopyProgram.createShader(NULL, NULL); + success = gCopyProgram.createShader(); } if (success) @@ -2845,7 +2938,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gCopyDepthProgram.clearPermutations(); gCopyDepthProgram.addPermutation("COPY_DEPTH", "1"); gCopyDepthProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gCopyDepthProgram.createShader(NULL, NULL); + success = gCopyDepthProgram.createShader(); } if (success) @@ -2855,7 +2948,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gAlphaMaskProgram.mShaderFiles.push_back(make_pair("interface/alphamaskV.glsl", GL_VERTEX_SHADER)); gAlphaMaskProgram.mShaderFiles.push_back(make_pair("interface/alphamaskF.glsl", GL_FRAGMENT_SHADER)); gAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gAlphaMaskProgram.createShader(NULL, NULL); + success = gAlphaMaskProgram.createShader(); } if (success) @@ -2869,7 +2962,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gReflectionMipProgram.mShaderFiles.push_back(make_pair("interface/splattexturerectV.glsl", GL_VERTEX_SHADER)); gReflectionMipProgram.mShaderFiles.push_back(make_pair("interface/reflectionmipF.glsl", GL_FRAGMENT_SHADER)); gReflectionMipProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gReflectionMipProgram.createShader(NULL, NULL); + success = gReflectionMipProgram.createShader(); } if (success) @@ -2883,7 +2976,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gGaussianProgram.mShaderFiles.push_back(make_pair("interface/splattexturerectV.glsl", GL_VERTEX_SHADER)); gGaussianProgram.mShaderFiles.push_back(make_pair("interface/gaussianF.glsl", GL_FRAGMENT_SHADER)); gGaussianProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gGaussianProgram.createShader(NULL, NULL); + success = gGaussianProgram.createShader(); } if (success && gGLManager.mHasCubeMapArray) @@ -2894,7 +2987,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenF.glsl", GL_FRAGMENT_SHADER)); gRadianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; gRadianceGenProgram.addPermutation("PROBE_FILTER_SAMPLES", "32"); - success = gRadianceGenProgram.createShader(NULL, NULL); + success = gRadianceGenProgram.createShader(); } if (success && gGLManager.mHasCubeMapArray) @@ -2906,7 +2999,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gHeroRadianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; gHeroRadianceGenProgram.addPermutation("HERO_PROBES", "1"); gHeroRadianceGenProgram.addPermutation("PROBE_FILTER_SAMPLES", "4"); - success = gHeroRadianceGenProgram.createShader(NULL, NULL); + success = gHeroRadianceGenProgram.createShader(); } if (success && gGLManager.mHasCubeMapArray) @@ -2916,7 +3009,7 @@ bool LLViewerShaderMgr::loadShadersInterface() gIrradianceGenProgram.mShaderFiles.push_back(make_pair("interface/irradianceGenV.glsl", GL_VERTEX_SHADER)); gIrradianceGenProgram.mShaderFiles.push_back(make_pair("interface/irradianceGenF.glsl", GL_FRAGMENT_SHADER)); gIrradianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - success = gIrradianceGenProgram.createShader(NULL, NULL); + success = gIrradianceGenProgram.createShader(); } if( !success ) diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index f0de9e9715..60ce8c430b 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -287,6 +287,9 @@ extern LLGLSLShader gDeferredPBROpaqueProgram; extern LLGLSLShader gDeferredPBRAlphaProgram; extern LLGLSLShader gHUDPBRAlphaProgram; +// GLTF shaders +extern LLGLSLShader gGLTFPBRMetallicRoughnessProgram; + // Encodes detail level for dropping textures, in accordance with the GLTF spec where possible // 0 is highest detail, -1 drops emissive, etc // Dropping metallic roughness is off-spec - Reserve for potato machines as needed diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index d526f1bd0d..c1062b9a01 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -335,6 +335,18 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const s return gTextureList.getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); } +//static +LLImageRaw* LLViewerTextureManager::getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype) +{ + return gTextureList.getRawImageFromMemory(data, size, mimetype); +} + +//static +LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype) +{ + return gTextureList.getImageFromMemory(data, size, mimetype); +} + LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host) { return gTextureList.getImageFromHost(image_id, f_type, host); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index d3d76f38e8..dc9182bf1b 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -440,8 +440,6 @@ private: bool mForceCallbackFetch; protected: - std::string mLocalFileName; - S32 mOrigWidth; S32 mOrigHeight; @@ -692,6 +690,14 @@ public: static LLViewerFetchedTexture* getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host) ; + // decode a given image data according to given mime type + // WARNING: caller is responsible for deleting the returned raw image + static LLImageRaw* getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype); + + // decode given image data according to given mime type + // WARNING: caller is responsible for deleting the returned image + static LLViewerFetchedTexture* getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype); + static void init() ; static void cleanup() ; }; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 21572ef620..b90c1868fc 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -360,7 +360,7 @@ void LLViewerTextureList::shutdown() mImageList.clear(); - mInitialized = false; //prevent loading textures again. + mInitialized = false ; //prevent loading textures again. } void LLViewerTextureList::dump() @@ -518,6 +518,39 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& return imagep; } +LLImageRaw* LLViewerTextureList::getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype) +{ + LLPointer<LLImageFormatted> image = LLImageFormatted::loadFromMemory(data, size, mimetype); + + if (image) + { + LLImageRaw* raw_image = new LLImageRaw(); + image->decode(raw_image, 0.f); + return raw_image; + } + else + { + return nullptr; + } +} + +LLViewerFetchedTexture* LLViewerTextureList::getImageFromMemory(const U8* data, U32 size, std::string_view mimetype) +{ + LLPointer<LLImageRaw> raw_image = getRawImageFromMemory(data, size, mimetype); + if (raw_image.notNull()) + { + LLViewerFetchedTexture* imagep = new LLViewerFetchedTexture(raw_image, FTT_LOCAL_FILE, true); + addImage(imagep, TEX_LIST_STANDARD); + + imagep->dontDiscard(); + imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_PREVIEW); + return imagep; + } + else + { + return nullptr; + } +} LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, FTType f_type, @@ -631,8 +664,8 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, imagep->forceActive() ; } - mFastCacheList.insert(imagep); - imagep->setInFastCacheList(true); + mFastCacheList.insert(imagep); + imagep->setInFastCacheList(true); return imagep ; } @@ -1310,63 +1343,63 @@ bool LLViewerTextureList::createUploadFile(const std::string& filename, LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; try { - // Load the image - LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); - if (image.isNull()) - { - LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL; - return false; - } - if (!image->load(filename)) - { - image->setLastError("Couldn't load the image to be uploaded."); - return false; - } - // Decompress or expand it in a raw image structure - LLPointer<LLImageRaw> raw_image = new LLImageRaw; - if (!image->decode(raw_image, 0.0f)) - { - image->setLastError("Couldn't decode the image to be uploaded."); - return false; - } - // Check the image constraints - if ((image->getComponents() != 3) && (image->getComponents() != 4)) - { - image->setLastError("Image files with less than 3 or more than 4 components are not supported."); - return false; - } - if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions) - { - std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", - min_image_dimentions, - min_image_dimentions, - image->getWidth(), - image->getHeight()); - image->setLastError(reason); - return false; - } - // Convert to j2c (JPEG2000) and save the file locally - LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square); - if (compressedImage.isNull()) - { - image->setLastError("Couldn't convert the image to jpeg2000."); - LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL; - return false; - } - if (!compressedImage->save(out_filename)) - { - image->setLastError("Couldn't create the jpeg2000 image for upload."); - LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL; - return false; - } - // Test to see if the encode and save worked - LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; - if (!integrity_test->loadAndValidate(out_filename)) - { - image->setLastError("The created jpeg2000 image is corrupt."); - LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL; - return false; - } + // Load the image + LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); + if (image.isNull()) + { + LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL; + return false; + } + if (!image->load(filename)) + { + image->setLastError("Couldn't load the image to be uploaded."); + return false; + } + // Decompress or expand it in a raw image structure + LLPointer<LLImageRaw> raw_image = new LLImageRaw; + if (!image->decode(raw_image, 0.0f)) + { + image->setLastError("Couldn't decode the image to be uploaded."); + return false; + } + // Check the image constraints + if ((image->getComponents() != 3) && (image->getComponents() != 4)) + { + image->setLastError("Image files with less than 3 or more than 4 components are not supported."); + return false; + } + if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions) + { + std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", + min_image_dimentions, + min_image_dimentions, + image->getWidth(), + image->getHeight()); + image->setLastError(reason); + return false; + } + // Convert to j2c (JPEG2000) and save the file locally + LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square); + if (compressedImage.isNull()) + { + image->setLastError("Couldn't convert the image to jpeg2000."); + LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL; + return false; + } + if (!compressedImage->save(out_filename)) + { + image->setLastError("Couldn't create the jpeg2000 image for upload."); + LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL; + return false; + } + // Test to see if the encode and save worked + LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; + if (!integrity_test->loadAndValidate( out_filename )) + { + image->setLastError("The created jpeg2000 image is corrupt."); + LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL; + return false; + } } catch (...) { diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 413209f50d..e4ebb7b0e8 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -192,6 +192,9 @@ private: const LLUUID& force_id = LLUUID::null ); + LLImageRaw* getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype); + LLViewerFetchedTexture* getImageFromMemory(const U8* data, U32 size, std::string_view mimetype); + LLViewerFetchedTexture* createImage(const LLUUID &image_id, FTType f_type, bool usemipmap = true, diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 368934dffc..ba255f2b24 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -115,14 +115,24 @@ LLTerrainMaterials::~LLTerrainMaterials() unboost(); } +void LLTerrainMaterials::apply(const LLModifyRegion& other) +{ + for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i) + { + const LLGLTFMaterial* other_override = other.getMaterialOverride(i); + LLGLTFMaterial* material_override = other_override ? new LLGLTFMaterial(*other_override) : nullptr; + setMaterialOverride(i, material_override); + } +} + bool LLTerrainMaterials::generateMaterials() { - if (texturesReady(true, true)) + if (makeTexturesReady(true, true)) { return true; } - if (materialsReady(true, true)) + if (makeMaterialsReady(true, true)) { return true; } @@ -188,24 +198,39 @@ void LLTerrainMaterials::setDetailAssetID(S32 asset, const LLUUID& id) mDetailTextures[asset] = fetch_terrain_texture(id); LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[asset]; mat = id.isNull() ? nullptr : gGLTFMaterialList.getMaterial(id); + mDetailRenderMaterials[asset] = nullptr; mMaterialTexturesSet[asset] = false; } +const LLGLTFMaterial* LLTerrainMaterials::getMaterialOverride(S32 asset) const +{ + return mDetailMaterialOverrides[asset]; +} + +void LLTerrainMaterials::setMaterialOverride(S32 asset, LLGLTFMaterial* mat_override) +{ + // Non-null overrides must be nontrivial. Otherwise, please set the override to null instead. + llassert(!mat_override || *mat_override != LLGLTFMaterial::sDefault); + + mDetailMaterialOverrides[asset] = mat_override; + mDetailRenderMaterials[asset] = nullptr; +} + LLTerrainMaterials::Type LLTerrainMaterials::getMaterialType() { LL_PROFILE_ZONE_SCOPED; - const bool use_textures = texturesReady(false, false) || !materialsReady(false, false); + const bool use_textures = makeTexturesReady(false, false) || !makeMaterialsReady(false, false); return use_textures ? Type::TEXTURE : Type::PBR; } -bool LLTerrainMaterials::texturesReady(bool boost, bool strict) +bool LLTerrainMaterials::makeTexturesReady(bool boost, bool strict) { bool ready[ASSET_COUNT]; - // *NOTE: Calls to textureReady may boost textures. Do not early-return. + // *NOTE: Calls to makeTextureReady may boost textures. Do not early-return. for (S32 i = 0; i < ASSET_COUNT; i++) { - ready[i] = mDetailTextures[i].notNull() && textureReady(mDetailTextures[i], boost); + ready[i] = mDetailTextures[i].notNull() && makeTextureReady(mDetailTextures[i], boost); } bool one_ready = false; @@ -221,13 +246,36 @@ bool LLTerrainMaterials::texturesReady(bool boost, bool strict) return one_ready; } -bool LLTerrainMaterials::materialsReady(bool boost, bool strict) +namespace +{ + bool material_asset_ready(LLFetchedGLTFMaterial* mat) { return mat && mat->isLoaded(); } +}; + +bool LLTerrainMaterials::makeMaterialsReady(bool boost, bool strict) { bool ready[ASSET_COUNT]; - // *NOTE: Calls to materialReady may boost materials/textures. Do not early-return. + // *NOTE: This section may boost materials/textures. Do not early-return if ready[i] is false. for (S32 i = 0; i < ASSET_COUNT; i++) { - ready[i] = materialReady(mDetailMaterials[i], mMaterialTexturesSet[i], boost, strict); + ready[i] = false; + LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[i]; + if (!material_asset_ready(mat)) { continue; } + + LLPointer<LLFetchedGLTFMaterial>& render_mat = mDetailRenderMaterials[i]; + if (!render_mat) + { + render_mat = new LLFetchedGLTFMaterial(); + *render_mat = *mat; + // This render_mat is effectively already loaded, because it gets its data from mat. + + LLPointer<LLGLTFMaterial>& override_mat = mDetailMaterialOverrides[i]; + if (override_mat) + { + render_mat->applyOverride(*override_mat); + } + } + + ready[i] = materialTexturesReady(render_mat, mMaterialTexturesSet[i], boost, strict); } #if 1 @@ -267,7 +315,7 @@ bool LLTerrainMaterials::materialsReady(bool boost, bool strict) // Boost the texture loading priority // Return true when ready to use (i.e. texture is sufficiently loaded) // static -bool LLTerrainMaterials::textureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost) +bool LLTerrainMaterials::makeTextureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost) { llassert(tex); if (!tex) { return false; } @@ -308,15 +356,13 @@ bool LLTerrainMaterials::textureReady(LLPointer<LLViewerFetchedTexture>& tex, bo return true; } -// Boost the loading priority of every known texture in the material -// Return true when ready to use +// Make sure to call material_asset_ready first +// strict = true -> all materials must be sufficiently loaded +// strict = false -> at least one material must be loaded // static -bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bool &textures_set, bool boost, bool strict) +bool LLTerrainMaterials::materialTexturesReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict) { - if (!mat || !mat->isLoaded()) - { - return false; - } + llassert(mat); // Material is loaded, but textures may not be if (!textures_set) @@ -331,17 +377,17 @@ bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bo mat->mEmissiveTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]); } - // *NOTE: Calls to textureReady may boost textures. Do not early-return. + // *NOTE: Calls to makeTextureReady may boost textures. Do not early-return. bool ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT]; ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = - mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].isNull() || textureReady(mat->mBaseColorTexture, boost); + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].isNull() || makeTextureReady(mat->mBaseColorTexture, boost); ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = - mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].isNull() || textureReady(mat->mNormalTexture, boost); + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].isNull() || makeTextureReady(mat->mNormalTexture, boost); ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].isNull() || - textureReady(mat->mMetallicRoughnessTexture, boost); + makeTextureReady(mat->mMetallicRoughnessTexture, boost); ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = - mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].isNull() || textureReady(mat->mEmissiveTexture, boost); + mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].isNull() || makeTextureReady(mat->mEmissiveTexture, boost); if (strict) { @@ -357,6 +403,16 @@ bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bo return true; } +// Boost the loading priority of every known texture in the material +// Return true when ready to use +// static +bool LLTerrainMaterials::makeMaterialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bool &textures_set, bool boost, bool strict) +{ + if (!material_asset_ready(mat)) { return false; } + + return materialTexturesReady(mat, textures_set, boost, strict); +} + // static const LLUUID (&LLVLComposition::getDefaultTextures())[ASSET_COUNT] { @@ -638,11 +694,11 @@ bool LLVLComposition::generateMinimapTileLand(const F32 x, const F32 y, const bool use_textures = getMaterialType() != LLTerrainMaterials::Type::PBR; if (use_textures) { - if (!texturesReady(true, true)) { return false; } + if (!makeTexturesReady(true, true)) { return false; } } else { - if (!materialsReady(true, true)) { return false; } + if (!makeMaterialsReady(true, true)) { return false; } } for (S32 i = 0; i < ASSET_COUNT; i++) @@ -668,19 +724,20 @@ bool LLVLComposition::generateMinimapTileLand(const F32 x, const F32 y, } else { - tex = mDetailMaterials[i]->mBaseColorTexture; - tex_emissive = mDetailMaterials[i]->mEmissiveTexture; - base_color_factor = LLColor3(mDetailMaterials[i]->mBaseColor); + LLPointer<LLFetchedGLTFMaterial>& mat = mDetailRenderMaterials[i]; + tex = mat->mBaseColorTexture; + tex_emissive = mat->mEmissiveTexture; + base_color_factor = LLColor3(mat->mBaseColor); // *HACK: Treat alpha as black - base_color_factor *= (mDetailMaterials[i]->mBaseColor.mV[VW]); - emissive_factor = mDetailMaterials[i]->mEmissiveColor; + base_color_factor *= (mat->mBaseColor.mV[VW]); + emissive_factor = mat->mEmissiveColor; has_base_color_factor = (base_color_factor.mV[VX] != 1.f || base_color_factor.mV[VY] != 1.f || base_color_factor.mV[VZ] != 1.f); has_emissive_factor = (emissive_factor.mV[VX] != 1.f || emissive_factor.mV[VY] != 1.f || emissive_factor.mV[VZ] != 1.f); - has_alpha = mDetailMaterials[i]->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_OPAQUE; + has_alpha = mat->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_OPAQUE; } if (!tex) { tex = LLViewerFetchedTexture::sWhiteImagep; } diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index cd5104d5a5..61c35ade28 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -35,9 +35,16 @@ class LLSurface; class LLViewerFetchedTexture; +class LLGLTFMaterial; class LLFetchedGLTFMaterial; -class LLTerrainMaterials +class LLModifyRegion +{ +public: + virtual const LLGLTFMaterial* getMaterialOverride(S32 asset) const = 0; +}; + +class LLTerrainMaterials : public LLModifyRegion { public: friend class LLDrawPoolTerrain; @@ -45,6 +52,8 @@ public: LLTerrainMaterials(); virtual ~LLTerrainMaterials(); + void apply(const LLModifyRegion& other); + // Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc. // So we need to compress heights into this range. static const S32 ASSET_COUNT = 4; @@ -62,20 +71,27 @@ public: virtual LLUUID getDetailAssetID(S32 asset); virtual void setDetailAssetID(S32 asset, const LLUUID& id); + const LLGLTFMaterial* getMaterialOverride(S32 asset) const override; + virtual void setMaterialOverride(S32 asset, LLGLTFMaterial* mat_override); Type getMaterialType(); - bool texturesReady(bool boost, bool strict); + bool makeTexturesReady(bool boost, bool strict); // strict = true -> all materials must be sufficiently loaded // strict = false -> at least one material must be loaded - bool materialsReady(bool boost, bool strict); + bool makeMaterialsReady(bool boost, bool strict); protected: void unboost(); - static bool textureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost); + static bool makeTextureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost); // strict = true -> all materials must be sufficiently loaded // strict = false -> at least one material must be loaded - static bool materialReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict); + static bool makeMaterialReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict); + // *NOTE: Prefer calling makeMaterialReady if mat is known to be LLFetchedGLTFMaterial + static bool materialTexturesReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict); + LLPointer<LLViewerFetchedTexture> mDetailTextures[ASSET_COUNT]; LLPointer<LLFetchedGLTFMaterial> mDetailMaterials[ASSET_COUNT]; + LLPointer<LLGLTFMaterial> mDetailMaterialOverrides[ASSET_COUNT]; + LLPointer<LLFetchedGLTFMaterial> mDetailRenderMaterials[ASSET_COUNT]; bool mMaterialTexturesSet[ASSET_COUNT]; }; @@ -124,9 +140,6 @@ public: bool getParamsReady() const { return mParamsReady; } protected: - static bool textureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost = false); - static bool materialReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost = false); - bool mParamsReady = false; LLSurface *mSurfacep; diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index f52130f784..c92de576f9 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -527,8 +527,19 @@ F32 LLVOCacheEntry::getSquaredPixelThreshold(bool is_front) return projection_threshold; } +extern bool gCubeSnapshot; + bool LLVOCacheEntry::isAnyVisible(const LLVector4a& camera_origin, const LLVector4a& local_camera_origin, F32 dist_threshold) { +#if 0 + // this is ill-conceived and should be removed pending QA + // In the name of saving memory, we evict objects that are still within view distance from memory + // This results in constant paging of objects in and out of memory, leading to poor performance + // and many unacceptable visual glitches when rotating the camera + + // Honestly, the entire VOCache partition system needs to be removed since it doubles the overhead of + // the spatial partition system and is redundant to the object cache, but this is a start + // - davep 2024.06.07 LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*)getGroup(); if(!group) { @@ -565,6 +576,9 @@ bool LLVOCacheEntry::isAnyVisible(const LLVector4a& camera_origin, const LLVecto } return vis; +#else + return true; +#endif } void LLVOCacheEntry::calcSceneContribution(const LLVector4a& camera_origin, bool needs_update, U32 last_update, F32 max_dist) @@ -1921,9 +1935,8 @@ void LLVOCache::writeGenericExtrasToCache(U64 handle, const LLUUID& id, const LL LLViewerRegion* pRegion = LLWorld::getInstance()->getRegionFromHandle(handle); U32 num_entries = 0; - U32 inmem_entries = 0; U32 skipped = 0; - inmem_entries = (U32)cache_extras_entry_map.size(); + size_t inmem_entries = cache_extras_entry_map.size(); for (auto [local_id, entry] : cache_extras_entry_map) { // Only write out GLTFOverrides that we can actually apply again on import. diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index da4df226ee..3c2a0ef6a3 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -6444,30 +6444,36 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta << ", proximal is " << inSpatialChannel() << LL_ENDL; - for (status_observer_set_t::iterator it = mStatusObservers.begin(); - it != mStatusObservers.end(); - ) - { - LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); - // In case onError() deleted an entry. - it = mStatusObservers.upper_bound(observer); - } + // this function is called from a coroutine, shuttle application hook back to main loop + auto work = [=]() + { + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); + } - // skipped to avoid speak button blinking - if ( status != LLVoiceClientStatusObserver::STATUS_JOINING - && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL - && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) - { - bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + // skipped to avoid speak button blinking + if (status != LLVoiceClientStatusObserver::STATUS_JOINING + && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL + && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED) + { + bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - gAgent.setVoiceConnected(voice_status); + gAgent.setVoiceConnected(voice_status); - if (voice_status) - { - LLFirstUse::speak(true); - } - } + if (voice_status) + { + LLFirstUse::speak(true); + } + } + }; + + LLAppViewer::instance()->postToMainCoro(work); } void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 876f034d64..de62256134 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -89,6 +89,7 @@ #include "llsculptidsize.h" #include "llavatarappearancedefines.h" #include "llgltfmateriallist.h" +#include "gltfscenemanager.h" const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; const F32 FORCE_CULL_AREA = 8.f; @@ -1133,6 +1134,11 @@ bool LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo } } + if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF) + { // notify GLTFSceneManager about new GLTF object + LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID()); + } + return true; } else if (NO_LOD == lod) @@ -1407,6 +1413,12 @@ bool LLVOVolume::calcLOD() return false; } + if (mGLTFAsset != nullptr) + { + // do not calculate LOD for GLTF objects + return false; + } + S32 cur_detail = 0; F32 radius; @@ -4616,7 +4628,7 @@ LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const bool LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32 *face_hitp, - LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) + LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) { if (!mbCanSelect @@ -5623,7 +5635,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LLVOVolume* vobj = drawablep->getVOVolume(); - if (!vobj || vobj->isDead()) + if (!vobj || vobj->isDead() || vobj->mGLTFAsset) { continue; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 81d594a1a8..f9ff8217af 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1881,6 +1881,11 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) { LLDrawable::drawable_vector_t::iterator curiter = iter++; LLDrawable *drawablep = *curiter; + if (!drawablep) + { + iter = moved_list.erase(curiter); + continue; + } bool done = true; if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE))) { @@ -6808,7 +6813,7 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst) mGlow[1].bindTexture(0, channel); } - channel = gLuminanceProgram.enableTexture(LLShaderMgr::DEFERRED_NORMAL); + channel = gLuminanceProgram.enableTexture(LLShaderMgr::NORMAL_MAP); if (channel > -1) { // bind the normal map to get the environment mask @@ -7273,7 +7278,7 @@ void LLPipeline::renderDoF(LLRenderTarget* src, LLRenderTarget* dst) LLVector4a result; result.clear(); - gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, false, false, true, true, nullptr, nullptr, nullptr, &result); + gViewerWindow->cursorIntersect(-1, -1, 512.f, nullptr, -1, false, false, true, true, nullptr, nullptr, nullptr, &result); focus_point.set(result.getF32ptr()); } @@ -7615,7 +7620,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_ gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); } - channel = shader.enableTexture(LLShaderMgr::DEFERRED_NORMAL, deferred_target->getUsage()); + channel = shader.enableTexture(LLShaderMgr::NORMAL_MAP, deferred_target->getUsage()); if (channel > -1) { deferred_target->bindTexture(2, channel, LLTexUnit::TFO_POINT); // frag_data[2] @@ -8650,7 +8655,7 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) LLRenderTarget* deferred_light_target = &mRT->deferredLight; stop_glerror(); - shader.disableTexture(LLShaderMgr::DEFERRED_NORMAL, deferred_target->getUsage()); + shader.disableTexture(LLShaderMgr::NORMAL_MAP, deferred_target->getUsage()); shader.disableTexture(LLShaderMgr::DEFERRED_DIFFUSE, deferred_target->getUsage()); shader.disableTexture(LLShaderMgr::DEFERRED_SPECULAR, deferred_target->getUsage()); shader.disableTexture(LLShaderMgr::DEFERRED_EMISSIVE, deferred_target->getUsage()); diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index e1eb513a31..0e7c522f74 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2858,6 +2858,70 @@ function="World.EnvPreset" parameter="mem_leaking" /> </menu_item_call> </menu> + <menu + create_jump_keys="true" + label="GLTF" + name="GLTF" + tear_off="true"> + <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="EnableGLTFSaveAs"/> + <menu_item_call.on_click + function="Advanced.ClickGLTFSaveAs" /> + </menu_item_call> + <menu_item_call + label="Upload..." + name="Upload..."> + <menu_item_call.on_enable + function="EnableGLTFUpload"/> + <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" label="Render Tests" @@ -2910,12 +2974,6 @@ function="World.EnvPreset" <menu_item_call.on_click function="Advanced.ClickHDRIPreview" /> </menu_item_call> - <menu_item_call - label="GLTF Scene Preview" - name="GLTF Scene Preview"> - <menu_item_call.on_click - function="Advanced.ClickGLTFScenePreview" /> - </menu_item_call> </menu> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9dc8393474..1584de6880 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -12461,7 +12461,7 @@ are wearing now. <notification icon="alertmodal.tga" - name="GLTFPreviewSelection" + name="GLTFOpenSelection" type="alert"> You must select an object to act as a handle to the GLTF asset you are previewing. <tag>fail</tag> @@ -12470,4 +12470,59 @@ are wearing now. yestext="OK"/> </notification> + <notification + icon="alertmodal.tga" + name="GLTFLoadFailed" + type="alert"> + Failed to load GLTF file. See log for details. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="GLTFSaveFailed" + type="alert"> + Failed to save GLTF file. See log for details. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="GLTFSaveSelection" + type="alert"> + You must select an object that has a GLTF asset associated with it. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="GLTFUploadSelection" + type="alert"> + You must select an object that has local-only GLTF asset associated with it. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="GLTFUploadInProgress" + type="alert"> + Upload is currently in progress. Please try again later. + <tag>fail</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml b/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml new file mode 100644 index 0000000000..9a90700056 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel + border="true" + follows="top|left" + height="460" + label="Terrain" + layout="topleft" + left="0" + name="Terrain" + top="320" + width="480"> + <text + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left="10" + name="region_text_lbl" + top="10" + width="100"> + Region: + </text> + <text + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left_delta="50" + name="region_text" + top_delta="0" + width="400"> + unknown + </text> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="detail_texture_text" + left="10" + top="35" + width="170"> + Terrain Textures + </text> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="detail_material_text" + left="10" + top="35" + width="170"> + Terrain Materials + </text> + <check_box + height="20" + halign="left" + valign="center" + follows="left|top" + layout="topleft" + top_delta="1" + left_delta="180" + label="PBR Metallic Roughness" + name="terrain_material_type" + tool_tip="If checked, use PBR Metallic Roughness materials for terrain. Otherwise, use textures." + left_pad="2" + width="200" /> + <texture_picker + follows="left|top" + height="121" + layout="topleft" + left="10" + name="texture_detail_0" + default_image_id="0bc58228-74a0-7e83-89bc-5c23464bcec5" + top_delta="30" + width="100" /> + <texture_picker + follows="top" + height="121" + layout="topleft" + left_pad="10" + name="texture_detail_1" + default_image_id="63338ede-0037-c4fd-855b-015d77112fc8" + top_delta="0" + width="100" /> + <texture_picker + follows="left|top" + height="121" + layout="topleft" + left_pad="10" + name="texture_detail_2" + default_image_id="303cd381-8560-7579-23f1-f0a880799740" + top_delta="0" + width="100" /> + <texture_picker + follows="left|top" + height="121" + layout="topleft" + left_pad="10" + name="texture_detail_3" + default_image_id="53a2f406-4895-1d13-d541-d2e3b86bc19c" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="121" + layout="topleft" + left="10" + name="material_detail_0" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="121" + layout="topleft" + left_pad="10" + name="material_detail_1" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="121" + layout="topleft" + left_pad="10" + name="material_detail_2" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <texture_picker + visible="false" + follows="left|top" + height="121" + layout="topleft" + left_pad="10" + name="material_detail_3" + pick_type="material" + default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a" + top_delta="0" + width="100" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl" + top_delta="104" + width="65"> + 1 (Low) + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="45" + name="height_text_lbl2" + top_delta="0" + width="100"> + 2 + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl3" + top_delta="0" + width="100"> + 3 + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl4" + top_delta="0" + width="100"> + 4 (High) + </text> + <layout_stack name="terrain_features_stack" + width="477" + height="230" + follows="all" + animate="false" + left="0" + top_delta="22" + orientation="vertical"> + <layout_panel name="frame_settings_terrain" + auto_resize="true" + user_resize="false" + height="230" + width="467" + min_height="0" + visible="true"> + <tab_container + follows="all" + halign="left" + height="230" + visible="true" + layout="topleft" + left="0" + name="terrain_tabs" + tab_position="top" + tab_width="110" + tab_padding_right="3" + top_pad="0" + width="700"> + <panel + border="true" + filename="panel_settings_terrain_elevation.xml" + label="Elevation" + layout="topleft" + left_delta="0" + top_pad="5" + name="terrain_elevation_panel" /> + <panel + border="true" + filename="panel_settings_terrain_transform.xml" + label="Transforms" + layout="topleft" + left_delta="0" + top_pad="5" + name="terrain_transform_panel" /> + </tab_container> + </layout_panel> + </layout_stack> + <button + enabled="true" + follows="left|top" + height="20" + label="Apply" + layout="topleft" + left="351" + name="apply_btn" + top_delta="310" + width="100" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml b/indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml new file mode 100644 index 0000000000..89443290ce --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml @@ -0,0 +1,307 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel + border="true" + follows="all" + label="Elevation" + layout="topleft" + left="0" + name="panel_settings_terrain_elevation" + top="0"> + <spinner + follows="left|top" + height="20" + label="Water Height" + label_width="120" + layout="topleft" + left="15" + max_val="100" + name="water_height_spin" + top_delta="18" + width="180" /> + <spinner + follows="left|top" + height="20" + increment="0.2" + label="Terrain Raise Limit" + label_width="120" + layout="topleft" + left="240" + max_val="100" + name="terrain_raise_spin" + top_delta="0" + width="180" /> + <spinner + follows="left|top" + height="20" + increment="0.2" + label="Terrain Lower Limit" + label_width="120" + layout="topleft" + left="240" + max_val="0" + min_val="-100" + name="terrain_lower_spin" + top_delta="20" + width="180" /> + <view_border + bevel_style="none" + follows="top|left" + height="60" + layout="topleft" + left="8" + top_delta="-30" + width="460" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl5" + top_delta="74" + width="300"> + Texture Elevation Ranges + </text> + <text + visible="false" + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl5_material" + top_delta="0" + width="300"> + Material Elevation Ranges + </text> + <text + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl10" + top_delta="30" + width="200" + word_wrap="true"> + These values represent the blend range for the textures above. + </text> + <text + visible="false" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl10_material" + top_delta="0" + width="200" + word_wrap="true"> + These values represent the blend range for the materials above. + </text> + <text + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + name="height_text_lbl11" + top_delta="32" + width="200" + word_wrap="true"> + Measured in meters, the LOW value is the MAXIMUM height of Texture #1, and the HIGH value is the MINIMUM height of Texture #4. + </text> + <text + visible="false" + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + name="height_text_lbl11_material" + top_delta="0" + width="200" + word_wrap="true"> + Measured in meters, the LOW value is the MAXIMUM height of Material #1, and the HIGH value is the MINIMUM height of Material #4. + </text> + <text + follows="left|top" + height="20" + layout="topleft" + left="270" + name="height_text_lbl6" + top_delta="-62" + width="100"> + Northwest + </text> + <text + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl7" + top_delta="0" + width="100"> + Northeast + </text> +<!-- northwest low--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left="230" + max_val="500" + min_val="-500" + name="height_start_spin_1" + top_delta="15" + width="100" /> +<!-- northeast low--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_start_spin_3" + top_delta="0" + width="100" /> +<!-- northwest high--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left="230" + max_val="500" + min_val="-500" + name="height_range_spin_1" + top_delta="20" + width="100" /> +<!-- northeast high--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_range_spin_3" + top_delta="0" + width="100" /> + <text + follows="left|top" + height="20" + layout="topleft" + left="270" + name="height_text_lbl8" + top_pad="10" + width="100"> + Southwest + </text> + <text + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl9" + top_delta="0" + width="100"> + Southeast + </text> +<!-- southwest low--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left="230" + max_val="500" + min_val="-500" + name="height_start_spin_0" + top_delta="15" + width="100" /> +<!-- southeast low--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_start_spin_2" + top_delta="0" + width="100" /> +<!--southwest high--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left="230" + max_val="500" + min_val="-500" + name="height_range_spin_0" + top_delta="20" + width="100" /> +<!-- southeast high--> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_range_spin_2" + top_delta="0" + width="100" /> +<!-- Terrain Download/Upload/Bake buttons --> + <button + follows="left|top" + height="20" + label="Download RAW terrain..." + layout="topleft" + left="10" + name="download_raw_btn" + tool_tip="Available only to estate owners, not managers" + top_delta="40" + width="160" /> + <button + follows="left|top" + height="20" + label="Upload RAW terrain..." + layout="topleft" + left_pad="10" + top_delta="0" + name="upload_raw_btn" + tool_tip="Available only to estate owners, not managers" + width="160" /> + <button + follows="left|top" + height="20" + label="Bake Terrain" + layout="topleft" + left_pad="10" + name="bake_terrain_btn" + tool_tip="Set current terrain as mid-point for raise/lower limits" + width="100" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml b/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml new file mode 100644 index 0000000000..0bf0d8cffc --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml @@ -0,0 +1,421 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel + border="true" + follows="all" + label="Samping" + layout="topleft" + left="0" + name="panel_settings_terrain_transform" + top="0"> + <text + type="string" + length="1" + halign="center" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain0_label" + left="48" + top_delta="0" + width="62"> + 1 + </text> + <text + type="string" + length="1" + halign="center" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain1_label" + left_delta="110" + top_delta="0" + width="62"> + 2 + </text> + <text + type="string" + length="1" + halign="center" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain2_label" + left_delta="110" + top_delta="0" + width="62"> + 3 + </text> + <text + type="string" + length="1" + halign="center" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain3_label" + left_delta="110" + top_delta="0" + width="62"> + 4 + </text> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain0ScaleU_label" + left="10" + top_pad="0" + width="170"> + Scale u + </text> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left="46" + min_val="-100" + max_val="100" + name="terrain0ScaleU" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-100" + max_val="100" + name="terrain1ScaleU" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-100" + max_val="100" + name="terrain2ScaleU" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-100" + max_val="100" + name="terrain3ScaleU" + width="64" /> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="8" + top_pad="8" + name="terrainScaleU_horizontal" + width="432" /> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain0ScaleV_label" + left="10" + top_pad="0" + width="170"> + Scale v + </text> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale v" + label_width="0" + layout="topleft" + left="46" + min_val="-100" + max_val="100" + name="terrain0ScaleV" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-100" + max_val="100" + name="terrain1ScaleV" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-100" + max_val="100" + name="terrain2ScaleV" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="1" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-100" + max_val="100" + name="terrain3ScaleV" + width="64" /> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="8" + top_pad="8" + name="terrainScaleV_horizontal" + width="432" /> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain0Rotation_label" + left="10" + top_pad="0" + width="170"> + Rotation + </text> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Rotation" + label_width="0" + layout="topleft" + left="46" + min_val="-360" + max_val="360" + name="terrain0Rotation" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-360" + max_val="360" + name="terrain1Rotation" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-360" + max_val="360" + name="terrain2Rotation" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-360" + max_val="360" + name="terrain3Rotation" + width="64" /> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="8" + top_pad="8" + name="terrainRotation_horizontal" + width="432" /> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain0OffsetU_label" + left="10" + top_pad="0" + width="170"> + Offset y + </text> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Offset u" + label_width="0" + layout="topleft" + left="46" + min_val="-999" + max_val="999" + name="terrain0OffsetU" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-999" + max_val="999" + name="terrain1OffsetU" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-999" + max_val="999" + name="terrain2OffsetU" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-999" + max_val="999" + name="terrain3OffsetU" + width="64" /> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="8" + top_pad="8" + name="terrainOffsetU_horizontal" + width="432" /> + <text + type="string" + length="1" + halign="left" + valign="center" + follows="left|top" + height="20" + layout="topleft" + name="terrain0OffsetV_label" + left="10" + top_pad="0" + width="170"> + Offset v + </text> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Offset v" + label_width="0" + layout="topleft" + left="46" + min_val="-999" + max_val="999" + name="terrain0OffsetV" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-999" + max_val="999" + name="terrain1OffsetV" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-999" + max_val="999" + name="terrain2OffsetV" + width="64" /> + <spinner + follows="left|top" + height="19" + initial_value="0" + label="Scale u" + label_width="0" + layout="topleft" + left_delta="110" + min_val="-999" + max_val="999" + name="terrain3OffsetV" + width="64" /> + <view_border + bevel_style="none" + follows="top|left" + height="0" + layout="topleft" + left="8" + top_pad="8" + name="terrainOffsetV_horizontal" + width="432" /> +</panel> |