summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autobuild.xml84
-rwxr-xr-xdoc/contributions.txt1
-rw-r--r--doc/testplans/RenderMaxTextureResolution.md16
-rw-r--r--doc/testplans/hdri_local_preview.md21
-rw-r--r--doc/testplans/material_preview.md83
-rw-r--r--doc/testplans/pbr_terrain_appearance.md35
-rw-r--r--doc/testplans/pbr_terrain_composition.md76
-rw-r--r--doc/testplans/terrain_loading.md29
-rw-r--r--indra/cmake/CMakeLists.txt2
-rw-r--r--indra/cmake/TinyEXR.cmake7
-rw-r--r--indra/llcommon/indra_constants.cpp1
-rw-r--r--indra/llcommon/indra_constants.h2
-rw-r--r--indra/llcommon/llstrider.h2
-rw-r--r--indra/llimage/llimage.cpp144
-rw-r--r--indra/llimage/llimage.h22
-rw-r--r--indra/llinventory/llsettingssky.cpp28
-rw-r--r--indra/llinventory/llsettingssky.h9
-rw-r--r--indra/llmath/llcamera.h1
-rw-r--r--indra/llmath/llmatrix4a.h14
-rw-r--r--indra/llmath/llvolume.cpp189
-rw-r--r--indra/llmath/llvolume.h7
-rw-r--r--indra/llmath/llvolumeoctree.cpp8
-rw-r--r--indra/llmath/llvolumeoctree.h97
-rw-r--r--indra/llplugin/llpluginprocessparent.cpp2
-rw-r--r--indra/llprimitive/llgltfmaterial.h10
-rw-r--r--indra/llprimitive/llprimitive.cpp13
-rw-r--r--indra/llprimitive/llprimitive.h3
-rw-r--r--indra/llprimitive/lltextureentry.cpp1
-rw-r--r--indra/llprimitive/lltextureentry.h2
-rw-r--r--indra/llrender/llglslshader.cpp4
-rw-r--r--indra/llrender/llglslshader.h3
-rw-r--r--indra/llrender/llgltexture.cpp3
-rw-r--r--indra/llrender/llgltexture.h5
-rw-r--r--indra/llrender/llrender.cpp24
-rw-r--r--indra/llrender/llrendertarget.cpp27
-rw-r--r--indra/llrender/llrendertarget.h3
-rw-r--r--indra/llrender/llshadermgr.cpp85
-rw-r--r--indra/llrender/llshadermgr.h33
-rw-r--r--indra/llrender/llvertexbuffer.cpp49
-rw-r--r--indra/llrender/llvertexbuffer.h13
-rw-r--r--indra/newview/CMakeLists.txt17
-rw-r--r--indra/newview/app_settings/settings.xml360
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl10
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl10
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl35
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl8
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl8
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl8
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/globalF.glsl45
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl18
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/materialV.glsl17
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/moonF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl10
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl347
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl473
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl96
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl15
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyF.glsl60
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyV.glsl12
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl12
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/treeF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/treeV.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl (renamed from indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl)21
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl76
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl74
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/bumpF.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/bumpV.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl10
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl12
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/materialF.glsl30
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl80
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl9
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl31
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl10
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl1
-rw-r--r--indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class3/environment/waterF.glsl3
-rw-r--r--indra/newview/featuretable.txt59
-rw-r--r--indra/newview/featuretable_mac.txt60
-rw-r--r--indra/newview/gltf/accessor.cpp66
-rw-r--r--indra/newview/gltf/accessor.h95
-rw-r--r--indra/newview/gltf/animation.cpp287
-rw-r--r--indra/newview/gltf/animation.h181
-rw-r--r--indra/newview/gltf/asset.cpp664
-rw-r--r--indra/newview/gltf/asset.h264
-rw-r--r--indra/newview/gltf/buffer_util.h402
-rw-r--r--indra/newview/gltf/primitive.cpp400
-rw-r--r--indra/newview/gltf/primitive.h93
-rw-r--r--indra/newview/gltfscenemanager.cpp564
-rw-r--r--indra/newview/gltfscenemanager.h69
-rw-r--r--indra/newview/llagentbenefits.cpp66
-rw-r--r--indra/newview/llagentbenefits.h9
-rw-r--r--indra/newview/llagentcamera.cpp2
-rw-r--r--indra/newview/llappviewer.cpp8
-rw-r--r--indra/newview/lldrawable.h1
-rw-r--r--indra/newview/lldrawpool.cpp17
-rw-r--r--indra/newview/lldrawpool.h14
-rw-r--r--indra/newview/lldrawpoolalpha.cpp17
-rw-r--r--indra/newview/lldrawpoolbump.cpp7
-rw-r--r--indra/newview/lldrawpoolpbropaque.cpp5
-rw-r--r--indra/newview/lldrawpoolterrain.cpp294
-rw-r--r--indra/newview/lldrawpoolterrain.h10
-rw-r--r--indra/newview/lldrawpooltree.cpp13
-rw-r--r--indra/newview/lldrawpoolwlsky.cpp55
-rw-r--r--indra/newview/lldynamictexture.cpp32
-rw-r--r--indra/newview/llenvironment.cpp14
-rw-r--r--indra/newview/llface.cpp4
-rw-r--r--indra/newview/llfeaturemanager.cpp4
-rw-r--r--indra/newview/llfetchedgltfmaterial.cpp69
-rw-r--r--indra/newview/llfetchedgltfmaterial.h17
-rw-r--r--indra/newview/llfilepicker.cpp7
-rw-r--r--indra/newview/llfilepicker.h1
-rw-r--r--indra/newview/llfloaterimagepreview.cpp12
-rw-r--r--indra/newview/llfloaterimagepreview.h26
-rw-r--r--indra/newview/llfloaternamedesc.h2
-rw-r--r--indra/newview/llfloaterregioninfo.cpp423
-rw-r--r--indra/newview/llfloaterregioninfo.h88
-rw-r--r--indra/newview/llgltfmateriallist.cpp7
-rw-r--r--indra/newview/llgltfmateriallist.h2
-rw-r--r--indra/newview/llgltfmaterialpreviewmgr.cpp590
-rw-r--r--indra/newview/llgltfmaterialpreviewmgr.h78
-rw-r--r--indra/newview/llheroprobemanager.cpp632
-rw-r--r--indra/newview/llheroprobemanager.h156
-rw-r--r--indra/newview/llmanip.cpp8
-rw-r--r--indra/newview/llmaniprotate.cpp293
-rw-r--r--indra/newview/llmaniptranslate.cpp225
-rw-r--r--indra/newview/llmaterialeditor.cpp35
-rw-r--r--indra/newview/llpanelface.cpp5
-rw-r--r--indra/newview/llpanelface.h3
-rw-r--r--indra/newview/llpanelmaininventory.cpp4
-rw-r--r--indra/newview/llpanelvolume.cpp51
-rw-r--r--indra/newview/llpanelvolume.h2
-rw-r--r--indra/newview/llreflectionmap.cpp9
-rw-r--r--indra/newview/llreflectionmap.h13
-rw-r--r--indra/newview/llreflectionmapmanager.cpp181
-rw-r--r--indra/newview/llreflectionmapmanager.h20
-rw-r--r--indra/newview/llselectmgr.cpp122
-rw-r--r--indra/newview/llselectmgr.h9
-rw-r--r--indra/newview/llsettingsvo.cpp35
-rw-r--r--indra/newview/llspatialpartition.cpp199
-rw-r--r--indra/newview/llsurface.cpp14
-rw-r--r--indra/newview/llsurface.h6
-rw-r--r--indra/newview/llsurfacepatch.cpp203
-rw-r--r--indra/newview/llsurfacepatch.h13
-rw-r--r--indra/newview/lltexturectrl.cpp131
-rw-r--r--indra/newview/lltexturectrl.h63
-rw-r--r--indra/newview/lltinygltfhelper.cpp57
-rw-r--r--indra/newview/lltinygltfhelper.h9
-rw-r--r--indra/newview/lltooldraganddrop.cpp100
-rw-r--r--indra/newview/lltoolselect.cpp2
-rw-r--r--indra/newview/llviewerassetupload.cpp2
-rw-r--r--indra/newview/llviewercamera.cpp6
-rw-r--r--indra/newview/llviewercontrol.cpp52
-rw-r--r--indra/newview/llviewerdisplay.cpp12
-rw-r--r--indra/newview/llviewermenu.cpp70
-rw-r--r--indra/newview/llviewermenufile.cpp89
-rw-r--r--indra/newview/llviewerobject.cpp169
-rw-r--r--indra/newview/llviewerobject.h16
-rwxr-xr-xindra/newview/llviewerregion.cpp129
-rw-r--r--indra/newview/llviewerregion.h6
-rw-r--r--indra/newview/llviewershadermgr.cpp292
-rw-r--r--indra/newview/llviewershadermgr.h32
-rw-r--r--indra/newview/llviewertexture.cpp18
-rw-r--r--indra/newview/llviewertexture.h2
-rw-r--r--indra/newview/llviewerwindow.cpp33
-rw-r--r--indra/newview/llviewerwindow.h7
-rw-r--r--indra/newview/llvlcomposition.cpp719
-rw-r--r--indra/newview/llvlcomposition.h85
-rw-r--r--indra/newview/llvoavatar.cpp5
-rw-r--r--indra/newview/llvoavatarself.cpp2
-rw-r--r--indra/newview/llvocache.cpp248
-rw-r--r--indra/newview/llvocache.h13
-rw-r--r--indra/newview/llvosurfacepatch.cpp225
-rw-r--r--indra/newview/llvosurfacepatch.h10
-rw-r--r--indra/newview/llvovolume.cpp82
-rw-r--r--indra/newview/llvovolume.h2
-rw-r--r--indra/newview/llxmlrpclistener.cpp94
-rw-r--r--indra/newview/pipeline.cpp225
-rw-r--r--indra/newview/pipeline.h21
-rw-r--r--indra/newview/skins/default/xui/de/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml201
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml57
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory_add.xml2
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml83
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml100
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_terrain.xml124
-rw-r--r--indra/newview/skins/default/xui/es/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/fr/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/it/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/ja/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/pl/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/pt/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/ru/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/tr/panel_region_terrain.xml2
-rw-r--r--indra/newview/skins/default/xui/zh/panel_region_terrain.xml2
216 files changed, 12167 insertions, 1981 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 1939c6734b..8642d74b0f 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1808,6 +1808,18 @@
</map>
<key>mikktspace</key>
<map>
+ <key>canonical_repo</key>
+ <string>https://bitbucket.org/lindenlab/3p-mikktspace</string>
+ <key>copyright</key>
+ <string>Copyright (C) 2011 by Morten S. Mikkelsen, Copyright (C) 2022 Blender Authors</string>
+ <key>description</key>
+ <string>Mikktspace Tangent Generator</string>
+ <key>license</key>
+ <string>Apache 2.0</string>
+ <key>license_file</key>
+ <string>mikktspace.txt</string>
+ <key>name</key>
+ <string>mikktspace</string>
<key>platforms</key>
<map>
<key>darwin64</key>
@@ -1815,58 +1827,46 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>6cc1585dba85b0226a2e7033a7e2a2ceaae7c983</string>
+ <string>65edf85c36a10001e32bdee582bec4732137208b</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-mikktspace/releases/download/v1-5cee1f4/mikktspace-1-darwin64-5cee1f4.tar.zst</string>
+ <string>https://github.com/secondlife/3p-mikktspace/releases/download/v2-e967e1b/mikktspace-1-darwin64-8756084692.tar.zst</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
- <key>windows64</key>
+ <key>linux64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
- <string>6b7d01ad54e4a88a001f66840c32329cedb28202</string>
+ <string>fa9dcee4584df7e7271fdf69c08e6fd3122a47fc</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-mikktspace/releases/download/v1-5cee1f4/mikktspace-1-windows64-5cee1f4.tar.zst</string>
+ <string>https://github.com/secondlife/3p-mikktspace/releases/download/v2-e967e1b/mikktspace-1-linux64-8756084692.tar.zst</string>
</map>
<key>name</key>
- <string>windows64</string>
+ <string>linux64</string>
</map>
- <key>linux64</key>
+ <key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
- <string>edc9782bf209e17ad1845498b42f16d733582082</string>
+ <string>130b33a70bdb3a8a188376c6a91840bdb61380a8</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
- <string>https://github.com/secondlife/3p-mikktspace/releases/download/v1-5cee1f4/mikktspace-1-linux64-5cee1f4.tar.zst</string>
+ <string>https://github.com/secondlife/3p-mikktspace/releases/download/v2-e967e1b/mikktspace-1-windows64-8756084692.tar.zst</string>
</map>
<key>name</key>
- <string>linux64</string>
+ <string>windows64</string>
</map>
</map>
- <key>license</key>
- <string>Copyright (C) 2011 by Morten S. Mikkelsen</string>
- <key>license_file</key>
- <string>mikktspace.txt</string>
- <key>copyright</key>
- <string>Copyright (C) 2011 by Morten S. Mikkelsen</string>
<key>version</key>
<string>1</string>
- <key>name</key>
- <string>mikktspace</string>
- <key>canonical_repo</key>
- <string>https://bitbucket.org/lindenlab/3p-mikktspace</string>
- <key>description</key>
- <string>Mikktspace Tangent Generator</string>
</map>
<key>minizip-ng</key>
<map>
@@ -3026,6 +3026,46 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>description</key>
<string>zlib data compression library for the next generation systems</string>
</map>
+ <key>tinyexr</key>
+ <map>
+ <key>platforms</key>
+ <map>
+ <key>common</key>
+ <map>
+ <key>archive</key>
+ <map>
+ <key>hash</key>
+ <string>9e0092c6a3aed1cb40a9e26df689c42c68142c9d</string>
+ <key>hash_algorithm</key>
+ <string>sha1</string>
+ <key>url</key>
+ <string>https://github.com/secondlife/3p-tinyexr/releases/download/v1.0.8-r1/tinyexr-v1.0.8-common-8755737750.tar.zst</string>
+ </map>
+ <key>name</key>
+ <string>common</string>
+ </map>
+ </map>
+ <key>license</key>
+ <string>3-clause BSD</string>
+ <key>license_file</key>
+ <string>LICENSES/tinyexr_license.txt</string>
+ <key>copyright</key>
+ <string>Copyright (c) 2014 - 2021, Syoyo Fujita and many contributors.</string>
+ <key>version</key>
+ <string>v1.0.8</string>
+ <key>name</key>
+ <string>tinyexr</string>
+ <key>vcs_branch</key>
+ <string>dependabot/github_actions/secondlife/action-autobuild-4</string>
+ <key>vcs_revision</key>
+ <string>4dc4d1d90d82a22843e2adf5130f9ecb5ee5769e</string>
+ <key>vcs_url</key>
+ <string>https://github.com/secondlife/3p-tinyexr</string>
+ <key>description</key>
+ <string>tinyexr import library</string>
+ <key>source_type</key>
+ <string>git</string>
+ </map>
</map>
<key>package_description</key>
<map>
diff --git a/doc/contributions.txt b/doc/contributions.txt
index a14ffa24fd..15aec5e282 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -245,6 +245,7 @@ Ansariel Hiller
SL-19623
SL-4126
SL-20224
+ https://github.com/secondlife/viewer/issues/1051
Aralara Rajal
Arare Chantilly
CHUIBUG-191
diff --git a/doc/testplans/RenderMaxTextureResolution.md b/doc/testplans/RenderMaxTextureResolution.md
new file mode 100644
index 0000000000..2b117050c7
--- /dev/null
+++ b/doc/testplans/RenderMaxTextureResolution.md
@@ -0,0 +1,16 @@
+The Setting RenderMaxTextureResolution controls the maximum resolution of non-boosted textures as displayed by the viewer.
+
+Valid values are 512-2048 (clamped in C++).
+
+![image](https://github.com/secondlife/viewer/assets/23218274/d0f889fc-8135-41d2-9d83-871ad4eebed5)
+
+![image](https://github.com/secondlife/viewer/assets/23218274/19950828-7eb1-4bb2-85d7-f35c63b34294)
+
+![image](https://github.com/secondlife/viewer/assets/23218274/249afc83-4de6-488d-a05e-4877d08573b1)
+
+Test Asset available on beta grid:
+Object: 'Damaged Helmet', AssetID 0623e759-11b5-746c-a75e-7ba1caa6eb0e
+
+
+
+
diff --git a/doc/testplans/hdri_local_preview.md b/doc/testplans/hdri_local_preview.md
new file mode 100644
index 0000000000..ba4f085100
--- /dev/null
+++ b/doc/testplans/hdri_local_preview.md
@@ -0,0 +1,21 @@
+A resident may swap out their sky for an EXR format HDRI for the purposes of previewing how their object would render in Second Life in an environment that matches the supplied HDRI. This should aid in matching inworld lighting with external tools so artists can know if their content has imported properly.
+
+To load an HDRI, click Develop->Render Tests->HDRI Preview:
+
+![image](https://github.com/secondlife/viewer/assets/23218274/fbdeab5f-dc1f-4406-be19-0c9ee7437b3f)
+
+Choose an EXR image. A library of publicly available HDRIs can be found here: https://polyhaven.com/hdris
+
+The Personal Lighting floater will open, and the sky will be replaced with the HDRI you chose. Reflection Probes will reset, and the scene will be illuminated by the HDRI.
+
+Three debug settings affect how the HDRI is displayed:
+
+RenderHDRIExposure - Exposure adjustment of HDRI when previewing an HDRI. Units are EV. Sane values would be -10 to 10.
+RenderHDRIRotation - Rotation (in degrees) of environment when previewing an HDRI.
+RenderHDRISplitScreen - What percentage of screen to render using HDRI vs EEP sky.
+
+Exposure and Rotation should behave similarly to the rotation and exposure controls in Substance Painter.
+
+Split Screen can be used to display an EEP sky side-by-side with an HDRI sky to aid in authoring an EEP sky that matches an HDRI sky. It is currently expected that EEP sun disc, moon, clouds, and stars do not render when previewing an HDRI, but that may change in the future.
+
+
diff --git a/doc/testplans/material_preview.md b/doc/testplans/material_preview.md
new file mode 100644
index 0000000000..6d2768f72a
--- /dev/null
+++ b/doc/testplans/material_preview.md
@@ -0,0 +1,83 @@
+# Material Preview
+
+## Overview
+
+Material preview is a UI feature which displays a lit spherical preview of a PBR material. It can be found in the following UIs:
+
+- The material picker swatch
+ - In the build floater, in the Texture tab, when applying a PBR material
+ - (If the feature is enabled) In the Region/Estate floater, in the Terrain tab, when applying PBR materials to terrain
+- In the floater to select a material from inventory, which can be opened by clicking the material picker swatch
+
+## Known Issues
+
+These are known issues that the current implementation of this feature does not address:
+
+- The material preview in the build floater is a preview of the base material ID only, and ignores other properties on the prim face like material overrides (https://github.com/secondlife/viewer/issues/865)
+- Alpha mask previews as alpha blend (https://github.com/secondlife/viewer/issues/866)
+- Double-sided previews as single-sided (https://github.com/secondlife/viewer/issues/867)
+- Material preview inherits some of its lighting from the current environment, and reflections from the default reflection probe (https://github.com/secondlife/viewer/issues/868)
+
+## General Regression Testing
+
+- Check that the material preview swatch looks OK with different materials selected
+- Check that the material preview swatch runs reasonably well on different systems, especially when the select material from inventory floater is also open
+ - In particular: AMD, MacOS, minimum spec machines
+- Watch out for regressions in rendering caused by opening a floater with a material preview swatch
+
+## Bug Fixes
+
+### Disappearing Objects Fix Test
+
+This test is recommended for verifying that https://github.com/secondlife/viewer-issues/issues/72 is fixed.
+
+#### Symptoms
+
+When the bug occurs, one or more of following types of objects could randomly disappear in the world, permanently until relog:
+
+- Objects
+- Water level in current region
+- Adjacent region/void water
+
+Note: Disappearing objects in reflections have a different root cause and are not covered by the fix.
+
+#### Bug Reproduction Steps
+
+Verify the disappearing objects bug does not reproduce, given the following steps:
+
+- Runtime prerequisites: Material preview swatch may not be available in your viewer or region if this feature is still behind a feature flag. It is safe to enable this feature manually by setting, "UIPreviewMaterial" to True in the advanced settings. The setting will persist for the current session.
+- Region prerequisites: Unknown, but a region with lots of objects in it seems to increase repro rate. The following locations have been known to easily reproduce the bug, as of 2024-02-16:
+ - http://maps.secondlife.com/secondlife/LindenWorld%20B/161/75/47
+ - [secondlife://Aditi/secondlife/Rumpus%20Room%202048/128/128/24](secondlife://Aditi/secondlife/Rumpus%20Room%202048/128/128/24)
+- Right click an object and select, "Edit item"
+- Go to texture tab, select PBR Metallic Roughness from dropdown, and click the button to select material from inventory
+- Ensure "Apply now" is checked in the inventory selection floater
+- Alternate between different materials from the inventory selection floater
+- Look around the world and check for permanently disappeared objects.
+
+### Dynamic Exposure Influence Fix Test
+
+This test is recommended for verifying that https://github.com/secondlife/viewer-issues/issues/72 is fixed.
+
+#### Symptoms
+
+Dynamic exposure in the world could be influenced by the material preview being displayed. If a material preview was being generated in a given frame, then, depending on the current environment, the user would observe an unpleasant flashing effect in the environment:
+
+- The world view could suddenly get darker and then fade back to normal exposure levels
+- The world view could suddenly get brighter and then fade back to normal exposure levels
+
+#### Bug Reproduction Steps
+
+Verify the dynamic exposure influence bug does not reproduce. Test using a few environment presets such as Default Midday, Sunset, and Midnight.
+
+- Right click an object and select, "Edit item"
+- Go to texture tab, select PBR Metallic Roughness from dropdown, and click the button to select material from inventory
+- Alternate between different materials from the inventory selection floater
+
+#### Regression Testing
+
+Dynamic exposure in the world should continue to work correctly. In particular:
+
+- Exposure should fade gradually from high exposure to low exposure and back as needed
+- Exposure should decrease in brighter environments
+- Exposure should increase in darker environments
diff --git a/doc/testplans/pbr_terrain_appearance.md b/doc/testplans/pbr_terrain_appearance.md
new file mode 100644
index 0000000000..4f0ee5c943
--- /dev/null
+++ b/doc/testplans/pbr_terrain_appearance.md
@@ -0,0 +1,35 @@
+# PBR Terrain Appearance
+
+## Tiling
+
+If two adjacent regions have the same PBR terrain settings, then:
+
+- There should not be seams between the two regions at their shared border
+- The ground should not suddenly slide beneath the avatar when moving between regions (except due to movement of the avatar, which is not covered by this test plan)
+
+## Feature Gating
+
+PBR terrain should have lower detail on lower graphics settings. PBR terrain will also not show emissive textures on some machines (like Macs) which do not support more than 16 textures.
+
+### Triplanar Mapping
+
+Triplanar mapping improves the texture repeats on the sides of terrain slopes.
+
+Availability of Triplanar mapping:
+
+- Medium-High and below: No triplanar mapping
+- High and above: Triplanar mapping
+
+### PBR Textures
+
+At the highest graphics support level, PBR terrain supports all PBR textures.
+
+Availability of PBR textures varies by machine and graphics setting:
+
+- Low: Base color only (looks similar to texture terrain)
+- Medium-Low, and machines that do not support greater than 16 textures such as Macs: All PBR textures enabled except emissive textures.
+- Medium: All PBR textures enabled
+
+### PBR Alpha
+
+PBR terrain does not support materials with alpha blend or double-sided. In addition, the viewer does not make any guarantees about what will render behind the terrain if alpha is used.
diff --git a/doc/testplans/pbr_terrain_composition.md b/doc/testplans/pbr_terrain_composition.md
new file mode 100644
index 0000000000..aadd97a94b
--- /dev/null
+++ b/doc/testplans/pbr_terrain_composition.md
@@ -0,0 +1,76 @@
+# PBR Terrain Composition
+
+## Feature Availability
+
+PBR Terrain is visible for all viewers with the PBR Terrain feature, regardless of if the feature flag is enabled.
+
+There is only one set of four asset IDs applied to the terrain. In other words, unlike PBR materials on prims, there is no fallback texture set for viewers that do not support PBR terrain. Viewers without support will view terrain as blank (solid grey or white).
+
+## Editing Terrain Composition
+
+All tests in this section assume the PBR terrain feature flag is enabled, and that the user has appropriate permissions to modify the terrain textures.
+
+### Feature Availability
+
+On the client, the advanced setting `RenderTerrainPBREnabled` is the PBR terrain feature flag.
+
+The PBR terrain feature flag should be set automatically when logging in/teleporting to a new region.
+
+- The flag should be enabled on regions where the PBR terrain feature is enabled
+- Otherwise the flag should be disabled
+
+When the PBR terrain feature flag is disabled:
+
+- The "PBR Metallic Roughness" checkbox should not be visible
+- The user should not be able to apply PBR terrain to the region, only textures.
+
+When the PBR terrain feature flag is enabled:
+
+- The "PBR Metallic Roughness" checkbox should be visible
+- The user should be able to apply PBR terrain or textures to the region, depending on if the "PBR Metallic Roughness" checkbox is checked.
+
+### Current Composition Type
+
+When the Region/Estate floater is opened to the terrain Tab, the current terrain should be shown in the four swatches, and the "PBR Metallic Roughness" checkbox should be checked or unchecked accordingly.
+
+- If it is texture terrain, the "PBR Metallic Roughness" checkbox should be unchecked, and the floater should display the four textures applied to the terrain.
+- If it is material terrain, the "PBR Metallic Roughness" checkbox should be checked, and the floater should display the four materials applied to the terrain.
+
+In addition, where possible, textual labels and descriptions in the tab should make sense given the current value of the "PBR Metallic Roughness" checkbox. If the checkbox is unchecked, the labels should refer to textures. If the checkbox is checked, the labels should refer to materials.
+
+### Toggling Composition Type
+
+When toggling the "PBR Metallic Roughness" checkbox to the opposite value, which does not correspond to the current terrain type, one of the following sets of four terrain swatches will be displayed:
+
+- The default textures/materials
+ - For textures, this is the default terrain texture set
+ - For materials, this is all blank materials, but this is subject to change
+- The previously applied texture/material set
+ - History is available on a best-effort basis only. In particular, the history does not persist on viewer restart.
+
+When toggling back the "PBR Metallic Roughness" checkbox to the original value, assuming nothing else has changed, then the current terrain should be shown in the four swatches again.
+
+### Saving Composition
+
+A user with appropriate permissions can change and save the textures or materials to the terrain. If the "PBR Metallic Roughness" checkbox is checked, the user applies materials, otherwise the user applies textures.
+
+The user should not be allowed to set the texture or material swatches to null.
+
+Saving may fail for the following reasons:
+
+- A terrain or material texture is invalid
+- A terrain texture is greater than the max texture upload resolution
+
+If saving the terrain fails for any reason, the terrain should not be updated.
+
+Unlike a viewer without PBR terrain support, the new viewer will no longer treat textures with alpha channels as invalid.
+
+## Graphics Features
+
+Texture terrain with transparency is not permitted to be applied in the viewer.
+
+See [PBR Terrain Appearance](./pbr_terrain_appearance.md) for supported PBR terrain features.
+
+## Minimap
+
+The minimap should display the terrain with appropriate textures and colors.
diff --git a/doc/testplans/terrain_loading.md b/doc/testplans/terrain_loading.md
new file mode 100644
index 0000000000..c1b170fcf8
--- /dev/null
+++ b/doc/testplans/terrain_loading.md
@@ -0,0 +1,29 @@
+# Terrain Loading
+
+## Behavior overview
+
+- Texture terrain should load if textures are applied to the region, and no PBR Metallic Roughness materials are applied.
+- PBR terrain should load if PBR materials are applied to the region, even if the RenderTerrainPBREnabled feature flag is disabled in debug settings. This setting only disables the display of PBR materials in the Region / Estate > Terrain UI.
+- Related subsystem: A change to the PBR terrain loading system may affect the texture terrain loading system and vice-versa
+- Related subsystem: Minimap should load if terrain loads
+ - They may not finish loading at the same time
+
+## Implementation details
+
+This section is provided mainly for clarification of how the terrain loading system works.
+
+The simulator sends 4 terrain composition UUIDs to the viewer for the region. The viewer does not know ahead-of-time if the terrain composition uses textures or materials. Therefore, to expedite terrain loading, the viewer makes up to 8 "top-level" asset requests simultaneously:
+
+- Up to 4 texture asset requests, one for each UUID
+- Up to 4 material asset requests, one for each UUID
+
+It is therefore expected that half of these asset lookups will fail.
+
+The viewer inspects the load success of these top-level assets to make the binary decision of whether to render all 4 texture assets or all 4 material assets. This determines the choice of composition for terrain both in-world and on the minimap.
+
+The minimap also attempts to wait for textures to partially load before it can render a tile for a given region:
+
+- When rendering texture terrain, the minimap attempts to wait for top-level texture assets to partially load
+- When rendering PBR material terrain, the minimap attempts to wait for any base color/emissive textures in the materials to partially load, if they are present
+
+We don't make guarantees that the minimap tile will render for the region if any aforementioned required textures/materials fail to sufficiently load. However, the minimap may make a best-effort attempt to render the region by ignoring or replacing data.
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 05c51c018d..da7e4be464 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -56,6 +56,8 @@ set(cmake_SOURCE_FILES
PulseAudio.cmake
Python.cmake
TemplateCheck.cmake
+ TinyEXR.cmake
+ TinyGLTF.cmake
Tut.cmake
UI.cmake
UnixInstall.cmake
diff --git a/indra/cmake/TinyEXR.cmake b/indra/cmake/TinyEXR.cmake
new file mode 100644
index 0000000000..e6d142d19d
--- /dev/null
+++ b/indra/cmake/TinyEXR.cmake
@@ -0,0 +1,7 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+use_prebuilt_binary(tinyexr)
+
+set(TINYEXR_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/tinyexr)
+
diff --git a/indra/llcommon/indra_constants.cpp b/indra/llcommon/indra_constants.cpp
index 9a0c565b06..39e232e480 100644
--- a/indra/llcommon/indra_constants.cpp
+++ b/indra/llcommon/indra_constants.cpp
@@ -89,3 +89,4 @@ const LLUUID IMG_USE_BAKED_AUX1 ("9742065b-19b5-297c-858a-29711d539043");
const LLUUID IMG_USE_BAKED_AUX2 ("03642e83-2bd1-4eb9-34b4-4c47ed586d2d");
const LLUUID IMG_USE_BAKED_AUX3 ("edd51b77-fc10-ce7a-4b3d-011dfc349e4f");
+const LLUUID BLANK_MATERIAL_ASSET_ID ("968cbad0-4dad-d64e-71b5-72bf13ad051a");
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index a16cfac2b9..a4b2efef98 100644
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -236,6 +236,8 @@ LL_COMMON_API extern const LLUUID DEFAULT_OBJECT_SPECULAR;
LL_COMMON_API extern const LLUUID DEFAULT_OBJECT_NORMAL;
LL_COMMON_API extern const LLUUID BLANK_OBJECT_NORMAL;
+LL_COMMON_API extern const LLUUID BLANK_MATERIAL_ASSET_ID;
+
// radius within which a chat message is fully audible
const F32 CHAT_NORMAL_RADIUS = 20.f;
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h
index ed9284d2c5..e7522484e6 100644
--- a/indra/llcommon/llstrider.h
+++ b/indra/llcommon/llstrider.h
@@ -37,8 +37,8 @@ template <class Object> class LLStrider
};
U32 mSkip;
public:
-
LLStrider() { mObjectp = NULL; mSkip = sizeof(Object); }
+ LLStrider(Object* first) { mObjectp = first; mSkip = sizeof(Object); }
~LLStrider() { }
const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;}
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index f9393dea54..ed8f36286f 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -31,6 +31,7 @@
#include "llmath.h"
#include "v4coloru.h"
+#include "v3color.h"
#include "llimagebmp.h"
#include "llimagetga.h"
@@ -989,6 +990,28 @@ void LLImageRaw::verticalFlip()
}
+bool LLImageRaw::checkHasTransparentPixels()
+{
+ if (getComponents() != 4)
+ {
+ return false;
+ }
+
+ U8* data = getData();
+ U32 pixels = getWidth() * getHeight();
+
+ // check alpha channel for all 255
+ for (U32 i = 0; i < pixels; ++i)
+ {
+ if (data[i * 4 + 3] != 255)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool LLImageRaw::optimizeAwayAlpha()
{
if (getComponents() == 4)
@@ -1026,6 +1049,34 @@ bool LLImageRaw::optimizeAwayAlpha()
return false;
}
+bool LLImageRaw::makeAlpha()
+{
+ if (getComponents() == 3)
+ {
+ U8* data = getData();
+ U32 pixels = getWidth() * getHeight();
+
+ // alpha channel doesn't exist, make a new copy of data with alpha channel
+ U8* new_data = (U8*) ll_aligned_malloc_16(getWidth() * getHeight() * 4);
+
+ for (U32 i = 0; i < pixels; ++i)
+ {
+ U32 di = i * 4;
+ U32 si = i * 3;
+ for (U32 j = 0; j < 3; ++j)
+ {
+ new_data[di+j] = data[si+j];
+ }
+ }
+
+ setDataAndSize(new_data, getWidth(), getHeight(), 3);
+
+ return true;
+ }
+
+ return false;
+}
+
void LLImageRaw::expandToPowerOfTwo(S32 max_dim, bool scale_image)
{
// Find new sizes
@@ -1110,7 +1161,7 @@ void LLImageRaw::composite( LLImageRaw* src )
return;
}
- llassert(3 == src->getComponents());
+ llassert((3 == src->getComponents()) || (4 == src->getComponents()));
llassert(3 == dst->getComponents());
if( 3 == dst->getComponents() )
@@ -1268,6 +1319,30 @@ void LLImageRaw::fill( const LLColor4U& color )
}
}
+void LLImageRaw::tint( const LLColor3& color )
+{
+ llassert( (3 == getComponents()) || (4 == getComponents()) );
+ if (isBufferInvalid())
+ {
+ LL_WARNS() << "Invalid image buffer" << LL_ENDL;
+ return;
+ }
+
+ S32 pixels = getWidth() * getHeight();
+ const S32 components = getComponents();
+ U8* data = getData();
+ for( S32 i = 0; i < pixels; i++ )
+ {
+ const float c0 = data[0] * color.mV[0];
+ const float c1 = data[1] * color.mV[1];
+ const float c2 = data[2] * color.mV[2];
+ data[0] = llclamp((U8)c0, 0, 255);
+ data[1] = llclamp((U8)c1, 0, 255);
+ data[2] = llclamp((U8)c2, 0, 255);
+ data += components;
+ }
+}
+
LLPointer<LLImageRaw> LLImageRaw::duplicate()
{
if(getNumRefs() < 2)
@@ -1799,6 +1874,73 @@ void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S3
}
}
+
+void LLImageRaw::addEmissive(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ if (!validateSrcAndDst(__FUNCTION__, src, dst))
+ {
+ return;
+ }
+
+ llassert((3 == src->getComponents()) || (4 == src->getComponents()));
+ llassert(3 == dst->getComponents());
+
+ if( 3 == dst->getComponents() )
+ {
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ addEmissiveUnscaled(src);
+ }
+ else
+ {
+ addEmissiveScaled(src);
+ }
+ }
+}
+
+void LLImageRaw::addEmissiveUnscaled(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert((3 == src->getComponents()) || (4 == src->getComponents()));
+ llassert((3 == dst->getComponents()) || (4 == dst->getComponents()));
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ U8* const src_data = src->getData();
+ U8* const dst_data = dst->getData();
+ for(S32 y = 0; y < dst->getHeight(); ++y)
+ {
+ const S32 src_row_offset = src->getComponents() * src->getWidth() * y;
+ const S32 dst_row_offset = dst->getComponents() * dst->getWidth() * y;
+ for (S32 x = 0; x < dst->getWidth(); ++x)
+ {
+ const S32 src_offset = src_row_offset + (x * src->getComponents());
+ const S32 dst_offset = dst_row_offset + (x * dst->getComponents());
+ U8* const src_pixel = src_data + src_offset;
+ U8* const dst_pixel = dst_data + dst_offset;
+ dst_pixel[0] = llmin(255, dst_pixel[0] + src_pixel[0]);
+ dst_pixel[1] = llmin(255, dst_pixel[1] + src_pixel[1]);
+ dst_pixel[2] = llmin(255, dst_pixel[2] + src_pixel[2]);
+ }
+ }
+}
+
+void LLImageRaw::addEmissiveScaled(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) );
+
+ LLImageRaw temp(dst->getWidth(), dst->getHeight(), dst->getComponents());
+ llassert_always(temp.getDataSize() > 0);
+ temp.copyScaled(src);
+
+ dst->addEmissiveUnscaled(&temp);
+}
+
+
bool LLImageRaw::validateSrcAndDst(std::string func, LLImageRaw* src, LLImageRaw* dst)
{
if (!src || !dst || src->isBufferInvalid() || dst->isBufferInvalid())
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 8f9e1b3c54..c49184e338 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -33,7 +33,7 @@
#include "lltrace.h"
const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2
-const S32 MAX_IMAGE_MIP = 11; // 2048x2048
+const S32 MAX_IMAGE_MIP = 12; // 4096x4096
// *TODO : Use MAX_IMAGE_MIP as max discard level and modify j2c management so that the number
// of levels is read from the header's file, not inferred from its size.
@@ -44,7 +44,7 @@ const S32 MAX_DISCARD_LEVEL = 5;
// and declared right here. Some come from the JPEG2000 spec, some conventions specific to SL.
const S32 MAX_DECOMPOSITION_LEVELS = 32; // Number of decomposition levels cannot exceed 32 according to jpeg2000 spec
const S32 MIN_DECOMPOSITION_LEVELS = 5; // the SL viewer will *crash* trying to decode images with fewer than 5 decomposition levels (unless image is small that is)
-const S32 MAX_PRECINCT_SIZE = 2048; // No reason to be bigger than MAX_IMAGE_SIZE
+const S32 MAX_PRECINCT_SIZE = 4096; // No reason to be bigger than MAX_IMAGE_SIZE
const S32 MIN_PRECINCT_SIZE = 4; // Can't be smaller than MIN_BLOCK_SIZE
const S32 MAX_BLOCK_SIZE = 64; // Max total block size is 4096, hence 64x64 when using square blocks
const S32 MIN_BLOCK_SIZE = 4; // Min block dim is 4 according to jpeg2000 spec
@@ -52,11 +52,11 @@ const S32 MIN_LAYER_SIZE = 2000; // Size of the first quality layer (after hea
const S32 MAX_NB_LAYERS = 64; // Max number of layers we'll entertain in SL (practical limit)
const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
-const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048
+const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 4096
const S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE;
const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
const S32 MAX_IMAGE_COMPONENTS = 8;
-const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 * 2048 * 8 = 16 MB
+const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //4096 * 4096 * 8 = 128 MB
// Note! These CANNOT be changed without modifying simulator code
// *TODO: change both to 1024 when SIM texture fetching is deprecated
@@ -71,6 +71,7 @@ const S32 HTTP_PACKET_SIZE = 1496;
class LLImageFormatted;
class LLImageRaw;
class LLColor4U;
+class LLColor3;
typedef enum e_image_codec
{
@@ -209,9 +210,13 @@ public:
void verticalFlip();
+ // Returns true if the image is not fully opaque
+ bool checkHasTransparentPixels();
// if the alpha channel is all 100% opaque, delete it
// returns true if alpha channel was deleted
bool optimizeAwayAlpha();
+ // Create an alpha channel if this image doesn't have one
+ bool makeAlpha();
static S32 biasedDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
static S32 expandDimToPowerOfTwo(S32 curr_dim, S32 max_dim = MAX_IMAGE_SIZE);
@@ -225,6 +230,9 @@ public:
// Fill the buffer with a constant color
void fill( const LLColor4U& color );
+ // Multiply this raw image by the given color
+ void tint( const LLColor3& color );
+
// Copy operations
//duplicate this raw image if refCount > 1.
@@ -268,6 +276,12 @@ public:
// Src and dst are same size. Src has 4 components. Dst has 3 components.
void compositeUnscaled4onto3( LLImageRaw* src );
+ // Emissive operations used by minimap
+ // Roughly emulates GLTF emissive texture, but is not GLTF-compliant
+ // *TODO: Remove in favor of shader
+ void addEmissive(LLImageRaw* src);
+ void addEmissiveScaled(LLImageRaw* src);
+ void addEmissiveUnscaled(LLImageRaw* src);
protected:
// Create an image from a local file (generally used in tools)
//bool createFromFile(const std::string& filename, bool j2c_lowest_mip_only = false);
diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp
index c3cd7262fb..6688dadd3a 100644
--- a/indra/llinventory/llsettingssky.cpp
+++ b/indra/llinventory/llsettingssky.cpp
@@ -407,7 +407,6 @@ LLSettingsSky::LLSettingsSky(const LLSD &data) :
mNextRainbowTextureId(),
mNextHaloTextureId()
{
- mCanAutoAdjust = !data.has(SETTING_REFLECTION_PROBE_AMBIANCE);
}
LLSettingsSky::LLSettingsSky():
@@ -430,8 +429,6 @@ void LLSettingsSky::replaceSettings(LLSD settings)
mNextBloomTextureId.setNull();
mNextRainbowTextureId.setNull();
mNextHaloTextureId.setNull();
-
- mCanAutoAdjust = !settings.has(SETTING_REFLECTION_PROBE_AMBIANCE);
}
void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother)
@@ -444,7 +441,6 @@ void LLSettingsSky::replaceWithSky(LLSettingsSky::ptr_t pother)
mNextBloomTextureId = pother->mNextBloomTextureId;
mNextRainbowTextureId = pother->mNextRainbowTextureId;
mNextHaloTextureId = pother->mNextHaloTextureId;
- mCanAutoAdjust = pother->mCanAutoAdjust;
}
void LLSettingsSky::blend(const LLSettingsBase::ptr_t &end, F64 blendf)
@@ -1146,7 +1142,6 @@ void LLSettingsSky::setSkyIceLevel(F32 ice_level)
void LLSettingsSky::setReflectionProbeAmbiance(F32 ambiance)
{
- mCanAutoAdjust = false; // we've now touched this sky in a "new" way, it can no longer auto adjust
setValue(SETTING_REFLECTION_PROBE_AMBIANCE, ambiance);
}
@@ -1448,24 +1443,6 @@ F32 LLSettingsSky::getReflectionProbeAmbiance(bool auto_adjust) const
return mSettings[SETTING_REFLECTION_PROBE_AMBIANCE].asReal();
}
-F32 LLSettingsSky::getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust) const
-{
-#if 0
- // feed cloud shadow back into reflection probe ambiance to mimic pre-reflection-probe behavior
- // without brightening dark/interior spaces
- F32 probe_ambiance = getReflectionProbeAmbiance(auto_adjust);
-
- if (probe_ambiance > 0.f && probe_ambiance < 1.f)
- {
- probe_ambiance += (1.f - probe_ambiance) * getCloudShadow() * cloud_shadow_scale;
- }
-
- return probe_ambiance;
-#else
- return getReflectionProbeAmbiance(auto_adjust);
-#endif
-}
-
F32 LLSettingsSky::getSkyBottomRadius() const
{
return mSettings[SETTING_SKY_BOTTOM_RADIUS].asReal();
@@ -1810,3 +1787,8 @@ LLUUID LLSettingsSky::getNextBloomTextureId() const
return mNextBloomTextureId;
}
+// if true, this sky is a candidate for auto-adjustment
+bool LLSettingsSky::canAutoAdjust() const
+{
+ return !mSettings.has(SETTING_REFLECTION_PROBE_AMBIANCE);
+}
diff --git a/indra/llinventory/llsettingssky.h b/indra/llinventory/llsettingssky.h
index 7ba7a9ba06..222ab040cb 100644
--- a/indra/llinventory/llsettingssky.h
+++ b/indra/llinventory/llsettingssky.h
@@ -139,10 +139,6 @@ public:
// auto_adjust - if true and canAutoAdjust() is true, return 1.0
F32 getReflectionProbeAmbiance(bool auto_adjust = false) const;
- // get the probe ambiance setting to use for rendering (adjusted by cloud shadow, aka cloud coverage)
- // auto_adjust - if true and canAutoAdjust() is true, return 1.0
- F32 getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust = false) const;
-
// Return first (only) profile layer represented in LLSD
LLSD getRayleighConfig() const;
LLSD getMieConfig() const;
@@ -340,7 +336,7 @@ public:
virtual void updateSettings() SETTINGS_OVERRIDE;
// if true, this sky is a candidate for auto-adjustment
- bool canAutoAdjust() const { return mCanAutoAdjust; }
+ bool canAutoAdjust() const;
protected:
static const std::string SETTING_LEGACY_EAST_ANGLE;
@@ -385,9 +381,6 @@ private:
mutable LLColor4 mTotalAmbient;
mutable LLColor4 mHazeColor;
- // if true, this sky is a candidate for auto adjustment
- bool mCanAutoAdjust = true;
-
typedef std::map<std::string, S32> mapNameToUniformId_t;
static mapNameToUniformId_t sNameToUniformMapping;
diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h
index c4d04f5d02..e1d3536f66 100644
--- a/indra/llmath/llcamera.h
+++ b/indra/llmath/llcamera.h
@@ -65,7 +65,6 @@ class LLCamera
: public LLCoordFrame
{
public:
-
LLCamera(const LLCamera& rhs)
{
*this = rhs;
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index 2cf50e9cd2..12fc6d570a 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -34,8 +34,8 @@
class LLMatrix4a
{
public:
- LL_ALIGN_16(LLVector4a mMatrix[4]);
-
+ LL_ALIGN_16(LLVector4a mMatrix[4]);
+
LLMatrix4a()
{
@@ -56,6 +56,16 @@ public:
return (F32*)&mMatrix;
}
+ inline LLMatrix4& asMatrix4()
+ {
+ return *(LLMatrix4*)this;
+ }
+
+ inline const LLMatrix4& asMatrix4() const
+ {
+ return *(LLMatrix4*)this;
+ }
+
inline void clear()
{
mMatrix[0].clear();
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index e694732da2..4384c732b2 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -45,16 +45,15 @@
#include "llmatrix3a.h"
#include "lloctree.h"
#include "llvolume.h"
-#include "llvolumeoctree.h"
#include "llstl.h"
#include "llsdserialize.h"
#include "llvector4a.h"
#include "llmatrix4a.h"
#include "llmeshoptimizer.h"
#include "lltimer.h"
+#include "llvolumeoctree.h"
-#include "mikktspace/mikktspace.h"
-#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file
+#include "mikktspace/mikktspace.hh"
#include "meshoptimizer/meshoptimizer.h"
@@ -377,77 +376,6 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons
}
}
-class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
-{
-public:
- const LLVolumeFace* mFace;
-
- LLVolumeOctreeRebound(const LLVolumeFace* face)
- {
- mFace = face;
- }
-
- virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
- { //this is a depth first traversal, so it's safe to assum all children have complete
- //bounding data
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
-
- LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
-
- LLVector4a& min = node->mExtents[0];
- LLVector4a& max = node->mExtents[1];
-
- if (!branch->isEmpty())
- { //node has data, find AABB that binds data set
- const LLVolumeTriangle* tri = *(branch->getDataBegin());
-
- //initialize min/max to first available vertex
- min = *(tri->mV[0]);
- max = *(tri->mV[0]);
-
- for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
- { //for each triangle in node
-
- //stretch by triangles in node
- tri = *iter;
-
- min.setMin(min, *tri->mV[0]);
- min.setMin(min, *tri->mV[1]);
- min.setMin(min, *tri->mV[2]);
-
- max.setMax(max, *tri->mV[0]);
- max.setMax(max, *tri->mV[1]);
- max.setMax(max, *tri->mV[2]);
- }
- }
- else if (branch->getChildCount() > 0)
- { //no data, but child nodes exist
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
-
- //initialize min/max to extents of first child
- min = child->mExtents[0];
- max = child->mExtents[1];
- }
- else
- {
- llassert(!branch->isLeaf()); // Empty leaf
- }
-
- for (S32 i = 0; i < branch->getChildCount(); ++i)
- { //stretch by child extents
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
- min.setMin(min, child->mExtents[0]);
- max.setMax(max, child->mExtents[1]);
- }
-
- node->mBounds[0].setAdd(min, max);
- node->mBounds[0].mul(0.5f);
-
- node->mBounds[1].setSub(max,min);
- node->mBounds[1].mul(0.5f);
- }
-};
-
//-------------------------------------------------------------------
// statics
//-------------------------------------------------------------------
@@ -5507,8 +5435,41 @@ struct MikktData
}
}
}
-};
+ uint32_t GetNumFaces()
+ {
+ return uint32_t(face->mNumIndices / 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 = tc[face_num * 3 + vert_num].mV;
+ return mikk::float3(uv[0], 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);
+ }
+};
bool LLVolumeFace::cacheOptimize(bool gen_tangents)
{ //optimize for vertex cache according to Forsyth method:
@@ -5520,62 +5481,9 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
{ // generate mikkt space tangents before cache optimizing since the index buffer may change
// a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh
// and is executed on a background thread
- SMikkTSpaceInterface ms;
-
- ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- LLVolumeFace* face = data->face;
- return face->mNumIndices / 3;
- };
-
- ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace)
- {
- return 3;
- };
-
- ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- F32* v = data->p[iFace * 3 + iVert].mV;
- fvPosOut[0] = v[0];
- fvPosOut[1] = v[1];
- fvPosOut[2] = v[2];
- };
-
- ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- F32* n = data->n[iFace * 3 + iVert].mV;
- fvNormOut[0] = n[0];
- fvNormOut[1] = n[1];
- fvNormOut[2] = n[2];
- };
-
- ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- F32* tc = data->tc[iFace * 3 + iVert].mV;
- fvTexcOut[0] = tc[0];
- fvTexcOut[1] = tc[1];
- };
-
- ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
- {
- MikktData* data = (MikktData*)pContext->m_pUserData;
- S32 i = iFace * 3 + iVert;
-
- data->t[i].set(fvTangent);
- data->t[i].mV[3] = fSign;
- };
-
- ms.m_setTSpace = nullptr;
-
MikktData data(this);
-
- SMikkTSpaceContext ctx = { &ms, &data };
-
- genTangSpaceDefault(&ctx);
+ mikk::Mikktspace ctx(data);
+ ctx.genTangSpace();
//re-weld
meshopt_Stream mos[] =
@@ -5596,9 +5504,6 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
if (vert_count < 65535 && vert_count != 0)
{
- std::vector<U32> indices;
- indices.resize(mNumIndices);
-
//copy results back into volume
resizeVertices(vert_count);
@@ -5687,8 +5592,7 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
llassert(mNumIndices % 3 == 0);
- mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL);
- new LLVolumeOctreeListener(mOctree);
+ mOctree = new LLVolumeOctree(center, size);
const U32 num_triangles = mNumIndices / 3;
// Initialize all the triangles we need
mOctreeTriangles = new LLVolumeTriangle[num_triangles];
@@ -5743,7 +5647,7 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
while (!mOctree->balance()) { }
//calculate AABB for each node
- LLVolumeOctreeRebound rebound(this);
+ LLVolumeOctreeRebound rebound;
rebound.traverse(mOctree);
if (gDebugGL)
@@ -5756,12 +5660,12 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
void LLVolumeFace::destroyOctree()
{
delete mOctree;
- mOctree = NULL;
+ mOctree = nullptr;
delete[] mOctreeTriangles;
- mOctreeTriangles = NULL;
+ mOctreeTriangles = nullptr;
}
-const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const
+const LLVolumeOctree* LLVolumeFace::getOctree() const
{
return mOctree;
}
@@ -6476,9 +6380,6 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
return TRUE;
}
-void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
- const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent);
-
void LLVolumeFace::createTangents()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
@@ -6496,7 +6397,7 @@ void LLVolumeFace::createTangents()
(*ptr++).clear();
}
- CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents);
+ LLCalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents);
//normalize normals
for (U32 i = 0; i < mNumVertices; i++)
@@ -7206,7 +7107,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
}
//adapted from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
-void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
+void LLCalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 71878b8cb6..ccc0671315 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -41,6 +41,7 @@ template <class T, typename T_PTR> class LLOctreeNode;
class LLVolumeFace;
class LLVolume;
class LLVolumeTriangle;
+class LLVolumeOctree;
#include "lluuid.h"
#include "v4color.h"
@@ -913,7 +914,7 @@ public:
void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
void destroyOctree();
// Get a reference to the octree, which may be null
- const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const;
+ const LLVolumeOctree* getOctree() const;
enum
{
@@ -987,7 +988,7 @@ public:
LLVector3 mNormalizedScale = LLVector3(1,1,1);
private:
- LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
+ LLVolumeOctree* mOctree;
LLVolumeTriangle* mOctreeTriangles;
BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
@@ -1142,6 +1143,8 @@ public:
std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
+void LLCalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal, const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent);
+
BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size);
BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size);
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
index 6894d04d3c..95c7cb0b5c 100644
--- a/indra/llmath/llvolumeoctree.cpp
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -92,15 +92,15 @@ void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTria
}
LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
- const LLVolumeFace* face, F32* closest_t,
+ LLVolumeFace* face, F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
- : mFace(face),
- mStart(start),
+ : mStart(start),
mDir(dir),
mIntersection(intersection),
mTexCoord(tex_coord),
mNormal(normal),
mTangent(tangent),
+ mFace(face),
mClosestT(closest_t),
mHitFace(false)
{
@@ -139,7 +139,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
{
*mClosestT = t;
mHitFace = true;
-
+ mHitTriangle = tri;
if (mIntersection != NULL)
{
LLVector4a intersect = mDir;
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
index d65bca5e52..0bbb793896 100644
--- a/indra/llmath/llvolumeoctree.h
+++ b/indra/llmath/llvolumeoctree.h
@@ -62,7 +62,7 @@ public:
LL_ALIGN_16(LLVector4a mPositionGroup);
const LLVector4a* mV[3];
- U16 mIndex[3];
+ U32 mIndex[3];
F32 mRadius;
mutable S32 mBinIndex;
@@ -112,7 +112,6 @@ public:
class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
{
public:
- const LLVolumeFace* mFace;
LLVector4a mStart;
LLVector4a mDir;
LLVector4a mEnd;
@@ -121,10 +120,13 @@ public:
LLVector4a* mNormal;
LLVector4a* mTangent;
F32* mClosestT;
+ LLVolumeFace* mFace;
bool mHitFace;
+ const LLVolumeTriangle* mHitTriangle = nullptr;
- LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
- const LLVolumeFace* face, F32* closest_t,
+ LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
+ LLVolumeFace* face,
+ F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
@@ -137,4 +139,91 @@ class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle, LLVolum
virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch);
};
+class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
+{
+public:
+ LLVolumeOctreeRebound()
+ {
+ }
+
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
+ { //this is a depth first traversal, so it's safe to assum all children have complete
+ //bounding data
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+
+ LLVolumeOctreeListener* node = (LLVolumeOctreeListener*)branch->getListener(0);
+
+ LLVector4a& min = node->mExtents[0];
+ LLVector4a& max = node->mExtents[1];
+
+ if (!branch->isEmpty())
+ { //node has data, find AABB that binds data set
+ const LLVolumeTriangle* tri = *(branch->getDataBegin());
+
+ //initialize min/max to first available vertex
+ min = *(tri->mV[0]);
+ max = *(tri->mV[0]);
+
+ for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
+ { //for each triangle in node
+
+ //stretch by triangles in node
+ tri = *iter;
+
+ min.setMin(min, *tri->mV[0]);
+ min.setMin(min, *tri->mV[1]);
+ min.setMin(min, *tri->mV[2]);
+
+ max.setMax(max, *tri->mV[0]);
+ max.setMax(max, *tri->mV[1]);
+ max.setMax(max, *tri->mV[2]);
+ }
+ }
+ else if (branch->getChildCount() > 0)
+ { //no data, but child nodes exist
+ LLVolumeOctreeListener* child = (LLVolumeOctreeListener*)branch->getChild(0)->getListener(0);
+
+ //initialize min/max to extents of first child
+ min = child->mExtents[0];
+ max = child->mExtents[1];
+ }
+ else
+ {
+ llassert(!branch->isLeaf()); // Empty leaf
+ }
+
+ for (S32 i = 0; i < branch->getChildCount(); ++i)
+ { //stretch by child extents
+ LLVolumeOctreeListener* child = (LLVolumeOctreeListener*)branch->getChild(i)->getListener(0);
+ min.setMin(min, child->mExtents[0]);
+ max.setMax(max, child->mExtents[1]);
+ }
+
+ node->mBounds[0].setAdd(min, max);
+ node->mBounds[0].mul(0.5f);
+
+ node->mBounds[1].setSub(max, min);
+ node->mBounds[1].mul(0.5f);
+ }
+};
+
+class LLVolumeOctree : public LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>, public LLRefCount
+{
+public:
+ LLVolumeOctree(const LLVector4a& center, const LLVector4a& size)
+ :
+ LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, nullptr),
+ LLRefCount()
+ {
+ new LLVolumeOctreeListener(this);
+ }
+
+ LLVolumeOctree()
+ : LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(LLVector4a::getZero(), LLVector4a(1.f,1.f,1.f), nullptr),
+ LLRefCount()
+ {
+ new LLVolumeOctreeListener(this);
+ }
+};
+
#endif
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index 756d0b5db8..f5966b71de 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -582,7 +582,7 @@ void LLPluginProcessParent::idle(void)
params.args.add("-e");
params.args.add("tell application \"Terminal\"");
params.args.add("-e");
- params.args.add(STRINGIZE("set win to do script \"gdb -pid "
+ params.args.add(STRINGIZE("set win to do script \"lldb -pid "
<< mProcess->getProcessID() << "\""));
params.args.add("-e");
params.args.add("do script \"continue\" in win");
diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h
index f62cab1a61..855cb58832 100644
--- a/indra/llprimitive/llgltfmaterial.h
+++ b/indra/llprimitive/llgltfmaterial.h
@@ -130,6 +130,16 @@ public:
bool mOverrideDoubleSided = false;
bool mOverrideAlphaMode = false;
+ // *TODO: If/when we implement additional GLTF extensions, they may not be
+ // compatible with our GLTF terrain implementation. We may want to disallow
+ // materials with some features from being set on terrain, if their
+ // implementation on terrain is not compliant with the spec:
+ // - KHR_materials_transmission: Probably OK?
+ // - KHR_materials_ior: Probably OK?
+ // - KHR_materials_volume: Likely incompatible, as our terrain
+ // heightmaps cannot currently be described as finite enclosed
+ // volumes.
+ // See also LLPanelRegionTerrainInfo::validateMaterials
// These fields are local to viewer and are a part of local bitmap support
typedef std::map<LLUUID, LLUUID> local_tex_map_t;
local_tex_map_t mTrackingIdToLocalTexture;
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index 904747af2d..53d09df618 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -1934,6 +1934,19 @@ void LLReflectionProbeParams::setIsDynamic(bool is_dynamic)
}
}
+
+void LLReflectionProbeParams::setIsMirror(bool is_mirror)
+{
+ if (is_mirror)
+ {
+ mFlags |= FLAG_MIRROR;
+ }
+ else
+ {
+ mFlags &= ~FLAG_MIRROR;
+ }
+}
+
//============================================================================
LLFlexibleObjectData::LLFlexibleObjectData()
{
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index 0b7dbd703a..5622483861 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -186,6 +186,7 @@ public:
{
FLAG_BOX_VOLUME = 0x01, // use a box influence volume
FLAG_DYNAMIC = 0x02, // render dynamic objects (avatars) into this Reflection Probe
+ FLAG_MIRROR = 0x04, // This probe is used for reflections on realtime mirrors.
};
protected:
@@ -209,11 +210,13 @@ public:
void setClipDistance(F32 distance) { mClipDistance = llclamp(distance, REFLECTION_PROBE_MIN_CLIP_DISTANCE, REFLECTION_PROBE_MAX_CLIP_DISTANCE); }
void setIsBox(bool is_box);
void setIsDynamic(bool is_dynamic);
+ void setIsMirror(bool is_mirror);
F32 getAmbiance() const { return mAmbiance; }
F32 getClipDistance() const { return mClipDistance; }
bool getIsBox() const { return (mFlags & FLAG_BOX_VOLUME) != 0; }
bool getIsDynamic() const { return (mFlags & FLAG_DYNAMIC) != 0; }
+ bool getIsMirror() const { return (mFlags & FLAG_MIRROR) != 0; }
};
//-------------------------------------------------
diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index 71caff1686..ee2a4c769a 100644
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -685,6 +685,7 @@ S32 LLTextureEntry::setMaterialParams(const LLMaterialPtr pMaterialParams)
mMaterialUpdatePending = true;
}
mMaterial = pMaterialParams;
+
return TEM_CHANGE_TEXTURE;
}
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
index f5f2c0172d..0935147688 100644
--- a/indra/llprimitive/lltextureentry.h
+++ b/indra/llprimitive/lltextureentry.h
@@ -134,7 +134,7 @@ public:
S32 setGlow(F32 glow);
S32 setMaterialID(const LLMaterialID& pMaterialID);
S32 setMaterialParams(const LLMaterialPtr pMaterialParams);
-
+
virtual const LLUUID &getID() const { return mID; }
const LLColor4 &getColor() const { return mColor; }
const F32 getAlpha() const { return mColor.mV[VALPHA]; }
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index ccfb8f69be..be5ad08fbe 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -1184,12 +1184,14 @@ S32 LLGLSLShader::getTextureChannel(S32 uniform) const
S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTexUnit::eTextureColorSpace space)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
-
+
if (uniform < 0 || uniform >= (S32)mTexture.size())
{
LL_SHADER_UNIFORM_ERRS() << "Uniform out of range: " << uniform << LL_ENDL;
return -1;
}
+
+
S32 index = mTexture[uniform];
if (index != -1)
{
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 43d095f73a..ed0f27d143 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -49,13 +49,14 @@ public:
bool hasShadows = false;
bool hasAmbientOcclusion = false;
bool hasSrgb = false;
- bool encodesNormal = false; // include: shaders\class1\environment\encodeNormF.glsl
bool isDeferred = false;
bool hasScreenSpaceReflections = false;
bool disableTextureIndex = false;
bool hasAlphaMask = false;
bool hasReflectionProbes = false;
bool attachNothing = false;
+ bool hasHeroProbes = false;
+ bool isPBRTerrain = false;
};
// ============= Structure for caching shader uniforms ===============
diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index b616002b49..a590a8e745 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -95,7 +95,8 @@ void LLGLTexture::setBoostLevel(S32 level)
mBoostLevel = level ;
if(mBoostLevel != LLGLTexture::BOOST_NONE
&& mBoostLevel != LLGLTexture::BOOST_ICON
- && mBoostLevel != LLGLTexture::BOOST_THUMBNAIL)
+ && mBoostLevel != LLGLTexture::BOOST_THUMBNAIL
+ && mBoostLevel != LLGLTexture::BOOST_TERRAIN)
{
setNoDelete() ;
}
diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h
index 24849d1d1b..fa6faa2583 100644
--- a/indra/llrender/llgltexture.h
+++ b/indra/llrender/llgltexture.h
@@ -42,7 +42,7 @@ class LLGLTexture : public LLTexture
public:
enum
{
- MAX_IMAGE_SIZE_DEFAULT = 1024,
+ MAX_IMAGE_SIZE_DEFAULT = 2048,
INVALID_DISCARD_LEVEL = 0x7fff
};
@@ -52,10 +52,11 @@ public:
BOOST_AVATAR ,
BOOST_AVATAR_BAKED ,
BOOST_SCULPTED ,
+ BOOST_TERRAIN , // Needed for minimap generation for now. Lower than BOOST_HIGH so the texture stats don't get forced, i.e. texture stats are manually managed by minimap/terrain instead.
BOOST_HIGH = 10,
BOOST_BUMP ,
- BOOST_TERRAIN , // has to be high priority for minimap / low detail
+ BOOST_UNUSED_1 , // Placeholder to avoid disrupting habits around texture debug
BOOST_SELECTED ,
BOOST_AVATAR_BAKED_SELF ,
BOOST_AVATAR_SELF , // needed for baking avatar
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 4d64dc9e10..358b35f36b 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -2068,10 +2068,6 @@ void LLRender::diffuseColor3f(F32 r, F32 g, F32 b)
{
shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,1.f);
}
- else
- {
- glColor3f(r,g,b);
- }
}
void LLRender::diffuseColor3fv(const F32* c)
@@ -2083,10 +2079,6 @@ void LLRender::diffuseColor3fv(const F32* c)
{
shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0], c[1], c[2], 1.f);
}
- else
- {
- glColor3fv(c);
- }
}
void LLRender::diffuseColor4f(F32 r, F32 g, F32 b, F32 a)
@@ -2098,10 +2090,6 @@ void LLRender::diffuseColor4f(F32 r, F32 g, F32 b, F32 a)
{
shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r,g,b,a);
}
- else
- {
- glColor4f(r,g,b,a);
- }
}
void LLRender::diffuseColor4fv(const F32* c)
@@ -2113,10 +2101,6 @@ void LLRender::diffuseColor4fv(const F32* c)
{
shader->uniform4fv(LLShaderMgr::DIFFUSE_COLOR, 1, c);
}
- else
- {
- glColor4fv(c);
- }
}
void LLRender::diffuseColor4ubv(const U8* c)
@@ -2128,10 +2112,6 @@ void LLRender::diffuseColor4ubv(const U8* c)
{
shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, c[0]/255.f, c[1]/255.f, c[2]/255.f, c[3]/255.f);
}
- else
- {
- glColor4ubv(c);
- }
}
void LLRender::diffuseColor4ub(U8 r, U8 g, U8 b, U8 a)
@@ -2143,10 +2123,6 @@ void LLRender::diffuseColor4ub(U8 r, U8 g, U8 b, U8 a)
{
shader->uniform4f(LLShaderMgr::DIFFUSE_COLOR, r/255.f, g/255.f, b/255.f, a/255.f);
}
- else
- {
- glColor4ub(r,g,b,a);
- }
}
diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp
index 88c48e5166..9cd7527d3e 100644
--- a/indra/llrender/llrendertarget.cpp
+++ b/indra/llrender/llrendertarget.cpp
@@ -567,4 +567,31 @@ bool LLRenderTarget::isBoundInStack() const
return cur == this;
}
+void LLRenderTarget::swapFBORefs(LLRenderTarget& other)
+{
+ // Must be initialized
+ llassert(mFBO);
+ llassert(other.mFBO);
+ // Must be unbound
+ // *NOTE: mPreviousRT can be non-null even if this target is unbound - presumably for debugging purposes?
+ llassert(sCurFBO != mFBO);
+ llassert(sCurFBO != other.mFBO);
+ llassert(!isBoundInStack());
+ llassert(!other.isBoundInStack());
+
+ // Must be same type
+ llassert(sUseFBO == other.sUseFBO);
+ llassert(mResX == other.mResX);
+ llassert(mResY == other.mResY);
+ llassert(mInternalFormat == other.mInternalFormat);
+ llassert(mTex.size() == other.mTex.size());
+ llassert(mDepth == other.mDepth);
+ llassert(mUseDepth == other.mUseDepth);
+ llassert(mGenerateMipMaps == other.mGenerateMipMaps);
+ llassert(mMipLevels == other.mMipLevels);
+ llassert(mUsage == other.mUsage);
+
+ std::swap(mFBO, other.mFBO);
+ std::swap(mTex, other.mTex);
+}
diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h
index 9fcea35e3d..da401572d1 100644
--- a/indra/llrender/llrendertarget.h
+++ b/indra/llrender/llrendertarget.h
@@ -169,6 +169,9 @@ public:
static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; }
+ // *HACK
+ void swapFBORefs(LLRenderTarget& other);
+
protected:
U32 mResX;
U32 mResY;
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index f78be910d2..636e13719a 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -44,6 +44,7 @@ using std::make_pair;
using std::string;
LLShaderMgr * LLShaderMgr::sInstance = NULL;
+bool LLShaderMgr::sMirrorsEnabled = false;
LLShaderMgr::LLShaderMgr()
{
@@ -183,7 +184,13 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
// Attach Fragment Shader Features Next
///////////////////////////////////////
-// NOTE order of shader object attaching is VERY IMPORTANT!!!
+ // NOTE order of shader object attaching is VERY IMPORTANT!!!
+
+ if (!shader->attachFragmentObject("deferred/globalF.glsl"))
+ {
+ return FALSE;
+ }
+
if (features->hasSrgb || features->hasAtmospherics || features->calculatesAtmospherics || features->isDeferred)
{
if (!shader->attachFragmentObject("environment/srgbF.glsl"))
@@ -257,14 +264,6 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
}
}
- if (features->encodesNormal)
- {
- if (!shader->attachFragmentObject("environment/encodeNormF.glsl"))
- {
- return FALSE;
- }
- }
-
if (features->hasAtmospherics || features->isDeferred)
{
if (!shader->attachFragmentObject("windlight/atmosphericsFuncs.glsl")) {
@@ -277,6 +276,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
}
}
+ if (features->isPBRTerrain)
+ {
+ if (!shader->attachFragmentObject("deferred/pbrterrainUtilF.glsl"))
+ {
+ return FALSE;
+ }
+ }
+
// NOTE order of shader object attaching is VERY IMPORTANT!!!
if (features->hasAtmospherics)
{
@@ -321,7 +328,7 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
return FALSE;
}
}
- shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels-1, 1);
+ shader->mFeatures.mIndexedTextureChannels = llmax(LLGLSLShader::sIndexedTextureChannels, 1);
}
}
@@ -572,21 +579,38 @@ GLuint LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shader_lev
}
else
{
- //set version to 1.40
- shader_code_text[shader_code_count++] = strdup("#version 140\n");
- //some implementations of GLSL 1.30 require integer precision be explicitly declared
- extra_code_text[extra_code_count++] = strdup("precision mediump int;\n");
- extra_code_text[extra_code_count++] = strdup("precision highp float;\n");
+ if (type == GL_GEOMETRY_SHADER)
+ {
+ //set version to 1.50
+ shader_code_text[shader_code_count++] = strdup("#version 150\n");
+ //some implementations of GLSL 1.30 require integer precision be explicitly declared
+ extra_code_text[extra_code_count++] = strdup("precision mediump int;\n");
+ extra_code_text[extra_code_count++] = strdup("precision highp float;\n");
+ }
+ else
+ {
+ //set version to 1.40
+ shader_code_text[shader_code_count++] = strdup("#version 140\n");
+ //some implementations of GLSL 1.30 require integer precision be explicitly declared
+ extra_code_text[extra_code_count++] = strdup("precision mediump int;\n");
+ extra_code_text[extra_code_count++] = strdup("precision highp float;\n");
+ }
}
extra_code_text[extra_code_count++] = strdup("#define FXAA_GLSL_130 1\n");
}
+ if (sMirrorsEnabled)
+ {
+ extra_code_text[extra_code_count++] = strdup("#define HERO_PROBES 1\n");
+ }
+
// Use alpha float to store bit flags
// See: C++: addDeferredAttachment(), shader: frag_data[2]
extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_SKIP_ATMOS 0.0 \n"); // atmo kill
extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_ATMOS 0.34\n"); // bit 0
extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_PBR 0.67\n"); // bit 1
+ extra_code_text[extra_code_count++] = strdup("#define GBUFFER_FLAG_HAS_HDRI 1.0\n"); // bit 2
extra_code_text[extra_code_count++] = strdup("#define GET_GBUFFER_FLAG(flag) (abs(norm.w-flag)< 0.1)\n");
if (defines)
@@ -1192,6 +1216,9 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("emissiveColor");
mReservedUniforms.push_back("metallicFactor");
mReservedUniforms.push_back("roughnessFactor");
+ mReservedUniforms.push_back("mirror_flag");
+ mReservedUniforms.push_back("clipPlane");
+ mReservedUniforms.push_back("clipSign");
mReservedUniforms.push_back("diffuseMap");
mReservedUniforms.push_back("altDiffuseMap");
@@ -1204,6 +1231,7 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("sceneDepth");
mReservedUniforms.push_back("reflectionProbes");
mReservedUniforms.push_back("irradianceProbes");
+ mReservedUniforms.push_back("heroProbes");
mReservedUniforms.push_back("cloud_noise_texture");
mReservedUniforms.push_back("cloud_noise_texture_next");
mReservedUniforms.push_back("fullbright");
@@ -1374,8 +1402,32 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("detail_1");
mReservedUniforms.push_back("detail_2");
mReservedUniforms.push_back("detail_3");
+
mReservedUniforms.push_back("alpha_ramp");
+ mReservedUniforms.push_back("detail_0_base_color");
+ mReservedUniforms.push_back("detail_1_base_color");
+ mReservedUniforms.push_back("detail_2_base_color");
+ mReservedUniforms.push_back("detail_3_base_color");
+ mReservedUniforms.push_back("detail_0_normal");
+ mReservedUniforms.push_back("detail_1_normal");
+ mReservedUniforms.push_back("detail_2_normal");
+ mReservedUniforms.push_back("detail_3_normal");
+ mReservedUniforms.push_back("detail_0_metallic_roughness");
+ mReservedUniforms.push_back("detail_1_metallic_roughness");
+ mReservedUniforms.push_back("detail_2_metallic_roughness");
+ mReservedUniforms.push_back("detail_3_metallic_roughness");
+ mReservedUniforms.push_back("detail_0_emissive");
+ mReservedUniforms.push_back("detail_1_emissive");
+ mReservedUniforms.push_back("detail_2_emissive");
+ mReservedUniforms.push_back("detail_3_emissive");
+
+ mReservedUniforms.push_back("baseColorFactors");
+ mReservedUniforms.push_back("metallicFactors");
+ mReservedUniforms.push_back("roughnessFactors");
+ mReservedUniforms.push_back("emissiveColors");
+ mReservedUniforms.push_back("minimum_alphas");
+
mReservedUniforms.push_back("origin");
mReservedUniforms.push_back("display_gamma");
@@ -1397,6 +1449,7 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("cloud_variance");
mReservedUniforms.push_back("reflection_probe_ambiance");
mReservedUniforms.push_back("max_probe_lod");
+ mReservedUniforms.push_back("probe_strength");
mReservedUniforms.push_back("sh_input_r");
mReservedUniforms.push_back("sh_input_g");
@@ -1407,6 +1460,8 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("sun_up_factor");
mReservedUniforms.push_back("moonlight_color");
+ mReservedUniforms.push_back("debug_normal_draw_length");
+
llassert(mReservedUniforms.size() == END_RESERVED_UNIFORMS);
std::set<std::string> dupe_check;
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 79a24773e1..eb13418cd7 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -85,6 +85,9 @@ public:
EMISSIVE_COLOR, // "emissiveColor"
METALLIC_FACTOR, // "metallicFactor"
ROUGHNESS_FACTOR, // "roughnessFactor"
+ MIRROR_FLAG, // "mirror_flag"
+ CLIP_PLANE, // "clipPlane"
+ CLIP_SIGN, // "clipSign"
DIFFUSE_MAP, // "diffuseMap"
ALTERNATE_DIFFUSE_MAP, // "altDiffuseMap"
SPECULAR_MAP, // "specularMap"
@@ -96,6 +99,7 @@ public:
SCENE_DEPTH, // "sceneDepth"
REFLECTION_PROBES, // "reflectionProbes"
IRRADIANCE_PROBES, // "irradianceProbes"
+ HERO_PROBE, // "heroProbes"
CLOUD_NOISE_MAP, // "cloud_noise_texture"
CLOUD_NOISE_MAP_NEXT, // "cloud_noise_texture_next"
FULLBRIGHT, // "fullbright"
@@ -251,8 +255,32 @@ public:
TERRAIN_DETAIL1, // "detail_1"
TERRAIN_DETAIL2, // "detail_2"
TERRAIN_DETAIL3, // "detail_3"
+
TERRAIN_ALPHARAMP, // "alpha_ramp"
+ TERRAIN_DETAIL0_BASE_COLOR, // "detail_0_base_color" (GLTF)
+ TERRAIN_DETAIL1_BASE_COLOR, // "detail_1_base_color" (GLTF)
+ TERRAIN_DETAIL2_BASE_COLOR, // "detail_2_base_color" (GLTF)
+ TERRAIN_DETAIL3_BASE_COLOR, // "detail_3_base_color" (GLTF)
+ TERRAIN_DETAIL0_NORMAL, // "detail_0_normal" (GLTF)
+ TERRAIN_DETAIL1_NORMAL, // "detail_1_normal" (GLTF)
+ TERRAIN_DETAIL2_NORMAL, // "detail_2_normal" (GLTF)
+ TERRAIN_DETAIL3_NORMAL, // "detail_3_normal" (GLTF)
+ TERRAIN_DETAIL0_METALLIC_ROUGHNESS, // "detail_0_metallic_roughness" (GLTF)
+ TERRAIN_DETAIL1_METALLIC_ROUGHNESS, // "detail_1_metallic_roughness" (GLTF)
+ TERRAIN_DETAIL2_METALLIC_ROUGHNESS, // "detail_2_metallic_roughness" (GLTF)
+ TERRAIN_DETAIL3_METALLIC_ROUGHNESS, // "detail_3_metallic_roughness" (GLTF)
+ TERRAIN_DETAIL0_EMISSIVE, // "detail_0_emissive" (GLTF)
+ TERRAIN_DETAIL1_EMISSIVE, // "detail_1_emissive" (GLTF)
+ TERRAIN_DETAIL2_EMISSIVE, // "detail_2_emissive" (GLTF)
+ TERRAIN_DETAIL3_EMISSIVE, // "detail_3_emissive" (GLTF)
+
+ TERRAIN_BASE_COLOR_FACTORS, // "baseColorFactors" (GLTF)
+ TERRAIN_METALLIC_FACTORS, // "metallicFactors" (GLTF)
+ TERRAIN_ROUGHNESS_FACTORS, // "roughnessFactors" (GLTF)
+ TERRAIN_EMISSIVE_COLORS, // "emissiveColors" (GLTF)
+ TERRAIN_MINIMUM_ALPHAS, // "minimum_alphas" (GLTF)
+
SHINY_ORIGIN, // "origin"
DISPLAY_GAMMA, // "display_gamma"
@@ -279,6 +307,7 @@ public:
REFLECTION_PROBE_AMBIANCE, // "reflection_probe_ambiance"
REFLECTION_PROBE_MAX_LOD, // "max_probe_lod"
+ REFLECTION_PROBE_STRENGTH, // "probe_strength"
SH_INPUT_L1R, // "sh_input_r"
SH_INPUT_L1G, // "sh_input_g"
SH_INPUT_L1B, // "sh_input_b"
@@ -287,6 +316,9 @@ public:
WATER_EDGE_FACTOR, // "water_edge"
SUN_UP_FACTOR, // "sun_up_factor"
MOONLIGHT_COLOR, // "moonlight_color"
+
+ DEBUG_NORMAL_DRAW_LENGTH, // "debug_normal_draw_length"
+
END_RESERVED_UNIFORMS
} eGLSLReservedUniforms;
// clang-format on
@@ -336,6 +368,7 @@ public:
bool mShaderCacheInitialized = false;
bool mShaderCacheEnabled = false;
std::string mShaderCacheDir;
+ static bool sMirrorsEnabled;
protected:
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index de27636c33..1d81c3778b 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -657,7 +657,7 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto
U16 idx = indicesp[i];
gGL.vertex3fv(pos[idx].getF32ptr());
}
-}
+ }
gGL.end();
gGL.flush();
}
@@ -741,8 +741,8 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi
llassert(mGLBuffer == sGLRenderBuffer);
llassert(mGLIndices == sGLRenderIndices);
gGL.syncMatrices();
- glDrawRangeElements(sGLMode[mode], start, end, count, GL_UNSIGNED_SHORT,
- (GLvoid*) (indices_offset * sizeof(U16)));
+ glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType,
+ (GLvoid*) (indices_offset * (size_t) mIndicesStride));
}
void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
@@ -1139,7 +1139,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count)
}
// flush the given byte range
-// target -- "targret" parameter for glBufferSubData
+// target -- "target" parameter for glBufferSubData
// start -- first byte to copy
// end -- last byte to copy (NOT last byte + 1)
// data -- mMappedData or mMappedIndexData
@@ -1301,6 +1301,8 @@ bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector4a>& strider, U32 index,
}
bool LLVertexBuffer::getIndexStrider(LLStrider<U16>& strider, U32 index, S32 count)
{
+ llassert(mIndicesStride == 2); // cannot access 32-bit indices with U16 strider
+ llassert(mIndicesType == GL_UNSIGNED_SHORT);
return VertexBufferStrider<U16,TYPE_INDEX>::get(*this, strider, index, count);
}
bool LLVertexBuffer::getTexCoord0Strider(LLStrider<LLVector2>& strider, U32 index, S32 count)
@@ -1319,6 +1321,10 @@ bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, U32 index,
{
return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index, count);
}
+bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector4a>& strider, U32 index, S32 count)
+{
+ return VertexBufferStrider<LLVector4a, TYPE_NORMAL>::get(*this, strider, index, count);
+}
bool LLVertexBuffer::getTangentStrider(LLStrider<LLVector3>& strider, U32 index, S32 count)
{
return VertexBufferStrider<LLVector3,TYPE_TANGENT>::get(*this, strider, index, count);
@@ -1503,4 +1509,39 @@ void LLVertexBuffer::setColorData(const LLColor4U* data)
flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data);
}
+void LLVertexBuffer::setNormalData(const LLVector4a* data)
+{
+ llassert(sGLRenderBuffer == mGLBuffer);
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data);
+}
+
+void LLVertexBuffer::setTangentData(const LLVector4a* data)
+{
+ llassert(sGLRenderBuffer == mGLBuffer);
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data);
+}
+
+void LLVertexBuffer::setWeight4Data(const LLVector4a* data)
+{
+ llassert(sGLRenderBuffer == mGLBuffer);
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data);
+}
+
+void LLVertexBuffer::setIndexData(const U16* data)
+{
+ llassert(sGLRenderIndices == mGLIndices);
+ flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data);
+}
+
+void LLVertexBuffer::setIndexData(const U32* data)
+{
+ llassert(sGLRenderIndices == mGLIndices);
+ if (mIndicesType != GL_UNSIGNED_INT)
+ { // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices
+ mIndicesType = GL_UNSIGNED_INT;
+ mIndicesStride = 4;
+ mNumIndices /= 2;
+ }
+ flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data);
+}
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index f2d146d4bb..669d89aabf 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -180,6 +180,7 @@ public:
bool getTexCoord1Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
bool getTexCoord2Strider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
bool getNormalStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
+ bool getNormalStrider(LLStrider<LLVector4a>& strider, U32 index = 0, S32 count = -1);
bool getTangentStrider(LLStrider<LLVector3>& strider, U32 index=0, S32 count = -1);
bool getTangentStrider(LLStrider<LLVector4a>& strider, U32 index=0, S32 count = -1);
bool getColorStrider(LLStrider<LLColor4U>& strider, U32 index=0, S32 count = -1);
@@ -187,15 +188,15 @@ public:
bool getWeightStrider(LLStrider<F32>& strider, U32 index=0, S32 count = -1);
bool getWeight4Strider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
bool getClothWeightStrider(LLStrider<LLVector4>& strider, U32 index=0, S32 count = -1);
- bool getBasecolorTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getNormalTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getMetallicRoughnessTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
- bool getEmissiveTexcoordStrider(LLStrider<LLVector2>& strider, U32 index=0, S32 count = -1);
void setPositionData(const LLVector4a* data);
+ void setNormalData(const LLVector4a* data);
+ void setTangentData(const LLVector4a* data);
+ void setWeight4Data(const LLVector4a* data);
void setTexCoordData(const LLVector2* data);
void setColorData(const LLColor4U* data);
-
+ void setIndexData(const U16* data);
+ void setIndexData(const U32* data);
U32 getNumVerts() const { return mNumVerts; }
U32 getNumIndices() const { return mNumIndices; }
@@ -227,6 +228,8 @@ protected:
U32 mGLIndices = 0; // GL IBO handle
U32 mNumVerts = 0; // Number of vertices allocated
U32 mNumIndices = 0; // Number of indices allocated
+ U32 mIndicesType = GL_UNSIGNED_SHORT; // type of indices in index buffer
+ U32 mIndicesStride = 2; // size of each index in bytes
U32 mOffsets[TYPE_MAX]; // byte offsets into mMappedData of each attribute
U8* mMappedData = nullptr; // pointer to currently mapped data (NULL if unmapped)
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index a6ae041935..738d8e540d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -38,6 +38,7 @@ include(OpenGL)
include(OpenSSL)
include(PNG)
include(TemplateCheck)
+include(TinyEXR)
include(ThreeJS)
include(Tracy)
include(UI)
@@ -72,8 +73,12 @@ if (NOT HAVOK_TPV)
endif()
endif (NOT HAVOK_TPV)
-
set(viewer_SOURCE_FILES
+ gltfscenemanager.cpp
+ gltf/asset.cpp
+ gltf/accessor.cpp
+ gltf/primitive.cpp
+ gltf/animation.cpp
groupchatlistener.cpp
llaccountingcostmanager.cpp
llaisapi.cpp
@@ -313,6 +318,7 @@ set(viewer_SOURCE_FILES
llgiveinventory.cpp
llglsandbox.cpp
llgltfmateriallist.cpp
+ llgltfmaterialpreviewmgr.cpp
llgroupactions.cpp
llgroupiconctrl.cpp
llgrouplist.cpp
@@ -523,6 +529,7 @@ set(viewer_SOURCE_FILES
llrecentpeople.cpp
llreflectionmap.cpp
llreflectionmapmanager.cpp
+ llheroprobemanager.cpp
llregioninfomodel.cpp
llregionposition.cpp
llremoteparcelrequest.cpp
@@ -726,7 +733,13 @@ set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING
set(viewer_HEADER_FILES
CMakeLists.txt
ViewerInstall.cmake
+ gltfscenemanager.h
groupchatlistener.h
+ gltf/asset.h
+ gltf/accessor.h
+ gltf/buffer_util.h
+ gltf/primitive.h
+ gltf/animation.h
llaccountingcost.h
llaccountingcostmanager.h
llaisapi.h
@@ -969,6 +982,7 @@ set(viewer_HEADER_FILES
llgesturemgr.h
llgiveinventory.h
llgltfmateriallist.h
+ llgltfmaterialpreviewmgr.h
llgroupactions.h
llgroupiconctrl.h
llgrouplist.h
@@ -1165,6 +1179,7 @@ set(viewer_HEADER_FILES
llrecentpeople.h
llreflectionmap.h
llreflectionmapmanager.h
+ llheroprobemanager.h
llregioninfomodel.h
llregionposition.h
llremoteparcelrequest.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 2f7c256b49..e1eeefa755 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -8732,7 +8732,7 @@
<key>OctreeAlphaDistanceFactor</key>
<map>
<key>Comment</key>
- <string>Multiplier on alpha object distance for determining octree node size </string>
+ <string>Multiplier on alpha object distance for determining octree node size. First two parameters are currently unused. Third parameter is distance at which to perform detailed alpha sorting.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -8741,7 +8741,7 @@
<array>
<real>0.1</real>
<real>0.0</real>
- <real>0.0</real>
+ <real>64.0</real>
</array>
</map>
@@ -8861,17 +8861,6 @@
<key>Value</key>
<real>32.0</real>
</map>
- <key>RenderCloudShadowAmbianceFactor</key>
- <map>
- <key>Comment</key>
- <string>Amount that cloud shadow (aka cloud coverage) contributes to reflection probe ambiance</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.1</real>
- </map>
<key>RenderCPUBasis</key>
<map>
<key>Comment</key>
@@ -9186,6 +9175,17 @@
<real>0.00</real>
</array>
</map>
+ <key>RenderMirrors</key>
+ <map>
+ <key>Comment</key>
+ <string>Renders realtime mirrors.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>RenderScreenSpaceReflections</key>
<map>
<key>Comment</key>
@@ -9296,6 +9296,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>RenderDesaturateIrradiance</key>
+ <map>
+ <key>Comment</key>
+ <string>Desaturate irradiance to remove blue tint</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>RenderDebugAlphaMask</key>
<map>
<key>Comment</key>
@@ -9362,6 +9373,50 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>RenderHDRIExposure</key>
+ <map>
+ <key>Comment</key>
+ <string>Exposure adjustment of HDRI when previewing an HDRI. Units are EV. Sane values would be -10 to 10.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>RenderHDRIRotation</key>
+ <map>
+ <key>Comment</key>
+ <string>Rotation (in degrees) of environment when previewing an HDRI.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>RenderHDRISplitScreen</key>
+ <map>
+ <key>Comment</key>
+ <string>What percentage of screen to render using HDRI vs EEP sky.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>RenderHDRIIrradianceOnly</key>
+ <map>
+ <key>Comment</key>
+ <string>Only use HDRI sky for irradiance map when RenderHDRISplitScreen is 0</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>RenderMaxOpenGLVersion</key>
<map>
<key>Comment</key>
@@ -9395,6 +9450,17 @@
<key>Value</key>
<integer>16</integer>
</map>
+ <key>RenderMaxTextureResolution</key>
+ <map>
+ <key>Comment</key>
+ <string>Maximum texture resolution to download for non-boosted textures.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>2048</integer>
+ </map>
<key>RenderDebugTextureBind</key>
<map>
<key>Comment</key>
@@ -10390,6 +10456,50 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>RenderHeroProbeResolution</key>
+ <map>
+ <key>Comment</key>
+ <string>Resolution to render hero probes used for mirrors, water, etc.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1024</integer>
+ </map>
+ <key>RenderHeroProbeDistance</key>
+ <map>
+ <key>Comment</key>
+ <string>Distance in meters for hero probes to render out to.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>8</real>
+ </map>
+ <key>RenderHeroProbeUpdateRate</key>
+ <map>
+ <key>Comment</key>
+ <string>How many frames to wait for until it's time to render the probe. E.g., every other frame (1), every two frames (2), every three frames (3) etc.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>2</integer>
+ </map>
+ <key>RenderHeroProbeConservativeUpdateMultiplier</key>
+ <map>
+ <key>Comment</key>
+ <string>How many probe updates to wait until it's time to update faces that are not directly facing the camera. Acts as a multiplier. E.g., frames to the periphery of the camera updating once every 3 updates, vs ones directly facing the camera updating every update.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>16</integer>
+ </map>
<key>RenderReflectionProbeVolumes</key>
<map>
<key>Comment</key>
@@ -10602,38 +10712,49 @@
<integer>3</integer>
</map>
<key>RenderReflectionRes</key>
- <map>
- <key>Comment</key>
- <string>Reflection map resolution.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>S32</string>
- <key>Value</key>
- <integer>64</integer>
- </map>
- <key>RenderResolutionDivisor</key>
- <map>
- <key>Comment</key>
- <string>Divisor for rendering 3D scene at reduced resolution.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>U32</string>
- <key>Value</key>
- <integer>1</integer>
- </map>
- <key>RenderShaderLightingMaxLevel</key>
- <map>
- <key>Comment</key>
- <string>Max lighting level to use in the shader (class 3 is default, 2 is less lights, 1 is sun/moon only. Works around shader compiler bugs on certain platforms.)</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>S32</string>
- <key>Value</key>
- <integer>3</integer>
- </map>
+ <map>
+ <key>Comment</key>
+ <string>Reflection map resolution.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>64</integer>
+ </map>
+ <key>RenderReservedTextureIndices</key>
+ <map>
+ <key>Comment</key>
+ <string>Count of texture indices to reserve for shadow and reflection maps when using indexed texture rendering. Probably only want to set from the login screen.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>14</integer>
+ </map>
+ <key>RenderResolutionDivisor</key>
+ <map>
+ <key>Comment</key>
+ <string>Divisor for rendering 3D scene at reduced resolution.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>RenderShaderLightingMaxLevel</key>
+ <map>
+ <key>Comment</key>
+ <string>Max lighting level to use in the shader (class 3 is default, 2 is less lights, 1 is sun/moon only. Works around shader compiler bugs on certain platforms.)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>3</integer>
+ </map>
<key>RenderSkyAutoAdjustLegacy</key>
<map>
<key>Comment</key>
@@ -10756,6 +10877,17 @@
<key>Value</key>
<real>0.5</real>
</map>
+ <key>RenderDiffuseLuminanceScale</key>
+ <map>
+ <key>Comment</key>
+ <string>Luminance adjustment for diffuse surfaces to aid auto-exposure behavior</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.5</real>
+ </map>
<key>RenderShaderLODThreshold</key>
<map>
<key>Comment</key>
@@ -10814,7 +10946,7 @@
<key>RenderTerrainScale</key>
<map>
<key>Comment</key>
- <string>Terrain detail texture scale</string>
+ <string>Terrain detail texture scale (meters)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -10822,6 +10954,83 @@
<key>Value</key>
<real>12.0</real>
</map>
+ <key>RenderTerrainPBREnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>EXPERIMENTAL: Enable PBR Terrain features.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <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>
+ <string>Detail level for PBR terrain. 0 is full detail. Negative values drop rendering features, in accordance with the GLTF specification when possible, which reduces the number of texture binds.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>RenderTerrainPBRScale</key>
+ <map>
+ <key>Comment</key>
+ <string>PBR terrain detail texture scale (meters)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>8.0</real>
+ </map>
+ <key>RenderTerrainPBRPlanarSampleCount</key>
+ <map>
+ <key>Comment</key>
+ <string>How many UV planes to sample PBR terrain textures from. 1 is "flat", 3 is triplanar mapping (aka box mapping)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <real>3</real>
+ </map>
+ <key>RenderTerrainPBRTriplanarBlendFactor</key>
+ <map>
+ <key>Comment</key>
+ <string>Higher values create sharper transitions, but are more likely to produce artifacts.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>8.0</real>
+ </map>
+ <key>RenderTerrainPBRNormalsEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>EXPERIMENTAL: Change normal gen for PBR Terrain.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>RenderTrackerBeacon</key>
<map>
<key>Comment</key>
@@ -13752,6 +13961,17 @@
<key>Value</key>
<integer>2</integer>
</map>
+ <key>UIPreviewMaterial</key>
+ <map>
+ <key>Comment</key>
+ <string>Whether or not PBR material swatch is enabled</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <real>0</real>
+ </map>
<key>UIResizeBarHeight</key>
<map>
<key>Comment</key>
@@ -15667,7 +15887,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
- <integer>2048</integer>
+ <integer>1024</integer>
</map>
<key>max_texture_dimension_Y</key>
<map>
@@ -15678,7 +15898,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
- <integer>2048</integer>
+ <integer>1024</integer>
</map>
<!-- End of back compatibility settings -->
<key>teleport_offer_invitation_max_length</key>
@@ -16554,6 +16774,50 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>LocalTerrainAsset1</key>
+ <map>
+ <key>Comment</key>
+ <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>00000000-0000-0000-0000-000000000000</string>
+ </map>
+ <key>LocalTerrainAsset2</key>
+ <map>
+ <key>Comment</key>
+ <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>00000000-0000-0000-0000-000000000000</string>
+ </map>
+ <key>LocalTerrainAsset3</key>
+ <map>
+ <key>Comment</key>
+ <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>00000000-0000-0000-0000-000000000000</string>
+ </map>
+ <key>LocalTerrainAsset4</key>
+ <map>
+ <key>Comment</key>
+ <string>If set to a non-null UUID, overrides the terrain asset locally for all regions with material assets. Local terrain assets are not visible to others. Please keep in mind that this debug setting may be temporary. Do not rely on this setting existing in future viewer builds.</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>00000000-0000-0000-0000-000000000000</string>
+ </map>
<key>PathfindingRetrieveNeighboringRegion</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl
index 83b0ba096c..43863dd37a 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl
@@ -26,6 +26,7 @@
uniform mat3 normal_matrix;
uniform mat4 texture_matrix0;
uniform mat4 modelview_projection_matrix;
+uniform mat4 modelview_matrix;
in vec3 position;
in vec3 normal;
@@ -35,10 +36,12 @@ in vec2 texcoord0;
out vec3 vary_normal;
out vec4 vertex_color;
out vec2 vary_texcoord0;
+out vec3 vary_position;
void main()
{
//transform vertex
+ vary_position = (modelview_matrix * vec4(position.xyz, 1.0)).xyz;
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
index 6ebe4ce251..db7597dd74 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl
@@ -25,7 +25,7 @@
/*[EXTRA_CODE_HERE]*/
-out vec4 frag_data[3];
+out vec4 frag_data[4];
uniform sampler2D diffuseMap;
@@ -33,11 +33,14 @@ uniform float minimum_alpha;
in vec3 vary_normal;
in vec2 vary_texcoord0;
+in vec3 vary_position;
-vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
+
vec4 diff = texture(diffuseMap, vary_texcoord0.xy);
if (diff.a < minimum_alpha)
@@ -48,6 +51,7 @@ void main()
frag_data[0] = vec4(diff.rgb, 0.0);
frag_data[1] = vec4(0,0,0,0);
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl
index bc0c11ec46..5cc2846156 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/avatarV.glsl
@@ -35,6 +35,7 @@ in vec4 weight;
out vec3 vary_normal;
out vec2 vary_texcoord0;
+out vec3 vary_position;
void main()
{
@@ -57,6 +58,7 @@ void main()
vary_normal = norm;
+ vary_position = pos.xyz;
gl_Position = projection_matrix * pos;
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
index 08baf98686..8627ab1852 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
@@ -40,12 +40,12 @@ uniform float kern_scale;
in vec2 vary_fragcoord;
vec4 getPosition(vec2 pos_screen);
-vec3 getNorm(vec2 pos_screen);
+vec4 getNorm(vec2 pos_screen);
void main()
{
vec2 tc = vary_fragcoord.xy;
- vec3 norm = getNorm(tc);
+ vec4 norm = getNorm(tc);
vec3 pos = getPosition(tc).xyz;
vec4 ccol = texture(lightMap, tc).rgba;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
index 35f483f633..8483f257fa 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
@@ -37,11 +37,13 @@ in vec3 vary_mat2;
in vec4 vertex_color;
in vec2 vary_texcoord0;
+in vec3 vary_position;
-vec2 encode_normal(vec3 n);
-
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
+
vec4 col = texture(diffuseMap, vary_texcoord0.xy);
if(col.a < minimum_alpha)
@@ -60,6 +62,6 @@ void main()
frag_data[1] = vertex_color.aaaa; // spec
//frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested
vec3 nvn = normalize(tnorm);
- frag_data[2] = vec4(encode_normal(nvn), vertex_color.a, GBUFFER_FLAG_HAS_ATMOS);
- frag_data[3] = vec4(0);
+ frag_data[2] = vec4(nvn, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[3] = vec4(vertex_color.a, 0, 0, 0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
index 3af2eab1e4..74319349f6 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
@@ -23,6 +23,7 @@
* $/LicenseInfo$
*/
+uniform mat4 modelview_matrix;
uniform mat3 normal_matrix;
uniform mat4 texture_matrix0;
uniform mat4 modelview_projection_matrix;
@@ -38,11 +39,11 @@ out vec3 vary_mat1;
out vec3 vary_mat2;
out vec4 vertex_color;
out vec2 vary_texcoord0;
+out vec3 vary_position;
#ifdef HAS_SKIN
mat4 getObjectSkinnedTransform();
uniform mat4 projection_matrix;
-uniform mat4 modelview_matrix;
#endif
void main()
@@ -52,11 +53,13 @@ void main()
mat4 mat = getObjectSkinnedTransform();
mat = modelview_matrix * mat;
vec3 pos = (mat*vec4(position.xyz, 1.0)).xyz;
+ vary_position = pos;
gl_Position = projection_matrix*vec4(pos, 1.0);
vec3 n = normalize((mat * vec4(normal.xyz+position.xyz, 1.0)).xyz-pos.xyz);
vec3 t = normalize((mat * vec4(tangent.xyz+position.xyz, 1.0)).xyz-pos.xyz);
#else
+ vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
vec3 n = normalize(normal_matrix * normal);
vec3 t = normalize(normal_matrix * tangent.xyz);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
index f9ebf33b4a..f6696e270c 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl
@@ -50,6 +50,7 @@ SOFTWARE.
uniform sampler2D normalMap;
uniform sampler2D depthMap;
+uniform sampler2D emissiveRect;
uniform sampler2D projectionMap; // rgba
uniform sampler2D brdfLut;
@@ -140,40 +141,20 @@ vec2 getScreenCoordinate(vec2 screenpos)
return sc - vec2(1.0, 1.0);
}
-// See: https://aras-p.info/texts/CompactNormalStorage.html
-// Method #4: Spheremap Transform, Lambert Azimuthal Equal-Area projection
-vec3 getNorm(vec2 screenpos)
+vec4 getNorm(vec2 screenpos)
{
- vec2 enc = texture(normalMap, screenpos.xy).xy;
- vec2 fenc = enc*4-2;
- float f = dot(fenc,fenc);
- float g = sqrt(1-f/4);
- vec3 n;
- n.xy = fenc*g;
- n.z = 1-f/2;
- return n;
-}
-
-vec3 getNormalFromPacked(vec4 packedNormalEnvIntensityFlags)
-{
- vec2 enc = packedNormalEnvIntensityFlags.xy;
- vec2 fenc = enc*4-2;
- float f = dot(fenc,fenc);
- float g = sqrt(1-f/4);
- vec3 n;
- n.xy = fenc*g;
- n.z = 1-f/2;
- return normalize(n); // TODO: Is this normalize redundant?
+ return texture(normalMap, screenpos.xy);
}
// return packedNormalEnvIntensityFlags since GBUFFER_FLAG_HAS_PBR needs .w
// See: C++: addDeferredAttachments(), GLSL: softenLightF
vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity)
{
- vec4 packedNormalEnvIntensityFlags = texture(normalMap, screenpos.xy);
- n = getNormalFromPacked( packedNormalEnvIntensityFlags );
- envIntensity = packedNormalEnvIntensityFlags.z;
- return packedNormalEnvIntensityFlags;
+ vec4 norm = texture(normalMap, screenpos.xy);
+ n = norm.xyz;
+ envIntensity = texture(emissiveRect, screenpos.xy).r;
+
+ return norm;
}
// get linear depth value given a depth buffer sample d and znear and zfar values
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl
index 6f3b94f734..82d5d363d2 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl
@@ -31,14 +31,18 @@ uniform float minimum_alpha;
uniform sampler2D diffuseMap;
+in vec3 vary_position;
+
in vec3 vary_normal;
in vec4 vertex_color;
in vec2 vary_texcoord0;
-vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
+
vec4 col = texture(diffuseMap, vary_texcoord0.xy) * vertex_color;
if (col.a < minimum_alpha)
@@ -49,7 +53,7 @@ void main()
frag_data[0] = vec4(col.rgb, 0.0);
frag_data[1] = vec4(0,0,0,0); // spec
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl
index e5f2af2c53..788ea633fc 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskIndexedF.glsl
@@ -28,16 +28,19 @@
out vec4 frag_data[4];
in vec3 vary_normal;
+in vec3 vary_position;
uniform float minimum_alpha;
in vec4 vertex_color;
in vec2 vary_texcoord0;
-vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
+
vec4 col = diffuseLookup(vary_texcoord0.xy) * vertex_color;
if (col.a < minimum_alpha)
@@ -48,6 +51,6 @@ void main()
frag_data[0] = vec4(col.rgb, 0.0);
frag_data[1] = vec4(0,0,0,0);
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl
index 234f096ed5..22e6d60419 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskNoColorF.glsl
@@ -34,8 +34,6 @@ uniform sampler2D diffuseMap;
in vec3 vary_normal;
in vec2 vary_texcoord0;
-vec2 encode_normal(vec3 n);
-
void main()
{
vec4 col = texture(diffuseMap, vary_texcoord0.xy);
@@ -48,7 +46,7 @@ void main()
frag_data[0] = vec4(col.rgb, 0.0);
frag_data[1] = vec4(0,0,0,0); // spec
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl
index 68fb8bf499..799fc62a42 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl
@@ -32,17 +32,19 @@ uniform sampler2D diffuseMap;
in vec3 vary_normal;
in vec4 vertex_color;
in vec2 vary_texcoord0;
+in vec3 vary_position;
-vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
vec3 col = vertex_color.rgb * texture(diffuseMap, vary_texcoord0.xy).rgb;
frag_data[0] = vec4(col, 0.0);
frag_data[1] = vertex_color.aaaa; // spec
//frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), vertex_color.a, GBUFFER_FLAG_HAS_ATMOS);
- frag_data[3] = vec4(0);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[3] = vec4(vertex_color.a, 0, 0, 0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl
index 93d561504e..3362a180c6 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseIndexedF.glsl
@@ -30,12 +30,14 @@ out vec4 frag_data[4];
in vec3 vary_normal;
in vec4 vertex_color;
in vec2 vary_texcoord0;
+in vec3 vary_position;
-vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 pos);
vec3 linear_to_srgb(vec3 c);
void main()
{
+ mirrorClip(vary_position);
vec3 col = vertex_color.rgb * diffuseLookup(vary_texcoord0.xy).rgb;
vec3 spec;
@@ -44,6 +46,6 @@ void main()
frag_data[0] = vec4(col, 0.0);
frag_data[1] = vec4(spec, vertex_color.a); // spec
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), vertex_color.a, GBUFFER_FLAG_HAS_ATMOS);
- frag_data[3] = vec4(0);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[3] = vec4(vertex_color.a, 0, 0, 0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
index 2402cc3b70..64230dc680 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl
@@ -36,13 +36,16 @@ out vec3 vary_normal;
out vec4 vertex_color;
out vec2 vary_texcoord0;
+out vec3 vary_position;
void passTextureIndex();
+uniform mat4 modelview_matrix;
+
#ifdef HAS_SKIN
mat4 getObjectSkinnedTransform();
uniform mat4 projection_matrix;
-uniform mat4 modelview_matrix;
+
#endif
void main()
@@ -51,9 +54,11 @@ void main()
mat4 mat = getObjectSkinnedTransform();
mat = modelview_matrix * mat;
vec4 pos = mat * vec4(position.xyz, 1.0);
+ vary_position = pos.xyz;
gl_Position = projection_matrix * pos;
vary_normal = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
#else
+ vary_position = (modelview_matrix * vec4(position.xyz, 1.0)).xyz;
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
vary_normal = normalize(normal_matrix * normal);
#endif
diff --git a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
index 9ac4ceb37e..eff7221ae7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
@@ -28,7 +28,9 @@
out vec4 frag_color;
uniform sampler2D emissiveRect;
+#ifdef USE_LAST_EXPOSURE
uniform sampler2D exposureMap;
+#endif
uniform float dt;
uniform vec2 noiseVec;
@@ -51,10 +53,12 @@ void main()
L /= max_L;
L = pow(L, 2.0);
float s = mix(dynamic_exposure_params.z, dynamic_exposure_params.y, L);
-
+
+#ifdef USE_LAST_EXPOSURE
float prev = texture(exposureMap, vec2(0.5,0.5)).r;
s = mix(prev, s, min(dt*2.0*abs(prev-s), 0.04));
+#endif
frag_color = max(vec4(s, s, s, dt), vec4(0.0));
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
index a6fab10791..52dfed06ae 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
@@ -50,9 +50,11 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
vec4 applySkyAndWaterFog(vec3 pos, vec3 additive, vec3 atten, vec4 color);
#endif
+void mirrorClip(vec3 pos);
+
void main()
{
-
+ mirrorClip(vary_position);
#ifdef IS_ALPHA
waterClip(vary_position.xyz);
#endif
diff --git a/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl b/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl
new file mode 100644
index 0000000000..7e3e7d9271
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/globalF.glsl
@@ -0,0 +1,45 @@
+/**
+ * @file class1/deferred/globalF.glsl
+ *
+ * $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$
+ */
+
+
+ // Global helper functions included in every fragment shader
+ // DO NOT declare sampler uniforms here as OS X doesn't compile
+ // them out
+
+uniform float mirror_flag;
+uniform vec4 clipPlane;
+uniform float clipSign;
+
+void mirrorClip(vec3 pos)
+{
+ if (mirror_flag > 0)
+ {
+ if ((dot(pos.xyz, clipPlane.xyz) + clipPlane.w) < 0.0)
+ {
+ discard;
+ }
+ }
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl b/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl
index aae595f619..c64f65e32c 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl
@@ -37,7 +37,6 @@ uniform sampler2D specularMap;
in vec2 vary_texcoord0;
vec3 linear_to_srgb(vec3 c);
-vec2 encode_normal (vec3 n);
void main()
{
@@ -53,6 +52,6 @@ void main()
frag_data[0] = vec4(col.rgb, 0.0);
frag_data[1] = spec;
- frag_data[2] = vec4(encode_normal(norm.xyz),0,GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(norm.xyz, GBUFFER_FLAG_HAS_ATMOS);
frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
index c8afacf9bb..95b2f80e06 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
@@ -34,6 +34,8 @@ in vec2 vary_fragcoord;
uniform sampler2D diffuseRect;
uniform sampler2D emissiveRect;
+uniform sampler2D normalMap;
+uniform float diffuse_luminance_scale;
float lum(vec3 col)
{
@@ -45,7 +47,21 @@ void main()
{
vec2 tc = vary_fragcoord*0.6+0.2;
tc.y -= 0.1; // HACK - nudge exposure sample down a little bit to favor ground over sky
- vec3 c = texture(diffuseRect, tc).rgb + texture(emissiveRect, tc).rgb;
+ vec3 c = texture(diffuseRect, tc).rgb;
+
+ vec4 norm = texture(normalMap, tc);
+
+ if (!GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_HDRI) &&
+ !GET_GBUFFER_FLAG(GBUFFER_FLAG_SKIP_ATMOS))
+ {
+ // Apply the diffuse luminance scale to objects but not the sky
+ // Prevents underexposing when looking at bright environments
+ // while still allowing for realistically bright skies.
+ c *= diffuse_luminance_scale;
+ }
+
+ c += texture(emissiveRect, tc).rgb;
+
float L = lum(c);
frag_color = vec4(max(L, 0.0));
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl
index 7cdddfe8db..ddf878ae60 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl
@@ -28,25 +28,18 @@
#define DIFFUSE_ALPHA_MODE_MASK 2
#define DIFFUSE_ALPHA_MODE_EMISSIVE 3
-#ifdef HAS_SKIN
uniform mat4 modelview_matrix;
uniform mat4 projection_matrix;
+uniform mat4 modelview_projection_matrix;
+
+#ifdef HAS_SKIN
mat4 getObjectSkinnedTransform();
#else
uniform mat3 normal_matrix;
-uniform mat4 modelview_projection_matrix;
-#endif
-
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
-
-#if !defined(HAS_SKIN)
-uniform mat4 modelview_matrix;
#endif
out vec3 vary_position;
-#endif
-
uniform mat4 texture_matrix0;
in vec3 position;
@@ -85,9 +78,7 @@ void main()
vec3 pos = (mat*vec4(position.xyz,1.0)).xyz;
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
vary_position = pos;
-#endif
gl_Position = projection_matrix*vec4(pos,1.0);
@@ -133,10 +124,8 @@ void main()
vertex_color = diffuse_color;
-#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
#if !defined(HAS_SKIN)
vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
#endif
-#endif
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl b/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl
index 183354b9bd..6ef556d7e8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl
@@ -55,7 +55,7 @@ void main()
frag_data[0] = vec4(0);
frag_data[1] = vec4(0.0);
- frag_data[2] = vec4(0.0, 0.0, 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(0.0, 0.0, 0.0, GBUFFER_FLAG_SKIP_ATMOS);
frag_data[3] = vec4(c.rgb, c.a);
// Added and commented out for a ground truth. Do not uncomment - Geenz
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
index faa273b834..1d5f810cf3 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
@@ -54,14 +54,20 @@ in vec2 emissive_texcoord;
uniform float minimum_alpha; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlphaCutoff()
-vec2 encode_normal(vec3 n);
vec3 linear_to_srgb(vec3 c);
vec3 srgb_to_linear(vec3 c);
+uniform vec4 clipPlane;
+uniform float clipSign;
+
+void mirrorClip(vec3 pos);
+
uniform mat3 normal_matrix;
void main()
{
+ mirrorClip(vary_position);
+
vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba;
if (basecolor.a < minimum_alpha)
{
@@ -103,7 +109,7 @@ void main()
// See: C++: addDeferredAttachments(), GLSL: softenLightF
frag_data[0] = max(vec4(col, 0.0), vec4(0)); // Diffuse
frag_data[1] = max(vec4(spec.rgb,vertex_color.a), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
- frag_data[2] = max(vec4(encode_normal(tnorm), vertex_color.a, GBUFFER_FLAG_HAS_PBR), vec4(0)); // normal, environment intensity, flags
+ frag_data[2] = vec4(tnorm, GBUFFER_FLAG_HAS_PBR); // normal, environment intensity, flags
frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
index 160ae7a215..5e0141910b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
@@ -28,8 +28,9 @@
//deferred opaque implementation
-#ifdef HAS_SKIN
uniform mat4 modelview_matrix;
+
+#ifdef HAS_SKIN
uniform mat4 projection_matrix;
mat4 getObjectSkinnedTransform();
#else
@@ -59,6 +60,7 @@ out vec4 vertex_color;
out vec3 vary_tangent;
flat out float vary_sign;
out vec3 vary_normal;
+out vec3 vary_position;
vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
@@ -71,10 +73,11 @@ void main()
mat = modelview_matrix * mat;
vec3 pos = (mat*vec4(position.xyz,1.0)).xyz;
-
+ vary_position = pos;
gl_Position = projection_matrix*vec4(pos,1.0);
#else
+ vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
//transform vertex
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
#endif
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
new file mode 100644
index 0000000000..de4745c1c4
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
@@ -0,0 +1,347 @@
+/**
+ * @file class1\deferred\terrainF.glsl
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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]*/
+
+#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
+
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+#define TerrainCoord vec4[2]
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+#define TerrainCoord vec2
+#endif
+
+#define MIX_X 1 << 3
+#define MIX_Y 1 << 4
+#define MIX_Z 1 << 5
+#define MIX_W 1 << 6
+
+struct TerrainMix
+{
+ vec4 weight;
+ int type;
+};
+
+TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal);
+
+struct PBRMix
+{
+ vec4 col; // RGB color with alpha, linear space
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ vec3 orm; // Occlusion, roughness, metallic
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ vec2 rm; // Roughness, metallic
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ vec3 vNt; // Unpacked normal texture sample, vector
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ vec3 emissive; // RGB emissive color, linear space
+#endif
+};
+
+PBRMix init_pbr_mix();
+
+PBRMix terrain_sample_and_multiply_pbr(
+ TerrainCoord terrain_coord
+ , sampler2D tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , sampler2D tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , sampler2D tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , sampler2D tex_emissive
+#endif
+ , vec4 factor_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , vec3 factor_orm
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , vec2 factor_rm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , vec3 factor_emissive
+#endif
+ );
+
+PBRMix mix_pbr(PBRMix mix1, PBRMix mix2, float mix2_weight);
+
+out vec4 frag_data[4];
+
+uniform sampler2D alpha_ramp;
+
+// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures
+uniform sampler2D detail_0_base_color;
+uniform sampler2D detail_1_base_color;
+uniform sampler2D detail_2_base_color;
+uniform sampler2D detail_3_base_color;
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+uniform sampler2D detail_0_normal;
+uniform sampler2D detail_1_normal;
+uniform sampler2D detail_2_normal;
+uniform sampler2D detail_3_normal;
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+uniform sampler2D detail_0_metallic_roughness;
+uniform sampler2D detail_1_metallic_roughness;
+uniform sampler2D detail_2_metallic_roughness;
+uniform sampler2D detail_3_metallic_roughness;
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+uniform sampler2D detail_0_emissive;
+uniform sampler2D detail_1_emissive;
+uniform sampler2D detail_2_emissive;
+uniform sampler2D detail_3_emissive;
+#endif
+
+uniform vec4[4] baseColorFactors; // See also vertex_color in pbropaqueV.glsl
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+uniform vec4 metallicFactors;
+uniform vec4 roughnessFactors;
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+uniform vec3[4] emissiveColors;
+#endif
+uniform vec4 minimum_alphas; // PBR alphaMode: MASK, See: mAlphaCutoff, setAlphaCutoff()
+
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+in vec4[2] vary_coords;
+#endif
+in vec3 vary_position;
+in vec3 vary_normal;
+in vec3 vary_tangent;
+flat in float vary_sign;
+in vec4 vary_texcoord0;
+in vec4 vary_texcoord1;
+
+void mirrorClip(vec3 position);
+
+float terrain_mix(TerrainMix tm, vec4 tms4);
+
+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;
+
+ TerrainMix tm = get_terrain_mix_weights(alpha1, alpha2, alphaFinal);
+
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ // RGB = Occlusion, Roughness, Metal
+ // default values, see LLViewerTexture::sDefaultPBRORMImagep
+ // occlusion 1.0
+ // roughness 0.0
+ // metal 0.0
+ vec3[4] orm_factors;
+ orm_factors[0] = vec3(1.0, roughnessFactors.x, metallicFactors.x);
+ orm_factors[1] = vec3(1.0, roughnessFactors.y, metallicFactors.y);
+ orm_factors[2] = vec3(1.0, roughnessFactors.z, metallicFactors.z);
+ orm_factors[3] = vec3(1.0, roughnessFactors.w, metallicFactors.w);
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ vec2[4] rm_factors;
+ rm_factors[0] = vec2(roughnessFactors.x, metallicFactors.x);
+ rm_factors[1] = vec2(roughnessFactors.y, metallicFactors.y);
+ rm_factors[2] = vec2(roughnessFactors.z, metallicFactors.z);
+ rm_factors[3] = vec2(roughnessFactors.w, metallicFactors.w);
+#endif
+
+ PBRMix mix = init_pbr_mix();
+ PBRMix mix2;
+ switch (tm.type & MIX_X)
+ {
+ case MIX_X:
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_0_base_color
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , detail_0_metallic_roughness
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , detail_0_normal
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , detail_0_emissive
+#endif
+ , baseColorFactors[0]
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , orm_factors[0]
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , rm_factors[0]
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , emissiveColors[0]
+#endif
+ );
+ mix = mix_pbr(mix, mix2, tm.weight.x);
+ break;
+ default:
+ break;
+ }
+ switch (tm.type & MIX_Y)
+ {
+ case MIX_Y:
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_1_base_color
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , detail_1_metallic_roughness
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , detail_1_normal
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , detail_1_emissive
+#endif
+ , baseColorFactors[1]
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , orm_factors[1]
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , rm_factors[1]
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , emissiveColors[1]
+#endif
+ );
+ mix = mix_pbr(mix, mix2, tm.weight.y);
+ break;
+ default:
+ break;
+ }
+ switch (tm.type & MIX_Z)
+ {
+ case MIX_Z:
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_2_base_color
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , detail_2_metallic_roughness
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , detail_2_normal
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , detail_2_emissive
+#endif
+ , baseColorFactors[2]
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , orm_factors[2]
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , rm_factors[2]
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , emissiveColors[2]
+#endif
+ );
+ mix = mix_pbr(mix, mix2, tm.weight.z);
+ break;
+ default:
+ break;
+ }
+ switch (tm.type & MIX_W)
+ {
+ case MIX_W:
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_3_base_color
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , detail_3_metallic_roughness
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , detail_3_normal
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , detail_3_emissive
+#endif
+ , baseColorFactors[3]
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , orm_factors[3]
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , rm_factors[3]
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , emissiveColors[3]
+#endif
+ );
+ mix = mix_pbr(mix, mix2, tm.weight.w);
+ break;
+ default:
+ break;
+ }
+
+ float minimum_alpha = terrain_mix(tm, minimum_alphas);
+ if (mix.col.a < minimum_alpha)
+ {
+ discard;
+ }
+ 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 = 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;
+#else
+ vec3 tnorm = vary_normal;
+ tnorm *= gl_FrontFacing ? 1.0 : -1.0;
+#endif
+
+
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+#define emissive mix.emissive
+#else
+#define emissive vec3(0)
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+#define orm mix.orm
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+#define orm vec3(1.0, mix.rm)
+#else
+// Matte plastic potato terrain
+#define orm vec3(1.0, 1.0, 0.0)
+#endif
+ frag_data[0] = max(vec4(mix.col.xyz, 0.0), vec4(0)); // Diffuse
+ frag_data[1] = max(vec4(orm.rgb, base_color_factor_alpha), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
+ frag_data[2] = vec4(tnorm, GBUFFER_FLAG_HAS_PBR); // normal, flags
+ frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl
new file mode 100644
index 0000000000..935c3f9301
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl
@@ -0,0 +1,473 @@
+/**
+ * @file class1\deferred\pbrterrainUtilF.glsl
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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]*/
+
+/**
+ * Triplanar mapping implementation adapted from Inigo Quilez' example shader,
+ * MIT license.
+ * https://www.shadertoy.com/view/MtsGWH
+ * Copyright © 2015 Inigo Quilez
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions: The above copyright
+ * notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS",
+ * WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#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
+
+in vec3 vary_vertex_normal;
+
+vec3 srgb_to_linear(vec3 c);
+
+// A relatively agressive threshold for terrain material mixing sampling
+// cutoff. This ensures that only one or two materials are used in most places,
+// making PBR terrain blending more performant. Should be greater than 0 to work.
+#define TERRAIN_RAMP_MIX_THRESHOLD 0.1
+// A small threshold for triplanar mapping sampling cutoff. This and
+// TERRAIN_TRIPLANAR_BLEND_FACTOR together ensures that only one or two samples
+// per texture are used in most places, making triplanar mapping more
+// performant. Should be greater than 0 to work.
+// There's also an artistic design choice in the use of these factors, and the
+// use of triplanar generally. Don't take these triplanar constants for granted.
+#define TERRAIN_TRIPLANAR_MIX_THRESHOLD 0.01
+
+#define SAMPLE_X 1 << 0
+#define SAMPLE_Y 1 << 1
+#define SAMPLE_Z 1 << 2
+#define MIX_X 1 << 3
+#define MIX_Y 1 << 4
+#define MIX_Z 1 << 5
+#define MIX_W 1 << 6
+
+struct PBRMix
+{
+ vec4 col; // RGB color with alpha, linear space
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ vec3 orm; // Occlusion, roughness, metallic
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ vec2 rm; // Roughness, metallic
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ vec3 vNt; // Unpacked normal texture sample, vector
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ vec3 emissive; // RGB emissive color, linear space
+#endif
+};
+
+PBRMix init_pbr_mix()
+{
+ PBRMix mix;
+ mix.col = vec4(0);
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ mix.orm = vec3(0);
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ mix.rm = vec2(0);
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix.vNt = vec3(0);
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ mix.emissive = vec3(0);
+#endif
+ return mix;
+}
+
+// Usage example, for two weights:
+// vec2 weights = ... // Weights must add up to 1
+// PBRMix mix = init_pbr_mix();
+// PBRMix mix1 = ...
+// mix = mix_pbr(mix, mix1, weights.x);
+// PBRMix mix2 = ...
+// mix = mix_pbr(mix, mix2, weights.y);
+PBRMix mix_pbr(PBRMix mix1, PBRMix mix2, float mix2_weight)
+{
+ PBRMix mix;
+ mix.col = mix1.col + (mix2.col * mix2_weight);
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ mix.orm = mix1.orm + (mix2.orm * mix2_weight);
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ mix.rm = mix1.rm + (mix2.rm * mix2_weight);
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix.vNt = mix1.vNt + (mix2.vNt * mix2_weight);
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ mix.emissive = mix1.emissive + (mix2.emissive * mix2_weight);
+#endif
+ return mix;
+}
+
+PBRMix sample_pbr(
+ vec2 uv
+ , sampler2D tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , sampler2D tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , sampler2D tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , sampler2D tex_emissive
+#endif
+ )
+{
+ PBRMix mix;
+ mix.col = texture(tex_col, uv);
+ mix.col.rgb = srgb_to_linear(mix.col.rgb);
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ mix.orm = texture(tex_orm, uv).xyz;
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ mix.rm = texture(tex_orm, uv).yz;
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix.vNt = texture(tex_vNt, uv).xyz*2.0-1.0;
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ mix.emissive = srgb_to_linear(texture(tex_emissive, uv).xyz);
+#endif
+ return mix;
+}
+
+struct TerrainTriplanar
+{
+ vec3 weight;
+ int type;
+};
+
+struct TerrainMix
+{
+ vec4 weight;
+ int type;
+};
+
+#define TerrainMixSample vec4[4]
+#define TerrainMixSample3 vec3[4]
+
+TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal)
+{
+ TerrainMix tm;
+ vec4 sample_x = vec4(1,0,0,0);
+ vec4 sample_y = vec4(0,1,0,0);
+ vec4 sample_z = vec4(0,0,1,0);
+ vec4 sample_w = vec4(0,0,0,1);
+
+ tm.weight = mix( mix(sample_w, sample_z, alpha2), mix(sample_y, sample_x, alpha1), alphaFinal );
+ tm.weight -= TERRAIN_RAMP_MIX_THRESHOLD;
+ ivec4 usage = max(ivec4(0), ivec4(ceil(tm.weight)));
+ // Prevent negative weights and keep weights balanced
+ tm.weight = tm.weight*vec4(usage);
+ tm.weight /= (tm.weight.x + tm.weight.y + tm.weight.z + tm.weight.w);
+
+ tm.type = (usage.x * MIX_X) |
+ (usage.y * MIX_Y) |
+ (usage.z * MIX_Z) |
+ (usage.w * MIX_W);
+ return tm;
+}
+
+TerrainTriplanar _t_triplanar()
+{
+ float sharpness = TERRAIN_TRIPLANAR_BLEND_FACTOR;
+ float threshold = TERRAIN_TRIPLANAR_MIX_THRESHOLD;
+ vec3 weight_signed = pow(abs(vary_vertex_normal), vec3(sharpness));
+ weight_signed /= (weight_signed.x + weight_signed.y + weight_signed.z);
+ weight_signed -= vec3(threshold);
+ TerrainTriplanar tw;
+ // *NOTE: Make sure the threshold doesn't affect the materials
+ tw.weight = max(vec3(0), weight_signed);
+ tw.weight /= (tw.weight.x + tw.weight.y + tw.weight.z);
+ ivec3 usage = ivec3(round(max(vec3(0), sign(weight_signed))));
+ tw.type = ((usage.x) * SAMPLE_X) |
+ ((usage.y) * SAMPLE_Y) |
+ ((usage.z) * SAMPLE_Z);
+ return tw;
+}
+
+// Assume weights add to 1
+float terrain_mix(TerrainMix tm, vec4 tms4)
+{
+ return (tm.weight.x * tms4[0]) +
+ (tm.weight.y * tms4[1]) +
+ (tm.weight.z * tms4[2]) +
+ (tm.weight.w * tms4[3]);
+}
+
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+// Triplanar mapping
+
+// Pre-transformed texture coordinates for each axial uv slice (Packing: xy, yz, (-x)z, unused)
+#define TerrainCoord vec4[2]
+
+vec2 _t_uv(vec2 uv_unflipped, 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;
+}
+
+vec3 _t_normal_post_1(vec3 vNt0, float sign_or_zero)
+{
+ // Assume normal is unpacked
+ vec3 vNt1 = vNt0;
+ // Get sign
+ float sign = sign_or_zero;
+ // Handle case where sign is 0
+ sign = (2.0*sign) + 1.0;
+ sign /= abs(sign);
+ // If the sign is negative, rotate normal by 180 degrees
+ vNt1.xy = (min(0, sign) * vNt1.xy) + (min(0, -sign) * -vNt1.xy);
+ return vNt1;
+}
+
+// Triplanar-specific normal texture fixes
+vec3 _t_normal_post_x(vec3 vNt0)
+{
+ vec3 vNt_x = _t_normal_post_1(vNt0, sign(vary_vertex_normal.x));
+ // *HACK: Transform normals according to orientation of the UVs
+ vNt_x.xy = vec2(-vNt_x.y, vNt_x.x);
+ return vNt_x;
+}
+vec3 _t_normal_post_y(vec3 vNt0)
+{
+ vec3 vNt_y = _t_normal_post_1(vNt0, sign(vary_vertex_normal.y));
+ // *HACK: Transform normals according to orientation of the UVs
+ vNt_y.xy = -vNt_y.xy;
+ return vNt_y;
+}
+vec3 _t_normal_post_z(vec3 vNt0)
+{
+ vec3 vNt_z = _t_normal_post_1(vNt0, sign(vary_vertex_normal.z));
+ return vNt_z;
+}
+
+PBRMix terrain_sample_pbr(
+ TerrainCoord terrain_coord
+ , TerrainTriplanar tw
+ , sampler2D tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , sampler2D tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , sampler2D tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , sampler2D tex_emissive
+#endif
+ )
+{
+ 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))
+ switch (tw.type & SAMPLE_X)
+ {
+ case SAMPLE_X:
+ PBRMix mix_x = sample_pbr(
+ get_uv_x()
+ , tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , tex_emissive
+#endif
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ // Triplanar-specific normal texture fix
+ mix_x.vNt = _t_normal_post_x(mix_x.vNt);
+#endif
+ mix = mix_pbr(mix, mix_x, tw.weight.x);
+ break;
+ default:
+ break;
+ }
+
+ switch (tw.type & SAMPLE_Y)
+ {
+ case SAMPLE_Y:
+ PBRMix mix_y = sample_pbr(
+ get_uv_y()
+ , tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , tex_emissive
+#endif
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ // Triplanar-specific normal texture fix
+ mix_y.vNt = _t_normal_post_y(mix_y.vNt);
+#endif
+ mix = mix_pbr(mix, mix_y, tw.weight.y);
+ break;
+ default:
+ break;
+ }
+
+ switch (tw.type & SAMPLE_Z)
+ {
+ case SAMPLE_Z:
+ PBRMix mix_z = sample_pbr(
+ get_uv_z()
+ , tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , tex_emissive
+#endif
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ // Triplanar-specific normal texture fix
+ // *NOTE: Bottom face has not been tested
+ mix_z.vNt = _t_normal_post_z(mix_z.vNt);
+#endif
+ mix = mix_pbr(mix, mix_z, tw.weight.z);
+ break;
+ default:
+ break;
+ }
+
+ return mix;
+}
+
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+
+#define TerrainCoord vec2
+
+#define terrain_sample_pbr sample_pbr
+
+#endif
+
+PBRMix multiply_factors_pbr(
+ PBRMix mix_in
+ , vec4 factor_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , vec3 factor_orm
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , vec2 factor_rm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , vec3 factor_emissive
+#endif
+ )
+{
+ PBRMix mix = mix_in;
+ mix.col *= factor_col;
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ mix.orm *= factor_orm;
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ mix.rm *= factor_rm;
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ mix.emissive *= factor_emissive;
+#endif
+ return mix;
+}
+
+PBRMix terrain_sample_and_multiply_pbr(
+ TerrainCoord terrain_coord
+ , sampler2D tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , sampler2D tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , sampler2D tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , sampler2D tex_emissive
+#endif
+ , vec4 factor_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , vec3 factor_orm
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , vec2 factor_rm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , vec3 factor_emissive
+#endif
+ )
+{
+ PBRMix mix = terrain_sample_pbr(
+ terrain_coord
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+ , _t_triplanar()
+#endif
+ , tex_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , tex_orm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ , tex_vNt
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , tex_emissive
+#endif
+ );
+
+ mix = multiply_factors_pbr(mix
+ , factor_col
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+ , factor_orm
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ , factor_rm
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ , factor_emissive
+#endif
+ );
+
+ return mix;
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
new file mode 100644
index 0000000000..489fc26e3f
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
@@ -0,0 +1,96 @@
+/**
+ * @file class1\environment\pbrterrainV.glsl
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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$
+ */
+
+uniform mat3 normal_matrix;
+uniform mat4 texture_matrix0;
+uniform mat4 modelview_matrix;
+uniform mat4 modelview_projection_matrix;
+
+in vec3 position;
+in vec3 normal;
+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;
+flat out float vary_sign;
+out vec4 vary_texcoord0;
+out vec4 vary_texcoord1;
+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;
+
+vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
+vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
+
+void main()
+{
+ //transform vertex
+ gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
+ vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
+
+ vec3 n = normal_matrix * normal;
+ 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(tangent_space_transform(vec4(t, tangent.w), n, texture_base_color_transform, texture_matrix0));
+ vary_sign = tangent.w;
+ 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.
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+ // xy
+ vary_coords[0].xy = texture_transform(position.xy, texture_base_color_transform, texture_matrix0);
+ // yz
+ vary_coords[0].zw = texture_transform(position.yz, texture_base_color_transform, texture_matrix0);
+ // (-x)z
+ vary_coords[1].xy = texture_transform(position.xz * vec2(-1, 1), texture_base_color_transform, texture_matrix0);
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+ vary_texcoord0.xy = texture_transform(position.xy, texture_base_color_transform, texture_matrix0);
+#endif
+
+ vec4 tc = vec4(texcoord1,0,1);
+ vary_texcoord0.zw = tc.xy;
+ vary_texcoord1.xy = tc.xy-vec2(2.0, 0.0);
+ vary_texcoord1.zw = tc.xy-vec2(1.0, 0.0);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
index 3443785e1a..1629ed91c8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl
@@ -97,6 +97,7 @@ vec3 toneMapACES_Hill(vec3 color)
uniform float exposure;
uniform float gamma;
+uniform float aces_mix;
vec3 toneMap(vec3 color)
{
@@ -106,7 +107,7 @@ vec3 toneMap(vec3 color)
color *= exposure * exp_scale;
// mix ACES and Linear here as a compromise to avoid over-darkening legacy content
- color = mix(toneMapACES_Hill(color), color, 0.3);
+ color = mix(toneMapACES_Hill(color), color, aces_mix);
#endif
return color;
@@ -152,6 +153,15 @@ float noise(vec2 x) {
//=============================
+void debugExposure(inout vec3 color)
+{
+ float exp_scale = texture(exposureMap, vec2(0.5,0.5)).r;
+ exp_scale *= 0.5;
+ if (abs(vary_fragcoord.y-exp_scale) < 0.01 && vary_fragcoord.x < 0.1)
+ {
+ color = vec3(1,0,0);
+ }
+}
vec3 legacyGamma(vec3 color)
{
@@ -180,7 +190,8 @@ void main()
vec3 seed = (diff.rgb+vec3(1.0))*vec3(tc.xy, tc.x+tc.y);
vec3 nz = vec3(noise(seed.rg), noise(seed.gb), noise(seed.rb));
diff.rgb += nz*0.003;
-
+
+ //debugExposure(diff.rgb);
frag_color = max(diff, vec4(0));
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
index 9d9ba49d82..785c748234 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
@@ -27,6 +27,15 @@
in vec3 vary_HazeColor;
in float vary_LightNormPosDot;
+#ifdef HAS_HDRI
+in vec4 vary_position;
+in vec3 vary_rel_pos;
+uniform float sky_hdr_scale;
+uniform float hdri_split_screen;
+uniform mat3 env_mat;
+uniform sampler2D environmentMap;
+#endif
+
uniform sampler2D rainbow_map;
uniform sampler2D halo_map;
@@ -37,6 +46,9 @@ uniform float ice_level;
out vec4 frag_data[4];
vec3 srgb_to_linear(vec3 c);
+vec3 linear_to_srgb(vec3 c);
+
+#define PI 3.14159265
/////////////////////////////////////////////////////////////////////////
// The fragment shader for the sky
@@ -71,24 +83,42 @@ vec3 halo22(float d)
void main()
{
- // Potential Fill-rate optimization. Add cloud calculation
- // back in and output alpha of 0 (so that alpha culling kills
- // the fragment) if the sky wouldn't show up because the clouds
- // are fully opaque.
-
- vec3 color = vary_HazeColor;
-
- float rel_pos_lightnorm = vary_LightNormPosDot;
- float optic_d = rel_pos_lightnorm;
- vec3 halo_22 = halo22(optic_d);
- color.rgb += rainbow(optic_d);
- color.rgb += halo_22;
- color.rgb *= 2.;
- color.rgb = clamp(color.rgb, vec3(0), vec3(5));
+ vec3 color;
+#ifdef HAS_HDRI
+ vec3 frag_coord = vary_position.xyz/vary_position.w;
+ if (-frag_coord.x > ((1.0-hdri_split_screen)*2.0-1.0))
+ {
+ vec3 pos = normalize(vary_rel_pos);
+ pos = env_mat * pos;
+ vec2 texCoord = vec2(atan(pos.z, pos.x) + PI, acos(pos.y)) / vec2(2.0 * PI, PI);
+ color = textureLod(environmentMap, texCoord.xy, 0).rgb * sky_hdr_scale;
+ color = min(color, vec3(8192*8192*16)); // stupidly large value arrived at by binary search -- avoids framebuffer corruption from some HDRIs
+
+ frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_HAS_HDRI);
+ }
+ else
+#endif
+ {
+ // Potential Fill-rate optimization. Add cloud calculation
+ // back in and output alpha of 0 (so that alpha culling kills
+ // the fragment) if the sky wouldn't show up because the clouds
+ // are fully opaque.
+
+ color = vary_HazeColor;
+
+ float rel_pos_lightnorm = vary_LightNormPosDot;
+ float optic_d = rel_pos_lightnorm;
+ vec3 halo_22 = halo22(optic_d);
+ color.rgb += rainbow(optic_d);
+ color.rgb += halo_22;
+ color.rgb *= 2.;
+ color.rgb = clamp(color.rgb, vec3(0), vec3(5));
+
+ frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_SKIP_ATMOS);
+ }
frag_data[0] = vec4(0);
frag_data[1] = vec4(0);
- frag_data[2] = vec4(0.0,0.0,0.0,GBUFFER_FLAG_SKIP_ATMOS); //1.0 in norm.w masks off fog
frag_data[3] = vec4(color.rgb, 1.0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
index 17ce2dee5b..24d2db2183 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
@@ -35,6 +35,11 @@ in vec3 position;
out vec3 vary_HazeColor;
out float vary_LightNormPosDot;
+#ifdef HAS_HDRI
+out vec4 vary_position;
+out vec3 vary_rel_pos;
+#endif
+
// Inputs
uniform vec3 camPosLocal;
@@ -72,6 +77,11 @@ void main()
// Get relative position
vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0);
+#ifdef HAS_HDRI
+ vary_rel_pos = rel_pos;
+ vary_position = pos;
+#endif
+
// Adj position vector to clamp altitude
if (rel_pos.y > 0.)
{
@@ -98,7 +108,7 @@ void main()
vec3 light_atten = (blue_density + vec3(haze_density * 0.25)) * (density_multiplier * max_y);
// Calculate relative weights
- vec3 combined_haze = abs(blue_density) + vec3(abs(haze_density));
+ vec3 combined_haze = max(abs(blue_density) + vec3(abs(haze_density)), vec3(1e-6));
vec3 blue_weight = blue_density / combined_haze;
vec3 haze_weight = haze_density / combined_haze;
diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
index f6d509e2c6..5f598f84a7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
@@ -38,10 +38,11 @@ in vec3 vary_normal;
in vec4 vary_texcoord0;
in vec4 vary_texcoord1;
-vec2 encode_normal(vec3 n);
+void mirrorClip(vec3 position);
void main()
{
+ mirrorClip(pos);
/// Note: This should duplicate the blending functionality currently used for the terrain rendering.
vec4 color0 = texture(detail_0, vary_texcoord0.xy);
@@ -59,7 +60,7 @@ void main()
frag_data[0] = outColor;
frag_data[1] = vec4(0.0,0.0,0.0,-1.0);
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
index f6d3b59e85..8e1e4b54d5 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
@@ -25,12 +25,12 @@
uniform mat3 normal_matrix;
uniform mat4 texture_matrix0;
+uniform mat4 modelview_matrix;
uniform mat4 modelview_projection_matrix;
in vec3 position;
in vec3 normal;
in vec4 diffuse_color;
-in vec2 texcoord0;
in vec2 texcoord1;
out vec3 pos;
@@ -41,18 +41,16 @@ out vec4 vary_texcoord1;
uniform vec4 object_plane_s;
uniform vec4 object_plane_t;
-vec4 texgen_object(vec4 vpos, vec4 tc, mat4 mat, vec4 tp0, vec4 tp1)
+vec2 texgen_object(vec4 vpos, mat4 mat, vec4 tp0, vec4 tp1)
{
vec4 tcoord;
tcoord.x = dot(vpos, tp0);
tcoord.y = dot(vpos, tp1);
- tcoord.z = tc.z;
- tcoord.w = tc.w;
tcoord = mat * tcoord;
- return tcoord;
+ return tcoord.xy;
}
void main()
@@ -62,12 +60,12 @@ void main()
vec4 t_pos = modelview_projection_matrix * pre_pos;
gl_Position = t_pos;
- pos = t_pos.xyz;
+ pos = (modelview_matrix*pre_pos).xyz;
vary_normal = normalize(normal_matrix * normal);
// Transform and pass tex coords
- vary_texcoord0.xy = texgen_object(vec4(position, 1.0), vec4(texcoord0,0,1), texture_matrix0, object_plane_s, object_plane_t).xy;
+ vary_texcoord0.xy = texgen_object(vec4(position, 1.0), texture_matrix0, object_plane_s, object_plane_t);
vec4 t = vec4(texcoord1,0,1);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl
index 636dfed4ba..732333311c 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl
@@ -65,7 +65,7 @@ vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl
// Apply texture animation first to avoid shearing and other artifacts
texcoord = (sl_animation_transform * vec4(texcoord, 0, 1)).xy;
// Convert to left-handed coordinate system. The offset of 1 is necessary
- // for rotations to be applied correctly.
+ // for rotation and scale to be applied correctly.
texcoord.y = 1.0 - texcoord.y;
texcoord = khr_texture_transform(texcoord, khr_gltf_transform[0].xy, khr_gltf_transform[0].z, khr_gltf_transform[1].xy);
// Convert back to right-handed coordinate system
diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
index e2d87e68fa..708acd0194 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
@@ -32,13 +32,14 @@ uniform sampler2D diffuseMap;
in vec4 vertex_color;
in vec3 vary_normal;
in vec2 vary_texcoord0;
+in vec3 vary_position;
uniform float minimum_alpha;
-vec2 encode_normal(vec3 n);
-
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
vec4 col = texture(diffuseMap, vary_texcoord0.xy);
if (col.a < minimum_alpha)
{
@@ -48,6 +49,6 @@ void main()
frag_data[0] = vec4(vertex_color.rgb*col.rgb, 0.0);
frag_data[1] = vec4(0,0,0,0);
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(encode_normal(nvn.xyz), 0.0, GBUFFER_FLAG_HAS_ATMOS);
+ frag_data[2] = vec4(nvn.xyz, GBUFFER_FLAG_HAS_ATMOS);
frag_data[3] = vec4(0);
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl
index ce8a10712c..c84fccd4c6 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/treeV.glsl
@@ -24,6 +24,7 @@
*/
uniform mat4 texture_matrix0;
+uniform mat4 modelview_matrix;
uniform mat4 modelview_projection_matrix;
uniform mat3 normal_matrix;
@@ -34,11 +35,14 @@ in vec2 texcoord0;
out vec3 vary_normal;
out vec4 vertex_color;
out vec2 vary_texcoord0;
+out vec3 vary_position;
void main()
{
//transform vertex
gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
+ vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
+
vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
vary_normal = normalize(normal_matrix * normal);
diff --git a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl
index 6cd2445522..388042e7e0 100644
--- a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl
@@ -1,9 +1,9 @@
-/**
- * @file encodeNormF.glsl
+/**
+ * @file normaldebugF.glsl
*
- * $LicenseInfo:firstyear=2018&license=viewerlgpl$
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2018, Linden Research, Inc.
+ * Copyright (C) 2023, 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
@@ -23,12 +23,11 @@
* $/LicenseInfo$
*/
-// Lambert Azimuthal Equal-Area projection
-// See: https://aras-p.info/texts/CompactNormalStorage.html
-// Also see: A_bit_more_deferred_-_CryEngine3.ppt
-vec2 encode_normal(vec3 n)
+out vec4 frag_color;
+
+in vec4 vertex_color;
+
+void main()
{
- float f = sqrt(8 * n.z + 8);
- return n.xy / f + 0.5;
+ frag_color = max(vertex_color, vec4(0));
}
-
diff --git a/indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl
new file mode 100644
index 0000000000..51d05cd507
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugG.glsl
@@ -0,0 +1,76 @@
+/**
+ * @file normaldebugG.glsl
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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$
+ */
+
+// *NOTE: Geometry shaders have a reputation for being slow. Consider using
+// compute shaders instead, which have a reputation for being fast. This
+// geometry shader in particular seems to run fine on my machine, but I won't
+// vouch for this in performance-critical areas.
+// -Cosmic,2023-09-28
+
+out vec4 vertex_color;
+
+in vec4 normal_g[];
+#ifdef HAS_ATTRIBUTE_TANGENT
+in vec4 tangent_g[];
+#endif
+
+layout(triangles) in;
+#ifdef HAS_ATTRIBUTE_TANGENT
+layout(line_strip, max_vertices = 12) out;
+#else
+layout(line_strip, max_vertices = 6) out;
+#endif
+
+void triangle_normal_debug(int i)
+{
+ // Normal
+ vec4 normal_color = vec4(1.0, 1.0, 0.0, 1.0);
+ gl_Position = gl_in[i].gl_Position;
+ vertex_color = normal_color;
+ EmitVertex();
+ gl_Position = normal_g[i];
+ vertex_color = normal_color;
+ EmitVertex();
+ EndPrimitive();
+
+#ifdef HAS_ATTRIBUTE_TANGENT
+ // Tangent
+ vec4 tangent_color = vec4(0.0, 1.0, 1.0, 1.0);
+ gl_Position = gl_in[i].gl_Position;
+ vertex_color = tangent_color;
+ EmitVertex();
+ gl_Position = tangent_g[i];
+ vertex_color = tangent_color;
+ EmitVertex();
+ EndPrimitive();
+#endif
+}
+
+void main()
+{
+ triangle_normal_debug(0);
+ triangle_normal_debug(1);
+ triangle_normal_debug(2);
+}
diff --git a/indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl
new file mode 100644
index 0000000000..b198bc2485
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugV.glsl
@@ -0,0 +1,74 @@
+/**
+ * @file normaldebugV.glsl
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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$
+ */
+
+in vec3 position;
+in vec3 normal;
+out vec4 normal_g;
+#ifdef HAS_ATTRIBUTE_TANGENT
+in vec4 tangent;
+out vec4 tangent_g;
+#endif
+
+uniform float debug_normal_draw_length;
+
+#ifdef HAS_SKIN
+mat4 getObjectSkinnedTransform();
+#else
+uniform mat3 normal_matrix;
+#endif
+uniform mat4 projection_matrix;
+uniform mat4 modelview_matrix;
+
+// *NOTE: Should use the modelview_projection_matrix here in the non-skinned
+// case for efficiency, but opting for the simplier implementation for now as
+// this is debug code. Also, the skinned version hasn't beeen tested yet.
+// world_pos = mat * vec4(position.xyz, 1.0)
+vec4 get_screen_normal(vec3 position, vec4 world_pos, vec3 normal, mat4 mat)
+{
+ vec4 world_norm = mat * vec4((position + normal), 1.0);
+ world_norm.xyz -= world_pos.xyz;
+ world_norm.xyz = debug_normal_draw_length * normalize(world_norm.xyz);
+ world_norm.xyz += world_pos.xyz;
+ return projection_matrix * world_norm;
+}
+
+void main()
+{
+#ifdef HAS_SKIN
+ mat4 mat = getObjectSkinnedTransform();
+ mat = modelview_matrix * mat;
+#else
+#define mat modelview_matrix
+#endif
+
+ vec4 world_pos = mat * vec4(position.xyz,1.0);
+
+ gl_Position = projection_matrix * world_pos;
+ normal_g = get_screen_normal(position.xyz, world_pos, normal.xyz, mat);
+#ifdef HAS_ATTRIBUTE_TANGENT
+ tangent_g = get_screen_normal(position.xyz, world_pos, tangent.xyz, mat);
+#endif
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl
index 9ecdf0bf77..c1ed1bfe6e 100644
--- a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl
@@ -38,6 +38,7 @@ in vec3 vary_dir;
uniform float mipLevel;
uniform int u_width;
uniform float max_probe_lod;
+uniform float probe_strength;
// =============================================================================================================
@@ -129,7 +130,7 @@ vec4 prefilterEnvMap(vec3 R)
float totalWeight = 0.0;
float envMapDim = float(textureSize(reflectionProbes, 0).s);
float roughness = mipLevel/max_probe_lod;
- int numSamples = max(int(32*roughness), 1);
+ int numSamples = max(int(PROBE_FILTER_SAMPLES*roughness), 1);
float numMips = max_probe_lod+1;
@@ -163,5 +164,6 @@ void main()
{
vec3 N = normalize(vary_dir);
frag_color = max(prefilterEnvMap(N), vec4(0));
+ frag_color.a *= probe_strength;
}
// =============================================================================================================
diff --git a/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl b/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl
index 67c99530e3..142f2a5d71 100644
--- a/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/bumpF.glsl
@@ -30,9 +30,13 @@ uniform sampler2D texture1;
in vec2 vary_texcoord0;
in vec2 vary_texcoord1;
+in vec3 vary_position;
+
+void mirrorClip(vec3 pos);
void main()
{
+ mirrorClip(vary_position);
float tex0 = texture(texture0, vary_texcoord0.xy).a;
float tex1 = texture(texture1, vary_texcoord1.xy).a;
diff --git a/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl b/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl
index 7d5417919e..b8a02fbdec 100644
--- a/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl
+++ b/indra/newview/app_settings/shaders/class1/objects/bumpV.glsl
@@ -23,6 +23,7 @@
* $/LicenseInfo$
*/
+uniform mat4 modelview_matrix;
uniform mat4 texture_matrix0;
uniform mat4 modelview_projection_matrix;
@@ -32,11 +33,11 @@ in vec2 texcoord1;
out vec2 vary_texcoord0;
out vec2 vary_texcoord1;
+out vec3 vary_position;
#ifdef HAS_SKIN
mat4 getObjectSkinnedTransform();
uniform mat4 projection_matrix;
-uniform mat4 modelview_matrix;
#endif
void main()
@@ -46,8 +47,10 @@ void main()
mat4 mat = getObjectSkinnedTransform();
mat = modelview_matrix * mat;
vec4 pos = mat * vec4(position.xyz, 1.0);
+ vary_position = pos.xyz;
gl_Position = projection_matrix * pos;
#else
+ vary_position = (modelview_matrix * vec4(position.xyz, 1.0)).xyz;
gl_Position = modelview_projection_matrix*vec4(position.xyz, 1.0);
#endif
vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
index a1da4b1f9a..40b7f9cf0e 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
@@ -66,7 +66,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
// I had thought blue_density and haze_density should have equal weighting,
// but attenuation due to haze_density tends to seem too strong
- vec3 combined_haze = blue_density + vec3(haze_density);
+ vec3 combined_haze = max(blue_density + vec3(haze_density), vec3(1e-6));
vec3 blue_weight = blue_density / combined_haze;
vec3 haze_weight = vec3(haze_density) / combined_haze;
@@ -98,7 +98,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
haze_glow = max(haze_glow, .001); // set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
haze_glow *= glow.x;
// higher glow.x gives dimmer glow (because next step is 1 / "angle")
- haze_glow = pow(haze_glow, glow.z);
+ haze_glow = clamp(pow(haze_glow, glow.z), -10, 10);
// glow.z should be negative, so we're doing a sort of (1 / "angle") function
// add "minimum anti-solar illumination"
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
index acd32a81b3..7a1a54a77a 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
@@ -68,7 +68,6 @@ void waterClip(vec3 pos);
vec3 srgb_to_linear(vec3 c);
vec3 linear_to_srgb(vec3 c);
-vec2 encode_normal (vec3 n);
vec4 applySkyAndWaterFog(vec3 pos, vec3 additive, vec3 atten, vec4 color);
void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
@@ -78,6 +77,8 @@ float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
float getAmbientClamp();
+void mirrorClip(vec3 pos);
+
void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv,
vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear);
@@ -167,6 +168,8 @@ vec3 calcPointLightOrSpotLight(vec3 light_col, vec3 diffuse, vec3 v, vec3 n, vec
void main()
{
+ mirrorClip(vary_position);
+
vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5;
vec4 pos = vec4(vary_position, 1.0);
diff --git a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl
index 003dd05e6f..059c2a64ce 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl
@@ -90,6 +90,7 @@ 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 mirrorClip(vec3 pos);
void waterClip(vec3 pos);
void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor);
@@ -156,6 +157,8 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
void main()
{
+ mirrorClip(vary_position);
+
vec3 color = vec3(0,0,0);
vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
index 1ea57516a4..1bd5f5a718 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
@@ -35,7 +35,7 @@ in vec2 vary_fragcoord;
uniform vec3 sun_dir;
uniform float shadow_bias;
-vec3 getNorm(vec2 pos_screen);
+vec4 getNorm(vec2 pos_screen);
vec4 getPosition(vec2 pos_screen);
float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
@@ -45,13 +45,13 @@ void main()
{
vec2 pos_screen = vary_fragcoord.xy;
vec4 pos = getPosition(pos_screen);
- vec3 norm = getNorm(pos_screen);
+ vec4 norm = getNorm(pos_screen);
vec4 col;
- col.r = sampleDirectionalShadow(pos.xyz, norm, pos_screen);
+ col.r = sampleDirectionalShadow(pos.xyz, norm.xyz, pos_screen);
col.g = 1.0f;
- col.b = sampleSpotShadow(pos.xyz, norm, 0, pos_screen);
- col.a = sampleSpotShadow(pos.xyz, norm, 1, pos_screen);
+ col.b = sampleSpotShadow(pos.xyz, norm.xyz, 0, pos_screen);
+ col.a = sampleSpotShadow(pos.xyz, norm.xyz, 1, pos_screen);
frag_color = clamp(col, vec4(0), vec4(1));
}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
index 0126e09d4c..e0333b6044 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
@@ -32,7 +32,7 @@ out vec4 frag_color;
in vec2 vary_fragcoord;
vec4 getPosition(vec2 pos_screen);
-vec3 getNorm(vec2 pos_screen);
+vec4 getNorm(vec2 pos_screen);
float sampleDirectionalShadow(vec3 shadow_pos, vec3 norm, vec2 pos_screen);
float sampleSpotShadow(vec3 shadow_pos, vec3 norm, int index, vec2 pos_screen);
@@ -42,13 +42,13 @@ void main()
{
vec2 pos_screen = vary_fragcoord.xy;
vec4 pos = getPosition(pos_screen);
- vec3 norm = getNorm(pos_screen);
+ vec4 norm = getNorm(pos_screen);
vec4 col;
- col.r = sampleDirectionalShadow(pos.xyz, norm, pos_screen);
- col.g = calcAmbientOcclusion(pos, norm, pos_screen);
- col.b = sampleSpotShadow(pos.xyz, norm, 0, pos_screen);
- col.a = sampleSpotShadow(pos.xyz, norm, 1, pos_screen);
+ col.r = sampleDirectionalShadow(pos.xyz, norm.xyz, pos_screen);
+ col.g = calcAmbientOcclusion(pos, norm.xyz, pos_screen);
+ col.b = sampleSpotShadow(pos.xyz, norm.xyz, 0, pos_screen);
+ col.a = sampleSpotShadow(pos.xyz, norm.xyz, 1, pos_screen);
frag_color = clamp(col, vec4(0), vec4(1));
}
diff --git a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl
index 8430cca325..c382a9fbc0 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl
@@ -53,8 +53,11 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout
void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity);
+void mirrorClip(vec3 pos);
+
void main()
{
+ mirrorClip(vary_position);
#ifdef HAS_DIFFUSE_LOOKUP
vec4 color = diffuseLookup(vary_texcoord0.xy);
#else
diff --git a/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl
index 0b154e82ad..4af57e3b80 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl
@@ -33,7 +33,7 @@ uniform vec3 moon_dir;
uniform int sun_up_factor;
in vec2 vary_fragcoord;
-vec3 getNorm(vec2 pos_screen);
+vec4 getNorm(vec2 pos_screen);
vec4 getPositionWithDepth(vec2 pos_screen, float depth);
void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
@@ -53,8 +53,7 @@ void main()
vec2 tc = vary_fragcoord.xy;
float depth = getDepth(tc.xy);
vec4 pos = getPositionWithDepth(tc, depth);
- vec4 norm = texture(normalMap, tc);
- norm.xyz = getNorm(tc);
+ vec4 norm = getNorm(tc);
vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
vec3 color = vec3(0);
diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
index ec1e49eeb4..2a20eb79d2 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
@@ -45,6 +45,13 @@ void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float
vec3 srgb_to_linear(vec3 cs);
vec3 linear_to_srgb(vec3 cs);
+uniform mat4 modelview_matrix;
+uniform mat3 normal_matrix;
+
+in vec3 vary_position;
+
+void mirrorClip(vec3 pos);
+
#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
out vec4 frag_color;
@@ -66,12 +73,12 @@ uniform vec4 morphFactor;
uniform vec3 camPosLocal;
uniform mat3 env_mat;
+uniform float is_mirror;
+
uniform vec3 sun_dir;
uniform vec3 moon_dir;
in vec2 vary_fragcoord;
-in vec3 vary_position;
-
uniform mat4 proj_mat;
uniform mat4 inv_proj;
uniform vec2 screen_res;
@@ -209,8 +216,6 @@ in vec3 vary_normal;
in vec4 vertex_color;
in vec2 vary_texcoord0;
-vec2 encode_normal(vec3 n);
-
// get the transformed normal and apply glossiness component from normal map
vec3 getNormal(inout float glossiness)
{
@@ -285,12 +290,12 @@ float getShadow(vec3 pos, vec3 norm)
void main()
{
+ mirrorClip(vary_position);
waterClip();
// diffcol == diffuse map combined with vertex color
vec4 diffcol = texture(diffuseMap, vary_texcoord0.xy);
diffcol.rgb *= vertex_color.rgb;
-
alphaMask(diffcol.a);
// spec == specular map combined with specular color
@@ -299,8 +304,6 @@ void main()
float glossiness = specular_color.a;
vec3 norm = getNormal(glossiness);
- vec2 abnormal = encode_normal(norm.xyz);
-
float emissive = getEmissive(diffcol);
#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND)
@@ -407,10 +410,15 @@ void main()
#else // mode is not DIFFUSE_ALPHA_MODE_BLEND, encode to gbuffer
// deferred path // See: C++: addDeferredAttachment(), shader: softenLightF.glsl
- frag_data[0] = vec4(diffcol.rgb, emissive); // gbuffer is sRGB for legacy materials
- frag_data[1] = vec4(spec.rgb, glossiness); // XYZ = Specular color. W = Specular exponent.
- frag_data[2] = vec4(encode_normal(norm), env, GBUFFER_FLAG_HAS_ATMOS);; // XY = Normal. Z = Env. intensity. W = 1 skip atmos (mask off fog)
- frag_data[3] = vec4(0);
+
+ float flag = GBUFFER_FLAG_HAS_ATMOS;
+
+ frag_data[0] = max(vec4(diffcol.rgb, emissive), vec4(0)); // gbuffer is sRGB for legacy materials
+ frag_data[1] = max(vec4(spec.rgb, glossiness), vec4(0)); // XYZ = Specular color. W = Specular exponent.
+ frag_data[2] = vec4(norm, flag); // XY = Normal. Z = Env. intensity. W = 1 skip atmos (mask off fog)
+ frag_data[3] = vec4(env, 0, 0, 0);
+
#endif
}
+
diff --git a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl
index ec8168465e..edfd6cbced 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl
@@ -48,7 +48,7 @@ in vec4 vary_fragcoord;
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);
vec4 getPosition(vec2 pos_screen);
-vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity);
+vec4 getNorm(vec2 screenpos);
vec2 getScreenXY(vec4 clip);
vec2 getScreenCoord(vec4 clip);
vec3 srgb_to_linear(vec3 c);
@@ -74,9 +74,8 @@ void main()
discard;
}
- float envIntensity; // not used for this shader
- vec3 n;
- vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity); // need `norm.w` for GET_GBUFFER_FLAG()
+ vec4 norm = getNorm(tc); // need `norm.w` for GET_GBUFFER_FLAG()
+ vec3 n = norm.xyz;
vec4 spec = texture(specularRect, tc);
vec3 diffuse = texture(diffuseRect, tc).rgb;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl
index 31af1208bd..60be9f4407 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl
@@ -52,7 +52,7 @@ uniform vec4 viewport;
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);
-vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity);
+vec4 getNorm(vec2 screenpos);
vec4 getPosition(vec2 pos_screen);
vec2 getScreenXY(vec4 clip);
vec2 getScreenCoord(vec4 clip);
@@ -72,9 +72,8 @@ void main()
vec2 tc = getScreenCoord(vary_fragcoord);
vec3 pos = getPosition(tc).xyz;
- float envIntensity;
- vec3 n;
- vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity); // need `norm.w` for GET_GBUFFER_FLAG()
+ vec4 norm = getNorm(tc); // need `norm.w` for GET_GBUFFER_FLAG()
+ vec3 n = norm.xyz;
vec3 diffuse = texture(diffuseRect, tc).rgb;
vec4 spec = texture(specularRect, tc);
diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
index 906e66ecc8..90c84cc428 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
@@ -31,6 +31,7 @@ float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n,
uniform samplerCubeArray reflectionProbes;
uniform samplerCubeArray irradianceProbes;
+
uniform sampler2D sceneMap;
uniform int cube_snapshot;
uniform float max_probe_lod;
@@ -47,6 +48,7 @@ layout (std140) uniform ReflectionProbes
/// box[0..2] - plane 0 .. 2 in [A,B,C,D] notation
// box[3][0..2] - plane thickness
mat4 refBox[MAX_REFMAP_COUNT];
+ mat4 heroBox;
// list of bounding spheres for reflection probes sorted by distance to camera (closest first)
vec4 refSphere[MAX_REFMAP_COUNT];
// extra parameters
@@ -55,6 +57,7 @@ layout (std140) uniform ReflectionProbes
// z - fade in
// w - znear
vec4 refParams[MAX_REFMAP_COUNT];
+ vec4 heroSphere;
// index of cube map in reflectionProbes for a corresponding reflection probe
// e.g. cube map channel of refSphere[2] is stored in refIndex[2]
// refIndex.x - cubemap channel in reflectionProbes
@@ -70,6 +73,10 @@ layout (std140) uniform ReflectionProbes
// number of reflection probes present in refSphere
int refmapCount;
+
+ int heroShape;
+ int heroMipCount;
+ int heroProbeCount;
};
// Inputs
@@ -365,11 +372,11 @@ return texCUBE(envMap, ReflDirectionWS);
// i - probe index in refBox/refSphere
// d - distance to nearest wall in clip space
// scale - scale of box, default 1.0
-vec3 boxIntersect(vec3 origin, vec3 dir, int i, out float d, float scale)
+vec3 boxIntersect(vec3 origin, vec3 dir, mat4 i, out float d, float scale)
{
// Intersection with OBB convert to unit box space
// Transform in local unit parallax cube space (scaled and rotated)
- mat4 clipToLocal = refBox[i];
+ mat4 clipToLocal = i;
vec3 RayLS = mat3(clipToLocal) * dir;
vec3 PositionLS = (clipToLocal * vec4(origin, 1.0)).xyz;
@@ -388,7 +395,7 @@ vec3 boxIntersect(vec3 origin, vec3 dir, int i, out float d, float scale)
return IntersectPositionCS;
}
-vec3 boxIntersect(vec3 origin, vec3 dir, int i, out float d)
+vec3 boxIntersect(vec3 origin, vec3 dir, mat4 i, out float d)
{
return boxIntersect(origin, dir, i, d, 1.0);
}
@@ -443,9 +450,9 @@ void boxIntersectionDebug( in vec3 ro, in vec3 p, vec3 boxSize, inout vec4 col)
}
-void boxIntersectDebug(vec3 origin, vec3 pos, int i, inout vec4 col)
+void boxIntersectDebug(vec3 origin, vec3 pos, mat4 i, inout vec4 col)
{
- mat4 clipToLocal = refBox[i];
+ mat4 clipToLocal = i;
// transform into unit cube space
origin = (clipToLocal * vec4(origin, 1.0)).xyz;
@@ -462,7 +469,7 @@ void boxIntersectDebug(vec3 origin, vec3 pos, int i, inout vec4 col)
// r - radius of probe influence volume
// i - index of probe in refSphere
// dw - distance weight
-float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, int i, out float dw)
+float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, vec4 i, out float dw)
{
float r1 = r * 0.5; // 50% of radius (outer sphere to start interpolating down)
vec3 delta = pos.xyz - origin;
@@ -471,7 +478,7 @@ float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, int i, out float dw
float atten = 1.0 - max(d2 - r1, 0.0) / max((r - r1), 0.001);
float w = 1.0 / d2;
- w *= refParams[i].z;
+ w *= i.z;
dw = w * atten * max(r, 1.0)*4;
@@ -497,7 +504,7 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c,
if (refIndex[i].w < 0)
{ // box probe
float d = 0;
- v = boxIntersect(pos, dir, i, d);
+ v = boxIntersect(pos, dir, refBox[i], d);
w = max(d, 0.001);
}
@@ -511,7 +518,7 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c,
refIndex[i].w < 1 ? 4096.0*4096.0 : // <== effectively disable parallax correction for automatically placed probes to keep from bombing the world with obvious spheres
rr);
- w = sphereWeight(pos, dir, refSphere[i].xyz, r, i, dw);
+ w = sphereWeight(pos, dir, refSphere[i].xyz, r, refParams[i], dw);
}
v -= c;
@@ -537,7 +544,7 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int
if (refIndex[i].w < 0)
{
float d = 0.0;
- v = boxIntersect(pos, dir, i, d, 3.0);
+ v = boxIntersect(pos, dir, refBox[i], d, 3.0);
w = max(d, 0.001);
}
else
@@ -551,7 +558,7 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int
refIndex[i].w < 1 ? 4096.0*4096.0 : // <== effectively disable parallax correction for automatically placed probes to keep from bombing the world with obvious spheres
rr);
- w = sphereWeight(pos, dir, refSphere[i].xyz, r, i, dw);
+ w = sphereWeight(pos, dir, refSphere[i].xyz, r, refParams[i], dw);
}
v -= c;
@@ -681,6 +688,49 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit)
return col[1]+col[0];
}
+#if defined(HERO_PROBES)
+
+uniform vec4 clipPlane;
+uniform samplerCubeArray heroProbes;
+
+void tapHeroProbe(inout vec3 glossenv, vec3 pos, vec3 norm, float glossiness)
+{
+ float clipDist = dot(pos.xyz, clipPlane.xyz) + clipPlane.w;
+ float w = 0;
+ float dw = 0;
+ float falloffMult = 10;
+ vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
+ if (heroShape < 1)
+ {
+ float d = 0;
+ boxIntersect(pos, norm, heroBox, d, 1.0);
+
+ w = max(d, 0);
+ }
+ else
+ {
+ float r = heroSphere.w;
+
+ w = sphereWeight(pos, refnormpersp, heroSphere.xyz, r, vec4(1), dw);
+ }
+
+ clipDist = clipDist * 0.95 + 0.05;
+ clipDist = clamp(clipDist * falloffMult, 0, 1);
+ w = clamp(w * falloffMult * clipDist, 0, 1);
+ w = mix(0, w, clamp(glossiness - 0.75, 0, 1) * 4); // We only generate a quarter of the mips for the hero probes. Linearly interpolate between normal probes and hero probes based upon glossiness.
+ glossenv = mix(glossenv, textureLod(heroProbes, vec4(env_mat * refnormpersp, 0), (1.0-glossiness)*heroMipCount).xyz, w);
+}
+
+#else
+
+void tapHeroProbe(inout vec3 glossenv, vec3 pos, vec3 norm, float glossiness)
+{
+}
+
+#endif
+
+
+
void doProbeSample(inout vec3 ambenv, inout vec3 glossenv,
vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit)
{
@@ -712,6 +762,8 @@ void doProbeSample(inout vec3 ambenv, inout vec3 glossenv,
glossenv = mix(glossenv, ssr.rgb, ssr.a);
}
#endif
+
+ tapHeroProbe(glossenv, pos, norm, glossiness);
}
void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
@@ -747,7 +799,7 @@ void debugTapRefMap(vec3 pos, vec3 dir, float depth, int i, inout vec4 col)
{
if (refIndex[i].w < 0)
{
- boxIntersectDebug(origin, pos, i, col);
+ boxIntersectDebug(origin, pos, refBox[i], col);
}
else
{
@@ -799,6 +851,7 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout
{
float lod = (1.0-glossiness)*reflection_lods;
glossenv = sampleProbes(pos, normalize(refnormpersp), lod);
+
}
if (envIntensity > 0.0)
@@ -826,6 +879,9 @@ void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout
}
#endif
+ tapHeroProbe(glossenv, pos, norm, glossiness);
+ tapHeroProbe(legacyenv, pos, norm, 1.0);
+
glossenv = clamp(glossenv, vec3(0), vec3(10));
}
diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl
index dbf20fe2d8..deb276ef9d 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl
@@ -40,14 +40,13 @@ uniform sampler2D specularRect;
uniform sampler2D diffuseRect;
uniform sampler2D diffuseMap;
-vec3 getNorm(vec2 screenpos);
+vec4 getNorm(vec2 screenpos);
float getDepth(vec2 pos_screen);
float linearDepth(float d, float znear, float zfar);
float linearDepth01(float d, float znear, float zfar);
vec4 getPositionWithDepth(vec2 pos_screen, float depth);
vec4 getPosition(vec2 pos_screen);
-vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity);
float random (vec2 uv);
@@ -57,9 +56,7 @@ void main()
{
vec2 tc = vary_fragcoord.xy;
float depth = linearDepth01(getDepth(tc), zNear, zFar);
- float envIntensity;
- vec3 n;
- vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity); // need `norm.w` for GET_GBUFFER_FLAG()
+ vec4 norm = getNorm(tc); // need `norm.w` for GET_GBUFFER_FLAG()
vec3 pos = getPositionWithDepth(tc, getDepth(tc)).xyz;
vec4 spec = texture(specularRect, tc);
vec2 hitpixel;
@@ -84,7 +81,7 @@ void main()
vec4 collectedColor = vec4(0);
- float w = tapScreenSpaceReflection(4, tc, pos, n, collectedColor, diffuseMap, 0);
+ float w = tapScreenSpaceReflection(4, tc, pos, norm.xyz, collectedColor, diffuseMap, 0);
collectedColor.rgb *= specCol.rgb;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
index 5e8fe9301a..96c32734e4 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl
@@ -50,6 +50,7 @@ uniform float ssao_irradiance_max;
#endif
// Inputs
+uniform vec4 clipPlane;
uniform mat3 env_mat;
uniform mat3 ssao_effect_mat;
uniform vec3 sun_dir;
@@ -60,7 +61,7 @@ in vec2 vary_fragcoord;
uniform mat4 inv_proj;
uniform vec2 screen_res;
-vec3 getNorm(vec2 pos_screen);
+vec4 getNorm(vec2 pos_screen);
vec4 getPositionWithDepth(vec2 pos_screen, float depth);
void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
@@ -127,13 +128,13 @@ void main()
vec2 tc = vary_fragcoord.xy;
float depth = getDepth(tc.xy);
vec4 pos = getPositionWithDepth(tc, depth);
- vec4 norm = texture(normalMap, tc);
- float envIntensity = norm.z;
- norm.xyz = getNorm(tc);
+ vec4 norm = getNorm(tc);
+ vec3 colorEmissive = texture(emissiveRect, tc).rgb;
+ float envIntensity = colorEmissive.r;
vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
vec4 baseColor = texture(diffuseRect, tc);
- vec4 spec = texture(specularRect, vary_fragcoord.xy); // NOTE: PBR linear Emissive
+ vec4 spec = texture(specularRect, tc); // NOTE: PBR linear Emissive
#if defined(HAS_SUN_SHADOW) || defined(HAS_SSAO)
vec2 scol_ambocc = texture(lightMap, vary_fragcoord.xy).rg;
@@ -168,17 +169,17 @@ void main()
if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR))
{
- vec3 orm = texture(specularRect, tc).rgb;
+ vec3 orm = spec.rgb;
float perceptualRoughness = orm.g;
float metallic = orm.b;
float ao = orm.r;
- vec3 colorEmissive = texture(emissiveRect, tc).rgb;
+
// PBR IBL
float gloss = 1.0 - perceptualRoughness;
sampleReflectionProbes(irradiance, radiance, tc, pos.xyz, norm.xyz, gloss, false, amblit_linear);
-
+
adjustIrradiance(irradiance, ambocc);
vec3 diffuseColor;
@@ -188,10 +189,15 @@ void main()
vec3 v = -normalize(pos.xyz);
color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten);
}
- else if (!GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_ATMOS))
+ else if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_HDRI))
+ {
+ // actual HDRI sky, just copy color value
+ color = colorEmissive.rgb;
+ }
+ else if (GET_GBUFFER_FLAG(GBUFFER_FLAG_SKIP_ATMOS))
{
- //should only be true of WL sky, just port over base color value
- color = texture(emissiveRect, tc).rgb;
+ //should only be true of WL sky, port over base color value and scale for fake HDR
+ color = colorEmissive.rgb;
color = srgb_to_linear(color);
color *= sky_hdr_scale;
}
@@ -209,7 +215,7 @@ void main()
vec3 legacyenv = vec3(0);
sampleReflectionProbesLegacy(irradiance, glossenv, legacyenv, tc, pos.xyz, norm.xyz, spec.a, envIntensity, false, amblit_linear);
-
+
adjustIrradiance(irradiance, ambocc);
// apply lambertian IBL only (see pbrIbl)
@@ -244,6 +250,7 @@ void main()
// add radiance map
applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz);
+
}
color.rgb = mix(color.rgb, baseColor.rgb, baseColor.a);
diff --git a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl
index d31b37fb60..319fa86148 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl
@@ -72,7 +72,7 @@ uniform mat4 inv_proj;
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);
bool clipProjectedLightVars(vec3 center, vec3 pos, out float dist, out float l_dist, out vec3 lv, out vec4 proj_tc );
-vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity);
+vec4 getNorm(vec2 screenpos);
vec3 getProjectedLightAmbiance(float amb_da, float attenuation, float lit, float nl, float noise, vec2 projected_uv);
vec3 getProjectedLightDiffuseColor(float light_distance, vec2 projected_uv );
vec2 getScreenCoord(vec4 clip);
@@ -121,9 +121,8 @@ void main()
shadow = clamp(shadow, 0.0, 1.0);
}
- float envIntensity;
- vec3 n;
- vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity);
+ vec4 norm = getNorm(tc);
+ vec3 n = norm.xyz;
float dist_atten = calcLegacyDistanceAttenuation(dist, falloff);
if (dist_atten <= 0.0)
@@ -145,7 +144,6 @@ void main()
if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR))
{
- vec3 colorEmissive = texture(emissiveRect, tc).rgb;
vec3 orm = spec.rgb;
float perceptualRoughness = orm.g;
float metallic = orm.b;
@@ -182,6 +180,8 @@ void main()
}
else
{
+ float envIntensity = texture(emissiveRect, tc).r;
+
diffuse = srgb_to_linear(diffuse);
spec.rgb = srgb_to_linear(spec.rgb);
diff --git a/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl
index f6b8299f91..f6bef1e498 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl
@@ -56,7 +56,6 @@ void main()
}
vec4 pos = getPositionWithDepth(tc, depth);
- vec4 norm = texture(normalMap, tc);
vec4 fogged = getWaterFogView(pos.xyz);
diff --git a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl
index 223e55eb69..ef086fc3be 100644
--- a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl
+++ b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl
@@ -55,9 +55,11 @@ in vec4 view;
in vec3 vary_position;
vec4 applyWaterFogViewLinearNoClip(vec3 pos, vec4 color);
+void mirrorClip(vec3 position);
void main()
{
+ mirrorClip(vary_position);
vec4 color;
//get detail normals
diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl
index b364e454e8..b2a81aa025 100644
--- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl
+++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl
@@ -35,6 +35,8 @@ vec3 scaleSoftClipFragLinear(vec3 l);
void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
vec4 applyWaterFogViewLinear(vec3 pos, vec4 color);
+void mirrorClip(vec3 pos);
+
// PBR interface
vec2 BRDF(float NoV, float roughness);
@@ -129,6 +131,7 @@ vec3 getPositionWithNDC(vec3 ndc);
void main()
{
+ mirrorClip(vary_position);
vN = vary_normal;
vT = vary_tangent;
vB = cross(vN, vT);
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index f92d9a2a0e..8950770172 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -1,4 +1,4 @@
-version 59
+version 60
// The version number above should be incremented IF AND ONLY IF some
// change has been made that is sufficiently important to justify
// resetting the graphics preferences of all users to the recommended
@@ -48,6 +48,8 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 2
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTreeLODFactor 1 1.0
RenderVBOEnable 1 1
RenderVBOMappingDisable 1 1
@@ -73,6 +75,7 @@ RenderGLMultiThreadedTextures 1 0
RenderGLMultiThreadedMedia 1 1
RenderReflectionProbeResolution 1 128
RenderScreenSpaceReflections 1 1
+RenderMirrors 1 1
//
@@ -94,6 +97,8 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderTerrainDetail 1 0
RenderTerrainLODFactor 1 1
+RenderTerrainPBRDetail 1 -4
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTreeLODFactor 1 0
RenderVolumeLODFactor 1 1.125
RenderDeferredSSAO 1 0
@@ -103,6 +108,11 @@ WLSkyDetail 1 96
RenderFSAASamples 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 0
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 256
+RenderHeroProbeDistance 1 4
+RenderHeroProbeUpdateRate 1 4
+RenderHeroProbeConservativeUpdateMultiplier 1 16
//
// Medium Low Graphics Settings
@@ -123,6 +133,8 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 1.0
+RenderTerrainPBRDetail 1 -1
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.125
RenderDeferredSSAO 1 0
@@ -132,6 +144,11 @@ WLSkyDetail 1 96
RenderFSAASamples 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 0
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 256
+RenderHeroProbeDistance 1 6
+RenderHeroProbeUpdateRate 1 3
+RenderHeroProbeConservativeUpdateMultiplier 1 16
//
// Medium Graphics Settings (standard)
@@ -150,6 +167,8 @@ RenderLocalLightCount 1 512
RenderTransparentWater 1 0
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.25
RenderDeferredSSAO 1 0
@@ -161,6 +180,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 1
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 6
+RenderHeroProbeUpdateRate 1 3
+RenderHeroProbeConservativeUpdateMultiplier 1 16
//
// Medium High Graphics Settings
@@ -179,6 +203,8 @@ RenderLocalLightCount 1 1024
RenderTransparentWater 1 1
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.375
RenderDeferredSSAO 1 0
@@ -190,6 +216,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 2
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 6
+RenderHeroProbeUpdateRate 1 2
+RenderHeroProbeConservativeUpdateMultiplier 1 8
//
// High Graphics Settings (SSAO + sun shadows)
@@ -208,6 +239,8 @@ RenderLocalLightCount 1 2048
RenderTransparentWater 1 1
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.5
RenderDeferredSSAO 1 1
@@ -219,6 +252,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 3
+RenderMirrors 1 1
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 8
+RenderHeroProbeUpdateRate 1 2
+RenderHeroProbeConservativeUpdateMultiplier 1 8
//
// High Ultra Graphics Settings (deferred + SSAO + all shadows)
@@ -236,6 +274,8 @@ RenderMaxPartCount 1 4096
RenderLocalLightCount 1 4096
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTransparentWater 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.75
@@ -248,6 +288,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 3
+RenderMirrors 1 1
+RenderHeroProbeResolution 1 1024
+RenderHeroProbeDistance 1 16
+RenderHeroProbeUpdateRate 1 1
+RenderHeroProbeConservativeUpdateMultiplier 1 4
//
// Ultra graphics (REALLY PURTY!)
@@ -264,6 +309,8 @@ RenderLocalLightCount 1 8192
RenderMaxPartCount 1 8192
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTransparentWater 1 1
RenderTreeLODFactor 1 1.0
RenderVolumeLODFactor 1 2.0
@@ -277,6 +324,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 3
+RenderMirrors 1 1
+RenderHeroProbeResolution 1 2048
+RenderHeroProbeDistance 1 16
+RenderHeroProbeUpdateRate 1 1
+RenderHeroProbeConservativeUpdateMultiplier 1 4
//
// Class Unknown Hardware (unknown)
@@ -285,6 +337,7 @@ list Unknown
RenderShadowDetail 1 0
RenderDeferredSSAO 1 0
RenderUseAdvancedAtmospherics 1 0
+RenderMirrors 1 0
//
// VRAM > 512MB
@@ -306,6 +359,7 @@ RenderTransparentWater 1 0
RenderDeferredSSAO 0 0
RenderShadowDetail 0 0
RenderReflectionProbeDetail 0 -1
+RenderMirrors 0 0
list Intel
RenderAnisotropic 1 0
@@ -320,4 +374,7 @@ list GL3
RenderFSAASamples 0 0
RenderReflectionsEnabled 0 0
RenderReflectionProbeDetail 0 0
+RenderMirrors 0 0
+list TexUnit16orLess
+RenderTerrainPBRDetail 1 -1
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index 40aaccc8cb..8c71235f37 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -1,4 +1,4 @@
-version 56
+version 57
// The version number above should be incremented IF AND ONLY IF some
// change has been made that is sufficiently important to justify
// resetting the graphics preferences of all users to the recommended
@@ -45,6 +45,8 @@ RenderObjectBump 1 1
RenderLocalLightCount 1 4096
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTransparentWater 1 1
RenderTreeLODFactor 1 1.0
RenderVBOEnable 1 1
@@ -72,6 +74,7 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 2
RenderScreenSpaceReflections 1 1
RenderReflectionProbeLevel 1 3
+RenderMirrors 1 1
//
// Low Graphics Settings
@@ -89,6 +92,8 @@ RenderLocalLightCount 1 8
RenderMaxPartCount 1 0
RenderTerrainDetail 1 0
RenderTerrainLODFactor 1 1
+RenderTerrainPBRDetail 1 -4
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTransparentWater 1 0
RenderTreeLODFactor 1 0
RenderVolumeLODFactor 1 1.125
@@ -101,6 +106,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 0
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 256
+RenderHeroProbeDistance 1 4
+RenderHeroProbeUpdateRate 1 4
+RenderHeroProbeConservativeUpdateMultiplier 1 16
//
// Medium Low Graphics Settings
@@ -118,6 +128,8 @@ RenderMaxPartCount 1 2048
RenderLocalLightCount 1 256
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 1.0
+RenderTerrainPBRDetail 1 -1
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTransparentWater 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.125
@@ -130,6 +142,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 0
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 256
+RenderHeroProbeDistance 1 6
+RenderHeroProbeUpdateRate 1 3
+RenderHeroProbeConservativeUpdateMultiplier 1 16
//
// Medium Graphics Settings (standard)
@@ -147,6 +164,8 @@ RenderMaxPartCount 1 4096
RenderLocalLightCount 1 512
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTransparentWater 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.25
@@ -159,6 +178,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 0
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 6
+RenderHeroProbeUpdateRate 1 3
+RenderHeroProbeConservativeUpdateMultiplier 1 16
//
// Medium High Graphics Settings
@@ -176,6 +200,8 @@ RenderMaxPartCount 1 4096
RenderLocalLightCount 1 1024
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 1
RenderTransparentWater 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.375
@@ -188,6 +214,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 0
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 0
+RenderMirrors 1 0
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 6
+RenderHeroProbeUpdateRate 1 2
+RenderHeroProbeConservativeUpdateMultiplier 1 8
//
// High Graphics Settings (SSAO + sun shadows)
@@ -205,6 +236,8 @@ RenderMaxPartCount 1 4096
RenderLocalLightCount 1 2048
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTransparentWater 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.5
@@ -217,6 +250,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 1
+RenderMirrors 1 1
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 8
+RenderHeroProbeUpdateRate 1 2
+RenderHeroProbeConservativeUpdateMultiplier 1 8
//
// High Ultra Graphics Settings (SSAO + all shadows)
@@ -234,6 +272,8 @@ RenderMaxPartCount 1 4096
RenderLocalLightCount 1 4096
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTransparentWater 1 1
RenderTreeLODFactor 1 0.5
RenderVolumeLODFactor 1 1.75
@@ -246,6 +286,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 2
+RenderMirrors 1 1
+RenderHeroProbeResolution 1 512
+RenderHeroProbeDistance 1 16
+RenderHeroProbeUpdateRate 1 1
+RenderHeroProbeConservativeUpdateMultiplier 1 4
//
// Ultra graphics (REALLY PURTY!)
@@ -262,6 +307,8 @@ RenderLocalLightCount 1 8192
RenderMaxPartCount 1 8192
RenderTerrainDetail 1 1
RenderTerrainLODFactor 1 2.0
+RenderTerrainPBRDetail 1 0
+RenderTerrainPBRPlanarSampleCount 1 3
RenderTransparentWater 1 1
RenderTreeLODFactor 1 1.0
RenderVolumeLODFactor 1 2.0
@@ -275,6 +322,11 @@ RenderReflectionsEnabled 1 1
RenderReflectionProbeDetail 1 1
RenderScreenSpaceReflections 1 0
RenderReflectionProbeLevel 1 3
+RenderMirrors 1 1
+RenderHeroProbeResolution 1 1024
+RenderHeroProbeDistance 1 16
+RenderHeroProbeUpdateRate 1 1
+RenderHeroProbeConservativeUpdateMultiplier 1 4
//
// Class Unknown Hardware (unknown)
@@ -283,6 +335,7 @@ list Unknown
RenderShadowDetail 1 0
RenderDeferredSSAO 1 0
RenderUseAdvancedAtmospherics 1 0
+RenderMirrors 1 0
//
@@ -304,10 +357,14 @@ RenderTerrainDetail 1 0
RenderDeferredSSAO 0 0
RenderUseAdvancedAtmospherics 0 0
RenderShadowDetail 0 0
+RenderMirrors 0 0
list TexUnit8orLess
RenderDeferredSSAO 0 0
+list TexUnit16orLess
+RenderTerrainPBRDetail 1 -1
+
list AMD
RenderDeferredSSAO 1 0
@@ -319,3 +376,4 @@ list GL3
RenderFSAASamples 0 0
RenderReflectionProbeDetail 0 0
RenderReflectionsEnabled 0 0
+RenderMirrors 0 0
diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp
new file mode 100644
index 0000000000..55d36b7a32
--- /dev/null
+++ b/indra/newview/gltf/accessor.cpp
@@ -0,0 +1,66 @@
+/**
+ * @file accessor.cpp
+ * @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$
+ */
+
+#include "../llviewerprecompiledheaders.h"
+
+#include "asset.h"
+
+using namespace LL::GLTF;
+
+const Buffer& Buffer::operator=(const tinygltf::Buffer& src)
+{
+ mData = src.data;
+ mName = src.name;
+ mUri = src.uri;
+ return *this;
+}
+
+const BufferView& BufferView::operator=(const tinygltf::BufferView& src)
+{
+ mBuffer = src.buffer;
+ mByteLength = src.byteLength;
+ mByteOffset = src.byteOffset;
+ mByteStride = src.byteStride;
+ mTarget = src.target;
+ mName = src.name;
+ return *this;
+}
+
+const Accessor& Accessor::operator=(const tinygltf::Accessor& src)
+{
+ mBufferView = src.bufferView;
+ mByteOffset = src.byteOffset;
+ mComponentType = src.componentType;
+ mCount = src.count;
+ mType = src.type;
+ mNormalized = src.normalized;
+ mName = src.name;
+ mMax = src.maxValues;
+ mMin = src.minValues;
+
+ return *this;
+}
+
diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h
new file mode 100644
index 0000000000..9b8265d8da
--- /dev/null
+++ b/indra/newview/gltf/accessor.h
@@ -0,0 +1,95 @@
+#pragma once
+
+/**
+ * @file asset.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$
+ */
+
+#include "../lltinygltfhelper.h"
+#include "llstrider.h"
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+
+ constexpr S32 INVALID_INDEX = -1;
+
+ class Buffer
+ {
+ public:
+ std::vector<U8> mData;
+ std::string mName;
+ std::string mUri;
+
+ const Buffer& operator=(const tinygltf::Buffer& src);
+ };
+
+ class BufferView
+ {
+ public:
+ S32 mBuffer = INVALID_INDEX;
+ S32 mByteLength;
+ S32 mByteOffset;
+ S32 mByteStride;
+ S32 mTarget;
+ S32 mComponentType;
+
+ std::string mName;
+
+ const BufferView& operator=(const tinygltf::BufferView& src);
+
+ };
+
+ class Accessor
+ {
+ public:
+ S32 mBufferView = INVALID_INDEX;
+ S32 mByteOffset;
+ S32 mComponentType;
+ S32 mCount;
+ std::vector<double> mMax;
+ std::vector<double> mMin;
+
+ enum class Type : S32
+ {
+ 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
+ };
+
+ S32 mType;
+ bool mNormalized;
+ std::string mName;
+
+ const Accessor& operator=(const tinygltf::Accessor& src);
+ };
+ }
+}
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp
new file mode 100644
index 0000000000..da6d02b356
--- /dev/null
+++ b/indra/newview/gltf/animation.cpp
@@ -0,0 +1,287 @@
+/**
+ * @file animation.cpp
+ * @brief LL GLTF Animation 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$
+ */
+
+#include "../llviewerprecompiledheaders.h"
+
+#include "asset.h"
+#include "buffer_util.h"
+
+using namespace LL::GLTF;
+
+void Animation::allocateGLResources(Asset& asset)
+{
+ if (!mSamplers.empty())
+ {
+ mMinTime = FLT_MAX;
+ mMaxTime = -FLT_MAX;
+ for (auto& sampler : mSamplers)
+ {
+ sampler.allocateGLResources(asset);
+ mMinTime = llmin(sampler.mMinTime, mMinTime);
+ mMaxTime = llmax(sampler.mMaxTime, mMaxTime);
+ }
+ }
+ else
+ {
+ mMinTime = mMaxTime = 0.f;
+ }
+
+ for (auto& channel : mRotationChannels)
+ {
+ channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
+ }
+
+ for (auto& channel : mTranslationChannels)
+ {
+ channel.allocateGLResources(asset, mSamplers[channel.mSampler]);
+ }
+}
+
+void Animation::update(Asset& asset, F32 dt)
+{
+ mTime += dt;
+
+ apply(asset, mTime);
+}
+
+void Animation::apply(Asset& asset, float time)
+{
+ // convert time to animation loop time
+ time = fmod(time, mMaxTime - mMinTime) + mMinTime;
+
+ // apply each channel
+ for (auto& channel : mRotationChannels)
+ {
+ channel.apply(asset, mSamplers[channel.mSampler], time);
+ }
+
+ for (auto& channel : mTranslationChannels)
+ {
+ channel.apply(asset, mSamplers[channel.mSampler], time);
+ }
+};
+
+
+void Animation::Sampler::allocateGLResources(Asset& asset)
+{
+ Accessor& accessor = asset.mAccessors[mInput];
+ mMinTime = accessor.mMin[0];
+ mMaxTime = accessor.mMax[0];
+
+ mFrameTimes.resize(accessor.mCount);
+
+ LLStrider<F32> frame_times = mFrameTimes.data();
+ copy(asset, accessor, frame_times);
+}
+
+void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
+{
+ if (time < mMinTime)
+ {
+ frameIndex = 0;
+ t = 0.0f;
+ return;
+ }
+
+ if (mFrameTimes.size() > 1)
+ {
+ if (time > mMaxTime)
+ {
+ frameIndex = mFrameTimes.size() - 2;
+ t = 1.0f;
+ return;
+ }
+
+ frameIndex = mFrameTimes.size() - 2;
+ t = 1.f;
+
+ for (U32 i = 0; i < mFrameTimes.size() - 1; i++)
+ {
+ if (time >= mFrameTimes[i] && time < mFrameTimes[i + 1])
+ {
+ frameIndex = i;
+ t = (time - mFrameTimes[i]) / (mFrameTimes[i + 1] - mFrameTimes[i]);
+ return;
+ }
+ }
+ }
+ else
+ {
+ frameIndex = 0;
+ t = 0.0f;
+ }
+}
+
+void Animation::RotationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
+{
+ Accessor& accessor = asset.mAccessors[sampler.mOutput];
+
+ copy(asset, accessor, mRotations);
+}
+
+void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
+{
+ U32 frameIndex;
+ F32 t;
+
+ Node& node = asset.mNodes[mTarget.mNode];
+
+ sampler.getFrameInfo(asset, time, frameIndex, t);
+
+ if (sampler.mFrameTimes.size() == 1)
+ {
+ node.setRotation(mRotations[0]);
+ }
+ else
+ {
+ // interpolate
+ LLQuaternion q0(mRotations[frameIndex].get_value());
+ LLQuaternion q1(mRotations[frameIndex + 1].get_value());
+
+ LLQuaternion qf = slerp(t, q0, q1);
+
+ qf.normalize();
+ node.setRotation(glh::quaternionf(qf.mQ));
+ }
+}
+
+void Animation::TranslationChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
+{
+ Accessor& accessor = asset.mAccessors[sampler.mOutput];
+
+ copy(asset, accessor, mTranslations);
+}
+
+void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
+{
+ U32 frameIndex;
+ F32 t;
+
+ Node& node = asset.mNodes[mTarget.mNode];
+
+ sampler.getFrameInfo(asset, time, frameIndex, t);
+
+ if (sampler.mFrameTimes.size() == 1)
+ {
+ node.setTranslation(mTranslations[0]);
+ }
+ else
+ {
+ // interpolate
+ const glh::vec3f& v0 = mTranslations[frameIndex];
+ const glh::vec3f& v1 = mTranslations[frameIndex + 1];
+
+ glh::vec3f vf = v0 + t * (v1 - v0);
+
+ node.setTranslation(vf);
+ }
+}
+
+void Animation::ScaleChannel::allocateGLResources(Asset& asset, Animation::Sampler& sampler)
+{
+ Accessor& accessor = asset.mAccessors[sampler.mOutput];
+
+ copy(asset, accessor, mScales);
+}
+
+void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
+{
+ U32 frameIndex;
+ F32 t;
+
+ Node& node = asset.mNodes[mTarget.mNode];
+
+ sampler.getFrameInfo(asset, time, frameIndex, t);
+
+ if (sampler.mFrameTimes.size() == 1)
+ {
+ node.setScale(mScales[0]);
+ }
+ else
+ {
+ // interpolate
+ const glh::vec3f& v0 = mScales[frameIndex];
+ const glh::vec3f& v1 = mScales[frameIndex + 1];
+
+ glh::vec3f vf = v0 + t * (v1 - v0);
+
+ node.setScale(vf);
+ }
+}
+
+const Animation& Animation::operator=(const tinygltf::Animation& src)
+{
+ mName = src.name;
+
+ mSamplers.resize(src.samplers.size());
+ for (U32 i = 0; i < src.samplers.size(); ++i)
+ {
+ mSamplers[i] = src.samplers[i];
+ }
+
+ for (U32 i = 0; i < src.channels.size(); ++i)
+ {
+ if (src.channels[i].target_path == "rotation")
+ {
+ mRotationChannels.push_back(RotationChannel());
+ mRotationChannels.back() = src.channels[i];
+ }
+
+ if (src.channels[i].target_path == "translation")
+ {
+ mTranslationChannels.push_back(TranslationChannel());
+ mTranslationChannels.back() = src.channels[i];
+ }
+
+ if (src.channels[i].target_path == "scale")
+ {
+ mScaleChannels.push_back(ScaleChannel());
+ mScaleChannels.back() = src.channels[i];
+ }
+ }
+
+ return *this;
+}
+
+void Skin::allocateGLResources(Asset& asset)
+{
+ if (mInverseBindMatrices != INVALID_INDEX)
+ {
+ Accessor& accessor = asset.mAccessors[mInverseBindMatrices];
+ copy(asset, accessor, mInverseBindMatricesData);
+ }
+}
+
+const Skin& Skin::operator=(const tinygltf::Skin& src)
+{
+ mName = src.name;
+ mSkeleton = src.skeleton;
+ mInverseBindMatrices = src.inverseBindMatrices;
+ mJoints = src.joints;
+
+ return *this;
+}
+
diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h
new file mode 100644
index 0000000000..869eae963a
--- /dev/null
+++ b/indra/newview/gltf/animation.h
@@ -0,0 +1,181 @@
+#pragma once
+
+/**
+ * @file animation.h
+ * @brief LL GLTF Animation 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$
+ */
+
+#include "accessor.h"
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+
+ class Animation
+ {
+ public:
+ class Sampler
+ {
+ public:
+ std::vector<F32> mFrameTimes;
+
+ F32 mMinTime = -FLT_MAX;
+ F32 mMaxTime = FLT_MAX;
+
+ S32 mInput = INVALID_INDEX;
+ 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;
+
+ return *this;
+ }
+
+ // get the frame index and time for the specified time
+ // asset -- the asset to reference for Accessors
+ // time -- the animation time to get the frame info for
+ // frameIndex -- index of the closest frame that precedes the specified time
+ // t - interpolant value between the frameIndex and the next frame
+ void getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t);
+ };
+
+ class Channel
+ {
+ public:
+ class Target
+ {
+ public:
+ S32 mNode = INVALID_INDEX;
+ std::string mPath;
+ };
+
+ 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;
+ }
+
+ };
+
+ class RotationChannel : public Channel
+ {
+ public:
+ std::vector<glh::quaternionf> mRotations;
+
+ const RotationChannel& operator=(const tinygltf::AnimationChannel& src)
+ {
+ Channel::operator=(src);
+ return *this;
+ }
+
+ // prepare data needed for rendering
+ // asset -- asset to reference for Accessors
+ // sampler -- Sampler associated with this channel
+ void allocateGLResources(Asset& asset, Sampler& sampler);
+
+ void apply(Asset& asset, Sampler& sampler, F32 time);
+ };
+
+ class TranslationChannel : public Channel
+ {
+ public:
+ std::vector<glh::vec3f> mTranslations;
+
+ const TranslationChannel& operator=(const tinygltf::AnimationChannel& src)
+ {
+ Channel::operator=(src);
+ return *this;
+ }
+
+ // prepare data needed for rendering
+ // asset -- asset to reference for Accessors
+ // sampler -- Sampler associated with this channel
+ void allocateGLResources(Asset& asset, Sampler& sampler);
+
+ void apply(Asset& asset, Sampler& sampler, F32 time);
+ };
+
+ class ScaleChannel : public Channel
+ {
+ public:
+ std::vector<glh::vec3f> mScales;
+
+ const ScaleChannel& operator=(const tinygltf::AnimationChannel& src)
+ {
+ Channel::operator=(src);
+ return *this;
+ }
+
+ // prepare data needed for rendering
+ // asset -- asset to reference for Accessors
+ // sampler -- Sampler associated with this channel
+ void allocateGLResources(Asset& asset, Sampler& sampler);
+
+ void apply(Asset& asset, Sampler& sampler, F32 time);
+ };
+
+ std::string mName;
+ std::vector<Sampler> mSamplers;
+
+ // min/max time values for all samplers combined
+ F32 mMinTime = 0.f;
+ F32 mMaxTime = 0.f;
+
+ // current time of the animation
+ F32 mTime = 0.f;
+
+ std::vector<RotationChannel> mRotationChannels;
+ std::vector<TranslationChannel> mTranslationChannels;
+ std::vector<ScaleChannel> mScaleChannels;
+
+ const Animation& operator=(const tinygltf::Animation& src);
+
+ void allocateGLResources(Asset& asset);
+
+ void update(Asset& asset, float dt);
+
+ // apply this animation at the specified time
+ void apply(Asset& asset, F32 time);
+ };
+
+ }
+}
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
new file mode 100644
index 0000000000..313e82bf01
--- /dev/null
+++ b/indra/newview/gltf/asset.cpp
@@ -0,0 +1,664 @@
+/**
+ * @file asset.cpp
+ * @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$
+ */
+
+#include "../llviewerprecompiledheaders.h"
+
+#include "asset.h"
+#include "llvolumeoctree.h"
+#include "../llviewershadermgr.h"
+#include "../llviewercontrol.h"
+
+using namespace LL::GLTF;
+
+void Scene::updateTransforms(Asset& asset)
+{
+ LLMatrix4a identity;
+ identity.setIdentity();
+ for (auto& nodeIndex : mNodes)
+ {
+ Node& node = asset.mNodes[nodeIndex];
+ node.updateTransforms(asset, identity);
+ }
+}
+
+void Scene::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview)
+{
+ for (auto& nodeIndex : mNodes)
+ {
+ Node& node = asset.mNodes[nodeIndex];
+ node.updateRenderTransforms(asset, modelview);
+ }
+}
+
+void Node::updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview)
+{
+ matMul(mMatrix, modelview, mRenderMatrix);
+
+ for (auto& childIndex : mChildren)
+ {
+ Node& child = asset.mNodes[childIndex];
+ child.updateRenderTransforms(asset, mRenderMatrix);
+ }
+}
+
+LLMatrix4a inverse(const LLMatrix4a& mat);
+
+void Node::updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix)
+{
+ makeMatrixValid();
+ matMul(mMatrix, parentMatrix, mAssetMatrix);
+ mAssetMatrixInv = inverse(mAssetMatrix);
+
+ S32 my_index = this - &asset.mNodes[0];
+
+ for (auto& childIndex : mChildren)
+ {
+ Node& child = asset.mNodes[childIndex];
+ child.mParent = my_index;
+ child.updateTransforms(asset, mAssetMatrix);
+ }
+}
+
+void Asset::updateTransforms()
+{
+ for (auto& scene : mScenes)
+ {
+ scene.updateTransforms(*this);
+ }
+}
+
+void Asset::updateRenderTransforms(const LLMatrix4a& 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);
+ }
+ }
+
+#endif
+
+}
+
+S32 Asset::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ LLVector4a* intersection, // return the intersection point
+ LLVector2* tex_coord, // return the texture coordinates of the intersection point
+ LLVector4a* normal, // return the surface normal at the intersection point
+ LLVector4a* tangent, // return the surface tangent at the intersection point
+ S32* primitive_hitp
+)
+{
+ S32 node_hit = -1;
+ S32 primitive_hit = -1;
+
+ LLVector4a local_start;
+ LLVector4a asset_end = end;
+ LLVector4a local_end;
+ LLVector4a p;
+
+
+ for (auto& node : mNodes)
+ {
+ if (node.mMesh != INVALID_INDEX)
+ {
+
+ bool newHit = false;
+
+ // transform start and end to this node's local space
+ node.mAssetMatrixInv.affineTransform(start, local_start);
+ node.mAssetMatrixInv.affineTransform(asset_end, local_end);
+
+ Mesh& mesh = mMeshes[node.mMesh];
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ const LLVolumeTriangle* tri = primitive.lineSegmentIntersect(local_start, local_end, &p, tex_coord, normal, tangent);
+ if (tri)
+ {
+ newHit = true;
+ local_end = p;
+
+ // pointer math to get the node index
+ node_hit = &node - &mNodes[0];
+ llassert(&mNodes[node_hit] == &node);
+
+ //pointer math to get the primitive index
+ primitive_hit = &primitive - &mesh.mPrimitives[0];
+ llassert(&mesh.mPrimitives[primitive_hit] == &primitive);
+ }
+ }
+
+ if (newHit)
+ {
+ // shorten line segment on hit
+ node.mAssetMatrix.affineTransform(p, asset_end);
+
+ // transform results back to asset space
+ if (intersection)
+ {
+ *intersection = asset_end;
+ }
+
+ if (normal || tangent)
+ {
+ LLMatrix4 normalMatrix(node.mAssetMatrixInv.getF32ptr());
+
+ normalMatrix.transpose();
+
+ LLMatrix4a norm_mat;
+ norm_mat.loadu((F32*)normalMatrix.mMatrix);
+
+ if (normal)
+ {
+ LLVector4a n = *normal;
+ F32 w = n.getF32ptr()[3];
+ n.getF32ptr()[3] = 0.0f;
+
+ norm_mat.affineTransform(n, *normal);
+ normal->getF32ptr()[3] = w;
+ }
+
+ if (tangent)
+ {
+ LLVector4a t = *tangent;
+ F32 w = t.getF32ptr()[3];
+ t.getF32ptr()[3] = 0.0f;
+
+ norm_mat.affineTransform(t, *tangent);
+ tangent->getF32ptr()[3] = w;
+ }
+ }
+ }
+ }
+ }
+
+ if (node_hit != -1)
+ {
+ if (primitive_hitp)
+ {
+ *primitive_hitp = primitive_hit;
+ }
+ }
+
+ return node_hit;
+}
+
+
+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);
+ mMatrixValid = true;
+ }
+}
+
+void Node::makeTRSValid()
+{
+ if (!mTRSValid && mMatrixValid)
+ {
+ glh::matrix4f t(mMatrix.getF32ptr());
+
+ 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;
+ }
+}
+
+void Node::setRotation(const glh::quaternionf& q)
+{
+ makeTRSValid();
+ mRotation = q;
+ mMatrixValid = false;
+}
+
+void Node::setTranslation(const glh::vec3f& t)
+{
+ makeTRSValid();
+ mTranslation = t;
+ mMatrixValid = false;
+}
+
+void Node::setScale(const glh::vec3f& s)
+{
+ makeTRSValid();
+ mScale = s;
+ mMatrixValid = false;
+}
+
+const Node& Node::operator=(const tinygltf::Node& src)
+{
+ F32* dstMatrix = mMatrix.getF32ptr();
+
+ if (src.matrix.size() == 16)
+ {
+ // Node has a transformation matrix, just copy it
+ for (U32 i = 0; i < 16; ++i)
+ {
+ dstMatrix[i] = (F32)src.matrix[i];
+ }
+
+ mMatrixValid = true;
+ }
+ else if (!src.rotation.empty() || !src.translation.empty() || !src.scale.empty())
+ {
+ // node has rotation/translation/scale, convert to matrix
+ if (src.rotation.size() == 4)
+ {
+ mRotation = glh::quaternionf((F32)src.rotation[0], (F32)src.rotation[1], (F32)src.rotation[2], (F32)src.rotation[3]);
+ }
+
+ if (src.translation.size() == 3)
+ {
+ mTranslation = glh::vec3f((F32)src.translation[0], (F32)src.translation[1], (F32)src.translation[2]);
+ }
+
+ glh::vec3f scale;
+ if (src.scale.size() == 3)
+ {
+ mScale = glh::vec3f((F32)src.scale[0], (F32)src.scale[1], (F32)src.scale[2]);
+ }
+ else
+ {
+ mScale.set_value(1.f, 1.f, 1.f);
+ }
+
+ mTRSValid = true;
+ }
+ else
+ {
+ // node specifies no transformation, set to identity
+ mMatrix.setIdentity();
+ }
+
+ mChildren = src.children;
+ mMesh = src.mesh;
+ mSkin = src.skin;
+ mName = src.name;
+
+ return *this;
+}
+
+void Asset::render(bool opaque, bool rigged)
+{
+ if (rigged)
+ {
+ gGL.loadIdentity();
+ }
+
+ for (auto& node : mNodes)
+ {
+ if (node.mSkin != INVALID_INDEX)
+ {
+ if (rigged)
+ {
+ Skin& skin = mSkins[node.mSkin];
+ skin.uploadMatrixPalette(*this, node);
+ }
+ else
+ {
+ //skip static nodes if we're rendering rigged
+ continue;
+ }
+ }
+ else if (rigged)
+ {
+ // skip rigged nodes if we're not rendering rigged
+ continue;
+ }
+
+
+ if (node.mMesh != INVALID_INDEX)
+ {
+ 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];
+
+ if ((material.mMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_BLEND) == opaque)
+ {
+ continue;
+ }
+ material.mMaterial->bind();
+ cull = !material.mMaterial->mDoubleSided;
+ }
+ else
+ {
+ if (!opaque)
+ {
+ continue;
+ }
+ 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());
+ }
+
+ }
+ }
+ }
+}
+
+void Asset::renderOpaque()
+{
+ render(true);
+}
+
+void Asset::renderTransparent()
+{
+ render(false);
+}
+
+void Asset::update()
+{
+ F32 dt = gFrameTimeSeconds - mLastUpdateTime;
+
+ if (dt > 0.f)
+ {
+ mLastUpdateTime = gFrameTimeSeconds;
+ if (mAnimations.size() > 0)
+ {
+ 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);
+ }
+
+ updateTransforms();
+ }
+}
+
+void Asset::allocateGLResources(const std::string& filename, const tinygltf::Model& model)
+{
+ // do images first as materials may depend on images
+ for (auto& image : mImages)
+ {
+ image.allocateGLResources();
+ }
+
+ // do materials before meshes as meshes may depend on materials
+ for (U32 i = 0; i < mMaterials.size(); ++i)
+ {
+ mMaterials[i].allocateGLResources(*this);
+ LLTinyGLTFHelper::getMaterialFromModel(filename, model, i, mMaterials[i].mMaterial, mMaterials[i].mName, true);
+ }
+
+ for (auto& mesh : mMeshes)
+ {
+ mesh.allocateGLResources(*this);
+ }
+
+ for (auto& animation : mAnimations)
+ {
+ animation.allocateGLResources(*this);
+ }
+
+ for (auto& skin : mSkins)
+ {
+ skin.allocateGLResources(*this);
+ }
+}
+
+const Asset& Asset::operator=(const tinygltf::Model& src)
+{
+ mScenes.resize(src.scenes.size());
+ for (U32 i = 0; i < src.scenes.size(); ++i)
+ {
+ mScenes[i] = src.scenes[i];
+ }
+
+ mNodes.resize(src.nodes.size());
+ for (U32 i = 0; i < src.nodes.size(); ++i)
+ {
+ mNodes[i] = src.nodes[i];
+ }
+
+ mMeshes.resize(src.meshes.size());
+ for (U32 i = 0; i < src.meshes.size(); ++i)
+ {
+ mMeshes[i] = src.meshes[i];
+ }
+
+ mMaterials.resize(src.materials.size());
+ for (U32 i = 0; i < src.materials.size(); ++i)
+ {
+ mMaterials[i] = src.materials[i];
+ }
+
+ mBuffers.resize(src.buffers.size());
+ for (U32 i = 0; i < src.buffers.size(); ++i)
+ {
+ mBuffers[i] = src.buffers[i];
+ }
+
+ mBufferViews.resize(src.bufferViews.size());
+ for (U32 i = 0; i < src.bufferViews.size(); ++i)
+ {
+ mBufferViews[i] = src.bufferViews[i];
+ }
+
+ mTextures.resize(src.textures.size());
+ for (U32 i = 0; i < src.textures.size(); ++i)
+ {
+ mTextures[i] = src.textures[i];
+ }
+
+ mSamplers.resize(src.samplers.size());
+ for (U32 i = 0; i < src.samplers.size(); ++i)
+ {
+ mSamplers[i] = src.samplers[i];
+ }
+
+ mImages.resize(src.images.size());
+ for (U32 i = 0; i < src.images.size(); ++i)
+ {
+ mImages[i] = src.images[i];
+ }
+
+ mAccessors.resize(src.accessors.size());
+ for (U32 i = 0; i < src.accessors.size(); ++i)
+ {
+ mAccessors[i] = src.accessors[i];
+ }
+
+ mAnimations.resize(src.animations.size());
+ for (U32 i = 0; i < src.animations.size(); ++i)
+ {
+ mAnimations[i] = src.animations[i];
+ }
+
+ mSkins.resize(src.skins.size());
+ for (U32 i = 0; i < src.skins.size(); ++i)
+ {
+ mSkins[i] = src.skins[i];
+ }
+
+ return *this;
+}
+
+const Material& Material::operator=(const tinygltf::Material& src)
+{
+ mName = src.name;
+ return *this;
+}
+
+void Material::allocateGLResources(Asset& asset)
+{
+ // allocate material
+ mMaterial = new LLFetchedGLTFMaterial();
+}
+
+const Mesh& Mesh::operator=(const tinygltf::Mesh& src)
+{
+ mPrimitives.resize(src.primitives.size());
+ for (U32 i = 0; i < src.primitives.size(); ++i)
+ {
+ mPrimitives[i] = src.primitives[i];
+ }
+
+ mWeights = src.weights;
+ mName = src.name;
+
+ return *this;
+}
+
+void Mesh::allocateGLResources(Asset& asset)
+{
+ for (auto& primitive : mPrimitives)
+ {
+ primitive.allocateGLResources(asset);
+ }
+}
+
+const Scene& Scene::operator=(const tinygltf::Scene& src)
+{
+ mNodes = src.nodes;
+ mName = src.name;
+
+ return *this;
+}
+
+const Texture& Texture::operator=(const tinygltf::Texture& src)
+{
+ mSampler = src.sampler;
+ mSource = src.source;
+ mName = src.name;
+
+ return *this;
+}
+
+const Sampler& Sampler::operator=(const tinygltf::Sampler& src)
+{
+ mMagFilter = src.magFilter;
+ mMinFilter = src.minFilter;
+ mWrapS = src.wrapS;
+ mWrapT = src.wrapT;
+ mName = src.name;
+
+ return *this;
+}
+
+void Skin::uploadMatrixPalette(Asset& asset, Node& node)
+{
+ // prepare matrix palette
+
+ // modelview will be applied by the shader, so assume matrix palette is in asset space
+ std::vector<glh::matrix4f> t_mp;
+
+ t_mp.resize(mJoints.size());
+
+ for (U32 i = 0; i < mJoints.size(); ++i)
+ {
+ Node& joint = asset.mNodes[mJoints[i]];
+
+ //t_mp[i].set_value(joint.mRenderMatrix.getF32ptr());
+ //t_mp[i] = t_mp[i] * mInverseBindMatricesData[i];
+
+ //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];
+
+ }
+
+ std::vector<F32> glmp;
+
+ glmp.resize(mJoints.size() * 12);
+
+ F32* mp = glmp.data();
+
+ for (U32 i = 0; i < mJoints.size(); ++i)
+ {
+ F32* m = (F32*)t_mp[i].m;
+
+ 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];
+ }
+
+ LLGLSLShader::sCurBoundShaderPtr->uniformMatrix3x4fv(LLViewerShaderMgr::AVATAR_MATRIX,
+ mJoints.size(),
+ FALSE,
+ (GLfloat*)glmp.data());
+}
+
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
new file mode 100644
index 0000000000..6e576a1ffe
--- /dev/null
+++ b/indra/newview/gltf/asset.h
@@ -0,0 +1,264 @@
+#pragma once
+
+/**
+ * @file asset.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$
+ */
+
+#include "llvertexbuffer.h"
+#include "llvolumeoctree.h"
+#include "../lltinygltfhelper.h"
+#include "accessor.h"
+#include "primitive.h"
+#include "animation.h"
+
+extern F32SecondsImplicit gFrameTimeSeconds;
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+
+ 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;
+ std::string mName;
+
+ const Material& operator=(const tinygltf::Material& src);
+
+ void allocateGLResources(Asset& asset);
+ };
+
+ class Mesh
+ {
+ public:
+ std::vector<Primitive> mPrimitives;
+ std::vector<double> mWeights;
+ std::string mName;
+
+ const Mesh& operator=(const tinygltf::Mesh& src);
+
+ void allocateGLResources(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
+
+ glh::vec3f mTranslation;
+ glh::quaternionf mRotation;
+ glh::vec3f mScale;
+
+ // if true, mMatrix is valid and up to date
+ bool mMatrixValid = false;
+
+ // if true, translation/rotation/scale are valid and up to date
+ bool mTRSValid = false;
+
+ bool mNeedsApplyMatrix = false;
+
+ std::vector<S32> mChildren;
+ S32 mParent = INVALID_INDEX;
+
+ S32 mMesh = INVALID_INDEX;
+ S32 mSkin = INVALID_INDEX;
+
+ std::string mName;
+
+ const Node& operator=(const tinygltf::Node& src);
+
+ // 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);
+
+ // update mAssetMatrix and mAssetMatrixInv
+ void updateTransforms(Asset& asset, const LLMatrix4a& parentMatrix);
+
+ // ensure mMatrix is valid -- if mMatrixValid is false and mTRSValid is true, will update mMatrix to match Translation/Rotation/Scale
+ void makeMatrixValid();
+
+ // ensure Translation/Rotation/Scale are valid -- if mTRSValid is false and mMatrixValid is true, will update Translation/Rotation/Scale to match mMatrix
+ void makeTRSValid();
+
+ // Set rotation of this node
+ // SIDE EFFECT: invalidates mMatrix
+ void setRotation(const glh::quaternionf& rotation);
+
+ // Set translation of this node
+ // SIDE EFFECT: invalidates mMatrix
+ void setTranslation(const glh::vec3f& translation);
+
+ // Set scale of this node
+ // SIDE EFFECT: invalidates mMatrix
+ void setScale(const glh::vec3f& scale);
+ };
+
+ class Skin
+ {
+ public:
+ S32 mInverseBindMatrices = INVALID_INDEX;
+ S32 mSkeleton = INVALID_INDEX;
+ std::vector<S32> mJoints;
+ std::string mName;
+ std::vector<glh::matrix4f> mInverseBindMatricesData;
+
+ void allocateGLResources(Asset& asset);
+ void uploadMatrixPalette(Asset& asset, Node& node);
+
+ const Skin& operator=(const tinygltf::Skin& src);
+ };
+
+ class Scene
+ {
+ public:
+ std::vector<S32> mNodes;
+ std::string mName;
+
+ const Scene& operator=(const tinygltf::Scene& src);
+
+ void updateTransforms(Asset& asset);
+ void updateRenderTransforms(Asset& asset, const LLMatrix4a& modelview);
+ };
+
+ class Texture
+ {
+ public:
+ S32 mSampler = INVALID_INDEX;
+ S32 mSource = INVALID_INDEX;
+ std::string mName;
+
+ const Texture& operator=(const tinygltf::Texture& src);
+ };
+
+ class Sampler
+ {
+ public:
+ S32 mMagFilter;
+ S32 mMinFilter;
+ S32 mWrapS;
+ S32 mWrapT;
+ std::string mName;
+
+ const Sampler& operator=(const tinygltf::Sampler& src);
+ };
+
+ class Image
+ {
+ public:
+ std::string mName;
+ std::string mUri;
+ std::string mMimeType;
+ std::vector<U8> mData;
+ S32 mWidth;
+ S32 mHeight;
+ S32 mComponent;
+ S32 mBits;
+ 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
+
+ }
+ };
+
+ // C++ representation of a GLTF Asset
+ class Asset : public LLRefCount
+ {
+ public:
+ std::vector<Scene> mScenes;
+ std::vector<Node> mNodes;
+ std::vector<Mesh> mMeshes;
+ std::vector<Material> mMaterials;
+ std::vector<Buffer> mBuffers;
+ std::vector<BufferView> mBufferViews;
+ std::vector<Texture> mTextures;
+ std::vector<Sampler> mSamplers;
+ std::vector<Image> mImages;
+ std::vector<Accessor> mAccessors;
+ std::vector<Animation> mAnimations;
+ std::vector<Skin> mSkins;
+
+ // 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);
+
+ // Called periodically (typically once per frame)
+ // Any ongoing work (such as animations) should be handled here
+ // NOT guaranteed to be called every frame
+ // MAY be called more than once per frame
+ // Upon return, all Node Matrix transforms should be up to date
+ void update();
+
+ // update asset-to-node and node-to-asset transforms
+ void updateTransforms();
+
+ // update node render transforms
+ void updateRenderTransforms(const LLMatrix4a& modelview);
+
+ void render(bool opaque, bool rigged = false);
+ void renderOpaque();
+ void renderTransparent();
+
+ // 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
+ S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ LLVector4a* intersection = nullptr, // return the intersection point
+ LLVector2* tex_coord = nullptr, // return the texture coordinates of the intersection point
+ LLVector4a* normal = nullptr, // return the surface normal at the intersection point
+ LLVector4a* tangent = nullptr, // return the surface tangent at the intersection point
+ S32* primitive_hitp = nullptr // return the index of the primitive that was hit
+ );
+
+ const Asset& operator=(const tinygltf::Model& src);
+
+ };
+ }
+}
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
new file mode 100644
index 0000000000..4e6f5901e7
--- /dev/null
+++ b/indra/newview/gltf/buffer_util.h
@@ -0,0 +1,402 @@
+#pragma once
+
+/**
+ * @file buffer_util.inl
+ * @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$
+ */
+
+// inline template implementations for copying data out of GLTF buffers
+// DO NOT include from header files to avoid the need to rebuild the whole project
+// whenever we add support for more types
+
+#ifdef _MSC_VER
+#define LL_FUNCSIG __FUNCSIG__
+#else
+#define LL_FUNCSIG __PRETTY_FUNCTION__
+#endif
+
+namespace LL
+{
+ namespace GLTF
+ {
+ // copy one Scalar from src to dst
+ template<class S, class T>
+ static 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one vec2 from src to dst
+ template<class S, class T>
+ static void copyMat2(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 copyMat3(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 copyMat4(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ //=========================================================================================================
+ // 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)
+ {
+ dst = *src;
+ }
+
+ template<>
+ void copyScalar<U32, U32>(U32* src, U32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ void copyScalar<U32, U16>(U32* src, U16& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ void copyScalar<U16, U16>(U16* src, U16& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ void copyScalar<U16, U32>(U16* src, U32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ void copyScalar<U8, U16>(U8* src, U16& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ void copyScalar<U8, U32>(U8* src, U32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ 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)
+ {
+ dst.set_value(src[0], src[1], src[2]);
+ }
+
+ template<>
+ void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst)
+ {
+ dst.load3(src);
+ }
+
+ template<>
+ 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)
+ {
+ dst.set(src[0], src[1], src[2], src[3]);
+ }
+
+ template<>
+ 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)
+ {
+ dst.set(src[0]*255, src[1]*255, src[2]*255, src[3]*255);
+ }
+
+ template<>
+ void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst)
+ {
+ dst.loadua(src);
+ }
+
+ template<>
+ 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)
+ {
+ dst.set(src[0], src[1], src[2], src[3]);
+ }
+
+ template<>
+ void copyVec4<F32, glh::quaternionf>(F32* src, glh::quaternionf& dst)
+ {
+ dst.set_value(src);
+ }
+
+ template<>
+ void copyMat4<F32, glh::matrix4f>(F32* src, glh::matrix4f& dst)
+ {
+ dst.set_value(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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyScalar(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ // 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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyVec2(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ // 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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyVec3(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ // 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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyVec4(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ // 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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyMat2(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ // 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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyMat3(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ // 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)
+ {
+ for (S32 i = 0; i < count; ++i)
+ {
+ copyMat4(src, *dst);
+ dst++;
+ src = (S*)((U8*)src + stride);
+ }
+ }
+
+ template<class S, class T>
+ static void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride)
+ {
+ if (accessor.mType == (S32)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)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride;
+ copyVec2((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == (S32)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)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;
+ copyVec4((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == (S32)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)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 9 : byteStride;
+ copyMat3((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == (S32)Accessor::Type::MAT4)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 16 : byteStride;
+ copyMat4((S*)src, dst, stride, accessor.mCount);
+ }
+ else
+ {
+ LL_ERRS("GLTF") << "Unsupported accessor type" << LL_ENDL;
+ }
+ }
+
+ // copy data from accessor to strider
+ template<class T>
+ static 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)
+ {
+ LL::GLTF::copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
+ {
+ LL::GLTF::copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
+ {
+ LL::GLTF::copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)
+ {
+ LL::GLTF::copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_SHORT)
+ {
+ LL::GLTF::copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_BYTE)
+ {
+ LL::GLTF::copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
+ }
+ else if (accessor.mComponentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)
+ {
+ LL::GLTF::copy(asset, accessor, (const F64*)src, dst, bufferView.mByteStride);
+ }
+ else
+ {
+ LL_ERRS("GLTF") << "Unsupported component type" << LL_ENDL;
+ }
+ }
+
+ // copy data from accessor to vector
+ template<class T>
+ static void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst)
+ {
+ dst.resize(accessor.mCount);
+ LLStrider<T> strider = dst.data();
+ copy(asset, accessor, strider);
+ }
+ }
+}
+
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
new file mode 100644
index 0000000000..b57a0af18d
--- /dev/null
+++ b/indra/newview/gltf/primitive.cpp
@@ -0,0 +1,400 @@
+/**
+ * @file primitive.cpp
+ * @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$
+ */
+
+#include "../llviewerprecompiledheaders.h"
+
+#include "asset.h"
+#include "buffer_util.h"
+
+#include "../lltinygltfhelper.h"
+
+using namespace LL::GLTF;
+
+void Primitive::allocateGLResources(Asset& asset)
+{
+ // allocate vertex buffer
+ // We diverge from the intent of the GLTF format here to work with our existing render pipeline
+ // GLTF wants us to copy the buffer views into GPU storage as is and build render commands that source that data.
+ // For our engine, though, it's better to rearrange the buffers at load time into a layout that's more consistent.
+ // The GLTF native approach undoubtedly works well if you can count on VAOs, but VAOs perform much worse with our scenes.
+
+ // load vertex data
+ for (auto& it : mAttributes)
+ {
+ const std::string& attribName = it.first;
+ Accessor& accessor = asset.mAccessors[it.second];
+
+ // load vertex data
+ if (attribName == "POSITION")
+ {
+ copy(asset, accessor, mPositions);
+ }
+ else if (attribName == "NORMAL")
+ {
+ copy(asset, accessor, mNormals);
+ }
+ else if (attribName == "TANGENT")
+ {
+ copy(asset, accessor, mTangents);
+ }
+ else if (attribName == "COLOR_0")
+ {
+ copy(asset, accessor, mColors);
+ }
+ else if (attribName == "TEXCOORD_0")
+ {
+ copy(asset, accessor, mTexCoords);
+ }
+ else if (attribName == "JOINTS_0")
+ {
+ copy(asset, accessor, mJoints);
+ }
+ else if (attribName == "WEIGHTS_0")
+ {
+ copy(asset, accessor, mWeights);
+ }
+ }
+
+ // copy index buffer
+ if (mIndices != INVALID_INDEX)
+ {
+ Accessor& accessor = asset.mAccessors[mIndices];
+ copy(asset, accessor, mIndexArray);
+ }
+
+ U32 mask = ATTRIBUTE_MASK;
+
+ if (!mWeights.empty())
+ {
+ mask |= LLVertexBuffer::MAP_WEIGHT4;
+ }
+
+ mVertexBuffer = new LLVertexBuffer(mask);
+ mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size()*2); // double the size of the index buffer for 32-bit indices
+
+ mVertexBuffer->setBuffer();
+ mVertexBuffer->setPositionData(mPositions.data());
+
+ if (!mIndexArray.empty())
+ {
+ mVertexBuffer->setIndexData(mIndexArray.data());
+ }
+
+ if (mTexCoords.empty())
+ {
+ mTexCoords.resize(mPositions.size());
+ }
+
+ // flip texcoord y, upload, then flip back (keep the off-spec data in vram only)
+ for (auto& tc : mTexCoords)
+ {
+ tc[1] = 1.f - tc[1];
+ }
+ mVertexBuffer->setTexCoordData(mTexCoords.data());
+
+ for (auto& tc : mTexCoords)
+ {
+ tc[1] = 1.f - tc[1];
+ }
+
+ if (mColors.empty())
+ {
+ mColors.resize(mPositions.size(), LLColor4U::white);
+ }
+
+ // bake material basecolor into color array
+ if (mMaterial != INVALID_INDEX)
+ {
+ const Material& material = asset.mMaterials[mMaterial];
+ LLColor4 baseColor = material.mMaterial->mBaseColor;
+ for (auto& dst : mColors)
+ {
+ dst = LLColor4U(baseColor * LLColor4(dst));
+ }
+ }
+
+ mVertexBuffer->setColorData(mColors.data());
+
+ if (mNormals.empty())
+ {
+ mNormals.resize(mPositions.size(), LLVector4a(0, 0, 1, 0));
+ }
+
+ mVertexBuffer->setNormalData(mNormals.data());
+
+ if (mTangents.empty())
+ {
+ // TODO: generate tangents if needed
+ mTangents.resize(mPositions.size(), LLVector4a(1, 0, 0, 1));
+ }
+
+ mVertexBuffer->setTangentData(mTangents.data());
+
+ if (!mWeights.empty())
+ {
+ std::vector<LLVector4a> weight_data;
+ weight_data.resize(mWeights.size());
+
+ 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]);
+ };
+
+ mVertexBuffer->setWeight4Data(weight_data.data());
+ }
+
+ createOctree();
+
+ mVertexBuffer->unbind();
+}
+
+void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
+{
+ //store pointers to vertex data
+ tri->mV[0] = &v0;
+ tri->mV[1] = &v1;
+ tri->mV[2] = &v2;
+
+ //store indices
+ tri->mIndex[0] = i0;
+ tri->mIndex[1] = i1;
+ tri->mIndex[2] = i2;
+
+ //get minimum point
+ LLVector4a min = v0;
+ min.setMin(min, v1);
+ min.setMin(min, v2);
+
+ //get maximum point
+ LLVector4a max = v0;
+ max.setMax(max, v1);
+ max.setMax(max, v2);
+
+ //compute center
+ LLVector4a center;
+ center.setAdd(min, max);
+ center.mul(0.5f);
+
+ tri->mPositionGroup = center;
+
+ //compute "radius"
+ LLVector4a size;
+ size.setSub(max, min);
+
+ tri->mRadius = size.getLength3().getF32() * scaler;
+}
+
+void Primitive::createOctree()
+{
+ // create octree
+ mOctree = new LLVolumeOctree();
+
+ F32 scaler = 0.25f;
+
+ if (mMode == TINYGLTF_MODE_TRIANGLES)
+ {
+ const U32 num_triangles = mVertexBuffer->getNumIndices() / 3;
+ // Initialize all the triangles we need
+ mOctreeTriangles.resize(num_triangles);
+
+ for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
+ { //for each triangle
+ const U32 index = triangle_index * 3;
+ LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
+ S32 i0 = mIndexArray[index];
+ S32 i1 = mIndexArray[index + 1];
+ S32 i2 = mIndexArray[index + 2];
+
+ const LLVector4a& v0 = mPositions[i0];
+ const LLVector4a& v1 = mPositions[i1];
+ const LLVector4a& v2 = mPositions[i2];
+
+ initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2);
+
+ //insert
+ mOctree->insert(tri);
+ }
+ }
+ else if (mMode == TINYGLTF_MODE_TRIANGLE_STRIP)
+ {
+ const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
+ // Initialize all the triangles we need
+ mOctreeTriangles.resize(num_triangles);
+
+ for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
+ { //for each triangle
+ const U32 index = triangle_index + 2;
+ LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
+ S32 i0 = mIndexArray[index];
+ S32 i1 = mIndexArray[index - 1];
+ S32 i2 = mIndexArray[index - 2];
+
+ const LLVector4a& v0 = mPositions[i0];
+ const LLVector4a& v1 = mPositions[i1];
+ const LLVector4a& v2 = mPositions[i2];
+
+ initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2);
+
+ //insert
+ mOctree->insert(tri);
+ }
+ }
+ else if (mMode == TINYGLTF_MODE_TRIANGLE_FAN)
+ {
+ const U32 num_triangles = mVertexBuffer->getNumIndices() - 2;
+ // Initialize all the triangles we need
+ mOctreeTriangles.resize(num_triangles);
+
+ for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
+ { //for each triangle
+ const U32 index = triangle_index + 2;
+ LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
+ S32 i0 = mIndexArray[0];
+ S32 i1 = mIndexArray[index - 1];
+ S32 i2 = mIndexArray[index - 2];
+
+ const LLVector4a& v0 = mPositions[i0];
+ const LLVector4a& v1 = mPositions[i1];
+ const LLVector4a& v2 = mPositions[i2];
+
+ initOctreeTriangle(tri, scaler, i0, i1, i2, v0, v1, v2);
+
+ //insert
+ mOctree->insert(tri);
+ }
+ }
+ else if (mMode == TINYGLTF_MODE_POINTS ||
+ mMode == TINYGLTF_MODE_LINE ||
+ mMode == TINYGLTF_MODE_LINE_LOOP ||
+ mMode == TINYGLTF_MODE_LINE_STRIP)
+ {
+ // nothing to do, no volume... maybe add some collision geometry around these primitive types?
+ }
+
+ else
+ {
+ LL_ERRS() << "Unsupported Primitive mode" << LL_ENDL;
+ }
+
+ //remove unneeded octree layers
+ while (!mOctree->balance()) {}
+
+ //calculate AABB for each node
+ LLVolumeOctreeRebound rebound;
+ rebound.traverse(mOctree);
+}
+
+const LLVolumeTriangle* Primitive::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out)
+{
+ if (mOctree.isNull())
+ {
+ return nullptr;
+ }
+
+ LLVector4a dir;
+ dir.setSub(end, start);
+
+ F32 closest_t = 2.f; // must be larger than 1
+
+ //create a proxy LLVolumeFace for the raycast
+ LLVolumeFace face;
+ face.mPositions = mPositions.data();
+ face.mTexCoords = mTexCoords.data();
+ face.mNormals = mNormals.data();
+ face.mTangents = mTangents.data();
+ face.mIndices = nullptr; // unreferenced
+
+ face.mNumIndices = mIndexArray.size();
+ face.mNumVertices = mPositions.size();
+
+ LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
+ intersect.traverse(mOctree);
+
+ // null out proxy data so it doesn't get freed
+ face.mPositions = face.mNormals = face.mTangents = nullptr;
+ face.mIndices = nullptr;
+ face.mTexCoords = nullptr;
+
+ return intersect.mHitTriangle;
+}
+
+Primitive::~Primitive()
+{
+ mOctree = nullptr;
+}
+
+
+const Primitive& Primitive::operator=(const tinygltf::Primitive& src)
+{
+ // 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;
+ default:
+ mGLMode = GL_TRIANGLES;
+ }
+
+ return *this;
+}
diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h
new file mode 100644
index 0000000000..07e8e7deb2
--- /dev/null
+++ b/indra/newview/gltf/primitive.h
@@ -0,0 +1,93 @@
+#pragma once
+
+/**
+ * @file primitive.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$
+ */
+
+#include "llvertexbuffer.h"
+#include "llvolumeoctree.h"
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+
+ constexpr U32 ATTRIBUTE_MASK =
+ LLVertexBuffer::MAP_VERTEX |
+ LLVertexBuffer::MAP_NORMAL |
+ LLVertexBuffer::MAP_TEXCOORD0 |
+ LLVertexBuffer::MAP_TANGENT |
+ LLVertexBuffer::MAP_COLOR;
+
+ class Primitive
+ {
+ public:
+ ~Primitive();
+
+ // GPU copy of mesh data
+ LLPointer<LLVertexBuffer> mVertexBuffer;
+
+ // CPU copy of mesh data
+ std::vector<LLVector2> mTexCoords;
+ std::vector<LLVector4a> mNormals;
+ std::vector<LLVector4a> mTangents;
+ std::vector<LLVector4a> mPositions;
+ std::vector<LLVector4a> mJoints;
+ std::vector<LLVector4a> mWeights;
+ std::vector<LLColor4U> mColors;
+ std::vector<U32> mIndexArray;
+
+ // raycast acceleration structure
+ LLPointer<LLVolumeOctree> mOctree;
+ std::vector<LLVolumeTriangle> mOctreeTriangles;
+
+ S32 mMaterial = -1;
+ U32 mMode = TINYGLTF_MODE_TRIANGLES; // default to triangles
+ U32 mGLMode = LLRender::TRIANGLES;
+ S32 mIndices = -1;
+ std::unordered_map<std::string, int> mAttributes;
+
+ // create octree based on vertex buffer
+ // must be called before buffer is unmapped and after buffer is populated with good data
+ void createOctree();
+
+ //get the LLVolumeTriangle that intersects with the given line segment at the point
+ //closest to start. Moves end to the point of intersection. Returns nullptr if no intersection.
+ //Line segment must be in the same coordinate frame as this Primitive
+ const LLVolumeTriangle* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ LLVector4a* intersection = NULL, // return the intersection point
+ LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
+ LLVector4a* normal = NULL, // return the surface normal at the intersection point
+ LLVector4a* tangent = NULL // return the surface tangent at the intersection point
+ );
+
+ const Primitive& operator=(const tinygltf::Primitive& src);
+
+ void allocateGLResources(Asset& asset);
+ };
+ }
+}
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
new file mode 100644
index 0000000000..4e3439ea5c
--- /dev/null
+++ b/indra/newview/gltfscenemanager.cpp
@@ -0,0 +1,564 @@
+/**
+ * @file gltfscenemanager.cpp
+ * @brief Builds menus out of items.
+ *
+ * $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 "gltfscenemanager.h"
+#include "llviewermenufile.h"
+#include "llappviewer.h"
+#include "lltinygltfhelper.h"
+#include "llvertexbuffer.h"
+#include "llselectmgr.h"
+#include "llagent.h"
+#include "llnotificationsutil.h"
+#include "llvoavatarself.h"
+#include "llvolumeoctree.h"
+#include "gltf/asset.h"
+#include "pipeline.h"
+#include "llviewershadermgr.h"
+
+
+using namespace LL;
+
+// temporary location of LL GLTF Implementation
+using namespace LL::GLTF;
+
+void GLTFSceneManager::load()
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+
+ if (obj)
+ {
+ // Load a scene from disk
+ 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().load(filenames[0]);
+ }
+ },
+ LLFilePicker::FFLOAD_GLTF,
+ true);
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFPreviewSelection");
+ }
+}
+
+void GLTFSceneManager::load(const std::string& filename)
+{
+ tinygltf::Model model;
+ LLTinyGLTFHelper::loadModel(filename, model);
+
+ LLPointer<Asset> asset = new Asset();
+ *asset = model;
+
+ gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
+ asset->allocateGLResources(filename, model);
+ 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;
+
+ if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
+ {
+ mObjects.push_back(obj);
+ }
+ }
+}
+
+GLTFSceneManager::~GLTFSceneManager()
+{
+ mObjects.clear();
+}
+
+void GLTFSceneManager::renderOpaque()
+{
+ render(true);
+}
+
+void GLTFSceneManager::renderAlpha()
+{
+ render(false);
+}
+
+void GLTFSceneManager::update()
+{
+ for (U32 i = 0; i < mObjects.size(); ++i)
+ {
+ if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr)
+ {
+ mObjects.erase(mObjects.begin() + i);
+ --i;
+ continue;
+ }
+
+ Asset* asset = mObjects[i]->mGLTFAsset;
+
+ asset->update();
+
+ }
+}
+
+void GLTFSceneManager::render(bool opaque, bool rigged)
+{
+ // for debugging, just render the whole scene as opaque
+ // by traversing the whole scenegraph
+ // Assumes camera transform is already set and
+ // appropriate shader is already bound
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+
+ for (U32 i = 0; i < mObjects.size(); ++i)
+ {
+ if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr)
+ {
+ mObjects.erase(mObjects.begin() + i);
+ --i;
+ continue;
+ }
+
+ Asset* asset = mObjects[i]->mGLTFAsset;
+
+ gGL.pushMatrix();
+
+ LLMatrix4a mat = mObjects[i]->getGLTFAssetToAgentTransform();
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+
+ matMul(mat, modelview, modelview);
+
+ asset->updateRenderTransforms(modelview);
+ asset->render(opaque, rigged);
+
+ gGL.popMatrix();
+ }
+}
+
+LLMatrix4a inverse(const LLMatrix4a& mat)
+{
+ glh::matrix4f m((F32*)mat.mMatrix);
+ m = m.inverse();
+ LLMatrix4a ret;
+ ret.loadu(m.m);
+ return ret;
+}
+
+bool GLTFSceneManager::lineSegmentIntersect(LLVOVolume* obj, Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, S32* node_hit, S32* primitive_hit,
+ LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
+
+{
+ // line segment intersection test
+ // start and end should be in agent space
+ // volume space and asset space should be the same coordinate frame
+ // results should be transformed back to agent space
+
+ bool ret = false;
+
+ LLVector4a local_start;
+ LLVector4a local_end;
+
+ LLMatrix4a asset_to_agent = obj->getGLTFAssetToAgentTransform();
+ LLMatrix4a agent_to_asset = inverse(asset_to_agent);
+
+ agent_to_asset.affineTransform(start, local_start);
+ agent_to_asset.affineTransform(end, local_end);
+
+ LLVector4a p;
+ LLVector4a n;
+ LLVector2 tc;
+ LLVector4a tn;
+
+ if (intersection != NULL)
+ {
+ p = *intersection;
+ }
+
+ if (tex_coord != NULL)
+ {
+ tc = *tex_coord;
+ }
+
+ if (normal != NULL)
+ {
+ n = *normal;
+ }
+
+ if (tangent != NULL)
+ {
+ tn = *tangent;
+ }
+
+ S32 hit_node_index = asset->lineSegmentIntersect(local_start, local_end, &p, &tc, &n, &tn, primitive_hit);
+
+ if (hit_node_index >= 0)
+ {
+ local_end = p;
+ if (node_hit != NULL)
+ {
+ *node_hit = hit_node_index;
+ }
+
+ if (intersection != NULL)
+ {
+ asset_to_agent.affineTransform(p, *intersection);
+ }
+
+ if (normal != NULL)
+ {
+ LLVector3 v_n(n.getF32ptr());
+ normal->load3(obj->volumeDirectionToAgent(v_n).mV);
+ (*normal).normalize3fast();
+ }
+
+ if (tangent != NULL)
+ {
+ LLVector3 v_tn(tn.getF32ptr());
+
+ LLVector4a trans_tangent;
+ trans_tangent.load3(obj->volumeDirectionToAgent(v_tn).mV);
+
+ LLVector4Logical mask;
+ mask.clear();
+ mask.setElement<3>();
+
+ tangent->setSelectWithMask(mask, tn, trans_tangent);
+ (*tangent).normalize3fast();
+ }
+
+ if (tex_coord != NULL)
+ {
+ *tex_coord = tc;
+ }
+
+ ret = true;
+ }
+
+ return ret;
+}
+
+LLDrawable* GLTFSceneManager::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ BOOL pick_transparent,
+ BOOL pick_rigged,
+ BOOL pick_unselectable,
+ BOOL pick_reflection_probe,
+ S32* node_hit, // return the index of the node that was hit
+ S32* primitive_hit, // return the index of the primitive that was hit
+ LLVector4a* intersection, // return the intersection point
+ LLVector2* tex_coord, // return the texture coordinates of the intersection point
+ LLVector4a* normal, // return the surface normal at the intersection point
+ LLVector4a* tangent) // return the surface tangent at the intersection point
+{
+ LLDrawable* drawable = nullptr;
+
+ LLVector4a local_end = end;
+ LLVector4a position;
+
+ for (U32 i = 0; i < mObjects.size(); ++i)
+ {
+ if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr || !mObjects[i]->getVolume())
+ {
+ mObjects.erase(mObjects.begin() + i);
+ --i;
+ continue;
+ }
+
+ // 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))
+ {
+ local_end = position;
+ if (intersection)
+ {
+ *intersection = position;
+ }
+ drawable = mObjects[i]->mDrawable;
+ }
+ }
+
+ return drawable;
+}
+
+void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size);
+
+extern LLVector4a gDebugRaycastStart;
+extern LLVector4a gDebugRaycastEnd;
+
+void renderOctreeRaycast(const LLVector4a& start, const LLVector4a& end, const LLVolumeOctree* octree);
+
+void renderAssetDebug(LLViewerObject* obj, Asset* asset)
+{
+ // render debug
+ // assumes appropriate shader is already bound
+ // assumes modelview matrix is already set
+
+ gGL.pushMatrix();
+
+ // get raycast in asset space
+ LLMatrix4a agent_to_asset = obj->getAgentToGLTFAssetTransform();
+
+ LLVector4a start;
+ LLVector4a end;
+
+ agent_to_asset.affineTransform(gDebugRaycastStart, start);
+ agent_to_asset.affineTransform(gDebugRaycastEnd, end);
+
+
+ for (auto& node : asset->mNodes)
+ {
+ Mesh& mesh = asset->mMeshes[node.mMesh];
+
+ if (node.mMesh != INVALID_INDEX)
+ {
+ gGL.loadMatrix((F32*)node.mRenderMatrix.mMatrix);
+
+ // draw bounding box of mesh primitives
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
+ {
+ gGL.color3f(0.f, 1.f, 1.f);
+
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ auto* listener = (LLVolumeOctreeListener*) primitive.mOctree->getListener(0);
+
+ LLVector4a center = listener->mBounds[0];
+ LLVector4a size = listener->mBounds[1];
+
+ drawBoxOutline(center, size);
+ }
+ }
+
+#if 0
+ 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);
+
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ if (primitive.mOctree.notNull())
+ {
+ renderOctreeRaycast(local_start, local_end, primitive.mOctree);
+ }
+ }
+
+ gGL.flush();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+#endif
+ }
+ }
+
+ gGL.popMatrix();
+}
+
+void GLTFSceneManager::renderDebug()
+{
+ if (!gPipeline.hasRenderDebugMask(
+ LLPipeline::RENDER_DEBUG_BBOXES |
+ LLPipeline::RENDER_DEBUG_RAYCAST |
+ LLPipeline::RENDER_DEBUG_NODES))
+ {
+ return;
+ }
+
+ gDebugProgram.bind();
+
+ LLGLDisable cullface(GL_CULL_FACE);
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gPipeline.disableLights();
+
+ // force update all mRenderMatrix, not just nodes with meshes
+ for (auto& obj : mObjects)
+ {
+ if (obj->isDead() || obj->mGLTFAsset == nullptr)
+ {
+ continue;
+ }
+
+ LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+
+ matMul(mat, modelview, modelview);
+
+ Asset* asset = obj->mGLTFAsset;
+
+ for (auto& node : asset->mNodes)
+ {
+ matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
+ }
+ }
+
+ for (auto& obj : mObjects)
+ {
+ if (obj->isDead() || obj->mGLTFAsset == nullptr)
+ {
+ continue;
+ }
+
+ Asset* asset = obj->mGLTFAsset;
+
+ LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+
+ matMul(mat, modelview, modelview);
+
+ renderAssetDebug(obj, asset);
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_NODES))
+ { //render node hierarchy
+
+ for (U32 i = 0; i < 2; ++i)
+ {
+ LLGLDepthTest depth(GL_TRUE, i == 0 ? GL_FALSE : GL_TRUE, i == 0 ? GL_GREATER : GL_LEQUAL);
+ LLGLState blend(GL_BLEND, i == 0 ? TRUE : FALSE);
+
+
+ gGL.pushMatrix();
+
+ for (auto& obj : mObjects)
+ {
+ if (obj->isDead() || obj->mGLTFAsset == nullptr)
+ {
+ continue;
+ }
+
+ LLMatrix4a mat = obj->getGLTFAssetToAgentTransform();
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+
+ matMul(mat, modelview, modelview);
+
+ Asset* asset = obj->mGLTFAsset;
+
+ for (auto& node : asset->mNodes)
+ {
+ // force update all mRenderMatrix, not just nodes with meshes
+ matMul(node.mAssetMatrix, modelview, node.mRenderMatrix);
+
+ gGL.loadMatrix(node.mRenderMatrix.getF32ptr());
+ // render x-axis red, y-axis green, z-axis blue
+ gGL.color4f(1.f, 0.f, 0.f, 0.5f);
+ gGL.begin(LLRender::LINES);
+ gGL.vertex3f(0.f, 0.f, 0.f);
+ gGL.vertex3f(1.f, 0.f, 0.f);
+ gGL.end();
+ gGL.flush();
+
+ gGL.color4f(0.f, 1.f, 0.f, 0.5f);
+ gGL.begin(LLRender::LINES);
+ gGL.vertex3f(0.f, 0.f, 0.f);
+ gGL.vertex3f(0.f, 1.f, 0.f);
+ gGL.end();
+ gGL.flush();
+
+ gGL.begin(LLRender::LINES);
+ gGL.color4f(0.f, 0.f, 1.f, 0.5f);
+ gGL.vertex3f(0.f, 0.f, 0.f);
+ gGL.vertex3f(0.f, 0.f, 1.f);
+ gGL.end();
+ gGL.flush();
+
+ // render path to child nodes cyan
+ gGL.color4f(0.f, 1.f, 1.f, 0.5f);
+ gGL.begin(LLRender::LINES);
+ for (auto& child_idx : node.mChildren)
+ {
+ Node& child = asset->mNodes[child_idx];
+ gGL.vertex3f(0.f, 0.f, 0.f);
+ gGL.vertex3fv(child.mMatrix.getTranslation().getF32ptr());
+ }
+ gGL.end();
+ gGL.flush();
+ }
+ }
+
+ gGL.popMatrix();
+ }
+
+ }
+
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
+ {
+ S32 node_hit = -1;
+ S32 primitive_hit = -1;
+ LLVector4a intersection;
+
+ LLDrawable* drawable = lineSegmentIntersect(gDebugRaycastStart, gDebugRaycastEnd, TRUE, TRUE, TRUE, TRUE, &node_hit, &primitive_hit, &intersection, nullptr, nullptr, nullptr);
+
+ if (drawable)
+ {
+ gGL.pushMatrix();
+ Asset* asset = drawable->getVObj()->mGLTFAsset;
+ Node* node = &asset->mNodes[node_hit];
+ Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit];
+
+ gGL.flush();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ gGL.color3f(1, 0, 1);
+ drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f));
+
+ gGL.loadMatrix((F32*) node->mRenderMatrix.mMatrix);
+
+
+
+ auto* listener = (LLVolumeOctreeListener*) primitive->mOctree->getListener(0);
+ drawBoxOutline(listener->mBounds[0], listener->mBounds[1]);
+
+ gGL.flush();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ gGL.popMatrix();
+ }
+ }
+ gDebugProgram.unbind();
+}
+
diff --git a/indra/newview/gltfscenemanager.h b/indra/newview/gltfscenemanager.h
new file mode 100644
index 0000000000..d286f335e4
--- /dev/null
+++ b/indra/newview/gltfscenemanager.h
@@ -0,0 +1,69 @@
+#pragma once
+
+/**
+ * @file gltfscenemanager.h
+ * @brief Builds menus out of items.
+ *
+ * $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 "llsingleton.h"
+#include "llviewerobject.h"
+
+namespace LL
+{
+ class GLTFSceneManager : public LLSimpleton<GLTFSceneManager>
+ {
+ public:
+ ~GLTFSceneManager();
+ // load GLTF file from disk
+
+ void load(); // open filepicker to choose asset
+ void load(const std::string& filename); // load asset from filename
+
+ void update();
+ void render(bool opaque, bool rigged = false);
+ void renderOpaque();
+ void renderAlpha();
+
+ LLDrawable* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ BOOL pick_transparent,
+ BOOL pick_rigged,
+ BOOL pick_unselectable,
+ BOOL pick_reflection_probe,
+ S32* node_hit, // return the index of the node that was hit
+ S32* primitive_hit, // return the index of the primitive that was hit
+ LLVector4a* intersection, // return the intersection point
+ LLVector2* tex_coord, // return the texture coordinates of the intersection point
+ 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,
+ LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
+
+ void renderDebug();
+
+ std::vector<LLPointer<LLViewerObject>> mObjects;
+ };
+}
+
+
diff --git a/indra/newview/llagentbenefits.cpp b/indra/newview/llagentbenefits.cpp
index 2d219735a0..0717d92ea7 100644
--- a/indra/newview/llagentbenefits.cpp
+++ b/indra/newview/llagentbenefits.cpp
@@ -25,6 +25,7 @@
#include "llviewerprecompiledheaders.h"
#include "llagentbenefits.h"
+#include "llviewertexture.h"
LLAgentBenefits::LLAgentBenefits():
m_initalized(false),
@@ -95,6 +96,26 @@ bool LLAgentBenefits::init(const LLSD& benefits_sd)
return false;
}
+ if (benefits_sd.has("large_texture_upload_cost"))
+ {
+ LLSD large_texture_cost = benefits_sd.get("large_texture_upload_cost");
+ if (large_texture_cost.isArray())
+ {
+ LLSD::array_const_iterator end = large_texture_cost.endArray();
+ LLSD::array_const_iterator it = large_texture_cost.beginArray();
+ for (; it != end; ++it)
+ {
+ m_2k_texture_upload_cost.push_back(it->asInteger());
+ }
+ std::sort(m_2k_texture_upload_cost.begin(), m_2k_texture_upload_cost.end());
+ }
+ }
+
+ if (m_2k_texture_upload_cost.empty())
+ {
+ m_2k_texture_upload_cost.push_back(m_texture_upload_cost);
+ }
+
// FIXME PREMIUM - either use this field or get rid of it
m_initalized = true;
return true;
@@ -140,12 +161,55 @@ S32 LLAgentBenefits::getTextureUploadCost() const
return m_texture_upload_cost;
}
+S32 LLAgentBenefits::getTextureUploadCost(const LLViewerTexture* tex) const
+{
+ if (tex)
+ {
+ S32 area = tex->getFullHeight() * tex->getFullWidth();
+ if (area >= MIN_2K_TEXTURE_AREA)
+ {
+ return get2KTextureUploadCost(area);
+ }
+ else
+ {
+ return getTextureUploadCost();
+ }
+ }
+ return getTextureUploadCost();
+}
+
+S32 LLAgentBenefits::getTextureUploadCost(const LLImageBase* tex) const
+{
+ if (tex)
+ {
+ S32 area = tex->getHeight() * tex->getWidth();
+ if (area >= MIN_2K_TEXTURE_AREA)
+ {
+ return get2KTextureUploadCost(area);
+ }
+ else
+ {
+ return getTextureUploadCost();
+ }
+ }
+ return getTextureUploadCost();
+}
+
+S32 LLAgentBenefits::get2KTextureUploadCost(S32 area) const
+{
+ if (m_2k_texture_upload_cost.empty())
+ {
+ return m_texture_upload_cost;
+ }
+ return m_2k_texture_upload_cost[0];
+}
+
bool LLAgentBenefits::findUploadCost(LLAssetType::EType& asset_type, S32& cost) const
{
bool succ = false;
if (asset_type == LLAssetType::AT_TEXTURE)
{
- cost = getTextureUploadCost();
+ cost = getTextureUploadCost();
succ = true;
}
else if (asset_type == LLAssetType::AT_SOUND)
diff --git a/indra/newview/llagentbenefits.h b/indra/newview/llagentbenefits.h
index 48aa6bd869..962d0f9371 100644
--- a/indra/newview/llagentbenefits.h
+++ b/indra/newview/llagentbenefits.h
@@ -30,9 +30,14 @@
#include "llsd.h"
#include "llassettype.h"
+class LLViewerTexture;
+class LLImageBase;
+
class LLAgentBenefits
{
public:
+ static constexpr S32 MIN_2K_TEXTURE_AREA = 1024 * 1024 + 1;
+
LLAgentBenefits();
~LLAgentBenefits();
LOG_CLASS(LLAgentBenefits);
@@ -47,6 +52,9 @@ public:
S32 getPicksLimit() const;
S32 getSoundUploadCost() const;
S32 getTextureUploadCost() const;
+ S32 getTextureUploadCost(const LLViewerTexture* tex) const;
+ S32 getTextureUploadCost(const LLImageBase* tex) const;
+ S32 get2KTextureUploadCost(S32 area) const;
bool findUploadCost(LLAssetType::EType& asset_type, S32& cost) const;
@@ -59,6 +67,7 @@ private:
S32 m_picks_limit;
S32 m_sound_upload_cost;
S32 m_texture_upload_cost;
+ std::vector<S32> m_2k_texture_upload_cost;
bool m_initalized;
};
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 0d0d6e7e46..1764acfc67 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -2525,7 +2525,7 @@ void LLAgentCamera::setFocusGlobal(const LLPickInfo& pick)
{
LLViewerObject* objectp = gObjectList.findObject(pick.mObjectID);
- if (objectp)
+ if (objectp && pick.mGLTFNodeIndex == -1)
{
// focus on object plus designated offset
// which may or may not be same as pick.mPosGlobal
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index d6a4c41497..90f9b38f2d 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -228,6 +228,7 @@
#include "pipeline.h"
#include "llgesturemgr.h"
#include "llsky.h"
+#include "llvlcomposition.h"
#include "llvlmanager.h"
#include "llviewercamera.h"
#include "lldrawpoolbump.h"
@@ -238,6 +239,8 @@
#include "llavatariconctrl.h"
#include "llgroupiconctrl.h"
#include "llviewerassetstats.h"
+#include "gltfscenemanager.h"
+
#include "workqueue.h"
using namespace LL;
@@ -1278,6 +1281,8 @@ bool LLAppViewer::init()
LLWorld::createInstance();
LLSelectMgr::createInstance();
LLViewerCamera::createInstance();
+ LL::GLTFSceneManager::createInstance();
+
#if LL_WINDOWS
if (!mSecondInstance)
@@ -2153,7 +2158,7 @@ bool LLAppViewer::cleanup()
ll_close_fail_log();
LLError::LLCallStacks::cleanup();
-
+ LL::GLTFSceneManager::deleteSingleton();
LLEnvironment::deleteSingleton();
LLSelectMgr::deleteSingleton();
LLViewerEventRecorder::deleteSingleton();
@@ -4858,6 +4863,7 @@ void LLAppViewer::idle()
if (!(logoutRequestSent() && hasSavedFinalSnapshot()))
{
gObjectList.update(gAgent);
+ LL::GLTFSceneManager::instance().update();
}
}
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
index 970e8c8b2a..fc182fc2e2 100644
--- a/indra/newview/lldrawable.h
+++ b/indra/newview/lldrawable.h
@@ -286,6 +286,7 @@ public:
ANIMATED_CHILD = 0x01000000,
ACTIVE_CHILD = 0x02000000,
FOR_UNLOAD = 0x04000000, //should be unload from memory
+ MIRROR = 0x08000000, // Used as a mirror, needs a hero probe position to be calculated.
} EDrawableFlags;
public:
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index 50210b06c4..556760632a 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -567,14 +567,19 @@ void LLRenderPass::pushRiggedMaskBatches(U32 type, bool texture, bool batch_text
void LLRenderPass::applyModelMatrix(const LLDrawInfo& params)
{
- if (params.mModelMatrix != gGLLastMatrix)
+ applyModelMatrix(params.mModelMatrix);
+}
+
+void LLRenderPass::applyModelMatrix(const LLMatrix4* model_matrix)
+{
+ if (model_matrix != gGLLastMatrix)
{
- gGLLastMatrix = params.mModelMatrix;
+ gGLLastMatrix = model_matrix;
gGL.matrixMode(LLRender::MM_MODELVIEW);
gGL.loadMatrix(gGLModelView);
- if (params.mModelMatrix)
+ if (model_matrix)
{
- gGL.multMatrix((GLfloat*) params.mModelMatrix->mMatrix);
+ gGL.multMatrix((GLfloat*) model_matrix->mMatrix);
}
gPipeline.mMatrixOpCount++;
}
@@ -746,6 +751,7 @@ void LLRenderPass::pushUntexturedGLTFBatches(U32 type)
}
}
+// static
void LLRenderPass::pushGLTFBatch(LLDrawInfo& params)
{
auto& mat = params.mGLTFMaterial;
@@ -764,6 +770,7 @@ void LLRenderPass::pushGLTFBatch(LLDrawInfo& params)
teardown_texture_matrix(params);
}
+// static
void LLRenderPass::pushUntexturedGLTFBatch(LLDrawInfo& params)
{
auto& mat = params.mGLTFMaterial;
@@ -825,6 +832,7 @@ void LLRenderPass::pushUntexturedRiggedGLTFBatches(U32 type)
}
+// static
void LLRenderPass::pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId)
{
if (params.mAvatar.notNull() && (lastAvatar != params.mAvatar || lastMeshId != params.mSkinInfo->mHash))
@@ -837,6 +845,7 @@ void LLRenderPass::pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvat
pushGLTFBatch(params);
}
+// static
void LLRenderPass::pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId)
{
if (params.mAvatar.notNull() && (lastAvatar != params.mAvatar || lastMeshId != params.mSkinInfo->mHash))
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
index 0925a01439..c69f386c6d 100644
--- a/indra/newview/lldrawpool.h
+++ b/indra/newview/lldrawpool.h
@@ -58,9 +58,9 @@ public:
POOL_SIMPLE,
POOL_FULLBRIGHT,
POOL_BUMP,
- POOL_TERRAIN,
POOL_MATERIALS,
POOL_GLTF_PBR,
+ POOL_TERRAIN,
POOL_GRASS,
POOL_GLTF_PBR_ALPHA_MASK,
POOL_TREE,
@@ -349,8 +349,8 @@ public:
void resetDrawOrders() { }
static void applyModelMatrix(const LLDrawInfo& params);
- // Use before a non-GLTF batch if it is interleaved with GLTF batches that share the same shader
- static void resetGLTFTextureTransform();
+ // For rendering that doesn't use LLDrawInfo for some reason
+ static void applyModelMatrix(const LLMatrix4* model_matrix);
void pushBatches(U32 type, bool texture = true, bool batch_textures = false);
void pushUntexturedBatches(U32 type);
@@ -374,10 +374,10 @@ public:
void pushUntexturedRiggedGLTFBatches(U32 type);
// push a single GLTF draw call
- void pushGLTFBatch(LLDrawInfo& params);
- void pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId);
- void pushUntexturedGLTFBatch(LLDrawInfo& params);
- void pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId);
+ static void pushGLTFBatch(LLDrawInfo& params);
+ static void pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId);
+ static void pushUntexturedGLTFBatch(LLDrawInfo& params);
+ static void pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId);
void pushMaskBatches(U32 type, bool texture = true, bool batch_textures = false);
void pushRiggedMaskBatches(U32 type, bool texture = true, bool batch_textures = false);
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 41dc95a8cb..fd27010877 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -49,6 +49,7 @@
#include "llspatialpartition.h"
#include "llglcommonfunc.h"
#include "llvoavatar.h"
+#include "gltfscenemanager.h"
#include "llenvironment.h"
@@ -260,6 +261,15 @@ void LLDrawPoolAlpha::forwardRender(bool rigged)
mAlphaDFactor = LLRender::BF_ONE_MINUS_SOURCE_ALPHA; // }
gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
+ 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);
+ }
+
// If the face is more than 90% transparent, then don't update the Depth buffer for Dof
// We don't want the nearly invisible objects to cause of DoF effects
renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, false, rigged);
@@ -468,7 +478,7 @@ bool LLDrawPoolAlpha::TexSetup(LLDrawInfo* draw, bool use_material)
}
}
}
-
+
return tex_setup;
}
@@ -658,7 +668,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged)
LLSpatialGroup::drawmap_elem_t& draw_info = rigged ? group->mDrawMap[LLRenderPass::PASS_ALPHA_RIGGED] : group->mDrawMap[LLRenderPass::PASS_ALPHA];
- for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
+ for (LLSpatialGroup::drawmap_elem_t::iterator k = draw_info.begin(); k != draw_info.end(); ++k)
{
LLDrawInfo& params = **k;
if ((bool)params.mAvatar != rigged)
@@ -804,9 +814,10 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged)
current_shader->setMinimumAlpha(0.f);
reset_minimum_alpha = true;
}
-
+
params.mVertexBuffer->setBuffer();
params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset);
+ stop_glerror();
if (reset_minimum_alpha)
{
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index a0ce0ef6cf..24df11c158 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -701,8 +701,7 @@ void LLBumpImageList::updateImages()
{
for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); )
{
- bump_image_map_t::iterator curiter = iter++;
- LLViewerTexture* image = curiter->second;
+ LLViewerTexture* image = iter->second;
if( image )
{
BOOL destroy = TRUE;
@@ -721,9 +720,11 @@ void LLBumpImageList::updateImages()
if( destroy )
{
//LL_INFOS() << "*** Destroying bright " << (void*)image << LL_ENDL;
- mBrightnessEntries.erase(curiter); // deletes the image thanks to reference counting
+ iter = mBrightnessEntries.erase(iter); // deletes the image thanks to reference counting
+ continue;
}
}
+ ++iter;
}
for (bump_image_map_t::iterator iter = mDarknessEntries.begin(); iter != mDarknessEntries.end(); )
diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp
index 86b790e2c5..a32b6b1687 100644
--- a/indra/newview/lldrawpoolpbropaque.cpp
+++ b/indra/newview/lldrawpoolpbropaque.cpp
@@ -30,6 +30,7 @@
#include "lldrawpoolpbropaque.h"
#include "llviewershadermgr.h"
#include "pipeline.h"
+#include "gltfscenemanager.h"
LLDrawPoolGLTFPBR::LLDrawPoolGLTFPBR(U32 type) :
LLRenderPass(type)
@@ -54,9 +55,13 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass)
llassert(!LLPipeline::sRenderingHUDs);
gDeferredPBROpaqueProgram.bind();
+
+ LL::GLTFSceneManager::instance().renderOpaque();
pushGLTFBatches(mRenderType);
+
gDeferredPBROpaqueProgram.bind(true);
+ LL::GLTFSceneManager::instance().render(true, true);
pushRiggedGLTFBatches(mRenderType + 1);
}
diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
index 77189dceae..c5932a6ad9 100644
--- a/indra/newview/lldrawpoolterrain.cpp
+++ b/indra/newview/lldrawpoolterrain.cpp
@@ -54,8 +54,9 @@
const F32 DETAIL_SCALE = 1.f/16.f;
int DebugDetailMap = 0;
-S32 LLDrawPoolTerrain::sDetailMode = 1;
+S32 LLDrawPoolTerrain::sPBRDetailMode = 0;
F32 LLDrawPoolTerrain::sDetailScale = DETAIL_SCALE;
+F32 LLDrawPoolTerrain::sPBRDetailScale = DETAIL_SCALE;
static LLGLSLShader* sShader = NULL;
static LLTrace::BlockTimerStatHandle FTM_SHADOW_TERRAIN("Terrain Shadow");
@@ -66,7 +67,8 @@ LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerTexture *texturep) :
{
// Hack!
sDetailScale = 1.f/gSavedSettings.getF32("RenderTerrainScale");
- sDetailMode = gSavedSettings.getS32("RenderTerrainDetail");
+ sPBRDetailScale = 1.f/gSavedSettings.getF32("RenderTerrainPBRScale");
+ sPBRDetailMode = gSavedSettings.getS32("RenderTerrainPBRDetail");
mAlphaRampImagep = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD);
//gGL.getTexUnit(0)->bind(mAlphaRampImagep.get());
@@ -105,13 +107,7 @@ U32 LLDrawPoolTerrain::getVertexDataMask()
void LLDrawPoolTerrain::prerender()
{
- sDetailMode = gSavedSettings.getS32("RenderTerrainDetail");
-}
-
-//static
-S32 LLDrawPoolTerrain::getDetailMode()
-{
- return sDetailMode;
+ sPBRDetailMode = gSavedSettings.getS32("RenderTerrainPBRDetail");
}
void LLDrawPoolTerrain::boostTerrainDetailTextures()
@@ -119,21 +115,13 @@ void LLDrawPoolTerrain::boostTerrainDetailTextures()
// Hack! Get the region that this draw pool is rendering from!
LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
LLVLComposition *compp = regionp->getComposition();
- for (S32 i = 0; i < 4; i++)
- {
- compp->mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN);
- compp->mDetailTextures[i]->addTextureStats(1024.f * 1024.f);
- }
+ compp->boost();
}
void LLDrawPoolTerrain::beginDeferredPass(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_TERRAIN);
LLFacePool::beginRenderPass(pass);
-
- sShader = &gDeferredTerrainProgram;
-
- sShader->bind();
}
void LLDrawPoolTerrain::endDeferredPass(S32 pass)
@@ -204,19 +192,8 @@ void LLDrawPoolTerrain::drawLoop()
{
LLFace *facep = *iter;
- LLMatrix4* model_matrix = &(facep->getDrawable()->getRegion()->mRenderMatrix);
-
- if (model_matrix != gGLLastMatrix)
- {
- llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW);
- gGLLastMatrix = model_matrix;
- gGL.loadMatrix(gGLModelView);
- if (model_matrix)
- {
- gGL.multMatrix((GLfloat*) model_matrix->mMatrix);
- }
- gPipeline.mMatrixOpCount++;
- }
+ llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW);
+ LLRenderPass::applyModelMatrix(&facep->getDrawable()->getRegion()->mRenderMatrix);
facep->renderIndexed();
}
@@ -225,9 +202,34 @@ void LLDrawPoolTerrain::drawLoop()
void LLDrawPoolTerrain::renderFullShader()
{
+ const BOOL use_local_materials = gLocalTerrainMaterials.materialsReady(true, false);
// Hack! Get the region that this draw pool is rendering from!
LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
LLVLComposition *compp = regionp->getComposition();
+ const BOOL use_textures = !use_local_materials && (compp->getMaterialType() == LLTerrainMaterials::Type::TEXTURE);
+
+ if (use_textures)
+ {
+ // Use textures
+ sShader = &gDeferredTerrainProgram;
+ sShader->bind();
+ renderFullShaderTextures();
+ }
+ else
+ {
+ // Use materials
+ sShader = &gDeferredPBRTerrainProgram;
+ sShader->bind();
+ renderFullShaderPBR(use_local_materials);
+ }
+}
+
+void LLDrawPoolTerrain::renderFullShaderTextures()
+{
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+
LLViewerTexture *detail_texture0p = compp->mDetailTextures[0];
LLViewerTexture *detail_texture1p = compp->mDetailTextures[1];
LLViewerTexture *detail_texture2p = compp->mDetailTextures[2];
@@ -322,6 +324,236 @@ void LLDrawPoolTerrain::renderFullShader()
gGL.getTexUnit(detail0)->activate();
}
+// *TODO: Investigate use of bindFast for PBR terrain textures
+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;
+
+ constexpr U32 terrain_material_count = LLVLComposition::ASSET_COUNT;
+#ifdef SHOW_ASSERT
+ constexpr U32 shader_material_count = 1 + LLViewerShaderMgr::TERRAIN_DETAIL3_BASE_COLOR - LLViewerShaderMgr::TERRAIN_DETAIL0_BASE_COLOR;
+ llassert(shader_material_count == terrain_material_count);
+#endif
+
+ if (local_materials)
+ {
+ // Override region terrain with the global local override terrain
+ fetched_materials = &gLocalTerrainMaterials.mDetailMaterials;
+ }
+ const LLGLTFMaterial* materials[terrain_material_count];
+ for (U32 i = 0; i < terrain_material_count; ++i)
+ {
+ materials[i] = (*fetched_materials)[i].get();
+ if (!materials[i]) { materials[i] = &LLGLTFMaterial::sDefault; }
+ }
+
+ S32 detail_basecolor[terrain_material_count];
+ S32 detail_normal[terrain_material_count];
+ S32 detail_metalrough[terrain_material_count];
+ S32 detail_emissive[terrain_material_count];
+
+ for (U32 i = 0; i < terrain_material_count; ++i)
+ {
+ LLViewerTexture* detail_basecolor_texturep = nullptr;
+ LLViewerTexture* detail_normal_texturep = nullptr;
+ LLViewerTexture* detail_metalrough_texturep = nullptr;
+ LLViewerTexture* detail_emissive_texturep = nullptr;
+
+ const LLFetchedGLTFMaterial* fetched_material = (*fetched_materials)[i].get();
+ if (fetched_material)
+ {
+ detail_basecolor_texturep = fetched_material->mBaseColorTexture;
+ detail_normal_texturep = fetched_material->mNormalTexture;
+ detail_metalrough_texturep = fetched_material->mMetallicRoughnessTexture;
+ detail_emissive_texturep = fetched_material->mEmissiveTexture;
+ }
+
+ detail_basecolor[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_BASE_COLOR + i);
+ if (detail_basecolor_texturep)
+ {
+ gGL.getTexUnit(detail_basecolor[i])->bind(detail_basecolor_texturep);
+ }
+ else
+ {
+ gGL.getTexUnit(detail_basecolor[i])->bind(LLViewerFetchedTexture::sWhiteImagep);
+ }
+ gGL.getTexUnit(detail_basecolor[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP);
+ gGL.getTexUnit(detail_basecolor[i])->activate();
+
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_NORMAL)
+ {
+ detail_normal[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_NORMAL + i);
+ if (detail_normal_texturep)
+ {
+ gGL.getTexUnit(detail_normal[i])->bind(detail_normal_texturep);
+ }
+ else
+ {
+ gGL.getTexUnit(detail_normal[i])->bind(LLViewerFetchedTexture::sFlatNormalImagep);
+ }
+ gGL.getTexUnit(detail_normal[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP);
+ gGL.getTexUnit(detail_normal[i])->activate();
+ }
+
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ {
+ detail_metalrough[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_METALLIC_ROUGHNESS + i);
+ if (detail_metalrough_texturep)
+ {
+ gGL.getTexUnit(detail_metalrough[i])->bind(detail_metalrough_texturep);
+ }
+ else
+ {
+ gGL.getTexUnit(detail_metalrough[i])->bind(LLViewerFetchedTexture::sWhiteImagep);
+ }
+ gGL.getTexUnit(detail_metalrough[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP);
+ gGL.getTexUnit(detail_metalrough[i])->activate();
+ }
+
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ {
+ detail_emissive[i] = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_EMISSIVE + i);
+ if (detail_emissive_texturep)
+ {
+ gGL.getTexUnit(detail_emissive[i])->bind(detail_emissive_texturep);
+ }
+ else
+ {
+ gGL.getTexUnit(detail_emissive[i])->bind(LLViewerFetchedTexture::sWhiteImagep);
+ }
+ gGL.getTexUnit(detail_emissive[i])->setTextureAddressMode(LLTexUnit::TAM_WRAP);
+ gGL.getTexUnit(detail_emissive[i])->activate();
+ }
+ }
+
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+ llassert(shader);
+
+
+ // *TODO: Figure out why this offset is *sometimes* producing seams at the
+ // region edge, and repeat jumps when crossing regions, when
+ // RenderTerrainPBRScale is not a factor of the region scale.
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sPBRDetailScale)*sPBRDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sPBRDetailScale)*sPBRDetailScale;
+
+ LLGLTFMaterial::TextureTransform base_color_transform;
+ base_color_transform.mScale = LLVector2(sPBRDetailScale, sPBRDetailScale);
+ base_color_transform.mOffset = LLVector2(offset_x, offset_y);
+ 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);
+
+ LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
+
+ //
+ // Alpha Ramp
+ //
+ S32 alpha_ramp = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
+ gGL.getTexUnit(alpha_ramp)->bind(m2DAlphaRampImagep);
+ gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+
+ //
+ // GLTF uniforms
+ //
+
+ LLColor4 base_color_factors[terrain_material_count];
+ F32 metallic_factors[terrain_material_count];
+ F32 roughness_factors[terrain_material_count];
+ LLColor3 emissive_colors[terrain_material_count];
+ F32 minimum_alphas[terrain_material_count];
+ for (U32 i = 0; i < terrain_material_count; ++i)
+ {
+ const LLGLTFMaterial* material = materials[i];
+
+ base_color_factors[i] = material->mBaseColor;
+ metallic_factors[i] = material->mMetallicFactor;
+ roughness_factors[i] = material->mRoughnessFactor;
+ emissive_colors[i] = material->mEmissiveColor;
+ // glTF 2.0 Specification 3.9.4. Alpha Coverage
+ // mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK
+ // Use 0 here due to GLTF terrain blending (LLGLTFMaterial::bind uses
+ // -1 for easier debugging)
+ F32 min_alpha = -0.0f;
+ if (material->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
+ {
+ // dividing the alpha cutoff by transparency here allows the shader to compare against
+ // the alpha value of the texture without needing the transparency value
+ min_alpha = material->mAlphaCutoff/material->mBaseColor.mV[3];
+ }
+ minimum_alphas[i] = min_alpha;
+ }
+ shader->uniform4fv(LLShaderMgr::TERRAIN_BASE_COLOR_FACTORS, terrain_material_count, (F32*)base_color_factors);
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ {
+ shader->uniform4f(LLShaderMgr::TERRAIN_METALLIC_FACTORS, metallic_factors[0], metallic_factors[1], metallic_factors[2], metallic_factors[3]);
+ shader->uniform4f(LLShaderMgr::TERRAIN_ROUGHNESS_FACTORS, roughness_factors[0], roughness_factors[1], roughness_factors[2], roughness_factors[3]);
+ }
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ {
+ shader->uniform3fv(LLShaderMgr::TERRAIN_EMISSIVE_COLORS, terrain_material_count, (F32*)emissive_colors);
+ }
+ shader->uniform4f(LLShaderMgr::TERRAIN_MINIMUM_ALPHAS, minimum_alphas[0], minimum_alphas[1], minimum_alphas[2], minimum_alphas[3]);
+
+ // GL_BLEND disabled by default
+ drawLoop();
+
+ // Disable multitexture
+
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
+
+ gGL.getTexUnit(alpha_ramp)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(alpha_ramp)->disable();
+ gGL.getTexUnit(alpha_ramp)->activate();
+
+ for (U32 i = 0; i < terrain_material_count; ++i)
+ {
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_BASE_COLOR + i);
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_NORMAL)
+ {
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_NORMAL + i);
+ }
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ {
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_METALLIC_ROUGHNESS + i);
+ }
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ {
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0_EMISSIVE + i);
+ }
+
+ gGL.getTexUnit(detail_basecolor[i])->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(detail_basecolor[i])->disable();
+ gGL.getTexUnit(detail_basecolor[i])->activate();
+
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_NORMAL)
+ {
+ gGL.getTexUnit(detail_normal[i])->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(detail_normal[i])->disable();
+ gGL.getTexUnit(detail_normal[i])->activate();
+ }
+
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+ {
+ gGL.getTexUnit(detail_metalrough[i])->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(detail_metalrough[i])->disable();
+ gGL.getTexUnit(detail_metalrough[i])->activate();
+ }
+
+ if (sPBRDetailMode >= TERRAIN_PBR_DETAIL_EMISSIVE)
+ {
+ gGL.getTexUnit(detail_emissive[i])->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(detail_emissive[i])->disable();
+ gGL.getTexUnit(detail_emissive[i])->activate();
+ }
+ }
+}
+
void LLDrawPoolTerrain::hilightParcelOwners()
{
{ //use fullbright shader for highlighting
diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h
index 03bef31541..13f031c8e7 100644
--- a/indra/newview/lldrawpoolterrain.h
+++ b/indra/newview/lldrawpoolterrain.h
@@ -37,13 +37,12 @@ public:
{
VERTEX_DATA_MASK = LLVertexBuffer::MAP_VERTEX |
LLVertexBuffer::MAP_NORMAL |
+ LLVertexBuffer::MAP_TANGENT | // Only PBR terrain uses this currently
LLVertexBuffer::MAP_TEXCOORD0 |
LLVertexBuffer::MAP_TEXCOORD1
};
virtual U32 getVertexDataMask();
- static S32 getDetailMode();
-
LLDrawPoolTerrain(LLViewerTexture *texturep);
virtual ~LLDrawPoolTerrain();
@@ -67,8 +66,9 @@ public:
LLPointer<LLViewerTexture> m2DAlphaRampImagep;
LLPointer<LLViewerTexture> mAlphaNoiseImagep;
- static S32 sDetailMode;
- static F32 sDetailScale; // meters per texture
+ static S32 sPBRDetailMode;
+ static F32 sDetailScale; // textures per meter
+ static F32 sPBRDetailScale; // textures per meter
protected:
void boostTerrainDetailTextures();
@@ -79,6 +79,8 @@ protected:
void renderFull2TU();
void renderFull4TU();
void renderFullShader();
+ void renderFullShaderTextures();
+ void renderFullShaderPBR(BOOL local_materials = false);
void drawLoop();
private:
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
index 9dcbc48697..50c4a2c1b3 100644
--- a/indra/newview/lldrawpooltree.cpp
+++ b/indra/newview/lldrawpooltree.cpp
@@ -85,17 +85,8 @@ void LLDrawPoolTree::renderDeferred(S32 pass)
{
LLMatrix4* model_matrix = &(face->getDrawable()->getRegion()->mRenderMatrix);
- if (model_matrix != gGLLastMatrix)
- {
- gGLLastMatrix = model_matrix;
- gGL.loadMatrix(gGLModelView);
- if (model_matrix)
- {
- llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW);
- gGL.multMatrix((GLfloat*)model_matrix->mMatrix);
- }
- gPipeline.mMatrixOpCount++;
- }
+ llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW);
+ LLRenderPass::applyModelMatrix(model_matrix);
buff->setBuffer();
buff->drawRange(LLRender::TRIANGLES, 0, buff->getNumVerts() - 1, buff->getNumIndices(), 0);
diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp
index b14235f25c..68be354533 100644
--- a/indra/newview/lldrawpoolwlsky.cpp
+++ b/indra/newview/lldrawpoolwlsky.cpp
@@ -44,6 +44,7 @@
#include "llsky.h"
#include "llvowlsky.h"
#include "llsettingsvo.h"
+#include "llviewercontrol.h"
extern BOOL gCubeSnapshot;
@@ -127,6 +128,19 @@ void LLDrawPoolWLSky::renderDome(const LLVector3& camPosLocal, F32 camHeightLoca
gGL.popMatrix();
}
+extern LLPointer<LLImageGL> gEXRImage;
+
+static bool use_hdri_sky()
+{
+ static LLCachedControl<F32> hdri_split(gSavedSettings, "RenderHDRISplitScreen", 1.f);
+ static LLCachedControl<bool> irradiance_only(gSavedSettings, "RenderHDRIIrradianceOnly", false);
+
+ return gCubeSnapshot && (!irradiance_only || !gPipeline.mReflectionMapManager.isRadiancePass()) ? gEXRImage.notNull() : // always use HDRI for reflection probes when available
+ gEXRImage.notNull() ? hdri_split > 0.f : // fallback to EEP sky when split screen is zero
+ false; // no HDRI available, always use EEP sky
+
+}
+
void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 camHeightLocal) const
{
if (!gSky.mVOSkyp)
@@ -138,9 +152,34 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca
if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
{
- LLGLSPipelineDepthTestSkyBox sky(true, true);
+ if (use_hdri_sky())
+ {
+ sky_shader = &gEnvironmentMapProgram;
+ sky_shader->bind();
+ S32 idx = sky_shader->enableTexture(LLShaderMgr::ENVIRONMENT_MAP);
+ if (idx > -1)
+ {
+ gGL.getTexUnit(idx)->bind(gEXRImage);
+ }
+
+ static LLCachedControl<F32> hdri_exposure(gSavedSettings, "RenderHDRIExposure", 0.0f);
+ static LLCachedControl<F32> hdri_rotation(gSavedSettings, "RenderHDRIRotation", 0.f);
+ static LLCachedControl<F32> hdri_split(gSavedSettings, "RenderHDRISplitScreen", 1.f);
+ static LLStaticHashedString hdri_split_screen("hdri_split_screen");
- sky_shader->bind();
+ LLMatrix3 rot;
+ rot.setRot(0.f, hdri_rotation*DEG_TO_RAD, 0.f);
+
+ sky_shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, powf(2.f, hdri_exposure));
+ sky_shader->uniformMatrix3fv(LLShaderMgr::DEFERRED_ENV_MAT, 1, GL_FALSE, (F32*) rot.mMatrix);
+ sky_shader->uniform1f(hdri_split_screen, gCubeSnapshot ? 1.f : hdri_split);
+ }
+ else
+ {
+ sky_shader->bind();
+ }
+
+ LLGLSPipelineDepthTestSkyBox sky(true, true);
sky_shader->uniform1i(LLShaderMgr::CUBE_SNAPSHOT, gCubeSnapshot ? 1 : 0);
@@ -180,7 +219,7 @@ void LLDrawPoolWLSky::renderSkyHazeDeferred(const LLVector3& camPosLocal, F32 ca
void LLDrawPoolWLSky::renderStarsDeferred(const LLVector3& camPosLocal) const
{
- if (!gSky.mVOSkyp)
+ if (!gSky.mVOSkyp || use_hdri_sky())
{
return;
}
@@ -251,6 +290,11 @@ void LLDrawPoolWLSky::renderStarsDeferred(const LLVector3& camPosLocal) const
void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32 camHeightLocal, LLGLSLShader* cloudshader) const
{
+ if (use_hdri_sky())
+ {
+ return;
+ }
+
if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS) && gSky.mVOSkyp && gSky.mVOSkyp->getCloudNoiseTex())
{
LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky();
@@ -310,7 +354,7 @@ void LLDrawPoolWLSky::renderSkyCloudsDeferred(const LLVector3& camPosLocal, F32
void LLDrawPoolWLSky::renderHeavenlyBodies()
{
- if (!gSky.mVOSkyp) return;
+ if (!gSky.mVOSkyp || use_hdri_sky()) return;
LLGLSPipelineBlendSkyBox gls_skybox(true, true); // SL-14113 we need moon to write to depth to clip stars behind
@@ -438,8 +482,6 @@ void LLDrawPoolWLSky::renderDeferred(S32 pass)
const F32 camHeightLocal = LLEnvironment::instance().getCamHeight();
- gGL.setColorMask(true, false);
-
LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin();
if (gPipeline.canUseWindLightShaders())
@@ -456,7 +498,6 @@ void LLDrawPoolWLSky::renderDeferred(S32 pass)
renderSkyCloudsDeferred(origin, camHeightLocal, cloud_shader);
}
}
- gGL.setColorMask(true, true);
}
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index 425acd3392..24818241ab 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -118,6 +118,8 @@ BOOL LLViewerDynamicTexture::render()
//-----------------------------------------------------------------------------
void LLViewerDynamicTexture::preRender(BOOL clear_depth)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
//use the bottom left corner
mOrigin.set(0, 0);
@@ -181,19 +183,26 @@ void LLViewerDynamicTexture::postRender(BOOL success)
//-----------------------------------------------------------------------------
BOOL LLViewerDynamicTexture::updateAllInstances()
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
sNumRenders = 0;
if (gGLManager.mIsDisabled)
{
return TRUE;
}
- bool use_fbo = gPipeline.mBake.isComplete() && !gGLManager.mIsAMD;
+ LLRenderTarget& bake_target = gPipeline.mAuxillaryRT.deferredScreen;
- if (use_fbo)
- {
- gPipeline.mBake.bindTarget();
- gPipeline.mBake.clear();
- }
+ if (!bake_target.isComplete())
+ {
+ llassert(false);
+ return FALSE;
+ }
+ llassert(bake_target.getWidth() >= LLPipeline::MAX_BAKE_WIDTH);
+ llassert(bake_target.getHeight() >= LLPipeline::MAX_BAKE_WIDTH);
+
+ bake_target.bindTarget();
+ bake_target.clear();
LLGLSLShader::unbind();
LLVertexBuffer::unbind();
@@ -208,11 +217,13 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
LLViewerDynamicTexture *dynamicTexture = *iter;
if (dynamicTexture->needsRender())
{
+ llassert(dynamicTexture->getFullWidth() <= LLPipeline::MAX_BAKE_WIDTH);
+ llassert(dynamicTexture->getFullHeight() <= LLPipeline::MAX_BAKE_WIDTH);
+
glClear(GL_DEPTH_BUFFER_BIT);
- gDepthDirty = TRUE;
gGL.color4f(1,1,1,1);
- dynamicTexture->setBoundTarget(use_fbo ? &gPipeline.mBake : nullptr);
+ dynamicTexture->setBoundTarget(&bake_target);
dynamicTexture->preRender(); // Must be called outside of startRender()
result = FALSE;
if (dynamicTexture->render())
@@ -229,10 +240,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
}
}
- if (use_fbo)
- {
- gPipeline.mBake.flush();
- }
+ bake_target.flush();
gGL.flush();
diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp
index 60c2682078..0b535e15b0 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -1675,8 +1675,6 @@ void LLEnvironment::update(const LLViewerCamera * cam)
updateSettingsUniforms();
- // *TODO: potential optimization - this block may only need to be
- // executed some of the time. For example for water shaders only.
{
LLViewerShaderMgr::shader_iter shaders_iter, end_shaders;
end_shaders = LLViewerShaderMgr::instance()->endShaders();
@@ -1687,6 +1685,10 @@ void LLEnvironment::update(const LLViewerCamera * cam)
|| shaders_iter->mShaderGroup == LLGLSLShader::SG_WATER))
{
shaders_iter->mUniformsDirty = TRUE;
+ if (shaders_iter->mRiggedVariant)
+ {
+ shaders_iter->mRiggedVariant->mUniformsDirty = TRUE;
+ }
}
}
}
@@ -1768,8 +1770,10 @@ void LLEnvironment::updateGLVariablesForSettings(LLShaderUniforms* uniforms, con
case LLSD::TypeArray:
{
LLVector4 vect4(value);
+ // always identify as a radiance pass if desaturating irradiance is disabled
+ static LLCachedControl<bool> desaturate_irradiance(gSavedSettings, "RenderDesaturateIrradiance", true);
- if (gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass())
+ if (desaturate_irradiance && gCubeSnapshot && !gPipeline.mReflectionMapManager.isRadiancePass())
{ // maximize and remove tinting if this is an irradiance map render pass and the parameter feeds into the sky background color
auto max_vec = [](LLVector4 col)
{
@@ -2964,7 +2968,7 @@ void LLEnvironment::DayTransition::animate()
// pause probe updates and reset reflection maps on sky change
- gPipeline.mReflectionMapManager.pause();
+ gPipeline.mReflectionMapManager.pause(mTransitionTime);
gPipeline.mReflectionMapManager.reset();
mSky = mStartSky->buildClone();
@@ -3567,7 +3571,7 @@ namespace
mInjectedSky->setSource(target_sky);
// clear reflection probes and pause updates during sky change
- gPipeline.mReflectionMapManager.pause();
+ gPipeline.mReflectionMapManager.pause(transition);
gPipeline.mReflectionMapManager.reset();
mBlenderSky = std::make_shared<LLSettingsBlenderTimeDelta>(target_sky, start_sky, psky, transition);
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index c1776705f9..ccee57cf69 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -1855,7 +1855,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
S32* vp = (S32*) &val;
*vp = index;
- llassert(index <= LLGLSLShader::sIndexedTextureChannels-1);
+ llassert(index < LLGLSLShader::sIndexedTextureChannels);
LLVector4Logical mask;
mask.clear();
@@ -1889,7 +1889,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
F32* normals = (F32*) norm.get();
LLVector4a* src = vf.mNormals;
LLVector4a* end = src+num_vertices;
-
+
while (src < end)
{
LLVector4a normal;
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index f482d5a37d..9fff505c2a 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -664,6 +664,10 @@ void LLFeatureManager::applyBaseMasks()
{
maskFeatures("TexUnit8orLess");
}
+ if (gGLManager.mNumTextureImageUnits <= 16)
+ {
+ maskFeatures("TexUnit16orLess");
+ }
if (gGLManager.mVRAM > 512)
{
maskFeatures("VRAMGT512");
diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp
index 46b9dffae9..1dd1dbabbe 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -34,11 +34,12 @@
#include "llshadermgr.h"
#include "pipeline.h"
+//static
+LLFetchedGLTFMaterial LLFetchedGLTFMaterial::sDefault;
+
LLFetchedGLTFMaterial::LLFetchedGLTFMaterial()
: LLGLTFMaterial()
, mExpectedFlusTime(0.f)
- , mActive(true)
- , mFetching(false)
{
}
@@ -78,7 +79,14 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
{
// dividing the alpha cutoff by transparency here allows the shader to compare against
// the alpha value of the texture without needing the transparency value
- min_alpha = mAlphaCutoff/mBaseColor.mV[3];
+ if (mBaseColor.mV[3] > 0.f)
+ {
+ min_alpha = mAlphaCutoff / mBaseColor.mV[3];
+ }
+ else
+ {
+ min_alpha = 1024.f;
+ }
}
shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha);
}
@@ -242,10 +250,11 @@ void LLFetchedGLTFMaterial::onMaterialComplete(std::function<void()> material_co
materialCompleteCallbacks.push_back(material_complete);
}
-void LLFetchedGLTFMaterial::materialComplete()
+void LLFetchedGLTFMaterial::materialComplete(bool success)
{
llassert(mFetching);
mFetching = false;
+ mFetchSuccess = success;
for (std::function<void()> material_complete : materialCompleteCallbacks)
{
@@ -254,55 +263,3 @@ void LLFetchedGLTFMaterial::materialComplete()
materialCompleteCallbacks.clear();
materialCompleteCallbacks.shrink_to_fit();
}
-
-LLPointer<LLViewerFetchedTexture> LLFetchedGLTFMaterial::getUITexture()
-{
- if (mFetching)
- {
- return nullptr;
- }
-
- auto fetch_texture_for_ui = [](LLPointer<LLViewerFetchedTexture>& img, const LLUUID& id)
- {
- if (id.notNull())
- {
- if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id))
- {
- LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
- if (obj)
- {
- LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(id);
- img = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL;
- }
-
- }
- else
- {
- img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- }
- }
- if (img)
- {
- img->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
- img->forceToSaveRawImage(0);
- }
- };
-
- fetch_texture_for_ui(mBaseColorTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]);
- fetch_texture_for_ui(mNormalTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]);
- fetch_texture_for_ui(mMetallicRoughnessTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]);
- fetch_texture_for_ui(mEmissiveTexture, mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]);
-
- if ((mBaseColorTexture && (mBaseColorTexture->getRawImageLevel() != 0)) ||
- (mNormalTexture && (mNormalTexture->getRawImageLevel() != 0)) ||
- (mMetallicRoughnessTexture && (mMetallicRoughnessTexture->getRawImageLevel() != 0)) ||
- (mEmissiveTexture && (mEmissiveTexture->getRawImageLevel() != 0)))
- {
- return nullptr;
- }
-
- // *HACK: Use one of the PBR texture components as the preview texture for now
- mPreviewTexture = mBaseColorTexture;
-
- return mPreviewTexture;
-}
diff --git a/indra/newview/llfetchedgltfmaterial.h b/indra/newview/llfetchedgltfmaterial.h
index a9e539633d..7550c75b45 100644
--- a/indra/newview/llfetchedgltfmaterial.h
+++ b/indra/newview/llfetchedgltfmaterial.h
@@ -40,6 +40,8 @@ public:
virtual ~LLFetchedGLTFMaterial();
LLFetchedGLTFMaterial& operator=(const LLFetchedGLTFMaterial& rhs);
+ // LLGLTFMaterial::operator== is defined, but LLFetchedGLTFMaterial::operator== is not.
+ bool operator==(const LLGLTFMaterial& rhs) const = delete;
// If this material is loaded, fire the given function
void onMaterialComplete(std::function<void()> material_complete);
@@ -49,8 +51,7 @@ public:
void bind(LLViewerTexture* media_tex = nullptr);
bool isFetching() const { return mFetching; }
-
- LLPointer<LLViewerFetchedTexture> getUITexture();
+ bool isLoaded() const { return !mFetching && mFetchSuccess; }
void addTextureEntry(LLTextureEntry* te) override;
void removeTextureEntry(LLTextureEntry* te) override;
@@ -65,18 +66,18 @@ public:
std::set<LLTextureEntry*> mTextureEntires;
- // Texture used for previewing the material in the UI
- LLPointer<LLViewerFetchedTexture> mPreviewTexture;
-
+ // default material for when assets don't have one
+ static LLFetchedGLTFMaterial sDefault;
protected:
// Lifetime management
void materialBegin();
- void materialComplete();
+ void materialComplete(bool success);
F64 mExpectedFlusTime; // since epoch in seconds
- bool mActive;
- bool mFetching;
+ bool mActive = true;
+ bool mFetching = false;
+ bool mFetchSuccess = false;
std::vector<std::function<void()>> materialCompleteCallbacks;
};
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 4ad136e13a..b82172c506 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -61,6 +61,7 @@ LLFilePicker LLFilePicker::sInstance;
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
+#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
#define SCRIPT_FILTER L"Script files (*.lsl)\0*.lsl\0"
#define DICTIONARY_FILTER L"Dictionary files (*.dic; *.xcu)\0*.dic;*.xcu\0"
@@ -228,6 +229,10 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter)
IMAGE_FILTER \
L"\0";
break;
+ case FFLOAD_HDRI:
+ mOFN.lpstrFilter = HDRI_FILTER \
+ L"\0";
+ break;
case FFLOAD_SCRIPT:
mOFN.lpstrFilter = SCRIPT_FILTER \
L"\0";
@@ -663,6 +668,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
allowedv->push_back("gltf");
allowedv->push_back("glb");
break;
+ case FFLOAD_HDRI:
+ allowedv->push_back("exr");
case FFLOAD_COLLADA:
allowedv->push_back("dae");
break;
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 38daff9937..891c0c0482 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -89,6 +89,7 @@ public:
FFLOAD_EXE = 14, // Note: EXE will be treated as ALL on Windows and Linux but not on Darwin
FFLOAD_MATERIAL = 15,
FFLOAD_MATERIAL_TEXTURE = 16,
+ FFLOAD_HDRI = 17,
};
enum ESaveFilter
diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp
index ba0f97e2e1..2a5106f79e 100644
--- a/indra/newview/llfloaterimagepreview.cpp
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -34,6 +34,7 @@
#include "llimagepng.h"
#include "llagent.h"
+#include "llagentbenefits.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
#include "llcombobox.h"
@@ -144,6 +145,15 @@ BOOL LLFloaterImagePreview::postBuild()
return TRUE;
}
+
+//-----------------------------------------------------------------------------
+// getExpectedUploadCost()
+//-----------------------------------------------------------------------------
+S32 LLFloaterImagePreview::getExpectedUploadCost() const
+{
+ return LLAgentBenefitsMgr::current().getTextureUploadCost(mRawImagep);
+}
+
//-----------------------------------------------------------------------------
// LLFloaterImagePreview()
//-----------------------------------------------------------------------------
@@ -378,7 +388,7 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
return false;
}
- raw_image->biasedScaleToPowerOfTwo(1024);
+ raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
mRawImagep = raw_image;
return true;
diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h
index d034622c35..426dd926ae 100644
--- a/indra/newview/llfloaterimagepreview.h
+++ b/indra/newview/llfloaterimagepreview.h
@@ -48,17 +48,17 @@ protected:
public:
LLImagePreviewSculpted(S32 width, S32 height);
- /*virtual*/ S8 getType() const ;
+ S8 getType() const override;
void setPreviewTarget(LLImageRaw *imagep, F32 distance);
void setTexture(U32 name) { mTextureName = name; }
- BOOL render();
+ BOOL render() override;
void refresh();
void rotate(F32 yaw_radians, F32 pitch_radians);
void zoom(F32 zoom_amt);
void pan(F32 right, F32 up);
- virtual BOOL needsRender() { return mNeedsUpdate; }
+ virtual BOOL needsRender() override { return mNeedsUpdate; }
protected:
BOOL mNeedsUpdate;
@@ -81,18 +81,18 @@ protected:
public:
LLImagePreviewAvatar(S32 width, S32 height);
- /*virtual*/ S8 getType() const ;
+ S8 getType() const override;
void setPreviewTarget(const std::string& joint_name, const std::string& mesh_name, LLImageRaw* imagep, F32 distance, BOOL male);
void setTexture(U32 name) { mTextureName = name; }
void clearPreviewTexture(const std::string& mesh_name);
- BOOL render();
+ BOOL render() override;
void refresh();
void rotate(F32 yaw_radians, F32 pitch_radians);
void zoom(F32 zoom_amt);
void pan(F32 right, F32 up);
- virtual BOOL needsRender() { return mNeedsUpdate; }
+ virtual BOOL needsRender() override { return mNeedsUpdate; }
protected:
BOOL mNeedsUpdate;
@@ -113,12 +113,14 @@ public:
LLFloaterImagePreview(const std::string& filename);
virtual ~LLFloaterImagePreview();
- virtual BOOL postBuild();
+ BOOL postBuild() override;
+
+ S32 getExpectedUploadCost() const override;
- BOOL handleMouseDown(S32 x, S32 y, MASK mask);
- BOOL handleMouseUp(S32 x, S32 y, MASK mask);
- BOOL handleHover(S32 x, S32 y, MASK mask);
- BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override;
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask) override;
+ BOOL handleHover(S32 x, S32 y, MASK mask) override;
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) override;
static void onMouseCaptureLostImagePreview(LLMouseHandler*);
@@ -126,7 +128,7 @@ public:
protected:
static void onPreviewTypeCommit(LLUICtrl*,void*);
- void draw();
+ void draw() override;
bool loadImage(const std::string& filename);
LLPointer<LLImageRaw> mRawImagep;
diff --git a/indra/newview/llfloaternamedesc.h b/indra/newview/llfloaternamedesc.h
index 589f470e82..cf3b512fab 100644
--- a/indra/newview/llfloaternamedesc.h
+++ b/indra/newview/llfloaternamedesc.h
@@ -47,7 +47,7 @@ public:
void onBtnCancel();
void doCommit();
- S32 getExpectedUploadCost() const;
+ virtual S32 getExpectedUploadCost() const;
protected:
virtual void onCommit();
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 187ac9d323..9ce7a07ee4 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -62,6 +62,7 @@
#include "llfloaterreg.h"
#include "llfloaterregiondebugconsole.h"
#include "llfloatertelehub.h"
+#include "llgltfmateriallist.h"
#include "llinventorymodel.h"
#include "lllineeditor.h"
#include "llnamelistctrl.h"
@@ -86,7 +87,6 @@
#include "llviewerstats.h"
#include "llviewertexteditor.h"
#include "llviewerwindow.h"
-#include "llvlcomposition.h"
#include "lltrans.h"
#include "llagentui.h"
#include "llmeshrepository.h"
@@ -100,7 +100,6 @@
#include "llavatarnamecache.h"
#include "llenvironment.h"
-const S32 TERRAIN_TEXTURE_COUNT = 4;
const S32 CORNER_COUNT = 4;
const U32 MAX_LISTED_NAMES = 100;
@@ -340,7 +339,6 @@ void LLFloaterRegionInfo::onRegionChanged()
}
}
-// static
void LLFloaterRegionInfo::requestRegionInfo()
{
LLTabContainer* tab = findChild<LLTabContainer>("region_panels");
@@ -605,6 +603,16 @@ LLPanelRegionEnvironment* LLFloaterRegionInfo::getPanelEnvironment()
return panel;
}
+LLTerrainMaterials::Type material_type_from_ctrl(LLCheckBoxCtrl* ctrl)
+{
+ return ctrl->get() ? LLTerrainMaterials::Type::PBR : LLTerrainMaterials::Type::TEXTURE;
+}
+
+void material_type_to_ctrl(LLCheckBoxCtrl* ctrl, LLTerrainMaterials::Type new_type)
+{
+ ctrl->set(new_type == LLTerrainMaterials::Type::PBR);
+}
+
// static
LLPanelRegionTerrainInfo* LLFloaterRegionInfo::getPanelRegionTerrain()
{
@@ -1305,18 +1313,25 @@ void LLPanelRegionDebugInfo::onClickDebugConsole(void* data)
LLFloaterReg::showInstance("region_debug_console");
}
-BOOL LLPanelRegionTerrainInfo::validateTextureSizes()
+bool LLPanelRegionTerrainInfo::validateTextureSizes()
{
- static const S32 MAX_TERRAIN_TEXTURE_SIZE = 1024;
- for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ if (mMaterialTypeCtrl)
+ {
+ const LLTerrainMaterials::Type material_type = material_type_from_ctrl(mMaterialTypeCtrl);
+ const bool is_material_selected = material_type == LLTerrainMaterials::Type::PBR;
+ if (is_material_selected) { return true; }
+ }
+
+ bool valid = true;
+ static LLCachedControl<U32> max_texture_resolution(gSavedSettings, "RenderMaxTextureResolution", 2048);
+ const S32 max_terrain_texture_size = (S32)max_texture_resolution;
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
- std::string buffer;
- buffer = llformat("texture_detail_%d", i);
- LLTextureCtrl* texture_ctrl = getChild<LLTextureCtrl>(buffer);
+ LLTextureCtrl* texture_ctrl = mTextureDetailCtrl[i];
if (!texture_ctrl) continue;
LLUUID image_asset_id = texture_ctrl->getImageAssetID();
- LLViewerTexture* img = LLViewerTextureManager::getFetchedTexture(image_asset_id);
+ LLViewerFetchedTexture* img = LLViewerTextureManager::getFetchedTexture(image_asset_id);
S32 components = img->getComponents();
// Must ask for highest resolution version's width. JC
S32 width = img->getFullWidth();
@@ -1324,31 +1339,133 @@ BOOL LLPanelRegionTerrainInfo::validateTextureSizes()
//LL_INFOS() << "texture detail " << i << " is " << width << "x" << height << "x" << components << LL_ENDL;
- if (components != 3)
+ if (components != 3 && components != 4)
{
LLSD args;
args["TEXTURE_NUM"] = i+1;
args["TEXTURE_BIT_DEPTH"] = llformat("%d",components * 8);
- args["MAX_SIZE"] = MAX_TERRAIN_TEXTURE_SIZE;
+ args["MAX_SIZE"] = max_terrain_texture_size;
LLNotificationsUtil::add("InvalidTerrainBitDepth", args);
- return FALSE;
+ valid = false;
+ continue;
}
- if (width > MAX_TERRAIN_TEXTURE_SIZE || height > MAX_TERRAIN_TEXTURE_SIZE)
+ if (components == 4)
+ {
+ if (!img->hasSavedRawImage())
+ {
+ // Raw image isn't loaded yet
+ // Assume it's invalid due to presence of alpha channel
+ LLSD args;
+ args["TEXTURE_NUM"] = i+1;
+ args["TEXTURE_BIT_DEPTH"] = llformat("%d",components * 8);
+ LLNotificationsUtil::add("InvalidTerrainAlphaNotFullyLoaded", args);
+ valid = false;
+ }
+ else
+ {
+ // Slower path: Calculate alpha from raw image pixels (not needed
+ // for GLTF materials, which use alphaMode to determine
+ // transparency)
+ // Raw image is pretty much guaranteed to be saved due to the texture swatches
+ LLImageRaw* raw = img->getSavedRawImage();
+ if (raw->checkHasTransparentPixels())
+ {
+ LLSD args;
+ args["TEXTURE_NUM"] = i+1;
+ LLNotificationsUtil::add("InvalidTerrainAlpha", args);
+ valid = false;
+ }
+ LL_WARNS() << "Terrain texture image in slot " << i << " with ID " << image_asset_id << " has alpha channel, but pixels are opaque. Is alpha being optimized away in the texture uploader?" << LL_ENDL;
+ }
+ }
+
+ if (width > max_terrain_texture_size || height > max_terrain_texture_size)
{
LLSD args;
args["TEXTURE_NUM"] = i+1;
args["TEXTURE_SIZE_X"] = width;
args["TEXTURE_SIZE_Y"] = height;
- args["MAX_SIZE"] = MAX_TERRAIN_TEXTURE_SIZE;
+ args["MAX_SIZE"] = max_terrain_texture_size;
LLNotificationsUtil::add("InvalidTerrainSize", args);
- return FALSE;
-
+ valid = false;
}
}
- return TRUE;
+ return valid;
+}
+
+bool LLPanelRegionTerrainInfo::validateMaterials()
+{
+ if (mMaterialTypeCtrl)
+ {
+ const LLTerrainMaterials::Type material_type = material_type_from_ctrl(mMaterialTypeCtrl);
+ const bool is_texture_selected = material_type == LLTerrainMaterials::Type::TEXTURE;
+ if (is_texture_selected) { return true; }
+ }
+
+ // *TODO: If/when we implement additional GLTF extensions, they may not be
+ // compatible with our GLTF terrain implementation. We may want to disallow
+ // materials with some features from being set on terrain, if their
+ // implementation on terrain is not compliant with the spec:
+ // - KHR_materials_transmission: Probably OK?
+ // - KHR_materials_ior: Probably OK?
+ // - KHR_materials_volume: Likely incompatible, as our terrain
+ // heightmaps cannot currently be described as finite enclosed
+ // volumes.
+ // See also LLGLTFMaterial
+#ifdef LL_WINDOWS
+ llassert(sizeof(LLGLTFMaterial) == 232);
+#endif
+
+ bool valid = true;
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLTextureCtrl* material_ctrl = mMaterialDetailCtrl[i];
+ if (!material_ctrl) { continue; }
+
+ const LLUUID& material_asset_id = material_ctrl->getImageAssetID();
+ llassert(material_asset_id.notNull());
+ if (material_asset_id.isNull()) { return false; }
+ const LLFetchedGLTFMaterial* material = gGLTFMaterialList.getMaterial(material_asset_id);
+ if (!material->isLoaded())
+ {
+ if (material->isFetching())
+ {
+ LLSD args;
+ args["MATERIAL_NUM"] = i + 1;
+ LLNotificationsUtil::add("InvalidTerrainMaterialNotLoaded", args);
+ }
+ else // Loading failed
+ {
+ LLSD args;
+ args["MATERIAL_NUM"] = i + 1;
+ LLNotificationsUtil::add("InvalidTerrainMaterialLoadFailed", args);
+ }
+ valid = false;
+ continue;
+ }
+
+ if (material->mDoubleSided)
+ {
+ LLSD args;
+ args["MATERIAL_NUM"] = i + 1;
+ LLNotificationsUtil::add("InvalidTerrainMaterialDoubleSided", args);
+ valid = false;
+ }
+ if (material->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_OPAQUE && material->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_MASK)
+ {
+ LLSD args;
+ args["MATERIAL_NUM"] = i + 1;
+ const char* alpha_mode = material->getAlphaMode();
+ args["MATERIAL_ALPHA_MODE"] = alpha_mode;
+ LLNotificationsUtil::add("InvalidTerrainMaterialAlphaMode", args);
+ valid = false;
+ }
+ }
+
+ return valid;
}
BOOL LLPanelRegionTerrainInfo::validateTextureHeights()
@@ -1370,6 +1487,21 @@ BOOL LLPanelRegionTerrainInfo::validateTextureHeights()
/////////////////////////////////////////////////////////////////////////////
// LLPanelRegionTerrainInfo
/////////////////////////////////////////////////////////////////////////////
+
+LLPanelRegionTerrainInfo::LLPanelRegionTerrainInfo()
+: LLPanelRegionInfo()
+{
+ const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ mLastSetTextures[i] = default_textures[i];
+ }
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ mLastSetMaterials[i] = BLANK_MATERIAL_ASSET_ID;
+ }
+}
+
// Initialize statics
BOOL LLPanelRegionTerrainInfo::postBuild()
@@ -1380,11 +1512,22 @@ BOOL LLPanelRegionTerrainInfo::postBuild()
initCtrl("terrain_raise_spin");
initCtrl("terrain_lower_spin");
+ mMaterialTypeCtrl = findChild<LLCheckBoxCtrl>("terrain_material_type");
+ if (mMaterialTypeCtrl) { mMaterialTypeCtrl->setCommitCallback(boost::bind(&LLPanelRegionTerrainInfo::onSelectMaterialType, this)); }
+
std::string buffer;
- for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+
+ 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);
}
for(S32 i = 0; i < CORNER_COUNT; ++i)
@@ -1405,6 +1548,57 @@ BOOL LLPanelRegionTerrainInfo::postBuild()
return LLPanelRegionInfo::postBuild();
}
+void LLPanelRegionTerrainInfo::onSelectMaterialType()
+{
+ updateForMaterialType();
+ onChangeAnything();
+}
+
+void LLPanelRegionTerrainInfo::updateForMaterialType()
+{
+ if (!mMaterialTypeCtrl) { return; }
+ const LLTerrainMaterials::Type material_type = material_type_from_ctrl(mMaterialTypeCtrl);
+ const bool show_texture_controls = material_type == LLTerrainMaterials::Type::TEXTURE;
+ const bool show_material_controls = material_type == LLTerrainMaterials::Type::PBR;
+
+ // Toggle visibility of correct swatches
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLTextureCtrl* texture_ctrl = mTextureDetailCtrl[i];
+ if (texture_ctrl)
+ {
+ texture_ctrl->setVisible(show_texture_controls);
+ }
+ }
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLTextureCtrl* material_ctrl = mMaterialDetailCtrl[i];
+ if (material_ctrl)
+ {
+ material_ctrl->setVisible(show_material_controls);
+ }
+ }
+
+ // Toggle visibility of labels
+ LLUICtrl* texture_label = findChild<LLUICtrl>("detail_texture_text");
+ if (texture_label) { texture_label->setVisible(show_texture_controls); }
+ LLUICtrl* material_label = findChild<LLUICtrl>("detail_material_text");
+ if (material_label) { material_label->setVisible(show_material_controls); }
+
+ // Toggle visibility of documentation labels for terrain blending ranges
+ const std::vector<std::string> doc_suffixes { "5", "10", "11" };
+ std::string buffer;
+ for (const std::string& suffix : doc_suffixes)
+ {
+ buffer = "height_text_lbl" + suffix;
+ LLUICtrl* texture_doc_label = findChild<LLUICtrl>(buffer);
+ if (texture_doc_label) { texture_doc_label->setVisible(show_texture_controls); }
+ buffer += "_material";
+ LLUICtrl* material_doc_label = findChild<LLUICtrl>(buffer);
+ if (material_doc_label) { material_doc_label->setVisible(show_material_controls); }
+ }
+}
+
// virtual
bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
{
@@ -1421,21 +1615,99 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
getChild<LLUICtrl>("region_text")->setValue(LLSD(region->getName()));
LLVLComposition* compp = region->getComposition();
- LLTextureCtrl* texture_ctrl;
- std::string buffer;
- for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
- {
- buffer = llformat("texture_detail_%d", i);
- texture_ctrl = getChild<LLTextureCtrl>(buffer);
- if(texture_ctrl)
- {
- LL_DEBUGS() << "Detail Texture " << i << ": "
- << compp->getDetailTextureID(i) << LL_ENDL;
- LLUUID tmp_id(compp->getDetailTextureID(i));
- texture_ctrl->setImageAssetID(tmp_id);
- }
- }
+ 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);
+
+ bool set_texture_swatches;
+ bool set_material_swatches;
+ bool reset_texture_swatches;
+ bool reset_material_swatches;
+ LLTerrainMaterials::Type material_type;
+ if (!textures_ready && !materials_ready)
+ {
+ // Are these 4 texture IDs or 4 material IDs? Who knows! Let's set
+ // the IDs on both pickers for now.
+ material_type = LLTerrainMaterials::Type::TEXTURE;
+ set_texture_swatches = true;
+ set_material_swatches = true;
+ reset_texture_swatches = false;
+ reset_material_swatches = false;
+ }
+ else
+ {
+ material_type = compp->getMaterialType();
+ set_texture_swatches = material_type == LLTerrainMaterials::Type::TEXTURE;
+ set_material_swatches = !set_texture_swatches;
+ reset_texture_swatches = !set_texture_swatches;
+ reset_material_swatches = !set_material_swatches;
+ }
+
+ if (mMaterialTypeCtrl)
+ {
+ material_type_to_ctrl(mMaterialTypeCtrl, material_type);
+ updateForMaterialType();
+ mMaterialTypeCtrl->setVisible(feature_pbr_terrain_enabled);
+ }
+
+ if (set_texture_swatches)
+ {
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLTextureCtrl* asset_ctrl = mTextureDetailCtrl[i];
+ if(asset_ctrl)
+ {
+ LL_DEBUGS("Terrain", "Texture") << "Detail Texture " << i << ": "
+ << compp->getDetailAssetID(i) << LL_ENDL;
+ LLUUID tmp_id(compp->getDetailAssetID(i));
+ asset_ctrl->setImageAssetID(tmp_id);
+ }
+ }
+ }
+ if (set_material_swatches)
+ {
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLTextureCtrl* asset_ctrl = mMaterialDetailCtrl[i];
+ if(asset_ctrl)
+ {
+ LL_DEBUGS("Terrain", "Material") << "Detail Material " << i << ": "
+ << compp->getDetailAssetID(i) << LL_ENDL;
+ LLUUID tmp_id(compp->getDetailAssetID(i));
+ asset_ctrl->setImageAssetID(tmp_id);
+ }
+ }
+ }
+ if (reset_texture_swatches)
+ {
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LL_DEBUGS("Terrain", "Texture") << "Reset Texture swatch " << i
+ << LL_ENDL;
+ LLTextureCtrl* asset_ctrl = mTextureDetailCtrl[i];
+ if(asset_ctrl)
+ {
+ asset_ctrl->setImageAssetID(mLastSetTextures[i]);
+ }
+ }
+ }
+ if (reset_material_swatches)
+ {
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LL_DEBUGS("Terrain", "Material") << "Reset Material swatch " << i
+ << LL_ENDL;
+ LLTextureCtrl* asset_ctrl = mMaterialDetailCtrl[i];
+ if(asset_ctrl)
+ {
+ asset_ctrl->setImageAssetID(mLastSetMaterials[i]);
+ }
+ }
+ }
+
+ std::string buffer;
for(S32 i = 0; i < CORNER_COUNT; ++i)
{
buffer = llformat("height_start_spin_%d", i);
@@ -1449,6 +1721,9 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
LL_DEBUGS() << "no region set" << LL_ENDL;
getChild<LLUICtrl>("region_text")->setValue(LLSD(""));
}
+
+ // Update visibility of terrain swatches, etc
+ refresh();
getChildView("download_raw_btn")->setEnabled(owner_or_god);
getChildView("upload_raw_btn")->setEnabled(owner_or_god);
@@ -1462,21 +1737,6 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
BOOL LLPanelRegionTerrainInfo::sendUpdate()
{
LL_INFOS() << "LLPanelRegionTerrainInfo::sendUpdate" << LL_ENDL;
- std::string buffer;
- strings_t strings;
- LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
-
- // update the model
- LLRegionInfoModel& region_info = LLRegionInfoModel::instance();
- region_info.mWaterHeight = (F32) getChild<LLUICtrl>("water_height_spin")->getValue().asReal();
- region_info.mTerrainRaiseLimit = (F32) getChild<LLUICtrl>("terrain_raise_spin")->getValue().asReal();
- region_info.mTerrainLowerLimit = (F32) getChild<LLUICtrl>("terrain_lower_spin")->getValue().asReal();
-
- // and sync the region with it
- region_info.sendRegionTerrain(invoice);
-
- // =======================================
- // Assemble and send texturedetail message
// Make sure user hasn't chosen wacky textures.
if (!validateTextureSizes())
@@ -1484,6 +1744,12 @@ BOOL LLPanelRegionTerrainInfo::sendUpdate()
return FALSE;
}
+ // Prevent applying unsupported alpha blend/double-sided materials
+ if (!validateMaterials())
+ {
+ return FALSE;
+ }
+
// Check if terrain Elevation Ranges are correct
if (gSavedSettings.getBOOL("RegionCheckTextureHeights") && !validateTextureHeights())
{
@@ -1499,22 +1765,57 @@ BOOL LLPanelRegionTerrainInfo::sendUpdate()
}
}
- LLTextureCtrl* texture_ctrl;
+ std::string buffer;
+ strings_t strings;
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+
+ // update the model
+ LLRegionInfoModel& region_info = LLRegionInfoModel::instance();
+ region_info.mWaterHeight = (F32) getChild<LLUICtrl>("water_height_spin")->getValue().asReal();
+ region_info.mTerrainRaiseLimit = (F32) getChild<LLUICtrl>("terrain_raise_spin")->getValue().asReal();
+ region_info.mTerrainLowerLimit = (F32) getChild<LLUICtrl>("terrain_lower_spin")->getValue().asReal();
+
+ // and sync the region with it
+ region_info.sendRegionTerrain(invoice);
+
+ // =======================================
+ // Assemble and send texturedetail message
+
std::string id_str;
LLMessageSystem* msg = gMessageSystem;
- for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
- {
- buffer = llformat("texture_detail_%d", i);
- texture_ctrl = getChild<LLTextureCtrl>(buffer);
- if(texture_ctrl)
- {
- LLUUID tmp_id(texture_ctrl->getImageAssetID());
- tmp_id.toString(id_str);
- buffer = llformat("%d %s", i, id_str.c_str());
- strings.push_back(buffer);
- }
- }
+ // Send either material IDs instead of texture IDs depending on
+ // material_type - they both occupy the same slot.
+ const LLTerrainMaterials::Type material_type = mMaterialTypeCtrl ? material_type_from_ctrl(mMaterialTypeCtrl) : LLTerrainMaterials::Type::TEXTURE;
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLTextureCtrl* asset_ctrl;
+ if (material_type == LLTerrainMaterials::Type::PBR)
+ {
+ asset_ctrl = mMaterialDetailCtrl[i];
+ }
+ else
+ {
+ asset_ctrl = mTextureDetailCtrl[i];
+ }
+
+ if (!asset_ctrl) { continue; }
+
+ LLUUID tmp_id(asset_ctrl->getImageAssetID());
+ tmp_id.toString(id_str);
+ buffer = llformat("%d %s", i, id_str.c_str());
+ strings.push_back(buffer);
+
+ // Store asset for later terrain editing
+ if (material_type == LLTerrainMaterials::Type::PBR)
+ {
+ mLastSetMaterials[i] = tmp_id;
+ }
+ else
+ {
+ mLastSetTextures[i] = tmp_id;
+ }
+ }
sendEstateOwnerMessage(msg, "texturedetail", invoice, strings);
strings.clear();
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index 3eb39b250f..da17212016 100644
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -36,6 +36,7 @@
#include "llpanel.h"
#include "llextendedstatus.h"
#include "llpanelenvironment.h"
+#include "llvlcomposition.h"
#include "lleventcoro.h"
@@ -55,6 +56,7 @@ class LLRadioGroup;
class LLSliderCtrl;
class LLSpinCtrl;
class LLTextBox;
+class LLTextureCtrl;
class LLPanelRegionGeneralInfo;
class LLPanelRegionDebugInfo;
@@ -75,9 +77,9 @@ class LLFloaterRegionInfo : public LLFloater
public:
- /*virtual*/ void onOpen(const LLSD& key);
- /*virtual*/ void onClose(bool app_quitting);
- /*virtual*/ BOOL postBuild();
+ void onOpen(const LLSD& key) override;
+ void onClose(bool app_quitting) override;
+ BOOL postBuild() override;
static void processEstateOwnerRequest(LLMessageSystem* msg, void**);
@@ -98,7 +100,7 @@ public:
static LLPanelRegionEnvironment* getPanelEnvironment();
// from LLPanel
- virtual void refresh();
+ void refresh() override;
void onRegionChanged();
void requestRegionInfo();
@@ -144,7 +146,7 @@ public:
virtual bool refreshFromRegion(LLViewerRegion* region);
virtual bool estateUpdate(LLMessageSystem* msg) { return true; }
- virtual BOOL postBuild();
+ BOOL postBuild() override;
virtual void updateChild(LLUICtrl* child_ctrl);
void enableButton(const std::string& btn_name, BOOL enable = TRUE);
@@ -184,16 +186,15 @@ public:
: LLPanelRegionInfo() {}
~LLPanelRegionGeneralInfo() {}
- virtual bool refreshFromRegion(LLViewerRegion* region);
+ bool refreshFromRegion(LLViewerRegion* region) override;
- // LLPanel
- virtual BOOL postBuild();
+ BOOL postBuild() override;
void onBtnSet();
void setObjBonusFactor(F32 object_bonus_factor) {mObjBonusFactor = object_bonus_factor;}
protected:
- virtual BOOL sendUpdate();
+ BOOL sendUpdate() override;
void onClickKick();
void onKickCommit(const uuid_vec_t& ids);
static void onClickKickAll(void* userdata);
@@ -214,13 +215,13 @@ public:
LLPanelRegionDebugInfo()
: LLPanelRegionInfo(), mTargetAvatar() {}
~LLPanelRegionDebugInfo() {}
- // LLPanel
- virtual BOOL postBuild();
+
+ BOOL postBuild() override;
- virtual bool refreshFromRegion(LLViewerRegion* region);
+ bool refreshFromRegion(LLViewerRegion* region) override;
protected:
- virtual BOOL sendUpdate();
+ BOOL sendUpdate() override;
void onClickChooseAvatar();
void callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names);
@@ -244,20 +245,22 @@ class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
LOG_CLASS(LLPanelRegionTerrainInfo);
public:
- LLPanelRegionTerrainInfo() : LLPanelRegionInfo() {}
+ LLPanelRegionTerrainInfo();
~LLPanelRegionTerrainInfo() {}
- virtual BOOL postBuild(); // LLPanel
+ BOOL postBuild() override;
- virtual bool refreshFromRegion(LLViewerRegion* region); // refresh local settings from region update from simulator
+ bool refreshFromRegion(LLViewerRegion* region) override; // refresh local settings from region update from simulator
void setEnvControls(bool available); // Whether environment settings are available for this region
- BOOL validateTextureSizes();
+ bool validateTextureSizes();
+ bool validateMaterials();
BOOL validateTextureHeights();
//static void onChangeAnything(LLUICtrl* ctrl, void* userData); // callback for any change, to enable commit button
- virtual BOOL sendUpdate();
+ void onSelectMaterialType();
+ void updateForMaterialType();
static void onClickDownloadRaw(void*);
static void onClickUploadRaw(void*);
@@ -265,9 +268,17 @@ public:
bool callbackBakeTerrain(const LLSD& notification, const LLSD& response);
bool callbackTextureHeights(const LLSD& notification, const LLSD& response);
+protected:
+ BOOL sendUpdate() override;
+
private:
bool mConfirmedTextureHeights;
bool mAskedTextureHeights;
+ LLCheckBoxCtrl* mMaterialTypeCtrl = nullptr;
+ LLTextureCtrl* mTextureDetailCtrl[LLTerrainMaterials::ASSET_COUNT];
+ LLTextureCtrl* mMaterialDetailCtrl[LLTerrainMaterials::ASSET_COUNT];
+ LLUUID mLastSetTextures[LLTerrainMaterials::ASSET_COUNT];
+ LLUUID mLastSetMaterials[LLTerrainMaterials::ASSET_COUNT];
};
/////////////////////////////////////////////////////////////////////////////
@@ -303,13 +314,12 @@ public:
static void updateEstateName(const std::string& name);
static void updateEstateOwnerName(const std::string& name);
- virtual bool refreshFromRegion(LLViewerRegion* region);
- virtual bool estateUpdate(LLMessageSystem* msg);
+ bool refreshFromRegion(LLViewerRegion* region) override;
+ bool estateUpdate(LLMessageSystem* msg) override;
- // LLPanel
- virtual BOOL postBuild();
- virtual void updateChild(LLUICtrl* child_ctrl);
- virtual void refresh();
+ BOOL postBuild() override;
+ void updateChild(LLUICtrl* child_ctrl) override;
+ void refresh() override;
void refreshFromEstate();
@@ -319,7 +329,7 @@ public:
void setOwnerName(const std::string& name);
protected:
- virtual BOOL sendUpdate();
+ BOOL sendUpdate() override;
// confirmation dialog callback
bool callbackChangeLindenEstate(const LLSD& notification, const LLSD& response);
@@ -339,17 +349,16 @@ public:
LLPanelEstateCovenant();
~LLPanelEstateCovenant() {}
- // LLPanel
- virtual BOOL postBuild();
- virtual void updateChild(LLUICtrl* child_ctrl);
- virtual bool refreshFromRegion(LLViewerRegion* region);
- virtual bool estateUpdate(LLMessageSystem* msg);
+ BOOL postBuild() override;
+ void updateChild(LLUICtrl* child_ctrl) override;
+ bool refreshFromRegion(LLViewerRegion* region) override;
+ bool estateUpdate(LLMessageSystem* msg) override;
// LLView overrides
BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
BOOL drop, EDragAndDropType cargo_type,
void *cargo_data, EAcceptance *accept,
- std::string& tooltip_msg);
+ std::string& tooltip_msg) override;
static bool confirmChangeCovenantCallback(const LLSD& notification, const LLSD& response);
static void resetCovenantID(void* userdata);
static bool confirmResetCovenantCallback(const LLSD& notification, const LLSD& response);
@@ -382,7 +391,7 @@ public:
} EAssetStatus;
protected:
- virtual BOOL sendUpdate();
+ BOOL sendUpdate() override;
LLTextBox* mEstateNameText;
LLTextBox* mEstateOwnerText;
LLTextBox* mLastModifiedText;
@@ -401,16 +410,19 @@ class LLPanelRegionExperiences : public LLPanelRegionInfo
public:
LLPanelRegionExperiences(){}
- /*virtual*/ BOOL postBuild();
- virtual BOOL sendUpdate();
+ BOOL postBuild() override;
static bool experienceCoreConfirm(const LLSD& notification, const LLSD& response);
static void sendEstateExperienceDelta(U32 flags, const LLUUID& agent_id);
static void infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content);
- bool refreshFromRegion(LLViewerRegion* region);
+ bool refreshFromRegion(LLViewerRegion* region) override;
void sendPurchaseRequest()const;
void processResponse( const LLSD& content );
+
+protected:
+ BOOL sendUpdate() override;
+
private:
void refreshRegionExperiences();
@@ -435,8 +447,8 @@ class LLPanelEstateAccess : public LLPanelRegionInfo
public:
LLPanelEstateAccess();
- virtual BOOL postBuild();
- virtual void updateChild(LLUICtrl* child_ctrl);
+ BOOL postBuild() override;
+ void updateChild(LLUICtrl* child_ctrl) override;
void updateControls(LLViewerRegion* region);
void updateLists();
@@ -444,7 +456,7 @@ public:
void setPendingUpdate(bool pending) { mPendingUpdate = pending; }
bool getPendingUpdate() { return mPendingUpdate; }
- virtual bool refreshFromRegion(LLViewerRegion* region);
+ bool refreshFromRegion(LLViewerRegion* region) override;
private:
void onClickAddAllowedAgent();
diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp
index 92c58a2dbc..4c9dc8f7cd 100644
--- a/indra/newview/llgltfmateriallist.cpp
+++ b/indra/newview/llgltfmateriallist.cpp
@@ -58,8 +58,6 @@ LLGLTFMaterialList::modify_queue_t LLGLTFMaterialList::sModifyQueue;
LLGLTFMaterialList::apply_queue_t LLGLTFMaterialList::sApplyQueue;
LLSD LLGLTFMaterialList::sUpdates;
-const LLUUID LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID("968cbad0-4dad-d64e-71b5-72bf13ad051a");
-
#ifdef SHOW_ASSERT
// return true if given data is (probably) valid update message for ModifyMaterialParams capability
static bool is_valid_update(const LLSD& data)
@@ -247,6 +245,7 @@ void LLGLTFMaterialList::applyOverrideMessage(LLMessageSystem* msg, const std::s
}
region->cacheFullUpdateGLTFOverride(cache);
+ LL_DEBUGS("GLTF") << "GLTF Material Override: " << cache.mObjectId << " " << cache.mLocalId << " " << cache.mRegionHandle << " (sides:" << (cache.mSides.size()) << ")" << LL_ENDL;
}
}
@@ -471,7 +470,7 @@ void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::ETyp
if (status != LL_ERR_NOERR)
{
LL_WARNS("GLTF") << "Error getting material asset data: " << LLAssetStorage::getErrorString(status) << " (" << status << ")" << LL_ENDL;
- asset_data->mMaterial->materialComplete();
+ asset_data->mMaterial->materialComplete(false);
delete asset_data;
}
else
@@ -556,7 +555,7 @@ void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::ETyp
LL_DEBUGS("GLTF") << "Failed to get material " << id << LL_ENDL;
}
- asset_data->mMaterial->materialComplete();
+ asset_data->mMaterial->materialComplete(true);
delete asset_data;
});
diff --git a/indra/newview/llgltfmateriallist.h b/indra/newview/llgltfmateriallist.h
index f1c4ce20f9..6eab86ff0a 100644
--- a/indra/newview/llgltfmateriallist.h
+++ b/indra/newview/llgltfmateriallist.h
@@ -40,8 +40,6 @@ class LLGLTFOverrideCacheEntry;
class LLGLTFMaterialList
{
public:
- static const LLUUID BLANK_MATERIAL_ASSET_ID;
-
LLGLTFMaterialList() {}
diff --git a/indra/newview/llgltfmaterialpreviewmgr.cpp b/indra/newview/llgltfmaterialpreviewmgr.cpp
new file mode 100644
index 0000000000..1c39f1ae7d
--- /dev/null
+++ b/indra/newview/llgltfmaterialpreviewmgr.cpp
@@ -0,0 +1,590 @@
+/**
+ * @file llgltfmaterialpreviewmgr.cpp
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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 "llgltfmaterialpreviewmgr.h"
+
+#include <memory>
+#include <vector>
+
+#include "llavatarappearancedefines.h"
+#include "llenvironment.h"
+#include "llselectmgr.h"
+#include "llviewercamera.h"
+#include "llviewercontrol.h"
+#include "llviewerobject.h"
+#include "llviewershadermgr.h"
+#include "llviewertexturelist.h"
+#include "llviewerwindow.h"
+#include "llvolumemgr.h"
+#include "pipeline.h"
+
+LLGLTFMaterialPreviewMgr gGLTFMaterialPreviewMgr;
+
+namespace
+{
+ constexpr S32 FULLY_LOADED = 0;
+ constexpr S32 NOT_LOADED = 99;
+};
+
+LLGLTFPreviewTexture::MaterialLoadLevels::MaterialLoadLevels()
+{
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ levels[i] = NOT_LOADED;
+ }
+}
+
+bool LLGLTFPreviewTexture::MaterialLoadLevels::isFullyLoaded()
+{
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ if (levels[i] != FULLY_LOADED) { return false; }
+ }
+ return true;
+}
+
+S32& LLGLTFPreviewTexture::MaterialLoadLevels::operator[](size_t i)
+{
+ llassert(i >= 0 && i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT);
+ return levels[i];
+}
+
+const S32& LLGLTFPreviewTexture::MaterialLoadLevels::operator[](size_t i) const
+{
+ llassert(i >= 0 && i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT);
+ return levels[i];
+}
+
+bool LLGLTFPreviewTexture::MaterialLoadLevels::operator<(const MaterialLoadLevels& other) const
+{
+ bool less = false;
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ if (((*this)[i] > other[i])) { return false; }
+ less = less || ((*this)[i] < other[i]);
+ }
+ return less;
+}
+
+bool LLGLTFPreviewTexture::MaterialLoadLevels::operator>(const MaterialLoadLevels& other) const
+{
+ bool great = false;
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ if (((*this)[i] < other[i])) { return false; }
+ great = great || ((*this)[i] > other[i]);
+ }
+ return great;
+}
+
+namespace
+{
+ void fetch_texture_for_ui(LLPointer<LLViewerFetchedTexture>& img, const LLUUID& id)
+ {
+ if (!img && id.notNull())
+ {
+ if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id))
+ {
+ LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+ if (obj)
+ {
+ LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(id);
+ img = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL;
+ }
+ }
+ else
+ {
+ img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ }
+ }
+ if (img)
+ {
+ img->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+ img->forceToSaveRawImage(0);
+ }
+ };
+
+ // *NOTE: Does not use the same conventions as texture discard level. Lower is better.
+ S32 get_texture_load_level(const LLPointer<LLViewerFetchedTexture>& texture)
+ {
+ if (!texture) { return FULLY_LOADED; }
+ const S32 raw_level = texture->getDiscardLevel();
+ if (raw_level < 0) { return NOT_LOADED; }
+ return raw_level;
+ }
+
+ LLGLTFPreviewTexture::MaterialLoadLevels get_material_load_levels(LLFetchedGLTFMaterial& material)
+ {
+ llassert(!material.isFetching());
+
+ using MaterialTextures = LLPointer<LLViewerFetchedTexture>*[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT];
+
+ MaterialTextures textures;
+
+ textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR] = &material.mBaseColorTexture;
+ textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] = &material.mNormalTexture;
+ textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] = &material.mMetallicRoughnessTexture;
+ textures[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] = &material.mEmissiveTexture;
+
+ LLGLTFPreviewTexture::MaterialLoadLevels levels;
+
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ fetch_texture_for_ui(*textures[i], material.mTextureId[i]);
+ levels[i] = get_texture_load_level(*textures[i]);
+ }
+
+ return levels;
+ }
+
+ // Is the material loaded enough to start rendering a preview?
+ bool is_material_loaded_enough_for_ui(LLFetchedGLTFMaterial& material)
+ {
+ if (material.isFetching())
+ {
+ return false;
+ }
+
+ LLGLTFPreviewTexture::MaterialLoadLevels levels = get_material_load_levels(material);
+
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ if (levels[i] == NOT_LOADED)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+}; // namespace
+
+LLGLTFPreviewTexture::LLGLTFPreviewTexture(LLPointer<LLFetchedGLTFMaterial> material, S32 width)
+ : LLViewerDynamicTexture(width, width, 4, EOrder::ORDER_MIDDLE, FALSE)
+ , mGLTFMaterial(material)
+{
+}
+
+// static
+LLPointer<LLGLTFPreviewTexture> LLGLTFPreviewTexture::create(LLPointer<LLFetchedGLTFMaterial> material)
+{
+ return new LLGLTFPreviewTexture(material, LLPipeline::MAX_BAKE_WIDTH);
+}
+
+BOOL LLGLTFPreviewTexture::needsRender()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if (!mShouldRender && mBestLoad.isFullyLoaded()) { return false; }
+ MaterialLoadLevels current_load = get_material_load_levels(*mGLTFMaterial.get());
+ if (current_load < mBestLoad)
+ {
+ mShouldRender = true;
+ mBestLoad = current_load;
+ return true;
+ }
+ return false;
+}
+
+void LLGLTFPreviewTexture::preRender(BOOL clear_depth)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ llassert(mShouldRender);
+ if (!mShouldRender) { return; }
+
+ LLViewerDynamicTexture::preRender(clear_depth);
+}
+
+
+namespace {
+
+struct GLTFPreviewModel
+{
+ GLTFPreviewModel(LLPointer<LLDrawInfo>& info, const LLMatrix4& mat)
+ : mDrawInfo(info)
+ , mModelMatrix(mat)
+ {
+ mDrawInfo->mModelMatrix = &mModelMatrix;
+ }
+ GLTFPreviewModel(GLTFPreviewModel&) = delete;
+ ~GLTFPreviewModel()
+ {
+ // No model matrix necromancy
+ llassert(gGLLastMatrix != &mModelMatrix);
+ gGLLastMatrix = nullptr;
+ }
+ LLPointer<LLDrawInfo> mDrawInfo;
+ LLMatrix4 mModelMatrix; // Referenced by mDrawInfo
+};
+
+using PreviewSpherePart = std::unique_ptr<GLTFPreviewModel>;
+using PreviewSphere = std::vector<PreviewSpherePart>;
+
+// Like LLVolumeGeometryManager::registerFace but without batching or too-many-indices/vertices checking.
+PreviewSphere create_preview_sphere(LLPointer<LLFetchedGLTFMaterial>& material, const LLMatrix4& model_matrix)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ const LLColor4U vertex_color(material->mBaseColor);
+
+ LLPrimitive prim;
+ prim.init_primitive(LL_PCODE_VOLUME);
+ LLVolumeParams params;
+ params.setType(LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE);
+ params.setBeginAndEndS(0.f, 1.f);
+ params.setBeginAndEndT(0.f, 1.f);
+ params.setRatio(1, 1);
+ params.setShear(0, 0);
+ constexpr auto MAX_LOD = LLVolumeLODGroup::NUM_LODS - 1;
+ prim.setVolume(params, MAX_LOD);
+
+ LLVolume* volume = prim.getVolume();
+ llassert(volume);
+ for (LLVolumeFace& face : volume->getVolumeFaces())
+ {
+ face.createTangents();
+ }
+
+ PreviewSphere preview_sphere;
+ preview_sphere.reserve(volume->getNumFaces());
+
+ LLPointer<LLVertexBuffer> buf = new LLVertexBuffer(
+ LLVertexBuffer::MAP_VERTEX |
+ LLVertexBuffer::MAP_NORMAL |
+ LLVertexBuffer::MAP_TEXCOORD0 |
+ LLVertexBuffer::MAP_COLOR |
+ LLVertexBuffer::MAP_TANGENT
+ );
+ U32 nv = 0;
+ U32 ni = 0;
+ for (LLVolumeFace& face : volume->getVolumeFaces())
+ {
+ nv += face.mNumVertices;
+ ni += face.mNumIndices;
+ }
+ buf->allocateBuffer(nv, ni);
+
+ // UV hacks
+ // Higher factor helps to see more details on the preview sphere
+ const LLVector2 uv_factor(2.0f, 2.0f);
+ // Offset places center of material in center of view
+ const LLVector2 uv_offset(-0.5f, -0.5f);
+
+ LLStrider<U16> indices;
+ LLStrider<LLVector4a> positions;
+ LLStrider<LLVector4a> normals;
+ LLStrider<LLVector2> texcoords;
+ LLStrider<LLColor4U> colors;
+ LLStrider<LLVector4a> tangents;
+ buf->getIndexStrider(indices);
+ buf->getVertexStrider(positions);
+ buf->getNormalStrider(normals);
+ buf->getTexCoord0Strider(texcoords);
+ buf->getColorStrider(colors);
+ buf->getTangentStrider(tangents);
+ U32 index_offset = 0;
+ U32 vertex_offset = 0;
+ for (const LLVolumeFace& face : volume->getVolumeFaces())
+ {
+ for (S32 i = 0; i < face.mNumIndices; ++i)
+ {
+ *indices++ = face.mIndices[i] + vertex_offset;
+ }
+ for (S32 v = 0; v < face.mNumVertices; ++v)
+ {
+ *positions++ = face.mPositions[v];
+ *normals++ = face.mNormals[v];
+ LLVector2 uv(face.mTexCoords[v]);
+ uv.scaleVec(uv_factor);
+ uv += uv_offset;
+ *texcoords++ = uv;
+ *colors++ = vertex_color;
+ *tangents++ = face.mTangents[v];
+ }
+
+ constexpr LLViewerTexture* no_media = nullptr;
+ LLPointer<LLDrawInfo> info = new LLDrawInfo(U16(vertex_offset), U16(vertex_offset + face.mNumVertices - 1), face.mNumIndices, index_offset, no_media, buf.get());
+ info->mGLTFMaterial = material;
+ preview_sphere.emplace_back(std::make_unique<GLTFPreviewModel>(info, model_matrix));
+ index_offset += face.mNumIndices;
+ vertex_offset += face.mNumVertices;
+ }
+
+ buf->unmapBuffer();
+
+ return preview_sphere;
+}
+
+void set_preview_sphere_material(PreviewSphere& preview_sphere, LLPointer<LLFetchedGLTFMaterial>& material)
+{
+ llassert(!preview_sphere.empty());
+ if (preview_sphere.empty()) { return; }
+
+ const LLColor4U vertex_color(material->mBaseColor);
+
+ // See comments about unmapBuffer in llvertexbuffer.h
+ for (PreviewSpherePart& part : preview_sphere)
+ {
+ LLDrawInfo* info = part->mDrawInfo.get();
+ info->mGLTFMaterial = material;
+ LLVertexBuffer* buf = info->mVertexBuffer.get();
+ LLStrider<LLColor4U> colors;
+ const S32 count = info->mEnd - info->mStart + 1;
+ buf->getColorStrider(colors, info->mStart, count);
+ for (S32 i = 0; i < count; ++i)
+ {
+ *colors++ = vertex_color;
+ }
+ buf->unmapBuffer();
+ }
+}
+
+PreviewSphere& get_preview_sphere(LLPointer<LLFetchedGLTFMaterial>& material, const LLMatrix4& model_matrix)
+{
+ static PreviewSphere preview_sphere;
+ if (preview_sphere.empty())
+ {
+ preview_sphere = create_preview_sphere(material, model_matrix);
+ }
+ else
+ {
+ set_preview_sphere_material(preview_sphere, material);
+ }
+ return preview_sphere;
+}
+
+// Final, direct modifications to shader constants, just before render
+void fixup_shader_constants(LLGLSLShader& shader)
+{
+ // Sunlight intensity of 0 no matter what
+ shader.uniform1i(LLShaderMgr::SUN_UP_FACTOR, 1);
+ shader.uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, LLColor3::white.mV);
+ shader.uniform1f(LLShaderMgr::DENSITY_MULTIPLIER, 0.0f);
+
+ // Ignore sun shadow (if enabled)
+ for (U32 i = 0; i < 6; i++)
+ {
+ const S32 channel = shader.getTextureChannel(LLShaderMgr::DEFERRED_SHADOW0+i);
+ if (channel != -1)
+ {
+ gGL.getTexUnit(channel)->bind(LLViewerFetchedTexture::sWhiteImagep, TRUE);
+ }
+ }
+}
+
+// Set a variable to a value temporarily, and restor the variable's old value
+// when this object leaves scope.
+template<typename T>
+struct SetTemporarily
+{
+ T* mRef;
+ T mOldVal;
+ SetTemporarily(T* var, T temp_val)
+ {
+ mRef = var;
+ mOldVal = *mRef;
+ *mRef = temp_val;
+ }
+ ~SetTemporarily()
+ {
+ *mRef = mOldVal;
+ }
+};
+
+}; // namespace
+
+BOOL LLGLTFPreviewTexture::render()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if (!mShouldRender) { return FALSE; }
+
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ LLGLDepthTest(GL_FALSE);
+ LLGLDisable stencil(GL_STENCIL_TEST);
+ LLGLDisable scissor(GL_SCISSOR_TEST);
+ SetTemporarily<bool> no_dof(&LLPipeline::RenderDepthOfField, false);
+ SetTemporarily<bool> no_glow(&LLPipeline::sRenderGlow, false);
+ SetTemporarily<bool> no_ssr(&LLPipeline::RenderScreenSpaceReflections, false);
+ SetTemporarily<U32> no_fxaa(&LLPipeline::RenderFSAASamples, U32(0));
+ SetTemporarily<LLPipeline::RenderTargetPack*> use_auxiliary_render_target(&gPipeline.mRT, &gPipeline.mAuxillaryRT);
+
+ LLVector3 light_dir3(1.0f, 1.0f, 1.0f);
+ light_dir3.normalize();
+ const LLVector4 light_dir = LLVector4(light_dir3, 0);
+ const S32 old_local_light_count = gSavedSettings.get<S32>("RenderLocalLightCount");
+ gSavedSettings.set<S32>("RenderLocalLightCount", 0);
+
+ gPipeline.mReflectionMapManager.forceDefaultProbeAndUpdateUniforms();
+
+ LLViewerCamera camera;
+
+ // Calculate the object distance at which the object of a given radius will
+ // span the partial width of the screen given by fill_ratio.
+ // Assume the primitive has a scale of 1 (this is the default).
+ constexpr F32 fill_ratio = 0.8f;
+ constexpr F32 object_radius = 0.5f;
+ const F32 object_distance = (object_radius / fill_ratio) * tan(camera.getDefaultFOV());
+ // Negative coordinate shows the textures on the sphere right-side up, when
+ // combined with the UV hacks in create_preview_sphere
+ const LLVector3 object_position(0.0, -object_distance, 0.0);
+ LLMatrix4 object_transform;
+ object_transform.translate(object_position);
+
+ // Set up camera and viewport
+ const LLVector3 origin(0.0, 0.0, 0.0);
+ camera.lookAt(origin, object_position);
+ camera.setAspect(mFullHeight / mFullWidth);
+ const LLRect texture_rect(0, mFullHeight, mFullWidth, 0);
+ camera.setPerspective(NOT_FOR_SELECTION, texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight(), FALSE, camera.getNear(), MAX_FAR_CLIP*2.f);
+
+ // Generate sphere object on-the-fly. Discard afterwards. (Vertex buffer is
+ // discarded, but the sphere should be cached in LLVolumeMgr.)
+ PreviewSphere& preview_sphere = get_preview_sphere(mGLTFMaterial, object_transform);
+
+ gPipeline.setupHWLights();
+ glh::matrix4f mat = copy_matrix(gGLModelView);
+ glh::vec4f transformed_light_dir(light_dir.mV);
+ mat.mult_matrix_vec(transformed_light_dir);
+ SetTemporarily<LLVector4> force_sun_direction_high_graphics(&gPipeline.mTransformedSunDir, LLVector4(transformed_light_dir.v));
+ // Override lights to ensure the sun is always shining from a certain direction (low graphics)
+ // See also force_sun_direction_high_graphics and fixup_shader_constants
+ {
+ LLLightState* light = gGL.getLight(0);
+ light->setPosition(light_dir);
+ constexpr bool sun_up = true;
+ light->setSunPrimary(sun_up);
+ }
+
+ LLRenderTarget& screen = gPipeline.mAuxillaryRT.screen;
+
+ // *HACK: Force reset of the model matrix
+ gGLLastMatrix = nullptr;
+
+#if 0
+ if (mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_OPAQUE || mGLTFMaterial->mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
+ {
+ // *TODO: Opaque/alpha mask rendering
+ }
+ else
+#endif
+ {
+ // Alpha blend rendering
+
+ screen.bindTarget();
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ LLGLSLShader& shader = gDeferredPBRAlphaProgram;
+
+ gPipeline.bindDeferredShader(shader);
+ fixup_shader_constants(shader);
+
+ for (PreviewSpherePart& part : preview_sphere)
+ {
+ LLRenderPass::pushGLTFBatch(*part->mDrawInfo);
+ }
+
+ gPipeline.unbindDeferredShader(shader);
+
+ screen.flush();
+ }
+
+ // *HACK: Hide mExposureMap from generateExposure
+ gPipeline.mExposureMap.swapFBORefs(gPipeline.mLastExposure);
+
+ gPipeline.copyScreenSpaceReflections(&screen, &gPipeline.mSceneMap);
+ gPipeline.generateLuminance(&screen, &gPipeline.mLuminanceMap);
+ gPipeline.generateExposure(&gPipeline.mLuminanceMap, &gPipeline.mExposureMap, /*use_history = */ false);
+ gPipeline.gammaCorrect(&screen, &gPipeline.mPostMap);
+ LLVertexBuffer::unbind();
+ gPipeline.generateGlow(&gPipeline.mPostMap);
+ gPipeline.combineGlow(&gPipeline.mPostMap, &screen);
+ gPipeline.renderDoF(&screen, &gPipeline.mPostMap);
+ gPipeline.applyFXAA(&gPipeline.mPostMap, &screen);
+
+ // *HACK: Restore mExposureMap (it will be consumed by generateExposure next frame)
+ gPipeline.mExposureMap.swapFBORefs(gPipeline.mLastExposure);
+
+ // Final render
+
+ gDeferredPostNoDoFProgram.bind();
+
+ // From LLPipeline::renderFinalize: "Whatever is last in the above post processing chain should _always_ be rendered directly here. If not, expect problems."
+ gDeferredPostNoDoFProgram.bindTexture(LLShaderMgr::DEFERRED_DIFFUSE, &screen);
+ gDeferredPostNoDoFProgram.bindTexture(LLShaderMgr::DEFERRED_DEPTH, mBoundTarget, true);
+
+ {
+ LLGLDepthTest depth_test(GL_TRUE, GL_TRUE, GL_ALWAYS);
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+ }
+
+ gDeferredPostNoDoFProgram.unbind();
+
+ // Clean up
+ gPipeline.setupHWLights();
+ gPipeline.mReflectionMapManager.forceDefaultProbeAndUpdateUniforms(false);
+ gSavedSettings.set<S32>("RenderLocalLightCount", old_local_light_count);
+
+ return TRUE;
+}
+
+void LLGLTFPreviewTexture::postRender(BOOL success)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if (!mShouldRender) { return; }
+ mShouldRender = false;
+
+ LLViewerDynamicTexture::postRender(success);
+}
+
+LLPointer<LLViewerTexture> LLGLTFMaterialPreviewMgr::getPreview(LLPointer<LLFetchedGLTFMaterial> &material)
+{
+ if (!material)
+ {
+ return nullptr;
+ }
+
+ static LLCachedControl<bool> sUIPreviewMaterial(gSavedSettings, "UIPreviewMaterial", false);
+ if (!sUIPreviewMaterial)
+ {
+ fetch_texture_for_ui(material->mBaseColorTexture, material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]);
+ return material->mBaseColorTexture;
+ }
+
+ if (!is_material_loaded_enough_for_ui(*material))
+ {
+ return nullptr;
+ }
+
+ return LLGLTFPreviewTexture::create(material);
+}
diff --git a/indra/newview/llgltfmaterialpreviewmgr.h b/indra/newview/llgltfmaterialpreviewmgr.h
new file mode 100644
index 0000000000..981c8b0592
--- /dev/null
+++ b/indra/newview/llgltfmaterialpreviewmgr.h
@@ -0,0 +1,78 @@
+/**
+ * @file llgltfmaterialpreviewmgr.h
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2023, 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 "lldrawpool.h"
+#include "lldynamictexture.h"
+#include "llfetchedgltfmaterial.h"
+#include "llsingleton.h"
+#include "lltexture.h"
+
+class LLGLTFPreviewTexture : public LLViewerDynamicTexture
+{
+protected:
+ LLGLTFPreviewTexture(LLPointer<LLFetchedGLTFMaterial> material, S32 width);
+
+public:
+ // Width scales with size of material's textures
+ static LLPointer<LLGLTFPreviewTexture> create(LLPointer<LLFetchedGLTFMaterial> material);
+
+ BOOL needsRender() override;
+ void preRender(BOOL clear_depth = TRUE) override;
+ BOOL render() override;
+ void postRender(BOOL success) override;
+
+ struct MaterialLoadLevels
+ {
+ S32 levels[LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT];
+
+ MaterialLoadLevels();
+ bool isFullyLoaded();
+ S32& operator[](size_t i);
+ const S32& operator[](size_t i) const;
+ // Less is better
+ // Returns false if lhs is not strictly less or equal for all levels
+ bool operator<(const MaterialLoadLevels& other) const;
+ // Less is better
+ // Returns false if lhs is not strictly greater or equal for all levels
+ bool operator>(const MaterialLoadLevels& other) const;
+ };
+
+private:
+ LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial;
+ bool mShouldRender = true;
+ MaterialLoadLevels mBestLoad;
+};
+
+class LLGLTFMaterialPreviewMgr
+{
+ public:
+ // Returns null if the material is not loaded yet.
+ // *NOTE: User should cache the texture if the same material is being previewed
+ LLPointer<LLViewerTexture> getPreview(LLPointer<LLFetchedGLTFMaterial> &material);
+};
+
+extern LLGLTFMaterialPreviewMgr gGLTFMaterialPreviewMgr;
diff --git a/indra/newview/llheroprobemanager.cpp b/indra/newview/llheroprobemanager.cpp
new file mode 100644
index 0000000000..55539a49d8
--- /dev/null
+++ b/indra/newview/llheroprobemanager.cpp
@@ -0,0 +1,632 @@
+/**
+ * @file LLHeroProbeManager.cpp
+ * @brief LLHeroProbeManager class implementation
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llheroprobemanager.h"
+#include "llreflectionmapmanager.h"
+#include "llviewercamera.h"
+#include "llspatialpartition.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+#include "llviewershadermgr.h"
+#include "llviewercontrol.h"
+#include "llenvironment.h"
+#include "llstartup.h"
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "llviewerwindow.h"
+#include "llviewerjoystick.h"
+#include "llviewermediafocus.h"
+
+extern BOOL gCubeSnapshot;
+extern BOOL gTeleportDisplay;
+
+// get the next highest power of two of v (or v if v is already a power of two)
+//defined in llvertexbuffer.cpp
+extern U32 nhpo2(U32 v);
+
+static void touch_default_probe(LLReflectionMap* probe)
+{
+ if (LLViewerCamera::getInstance())
+ {
+ LLVector3 origin = LLViewerCamera::getInstance()->getOrigin();
+ origin.mV[2] += 64.f;
+
+ probe->mOrigin.load3(origin.mV);
+ }
+}
+
+LLHeroProbeManager::LLHeroProbeManager()
+{
+}
+
+LLHeroProbeManager::~LLHeroProbeManager()
+{
+ cleanup();
+
+ mHeroVOList.clear();
+ mNearestHero = nullptr;
+}
+
+// helper class to seed octree with probes
+void LLHeroProbeManager::update()
+{
+ if (!LLPipeline::RenderMirrors || gTeleportDisplay || LLStartUp::getStartupState() < STATE_PRECACHE)
+ {
+ return;
+ }
+
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+ llassert(!gCubeSnapshot); // assert a snapshot is not in progress
+ if (LLAppViewer::instance()->logoutRequestSent())
+ {
+ return;
+ }
+
+ initReflectionMaps();
+
+ if (!mRenderTarget.isComplete())
+ {
+ U32 color_fmt = GL_RGBA16F;
+ mRenderTarget.allocate(mProbeResolution, mProbeResolution, color_fmt, true);
+ }
+
+ if (mMipChain.empty())
+ {
+ U32 res = mProbeResolution;
+ U32 count = log2((F32)res) + 0.5f;
+
+ mMipChain.resize(count);
+ for (int i = 0; i < count; ++i)
+ {
+ mMipChain[i].allocate(res, res, GL_RGBA16F);
+ res /= 2;
+ }
+ }
+
+ llassert(mProbes[0] == mDefaultProbe);
+
+ LLVector4a probe_pos;
+ LLVector3 camera_pos = LLViewerCamera::instance().mOrigin;
+ F32 near_clip = 0.1f;
+ bool probe_present = false;
+ LLQuaternion cameraOrientation = LLViewerCamera::instance().getQuaternion();
+ LLVector3 cameraDirection = LLVector3::z_axis * cameraOrientation;
+
+ if (mHeroVOList.size() > 0)
+ {
+ // Find our nearest hero candidate.
+ float last_distance = 99999.f;
+ float camera_center_distance = 99999.f;
+ for (auto vo : mHeroVOList)
+ {
+ if (vo && !vo->isDead() && vo->mDrawable.notNull())
+ {
+ float distance = (LLViewerCamera::instance().getOrigin() - vo->getPositionAgent()).magVec();
+ float center_distance = cameraDirection * (vo->getPositionAgent() - camera_pos);
+
+ if (distance > LLViewerCamera::instance().getFar())
+ continue;
+
+ LLVector4a center;
+ center.load3(vo->getPositionAgent().mV);
+ LLVector4a size;
+
+ size.load3(vo->getScale().mV);
+
+ bool visible = LLViewerCamera::instance().AABBInFrustum(center, size);
+
+ if (distance < last_distance && center_distance < camera_center_distance && visible)
+ {
+ probe_present = true;
+ mNearestHero = vo;
+ last_distance = distance;
+ camera_center_distance = center_distance;
+ }
+ }
+ else
+ {
+ unregisterViewerObject(vo);
+ }
+ }
+
+ // Don't even try to do anything if we didn't find a single mirror present.
+ if (!probe_present)
+ return;
+
+ if (mNearestHero != nullptr && !mNearestHero->isDead() && mNearestHero->mDrawable.notNull())
+ {
+ LLVector3 hero_pos = mNearestHero->getPositionAgent();
+ LLVector3 face_normal = LLVector3(0, 0, 1);
+
+ face_normal *= mNearestHero->mDrawable->getWorldRotation();
+ face_normal.normalize();
+
+ LLVector3 offset = camera_pos - hero_pos;
+ LLVector3 project = face_normal * (offset * face_normal);
+ LLVector3 reject = offset - project;
+ LLVector3 point = (reject - project) + hero_pos;
+
+ mCurrentClipPlane.setVec(hero_pos, face_normal);
+ mMirrorPosition = hero_pos;
+ mMirrorNormal = face_normal;
+
+ probe_pos.load3(point.mV);
+
+ // Detect visible faces of a cube based on camera direction and distance
+
+ // Define the cube faces
+ static LLVector3 cubeFaces[6] = {
+ LLVector3(1, 0, 0),
+ LLVector3(-1, 0, 0),
+ LLVector3(0, 1, 0),
+ LLVector3(0, -1, 0),
+ LLVector3(0, 0, 1),
+ LLVector3(0, 0, -1)
+ };
+
+ // Iterate through each face of the cube
+ for (int i = 0; i < 6; i++)
+ {
+ float cube_facing = fmax(-1, fmin(1.0f, cameraDirection * cubeFaces[i])) * 0.6 + 0.4;
+
+ float updateRate;
+ if (cube_facing < 0.1f)
+ {
+ updateRate = 0;
+ }
+ else
+ {
+ updateRate = ceilf(cube_facing * gPipeline.RenderHeroProbeConservativeUpdateMultiplier);
+ }
+
+ mFaceUpdateList[i] = updateRate;
+ }
+ }
+ else
+ {
+ mNearestHero = nullptr;
+ }
+
+ mHeroProbeStrength = 1;
+ }
+ else
+ {
+ probe_pos.load3(camera_pos.mV);
+ }
+
+
+ static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1);
+ static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3);
+
+ if (mNearestHero != nullptr)
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime");
+ // Probe 0 is always our mirror probe.
+ mProbes[0]->mOrigin = probe_pos;
+
+ bool radiance_pass = gPipeline.mReflectionMapManager.isRadiancePass();
+
+ gPipeline.mReflectionMapManager.mRadiancePass = true;
+ mRenderingMirror = true;
+
+ doOcclusion();
+
+ for (U32 j = 0; j < mProbes.size(); j++)
+ {
+ for (U32 i = 0; i < 6; ++i)
+ {
+ if (mFaceUpdateList[i] > 0 && mCurrentProbeUpdateFrame % mFaceUpdateList[i] == 0)
+ {
+ updateProbeFace(mProbes[j], i, mNearestHero->getReflectionProbeIsDynamic() && sDetail > 0, near_clip);
+ mCurrentProbeUpdateFrame = 0;
+ }
+ }
+ generateRadiance(mProbes[j]);
+ }
+ mRenderingMirror = false;
+
+ gPipeline.mReflectionMapManager.mRadiancePass = radiance_pass;
+
+ mProbes[0]->mViewerObject = mNearestHero;
+ mProbes[0]->autoAdjustOrigin();
+ }
+
+ mCurrentProbeUpdateFrame++;
+}
+
+// Do the reflection map update render passes.
+// For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated
+// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate
+// a simple mip chain (not convolution filter).
+// At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe
+// The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain.
+// At the end of these passes, a radiance map is generated for this probe and placed into the radiance cube map array at the index for this probe.
+// In effect this simulates single-bounce lighting.
+void LLHeroProbeManager::updateProbeFace(LLReflectionMap* probe, U32 face, bool is_dynamic, F32 near_clip)
+{
+ // hacky hot-swap of camera specific render targets
+ gPipeline.mRT = &gPipeline.mHeroProbeRT;
+
+ probe->update(mRenderTarget.getWidth(), face, is_dynamic, near_clip);
+
+ gPipeline.mRT = &gPipeline.mMainRT;
+
+ S32 sourceIdx = mReflectionProbeCount;
+
+ // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes.
+ sourceIdx += 1;
+
+ gGL.setColorMask(true, true);
+ LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+ LLGLDisable cull(GL_CULL_FACE);
+ LLGLDisable blend(GL_BLEND);
+
+ // downsample to placeholder map
+ {
+ gGL.matrixMode(gGL.MM_MODELVIEW);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+
+ gGL.matrixMode(gGL.MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+
+ gGL.flush();
+ U32 res = mProbeResolution * 2;
+
+ static LLStaticHashedString resScale("resScale");
+ static LLStaticHashedString direction("direction");
+ static LLStaticHashedString znear("znear");
+ static LLStaticHashedString zfar("zfar");
+
+ LLRenderTarget *screen_rt = &gPipeline.mHeroProbeRT.screen;
+ LLRenderTarget *depth_rt = &gPipeline.mHeroProbeRT.deferredScreen;
+
+ // perform a gaussian blur on the super sampled render before downsampling
+ {
+ gGaussianProgram.bind();
+ gGaussianProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2));
+ S32 diffuseChannel = gGaussianProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
+
+ // horizontal
+ gGaussianProgram.uniform2f(direction, 1.f, 0.f);
+ gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
+ mRenderTarget.bindTarget();
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+ mRenderTarget.flush();
+
+ // vertical
+ gGaussianProgram.uniform2f(direction, 0.f, 1.f);
+ gGL.getTexUnit(diffuseChannel)->bind(&mRenderTarget);
+ screen_rt->bindTarget();
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+ screen_rt->flush();
+ gGaussianProgram.unbind();
+ }
+
+ S32 mips = log2((F32)mProbeResolution) + 0.5f;
+
+ gReflectionMipProgram.bind();
+ S32 diffuseChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_TEXTURE);
+ S32 depthChannel = gReflectionMipProgram.enableTexture(LLShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_TEXTURE);
+
+ for (int i = 0; i < mMipChain.size(); ++i)
+ {
+ LL_PROFILE_GPU_ZONE("probe mip");
+ mMipChain[i].bindTarget();
+ if (i == 0)
+ {
+ gGL.getTexUnit(diffuseChannel)->bind(screen_rt);
+ }
+ else
+ {
+ gGL.getTexUnit(diffuseChannel)->bind(&(mMipChain[i - 1]));
+ }
+
+ gGL.getTexUnit(depthChannel)->bind(depth_rt, true);
+
+ gReflectionMipProgram.uniform1f(resScale, 1.f / (mProbeResolution * 2));
+ gReflectionMipProgram.uniform1f(znear, probe->getNearClip());
+ gReflectionMipProgram.uniform1f(zfar, MAX_FAR_CLIP);
+
+ gPipeline.mScreenTriangleVB->setBuffer();
+ gPipeline.mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
+
+ res /= 2;
+
+ S32 mip = i - (mMipChain.size() - mips);
+
+ if (mip >= 0)
+ {
+ LL_PROFILE_GPU_ZONE("probe mip copy");
+ mTexture->bind(0);
+
+ glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, mip, 0, 0, sourceIdx * 6 + face, 0, 0, res, res);
+
+ mTexture->unbind();
+ }
+ mMipChain[i].flush();
+ }
+
+ gGL.popMatrix();
+ gGL.matrixMode(gGL.MM_MODELVIEW);
+ gGL.popMatrix();
+
+ gGL.getTexUnit(diffuseChannel)->unbind(LLTexUnit::TT_TEXTURE);
+ gReflectionMipProgram.unbind();
+ }
+}
+
+// Separate out radiance generation as a separate stage.
+// This is to better enable independent control over how we generate radiance vs. having it coupled with processing the final face of the probe.
+// Useful when we may not always be rendering a full set of faces of the probe.
+void LLHeroProbeManager::generateRadiance(LLReflectionMap* probe)
+{
+ S32 sourceIdx = mReflectionProbeCount;
+
+ // Unlike the reflectionmap manager, all probes are considered "realtime" for hero probes.
+ sourceIdx += 1;
+ {
+ mMipChain[0].bindTarget();
+ static LLStaticHashedString sSourceIdx("sourceIdx");
+
+ {
+ // generate radiance map (even if this is not the irradiance map, we need the mip chain for the irradiance map)
+ gHeroRadianceGenProgram.bind();
+ mVertexBuffer->setBuffer();
+
+ S32 channel = gHeroRadianceGenProgram.enableTexture(LLShaderMgr::REFLECTION_PROBES, LLTexUnit::TT_CUBE_MAP_ARRAY);
+ mTexture->bind(channel);
+ gHeroRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
+ gHeroRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
+ gHeroRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, mHeroProbeStrength);
+
+ U32 res = mMipChain[0].getWidth();
+
+ for (int i = 0; i < mMipChain.size() / 4; ++i)
+ {
+ LL_PROFILE_GPU_ZONE("probe radiance gen");
+ static LLStaticHashedString sMipLevel("mipLevel");
+ static LLStaticHashedString sRoughness("roughness");
+ static LLStaticHashedString sWidth("u_width");
+ static LLStaticHashedString sStrength("probe_strength");
+
+ gHeroRadianceGenProgram.uniform1f(sRoughness, (F32) i / (F32) (mMipChain.size() - 1));
+ gHeroRadianceGenProgram.uniform1f(sMipLevel, i);
+ gHeroRadianceGenProgram.uniform1i(sWidth, mProbeResolution);
+ gHeroRadianceGenProgram.uniform1f(sStrength, 1);
+
+ for (int cf = 0; cf < 6; ++cf)
+ { // for each cube face
+ LLCoordFrame frame;
+ frame.lookAt(LLVector3(0, 0, 0), LLCubeMapArray::sClipToCubeLookVecs[cf], LLCubeMapArray::sClipToCubeUpVecs[cf]);
+
+ F32 mat[16];
+ frame.getOpenGLRotation(mat);
+ gGL.loadMatrix(mat);
+
+ mVertexBuffer->drawArrays(gGL.TRIANGLE_STRIP, 0, 4);
+
+ glCopyTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, i, 0, 0, probe->mCubeIndex * 6 + cf, 0, 0, res, res);
+ }
+
+ if (i != mMipChain.size() - 1)
+ {
+ res /= 2;
+ glViewport(0, 0, res, res);
+ }
+ }
+
+ gHeroRadianceGenProgram.unbind();
+ }
+
+ mMipChain[0].flush();
+ }
+}
+
+void LLHeroProbeManager::updateUniforms()
+{
+ if (!gPipeline.RenderMirrors)
+ {
+ return;
+ }
+
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+ LLVector4a oa; // scratch space for transformed origin
+ oa.set(0, 0, 0, 0);
+ mHeroData.heroProbeCount = 1;
+
+ if (mNearestHero != nullptr && !mNearestHero->isDead())
+ {
+ if (mNearestHero->getReflectionProbeIsBox())
+ {
+ LLVector3 s = mNearestHero->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));
+ mProbes[0]->mRadius = s.magVec();
+ }
+ else
+ {
+ mProbes[0]->mRadius = mNearestHero->getScale().mV[0] * 0.5f;
+ }
+
+ modelview.affineTransform(mProbes[0]->mOrigin, oa);
+ mHeroData.heroShape = 0;
+ if (!mProbes[0]->getBox(mHeroData.heroBox))
+ {
+ mHeroData.heroShape = 1;
+ }
+
+ mHeroData.heroSphere.set(oa.getF32ptr());
+ mHeroData.heroSphere.mV[3] = mProbes[0]->mRadius;
+ }
+
+ mHeroData.heroMipCount = mMipChain.size();
+}
+
+void LLHeroProbeManager::renderDebug()
+{
+ gDebugProgram.bind();
+
+ for (auto& probe : mProbes)
+ {
+ renderReflectionProbe(probe);
+ }
+
+ gDebugProgram.unbind();
+}
+
+
+void LLHeroProbeManager::initReflectionMaps()
+{
+ U32 count = LL_MAX_HERO_PROBE_COUNT;
+
+ if ((mTexture.isNull() || mReflectionProbeCount != count || mReset) && LLPipeline::RenderMirrors)
+ {
+
+ if (mReset)
+ {
+ cleanup();
+ }
+
+ mReset = false;
+ mReflectionProbeCount = count;
+ mProbeResolution = gSavedSettings.getS32("RenderHeroProbeResolution");
+ mMaxProbeLOD = log2f(mProbeResolution) - 1.f; // number of mips - 1
+
+ mTexture = new LLCubeMapArray();
+
+ // store mReflectionProbeCount+2 cube maps, final two cube maps are used for render target and radiance map generation source)
+ mTexture->allocate(mProbeResolution, 3, mReflectionProbeCount + 2);
+
+ if (mDefaultProbe.isNull())
+ {
+ llassert(mProbes.empty()); // default probe MUST be the first probe created
+ mDefaultProbe = new LLReflectionMap();
+ mProbes.push_back(mDefaultProbe);
+ }
+
+ llassert(mProbes[0] == mDefaultProbe);
+
+ // For hero probes, we treat this as the main mirror probe.
+
+ mDefaultProbe->mCubeIndex = 0;
+ mDefaultProbe->mCubeArray = mTexture;
+ mDefaultProbe->mDistance = gSavedSettings.getF32("RenderHeroProbeDistance");
+ mDefaultProbe->mRadius = 4096.f;
+ mDefaultProbe->mProbeIndex = 0;
+ touch_default_probe(mDefaultProbe);
+
+ mProbes.push_back(mDefaultProbe);
+ }
+
+ if (mVertexBuffer.isNull())
+ {
+ U32 mask = LLVertexBuffer::MAP_VERTEX;
+ LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(mask);
+ buff->allocateBuffer(4, 0);
+
+ LLStrider<LLVector3> v;
+
+ buff->getVertexStrider(v);
+
+ v[0] = LLVector3(-1, -1, -1);
+ v[1] = LLVector3(1, -1, -1);
+ v[2] = LLVector3(-1, 1, -1);
+ v[3] = LLVector3(1, 1, -1);
+
+ buff->unmapBuffer();
+
+ mVertexBuffer = buff;
+ }
+}
+
+void LLHeroProbeManager::cleanup()
+{
+ mVertexBuffer = nullptr;
+ mRenderTarget.release();
+ mHeroRenderTarget.release();
+
+ mMipChain.clear();
+
+ mTexture = nullptr;
+
+ mProbes.clear();
+
+ mReflectionMaps.clear();
+
+ mDefaultProbe = nullptr;
+ mUpdatingProbe = nullptr;
+ /*
+ */
+}
+
+void LLHeroProbeManager::doOcclusion()
+{
+ LLVector4a eye;
+ eye.load3(LLViewerCamera::instance().getOrigin().mV);
+
+ for (auto& probe : mProbes)
+ {
+ if (probe != nullptr && probe != mDefaultProbe)
+ {
+ probe->doOcclusion(eye);
+ }
+ }
+}
+
+void LLHeroProbeManager::reset()
+{
+ mReset = true;
+}
+
+bool LLHeroProbeManager::registerViewerObject(LLVOVolume* drawablep)
+{
+ llassert(drawablep != nullptr);
+
+ if (std::find(mHeroVOList.begin(), mHeroVOList.end(), drawablep) == mHeroVOList.end())
+ {
+ // Probe isn't in our list for consideration. Add it.
+ mHeroVOList.push_back(drawablep);
+ return true;
+ }
+
+ return false;
+}
+
+void LLHeroProbeManager::unregisterViewerObject(LLVOVolume* drawablep)
+{
+ std::vector<LLPointer<LLVOVolume>>::iterator found_itr = std::find(mHeroVOList.begin(), mHeroVOList.end(), drawablep);
+ if (found_itr != mHeroVOList.end())
+ {
+ mHeroVOList.erase(found_itr);
+ }
+}
diff --git a/indra/newview/llheroprobemanager.h b/indra/newview/llheroprobemanager.h
new file mode 100644
index 0000000000..5df146f2f1
--- /dev/null
+++ b/indra/newview/llheroprobemanager.h
@@ -0,0 +1,156 @@
+/**
+ * @file llheroprobemanager.h
+ * @brief LLHeroProbeManager class declaration
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "llreflectionmap.h"
+#include "llrendertarget.h"
+#include "llcubemaparray.h"
+#include "llcubemap.h"
+#include "lldrawable.h"
+
+class LLSpatialGroup;
+class LLViewerObject;
+
+// number of reflection probes to keep in vram
+#define LL_MAX_HERO_PROBE_COUNT 2
+
+struct HeroProbeData
+{
+ LLMatrix4 heroBox;
+ LLVector4 heroSphere;
+ GLint heroShape;
+ GLint heroMipCount;
+ GLint heroProbeCount;
+};
+
+class alignas(16) LLHeroProbeManager
+{
+ LL_ALIGN_NEW
+public:
+ enum class DetailLevel
+ {
+ STATIC_ONLY = 0,
+ STATIC_AND_DYNAMIC,
+ REALTIME = 2
+ };
+
+ // allocate an environment map of the given resolution
+ LLHeroProbeManager();
+ ~LLHeroProbeManager();
+
+ // release any GL state
+ void cleanup();
+
+ // maintain reflection probes
+ void update();
+
+ // debug display, called from llspatialpartition if reflection
+ // probe debug display is active
+ void renderDebug();
+
+ // call once at startup to allocate cubemap arrays
+ void initReflectionMaps();
+
+ // perform occlusion culling on all active reflection probes
+ void doOcclusion();
+
+ void reset();
+
+ bool registerViewerObject(LLVOVolume *drawablep);
+ void unregisterViewerObject(LLVOVolume* drawablep);
+
+ bool isMirrorPass() const { return mRenderingMirror; }
+
+ LLVector3 mMirrorPosition;
+ LLVector3 mMirrorNormal;
+ HeroProbeData mHeroData;
+
+private:
+ friend class LLPipeline;
+ friend class LLReflectionMapManager;
+
+ // update UBO used for rendering (call only once per render pipe flush)
+ void updateUniforms();
+
+ // bind UBO used for rendering
+
+ // render target for cube snapshots
+ // used to generate mipmaps without doing a copy-to-texture
+ LLRenderTarget mRenderTarget;
+
+ LLRenderTarget mHeroRenderTarget;
+
+ std::vector<LLRenderTarget> mMipChain;
+
+ // storage for reflection probe radiance maps (plus two scratch space cubemaps)
+ LLPointer<LLCubeMapArray> mTexture;
+
+ // vertex buffer for pushing verts to filter shaders
+ LLPointer<LLVertexBuffer> mVertexBuffer;
+
+ LLPlane mCurrentClipPlane;
+
+
+ // update the specified face of the specified probe
+ void updateProbeFace(LLReflectionMap* probe, U32 face, bool is_dynamic, F32 near_clip);
+ void generateRadiance(LLReflectionMap *probe);
+
+ // list of active reflection maps
+ std::vector<LLPointer<LLReflectionMap>> mProbes;
+
+ // list of maps being used for rendering
+ std::vector<LLReflectionMap*> mReflectionMaps;
+
+ LLReflectionMap* mUpdatingProbe = nullptr;
+
+ LLPointer<LLReflectionMap> mDefaultProbe; // default reflection probe to fall back to for pixels with no probe influences (should always be at cube index 0)
+
+ // number of reflection probes to use for rendering
+ U32 mReflectionProbeCount;
+
+ // resolution of reflection probes
+ U32 mProbeResolution = 1024;
+
+ // maximum LoD of reflection probes (mip levels - 1)
+ F32 mMaxProbeLOD = 6.f;
+
+ F32 mHeroProbeStrength = 1.f;
+ bool mIsInTransition = false;
+
+ // if true, reset all probe render state on the next update (for teleports and sky changes)
+ bool mReset = false;
+
+ bool mRenderingMirror = false;
+ std::map<int, int> mFaceUpdateList;
+
+ U32 mCurrentProbeUpdateFrame = 0;
+
+ std::vector<LLPointer<LLVOVolume>> mHeroVOList;
+ LLPointer<LLVOVolume> mNearestHero;
+
+};
+
diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp
index feb691520f..8a284686ad 100644
--- a/indra/newview/llmanip.cpp
+++ b/indra/newview/llmanip.cpp
@@ -355,8 +355,14 @@ LLVector3 LLManip::getSavedPivotPoint() const
LLVector3 LLManip::getPivotPoint()
{
- if (mObjectSelection->getFirstObject() && mObjectSelection->getObjectCount() == 1 && mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
+ LLViewerObject* object = mObjectSelection->getFirstObject();
+ if (object && mObjectSelection->getObjectCount() == 1 && mObjectSelection->getSelectType() != SELECT_TYPE_HUD)
{
+ LLSelectNode* select_node = mObjectSelection->getFirstNode();
+ if (select_node->mSelectedGLTFNode != -1)
+ {
+ return object->getGLTFNodePositionAgent(select_node->mSelectedGLTFNode);
+ }
return mObjectSelection->getFirstObject()->getPivotPositionAgent();
}
return LLSelectMgr::getInstance()->getBBoxOfSelection().getCenterAgent();
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp
index 7f37f98568..1ac1992fe0 100644
--- a/indra/newview/llmaniprotate.cpp
+++ b/indra/newview/llmaniprotate.cpp
@@ -556,6 +556,7 @@ void LLManipRotate::drag( S32 x, S32 y )
BOOL damped = mSmoothRotate;
mSmoothRotate = FALSE;
+ bool gltf_mode = false;
for (LLObjectSelection::iterator iter = mObjectSelection->begin();
iter != mObjectSelection->end(); iter++)
@@ -569,154 +570,177 @@ void LLManipRotate::drag( S32 x, S32 y )
((root_object == NULL) || !root_object->isPermanentEnforced()) &&
(object->isRootEdit() || selectNode->mIndividualSelection))
{
- if (!object->isRootEdit())
- {
- // child objects should not update if parent is selected
- LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
- if (editable_root->isSelected())
- {
- // we will be moved properly by our parent, so skip
- continue;
- }
- }
- LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
- std::vector<LLVector3>& child_positions = object->mUnselectedChildrenPositions ;
- std::vector<LLQuaternion> child_rotations;
- if (object->isRootEdit() && selectNode->mIndividualSelection)
- {
- object->saveUnselectedChildrenRotation(child_rotations) ;
- object->saveUnselectedChildrenPosition(child_positions) ;
- }
+ if (selectNode->mSelectedGLTFNode != -1)
+ {
+ LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
- if (object->getParent() && object->mDrawable.notNull())
- {
- LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
- invParentRotation.transQuat();
+ object->setGLTFNodeRotationAgent(selectNode->mSelectedGLTFNode, new_rot);
- object->setRotation(new_rot * invParentRotation, damped);
- rebuild(object);
- }
- else
- {
- object->setRotation(new_rot, damped);
- LLVOAvatar* avatar = object->asAvatar();
- if (avatar && avatar->isSelf()
- && LLSelectMgr::getInstance()->mAllowSelectAvatar
- && !object->getParent())
+ gltf_mode = true;
+ }
+ else if (!gltf_mode)
+ {
+ if (!object->isRootEdit())
{
- // Normal avatars use object's orienttion, but self uses
- // separate LLCoordFrame
- // See LVOAvatar::updateOrientation()
- if (gAgentCamera.getFocusOnAvatar())
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
{
- //Don't rotate camera with avatar
- gAgentCamera.setFocusOnAvatar(false, false, false);
+ // we will be moved properly by our parent, so skip
+ continue;
}
+ }
- LLVector3 at_axis = mAgentSelfAtAxis;
- at_axis *= mRotation;
- at_axis.mV[VZ] = 0.f;
- at_axis.normalize();
- gAgent.resetAxes(at_axis);
+ LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
+ std::vector<LLVector3>& child_positions = object->mUnselectedChildrenPositions;
+ std::vector<LLQuaternion> child_rotations;
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ object->saveUnselectedChildrenRotation(child_rotations);
+ object->saveUnselectedChildrenPosition(child_positions);
}
- rebuild(object);
- }
- // for individually selected roots, we need to counterrotate all the children
- if (object->isRootEdit() && selectNode->mIndividualSelection)
- {
- //RN: must do non-damped updates on these objects so relative rotation appears constant
- // instead of having two competing slerps making the child objects appear to "wobble"
- object->resetChildrenRotationAndPosition(child_rotations, child_positions) ;
- }
+ if (object->getParent() && object->mDrawable.notNull())
+ {
+ LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ invParentRotation.transQuat();
+
+ object->setRotation(new_rot * invParentRotation, damped);
+ rebuild(object);
+ }
+ else
+ {
+ object->setRotation(new_rot, damped);
+ LLVOAvatar* avatar = object->asAvatar();
+ if (avatar && avatar->isSelf()
+ && LLSelectMgr::getInstance()->mAllowSelectAvatar
+ && !object->getParent())
+ {
+ // Normal avatars use object's orienttion, but self uses
+ // separate LLCoordFrame
+ // See LVOAvatar::updateOrientation()
+ if (gAgentCamera.getFocusOnAvatar())
+ {
+ //Don't rotate camera with avatar
+ gAgentCamera.setFocusOnAvatar(false, false, false);
+ }
+
+ LLVector3 at_axis = mAgentSelfAtAxis;
+ at_axis *= mRotation;
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normalize();
+ gAgent.resetAxes(at_axis);
+ }
+ rebuild(object);
+ }
+
+ // for individually selected roots, we need to counterrotate all the children
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ //RN: must do non-damped updates on these objects so relative rotation appears constant
+ // instead of having two competing slerps making the child objects appear to "wobble"
+ object->resetChildrenRotationAndPosition(child_rotations, child_positions);
+ }
+ }
}
}
// update positions
- for (LLObjectSelection::iterator iter = mObjectSelection->begin();
- iter != mObjectSelection->end(); iter++)
- {
- LLSelectNode* selectNode = *iter;
- LLViewerObject* object = selectNode->getObject();
- LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
+ if (!gltf_mode)
+ {
+ for (LLObjectSelection::iterator iter = mObjectSelection->begin();
+ iter != mObjectSelection->end(); iter++)
+ {
+ LLSelectNode* selectNode = *iter;
+ LLViewerObject* object = selectNode->getObject();
+ LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
- // to avoid cumulative position changes we calculate the objects new position using its saved position
- if (object && object->permMove() && !object->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()))
- {
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
-
- LLVector3 old_position;
- LLVector3 new_position;
-
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- // need to work in drawable space to handle selected items from multiple attachments
- // (which have no shared frame of reference other than their render positions)
- LLXform* parent_xform = object->mDrawable->getXform()->getParent();
- new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
- old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
- }
- else
- {
- new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
- old_position = object->getPositionAgent();
- }
- new_position = (new_position - center) * mRotation; // new relative rotated position
- new_position += center;
-
- if (object->isRootEdit() && !object->isAttachment())
- {
- LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
- new_pos_global = LLWorld::getInstance()->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
- new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
- }
+ // to avoid cumulative position changes we calculate the objects new position using its saved position
+ if (object && object->permMove() && !object->isPermanentEnforced() &&
+ ((root_object == NULL) || !root_object->isPermanentEnforced()))
+ {
+ LLVector3 center = gAgent.getPosAgentFromGlobal(mRotationCenter);
- // for individually selected child objects
- if (!object->isRootEdit() && selectNode->mIndividualSelection)
- {
- LLViewerObject* parentp = (LLViewerObject*)object->getParent();
- if (!parentp->isSelected())
- {
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- // find position relative to render position of parent
- object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
- rebuild(object);
- }
- else
- {
- object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
- rebuild(object);
- }
- }
- }
- else if (object->isRootEdit())
- {
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- LLXform* parent_xform = object->mDrawable->getXform()->getParent();
- object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
- rebuild(object);
- }
- else
- {
- object->setPositionAgent(new_position);
- rebuild(object);
- }
- }
+ LLVector3 old_position;
+ LLVector3 new_position;
- // for individually selected roots, we need to counter-translate all unselected children
- if (object->isRootEdit() && selectNode->mIndividualSelection)
- {
- // only offset by parent's translation as we've already countered parent's rotation
- rebuild(object);
- object->resetChildrenPosition(old_position - new_position) ;
- }
- }
- }
+ if (selectNode->mSelectedGLTFNode != -1)
+ {
+
+ }
+ else
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // need to work in drawable space to handle selected items from multiple attachments
+ // (which have no shared frame of reference other than their render positions)
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
+ }
+ else
+ {
+ new_position = gAgent.getPosAgentFromGlobal(selectNode->mSavedPositionGlobal);
+ old_position = object->getPositionAgent();
+ }
+
+ new_position = (new_position - center) * mRotation; // new relative rotated position
+ new_position += center;
+
+ if (object->isRootEdit() && !object->isAttachment())
+ {
+ LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
+ new_pos_global = LLWorld::getInstance()->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
+ new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
+ }
+
+ // for individually selected child objects
+ if (!object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ LLViewerObject* parentp = (LLViewerObject*)object->getParent();
+ if (!parentp->isSelected())
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // find position relative to render position of parent
+ object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
+ rebuild(object);
+ }
+ else
+ {
+ object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
+ rebuild(object);
+ }
+ }
+ }
+ else if (object->isRootEdit())
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
+ rebuild(object);
+ }
+ else
+ {
+ object->setPositionAgent(new_position);
+ rebuild(object);
+ }
+ }
+
+ // for individually selected roots, we need to counter-translate all unselected children
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ // only offset by parent's translation as we've already countered parent's rotation
+ rebuild(object);
+ object->resetChildrenPosition(old_position - new_position);
+ }
+ }
+ }
+ }
+ }
// store changes to override updates
for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin();
@@ -725,12 +749,13 @@ void LLManipRotate::drag( S32 x, S32 y )
LLSelectNode* selectNode = *iter;
LLViewerObject*cur = selectNode->getObject();
LLViewerObject *root_object = (cur == NULL) ? NULL : cur->getRootEdit();
- if( cur->permModify() && cur->permMove() && !cur->isPermanentEnforced() &&
+
+ if( cur->permModify() && cur->permMove() && !cur->isPermanentEnforced() &&
((root_object == NULL) || !root_object->isPermanentEnforced()) &&
(!cur->isAvatar() || LLSelectMgr::getInstance()->mAllowSelectAvatar))
{
- selectNode->mLastRotation = cur->getRotation();
- selectNode->mLastPositionLocal = cur->getPosition();
+ selectNode->mLastRotation = cur->getRotation();
+ selectNode->mLastPositionLocal = cur->getPosition();
}
}
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
index cafb3fef67..9fc5ad502c 100644
--- a/indra/newview/llmaniptranslate.cpp
+++ b/indra/newview/llmaniptranslate.cpp
@@ -655,114 +655,125 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
LLVector3d clamped_relative_move = axis_magnitude * axis_d; // scalar multiply
LLVector3 clamped_relative_move_f = (F32)axis_magnitude * axis_f; // scalar multiply
- for (LLObjectSelection::iterator iter = mObjectSelection->begin();
- iter != mObjectSelection->end(); iter++)
- {
- LLSelectNode* selectNode = *iter;
- LLViewerObject* object = selectNode->getObject();
-
- // Only apply motion to root objects and objects selected
- // as "individual".
- if (!object->isRootEdit() && !selectNode->mIndividualSelection)
- {
- continue;
- }
-
- if (!object->isRootEdit())
- {
- // child objects should not update if parent is selected
- LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
- if (editable_root->isSelected())
- {
- // we will be moved properly by our parent, so skip
- continue;
- }
- }
-
- LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
- if (object->permMove() && !object->isPermanentEnforced() &&
- ((root_object == NULL) || !root_object->isPermanentEnforced()))
- {
- // handle attachments in local space
- if (object->isAttachment() && object->mDrawable.notNull())
- {
- // calculate local version of relative move
- LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
- objWorldRotation.transQuat();
-
- LLVector3 old_position_local = object->getPosition();
- LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
-
- //RN: I forget, but we need to do this because of snapping which doesn't often result
- // in position changes even when the mouse moves
- object->setPosition(new_position_local);
- rebuild(object);
- gAgentAvatarp->clampAttachmentPositions();
- new_position_local = object->getPosition();
-
- if (selectNode->mIndividualSelection)
- {
- // counter-translate child objects if we are moving the root as an individual
- object->resetChildrenPosition(old_position_local - new_position_local, TRUE) ;
- }
- }
- else
- {
- // compute new position to send to simulators, but don't set it yet.
- // We need the old position to know which simulator to send the move message to.
- LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move;
-
- // Don't let object centers go too far underground
- F64 min_height = LLWorld::getInstance()->getMinAllowedZ(object, object->getPositionGlobal());
- if (new_position_global.mdV[VZ] < min_height)
- {
- new_position_global.mdV[VZ] = min_height;
- }
-
- // For safety, cap heights where objects can be dragged
- if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
- {
- new_position_global.mdV[VZ] = MAX_OBJECT_Z;
- }
-
- // Grass is always drawn on the ground, so clamp its position to the ground
- if (object->getPCode() == LL_PCODE_LEGACY_GRASS)
- {
- new_position_global.mdV[VZ] = LLWorld::getInstance()->resolveLandHeightGlobal(new_position_global) + 1.f;
- }
-
- if (object->isRootEdit())
- {
- new_position_global = LLWorld::getInstance()->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
- }
-
- // PR: Only update if changed
- LLVector3 old_position_agent = object->getPositionAgent();
- LLVector3 new_position_agent = gAgent.getPosAgentFromGlobal(new_position_global);
- if (object->isRootEdit())
- {
- // finally, move parent object after children have calculated new offsets
- object->setPositionAgent(new_position_agent);
- rebuild(object);
- }
- else
- {
- LLViewerObject* root_object = object->getRootEdit();
- new_position_agent -= root_object->getPositionAgent();
- new_position_agent = new_position_agent * ~root_object->getRotation();
- object->setPositionParent(new_position_agent, FALSE);
- rebuild(object);
- }
+ for (LLObjectSelection::iterator iter = mObjectSelection->begin();
+ iter != mObjectSelection->end(); iter++)
+ {
+ LLSelectNode* selectNode = *iter;
+ LLViewerObject* object = selectNode->getObject();
+
+ if (selectNode->mSelectedGLTFNode != -1)
+ {
+ // manipulating a GLTF node
+ clamped_relative_move_f -= selectNode->mLastMoveLocal;
+ object->moveGLTFNode(selectNode->mSelectedGLTFNode, clamped_relative_move_f);
+ selectNode->mLastMoveLocal += clamped_relative_move_f;
+ }
+ else
+ {
+ // Only apply motion to root objects and objects selected
+ // as "individual".
+ if (!object->isRootEdit() && !selectNode->mIndividualSelection)
+ {
+ continue;
+ }
+
+ if (!object->isRootEdit())
+ {
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
+ {
+ // we will be moved properly by our parent, so skip
+ continue;
+ }
+ }
+
+ LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
+ if (object->permMove() && !object->isPermanentEnforced() &&
+ ((root_object == NULL) || !root_object->isPermanentEnforced()))
+ {
+ // handle attachments in local space
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // calculate local version of relative move
+ LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ objWorldRotation.transQuat();
+
+ LLVector3 old_position_local = object->getPosition();
+ LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
+
+ //RN: I forget, but we need to do this because of snapping which doesn't often result
+ // in position changes even when the mouse moves
+ object->setPosition(new_position_local);
+ rebuild(object);
+ gAgentAvatarp->clampAttachmentPositions();
+ new_position_local = object->getPosition();
+
+ if (selectNode->mIndividualSelection)
+ {
+ // counter-translate child objects if we are moving the root as an individual
+ object->resetChildrenPosition(old_position_local - new_position_local, TRUE);
+ }
+ }
+ else
+ {
+ // compute new position to send to simulators, but don't set it yet.
+ // We need the old position to know which simulator to send the move message to.
+ LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move;
+
+ // Don't let object centers go too far underground
+ F64 min_height = LLWorld::getInstance()->getMinAllowedZ(object, object->getPositionGlobal());
+ if (new_position_global.mdV[VZ] < min_height)
+ {
+ new_position_global.mdV[VZ] = min_height;
+ }
+
+ // For safety, cap heights where objects can be dragged
+ if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
+ {
+ new_position_global.mdV[VZ] = MAX_OBJECT_Z;
+ }
+
+ // Grass is always drawn on the ground, so clamp its position to the ground
+ if (object->getPCode() == LL_PCODE_LEGACY_GRASS)
+ {
+ new_position_global.mdV[VZ] = LLWorld::getInstance()->resolveLandHeightGlobal(new_position_global) + 1.f;
+ }
+
+ if (object->isRootEdit())
+ {
+ new_position_global = LLWorld::getInstance()->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
+ }
+
+ // PR: Only update if changed
+ LLVector3 old_position_agent = object->getPositionAgent();
+ LLVector3 new_position_agent = gAgent.getPosAgentFromGlobal(new_position_global);
+ if (object->isRootEdit())
+ {
+ // finally, move parent object after children have calculated new offsets
+ object->setPositionAgent(new_position_agent);
+ rebuild(object);
+ }
+ else
+ {
+ LLViewerObject* root_object = object->getRootEdit();
+ new_position_agent -= root_object->getPositionAgent();
+ new_position_agent = new_position_agent * ~root_object->getRotation();
+ object->setPositionParent(new_position_agent, FALSE);
+ rebuild(object);
+ }
+
+ if (selectNode->mIndividualSelection)
+ {
+ // counter-translate child objects if we are moving the root as an individual
+ object->resetChildrenPosition(old_position_agent - new_position_agent, TRUE);
+ }
+ }
+ selectNode->mLastPositionLocal = object->getPosition();
+ }
+ }
+ }
- if (selectNode->mIndividualSelection)
- {
- // counter-translate child objects if we are moving the root as an individual
- object->resetChildrenPosition(old_position_agent - new_position_agent, TRUE) ;
- }
- }
- selectNode->mLastPositionLocal = object->getPosition();
- }
- }
LLSelectMgr::getInstance()->updateSelectionCenter();
gAgentCamera.clearFocusObject();
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index b7828db3a9..84446b626a 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -489,11 +489,10 @@ BOOL LLMaterialEditor::postBuild()
}
else
{
- S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
- getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
- getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
- getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
- getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
+ getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched)));
+ getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched)));
+ getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched)));
+ getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched)));
}
boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void* userData)
@@ -842,25 +841,24 @@ void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag)
setCanSave(false);
}
- S32 upload_texture_count = 0;
- if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId())
+ mExpectedUploadCost = 0;
+ if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId() && mBaseColorFetched)
{
- upload_texture_count++;
+ mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched);
}
- if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId())
+ if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId() && mMetallicRoughnessFetched)
{
- upload_texture_count++;
+ mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched);
}
- if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId())
+ if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId() && mEmissiveFetched)
{
- upload_texture_count++;
+ mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched);
}
- if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId())
+ if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId() && mNormalFetched)
{
- upload_texture_count++;
+ mExpectedUploadCost += LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched);
}
- mExpectedUploadCost = upload_texture_count * LLAgentBenefitsMgr::current().getTextureUploadCost();
getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost));
}
@@ -1870,7 +1868,7 @@ static void pack_textures(
if (normal_img)
{
// create a losslessly compressed version of the normal map
- normal_j2c = LLViewerTextureList::convertToUploadFile(normal_img, 1024, false, true);
+ normal_j2c = LLViewerTextureList::convertToUploadFile(normal_img, 2048, false, true);
LL_DEBUGS("MaterialEditor") << "Normal: " << normal_j2c->getDataSize() << LL_ENDL;
}
@@ -2124,7 +2122,7 @@ bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector<Pe
// Look for the item to base permissions off of
item_out = nullptr;
- const bool blank_material = func.mMaterialId == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ const bool blank_material = func.mMaterialId == BLANK_MATERIAL_ASSET_ID;
if (!blank_material)
{
LLAssetIDMatchesWithPerms item_has_perms(func.mMaterialId, ops);
@@ -3495,8 +3493,7 @@ void LLMaterialEditor::saveTexture(LLImageJ2C* img, const std::string& name, con
std::string buffer;
buffer.assign((const char*) img->getData(), img->getDataSize());
- U32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
-
+ U32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(img);
LLSD key = getKey();
std::function<bool(LLUUID itemId, LLSD response, std::string reason)> failed_upload([key](LLUUID assetId, LLSD response, std::string reason)
{
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index a527ebe47f..5ee25b76a2 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -328,7 +328,7 @@ BOOL LLPanelFace::postBuild()
if (pbr_ctrl)
{
pbr_ctrl->setDefaultImageAssetID(LLUUID::null);
- pbr_ctrl->setBlankImageAssetID(LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID);
+ pbr_ctrl->setBlankImageAssetID(BLANK_MATERIAL_ASSET_ID);
pbr_ctrl->setCommitCallback(boost::bind(&LLPanelFace::onCommitPbr, this, _2));
pbr_ctrl->setOnCancelCallback(boost::bind(&LLPanelFace::onCancelPbr, this, _2));
pbr_ctrl->setOnSelectCallback(boost::bind(&LLPanelFace::onSelectPbr, this, _2));
@@ -1793,6 +1793,7 @@ 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);
}
@@ -2028,7 +2029,7 @@ void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material,
{
mVOInventoryListener = nullptr;
}
- if (!identical_pbr || pbr_id.isNull() || pbr_id == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID)
+ if (!identical_pbr || pbr_id.isNull() || pbr_id == BLANK_MATERIAL_ASSET_ID)
{
mAgentInventoryListener = nullptr;
}
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
index a64b85815f..05860b995f 100644
--- a/indra/newview/llpanelface.h
+++ b/indra/newview/llpanelface.h
@@ -145,6 +145,7 @@ protected:
void sendTexGen(); // applies and sends bump map
void sendShiny(U32 shininess); // applies and sends shininess
void sendFullbright(); // applies and sends full bright
+
void sendGlow();
void alignTestureLayer();
@@ -234,7 +235,7 @@ protected:
static void onCommitShiny( LLUICtrl* ctrl, void* userdata);
static void onCommitAlphaMode( LLUICtrl* ctrl, void* userdata);
static void onCommitFullbright( LLUICtrl* ctrl, void* userdata);
- static void onCommitGlow( LLUICtrl* ctrl, void *userdata);
+ static void onCommitGlow( LLUICtrl* ctrl, void *userdata);
static void onCommitPlanarAlign( LLUICtrl* ctrl, void* userdata);
static void onCommitRepeatsPerMeter( LLUICtrl* ctrl, void* userinfo);
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 20241aac24..2db431d165 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -276,14 +276,12 @@ BOOL LLPanelMainInventory::postBuild()
initListCommandsHandlers();
- const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost());
const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost());
const std::string animation_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getAnimationUploadCost());
LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get();
if (menu)
{
- menu->getChild<LLMenuItemGL>("Upload Image")->setLabelArg("[COST]", texture_upload_cost_str);
menu->getChild<LLMenuItemGL>("Upload Sound")->setLabelArg("[COST]", sound_upload_cost_str);
menu->getChild<LLMenuItemGL>("Upload Animation")->setLabelArg("[COST]", animation_upload_cost_str);
}
@@ -2229,11 +2227,9 @@ void LLPanelMainInventory::setUploadCostIfNeeded()
LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get();
if(mNeedUploadCost && menu)
{
- const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost());
const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost());
const std::string animation_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getAnimationUploadCost());
- menu->getChild<LLView>("Upload Image")->setLabelArg("[COST]", texture_upload_cost_str);
menu->getChild<LLView>("Upload Sound")->setLabelArg("[COST]", sound_upload_cost_str);
menu->getChild<LLView>("Upload Animation")->setLabelArg("[COST]", animation_upload_cost_str);
}
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index 595609b4de..91c33b461f 100644
--- a/indra/newview/llpanelvolume.cpp
+++ b/indra/newview/llpanelvolume.cpp
@@ -150,7 +150,7 @@ BOOL LLPanelVolume::postBuild()
// REFLECTION PROBE Parameters
{
childSetCommitCallback("Reflection Probe", onCommitIsReflectionProbe, this);
- childSetCommitCallback("Probe Dynamic", onCommitProbe, this);
+ childSetCommitCallback("Probe Update Type", onCommitProbe, this);
childSetCommitCallback("Probe Volume Type", onCommitProbe, this);
childSetCommitCallback("Probe Ambiance", onCommitProbe, this);
childSetCommitCallback("Probe Near Clip", onCommitProbe, this);
@@ -304,7 +304,7 @@ void LLPanelVolume::getState( )
getChildView("select_single")->setVisible(true);
getChildView("select_single")->setEnabled(true);
}
-
+
// Light properties
BOOL is_light = volobjp && volobjp->getIsLight();
getChild<LLUICtrl>("Light Checkbox Ctrl")->setValue(is_light);
@@ -392,17 +392,25 @@ void LLPanelVolume::getState( )
bool probe_enabled = is_probe && editable && single_volume;
+ bool mirrors_enabled = LLPipeline::RenderMirrors;
+
+ getChildView("Probe Update Type")->setVisible(mirrors_enabled);
+ getChildView("Probe Update Label")->setVisible(mirrors_enabled);
+ getChildView("Probe Dynamic")->setVisible(!mirrors_enabled);
+
getChildView("Probe Dynamic")->setEnabled(probe_enabled);
+ getChildView("Probe Update Type")->setEnabled(probe_enabled);
getChildView("Probe Volume Type")->setEnabled(probe_enabled);
getChildView("Probe Ambiance")->setEnabled(probe_enabled);
getChildView("Probe Near Clip")->setEnabled(probe_enabled);
+ getChildView("Probe Update Label")->setEnabled(probe_enabled);
if (!probe_enabled)
{
getChild<LLComboBox>("Probe Volume Type", true)->clear();
getChild<LLSpinCtrl>("Probe Ambiance", true)->clear();
getChild<LLSpinCtrl>("Probe Near Clip", true)->clear();
- getChild<LLCheckBoxCtrl>("Probe Dynamic", true)->clear();
+ getChild<LLComboBox>("Probe Update Type", true)->clear();
}
else
{
@@ -416,10 +424,30 @@ void LLPanelVolume::getState( )
volume_type = "Sphere";
}
+
+ std::string update_type = "Static";
+
+ if (volobjp->getReflectionProbeIsDynamic() && !volobjp->getReflectionProbeIsMirror())
+ {
+ update_type = "Dynamic";
+ }
+ else if (volobjp->getReflectionProbeIsMirror() && !volobjp->getReflectionProbeIsDynamic())
+ {
+ update_type = "Mirror";
+
+ }
+ else if (volobjp->getReflectionProbeIsDynamic() && volobjp->getReflectionProbeIsMirror())
+ {
+ update_type = "Dynamic Mirror";
+ }
+
+ getChildView("Probe Ambiance")->setEnabled(update_type != "Mirror");
+ getChildView("Probe Near Clip")->setEnabled(update_type != "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());
- getChild<LLCheckBoxCtrl>("Probe Dynamic", true)->setValue(volobjp->getReflectionProbeIsDynamic());
+ getChild<LLComboBox>("Probe Update Type", true)->setValue(update_type);
}
// Animated Mesh
@@ -706,7 +734,7 @@ void LLPanelVolume::clearCtrls()
getChildView("Reflection Probe")->setEnabled(false);;
getChildView("Probe Volume Type")->setEnabled(false);
- getChildView("Probe Dynamic")->setEnabled(false);
+ getChildView("Probe Update Type")->setEnabled(false);
getChildView("Probe Ambiance")->setEnabled(false);
getChildView("Probe Near Clip")->setEnabled(false);
getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(false);
@@ -1174,6 +1202,7 @@ void LLPanelVolume::onCopyLight()
clipboard["reflection_probe"]["ambiance"] = volobjp->getReflectionProbeAmbiance();
clipboard["reflection_probe"]["near_clip"] = volobjp->getReflectionProbeNearClip();
clipboard["reflection_probe"]["dynamic"] = volobjp->getReflectionProbeIsDynamic();
+ clipboard["reflection_probe"]["mirror"] = volobjp->getReflectionProbeIsMirror();
}
mClipboardParams["light"] = clipboard;
@@ -1231,6 +1260,7 @@ void LLPanelVolume::onPasteLight()
volobjp->setReflectionProbeAmbiance((F32)clipboard["reflection_probe"]["ambiance"].asReal());
volobjp->setReflectionProbeNearClip((F32)clipboard["reflection_probe"]["near_clip"].asReal());
volobjp->setReflectionProbeIsDynamic(clipboard["reflection_probe"]["dynamic"].asBoolean());
+ volobjp->setReflectionProbeIsMirror(clipboard["reflection_probe"]["mirror"].asBoolean());
}
else
{
@@ -1399,7 +1429,16 @@ void LLPanelVolume::onCommitProbe(LLUICtrl* ctrl, void* userdata)
volobjp->setReflectionProbeAmbiance((F32)self->getChild<LLUICtrl>("Probe Ambiance")->getValue().asReal());
volobjp->setReflectionProbeNearClip((F32)self->getChild<LLUICtrl>("Probe Near Clip")->getValue().asReal());
- volobjp->setReflectionProbeIsDynamic(self->getChild<LLUICtrl>("Probe Dynamic")->getValue().asBoolean());
+
+ std::string update_type = self->getChild<LLUICtrl>("Probe Update Type")->getValue().asString();
+
+ bool is_mirror = update_type.find("Mirror") != std::string::npos;
+
+ volobjp->setReflectionProbeIsDynamic(update_type.find("Dynamic") != std::string::npos);
+ volobjp->setReflectionProbeIsMirror(is_mirror);
+
+ self->getChildView("Probe Ambiance")->setEnabled(!is_mirror);
+ self->getChildView("Probe Near Clip")->setEnabled(!is_mirror);
std::string shape_type = self->getChild<LLUICtrl>("Probe Volume Type")->getValue().asString();
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
index 01b7ebb75c..aafefa918f 100644
--- a/indra/newview/llpanelvolume.h
+++ b/indra/newview/llpanelvolume.h
@@ -57,7 +57,7 @@ public:
void refresh();
void sendIsLight();
-
+
// when an object is becoming a refleciton probe, present a dialog asking for confirmation
// otherwise, send the reflection probe update immediately
void sendIsReflectionProbe();
diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp
index a26445b4bc..30a61144b9 100644
--- a/indra/newview/llreflectionmap.cpp
+++ b/indra/newview/llreflectionmap.cpp
@@ -49,7 +49,7 @@ LLReflectionMap::~LLReflectionMap()
}
}
-void LLReflectionMap::update(U32 resolution, U32 face)
+void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic, F32 near_clip, bool useClipPlane, LLPlane clipPlane)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
mLastUpdateTime = gFrameTimeSeconds;
@@ -63,7 +63,10 @@ void LLReflectionMap::update(U32 resolution, U32 face)
{
resolution /= 2;
}
- gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, getNearClip(), getIsDynamic());
+
+ F32 clip = (near_clip > 0) ? near_clip : getNearClip();
+
+ gViewerWindow->cubeSnapshot(LLVector3(mOrigin), mCubeArray, mCubeIndex, face, clip, getIsDynamic() || force_dynamic, useClipPlane, clipPlane);
}
void LLReflectionMap::autoAdjustOrigin()
@@ -163,7 +166,7 @@ void LLReflectionMap::autoAdjustOrigin()
}
}
- else if (mViewerObject)
+ else if (mViewerObject && !mViewerObject->isDead())
{
mPriority = 1;
mOrigin.load3(mViewerObject->getPositionAgent().mV);
diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h
index 7ea0fe6187..9e888f20d0 100644
--- a/indra/newview/llreflectionmap.h
+++ b/indra/newview/llreflectionmap.h
@@ -36,6 +36,15 @@ class alignas(16) LLReflectionMap : public LLRefCount
{
LL_ALIGN_NEW
public:
+
+ enum class ProbeType
+ {
+ ALL = 0,
+ RADIANCE,
+ IRRADIANCE,
+ REFLECTION
+ };
+
// allocate an environment map of the given resolution
LLReflectionMap();
@@ -43,7 +52,7 @@ public:
// update this environment map
// resolution - size of cube map to generate
- void update(U32 resolution, U32 face);
+ void update(U32 resolution, U32 face, bool force_dynamic = false, F32 near_clip = -1.f, bool useClipPlane = false, LLPlane clipPlane = LLPlane(LLVector3(0, 0, 0), LLVector3(0, 0, 1)));
// for volume partition probes, try to place this probe in the best spot
void autoAdjustOrigin();
@@ -127,5 +136,7 @@ public:
GLuint mOcclusionQuery = 0;
bool mOccluded = false;
U32 mOcclusionPendingFrames = 0;
+
+ ProbeType mType;
};
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index 69674417c1..5a8f44f5c2 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -27,6 +27,9 @@
#include "llviewerprecompiledheaders.h"
#include "llreflectionmapmanager.h"
+
+#include <vector>
+
#include "llviewercamera.h"
#include "llspatialpartition.h"
#include "llviewerregion.h"
@@ -35,6 +38,86 @@
#include "llviewercontrol.h"
#include "llenvironment.h"
#include "llstartup.h"
+#include "llviewermenufile.h"
+#include "llnotificationsutil.h"
+
+#if LL_WINDOWS
+#pragma warning (push)
+#pragma warning (disable : 4702) // compiler complains unreachable code
+#endif
+#define TINYEXR_USE_MINIZ 0
+#include "zlib.h"
+#define TINYEXR_IMPLEMENTATION
+#include "tinyexr/tinyexr.h"
+#if LL_WINDOWS
+#pragma warning (pop)
+#endif
+
+LLPointer<LLImageGL> gEXRImage;
+
+void load_exr(const std::string& filename)
+{
+ // reset reflection maps when previewing a new HDRI
+ gPipeline.mReflectionMapManager.reset();
+ gPipeline.mReflectionMapManager.initReflectionMaps();
+
+ float* out; // width * height * RGBA
+ int width;
+ int height;
+ const char* err = NULL; // or nullptr in C++11
+
+ int ret = LoadEXRWithLayer(&out, &width, &height, filename.c_str(), /* layername */ nullptr, &err);
+ if (ret == TINYEXR_SUCCESS)
+ {
+ U32 texName = 0;
+ LLImageGL::generateTextures(1, &texName);
+
+ gEXRImage = new LLImageGL(texName, 4, GL_TEXTURE_2D, GL_RGB16F, GL_RGB16F, GL_FLOAT, LLTexUnit::TAM_CLAMP);
+ gEXRImage->setHasMipMaps(TRUE);
+ gEXRImage->setUseMipMaps(TRUE);
+ gEXRImage->setFilteringOption(LLTexUnit::TFO_TRILINEAR);
+
+ gGL.getTexUnit(0)->bind(gEXRImage);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGBA, GL_FLOAT, out);
+ free(out); // release memory of image data
+
+ glGenerateMipmap(GL_TEXTURE_2D);
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ }
+ else
+ {
+ LLSD notif_args;
+ notif_args["WHAT"] = filename;
+ notif_args["REASON"] = "Unknown";
+ if (err)
+ {
+ notif_args["REASON"] = std::string(err);
+ FreeEXRErrorMessage(err); // release memory of error message.
+ }
+ LLNotificationsUtil::add("CannotLoad", notif_args);
+ }
+}
+
+void hdri_preview()
+{
+ 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)
+ {
+ load_exr(filenames[0]);
+ }
+ },
+ LLFilePicker::FFLOAD_HDRI,
+ true);
+}
extern BOOL gCubeSnapshot;
extern BOOL gTeleportDisplay;
@@ -130,6 +213,11 @@ void LLReflectionMapManager::update()
return;
}
+ if (mPaused && gFrameTimeSeconds > mResumeTime)
+ {
+ resume();
+ }
+
initReflectionMaps();
if (!mRenderTarget.isComplete())
@@ -306,8 +394,8 @@ void LLReflectionMapManager::update()
}
}
- if (realtime &&
- closestDynamic == nullptr &&
+ if (realtime &&
+ closestDynamic == nullptr &&
probe->mCubeIndex != -1 &&
probe->getIsDynamic())
{
@@ -322,7 +410,7 @@ void LLReflectionMapManager::update()
// should do a full irradiance pass on "odd" frames and a radiance pass on "even" frames
closestDynamic->autoAdjustOrigin();
- // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set
+ // store and override the value of "isRadiancePass" -- parts of the render pipe rely on "isRadiancePass" to set
// lighting values etc
bool radiance_pass = isRadiancePass();
mRadiancePass = mRealtimeRadiancePass;
@@ -573,7 +661,7 @@ void LLReflectionMapManager::doProbeUpdate()
// Do the reflection map update render passes.
// For every 12 calls of this function, one complete reflection probe radiance map and irradiance map is generated
-// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate
+// First six passes render the scene with direct lighting only into a scratch space cube map at the end of the cube map array and generate
// a simple mip chain (not convolution filter).
// At the end of these passes, an irradiance map is generated for this probe and placed into the irradiance cube map array at the index for this probe
// The next six passes render the scene with both radiance and irradiance into the same scratch space cube map and generate a simple mip chain.
@@ -734,6 +822,7 @@ void LLReflectionMapManager::updateProbeFace(LLReflectionMap* probe, U32 face)
mTexture->bind(channel);
gRadianceGenProgram.uniform1i(sSourceIdx, sourceIdx);
gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mMaxProbeLOD);
+ gRadianceGenProgram.uniform1f(LLShaderMgr::REFLECTION_PROBE_STRENGTH, 1.f);
U32 res = mMipChain[0].getWidth();
@@ -827,9 +916,10 @@ void LLReflectionMapManager::reset()
mReset = true;
}
-void LLReflectionMapManager::pause()
+void LLReflectionMapManager::pause(F32 duration)
{
mPaused = true;
+ mResumeTime = gFrameTimeSeconds + duration;
}
void LLReflectionMapManager::resume()
@@ -898,33 +988,41 @@ void LLReflectionMapManager::updateUniforms()
// see class3/deferred/reflectionProbeF.glsl
struct ReflectionProbeData
{
- // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of
+ // for box probes, matrix that transforms from camera space to a [-1, 1] cube representing the bounding box of
// the box probe
- LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
+ LLMatrix4 refBox[LL_MAX_REFLECTION_PROBE_COUNT];
+
+ LLMatrix4 heroBox;
// for sphere probes, origin (xyz) and radius (w) of refmaps in clip space
- LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
+ LLVector4 refSphere[LL_MAX_REFLECTION_PROBE_COUNT];
- // extra parameters
+ // extra parameters
// x - irradiance scale
// y - radiance scale
// z - fade in
// w - znear
LLVector4 refParams[LL_MAX_REFLECTION_PROBE_COUNT];
+ LLVector4 heroSphere;
+
// indices used by probe:
// [i][0] - cubemap array index for this probe
// [i][1] - index into "refNeighbor" for probes that intersect this probe
// [i][2] - number of probes that intersect this probe, or -1 for no neighbors
// [i][3] - priority (probe type stored in sign bit - positive for spheres, negative for boxes)
- GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4];
+ GLint refIndex[LL_MAX_REFLECTION_PROBE_COUNT][4];
// list of neighbor indices
- GLint refNeighbor[4096];
+ GLint refNeighbor[4096];
GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth
// numbrer of active refmaps
- GLint refmapCount;
+ GLint refmapCount;
+
+ GLint heroShape;
+ GLint heroMipCount;
+ GLint heroProbeCount;
};
mReflectionMaps.resize(mReflectionProbeCount);
@@ -954,9 +1052,8 @@ void LLReflectionMapManager::updateUniforms()
LLEnvironment& environment = LLEnvironment::instance();
LLSettingsSky::ptr_t psky = environment.getCurrentSky();
- static LLCachedControl<F32> cloud_shadow_scale(gSavedSettings, "RenderCloudShadowAmbianceFactor", 0.125f);
static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
- F32 minimum_ambiance = psky->getTotalReflectionProbeAmbiance(cloud_shadow_scale, should_auto_adjust);
+ F32 minimum_ambiance = psky->getReflectionProbeAmbiance(should_auto_adjust);
bool is_ambiance_pass = gCubeSnapshot && !isRadiancePass();
F32 ambscale = is_ambiance_pass ? 0.f : 1.f;
@@ -1012,7 +1109,6 @@ void LLReflectionMapManager::updateUniforms()
{
refmap->mRadius = refmap->mViewerObject->getScale().mV[0] * 0.5f;
}
-
}
modelview.affineTransform(refmap->mOrigin, oa);
rpd.refSphere[count].set(oa.getF32ptr());
@@ -1115,6 +1211,16 @@ void LLReflectionMapManager::updateUniforms()
rpd.refmapCount = count;
+ gPipeline.mHeroProbeManager.updateUniforms();
+
+ // Get the hero data.
+
+ rpd.heroBox = gPipeline.mHeroProbeManager.mHeroData.heroBox;
+ rpd.heroSphere = gPipeline.mHeroProbeManager.mHeroData.heroSphere;
+ rpd.heroShape = gPipeline.mHeroProbeManager.mHeroData.heroShape;
+ rpd.heroMipCount = gPipeline.mHeroProbeManager.mHeroData.heroMipCount;
+ rpd.heroProbeCount = gPipeline.mHeroProbeManager.mHeroData.heroProbeCount;
+
//copy rpd into uniform buffer object
if (mUBO == 0)
{
@@ -1152,7 +1258,7 @@ void LLReflectionMapManager::setUniforms()
}
if (mUBO == 0)
- {
+ {
updateUniforms();
}
glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
@@ -1262,6 +1368,8 @@ void LLReflectionMapManager::initReflectionMaps()
if (mTexture.isNull() || mReflectionProbeCount != count || mReset)
{
+ gEXRImage = nullptr;
+
mReset = false;
mReflectionProbeCount = count;
mProbeResolution = nhpo2(llclamp(gSavedSettings.getU32("RenderReflectionProbeResolution"), (U32)64, (U32)512));
@@ -1319,7 +1427,6 @@ void LLReflectionMapManager::initReflectionMaps()
mDefaultProbe->mComplete = default_complete;
touch_default_probe(mDefaultProbe);
-
}
if (mVertexBuffer.isNull())
@@ -1343,8 +1450,8 @@ void LLReflectionMapManager::initReflectionMaps()
}
}
-void LLReflectionMapManager::cleanup()
-{
+void LLReflectionMapManager::cleanup()
+{
mVertexBuffer = nullptr;
mRenderTarget.release();
@@ -1383,3 +1490,39 @@ void LLReflectionMapManager::doOcclusion()
}
}
}
+
+void LLReflectionMapManager::forceDefaultProbeAndUpdateUniforms(bool force)
+{
+ static std::vector<bool> mProbeWasOccluded;
+
+ if (force)
+ {
+ llassert(mProbeWasOccluded.empty());
+
+ for (size_t i = 0; i < mProbes.size(); ++i)
+ {
+ auto& probe = mProbes[i];
+ mProbeWasOccluded.push_back(probe->mOccluded);
+ if (probe != nullptr && probe != mDefaultProbe)
+ {
+ probe->mOccluded = true;
+ }
+ }
+
+ updateUniforms();
+ }
+ else
+ {
+ llassert(mProbes.size() == mProbeWasOccluded.size());
+
+ const size_t n = llmin(mProbes.size(), mProbeWasOccluded.size());
+ for (size_t i = 0; i < n; ++i)
+ {
+ auto& probe = mProbes[i];
+ llassert(probe->mOccluded == (probe != mDefaultProbe));
+ probe->mOccluded = mProbeWasOccluded[i];
+ }
+ mProbeWasOccluded.clear();
+ mProbeWasOccluded.shrink_to_fit();
+ }
+}
diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h
index b77a33da89..5c0651bc24 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -43,21 +43,23 @@ class LLViewerObject;
// reflection probe mininum scale
#define LL_REFLECTION_PROBE_MINIMUM_SCALE 1.f;
+void renderReflectionProbe(LLReflectionMap* probe);
+
class alignas(16) LLReflectionMapManager
{
LL_ALIGN_NEW
public:
- enum class DetailLevel
+ enum class DetailLevel
{
STATIC_ONLY = 0,
STATIC_AND_DYNAMIC,
REALTIME = 2
};
- // allocate an environment map of the given resolution
+ // allocate an environment map of the given resolution
LLReflectionMapManager();
- // release any GL state
+ // release any GL state
void cleanup();
// maintain reflection probes
@@ -85,7 +87,8 @@ public:
void reset();
// pause all updates other than the default probe
- void pause();
+ // duration - number of seconds to pause (default 10)
+ void pause(F32 duration = 10.f);
// unpause (see pause)
void resume();
@@ -106,8 +109,14 @@ public:
// perform occlusion culling on all active reflection probes
void doOcclusion();
+ // *HACK: "cull" all reflection probes except the default one. Only call
+ // this if you don't intend to call updateUniforms directly. Call again
+ // with false when done.
+ void forceDefaultProbeAndUpdateUniforms(bool force = true);
+
private:
friend class LLPipeline;
+ friend class LLHeroProbeManager;
// initialize mCubeFree array to default values
void initCubeFree();
@@ -119,7 +128,7 @@ private:
// returns -1 if allocation failed
S32 allocateCubeIndex();
- // update the neighbors of the given probe
+ // update the neighbors of the given probe
void updateNeighbors(LLReflectionMap* probe);
// update UBO used for rendering (call only once per render pipe flush)
@@ -200,5 +209,6 @@ private:
// if true, only update the default probe
bool mPaused = false;
+ F32 mResumeTime = 0.f;
};
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index b50c0f60bd..20b00974e1 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -456,7 +456,7 @@ void LLSelectMgr::overrideAvatarUpdates()
//-----------------------------------------------------------------------------
// Select just the object, not any other group members.
//-----------------------------------------------------------------------------
-LLObjectSelectionHandle LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face)
+LLObjectSelectionHandle LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face, S32 gltf_node, S32 gltf_primitive)
{
llassert( object );
@@ -481,7 +481,7 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectOnly(LLViewerObject* object, S3
// Place it in the list and tag it.
// This will refresh dialogs.
- addAsIndividual(object, face);
+ addAsIndividual(object, face, TRUE, gltf_node, gltf_primitive);
// Stop the object from moving (this anticipates changes on the
// simulator in LLTask::userSelect)
@@ -1033,7 +1033,7 @@ void LLSelectMgr::addAsFamily(std::vector<LLViewerObject*>& objects, BOOL add_to
//-----------------------------------------------------------------------------
// addAsIndividual() - a single object, face, etc
//-----------------------------------------------------------------------------
-void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable)
+void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable, S32 gltf_node, S32 gltf_primitive)
{
// check to see if object is already in list
LLSelectNode *nodep = mSelectedObjects->findNode(objectp);
@@ -1080,6 +1080,13 @@ void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoab
return;
}
+ // Handle glTF node selection
+ if (gltf_node >= 0)
+ {
+ nodep->selectGLTFNode(gltf_node, gltf_primitive, TRUE);
+
+ }
+
saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
updateSelectionCenter();
dialog_refresh_all();
@@ -1782,7 +1789,7 @@ bool LLObjectSelection::applyRestrictedPbrMaterialToTEs(LLViewerInventoryItem* i
LLUUID asset_id = item->getAssetUUID();
if (asset_id.isNull())
{
- asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ asset_id = BLANK_MATERIAL_ASSET_ID;
}
bool material_copied_all_faces = true;
@@ -1987,7 +1994,7 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
asset_id = mItem->getAssetUUID();
if (asset_id.isNull())
{
- asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ asset_id = BLANK_MATERIAL_ASSET_ID;
}
}
@@ -5211,46 +5218,57 @@ void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type)
{
return true; // skip
}
- selectNode->mSavedPositionLocal = object->getPosition();
- if (object->isAttachment())
- {
- if (object->isRootEdit())
- {
- LLXform* parent_xform = object->mDrawable->getXform()->getParent();
- if (parent_xform)
- {
- selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
- }
- else
- {
- selectNode->mSavedPositionGlobal = object->getPositionGlobal();
- }
- }
- else
- {
- LLViewerObject* attachment_root = (LLViewerObject*)object->getParent();
- LLXform* parent_xform = attachment_root ? attachment_root->mDrawable->getXform()->getParent() : NULL;
- if (parent_xform)
- {
- LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
- LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation());
- selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos);
- }
- else
- {
- selectNode->mSavedPositionGlobal = object->getPositionGlobal();
- }
- }
- selectNode->mSavedRotation = object->getRenderRotation();
- }
- else
- {
- selectNode->mSavedPositionGlobal = object->getPositionGlobal();
- selectNode->mSavedRotation = object->getRotationRegion();
- }
-
- selectNode->mSavedScale = object->getScale();
- selectNode->saveTextureScaleRatios(mManager->mTextureChannel);
+
+ if (selectNode->mSelectedGLTFNode != -1)
+ {
+ // save GLTF node state
+ object->getGLTFNodeTransformAgent(selectNode->mSelectedGLTFNode, &selectNode->mSavedPositionLocal, &selectNode->mSavedRotation, &selectNode->mSavedScale);
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent(selectNode->mSavedPositionLocal);
+ selectNode->mLastMoveLocal.setZero();
+ }
+ else
+ {
+ selectNode->mSavedPositionLocal = object->getPosition();
+ if (object->isAttachment())
+ {
+ if (object->isRootEdit())
+ {
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ if (parent_xform)
+ {
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
+ }
+ else
+ {
+ selectNode->mSavedPositionGlobal = object->getPositionGlobal();
+ }
+ }
+ else
+ {
+ LLViewerObject* attachment_root = (LLViewerObject*)object->getParent();
+ LLXform* parent_xform = attachment_root ? attachment_root->mDrawable->getXform()->getParent() : NULL;
+ if (parent_xform)
+ {
+ LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation());
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos);
+ }
+ else
+ {
+ selectNode->mSavedPositionGlobal = object->getPositionGlobal();
+ }
+ }
+ selectNode->mSavedRotation = object->getRenderRotation();
+ }
+ else
+ {
+ selectNode->mSavedPositionGlobal = object->getPositionGlobal();
+ selectNode->mSavedRotation = object->getRotationRegion();
+ }
+
+ selectNode->mSavedScale = object->getScale();
+ selectNode->saveTextureScaleRatios(mManager->mTextureChannel);
+ }
return true;
}
} func(action_type, this);
@@ -6682,8 +6700,7 @@ LLSelectNode::~LLSelectNode()
}
}
-
- delete mPermissions;
+ delete mPermissions;
mPermissions = NULL;
}
@@ -6711,6 +6728,17 @@ void LLSelectNode::selectTE(S32 te_index, BOOL selected)
mLastTESelected = te_index;
}
+void LLSelectNode::selectGLTFNode(S32 node_index, S32 primitive_index, bool selected)
+{
+ if (node_index < 0)
+ {
+ return;
+ }
+
+ mSelectedGLTFNode = node_index;
+ mSelectedGLTFPrimitive = primitive_index;
+}
+
BOOL LLSelectNode::isTESelected(S32 te_index) const
{
if (te_index < 0 || te_index >= mObject->getNumTEs())
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
index f89209b437..66c794af4c 100644
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -175,6 +175,7 @@ public:
void selectAllTEs(BOOL b);
void selectTE(S32 te_index, BOOL selected);
+ void selectGLTFNode(S32 node_index, S32 primitive_index, bool selected);
BOOL isTESelected(S32 te_index) const;
bool hasSelectedTE() const { return TE_SELECT_MASK_ALL & mTESelectMask; }
S32 getLastSelectedTE() const;
@@ -217,6 +218,7 @@ public:
S16 mInventorySerial;
LLVector3 mSavedPositionLocal; // for interactively modifying object position
LLVector3 mLastPositionLocal;
+ LLVector3 mLastMoveLocal;
LLVector3d mSavedPositionGlobal; // for interactively modifying object position
LLVector3 mSavedScale; // for interactively modifying object scale
LLVector3 mLastScale;
@@ -240,11 +242,14 @@ public:
std::vector<LLVector3> mSilhouetteVertices; // array of vertices to render silhouette of object
std::vector<LLVector3> mSilhouetteNormals; // array of normals to render silhouette of object
BOOL mSilhouetteExists; // need to generate silhouette?
+ S32 mSelectedGLTFNode = -1;
+ S32 mSelectedGLTFPrimitive = -1;
protected:
LLPointer<LLViewerObject> mObject;
S32 mTESelectMask;
S32 mLastTESelected;
+
};
class LLObjectSelection : public LLRefCount
@@ -533,7 +538,7 @@ public:
LLObjectSelectionHandle selectObjectAndFamily(LLViewerObject* object, BOOL add_to_end = FALSE, BOOL ignore_select_owned = FALSE);
// For when you want just a child object.
- LLObjectSelectionHandle selectObjectOnly(LLViewerObject* object, S32 face = SELECT_ALL_TES);
+ LLObjectSelectionHandle selectObjectOnly(LLViewerObject* object, S32 face = SELECT_ALL_TES, S32 gltf_node = -1, S32 gltf_primitive = -1);
// Same as above, but takes a list of objects. Used by rectangle select.
LLObjectSelectionHandle selectObjectAndFamily(const std::vector<LLViewerObject*>& object_list, BOOL send_to_sim = TRUE);
@@ -833,7 +838,7 @@ public:
void remove(std::vector<LLViewerObject*>& objects);
void remove(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE);
void removeAll();
- void addAsIndividual(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE);
+ void addAsIndividual(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE, S32 gltf_node = -1, S32 gltf_primitive = -1);
void promoteSelectionToRoot();
void demoteSelectionToIndividuals();
diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp
index c07c939862..ff9332aeee 100644
--- a/indra/newview/llsettingsvo.cpp
+++ b/indra/newview/llsettingsvo.cpp
@@ -718,11 +718,11 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force)
LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky();
// TODO -- make these getters return vec3s
- LLVector3 sunDiffuse = LLVector3(psky->getSunlightColor().mV);
- LLVector3 moonDiffuse = LLVector3(psky->getMoonlightColor().mV);
+ LLVector3 sun_light_color = LLVector3(psky->getSunlightColor().mV);
+ LLVector3 moon_light_color = LLVector3(psky->getMoonlightColor().mV);
- shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sunDiffuse);
- shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, moonDiffuse);
+ shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_light_color);
+ shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, moon_light_color);
shader->uniform3fv(LLShaderMgr::CLOUD_COLOR, LLVector3(psky->getCloudColor().mV));
@@ -745,8 +745,7 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force)
shader->uniform1f(LLShaderMgr::SKY_SUNLIGHT_SCALE, sunlight_scale);
shader->uniform1f(LLShaderMgr::SKY_AMBIENT_SCALE, ambient_scale);
- static LLCachedControl<F32> cloud_shadow_scale(gSavedSettings, "RenderCloudShadowAmbianceFactor", 0.125f);
- F32 probe_ambiance = getTotalReflectionProbeAmbiance(cloud_shadow_scale);
+ F32 probe_ambiance = getReflectionProbeAmbiance();
if (irradiance_pass)
{ // during an irradiance map update, disable ambient lighting (direct lighting only) and desaturate sky color (avoid tinting the world blue)
@@ -765,9 +764,9 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force)
shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, auto_adjust_hdr_scale);
LLColor3 blue_horizon = getBlueHorizon() * auto_adjust_blue_horizon_scale;
LLColor3 blue_density = getBlueDensity() * auto_adjust_blue_density_scale;
- LLColor3 sun_diffuse = getSunDiffuse() * auto_adjust_sun_color_scale;
+ sun_light_color = sun_light_color * auto_adjust_sun_color_scale;
- shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_diffuse.mV);
+ shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_light_color.mV);
shader->uniform3fv(LLShaderMgr::BLUE_DENSITY, blue_density.mV);
shader->uniform3fv(LLShaderMgr::BLUE_HORIZON, blue_horizon.mV);
@@ -1009,6 +1008,7 @@ void LLSettingsVOWater::applySpecial(void *ptarget, bool force)
glh::matrix4f mat(modelView);
glh::matrix4f invtrans = mat.inverse().transpose();
+ invtrans.m[3] = invtrans.m[7] = invtrans.m[11] = 0.f;
glh::vec3f enorm;
glh::vec3f ep;
invtrans.mult_matrix_vec(norm, enorm);
@@ -1017,12 +1017,29 @@ void LLSettingsVOWater::applySpecial(void *ptarget, bool force)
LLVector4 waterPlane(enorm.v[0], enorm.v[1], enorm.v[2], -ep.dot(enorm));
+ norm = glh::vec3f(gPipeline.mHeroProbeManager.mMirrorNormal.mV);
+ p = glh::vec3f(gPipeline.mHeroProbeManager.mMirrorPosition.mV);
+ invtrans.mult_matrix_vec(norm, enorm);
+ enorm.normalize();
+ mat.mult_matrix_vec(p, ep);
+
+ LLVector4 mirrorPlane(enorm.v[0], enorm.v[1], enorm.v[2], -ep.dot(enorm));
+
LLDrawPoolAlpha::sWaterPlane = waterPlane;
shader->uniform4fv(LLShaderMgr::WATER_WATERPLANE, waterPlane.mV);
-
+ shader->uniform4fv(LLShaderMgr::CLIP_PLANE, mirrorPlane.mV);
LLVector4 light_direction = env.getClampedLightNorm();
+ if (gPipeline.mHeroProbeManager.isMirrorPass())
+ {
+ shader->uniform1f(LLShaderMgr::MIRROR_FLAG, 1);
+ }
+ else
+ {
+ shader->uniform1f(LLShaderMgr::MIRROR_FLAG, 0);
+ }
+
F32 waterFogKS = 1.f / llmax(light_direction.mV[2], WATER_FOG_LIGHT_CLAMP);
shader->uniform1f(LLShaderMgr::WATER_FOGKS, waterFogKS);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index faf6f15015..f3b5bb0565 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -47,6 +47,7 @@
#include "pipeline.h"
#include "llmeshrepository.h"
#include "llrender.h"
+#include "lldrawpool.h"
#include "lloctree.h"
#include "llphysicsshapebuilderutil.h"
#include "llvoavatar.h"
@@ -1990,7 +1991,11 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
drawBoxOutline(pos,size);
}
}
-
+// *TODO: LLDrawables which are not part of LLVOVolumes fall into a different
+// code path which uses a shader - it was tested to be faster than mapping a
+// vertex buffer in the terrain case. Consider using it for LLVOVolumes as well
+// to simplify and speed up this debug code. Alternatively, a compute shader is
+// likely faster. -Cosmic,2023-09-28
void renderNormals(LLDrawable *drawablep)
{
if (!drawablep->isVisible())
@@ -1998,11 +2003,13 @@ void renderNormals(LLDrawable *drawablep)
LLVertexBuffer::unbind();
+ LLViewerObject* obj = drawablep->getVObj();
LLVOVolume *vol = drawablep->getVOVolume();
- if (vol)
+ if (obj)
{
- LLVolume *volume = vol->getVolume();
+ LLGLEnable blend(GL_BLEND);
+ LLGLDepthTest gl_depth(GL_TRUE, GL_FALSE);
// Drawable's normals & tangents are stored in model space, i.e. before any scaling is applied.
//
@@ -2011,68 +2018,136 @@ void renderNormals(LLDrawable *drawablep)
// transform. We get that effect here by pre-applying the inverse scale (twice, because
// one forward scale will be re-applied via the MVP in the vertex shader)
- LLVector3 scale_v3 = vol->getScale();
- float scale_len = scale_v3.length();
- LLVector4a obj_scale(scale_v3.mV[VX], scale_v3.mV[VY], scale_v3.mV[VZ]);
- obj_scale.normalize3();
+ LLVector4a inv_scale;
+ float scale_len;
+ if (vol)
+ {
+ LLVector3 scale_v3 = vol->getScale();
+ LLVector4a obj_scale(scale_v3.mV[VX], scale_v3.mV[VY], scale_v3.mV[VZ]);
+ obj_scale.normalize3();
- // Normals &tangent line segments get scaled along with the object. Divide by scale length
- // to keep the as-viewed lengths (relatively) constant with the debug setting length
- float draw_length = gSavedSettings.getF32("RenderDebugNormalScale") / scale_len;
+ // Create inverse-scale vector for normals
+ inv_scale.set(1.0 / scale_v3.mV[VX], 1.0 / scale_v3.mV[VY], 1.0 / scale_v3.mV[VZ], 0.0);
+ inv_scale.mul(inv_scale); // Squared, to apply inverse scale twice
- // Create inverse-scale vector for normals
- LLVector4a inv_scale(1.0 / scale_v3.mV[VX], 1.0 / scale_v3.mV[VY], 1.0 / scale_v3.mV[VZ]);
- inv_scale.mul(inv_scale); // Squared, to apply inverse scale twice
- inv_scale.normalize3fast();
+ inv_scale.normalize3fast();
+ scale_len = scale_v3.length();
+ }
+ else
+ {
+ inv_scale.set(1.0, 1.0, 1.0, 0.0);
+ scale_len = 1.0;
+ }
gGL.pushMatrix();
- gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix);
+ if (vol)
+ {
+ gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix);
+ }
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ // Normals &tangent line segments get scaled along with the object. Divide by scale length
+ // to keep the as-viewed lengths (relatively) constant with the debug setting length
+ float draw_length = gSavedSettings.getF32("RenderDebugNormalScale") / scale_len;
+
+ std::vector<LLVolumeFace>* faces = nullptr;
+ std::vector<LLFace*>* drawable_faces = nullptr;
+ if (vol)
{
- const LLVolumeFace &face = volume->getVolumeFace(i);
+ LLVolume* volume = vol->getVolume();
+ faces = &volume->getVolumeFaces();
+ }
+ else
+ {
+ drawable_faces = &drawablep->getFaces();
+ }
- gGL.flush();
- gGL.diffuseColor4f(1, 1, 0, 1);
- gGL.begin(LLRender::LINES);
- for (S32 j = 0; j < face.mNumVertices; ++j)
- {
- LLVector4a n, p;
+ if (faces)
+ {
+ for (auto it = faces->begin(); it != faces->end(); ++it)
+ {
+ const LLVolumeFace& face = *it;
- n.setMul(face.mNormals[j], 1.0);
- n.mul(inv_scale); // Pre-scale normal, so it's left with an inverse-transpose xform after MVP
- n.normalize3fast();
- n.mul(draw_length);
- p.setAdd(face.mPositions[j], n);
+ gGL.flush();
+ gGL.diffuseColor4f(1, 1, 0, 1);
+ gGL.begin(LLRender::LINES);
+ for (S32 j = 0; j < face.mNumVertices; ++j)
+ {
+ LLVector4a n, p;
- gGL.vertex3fv(face.mPositions[j].getF32ptr());
- gGL.vertex3fv(p.getF32ptr());
- }
- gGL.end();
+ n.setMul(face.mNormals[j], 1.0);
+ n.mul(inv_scale); // Pre-scale normal, so it's left with an inverse-transpose xform after MVP
+ n.normalize3fast();
+ n.mul(draw_length);
+ p.setAdd(face.mPositions[j], n);
+
+ gGL.vertex3fv(face.mPositions[j].getF32ptr());
+ gGL.vertex3fv(p.getF32ptr());
+ }
+ gGL.end();
- // Tangents are simple vectors and do not require reorientation via pre-scaling
- if (face.mTangents)
+ // Tangents are simple vectors and do not require reorientation via pre-scaling
+ if (face.mTangents)
+ {
+ gGL.flush();
+ gGL.diffuseColor4f(0, 1, 1, 1);
+ gGL.begin(LLRender::LINES);
+ for (S32 j = 0; j < face.mNumVertices; ++j)
+ {
+ LLVector4a t, p;
+
+ t.setMul(face.mTangents[j], 1.0f);
+ t.normalize3fast();
+ t.mul(draw_length);
+ p.setAdd(face.mPositions[j], t);
+
+ gGL.vertex3fv(face.mPositions[j].getF32ptr());
+ gGL.vertex3fv(p.getF32ptr());
+ }
+ gGL.end();
+ }
+ }
+ }
+ else if (drawable_faces)
+ {
+ // *HACK: Prepare to restore previous shader as other debug code depends on a simpler shader being present
+ llassert(LLGLSLShader::sCurBoundShaderPtr == &gDebugProgram);
+ LLGLSLShader* prev_shader = LLGLSLShader::sCurBoundShaderPtr;
+ for (auto it = drawable_faces->begin(); it != drawable_faces->end(); ++it)
{
- gGL.flush();
- gGL.diffuseColor4f(0, 1, 1, 1);
- gGL.begin(LLRender::LINES);
- for (S32 j = 0; j < face.mNumVertices; ++j)
+ LLFace* facep = *it;
+ LLFace& face = **it;
+ LLVertexBuffer* buf = face.getVertexBuffer();
+ if (!buf) { continue; }
+ U32 mask_vn = LLVertexBuffer::TYPE_VERTEX | LLVertexBuffer::TYPE_NORMAL;
+ if ((buf->getTypeMask() & mask_vn) != mask_vn) { continue; }
+
+ LLGLSLShader* shader;
+ if ((buf->getTypeMask() & LLVertexBuffer::TYPE_TANGENT) != LLVertexBuffer::TYPE_TANGENT)
{
- LLVector4a t, p;
+ shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_DEFAULT];
+ }
+ else
+ {
+ shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_WITH_TANGENTS];
+ }
+ shader->bind();
- t.setMul(face.mTangents[j], 1.0f);
- t.normalize3fast();
- t.mul(draw_length);
- p.setAdd(face.mPositions[j], t);
+ shader->uniform1f(LLShaderMgr::DEBUG_NORMAL_DRAW_LENGTH, draw_length);
- gGL.vertex3fv(face.mPositions[j].getF32ptr());
- gGL.vertex3fv(p.getF32ptr());
- }
- gGL.end();
+ LLRenderPass::applyModelMatrix(&facep->getDrawable()->getRegion()->mRenderMatrix);
+
+ buf->setBuffer();
+ // *NOTE: The render type in the vertex shader is TRIANGLES, but gets converted to LINES in the geometry shader
+ // *NOTE: For terrain normal debug, this seems to also include vertices for water, which is technically not part of the terrain. Should fix that at some point.
+ buf->drawRange(LLRender::TRIANGLES, face.getGeomIndex(), face.getGeomIndex() + face.getGeomCount()-1, face.getIndicesCount(), face.getIndicesStart());
}
- }
+ if (prev_shader)
+ {
+ prev_shader->bind();
+ }
+ }
gGL.popMatrix();
}
@@ -2791,10 +2866,8 @@ void renderLights(LLDrawable* drawablep)
class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
{
public:
-
-
LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t)
- : LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL)
+ : LLOctreeTriangleRayIntersect(start, dir, nullptr, closest_t, NULL, NULL, NULL, NULL)
{
}
@@ -2818,7 +2891,7 @@ public:
size.set(vl->mBounds[1].getF32ptr());
}
- drawBoxOutline(center, size);
+ drawBoxOutline(center, size);
for (U32 i = 0; i < 2; i++)
{
@@ -2862,6 +2935,13 @@ public:
}
};
+void renderOctreeRaycast(const LLVector4a& start, const LLVector4a& end, const LLVolumeOctree* octree)
+{
+ F32 t = 1.f;
+ LLRenderOctreeRaycast render(start, end, &t);
+ render.traverse(octree);
+}
+
void renderRaycast(LLDrawable* drawablep)
{
if (drawablep->getNumFaces())
@@ -2919,29 +2999,22 @@ void renderRaycast(LLDrawable* drawablep)
dir.setSub(end, start);
gGL.flush();
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
{
//render face positions
- LLVertexBuffer::unbind();
- gGL.diffuseColor4f(0,1,1,0.5f);
- glVertexPointer(3, GL_FLOAT, sizeof(LLVector4a), face.mPositions);
- gGL.syncMatrices();
- glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices);
+ //gGL.diffuseColor4f(0,1,1,0.5f);
+ //LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mPositions, nullptr, face.mNumIndices, face.mIndices);
}
if (!volume->isUnique())
{
- F32 t = 1.f;
-
if (!face.getOctree())
{
((LLVolumeFace*) &face)->createOctree();
}
- LLRenderOctreeRaycast render(start, dir, &t);
-
- render.traverse(face.getOctree());
+ renderOctreeRaycast(start, end, face.getOctree());
}
gGL.popMatrix();
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index 1418499f8b..804afe6266 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -643,17 +643,18 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
}
}
-BOOL LLSurface::idleUpdate(F32 max_update_time)
+template<bool PBR>
+bool LLSurface::idleUpdate(F32 max_update_time)
{
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
{
- return FALSE;
+ return false;
}
// Perform idle time update of non-critical stuff.
// In this case, texture and normal updates.
LLTimer update_timer;
- BOOL did_update = FALSE;
+ bool did_update = false;
// If the Z height data has changed, we need to rebuild our
// property line vertex arrays.
@@ -669,13 +670,13 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
{
std::set<LLSurfacePatch *>::iterator curiter = iter++;
LLSurfacePatch *patchp = *curiter;
- patchp->updateNormals();
+ patchp->updateNormals<PBR>();
patchp->updateVerticalStats();
if (max_update_time == 0.f || update_timer.getElapsedTimeF32() < max_update_time)
{
if (patchp->updateTexture())
{
- did_update = TRUE;
+ did_update = true;
patchp->clearDirty();
mDirtyPatchList.erase(curiter);
}
@@ -691,6 +692,9 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
return did_update;
}
+template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
+template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
+
void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch)
{
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index 33a64ae7d5..b7b47d2a1c 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -112,7 +112,8 @@ public:
LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
// Update methods (called during idle, normally)
- BOOL idleUpdate(F32 max_update_time);
+ template<bool PBR>
+ bool idleUpdate(F32 max_update_time);
BOOL containsPosition(const LLVector3 &position);
@@ -224,6 +225,9 @@ private:
static S32 sTextureSize; // Size of the surface texture
};
+extern template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
+extern template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
+
// . __.
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
index 449d3d95c8..92b1273041 100644
--- a/indra/newview/llsurfacepatch.cpp
+++ b/indra/newview/llsurfacepatch.cpp
@@ -221,7 +221,9 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
*vertex = pos_agent-mVObjp->getRegion()->getOriginAgent();
LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent();
- LLVector3 tex_pos = rel_pos * (1.f/surface_stride);
+ // *NOTE: Only PBR terrain uses the UVs right now. Texture terrain just ignores it.
+ // *NOTE: In the future, UVs and horizontal position will no longer have a 1:1 relationship for PBR terrain
+ LLVector3 tex_pos = rel_pos;
tex0->mV[0] = tex_pos.mV[0];
tex0->mV[1] = tex_pos.mV[1];
tex1->mV[0] = mSurfacep->getRegion()->getCompositionXY(llfloor(mOriginRegion.mV[0])+x, llfloor(mOriginRegion.mV[1])+y);
@@ -241,7 +243,8 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
}
-void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
+template<>
+void LLSurfacePatch::calcNormal</*PBR=*/false>(const U32 x, const U32 y, const U32 stride)
{
U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
U32 surface_stride = mSurfacep->getGridsPerEdge();
@@ -354,6 +357,166 @@ void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
*(mDataNorm + surface_stride * y + x) = normal;
}
+template<>
+void LLSurfacePatch::calcNormal</*PBR=*/true>(const U32 x, const U32 y, const U32 stride)
+{
+ llassert(mDataNorm);
+ constexpr U32 index = 0;
+
+ const U32 surface_stride = mSurfacep->getGridsPerEdge();
+ LLVector3& normal_out = *(mDataNorm + surface_stride * y + x);
+ calcNormalFlat(normal_out, x, y, index);
+}
+
+// Calculate the flat normal of a triangle whose least coordinate is specified by the given x,y values.
+// If index = 0, calculate the normal of the first triangle, otherwise calculate the normal of the second.
+void LLSurfacePatch::calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index)
+{
+ llassert(index == 0 || index == 1);
+
+ U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+
+ // Vertex stride is always 1 because we want the flat surface of the current triangle face
+ constexpr U32 stride = 1;
+
+ const F32 mpg = mSurfacep->getMetersPerGrid() * stride;
+
+ S32 poffsets[2][2][2];
+ poffsets[0][0][0] = x;
+ poffsets[0][0][1] = y;
+
+ poffsets[0][1][0] = x;
+ poffsets[0][1][1] = y + stride;
+
+ poffsets[1][0][0] = x + stride;
+ poffsets[1][0][1] = y;
+
+ poffsets[1][1][0] = x + stride;
+ poffsets[1][1][1] = y + stride;
+
+ const LLSurfacePatch *ppatches[2][2];
+
+ // LLVector3 p1, p2, p3, p4;
+
+ ppatches[0][0] = this;
+ ppatches[0][1] = this;
+ ppatches[1][0] = this;
+ ppatches[1][1] = this;
+
+ U32 i, j;
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ if (poffsets[i][j][0] < 0)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(WEST))
+ {
+ poffsets[i][j][0] = 0;
+ }
+ else
+ {
+ poffsets[i][j][0] += patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(WEST);
+ }
+ }
+ if (poffsets[i][j][1] < 0)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(SOUTH))
+ {
+ poffsets[i][j][1] = 0;
+ }
+ else
+ {
+ poffsets[i][j][1] += patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(SOUTH);
+ }
+ }
+ if (poffsets[i][j][0] >= (S32)patch_width)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(EAST))
+ {
+ poffsets[i][j][0] = patch_width - 1;
+ }
+ else
+ {
+ poffsets[i][j][0] -= patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(EAST);
+ }
+ }
+ if (poffsets[i][j][1] >= (S32)patch_width)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(NORTH))
+ {
+ poffsets[i][j][1] = patch_width - 1;
+ }
+ else
+ {
+ poffsets[i][j][1] -= patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(NORTH);
+ }
+ }
+ }
+ }
+
+ LLVector3 p00(-mpg,-mpg,
+ *(ppatches[0][0]->mDataZ
+ + poffsets[0][0][0]
+ + poffsets[0][0][1]*surface_stride));
+ LLVector3 p01(-mpg,+mpg,
+ *(ppatches[0][1]->mDataZ
+ + poffsets[0][1][0]
+ + poffsets[0][1][1]*surface_stride));
+ LLVector3 p10(+mpg,-mpg,
+ *(ppatches[1][0]->mDataZ
+ + poffsets[1][0][0]
+ + poffsets[1][0][1]*surface_stride));
+ LLVector3 p11(+mpg,+mpg,
+ *(ppatches[1][1]->mDataZ
+ + poffsets[1][1][0]
+ + poffsets[1][1][1]*surface_stride));
+
+ // Triangle index / coordinate convention
+ // for a single surface patch
+ //
+ // p01 p11
+ //
+ // ^ ._____.
+ // | |\ |
+ // | | \ 1 |
+ // | | \ |
+ // | 0 \ |
+ // y |____\|
+ //
+ // p00 x ---> p10
+ //
+ // (z up / out of the screen due to right-handed coordinate system)
+
+ LLVector3 normal;
+ if (index == 0)
+ {
+ LLVector3 c1 = p10 - p00;
+ LLVector3 c2 = p01 - p00;
+
+ normal = c1;
+ normal %= c2;
+ normal.normVec();
+ }
+ else // index == 1
+ {
+ LLVector3 c1 = p11 - p01;
+ LLVector3 c2 = p11 - p10;
+
+ normal = c1;
+ normal %= c2;
+ normal.normVec();
+ }
+
+ llassert(&normal_out);
+ normal_out = normal;
+}
+
const LLVector3 &LLSurfacePatch::getNormal(const U32 x, const U32 y) const
{
U32 surface_stride = mSurfacep->getGridsPerEdge();
@@ -451,6 +614,7 @@ void LLSurfacePatch::updateVerticalStats()
}
+template<bool PBR>
void LLSurfacePatch::updateNormals()
{
if (mSurfacep->mType == 'w')
@@ -468,9 +632,9 @@ void LLSurfacePatch::updateNormals()
{
for (j = 0; j <= grids_per_patch_edge; j++)
{
- calcNormal(grids_per_patch_edge, j, 2);
- calcNormal(grids_per_patch_edge - 1, j, 2);
- calcNormal(grids_per_patch_edge - 2, j, 2);
+ calcNormal<PBR>(grids_per_patch_edge, j, 2);
+ calcNormal<PBR>(grids_per_patch_edge - 1, j, 2);
+ calcNormal<PBR>(grids_per_patch_edge - 2, j, 2);
}
dirty_patch = TRUE;
@@ -481,9 +645,9 @@ void LLSurfacePatch::updateNormals()
{
for (i = 0; i <= grids_per_patch_edge; i++)
{
- calcNormal(i, grids_per_patch_edge, 2);
- calcNormal(i, grids_per_patch_edge - 1, 2);
- calcNormal(i, grids_per_patch_edge - 2, 2);
+ calcNormal<PBR>(i, grids_per_patch_edge, 2);
+ calcNormal<PBR>(i, grids_per_patch_edge - 1, 2);
+ calcNormal<PBR>(i, grids_per_patch_edge - 2, 2);
}
dirty_patch = TRUE;
@@ -494,8 +658,8 @@ void LLSurfacePatch::updateNormals()
{
for (j = 0; j < grids_per_patch_edge; j++)
{
- calcNormal(0, j, 2);
- calcNormal(1, j, 2);
+ calcNormal<PBR>(0, j, 2);
+ calcNormal<PBR>(1, j, 2);
}
dirty_patch = TRUE;
}
@@ -505,8 +669,8 @@ void LLSurfacePatch::updateNormals()
{
for (i = 0; i < grids_per_patch_edge; i++)
{
- calcNormal(i, 0, 2);
- calcNormal(i, 1, 2);
+ calcNormal<PBR>(i, 0, 2);
+ calcNormal<PBR>(i, 1, 2);
}
dirty_patch = TRUE;
}
@@ -582,10 +746,10 @@ void LLSurfacePatch::updateNormals()
// We've got a northeast patch in the same surface.
// The z and normals will be handled by that patch.
}
- calcNormal(grids_per_patch_edge, grids_per_patch_edge, 2);
- calcNormal(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
- calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
- calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
+ calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge, 2);
+ calcNormal<PBR>(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
+ calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
+ calcNormal<PBR>(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
dirty_patch = TRUE;
}
@@ -596,7 +760,7 @@ void LLSurfacePatch::updateNormals()
{
for (i=2; i < grids_per_patch_edge - 2; i++)
{
- calcNormal(i, j, 2);
+ calcNormal<PBR>(i, j, 2);
}
}
dirty_patch = TRUE;
@@ -613,6 +777,9 @@ void LLSurfacePatch::updateNormals()
}
}
+template void LLSurfacePatch::updateNormals</*PBR=*/false>();
+template void LLSurfacePatch::updateNormals</*PBR=*/true>();
+
void LLSurfacePatch::updateEastEdge()
{
U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
@@ -739,7 +906,7 @@ void LLSurfacePatch::updateGL()
updateCompositionStats();
F32 tex_patch_size = meters_per_grid*grids_per_patch_edge;
- if (comp->generateTexture((F32)origin_region[VX], (F32)origin_region[VY],
+ if (comp->generateMinimapTileLand((F32)origin_region[VX], (F32)origin_region[VY],
tex_patch_size, tex_patch_size))
{
mSTexUpdate = FALSE;
diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h
index 8c8f501dce..ec3864ce44 100644
--- a/indra/newview/llsurfacepatch.h
+++ b/indra/newview/llsurfacepatch.h
@@ -77,6 +77,7 @@ public:
void updateVerticalStats();
void updateCompositionStats();
+ template<bool PBR>
void updateNormals();
void updateEastEdge();
@@ -102,9 +103,18 @@ public:
LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset.
LLVector2 getTexCoords(const U32 x, const U32 y) const;
+ // Per-vertex normals
+ // *TODO: PBR=true is a test implementation solely for proof-of-concept.
+ // Final implementation would likely be very different and may not even use
+ // this function. If we decide to keep calcNormalFlat, remove index as it
+ // is a debug parameter for testing.
+ template<bool PBR>
void calcNormal(const U32 x, const U32 y, const U32 stride);
const LLVector3 &getNormal(const U32 x, const U32 y) const;
+ // Per-triangle normals for flat edges
+ void calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index /* 0 or 1 */);
+
void eval(const U32 x, const U32 y, const U32 stride,
LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1);
@@ -181,5 +191,8 @@ protected:
LLSurface *mSurfacep; // Pointer to "parent" surface
};
+extern template void LLSurfacePatch::updateNormals</*PBR=*/false>();
+extern template void LLSurfacePatch::updateNormals</*PBR=*/true>();
+
#endif // LL_LLSURFACEPATCH_H
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 62d3fa28bf..91c2755ea5 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -72,6 +72,7 @@
#include "llradiogroup.h"
#include "llfloaterreg.h"
+#include "llgltfmaterialpreviewmgr.h"
#include "lllocalbitmaps.h"
#include "lllocalgltfmaterials.h"
#include "llerror.h"
@@ -229,7 +230,7 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti
LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID());
if (mInventoryPickType == PICK_MATERIAL
- && mImageAssetID == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID
+ && mImageAssetID == BLANK_MATERIAL_ASSET_ID
&& itemp && itemp->getAssetUUID().isNull())
{
item_id = inv_view->getUUID();
@@ -272,7 +273,7 @@ void LLFloaterTexturePicker::setImageIDFromItem(const LLInventoryItem* itemp, bo
if (mInventoryPickType == PICK_MATERIAL && asset_id.isNull())
{
// If an inventory item has a null asset, consider it a valid blank material(gltf)
- asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ asset_id = BLANK_MATERIAL_ASSET_ID;
}
setImageID(asset_id, set_selection);
}
@@ -537,6 +538,8 @@ void LLFloaterTexturePicker::onClose(bool app_quitting)
}
stopUsingPipette();
sLastPickerMode = mModeSelector->getValue().asInteger();
+ // *NOTE: Vertex buffer for sphere preview is still cached
+ mGLTFPreview = nullptr;
}
// virtual
@@ -659,6 +662,7 @@ void LLFloaterTexturePicker::draw()
if( mOwner )
{
mTexturep = NULL;
+ LLPointer<LLFetchedGLTFMaterial> old_material = mGLTFMaterial;
mGLTFMaterial = NULL;
if (mImageAssetID.notNull())
{
@@ -666,10 +670,27 @@ void LLFloaterTexturePicker::draw()
{
mGLTFMaterial = (LLFetchedGLTFMaterial*) gGLTFMaterialList.getMaterial(mImageAssetID);
llassert(mGLTFMaterial == nullptr || dynamic_cast<LLFetchedGLTFMaterial*>(gGLTFMaterialList.getMaterial(mImageAssetID)) != nullptr);
+ if (mGLTFPreview.isNull() || mGLTFMaterial.isNull() || (old_material.notNull() && (old_material.get() != mGLTFMaterial.get())))
+ {
+ // Only update the preview if needed, since gGLTFMaterialPreviewMgr does not cache the preview.
+ if (mGLTFMaterial.isNull())
+ {
+ mGLTFPreview = nullptr;
+ }
+ else
+ {
+ mGLTFPreview = gGLTFMaterialPreviewMgr.getPreview(mGLTFMaterial);
+ }
+ }
+ if (mGLTFPreview)
+ {
+ mGLTFPreview->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+ }
}
else
{
LLPointer<LLViewerFetchedTexture> texture = NULL;
+ mGLTFPreview = nullptr;
if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID))
{
@@ -679,7 +700,7 @@ void LLFloaterTexturePicker::draw()
if (obj)
{
LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(mImageAssetID);
- texture = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL;
+ texture = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL;
}
}
@@ -720,27 +741,25 @@ void LLFloaterTexturePicker::draw()
// If the floater is focused, don't apply its alpha to the texture (STORM-677).
const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
- LLViewerTexture* texture = nullptr;
+ LLViewerTexture* preview;
if (mGLTFMaterial)
{
- texture = mGLTFMaterial->getUITexture();
+ preview = mGLTFPreview.get();
}
else
{
- texture = mTexturep.get();
+ preview = mTexturep.get();
}
- if( texture )
+ if( preview )
{
- if( texture->getComponents() == 4 )
+ preview->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+ if( preview->getComponents() == 4 )
{
gl_rect_2d_checkerboard( interior, alpha );
}
- gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), texture, UI_VERTEX_COLOR % alpha );
-
- // Pump the priority
- texture->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+ gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), preview, UI_VERTEX_COLOR % alpha );
}
else if (!mFallbackImage.isNull())
{
@@ -794,7 +813,7 @@ const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL co
}
LLUUID loockup_id = asset_id;
- if (mInventoryPickType == PICK_MATERIAL && loockup_id == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID)
+ if (mInventoryPickType == PICK_MATERIAL && loockup_id == BLANK_MATERIAL_ASSET_ID)
{
// default asset id means we are looking for an inventory item with a default asset UUID (null)
loockup_id = LLUUID::null;
@@ -889,7 +908,7 @@ void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op)
LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID());
if (mInventoryPickType == PICK_MATERIAL
- && mImageAssetID == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID
+ && mImageAssetID == BLANK_MATERIAL_ASSET_ID
&& itemp && itemp->getAssetUUID().isNull())
{
inventory_id = inv_view->getUUID();
@@ -1639,7 +1658,7 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p)
mShowLoadingPlaceholder( TRUE ),
mOpenTexPreview(false),
mBakeTextureEnabled(true),
- mInventoryPickType(PICK_TEXTURE),
+ mInventoryPickType(p.pick_type),
mImageAssetID(p.image_id),
mDefaultImageAssetID(p.default_image_id),
mDefaultImageName(p.default_image_name),
@@ -1743,6 +1762,19 @@ void LLTextureCtrl::setFilterPermissionMasks(PermissionMask mask)
setDnDFilterPermMask(mask);
}
+void LLTextureCtrl::onVisibilityChange(BOOL new_visibility)
+{
+ if (!new_visibility)
+ {
+ // *NOTE: Vertex buffer for sphere preview is still cached
+ mGLTFPreview = nullptr;
+ }
+ else
+ {
+ llassert(!mGLTFPreview);
+ }
+}
+
void LLTextureCtrl::setVisible( BOOL visible )
{
if( !visible )
@@ -2157,48 +2189,73 @@ void LLTextureCtrl::draw()
{
mBorder->setKeyboardFocusHighlight(hasFocus());
+ LLPointer<LLViewerTexture> preview = NULL;
+
if (!mValid)
{
mTexturep = NULL;
+ mGLTFMaterial = NULL;
+ mGLTFPreview = NULL;
}
else if (!mImageAssetID.isNull())
{
- LLPointer<LLViewerFetchedTexture> texture = NULL;
-
if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(mImageAssetID))
{
LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
if (obj)
{
LLViewerTexture* viewerTexture = obj->getBakedTextureForMagicId(mImageAssetID);
- texture = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL;
+ mTexturep = viewerTexture ? dynamic_cast<LLViewerFetchedTexture*>(viewerTexture) : NULL;
+ mGLTFMaterial = NULL;
+ mGLTFPreview = NULL;
+
+ preview = mTexturep;
}
}
- if (texture.isNull())
+ if (preview.isNull())
{
+ LLPointer<LLFetchedGLTFMaterial> old_material = mGLTFMaterial;
+ mGLTFMaterial = NULL;
+ mTexturep = NULL;
if (mInventoryPickType == PICK_MATERIAL)
{
- LLPointer<LLFetchedGLTFMaterial> material = gGLTFMaterialList.getMaterial(mImageAssetID);
- if (material)
+ mGLTFMaterial = gGLTFMaterialList.getMaterial(mImageAssetID);
+ if (mGLTFPreview.isNull() || mGLTFMaterial.isNull() || (old_material.notNull() && (old_material.get() != mGLTFMaterial.get())))
+ {
+ // Only update the preview if needed, since gGLTFMaterialPreviewMgr does not cache the preview.
+ if (mGLTFMaterial.isNull())
+ {
+ mGLTFPreview = nullptr;
+ }
+ else
+ {
+ mGLTFPreview = gGLTFMaterialPreviewMgr.getPreview(mGLTFMaterial);
+ }
+ }
+ if (mGLTFPreview)
{
- texture = material->getUITexture();
+ mGLTFPreview->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
}
+
+ preview = mGLTFPreview;
}
else
{
- texture = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- texture->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
- texture->forceToSaveRawImage(0);
+ mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ mTexturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
+ mTexturep->forceToSaveRawImage(0);
+
+ preview = mTexturep;
}
}
-
- mTexturep = texture;
}
else//mImageAssetID == LLUUID::null
{
mTexturep = NULL;
+ mGLTFMaterial = NULL;
+ mGLTFPreview = NULL;
}
// Border
@@ -2211,15 +2268,15 @@ void LLTextureCtrl::draw()
// If we're in a focused floater, don't apply the floater's alpha to the texture (STORM-677).
const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
- if( mTexturep )
+ if( preview )
{
- if( mTexturep->getComponents() == 4 )
+ if( preview->getComponents() == 4 )
{
gl_rect_2d_checkerboard( interior, alpha );
}
- gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha);
- mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+ gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), preview, UI_VERTEX_COLOR % alpha);
+ preview->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
}
else if (!mFallbackImage.isNull())
{
@@ -2338,7 +2395,7 @@ BOOL LLTextureCtrl::doDrop(LLInventoryItem* item)
if (mInventoryPickType == PICK_MATERIAL && asset_id.isNull())
{
// If an inventory material has a null asset, consider it a valid blank material(gltf)
- asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ asset_id = BLANK_MATERIAL_ASSET_ID;
}
setImageAssetID(asset_id);
@@ -2366,6 +2423,16 @@ LLSD LLTextureCtrl::getValue() const
return LLSD(getImageAssetID());
}
+namespace LLInitParam
+{
+ void TypeValues<EPickInventoryType>::declareValues()
+ {
+ declare("texture_material", PICK_TEXTURE_MATERIAL);
+ declare("texture", PICK_TEXTURE);
+ declare("material", PICK_MATERIAL);
+ }
+}
+
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index cb6ce636e0..f193b0c083 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -64,13 +64,6 @@ bool get_is_predefined_texture(LLUUID asset_id);
LLUUID get_copy_free_item_by_asset_id(LLUUID image_id, bool no_trans_perm = false);
bool get_can_copy_texture(LLUUID image_id);
-enum LLPickerSource
-{
- PICKER_INVENTORY,
- PICKER_LOCAL,
- PICKER_BAKE,
- PICKER_UNKNOWN, // on cancel, default ids
-};
typedef enum e_pick_inventory_type
{
@@ -79,6 +72,23 @@ typedef enum e_pick_inventory_type
PICK_MATERIAL = 2,
} EPickInventoryType;
+namespace LLInitParam
+{
+ template<>
+ struct TypeValues<EPickInventoryType> : public TypeValuesHelper<EPickInventoryType>
+ {
+ static void declareValues();
+ };
+}
+
+enum LLPickerSource
+{
+ PICKER_INVENTORY,
+ PICKER_LOCAL,
+ PICKER_BAKE,
+ PICKER_UNKNOWN, // on cancel, default ids
+};
+
//////////////////////////////////////////////////////////////////////////////////////////
// LLTextureCtrl
@@ -100,6 +110,7 @@ public:
Optional<LLUUID> image_id;
Optional<LLUUID> default_image_id;
Optional<std::string> default_image_name;
+ Optional<EPickInventoryType> pick_type;
Optional<bool> allow_no_texture;
Optional<bool> can_apply_immediately;
Optional<bool> no_commit_on_selection; // alternative mode: commit occurs and the widget gets dirty
@@ -117,6 +128,7 @@ public:
: image_id("image"),
default_image_id("default_image_id"),
default_image_name("default_image_name"),
+ pick_type("pick_type", PICK_TEXTURE),
allow_no_texture("allow_no_texture", false),
can_apply_immediately("can_apply_immediately"),
no_commit_on_selection("no_commit_on_selection", false),
@@ -136,26 +148,28 @@ public:
// LLView interface
- virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
- virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
- BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
- EAcceptance *accept,
- std::string& tooltip_msg);
- virtual BOOL handleHover(S32 x, S32 y, MASK mask);
- virtual BOOL handleUnicodeCharHere(llwchar uni_char);
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask) override;
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ std::string& tooltip_msg) override;
+ BOOL handleHover(S32 x, S32 y, MASK mask) override;
+ BOOL handleUnicodeCharHere(llwchar uni_char) override;
+
+ void draw() override;
+ void setVisible( BOOL visible ) override;
+ void setEnabled( BOOL enabled ) override;
- virtual void draw();
- virtual void setVisible( BOOL visible );
- virtual void setEnabled( BOOL enabled );
+ void onVisibilityChange(BOOL new_visibility) override;
- void setValid(BOOL valid);
+ void setValid(BOOL valid);
- // LLUICtrl interface
- virtual void clear();
+ // LLUICtrl interface
+ void clear() override;
- // Takes a UUID, wraps get/setImageAssetID
- virtual void setValue(const LLSD& value);
- virtual LLSD getValue() const;
+ // Takes a UUID, wraps get/setImageAssetID
+ void setValue(const LLSD& value) override;
+ LLSD getValue() const override;
// LLTextureCtrl interface
void showPicker(BOOL take_focus);
@@ -250,6 +264,8 @@ private:
commit_callback_t mOnCloseCallback;
texture_selected_callback mOnTextureSelectedCallback;
LLPointer<LLViewerFetchedTexture> mTexturep;
+ LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial;
+ LLPointer<LLViewerTexture> mGLTFPreview;
LLUIColor mBorderColor;
LLUUID mImageItemID;
LLUUID mImageAssetID;
@@ -382,6 +398,7 @@ protected:
LLPointer<LLViewerTexture> mTexturep;
LLPointer<LLFetchedGLTFMaterial> mGLTFMaterial;
+ LLPointer<LLViewerTexture> mGLTFPreview;
LLView* mOwner;
LLUUID mImageAssetID; // Currently selected texture
diff --git a/indra/newview/lltinygltfhelper.cpp b/indra/newview/lltinygltfhelper.cpp
index 5b75db37d0..4e41f9959a 100644
--- a/indra/newview/lltinygltfhelper.cpp
+++ b/indra/newview/lltinygltfhelper.cpp
@@ -87,20 +87,30 @@ void LLTinyGLTFHelper::initFetchedTextures(tinygltf::Material& material,
{
strip_alpha_channel(mr_img);
- if (occlusion_img && material.pbrMetallicRoughness.metallicRoughnessTexture.index != material.occlusionTexture.index)
+ if (occlusion_img)
{
- // occlusion is a distinct texture from pbrMetallicRoughness
- // pack into mr red channel
- int occlusion_idx = material.occlusionTexture.index;
- int mr_idx = material.pbrMetallicRoughness.metallicRoughnessTexture.index;
- if (occlusion_idx != mr_idx)
+ if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != material.occlusionTexture.index)
{
- //scale occlusion image to match resolution of mr image
- occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight());
-
- copy_red_channel(occlusion_img, mr_img);
+ // occlusion is a distinct texture from pbrMetallicRoughness
+ // pack into mr red channel
+ int occlusion_idx = material.occlusionTexture.index;
+ int mr_idx = material.pbrMetallicRoughness.metallicRoughnessTexture.index;
+ if (occlusion_idx != mr_idx)
+ {
+ //scale occlusion image to match resolution of mr image
+ occlusion_img->scale(mr_img->getWidth(), mr_img->getHeight());
+
+ copy_red_channel(occlusion_img, mr_img);
+ }
}
}
+ else if (material.occlusionTexture.index == -1)
+ {
+ // no occlusion, make sure red channel of ORM is all 255
+ occlusion_img = new LLImageRaw(mr_img->getWidth(), mr_img->getHeight(), 3);
+ occlusion_img->clear(255, 255, 255);
+ copy_red_channel(occlusion_img, mr_img);
+ }
}
else if (occlusion_img)
{
@@ -147,7 +157,7 @@ const tinygltf::Image * LLTinyGLTFHelper::getImageFromTextureIndex(const tinyglt
return nullptr;
}
-LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name)
+LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, std::string & name, bool flip)
{
const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index);
LLImageRaw* rawImage = nullptr;
@@ -159,14 +169,17 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny
{
name = image->name;
rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
- rawImage->verticalFlip();
+ if (flip)
+ {
+ rawImage->verticalFlip();
+ }
rawImage->optimizeAwayAlpha();
}
return rawImage;
}
-LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index)
+LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tinygltf::Model & model, S32 texture_index, bool flip)
{
const tinygltf::Image* image = getImageFromTextureIndex(model, texture_index);
LLImageRaw* rawImage = nullptr;
@@ -177,7 +190,10 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny
image->component <= 4)
{
rawImage = new LLImageRaw(&image->image[0], image->width, image->height, image->component);
- rawImage->verticalFlip();
+ if (flip)
+ {
+ rawImage->verticalFlip();
+ }
rawImage->optimizeAwayAlpha();
}
@@ -237,7 +253,8 @@ bool LLTinyGLTFHelper::getMaterialFromModel(
const tinygltf::Model& model_in,
S32 mat_index,
LLFetchedGLTFMaterial* material,
- std::string& material_name)
+ std::string& material_name,
+ bool flip)
{
llassert(material);
@@ -256,18 +273,18 @@ bool LLTinyGLTFHelper::getMaterialFromModel(
material_name = material_in.name;
// get base color texture
- LLPointer<LLImageRaw> base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index);
+ LLPointer<LLImageRaw> base_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.baseColorTexture.index, flip);
// get normal map
- LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index);
+ LLPointer<LLImageRaw> normal_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.normalTexture.index, flip);
// get metallic-roughness texture
- LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index);
+ LLPointer<LLImageRaw> mr_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.pbrMetallicRoughness.metallicRoughnessTexture.index, flip);
// get emissive texture
- LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index);
+ LLPointer<LLImageRaw> emissive_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.emissiveTexture.index, flip);
// get occlusion map if needed
LLPointer<LLImageRaw> occlusion_img;
if (material_in.occlusionTexture.index != material_in.pbrMetallicRoughness.metallicRoughnessTexture.index)
{
- occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index);
+ occlusion_img = LLTinyGLTFHelper::getTexture(folder, model_in, material_in.occlusionTexture.index, flip);
}
LLPointer<LLViewerFetchedTexture> base_color_tex;
diff --git a/indra/newview/lltinygltfhelper.h b/indra/newview/lltinygltfhelper.h
index 256f6c854f..da505b41e9 100644
--- a/indra/newview/lltinygltfhelper.h
+++ b/indra/newview/lltinygltfhelper.h
@@ -38,10 +38,8 @@ namespace LLTinyGLTFHelper
{
LLColor4 getColor(const std::vector<double>& in);
const tinygltf::Image* getImageFromTextureIndex(const tinygltf::Model& model, S32 texture_index);
- LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name);
- LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index);
-
- LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index);
+ LLImageRaw* getTexture(const std::string& folder, const tinygltf::Model& model, S32 texture_index, std::string& name, bool flip = true);
+ 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);
@@ -50,7 +48,8 @@ namespace LLTinyGLTFHelper
const tinygltf::Model& model,
S32 mat_index,
LLFetchedGLTFMaterial* material,
- std::string& material_name);
+ std::string& material_name,
+ bool flip = true);
void initFetchedTextures(tinygltf::Material& material,
LLPointer<LLImageRaw>& base_color_img,
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index bfa9386cd4..293c47bf73 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1077,51 +1077,71 @@ void set_texture_to_material(LLViewerObject* hit_obj,
LLGLTFMaterial::TextureInfo drop_channel)
{
LLTextureEntry* te = hit_obj->getTE(hit_face);
- if (te)
+ if (!te)
{
- LLPointer<LLGLTFMaterial> material = te->getGLTFMaterialOverride();
+ return;
+ }
- // make a copy to not invalidate existing
- // material for multiple objects
- if (material.isNull())
- {
- // Start with a material override which does not make any changes
- material = new LLGLTFMaterial();
- }
- else
- {
- material = new LLGLTFMaterial(*material);
- }
+ const LLUUID base_mat_id = hit_obj->getRenderMaterialID(hit_face);
+ if (base_mat_id.isNull())
+ {
+ return;
+ }
- switch (drop_channel)
- {
- case LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR:
- default:
- {
- material->setBaseColorId(asset_id);
- }
- break;
+ if (hit_obj->isInventoryDirty() && hit_obj->hasInventoryListeners())
+ {
+ hit_obj->requestInventory();
+ return;
+ }
- case LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS:
- {
- material->setOcclusionRoughnessMetallicId(asset_id);
- }
- break;
+ LLViewerInventoryItem* mat_item = hit_obj->getInventoryItemByAsset(base_mat_id);
+ if (mat_item && !mat_item->getPermissions().allowModifyBy(gAgentID))
+ {
+ return;
+ }
- case LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE:
- {
- material->setEmissiveId(asset_id);
- }
- break;
+ LLPointer<LLGLTFMaterial> material = te->getGLTFMaterialOverride();
- case LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL:
- {
- material->setNormalId(asset_id);
- }
- break;
- }
- LLGLTFMaterialList::queueModify(hit_obj, hit_face, material);
+ // make a copy to not invalidate existing
+ // material for multiple objects
+ if (material.isNull())
+ {
+ // Start with a material override which does not make any changes
+ material = new LLGLTFMaterial();
+ }
+ else
+ {
+ material = new LLGLTFMaterial(*material);
+ }
+
+ switch (drop_channel)
+ {
+ case LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR:
+ default:
+ {
+ material->setBaseColorId(asset_id);
+ }
+ break;
+
+ case LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS:
+ {
+ material->setOcclusionRoughnessMetallicId(asset_id);
+ }
+ break;
+
+ case LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE:
+ {
+ material->setEmissiveId(asset_id);
+ }
+ break;
+
+ case LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL:
+ {
+ material->setNormalId(asset_id);
+ }
+ break;
}
+ LLGLTFMaterialList::queueModify(hit_obj, hit_face, material);
}
void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj,
@@ -1262,7 +1282,7 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj,
if (asset_id.isNull())
{
// use blank material
- asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ asset_id = BLANK_MATERIAL_ASSET_ID;
}
hit_obj->setRenderMaterialID(hit_face, asset_id);
@@ -1298,7 +1318,7 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj,
if (asset_id.isNull())
{
// use blank material
- asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID;
+ asset_id = BLANK_MATERIAL_ASSET_ID;
}
hit_obj->setRenderMaterialIDs(asset_id);
diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp
index f7455c6cb8..907cec4d17 100644
--- a/indra/newview/lltoolselect.cpp
+++ b/indra/newview/lltoolselect.cpp
@@ -173,7 +173,7 @@ LLObjectSelectionHandle LLToolSelect::handleObjectSelection(const LLPickInfo& pi
if ( ignore_group )
{
- LLSelectMgr::getInstance()->selectObjectOnly(object, SELECT_ALL_TES);
+ LLSelectMgr::getInstance()->selectObjectOnly(object, SELECT_ALL_TES, pick.mGLTFNodeIndex, pick.mGLTFPrimitiveIndex);
}
else
{
diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp
index e2e321af0d..882e08935c 100644
--- a/indra/newview/llviewerassetupload.cpp
+++ b/indra/newview/llviewerassetupload.cpp
@@ -156,9 +156,9 @@ LLSD LLResourceUploadInfo::generatePostBody()
body["next_owner_mask"] = LLSD::Integer(mNextOwnerPerms);
body["group_mask"] = LLSD::Integer(mGroupPerms);
body["everyone_mask"] = LLSD::Integer(mEveryonePerms);
+ body["expected_upload_cost"] = mExpectedUploadCost;
return body;
-
}
void LLResourceUploadInfo::logPreparedUpload()
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index 4134e35f87..9ba42e4f47 100644
--- a/indra/newview/llviewercamera.cpp
+++ b/indra/newview/llviewercamera.cpp
@@ -764,6 +764,12 @@ BOOL LLViewerCamera::cameraUnderWater() const
{
LLViewerRegion* regionp = LLWorld::instance().getRegionFromPosAgent(getOrigin());
+ if (gPipeline.mHeroProbeManager.isMirrorPass())
+ {
+ // TODO: figure out how to handle this case
+ return FALSE;
+ }
+
if (!regionp)
{
regionp = gAgent.getRegion();
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 7738cb904e..1fc51ac23c 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -54,6 +54,7 @@
#include "llvotree.h"
#include "llvovolume.h"
#include "llworld.h"
+#include "llvlcomposition.h"
#include "pipeline.h"
#include "llviewerjoystick.h"
#include "llviewerobjectlist.h"
@@ -117,12 +118,25 @@ static bool handleRenderFarClipChanged(const LLSD& newvalue)
return false;
}
-static bool handleTerrainDetailChanged(const LLSD& newvalue)
+static bool handleTerrainScaleChanged(const LLSD& newvalue)
{
- LLDrawPoolTerrain::sDetailMode = newvalue.asInteger();
+ F64 scale = newvalue.asReal();
+ if (scale != 0.0)
+ {
+ LLDrawPoolTerrain::sDetailScale = F32(1.0 / scale);
+ }
return true;
}
+static bool handlePBRTerrainScaleChanged(const LLSD& newvalue)
+{
+ F64 scale = newvalue.asReal();
+ if (scale != 0.0)
+ {
+ LLDrawPoolTerrain::sPBRDetailScale = F32(1.0 / scale);
+ }
+ return true;
+}
static bool handleDebugAvatarJointsChanged(const LLSD& newvalue)
{
@@ -423,6 +437,19 @@ static bool handleReflectionProbeDetailChanged(const LLSD& newvalue)
gPipeline.createGLBuffers();
LLViewerShaderMgr::instance()->setShaders();
gPipeline.mReflectionMapManager.reset();
+ gPipeline.mHeroProbeManager.reset();
+ }
+ return true;
+}
+
+static bool handleHeroProbeResolutionChanged(const LLSD &newvalue)
+{
+ if (gPipeline.isInit())
+ {
+ LLPipeline::refreshCachedSettings();
+ gPipeline.mHeroProbeManager.reset();
+ gPipeline.releaseGLBuffers();
+ gPipeline.createGLBuffers();
}
return true;
}
@@ -650,6 +677,16 @@ void handleFPSTuningStrategyChanged(const LLSD& newValue)
const auto newval = gSavedSettings.getU32("TuningFPSStrategy");
LLPerfStats::tunables.userFPSTuningStrategy = newval;
}
+
+void handleLocalTerrainChanged(const LLSD& newValue)
+{
+ for (U32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ const auto setting = gSavedSettings.getString(std::string("LocalTerrainAsset") + std::to_string(i + 1));
+ const LLUUID materialID(setting);
+ gLocalTerrainMaterials.setDetailAssetID(i, materialID);
+ }
+}
////////////////////////////////////////////////////////////////////////////
LLPointer<LLControlVariable> setting_get_control(LLControlGroup& group, const std::string& setting)
@@ -684,7 +721,11 @@ void settings_setup_listeners()
{
setting_setup_signal_listener(gSavedSettings, "FirstPersonAvatarVisible", handleRenderAvatarMouselookChanged);
setting_setup_signal_listener(gSavedSettings, "RenderFarClip", handleRenderFarClipChanged);
- setting_setup_signal_listener(gSavedSettings, "RenderTerrainDetail", handleTerrainDetailChanged);
+ setting_setup_signal_listener(gSavedSettings, "RenderTerrainScale", handleTerrainScaleChanged);
+ setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRScale", handlePBRTerrainScaleChanged);
+ setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRDetail", handleSetShaderChanged);
+ setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRPlanarSampleCount", handleSetShaderChanged);
+ setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRTriplanarBlendFactor", handleSetShaderChanged);
setting_setup_signal_listener(gSavedSettings, "OctreeStaticObjectSizeFactor", handleRepartition);
setting_setup_signal_listener(gSavedSettings, "OctreeDistanceFactor", handleRepartition);
setting_setup_signal_listener(gSavedSettings, "OctreeMaxNodeCapacity", handleRepartition);
@@ -724,6 +765,7 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "RenderReflectionProbeDetail", handleReflectionProbeDetailChanged);
setting_setup_signal_listener(gSavedSettings, "RenderReflectionsEnabled", handleReflectionProbeDetailChanged);
setting_setup_signal_listener(gSavedSettings, "RenderScreenSpaceReflections", handleReflectionProbeDetailChanged);
+ setting_setup_signal_listener(gSavedSettings, "RenderHeroProbeResolution", handleHeroProbeResolutionChanged);
setting_setup_signal_listener(gSavedSettings, "RenderShadowDetail", handleSetShaderChanged);
setting_setup_signal_listener(gSavedSettings, "RenderDeferredSSAO", handleSetShaderChanged);
setting_setup_signal_listener(gSavedSettings, "RenderPerformanceTest", handleRenderPerfTestChanged);
@@ -830,6 +872,10 @@ 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);
setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged);
}
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index e1d6f71cce..2a77146101 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -656,6 +656,14 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
if (!gDisconnected)
{
+ // Render mirrors and associated hero probes before we render the rest of the scene.
+ // This ensures the scene state in the hero probes are exactly the same as the rest of the scene before we render it.
+ if (gPipeline.RenderMirrors && !gSnapshot && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0))
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Update hero probes");
+ gPipeline.mHeroProbeManager.update();
+ }
+
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("display - 1");
LLAppViewer::instance()->pingMainloopTimeout("Display:Update");
if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD))
@@ -696,7 +704,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
}
gPipeline.updateGL();
-
+
stop_glerror();
LLAppViewer::instance()->pingMainloopTimeout("Display:Cull");
@@ -1064,7 +1072,7 @@ void display_cube_face()
LLSpatialGroup::sNoDelete = TRUE;
S32 occlusion = LLPipeline::sUseOcclusion;
- LLPipeline::sUseOcclusion = 0; // occlusion data is from main camera point of view, don't read or write it during cube snapshots
+ LLPipeline::sUseOcclusion = 1; // occlusion data is from main camera point of view, don't read or write it during cube snapshots
//gDepthDirty = TRUE; //let "real" render pipe know it can't trust the depth buffer for occlusion data
static LLCullResult result;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 625d32f59d..c1a8c5df9e 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -141,6 +141,7 @@
#include <boost/algorithm/string.hpp>
#include "llcleanup.h"
#include "llviewershadermgr.h"
+#include "gltfscenemanager.h"
using namespace LLAvatarAppearanceDefines;
@@ -528,10 +529,8 @@ void init_menus()
LLGridManager::getInstance()->isInProductionGrid());
// *TODO:Also fix cost in llfolderview.cpp for Inventory menus
- const std::string texture_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost());
const std::string sound_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getSoundUploadCost());
const std::string animation_upload_cost_str = std::to_string(LLAgentBenefitsMgr::current().getAnimationUploadCost());
- gMenuHolder->childSetLabelArg("Upload Image", "[COST]", texture_upload_cost_str);
gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", sound_upload_cost_str);
gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", animation_upload_cost_str);
@@ -742,10 +741,30 @@ U32 render_type_from_string(std::string render_type)
{
return LLPipeline::RENDER_TYPE_SIMPLE;
}
+ if ("materials" == render_type)
+ {
+ return LLPipeline::RENDER_TYPE_MATERIALS;
+ }
else if ("alpha" == render_type)
{
return LLPipeline::RENDER_TYPE_ALPHA;
}
+ else if ("alpha_mask" == render_type)
+ {
+ return LLPipeline::RENDER_TYPE_ALPHA_MASK;
+ }
+ else if ("fullbright_alpha_mask" == render_type)
+ {
+ return LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK;
+ }
+ else if ("fullbright" == render_type)
+ {
+ return LLPipeline::RENDER_TYPE_FULLBRIGHT;
+ }
+ else if ("glow" == render_type)
+ {
+ return LLPipeline::RENDER_TYPE_GLOW;
+ }
else if ("tree" == render_type)
{
return LLPipeline::RENDER_TYPE_TREE;
@@ -998,6 +1017,10 @@ U64 info_display_from_string(std::string info_display)
{
return LLPipeline::RENDER_DEBUG_OCTREE;
}
+ else if ("nodes" == info_display)
+ {
+ return LLPipeline::RENDER_DEBUG_NODES;
+ }
else if ("shadow frusta" == info_display)
{
return LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA;
@@ -2174,6 +2197,20 @@ class LLAdvancedPurgeShaderCache : public view_listener_t
}
};
+/////////////////////
+// REBUILD TERRAIN //
+/////////////////////
+
+
+class LLAdvancedRebuildTerrain : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ gPipeline.rebuildTerrain();
+ return true;
+ }
+};
+
////////////////////
// EVENT Recorder //
///////////////////
@@ -7929,6 +7966,30 @@ class LLAdvancedClickRenderBenchmark: public view_listener_t
}
};
+void hdri_preview();
+
+class LLAdvancedClickHDRIPreview: 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)
+ LLFloaterReg::showInstance("env_adjust_snapshot");
+ hdri_preview();
+ return true;
+ }
+};
+
+
+class LLAdvancedClickGLTFScenePreview : 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;
+ }
+};
+
// these are used in the gl menus to set control values that require shader recompilation
class LLToggleShaderControl : public view_listener_t
{
@@ -9276,6 +9337,8 @@ void LLUploadCostCalculator::calculateCost(const std::string& asset_type_str)
if (asset_type_str == "texture")
{
+ // This use minimal texture cost to allow bulk and
+ // texture upload menu options to be visible
upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
}
else if (asset_type_str == "animation")
@@ -9573,7 +9636,10 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickRenderShadowOption(), "Advanced.ClickRenderShadowOption");
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 LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
+ view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode");
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 5461e0f362..240c756411 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -546,21 +546,39 @@ void do_bulk_upload(std::vector<std::string> filenames, const LLSD& notification
std::string ext = gDirUtilp->getExtension(filename);
LLAssetType::EType asset_type;
U32 codec;
- S32 expected_upload_cost;
- if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec) &&
- LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost))
- {
- LLResourceUploadInfo::ptr_t uploadInfo(new LLNewFileResourceUploadInfo(
- filename,
- asset_name,
- asset_name, 0,
- LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
- LLFloaterPerms::getNextOwnerPerms("Uploads"),
- LLFloaterPerms::getGroupPerms("Uploads"),
- LLFloaterPerms::getEveryonePerms("Uploads"),
- expected_upload_cost));
-
- upload_new_resource(uploadInfo);
+ S32 expected_upload_cost = 0;
+
+ if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec))
+ {
+ bool resource_upload = false;
+ if (asset_type == LLAssetType::AT_TEXTURE)
+ {
+ LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec);
+ if (gDirUtilp->fileExists(filename) && image_frmted->load(filename))
+ {
+ expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(image_frmted);
+ resource_upload = true;
+ }
+ }
+ else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, expected_upload_cost))
+ {
+ resource_upload = true;
+ }
+
+ if (resource_upload)
+ {
+ LLResourceUploadInfo::ptr_t uploadInfo(new LLNewFileResourceUploadInfo(
+ filename,
+ asset_name,
+ asset_name, 0,
+ LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost));
+
+ upload_new_resource(uploadInfo);
+ }
}
// gltf does not use normal upload procedure
@@ -602,17 +620,26 @@ bool get_bulk_upload_expected_cost(const std::vector<std::string>& filenames, S3
U32 codec;
S32 cost;
- if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec) &&
- LLAgentBenefitsMgr::current().findUploadCost(asset_type, cost))
+ if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec))
{
- total_cost += cost;
- file_count++;
- }
+ if (asset_type == LLAssetType::AT_TEXTURE)
+ {
+ LLPointer<LLImageFormatted> image_frmted = LLImageFormatted::createFromType(codec);
+ if (gDirUtilp->fileExists(filename) && image_frmted->load(filename))
+ {
+ total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(image_frmted);
+ file_count++;
+ }
+ }
+ else if (LLAgentBenefitsMgr::current().findUploadCost(asset_type, cost))
+ {
+ total_cost += cost;
+ file_count++;
+ }
+ }
if (ext == "gltf" || ext == "glb")
{
- S32 texture_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost();
-
tinygltf::Model model;
if (LLTinyGLTFHelper::loadModel(filename, model))
@@ -629,24 +656,22 @@ bool get_bulk_upload_expected_cost(const std::vector<std::string>& filenames, S3
{
// Todo: make it account for possibility of same texture in different
// materials and even in scope of same material
- S32 texture_count = 0;
- if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].notNull())
+ if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].notNull() && material->mBaseColorTexture)
{
- texture_count++;
+ total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mBaseColorTexture);
}
- if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].notNull())
+ if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].notNull() && material->mMetallicRoughnessTexture)
{
- texture_count++;
+ total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mMetallicRoughnessTexture);
}
- if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].notNull())
+ if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].notNull() && material->mNormalTexture)
{
- texture_count++;
+ total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mNormalTexture);
}
- if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].notNull())
+ if (material->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].notNull() && material->mEmissiveTexture)
{
- texture_count++;
+ total_cost += LLAgentBenefitsMgr::current().getTextureUploadCost(material->mEmissiveTexture);
}
- total_cost += texture_count * texture_upload_cost;
file_count++;
}
}
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index f1fac106c7..b66d4b1dab 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -1529,12 +1529,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
std::string temp_string;
mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, temp_string, block_num );
-
+
LLColor4U coloru;
mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num);
-
+
// alpha was flipped so that it zero encoded better
coloru.mV[3] = 255 - coloru.mV[3];
+
mText->setColor(LLColor4(coloru));
mText->setString(temp_string);
@@ -1914,6 +1915,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
{
std::string temp_string;
dp->unpackString(temp_string, "Text");
+
LLColor4U coloru;
dp->unpackBinaryDataFixed(coloru.mV, 4, "Color");
coloru.mV[3] = 255 - coloru.mV[3];
@@ -4372,6 +4374,168 @@ const LLVector3 &LLViewerObject::getPositionAgent() const
return mPositionAgent;
}
+LLMatrix4a LLViewerObject::getGLTFAssetToAgentTransform() const
+{
+ LLMatrix4 root;
+ root.initScale(getScale());
+ root.rotate(getRenderRotation());
+ root.translate(getPositionAgent());
+
+ LLMatrix4a mat;
+ mat.loadu((F32*)root.mMatrix);
+
+ return mat;
+}
+
+LLVector3 LLViewerObject::getGLTFNodePositionAgent(S32 node_index) const
+{
+ LLVector3 ret;
+ getGLTFNodeTransformAgent(node_index, &ret, nullptr, nullptr);
+ return ret;
+
+}
+
+LLMatrix4a LLViewerObject::getAgentToGLTFAssetTransform() const
+{
+ LLMatrix4 root;
+ LLVector3 scale = getScale();
+ scale.mV[0] = 1.f / scale.mV[0];
+ scale.mV[1] = 1.f / scale.mV[1];
+ scale.mV[2] = 1.f / scale.mV[2];
+
+ root.translate(-getPositionAgent());
+ root.rotate(~getRenderRotation());
+
+ LLMatrix4 scale_mat;
+ scale_mat.initScale(scale);
+
+ root *= scale_mat;
+ LLMatrix4a mat;
+ mat.loadu((F32*)root.mMatrix);
+
+ return mat;
+}
+
+LLMatrix4a LLViewerObject::getGLTFNodeTransformAgent(S32 node_index) const
+{
+ LLMatrix4a mat;
+
+ if (mGLTFAsset.notNull() && 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);
+
+ mat = node_to_agent;
+ }
+ else
+ {
+ mat.setIdentity();
+ }
+
+ return mat;
+}
+void LLViewerObject::getGLTFNodeTransformAgent(S32 node_index, LLVector3* position, LLQuaternion* rotation, LLVector3* scale) const
+{
+ LLMatrix4a node_to_agent = getGLTFNodeTransformAgent(node_index);
+
+ if (position)
+ {
+ LLVector4a p = node_to_agent.getTranslation();
+ position->set(p.getF32ptr());
+ }
+
+ if (rotation)
+ {
+ rotation->set(node_to_agent.asMatrix4());
+ }
+
+ if (scale)
+ {
+ scale->mV[0] = node_to_agent.mMatrix[0].getLength3().getF32();
+ scale->mV[1] = node_to_agent.mMatrix[1].getLength3().getF32();
+ scale->mV[2] = node_to_agent.mMatrix[2].getLength3().getF32();
+ }
+}
+
+void decomposeMatrix(const LLMatrix4a& mat, LLVector3& position, LLQuaternion& rotation, LLVector3& scale)
+{
+ LLVector4a p = mat.getTranslation();
+ position.set(p.getF32ptr());
+
+ rotation.set(mat.asMatrix4());
+
+ scale.mV[0] = mat.mMatrix[0].getLength3().getF32();
+ scale.mV[1] = mat.mMatrix[1].getLength3().getF32();
+ scale.mV[2] = mat.mMatrix[2].getLength3().getF32();
+}
+
+void LLViewerObject::setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion& rotation)
+{
+ if (mGLTFAsset.notNull() && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
+ {
+ auto& node = mGLTFAsset->mNodes[node_index];
+
+ LLMatrix4a agent_to_asset = getAgentToGLTFAssetTransform();
+ LLMatrix4a agent_to_node = agent_to_asset;
+
+ if (node.mParent != -1)
+ {
+ auto& parent = mGLTFAsset->mNodes[node.mParent];
+ matMul(agent_to_asset, parent.mAssetMatrixInv, agent_to_node);
+ }
+
+ LLQuaternion agent_to_node_rot(agent_to_node.asMatrix4());
+ LLQuaternion new_rot;
+
+ new_rot = rotation * agent_to_node_rot;
+ new_rot.normalize();
+
+ LLVector3 pos;
+ LLQuaternion rot;
+ LLVector3 scale;
+ decomposeMatrix(node.mMatrix, pos, rot, scale);
+
+ node.mMatrix.asMatrix4().initAll(scale, new_rot, pos);
+
+ mGLTFAsset->updateTransforms();
+ }
+}
+
+void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
+{
+ if (mGLTFAsset.notNull() && 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);
+
+ LLVector4a origin = LLVector4a::getZero();
+ LLVector4a offset_v;
+ offset_v.load3(offset.mV);
+
+
+ agent_to_node.affineTransform(offset_v, offset_v);
+ agent_to_node.affineTransform(origin, origin);
+
+ offset_v.sub(origin);
+ offset_v.getF32ptr()[3] = 1.f;
+
+ LLMatrix4a trans;
+ trans.setIdentity();
+ trans.mMatrix[3] = offset_v;
+
+ matMul(trans, node.mMatrix, node.mMatrix);
+
+ // TODO -- only update transforms for this node and its children (or use a dirty flag)
+ mGLTFAsset->updateTransforms();
+ }
+}
+
const LLVector3 &LLViewerObject::getPositionRegion() const
{
if (!isRoot())
@@ -5359,7 +5523,6 @@ S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright)
return retval;
}
-
S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags)
{
// this might need work for media type
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 80da7b2f73..64c1ee6633 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -45,6 +45,7 @@
#include "llbbox.h"
#include "llrigginginfo.h"
#include "llreflectionmap.h"
+#include "gltf/asset.h"
class LLAgent; // TODO: Get rid of this.
class LLAudioSource;
@@ -314,6 +315,18 @@ public:
virtual const LLVector3 &getPositionAgent() const;
virtual const LLVector3 getRenderPosition() const;
+ LLMatrix4a getAgentToGLTFAssetTransform() const;
+ LLMatrix4a getGLTFAssetToAgentTransform() const;
+ LLVector3 getGLTFNodePositionAgent(S32 node_index) const;
+ LLMatrix4a getGLTFNodeTransformAgent(S32 node_index) const;
+ void getGLTFNodeTransformAgent(S32 node_index, LLVector3* position, LLQuaternion* rotation, LLVector3* scale) const;
+
+ // move the node at the given index by the given offset in agent space
+ void moveGLTFNode(S32 node_index, const LLVector3& offset);
+
+ // set the rotation in agent space of the given node
+ void setGLTFNodeRotationAgent(S32 node_index, const LLQuaternion& rotation);
+
virtual const LLVector3 getPivotPositionAgent() const; // Usually = to getPositionAgent, unless like flex objects it's not
LLViewerObject* getRootEdit() const;
@@ -722,6 +735,8 @@ public:
F32 mPhysicsDensity;
F32 mPhysicsRestitution;
+ // Associated GLTF Asset
+ LLPointer<LL::GLTF::Asset> mGLTFAsset;
// Pipeline classes
LLPointer<LLDrawable> mDrawable;
@@ -944,6 +959,7 @@ public:
// reflection probe state
bool mIsReflectionProbe = false; // if true, this object should register itself with LLReflectionProbeManager
LLPointer<LLReflectionMap> mReflectionProbe = nullptr; // reflection probe coupled to this viewer object. If not null, should be deregistered when this object is destroyed
+ bool mIsHeroProbe = false; // This is a special case for mirrors and other high resolution probes.
// the amount of GPU time (in ms) it took to render this object according to LLPipeline::profileAvatar
// -1.f if no profile data available
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index eba7189a82..61ab2791fb 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -794,8 +794,9 @@ void LLViewerRegion::loadObjectCache()
if(LLVOCache::instanceExists())
{
LLVOCache & vocache = LLVOCache::instance();
- vocache.readFromCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap);
- vocache.readGenericExtrasFromCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesLLSD);
+ // Without this a "corrupted" vocache persists until a cache clear or other rewrite. Mark as dirty hereif read fails to force a rewrite.
+ mCacheDirty = !vocache.readFromCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap);
+ vocache.readGenericExtrasFromCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesLLSD, mImpl->mCacheMap);
if (mImpl->mCacheMap.empty())
{
@@ -1112,6 +1113,11 @@ void LLViewerRegion::dirtyHeights()
}
}
+void LLViewerRegion::dirtyAllPatches()
+{
+ getLand().dirtyAllPatches();
+}
+
//physically delete the cache entry
void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering)
{
@@ -1162,13 +1168,13 @@ void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering)
child = entry->getChild();
}
}
-
+ // Kill the assocaited overrides
+ mImpl->mGLTFOverridesLLSD.erase(entry->getLocalID());
//will remove it from the object cache, real deletion
entry->setState(LLVOCacheEntry::INACTIVE);
entry->removeOctreeEntry();
entry->setValid(FALSE);
- // TODO kill extras/material overrides cache too
}
//physically delete the cache entry
@@ -1612,7 +1618,19 @@ void LLViewerRegion::idleUpdate(F32 max_update_time)
mLastUpdate = LLViewerOctreeEntryData::getCurrentFrame();
- mImpl->mLandp->idleUpdate(max_update_time);
+ static LLCachedControl<bool> pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false);
+ static LLCachedControl<bool> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", false);
+ bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR);
+ bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals;
+
+ if (!pbr_land)
+ {
+ mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time);
+ }
+ else
+ {
+ mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time);
+ }
if (mParcelOverlay)
{
@@ -1913,7 +1931,21 @@ LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* o
// As above, but forcibly do the update.
void LLViewerRegion::forceUpdate()
{
- mImpl->mLandp->idleUpdate(0.f);
+ constexpr F32 max_update_time = 0.f;
+
+ static LLCachedControl<bool> pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false);
+ static LLCachedControl<bool> pbr_terrain_experimental_normals(gSavedSettings, "RenderTerrainPBRNormalsEnabled", false);
+ bool pbr_material = mImpl->mCompositionp && (mImpl->mCompositionp->getMaterialType() == LLTerrainMaterials::Type::PBR);
+ bool pbr_land = pbr_material && pbr_terrain_enabled && pbr_terrain_experimental_normals;
+
+ if (!pbr_land)
+ {
+ mImpl->mLandp->idleUpdate</*PBR=*/false>(max_update_time);
+ }
+ else
+ {
+ mImpl->mLandp->idleUpdate</*PBR=*/true>(max_update_time);
+ }
if (mParcelOverlay)
{
@@ -2414,7 +2446,55 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
mSimulatorFeatures = sim_features;
setSimulatorFeaturesReceived(true);
-
+
+ // WARNING: this is called from a coroutine, and flipping saved settings has a LOT of side effects, shuttle
+ // the work below back to the main loop
+ //
+
+ // copy features to lambda in case the region is deleted before the lambda is executed
+ LLSD features = mSimulatorFeatures;
+
+ auto work = [=]()
+ {
+ // if region has MaxTextureResolution, set max_texture_dimension settings, otherwise use default
+ if (features.has("MaxTextureResolution"))
+ {
+ S32 max_texture_resolution = features["MaxTextureResolution"].asInteger();
+ gSavedSettings.setS32("max_texture_dimension_X", max_texture_resolution);
+ gSavedSettings.setS32("max_texture_dimension_Y", max_texture_resolution);
+ }
+ else
+ {
+ gSavedSettings.setS32("max_texture_dimension_X", 1024);
+ gSavedSettings.setS32("max_texture_dimension_Y", 1024);
+ }
+
+ if (features.has("PBRTerrainEnabled"))
+ {
+ bool enabled = features["PBRTerrainEnabled"];
+ gSavedSettings.setBOOL("RenderTerrainPBREnabled", enabled);
+ }
+ else
+ {
+ gSavedSettings.setBOOL("RenderTerrainPBREnabled", false);
+ }
+
+ if (features.has("PBRMaterialSwatchEnabled"))
+ {
+ bool enabled = features["PBRMaterialSwatchEnabled"];
+ gSavedSettings.setBOOL("UIPreviewMaterial", enabled);
+ }
+ else
+ {
+ gSavedSettings.setBOOL("UIPreviewMaterial", false);
+ }
+ };
+
+ auto workqueue = LL::WorkQueue::getInstance("mainloop");
+ if (workqueue)
+ {
+ LL::WorkQueue::postMaybe(workqueue, work);
+ }
}
//this is called when the parent is not cacheable.
@@ -2672,7 +2752,14 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObjec
void LLViewerRegion::cacheFullUpdateGLTFOverride(const LLGLTFOverrideCacheEntry &override_data)
{
U32 local_id = override_data.mLocalId;
- mImpl->mGLTFOverridesLLSD[local_id] = override_data;
+ if (override_data.mSides.size() > 0)
+ { // empty override means overrides were removed from this object
+ mImpl->mGLTFOverridesLLSD[local_id] = override_data;
+ }
+ else
+ {
+ mImpl->mGLTFOverridesLLSD.erase(local_id);
+ }
}
LLVOCacheEntry* LLViewerRegion::getCacheEntryForOctree(U32 local_id)
@@ -2889,6 +2976,11 @@ void LLViewerRegion::dumpCache()
// TODO - add overrides cache too
}
+void LLViewerRegion::clearVOCacheFromMemory()
+{
+ mImpl->mCacheMap.clear();
+}
+
void LLViewerRegion::unpackRegionHandshake()
{
LLMessageSystem *msg = gMessageSystem;
@@ -2973,20 +3065,20 @@ void LLViewerRegion::unpackRegionHandshake()
// Get the 4 textures for land
msg->getUUID("RegionInfo", "TerrainDetail0", tmp_id);
- changed |= (tmp_id != compp->getDetailTextureID(0));
- compp->setDetailTextureID(0, tmp_id);
+ changed |= (tmp_id != compp->getDetailAssetID(0));
+ compp->setDetailAssetID(0, tmp_id);
msg->getUUID("RegionInfo", "TerrainDetail1", tmp_id);
- changed |= (tmp_id != compp->getDetailTextureID(1));
- compp->setDetailTextureID(1, tmp_id);
+ changed |= (tmp_id != compp->getDetailAssetID(1));
+ compp->setDetailAssetID(1, tmp_id);
msg->getUUID("RegionInfo", "TerrainDetail2", tmp_id);
- changed |= (tmp_id != compp->getDetailTextureID(2));
- compp->setDetailTextureID(2, tmp_id);
+ changed |= (tmp_id != compp->getDetailAssetID(2));
+ compp->setDetailAssetID(2, tmp_id);
msg->getUUID("RegionInfo", "TerrainDetail3", tmp_id);
- changed |= (tmp_id != compp->getDetailTextureID(3));
- compp->setDetailTextureID(3, tmp_id);
+ changed |= (tmp_id != compp->getDetailAssetID(3));
+ compp->setDetailAssetID(3, tmp_id);
// Get the start altitude and range values for land textures
F32 tmp_f32;
@@ -3672,6 +3764,11 @@ void LLViewerRegion::applyCacheMiscExtras(LLViewerObject* obj)
auto iter = mImpl->mGLTFOverridesLLSD.find(local_id);
if (iter != mImpl->mGLTFOverridesLLSD.end())
{
+ // UUID can be inserted null, so backfill the UUID if it was left empty
+ if (iter->second.mObjectId.isNull())
+ {
+ iter->second.mObjectId = obj->getID();
+ }
llassert(iter->second.mGLTFMaterial.size() == iter->second.mSides.size());
for (auto& side : iter->second.mGLTFMaterial)
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 622490c881..1c89cc4e6d 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -163,6 +163,9 @@ public:
// Call this whenever you change the height data in the region.
// (Automatically called by LLSurfacePatch's update routine)
void dirtyHeights();
+ // Call this whenever you want to force all terrain to rebuild.
+ // (For example, if a global terrain config option has changed)
+ void dirtyAllPatches();
LLViewerParcelOverlay *getParcelOverlay() const
{ return mParcelOverlay; }
@@ -375,7 +378,8 @@ public:
LLViewerObject* updateCacheEntry(U32 local_id, LLViewerObject* objectp);
void findOrphans(U32 parent_id);
void clearCachedVisibleObjects();
- void dumpCache();
+ void dumpCache ();
+ void clearVOCacheFromMemory();
void unpackRegionHandshake();
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 2bab50300b..5144b28ce9 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -69,6 +69,14 @@ bool LLViewerShaderMgr::sSkipReload = false;
LLVector4 gShinyOrigin;
+S32 clamp_terrain_mapping(S32 mapping)
+{
+ // 1 = "flat", 2 not implemented, 3 = triplanar mapping
+ mapping = llclamp(mapping, 1, 3);
+ if (mapping == 2) { mapping = 1; }
+ return mapping;
+}
+
//utility shaders
LLGLSLShader gOcclusionProgram;
LLGLSLShader gSkinnedOcclusionProgram;
@@ -76,13 +84,16 @@ LLGLSLShader gOcclusionCubeProgram;
LLGLSLShader gGlowCombineProgram;
LLGLSLShader gReflectionMipProgram;
LLGLSLShader gGaussianProgram;
-LLGLSLShader gRadianceGenProgram;
+LLGLSLShader gRadianceGenProgram;
+LLGLSLShader gHeroRadianceGenProgram;
LLGLSLShader gIrradianceGenProgram;
LLGLSLShader gGlowCombineFXAAProgram;
LLGLSLShader gTwoTextureCompareProgram;
LLGLSLShader gOneTextureFilterProgram;
LLGLSLShader gDebugProgram;
LLGLSLShader gSkinnedDebugProgram;
+LLGLSLShader gNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT];
+LLGLSLShader gSkinnedNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT];
LLGLSLShader gClipProgram;
LLGLSLShader gAlphaMaskProgram;
LLGLSLShader gBenchmarkProgram;
@@ -95,7 +106,6 @@ LLGLSLShader gObjectPreviewProgram;
LLGLSLShader gSkinnedObjectPreviewProgram;
LLGLSLShader gPhysicsPreviewProgram;
LLGLSLShader gObjectFullbrightAlphaMaskProgram;
-LLGLSLShader gSkinnedObjectFullbrightAlphaMaskProgram;
LLGLSLShader gObjectBumpProgram;
LLGLSLShader gSkinnedObjectBumpProgram;
LLGLSLShader gObjectAlphaMaskNoColorProgram;
@@ -186,10 +196,12 @@ LLGLSLShader gDeferredPostGammaCorrectProgram;
LLGLSLShader gNoPostGammaCorrectProgram;
LLGLSLShader gLegacyPostGammaCorrectProgram;
LLGLSLShader gExposureProgram;
+LLGLSLShader gExposureProgramNoFade;
LLGLSLShader gLuminanceProgram;
LLGLSLShader gFXAAProgram;
LLGLSLShader gDeferredPostNoDoFProgram;
LLGLSLShader gDeferredWLSkyProgram;
+LLGLSLShader gEnvironmentMapProgram;
LLGLSLShader gDeferredWLCloudProgram;
LLGLSLShader gDeferredWLSunProgram;
LLGLSLShader gDeferredWLMoonProgram;
@@ -214,9 +226,10 @@ LLGLSLShader gDeferredSkinnedPBROpaqueProgram;
LLGLSLShader gHUDPBRAlphaProgram;
LLGLSLShader gDeferredPBRAlphaProgram;
LLGLSLShader gDeferredSkinnedPBRAlphaProgram;
+LLGLSLShader gDeferredPBRTerrainProgram;
//helper for making a rigged variant of a given shader
-bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader)
+static bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader)
{
riggedShader.mName = llformat("Skinned %s", shader.mName.c_str());
riggedShader.mFeatures = shader.mFeatures;
@@ -231,64 +244,100 @@ bool make_rigged_variant(LLGLSLShader& shader, LLGLSLShader& riggedShader)
return riggedShader.createShader(NULL, NULL);
}
+#ifdef SHOW_ASSERT
+// return true if there are no redundant shaders in the given vector
+// also checks for redundant variants
+static bool no_redundant_shaders(const std::vector<LLGLSLShader*>& shaders)
+{
+ std::set<std::string> names;
+ for (LLGLSLShader* shader : shaders)
+ {
+ if (names.find(shader->mName) != names.end())
+ {
+ LL_WARNS("Shader") << "Redundant shader: " << shader->mName << LL_ENDL;
+ return false;
+ }
+ names.insert(shader->mName);
+
+ if (shader->mRiggedVariant)
+ {
+ if (names.find(shader->mRiggedVariant->mName) != names.end())
+ {
+ LL_WARNS("Shader") << "Redundant shader: " << shader->mRiggedVariant->mName << LL_ENDL;
+ return false;
+ }
+ names.insert(shader->mRiggedVariant->mName);
+ }
+ }
+ return true;
+}
+#endif
+
+
LLViewerShaderMgr::LLViewerShaderMgr() :
mShaderLevel(SHADER_COUNT, 0),
mMaxAvatarShaderLevel(0)
{
+}
+
+LLViewerShaderMgr::~LLViewerShaderMgr()
+{
+ mShaderLevel.clear();
+ mShaderList.clear();
+}
+
+void LLViewerShaderMgr::finalizeShaderList()
+{
//ONLY shaders that need WL Param management should be added here
- mShaderList.push_back(&gAvatarProgram);
- mShaderList.push_back(&gWaterProgram);
- mShaderList.push_back(&gWaterEdgeProgram);
- mShaderList.push_back(&gAvatarEyeballProgram);
- mShaderList.push_back(&gImpostorProgram);
- mShaderList.push_back(&gObjectBumpProgram);
- mShaderList.push_back(&gSkinnedObjectBumpProgram);
- mShaderList.push_back(&gObjectFullbrightAlphaMaskProgram);
- mShaderList.push_back(&gSkinnedObjectFullbrightAlphaMaskProgram);
- mShaderList.push_back(&gObjectAlphaMaskNoColorProgram);
- mShaderList.push_back(&gUnderWaterProgram);
- mShaderList.push_back(&gDeferredSunProgram);
+ mShaderList.push_back(&gAvatarProgram);
+ mShaderList.push_back(&gWaterProgram);
+ mShaderList.push_back(&gWaterEdgeProgram);
+ mShaderList.push_back(&gAvatarEyeballProgram);
+ mShaderList.push_back(&gImpostorProgram);
+ mShaderList.push_back(&gObjectBumpProgram);
+ mShaderList.push_back(&gObjectFullbrightAlphaMaskProgram);
+ mShaderList.push_back(&gObjectAlphaMaskNoColorProgram);
+ mShaderList.push_back(&gUnderWaterProgram);
+ mShaderList.push_back(&gDeferredSunProgram);
mShaderList.push_back(&gHazeProgram);
mShaderList.push_back(&gHazeWaterProgram);
- mShaderList.push_back(&gDeferredSoftenProgram);
- mShaderList.push_back(&gDeferredAlphaProgram);
+ mShaderList.push_back(&gDeferredSoftenProgram);
+ mShaderList.push_back(&gDeferredAlphaProgram);
mShaderList.push_back(&gHUDAlphaProgram);
- mShaderList.push_back(&gDeferredSkinnedAlphaProgram);
- mShaderList.push_back(&gDeferredAlphaImpostorProgram);
- mShaderList.push_back(&gDeferredSkinnedAlphaImpostorProgram);
- mShaderList.push_back(&gDeferredFullbrightProgram);
+ mShaderList.push_back(&gDeferredAlphaImpostorProgram);
+ mShaderList.push_back(&gDeferredFullbrightProgram);
mShaderList.push_back(&gHUDFullbrightProgram);
- mShaderList.push_back(&gDeferredFullbrightAlphaMaskProgram);
+ mShaderList.push_back(&gDeferredFullbrightAlphaMaskProgram);
mShaderList.push_back(&gHUDFullbrightAlphaMaskProgram);
mShaderList.push_back(&gDeferredFullbrightAlphaMaskAlphaProgram);
mShaderList.push_back(&gHUDFullbrightAlphaMaskAlphaProgram);
- mShaderList.push_back(&gDeferredFullbrightShinyProgram);
+ mShaderList.push_back(&gDeferredFullbrightShinyProgram);
mShaderList.push_back(&gHUDFullbrightShinyProgram);
- mShaderList.push_back(&gDeferredSkinnedFullbrightShinyProgram);
- mShaderList.push_back(&gDeferredSkinnedFullbrightProgram);
- mShaderList.push_back(&gDeferredSkinnedFullbrightAlphaMaskProgram);
- mShaderList.push_back(&gDeferredSkinnedFullbrightAlphaMaskAlphaProgram);
- mShaderList.push_back(&gDeferredEmissiveProgram);
- mShaderList.push_back(&gDeferredSkinnedEmissiveProgram);
- mShaderList.push_back(&gDeferredAvatarEyesProgram);
+ mShaderList.push_back(&gDeferredEmissiveProgram);
+ mShaderList.push_back(&gDeferredAvatarEyesProgram);
mShaderList.push_back(&gDeferredAvatarAlphaProgram);
- mShaderList.push_back(&gDeferredWLSkyProgram);
- mShaderList.push_back(&gDeferredWLCloudProgram);
+ mShaderList.push_back(&gEnvironmentMapProgram);
+ mShaderList.push_back(&gDeferredWLSkyProgram);
+ mShaderList.push_back(&gDeferredWLCloudProgram);
mShaderList.push_back(&gDeferredWLMoonProgram);
mShaderList.push_back(&gDeferredWLSunProgram);
mShaderList.push_back(&gDeferredPBRAlphaProgram);
mShaderList.push_back(&gHUDPBRAlphaProgram);
- mShaderList.push_back(&gDeferredSkinnedPBRAlphaProgram);
mShaderList.push_back(&gDeferredPostGammaCorrectProgram); // for gamma
mShaderList.push_back(&gNoPostGammaCorrectProgram);
mShaderList.push_back(&gLegacyPostGammaCorrectProgram);
-
-}
-
-LLViewerShaderMgr::~LLViewerShaderMgr()
-{
- mShaderLevel.clear();
- mShaderList.clear();
+ mShaderList.push_back(&gDeferredDiffuseProgram);
+ mShaderList.push_back(&gDeferredBumpProgram);
+ mShaderList.push_back(&gDeferredPBROpaqueProgram);
+ mShaderList.push_back(&gDeferredAvatarProgram);
+ mShaderList.push_back(&gDeferredTerrainProgram);
+ mShaderList.push_back(&gDeferredPBRTerrainProgram);
+ mShaderList.push_back(&gDeferredDiffuseAlphaMaskProgram);
+ mShaderList.push_back(&gDeferredNonIndexedDiffuseAlphaMaskProgram);
+ mShaderList.push_back(&gDeferredTreeProgram);
+
+ // make sure there are no redundancies
+ llassert(no_redundant_shaders(mShaderList));
}
// static
@@ -343,6 +392,10 @@ void LLViewerShaderMgr::setShaders()
return;
}
+ mShaderList.clear();
+
+ LLShaderMgr::sMirrorsEnabled = LLPipeline::RenderMirrors;
+
if (!gGLManager.mHasRequirements)
{
// Viewer will show 'hardware requirements' warning later
@@ -370,7 +423,10 @@ void LLViewerShaderMgr::setShaders()
static LLCachedControl<U32> max_texture_index(gSavedSettings, "RenderMaxTextureIndex", 16);
// when using indexed texture rendering, leave some texture units available for shadow and reflection maps
- LLGLSLShader::sIndexedTextureChannels = llmax(llmin(gGLManager.mNumTextureImageUnits-12, (S32) max_texture_index), 1);
+ static LLCachedControl<S32> reserved_texture_units(gSavedSettings, "RenderReservedTextureIndices", 14);
+
+ LLGLSLShader::sIndexedTextureChannels =
+ llclamp<S32>(max_texture_index, 1, gGLManager.mNumTextureImageUnits-reserved_texture_units);
reentrance = true;
@@ -524,6 +580,8 @@ void LLViewerShaderMgr::setShaders()
}
gPipeline.createGLBuffers();
+ finalizeShaderList();
+
reentrance = false;
}
@@ -623,6 +681,16 @@ std::string LLViewerShaderMgr::loadBasicShaders()
attribs["REF_SAMPLE_COUNT"] = "32";
}
+ { // PBR terrain
+ const S32 mapping = clamp_terrain_mapping(gSavedSettings.getS32("RenderTerrainPBRPlanarSampleCount"));
+ attribs["TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT"] = llformat("%d", mapping);
+ const F32 triplanar_factor = gSavedSettings.getF32("RenderTerrainPBRTriplanarBlendFactor");
+ attribs["TERRAIN_TRIPLANAR_BLEND_FACTOR"] = llformat("%.2f", triplanar_factor);
+ S32 detail = gSavedSettings.getS32("RenderTerrainPBRDetail");
+ detail = llclamp(detail, TERRAIN_PBR_DETAIL_MIN, TERRAIN_PBR_DETAIL_MAX);
+ attribs["TERRAIN_PBR_DETAIL"] = llformat("%d", detail);
+ }
+
LLGLSLShader::sGlobalDefines = attribs;
// We no longer have to bind the shaders to global glhandles, they are automatically added to a map now.
@@ -655,11 +723,12 @@ std::string LLViewerShaderMgr::loadBasicShaders()
index_channels.push_back(-1); shaders.push_back( make_pair( "windlight/atmosphericsFuncs.glsl", mShaderLevel[SHADER_WINDLIGHT] ) );
index_channels.push_back(-1); shaders.push_back( make_pair( "windlight/atmosphericsF.glsl", mShaderLevel[SHADER_WINDLIGHT] ) );
index_channels.push_back(-1); shaders.push_back( make_pair( "environment/waterFogF.glsl", mShaderLevel[SHADER_WATER] ) );
- index_channels.push_back(-1); shaders.push_back( make_pair( "environment/encodeNormF.glsl", mShaderLevel[SHADER_ENVIRONMENT] ) );
index_channels.push_back(-1); shaders.push_back( make_pair( "environment/srgbF.glsl", mShaderLevel[SHADER_ENVIRONMENT] ) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/deferredUtil.glsl", 1) );
+ index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/globalF.glsl", 1));
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/shadowUtil.glsl", 1) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/aoUtil.glsl", 1) );
+ index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/pbrterrainUtilF.glsl", 1) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/reflectionProbeF.glsl", has_reflection_probes ? 3 : 2) );
index_channels.push_back(-1); shaders.push_back( make_pair( "deferred/screenSpaceReflUtil.glsl", ssr ? 3 : 1) );
index_channels.push_back(-1); shaders.push_back( make_pair( "lighting/lightNonIndexedF.glsl", mShaderLevel[SHADER_LIGHTING] ) );
@@ -917,11 +986,13 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredCoFProgram.unload();
gDeferredDoFCombineProgram.unload();
gExposureProgram.unload();
+ gExposureProgramNoFade.unload();
gLuminanceProgram.unload();
gDeferredPostGammaCorrectProgram.unload();
gNoPostGammaCorrectProgram.unload();
gLegacyPostGammaCorrectProgram.unload();
gFXAAProgram.unload();
+ gEnvironmentMapProgram.unload();
gDeferredWLSkyProgram.unload();
gDeferredWLCloudProgram.unload();
gDeferredWLSunProgram.unload();
@@ -951,6 +1022,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredSkinnedPBROpaqueProgram.unload();
gDeferredPBRAlphaProgram.unload();
gDeferredSkinnedPBRAlphaProgram.unload();
+ gDeferredPBRTerrainProgram.unload();
return TRUE;
}
@@ -970,7 +1042,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredDiffuseProgram.mName = "Deferred Diffuse Shader";
- gDeferredDiffuseProgram.mFeatures.encodesNormal = true;
gDeferredDiffuseProgram.mFeatures.hasSrgb = true;
gDeferredDiffuseProgram.mShaderFiles.clear();
gDeferredDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseV.glsl", GL_VERTEX_SHADER));
@@ -984,7 +1055,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredDiffuseAlphaMaskProgram.mName = "Deferred Diffuse Alpha Mask Shader";
- gDeferredDiffuseAlphaMaskProgram.mFeatures.encodesNormal = true;
gDeferredDiffuseAlphaMaskProgram.mShaderFiles.clear();
gDeferredDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseV.glsl", GL_VERTEX_SHADER));
gDeferredDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseAlphaMaskIndexedF.glsl", GL_FRAGMENT_SHADER));
@@ -997,7 +1067,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredNonIndexedDiffuseAlphaMaskProgram.mName = "Deferred Diffuse Non-Indexed Alpha Mask Shader";
- gDeferredNonIndexedDiffuseAlphaMaskProgram.mFeatures.encodesNormal = true;
gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderFiles.clear();
gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseV.glsl", GL_VERTEX_SHADER));
gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/diffuseAlphaMaskF.glsl", GL_FRAGMENT_SHADER));
@@ -1009,7 +1078,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mName = "Deferred Diffuse Non-Indexed Alpha Mask No Color Shader";
- gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mFeatures.encodesNormal = true;
gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderFiles.clear();
gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("deferred/diffuseNoColorV.glsl", GL_VERTEX_SHADER));
gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("deferred/diffuseAlphaMaskNoColorF.glsl", GL_FRAGMENT_SHADER));
@@ -1021,7 +1089,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredBumpProgram.mName = "Deferred Bump Shader";
- gDeferredBumpProgram.mFeatures.encodesNormal = true;
gDeferredBumpProgram.mShaderFiles.clear();
gDeferredBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpV.glsl", GL_VERTEX_SHADER));
gDeferredBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER));
@@ -1044,9 +1111,17 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
{
if (success)
{
- mShaderList.push_back(&gDeferredMaterialProgram[i]);
+ bool has_skin = i & 0x10;
- gDeferredMaterialProgram[i].mName = llformat("Deferred Material Shader %d", i);
+ if (!has_skin)
+ {
+ mShaderList.push_back(&gDeferredMaterialProgram[i]);
+ gDeferredMaterialProgram[i].mName = llformat("Material Shader %d", i);
+ }
+ else
+ {
+ gDeferredMaterialProgram[i].mName = llformat("Skinned Material Shader %d", i);
+ }
U32 alpha_mode = i & 0x3;
@@ -1083,9 +1158,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredMaterialProgram[i].addPermutation("HAS_SUN_SHADOW", "1");
}
- bool has_skin = i & 0x10;
+
gDeferredMaterialProgram[i].mFeatures.hasSrgb = true;
- gDeferredMaterialProgram[i].mFeatures.encodesNormal = true;
gDeferredMaterialProgram[i].mFeatures.calculatesAtmospherics = true;
gDeferredMaterialProgram[i].mFeatures.hasAtmospherics = true;
gDeferredMaterialProgram[i].mFeatures.hasGamma = true;
@@ -1119,7 +1193,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredPBROpaqueProgram.mName = "Deferred PBR Opaque Shader";
- gDeferredPBROpaqueProgram.mFeatures.encodesNormal = true;
gDeferredPBROpaqueProgram.mFeatures.hasSrgb = true;
gDeferredPBROpaqueProgram.mShaderFiles.clear();
@@ -1180,7 +1253,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
shader->mFeatures.hasLighting = false;
shader->mFeatures.isAlphaLighting = true;
shader->mFeatures.hasSrgb = true;
- shader->mFeatures.encodesNormal = true;
shader->mFeatures.calculatesAtmospherics = true;
shader->mFeatures.hasAtmospherics = true;
shader->mFeatures.hasGamma = true;
@@ -1242,12 +1314,38 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
success = shader->createShader(NULL, NULL);
llassert(success);
}
+
+ if (success)
+ {
+ S32 detail = gSavedSettings.getS32("RenderTerrainPBRDetail");
+ detail = llclamp(detail, TERRAIN_PBR_DETAIL_MIN, TERRAIN_PBR_DETAIL_MAX);
+ const S32 mapping = clamp_terrain_mapping(gSavedSettings.getS32("RenderTerrainPBRPlanarSampleCount"));
+ gDeferredPBRTerrainProgram.mName = llformat("Deferred PBR Terrain Shader %d %s",
+ detail,
+ (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;
+ gDeferredPBRTerrainProgram.mFeatures.hasTransport = true;
+ gDeferredPBRTerrainProgram.mFeatures.isPBRTerrain = true;
+
+ gDeferredPBRTerrainProgram.mShaderFiles.clear();
+ gDeferredPBRTerrainProgram.mShaderFiles.push_back(make_pair("deferred/pbrterrainV.glsl", GL_VERTEX_SHADER));
+ gDeferredPBRTerrainProgram.mShaderFiles.push_back(make_pair("deferred/pbrterrainF.glsl", GL_FRAGMENT_SHADER));
+ 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);
+ llassert(success);
+ }
if (success)
{
gDeferredTreeProgram.mName = "Deferred Tree Shader";
gDeferredTreeProgram.mShaderFiles.clear();
- gDeferredTreeProgram.mFeatures.encodesNormal = true;
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];
@@ -1282,8 +1380,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
{
gDeferredImpostorProgram.mName = "Deferred Impostor Shader";
gDeferredImpostorProgram.mFeatures.hasSrgb = true;
- gDeferredImpostorProgram.mFeatures.encodesNormal = true;
- //gDeferredImpostorProgram.mFeatures.isDeferred = true;
gDeferredImpostorProgram.mShaderFiles.clear();
gDeferredImpostorProgram.mShaderFiles.push_back(make_pair("deferred/impostorV.glsl", GL_VERTEX_SHADER));
gDeferredImpostorProgram.mShaderFiles.push_back(make_pair("deferred/impostorF.glsl", GL_FRAGMENT_SHADER));
@@ -1445,7 +1541,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
shader->mFeatures.isAlphaLighting = true;
shader->mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels
shader->mFeatures.hasSrgb = true;
- shader->mFeatures.encodesNormal = true;
shader->mFeatures.calculatesAtmospherics = true;
shader->mFeatures.hasAtmospherics = true;
shader->mFeatures.hasGamma = true;
@@ -1507,7 +1602,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
shader->mFeatures.hasSrgb = true;
shader->mFeatures.isAlphaLighting = true;
- shader->mFeatures.encodesNormal = true;
shader->mFeatures.hasShadows = use_sun_shadow;
shader->mFeatures.hasReflectionProbes = true;
shader->mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
@@ -1555,7 +1649,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredAvatarEyesProgram.mFeatures.hasAtmospherics = true;
gDeferredAvatarEyesProgram.mFeatures.disableTextureIndex = true;
gDeferredAvatarEyesProgram.mFeatures.hasSrgb = true;
- gDeferredAvatarEyesProgram.mFeatures.encodesNormal = true;
gDeferredAvatarEyesProgram.mFeatures.hasShadows = true;
gDeferredAvatarEyesProgram.mShaderFiles.clear();
@@ -1953,10 +2046,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredTerrainProgram.mName = "Deferred Terrain Shader";
- gDeferredTerrainProgram.mFeatures.encodesNormal = true;
gDeferredTerrainProgram.mFeatures.hasSrgb = true;
- gDeferredTerrainProgram.mFeatures.calculatesLighting = false;
- gDeferredTerrainProgram.mFeatures.hasLighting = false;
gDeferredTerrainProgram.mFeatures.isAlphaLighting = true;
gDeferredTerrainProgram.mFeatures.disableTextureIndex = true; //hack to disable auto-setup of texture channels
gDeferredTerrainProgram.mFeatures.calculatesAtmospherics = true;
@@ -1975,7 +2065,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
{
gDeferredAvatarProgram.mName = "Deferred Avatar Shader";
gDeferredAvatarProgram.mFeatures.hasSkinning = true;
- gDeferredAvatarProgram.mFeatures.encodesNormal = true;
gDeferredAvatarProgram.mShaderFiles.clear();
gDeferredAvatarProgram.mShaderFiles.push_back(make_pair("deferred/avatarV.glsl", GL_VERTEX_SHADER));
gDeferredAvatarProgram.mShaderFiles.push_back(make_pair("deferred/avatarF.glsl", GL_FRAGMENT_SHADER));
@@ -1993,7 +2082,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gDeferredAvatarAlphaProgram.mFeatures.isAlphaLighting = true;
gDeferredAvatarAlphaProgram.mFeatures.disableTextureIndex = true;
gDeferredAvatarAlphaProgram.mFeatures.hasSrgb = true;
- gDeferredAvatarAlphaProgram.mFeatures.encodesNormal = true;
gDeferredAvatarAlphaProgram.mFeatures.calculatesAtmospherics = true;
gDeferredAvatarAlphaProgram.mFeatures.hasAtmospherics = true;
gDeferredAvatarAlphaProgram.mFeatures.hasGamma = true;
@@ -2029,6 +2117,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
gExposureProgram.mFeatures.isDeferred = true;
gExposureProgram.mShaderFiles.clear();
gExposureProgram.clearPermutations();
+ gExposureProgram.addPermutation("USE_LAST_EXPOSURE", "1");
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];
@@ -2038,6 +2127,20 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
+ gExposureProgramNoFade.mName = "Exposure (no fade)";
+ gExposureProgramNoFade.mFeatures.hasSrgb = true;
+ gExposureProgramNoFade.mFeatures.isDeferred = true;
+ gExposureProgramNoFade.mShaderFiles.clear();
+ gExposureProgramNoFade.clearPermutations();
+ 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);
+ llassert(success);
+ }
+
+ if (success)
+ {
gLuminanceProgram.mName = "Luminance";
gLuminanceProgram.mShaderFiles.clear();
gLuminanceProgram.clearPermutations();
@@ -2153,6 +2256,26 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
llassert(success);
}
+ if (success)
+ {
+ gEnvironmentMapProgram.mName = "Environment Map Program";
+ gEnvironmentMapProgram.mShaderFiles.clear();
+ gEnvironmentMapProgram.mFeatures.calculatesAtmospherics = true;
+ gEnvironmentMapProgram.mFeatures.hasAtmospherics = true;
+ gEnvironmentMapProgram.mFeatures.hasGamma = true;
+ gEnvironmentMapProgram.mFeatures.hasSrgb = true;
+
+ gEnvironmentMapProgram.clearPermutations();
+ gEnvironmentMapProgram.addPermutation("HAS_HDRI", "1");
+ gEnvironmentMapProgram.mShaderFiles.push_back(make_pair("deferred/skyV.glsl", GL_VERTEX_SHADER));
+ gEnvironmentMapProgram.mShaderFiles.push_back(make_pair("deferred/skyF.glsl", GL_FRAGMENT_SHADER));
+ gEnvironmentMapProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+ gEnvironmentMapProgram.mShaderGroup = LLGLSLShader::SG_SKY;
+
+ success = gEnvironmentMapProgram.createShader(NULL, NULL);
+ llassert(success);
+ }
+
if (success)
{
gDeferredWLSkyProgram.mName = "Deferred Windlight Sky Shader";
@@ -2292,7 +2415,6 @@ BOOL LLViewerShaderMgr::loadShadersObject()
if (success)
{
gObjectBumpProgram.mName = "Bump Shader";
- gObjectBumpProgram.mFeatures.encodesNormal = true;
gObjectBumpProgram.mShaderFiles.clear();
gObjectBumpProgram.mShaderFiles.push_back(make_pair("objects/bumpV.glsl", GL_VERTEX_SHADER));
gObjectBumpProgram.mShaderFiles.push_back(make_pair("objects/bumpF.glsl", GL_FRAGMENT_SHADER));
@@ -2643,6 +2765,33 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
if (success)
{
+ for (S32 variant = 0; variant < NORMAL_DEBUG_SHADER_COUNT; ++variant)
+ {
+ LLGLSLShader& shader = gNormalDebugProgram[variant];
+ LLGLSLShader& skinned_shader = gSkinnedNormalDebugProgram[variant];
+ shader.mName = "Normal Debug Shader";
+ shader.mShaderFiles.clear();
+ shader.mShaderFiles.push_back(make_pair("interface/normaldebugV.glsl", GL_VERTEX_SHADER));
+ // *NOTE: Geometry shaders have a reputation for being slow.
+ // Consider using compute shaders instead, which have a reputation
+ // for being fast. This geometry shader in particular seems to run
+ // fine on my machine, but I won't vouch for this in
+ // performance-critical areas. -Cosmic,2023-09-28
+ shader.mShaderFiles.push_back(make_pair("interface/normaldebugG.glsl", GL_GEOMETRY_SHADER));
+ shader.mShaderFiles.push_back(make_pair("interface/normaldebugF.glsl", GL_FRAGMENT_SHADER));
+ shader.mRiggedVariant = &skinned_shader;
+ shader.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+ if (variant == NORMAL_DEBUG_SHADER_WITH_TANGENTS)
+ {
+ shader.addPermutation("HAS_ATTRIBUTE_TANGENT", "1");
+ }
+ success = make_rigged_variant(shader, skinned_shader);
+ success = success && shader.createShader(NULL, NULL);
+ }
+ }
+
+ if (success)
+ {
gClipProgram.mName = "Clip Shader";
gClipProgram.mShaderFiles.clear();
gClipProgram.mShaderFiles.push_back(make_pair("interface/clipV.glsl", GL_VERTEX_SHADER));
@@ -2744,8 +2893,21 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
gRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenV.glsl", GL_VERTEX_SHADER));
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);
}
+
+ if (success && gGLManager.mHasCubeMapArray)
+ {
+ gHeroRadianceGenProgram.mName = "Hero Radiance Gen Shader";
+ gHeroRadianceGenProgram.mShaderFiles.clear();
+ gHeroRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenV.glsl", GL_VERTEX_SHADER));
+ gHeroRadianceGenProgram.mShaderFiles.push_back(make_pair("interface/radianceGenF.glsl", GL_FRAGMENT_SHADER));
+ gHeroRadianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+ gHeroRadianceGenProgram.addPermutation("HERO_PROBES", "1");
+ gHeroRadianceGenProgram.addPermutation("PROBE_FILTER_SAMPLES", "4");
+ success = gHeroRadianceGenProgram.createShader(NULL, NULL);
+ }
if (success && gGLManager.mHasCubeMapArray)
{
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 04da7e48ae..c51f583ebc 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -41,6 +41,10 @@ public:
LLViewerShaderMgr();
/* virtual */ ~LLViewerShaderMgr();
+ // Add shaders to mShaderList for later uniform propagation
+ // Will assert on redundant shader entries in debug builds
+ void finalizeShaderList();
+
// singleton pattern implementation
static LLViewerShaderMgr * instance();
static void releaseInstance();
@@ -153,9 +157,18 @@ extern LLGLSLShader gGlowCombineProgram;
extern LLGLSLShader gReflectionMipProgram;
extern LLGLSLShader gGaussianProgram;
extern LLGLSLShader gRadianceGenProgram;
+extern LLGLSLShader gHeroRadianceGenProgram;
extern LLGLSLShader gIrradianceGenProgram;
extern LLGLSLShader gGlowCombineFXAAProgram;
extern LLGLSLShader gDebugProgram;
+enum NormalDebugShaderVariant : S32
+{
+ NORMAL_DEBUG_SHADER_DEFAULT,
+ NORMAL_DEBUG_SHADER_WITH_TANGENTS,
+ NORMAL_DEBUG_SHADER_COUNT
+};
+extern LLGLSLShader gNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT];
+extern LLGLSLShader gSkinnedNormalDebugProgram[NORMAL_DEBUG_SHADER_COUNT];
extern LLGLSLShader gClipProgram;
extern LLGLSLShader gBenchmarkProgram;
extern LLGLSLShader gReflectionProbeDisplayProgram;
@@ -171,7 +184,6 @@ extern LLGLSLShader gOneTextureFilterProgram;
//object shaders
extern LLGLSLShader gObjectPreviewProgram;
extern LLGLSLShader gPhysicsPreviewProgram;
-extern LLGLSLShader gSkinnedObjectFullbrightAlphaMaskProgram;
extern LLGLSLShader gObjectBumpProgram;
extern LLGLSLShader gSkinnedObjectBumpProgram;
extern LLGLSLShader gObjectAlphaMaskNoColorProgram;
@@ -237,6 +249,7 @@ extern LLGLSLShader gDeferredPostGammaCorrectProgram;
extern LLGLSLShader gNoPostGammaCorrectProgram;
extern LLGLSLShader gLegacyPostGammaCorrectProgram;
extern LLGLSLShader gExposureProgram;
+extern LLGLSLShader gExposureProgramNoFade;
extern LLGLSLShader gLuminanceProgram;
extern LLGLSLShader gDeferredAvatarShadowProgram;
extern LLGLSLShader gDeferredAvatarAlphaShadowProgram;
@@ -253,6 +266,7 @@ extern LLGLSLShader gHUDFullbrightAlphaMaskAlphaProgram;
extern LLGLSLShader gDeferredEmissiveProgram;
extern LLGLSLShader gDeferredAvatarEyesProgram;
extern LLGLSLShader gDeferredAvatarAlphaProgram;
+extern LLGLSLShader gEnvironmentMapProgram;
extern LLGLSLShader gDeferredWLSkyProgram;
extern LLGLSLShader gDeferredWLCloudProgram;
extern LLGLSLShader gDeferredWLSunProgram;
@@ -272,4 +286,20 @@ extern LLGLSLShader gPBRGlowProgram;
extern LLGLSLShader gDeferredPBROpaqueProgram;
extern LLGLSLShader gDeferredPBRAlphaProgram;
extern LLGLSLShader gHUDPBRAlphaProgram;
+
+// 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
+// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures
+enum TerrainPBRDetail : S32
+{
+ TERRAIN_PBR_DETAIL_MAX = 0,
+ TERRAIN_PBR_DETAIL_EMISSIVE = 0,
+ TERRAIN_PBR_DETAIL_OCCLUSION = -1,
+ TERRAIN_PBR_DETAIL_NORMAL = -2,
+ TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS = -3,
+ TERRAIN_PBR_DETAIL_BASE_COLOR = -4,
+ TERRAIN_PBR_DETAIL_MIN = -4,
+};
+extern LLGLSLShader gDeferredPBRTerrainProgram;
#endif
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 04ef441a69..6b496cf658 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1699,7 +1699,7 @@ void LLViewerFetchedTexture::processTextureStats()
{
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
{
- mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
+ mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096
}
else
{
@@ -1712,7 +1712,7 @@ void LLViewerFetchedTexture::processTextureStats()
}
else
{
- U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
+ U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096
if(!mKnownDrawWidth || !mKnownDrawHeight || mFullWidth <= mKnownDrawWidth || mFullHeight <= mKnownDrawHeight)
{
if (mFullWidth > desired_size || mFullHeight > desired_size)
@@ -3079,7 +3079,15 @@ void LLViewerLODTexture::processTextureStats()
updateVirtualSize();
static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes", false);
-
+
+ { // restrict texture resolution to download based on RenderMaxTextureResolution
+ static LLCachedControl<U32> max_texture_resolution(gSavedSettings, "RenderMaxTextureResolution", 2048);
+ // sanity clamp debug setting to avoid settings hack shenanigans
+ F32 tex_res = (F32)llclamp((S32)max_texture_resolution, 512, 2048);
+ tex_res *= tex_res;
+ mMaxVirtualSize = llmin(mMaxVirtualSize, tex_res);
+ }
+
if (textures_fullres)
{
mDesiredDiscardLevel = 0;
@@ -3089,7 +3097,7 @@ void LLViewerLODTexture::processTextureStats()
{
mDesiredDiscardLevel = 0;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
- mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
+ mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096
}
else if (mBoostLevel < LLGLTexture::BOOST_HIGH && mMaxVirtualSize <= 10.f)
{
@@ -3134,7 +3142,7 @@ void LLViewerLODTexture::processTextureStats()
discard_level = floorf(discard_level);
F32 min_discard = 0.f;
- U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
+ U32 desired_size = MAX_IMAGE_SIZE_DEFAULT; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096
if (mBoostLevel <= LLGLTexture::BOOST_SCULPTED)
{
desired_size = DESIRED_NORMAL_TEXTURE_SIZE;
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 35fb0a2237..52b2f19ada 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -398,7 +398,6 @@ public:
BOOL isCachedRawImageReady() const {return mCachedRawImageReady ;}
BOOL isRawImageValid()const { return mIsRawImageValid ; }
void forceToSaveRawImage(S32 desired_discard = 0, F32 kept_time = 0.f) ;
- void forceToRefetchTexture(S32 desired_discard = 0, F32 kept_time = 60.f);
/*virtual*/ void setCachedRawImage(S32 discard_level, LLImageRaw* imageraw) override;
void destroySavedRawImage() ;
LLImageRaw* getSavedRawImage() ;
@@ -420,6 +419,7 @@ public:
protected:
/*virtual*/ void switchToCachedImage() override;
S32 getCurrentDiscardLevelForFetching() ;
+ void forceToRefetchTexture(S32 desired_discard = 0, F32 kept_time = 60.f);
private:
void init(bool firstinit) ;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 37e64dfc17..a0b0fabb66 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -246,6 +246,8 @@ LLVector2 gDebugRaycastTexCoord;
LLVector4a gDebugRaycastNormal;
LLVector4a gDebugRaycastTangent;
S32 gDebugRaycastFaceHit;
+S32 gDebugRaycastGLTFNodeHit;
+S32 gDebugRaycastGLTFPrimitiveHit;
LLVector4a gDebugRaycastStart;
LLVector4a gDebugRaycastEnd;
@@ -3348,9 +3350,11 @@ void LLViewerWindow::updateUI()
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
{
- gDebugRaycastFaceHit = -1;
+ gDebugRaycastFaceHit = gDebugRaycastGLTFNodeHit = gDebugRaycastGLTFPrimitiveHit = -1;
gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, FALSE,
&gDebugRaycastFaceHit,
+ &gDebugRaycastGLTFNodeHit,
+ &gDebugRaycastGLTFPrimitiveHit,
&gDebugRaycastIntersection,
&gDebugRaycastTexCoord,
&gDebugRaycastNormal,
@@ -4343,6 +4347,8 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
BOOL pick_unselectable,
BOOL pick_reflection_probe,
S32* face_hit,
+ S32* gltf_node_hit,
+ S32* gltf_primitive_hit,
LLVector4a *intersection,
LLVector2 *uv,
LLVector4a *normal,
@@ -4436,7 +4442,7 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
if (!found) // if not found in HUD, look in world:
{
found = gPipeline.lineSegmentIntersectInWorld(mw_start, mw_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe,
- face_hit, intersection, uv, normal, tangent);
+ face_hit, gltf_node_hit, gltf_primitive_hit, intersection, uv, normal, tangent);
if (found && !pick_transparent)
{
gDebugRaycastIntersection = *intersection;
@@ -5311,7 +5317,7 @@ BOOL LLViewerWindow::simpleSnapshot(LLImageRaw* raw, S32 image_width, S32 image_
void display_cube_face();
-BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubearray, S32 cubeIndex, S32 face, F32 near_clip, bool dynamic_render)
+BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubearray, S32 cubeIndex, S32 face, F32 near_clip, bool dynamic_render, bool useCustomClipPlane, LLPlane clipPlane)
{
// NOTE: implementation derived from LLFloater360Capture::capture360Images() and simpleSnapshot
LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
@@ -5342,6 +5348,14 @@ BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubea
camera->setOrigin(origin);
camera->setNear(near_clip);
+ LLPlane previousClipPlane;
+
+ if (useCustomClipPlane)
+ {
+ previousClipPlane = camera->getUserClipPlane();
+ camera->setUserClipPlane(clipPlane);
+ }
+
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // stencil buffer is deprecated | GL_STENCIL_BUFFER_BIT);
U32 dynamic_render_types[] = {
@@ -5448,6 +5462,11 @@ BOOL LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubea
gPipeline.resetDrawOrders();
mWorldViewRectRaw = window_rect;
+
+ if (useCustomClipPlane)
+ {
+ camera->setUserClipPlane(previousClipPlane);
+ }
// restore original view/camera/avatar settings settings
*camera = saved_camera;
@@ -6127,8 +6146,8 @@ LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos,
void LLPickInfo::fetchResults()
{
-
S32 face_hit = -1;
+
LLVector4a intersection, normal;
LLVector4a tangent;
@@ -6151,8 +6170,8 @@ void LLPickInfo::fetchResults()
}
LLViewerObject* hit_object = gViewerWindow->cursorIntersect(mMousePt.mX, mMousePt.mY, 512.f,
- NULL, -1, mPickTransparent, mPickRigged, mPickUnselectable, mPickReflectionProbe, &face_hit,
- &intersection, &uv, &normal, &tangent, &start, &end);
+ nullptr, -1, mPickTransparent, mPickRigged, mPickUnselectable, mPickReflectionProbe, &face_hit, &mGLTFNodeIndex, &mGLTFPrimitiveIndex,
+ &intersection, &uv, &normal, &tangent, &start, &end);
mPickPt = mMousePt;
@@ -6298,6 +6317,8 @@ void LLPickInfo::getSurfaceInfo()
if (gViewerWindow->cursorIntersect(ll_round((F32)mMousePt.mX), ll_round((F32)mMousePt.mY), 1024.f,
objectp, -1, mPickTransparent, mPickRigged, mPickUnselectable, mPickReflectionProbe,
&mObjectFace,
+ &mGLTFNodeIndex,
+ &mGLTFPrimitiveIndex,
&intersection,
&mSTCoords,
&normal,
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 346073b8a1..121b5b6fad 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -119,6 +119,8 @@ public:
LLUUID mParticleOwnerID;
LLUUID mParticleSourceID;
S32 mObjectFace;
+ S32 mGLTFNodeIndex = -1;
+ S32 mGLTFPrimitiveIndex = -1;
LLHUDIcon* mHUDIcon;
LLVector3 mIntersection;
LLVector2 mUVCoords;
@@ -374,7 +376,8 @@ public:
// index - cube index in the array to use (cube index, not face-layer)
// face - which cube face to update
// near_clip - near clip setting to use
- BOOL cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubearray, S32 index, S32 face, F32 near_clip, bool render_avatars);
+ BOOL cubeSnapshot(const LLVector3 &origin, LLCubeMapArray *cubearray, S32 index, S32 face, F32 near_clip, bool render_avatars,
+ bool customCullingPlane = false, LLPlane cullingPlane = LLPlane(LLVector3(0, 0, 0), LLVector3(0, 0, 1)));
// special implementation of simpleSnapshot for reflection maps
@@ -422,6 +425,8 @@ public:
BOOL pick_unselectable = TRUE,
BOOL pick_reflection_probe = TRUE,
S32* face_hit = NULL,
+ S32* gltf_node_hit = nullptr,
+ S32* gltf_primitive_hit = nullptr,
LLVector4a *intersection = NULL,
LLVector2 *uv = NULL,
LLVector4a *normal = NULL,
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index 001fab7755..8d3b5cc8a5 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -28,47 +28,360 @@
#include "llvlcomposition.h"
+#include <functional>
+
#include "llerror.h"
#include "v3math.h"
#include "llsurface.h"
#include "lltextureview.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
+#include "llfetchedgltfmaterial.h"
+#include "llgltfmateriallist.h"
#include "llviewerregion.h"
#include "noise.h"
#include "llregionhandle.h" // for from_region_handle
#include "llviewercontrol.h"
+extern LLColor4U MAX_WATER_COLOR;
+
+static const U32 BASE_SIZE = 128;
+static const F32 TERRAIN_DECODE_PRIORITY = 2048.f * 2048.f;
-F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
+namespace
+{
+ F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
+ {
+ // Not sure if this is the right math...
+ // Take weighted average of all four points (bilinear interpolation)
+ F32 result;
+
+ const F32 inv_x_frac = 1.f - x_frac;
+ const F32 inv_y_frac = 1.f - y_frac;
+ result = inv_x_frac*inv_y_frac*v00
+ + x_frac*inv_y_frac*v10
+ + inv_x_frac*y_frac*v01
+ + x_frac*y_frac*v11;
+
+ return result;
+ }
+
+ void boost_minimap_texture(LLViewerFetchedTexture* tex, F32 virtual_size)
+ {
+ llassert(tex);
+ if (!tex) { return; }
+
+ tex->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case the raw image is at low detail
+ tex->addTextureStats(virtual_size); // priority
+ }
+
+ void boost_minimap_material(LLFetchedGLTFMaterial* mat, F32 virtual_size)
+ {
+ if (!mat) { return; }
+ if (mat->mBaseColorTexture) { boost_minimap_texture(mat->mBaseColorTexture, virtual_size); }
+ if (mat->mNormalTexture) { boost_minimap_texture(mat->mNormalTexture, virtual_size); }
+ if (mat->mMetallicRoughnessTexture) { boost_minimap_texture(mat->mMetallicRoughnessTexture, virtual_size); }
+ if (mat->mEmissiveTexture) { boost_minimap_texture(mat->mEmissiveTexture, virtual_size); }
+ }
+
+ void unboost_minimap_texture(LLViewerFetchedTexture* tex)
+ {
+ if (!tex) { return; }
+ tex->setBoostLevel(LLGLTexture::BOOST_NONE);
+ tex->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1);
+ }
+
+ void unboost_minimap_material(LLFetchedGLTFMaterial* mat)
+ {
+ if (!mat) { return; }
+ if (mat->mBaseColorTexture) { unboost_minimap_texture(mat->mBaseColorTexture); }
+ if (mat->mNormalTexture) { unboost_minimap_texture(mat->mNormalTexture); }
+ if (mat->mMetallicRoughnessTexture) { unboost_minimap_texture(mat->mMetallicRoughnessTexture); }
+ if (mat->mEmissiveTexture) { unboost_minimap_texture(mat->mEmissiveTexture); }
+ }
+};
+
+LLTerrainMaterials::LLTerrainMaterials()
{
- // Not sure if this is the right math...
- // Take weighted average of all four points (bilinear interpolation)
- F32 result;
-
- const F32 inv_x_frac = 1.f - x_frac;
- const F32 inv_y_frac = 1.f - y_frac;
- result = inv_x_frac*inv_y_frac*v00
- + x_frac*inv_y_frac*v10
- + inv_x_frac*y_frac*v01
- + x_frac*y_frac*v11;
-
- return result;
+ for (S32 i = 0; i < ASSET_COUNT; ++i)
+ {
+ mMaterialTexturesSet[i] = false;
+ }
}
+LLTerrainMaterials::~LLTerrainMaterials()
+{
+ unboost();
+}
-LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
- LLViewerLayer(width, scale),
- mParamsReady(FALSE)
+BOOL LLTerrainMaterials::generateMaterials()
{
- mSurfacep = surfacep;
+ if (texturesReady(true, true))
+ {
+ return TRUE;
+ }
+ if (materialsReady(true, true))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLTerrainMaterials::boost()
+{
+ for (S32 i = 0; i < ASSET_COUNT; ++i)
+ {
+ LLPointer<LLViewerFetchedTexture>& tex = mDetailTextures[i];
+ llassert(tex.notNull());
+ boost_minimap_texture(tex, TERRAIN_DECODE_PRIORITY);
+
+ LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[i];
+ boost_minimap_material(mat, TERRAIN_DECODE_PRIORITY);
+ }
+}
+
+void LLTerrainMaterials::unboost()
+{
+ for (S32 i = 0; i < ASSET_COUNT; ++i)
+ {
+ LLPointer<LLViewerFetchedTexture>& tex = mDetailTextures[i];
+ unboost_minimap_texture(tex);
+
+ LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[i];
+ unboost_minimap_material(mat);
+ }
+}
+
+LLUUID LLTerrainMaterials::getDetailAssetID(S32 asset)
+{
+ llassert(mDetailTextures[asset] && mDetailMaterials[asset]);
+ // Assume both the the material and texture were fetched in the same way
+ // using the same UUID. However, we may not know at this point which one
+ // will load.
+ return mDetailTextures[asset] ? mDetailTextures[asset]->getID() : LLUUID::null;
+}
+
+LLPointer<LLViewerFetchedTexture> fetch_terrain_texture(const LLUUID& id)
+{
+ if (id.isNull())
+ {
+ return nullptr;
+ }
+
+ LLPointer<LLViewerFetchedTexture> tex = LLViewerTextureManager::getFetchedTexture(id);
+ return tex;
+}
+
+void LLTerrainMaterials::setDetailAssetID(S32 asset, const LLUUID& id)
+{
+ // *NOTE: If there were multiple terrain swatches using the same asset
+ // ID, the asset still in use will be temporarily unboosted.
+ // It will be boosted again during terrain rendering.
+ unboost_minimap_texture(mDetailTextures[asset]);
+ unboost_minimap_material(mDetailMaterials[asset]);
+
+ // This is terrain texture, but we are not setting it as BOOST_TERRAIN
+ // since we will be manipulating it later as needed.
+ mDetailTextures[asset] = fetch_terrain_texture(id);
+ LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[asset];
+ mat = id.isNull() ? nullptr : gGLTFMaterialList.getMaterial(id);
+ mMaterialTexturesSet[asset] = false;
+}
+
+LLTerrainMaterials::Type LLTerrainMaterials::getMaterialType()
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ const BOOL use_textures = texturesReady(false, false) || !materialsReady(false, false);
+ return use_textures ? Type::TEXTURE : Type::PBR;
+}
+
+bool LLTerrainMaterials::texturesReady(bool boost, bool strict)
+{
+ bool ready[ASSET_COUNT];
+ // *NOTE: Calls to textureReady may boost textures. Do not early-return.
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ ready[i] = mDetailTextures[i].notNull() && textureReady(mDetailTextures[i], boost);
+ }
+
+ bool one_ready = false;
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ const bool current_ready = ready[i];
+ one_ready = one_ready || current_ready;
+ if (!current_ready && strict)
+ {
+ return false;
+ }
+ }
+ return one_ready;
+}
+
+bool LLTerrainMaterials::materialsReady(bool boost, bool strict)
+{
+ bool ready[ASSET_COUNT];
+ // *NOTE: Calls to materialReady may boost materials/textures. Do not early-return.
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ ready[i] = materialReady(mDetailMaterials[i], mMaterialTexturesSet[i], boost, strict);
+ }
+
+#if 1
+ static LLCachedControl<bool> sRenderTerrainPBREnabled(gSavedSettings, "RenderTerrainPBREnabled", false);
+ static LLCachedControl<bool> sRenderTerrainPBRForce(gSavedSettings, "RenderTerrainPBRForce", false);
+ if (sRenderTerrainPBREnabled && sRenderTerrainPBRForce)
+ {
+ bool defined = true;
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ if (!mDetailMaterials[i])
+ {
+ defined = false;
+ break;
+ }
+ }
+ if (defined)
+ {
+ return true;
+ }
+ }
+#endif
+
+ bool one_ready = false;
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ const bool current_ready = ready[i];
+ one_ready = one_ready || current_ready;
+ if (!current_ready && strict)
+ {
+ return false;
+ }
+ }
+ return one_ready;
+}
+
+// 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)
+{
+ llassert(tex);
+ if (!tex) { return false; }
+
+ if (tex->getDiscardLevel() < 0)
+ {
+ if (boost)
+ {
+ boost_minimap_texture(tex, BASE_SIZE*BASE_SIZE);
+ }
+ return false;
+ }
+ if ((tex->getDiscardLevel() != 0 &&
+ (tex->getWidth() < BASE_SIZE ||
+ tex->getHeight() < BASE_SIZE)))
+ {
+ if (boost)
+ {
+ boost_minimap_texture(tex, BASE_SIZE*BASE_SIZE);
+
+ S32 width = tex->getFullWidth();
+ S32 height = tex->getFullHeight();
+ S32 min_dim = llmin(width, height);
+ S32 ddiscard = 0;
+ while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
+ {
+ ddiscard++;
+ min_dim /= 2;
+ }
+ tex->setMinDiscardLevel(ddiscard);
+ }
+ return false;
+ }
+ if (tex->getComponents() == 0)
+ {
+ return false;
+ }
+ return true;
+}
+
+// Boost the loading priority of every known texture in the material
+// Return true when ready to use
+// static
+bool LLTerrainMaterials::materialReady(LLPointer<LLFetchedGLTFMaterial> &mat, bool &textures_set, bool boost, bool strict)
+{
+ if (!mat || !mat->isLoaded())
+ {
+ return false;
+ }
+
+ // Material is loaded, but textures may not be
+ if (!textures_set)
+ {
+ textures_set = true;
+ // *NOTE: These can sometimes be set to to nullptr due to
+ // updateTEMaterialTextures. For the sake of robustness, we emulate
+ // that fetching behavior by setting textures of null IDs to nullptr.
+ mat->mBaseColorTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]);
+ mat->mNormalTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]);
+ mat->mMetallicRoughnessTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]);
+ mat->mEmissiveTexture = fetch_terrain_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]);
+ }
+
+ // *NOTE: Calls to textureReady 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);
+ ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL] =
+ mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].isNull() || textureReady(mat->mNormalTexture, boost);
+ ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS] =
+ mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].isNull() ||
+ textureReady(mat->mMetallicRoughnessTexture, boost);
+ ready[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE] =
+ mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].isNull() || textureReady(mat->mEmissiveTexture, boost);
+
+ if (strict)
+ {
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ if (!ready[i])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// static
+const LLUUID (&LLVLComposition::getDefaultTextures())[ASSET_COUNT]
+{
+ const static LLUUID default_textures[LLVLComposition::ASSET_COUNT] =
+ {
+ TERRAIN_DIRT_DETAIL,
+ TERRAIN_GRASS_DETAIL,
+ TERRAIN_MOUNTAIN_DETAIL,
+ TERRAIN_ROCK_DETAIL
+ };
+ return default_textures;
+}
+
+LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
+ LLTerrainMaterials(),
+ LLViewerLayer(width, scale)
+{
// Load Terrain Textures - Original ones
- setDetailTextureID(0, TERRAIN_DIRT_DETAIL);
- setDetailTextureID(1, TERRAIN_GRASS_DETAIL);
- setDetailTextureID(2, TERRAIN_MOUNTAIN_DETAIL);
- setDetailTextureID(3, TERRAIN_ROCK_DETAIL);
+ const LLUUID (&default_textures)[LLVLComposition::ASSET_COUNT] = LLVLComposition::getDefaultTextures();
+ for (S32 i = 0; i < ASSET_COUNT; ++i)
+ {
+ setDetailAssetID(i, default_textures[i]);
+ }
+
+ mSurfacep = surfacep;
// Initialize the texture matrix to defaults.
for (S32 i = 0; i < CORNER_COUNT; ++i)
@@ -76,14 +389,12 @@ LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32
mStartHeight[i] = gSavedSettings.getF32("TerrainColorStartHeight");
mHeightRange[i] = gSavedSettings.getF32("TerrainColorHeightRange");
}
- mTexScaleX = 16.f;
- mTexScaleY = 16.f;
- mTexturesLoaded = FALSE;
}
LLVLComposition::~LLVLComposition()
{
+ LLTerrainMaterials::~LLTerrainMaterials();
}
@@ -92,20 +403,6 @@ void LLVLComposition::setSurface(LLSurface *surfacep)
mSurfacep = surfacep;
}
-
-void LLVLComposition::setDetailTextureID(S32 corner, const LLUUID& id)
-{
- if(id.isNull())
- {
- return;
- }
- // This is terrain texture, but we are not setting it as BOOST_TERRAIN
- // since we will be manipulating it later as needed.
- mDetailTextures[corner] = LLViewerTextureManager::getFetchedTexture(id);
- mDetailTextures[corner]->setNoDelete() ;
- mRawImages[corner] = NULL;
-}
-
BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
const F32 width, const F32 height)
{
@@ -149,10 +446,6 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
const F32 noise_magnitude = 2.f; // Degree to which noise modulates composition layer (versus
// simple height)
- // Heights map into textures as 0-1 = first, 1-2 = second, etc.
- // So we need to compress heights into this range.
- const S32 NUM_TEXTURES = 4;
-
const F32 xyScaleInv = (1.f / xyScale);
const F32 zScaleInv = (1.f / zScale);
@@ -199,7 +492,7 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
twiddle += turbulence2(vec, 2)*slope_squared; // High frequency component
twiddle *= noise_magnitude;
- F32 scaled_noisy_height = (height + twiddle - start_height) * F32(NUM_TEXTURES) / height_range;
+ F32 scaled_noisy_height = (height + twiddle - start_height) * F32(ASSET_COUNT) / height_range;
scaled_noisy_height = llmax(0.f, scaled_noisy_height);
scaled_noisy_height = llmin(3.f, scaled_noisy_height);
@@ -209,49 +502,122 @@ BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
return TRUE;
}
-static const U32 BASE_SIZE = 128;
+LLTerrainMaterials gLocalTerrainMaterials;
BOOL LLVLComposition::generateComposition()
{
-
if (!mParamsReady)
{
// All the parameters haven't been set yet (we haven't gotten the message from the sim)
return FALSE;
}
- for (S32 i = 0; i < 4; i++)
- {
- if (mDetailTextures[i]->getDiscardLevel() < 0)
- {
- mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail
- mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE);
- return FALSE;
- }
- if ((mDetailTextures[i]->getDiscardLevel() != 0 &&
- (mDetailTextures[i]->getWidth() < BASE_SIZE ||
- mDetailTextures[i]->getHeight() < BASE_SIZE)))
- {
- S32 width = mDetailTextures[i]->getFullWidth();
- S32 height = mDetailTextures[i]->getFullHeight();
- S32 min_dim = llmin(width, height);
- S32 ddiscard = 0;
- while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
- {
- ddiscard++;
- min_dim /= 2;
- }
- mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail
- mDetailTextures[i]->setMinDiscardLevel(ddiscard);
- mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE); // priority
- return FALSE;
- }
- }
-
- return TRUE;
+ return LLTerrainMaterials::generateMaterials();
}
-BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
+namespace
+{
+ void prepare_fallback_image(LLImageRaw* raw_image)
+ {
+ raw_image->resize(BASE_SIZE, BASE_SIZE, 4);
+ raw_image->fill(LLColor4U::white);
+ }
+
+ // Check if the raw image is loaded for this texture at a discard
+ // level the minimap can use, and if not then try to get it loaded.
+ bool prepare_raw_image(LLPointer<LLImageRaw>& raw_image, bool emissive, LLViewerFetchedTexture* tex, bool& delete_raw_post)
+ {
+ if (!tex)
+ {
+ if (!emissive)
+ {
+ prepare_fallback_image(raw_image);
+ }
+ else
+ {
+ llassert(!raw_image);
+ raw_image = nullptr;
+ }
+ return true;
+ }
+ if (raw_image)
+ {
+ // Callback already initiated
+ if (raw_image->getDataSize() > 0)
+ {
+ // Callback finished
+ delete_raw_post = true;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ raw_image = new LLImageRaw();
+
+ S32 ddiscard = 0;
+ {
+ S32 min_dim = llmin(tex->getFullWidth(), tex->getFullHeight());
+ while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
+ {
+ ddiscard++;
+ min_dim /= 2;
+ }
+ }
+
+ struct PendingImage
+ {
+ LLImageRaw* mRawImage;
+ S32 mDesiredDiscard;
+ LLUUID mTextureId;
+ PendingImage(LLImageRaw* raw_image, S32 ddiscard, const LLUUID& texture_id)
+ : mRawImage(raw_image)
+ , mDesiredDiscard(ddiscard)
+ , mTextureId(texture_id)
+ {
+ mRawImage->ref();
+ }
+ ~PendingImage()
+ {
+ mRawImage->unref();
+ }
+ };
+ PendingImage* pending_image = new PendingImage(raw_image, ddiscard, tex->getID());
+
+ loaded_callback_func cb = [](BOOL success, LLViewerFetchedTexture * src_vi, LLImageRaw * src, LLImageRaw * src_aux, S32 discard_level, BOOL is_final, void* userdata) {
+ PendingImage* pending = (PendingImage*)userdata;
+ // Owning LLVLComposition still exists
+
+ // Assume mRawImage only used by single LLVLComposition for now
+ const bool in_use_by_composition = pending->mRawImage->getNumRefs() > 1;
+ llassert(pending->mRawImage->getNumRefs());
+ llassert(pending->mRawImage->getNumRefs() <= 2);
+ const bool needs_data = !pending->mRawImage->getDataSize();
+ if (in_use_by_composition && needs_data)
+ {
+ if (success && pending->mDesiredDiscard == discard_level)
+ {
+ pending->mRawImage->resize(BASE_SIZE, BASE_SIZE, src->getComponents());
+ pending->mRawImage->copyScaled(src);
+ }
+ else if (is_final)
+ {
+ prepare_fallback_image(pending->mRawImage);
+ }
+ }
+
+ if (is_final) { delete pending; }
+ };
+ tex->setLoadedCallback(cb, ddiscard, true, false, pending_image, nullptr);
+ tex->forceToSaveRawImage(ddiscard);
+
+ return false;
+ }
+};
+
+BOOL LLVLComposition::generateMinimapTileLand(const F32 x, const F32 y,
const F32 width, const F32 height)
{
LL_PROFILE_ZONE_SCOPED
@@ -259,8 +625,6 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
llassert(x >= 0.f);
llassert(y >= 0.f);
- LLTimer gen_timer;
-
///////////////////////////
//
// Generate raw data arrays for surface textures
@@ -268,52 +632,138 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
//
// These have already been validated by generateComposition.
- U8* st_data[4];
- S32 st_data_size[4]; // for debugging
-
- for (S32 i = 0; i < 4; i++)
+ U8* st_data[ASSET_COUNT];
+ S32 st_data_size[ASSET_COUNT]; // for debugging
+
+ const bool use_textures = getMaterialType() != LLTerrainMaterials::Type::PBR;
+ if (use_textures)
+ {
+ if (!texturesReady(true, true)) { return FALSE; }
+ }
+ else
+ {
+ if (!materialsReady(true, true)) { return FALSE; }
+ }
+
+ for (S32 i = 0; i < ASSET_COUNT; i++)
{
if (mRawImages[i].isNull())
{
// Read back a raw image for this discard level, if it exists
- S32 min_dim = llmin(mDetailTextures[i]->getFullWidth(), mDetailTextures[i]->getFullHeight());
- S32 ddiscard = 0;
- while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
- {
- ddiscard++;
- min_dim /= 2;
- }
-
- BOOL delete_raw = (mDetailTextures[i]->reloadRawImage(ddiscard) != NULL) ;
- if(mDetailTextures[i]->getRawImageLevel() != ddiscard)//raw iamge is not ready, will enter here again later.
- {
- if (mDetailTextures[i]->getFetchPriority() <= 0.0f && !mDetailTextures[i]->hasSavedRawImage())
+ LLViewerFetchedTexture* tex;
+ LLViewerFetchedTexture* tex_emissive; // Can be null
+ bool has_base_color_factor;
+ bool has_emissive_factor;
+ bool has_alpha;
+ LLColor3 base_color_factor;
+ LLColor3 emissive_factor;
+ if (use_textures)
+ {
+ tex = mDetailTextures[i];
+ tex_emissive = nullptr;
+ has_base_color_factor = false;
+ has_emissive_factor = false;
+ has_alpha = false;
+ llassert(tex);
+ }
+ else
+ {
+ tex = mDetailMaterials[i]->mBaseColorTexture;
+ tex_emissive = mDetailMaterials[i]->mEmissiveTexture;
+ base_color_factor = LLColor3(mDetailMaterials[i]->mBaseColor);
+ // *HACK: Treat alpha as black
+ base_color_factor *= (mDetailMaterials[i]->mBaseColor.mV[VW]);
+ emissive_factor = mDetailMaterials[i]->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;
+ }
+
+ if (!tex) { tex = LLViewerFetchedTexture::sWhiteImagep; }
+
+ bool delete_raw_post = false;
+ bool delete_raw_post_emissive = false;
+ if (!prepare_raw_image(mRawImagesBaseColor[i], false, tex, delete_raw_post)) { return FALSE; }
+ if (tex_emissive && !prepare_raw_image(mRawImagesEmissive[i], true, tex_emissive, delete_raw_post_emissive)) { return FALSE; }
+ // tex_emissive can be null, and then will be ignored
+
+ // In the simplest case, the minimap image is just the base color.
+ // This will be replaced if we need to do any tinting/compositing.
+ mRawImages[i] = mRawImagesBaseColor[i];
+
+ // *TODO: This isn't quite right for PBR:
+ // 1) It does not convert the color images from SRGB to linear
+ // before mixing (which will always require copying the image).
+ // 2) It mixes emissive and base color before mixing terrain
+ // materials, but it should be the other way around
+ // Long-term, we should consider a method that is more
+ // maintainable. Shaders, perhaps? Bake shaders to textures?
+ LLPointer<LLImageRaw> raw_emissive;
+ if (tex_emissive)
+ {
+ raw_emissive = mRawImagesEmissive[i];
+ if (has_emissive_factor ||
+ tex_emissive->getWidth(tex_emissive->getRawImageLevel()) != BASE_SIZE ||
+ tex_emissive->getHeight(tex_emissive->getRawImageLevel()) != BASE_SIZE ||
+ tex_emissive->getComponents() != 4)
{
- mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_MAP);
- mDetailTextures[i]->forceToRefetchTexture(ddiscard);
+ LLPointer<LLImageRaw> newraw_emissive = new LLImageRaw(BASE_SIZE, BASE_SIZE, 4);
+ // Copy RGB, leave alpha alone (set to opaque by default)
+ newraw_emissive->copy(mRawImagesEmissive[i]);
+ if (has_emissive_factor)
+ {
+ newraw_emissive->tint(emissive_factor);
+ }
+ raw_emissive = newraw_emissive;
}
-
- if(delete_raw)
- {
- mDetailTextures[i]->destroyRawImage() ;
- }
- LL_DEBUGS("Terrain") << "cached raw data for terrain detail texture is not ready yet: " << mDetailTextures[i]->getID() << " Discard: " << ddiscard << LL_ENDL;
- return FALSE;
- }
-
- mRawImages[i] = mDetailTextures[i]->getRawImage() ;
- if(delete_raw)
- {
- mDetailTextures[i]->destroyRawImage() ;
- }
- if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE ||
- mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE ||
- mDetailTextures[i]->getComponents() != 3)
+ }
+ if (has_base_color_factor ||
+ raw_emissive ||
+ has_alpha ||
+ tex->getWidth(tex->getRawImageLevel()) != BASE_SIZE ||
+ tex->getHeight(tex->getRawImageLevel()) != BASE_SIZE ||
+ tex->getComponents() != 3)
{
LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3);
- newraw->composite(mRawImages[i]);
+ if (has_alpha)
+ {
+ // Approximate the water underneath terrain alpha with solid water color
+ newraw->clear(
+ MAX_WATER_COLOR.mV[VX],
+ MAX_WATER_COLOR.mV[VY],
+ MAX_WATER_COLOR.mV[VZ],
+ 255);
+ }
+ newraw->composite(mRawImagesBaseColor[i]);
+ if (has_base_color_factor)
+ {
+ newraw->tint(base_color_factor);
+ }
+ // Apply emissive texture
+ if (raw_emissive)
+ {
+ newraw->addEmissive(raw_emissive);
+ }
+
mRawImages[i] = newraw; // deletes old
}
+
+ if (delete_raw_post)
+ {
+ tex->destroyRawImage();
+ }
+ if (delete_raw_post_emissive)
+ {
+ tex_emissive->destroyRawImage();
+ }
+
+ // Remove intermediary image references
+ mRawImagesBaseColor[i] = nullptr;
+ mRawImagesEmissive[i] = nullptr;
}
st_data[i] = mRawImages[i]->getData();
st_data_size[i] = mRawImages[i]->getDataSize();
@@ -333,12 +783,12 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
if (x_end > mWidth)
{
- LL_WARNS("Terrain") << "x end > width" << LL_ENDL;
+ llassert(false);
x_end = mWidth;
}
if (y_end > mWidth)
{
- LL_WARNS("Terrain") << "y end > width" << LL_ENDL;
+ llassert(false);
y_end = mWidth;
}
@@ -368,7 +818,7 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
if (tex_comps != st_comps)
{
- LL_WARNS("Terrain") << "Base texture comps != input texture comps" << LL_ENDL;
+ llassert(false);
return FALSE;
}
@@ -459,29 +909,36 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
}
texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin);
- for (S32 i = 0; i < 4; i++)
- {
- // Un-boost detatil textures (will get re-boosted if rendering in high detail)
- mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_NONE);
- mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1);
- }
+ // Un-boost detail textures (will get re-boosted if rendering in high detail)
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ unboost_minimap_texture(mDetailTextures[i]);
+ }
+
+ // Un-boost textures for each detail material (will get re-boosted if rendering in high detail)
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ unboost_minimap_material(mDetailMaterials[i]);
+ }
return TRUE;
}
-LLUUID LLVLComposition::getDetailTextureID(S32 corner)
-{
- return mDetailTextures[corner]->getID();
-}
-
-LLViewerFetchedTexture* LLVLComposition::getDetailTexture(S32 corner)
+F32 LLVLComposition::getStartHeight(S32 corner)
{
- return mDetailTextures[corner];
+ return mStartHeight[corner];
}
-F32 LLVLComposition::getStartHeight(S32 corner)
+void LLVLComposition::setDetailAssetID(S32 asset, const LLUUID& id)
{
- return mStartHeight[corner];
+ if (id.isNull())
+ {
+ return;
+ }
+ LLTerrainMaterials::setDetailAssetID(asset, id);
+ mRawImages[asset] = NULL;
+ mRawImagesBaseColor[asset] = NULL;
+ mRawImagesEmissive[asset] = NULL;
}
void LLVLComposition::setStartHeight(S32 corner, const F32 start_height)
diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h
index 2dd04ac5a5..5db832e034 100644
--- a/indra/newview/llvlcomposition.h
+++ b/indra/newview/llvlcomposition.h
@@ -28,13 +28,68 @@
#define LL_LLVLCOMPOSITION_H
#include "llviewerlayer.h"
-#include "llviewertexture.h"
+#include "llpointer.h"
+
+#include "llimage.h"
class LLSurface;
-class LLVLComposition : public LLViewerLayer
+class LLViewerFetchedTexture;
+class LLFetchedGLTFMaterial;
+
+class LLTerrainMaterials
{
public:
+ friend class LLDrawPoolTerrain;
+
+ LLTerrainMaterials();
+ virtual ~LLTerrainMaterials();
+
+ // 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;
+
+ enum class Type
+ {
+ TEXTURE,
+ PBR,
+ COUNT
+ };
+
+ BOOL generateMaterials();
+
+ void boost();
+
+ virtual LLUUID getDetailAssetID(S32 asset);
+ virtual void setDetailAssetID(S32 asset, const LLUUID& id);
+ Type getMaterialType();
+ bool texturesReady(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);
+
+protected:
+ void unboost();
+ static bool textureReady(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);
+ LLPointer<LLViewerFetchedTexture> mDetailTextures[ASSET_COUNT];
+ LLPointer<LLFetchedGLTFMaterial> mDetailMaterials[ASSET_COUNT];
+ bool mMaterialTexturesSet[ASSET_COUNT];
+};
+
+// Local materials to override all regions
+extern LLTerrainMaterials gLocalTerrainMaterials;
+
+class LLVLComposition : public LLTerrainMaterials, public LLViewerLayer
+{
+public:
+ // 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;
+ static const LLUUID (&getDefaultTextures())[ASSET_COUNT];
+
LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale);
/*virtual*/ ~LLVLComposition();
@@ -44,7 +99,7 @@ public:
BOOL generateHeights(const F32 x, const F32 y, const F32 width, const F32 height);
BOOL generateComposition();
// Generate texture from composition values.
- BOOL generateTexture(const F32 x, const F32 y, const F32 width, const F32 height);
+ BOOL generateMinimapTileLand(const F32 x, const F32 y, const F32 width, const F32 height);
// Use these as indeces ito the get/setters below that use 'corner'
enum ECorner
@@ -55,12 +110,11 @@ public:
NORTHEAST = 3,
CORNER_COUNT = 4
};
- LLUUID getDetailTextureID(S32 corner);
- LLViewerFetchedTexture* getDetailTexture(S32 corner);
+
+ void setDetailAssetID(S32 asset, const LLUUID& id) override;
F32 getStartHeight(S32 corner);
F32 getHeightRange(S32 corner);
- void setDetailTextureID(S32 corner, const LLUUID& id);
void setStartHeight(S32 corner, F32 start_height);
void setHeightRange(S32 corner, F32 range);
@@ -68,19 +122,26 @@ public:
friend class LLDrawPoolTerrain;
void setParamsReady() { mParamsReady = TRUE; }
BOOL getParamsReady() const { return mParamsReady; }
+
protected:
- BOOL mParamsReady;
+ 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;
- BOOL mTexturesLoaded;
- LLPointer<LLViewerFetchedTexture> mDetailTextures[CORNER_COUNT];
- LLPointer<LLImageRaw> mRawImages[CORNER_COUNT];
+ // Final minimap raw images
+ LLPointer<LLImageRaw> mRawImages[LLTerrainMaterials::ASSET_COUNT];
+
+ // Only non-null during minimap tile generation
+ LLPointer<LLImageRaw> mRawImagesBaseColor[LLTerrainMaterials::ASSET_COUNT];
+ LLPointer<LLImageRaw> mRawImagesEmissive[LLTerrainMaterials::ASSET_COUNT];
F32 mStartHeight[CORNER_COUNT];
F32 mHeightRange[CORNER_COUNT];
- F32 mTexScaleX;
- F32 mTexScaleY;
+ F32 mTexScaleX = 16.f;
+ F32 mTexScaleY = 16.f;
};
#endif //LL_LLVLCOMPOSITION_H
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 8ecfa3eed1..d8ac3613bc 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -4698,7 +4698,7 @@ bool LLVOAvatar::updateCharacter(LLAgent &agent)
mSpeed = speed;
// update animations
- if (!visible)
+ if (!visible && !isSelf()) // NOTE: never do a "hidden update" for self avatar as it interrupts controller processing
{
updateMotions(LLCharacter::HIDDEN_UPDATE);
}
@@ -5250,9 +5250,6 @@ U32 LLVOAvatar::renderRigid()
return 0;
}
- bool should_alpha_mask = shouldAlphaMask();
- LLGLState test(GL_ALPHA_TEST, should_alpha_mask);
-
if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar())
{
LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index f12fc3babc..01e030a300 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -1066,7 +1066,7 @@ void LLVOAvatarSelf::updateAttachmentVisibility(U32 camera_mode)
switch (camera_mode)
{
case CAMERA_MODE_MOUSELOOK:
- if (LLVOAvatar::sVisibleInFirstPerson && attachment->getVisibleInFirstPerson())
+ if ((LLVOAvatar::sVisibleInFirstPerson && attachment->getVisibleInFirstPerson()) || gPipeline.mHeroProbeManager.isMirrorPass())
{
attachment->setAttachmentVisibility(TRUE);
}
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index dd5b9f9fd5..8dad833eaa 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -33,7 +33,7 @@
#include "llviewerregion.h"
#include "llagentcamera.h"
#include "llsdserialize.h"
-
+#include "llworld.h" // For LLWorld::getInstance()
//static variables
U32 LLVOCacheEntry::sMinFrameRange = 0;
F32 LLVOCacheEntry::sNearRadius = 1.0f;
@@ -55,6 +55,10 @@ BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes)
return apr_file->write(src, n_bytes) == n_bytes ;
}
+// Material Override Cache needs a version label, so we can upgrade this later.
+const std::string LLGLTFOverrideCacheEntry::VERSION_LABEL = {"GLTFCacheVer"};
+const int LLGLTFOverrideCacheEntry::VERSION = 1;
+
bool LLGLTFOverrideCacheEntry::fromLLSD(const LLSD& data)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK;
@@ -235,6 +239,8 @@ LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
}
else
{
+ // Improve logging around vocache
+ LL_WARNS() << "Error loading cache entry for " << mLocalID << ", size " << size << " aborting!" << LL_ENDL;
delete[] mBuffer ;
mBuffer = NULL ;
}
@@ -1261,6 +1267,17 @@ void LLVOCache::removeEntry(HeaderEntryInfo* entry)
{
return;
}
+ // Bit more tracking of cache creation/destruction.
+ std::string filename;
+ getObjectCacheFilename(entry->mHandle, filename);
+ LL_INFOS() << "Removing entry for region with filename" << filename << LL_ENDL;
+
+ // make sure corresponding LLViewerRegion also clears its in-memory cache
+ LLViewerRegion* regionp = LLWorld::instance().getRegionFromHandle(entry->mHandle);
+ if (regionp)
+ {
+ regionp->clearVOCacheFromMemory();
+ }
header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry);
if(iter != mHeaderEntryQueue.end())
@@ -1330,7 +1347,15 @@ void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
std::string filename;
getObjectCacheFilename(entry->mHandle, filename);
+ LL_WARNS("GLTF", "VOCache") << "Removing object cache for handle " << entry->mHandle << "Filename: " << filename << LL_ENDL;
LLAPRFile::remove(filename, mLocalAPRFilePoolp);
+
+ // Note: `removeFromCache` should take responsibility for cleaning up all cache artefacts specfic to the handle/entry.
+ // as such this now includes the generic extras
+ filename = getObjectCacheExtrasFilename(entry->mHandle);
+ LL_WARNS("GLTF", "VOCache") << "Removing generic extras for handle " << entry->mHandle << "Filename: " << filename << LL_ENDL;
+ LLFile::remove(filename);
+
entry->mTime = INVALID_TIME ;
updateEntry(entry) ; //update the head file.
}
@@ -1344,7 +1369,7 @@ void LLVOCache::readCacheHeader()
}
//clear stale info.
- clearCacheInMemory();
+ clearCacheInMemory();
bool success = true ;
if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
@@ -1478,12 +1503,14 @@ BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
}
-void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
+// we now return bool to trigger dirty cache
+// this in turn forces a rewrite after a partial read due to corruption.
+bool LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
{
if(!mEnabled)
{
LL_WARNS() << "Not reading cache for handle " << handle << "): Cache is currently disabled." << LL_ENDL;
- return ;
+ return true; // no problem we're just read only
}
llassert_always(mInitialized);
@@ -1491,12 +1518,13 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
if(iter == mHandleEntryMap.end()) //no cache
{
LL_WARNS() << "No handle map entry for " << handle << LL_ENDL;
- return ;
+ return false; // arguably no a problem, but we'll mark this as dirty anyway.
}
bool success = true ;
+ S32 num_entries = 0 ; // lifted out of inner loop.
+ std::string filename; // lifted out of loop
{
- std::string filename;
LLUUID cache_id;
getObjectCacheFilename(handle, filename);
LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
@@ -1513,7 +1541,6 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
if(success)
{
- S32 num_entries; // if removal was enabled during write num_entries might be wrong
success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
if(success)
@@ -1542,11 +1569,17 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca
}
}
- return ;
+ LL_DEBUGS("GLTF", "VOCache") << "Read " << cache_entry_map.size() << " entries from object cache " << filename << ", expected " << num_entries << ", success=" << (success?"True":"False") << LL_ENDL;
+ return success;
}
-void LLVOCache::readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map)
+// We now pass in the cache entry map, so that we can remove entries from extras that are no longer in the primary cache.
+void LLVOCache::readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
{
+ int loaded= 0;
+ int discarded = 0;
+ // get ViewerRegion pointer from handle
+ LLViewerRegion* pRegion = LLWorld::getInstance()->getRegionFromHandle(handle);
if(!mEnabled)
{
LL_WARNS() << "Not reading cache for handle " << handle << "): Cache is currently disabled." << LL_ENDL;
@@ -1566,40 +1599,73 @@ void LLVOCache::readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCac
std::string line;
std::getline(in, line);
- if(!in.good()) {
+ if(!in.good())
+ {
LL_WARNS() << "Failed reading extras cache for handle " << handle << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
+ return;
+ }
+ // file formats need versions, let's add one. legacy cache files will be considered version 0
+ // This will make it easier to upgrade/revise later.
+ int versionNumber=0;
+ if (line.compare(0, LLGLTFOverrideCacheEntry::VERSION_LABEL.length(), LLGLTFOverrideCacheEntry::VERSION_LABEL) == 0)
+ {
+ std::string versionStr = line.substr(LLGLTFOverrideCacheEntry::VERSION_LABEL.length()+1); // skip the version label and ':'
+ versionNumber = std::stol(versionStr);
+ }
+ // For future versions we may call a legacy handler here, but realistically we'll just consider this cache out of date.
+ // The important thing is to make sure it gets removed.
+ if(versionNumber != LLGLTFOverrideCacheEntry::VERSION)
+ {
+ LL_WARNS() << "Unexpected version number " << versionNumber << " for extras cache for handle " << handle << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
return;
}
+ LL_DEBUGS("VOCache") << "Reading extras cache for handle " << handle << ", version " << versionNumber << LL_ENDL;
+ std::getline(in, line);
if(!LLUUID::validate(line))
{
LL_WARNS() << "Failed reading extras cache for handle" << handle << ". invalid uuid line: '" << line << "'" << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
return;
}
LLUUID cache_id(line);
if(cache_id != id)
{
- LL_INFOS() << "Cache ID doesn't match for this region, discarding" << LL_ENDL;
+ // if the cache id doesn't match the expected region we should just kill the file.
+ LL_WARNS() << "Cache ID doesn't match for this region, deleting it" << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
return;
}
U32 num_entries; // if removal was enabled during write num_entries might be wrong
std::getline(in, line);
- if(!in.good()) {
+ if(!in.good())
+ {
LL_WARNS() << "Failed reading extras cache for handle " << handle << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
return;
}
- try {
+ try
+ {
num_entries = std::stol(line);
}
catch(std::logic_error&) // either invalid_argument or out_of_range
{
LL_WARNS() << "Failed reading extras cache for handle " << handle << ". unreadable num_entries" << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
return;
}
- LL_DEBUGS("GLTF") << "Beginning reading extras cache for handle " << handle << ", " << num_entries << " entries" << LL_ENDL;
+ LL_DEBUGS("GLTF") << "Beginning reading extras cache for handle " << handle << " from " << getObjectCacheExtrasFilename(handle) << LL_ENDL;
LLSD entry_llsd;
for (U32 i = 0; i < num_entries && !in.eof(); i++)
@@ -1607,47 +1673,66 @@ void LLVOCache::readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCac
static const U32 max_size = 4096;
bool success = LLSDSerialize::deserialize(entry_llsd, in, max_size);
// check bool(in) this time since eof is not a failure condition here
- if(!success || !in) {
- LL_WARNS() << "Failed reading extras cache for handle " << handle << ", entry number " << i << LL_ENDL;
- return;
+ if(!success || !in)
+ {
+ LL_WARNS() << "Failed reading extras cache for handle " << handle << ", entry number " << i << " cache patrtial load only." << LL_ENDL;
+ in.close();
+ removeGenericExtrasForHandle(handle);
+ break;
}
LLGLTFOverrideCacheEntry entry;
entry.fromLLSD(entry_llsd);
U32 local_id = entry_llsd["local_id"].asInteger();
- cache_extras_entry_map[local_id] = entry;
+ // only add entries that exist in the primary cache
+ // this is a self-healing test that avoids us polluting the cache with entries that are no longer valid based on the main cache.
+ if(cache_entry_map.find(local_id)!= cache_entry_map.end())
+ {
+ // attempt to backfill a null objectId, though these shouldn't be in the persisted cache really
+ if(entry.mObjectId.isNull() && pRegion)
+ {
+ gObjectList.getUUIDFromLocal( entry.mObjectId, local_id, pRegion->getHost().getAddress(), pRegion->getHost().getPort() );
+ }
+ cache_extras_entry_map[local_id] = entry;
+ loaded++;
+ }
+ else
+ {
+ discarded++;
+ }
}
-
- LL_DEBUGS("GLTF") << "Completed reading extras cache for handle " << handle << ", " << num_entries << " entries" << LL_ENDL;
+ LL_DEBUGS("GLTF") << "Completed reading extras cache for handle " << handle << ", " << loaded << " loaded, " << discarded << " discarded" << LL_ENDL;
}
void LLVOCache::purgeEntries(U32 size)
{
+ LL_DEBUGS("VOCache","GLTF") << "Purging " << size << " entries from cache" << LL_ENDL;
while(mHeaderEntryQueue.size() > size)
{
header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
- HeaderEntryInfo* entry = *iter ;
- mHandleEntryMap.erase(entry->mHandle);
+ HeaderEntryInfo* entry = *iter ;
+ mHandleEntryMap.erase(entry->mHandle) ;
mHeaderEntryQueue.erase(iter) ;
- removeFromCache(entry) ;
+ removeFromCache(entry) ; // This now handles removing extras cache where appropriate.
delete entry;
- // TODO also delete extras
}
mNumEntries = mHandleEntryMap.size() ;
}
void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache, bool removal_enabled)
{
+ std::string filename;
+ getObjectCacheFilename(handle, filename);
if(!mEnabled)
{
- LL_WARNS() << "Not writing cache for handle " << handle << "): Cache is currently disabled." << LL_ENDL;
+ LL_WARNS() << "Not writing cache for " << filename << " (handle:" << handle << "): Cache is currently disabled." << LL_ENDL;
return ;
}
llassert_always(mInitialized);
if(mReadOnly)
{
- LL_WARNS() << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << LL_ENDL;
+ LL_WARNS() << "Not writing cache for " << filename << " (handle:" << handle << "): Cache is currently in read-only mode." << LL_ENDL;
return ;
}
@@ -1682,13 +1767,13 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
//update cache header
if(!updateEntry(entry))
{
- LL_WARNS() << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << LL_ENDL;
+ LL_WARNS() << "Failed to update cache header index " << entry->mIndex << ". " << filename << " handle = " << handle << LL_ENDL;
return ; //update failed.
}
if(!dirty_cache)
{
- LL_WARNS() << "Skipping write to cache for handle " << handle << ": cache not dirty" << LL_ENDL;
+ LL_WARNS() << "Skipping write to cache for " << filename << " (handle:" << handle << "): cache not dirty" << LL_ENDL;
return ; //nothing changed, no need to update.
}
@@ -1724,6 +1809,7 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
}
else
{
+ LL_WARNS() << "Failed to write cache entry to buffer for " << filename << ", entry number " << iter->second->getLocalID() << LL_ENDL;
success = false;
break;
}
@@ -1735,6 +1821,7 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
size_in_buffer = 0;
if (!success)
{
+ LL_WARNS() << "Failed to write cache to disk " << filename << LL_ENDL;
break;
}
}
@@ -1745,8 +1832,13 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
{
// final write
success = check_write(&apr_file, (void*)data_buffer, size_in_buffer);
+ if(!success)
+ {
+ LL_WARNS() << "Failed to write cache entry to disk " << filename << LL_ENDL;
+ }
size_in_buffer = 0;
}
+ LL_DEBUGS("VOCache") << "Wrote " << num_entries << " entries to the primary VOCache file " << filename << ". success = " << (success ? "True":"False") << LL_ENDL;
}
}
}
@@ -1759,6 +1851,28 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:
return ;
}
+void LLVOCache::removeGenericExtrasForHandle(U64 handle)
+{
+ if(mReadOnly)
+ {
+ LL_WARNS() << "Not removing cache for handle " << handle << ": Cache is currently in read-only mode." << LL_ENDL;
+ return ;
+ }
+
+ // NOTE: when removing the extras, we must also remove the objects so the simulator will send us a full upddate with the valid overrides
+ auto* entry = mHandleEntryMap[handle];
+ if (entry)
+ {
+ removeEntry(entry);
+ }
+ else
+ {
+ //shouldn't happen, but if it does, we should remove the extras file since it's orphaned
+ LL_WARNS("GLTF", "VOCache") << "Removing generic extras for handle " << entry->mHandle << "Filename: " << getObjectCacheExtrasFilename(handle) << LL_ENDL;
+ LLFile::remove(getObjectCacheExtrasFilename(entry->mHandle));
+ }
+}
+
void LLVOCache::writeGenericExtrasToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map, BOOL dirty_cache, bool removal_enabled)
{
if(!mEnabled)
@@ -1774,46 +1888,86 @@ void LLVOCache::writeGenericExtrasToCache(U64 handle, const LLUUID& id, const LL
return;
}
- std::string filename(getObjectCacheExtrasFilename(handle));
+ // <FS:Beq> FIRE-33808 - Material Override Cache causes long delays
+ std::string filename = getObjectCacheExtrasFilename(handle);
+ // </FS:Beq>
llofstream out(filename, std::ios::out | std::ios::binary);
if(!out.good())
{
LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ removeGenericExtrasForHandle(handle);
return;
- // TODO - clean up broken cache file
}
+ // It is good practice to version file formats so let's add one.
+ // legacy versions will be treated as version 0.
+ out << LLGLTFOverrideCacheEntry::VERSION_LABEL << ":" << LLGLTFOverrideCacheEntry::VERSION << '\n';
out << id << '\n';
if(!out.good())
{
LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ removeGenericExtrasForHandle(handle);
return;
- // TODO - clean up broken cache file
}
-
- U32 num_entries = cache_extras_entry_map.size();
- out << num_entries << '\n';
+ // Because we don't write out all the entries we need to record a placeholder and rewrite this later
+ auto num_entries_placeholder = out.tellp();
+ out << std::setw(10) << std::setfill('0') << 0 << '\n';
if(!out.good())
{
LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ removeGenericExtrasForHandle(handle);
return;
- // TODO - clean up broken cache file
}
- for (auto const & entry : cache_extras_entry_map)
+ // get ViewerRegion pointer from handle
+ LLViewerRegion* pRegion = LLWorld::getInstance()->getRegionFromHandle(handle);
+
+ U32 num_entries = 0;
+ U32 inmem_entries = 0;
+ U32 skipped = 0;
+ inmem_entries = cache_extras_entry_map.size();
+ for (auto [local_id, entry] : cache_extras_entry_map)
{
- S32 local_id = entry.first;
- LLSD entry_llsd = entry.second.toLLSD();
- entry_llsd["local_id"] = local_id;
- LLSDSerialize::serialize(entry_llsd, out, LLSDSerialize::LLSD_XML);
- out << '\n';
- if(!out.good())
+ // Only write out GLTFOverrides that we can actually apply again on import.
+ // worst case we have an extra cache miss.
+ // Note: A null mObjectId is valid when in memory as we might have a data race between GLTF of the object itself.
+ // This remains a valid state to persist as it is consistent with the localid checks on import with the main cache.
+ // the mObjectId will be updated if/when the local object is updated from the gObject list (due to full update)
+ if(entry.mObjectId.isNull() && pRegion)
{
- LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
- return;
- // TODO - clean up broken cache file
+ gObjectList.getUUIDFromLocal( entry.mObjectId, local_id, pRegion->getHost().getAddress(), pRegion->getHost().getPort() );
}
- }
- LL_DEBUGS("GLTF") << "Completed writing extras cache for handle " << handle << ", " << num_entries << " entries" << LL_ENDL;
+ if( entry.mSides.size() > 0 &&
+ entry.mSides.size() == entry.mGLTFMaterial.size()
+ )
+ {
+ LLSD entry_llsd = entry.toLLSD();
+ entry_llsd["local_id"] = (S32)local_id;
+ LLSDSerialize::serialize(entry_llsd, out, LLSDSerialize::LLSD_XML);
+ out << '\n';
+ if(!out.good())
+ {
+ // We're not in a good place when this happens so we might as well nuke the file.
+ LL_WARNS() << "Failed writing extras cache for handle " << handle << ". Corrupted cache file " << filename << " removed." << LL_ENDL;
+ removeGenericExtrasForHandle(handle);
+ return;
+ }
+ num_entries++;
+ }
+ else
+ {
+ skipped++;
+ }
+ }
+ // Rewrite the placeholder
+ out.seekp(num_entries_placeholder);
+ out << std::setw(10) << std::setfill('0') << num_entries << '\n';
+ if(!out.good())
+ {
+ LL_WARNS() << "Failed writing extras cache for handle " << handle << LL_ENDL;
+ removeGenericExtrasForHandle(handle);
+ return;
+ }
+ LL_DEBUGS("GLTF") << "Completed writing extras cache for handle " << handle << ", " << num_entries << " entries. Total in RAM: " << inmem_entries << " skipped (no persist): " << skipped << LL_ENDL;
}
diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h
index 8525edd121..d94e79ce1b 100644
--- a/indra/newview/llvocache.h
+++ b/indra/newview/llvocache.h
@@ -43,6 +43,8 @@ class LLCamera;
class LLGLTFOverrideCacheEntry
{
public:
+ static const std::string VERSION_LABEL;
+ static const int VERSION;
bool fromLLSD(const LLSD& data);
LLSD toLLSD() const;
@@ -96,12 +98,6 @@ public:
}
};
- struct ExtrasEntry
- {
- LLSD extras;
- std::string extras_raw;
- };
-
protected:
~LLVOCacheEntry();
public:
@@ -289,12 +285,13 @@ public:
void initCache(ELLPath location, U32 size, U32 cache_version);
void removeCache(ELLPath location, bool started = false) ;
- void readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) ;
- void readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map);
+ bool readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) ;
+ void readGenericExtrasFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map);
void writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache, bool removal_enabled);
void writeGenericExtrasToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_gltf_overrides_map_t& cache_extras_entry_map, BOOL dirty_cache, bool removal_enabled);
void removeEntry(U64 handle) ;
+ void removeGenericExtrasForHandle(U64 handle);
U32 getCacheEntries() { return mNumEntries; }
U32 getCacheEntriesMax() { return mCacheSize; }
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index 15fabf0414..69b9476d38 100644
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -39,6 +39,7 @@
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llvlcomposition.h"
+#include "llvolume.h"
#include "llvovolume.h"
#include "pipeline.h"
#include "llspatialpartition.h"
@@ -213,6 +214,7 @@ BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
void LLVOSurfacePatch::updateFaceSize(S32 idx)
{
+ LL_PROFILE_ZONE_SCOPED;
if (idx != 0)
{
LL_WARNS() << "Terrain partition requested invalid face!!!" << LL_ENDL;
@@ -241,48 +243,50 @@ BOOL LLVOSurfacePatch::updateLOD()
return TRUE;
}
-void LLVOSurfacePatch::getGeometry(LLStrider<LLVector3> &verticesp,
- LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
- LLStrider<LLVector2> &texCoords1p,
- LLStrider<U16> &indicesp)
+void LLVOSurfacePatch::getTerrainGeometry(LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ LLStrider<U16> &indicesp)
{
LLFace* facep = mDrawable->getFace(0);
- if (facep)
- {
- U32 index_offset = facep->getGeomIndex();
-
- updateMainGeometry(facep,
- verticesp,
- normalsp,
- texCoords0p,
- texCoords1p,
- indicesp,
- index_offset);
- updateNorthGeometry(facep,
- verticesp,
- normalsp,
- texCoords0p,
- texCoords1p,
- indicesp,
- index_offset);
- updateEastGeometry(facep,
- verticesp,
- normalsp,
- texCoords0p,
- texCoords1p,
- indicesp,
- index_offset);
- }
+ if (!facep)
+ {
+ return;
+ }
+
+ U32 index_offset = facep->getGeomIndex();
+
+ updateMainGeometry(facep,
+ verticesp,
+ normalsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+ updateNorthGeometry(facep,
+ verticesp,
+ normalsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+ updateEastGeometry(facep,
+ verticesp,
+ normalsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
}
void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
- LLStrider<LLVector3> &verticesp,
- LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
- LLStrider<LLVector2> &texCoords1p,
- LLStrider<U16> &indicesp,
- U32 &index_offset)
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ LLStrider<U16> &indicesp,
+ U32 &index_offset)
{
S32 i, j, x, y;
@@ -381,12 +385,12 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
- LLStrider<LLVector3> &verticesp,
- LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
- LLStrider<LLVector2> &texCoords1p,
- LLStrider<U16> &indicesp,
- U32 &index_offset)
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ LLStrider<U16> &indicesp,
+ U32 &index_offset)
{
S32 i, x, y;
@@ -571,12 +575,12 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
}
void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
- LLStrider<LLVector3> &verticesp,
- LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
- LLStrider<LLVector2> &texCoords1p,
- LLStrider<U16> &indicesp,
- U32 &index_offset)
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ LLStrider<U16> &indicesp,
+ U32 &index_offset)
{
S32 i, x, y;
@@ -982,6 +986,49 @@ LLTerrainPartition::LLTerrainPartition(LLViewerRegion* regionp)
mPartitionType = LLViewerRegion::PARTITION_TERRAIN;
}
+// Do not add vertices; honor strict vertex count specified by strider_vertex_count
+void gen_terrain_tangents(U16 strider_vertex_count,
+ U32 strider_index_count,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLVector4a> &tangentsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<U16> &indicesp)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ LLVector4a *vertices = new LLVector4a[strider_vertex_count];
+ LLVector4a *normals = new LLVector4a[strider_vertex_count];
+ LLVector4a *tangents = new LLVector4a[strider_vertex_count];
+ std::vector<LLVector2> texcoords(strider_vertex_count);
+ std::vector<U16> indices(strider_index_count);
+
+ for (U16 v = 0; v < strider_vertex_count; ++v)
+ {
+ F32 *vert = verticesp[v].mV;
+ vertices[v] = LLVector4a(vert[0], vert[1], vert[2], 1.f);
+ F32 *n = normalsp[v].mV;
+ normals[v] = LLVector4a(n[0], n[1], n[2], 1.f);
+ tangents[v] = tangentsp[v];
+ texcoords[v] = texCoords0p[v];
+ }
+ for (U32 i = 0; i < strider_index_count; ++i)
+ {
+ indices[i] = indicesp[i];
+ }
+
+ LLCalculateTangentArray(strider_vertex_count, vertices, normals, texcoords.data(), strider_index_count / 3, indices.data(), tangents);
+
+ for (U16 v = 0; v < strider_vertex_count; ++v)
+ {
+ tangentsp[v] = tangents[v];
+ }
+
+ delete[] vertices;
+ delete[] normals;
+ delete[] tangents;
+}
+
void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
{
LL_PROFILE_ZONE_SCOPED;
@@ -989,35 +1036,57 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
LLVertexBuffer* buffer = group->mVertexBuffer;
//get vertex buffer striders
- LLStrider<LLVector3> vertices;
- LLStrider<LLVector3> normals;
- LLStrider<LLVector2> texcoords2;
- LLStrider<LLVector2> texcoords;
- LLStrider<U16> indices;
-
- llassert_always(buffer->getVertexStrider(vertices));
- llassert_always(buffer->getNormalStrider(normals));
- llassert_always(buffer->getTexCoord0Strider(texcoords));
- llassert_always(buffer->getTexCoord1Strider(texcoords2));
- llassert_always(buffer->getIndexStrider(indices));
-
- U32 indices_index = 0;
- U32 index_offset = 0;
-
- for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
- {
- LLFace* facep = *i;
-
- facep->setIndicesIndex(indices_index);
- facep->setGeomIndex(index_offset);
- facep->setVertexBuffer(buffer);
-
- LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
- patchp->getGeometry(vertices, normals, texcoords, texcoords2, indices);
-
- indices_index += facep->getIndicesCount();
- index_offset += facep->getGeomCount();
- }
+ LLStrider<LLVector3> vertices_start;
+ LLStrider<LLVector3> normals_start;
+ LLStrider<LLVector4a> tangents_start;
+ LLStrider<LLVector2> texcoords_start;
+ LLStrider<LLVector2> texcoords2_start;
+ LLStrider<U16> indices_start;
+
+ llassert_always(buffer->getVertexStrider(vertices_start));
+ llassert_always(buffer->getNormalStrider(normals_start));
+ llassert_always(buffer->getTangentStrider(tangents_start));
+ llassert_always(buffer->getTexCoord0Strider(texcoords_start));
+ llassert_always(buffer->getTexCoord1Strider(texcoords2_start));
+ llassert_always(buffer->getIndexStrider(indices_start));
+
+ U32 indices_index = 0;
+ U32 index_offset = 0;
+
+ {
+ LLStrider<LLVector3> vertices = vertices_start;
+ LLStrider<LLVector3> normals = normals_start;
+ LLStrider<LLVector2> texcoords = texcoords_start;
+ LLStrider<LLVector2> texcoords2 = texcoords2_start;
+ LLStrider<U16> indices = indices_start;
+
+ for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
+ {
+ LLFace* facep = *i;
+
+ facep->setIndicesIndex(indices_index);
+ facep->setGeomIndex(index_offset);
+ facep->setVertexBuffer(buffer);
+
+ LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
+ patchp->getTerrainGeometry(vertices, normals, texcoords, texcoords2, indices);
+
+ indices_index += facep->getIndicesCount();
+ index_offset += facep->getGeomCount();
+ }
+ }
+
+ const bool has_tangents = tangents_start.get() != nullptr;
+ if (has_tangents)
+ {
+ LLStrider<LLVector3> vertices = vertices_start;
+ LLStrider<LLVector3> normals = normals_start;
+ LLStrider<LLVector4a> tangents = tangents_start;
+ LLStrider<LLVector2> texcoords = texcoords_start;
+ LLStrider<U16> indices = indices_start;
+
+ gen_terrain_tangents(index_offset, indices_index, vertices, normals, tangents, texcoords, indices);
+ }
buffer->unmapBuffer();
mFaceList.clear();
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index aed67162d1..a3dcb945d1 100644
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -63,11 +63,11 @@ public:
/*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
/*virtual*/ BOOL updateLOD();
/*virtual*/ void updateFaceSize(S32 idx);
- void getGeometry(LLStrider<LLVector3> &verticesp,
- LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
- LLStrider<LLVector2> &texCoords1p,
- LLStrider<U16> &indicesp);
+ void getTerrainGeometry(LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ LLStrider<U16> &indicesp);
/*virtual*/ void updateTextures();
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index e3f2afadc5..a956814147 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -296,6 +296,11 @@ void LLVOVolume::markDead()
{
mLightTexture->removeVolume(LLRender::LIGHT_TEX, this);
}
+
+ if (mIsHeroProbe)
+ {
+ gPipeline.mHeroProbeManager.unregisterViewerObject(this);
+ }
}
LLViewerObject::markDead();
@@ -996,7 +1001,7 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
{
updateReflectionProbePtr();
}
-
+
updateRadius();
bool force_update = true; // avoid non-alpha mDistance update being optimized away
mDrawable->updateDistance(*LLViewerCamera::getInstance(), force_update);
@@ -3408,6 +3413,29 @@ bool LLVOVolume::setReflectionProbeIsDynamic(bool is_dynamic)
return false;
}
+bool LLVOVolume::setReflectionProbeIsMirror(bool is_mirror)
+{
+ LLReflectionProbeParams *param_block = (LLReflectionProbeParams *) getParameterEntry(LLNetworkData::PARAMS_REFLECTION_PROBE);
+ if (param_block)
+ {
+ if (param_block->getIsMirror() != is_mirror)
+ {
+ LL_INFOS() << "Setting reflection probe mirror to " << is_mirror << LL_ENDL;
+ param_block->setIsMirror(is_mirror);
+ parameterChanged(LLNetworkData::PARAMS_REFLECTION_PROBE, true);
+
+ if (!is_mirror)
+ gPipeline.mHeroProbeManager.unregisterViewerObject(this);
+ else
+ gPipeline.mHeroProbeManager.registerViewerObject(this);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
F32 LLVOVolume::getReflectionProbeAmbiance() const
{
const LLReflectionProbeParams* param_block = (const LLReflectionProbeParams*)getParameterEntry(LLNetworkData::PARAMS_REFLECTION_PROBE);
@@ -3456,6 +3484,18 @@ bool LLVOVolume::getReflectionProbeIsDynamic() const
return false;
}
+bool LLVOVolume::getReflectionProbeIsMirror() const
+{
+ const LLReflectionProbeParams *param_block =
+ (const LLReflectionProbeParams *) getParameterEntry(LLNetworkData::PARAMS_REFLECTION_PROBE);
+ if (param_block)
+ {
+ return param_block->getIsMirror();
+ }
+
+ return false;
+}
+
U32 LLVOVolume::getVolumeInterfaceID() const
{
if (mVolumeImpl)
@@ -4375,14 +4415,30 @@ void LLVOVolume::updateReflectionProbePtr()
{
if (isReflectionProbe())
{
- if (mReflectionProbe.isNull())
+ if (mReflectionProbe.isNull() && !getReflectionProbeIsMirror())
{
mReflectionProbe = gPipeline.mReflectionMapManager.registerViewerObject(this);
}
+ else if (mReflectionProbe.isNull() && getReflectionProbeIsMirror())
+ {
+ // Geenz: This is a special case - what we want here is a hero probe.
+ // What we want to do here is instantiate a hero probe from the hero probe manager.
+
+ if (!mIsHeroProbe)
+ mIsHeroProbe = gPipeline.mHeroProbeManager.registerViewerObject(this);
+ }
}
- else if (mReflectionProbe.notNull())
+ else if (mReflectionProbe.notNull() || getReflectionProbeIsMirror())
{
- mReflectionProbe = nullptr;
+ if (mReflectionProbe.notNull())
+ {
+ mReflectionProbe = nullptr;
+ }
+
+ if (getReflectionProbeIsMirror())
+ {
+ gPipeline.mHeroProbeManager.unregisterViewerObject(this);
+ }
}
}
@@ -4557,7 +4613,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
@@ -4770,7 +4826,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
}
}
}
-
+
return ret;
}
@@ -6199,19 +6255,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
LLViewerTexture* last_tex = NULL;
- S32 texture_index_channels = 1;
-
- if (gGLManager.mGLSLVersionMajor > 1 || gGLManager.mGLSLVersionMinor >= 30)
- {
- texture_index_channels = LLGLSLShader::sIndexedTextureChannels-1; //always reserve one for shiny for now just for simplicity;
- }
-
- if (distance_sort)
- {
- texture_index_channels = gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels;
- }
-
- texture_index_channels = LLGLSLShader::sIndexedTextureChannels;
+ S32 texture_index_channels = LLGLSLShader::sIndexedTextureChannels;
bool flexi = false;
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index aadc1fbcf3..cac09609b3 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -301,12 +301,14 @@ public:
bool setReflectionProbeNearClip(F32 near_clip);
bool setReflectionProbeIsBox(bool is_box);
bool setReflectionProbeIsDynamic(bool is_dynamic);
+ bool setReflectionProbeIsMirror(bool is_mirror);
BOOL isReflectionProbe() const override;
F32 getReflectionProbeAmbiance() const;
F32 getReflectionProbeNearClip() const;
bool getReflectionProbeIsBox() const;
bool getReflectionProbeIsDynamic() const;
+ bool getReflectionProbeIsMirror() const;
// Flexible Objects
U32 getVolumeInterfaceID() const;
diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp
index 3c2c6d15c4..461d556946 100644
--- a/indra/newview/llxmlrpclistener.cpp
+++ b/indra/newview/llxmlrpclistener.cpp
@@ -411,34 +411,20 @@ private:
return parseValues(status_string, "", param);
}
- /**
- * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
- * @param key_pfx Used to describe a given key in log messages. At top
- * level, pass "". When parsing an options array, pass the top-level key
- * name of the array plus the index of the array entry; to this we'll
- * append the subkey of interest.
- * @param param XMLRPC_VALUE iterator. At top level, pass
- * XMLRPC_RequestGetData(XMLRPC_REQUEST).
- */
- LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
+ LLSD parseValue(std::string& status_string, const std::string& key, const std::string& key_pfx, XMLRPC_VALUE param)
{
- LLSD responses;
- for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
- current = XMLRPC_VectorNext(param))
+ LLSD response;
+
+ XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(param);
+ switch (type)
{
- std::string key(XMLRPC_GetValueID(current));
- LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
- XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current);
- switch (type)
- {
case xmlrpc_type_empty:
LL_INFOS("LLXMLRPCListener") << "Empty result for key " << key_pfx << key << LL_ENDL;
- responses.insert(key, LLSD());
break;
case xmlrpc_type_base64:
{
- S32 len = XMLRPC_GetValueStringLen(current);
- const char* buf = XMLRPC_GetValueBase64(current);
+ S32 len = XMLRPC_GetValueStringLen(param);
+ const char* buf = XMLRPC_GetValueBase64(param);
if ((len > 0) && buf)
{
// During implementation this code was not tested
@@ -449,49 +435,44 @@ private:
LLSD::Binary data;
data.resize(len);
memcpy((void*)&data[0], (void*)buf, len);
- responses.insert(key, data);
+ response = data;
}
else
{
LL_WARNS("LLXMLRPCListener") << "Potentially malformed xmlrpc_type_base64 for key "
<< key_pfx << key << LL_ENDL;
- responses.insert(key, LLSD());
}
break;
}
case xmlrpc_type_boolean:
{
- LLSD::Boolean val(XMLRPC_GetValueBoolean(current));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
- responses.insert(key, val);
+ response = LLSD::Boolean(XMLRPC_GetValueBoolean(param));
+ LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
break;
}
case xmlrpc_type_datetime:
{
- std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(current));
+ std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(param));
LL_DEBUGS("LLXMLRPCListener") << "val: " << iso8601_date << LL_ENDL;
- responses.insert(key, LLSD::Date(iso8601_date));
+ response = LLSD::Date(iso8601_date);
break;
}
case xmlrpc_type_double:
{
- LLSD::Real val(XMLRPC_GetValueDouble(current));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
- responses.insert(key, val);
+ response = LLSD::Real(XMLRPC_GetValueDouble(param));
+ LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
break;
}
case xmlrpc_type_int:
{
- LLSD::Integer val(XMLRPC_GetValueInt(current));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
- responses.insert(key, val);
+ response = LLSD::Integer(XMLRPC_GetValueInt(param));
+ LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
break;
}
case xmlrpc_type_string:
{
- LLSD::String val(XMLRPC_GetValueString(current));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
- responses.insert(key, val);
+ response = LLSD::String(XMLRPC_GetValueString(param));
+ LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
break;
}
case xmlrpc_type_mixed:
@@ -501,8 +482,8 @@ private:
// recursively parsing each submap and collecting them.
LLSD array;
int i = 0; // for descriptive purposes
- for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row;
- row = XMLRPC_VectorNext(current), ++i)
+ for (XMLRPC_VALUE row = XMLRPC_VectorRewind(param); row;
+ row = XMLRPC_VectorNext(param), ++i)
{
// Recursive call. For the lower-level key_pfx, if 'key'
// is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
@@ -510,21 +491,21 @@ private:
// "foo[0]:bar", and so forth.
// Parse the scalar subkey/value pairs from this array
// entry into a temp submap. Collect such submaps in 'array'.
- array.append(parseValues(status_string,
+
+ array.append(parseValue(status_string, "",
STRINGIZE(key_pfx << key << '[' << i << "]:"),
row));
}
// Having collected an 'array' of 'submap's, insert that whole
// 'array' as the value of this 'key'.
- responses.insert(key, array);
+ response = array;
break;
}
case xmlrpc_type_struct:
{
- LLSD submap = parseValues(status_string,
+ response = parseValues(status_string,
STRINGIZE(key_pfx << key << ':'),
- current);
- responses.insert(key, submap);
+ param);
break;
}
case xmlrpc_type_none: // Not expected
@@ -532,9 +513,30 @@ private:
// whoops - unrecognized type
LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key "
<< key_pfx << key << LL_ENDL;
- responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>'));
+ response = STRINGIZE("<bad XMLRPC type " << type << '>');
status_string = "BadType";
- }
+ }
+ return response;
+ }
+
+ /**
+ * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
+ * @param key_pfx Used to describe a given key in log messages. At top
+ * level, pass "". When parsing an options array, pass the top-level key
+ * name of the array plus the index of the array entry; to this we'll
+ * append the subkey of interest.
+ * @param param XMLRPC_VALUE iterator. At top level, pass
+ * XMLRPC_RequestGetData(XMLRPC_REQUEST).
+ */
+ LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
+ {
+ LLSD responses;
+ for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
+ current = XMLRPC_VectorNext(param))
+ {
+ std::string key(XMLRPC_GetValueID(current));
+ LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
+ responses.insert(key, parseValue(status_string, key, key_pfx, current));
}
return responses;
}
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index f5f2ba52bf..8b1e46343b 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -112,6 +112,7 @@
#include "llscenemonitor.h"
#include "llprogressview.h"
#include "llcleanup.h"
+#include "gltfscenemanager.h"
#include "llenvironment.h"
#include "llsettingsvo.h"
@@ -198,8 +199,13 @@ F32 LLPipeline::RenderScreenSpaceReflectionDepthRejectBias;
F32 LLPipeline::RenderScreenSpaceReflectionAdaptiveStepMultiplier;
S32 LLPipeline::RenderScreenSpaceReflectionGlossySamples;
S32 LLPipeline::RenderBufferVisualization;
+bool LLPipeline::RenderMirrors;
+S32 LLPipeline::RenderHeroProbeUpdateRate;
+S32 LLPipeline::RenderHeroProbeConservativeUpdateMultiplier;
LLTrace::EventStatHandle<S64> LLPipeline::sStatBatchSize("renderbatchsize");
+const U32 LLPipeline::MAX_BAKE_WIDTH = 512;
+
const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f;
const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f;
const F32 ALPHA_BLEND_CUTOFF = 0.598f;
@@ -325,8 +331,8 @@ bool addDeferredAttachments(LLRenderTarget& target, bool for_impostor = false)
{
bool valid = true
&& target.addColorAttachment(GL_RGBA) // frag-data[1] specular OR PBR ORM
- && target.addColorAttachment(GL_RGBA16F) // frag_data[2] normal+z+fogmask, See: class1\deferred\materialF.glsl & softenlight
- && target.addColorAttachment(GL_RGB16F); // frag_data[3] PBR emissive
+ && target.addColorAttachment(GL_RGBA16F) // frag_data[2] normal+fogmask, See: class1\deferred\materialF.glsl & softenlight
+ && target.addColorAttachment(GL_RGB16F); // frag_data[3] PBR emissive OR material env intensity
return valid;
}
@@ -555,6 +561,9 @@ void LLPipeline::init()
connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionAdaptiveStepMultiplier");
connectRefreshCachedSettingsSafe("RenderScreenSpaceReflectionGlossySamples");
connectRefreshCachedSettingsSafe("RenderBufferVisualization");
+ connectRefreshCachedSettingsSafe("RenderMirrors");
+ connectRefreshCachedSettingsSafe("RenderHeroProbeUpdateRate");
+ connectRefreshCachedSettingsSafe("RenderHeroProbeConservativeUpdateMultiplier");
gSavedSettings.getControl("RenderAutoHideSurfaceAreaLimit")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings));
}
@@ -638,6 +647,7 @@ void LLPipeline::cleanup()
mCubeVB = NULL;
mReflectionMapManager.cleanup();
+ mHeroProbeManager.cleanup();
}
//============================================================================
@@ -764,13 +774,31 @@ LLPipeline::eFBOStatus LLPipeline::doAllocateScreenBuffer(U32 resX, U32 resY)
bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
- if (mRT == &mMainRT && sReflectionProbesEnabled)
+ if (mRT == &mMainRT)
{ // hacky -- allocate auxillary buffer
+
gCubeSnapshot = TRUE;
mReflectionMapManager.initReflectionMaps();
+ mHeroProbeManager.initReflectionMaps();
+
+ if (sReflectionProbesEnabled)
+ {
+ gCubeSnapshot = TRUE;
+ mReflectionMapManager.initReflectionMaps();
+ }
+
mRT = &mAuxillaryRT;
U32 res = mReflectionMapManager.mProbeResolution * 4; //multiply by 4 because probes will be 16x super sampled
allocateScreenBuffer(res, res, samples);
+
+ if (RenderMirrors)
+ {
+ mHeroProbeManager.initReflectionMaps();
+ res = mHeroProbeManager.mProbeResolution; // We also scale the hero probe RT to the probe res since we don't super sample it.
+ mRT = &mHeroProbeRT;
+ allocateScreenBuffer(res, res, samples);
+ }
+
mRT = &mMainRT;
gCubeSnapshot = FALSE;
}
@@ -1043,6 +1071,15 @@ void LLPipeline::refreshCachedSettings()
RenderScreenSpaceReflectionAdaptiveStepMultiplier = gSavedSettings.getF32("RenderScreenSpaceReflectionAdaptiveStepMultiplier");
RenderScreenSpaceReflectionGlossySamples = gSavedSettings.getS32("RenderScreenSpaceReflectionGlossySamples");
RenderBufferVisualization = gSavedSettings.getS32("RenderBufferVisualization");
+ if (gSavedSettings.getBOOL("RenderMirrors") != (BOOL)RenderMirrors)
+ {
+ RenderMirrors = gSavedSettings.getBOOL("RenderMirrors");
+ LLViewerShaderMgr::instance()->clearShaderCache();
+ LLViewerShaderMgr::instance()->setShaders();
+ }
+ RenderHeroProbeUpdateRate = gSavedSettings.getS32("RenderHeroProbeUpdateRate");
+ RenderHeroProbeConservativeUpdateMultiplier = gSavedSettings.getS32("RenderHeroProbeConservativeUpdateMultiplier");
+
sReflectionProbesEnabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderReflectionsEnabled") && gSavedSettings.getBOOL("RenderReflectionsEnabled");
RenderSpotLight = nullptr;
@@ -1072,7 +1109,6 @@ void LLPipeline::releaseGLBuffers()
releaseLUTBuffers();
mWaterDis.release();
- mBake.release();
mSceneMap.release();
@@ -1118,6 +1154,12 @@ void LLPipeline::releaseScreenBuffers()
mRT->fxaaBuffer.release();
mRT->deferredScreen.release();
mRT->deferredLight.release();
+
+ mHeroProbeRT.uiScreen.release();
+ mHeroProbeRT.screen.release();
+ mHeroProbeRT.fxaaBuffer.release();
+ mHeroProbeRT.deferredScreen.release();
+ mHeroProbeRT.deferredLight.release();
}
void LLPipeline::releaseSunShadowTarget(U32 index)
@@ -1151,9 +1193,6 @@ void LLPipeline::createGLBuffers()
stop_glerror();
assertInitialized();
- // Use FBO for bake tex
- mBake.allocate(512, 512, GL_RGBA, true); // SL-12781 Build > Upload > Model; 3D Preview
-
stop_glerror();
GLuint resX = gViewerWindow->getWorldViewWidthRaw();
@@ -2230,7 +2269,8 @@ static LLTrace::BlockTimerStatHandle FTM_CULL("Object Culling");
// static
bool LLPipeline::isWaterClip()
{
- return (!sRenderTransparentWater || gCubeSnapshot) && !sRenderingHUDs;
+ // We always pretend that we're not clipping water when rendering mirrors.
+ return (gPipeline.mHeroProbeManager.isMirrorPass()) ? false : (!sRenderTransparentWater || gCubeSnapshot) && !sRenderingHUDs;
}
void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result)
@@ -2400,6 +2440,26 @@ void LLPipeline::doOcclusion(LLCamera& camera)
gGL.setColorMask(true, true);
}
+
+ if (sReflectionProbesEnabled && sUseOcclusion > 1 && !LLPipeline::sShadowRender && !gCubeSnapshot)
+ {
+ gGL.setColorMask(false, false);
+ LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+ LLGLDisable cull(GL_CULL_FACE);
+
+ gOcclusionCubeProgram.bind();
+
+ if (mCubeVB.isNull())
+ { //cube VB will be used for issuing occlusion queries
+ mCubeVB = ll_create_cube_vb(LLVertexBuffer::MAP_VERTEX);
+ }
+ mCubeVB->setBuffer();
+
+ mHeroProbeManager.doOcclusion();
+ gOcclusionCubeProgram.unbind();
+
+ gGL.setColorMask(true, true);
+ }
if (LLPipeline::sUseOcclusion > 1 &&
(sCull->hasOcclusionGroups() || LLVOCachePartition::sNeedsOcclusionCheck))
@@ -3777,6 +3837,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera, bool do_occlusion)
{
//update reflection probe uniform
mReflectionMapManager.updateUniforms();
+ mHeroProbeManager.updateUniforms();
}
U32 cur_type = 0;
@@ -4478,6 +4539,8 @@ void LLPipeline::renderDebug()
}
}
+ LL::GLTFSceneManager::instance().renderDebug();
+
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
{ //render visible selected group occlusion geometry
gDebugProgram.bind();
@@ -6247,6 +6310,8 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start,
bool pick_unselectable,
bool pick_reflection_probe,
S32* face_hit,
+ S32* gltf_node_hit,
+ S32* gltf_primitive_hit,
LLVector4a* intersection, // return the intersection point
LLVector2* tex_coord, // return the texture coordinates of the intersection point
LLVector4a* normal, // return the surface normal at the intersection point
@@ -6289,7 +6354,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start,
}
}
}
-
+
if (!sPickAvatar)
{
//save hit info in case we need to restore
@@ -6390,6 +6455,25 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start,
}
}
+ S32 node_hit = -1;
+ S32 primitive_hit = -1;
+ LLDrawable* hit = LL::GLTFSceneManager::instance().lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, &node_hit, &primitive_hit, &position, tex_coord, normal, tangent);
+ if (hit)
+ {
+ drawable = hit;
+ local_end = position;
+ }
+
+ if (gltf_node_hit)
+ {
+ *gltf_node_hit = node_hit;
+ }
+
+ if (gltf_primitive_hit)
+ {
+ *gltf_primitive_hit = primitive_hit;
+ }
+
if (intersection)
{
*intersection = position;
@@ -6505,6 +6589,15 @@ void LLPipeline::renderGLTFObjects(U32 type, bool texture, bool rigged)
gGL.loadMatrix(gGLModelView);
gGLLastMatrix = NULL;
+
+ if (!rigged)
+ {
+ LL::GLTFSceneManager::instance().renderOpaque();
+ }
+ else
+ {
+ LL::GLTFSceneManager::instance().render(true, true);
+ }
}
// Currently only used for shadows -Cosmic,2023-04-19
@@ -6542,7 +6635,7 @@ void LLPipeline::renderAlphaObjects(bool rigged)
LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up);
LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(ALPHA_BLEND_CUTOFF);
- mSimplePool->pushRiggedGLTFBatch(*pparams, lastAvatar, lastMeshId);
+ LLRenderPass::pushRiggedGLTFBatch(*pparams, lastAvatar, lastMeshId);
}
else
{
@@ -6568,7 +6661,7 @@ void LLPipeline::renderAlphaObjects(bool rigged)
LLGLSLShader::sCurBoundShaderPtr->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_up);
LLGLSLShader::sCurBoundShaderPtr->uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
LLGLSLShader::sCurBoundShaderPtr->setMinimumAlpha(ALPHA_BLEND_CUTOFF);
- mSimplePool->pushGLTFBatch(*pparams);
+ LLRenderPass::pushGLTFBatch(*pparams);
}
else
{
@@ -6713,6 +6806,8 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst)
gLuminanceProgram.bind();
+ static LLCachedControl<F32> diffuse_luminance_scale(gSavedSettings, "RenderDiffuseLuminanceScale", 1.0f);
+
S32 channel = 0;
channel = gLuminanceProgram.enableTexture(LLShaderMgr::DEFERRED_DIFFUSE);
if (channel > -1)
@@ -6726,6 +6821,16 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst)
mGlow[1].bindTexture(0, channel);
}
+ channel = gLuminanceProgram.enableTexture(LLShaderMgr::DEFERRED_NORMAL);
+ if (channel > -1)
+ {
+ // bind the normal map to get the environment mask
+ mRT->deferredScreen.bindTexture(2, channel, LLTexUnit::TFO_POINT);
+ }
+
+ static LLStaticHashedString diffuse_luminance_scale_s("diffuse_luminance_scale");
+ gLuminanceProgram.uniform1f(diffuse_luminance_scale_s, diffuse_luminance_scale);
+
mScreenTriangleVB->setBuffer();
mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
dst->flush();
@@ -6736,11 +6841,12 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst)
}
}
-void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
+void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history) {
// exposure sample
{
LL_PROFILE_GPU_ZONE("exposure sample");
+ if (use_history)
{
// copy last frame's exposure into mLastExposure
mLastExposure.bindTarget();
@@ -6757,55 +6863,73 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
LLGLDepthTest depth(GL_FALSE, GL_FALSE);
- gExposureProgram.bind();
+ LLGLSLShader* shader;
+ if (use_history)
+ {
+ shader = &gExposureProgram;
+ }
+ else
+ {
+ shader = &gExposureProgramNoFade;
+ }
+
+ shader->bind();
- S32 channel = gExposureProgram.enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
+ S32 channel = shader->enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
if (channel > -1)
{
- mLuminanceMap.bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR);
+ src->bindTexture(0, channel, LLTexUnit::TFO_TRILINEAR);
}
- channel = gExposureProgram.enableTexture(LLShaderMgr::EXPOSURE_MAP);
- if (channel > -1)
+ if (use_history)
{
- mLastExposure.bindTexture(0, channel);
+ channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP);
+ if (channel > -1)
+ {
+ mLastExposure.bindTexture(0, channel);
+ }
}
static LLStaticHashedString dt("dt");
static LLStaticHashedString noiseVec("noiseVec");
static LLStaticHashedString dynamic_exposure_params("dynamic_exposure_params");
static LLCachedControl<F32> dynamic_exposure_coefficient(gSavedSettings, "RenderDynamicExposureCoefficient", 0.175f);
- static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
+ static LLCachedControl<bool> should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true);
- LLSettingsSky::ptr_t sky = LLEnvironment::instance().getCurrentSky();
+ LLSettingsSky::ptr_t sky = LLEnvironment::instance().getCurrentSky();
- F32 probe_ambiance = LLEnvironment::instance().getCurrentSky()->getReflectionProbeAmbiance(should_auto_adjust);
- F32 exp_min = 1.f;
- F32 exp_max = 1.f;
-
- if (probe_ambiance > 0.f)
- {
- F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma())*2.f;
+ F32 probe_ambiance = LLEnvironment::instance().getCurrentSky()->getReflectionProbeAmbiance(should_auto_adjust);
+ F32 exp_min = 1.f;
+ F32 exp_max = 1.f;
- if (hdr_scale > 1.f)
- {
- exp_min = 1.f / hdr_scale;
- exp_max = hdr_scale;
- }
- }
- gExposureProgram.uniform1f(dt, gFrameIntervalSeconds);
- gExposureProgram.uniform2f(noiseVec, ll_frand() * 2.0 - 1.0, ll_frand() * 2.0 - 1.0);
- gExposureProgram.uniform3f(dynamic_exposure_params, dynamic_exposure_coefficient, exp_min, exp_max);
+ if (probe_ambiance > 0.f)
+ {
+ F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma()) * 2.f;
+
+ if (hdr_scale > 1.f)
+ {
+ exp_min = 1.f / hdr_scale;
+ exp_max = hdr_scale;
+ }
+ }
+ shader->uniform1f(dt, gFrameIntervalSeconds);
+ shader->uniform2f(noiseVec, ll_frand() * 2.0 - 1.0, ll_frand() * 2.0 - 1.0);
+ shader->uniform3f(dynamic_exposure_params, dynamic_exposure_coefficient, exp_min, exp_max);
mScreenTriangleVB->setBuffer();
mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
- gGL.getTexUnit(channel)->unbind(mLastExposure.getUsage());
- gExposureProgram.unbind();
+ if (use_history)
+ {
+ gGL.getTexUnit(channel)->unbind(mLastExposure.getUsage());
+ }
+ shader->unbind();
dst->flush();
}
}
+extern LLPointer<LLImageGL> gEXRImage;
+
void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) {
dst->bindTarget();
// gamma correct lighting
@@ -6842,8 +6966,10 @@ void LLPipeline::gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst) {
F32 e = llclamp(exposure(), 0.5f, 4.f);
static LLStaticHashedString s_exposure("exposure");
+ static LLStaticHashedString aces_mix("aces_mix");
shader.uniform1f(s_exposure, e);
+ shader.uniform1f(aces_mix, gEXRImage.notNull() ? 0.f : 0.3f);
mScreenTriangleVB->setBuffer();
mScreenTriangleVB->drawArrays(LLRender::TRIANGLES, 0, 3);
@@ -7160,7 +7286,7 @@ void LLPipeline::renderDoF(LLRenderTarget* src, LLRenderTarget* dst)
LLVector4a result;
result.clear();
- gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, TRUE, NULL, &result);
+ gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, TRUE, nullptr, nullptr, nullptr, &result);
focus_point.set(result.getF32ptr());
}
@@ -8215,6 +8341,7 @@ void LLPipeline::renderDeferredLighting()
LLPipeline::RENDER_TYPE_CONTROL_AV,
LLPipeline::RENDER_TYPE_ALPHA_MASK,
LLPipeline::RENDER_TYPE_FULLBRIGHT_ALPHA_MASK,
+ LLPipeline::RENDER_TYPE_TERRAIN,
LLPipeline::RENDER_TYPE_WATER,
END_RENDER_TYPES);
@@ -8618,6 +8745,17 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader)
mReflectionMapManager.mIrradianceMaps->bind(channel);
bound = true;
}
+
+ if (RenderMirrors)
+ {
+ channel = shader.enableTexture(LLShaderMgr::HERO_PROBE, LLTexUnit::TT_CUBE_MAP_ARRAY);
+ if (channel > -1 && mHeroProbeManager.mTexture.notNull())
+ {
+ mHeroProbeManager.mTexture->bind(channel);
+ bound = true;
+ }
+ }
+
if (bound)
{
@@ -10832,3 +10970,12 @@ void LLPipeline::rebuildDrawInfo()
}
}
+void LLPipeline::rebuildTerrain()
+{
+ for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
+ iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
+ {
+ LLViewerRegion* region = *iter;
+ region->dirtyAllPatches();
+ }
+}
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 88a7eab813..bfa4009a0b 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -39,6 +39,7 @@
#include "lldrawable.h"
#include "llrendertarget.h"
#include "llreflectionmapmanager.h"
+#include "llheroprobemanager.h"
#include <stack>
@@ -133,6 +134,8 @@ public:
// rebuild all LLVOVolume render batches
void rebuildDrawInfo();
+ // Rebuild all terrain
+ void rebuildTerrain();
// Clear LLFace mVertexBuffer pointers
void resetVertexBuffers(LLDrawable* drawable);
@@ -151,7 +154,7 @@ public:
void renderFinalize();
void copyScreenSpaceReflections(LLRenderTarget* src, LLRenderTarget* dst);
void generateLuminance(LLRenderTarget* src, LLRenderTarget* dst);
- void generateExposure(LLRenderTarget* src, LLRenderTarget* dst);
+ void generateExposure(LLRenderTarget* src, LLRenderTarget* dst, bool use_history = true);
void gammaCorrect(LLRenderTarget* src, LLRenderTarget* dst);
void generateGlow(LLRenderTarget* src);
void applyFXAA(LLRenderTarget* src, LLRenderTarget* dst);
@@ -207,6 +210,8 @@ public:
bool pick_unselectable,
bool pick_reflection_probe,
S32* face_hit, // return the face hit
+ S32* gltf_node_hit = nullptr, // return the gltf node hit
+ S32* gltf_primitive_hit = nullptr, // return the gltf primitive hit
LLVector4a* intersection = NULL, // return the intersection point
LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
LLVector4a* normal = NULL, // return the surface normal at the intersection point
@@ -458,6 +463,7 @@ public:
void handleShadowDetailChanged();
LLReflectionMapManager mReflectionMapManager;
+ LLHeroProbeManager mHeroProbeManager;
private:
void unloadShaders();
@@ -611,12 +617,12 @@ public:
RENDER_DEBUG_PHYSICS_SHAPES = 0x02000000,
RENDER_DEBUG_NORMALS = 0x04000000,
RENDER_DEBUG_LOD_INFO = 0x08000000,
- RENDER_DEBUG_ATTACHMENT_BYTES = 0x20000000, // not used
+ RENDER_DEBUG_NODES = 0x20000000,
RENDER_DEBUG_TEXEL_DENSITY = 0x40000000,
RENDER_DEBUG_TRIANGLE_COUNT = 0x80000000,
RENDER_DEBUG_IMPOSTORS = 0x100000000,
RENDER_DEBUG_REFLECTION_PROBES = 0x200000000,
- RENDER_DEBUG_PROBE_UPDATES = 0x400000000
+ RENDER_DEBUG_PROBE_UPDATES = 0x400000000,
};
public:
@@ -694,8 +700,12 @@ public:
RenderTargetPack mMainRT;
// auxillary 512x512 render target pack
+ // used by reflection probes and dynamic texture bakes
RenderTargetPack mAuxillaryRT;
+ // Auxillary render target pack scaled to the hero probe's per-face size.
+ RenderTargetPack mHeroProbeRT;
+
// currently used render target pack
RenderTargetPack* mRT;
@@ -754,7 +764,7 @@ public:
//water distortion texture (refraction)
LLRenderTarget mWaterDis;
- LLRenderTarget mBake;
+ static const U32 MAX_BAKE_WIDTH;
//texture for making the glow
LLRenderTarget mGlow[3];
@@ -1047,6 +1057,9 @@ public:
static F32 RenderScreenSpaceReflectionAdaptiveStepMultiplier;
static S32 RenderScreenSpaceReflectionGlossySamples;
static S32 RenderBufferVisualization;
+ static bool RenderMirrors;
+ static S32 RenderHeroProbeUpdateRate;
+ static S32 RenderHeroProbeConservativeUpdateMultiplier;
};
void render_bbox(const LLVector3 &min, const LLVector3 &max);
diff --git a/indra/newview/skins/default/xui/de/panel_region_terrain.xml b/indra/newview/skins/default/xui/de/panel_region_terrain.xml
index 42ba5b5269..7738427fe3 100644
--- a/indra/newview/skins/default/xui/de/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/de/panel_region_terrain.xml
@@ -10,7 +10,7 @@
<spinner label="Obere Terraingrenze" name="terrain_raise_spin"/>
<spinner label="Untere Terraingrenze" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Terraintexturen (erfordert 24-Bit-.tga-Dateien mit einer Größe von 1024x1024)
+ Terraintexturen
</text>
<text name="height_text_lbl">
1 (niedrig)
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
index bc237322af..94c889f4e4 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
@@ -335,21 +335,9 @@
name="FSAADisabled"
value="0" />
<combo_box.item
- label="2x"
- name="2x"
+ label="FXAA"
+ name="FXAA"
value="2" />
- <combo_box.item
- label="4x"
- name="4x"
- value="4" />
- <combo_box.item
- label="8x"
- name="8x"
- value="8" />
- <combo_box.item
- label="16x"
- name="16x"
- value="16" />
</combo_box>
<text
type="string"
@@ -357,44 +345,23 @@
follows="left|top"
height="16"
layout="topleft"
- left_pad="10"
- name="antialiasing restart"
- top_delta="0"
- width="130">
- (requires restart)
- </text>
- <view_border
- bevel_style="in"
- height="322"
- layout="topleft"
- left="385"
- name="vert_border"
- top="16"
- width="0"/>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="16"
- layout="topleft"
name="MeshText"
- top_delta="20"
- left="400"
- top="21"
+ top_delta="16"
+ left="10"
width="128">
- Mesh
+ Mesh
</text>
<slider
control_name="RenderTerrainLODFactor"
- follows="left|top"
+ follows="topleft"
height="16"
increment="0.125"
initial_value="160"
label="Terrain Mesh Detail:"
label_width="185"
layout="topleft"
- left="420"
+ left="30"
min_val="1"
max_val="2"
name="TerrainMeshDetail"
@@ -408,7 +375,7 @@
<text
type="string"
length="1"
- follows="left|top"
+ follows="topleft"
height="16"
layout="topleft"
name="TerrainMeshDetailText"
@@ -416,19 +383,19 @@
top_delta="0"
left_delta="304"
width="65">
- Low
+ Low
</text>
<slider
control_name="RenderTreeLODFactor"
- follows="left|top"
+ follows="topleft"
height="16"
increment="0.125"
initial_value="160"
label="Trees:"
label_width="185"
layout="topleft"
- left="420"
+ left="30"
name="TreeMeshDetail"
show_text="false"
top_delta="16"
@@ -447,7 +414,7 @@
top_delta="0"
left_delta="304"
width="65">
- Low
+ Low
</text>
<slider
@@ -459,7 +426,7 @@
label="Objects:"
label_width="185"
layout="topleft"
- left="420"
+ left="30"
min_val="0"
max_val="4"
name="ObjectMeshDetail"
@@ -480,7 +447,7 @@
top_delta="0"
left_delta="304"
width="65">
- Low
+ Low
</text>
<slider
@@ -491,7 +458,7 @@
label="Flexiprims:"
label_width="185"
layout="topleft"
- left="420"
+ left="30"
name="FlexibleMeshDetail"
show_text="false"
top_delta="16"
@@ -510,8 +477,28 @@
top_delta="0"
left_delta="304"
width="65">
- Low
+ Low
</text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left_pad="10"
+ name="antialiasing restart"
+ top_delta="0"
+ width="130">
+ (requires restart)
+ </text>
+ <view_border
+ bevel_style="in"
+ height="322"
+ layout="topleft"
+ left="385"
+ name="vert_border"
+ top="16"
+ width="0"/>
<text
type="string"
@@ -520,7 +507,7 @@
height="16"
layout="topleft"
name="ShadersText"
- top_delta="20"
+ top_delta="-10"
left="400"
width="128">
Shaders
@@ -534,7 +521,7 @@
layout="topleft"
left="420"
name="TransparentWater"
- top_delta="16"
+ top_delta="18"
width="300">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -555,7 +542,7 @@
max_val="128"
name="SkyMeshDetail"
show_text="false"
- top_delta="16"
+ top_delta="22"
width="260">
<slider.commit_callback
function="Pref.UpdateSliderText"
@@ -583,7 +570,7 @@
layout="topleft"
left="420"
name="UseSSAO"
- top_delta="16"
+ top_delta="22"
width="240">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -597,7 +584,7 @@
layout="topleft"
left="420"
name="UseDoF"
- top_delta="16"
+ top_delta="20"
width="240">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -612,7 +599,7 @@
left="420"
name="RenderShadowDetailText"
text_readonly_color="LabelDisabledColor"
- top_delta="16"
+ top_delta="22"
width="128">
Shadows:
</text>
@@ -646,7 +633,7 @@
layout="topleft"
left="420"
name="ScreenSpaceReflections"
- top_delta="16"
+ top_delta="24"
width="240">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -661,7 +648,7 @@
left="420"
name="ReflectionDetailText"
text_readonly_color="LabelDisabledColor"
- top_delta="16"
+ top_delta="22"
width="128">
Reflection Detail:
</text>
@@ -697,7 +684,7 @@
left="420"
name="ReflectionProbeText"
text_readonly_color="LabelDisabledColor"
- top_delta="16"
+ top_delta="22"
width="128">
Reflection Coverage:
</text>
@@ -743,10 +730,106 @@
max_val="1.5"
name="RenderExposure"
show_text="true"
- top_delta="20"
+ top_delta="24"
width="260">
</slider>
+ <!-- Mirror settings. -->
+ <check_box
+ control_name="RenderMirrors"
+ height="16"
+ initial_value="false"
+ label="Mirrors"
+ layout="topleft"
+ left="420"
+ name="Mirrors"
+ top_delta="24"
+ width="240">
+ <check_box.commit_callback
+ function="Pref.RenderOptionUpdate" />
+ </check_box>
+
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="420"
+ name="MirrorResolutionText"
+ text_readonly_color="LabelDisabledColor"
+ top_delta="22"
+ width="128">
+ Mirror Resolution:
+ </text>
+
+ <combo_box
+ control_name="RenderHeroProbeResolution"
+ height="18"
+ layout="topleft"
+ left_delta="130"
+ top_delta="0"
+ name="MirrorResolution"
+ width="150">
+ <combo_box.item
+ label="256"
+ name="0"
+ value="256"/>
+ <combo_box.item
+ label="512"
+ name="1"
+ value="512"/>
+ <combo_box.item
+ label="1024"
+ name="2"
+ value="1024"/>
+ <combo_box.item
+ label="2048"
+ name="3"
+ value="2048"/>
+ </combo_box>
+
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="16"
+ layout="topleft"
+ left="420"
+ name="HeroProbeUpdateText"
+ text_readonly_color="LabelDisabledColor"
+ top_delta="22"
+ width="128">
+ Mirror Update Rate:
+ </text>
+
+ <combo_box
+ control_name="RenderHeroProbeUpdateRate"
+ height="18"
+ layout="topleft"
+ left_delta="130"
+ top_delta="0"
+ name="HeroProbeUpdateRate"
+ width="150">
+ <combo_box.item
+ label="Every Frame"
+ name="0"
+ value="1"/>
+ <combo_box.item
+ label="Every 2nd Frame"
+ name="1"
+ value="2"/>
+ <combo_box.item
+ label="Every 3rd Frame"
+ name="2"
+ value="3"/>
+ <combo_box.item
+ label="Every 4th Frame"
+ name="3"
+ value="4"/>
+ </combo_box>
+ <!-- End of mirror settings -->
+
<!-- End of Advanced Settings block -->
<view_border
bevel_style="in"
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index 490b516ac6..e43143c8c3 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -2410,7 +2410,6 @@ even though the user gets a free copy.
name="object_horizontal"
top_pad="10"
width="278" />
-
<check_box
height="16"
label="Light"
@@ -2550,7 +2549,7 @@ even though the user gets a free copy.
follows="left|top"
name="Probe Volume Type"
tool_tip="Choose the probe influence volume"
- width="108">
+ width="140">
<combo_box.item
label="Sphere"
name="Sphere"
@@ -2560,16 +2559,56 @@ even though the user gets a free copy.
name="Box"
value="Box"/>
</combo_box>
+
<check_box
- height="16"
- label="Dynamic"
+ height="16"
+ label="Dynamic"
+ layout="topleft"
+ left="10"
+ name="Probe Dynamic"
+ tool_tip="When enabled, Avatars will appear in reflections within this probe's influence volume."
+ bottom_delta="19"
+ width="60" />
+
+ <text
+ bottom_delta="-8"
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
layout="topleft"
left="10"
- name="Probe Dynamic"
- tool_tip="When enabled, Avatars will appear in reflections within this probe's influence volume."
- bottom_delta="19"
- width="60" />
- <spinner bottom_delta="19"
+ name="Probe Update Label"
+ text_readonly_color="LabelDisabledColor"
+ width="100">
+ Probe Update
+ </text>
+ <combo_box
+ height="19"
+ top_delta="0"
+ left="144"
+ follows="left|top"
+ name="Probe Update Type"
+ tool_tip="Determines how the probe updates. Static updates the slowest and without avatars. Dynamic updates more frequently, with avatars visible in the probes. Mirror (Environment) turns this probe into a realtime planar projected probe that only reflects the environment, but does not calculate ambiance. Mirror (Everything) is similar to Mirror (Environment), but it reflects particles and avatars."
+ width="140">
+ <combo_box.item
+ label="Static"
+ name="Static"
+ value="Static" />
+ <combo_box.item
+ label="Dynamic"
+ name="Dynamic"
+ value="Dynamic"/>
+ <combo_box.item
+ label="Mirror (Environment)"
+ name="Mirror"
+ value="Mirror"/>
+ <combo_box.item
+ label="Mirror (Everything)"
+ name="Dynamic Mirror"
+ value="Dynamic Mirror"/>
+ </combo_box>
+ <spinner bottom_delta="17"
decimal_digits="3"
follows="left|top"
height="16"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
index 13064db712..ae96289f36 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml
@@ -13,7 +13,7 @@
name="upload"
tear_off="true">
<menu_item_call
- label="Image (L$[COST])..."
+ label="Image..."
layout="topleft"
name="Upload Image"
shortcut="control|U">
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 53f703e56d..644553e64c 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1620,7 +1620,7 @@ function="World.EnvPreset"
name="Upload"
tear_off="true">
<menu_item_call
- label="Image (L$[COST])..."
+ label="Image..."
layout="topleft"
name="Upload Image"
shortcut="control|U">
@@ -2039,7 +2039,57 @@ function="World.EnvPreset"
function="Advanced.ToggleRenderType"
parameter="simple" />
</menu_item_check>
- <menu_item_check
+ <menu_item_check
+ label="Materials"
+ name="Rendering Type Materials">
+ <menu_item_check.on_check
+ function="Advanced.CheckRenderType"
+ parameter="materials" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleRenderType"
+ parameter="materials" />
+ </menu_item_check>
+ <menu_item_check
+ label="Alpha Mask"
+ name="Rendering Type Alpha Mask">
+ <menu_item_check.on_check
+ function="Advanced.CheckRenderType"
+ parameter="alpha_mask" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleRenderType"
+ parameter="alpha_mask" />
+ </menu_item_check>
+ <menu_item_check
+ label="Fullbright Alpha Mask"
+ name="Rendering Type Fullbright Alpha Mask">
+ <menu_item_check.on_check
+ function="Advanced.CheckRenderType"
+ parameter="fullbright_alpha_mask" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleRenderType"
+ parameter="fullbright_alpha_mask" />
+ </menu_item_check>
+ <menu_item_check
+ label="Glow"
+ name="Rendering Type Glow">
+ <menu_item_check.on_check
+ function="Advanced.CheckRenderType"
+ parameter="glow" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleRenderType"
+ parameter="glow" />
+ </menu_item_check>
+ <menu_item_check
+ label="Fullbright"
+ name="Rendering Type Glow">
+ <menu_item_check.on_check
+ function="Advanced.CheckRenderType"
+ parameter="fullbright" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleRenderType"
+ parameter="fullbright" />
+ </menu_item_check>
+ <menu_item_check
label="Alpha"
name="Rendering Type Alpha"
shortcut="control|alt|shift|2">
@@ -2840,6 +2890,18 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClickRenderBenchmark" />
</menu_item_call>
+ <menu_item_call
+ label="HDRI Preview"
+ name="HDRI Preview">
+ <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"
@@ -2877,6 +2939,16 @@ function="World.EnvPreset"
parameter="octree" />
</menu_item_check>
<menu_item_check
+ label="GLTF Nodes"
+ name="GLTF Nodes">
+ <menu_item_check.on_check
+ function="Advanced.CheckInfoDisplay"
+ parameter="nodes" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleInfoDisplay"
+ parameter="nodes" />
+ </menu_item_check>
+ <menu_item_check
label="Shadow Frusta"
name="Shadow Frusta">
<menu_item_check.on_check
@@ -3319,6 +3391,13 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClearShaderCache" />
</menu_item_call>
+ <menu_item_call
+ enabled="true"
+ label="Rebuild Terrain"
+ name="Rebuild Terrain">
+ <menu_item_call.on_click
+ function="Advanced.RebuildTerrain" />
+ </menu_item_call>
<menu_item_separator />
<menu_item_call
enabled="true"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 3e1b1c4a67..ad1f12002e 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -4002,10 +4002,39 @@ Are you sure you want to return objects owned by [USER_NAME]?
icon="alertmodal.tga"
name="InvalidTerrainBitDepth"
type="alertmodal">
+ <unique combine="cancel_old" />
Couldn&apos;t set region textures:
Terrain texture [TEXTURE_NUM] has an invalid bit depth of [TEXTURE_BIT_DEPTH].
-Replace texture [TEXTURE_NUM] with a 24-bit [MAX_SIZE]x[MAX_SIZE] or smaller image then click &quot;Apply&quot; again.
+Replace texture [TEXTURE_NUM] with an RGB [MAX_SIZE]x[MAX_SIZE] or smaller image then click &quot;Apply&quot; again.
+ <tag>fail</tag>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="InvalidTerrainAlphaNotFullyLoaded"
+ type="alertmodal">
+ <unique combine="cancel_old" />
+Couldn&apos;t set region textures:
+Terrain texture [TEXTURE_NUM] is not fully loaded, but is assumed to contain transparency due to a bit depth of [TEXTURE_BIT_DEPTH]. Transparency is not currently supported for terrain textures.
+
+If texture [TEXTURE_NUM] is opaque, wait for the texture to fully load and then click &quot;Apply&quot; again.
+
+Alpha is only supported for terrain materials (PBR Metallic Roughness), when alphaMode="MASK" and doubleSided=false.
+ <tag>fail</tag>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="InvalidTerrainAlpha"
+ type="alertmodal">
+ <unique combine="cancel_old" />
+Couldn&apos;t set region textures:
+Terrain texture [TEXTURE_NUM] contains transparency. Transparency is not currently supported for terrain textures.
+
+Replace texture [TEXTURE_NUM] with an opaque RGB image, then click &quot;Apply&quot; again.
+
+Alpha is only supported for terrain materials (PBR Metallic Roughness), when alphaMode="MASK" and doubleSided=false.
<tag>fail</tag>
</notification>
@@ -4013,10 +4042,55 @@ Replace texture [TEXTURE_NUM] with a 24-bit [MAX_SIZE]x[MAX_SIZE] or smaller ima
icon="alertmodal.tga"
name="InvalidTerrainSize"
type="alertmodal">
+ <unique combine="cancel_old" />
Couldn&apos;t set region textures:
Terrain texture [TEXTURE_NUM] is too large at [TEXTURE_SIZE_X]x[TEXTURE_SIZE_Y].
-Replace texture [TEXTURE_NUM] with a 24-bit [MAX_SIZE]x[MAX_SIZE] or smaller image then click &quot;Apply&quot; again.
+Replace texture [TEXTURE_NUM] with an RGB [MAX_SIZE]x[MAX_SIZE] or smaller image then click &quot;Apply&quot; again.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="InvalidTerrainMaterialNotLoaded"
+ type="alertmodal">
+ <unique combine="cancel_old" />
+Couldn&apos;t set region materials:
+Terrain material [MATERIAL_NUM] is not loaded.
+
+Wait for the material to load, or replace material [MATERIAL_NUM] with a valid material.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="InvalidTerrainMaterialLoadFailed"
+ type="alertmodal">
+ <unique combine="cancel_old" />
+Couldn&apos;t set region materials:
+Terrain material [MATERIAL_NUM] failed to load.
+
+Replace material [MATERIAL_NUM] with a valid material.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="InvalidTerrainMaterialDoubleSided"
+ type="alertmodal">
+ <unique combine="cancel_old" />
+Couldn&apos;t set region materials:
+Terrain material [MATERIAL_NUM] is double-sided. Double-sided materials are not currently supported for PBR terrain.
+
+Replace material [MATERIAL_NUM] with a material with doubleSided=false.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="InvalidTerrainMaterialAlphaMode"
+ type="alertmodal">
+ <unique combine="cancel_old" />
+Couldn&apos;t set region materials:
+Terrain material [MATERIAL_NUM] is using the unsupported alphaMode="[MATERIAL_ALPHA_MODE]".
+
+Replace material [MATERIAL_NUM] with a material with alphaMode="OPAQUE" or alphaMode="MASK".
</notification>
<notification
@@ -9253,6 +9327,15 @@ Unable to upload texture: &apos;[NAME]&apos;
</notification>
<notification
+ icon="alertmodal.tga"
+ name="CannotLoad"
+ type="alertmodal">
+ Unable to load [WHAT].
+ [REASON]
+ <tag>fail</tag>
+ </notification>
+
+ <notification
icon="alertmodal.tga"
name="CannotUploadMaterial"
type="alertmodal">
@@ -12322,5 +12405,16 @@ are wearing now.
name="okignore"
yestext="OK"/>
</notification>
-
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFPreviewSelection"
+ type="alert">
+ You must select an object to act as a handle to the GLTF asset you are previewing.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
</notifications>
diff --git a/indra/newview/skins/default/xui/en/panel_region_terrain.xml b/indra/newview/skins/default/xui/en/panel_region_terrain.xml
index ad41691323..73e0a1000f 100644
--- a/indra/newview/skins/default/xui/en/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/en/panel_region_terrain.xml
@@ -79,26 +79,55 @@
<text
type="string"
length="1"
+ halign="left"
+ valign="center"
follows="left|top"
height="20"
layout="topleft"
- left="10"
name="detail_texture_text"
- top="110"
- width="300">
- Terrain Textures (requires 1024x1024, 24 bit .tga files)
+ left="10"
+ top="105"
+ 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="105"
+ 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="100"
layout="topleft"
- left_delta="0"
+ left="10"
name="texture_detail_0"
default_image_id="0bc58228-74a0-7e83-89bc-5c23464bcec5"
- top_delta="20"
+ top_delta="30"
width="100" />
<texture_picker
- follows="left|top"
+ follows="top"
height="100"
layout="topleft"
left_pad="10"
@@ -124,6 +153,50 @@
default_image_id="53a2f406-4895-1d13-d541-d2e3b86bc19c"
top_delta="0"
width="100" />
+ <texture_picker
+ visible="false"
+ follows="left|top"
+ height="100"
+ 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="100"
+ 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="100"
+ 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="100"
+ 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"
@@ -185,6 +258,19 @@
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"
@@ -196,6 +282,18 @@
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"
@@ -207,6 +305,18 @@
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"
diff --git a/indra/newview/skins/default/xui/es/panel_region_terrain.xml b/indra/newview/skins/default/xui/es/panel_region_terrain.xml
index 9aba5299cb..5e6e89893e 100644
--- a/indra/newview/skins/default/xui/es/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/es/panel_region_terrain.xml
@@ -12,7 +12,7 @@ del terreno" name="terrain_raise_spin"/>
<spinner bottom_delta="-34" label="Límite de bajada del
terreno" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Texturas del terreno (requiere archivos .tga de 1024x1024, 24 bits)
+ Texturas del terreno
</text>
<text name="height_text_lbl">
1 (bajo)
diff --git a/indra/newview/skins/default/xui/fr/panel_region_terrain.xml b/indra/newview/skins/default/xui/fr/panel_region_terrain.xml
index bbab00ca24..d99948804a 100644
--- a/indra/newview/skins/default/xui/fr/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/fr/panel_region_terrain.xml
@@ -12,7 +12,7 @@ terrain" name="terrain_raise_spin"/>
<spinner bottom_delta="-34" label="Limite d&apos;abaissement
du terrain" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Textures du terrain (fichiers .tga 1024 x 1024, 24 bit requis)
+ Textures du terrain
</text>
<text name="height_text_lbl">
1 (Bas)
diff --git a/indra/newview/skins/default/xui/it/panel_region_terrain.xml b/indra/newview/skins/default/xui/it/panel_region_terrain.xml
index e08c55f63b..0583b4e02e 100644
--- a/indra/newview/skins/default/xui/it/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/it/panel_region_terrain.xml
@@ -12,7 +12,7 @@ terreno" name="terrain_raise_spin"/>
<spinner bottom_delta="-34" label="Limite di abbassamento
del terreno" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Texture terreno (richiede file 1024x1024, 24 bit .tga)
+ Texture terreno
</text>
<text name="height_text_lbl">
1 (basso)
diff --git a/indra/newview/skins/default/xui/ja/panel_region_terrain.xml b/indra/newview/skins/default/xui/ja/panel_region_terrain.xml
index 5470bd6e3b..11e8d0d169 100644
--- a/indra/newview/skins/default/xui/ja/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/ja/panel_region_terrain.xml
@@ -10,7 +10,7 @@
<spinner label="地形の上昇限度" name="terrain_raise_spin"/>
<spinner label="地形の下降限度" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- 地形テクスチャ(1024✕1024 の 24 bit .tga ファイル)
+ 地形テクスチャ
</text>
<text name="height_text_lbl">
1(低)
diff --git a/indra/newview/skins/default/xui/pl/panel_region_terrain.xml b/indra/newview/skins/default/xui/pl/panel_region_terrain.xml
index 2d4286334f..c1dde04ff0 100644
--- a/indra/newview/skins/default/xui/pl/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/pl/panel_region_terrain.xml
@@ -7,7 +7,7 @@
<spinner label="Górny limit terenu" name="terrain_raise_spin" />
<spinner label="Dolny limit terenu" name="terrain_lower_spin" />
<text name="detail_texture_text">
- Tekstury terenu (1024x1024, 24 bitowy plik .tga)
+ Tekstury terenu
</text>
<text name="height_text_lbl">
1 (Nisko)
diff --git a/indra/newview/skins/default/xui/pt/panel_region_terrain.xml b/indra/newview/skins/default/xui/pt/panel_region_terrain.xml
index 1d312aeed9..92d6859882 100644
--- a/indra/newview/skins/default/xui/pt/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/pt/panel_region_terrain.xml
@@ -12,7 +12,7 @@ terreno" name="terrain_raise_spin"/>
<spinner bottom_delta="-34" label="Limite mais baixo do
terreno" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Texturas de terreno (exige arquivos .tga 1024x1024, 24 bit)
+ Texturas de terreno
</text>
<text name="height_text_lbl">
1 (Baixo)
diff --git a/indra/newview/skins/default/xui/ru/panel_region_terrain.xml b/indra/newview/skins/default/xui/ru/panel_region_terrain.xml
index 76b4f513a8..2752d409cb 100644
--- a/indra/newview/skins/default/xui/ru/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/ru/panel_region_terrain.xml
@@ -10,7 +10,7 @@
<spinner label="Верх. точка ландшафта" name="terrain_raise_spin"/>
<spinner label="Ниж. точка ландшафта" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Текстуры ландшафта (требования: 1024x1024, 24-битные, TGA)
+ Текстуры ландшафта
</text>
<text name="height_text_lbl">
1 (Низ)
diff --git a/indra/newview/skins/default/xui/tr/panel_region_terrain.xml b/indra/newview/skins/default/xui/tr/panel_region_terrain.xml
index e25047301d..00560d4c5b 100644
--- a/indra/newview/skins/default/xui/tr/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/tr/panel_region_terrain.xml
@@ -10,7 +10,7 @@
<spinner label="Yüzey Yükslt. Limiti" name="terrain_raise_spin"/>
<spinner label="Yüzey Alçatma Limiti" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- Yüzey Dokuları (1024x1024, 24 bit .tga dosyalar gerektirir)
+ Yüzey Dokuları
</text>
<text name="height_text_lbl">
1 (Düşük)
diff --git a/indra/newview/skins/default/xui/zh/panel_region_terrain.xml b/indra/newview/skins/default/xui/zh/panel_region_terrain.xml
index 81bce46876..8490eeaafc 100644
--- a/indra/newview/skins/default/xui/zh/panel_region_terrain.xml
+++ b/indra/newview/skins/default/xui/zh/panel_region_terrain.xml
@@ -10,7 +10,7 @@
<spinner label="地形提升限制" name="terrain_raise_spin"/>
<spinner label="地形降低限制" name="terrain_lower_spin"/>
<text name="detail_texture_text">
- 地形材質(須 1024x1024,24 位元 .tga 檔格式)
+ 地形材質
</text>
<text name="height_text_lbl">
1(低)