summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/keywords_lsl_default.xml9
-rw-r--r--indra/newview/app_settings/logcontrol.xml2
-rw-r--r--indra/newview/app_settings/settings.xml255
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/aoUtil.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl1
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl38
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl90
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl131
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/shadowUtil.glsl1
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/srgbF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl301
-rw-r--r--indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl262
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/copyF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl32
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/materialF.glsl1
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl1
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl1
-rw-r--r--indra/newview/gltf/README.md156
-rw-r--r--indra/newview/gltf/accessor.cpp274
-rw-r--r--indra/newview/gltf/accessor.h81
-rw-r--r--indra/newview/gltf/animation.cpp280
-rw-r--r--indra/newview/gltf/animation.h71
-rw-r--r--indra/newview/gltf/asset.cpp1100
-rw-r--r--indra/newview/gltf/asset.h290
-rw-r--r--indra/newview/gltf/buffer_util.h815
-rw-r--r--indra/newview/gltf/common.h83
-rw-r--r--indra/newview/gltf/primitive.cpp588
-rw-r--r--indra/newview/gltf/primitive.h42
-rw-r--r--indra/newview/gltfscenemanager.cpp736
-rw-r--r--indra/newview/gltfscenemanager.h35
-rw-r--r--indra/newview/installers/windows/FILES_ARE_UNICODE_UTF-16LE.txt12
-rw-r--r--indra/newview/llappviewer.cpp5
-rw-r--r--indra/newview/llappviewer.h3
-rw-r--r--indra/newview/llavatarrenderinfoaccountant.cpp2
-rw-r--r--indra/newview/lldrawpoolalpha.cpp25
-rw-r--r--indra/newview/lldrawpoolavatar.cpp4
-rw-r--r--indra/newview/lldrawpoolbump.cpp2
-rw-r--r--indra/newview/lldrawpoolpbropaque.cpp5
-rw-r--r--indra/newview/lldrawpoolsimple.cpp57
-rw-r--r--indra/newview/lldrawpoolterrain.cpp58
-rw-r--r--indra/newview/lldynamictexture.cpp4
-rw-r--r--indra/newview/llenvironment.cpp16
-rw-r--r--indra/newview/llface.cpp7
-rw-r--r--indra/newview/llfetchedgltfmaterial.cpp1
-rw-r--r--indra/newview/llfilepicker.cpp8
-rw-r--r--indra/newview/llfloaterregioninfo.cpp158
-rw-r--r--indra/newview/llfloaterregioninfo.h9
-rw-r--r--indra/newview/llglsandbox.cpp2
-rw-r--r--indra/newview/llheroprobemanager.cpp12
-rw-r--r--indra/newview/llimprocessing.cpp40
-rw-r--r--indra/newview/llinventorymodel.cpp8
-rw-r--r--indra/newview/lllocalbitmaps.cpp19
-rw-r--r--indra/newview/lllocalbitmaps.h1
-rw-r--r--indra/newview/lllocalgltfmaterials.cpp6
-rw-r--r--indra/newview/llmaniptranslate.cpp5
-rw-r--r--indra/newview/llpanelface.cpp1
-rw-r--r--indra/newview/llpanelvolume.cpp3
-rw-r--r--indra/newview/llpbrterrainfeatures.cpp198
-rw-r--r--indra/newview/llpbrterrainfeatures.h48
-rw-r--r--indra/newview/llreflectionmapmanager.cpp2
-rw-r--r--indra/newview/llsidepaneltaskinfo.cpp1
-rw-r--r--indra/newview/llskinningutil.cpp6
-rw-r--r--indra/newview/llskinningutil.h1
-rw-r--r--indra/newview/llstartup.cpp14
-rw-r--r--indra/newview/lltexturectrl.cpp8
-rw-r--r--indra/newview/lltinygltfhelper.cpp37
-rw-r--r--indra/newview/lltinygltfhelper.h1
-rw-r--r--indra/newview/lltracker.cpp139
-rw-r--r--indra/newview/llversioninfo.cpp2
-rw-r--r--indra/newview/llviewerassettype.cpp2
-rw-r--r--indra/newview/llviewerassetupload.cpp8
-rw-r--r--indra/newview/llviewercontrol.cpp45
-rw-r--r--indra/newview/llviewermedia.cpp12
-rw-r--r--indra/newview/llviewermedia.h4
-rw-r--r--indra/newview/llviewermenu.cpp84
-rw-r--r--indra/newview/llviewermessage.cpp186
-rw-r--r--indra/newview/llviewerobject.cpp56
-rw-r--r--indra/newview/llviewerobject.h17
-rwxr-xr-xindra/newview/llviewerregion.cpp40
-rw-r--r--indra/newview/llviewershadermgr.cpp349
-rw-r--r--indra/newview/llviewershadermgr.h3
-rw-r--r--indra/newview/llviewertexture.cpp12
-rw-r--r--indra/newview/llviewertexture.h10
-rw-r--r--indra/newview/llviewertexturelist.cpp153
-rw-r--r--indra/newview/llviewertexturelist.h3
-rw-r--r--indra/newview/llvlcomposition.cpp117
-rw-r--r--indra/newview/llvlcomposition.h29
-rw-r--r--indra/newview/llvocache.cpp17
-rw-r--r--indra/newview/llvoicevivox.cpp48
-rw-r--r--indra/newview/llvovolume.cpp16
-rw-r--r--indra/newview/pipeline.cpp13
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml70
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml57
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml260
-rw-r--r--indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml307
-rw-r--r--indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml421
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 &params_in, const S32 detail, bo
}
}
+ if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF)
+ { // notify GLTFSceneManager about new GLTF object
+ LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID());
+ }
+
return true;
}
else if (NO_LOD == lod)
@@ -1407,6 +1413,12 @@ bool LLVOVolume::calcLOD()
return false;
}
+ if (mGLTFAsset != nullptr)
+ {
+ // do not calculate LOD for GLTF objects
+ return false;
+ }
+
S32 cur_detail = 0;
F32 radius;
@@ -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>