summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/CMakeLists.txt2
-rw-r--r--indra/cmake/GLM.cmake7
-rw-r--r--indra/cmake/TinyEXR.cmake7
-rw-r--r--indra/llappearance/llavatarappearance.cpp87
-rw-r--r--indra/llappearance/llavatarappearance.h2
-rw-r--r--indra/llcommon/indra_constants.cpp1
-rw-r--r--indra/llcommon/indra_constants.h2
-rw-r--r--indra/llcommon/llapp.cpp6
-rw-r--r--indra/llcommon/llassettype.cpp2
-rw-r--r--indra/llcommon/llassettype.h4
-rw-r--r--indra/llcommon/llcommon.cpp2
-rw-r--r--indra/llcommon/llcoros.cpp27
-rw-r--r--indra/llcommon/llcoros.h22
-rw-r--r--indra/llcommon/llerror.cpp12
-rw-r--r--indra/llcommon/llevents.cpp36
-rw-r--r--indra/llcommon/llevents.h3
-rw-r--r--indra/llcommon/llfixedbuffer.h3
-rw-r--r--indra/llcommon/llmutex.cpp16
-rw-r--r--indra/llcommon/llprofiler.h2
-rw-r--r--indra/llcommon/llrefcount.h7
-rw-r--r--indra/llcommon/llstrider.h2
-rw-r--r--indra/llcommon/llthread.cpp24
-rw-r--r--indra/llcommon/lluuid.h4
-rw-r--r--indra/llcommon/tests/threadsafeschedule_test.cpp6
-rw-r--r--indra/llfilesystem/lldiskcache.cpp2
-rw-r--r--indra/llimage/llimage.cpp199
-rw-r--r--indra/llimage/llimage.h25
-rw-r--r--indra/llinventory/llinventorytype.cpp7
-rw-r--r--indra/llinventory/llinventorytype.h4
-rw-r--r--indra/llinventory/llpermissions.h32
-rw-r--r--indra/llinventory/llsettingssky.cpp28
-rw-r--r--indra/llinventory/llsettingssky.h21
-rw-r--r--indra/llmath/llcamera.h1
-rw-r--r--indra/llmath/llmatrix4a.h38
-rw-r--r--indra/llmath/llvector4a.h43
-rw-r--r--indra/llmath/llvolume.cpp189
-rw-r--r--indra/llmath/llvolume.h13
-rw-r--r--indra/llmath/llvolumeoctree.cpp14
-rw-r--r--indra/llmath/llvolumeoctree.h101
-rw-r--r--indra/llmessage/llavatarnamecache.cpp32
-rw-r--r--indra/llplugin/llpluginprocessparent.cpp19
-rw-r--r--indra/llplugin/llpluginprocessparent.h2
-rw-r--r--indra/llprimitive/CMakeLists.txt1
-rw-r--r--indra/llprimitive/llgltfmaterial.cpp15
-rw-r--r--indra/llprimitive/llgltfmaterial.h39
-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/tests/llgltfmaterial_test.cpp90
-rw-r--r--indra/llrender/llgl.cpp5
-rw-r--r--indra/llrender/llgl.h16
-rw-r--r--indra/llrender/llglslshader.cpp98
-rw-r--r--indra/llrender/llglslshader.h41
-rw-r--r--indra/llrender/llgltexture.cpp7
-rw-r--r--indra/llrender/llgltexture.h5
-rw-r--r--indra/llrender/llrender.cpp32
-rw-r--r--indra/llrender/llrendertarget.cpp27
-rw-r--r--indra/llrender/llrendertarget.h3
-rw-r--r--indra/llrender/llshadermgr.cpp96
-rw-r--r--indra/llrender/llshadermgr.h41
-rw-r--r--indra/llrender/llvertexbuffer.cpp245
-rw-r--r--indra/llrender/llvertexbuffer.h16
-rw-r--r--indra/llui/llconsole.cpp2
-rw-r--r--indra/llui/llui.h5
-rw-r--r--indra/newview/CMakeLists.txt19
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml604
-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.glsl32
-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.glsl75
-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.glsl22
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl20
-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.glsl34
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/materialV.glsl17
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/moonF.glsl16
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaMaskF.glsl14
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl4
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl34
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl401
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl468
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl175
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl13
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyF.glsl70
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/skyV.glsl16
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl27
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl36
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl49
-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/environment/srgbF.glsl2
-rw-r--r--indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl300
-rw-r--r--indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl171
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl (renamed from indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl)19
-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.glsl14
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl5
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl35
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl22
-rw-r--r--indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl26
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl3
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/hazeF.glsl13
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/materialF.glsl31
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl13
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl29
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl206
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl15
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl39
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl50
-rw-r--r--indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl5
-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/README.md156
-rw-r--r--indra/newview/gltf/accessor.cpp297
-rw-r--r--indra/newview/gltf/accessor.h120
-rw-r--r--indra/newview/gltf/animation.cpp460
-rw-r--r--indra/newview/gltf/animation.h162
-rw-r--r--indra/newview/gltf/asset.cpp1137
-rw-r--r--indra/newview/gltf/asset.h399
-rw-r--r--indra/newview/gltf/buffer_util.h1024
-rw-r--r--indra/newview/gltf/common.h83
-rw-r--r--indra/newview/gltf/primitive.cpp685
-rw-r--r--indra/newview/gltf/primitive.h104
-rw-r--r--indra/newview/gltfscenemanager.cpp1139
-rw-r--r--indra/newview/gltfscenemanager.h100
-rw-r--r--indra/newview/llagentbenefits.cpp64
-rw-r--r--indra/newview/llagentbenefits.h9
-rw-r--r--indra/newview/llagentcamera.cpp2
-rw-r--r--indra/newview/llappviewer.cpp13
-rw-r--r--indra/newview/llappviewer.h3
-rw-r--r--indra/newview/llavatarrenderinfoaccountant.cpp2
-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.cpp30
-rw-r--r--indra/newview/lldrawpoolavatar.cpp4
-rw-r--r--indra/newview/lldrawpoolbump.cpp9
-rw-r--r--indra/newview/lldrawpoolpbropaque.cpp4
-rw-r--r--indra/newview/lldrawpoolsimple.cpp57
-rw-r--r--indra/newview/lldrawpoolterrain.cpp322
-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.cpp28
-rw-r--r--indra/newview/llenvironment.cpp22
-rw-r--r--indra/newview/llface.cpp19
-rw-r--r--indra/newview/llfeaturemanager.cpp4
-rw-r--r--indra/newview/llfetchedgltfmaterial.cpp66
-rw-r--r--indra/newview/llfetchedgltfmaterial.h19
-rw-r--r--indra/newview/llfilepicker.cpp15
-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.cpp527
-rw-r--r--indra/newview/llfloaterregioninfo.h173
-rw-r--r--indra/newview/llglsandbox.cpp2
-rw-r--r--indra/newview/llgltfmateriallist.cpp7
-rw-r--r--indra/newview/llgltfmateriallist.h6
-rw-r--r--indra/newview/llgltfmaterialpreviewmgr.cpp590
-rw-r--r--indra/newview/llgltfmaterialpreviewmgr.h78
-rw-r--r--indra/newview/llheroprobemanager.cpp628
-rw-r--r--indra/newview/llheroprobemanager.h159
-rw-r--r--indra/newview/llimprocessing.cpp40
-rw-r--r--indra/newview/llinventoryfunctions.cpp6
-rw-r--r--indra/newview/llinventorymodel.cpp8
-rw-r--r--indra/newview/lllocalbitmaps.cpp19
-rw-r--r--indra/newview/lllocalbitmaps.h1
-rw-r--r--indra/newview/lllocalgltfmaterials.cpp6
-rw-r--r--indra/newview/llmanip.cpp8
-rw-r--r--indra/newview/llmaniprotate.cpp251
-rw-r--r--indra/newview/llmaniptranslate.cpp174
-rw-r--r--indra/newview/llmaterialeditor.cpp69
-rw-r--r--indra/newview/llmaterialeditor.h1
-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.cpp57
-rw-r--r--indra/newview/llpbrterrainfeatures.cpp198
-rw-r--r--indra/newview/llpbrterrainfeatures.h48
-rw-r--r--indra/newview/llreflectionmap.cpp23
-rw-r--r--indra/newview/llreflectionmap.h19
-rw-r--r--indra/newview/llreflectionmapmanager.cpp155
-rw-r--r--indra/newview/llreflectionmapmanager.h16
-rw-r--r--indra/newview/llselectmgr.cpp96
-rw-r--r--indra/newview/llselectmgr.h9
-rw-r--r--indra/newview/llsettingsvo.cpp35
-rw-r--r--indra/newview/llsidepaneltaskinfo.cpp1
-rw-r--r--indra/newview/llskinningutil.cpp6
-rw-r--r--indra/newview/llskinningutil.h1
-rw-r--r--indra/newview/llspatialpartition.cpp183
-rw-r--r--indra/newview/llstartup.cpp14
-rw-r--r--indra/newview/llsurface.cpp6
-rw-r--r--indra/newview/llsurface.h4
-rw-r--r--indra/newview/llsurfacepatch.cpp203
-rw-r--r--indra/newview/llsurfacepatch.h13
-rw-r--r--indra/newview/lltexturectrl.cpp134
-rw-r--r--indra/newview/lltexturectrl.h59
-rw-r--r--indra/newview/lltinygltfhelper.cpp98
-rw-r--r--indra/newview/lltinygltfhelper.h10
-rw-r--r--indra/newview/lltooldraganddrop.cpp102
-rw-r--r--indra/newview/lltoolselect.cpp2
-rw-r--r--indra/newview/llviewerassettype.cpp2
-rw-r--r--indra/newview/llviewerassetupload.cpp10
-rw-r--r--indra/newview/llviewercamera.cpp6
-rw-r--r--indra/newview/llviewercontrol.cpp89
-rw-r--r--indra/newview/llviewerdisplay.cpp9
-rw-r--r--indra/newview/llviewermedia.cpp12
-rw-r--r--indra/newview/llviewermedia.h4
-rw-r--r--indra/newview/llviewermenu.cpp148
-rw-r--r--indra/newview/llviewermenufile.cpp85
-rw-r--r--indra/newview/llviewermessage.cpp186
-rw-r--r--indra/newview/llviewerobject.cpp199
-rw-r--r--indra/newview/llviewerobject.h29
-rwxr-xr-xindra/newview/llviewerregion.cpp157
-rw-r--r--indra/newview/llviewerregion.h6
-rw-r--r--indra/newview/llviewershadermgr.cpp565
-rw-r--r--indra/newview/llviewershadermgr.h35
-rw-r--r--indra/newview/llviewertexture.cpp28
-rw-r--r--indra/newview/llviewertexture.h12
-rw-r--r--indra/newview/llviewertexturelist.cpp153
-rw-r--r--indra/newview/llviewertexturelist.h3
-rw-r--r--indra/newview/llviewerwindow.cpp33
-rw-r--r--indra/newview/llviewerwindow.h7
-rw-r--r--indra/newview/llvlcomposition.cpp743
-rw-r--r--indra/newview/llvlcomposition.h109
-rw-r--r--indra/newview/llvoavatar.cpp7
-rw-r--r--indra/newview/llvoavatarself.cpp2
-rw-r--r--indra/newview/llvocache.cpp258
-rw-r--r--indra/newview/llvocache.h13
-rw-r--r--indra/newview/llvoicevivox.cpp48
-rw-r--r--indra/newview/llvosurfacepatch.cpp155
-rw-r--r--indra/newview/llvosurfacepatch.h2
-rw-r--r--indra/newview/llvovolume.cpp90
-rw-r--r--indra/newview/llvovolume.h2
-rw-r--r--indra/newview/llxmlrpclistener.cpp94
-rw-r--r--indra/newview/pipeline.cpp203
-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.xml141
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml155
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_terrain.xml124
-rw-r--r--indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml263
-rw-r--r--indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml307
-rw-r--r--indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml365
-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
275 files changed, 18672 insertions, 2882 deletions
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index f2f081ecb0..32ce6fab92 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -52,6 +52,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/GLM.cmake b/indra/cmake/GLM.cmake
new file mode 100644
index 0000000000..84b155f6c5
--- /dev/null
+++ b/indra/cmake/GLM.cmake
@@ -0,0 +1,7 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+add_library( ll::glm INTERFACE IMPORTED )
+
+use_system_binary( glm )
+use_prebuilt_binary(glm)
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/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index de18fce0f8..95d55c835f 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -482,45 +482,70 @@ void LLAvatarAppearance::computeBodySize()
mCurrBodySizeState["mAnkleLeft scale"] = mAnkleLeftp->getScale();
mCurrBodySizeState["mFootLeft pos"] = mFootLeftp->getPosition();
- F32 old_height = mBodySize.mV[VZ];
+ LLVector3 pelvis_scale = mPelvisp->getScale();
+
+ // some of the joints have not been cached
+ LLVector3 skull = mSkullp->getPosition();
+ //LLVector3 skull_scale = mSkullp->getScale();
+
+ LLVector3 neck = mNeckp->getPosition();
+ LLVector3 neck_scale = mNeckp->getScale();
+
+ LLVector3 chest = mChestp->getPosition();
+ LLVector3 chest_scale = mChestp->getScale();
+
+ // the rest of the joints have been cached
+ LLVector3 head = mHeadp->getPosition();
+ LLVector3 head_scale = mHeadp->getScale();
+
+ LLVector3 torso = mTorsop->getPosition();
+ LLVector3 torso_scale = mTorsop->getScale();
+
+ LLVector3 hip = mHipLeftp->getPosition();
+ LLVector3 hip_scale = mHipLeftp->getScale();
+
+ LLVector3 knee = mKneeLeftp->getPosition();
+ LLVector3 knee_scale = mKneeLeftp->getScale();
+
+ LLVector3 ankle = mAnkleLeftp->getPosition();
+ LLVector3 ankle_scale = mAnkleLeftp->getScale();
+
+ LLVector3 foot = mFootLeftp->getPosition();
+
F32 old_offset = mAvatarOffset.mV[VZ];
- // TODO: Measure the real depth and width
- mPelvisToFoot = computePelvisToFoot();
- F32 new_height = computeBodyHeight();
- mBodySize.set(DEFAULT_AGENT_DEPTH, DEFAULT_AGENT_WIDTH, new_height);
- F32 new_offset = getVisualParamWeight(AVATAR_HOVER);
- mAvatarOffset.set(0, 0, new_offset);
+ mAvatarOffset.mV[VZ] = getVisualParamWeight(AVATAR_HOVER);
+
+ mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
+ knee.mV[VZ] * hip_scale.mV[VZ] -
+ ankle.mV[VZ] * knee_scale.mV[VZ] -
+ foot.mV[VZ] * ankle_scale.mV[VZ];
- if (mBodySize.mV[VZ] != old_height || new_offset != old_offset)
+ LLVector3 new_body_size;
+ new_body_size.mV[VZ] = mPelvisToFoot +
+ // the sqrt(2) correction below is an approximate
+ // correction to get to the top of the head
+ F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) +
+ head.mV[VZ] * neck_scale.mV[VZ] +
+ neck.mV[VZ] * chest_scale.mV[VZ] +
+ chest.mV[VZ] * torso_scale.mV[VZ] +
+ torso.mV[VZ] * pelvis_scale.mV[VZ];
+
+ // TODO -- measure the real depth and width
+ new_body_size.mV[VX] = DEFAULT_AGENT_DEPTH;
+ new_body_size.mV[VY] = DEFAULT_AGENT_WIDTH;
+
+ mAvatarOffset.mV[VX] = 0.0f;
+ mAvatarOffset.mV[VY] = 0.0f;
+
+ if (new_body_size != mBodySize || old_offset != mAvatarOffset.mV[VZ])
{
+ mBodySize = new_body_size;
+
compareJointStateMaps(mLastBodySizeState, mCurrBodySizeState);
}
}
-F32 LLAvatarAppearance::computeBodyHeight()
-{
- F32 result = mPelvisToFoot +
- // all these relative positions usually are positive
- mPelvisp->getScale().mV[VZ] * mTorsop->getPosition().mV[VZ] +
- mTorsop->getScale().mV[VZ] * mChestp->getPosition().mV[VZ] +
- mChestp->getScale().mV[VZ] * mNeckp->getPosition().mV[VZ] +
- mNeckp->getScale().mV[VZ] * mHeadp->getPosition().mV[VZ] +
- mHeadp->getScale().mV[VZ] * mSkullp->getPosition().mV[VZ] * 2;
- return result;
-}
-
-F32 LLAvatarAppearance::computePelvisToFoot()
-{
- F32 result =
- // all these relative positions usually are negative
- mPelvisp->getScale().mV[VZ] * mHipLeftp->getPosition().mV[VZ] +
- mHipLeftp->getScale().mV[VZ] * mKneeLeftp->getPosition().mV[VZ] +
- mKneeLeftp->getScale().mV[VZ] * mAnkleLeftp->getPosition().mV[VZ] +
- mAnkleLeftp->getScale().mV[VZ] * mFootLeftp->getPosition().mV[VZ] / 2;
- return -result;
-}
-
//-----------------------------------------------------------------------------
// parseSkeletonFile()
//-----------------------------------------------------------------------------
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index 1f33ae290e..13e504e639 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -147,8 +147,6 @@ public:
void compareJointStateMaps(joint_state_map_t& last_state,
joint_state_map_t& curr_state);
void computeBodySize();
- F32 computeBodyHeight();
- F32 computePelvisToFoot();
public:
typedef std::vector<LLAvatarJoint*> avatar_joint_list_t;
diff --git a/indra/llcommon/indra_constants.cpp b/indra/llcommon/indra_constants.cpp
index d24a221671..329dfcbe37 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 811313e56e..d2de88ff0a 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/llapp.cpp b/indra/llcommon/llapp.cpp
index 99ca0f740a..b85bd2573b 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -586,9 +586,10 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
switch (signum)
{
case SIGCHLD:
+ case SIGHUP:
if (LLApp::sLogInSignal)
{
- LL_INFOS() << "Signal handler - Got SIGCHLD from " << info->si_pid << LL_ENDL;
+ LL_INFOS() << "Signal handler - Got SIGCHLD or SIGHUP from " << info->si_pid << LL_ENDL;
}
return;
@@ -603,11 +604,10 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
raise(signum);
return;
case SIGINT:
- case SIGHUP:
case SIGTERM:
if (LLApp::sLogInSignal)
{
- LL_WARNS() << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << LL_ENDL;
+ LL_WARNS() << "Signal handler - Got SIGINT, or TERM, exiting gracefully" << LL_ENDL;
}
// Graceful exit
// Just set our state to quitting, not error
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index fe8510468a..c09cf7abd2 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -97,6 +97,8 @@ LLAssetDictionary::LLAssetDictionary()
addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false));
addEntry(LLAssetType::AT_SETTINGS, new AssetEntry("SETTINGS", "settings", "settings blob", true, true, true));
addEntry(LLAssetType::AT_MATERIAL, new AssetEntry("MATERIAL", "material", "render material", true, true, true));
+ addEntry(LLAssetType::AT_GLTF, new AssetEntry("GLTF", "gltf", "GLTF", true, true, true));
+ addEntry(LLAssetType::AT_GLTF_BIN, new AssetEntry("GLTF_BIN", "glbin", "GLTF binary", true, true, true));
addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false));
addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false));
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index 1989155550..547c3f4329 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -128,8 +128,10 @@ public:
AT_SETTINGS = 56, // Collection of settings
AT_MATERIAL = 57, // Render Material
+ AT_GLTF = 58, // gltf json document
+ AT_GLTF_BIN = 59, // gltf binary data
- AT_COUNT = 58,
+ AT_COUNT = 60,
// +*********************************************************+
// | TO ADD AN ELEMENT TO THIS ENUM: |
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index d22f26ff62..f1f3958fe0 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -128,7 +128,6 @@ void LLCommon::initClass()
sAprInitialized = true;
}
LLTimer::initClass();
- LLThreadSafeRefCount::initThreadSafeRefCount();
assert_main_thread(); // Make sure we record the main thread
if (!sMasterThreadRecorder)
{
@@ -143,7 +142,6 @@ void LLCommon::cleanupClass()
delete sMasterThreadRecorder;
sMasterThreadRecorder = NULL;
LLTrace::set_master_thread_recorder(NULL);
- LLThreadSafeRefCount::cleanupThreadSafeRefCount();
SUBSYSTEM_CLEANUP_DBG(LLTimer);
if (sAprInitialized)
{
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index aa8eca7d90..23b30bcc57 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2009-06-03
* @brief Implementation for llcoros.
- *
+ *
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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$
*/
@@ -61,6 +61,23 @@
#include <excpt.h>
#endif
+// static
+bool LLCoros::on_main_coro()
+{
+ if (!LLCoros::instanceExists() || LLCoros::getName().empty())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// static
+bool LLCoros::on_main_thread_main_coro()
+{
+ return on_main_coro() && on_main_thread();
+}
+
// static
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
{
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 71c1c1c443..00650a2454 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2009-06-02
* @brief Manage running boost::coroutine instances
- *
+ *
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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$
*/
@@ -94,6 +94,16 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
void cleanupSingleton() override;
public:
+ // For debugging, return true if on the main coroutine for the current thread
+ // Code that should not be executed from a coroutine should be protected by
+ // llassert(LLCoros::on_main_coro())
+ static bool on_main_coro();
+
+ // For debugging, return true if on the main thread and not in a coroutine
+ // Non-thread-safe code in the main loop should be protected by
+ // llassert(LLCoros::on_main_thread_main_coro())
+ static bool on_main_thread_main_coro();
+
/// The viewer's use of the term "coroutine" became deeply embedded before
/// the industry term "fiber" emerged to distinguish userland threads from
/// simpler, more transient kinds of coroutines. Semantically they've
@@ -158,7 +168,7 @@ public:
* LLCoros::launch()).
*/
static std::string getName();
-
+
/**
* rethrow() is called by the thread's main fiber to propagate an
* exception from any coroutine into the main fiber, where it can engage
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 94aee26df6..a45bc32028 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -506,7 +506,7 @@ namespace
LLError::TimeFunction mTimeFunction;
Recorders mRecorders;
- LLMutex mRecorderMutex;
+ LLCoros::Mutex mRecorderMutex;
int mShouldLogCallCounter;
@@ -1044,7 +1044,7 @@ namespace LLError
return;
}
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
s->mRecorders.push_back(recorder);
}
@@ -1055,7 +1055,7 @@ namespace LLError
return;
}
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder),
s->mRecorders.end());
}
@@ -1104,7 +1104,7 @@ namespace LLError
std::shared_ptr<RECORDER> findRecorder()
{
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
return findRecorderPos<RECORDER>(s).first;
}
@@ -1115,7 +1115,7 @@ namespace LLError
bool removeRecorder()
{
SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
auto found = findRecorderPos<RECORDER>(s);
if (found.first)
{
@@ -1221,7 +1221,7 @@ namespace
std::string escaped_message;
- LLMutexLock lock(&s->mRecorderMutex);
+ LLCoros::LockType lock(s->mRecorderMutex);
for (LLError::RecorderPtr& r : s->mRecorders)
{
if (!r->enabled())
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 5b4e69659d..8006f9d059 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2008-09-12
* @brief Implementation for llevents.
- *
+ *
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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$
*/
@@ -382,7 +382,7 @@ std::string LLEventPump::inventName(const std::string& pfx)
void LLEventPump::clear()
{
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
// Destroy the original LLStandardSignal instance, replacing it with a
// whole new one.
mSignal = std::make_shared<LLStandardSignal>();
@@ -394,7 +394,7 @@ void LLEventPump::reset()
{
// Resetting mSignal is supposed to disconnect everything on its own
// But due to crash on 'reset' added explicit cleanup to get more data
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
ConnectionMap::const_iterator iter = mConnections.begin();
ConnectionMap::const_iterator end = mConnections.end();
while (iter!=end)
@@ -419,12 +419,12 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
return LLBoundListener();
}
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
float nodePosition = 1.0;
- // if the supplied name is empty we are not interested in the ordering mechanism
- // and can bypass attempting to find the optimal location to insert the new
+ // if the supplied name is empty we are not interested in the ordering mechanism
+ // and can bypass attempting to find the optimal location to insert the new
// listener. We'll just tack it on to the end.
if (!name.empty()) // should be the same as testing against ANONYMOUS
{
@@ -569,12 +569,12 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
LLBoundListener bound = mSignal->connect(nodePosition, listener);
-
+
if (!name.empty())
{ // note that we are not tracking anonymous listeners here either.
- // This means that it is the caller's responsibility to either assign
- // to a TempBoundListerer (scoped_connection) or manually disconnect
- // when done.
+ // This means that it is the caller's responsibility to either assign
+ // to a TempBoundListerer (scoped_connection) or manually disconnect
+ // when done.
mConnections[name] = bound;
}
return bound;
@@ -582,7 +582,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
LLBoundListener LLEventPump::getListener(const std::string& name)
{
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
ConnectionMap::const_iterator found = mConnections.find(name);
if (found != mConnections.end())
{
@@ -594,7 +594,7 @@ LLBoundListener LLEventPump::getListener(const std::string& name)
void LLEventPump::stopListening(const std::string& name)
{
- LLMutexLock lock(&mConnectionListMutex);
+ LLCoros::LockType lock(mConnectionListMutex);
ConnectionMap::iterator found = mConnections.find(name);
if (found != mConnections.end())
{
@@ -641,9 +641,9 @@ bool LLEventMailDrop::post(const LLSD& event)
{
// forward the call to our base class
bool posted = LLEventStream::post(event);
-
+
if (!posted)
- { // if the event was not handled we will save it for later so that it can
+ { // if the event was not handled we will save it for later so that it can
// be posted to any future listeners when they attach.
mEventHistory.push_back(event);
}
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 9a0a6863f0..f97fca0a32 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -61,6 +61,7 @@
#include "llstl.h"
#include "llexception.h"
#include "llhandle.h"
+#include "llcoros.h"
/*==========================================================================*|
// override this to allow binding free functions with more parameters
@@ -601,7 +602,7 @@ private:
LLHandle<LLEventPumps> mRegistry;
std::string mName;
- LLMutex mConnectionListMutex;
+ LLCoros::Mutex mConnectionListMutex;
protected:
virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
index eca0792d35..1234d2014f 100644
--- a/indra/llcommon/llfixedbuffer.h
+++ b/indra/llcommon/llfixedbuffer.h
@@ -33,6 +33,7 @@
#include "llstring.h"
#include "llthread.h"
#include "llerrorcontrol.h"
+#include "llcoros.h"
// fixed buffer implementation
class LL_COMMON_API LLFixedBuffer : public LLLineBuffer
@@ -58,7 +59,7 @@ protected:
void addWLine(const LLWString& line);
protected:
- LLMutex mMutex ;
+ LLCoros::Mutex mMutex ;
};
#endif //LL_FIXED_BUFFER_H
diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp
index 7bdc391459..40c651d9c1 100644
--- a/indra/llcommon/llmutex.cpp
+++ b/indra/llcommon/llmutex.cpp
@@ -28,6 +28,7 @@
#include "llmutex.h"
#include "llthread.h"
#include "lltimer.h"
+#include "llcoros.h"
//---------------------------------------------------------------------
@@ -45,7 +46,17 @@ LLMutex::~LLMutex()
void LLMutex::lock()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
+ // LLMutex is not coroutine aware and should not be used from a coroutine
+ // If your code is running in a coroutine, you should use LLCoros::Mutex instead
+ // NOTE: If the stack trace you're staring at contains non-thread-safe code,
+ // you should use LLAppViewer::instance().postToMainThread() to shuttle execution
+ // back to the main loop.
+ // NOTE: If you got here from seeing this assert in your log and you're not seeing
+ // a stack trace that points here, put a breakpoint in on_main_coro and try again.
+ llassert(LLCoros::on_main_coro());
+
if(isSelfLocked())
{ //redundant lock
mCount++;
@@ -67,7 +78,8 @@ void LLMutex::lock()
void LLMutex::unlock()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
if (mCount > 0)
{ //not the root unlock
mCount--;
diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h
index af5e5777bf..722d9afca2 100644
--- a/indra/llcommon/llprofiler.h
+++ b/indra/llcommon/llprofiler.h
@@ -162,7 +162,7 @@ extern thread_local bool gProfilerEnabled;
#define LL_LABEL_OBJECT_GL(type, name, length, label)
-#if LL_PROFILER_CONFIGURATION > 1
+#if !LL_DARWIN && LL_PROFILER_CONFIGURATION > 1
#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size)
#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
#else
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index 33c9e956b1..3a253d8fa6 100644
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -89,13 +89,6 @@ private:
class LL_COMMON_API LLThreadSafeRefCount
{
-public:
- static void initThreadSafeRefCount(); // creates sMutex
- static void cleanupThreadSafeRefCount(); // destroys sMutex
-
-private:
- static LLMutex* sMutex;
-
protected:
virtual ~LLThreadSafeRefCount(); // use unref()
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h
index 6c76ab8104..06cf8d3480 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/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index cf1b51e0aa..faaaefd561 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -421,30 +421,6 @@ void LLThread::unlockData()
//============================================================================
-//----------------------------------------------------------------------------
-
-//static
-LLMutex* LLThreadSafeRefCount::sMutex = 0;
-
-//static
-void LLThreadSafeRefCount::initThreadSafeRefCount()
-{
- if (!sMutex)
- {
- sMutex = new LLMutex();
- }
-}
-
-//static
-void LLThreadSafeRefCount::cleanupThreadSafeRefCount()
-{
- delete sMutex;
- sMutex = NULL;
-}
-
-
-//----------------------------------------------------------------------------
-
LLThreadSafeRefCount::LLThreadSafeRefCount() :
mRef(0)
{
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index b382d6b3f9..bd4edc7993 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -37,8 +37,8 @@ class LLMutex;
const S32 UUID_BYTES = 16;
const S32 UUID_WORDS = 4;
-const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below
-const S32 UUID_STR_SIZE = 37;
+const S32 UUID_STR_LENGTH = 37; // number of bytes needed to store a UUID as a string
+const S32 UUID_STR_SIZE = 36; // .size() of a UUID in a std::string
const S32 UUID_BASE85_LENGTH = 21; // including the trailing NULL.
struct uuid_time_t {
diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp
index f2f17dd2e6..5e5d6e1259 100644
--- a/indra/llcommon/tests/threadsafeschedule_test.cpp
+++ b/indra/llcommon/tests/threadsafeschedule_test.cpp
@@ -47,11 +47,11 @@ namespace tut
// the timestamp for each one -- but since we're passing explicit
// timestamps, make the queue reorder them.
auto now{ Queue::Clock::now() };
- queue.push(Queue::TimeTuple(now + 200ms, "ghi"));
+ queue.push(Queue::TimeTuple(now + 200ms, "ghi"s));
// Given the various push() overloads, you have to match the type
// exactly: conversions are ambiguous.
- queue.push("abc"s);
- queue.push(now + 100ms, "def");
+ queue.push(now, "abc"s);
+ queue.push(now + 100ms, "def"s);
queue.close();
auto entry = queue.pop();
ensure_equals("failed to pop first", std::get<0>(entry), "abc"s);
diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp
index 54e49ebcb8..da2e960ed3 100644
--- a/indra/llfilesystem/lldiskcache.cpp
+++ b/indra/llfilesystem/lldiskcache.cpp
@@ -222,6 +222,8 @@ const std::string LLDiskCache::assetTypeToString(LLAssetType::EType at)
{ LLAssetType::AT_MESH, "MESH" },
{ LLAssetType::AT_SETTINGS, "SETTINGS" },
{ LLAssetType::AT_MATERIAL, "MATERIAL" },
+ { LLAssetType::AT_GLTF, "GLTF" },
+ { LLAssetType::AT_GLTF_BIN, "GLTF_BIN" },
{ LLAssetType::AT_UNKNOWN, "UNKNOWN" }
};
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index b8b71cde53..acd382d9a8 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"
@@ -1003,6 +1004,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()
{
LLImageDataLock lock(this);
@@ -1042,6 +1065,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)
{
LLImageDataLock lock(this);
@@ -1136,7 +1187,7 @@ void LLImageRaw::composite( const LLImageRaw* src )
return;
}
- llassert(3 == src->getComponents());
+ llassert((3 == src->getComponents()) || (4 == src->getComponents()));
llassert(3 == dst->getComponents());
if( 3 == dst->getComponents() )
@@ -1303,6 +1354,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)
@@ -1853,6 +1928,72 @@ void LLImageRaw::compositeRowScaled4onto3( const U8* in, U8* out, S32 in_pixel_l
}
}
+
+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);
+}
+
// static
bool LLImageRaw::validateSrcAndDst(std::string func, const LLImageRaw* src, const LLImageRaw* dst)
{
@@ -2092,6 +2233,61 @@ LLImageFormatted* LLImageFormatted::createFromType(S8 codec)
}
// static
+S8 LLImageFormatted::getCodecFromMimeType(std::string_view mimetype)
+{
+ if (mimetype == "image/bmp")
+ {
+ return IMG_CODEC_BMP;
+ }
+ else if (mimetype == "image/tga")
+ {
+ return IMG_CODEC_TGA;
+ }
+ else if (mimetype == "image/jpeg")
+ {
+ return IMG_CODEC_JPEG;
+ }
+ else if (mimetype == "image/png")
+ {
+ return IMG_CODEC_PNG;
+ }
+ else if (mimetype == "image/j2c")
+ {
+ return IMG_CODEC_J2C;
+ }
+ else if (mimetype == "image/dxt")
+ {
+ return IMG_CODEC_DXT;
+ }
+ return IMG_CODEC_INVALID;
+}
+
+// static
+LLImageFormatted* LLImageFormatted::createFromMimeType(std::string_view mimetype)
+{
+ S8 codec = getCodecFromMimeType(mimetype);
+ return createFromType(codec);
+}
+
+// static
+LLImageFormatted* LLImageFormatted::loadFromMemory(const U8* data_in, U32 size, std::string_view mimetype)
+{
+ LLImageFormatted* image = createFromMimeType(mimetype);
+ if (image)
+ {
+ U8* data = image->allocateData(size);
+ memcpy(data, data_in, size);
+
+ if (!image->updateData())
+ {
+ delete image;
+ image = NULL;
+ }
+ }
+ return image;
+}
+
+// static
LLImageFormatted* LLImageFormatted::createFromExtension(const std::string& instring)
{
std::string exten;
@@ -2271,6 +2467,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
//----------------------------------------------------------------------------
+
bool LLImageFormatted::load(const std::string &filename, int load_size)
{
resetLastError();
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 37add13ad7..42eecbb97c 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 (
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
{
@@ -225,9 +226,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);
@@ -241,6 +246,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.
@@ -272,6 +280,12 @@ public:
// Src and dst can be any size. Src and dst can each have 3 or 4 components.
void composite( const 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:
// Src and dst can be any size. Src has 4 components. Dst has 3 components.
void compositeScaled4onto3( const LLImageRaw* src );
@@ -308,7 +322,10 @@ class LLImageFormatted : public LLImageBase
{
public:
static LLImageFormatted* createFromType(S8 codec);
+ static LLImageFormatted* loadFromMemory(const U8* data, U32 size, std::string_view mimetype);
static LLImageFormatted* createFromExtension(const std::string& instring);
+ static LLImageFormatted* createFromMimeType(std::string_view mimetype);
+ static S8 getCodecFromMimeType(std::string_view mimetype);
protected:
/*virtual*/ ~LLImageFormatted();
diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp
index 303dd8b711..3dd1a5638d 100644
--- a/indra/llinventory/llinventorytype.cpp
+++ b/indra/llinventory/llinventorytype.cpp
@@ -83,6 +83,8 @@ LLInventoryDictionary::LLInventoryDictionary()
addEntry(LLInventoryType::IT_ANIMATION, new InventoryEntry("animation", "animation", 1, LLAssetType::AT_ANIMATION));
addEntry(LLInventoryType::IT_GESTURE, new InventoryEntry("gesture", "gesture", 1, LLAssetType::AT_GESTURE));
addEntry(LLInventoryType::IT_MESH, new InventoryEntry("mesh", "mesh", 1, LLAssetType::AT_MESH));
+ addEntry(LLInventoryType::IT_GLTF, new InventoryEntry("gltf", "gltf", 1, LLAssetType::AT_GLTF));
+ addEntry(LLInventoryType::IT_GLTF_BIN, new InventoryEntry("glbin", "glbin", 1, LLAssetType::AT_GLTF_BIN));
addEntry(LLInventoryType::IT_WIDGET, new InventoryEntry("widget", "widget", 1, LLAssetType::AT_WIDGET));
addEntry(LLInventoryType::IT_PERSON, new InventoryEntry("person", "person", 1, LLAssetType::AT_PERSON));
addEntry(LLInventoryType::IT_SETTINGS, new InventoryEntry("settings", "settings", 1, LLAssetType::AT_SETTINGS));
@@ -153,9 +155,12 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] =
LLInventoryType::IT_NONE, // 52 AT_RESERVED_3
LLInventoryType::IT_NONE, // 53 AT_RESERVED_4
LLInventoryType::IT_NONE, // 54 AT_RESERVED_5
+ LLInventoryType::IT_NONE, // 55 AT_RESERVED_6
- LLInventoryType::IT_SETTINGS, // 55 AT_SETTINGS <- why doesnt this match the value in llassettype.h? -brad
+ LLInventoryType::IT_SETTINGS, // 56 AT_SETTINGS
LLInventoryType::IT_MATERIAL, // 57 AT_MATERIAL
+ LLInventoryType::IT_GLTF, // 58 AT_GLTF
+ LLInventoryType::IT_GLTF_BIN, // 59 AT_GLTF_BIN
};
// static
diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h
index fd80a0be04..c90f8aa107 100644
--- a/indra/llinventory/llinventorytype.h
+++ b/indra/llinventory/llinventorytype.h
@@ -66,7 +66,9 @@ public:
IT_PERSON = 24,
IT_SETTINGS = 25,
IT_MATERIAL = 26,
- IT_COUNT = 27,
+ IT_GLTF = 27,
+ IT_GLTF_BIN = 28,
+ IT_COUNT = 29,
IT_UNKNOWN = 255,
IT_NONE = -1
diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h
index dfe527f314..a68abcfa34 100644
--- a/indra/llinventory/llpermissions.h
+++ b/indra/llinventory/llpermissions.h
@@ -270,6 +270,7 @@ public:
inline bool allowModifyBy(const LLUUID &agent_id) const;
inline bool allowCopyBy(const LLUUID& agent_id) const;
inline bool allowMoveBy(const LLUUID& agent_id) const;
+
inline bool allowModifyBy(const LLUUID &agent_id, const LLUUID& group) const;
inline bool allowCopyBy(const LLUUID& agent_id, const LLUUID& group) const;
inline bool allowMoveBy(const LLUUID &agent_id, const LLUUID &group) const;
@@ -278,27 +279,11 @@ public:
// current owner is allowed to transfer to the specified agent id.
inline bool allowTransferTo(const LLUUID &agent_id) const;
- //
- // DEPRECATED.
- //
- // These return true if the given agent can perform the function.
- // They also return true if the object isn't owned, or the
- // requesting agent is a system agent. See llpermissionsflags.h
- // for bits.
- //bool allowDeleteBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); }
- //bool allowEditBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); }
- // saves last owner and sets current owner
- //bool setOwner(const LLUUID& agent, const LLUUID& owner);
- // This method saves the last owner, sets the current owner to the
- // one provided, and sets the base mask as indicated.
- //bool setOwner(const LLUUID& agent, const LLUUID& owner, U32 new_base_mask);
-
- // Attempt to set or clear the given bitmask. Returns true if you
- // are allowed to modify the permissions. If you attempt to turn
- // on bits not allowed by the base bits, the function will return
- // true, but those bits will not be set.
- //bool setGroupBits( const LLUUID& agent, bool set, PermissionMask bits);
- //bool setEveryoneBits(const LLUUID& agent, bool set, PermissionMask bits);
+ // Returns true if the object can exported by the given agent
+ // (e.g. saved as a local .gltf file)
+ // The current test should return true if the agent is the owner
+ // AND the creator of the object.
+ inline bool allowExportBy(const LLUUID& agent_id) const;
//
// MISC METHODS and OPERATORS
@@ -353,6 +338,11 @@ bool LLPermissions::allowMoveBy(const LLUUID& agent) const
return allowOperationBy(PERM_MOVE, agent, LLUUID::null);
}
+bool LLPermissions::allowExportBy(const LLUUID& agent) const
+{
+ return agent == mOwner && agent == mCreator;
+}
+
bool LLPermissions::allowTransferTo(const LLUUID &agent_id) const
{
if (mIsGroupOwned)
diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp
index 3fedd61082..e14b2f25ed 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 8aade96484..222ab040cb 100644
--- a/indra/llinventory/llsettingssky.h
+++ b/indra/llinventory/llsettingssky.h
@@ -62,7 +62,7 @@ public:
static const std::string SETTING_DOME_OFFSET;
static const std::string SETTING_DOME_RADIUS;
static const std::string SETTING_GAMMA;
- static const std::string SETTING_GLOW;
+ static const std::string SETTING_GLOW;
static const std::string SETTING_LIGHT_NORMAL;
static const std::string SETTING_MAX_Y;
static const std::string SETTING_MOON_ROTATION;
@@ -92,7 +92,7 @@ public:
static const std::string SETTING_DENSITY_PROFILE_EXP_SCALE_FACTOR;
static const std::string SETTING_DENSITY_PROFILE_LINEAR_TERM;
static const std::string SETTING_DENSITY_PROFILE_CONSTANT_TERM;
-
+
static const std::string SETTING_SKY_MOISTURE_LEVEL;
static const std::string SETTING_SKY_DROPLET_RADIUS;
static const std::string SETTING_SKY_ICE_LEVEL;
@@ -117,7 +117,7 @@ public:
virtual std::string getSettingsType() const SETTINGS_OVERRIDE { return std::string("sky"); }
virtual LLSettingsType::type_e getSettingsTypeValue() const SETTINGS_OVERRIDE { return LLSettingsType::ST_SKY; }
- // Settings status
+ // Settings status
virtual void blend(const LLSettingsBase::ptr_t &end, F64 blendf) SETTINGS_OVERRIDE;
virtual void replaceSettings(LLSD settings) SETTINGS_OVERRIDE;
@@ -129,7 +129,7 @@ public:
F32 getSkyBottomRadius() const;
F32 getSkyTopRadius() const;
F32 getSunArcRadians() const;
- F32 getMieAnisotropy() const;
+ F32 getMieAnisotropy() const;
F32 getSkyMoistureLevel() const;
F32 getSkyDropletRadius() const;
@@ -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;
@@ -200,7 +196,7 @@ public:
F32 getCloudShadow() const;
void setCloudShadow(F32 val);
-
+
F32 getCloudVariance() const;
void setCloudVariance(F32 val);
@@ -299,7 +295,7 @@ public:
// color based on brightness
LLColor3 getMoonlightColor() const;
-
+
LLColor4 getMoonAmbient() const;
LLColor3 getMoonDiffuse() const;
LLColor4 getSunAmbient() 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 3b52810855..b6e0e4a2be 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 348feba27e..3b423f783a 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -46,6 +46,34 @@ public:
loadu(val);
}
+ explicit LLMatrix4a(const F32* val)
+ {
+ loadu(val);
+ }
+
+ static const LLMatrix4a& identity()
+ {
+ static const F32 v[] =
+ { 1.f, 0.f, 0.f, 0.f,
+ 0.f, 1.f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f
+ };
+ static LLMatrix4a identity_mat(v);
+
+ return identity_mat;
+ }
+
+ bool operator==(const LLMatrix4a& rhs) const
+ {
+ return mMatrix[0] == rhs.mMatrix[0] && mMatrix[1] == rhs.mMatrix[1] && mMatrix[2] == rhs.mMatrix[2] && mMatrix[3] == rhs.mMatrix[3];
+ }
+
+ bool operator!=(const LLMatrix4a& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
inline F32* getF32ptr()
{
return (F32*) &mMatrix;
@@ -56,6 +84,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/llvector4a.h b/indra/llmath/llvector4a.h
index ea80b33e2d..8ef560dadf 100644
--- a/indra/llmath/llvector4a.h
+++ b/indra/llmath/llvector4a.h
@@ -117,6 +117,49 @@ public:
mQ = q;
}
+ bool operator==(const LLVector4a& rhs) const
+ {
+ return equals4(rhs);
+ }
+
+ bool operator!=(const LLVector4a& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ const LLVector4a& operator+=(const LLVector4a& rhs)
+ {
+ add(rhs);
+ return *this;
+ }
+
+ const LLVector4a& operator-=(const LLVector4a& rhs)
+ {
+ sub(rhs);
+ return *this;
+ }
+
+ LLVector4a operator+(const LLVector4a& rhs) const
+ {
+ LLVector4a result = *this;
+ result.add(rhs);
+ return result;
+ }
+
+ LLVector4a operator-(const LLVector4a& rhs) const
+ {
+ LLVector4a result = *this;
+ result.sub(rhs);
+ return result;
+ }
+
+ LLVector4a cross3(const LLVector4a& b) const
+ {
+ LLVector4a result;
+ result.setCross3(*this, b);
+ return result;
+ }
+
////////////////////////////////////
// LOAD/STORE
////////////////////////////////////
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 8579e6becc..f3db9424d9 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"
@@ -354,77 +353,6 @@ bool LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& v
return true;
}
-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 (U32 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
//-------------------------------------------------------------------
@@ -5469,8 +5397,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:
@@ -5482,62 +5443,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[] =
@@ -5558,9 +5466,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);
@@ -5649,8 +5554,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];
@@ -5705,7 +5609,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)
@@ -5718,12 +5622,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;
}
@@ -6438,9 +6342,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;
@@ -6458,7 +6359,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 (S32 i = 0; i < mNumVertices; i++)
@@ -7186,7 +7087,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 e812e5f0cc..bbb2a16b0b 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"
@@ -189,11 +190,13 @@ constexpr U8 LL_SCULPT_TYPE_TORUS = 2;
constexpr U8 LL_SCULPT_TYPE_PLANE = 3;
constexpr U8 LL_SCULPT_TYPE_CYLINDER = 4;
constexpr U8 LL_SCULPT_TYPE_MESH = 5;
+constexpr U8 LL_SCULPT_TYPE_GLTF = 6;
+constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_GLTF;
+
constexpr U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE |
- LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH;
+ LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH | LL_SCULPT_TYPE_GLTF;
// for value checks, assign new value after adding new types
-constexpr U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_MESH;
constexpr U8 LL_SCULPT_FLAG_INVERT = 64;
constexpr U8 LL_SCULPT_FLAG_MIRROR = 128;
@@ -913,7 +916,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 +990,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);
@@ -1140,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 abcd47ea23..71288daa89 100644
--- a/indra/llmath/llvolumeoctree.cpp
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -91,15 +91,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)
{
@@ -138,7 +138,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
{
*mClosestT = t;
mHitFace = true;
-
+ mHitTriangle = tri;
if (mIntersection != NULL)
{
LLVector4a intersect = mDir;
@@ -151,7 +151,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
U32 idx1 = tri->mIndex[1];
U32 idx2 = tri->mIndex[2];
- if (mTexCoord != NULL)
+ if (mTexCoord != NULL && mFace->mTexCoords)
{
LLVector2* tc = (LLVector2*) mFace->mTexCoords;
*mTexCoord = ((1.f - a - b) * tc[idx0] +
@@ -160,7 +160,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
}
- if (mNormal != NULL)
+ if (mNormal != NULL && mFace->mNormals)
{
LLVector4a* norm = mFace->mNormals;
@@ -180,7 +180,7 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LL
*mNormal = n1;
}
- if (mTangent != NULL)
+ if (mTangent != NULL && mFace->mTangents)
{
LLVector4a* tangents = mFace->mTangents;
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
index 96918912ed..838e1d3db0 100644
--- a/indra/llmath/llvolumeoctree.h
+++ b/indra/llmath/llvolumeoctree.h
@@ -48,12 +48,6 @@ public:
*this = rhs;
}
- const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs)
- {
- LL_ERRS() << "Illegal operation!" << LL_ENDL;
- return *this;
- }
-
~LLVolumeTriangle()
{
@@ -62,7 +56,7 @@ public:
LL_ALIGN_16(LLVector4a mPositionGroup);
const LLVector4a* mV[3];
- U16 mIndex[3];
+ U32 mIndex[3];
F32 mRadius;
mutable S32 mBinIndex;
@@ -112,7 +106,6 @@ public:
class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
{
public:
- const LLVolumeFace* mFace;
LLVector4a mStart;
LLVector4a mDir;
LLVector4a mEnd;
@@ -121,10 +114,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,
+ LLVolumeFace* face,
+ F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
@@ -137,4 +133,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/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp
index ff7c2f8387..9b4454a847 100644
--- a/indra/llmessage/llavatarnamecache.cpp
+++ b/indra/llmessage/llavatarnamecache.cpp
@@ -45,6 +45,7 @@
#include "llcorehttputil.h"
#include "llexception.h"
#include "stringize.h"
+#include "workqueue.h"
#include <map>
#include <set>
@@ -179,19 +180,26 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU
if (LLAvatarNameCache::instanceExists())
{
- if (!success)
- { // on any sort of failure add dummy records for any agent IDs
- // in this request that we do not have cached already
- std::vector<LLUUID>::const_iterator it = agentIds.begin();
- for (; it != agentIds.end(); ++it)
- {
- const LLUUID& agent_id = *it;
- LLAvatarNameCache::getInstance()->handleAgentError(agent_id);
- }
- return;
- }
+ // dispatch handler execution back to mainloop
+ auto workqueue = LL::WorkQueue::getInstance("mainloop");
- LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults);
+ if (workqueue)
+ {
+ workqueue->post([=]()
+ {
+ if (!success)
+ { // on any sort of failure add dummy records for any agent IDs
+ // in this request that we do not have cached already
+ for (const auto& agent_id : agentIds)
+ {
+ LLAvatarNameCache::getInstance()->handleAgentError(agent_id);
+ }
+ return;
+ }
+
+ LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults);
+ });
+ }
}
}
catch (const LLCoros::Stop&)
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index 567a9b9559..00abcf740f 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -34,7 +34,6 @@
#include "llpluginmessageclasses.h"
#include "llsdserialize.h"
#include "stringize.h"
-
#include "llapr.h"
//virtual
@@ -46,7 +45,7 @@ LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
bool LLPluginProcessParent::sUseReadThread = false;
apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
-LLMutex *LLPluginProcessParent::sInstancesMutex;
+LLCoros::Mutex *LLPluginProcessParent::sInstancesMutex;
LLPluginProcessParent::mapInstances_t LLPluginProcessParent::sInstances;
LLThread *LLPluginProcessParent::sReadThread = NULL;
@@ -106,7 +105,7 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
{
if(!sInstancesMutex)
{
- sInstancesMutex = new LLMutex();
+ sInstancesMutex = new LLCoros::Mutex();
}
mOwner = owner;
@@ -176,7 +175,7 @@ LLPluginProcessParent::ptr_t LLPluginProcessParent::create(LLPluginProcessParent
// Don't add to the global list until fully constructed.
{
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
sInstances.insert(mapInstances_t::value_type(that.get(), that));
}
@@ -186,7 +185,7 @@ LLPluginProcessParent::ptr_t LLPluginProcessParent::create(LLPluginProcessParent
/*static*/
void LLPluginProcessParent::shutdown()
{
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator it;
for (it = sInstances.begin(); it != sInstances.end(); ++it)
@@ -244,7 +243,7 @@ bool LLPluginProcessParent::pollTick()
{
// this grabs a copy of the smart pointer to ourselves to ensure that we do not
// get destroyed until after this method returns.
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
mapInstances_t::iterator it = sInstances.find(this);
if (it != sInstances.end())
that = (*it).second;
@@ -263,7 +262,7 @@ void LLPluginProcessParent::removeFromProcessing()
// Remove from the global list before beginning destruction.
{
// Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
{
LLMutexLock lock2(&mIncomingQueueMutex);
sInstances.erase(this);
@@ -582,7 +581,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");
@@ -845,7 +844,7 @@ void LLPluginProcessParent::updatePollset()
return;
}
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
if(sPollSet)
{
@@ -968,7 +967,7 @@ void LLPluginProcessParent::poll(F64 timeout)
mapInstances_t::iterator it;
{
- LLMutexLock lock(sInstancesMutex);
+ LLCoros::LockType lock(*sInstancesMutex);
it = sInstances.find(thatId);
if (it != sInstances.end())
that = (*it).second;
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index 1966e6b13c..d1c4933d81 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -207,7 +207,7 @@ private:
apr_pollfd_t mPollFD;
static apr_pollset_t *sPollSet;
static bool sPollsetNeedsRebuild;
- static LLMutex *sInstancesMutex;
+ static LLCoros::Mutex *sInstancesMutex;
static mapInstances_t sInstances;
static void dirtyPollSet();
static void updatePollset();
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index 2bd1edaacc..972f502aa9 100644
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -8,6 +8,7 @@ include(LLCoreHttp)
include(LLPhysicsExtensions)
include(LLPrimitive)
include(GLH)
+include(GLM)
include(TinyGLTF)
set(llprimitive_SOURCE_FILES
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp
index e6cc070114..e8c9af5ea3 100644
--- a/indra/llprimitive/llgltfmaterial.cpp
+++ b/indra/llprimitive/llgltfmaterial.cpp
@@ -81,7 +81,7 @@ LLGLTFMaterial::LLGLTFMaterial()
#endif
}
-void LLGLTFMaterial::TextureTransform::getPacked(F32 (&packed)[8]) const
+void LLGLTFMaterial::TextureTransform::getPacked(Pack& packed) const
{
packed[0] = mScale.mV[VX];
packed[1] = mScale.mV[VY];
@@ -92,6 +92,15 @@ void LLGLTFMaterial::TextureTransform::getPacked(F32 (&packed)[8]) const
packed[3] = packed[6] = packed[7] = 0.f;
}
+void LLGLTFMaterial::TextureTransform::getPackedTight(PackTight& packed) const
+{
+ packed[0] = mScale.mV[VX];
+ packed[1] = mScale.mV[VY];
+ packed[2] = mRotation;
+ packed[3] = mOffset.mV[VX];
+ packed[4] = mOffset.mV[VY];
+}
+
bool LLGLTFMaterial::TextureTransform::operator==(const TextureTransform& other) const
{
return mOffset == other.mOffset && mScale == other.mScale && mRotation == other.mRotation;
@@ -672,7 +681,7 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat)
}
}
-void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data)
+void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data) const
{
LL_PROFILE_ZONE_SCOPED;
llassert(data.isUndefined());
@@ -681,7 +690,7 @@ void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& d
for (U32 i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i)
{
- LLUUID& texture_id = mTextureId[i];
+ const LLUUID& texture_id = mTextureId[i];
const LLUUID& override_texture_id = override_mat.mTextureId[i];
if (override_texture_id.notNull() && override_texture_id != texture_id)
{
diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h
index 9ac63cd292..e04b6d5eee 100644
--- a/indra/llprimitive/llgltfmaterial.h
+++ b/indra/llprimitive/llgltfmaterial.h
@@ -5,21 +5,21 @@
* $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$
*/
@@ -68,7 +68,12 @@ public:
LLVector2 mScale = { 1.f, 1.f };
F32 mRotation = 0.f;
- void getPacked(F32 (&packed)[8]) const;
+ static const size_t PACK_SIZE = 8;
+ static const size_t PACK_TIGHT_SIZE = 5;
+ using Pack = F32[PACK_SIZE];
+ using PackTight = F32[PACK_TIGHT_SIZE];
+ void getPacked(Pack& packed) const;
+ void getPackedTight(PackTight& packed) const;
bool operator==(const TextureTransform& other) const;
bool operator!=(const TextureTransform& other) const { return !(*this == other); }
@@ -110,6 +115,18 @@ public:
static const char* const GLTF_FILE_EXTENSION_TRANSFORM_ROTATION;
static const LLUUID GLTF_OVERRIDE_NULL_UUID;
+ // *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
+public:
+
// get a UUID based on a hash of this LLGLTFMaterial
LLUUID getHash() const;
@@ -178,14 +195,14 @@ public:
void writeToModel(tinygltf::Model& model, S32 mat_index) const;
virtual void applyOverride(const LLGLTFMaterial& override_mat);
-
+
// apply the given LLSD override data
void applyOverrideLLSD(const LLSD& data);
// Get the given override on this LLGLTFMaterial as LLSD
// override_mat -- the override source data
// data -- output LLSD object (should be passed in empty)
- void getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data);
+ void getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data) const;
// For base materials only (i.e. assets). Clears transforms to
// default since they're not supported in assets yet.
@@ -209,7 +226,6 @@ public:
bool hasLocalTextures() { return !mTrackingIdToLocalTexture.empty(); }
virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID &old_id, const LLUUID& new_id);
virtual void updateTextureTracking();
-
protected:
static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);
@@ -257,10 +273,11 @@ public:
F32 mAlphaCutoff;
AlphaMode mAlphaMode;
- bool mDoubleSided;
+
+ bool mDoubleSided = false;
// Override specific flags for state that can't use off-by-epsilon or UUID
// hack
- bool mOverrideDoubleSided;
- bool mOverrideAlphaMode;
+ bool mOverrideDoubleSided = false;
+ bool mOverrideAlphaMode = false;
};
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index 267ccd6a76..644476460c 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 ce27dad346..31bc76344c 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 9ebbeac482..ac482ffbf9 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/tests/llgltfmaterial_test.cpp b/indra/llprimitive/tests/llgltfmaterial_test.cpp
index 7a276c7b82..585b9da3ad 100644
--- a/indra/llprimitive/tests/llgltfmaterial_test.cpp
+++ b/indra/llprimitive/tests/llgltfmaterial_test.cpp
@@ -1,31 +1,33 @@
-/**
+/**
* @file llgltfmaterial_test.cpp
*
- * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * $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$
+ * $/LicenseInfo$
*/
#include "linden_common.h"
#include "lltut.h"
+#include <set>
+
#include "../llgltfmaterial.h"
#include "lluuid.cpp"
@@ -108,9 +110,9 @@ namespace tut
material.setAlphaCutoff(test_fraction);
// Because this is the default value, it should append to the extras field to mark it as an override
- material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_OPAQUE);
+ material.setAlphaMode(LLGLTFMaterial::ALPHA_MODE_OPAQUE, true);
// Because this is the default value, it should append to the extras field to mark it as an override
- material.setDoubleSided(false);
+ material.setDoubleSided(false, true);
return material;
}
@@ -366,4 +368,74 @@ namespace tut
ensure_equals("LLGLTFMaterial: double sided override flag unset", material.mOverrideDoubleSided, false);
}
}
+
+ template<typename T>
+ void ensure_material_hash_pre(LLGLTFMaterial& material, T& material_field, const T new_value, const std::string& field_name)
+ {
+ ensure("LLGLTFMaterial: Hash: Test field " + field_name + " is part of the test material object", (
+ size_t(&material_field) >= size_t(&material) &&
+ (size_t(&material_field) + sizeof(material_field)) <= (size_t(&material) + sizeof(material))
+ ));
+ ensure("LLGLTFMaterial: Hash: " + field_name + " differs and will cause a perturbation worth hashing", material_field != new_value);
+ }
+
+ template<typename T>
+ void ensure_material_hash_not_changed(LLGLTFMaterial& material, T& material_field, const T new_value, const std::string& field_name)
+ {
+ ensure_material_hash_pre(material, material_field, new_value, field_name);
+
+ const LLGLTFMaterial old_material = material;
+ material_field = new_value;
+ // If this test fails, consult LLGLTFMaterial::getHash, and optionally consult http://www.catb.org/esr/structure-packing/ for guidance on optimal memory packing (effectiveness is platform-dependent)
+ ensure_equals(("LLGLTFMaterial: Hash: Perturbing " + field_name + " to new value does NOT change the hash").c_str(), material.getHash(), old_material.getHash());
+ }
+
+ template<typename T>
+ void ensure_material_hash_changed(LLGLTFMaterial& material, T& material_field, const T new_value, const std::string& field_name)
+ {
+ ensure_material_hash_pre(material, material_field, new_value, field_name);
+
+ const LLGLTFMaterial old_material = material;
+ material_field = new_value;
+ // If this test fails, consult LLGLTFMaterial::getHash, and optionally consult http://www.catb.org/esr/structure-packing/ for guidance on optimal memory packing (effectiveness is platform-dependent)
+ ensure_not_equals(("LLGLTFMaterial: Hash: Perturbing " + field_name + " to new value changes the hash").c_str(), material.getHash(), old_material.getHash());
+ }
+
+#define ENSURE_HASH_NOT_CHANGED(HASH_MAT, SOURCE_MAT, FIELD) ensure_material_hash_not_changed(HASH_MAT, HASH_MAT.FIELD, SOURCE_MAT.FIELD, #FIELD)
+#define ENSURE_HASH_CHANGED(HASH_MAT, SOURCE_MAT, FIELD) ensure_material_hash_changed(HASH_MAT, HASH_MAT.FIELD, SOURCE_MAT.FIELD, #FIELD)
+
+ // Test LLGLTFMaterial::getHash, which is very sensitive to the ordering of fields
+ template<> template<>
+ void llgltfmaterial_object_t::test<12>()
+ {
+ // *NOTE: Due to direct manipulation of the fields of materials
+ // throughout this test, the resulting modified materials may not be
+ // compliant or properly serializable.
+
+ // Ensure all fields of source_mat are set to values that differ from
+ // LLGLTFMaterial::sDefault, even if that would result in an invalid
+ // material object.
+ LLGLTFMaterial source_mat = create_test_material();
+ source_mat.mTrackingIdToLocalTexture[LLUUID::generateNewID()] = LLUUID::generateNewID();
+ source_mat.mLocalTexDataDigest = 1;
+ source_mat.mAlphaMode = LLGLTFMaterial::ALPHA_MODE_MASK;
+ source_mat.mDoubleSided = true;
+
+ LLGLTFMaterial hash_mat;
+
+ ENSURE_HASH_NOT_CHANGED(hash_mat, source_mat, mTrackingIdToLocalTexture);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mLocalTexDataDigest);
+
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mTextureId);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mTextureTransform);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mBaseColor);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mEmissiveColor);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mMetallicFactor);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mRoughnessFactor);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mAlphaCutoff);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mAlphaMode);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mDoubleSided);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mOverrideDoubleSided);
+ ENSURE_HASH_CHANGED(hash_mat, source_mat, mOverrideAlphaMode);
+ }
}
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 7b9d24c1e9..7959b3bb57 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -1238,6 +1238,11 @@ bool LLGLManager::initGL()
glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples);
glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords);
glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
+ glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &mMaxUniformBlockSize);
+
+ // sanity clamp max uniform block size to 64k just in case
+ // there's some implementation that reports a crazy value
+ mMaxUniformBlockSize = llmin(mMaxUniformBlockSize, 65536);
if (mGLVersion >= 4.59f)
{
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index c9130545c1..909dad2e85 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -87,6 +87,7 @@ public:
S32 mGLMaxIndexRange;
S32 mGLMaxTextureSize;
F32 mMaxAnisotropy = 0.f;
+ S32 mMaxUniformBlockSize = 0;
// GL 4.x capabilities
bool mHasCubeMapArray = false;
@@ -152,13 +153,18 @@ void assert_glerror();
void clear_glerror();
-//#if LL_DEBUG
+
# define stop_glerror() assert_glerror()
# define llglassertok() assert_glerror()
-//#else
-//# define stop_glerror()
-//# define llglassertok()
-//#endif
+
+// stop_glerror is still needed on OS X but has performance implications
+// use macro below to conditionally add stop_glerror to non-release builds
+// on OS X
+#if LL_DARWIN && !LL_RELEASE_FOR_DOWNLOAD
+#define STOP_GLERROR stop_glerror()
+#else
+#define STOP_GLERROR
+#endif
#define llglassertok_always() assert_glerror()
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index 2caeb1c431..e47c842228 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -381,10 +381,7 @@ void LLGLSLShader::unloadInternal()
stop_glerror();
}
-bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
- std::vector<LLStaticHashedString>* uniforms,
- U32 varying_count,
- const char** varyings)
+bool LLGLSLShader::createShader()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@@ -454,11 +451,11 @@ bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
// Map attributes and uniforms
if (success)
{
- success = mapAttributes(attributes);
+ success = mapAttributes();
}
if (success)
{
- success = mapUniforms(uniforms);
+ success = mapUniforms();
}
if (!success)
{
@@ -469,7 +466,7 @@ bool LLGLSLShader::createShader(std::vector<LLStaticHashedString>* attributes,
{
LL_SHADER_LOADING_WARNS() << "Failed to link using shader level " << mShaderLevel << " trying again using shader level " << (mShaderLevel - 1) << LL_ENDL;
mShaderLevel--;
- return createShader(attributes, uniforms);
+ return createShader();
}
else
{
@@ -602,7 +599,7 @@ void LLGLSLShader::attachObjects(GLuint* objects, S32 count)
}
}
-bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attributes)
+bool LLGLSLShader::mapAttributes()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@@ -621,11 +618,10 @@ bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attrib
}
mAttribute.clear();
- U32 numAttributes = (attributes == NULL) ? 0U : static_cast<U32>(attributes->size());
#if LL_RELEASE_WITH_DEBUG_INFO
- mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, { -1, NULL });
+ mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size(), { -1, NULL });
#else
- mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size() + numAttributes, -1);
+ mAttribute.resize(LLShaderMgr::instance()->mReservedAttribs.size(), -1);
#endif
if (res)
@@ -649,19 +645,6 @@ bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attrib
LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
}
}
- if (attributes != NULL)
- {
- for (U32 i = 0; i < numAttributes; i++)
- {
- const char* name = (*attributes)[i].String().c_str();
- S32 index = glGetAttribLocation(mProgramObject, name);
- if (index != -1)
- {
- mAttribute[LLShaderMgr::instance()->mReservedAttribs.size() + i] = index;
- LL_DEBUGS("ShaderUniform") << "Attribute " << name << " assigned to channel " << index << LL_ENDL;
- }
- }
- }
return true;
}
@@ -669,7 +652,7 @@ bool LLGLSLShader::mapAttributes(const std::vector<LLStaticHashedString>* attrib
return false;
}
-void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString>* uniforms)
+void LLGLSLShader::mapUniform(GLint index)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@@ -756,21 +739,6 @@ void LLGLSLShader::mapUniform(GLint index, const vector<LLStaticHashedString>* u
return;
}
}
-
- if (uniforms != NULL)
- {
- for (U32 i = 0; i < uniforms->size(); i++)
- {
- if ((mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] == -1)
- && ((*uniforms)[i].String() == name))
- {
- //found it
- mUniform[i + LLShaderMgr::instance()->mReservedUniforms.size()] = location;
- mTexture[i + LLShaderMgr::instance()->mReservedUniforms.size()] = mapUniformTextureChannel(location, type, size);
- return;
- }
- }
- }
}
}
@@ -830,7 +798,7 @@ GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type, GLint
return -1;
}
-bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
+bool LLGLSLShader::mapUniforms()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER;
@@ -843,9 +811,8 @@ bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
mTexture.clear();
mValue.clear();
//initialize arrays
- U32 numUniforms = (uniforms == NULL) ? 0U : static_cast<U32>(uniforms->size());
- mUniform.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1);
- mTexture.resize(numUniforms + LLShaderMgr::instance()->mReservedUniforms.size(), -1);
+ mUniform.resize(LLShaderMgr::instance()->mReservedUniforms.size(), -1);
+ mTexture.resize(LLShaderMgr::instance()->mReservedUniforms.size(), -1);
bind();
@@ -946,26 +913,26 @@ bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
if (specularDiff || bumpLessDiff || envLessDiff || refLessDiff)
{
- mapUniform(diffuseMap, uniforms);
+ mapUniform(diffuseMap);
skip_index.insert(diffuseMap);
if (-1 != specularMap) {
- mapUniform(specularMap, uniforms);
+ mapUniform(specularMap);
skip_index.insert(specularMap);
}
if (-1 != bumpMap) {
- mapUniform(bumpMap, uniforms);
+ mapUniform(bumpMap);
skip_index.insert(bumpMap);
}
if (-1 != environmentMap) {
- mapUniform(environmentMap, uniforms);
+ mapUniform(environmentMap);
skip_index.insert(environmentMap);
}
if (-1 != reflectionMap) {
- mapUniform(reflectionMap, uniforms);
+ mapUniform(reflectionMap);
skip_index.insert(reflectionMap);
}
}
@@ -979,21 +946,29 @@ bool LLGLSLShader::mapUniforms(const vector<LLStaticHashedString>* uniforms)
if (skip_index.end() != skip_index.find(i)) continue;
//........................................................................................
- mapUniform(i, uniforms);
+ mapUniform(i);
}
//........................................................................................................................................
- if (mFeatures.hasReflectionProbes) // Set up block binding, in a way supported by Apple (rather than binding = 1 in .glsl).
- { // See slide 35 and more of https://docs.huihoo.com/apple/wwdc/2011/session_420__advances_in_opengl_for_mac_os_x_lion.pdf
- static const GLuint BLOCKBINDING = 1; //picked by us
- //Get the index, similar to a uniform location
- GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, "ReflectionProbes");
+ // Set up block binding, in a way supported by Apple (rather than binding = 1 in .glsl).
+ // See slide 35 and more of https://docs.huihoo.com/apple/wwdc/2011/session_420__advances_in_opengl_for_mac_os_x_lion.pdf
+ const char* ubo_names[] =
+ {
+ "ReflectionProbes", // UB_REFLECTION_PROBES
+ "GLTFJoints", // UB_GLTF_JOINTS
+ };
+
+ llassert(LL_ARRAY_SIZE(ubo_names) == NUM_UNIFORM_BLOCKS);
+
+ for (U32 i = 0; i < NUM_UNIFORM_BLOCKS; ++i)
+ {
+ GLuint UBOBlockIndex = glGetUniformBlockIndex(mProgramObject, ubo_names[i]);
if (UBOBlockIndex != GL_INVALID_INDEX)
{
- //Set this index to a binding index
- glUniformBlockBinding(mProgramObject, UBOBlockIndex, BLOCKBINDING);
+ glUniformBlockBinding(mProgramObject, UBOBlockIndex, i);
}
}
+
unbind();
LL_DEBUGS("ShaderUniform") << "Total Uniform Size: " << mTotalUniformSize << LL_ENDL;
@@ -1049,6 +1024,13 @@ void LLGLSLShader::bind()
}
}
+void LLGLSLShader::bind(U8 variant)
+{
+ llassert(mGLTFVariants.size() == LLGLSLShader::NUM_GLTF_VARIANTS);
+ llassert(variant < LLGLSLShader::NUM_GLTF_VARIANTS);
+ mGLTFVariants[variant].bind();
+}
+
void LLGLSLShader::bind(bool rigged)
{
if (rigged)
@@ -1190,6 +1172,8 @@ S32 LLGLSLShader::enableTexture(S32 uniform, LLTexUnit::eTextureType mode, LLTex
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 698da92877..3b4224cbfd 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -44,18 +44,20 @@ public:
bool hasTransport = false; // implies no lighting (it's possible to have neither though)
bool hasSkinning = false;
bool hasObjectSkinning = false;
+ bool mGLTF = false;
bool hasAtmospherics = false;
bool hasGamma = false;
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 ===============
@@ -144,6 +146,14 @@ public:
SG_COUNT
} eGroup;
+ enum UniformBlock : GLuint
+ {
+ UB_REFLECTION_PROBES,
+ UB_GLTF_JOINTS,
+ NUM_UNIFORM_BLOCKS
+ };
+
+
static std::set<LLGLSLShader*> sInstances;
static bool sProfileEnabled;
@@ -174,17 +184,14 @@ public:
// If force_read is true, will force an immediate readback (severe performance penalty)
bool readProfileQuery(bool for_runtime = false, bool force_read = false);
- bool createShader(std::vector<LLStaticHashedString>* attributes,
- std::vector<LLStaticHashedString>* uniforms,
- U32 varying_count = 0,
- const char** varyings = NULL);
+ bool createShader();
bool attachFragmentObject(std::string object);
bool attachVertexObject(std::string object);
void attachObject(GLuint object);
void attachObjects(GLuint* objects = NULL, S32 count = 0);
- bool mapAttributes(const std::vector<LLStaticHashedString>* attributes);
- bool mapUniforms(const std::vector<LLStaticHashedString>*);
- void mapUniform(GLint index, const std::vector<LLStaticHashedString>*);
+ bool mapAttributes();
+ bool mapUniforms();
+ void mapUniform(GLint index);
void uniform1i(U32 index, GLint i);
void uniform1f(U32 index, GLfloat v);
void fastUniform1f(U32 index, GLfloat v);
@@ -317,6 +324,24 @@ public:
// this pointer should be set to whichever shader represents this shader's rigged variant
LLGLSLShader* mRiggedVariant = nullptr;
+ // variants for use by GLTF renderer
+ // bit 0 = alpha mode blend (1) or opaque (0)
+ // bit 1 = rigged (1) or static (0)
+ // bit 2 = unlit (1) or lit (0)
+ struct GLTFVariant
+ {
+ constexpr static U8 ALPHA_BLEND = 1;
+ constexpr static U8 RIGGED = 2;
+ constexpr static U8 UNLIT = 4;
+ };
+
+ constexpr static U8 NUM_GLTF_VARIANTS = 8;
+
+ std::vector<LLGLSLShader> mGLTFVariants;
+
+ //helper to bind GLTF variant
+ void bind(U8 variant);
+
// hacky flag used for optimization in LLDrawPoolAlpha
bool mCanBindFast = false;
diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index 15d9bcc4eb..e614f45986 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -49,6 +49,10 @@ LLGLTexture::LLGLTexture(const LLImageRaw* raw, bool usemipmaps)
mUseMipMaps = usemipmaps ;
// Create an empty image of the specified size and width
mGLTexturep = new LLImageGL(raw, usemipmaps) ;
+ mFullWidth = mGLTexturep->getWidth();
+ mFullHeight = mGLTexturep->getHeight();
+ mComponents = mGLTexturep->getComponents();
+ setTexelsPerImage();
}
LLGLTexture::~LLGLTexture()
@@ -95,7 +99,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 c942008874..0901243f8f 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 7ad03a32a8..51028e5667 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -990,6 +990,7 @@ void LLRender::syncLightState()
void LLRender::syncMatrices()
{
+ STOP_GLERROR;
static const U32 name[] =
{
LLShaderMgr::MODELVIEW_MATRIX,
@@ -1012,8 +1013,6 @@ void LLRender::syncMatrices()
if (shader)
{
- //llassert(shader);
-
bool mvp_done = false;
U32 i = MM_MODELVIEW;
@@ -1134,6 +1133,7 @@ void LLRender::syncMatrices()
syncLightState();
}
}
+ STOP_GLERROR;
}
void LLRender::translatef(const GLfloat& x, const GLfloat& y, const GLfloat& z)
@@ -1585,6 +1585,7 @@ void LLRender::end()
}
void LLRender::flush()
{
+ STOP_GLERROR;
if (mCount > 0)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_PIPELINE;
@@ -1693,6 +1694,9 @@ void LLRender::flush()
vb->setColorData(mColorsp.get());
}
+#if LL_DARWIN
+ vb->unmapBuffer();
+#endif
vb->unbind();
sVBCache[vhash] = { vb , std::chrono::steady_clock::now() };
@@ -2068,10 +2072,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 +2083,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 +2094,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 +2105,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 +2116,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 +2127,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 1ffaec326b..efdde77a32 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 b5745b5b49..340276a752 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 0f08466c33..c3cb015556 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)
@@ -1133,6 +1157,7 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedAttribs.push_back("weight");
mReservedAttribs.push_back("weight4");
mReservedAttribs.push_back("clothing");
+ mReservedAttribs.push_back("joint");
mReservedAttribs.push_back("texture_index");
//matrix state
@@ -1155,7 +1180,9 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("texture_metallic_roughness_transform"); // (GLTF)
mReservedUniforms.push_back("texture_emissive_transform"); // (GLTF)
- llassert(mReservedUniforms.size() == LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM+1);
+ mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF)
+
+ llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS +1);
mReservedUniforms.push_back("viewport");
@@ -1192,10 +1219,16 @@ 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");
mReservedUniforms.push_back("specularMap");
+ mReservedUniforms.push_back("metallicRoughnessMap");
+ mReservedUniforms.push_back("normalMap");
+ mReservedUniforms.push_back("occlusionMap");
mReservedUniforms.push_back("emissiveMap");
mReservedUniforms.push_back("bumpMap");
mReservedUniforms.push_back("bumpMap2");
@@ -1204,9 +1237,9 @@ 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");
mReservedUniforms.push_back("lightnorm");
mReservedUniforms.push_back("sunlight_color");
mReservedUniforms.push_back("ambient_color");
@@ -1318,7 +1351,6 @@ void LLShaderMgr::initAttribsAndUniforms()
llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW5+1);
- mReservedUniforms.push_back("normalMap");
mReservedUniforms.push_back("positionMap");
mReservedUniforms.push_back("diffuseRect");
mReservedUniforms.push_back("specularRect");
@@ -1331,7 +1363,6 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("bloomMap");
mReservedUniforms.push_back("projectionMap");
mReservedUniforms.push_back("norm_mat");
- mReservedUniforms.push_back("texture_gamma");
mReservedUniforms.push_back("specular_color");
mReservedUniforms.push_back("env_intensity");
@@ -1374,8 +1405,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 +1452,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 +1463,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 7d34132203..53e3d010db 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -58,6 +58,8 @@ public:
TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, // "texture_metallic_roughness_transform" (GLTF)
TEXTURE_EMISSIVE_TRANSFORM, // "texture_emissive_transform" (GLTF)
+ TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF)
+
VIEWPORT, // "viewport"
LIGHT_POSITION, // "light_position"
LIGHT_DIRECTION, // "light_direction"
@@ -85,9 +87,15 @@ 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"
+ METALLIC_ROUGHNESS_MAP, // "metallicRoughnessMap"
+ NORMAL_MAP, // "normalMap"
+ OCCLUSION_MAP, // "occlusionMap"
EMISSIVE_MAP, // "emissiveMap"
BUMP_MAP, // "bumpMap"
BUMP_MAP2, // "bumpMap2"
@@ -96,9 +104,9 @@ 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"
LIGHTNORM, // "lightnorm"
SUNLIGHT_COLOR, // "sunlight_color"
AMBIENT, // "ambient_color"
@@ -196,7 +204,6 @@ public:
DEFERRED_SHADOW3, // "shadowMap3"
DEFERRED_SHADOW4, // "shadowMap4"
DEFERRED_SHADOW5, // "shadowMap5"
- DEFERRED_NORMAL, // "normalMap"
DEFERRED_POSITION, // "positionMap"
DEFERRED_DIFFUSE, // "diffuseRect"
DEFERRED_SPECULAR, // "specularRect"
@@ -209,7 +216,6 @@ public:
DEFERRED_BLOOM, // "bloomMap"
DEFERRED_PROJECTION, // "projectionMap"
DEFERRED_NORM_MATRIX, // "norm_mat"
- TEXTURE_GAMMA, // "texture_gamma"
SPECULAR_COLOR, // "specular_color"
ENVIRONMENT_INTENSITY, // "env_intensity"
@@ -251,8 +257,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 +309,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 +318,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 +370,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 4efd2ce887..5bace72ffd 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -290,6 +290,62 @@ static GLuint gen_buffer()
#define ANALYZE_VBO_POOL 0
+#if LL_DARWIN
+
+// experimental -- disable VBO pooling on OS X and use glMapBuffer
+class LLVBOPool
+{
+public:
+ U64 mAllocated = 0;
+
+ U64 getVramBytesUsed()
+ {
+ return mAllocated;
+ }
+
+ void allocate(GLenum type, U32 size, GLuint& name, U8*& data)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX;
+ STOP_GLERROR;
+ llassert(type == GL_ARRAY_BUFFER || type == GL_ELEMENT_ARRAY_BUFFER);
+ llassert(name == 0); // non zero name indicates a gl name that wasn't freed
+ llassert(data == nullptr); // non null data indicates a buffer that wasn't freed
+ llassert(size >= 2); // any buffer size smaller than a single index is nonsensical
+
+ mAllocated += size;
+
+ { //allocate a new buffer
+ LL_PROFILE_GPU_ZONE("vbo alloc");
+ // ON OS X, we don't allocate a VBO until the last possible moment
+ // in unmapBuffer
+ data = (U8*) ll_aligned_malloc_16(size);
+ STOP_GLERROR;
+ }
+ }
+
+ void free(GLenum type, U32 size, GLuint name, U8* data)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX;
+ llassert(type == GL_ARRAY_BUFFER || type == GL_ELEMENT_ARRAY_BUFFER);
+ llassert(size >= 2);
+
+ if (data)
+ {
+ ll_aligned_free_16(data);
+ }
+
+ mAllocated -= size;
+ STOP_GLERROR;
+ if (name)
+ {
+ glDeleteBuffers(1, &name);
+ }
+ STOP_GLERROR;
+ }
+};
+
+#else
+
class LLVBOPool
{
public:
@@ -509,9 +565,8 @@ public:
mIBOPool.clear();
mVBOPool.clear();
}
-
-
};
+#endif
static LLVBOPool* sVBOPool = nullptr;
@@ -545,6 +600,7 @@ const U32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
sizeof(F32), // TYPE_WEIGHT,
sizeof(LLVector4), // TYPE_WEIGHT4,
sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
+ sizeof(U64), // TYPE_JOINT,
sizeof(LLVector4), // TYPE_TEXTURE_INDEX (actually exists as position.w), no extra data, but stride is 16 bytes
};
@@ -562,6 +618,7 @@ static const std::string vb_type_name[] =
"TYPE_WEIGHT",
"TYPE_WEIGHT4",
"TYPE_CLOTHWEIGHT",
+ "TYPE_JOINT"
"TYPE_TEXTURE_INDEX",
"TYPE_MAX",
"TYPE_INDEX",
@@ -629,6 +686,8 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto
LL_PROFILE_ZONE_SCOPED_CATEGORY_VERTEX;
llassert(LLGLSLShader::sCurBoundShaderPtr != NULL);
+ STOP_GLERROR;
+
gGL.syncMatrices();
U32 mask = LLVertexBuffer::MAP_VERTEX;
@@ -657,7 +716,7 @@ void LLVertexBuffer::drawElements(U32 mode, const LLVector4a* pos, const LLVecto
U16 idx = indicesp[i];
gGL.vertex3fv(pos[idx].getF32ptr());
}
-}
+ }
gGL.end();
gGL.flush();
}
@@ -685,6 +744,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of
}
{
+#if 0 // not a reliable test for VBOs that are not backed by a CPU buffer
U16* idx = (U16*) mMappedIndexData+indices_offset;
for (U32 i = 0; i < count; ++i)
{
@@ -722,6 +782,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of
}
}
}
+#endif
}
return true;
@@ -739,8 +800,10 @@ 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)));
+ STOP_GLERROR;
+ glDrawRangeElements(sGLMode[mode], start, end, count, mIndicesType,
+ (GLvoid*) (indices_offset * (size_t) mIndicesStride));
+ STOP_GLERROR;
}
void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
@@ -756,7 +819,9 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
llassert(mGLIndices == sGLRenderIndices);
gGL.syncMatrices();
+ STOP_GLERROR;
glDrawArrays(sGLMode[mode], first, count);
+ STOP_GLERROR;
}
//static
@@ -779,9 +844,10 @@ void LLVertexBuffer::initClass(LLWindow* window)
//static
void LLVertexBuffer::unbind()
{
+ STOP_GLERROR;
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-
+ STOP_GLERROR;
sGLRenderBuffer = 0;
sGLRenderIndices = 0;
}
@@ -1034,8 +1100,7 @@ bool LLVertexBuffer::updateNumIndices(U32 nindices)
bool LLVertexBuffer::allocateBuffer(U32 nverts, U32 nindices)
{
- if (nverts < 0 || nindices < 0 ||
- nverts > 65536)
+ if (nverts < 0 || nindices < 0)
{
LL_ERRS() << "Bad vertex buffer allocation: " << nverts << " : " << nindices << LL_ENDL;
}
@@ -1078,6 +1143,7 @@ U8* LLVertexBuffer::mapVertexBuffer(LLVertexBuffer::AttributeType type, U32 inde
count = mNumVerts - index;
}
+#if !LL_DARWIN
U32 start = mOffsets[type] + sTypeSize[type] * index;
U32 end = start + sTypeSize[type] * count-1;
@@ -1098,7 +1164,7 @@ U8* LLVertexBuffer::mapVertexBuffer(LLVertexBuffer::AttributeType type, U32 inde
//didn't expand an existing region, make a new one
mMappedVertexRegions.push_back({ start, end });
}
-
+#endif
return mMappedData+mOffsets[type]+sTypeSize[type]*index;
}
@@ -1112,6 +1178,7 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count)
count = mNumIndices-index;
}
+#if !LL_DARWIN
U32 start = sizeof(U16) * index;
U32 end = start + sizeof(U16) * count-1;
@@ -1132,17 +1199,26 @@ U8* LLVertexBuffer::mapIndexBuffer(U32 index, S32 count)
//didn't expand an existing region, make a new one
mMappedIndexRegions.push_back({ start, end });
}
+#endif
return mMappedIndexData + sizeof(U16)*index;
}
// 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
-static void flush_vbo(GLenum target, U32 start, U32 end, void* data)
-{
+// data -- data to be flushed
+// dst -- mMappedData or mMappedIndexData
+static void flush_vbo(GLenum target, U32 start, U32 end, void* data, U8* dst)
+{
+#if LL_DARWIN
+ LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("vb memcpy");
+ STOP_GLERROR;
+ // copy into mapped buffer
+ memcpy(dst+start, data, end-start+1);
+#else
+ // skip mapped data and stream to GPU via glBufferSubData
if (end != 0)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("glBufferSubData");
@@ -1161,10 +1237,12 @@ static void flush_vbo(GLenum target, U32 start, U32 end, void* data)
glBufferSubData(target, i, size, (U8*) data + (i-start));
}
}
+#endif
}
void LLVertexBuffer::unmapBuffer()
{
+ STOP_GLERROR;
struct SortMappedRegion
{
bool operator()(const MappedRegion& lhs, const MappedRegion& rhs)
@@ -1173,9 +1251,51 @@ void LLVertexBuffer::unmapBuffer()
}
};
+#if LL_DARWIN
+ STOP_GLERROR;
+ if (mMappedData)
+ {
+ if (mGLBuffer)
+ {
+ glDeleteBuffers(1, &mGLBuffer);
+ }
+ mGLBuffer = gen_buffer();
+ glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer);
+ sGLRenderBuffer = mGLBuffer;
+ glBufferData(GL_ARRAY_BUFFER, mSize, mMappedData, GL_STATIC_DRAW);
+ }
+ else if (mGLBuffer != sGLRenderBuffer)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer);
+ sGLRenderBuffer = mGLBuffer;
+ }
+ STOP_GLERROR;
+
+ if (mMappedIndexData)
+ {
+ if (mGLIndices)
+ {
+ glDeleteBuffers(1, &mGLIndices);
+ }
+
+ mGLIndices = gen_buffer();
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices);
+ sGLRenderIndices = mGLIndices;
+
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndicesSize, mMappedIndexData, GL_STATIC_DRAW);
+ }
+ else if (mGLIndices != sGLRenderIndices)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices);
+ sGLRenderIndices = mGLIndices;
+ }
+ STOP_GLERROR;
+#else
+
if (!mMappedVertexRegions.empty())
{
LL_PROFILE_ZONE_NAMED_CATEGORY_VERTEX("unmapBuffer - vertex");
+
if (sGLRenderBuffer != mGLBuffer)
{
glBindBuffer(GL_ARRAY_BUFFER, mGLBuffer);
@@ -1196,14 +1316,13 @@ void LLVertexBuffer::unmapBuffer()
}
else
{
- flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start);
+ flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start, mMappedData);
start = region.mStart;
end = region.mEnd;
}
}
- flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start);
-
+ flush_vbo(GL_ARRAY_BUFFER, start, end, (U8*)mMappedData + start, mMappedData);
mMappedVertexRegions.clear();
}
@@ -1230,16 +1349,16 @@ void LLVertexBuffer::unmapBuffer()
}
else
{
- flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start);
+ flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start, mMappedIndexData);
start = region.mStart;
end = region.mEnd;
}
}
- flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start);
-
+ flush_vbo(GL_ELEMENT_ARRAY_BUFFER, start, end, (U8*)mMappedIndexData + start, mMappedIndexData);
mMappedIndexRegions.clear();
}
+#endif
}
//----------------------------------------------------------------------------
@@ -1299,6 +1418,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)
@@ -1317,6 +1438,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);
@@ -1354,6 +1479,13 @@ bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, U32 in
// Set for rendering
void LLVertexBuffer::setBuffer()
{
+ STOP_GLERROR;
+#if LL_DARWIN
+ if (!mGLBuffer)
+ { // OS X doesn't allocate a buffer until we call unmapBuffer
+ return;
+ }
+#endif
// no data may be pending
llassert(mMappedVertexRegions.empty());
llassert(mMappedIndexRegions.empty());
@@ -1386,12 +1518,15 @@ void LLVertexBuffer::setBuffer()
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mGLIndices);
sGLRenderIndices = mGLIndices;
}
+
+ STOP_GLERROR;
}
// virtual (default)
void LLVertexBuffer::setupVertexBuffer()
{
+ STOP_GLERROR;
U8* base = nullptr;
U32 data_mask = LLGLSLShader::sCurBoundShaderPtr->mAttributeMask;
@@ -1463,6 +1598,12 @@ void LLVertexBuffer::setupVertexBuffer()
void* ptr = (void*)(base + mOffsets[TYPE_WEIGHT4]);
glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_WEIGHT4], ptr);
}
+ if (data_mask & MAP_JOINT)
+ {
+ AttributeType loc = TYPE_JOINT;
+ void* ptr = (void*)(base + mOffsets[TYPE_JOINT]);
+ glVertexAttribIPointer(loc, 4, GL_UNSIGNED_SHORT, LLVertexBuffer::sTypeSize[TYPE_JOINT], ptr);
+ }
if (data_mask & MAP_CLOTHWEIGHT)
{
AttributeType loc = TYPE_CLOTHWEIGHT;
@@ -1481,24 +1622,84 @@ void LLVertexBuffer::setupVertexBuffer()
void* ptr = (void*)(base + mOffsets[TYPE_VERTEX]);
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_VERTEX], ptr);
}
+ STOP_GLERROR;
}
void LLVertexBuffer::setPositionData(const LLVector4a* data)
{
+#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
- flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, 0, sizeof(LLVector4a) * getNumVerts()-1, (U8*) data, mMappedData);
}
void LLVertexBuffer::setTexCoordData(const LLVector2* data)
{
+#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
- flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TEXCOORD0], mOffsets[TYPE_TEXCOORD0] + sTypeSize[TYPE_TEXCOORD0] * getNumVerts() - 1, (U8*)data, mMappedData);
}
void LLVertexBuffer::setColorData(const LLColor4U* data)
{
+#if !LL_DARWIN
+ llassert(sGLRenderBuffer == mGLBuffer);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data, mMappedData);
+}
+
+void LLVertexBuffer::setNormalData(const LLVector4a* data)
+{
+#if !LL_DARWIN
+ llassert(sGLRenderBuffer == mGLBuffer);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_NORMAL], mOffsets[TYPE_NORMAL] + sTypeSize[TYPE_NORMAL] * getNumVerts() - 1, (U8*) data, mMappedData);
+}
+
+void LLVertexBuffer::setTangentData(const LLVector4a* data)
+{
+#if !LL_DARWIN
+ llassert(sGLRenderBuffer == mGLBuffer);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_TANGENT], mOffsets[TYPE_TANGENT] + sTypeSize[TYPE_TANGENT] * getNumVerts() - 1, (U8*) data, mMappedData);
+}
+
+void LLVertexBuffer::setWeight4Data(const LLVector4a* data)
+{
+#if !LL_DARWIN
+ llassert(sGLRenderBuffer == mGLBuffer);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_WEIGHT4], mOffsets[TYPE_WEIGHT4] + sTypeSize[TYPE_WEIGHT4] * getNumVerts() - 1, (U8*) data, mMappedData);
+}
+
+void LLVertexBuffer::setJointData(const U64* data)
+{
+#if !LL_DARWIN
llassert(sGLRenderBuffer == mGLBuffer);
- flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_COLOR], mOffsets[TYPE_COLOR] + sTypeSize[TYPE_COLOR] * getNumVerts() - 1, (U8*) data);
+#endif
+ flush_vbo(GL_ARRAY_BUFFER, mOffsets[TYPE_JOINT], mOffsets[TYPE_JOINT] + sTypeSize[TYPE_JOINT] * getNumVerts() - 1, (U8*) data, mMappedData);
}
+void LLVertexBuffer::setIndexData(const U16* data)
+{
+#if !LL_DARWIN
+ llassert(sGLRenderIndices == mGLIndices);
+#endif
+ flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U16) * getNumIndices() - 1, (U8*) data, mMappedIndexData);
+}
+
+void LLVertexBuffer::setIndexData(const U32* data)
+{
+#if !LL_DARWIN
+ llassert(sGLRenderIndices == mGLIndices);
+#endif
+ if (mIndicesType != GL_UNSIGNED_INT)
+ { // HACK -- vertex buffers are initialized as 16-bit indices, but can be switched to 32-bit indices
+ mIndicesType = GL_UNSIGNED_INT;
+ mIndicesStride = 4;
+ mNumIndices /= 2;
+ }
+ flush_vbo(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(U32) * getNumIndices() - 1, (U8*)data, mMappedIndexData);
+}
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index a08ec2bb7a..94339191a4 100644
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -110,6 +110,7 @@ public:
TYPE_WEIGHT, // "weight"
TYPE_WEIGHT4, // "weight4"
TYPE_CLOTHWEIGHT, // "clothing"
+ TYPE_JOINT, // "joint"
TYPE_TEXTURE_INDEX, // "texture_index"
TYPE_MAX, // TYPE_MAX is the size/boundary marker for attributes that go in the vertex buffer
TYPE_INDEX, // TYPE_INDEX is beyond _MAX because it lives in a separate (index) buffer
@@ -129,6 +130,7 @@ public:
MAP_WEIGHT = (1<<TYPE_WEIGHT),
MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
+ MAP_JOINT = (1<<TYPE_JOINT),
MAP_TEXTURE_INDEX = (1<<TYPE_TEXTURE_INDEX),
};
@@ -180,6 +182,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 +190,16 @@ 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 setJointData(const U64* 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 +231,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/llui/llconsole.cpp b/indra/llui/llconsole.cpp
index 54bb8cbb94..9fbfb3e5fa 100644
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -380,7 +380,7 @@ void LLConsole::updateClass()
void LLConsole::update()
{
{
- LLMutexLock lock(&mMutex);
+ LLCoros::LockType lock(mMutex);
while (!mLines.empty())
{
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 492c790265..373a358544 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -77,7 +77,10 @@ enum EDragAndDropType
DAD_PERSON = 17,
DAD_SETTINGS = 18,
DAD_MATERIAL = 19,
- DAD_COUNT = 20, // number of types in this enum
+ DAD_GLTF = 20,
+ DAD_GLTF_BIN = 21,
+
+ DAD_COUNT = 22, // number of types in this enum
};
// Reasons for drags to be denied.
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 90a4409ce3..7568c08430 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -35,6 +35,7 @@ include(OpenGL)
include(OpenSSL)
include(PNG)
include(TemplateCheck)
+include(TinyEXR)
include(ThreeJS)
include(Tracy)
include(UI)
@@ -69,8 +70,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
@@ -309,6 +314,7 @@ set(viewer_SOURCE_FILES
llgiveinventory.cpp
llglsandbox.cpp
llgltfmateriallist.cpp
+ llgltfmaterialpreviewmgr.cpp
llgroupactions.cpp
llgroupiconctrl.cpp
llgrouplist.cpp
@@ -496,6 +502,7 @@ set(viewer_SOURCE_FILES
llpathfindingobject.cpp
llpathfindingobjectlist.cpp
llpathfindingpathtool.cpp
+ llpbrterrainfeatures.cpp
llpersistentnotificationstorage.cpp
llphysicsmotion.cpp
llphysicsshapebuilderutil.cpp
@@ -518,6 +525,7 @@ set(viewer_SOURCE_FILES
llrecentpeople.cpp
llreflectionmap.cpp
llreflectionmapmanager.cpp
+ llheroprobemanager.cpp
llregioninfomodel.cpp
llregionposition.cpp
llremoteparcelrequest.cpp
@@ -719,7 +727,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
@@ -961,6 +975,7 @@ set(viewer_HEADER_FILES
llgesturemgr.h
llgiveinventory.h
llgltfmateriallist.h
+ llgltfmaterialpreviewmgr.h
llgroupactions.h
llgroupiconctrl.h
llgrouplist.h
@@ -1134,6 +1149,7 @@ set(viewer_HEADER_FILES
llpathfindingobject.h
llpathfindingobjectlist.h
llpathfindingpathtool.h
+ llpbrterrainfeatures.h
llpersistentnotificationstorage.h
llphysicsmotion.h
llphysicsshapebuilderutil.h
@@ -1156,6 +1172,7 @@ set(viewer_HEADER_FILES
llrecentpeople.h
llreflectionmap.h
llreflectionmapmanager.h
+ llheroprobemanager.h
llregioninfomodel.h
llregionposition.h
llremoteparcelrequest.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 2fe040f424..5978ab1324 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-7.1.8
+7.1.9
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index af5c22b4d9..ecfef112a4 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -379,7 +379,7 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>AutoAcceptNewInventory</key>
+ <key>AutoAcceptNewInventory</key>
<map>
<key>Comment</key>
<string>Automatically accept new notecards/textures/landmarks</string>
@@ -7031,7 +7031,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>
@@ -7040,7 +7040,7 @@
<array>
<real>0.1</real>
<real>0.0</real>
- <real>0.0</real>
+ <real>64.0</real>
</array>
</map>
@@ -7138,17 +7138,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>
@@ -7463,6 +7452,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>
@@ -7573,6 +7573,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>
@@ -7639,6 +7650,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>
@@ -7672,6 +7727,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>
@@ -8678,6 +8744,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>
@@ -8890,38 +9000,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>
@@ -9044,6 +9165,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>
@@ -9102,7 +9234,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>
@@ -9110,6 +9242,94 @@
<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>RenderTerrainPBRTransformsEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>EXPERIMENTAL: Enable PBR Terrain texture transforms.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>RenderTerrainPBRNormalsEnabled</key>
+ <map>
+ <key>Comment</key>
+ <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>
@@ -11800,6 +12020,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>
@@ -13582,7 +13813,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
- <integer>2048</integer>
+ <integer>1024</integer>
</map>
<key>max_texture_dimension_Y</key>
<map>
@@ -13593,7 +13824,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>
@@ -14370,6 +14601,270 @@
<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>LocalTerrainTransform1ScaleU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset1 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform1ScaleV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset1 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform1Rotation</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset1 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform1OffsetU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset1 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform1OffsetV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset1 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform2ScaleU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset2 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform2ScaleV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset2 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform2Rotation</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset2 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform2OffsetU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset2 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform2OffsetV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset2 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform3ScaleU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset3 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform3ScaleV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset3 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform3Rotation</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset3 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform3OffsetU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset3 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform3OffsetV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset3 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform4ScaleU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset4 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform4ScaleV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset4 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>LocalTerrainTransform4Rotation</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset4 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform4OffsetU</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset4 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
+ <key>LocalTerrainTransform4OffsetV</key>
+ <map>
+ <key>Comment</key>
+ <string>KHR texture transform component if LocalTerrainAsset4 is set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.0</real>
+ </map>
<key>PathfindingRetrieveNeighboringRegion</key>
<map>
<key>Comment</key>
@@ -15254,5 +15749,16 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>GLTFEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable GLTF support. Set to true by simulator if the simulator you are connected to supports GLTF Asset upload. WARNING: Manually setting this to true will enable buttons that can drain your L$ balance by implicitly uploading textures without asking.</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
</map>
</llsd>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarEyesV.glsl
index d9a6c9e5f1..d3ca3ec8c1 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 63d8e12e62..b904df3a1b 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 74d16592de..aabbbac12a 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 19fc660c2d..8627ab1852 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl
@@ -1,24 +1,24 @@
-/**
+/**
* @file blurLightF.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, 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$
*/
@@ -40,18 +40,18 @@ 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()
+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;
-
+
vec2 dlt = kern_scale * delta / (1.0+norm.xy*norm.xy);
dlt /= max(-pos.z*dist_factor, 1.0);
-
+
vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free'
vec4 col = defined_weight.xyxx * ccol;
@@ -75,15 +75,15 @@ void main()
k[1] = (k[0]+k[2])*0.5f;
k[3] = (k[2]+k[4])*0.5f;
k[5] = (k[4]+k[6])*0.5f;
-
+
for (int i = 1; i < 7; i++)
{
vec2 samptc = tc + k[i].z*dlt*2.0;
samptc /= screen_res;
- vec3 samppos = getPosition(samptc).xyz;
+ vec3 samppos = getPosition(samptc).xyz;
float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
-
+
if (d*d <= pointplanedist_tolerance_pow2)
{
col += texture(lightMap, samptc)*k[i].xyxx;
@@ -95,10 +95,10 @@ void main()
{
vec2 samptc = tc - k[i].z*dlt*2.0;
samptc /= screen_res;
- vec3 samppos = getPosition(samptc).xyz;
+ vec3 samppos = getPosition(samptc).xyz;
float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane
-
+
if (d*d <= pointplanedist_tolerance_pow2)
{
col += texture(lightMap, samptc)*k[i].xyxx;
@@ -108,7 +108,7 @@ void main()
col /= defined_weight.xyxx;
//col.y *= col.y;
-
+
frag_color = max(col, vec4(0));
#ifdef IS_AMD_CARD
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl
index 11deecafbb..2cc3085cd0 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 4ac757be65..a381392f6c 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 5ef3d63eb2..3ea2248bec 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,11 @@ 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 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);
+ norm.xyz = normalize(norm.xyz);
+ return norm;
}
// get linear depth value given a depth buffer sample d and znear and zfar values
@@ -515,6 +487,43 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
return clamp(color, vec3(0), vec3(10));
}
+vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
+ float perceptualRoughness,
+ float metallic,
+ vec3 n, // normal
+ vec3 p, // pixel position
+ vec3 v, // view vector (negative normalized pixel position)
+ vec3 lp, // light position
+ vec3 ld, // light direction (for spotlights)
+ vec3 lightColor,
+ float lightSize, float falloff, float is_pointlight, float ambiance)
+{
+ vec3 color = vec3(0,0,0);
+
+ vec3 lv = lp.xyz - p;
+
+ float lightDist = length(lv);
+
+ float dist = lightDist / lightSize;
+ if (dist <= 1.0)
+ {
+ lv /= lightDist;
+
+ float dist_atten = calcLegacyDistanceAttenuation(dist, falloff);
+
+ // spotlight coefficient.
+ float spot = max(dot(-ld, lv), is_pointlight);
+ // spot*spot => GL_SPOT_EXPONENT=2
+ float spot_atten = spot*spot;
+
+ vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials
+
+ color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv);
+ }
+
+ return color;
+}
+
void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor)
{
vec3 f0 = vec3(0.04);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseAlphaMaskF.glsl
index c2fb3201f4..1751e17814 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 dce1f91bc3..f5b517a8ea 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 1fc719dde5..89ea0c1710 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 d3d375b20a..7f056a51e8 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 afdd043c7c..5c73878ba9 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 304c01ecc3..4bd31cef9e 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 709b47dcbd..eff7221ae7 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/exposureF.glsl
@@ -1,34 +1,36 @@
-/**
+/**
* @file exposureF.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]*/
out vec4 frag_color;
uniform sampler2D emissiveRect;
+#ifdef USE_LAST_EXPOSURE
uniform sampler2D exposureMap;
+#endif
uniform float dt;
uniform vec2 noiseVec;
@@ -41,7 +43,7 @@ float lum(vec3 col)
return dot(l, col);
}
-void main()
+void main()
{
vec2 tc = vec2(0.5,0.5);
@@ -51,11 +53,13 @@ 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 ec6a4a502f..52dfed06ae 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl
@@ -1,28 +1,28 @@
-/**
+/**
* @file deferred/fullbrightF.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, 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]*/
out vec4 frag_color;
@@ -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 main()
-{
+void mirrorClip(vec3 pos);
+void main()
+{
+ mirrorClip(vary_position);
#ifdef IS_ALPHA
waterClip(vary_position.xyz);
#endif
@@ -88,7 +90,7 @@ void main()
calcAtmosphericVars(pos.xyz, vec3(0), 1.0, sunlit, amblit, additive, atten);
color.rgb = applySkyAndWaterFog(pos, additive, atten, color).rgb;
-
+
#endif
#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 5561a3d488..99cb23839a 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 b9337a357f..95b2f80e06 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/luminanceF.glsl
@@ -1,32 +1,32 @@
-/**
+/**
* @file luminanceF.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]*/
-// take a luminance sample of diffuseRect and emissiveRect
+// take a luminance sample of diffuseRect and emissiveRect
out vec4 frag_color;
@@ -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)
{
@@ -41,11 +43,25 @@ float lum(vec3 col)
return dot(l, col);
}
-void main()
+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 5e48ff709f..b6528dfcf8 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 03a8518c36..6ef556d7e8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/moonF.glsl
@@ -1,28 +1,28 @@
-/**
+/**
* @file class1\deferred\moonF.glsl
*
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2005, 2020 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]*/
out vec4 frag_data[4];
@@ -34,7 +34,7 @@ uniform sampler2D diffuseMap;
in vec2 vary_texcoord0;
-void main()
+void main()
{
// Restore Pre-EEP alpha fade moon near horizon
float fade = 1.0;
@@ -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/pbrShadowAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaMaskF.glsl
index c1fb9f5d84..35b7602569 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaMaskF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrShadowAlphaMaskF.glsl
@@ -1,24 +1,24 @@
-/**
+/**
* @file pbrShadowAlphaMaskF.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$
*/
@@ -33,9 +33,9 @@ in vec4 vertex_color;
in vec2 vary_texcoord0;
uniform float minimum_alpha;
-void main()
+void main()
{
- float alpha = texture(diffuseMap,vary_texcoord0.xy).a;
+ float alpha = texture(diffuseMap,vary_texcoord0.xy).a * vertex_color.a;
if (alpha < minimum_alpha)
{
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl
index d0fc362db9..ae179d3f37 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbralphaV.glsl
@@ -51,8 +51,6 @@ uniform vec4[2] texture_emissive_transform;
out vec3 vary_fragcoord;
-uniform float near_clip;
-
in vec3 position;
in vec4 diffuse_color;
in vec3 normal;
@@ -88,7 +86,7 @@ void main()
#endif
gl_Position = vert;
- vary_fragcoord.xyz = vert.xyz + vec3(0,0,near_clip);
+ vary_fragcoord.xyz = vert.xyz;
base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
index ed19fba228..380d493636 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
@@ -1,24 +1,24 @@
-/**
+/**
* @file pbropaqueF.glsl
*
* $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$
*/
@@ -28,7 +28,7 @@
#ifndef IS_HUD
-// deferred opaque implementation
+// deferred opaque implementation
uniform sampler2D diffuseMap; //always in sRGB space
@@ -54,28 +54,38 @@ 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;
+ basecolor.rgb = srgb_to_linear(basecolor.rgb);
+
+ basecolor *= vertex_color;
+
if (basecolor.a < minimum_alpha)
{
discard;
}
- vec3 col = vertex_color.rgb * srgb_to_linear(basecolor.rgb);
+ vec3 col = basecolor.rgb;
// from mikktspace.com
vec3 vNt = texture(bumpMap, normal_texcoord.xy).xyz*2.0-1.0;
float sign = vary_sign;
vec3 vN = vary_normal;
vec3 vT = vary_tangent.xyz;
-
+
vec3 vB = sign * cross(vN, vT);
vec3 tnorm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
@@ -85,7 +95,7 @@ void main()
// roughness 0.0
// metal 0.0
vec3 spec = texture(specularMap, metallic_roughness_texcoord.xy).rgb;
-
+
spec.g *= roughnessFactor;
spec.b *= metallicFactor;
@@ -102,8 +112,8 @@ void main()
//emissive = tnorm*0.5+0.5;
// See: C++: addDeferredAttachments(), GLSL: softenLightF
frag_data[0] = max(vec4(col, 0.0), vec4(0)); // Diffuse
- frag_data[1] = max(vec4(spec.rgb,vertex_color.a), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
- frag_data[2] = max(vec4(encode_normal(tnorm), vertex_color.a, GBUFFER_FLAG_HAS_PBR), vec4(0)); // normal, environment intensity, flags
+ frag_data[1] = max(vec4(spec.rgb,0.0), vec4(0)); // PBR linear packed Occlusion, Roughness, Metal.
+ frag_data[2] = vec4(tnorm, GBUFFER_FLAG_HAS_PBR); // normal, environment intensity, flags
frag_data[3] = max(vec4(emissive,0), vec4(0)); // PBR sRGB Emissive
}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
index 53e4b732df..fd020afd57 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..abb899a876
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
@@ -0,0 +1,401 @@
+/**
+ * @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[3]
+#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[10] vary_coords;
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+in vec4[2] vary_coords;
+#endif
+in vec3 vary_position;
+in vec3 vary_normal;
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+in vec3 vary_tangents[4];
+flat in float vary_sign;
+#endif
+in vec4 vary_texcoord0;
+in vec4 vary_texcoord1;
+
+void mirrorClip(vec3 position);
+
+float terrain_mix(TerrainMix tm, vec4 tms4);
+
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+// from mikktspace.com
+vec3 mikktspace(vec3 vNt, vec3 vT)
+{
+ vec3 vN = vary_normal;
+
+ vec3 vB = vary_sign * cross(vN, vT);
+ vec3 tnorm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
+
+ tnorm *= gl_FrontFacing ? 1.0 : -1.0;
+
+ return tnorm;
+}
+#endif
+
+void main()
+{
+ // Make sure we clip the terrain if we're in a mirror.
+ mirrorClip(vary_position);
+
+ 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 pbr_mix = init_pbr_mix();
+ PBRMix mix2;
+ TerrainCoord terrain_texcoord;
+ switch (tm.type & MIX_X)
+ {
+ case MIX_X:
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+ terrain_texcoord[0].xy = vary_coords[0].xy;
+ terrain_texcoord[0].zw = vary_coords[0].zw;
+ terrain_texcoord[1].xy = vary_coords[1].xy;
+ terrain_texcoord[1].zw = vary_coords[1].zw;
+ terrain_texcoord[2].xy = vary_coords[2].xy;
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+ terrain_texcoord = vary_coords[0].xy;
+#endif
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_0_base_color
+#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
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix2.vNt = mikktspace(mix2.vNt, vary_tangents[0]);
+#endif
+ pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.x);
+ break;
+ default:
+ break;
+ }
+ switch (tm.type & MIX_Y)
+ {
+ case MIX_Y:
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+ terrain_texcoord[0].xy = vary_coords[2].zw;
+ terrain_texcoord[0].zw = vary_coords[3].xy;
+ terrain_texcoord[1].xy = vary_coords[3].zw;
+ terrain_texcoord[1].zw = vary_coords[4].xy;
+ terrain_texcoord[2].xy = vary_coords[4].zw;
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+ terrain_texcoord = vary_coords[0].zw;
+#endif
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_1_base_color
+#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
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix2.vNt = mikktspace(mix2.vNt, vary_tangents[1]);
+#endif
+ pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.y);
+ break;
+ default:
+ break;
+ }
+ switch (tm.type & MIX_Z)
+ {
+ case MIX_Z:
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+ terrain_texcoord[0].xy = vary_coords[5].xy;
+ terrain_texcoord[0].zw = vary_coords[5].zw;
+ terrain_texcoord[1].xy = vary_coords[6].xy;
+ terrain_texcoord[1].zw = vary_coords[6].zw;
+ terrain_texcoord[2].xy = vary_coords[7].xy;
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+ terrain_texcoord = vary_coords[1].xy;
+#endif
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_2_base_color
+#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
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix2.vNt = mikktspace(mix2.vNt, vary_tangents[2]);
+#endif
+ pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.z);
+ break;
+ default:
+ break;
+ }
+ switch (tm.type & MIX_W)
+ {
+ case MIX_W:
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+ terrain_texcoord[0].xy = vary_coords[7].zw;
+ terrain_texcoord[0].zw = vary_coords[8].xy;
+ terrain_texcoord[1].xy = vary_coords[8].zw;
+ terrain_texcoord[1].zw = vary_coords[9].xy;
+ terrain_texcoord[2].xy = vary_coords[9].zw;
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+ terrain_texcoord = vary_coords[1].zw;
+#endif
+ mix2 = terrain_sample_and_multiply_pbr(
+ terrain_texcoord
+ , detail_3_base_color
+#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
+ );
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ mix2.vNt = mikktspace(mix2.vNt, vary_tangents[3]);
+#endif
+ pbr_mix = mix_pbr(pbr_mix, mix2, tm.weight.w);
+ break;
+ default:
+ break;
+ }
+
+ float minimum_alpha = terrain_mix(tm, minimum_alphas);
+ if (pbr_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)
+ vec3 tnorm = normalize(pbr_mix.vNt);
+#else
+ vec3 tnorm = vary_normal;
+#endif
+ tnorm *= gl_FrontFacing ? 1.0 : -1.0;
+
+
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_EMISSIVE)
+#define mix_emissive pbr_mix.emissive
+#else
+#define mix_emissive vec3(0)
+#endif
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
+#define mix_orm pbr_mix.orm
+#elif (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS)
+#define mix_orm vec3(1.0, pbr_mix.rm)
+#else
+// Matte plastic potato terrain
+#define mix_orm vec3(1.0, 1.0, 0.0)
+#endif
+ frag_data[0] = max(vec4(pbr_mix.col.xyz, 0.0), vec4(0)); // Diffuse
+ frag_data[1] = max(vec4(mix_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(mix_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..7a7fd783ec
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl
@@ -0,0 +1,468 @@
+/**
+ * @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[3]
+
+// If sign_or_zero is positive, use uv_unflippped, otherwise use uv_flipped
+vec2 _t_uv(vec2 uv_unflipped, vec2 uv_flipped, float sign_or_zero)
+{
+ return mix(uv_flipped, uv_unflipped, max(0.0, sign_or_zero));
+}
+
+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, terrain_coord[1].zw, sign(vary_vertex_normal.x))
+#define get_uv_y() _t_uv(terrain_coord[1].xy, terrain_coord[2].xy, sign(vary_vertex_normal.y))
+#define get_uv_z() _t_uv(terrain_coord[0].xy, vec2(0), sign(vary_vertex_normal.z))
+ switch (tw.type & SAMPLE_X)
+ {
+ case SAMPLE_X:
+ 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..f8e826bbdb
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
@@ -0,0 +1,175 @@
+/**
+ * @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$
+ */
+
+#define TERRAIN_PBR_DETAIL_EMISSIVE 0
+#define TERRAIN_PBR_DETAIL_OCCLUSION -1
+#define TERRAIN_PBR_DETAIL_NORMAL -2
+#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3
+
+uniform mat3 normal_matrix;
+uniform mat4 texture_matrix0;
+uniform mat4 modelview_matrix;
+uniform mat4 modelview_projection_matrix;
+
+in vec3 position;
+in vec3 normal;
+in vec4 tangent;
+in vec4 diffuse_color;
+in vec2 texcoord1;
+
+out vec3 vary_vertex_normal; // Used by pbrterrainUtilF.glsl
+out vec3 vary_normal;
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+out vec3 vary_tangents[4];
+flat out float vary_sign;
+#endif
+out vec4 vary_texcoord0;
+out vec4 vary_texcoord1;
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+out vec4[10] vary_coords;
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+out vec4[2] vary_coords;
+#endif
+out vec3 vary_position;
+
+// *HACK: Each material uses only one texture transform, but the KHR texture
+// transform spec allows handling texture transforms separately for each
+// individual texture info.
+uniform vec4[5] terrain_texture_transforms;
+
+vec2 terrain_texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform);
+vec3 terrain_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform);
+
+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;
+
+#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
+ {
+ vec4[2] ttt;
+ // material 1
+ ttt[0].xyz = terrain_texture_transforms[0].xyz;
+ ttt[1].x = terrain_texture_transforms[0].w;
+ ttt[1].y = terrain_texture_transforms[1].x;
+ vary_tangents[0] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt));
+ // material 2
+ ttt[0].xyz = terrain_texture_transforms[1].yzw;
+ ttt[1].xy = terrain_texture_transforms[2].xy;
+ vary_tangents[1] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt));
+ // material 3
+ ttt[0].xy = terrain_texture_transforms[2].zw;
+ ttt[0].z = terrain_texture_transforms[3].x;
+ ttt[1].xy = terrain_texture_transforms[3].yz;
+ vary_tangents[2] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt));
+ // material 4
+ ttt[0].x = terrain_texture_transforms[3].w;
+ ttt[0].yz = terrain_texture_transforms[4].xy;
+ ttt[1].xy = terrain_texture_transforms[4].zw;
+ vary_tangents[3] = normalize(terrain_tangent_space_transform(vec4(t, tangent.w), n, ttt));
+ }
+
+ vary_sign = tangent.w;
+#endif
+ vary_normal = normalize(n);
+
+ // Transform and pass tex coords
+ {
+ vec4[2] ttt;
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+// Don't care about upside-down (transform_xy_flipped())
+#define transform_xy() terrain_texture_transform(position.xy, ttt)
+#define transform_yz() terrain_texture_transform(position.yz, ttt)
+#define transform_negx_z() terrain_texture_transform(position.xz * vec2(-1, 1), ttt)
+#define transform_yz_flipped() terrain_texture_transform(position.yz * vec2(-1, 1), ttt)
+#define transform_negx_z_flipped() terrain_texture_transform(position.xz, ttt)
+ // material 1
+ ttt[0].xyz = terrain_texture_transforms[0].xyz;
+ ttt[1].x = terrain_texture_transforms[0].w;
+ ttt[1].y = terrain_texture_transforms[1].x;
+ vary_coords[0].xy = transform_xy();
+ vary_coords[0].zw = transform_yz();
+ vary_coords[1].xy = transform_negx_z();
+ vary_coords[1].zw = transform_yz_flipped();
+ vary_coords[2].xy = transform_negx_z_flipped();
+ // material 2
+ ttt[0].xyz = terrain_texture_transforms[1].yzw;
+ ttt[1].xy = terrain_texture_transforms[2].xy;
+ vary_coords[2].zw = transform_xy();
+ vary_coords[3].xy = transform_yz();
+ vary_coords[3].zw = transform_negx_z();
+ vary_coords[4].xy = transform_yz_flipped();
+ vary_coords[4].zw = transform_negx_z_flipped();
+ // material 3
+ ttt[0].xy = terrain_texture_transforms[2].zw;
+ ttt[0].z = terrain_texture_transforms[3].x;
+ ttt[1].xy = terrain_texture_transforms[3].yz;
+ vary_coords[5].xy = transform_xy();
+ vary_coords[5].zw = transform_yz();
+ vary_coords[6].xy = transform_negx_z();
+ vary_coords[6].zw = transform_yz_flipped();
+ vary_coords[7].xy = transform_negx_z_flipped();
+ // material 4
+ ttt[0].x = terrain_texture_transforms[3].w;
+ ttt[0].yz = terrain_texture_transforms[4].xy;
+ ttt[1].xy = terrain_texture_transforms[4].zw;
+ vary_coords[7].zw = transform_xy();
+ vary_coords[8].xy = transform_yz();
+ vary_coords[8].zw = transform_negx_z();
+ vary_coords[9].xy = transform_yz_flipped();
+ vary_coords[9].zw = transform_negx_z_flipped();
+#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
+ // material 1
+ ttt[0].xyz = terrain_texture_transforms[0].xyz;
+ ttt[1].x = terrain_texture_transforms[0].w;
+ ttt[1].y = terrain_texture_transforms[1].x;
+ vary_coords[0].xy = terrain_texture_transform(position.xy, ttt);
+ // material 2
+ ttt[0].xyz = terrain_texture_transforms[1].yzw;
+ ttt[1].xy = terrain_texture_transforms[2].xy;
+ vary_coords[0].zw = terrain_texture_transform(position.xy, ttt);
+ // material 3
+ ttt[0].xy = terrain_texture_transforms[2].zw;
+ ttt[0].z = terrain_texture_transforms[3].x;
+ ttt[1].xy = terrain_texture_transforms[3].yz;
+ vary_coords[1].xy = terrain_texture_transform(position.xy, ttt);
+ // material 4
+ ttt[0].x = terrain_texture_transforms[3].w;
+ ttt[0].yz = terrain_texture_transforms[4].xy;
+ ttt[1].xy = terrain_texture_transforms[4].zw;
+ vary_coords[1].zw = terrain_texture_transform(position.xy, ttt);
+#endif
+ }
+
+ vec4 tc = vec4(texcoord1,0,1);
+ vary_texcoord0.zw = tc.xy;
+ 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 7a25f63260..a0eb6cfbb8 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)
{
@@ -181,6 +191,7 @@ void main()
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 a07a4301bc..785c748234 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
@@ -1,24 +1,24 @@
-/**
+/**
* @file class1/deferred/skyF.glsl
*
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2005, 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$
*/
@@ -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 6110b6ade0..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.)
{
@@ -92,13 +102,13 @@ void main()
// Initialize temp variables
vec3 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color * 0.7; //magic 0.7 to match legacy color
-
+
// Sunlight attenuation effect (hue and brightness) due to atmosphere
// this is used later for sunlight modulation at various altitudes
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;
@@ -142,7 +152,7 @@ void main()
sunlight *= max(0.0, (1. - cloud_shadow));
// Haze color below cloud
- vec3 add_below_cloud = (blue_horizon * blue_weight * (sunlight + ambient)
+ vec3 add_below_cloud = (blue_horizon * blue_weight * (sunlight + ambient)
+ (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient));
// Attenuate cloud color by atmosphere
diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
index bee9e6d2fe..5c79fd7315 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl
@@ -1,28 +1,28 @@
-/**
+/**
* @file class1\deferred\terrainF.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, 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]*/
out vec4 frag_data[4];
@@ -38,12 +38,13 @@ 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);
vec4 color1 = texture(detail_1, vary_texcoord0.xy);
vec4 color2 = texture(detail_2, vary_texcoord0.xy);
@@ -53,13 +54,13 @@ void main()
float alpha2 = texture(alpha_ramp,vary_texcoord1.xy).a;
float alphaFinal = texture(alpha_ramp, vary_texcoord1.zw).a;
vec4 outColor = mix( mix(color3, color2, alpha2), mix(color1, color0, alpha1), alphaFinal );
-
- outColor.a = 0.0; // yes, downstream atmospherics
-
- frag_data[0] = outColor;
+
+ outColor.a = 0.0; // yes, downstream atmospherics
+
+ frag_data[0] = max(outColor, vec4(0));
frag_data[1] = vec4(0.0,0.0,0.0,-1.0);
vec3 nvn = normalize(vary_normal);
- frag_data[2] = vec4(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 aab2abff1e..b4ab7cd169 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/terrainV.glsl
@@ -1,36 +1,36 @@
-/**
+/**
* @file class1\environment\terrainV.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, 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 diffuse_color;
-in vec2 texcoord0;
in vec2 texcoord1;
out vec3 pos;
@@ -41,18 +41,18 @@ 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.z = 0;
+ tcoord.w = 1;
- tcoord = mat * tcoord;
-
- return tcoord;
+ tcoord = mat * tcoord;
+
+ return tcoord.xy;
}
void main()
@@ -62,15 +62,15 @@ 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);
-
+
vary_texcoord0.zw = t.xy;
vary_texcoord1.xy = t.xy-vec2(2.0, 0.0);
vary_texcoord1.zw = t.xy-vec2(1.0, 0.0);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl
index 8e641522e3..bf5d106dab 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/textureUtilV.glsl
@@ -1,24 +1,24 @@
-/**
+/**
* @file class1/deferred/textureUtilV.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$
*/
@@ -48,6 +48,7 @@ vec2 khr_texture_transform(vec2 texcoord, vec2 scale, float rotation, vec2 offse
return (transform * vec3(texcoord, 1)).xy;
}
+// A texture transform function for PBR materials applied to shape prims/Collada model prims
// vertex_texcoord - The UV texture coordinates sampled from the vertex at
// runtime. Per SL convention, this is in a right-handed UV coordinate
// system. Collada models also have right-handed UVs.
@@ -65,7 +66,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
@@ -77,6 +78,19 @@ vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl
return texcoord;
}
+// Similar to texture_transform but no offset during coordinate system
+// conversion, and no texture animation support.
+vec2 terrain_texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform)
+{
+ vec2 texcoord = vertex_texcoord;
+
+ texcoord.y = -texcoord.y;
+ texcoord = khr_texture_transform(texcoord, khr_gltf_transform[0].xy, khr_gltf_transform[0].z, khr_gltf_transform[1].xy);
+ texcoord.y = -texcoord.y;
+
+ return texcoord;
+}
+
// Take the rotation only from both transforms and apply to the tangent. This
// accounts for the change of the topology of the normal texture when a texture
// rotation is applied to it.
@@ -120,3 +134,26 @@ vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] kh
return (weights.x * vertex_binormal.xyz) + (weights.y * vertex_tangent.xyz);
}
+
+// Similar to tangent_space_transform but no offset during coordinate system
+// conversion, and no texture animation support.
+vec3 terrain_tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform)
+{
+ // Immediately convert to left-handed coordinate system ((0,1) -> (0, -1))
+ vec2 weights = vec2(0, -1);
+
+ // Apply KHR_texture_transform (rotation only)
+ float khr_rotation = khr_gltf_transform[0].z;
+ mat2 khr_rotation_mat = mat2(
+ cos(khr_rotation),-sin(khr_rotation),
+ sin(khr_rotation), cos(khr_rotation)
+ );
+ weights = khr_rotation_mat * weights;
+
+ // Convert back to right-handed coordinate system
+ weights.y = -weights.y;
+
+ vec3 vertex_binormal = vertex_tangent.w * cross(vertex_normal, vertex_tangent.xyz);
+
+ return (weights.x * vertex_binormal.xyz) + (weights.y * vertex_tangent.xyz);
+}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl
index db6070f328..05922ecb1a 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 81900fba70..ef5602f1e5 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/srgbF.glsl b/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl
index 7e1d906878..d7f6d20547 100644
--- a/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl
+++ b/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl
@@ -23,8 +23,6 @@
* $/LicenseInfo$
*/
- uniform sampler2D exposureMap;
-
vec3 srgb_to_linear(vec3 cs)
{
vec3 low_range = cs / vec3(12.92);
diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl
new file mode 100644
index 0000000000..d71a3fad99
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessF.glsl
@@ -0,0 +1,300 @@
+/**
+ * @file pbrmetallicroughnessF.glsl
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+/*[EXTRA_CODE_HERE]*/
+
+
+// GLTF pbrMetallicRoughness implementation
+
+
+// ==================================
+// needed by all variants
+// ==================================
+uniform sampler2D diffuseMap; //always in sRGB space
+uniform sampler2D emissiveMap;
+uniform vec3 emissiveColor;
+in vec3 vary_position;
+in vec4 vertex_color;
+in vec2 base_color_texcoord;
+in vec2 emissive_texcoord;
+uniform float minimum_alpha;
+
+void mirrorClip(vec3 pos);
+vec3 linear_to_srgb(vec3 c);
+vec3 srgb_to_linear(vec3 c);
+// ==================================
+
+
+// ==================================
+// needed by all lit variants
+// ==================================
+#ifndef UNLIT
+uniform sampler2D normalMap;
+uniform sampler2D metallicRoughnessMap;
+uniform sampler2D occlusionMap;
+uniform float metallicFactor;
+uniform float roughnessFactor;
+in vec3 vary_normal;
+in vec3 vary_tangent;
+flat in float vary_sign;
+in vec2 normal_texcoord;
+in vec2 metallic_roughness_texcoord;
+#endif
+// ==================================
+
+
+// ==================================
+// needed by all alpha variants
+// ==================================
+#ifdef ALPHA_BLEND
+in vec3 vary_fragcoord;
+uniform vec4 clipPlane;
+uniform float clipSign;
+void waterClip(vec3 pos);
+void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, out vec3 sunlit, out vec3 amblit, out vec3 atten, out vec3 additive);
+vec4 applySkyAndWaterFog(vec3 pos, vec3 additive, vec3 atten, vec4 color);
+#endif
+// ==================================
+
+
+// ==================================
+// needed by lit alpha
+// ==================================
+#if defined(ALPHA_BLEND) && !defined(UNLIT)
+
+#ifdef HAS_SUN_SHADOW
+uniform sampler2D lightMap;
+uniform vec2 screen_res;
+#endif
+
+// Lights
+// See: LLRender::syncLightState()
+uniform vec4 light_position[8];
+uniform vec3 light_direction[8]; // spot direction
+uniform vec4 light_attenuation[8]; // linear, quadratic, is omni, unused, See: LLPipeline::setupHWLights() and syncLightState()
+uniform vec3 light_diffuse[8];
+uniform vec2 light_deferred_attenuation[8]; // light size and falloff
+
+uniform int sun_up_factor;
+uniform vec3 sun_dir;
+uniform vec3 moon_dir;
+
+void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float nh, out float nl, out float nv, out float vh, out float lightDist);
+float calcLegacyDistanceAttenuation(float distance, float falloff);
+float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen);
+void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv,
+ vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear);
+
+void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor);
+
+vec3 pbrBaseLight(vec3 diffuseColor,
+ vec3 specularColor,
+ float metallic,
+ vec3 pos,
+ vec3 norm,
+ float perceptualRoughness,
+ vec3 light_dir,
+ vec3 sunlit,
+ float scol,
+ vec3 radiance,
+ vec3 irradiance,
+ vec3 colorEmissive,
+ float ao,
+ vec3 additive,
+ vec3 atten);
+
+vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
+ float perceptualRoughness,
+ float metallic,
+ vec3 n, // normal
+ vec3 p, // pixel position
+ vec3 v, // view vector (negative normalized pixel position)
+ vec3 lp, // light position
+ vec3 ld, // light direction (for spotlights)
+ vec3 lightColor,
+ float lightSize, float falloff, float is_pointlight, float ambiance);
+
+#endif
+// ==================================
+
+
+// ==================================
+// output definition
+// ==================================
+#if defined(ALPHA_BLEND) || defined(UNLIT)
+out vec4 frag_color;
+#else
+out vec4 frag_data[4];
+#endif
+// ==================================
+
+
+void main()
+{
+
+// ==================================
+// all variants
+// mirror clip
+// base color
+// masking
+// emissive
+// ==================================
+ vec3 pos = vary_position;
+ mirrorClip(pos);
+
+ vec4 basecolor = texture(diffuseMap, base_color_texcoord.xy).rgba;
+ basecolor.rgb = srgb_to_linear(basecolor.rgb);
+ basecolor *= vertex_color;
+
+ if (basecolor.a < minimum_alpha)
+ {
+ discard;
+ }
+
+ vec3 emissive = emissiveColor;
+ emissive *= srgb_to_linear(texture(emissiveMap, emissive_texcoord.xy).rgb);
+// ==================================
+
+// ==================================
+// all lit variants
+// prepare norm
+// prepare orm
+// ==================================
+#ifndef UNLIT
+ // from mikktspace.com
+ vec3 vNt = texture(normalMap, normal_texcoord.xy).xyz*2.0-1.0;
+ float sign = vary_sign;
+ vec3 vN = vary_normal;
+ vec3 vT = vary_tangent.xyz;
+
+ vec3 vB = sign * cross(vN, vT);
+ vec3 norm = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
+ norm *= gl_FrontFacing ? 1.0 : -1.0;
+
+ // RGB = Occlusion, Roughness, Metal
+ // default values, see LLViewerTexture::sDefaultPBRORMImagep
+ // occlusion 1.0
+ // roughness 0.0
+ // metal 0.0
+ vec3 orm = texture(metallicRoughnessMap, metallic_roughness_texcoord.xy).rgb;
+ orm.r = texture(occlusionMap, metallic_roughness_texcoord.xy).r;
+ orm.g *= roughnessFactor;
+ orm.b *= metallicFactor;
+#endif
+// ==================================
+
+// ==================================
+// non alpha output
+// ==================================
+#ifndef ALPHA_BLEND
+#ifdef UNLIT
+ vec4 color = basecolor;
+ color.rgb += emissive.rgb;
+ frag_color = color;
+#else
+ frag_data[0] = max(vec4(basecolor.rgb, 0.0), vec4(0));
+ frag_data[1] = max(vec4(orm.rgb,0.0), vec4(0));
+ frag_data[2] = vec4(norm, GBUFFER_FLAG_HAS_PBR);
+ frag_data[3] = max(vec4(emissive,0), vec4(0));
+#endif
+#endif
+
+
+// ==================================
+// alpha implementation
+// ==================================
+#ifdef ALPHA_BLEND
+
+ float scol = 1.0;
+ vec3 sunlit;
+ vec3 amblit;
+ vec3 additive;
+ vec3 atten;
+
+ vec3 light_dir;
+
+#ifdef UNLIT
+ light_dir = vec3(0,0,1);
+ vec3 norm = vec3(0,0,1);
+#else
+ light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
+#endif
+
+ calcAtmosphericVarsLinear(pos.xyz, norm, light_dir, sunlit, amblit, additive, atten);
+
+#ifndef UNLIT
+ vec3 sunlit_linear = srgb_to_linear(sunlit);
+
+ vec2 frag = vary_fragcoord.xy/vary_fragcoord.z*0.5+0.5;
+
+#ifdef HAS_SUN_SHADOW
+ scol = sampleDirectionalShadow(pos.xyz, norm.xyz, frag);
+#endif
+
+ float perceptualRoughness = orm.g * roughnessFactor;
+ float metallic = orm.b * metallicFactor;
+
+ // PBR IBL
+ float gloss = 1.0 - perceptualRoughness;
+ vec3 irradiance = vec3(0);
+ vec3 radiance = vec3(0);
+ sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true, amblit);
+
+ vec3 diffuseColor;
+ vec3 specularColor;
+ calcDiffuseSpecular(basecolor.rgb, metallic, diffuseColor, specularColor);
+
+ vec3 v = -normalize(pos.xyz);
+
+ vec3 color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, emissive, orm.r, additive, atten);
+
+ vec3 light = vec3(0);
+
+ // Punctual lights
+#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w);
+
+ LIGHT_LOOP(1)
+ LIGHT_LOOP(2)
+ LIGHT_LOOP(3)
+ LIGHT_LOOP(4)
+ LIGHT_LOOP(5)
+ LIGHT_LOOP(6)
+ LIGHT_LOOP(7)
+
+ color.rgb += light.rgb;
+
+ color.rgb = applySkyAndWaterFog(pos.xyz, additive, atten, vec4(color, 1.0)).rgb;
+
+ float a = basecolor.a*vertex_color.a;
+
+ frag_color = max(vec4(color.rgb,a), vec4(0));
+#else // UNLIT
+ vec4 color = basecolor;
+ color.rgb += emissive.rgb;
+ frag_color = color;
+#endif
+#endif // ALPHA_BLEND
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl
new file mode 100644
index 0000000000..f123c29101
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/gltf/pbrmetallicroughnessV.glsl
@@ -0,0 +1,171 @@
+/**
+ * @file pbrmetallicroughnessV.glsl
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// GLTF pbrMetallicRoughness implementation
+
+uniform mat4 modelview_matrix;
+
+#ifdef HAS_SKIN
+uniform mat4 projection_matrix;
+#else
+uniform mat3 normal_matrix;
+uniform mat4 modelview_projection_matrix;
+#endif
+uniform mat4 texture_matrix0;
+
+uniform vec4[2] texture_base_color_transform;
+uniform vec4[2] texture_normal_transform;
+uniform vec4[2] texture_metallic_roughness_transform;
+uniform vec4[2] texture_emissive_transform;
+
+in vec3 position;
+in vec4 diffuse_color;
+in vec2 texcoord0;
+out vec2 base_color_texcoord;
+out vec2 emissive_texcoord;
+out vec4 vertex_color;
+out vec3 vary_position;
+
+#ifndef UNLIT
+in vec3 normal;
+in vec4 tangent;
+out vec2 normal_texcoord;
+out vec2 metallic_roughness_texcoord;
+out vec3 vary_tangent;
+flat out float vary_sign;
+out vec3 vary_normal;
+vec3 tangent_space_transform(vec4 vertex_tangent, vec3 vertex_normal, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
+#endif
+
+vec2 texture_transform(vec2 vertex_texcoord, vec4[2] khr_gltf_transform, mat4 sl_animation_transform);
+
+
+#ifdef ALPHA_BLEND
+out vec3 vary_fragcoord;
+#endif
+
+#ifdef HAS_SKIN
+in uvec4 joint;
+in vec4 weight4;
+
+layout (std140) uniform GLTFJoints
+{
+ // list of OBBs for user override probes
+ mat3x4 gltf_joints[MAX_JOINTS_PER_GLTF_OBJECT];
+};
+
+mat4 getGLTFSkinTransform()
+{
+ int i;
+
+ vec4 w = weight4;
+
+ uint i1 = joint.x;
+ uint i2 = joint.y;
+ uint i3 = joint.z;
+ uint i4 = joint.w;
+
+ mat3 mat = mat3(gltf_joints[i1])*w.x;
+ mat += mat3(gltf_joints[i2])*w.y;
+ mat += mat3(gltf_joints[i3])*w.z;
+ mat += mat3(gltf_joints[i4])*w.w;
+
+ vec3 trans = vec3(gltf_joints[i1][0].w,gltf_joints[i1][1].w,gltf_joints[i1][2].w)*w.x;
+ trans += vec3(gltf_joints[i2][0].w,gltf_joints[i2][1].w,gltf_joints[i2][2].w)*w.y;
+ trans += vec3(gltf_joints[i3][0].w,gltf_joints[i3][1].w,gltf_joints[i3][2].w)*w.z;
+ trans += vec3(gltf_joints[i4][0].w,gltf_joints[i4][1].w,gltf_joints[i4][2].w)*w.w;
+
+ mat4 ret;
+
+ ret[0] = vec4(mat[0], 0);
+ ret[1] = vec4(mat[1], 0);
+ ret[2] = vec4(mat[2], 0);
+ ret[3] = vec4(trans, 1.0);
+
+ return ret;
+
+#ifdef IS_AMD_CARD
+ // If it's AMD make sure the GLSL compiler sees the arrays referenced once by static index. Otherwise it seems to optimise the storage awawy which leads to unfun crashes and artifacts.
+ mat3x4 dummy1 = gltf_joints[0];
+ mat3x4 dummy2 = gltf_joints[MAX_JOINTS_PER_GLTF_OBJECT-1];
+#endif
+
+}
+
+#endif
+
+void main()
+{
+#ifdef HAS_SKIN
+ mat4 mat = getGLTFSkinTransform();
+
+ mat = modelview_matrix * mat;
+
+ vec3 pos = (mat*vec4(position.xyz,1.0)).xyz;
+ vary_position = pos;
+
+ vec4 vert = projection_matrix * vec4(pos, 1.0);
+ gl_Position = vert;
+
+#else
+ vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
+ //transform vertex
+ vec4 vert = modelview_projection_matrix * vec4(position.xyz, 1.0);
+ gl_Position = vert;
+#endif
+
+ base_color_texcoord = texture_transform(texcoord0, texture_base_color_transform, texture_matrix0);
+ emissive_texcoord = texture_transform(texcoord0, texture_emissive_transform, texture_matrix0);
+
+#ifndef UNLIT
+ normal_texcoord = texture_transform(texcoord0, texture_normal_transform, texture_matrix0);
+ metallic_roughness_texcoord = texture_transform(texcoord0, texture_metallic_roughness_transform, texture_matrix0);
+#endif
+
+
+#ifndef UNLIT
+#ifdef HAS_SKIN
+ vec3 n = (mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz;
+ vec3 t = (mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz;
+#else //HAS_SKIN
+ vec3 n = normal_matrix * normal;
+ vec3 t = normal_matrix * tangent.xyz;
+#endif
+
+ n = normalize(n);
+ vary_tangent = normalize(tangent_space_transform(vec4(t, tangent.w), n, texture_normal_transform, texture_matrix0));
+ vary_sign = tangent.w;
+ vary_normal = n;
+#endif
+
+ vertex_color = diffuse_color;
+#ifdef ALPHA_BLEND
+ vary_fragcoord = vert.xyz;
+#endif
+}
+
+
+
+
diff --git a/indra/newview/app_settings/shaders/class1/environment/encodeNormF.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl
index ca3e4f6718..2bfd45f3b0 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..ae726190c7
--- /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 eb0f7297ad..feb0947649 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 db26e64f17..09a505d69d 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 834c20e14d..95cdfb6fae 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 b7cd3f0589..d077670c96 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
@@ -57,16 +57,16 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
vec3 rel_pos_norm = normalize(rel_pos);
float rel_pos_len = length(rel_pos);
-
+
vec3 sunlight = (sun_up_factor == 1) ? sunlight_color: moonlight_color;
-
+
// sunlight attenuation effect (hue and brightness) due to atmosphere
// this is used later for sunlight modulation at various altitudes
vec3 light_atten = (blue_density + vec3(haze_density * 0.25)) * (density_multiplier * max_y);
// 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), -100000, 100000);
// glow.z should be negative, so we're doing a sort of (1 / "angle") function
// add "minimum anti-solar illumination"
@@ -119,7 +119,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
additive = (blue_horizon.rgb * blue_weight.rgb) * (cs + tmpAmbient.rgb) + (haze_horizon * haze_weight.rgb) * (cs * haze_glow + tmpAmbient.rgb);
// brightness of surface both sunlight and ambient
-
+
sunlit = sunlight.rgb;
amblit = tmpAmbient;
@@ -128,7 +128,7 @@ void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, ou
vec3 srgb_to_linear(vec3 col);
-// provide a touch of lighting in the opposite direction of the sun light
+// provide a touch of lighting in the opposite direction of the sun light
// so areas in shadow don't lose all detail
float ambientLighting(vec3 norm, vec3 light_dir)
{
@@ -150,7 +150,7 @@ void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, ou
// (allows for mixing of light sources other than sunlight e.g. reflection probes)
sunlit *= sky_sunlight_scale;
amblit *= sky_ambient_scale;
-
+
amblit = srgb_to_linear(amblit);
amblit *= ambientLighting(norm, light_dir);
}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
index e6bdaf265e..210ecce8db 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 34d86b6147..f4a8051427 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);
@@ -117,7 +118,7 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
vec3 v, // surface point to camera
vec3 l); //surface point to light
-vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
+vec3 pbrCalcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
float perceptualRoughness,
float metallic,
vec3 n, // normal
@@ -126,36 +127,12 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor,
vec3 lp, // light position
vec3 ld, // light direction (for spotlights)
vec3 lightColor,
- float lightSize, float falloff, float is_pointlight, float ambiance)
-{
- vec3 color = vec3(0,0,0);
-
- vec3 lv = lp.xyz - p;
-
- float lightDist = length(lv);
-
- float dist = lightDist / lightSize;
- if (dist <= 1.0)
- {
- lv /= lightDist;
-
- float dist_atten = calcLegacyDistanceAttenuation(dist, falloff);
-
- // spotlight coefficient.
- float spot = max(dot(-ld, lv), is_pointlight);
- // spot*spot => GL_SPOT_EXPONENT=2
- float spot_atten = spot*spot;
-
- vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials
-
- color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv);
- }
-
- return color;
-}
+ float lightSize, float falloff, float is_pointlight, float ambiance);
void main()
{
+ mirrorClip(vary_position);
+
vec3 color = vec3(0,0,0);
vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir;
@@ -227,7 +204,7 @@ void main()
vec3 light = vec3(0);
// Punctual lights
-#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w);
+#define LIGHT_LOOP(i) light += pbrCalcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w);
LIGHT_LOOP(1)
LIGHT_LOOP(2)
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
index 10bfe2c5d5..1bd5f5a718 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
@@ -1,24 +1,24 @@
-/**
+/**
* @file sunLightF.glsl
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, 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$
*/
@@ -35,23 +35,23 @@ 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);
float sampleSpotShadow(vec3 pos, vec3 norm, int index, vec2 pos_screen);
-void main()
+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 2f1819bff7..e0333b6044 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
@@ -1,27 +1,27 @@
-/**
+/**
* @file class2/deferred/sunLightSSAOF.glsl
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2007, 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]*/
out vec4 frag_color;
@@ -32,23 +32,23 @@ 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);
float calcAmbientOcclusion(vec4 pos, vec3 norm, vec2 pos_screen);
-void main()
+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 22408387b1..03dc3d7113 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 7f75b16cf0..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);
@@ -68,16 +67,16 @@ void main()
calcAtmosphericVarsLinear(pos.xyz, norm.xyz, light_dir, sunlit, amblit, additive, atten);
vec3 sunlit_linear = srgb_to_linear(sunlit);
-
+
// mask off atmospherics below water (when camera is under water)
bool do_atmospherics = false;
-
+
if (dot(vec3(0), waterPlane.xyz) + waterPlane.w > 0.0 ||
dot(pos.xyz, waterPlane.xyz) + waterPlane.w > 0.0)
{
do_atmospherics = true;
}
-
+
vec3 irradiance = vec3(0);
vec3 radiance = vec3(0);
@@ -102,5 +101,5 @@ void main()
}
frag_color = max(vec4(color.rgb, alpha), vec4(0)); //output linear since local lights will be added to this shader's results
-
+
}
diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl
index 20f063fe3e..26ab0406f6 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,11 +73,10 @@ 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;
@@ -209,8 +215,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 +289,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 +303,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 +409,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 31aca8a745..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);
@@ -56,8 +56,8 @@ vec3 srgb_to_linear(vec3 c);
// Util
vec3 hue_to_rgb(float hue);
-vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
- float perceptualRoughness,
+vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
+ float perceptualRoughness,
float metallic,
vec3 n, // normal
vec3 v, // surface point to camera
@@ -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;
@@ -92,7 +91,7 @@ void main()
float metallic = orm.b;
vec3 f0 = vec3(0.04);
vec3 baseColor = diffuse.rgb;
-
+
vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0);
diffuseColor *= 1.0 - metallic;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl
index c27310cf89..60be9f4407 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl
@@ -1,28 +1,28 @@
-/**
+/**
* @file class3\deferred\pointLightF.glsl
*
* $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$
*/
-
+
/*[EXTRA_CODE_HERE]*/
out vec4 frag_color;
@@ -52,15 +52,15 @@ 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);
vec3 srgb_to_linear(vec3 c);
float getDepth(vec2 tc);
-vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
- float perceptualRoughness,
+vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
+ float perceptualRoughness,
float metallic,
vec3 n, // normal
vec3 v, // surface point to camera
@@ -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);
@@ -94,13 +93,13 @@ void main()
if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR))
{
- vec3 colorEmissive = texture(emissiveRect, tc).rgb;
+ vec3 colorEmissive = texture(emissiveRect, tc).rgb;
vec3 orm = spec.rgb;
float perceptualRoughness = orm.g;
float metallic = orm.b;
vec3 f0 = vec3(0.04);
vec3 baseColor = diffuse.rgb;
-
+
vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0);
diffuseColor *= 1.0 - metallic;
@@ -137,7 +136,7 @@ void main()
final_color += lit*scol*color.rgb*spec.rgb;
}
}
-
+
if (dot(final_color, final_color) <= 0.0)
{
discard;
diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl
index ae81a4b472..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,14 +48,16 @@ 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
+ // extra parameters
// x - irradiance scale
// y - radiance scale
// 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
@@ -95,7 +102,7 @@ bool shouldSampleProbe(int i, vec3 pos)
if (refIndex[i].w < 0)
{
vec4 v = refBox[i] * vec4(pos, 1.0);
- if (abs(v.x) > 1 ||
+ if (abs(v.x) > 1 ||
abs(v.y) > 1 ||
abs(v.z) > 1)
{
@@ -222,7 +229,7 @@ void preProbeSample(vec3 pos)
}
}
count++;
-
+
++neighborIdx;
}
@@ -244,56 +251,56 @@ void preProbeSample(vec3 pos)
// original reference implementation:
/*
-bool intersect(const Ray &ray) const
-{
- float t0, t1; // solutions for t if the ray intersects
-#if 0
+bool intersect(const Ray &ray) const
+{
+ float t0, t1; // solutions for t if the ray intersects
+#if 0
// geometric solution
- Vec3f L = center - orig;
- float tca = L.dotProduct(dir);
+ Vec3f L = center - orig;
+ float tca = L.dotProduct(dir);
// if (tca < 0) return false;
- float d2 = L.dotProduct(L) - tca * tca;
- if (d2 > radius2) return false;
- float thc = sqrt(radius2 - d2);
- t0 = tca - thc;
- t1 = tca + thc;
-#else
+ float d2 = L.dotProduct(L) - tca * tca;
+ if (d2 > radius2) return false;
+ float thc = sqrt(radius2 - d2);
+ t0 = tca - thc;
+ t1 = tca + thc;
+#else
// analytic solution
- Vec3f L = orig - center;
- float a = dir.dotProduct(dir);
- float b = 2 * dir.dotProduct(L);
- float c = L.dotProduct(L) - radius2;
- if (!solveQuadratic(a, b, c, t0, t1)) return false;
-#endif
- if (t0 > t1) std::swap(t0, t1);
-
- if (t0 < 0) {
- t0 = t1; // if t0 is negative, let's use t1 instead
- if (t0 < 0) return false; // both t0 and t1 are negative
- }
-
- t = t0;
-
- return true;
+ Vec3f L = orig - center;
+ float a = dir.dotProduct(dir);
+ float b = 2 * dir.dotProduct(L);
+ float c = L.dotProduct(L) - radius2;
+ if (!solveQuadratic(a, b, c, t0, t1)) return false;
+#endif
+ if (t0 > t1) std::swap(t0, t1);
+
+ if (t0 < 0) {
+ t0 = t1; // if t0 is negative, let's use t1 instead
+ if (t0 < 0) return false; // both t0 and t1 are negative
+ }
+
+ t = t0;
+
+ return true;
} */
// adapted -- assume that origin is inside sphere, return intersection of ray with edge of sphere
vec3 sphereIntersect(vec3 origin, vec3 dir, vec3 center, float radius2)
-{
- float t0, t1; // solutions for t if the ray intersects
+{
+ float t0, t1; // solutions for t if the ray intersects
- vec3 L = center - origin;
+ vec3 L = center - origin;
float tca = dot(L,dir);
- float d2 = dot(L,L) - tca * tca;
-
- float thc = sqrt(radius2 - d2);
- t0 = tca - thc;
- t1 = tca + thc;
+ float d2 = dot(L,L) - tca * tca;
+ float thc = sqrt(radius2 - d2);
+ t0 = tca - thc;
+ t1 = tca + thc;
+
vec3 v = origin + dir * t1;
- return v;
-}
+ return v;
+}
void swap(inout float a, inout float b)
{
@@ -305,17 +312,17 @@ void swap(inout float a, inout float b)
// debug implementation, make no assumptions about origin
void sphereIntersectDebug(vec3 origin, vec3 dir, vec3 center, float radius2, float depth, inout vec4 col)
{
- float t[2]; // solutions for t if the ray intersects
+ float t[2]; // solutions for t if the ray intersects
// geometric solution
- vec3 L = center - origin;
+ vec3 L = center - origin;
float tca = dot(L, dir);
// if (tca < 0) return false;
- float d2 = dot(L, L) - tca * tca;
- if (d2 > radius2) return;
- float thc = sqrt(radius2 - d2);
- t[0] = tca - thc;
- t[1] = tca + thc;
+ float d2 = dot(L, L) - tca * tca;
+ if (d2 > radius2) return;
+ float thc = sqrt(radius2 - d2);
+ t[0] = tca - thc;
+ t[1] = tca + thc;
for (int i = 0; i < 2; ++i)
{
@@ -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);
}
@@ -404,8 +411,8 @@ void debugBoxCol(vec3 ro, vec3 rd, float t, vec3 p, inout vec4 col)
bool behind = dot(v,v) > dot(pos,pos);
float w = 0.25;
-
- if (behind)
+
+ if (behind)
{
w *= 0.5;
w /= (length(v)-length(pos))*0.5+1.0;
@@ -419,7 +426,7 @@ void debugBoxCol(vec3 ro, vec3 rd, float t, vec3 p, inout vec4 col)
// cribbed from https://iquilezles.org/articles/intersectors/
// axis aligned box centered at the origin, with size boxSize
-void boxIntersectionDebug( in vec3 ro, in vec3 p, vec3 boxSize, inout vec4 col)
+void boxIntersectionDebug( in vec3 ro, in vec3 p, vec3 boxSize, inout vec4 col)
{
vec3 rd = normalize(p-ro);
@@ -443,10 +450,10 @@ 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;
pos = (clipToLocal * vec4(pos, 1.0)).xyz;
@@ -462,16 +469,16 @@ 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)
+ float r1 = r * 0.5; // 50% of radius (outer sphere to start interpolating down)
vec3 delta = pos.xyz - origin;
float d2 = max(length(delta), 0.001);
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;
@@ -488,7 +495,7 @@ float sphereWeight(vec3 pos, vec3 dir, vec3 origin, float r, int i, out float dw
// lod - which mip to sample (lower is higher res, sharper reflections)
// c - center of probe
// r2 - radius of probe squared
-// i - index of probe
+// i - index of probe
vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, int i)
{
// parallax adjustment
@@ -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);
}
@@ -507,18 +514,18 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c,
float rr = r * r;
- v = sphereIntersect(pos, dir, c,
+ v = sphereIntersect(pos, dir, 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;
vec3 d = normalize(v);
v = env_mat * v;
-
+
vec4 ret = textureLod(reflectionProbes, vec4(v.xyz, refIndex[i].x), lod) * refParams[i].y;
return ret.rgb;
@@ -529,7 +536,7 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c,
// dir - pixel normal
// w - weight of sample (distance and angular attenuation)
// dw - weight of sample (distance only)
-// i - index of probe
+// i - index of probe
vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int i, vec3 amblit)
{
// parallax adjustment
@@ -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
@@ -547,16 +554,16 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int
// pad sphere for manual probe extending into automatic probe space
float rr = r * r;
- v = sphereIntersect(pos, dir, c,
+ v = sphereIntersect(pos, dir, 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;
v = env_mat * v;
-
+
vec3 col = textureLod(irradianceProbes, vec4(v.xyz, refIndex[i].x), 0).rgb * refParams[i].x;
col = mix(amblit, col, min(refParams[i].x, 1.0));
@@ -618,7 +625,7 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod)
col[1] *= 1.0/wsum[1];
col[0] = vec3(0);
}
-
+
return col[1]+col[0];
}
@@ -647,7 +654,7 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit)
{
continue;
}
-
+
{
float w = 0;
float dw = 0;
@@ -677,10 +684,53 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit)
col[1] *= 1.0/wsum[1];
col[0] = vec3(0);
}
-
+
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,8 +851,9 @@ 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)
{
legacyenv = sampleProbes(pos, normalize(refnormpersp), 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 63fa4ecc55..deb276ef9d 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflPostF.glsl
@@ -40,36 +40,33 @@ 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);
float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness);
-void main()
+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;
-
+
vec4 diffuse = texture(diffuseRect, tc);
vec3 specCol = spec.rgb;
vec4 fcol = texture(diffuseMap, tc);
- if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR))
+ if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR))
{
vec3 orm = specCol.rgb;
float perceptualRoughness = orm.g;
@@ -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 47b5934b84..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);
@@ -104,8 +105,8 @@ vec3 pbrBaseLight(vec3 diffuseColor,
vec3 additive,
vec3 atten);
-vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
- float perceptualRoughness,
+vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
+ float perceptualRoughness,
float metallic,
vec3 n, // normal
vec3 v, // surface point to camera
@@ -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,15 +169,15 @@ 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);
@@ -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;
}
@@ -199,7 +205,7 @@ void main()
{
// legacy shaders are still writng sRGB to gbuffer
baseColor.rgb = srgb_to_linear(baseColor.rgb);
-
+
spec.rgb = srgb_to_linear(spec.rgb);
float da = clamp(dot(norm.xyz, light_dir.xyz), 0.0, 1.0);
@@ -218,7 +224,7 @@ void main()
vec3 sun_contrib = min(da, scol) * sunlit_linear;
color.rgb += sun_contrib;
color.rgb *= baseColor.rgb;
-
+
vec3 refnormpersp = reflect(pos.xyz, norm.xyz);
if (spec.a > 0.0)
@@ -244,10 +250,11 @@ void main()
// add radiance map
applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz);
+
}
color.rgb = mix(color.rgb, baseColor.rgb, baseColor.a);
-
+
if (envIntensity > 0.0)
{ // add environment map
applyLegacyEnv(color, legacyenv, spec, pos.xyz, norm.xyz, envIntensity);
diff --git a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl
index 871c7ce812..319fa86148 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl
@@ -1,28 +1,28 @@
-/**
+/**
* @file class3\deferred\spotLightF.glsl
*
* $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$
*/
-
+
/*[EXTRA_CODE_HERE]*/
out vec4 frag_color;
@@ -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);
@@ -83,8 +83,8 @@ vec4 getPosition(vec2 pos_screen);
const float M_PI = 3.14159265;
-vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
- float perceptualRoughness,
+vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor,
+ float perceptualRoughness,
float metallic,
vec3 n, // normal
vec3 v, // surface point to camera
@@ -112,18 +112,17 @@ void main()
}
float shadow = 1.0;
-
+
if (proj_shadow_idx >= 0)
{
vec4 shd = texture(lightMap, tc);
shadow = (proj_shadow_idx==0)?shd.b:shd.a;
shadow += shadow_fade;
- shadow = clamp(shadow, 0.0, 1.0);
+ 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,13 +144,12 @@ 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;
vec3 f0 = vec3(0.04);
vec3 baseColor = diffuse.rgb;
-
+
vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0);
diffuseColor *= 1.0 - metallic;
@@ -169,7 +167,7 @@ void main()
if (nl > 0.0)
{
amb_da += (nl*0.5 + 0.5) * proj_ambiance;
-
+
dlit = getProjectedLightDiffuseColor( l_dist, proj_tc.xy );
vec3 intensity = dist_atten * dlit * 3.25 * shadow; // Legacy attenuation, magic number to balance with legacy materials
@@ -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);
@@ -205,11 +205,11 @@ void main()
// unshadowed for consistency between forward and deferred?
amb_da += (nl*0.5+0.5) /* * (1.0-shadow) */ * proj_ambiance;
}
-
+
amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy );
final_color += diffuse.rgb * amb_rgb * max(dot(-normalize(lv), n), 0.0);
}
-
+
if (spec.a > 0.0)
{
dlit *= min(nl*6.0, 1.0) * dist_atten;
@@ -218,7 +218,7 @@ void main()
float gtdenom = 2 * nh;
float gt = max(0, min(gtdenom * nv / vh, gtdenom * nl / vh));
-
+
if (nh > 0.0)
{
float scol = fres*texture(lightFunc, vec2(nh, spec.a)).r*gt/(nh*nl);
@@ -226,26 +226,26 @@ void main()
speccol = clamp(speccol, vec3(0), vec3(1));
final_color += speccol;
}
- }
+ }
if (envIntensity > 0.0)
{
vec3 ref = reflect(normalize(pos), n);
-
+
//project from point pos in direction ref to plane proj_p, proj_n
vec3 pdelta = proj_p-pos;
float ds = dot(ref, proj_n);
-
+
if (ds < 0.0)
{
vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds;
-
+
vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0));
if (stc.z > 0.0)
{
stc /= stc.w;
-
+
if (stc.x < 1.0 &&
stc.y < 1.0 &&
stc.x > 0.0 &&
diff --git a/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl
index 61059e2339..f6bef1e498 100644
--- a/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl
+++ b/indra/newview/app_settings/shaders/class3/deferred/waterHazeF.glsl
@@ -43,7 +43,7 @@ void main()
float depth = getDepth(tc.xy);
if (above_water > 0)
- {
+ {
// we want to depth test when the camera is above water, but some GPUs have a hard time
// with depth testing against render targets that are bound for sampling in the same shader
// so we do it manually here
@@ -56,10 +56,9 @@ void main()
}
vec4 pos = getPositionWithDepth(tc, depth);
- vec4 norm = texture(normalMap, tc);
vec4 fogged = getWaterFogView(pos.xyz);
frag_color = max(fogged, vec4(0)); //output linear since local lights will be added to this shader's results
-
+
}
diff --git a/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl b/indra/newview/app_settings/shaders/class3/environment/underWaterF.glsl
index 3bf606a252..728d70ebb2 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 88c38c46a2..a5592188a9 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/README.md b/indra/newview/gltf/README.md
new file mode 100644
index 0000000000..a2d43be1d6
--- /dev/null
+++ b/indra/newview/gltf/README.md
@@ -0,0 +1,156 @@
+# Linden Lab GLTF Implementation
+
+Currently in prototype stage. Much functionality is missing (blend shapes,
+multiple texture coordinates, etc).
+
+GLTF Specification can be found here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html.
+If this implementation disagrees with the GLTF Specification, the specification is correct.
+
+Class structure and naming should match the GLTF Specification as closely as possible while
+conforming to the LL coding standards. All code in headers should be contained in the
+LL::GLTF namespace.
+
+The implementation serves both the client and the server.
+
+## Design Principles
+
+- The implementation MUST be capable of round-trip serialization with no data loss beyond F64 to F32 conversions.
+- The implementation MUST use the same indexing scheme as the GLTF specification. Do not store pointers where the
+- GLTF specification stores indices, store indices.
+- Limit dependencies on llcommon as much as possible. Prefer std::, boost::, and (soon) glm:: over LL facsimiles.
+- Usage of LLSD is forbidden in the LL::GLTF namespace.
+- Use "using namespace" liberally in .cpp files, but never in .h files.
+- "using Foo = Bar" is permissible in .h files within the LL::GLTF namespace.
+
+## Loading, Copying, and Serialization
+Each class should provide two functions (Primitive shown for example):
+
+```
+// Serialize to the provided json object.
+// "obj" should be "this" in json form on return
+// Do not serialize default values
+void serialize(boost::json::object& obj) const;
+
+// Initialize from a provided json value
+const Primitive& operator=(const Value& src);
+```
+
+"serialize" implementations should use "write":
+
+```
+void Primitive::serialize(boost::json::object& dst) const
+{
+ write(mMaterial, "material", dst, -1);
+ write(mMode, "mode", dst, TINYGLTF_MODE_TRIANGLES);
+ write(mIndices, "indices", dst, INVALID_INDEX);
+ write(mAttributes, "attributes", dst);
+}
+```
+
+And operator= implementations should use "copy":
+
+```
+const Primitive& Primitive::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "material", mMaterial);
+ copy(src, "mode", mMode);
+ copy(src, "indices", mIndices);
+ copy(src, "attributes", mAttributes);
+
+ mGLMode = gltf_mode_to_gl_mode(mMode);
+ }
+ return *this;
+}
+```
+
+Parameters to "write" and "copy" MUST be ordered "src" before "dst"
+so the code reads as "write src to dst" and "copy src to dst".
+
+When reading string constants from GLTF json (i.e. "OPAQUE", "TRIANGLES"), these
+strings should be converted to enums inside operator=. It is permissible to
+store the original strings during prototyping to aid in development, but eventually
+we'll purge these strings from the implementation. However, implementations MUST
+preserve any and all "name" members.
+
+"write" and "copy" implementations MUST be stored in buffer_util.h.
+As implementers encounter new data types, you'll see compiler errors
+pointing at templates in buffer_util.h. See vec3 as a known good
+example of how to add support for a new type (there are bad examples, so beware):
+
+```
+// vec3
+template<>
+inline bool copy(const Value& src, vec3& dst)
+{
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 3)
+ {
+ if (arr[0].is_double() &&
+ arr[1].is_double() &&
+ arr[2].is_double())
+ {
+ dst = vec3(arr[0].get_double(), arr[1].get_double(), arr[2].get_double());
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+template<>
+inline bool write(const vec3& src, Value& dst)
+{
+ dst = boost::json::array();
+ boost::json::array& arr = dst.as_array();
+ arr.resize(3);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ return true;
+}
+
+```
+
+"write" MUST return true if ANY data was written
+"copy" MUST return true if ANY data was copied
+
+Speed is important, but so is safety. In writers, try to avoid redundant copies
+(prefer resize over push_back, convert dst to an empty array and fill it, don't
+make an array on the stack and copy it into dst).
+
+boost::json WILL throw exceptions if you call as_foo() on a mismatched type but
+WILL NOT throw exceptions on get_foo with a mismatched type. ALWAYS check is_foo
+before calling as_foo or get_foo. DO NOT add exception handlers. If boost throws
+an exception in serialization, the fix is to add type checks. If we see a large
+number of crash reports from boost::json exceptions, each of those reports
+indicates a place where we're missing "is_foo" checks. They are gold. Do not
+bury them with an exception handler.
+
+DO NOT rely on existing type conversion tools in the LL codebase -- LL data models
+conflict with the GLTF specification so we MUST provide conversions independent of
+our existing implementations.
+
+### JSON Serialization ###
+
+
+
+NEVER include buffer_util.h from a header.
+
+Loading from and saving to disk (import/export) is currently done using tinygltf, but this is not a long term
+solution. Eventually the implementation should rely solely on boost::json for reading and writing .gltf
+files and should handle .bin files natively.
+
+When serializing Images and Buffers to the server, clients MUST store a single UUID "uri" field and nothing else.
+The server MUST reject any data that violates this requirement.
+
+Clients MUST remove any Images from Buffers prior to upload to the server.
+Servers MAY reject Assets that contain Buffers with unreferenced data.
+
+... to be continued.
+
+
+
diff --git a/indra/newview/gltf/accessor.cpp b/indra/newview/gltf/accessor.cpp
new file mode 100644
index 0000000000..9f1cb0c1cd
--- /dev/null
+++ b/indra/newview/gltf/accessor.cpp
@@ -0,0 +1,297 @@
+/**
+ * @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"
+#include "buffer_util.h"
+#include "llfilesystem.h"
+
+using namespace LL::GLTF;
+using namespace boost::json;
+
+namespace LL
+{
+ namespace GLTF
+ {
+ Accessor::Type gltf_type_to_enum(const std::string& type)
+ {
+ if (type == "SCALAR")
+ {
+ return Accessor::Type::SCALAR;
+ }
+ else if (type == "VEC2")
+ {
+ return Accessor::Type::VEC2;
+ }
+ else if (type == "VEC3")
+ {
+ return Accessor::Type::VEC3;
+ }
+ else if (type == "VEC4")
+ {
+ return Accessor::Type::VEC4;
+ }
+ else if (type == "MAT2")
+ {
+ return Accessor::Type::MAT2;
+ }
+ else if (type == "MAT3")
+ {
+ return Accessor::Type::MAT3;
+ }
+ else if (type == "MAT4")
+ {
+ return Accessor::Type::MAT4;
+ }
+
+ LL_WARNS("GLTF") << "Unknown accessor type: " << type << LL_ENDL;
+ llassert(false);
+
+ return Accessor::Type::SCALAR;
+ }
+
+ std::string enum_to_gltf_type(Accessor::Type type)
+ {
+ switch (type)
+ {
+ case Accessor::Type::SCALAR:
+ return "SCALAR";
+ case Accessor::Type::VEC2:
+ return "VEC2";
+ case Accessor::Type::VEC3:
+ return "VEC3";
+ case Accessor::Type::VEC4:
+ return "VEC4";
+ case Accessor::Type::MAT2:
+ return "MAT2";
+ case Accessor::Type::MAT3:
+ return "MAT3";
+ case Accessor::Type::MAT4:
+ return "MAT4";
+ }
+
+ LL_WARNS("GLTF") << "Unknown accessor type: " << (S32)type << LL_ENDL;
+ llassert(false);
+
+ return "SCALAR";
+ }
+ }
+}
+
+void Buffer::erase(Asset& asset, S32 offset, S32 length)
+{
+ S32 idx = this - &asset.mBuffers[0];
+
+ mData.erase(mData.begin() + offset, mData.begin() + offset + length);
+
+ mByteLength = mData.size();
+
+ for (BufferView& view : asset.mBufferViews)
+ {
+ if (view.mBuffer == idx)
+ {
+ if (view.mByteOffset >= offset)
+ {
+ view.mByteOffset -= length;
+ }
+ }
+ }
+}
+
+bool Buffer::prep(Asset& asset)
+{
+ if (mByteLength == 0)
+ {
+ return false;
+ }
+
+ LLUUID id;
+ if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
+ { // loaded from an asset, fetch the buffer data from the asset store
+ LLFileSystem file(id, LLAssetType::AT_GLTF_BIN, LLFileSystem::READ);
+
+ if (mByteLength > file.getSize())
+ {
+ LL_WARNS("GLTF") << "Unexpected glbin size: " << id << " is " << file.getSize() << " bytes, expected " << mByteLength << LL_ENDL;
+ return false;
+ }
+
+ mData.resize(mByteLength);
+ if (!file.read((U8*)mData.data(), mData.size()))
+ {
+ LL_WARNS("GLTF") << "Failed to load buffer data from asset: " << id << LL_ENDL;
+ return false;
+ }
+ }
+ else if (mUri.find("data:") == 0)
+ { // loaded from a data URI, load the texture from the data
+ LL_WARNS() << "Data URIs not yet supported" << LL_ENDL;
+ return false;
+ }
+ else if (!asset.mFilename.empty() &&
+ !mUri.empty()) // <-- uri could be empty if we're loading from .glb
+ {
+ std::string dir = gDirUtilp->getDirName(asset.mFilename);
+ std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
+
+ std::ifstream file(bin_file, std::ios::binary);
+ if (!file.is_open())
+ {
+ LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
+ return false;
+ }
+
+ file.seekg(0, std::ios::end);
+ if (mByteLength > file.tellg())
+ {
+ LL_WARNS("GLTF") << "Unexpected file size: " << bin_file << " is " << file.tellg() << " bytes, expected " << mByteLength << LL_ENDL;
+ return false;
+ }
+ file.seekg(0, std::ios::beg);
+
+ mData.resize(mByteLength);
+ file.read((char*)mData.data(), mData.size());
+ }
+
+ // POSTCONDITION: on success, mData.size == mByteLength
+ llassert(mData.size() == mByteLength);
+ return true;
+}
+
+bool Buffer::save(Asset& asset, const std::string& folder)
+{
+ if (mUri.substr(0, 5) == "data:")
+ {
+ LL_WARNS("GLTF") << "Data URIs not yet supported" << LL_ENDL;
+ return false;
+ }
+
+ std::string bin_file = folder + gDirUtilp->getDirDelimiter();
+
+ if (mUri.empty())
+ {
+ if (mName.empty())
+ {
+ S32 idx = this - &asset.mBuffers[0];
+ mUri = llformat("buffer_%d.bin", idx);
+ }
+ else
+ {
+ mUri = mName + ".bin";
+ }
+ }
+
+ bin_file += mUri;
+
+ std::ofstream file(bin_file, std::ios::binary);
+ if (!file.is_open())
+ {
+ LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
+ return false;
+ }
+
+ file.write((char*)mData.data(), mData.size());
+
+ return true;
+}
+
+void Buffer::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mUri, "uri", dst);
+ write_always(mByteLength, "byteLength", dst);
+};
+
+const Buffer& Buffer::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "uri", mUri);
+ copy(src, "byteLength", mByteLength);
+
+ // NOTE: DO NOT attempt to handle the uri here.
+ // The uri is a reference to a file that is not loaded until
+ // after the json document is parsed
+ }
+ return *this;
+}
+
+void BufferView::serialize(object& dst) const
+{
+ write_always(mBuffer, "buffer", dst);
+ write_always(mByteLength, "byteLength", dst);
+ write(mByteOffset, "byteOffset", dst, 0);
+ write(mByteStride, "byteStride", dst, 0);
+ write(mTarget, "target", dst, -1);
+ write(mName, "name", dst);
+}
+
+const BufferView& BufferView::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "buffer", mBuffer);
+ copy(src, "byteLength", mByteLength);
+ copy(src, "byteOffset", mByteOffset);
+ copy(src, "byteStride", mByteStride);
+ copy(src, "target", mTarget);
+ copy(src, "name", mName);
+ }
+ return *this;
+}
+
+void Accessor::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mBufferView, "bufferView", dst, INVALID_INDEX);
+ write(mByteOffset, "byteOffset", dst, 0);
+ write_always(mComponentType, "componentType", dst);
+ write_always(mCount, "count", dst);
+ write_always(enum_to_gltf_type(mType), "type", dst);
+ write(mNormalized, "normalized", dst, false);
+ write(mMax, "max", dst);
+ write(mMin, "min", dst);
+}
+
+const Accessor& Accessor::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "bufferView", mBufferView);
+ copy(src, "byteOffset", mByteOffset);
+ copy(src, "componentType", mComponentType);
+ copy(src, "count", mCount);
+ copy(src, "type", mType);
+ copy(src, "normalized", mNormalized);
+ copy(src, "max", mMax);
+ copy(src, "min", mMin);
+ }
+ return *this;
+}
+
diff --git a/indra/newview/gltf/accessor.h b/indra/newview/gltf/accessor.h
new file mode 100644
index 0000000000..ec68c5f624
--- /dev/null
+++ b/indra/newview/gltf/accessor.h
@@ -0,0 +1,120 @@
+#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 "llstrider.h"
+#include "boost/json.hpp"
+
+#include "common.h"
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ constexpr S32 INVALID_INDEX = -1;
+
+ class Buffer
+ {
+ public:
+ std::vector<U8> mData;
+ std::string mName;
+ std::string mUri;
+ S32 mByteLength = 0;
+
+ // erase the given range from this buffer.
+ // also updates all buffer views in given asset that reference this buffer
+ void erase(Asset& asset, S32 offset, S32 length);
+
+ bool prep(Asset& asset);
+
+ void serialize(boost::json::object& obj) const;
+ const Buffer& operator=(const Value& value);
+
+ bool save(Asset& asset, const std::string& folder);
+ };
+
+ class BufferView
+ {
+ public:
+ S32 mBuffer = INVALID_INDEX;
+ S32 mByteLength = 0;
+ S32 mByteOffset = 0;
+ S32 mByteStride = 0;
+ S32 mTarget = -1;
+
+ std::string mName;
+
+ void serialize(boost::json::object& obj) const;
+ const BufferView& operator=(const Value& value);
+ };
+
+ class Accessor
+ {
+ public:
+ enum class Type : U8
+ {
+ SCALAR,
+ VEC2,
+ VEC3,
+ VEC4,
+ MAT2,
+ MAT3,
+ MAT4
+ };
+
+ enum class ComponentType : U32
+ {
+ BYTE = 5120,
+ UNSIGNED_BYTE = 5121,
+ SHORT = 5122,
+ UNSIGNED_SHORT = 5123,
+ UNSIGNED_INT = 5125,
+ FLOAT = 5126
+ };
+
+ std::vector<double> mMax;
+ std::vector<double> mMin;
+ std::string mName;
+ S32 mBufferView = INVALID_INDEX;
+ S32 mByteOffset = 0;
+ ComponentType mComponentType = ComponentType::BYTE;
+ S32 mCount = 0;
+ Type mType = Type::SCALAR;
+ bool mNormalized = false;
+
+ void serialize(boost::json::object& obj) const;
+ const Accessor& operator=(const Value& value);
+ };
+
+ // convert from "SCALAR", "VEC2", etc to Accessor::Type
+ Accessor::Type gltf_type_to_enum(const std::string& type);
+
+ // convert from Accessor::Type to "SCALAR", "VEC2", etc
+ std::string enum_to_gltf_type(Accessor::Type type);
+ }
+}
diff --git a/indra/newview/gltf/animation.cpp b/indra/newview/gltf/animation.cpp
new file mode 100644
index 0000000000..45e9e1ddef
--- /dev/null
+++ b/indra/newview/gltf/animation.cpp
@@ -0,0 +1,460 @@
+/**
+ * @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"
+#include "../llskinningutil.h"
+
+using namespace LL::GLTF;
+using namespace boost::json;
+
+bool Animation::prep(Asset& asset)
+{
+ if (!mSamplers.empty())
+ {
+ mMinTime = FLT_MAX;
+ mMaxTime = -FLT_MAX;
+ for (auto& sampler : mSamplers)
+ {
+ if (!sampler.prep(asset))
+ {
+ return false;
+ }
+ mMinTime = llmin(sampler.mMinTime, mMinTime);
+ mMaxTime = llmax(sampler.mMaxTime, mMaxTime);
+ }
+ }
+ else
+ {
+ mMinTime = mMaxTime = 0.f;
+ }
+
+ for (auto& channel : mRotationChannels)
+ {
+ if (!channel.prep(asset, mSamplers[channel.mSampler]))
+ {
+ return false;
+ }
+ }
+
+ for (auto& channel : mTranslationChannels)
+ {
+ if (!channel.prep(asset, mSamplers[channel.mSampler]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+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);
+ }
+};
+
+bool Animation::Sampler::prep(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);
+
+ return true;
+}
+
+
+void Animation::Sampler::serialize(object& obj) const
+{
+ write(mInput, "input", obj, INVALID_INDEX);
+ write(mOutput, "output", obj, INVALID_INDEX);
+ write(mInterpolation, "interpolation", obj, std::string("LINEAR"));
+ write(mMinTime, "min_time", obj);
+ write(mMaxTime, "max_time", obj);
+}
+
+const Animation::Sampler& Animation::Sampler::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "input", mInput);
+ copy(src, "output", mOutput);
+ copy(src, "interpolation", mInterpolation);
+ copy(src, "min_time", mMinTime);
+ copy(src, "max_time", mMaxTime);
+ }
+ return *this;
+}
+
+bool Animation::Channel::Target::operator==(const Channel::Target& rhs) const
+{
+ return mNode == rhs.mNode && mPath == rhs.mPath;
+}
+
+bool Animation::Channel::Target::operator!=(const Channel::Target& rhs) const
+{
+ return !(*this == rhs);
+}
+
+void Animation::Channel::Target::serialize(object& obj) const
+{
+ write(mNode, "node", obj, INVALID_INDEX);
+ write(mPath, "path", obj);
+}
+
+const Animation::Channel::Target& Animation::Channel::Target::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "node", mNode);
+ copy(src, "path", mPath);
+ }
+ return *this;
+}
+
+void Animation::Channel::serialize(object& obj) const
+{
+ write(mSampler, "sampler", obj, INVALID_INDEX);
+ write(mTarget, "target", obj);
+}
+
+const Animation::Channel& Animation::Channel::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "sampler", mSampler);
+ copy(src, "target", mTarget);
+ }
+ return *this;
+}
+
+void Animation::Sampler::getFrameInfo(Asset& asset, F32 time, U32& frameIndex, F32& t)
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ if (time < mMinTime)
+ {
+ frameIndex = 0;
+ 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;
+ }
+}
+
+bool Animation::RotationChannel::prep(Asset& asset, Animation::Sampler& sampler)
+{
+ Accessor& accessor = asset.mAccessors[sampler.mOutput];
+
+ copy(asset, accessor, mRotations);
+
+ return true;
+}
+
+void Animation::RotationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
+{
+ 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
+ quat qf = glm::slerp(mRotations[frameIndex], mRotations[frameIndex + 1], t);
+
+ qf = glm::normalize(qf);
+
+ node.setRotation(qf);
+ }
+}
+
+bool Animation::TranslationChannel::prep(Asset& asset, Animation::Sampler& sampler)
+{
+ Accessor& accessor = asset.mAccessors[sampler.mOutput];
+
+ copy(asset, accessor, mTranslations);
+
+ return true;
+}
+
+void Animation::TranslationChannel::apply(Asset& asset, Sampler& sampler, F32 time)
+{
+ 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 vec3& v0 = mTranslations[frameIndex];
+ const vec3& v1 = mTranslations[frameIndex + 1];
+
+ vec3 vf = v0 + t * (v1 - v0);
+
+ node.setTranslation(vf);
+ }
+}
+
+bool Animation::ScaleChannel::prep(Asset& asset, Animation::Sampler& sampler)
+{
+ Accessor& accessor = asset.mAccessors[sampler.mOutput];
+
+ copy(asset, accessor, mScales);
+
+ return true;
+}
+
+void Animation::ScaleChannel::apply(Asset& asset, Sampler& sampler, F32 time)
+{
+ 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 vec3& v0 = mScales[frameIndex];
+ const vec3& v1 = mScales[frameIndex + 1];
+
+ vec3 vf = v0 + t * (v1 - v0);
+
+ node.setScale(vf);
+ }
+}
+
+void Animation::serialize(object& obj) const
+{
+ write(mName, "name", obj);
+ write(mSamplers, "samplers", obj);
+
+ std::vector<Channel> channels;
+ channels.insert(channels.end(), mRotationChannels.begin(), mRotationChannels.end());
+ channels.insert(channels.end(), mTranslationChannels.begin(), mTranslationChannels.end());
+ channels.insert(channels.end(), mScaleChannels.begin(), mScaleChannels.end());
+
+ write(channels, "channels", obj);
+}
+
+const Animation& Animation::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ const object& obj = src.as_object();
+
+ copy(obj, "name", mName);
+ copy(obj, "samplers", mSamplers);
+
+ // make a temporory copy of generic channels
+ std::vector<Channel> channels;
+ copy(obj, "channels", channels);
+
+ // break up into channel specific implementations
+ for (auto& channel: channels)
+ {
+ if (channel.mTarget.mPath == "rotation")
+ {
+ mRotationChannels.push_back(channel);
+ }
+ else if (channel.mTarget.mPath == "translation")
+ {
+ mTranslationChannels.push_back(channel);
+ }
+ else if (channel.mTarget.mPath == "scale")
+ {
+ mScaleChannels.push_back(channel);
+ }
+ }
+ }
+ return *this;
+}
+
+Skin::~Skin()
+{
+ if (mUBO)
+ {
+ glDeleteBuffers(1, &mUBO);
+ }
+}
+
+void Skin::uploadMatrixPalette(Asset& asset)
+{
+ // prepare matrix palette
+
+ U32 max_joints = LLSkinningUtil::getMaxGLTFJointCount();
+
+ if (mUBO == 0)
+ {
+ glGenBuffers(1, &mUBO);
+ }
+
+ U32 joint_count = llmin(max_joints, mJoints.size());
+
+ std::vector<mat4> t_mp;
+
+ t_mp.resize(joint_count);
+
+ for (U32 i = 0; i < joint_count; ++i)
+ {
+ Node& joint = asset.mNodes[mJoints[i]];
+ // build matrix palette in asset space
+ t_mp[i] = joint.mAssetMatrix * mInverseBindMatricesData[i];
+ }
+
+ std::vector<F32> glmp;
+
+ glmp.resize(joint_count * 12);
+
+ F32* mp = glmp.data();
+
+ for (U32 i = 0; i < joint_count; ++i)
+ {
+ F32* m = glm::value_ptr(t_mp[i]);
+
+ U32 idx = i * 12;
+
+ mp[idx + 0] = m[0];
+ mp[idx + 1] = m[1];
+ mp[idx + 2] = m[2];
+ mp[idx + 3] = m[12];
+
+ mp[idx + 4] = m[4];
+ mp[idx + 5] = m[5];
+ mp[idx + 6] = m[6];
+ mp[idx + 7] = m[13];
+
+ mp[idx + 8] = m[8];
+ mp[idx + 9] = m[9];
+ mp[idx + 10] = m[10];
+ mp[idx + 11] = m[14];
+ }
+
+ glBindBuffer(GL_UNIFORM_BUFFER, mUBO);
+ glBufferData(GL_UNIFORM_BUFFER, glmp.size() * sizeof(F32), glmp.data(), GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
+
+bool Skin::prep(Asset& asset)
+{
+ if (mInverseBindMatrices != INVALID_INDEX)
+ {
+ Accessor& accessor = asset.mAccessors[mInverseBindMatrices];
+ copy(asset, accessor, mInverseBindMatricesData);
+ }
+
+ return true;
+}
+
+const Skin& Skin::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "skeleton", mSkeleton);
+ copy(src, "inverseBindMatrices", mInverseBindMatrices);
+ copy(src, "joints", mJoints);
+ }
+ return *this;
+}
+
+void Skin::serialize(object& obj) const
+{
+ write(mInverseBindMatrices, "inverseBindMatrices", obj, INVALID_INDEX);
+ write(mJoints, "joints", obj);
+ write(mName, "name", obj);
+ write(mSkeleton, "skeleton", obj, INVALID_INDEX);
+}
diff --git a/indra/newview/gltf/animation.h b/indra/newview/gltf/animation.h
new file mode 100644
index 0000000000..d5426fd4ce
--- /dev/null
+++ b/indra/newview/gltf/animation.h
@@ -0,0 +1,162 @@
+#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;
+
+ bool prep(Asset& asset);
+
+ void serialize(boost::json::object& dst) const;
+ const Sampler& operator=(const Value& value);
+
+ // get the frame index and time for the specified time
+ // asset -- the asset to reference for Accessors
+ // 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;
+
+ bool operator==(const Target& other) const;
+ bool operator!=(const Target& other) const;
+
+ void serialize(boost::json::object& dst) const;
+ const Target& operator=(const Value& value);
+ };
+
+ S32 mSampler = INVALID_INDEX;
+ Target mTarget;
+
+ void serialize(boost::json::object& dst) const;
+ const Channel& operator=(const Value& value);
+ };
+
+ class RotationChannel : public Channel
+ {
+ public:
+ RotationChannel() = default;
+ RotationChannel(const Channel& channel) : Channel(channel) {}
+
+ std::vector<quat> mRotations;
+
+ // prepare data needed for rendering
+ // asset -- asset to reference for Accessors
+ // sampler -- Sampler associated with this channel
+ bool prep(Asset& asset, Sampler& sampler);
+
+ void apply(Asset& asset, Sampler& sampler, F32 time);
+ };
+
+ class TranslationChannel : public Channel
+ {
+ public:
+ TranslationChannel() = default;
+ TranslationChannel(const Channel& channel) : Channel(channel) {}
+
+ std::vector<vec3> mTranslations;
+
+ // prepare data needed for rendering
+ // asset -- asset to reference for Accessors
+ // sampler -- Sampler associated with this channel
+ bool prep(Asset& asset, Sampler& sampler);
+
+ void apply(Asset& asset, Sampler& sampler, F32 time);
+ };
+
+ class ScaleChannel : public Channel
+ {
+ public:
+ ScaleChannel() = default;
+ ScaleChannel(const Channel& channel) : Channel(channel) {}
+
+ std::vector<vec3> mScales;
+
+ // prepare data needed for rendering
+ // asset -- asset to reference for Accessors
+ // sampler -- Sampler associated with this channel
+ bool prep(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;
+
+ void serialize(boost::json::object& dst) const;
+ const Animation& operator=(const Value& value);
+
+ bool prep(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..a4efb25860
--- /dev/null
+++ b/indra/newview/gltf/asset.cpp
@@ -0,0 +1,1137 @@
+/**
+ * @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"
+#include "../llviewertexturelist.h"
+#include "../pipeline.h"
+#include "buffer_util.h"
+#include <boost/url.hpp>
+#include "llimagejpeg.h"
+
+using namespace LL::GLTF;
+using namespace boost::json;
+
+
+namespace LL
+{
+ namespace GLTF
+ {
+ static std::unordered_set<std::string> ExtensionsSupported = {
+ "KHR_materials_unlit"
+ };
+
+ Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
+ {
+ if (alpha_mode == "OPAQUE")
+ {
+ return Material::AlphaMode::OPAQUE;
+ }
+ else if (alpha_mode == "MASK")
+ {
+ return Material::AlphaMode::MASK;
+ }
+ else if (alpha_mode == "BLEND")
+ {
+ return Material::AlphaMode::BLEND;
+ }
+ else
+ {
+ return Material::AlphaMode::OPAQUE;
+ }
+ }
+
+ std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode)
+ {
+ switch (alpha_mode)
+ {
+ case Material::AlphaMode::OPAQUE:
+ return "OPAQUE";
+ case Material::AlphaMode::MASK:
+ return "MASK";
+ case Material::AlphaMode::BLEND:
+ return "BLEND";
+ default:
+ return "OPAQUE";
+ }
+ }
+ }
+}
+
+
+void Scene::updateTransforms(Asset& asset)
+{
+ mat4 identity = glm::identity<mat4>();
+
+ for (auto& nodeIndex : mNodes)
+ {
+ Node& node = asset.mNodes[nodeIndex];
+ node.updateTransforms(asset, identity);
+ }
+}
+
+void Scene::updateRenderTransforms(Asset& asset, const mat4& modelview)
+{
+ for (auto& nodeIndex : mNodes)
+ {
+ Node& node = asset.mNodes[nodeIndex];
+ node.updateRenderTransforms(asset, modelview);
+ }
+}
+
+void Node::updateRenderTransforms(Asset& asset, const mat4& modelview)
+{
+ mRenderMatrix = modelview * mMatrix;
+
+ for (auto& childIndex : mChildren)
+ {
+ Node& child = asset.mNodes[childIndex];
+ child.updateRenderTransforms(asset, mRenderMatrix);
+ }
+}
+
+void Node::updateTransforms(Asset& asset, const mat4& parentMatrix)
+{
+ makeMatrixValid();
+ mAssetMatrix = parentMatrix * mMatrix;
+
+ mAssetMatrixInv = glm::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 mat4& modelview)
+{
+ // use mAssetMatrix to update render transforms from node list
+ for (auto& node : mNodes)
+ {
+ node.mRenderMatrix = modelview * node.mAssetMatrix;
+ }
+}
+
+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;
+
+ LLMatrix4a ami;
+ ami.loadu(glm::value_ptr(node.mAssetMatrixInv));
+ // transform start and end to this node's local space
+ ami.affineTransform(start, local_start);
+ ami.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)
+ {
+ LLMatrix4a am;
+ am.loadu(glm::value_ptr(node.mAssetMatrix));
+ // shorten line segment on hit
+ am.affineTransform(p, asset_end);
+
+ // transform results back to asset space
+ if (intersection)
+ {
+ *intersection = asset_end;
+ }
+
+ if (normal || tangent)
+ {
+ mat4 normalMatrix = glm::transpose(node.mAssetMatrixInv);
+
+ LLMatrix4a norm_mat;
+ norm_mat.loadu(glm::value_ptr(normalMatrix));
+
+ 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)
+ {
+ mMatrix = glm::recompose(mScale, mRotation, mTranslation, vec3(0,0,0), vec4(0,0,0,1));
+ mMatrixValid = true;
+ }
+
+ llassert(mMatrixValid);
+}
+
+void Node::makeTRSValid()
+{
+ if (!mTRSValid && mMatrixValid)
+ {
+ vec3 skew;
+ vec4 perspective;
+ glm::decompose(mMatrix, mScale, mRotation, mTranslation, skew, perspective);
+
+ mTRSValid = true;
+ }
+
+ llassert(mTRSValid);
+}
+
+void Node::setRotation(const quat& q)
+{
+ makeTRSValid();
+ mRotation = q;
+ mMatrixValid = false;
+}
+
+void Node::setTranslation(const vec3& t)
+{
+ makeTRSValid();
+ mTranslation = t;
+ mMatrixValid = false;
+}
+
+void Node::setScale(const vec3& s)
+{
+ makeTRSValid();
+ mScale = s;
+ mMatrixValid = false;
+}
+
+void Node::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mMatrix, "matrix", dst, glm::identity<mat4>());
+ write(mRotation, "rotation", dst, glm::identity<quat>());
+ write(mTranslation, "translation", dst, glm::vec3(0.f, 0.f, 0.f));
+ write(mScale, "scale", dst, vec3(1.f,1.f,1.f));
+ write(mChildren, "children", dst);
+ write(mMesh, "mesh", dst, INVALID_INDEX);
+ write(mSkin, "skin", dst, INVALID_INDEX);
+}
+
+const Node& Node::operator=(const Value& src)
+{
+ copy(src, "name", mName);
+ mMatrixValid = copy(src, "matrix", mMatrix);
+ copy(src, "rotation", mRotation);
+ copy(src, "translation", mTranslation);
+ copy(src, "scale", mScale);
+ copy(src, "children", mChildren);
+ copy(src, "mesh", mMesh);
+ copy(src, "skin", mSkin);
+
+ if (!mMatrixValid)
+ {
+ mTRSValid = true;
+ }
+
+ return *this;
+}
+
+void Image::serialize(object& dst) const
+{
+ write(mUri, "uri", dst);
+ write(mMimeType, "mimeType", dst);
+ write(mBufferView, "bufferView", dst, INVALID_INDEX);
+ write(mName, "name", dst);
+ write(mWidth, "width", dst, -1);
+ write(mHeight, "height", dst, -1);
+ write(mComponent, "component", dst, -1);
+ write(mBits, "bits", dst, -1);
+ write(mPixelType, "pixelType", dst, -1);
+}
+
+const Image& Image::operator=(const Value& src)
+{
+ copy(src, "uri", mUri);
+ copy(src, "mimeType", mMimeType);
+ copy(src, "bufferView", mBufferView);
+ copy(src, "name", mName);
+ copy(src, "width", mWidth);
+ copy(src, "height", mHeight);
+ copy(src, "component", mComponent);
+ copy(src, "bits", mBits);
+ copy(src, "pixelType", mPixelType);
+
+ return *this;
+}
+
+void Asset::update()
+{
+ F32 dt = gFrameTimeSeconds - mLastUpdateTime;
+
+ if (dt > 0.f)
+ {
+ 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();
+
+ for (auto& skin : mSkins)
+ {
+ skin.uploadMatrixPalette(*this);
+ }
+ }
+}
+
+bool Asset::prep()
+{
+ // check required extensions and fail if not supported
+ bool unsupported = false;
+ for (auto& extension : mExtensionsRequired)
+ {
+ if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
+ {
+ LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
+ unsupported = true;
+ }
+ }
+
+ if (unsupported)
+ {
+ return false;
+ }
+
+ // do buffers first as other resources depend on them
+ for (auto& buffer : mBuffers)
+ {
+ if (!buffer.prep(*this))
+ {
+ return false;
+ }
+ }
+
+ for (auto& image : mImages)
+ {
+ if (!image.prep(*this))
+ {
+ return false;
+ }
+ }
+
+ for (auto& mesh : mMeshes)
+ {
+ if (!mesh.prep(*this))
+ {
+ return false;
+ }
+ }
+
+ for (auto& animation : mAnimations)
+ {
+ if (!animation.prep(*this))
+ {
+ return false;
+ }
+ }
+
+ for (auto& skin : mSkins)
+ {
+ if (!skin.prep(*this))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Asset::Asset(const Value& src)
+{
+ *this = src;
+}
+
+bool Asset::load(std::string_view filename)
+{
+ mFilename = filename;
+ std::string ext = gDirUtilp->getExtension(mFilename);
+
+ std::ifstream file(filename.data(), std::ios::binary);
+ if (file.is_open())
+ {
+ std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+ file.close();
+
+ if (ext == "gltf")
+ {
+ Value val = parse(str);
+ *this = val;
+ return prep();
+ }
+ else if (ext == "glb")
+ {
+ return loadBinary(str);
+ }
+ else
+ {
+ LL_WARNS() << "Unsupported file type: " << ext << LL_ENDL;
+ return false;
+ }
+ }
+ else
+ {
+ LL_WARNS() << "Failed to open file: " << filename << LL_ENDL;
+ return false;
+ }
+
+ return false;
+}
+
+bool Asset::loadBinary(const std::string& data)
+{
+ // load from binary gltf
+ const U8* ptr = (const U8*)data.data();
+ const U8* end = ptr + data.size();
+
+ if (end - ptr < 12)
+ {
+ LL_WARNS("GLTF") << "GLB file too short" << LL_ENDL;
+ return false;
+ }
+
+ U32 magic = *(U32*)ptr;
+ ptr += 4;
+
+ if (magic != 0x46546C67)
+ {
+ LL_WARNS("GLTF") << "Invalid GLB magic" << LL_ENDL;
+ return false;
+ }
+
+ U32 version = *(U32*)ptr;
+ ptr += 4;
+
+ if (version != 2)
+ {
+ LL_WARNS("GLTF") << "Unsupported GLB version" << LL_ENDL;
+ return false;
+ }
+
+ U32 length = *(U32*)ptr;
+ ptr += 4;
+
+ if (length != data.size())
+ {
+ LL_WARNS("GLTF") << "GLB length mismatch" << LL_ENDL;
+ return false;
+ }
+
+ U32 chunkLength = *(U32*)ptr;
+ ptr += 4;
+
+ if (end - ptr < chunkLength + 8)
+ {
+ LL_WARNS("GLTF") << "GLB chunk too short" << LL_ENDL;
+ return false;
+ }
+
+ U32 chunkType = *(U32*)ptr;
+ ptr += 4;
+
+ if (chunkType != 0x4E4F534A)
+ {
+ LL_WARNS("GLTF") << "Invalid GLB chunk type" << LL_ENDL;
+ return false;
+ }
+
+ Value val = parse(std::string_view((const char*)ptr, chunkLength));
+ *this = val;
+
+ if (mBuffers.size() > 0 && mBuffers[0].mUri.empty())
+ {
+ // load binary chunk
+ ptr += chunkLength;
+
+ if (end - ptr < 8)
+ {
+ LL_WARNS("GLTF") << "GLB chunk too short" << LL_ENDL;
+ return false;
+ }
+
+ chunkLength = *(U32*)ptr;
+ ptr += 4;
+
+ chunkType = *(U32*)ptr;
+ ptr += 4;
+
+ if (chunkType != 0x004E4942)
+ {
+ LL_WARNS("GLTF") << "Invalid GLB chunk type" << LL_ENDL;
+ return false;
+ }
+
+ auto& buffer = mBuffers[0];
+
+ if (ptr + buffer.mByteLength <= end)
+ {
+ buffer.mData.resize(buffer.mByteLength);
+ memcpy(buffer.mData.data(), ptr, buffer.mByteLength);
+ ptr += buffer.mByteLength;
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Buffer too short" << LL_ENDL;
+ return false;
+ }
+ }
+
+ return prep();
+}
+
+const Asset& Asset::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ const object& obj = src.as_object();
+
+ const auto it = obj.find("asset");
+
+ if (it != obj.end())
+ {
+ const Value& asset = it->value();
+
+ copy(asset, "version", mVersion);
+ copy(asset, "minVersion", mMinVersion);
+ copy(asset, "generator", mGenerator);
+ copy(asset, "copyright", mCopyright);
+ copy(asset, "extras", mExtras);
+ }
+
+ copy(obj, "scene", mScene);
+ copy(obj, "scenes", mScenes);
+ copy(obj, "nodes", mNodes);
+ copy(obj, "meshes", mMeshes);
+ copy(obj, "materials", mMaterials);
+ copy(obj, "buffers", mBuffers);
+ copy(obj, "bufferViews", mBufferViews);
+ copy(obj, "textures", mTextures);
+ copy(obj, "samplers", mSamplers);
+ copy(obj, "images", mImages);
+ copy(obj, "accessors", mAccessors);
+ copy(obj, "animations", mAnimations);
+ copy(obj, "skins", mSkins);
+ copy(obj, "extensionsUsed", mExtensionsUsed);
+ copy(obj, "extensionsRequired", mExtensionsRequired);
+ }
+
+ return *this;
+}
+
+void Asset::serialize(object& dst) const
+{
+ static const std::string sGenerator = "Linden Lab GLTF Prototype v0.1";
+
+ dst["asset"] = object{};
+ object& asset = dst["asset"].get_object();
+
+ write(mVersion, "version", asset);
+ write(mMinVersion, "minVersion", asset, std::string());
+ write(sGenerator, "generator", asset);
+ write(mScene, "scene", dst, INVALID_INDEX);
+ write(mScenes, "scenes", dst);
+ write(mNodes, "nodes", dst);
+ write(mMeshes, "meshes", dst);
+ write(mMaterials, "materials", dst);
+ write(mBuffers, "buffers", dst);
+ write(mBufferViews, "bufferViews", dst);
+ write(mTextures, "textures", dst);
+ write(mSamplers, "samplers", dst);
+ write(mImages, "images", dst);
+ write(mAccessors, "accessors", dst);
+ write(mAnimations, "animations", dst);
+ write(mSkins, "skins", dst);
+ write(mExtensionsUsed, "extensionsUsed", dst);
+ write(mExtensionsRequired, "extensionsRequired", dst);
+}
+
+bool Asset::save(const std::string& filename)
+{
+ // get folder path
+ std::string folder = gDirUtilp->getDirName(filename);
+
+ // save images
+ for (auto& image : mImages)
+ {
+ if (!image.save(*this, folder))
+ {
+ return false;
+ }
+ }
+
+ // save buffers
+ // NOTE: save buffers after saving images as saving images
+ // may remove image data from buffers
+ for (auto& buffer : mBuffers)
+ {
+ if (!buffer.save(*this, folder))
+ {
+ return false;
+ }
+ }
+
+ // save .gltf
+ object obj;
+ serialize(obj);
+ std::string buffer = boost::json::serialize(obj, {});
+ std::ofstream file(filename, std::ios::binary);
+ file.write(buffer.c_str(), buffer.size());
+
+ return true;
+}
+
+void Asset::eraseBufferView(S32 bufferView)
+{
+ mBufferViews.erase(mBufferViews.begin() + bufferView);
+
+ for (auto& accessor : mAccessors)
+ {
+ if (accessor.mBufferView > bufferView)
+ {
+ accessor.mBufferView--;
+ }
+ }
+
+ for (auto& image : mImages)
+ {
+ if (image.mBufferView > bufferView)
+ {
+ image.mBufferView--;
+ }
+ }
+
+}
+
+LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
+
+bool Image::prep(Asset& asset)
+{
+ LLUUID id;
+ if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
+ { // loaded from an asset, fetch the texture from the asset system
+ mTexture = fetch_texture(id);
+ }
+ else if (mUri.find("data:") == 0)
+ { // embedded in a data URI, load the texture from the URI
+ LL_WARNS() << "Data URIs not yet supported" << LL_ENDL;
+ return false;
+ }
+ else if (mBufferView != INVALID_INDEX)
+ { // embedded in a buffer, load the texture from the buffer
+ BufferView& bufferView = asset.mBufferViews[mBufferView];
+ Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ U8* data = buffer.mData.data() + bufferView.mByteOffset;
+
+ mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
+
+ if (mTexture.isNull())
+ {
+ LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;
+ LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
+ LL_WARNS("GLTF") << " mimeType: " << mMimeType << LL_ENDL;
+
+ return false;
+ }
+ }
+ else if (!asset.mFilename.empty() && !mUri.empty())
+ { // loaded locally and not embedded, load the texture as a local preview
+ std::string dir = gDirUtilp->getDirName(asset.mFilename);
+ std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
+
+ LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
+ if (tracking_id.notNull())
+ {
+ LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
+ mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
+ LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
+ LL_WARNS("GLTF") << " file: " << img_file << LL_ENDL;
+
+ return false;
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load image: " << mName << LL_ENDL;
+ return false;
+ }
+
+ return true;
+}
+
+
+void Image::clearData(Asset& asset)
+{
+ if (mBufferView != INVALID_INDEX)
+ {
+ // remove data from buffer
+ BufferView& bufferView = asset.mBufferViews[mBufferView];
+ Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ buffer.erase(asset, bufferView.mByteOffset, bufferView.mByteLength);
+
+ asset.eraseBufferView(mBufferView);
+ }
+
+ mBufferView = INVALID_INDEX;
+ mWidth = -1;
+ mHeight = -1;
+ mComponent = -1;
+ mBits = -1;
+ mPixelType = -1;
+ mMimeType = "";
+}
+
+bool Image::save(Asset& asset, const std::string& folder)
+{
+ // NOTE: this *MUST* be a lossless save
+ // Artists use this to save their work repeatedly, so
+ // adding any compression artifacts here will degrade
+ // images over time.
+ std::string name = mName;
+ std::string error;
+ const std::string& delim = gDirUtilp->getDirDelimiter();
+ if (name.empty())
+ {
+ S32 idx = this - asset.mImages.data();
+ name = llformat("image_%d", idx);
+ }
+
+ if (mBufferView != INVALID_INDEX)
+ {
+ // we have the bytes of the original image, save that out in its
+ // original format
+ BufferView& bufferView = asset.mBufferViews[mBufferView];
+ Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+
+ std::string extension;
+
+ if (mMimeType == "image/jpeg")
+ {
+ extension = ".jpg";
+ }
+ else if (mMimeType == "image/png")
+ {
+ extension = ".png";
+ }
+ else
+ {
+ error = "Unknown mime type, saved as .bin";
+ extension = ".bin";
+ }
+
+ std::string filename = folder + delim + name + extension;
+
+ // set URI to non-j2c file for now, but later we'll want to reference the j2c hash
+ mUri = name + extension;
+
+ std::ofstream file(filename, std::ios::binary);
+ file.write((const char*)buffer.mData.data() + bufferView.mByteOffset, bufferView.mByteLength);
+ }
+ else if (mTexture.notNull())
+ {
+ auto bitmapmgr = LLLocalBitmapMgr::getInstance();
+ if (bitmapmgr->isLocal(mTexture->getID()))
+ {
+ LLUUID tracking_id = bitmapmgr->getTrackingID(mTexture->getID());
+ if (tracking_id.notNull())
+ { // copy original file to destination folder
+ std::string source = bitmapmgr->getFilename(tracking_id);
+ if (gDirUtilp->fileExists(source))
+ {
+ std::string filename = gDirUtilp->getBaseFileName(source);
+ std::string dest = folder + delim + filename;
+
+ LLFile::copy(source, dest);
+ mUri = filename;
+ }
+ else
+ {
+ error = "File not found: " + source;
+ }
+ }
+ else
+ {
+ error = "Local image missing.";
+ }
+ }
+ else if (!mUri.empty())
+ {
+ std::string from_dir = gDirUtilp->getDirName(asset.mFilename);
+ std::string base_filename = gDirUtilp->getBaseFileName(mUri);
+ std::string filename = from_dir + delim + base_filename;
+ if (gDirUtilp->fileExists(filename))
+ {
+ std::string dest = folder + delim + base_filename;
+ LLFile::copy(filename, dest);
+ mUri = base_filename;
+ }
+ else
+ {
+ error = "Original image file not found: " + filename;
+ }
+ }
+ else
+ {
+ error = "Image is not a local image and has no uri, cannot save.";
+ }
+ }
+
+ if (!error.empty())
+ {
+ LL_WARNS("GLTF") << "Failed to save " << name << ": " << error << LL_ENDL;
+ return false;
+ }
+
+ clearData(asset);
+
+ return true;
+}
+
+void Material::TextureInfo::serialize(object& dst) const
+{
+ write(mIndex, "index", dst, INVALID_INDEX);
+ write(mTexCoord, "texCoord", dst, 0);
+}
+
+const Material::TextureInfo& Material::TextureInfo::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "index", mIndex);
+ copy(src, "texCoord", mTexCoord);
+ }
+
+ return *this;
+}
+
+bool Material::TextureInfo::operator==(const Material::TextureInfo& rhs) const
+{
+ return mIndex == rhs.mIndex && mTexCoord == rhs.mTexCoord;
+}
+
+bool Material::TextureInfo::operator!=(const Material::TextureInfo& rhs) const
+{
+ return !(*this == rhs);
+}
+
+void Material::OcclusionTextureInfo::serialize(object& dst) const
+{
+ write(mIndex, "index", dst, INVALID_INDEX);
+ write(mTexCoord, "texCoord", dst, 0);
+ write(mStrength, "strength", dst, 1.f);
+}
+
+const Material::OcclusionTextureInfo& Material::OcclusionTextureInfo::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "index", mIndex);
+ copy(src, "texCoord", mTexCoord);
+ copy(src, "strength", mStrength);
+ }
+
+ return *this;
+}
+
+void Material::NormalTextureInfo::serialize(object& dst) const
+{
+ write(mIndex, "index", dst, INVALID_INDEX);
+ write(mTexCoord, "texCoord", dst, 0);
+ write(mScale, "scale", dst, 1.f);
+}
+
+const Material::NormalTextureInfo& Material::NormalTextureInfo::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "index", mIndex);
+ copy(src, "texCoord", mTexCoord);
+ copy(src, "scale", mScale);
+ }
+
+ return *this;
+}
+
+const Material::PbrMetallicRoughness& Material::PbrMetallicRoughness::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "baseColorFactor", mBaseColorFactor);
+ copy(src, "baseColorTexture", mBaseColorTexture);
+ copy(src, "metallicFactor", mMetallicFactor);
+ copy(src, "roughnessFactor", mRoughnessFactor);
+ copy(src, "metallicRoughnessTexture", mMetallicRoughnessTexture);
+ }
+
+ return *this;
+}
+
+void Material::PbrMetallicRoughness::serialize(object& dst) const
+{
+ write(mBaseColorFactor, "baseColorFactor", dst, vec4(1.f, 1.f, 1.f, 1.f));
+ write(mBaseColorTexture, "baseColorTexture", dst);
+ write(mMetallicFactor, "metallicFactor", dst, 1.f);
+ write(mRoughnessFactor, "roughnessFactor", dst, 1.f);
+ write(mMetallicRoughnessTexture, "metallicRoughnessTexture", dst);
+}
+
+bool Material::PbrMetallicRoughness::operator==(const Material::PbrMetallicRoughness& rhs) const
+{
+ return mBaseColorFactor == rhs.mBaseColorFactor &&
+ mBaseColorTexture == rhs.mBaseColorTexture &&
+ mMetallicFactor == rhs.mMetallicFactor &&
+ mRoughnessFactor == rhs.mRoughnessFactor &&
+ mMetallicRoughnessTexture == rhs.mMetallicRoughnessTexture;
+}
+
+bool Material::PbrMetallicRoughness::operator!=(const Material::PbrMetallicRoughness& rhs) const
+{
+ return !(*this == rhs);
+}
+
+const Material::Unlit& Material::Unlit::operator=(const Value& src)
+{
+ mPresent = true;
+ return *this;
+}
+
+void Material::Unlit::serialize(object& dst) const
+{
+ // no members and object has already been created, nothing to do
+}
+
+void Material::serialize(object& dst) const
+{
+ write(mName, "name", dst);
+ write(mEmissiveFactor, "emissiveFactor", dst, vec3(0.f, 0.f, 0.f));
+ write(mPbrMetallicRoughness, "pbrMetallicRoughness", dst);
+ write(mNormalTexture, "normalTexture", dst);
+ write(mOcclusionTexture, "occlusionTexture", dst);
+ write(mEmissiveTexture, "emissiveTexture", dst);
+ write(mAlphaMode, "alphaMode", dst, Material::AlphaMode::OPAQUE);
+ write(mAlphaCutoff, "alphaCutoff", dst, 0.5f);
+ write(mDoubleSided, "doubleSided", dst, false);
+ write_extensions(dst, &mUnlit, "KHR_materials_unlit");
+}
+
+const Material& Material::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "name", mName);
+ copy(src, "emissiveFactor", mEmissiveFactor);
+ copy(src, "pbrMetallicRoughness", mPbrMetallicRoughness);
+ copy(src, "normalTexture", mNormalTexture);
+ copy(src, "occlusionTexture", mOcclusionTexture);
+ copy(src, "emissiveTexture", mEmissiveTexture);
+ copy(src, "alphaMode", mAlphaMode);
+ copy(src, "alphaCutoff", mAlphaCutoff);
+ copy(src, "doubleSided", mDoubleSided);
+ copy_extensions(src,
+ "KHR_materials_unlit", &mUnlit );
+ }
+ return *this;
+}
+
+
+void Mesh::serialize(object& dst) const
+{
+ write(mPrimitives, "primitives", dst);
+ write(mWeights, "weights", dst);
+ write(mName, "name", dst);
+}
+
+const Mesh& Mesh::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "primitives", mPrimitives);
+ copy(src, "weights", mWeights);
+ copy(src, "name", mName);
+ }
+
+ return *this;
+}
+
+bool Mesh::prep(Asset& asset)
+{
+ for (auto& primitive : mPrimitives)
+ {
+ if (!primitive.prep(asset))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Scene::serialize(object& dst) const
+{
+ write(mNodes, "nodes", dst);
+ write(mName, "name", dst);
+}
+
+const Scene& Scene::operator=(const Value& src)
+{
+ copy(src, "nodes", mNodes);
+ copy(src, "name", mName);
+
+ return *this;
+}
+
+void Texture::serialize(object& dst) const
+{
+ write(mSampler, "sampler", dst, INVALID_INDEX);
+ write(mSource, "source", dst, INVALID_INDEX);
+ write(mName, "name", dst);
+}
+
+const Texture& Texture::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "sampler", mSampler);
+ copy(src, "source", mSource);
+ copy(src, "name", mName);
+ }
+
+ return *this;
+}
+
+void Sampler::serialize(object& dst) const
+{
+ write(mMagFilter, "magFilter", dst, LINEAR);
+ write(mMinFilter, "minFilter", dst, LINEAR_MIPMAP_LINEAR);
+ write(mWrapS, "wrapS", dst, REPEAT);
+ write(mWrapT, "wrapT", dst, REPEAT);
+ write(mName, "name", dst);
+}
+
+const Sampler& Sampler::operator=(const Value& src)
+{
+ copy(src, "magFilter", mMagFilter);
+ copy(src, "minFilter", mMinFilter);
+ copy(src, "wrapS", mWrapS);
+ copy(src, "wrapT", mWrapT);
+ copy(src, "name", mName);
+
+ return *this;
+}
+
+
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
new file mode 100644
index 0000000000..8f28e5905f
--- /dev/null
+++ b/indra/newview/gltf/asset.h
@@ -0,0 +1,399 @@
+#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 "accessor.h"
+#include "primitive.h"
+#include "animation.h"
+#include "boost/json.hpp"
+#include "common.h"
+#include "../llviewertexture.h"
+
+extern F32SecondsImplicit gFrameTimeSeconds;
+
+// wingdi defines OPAQUE, which conflicts with our enum
+#if defined(OPAQUE)
+#undef OPAQUE
+#endif
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+
+ class Extension
+ {
+ public:
+ // true if this extension is present in the gltf file
+ // otherwise false
+ bool mPresent = false;
+ };
+
+
+ class Material
+ {
+ public:
+
+ class Unlit : public Extension // KHR_materials_unlit implementation
+ {
+ public:
+ const Unlit& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ enum class AlphaMode
+ {
+ OPAQUE,
+ MASK,
+ BLEND
+ };
+
+ class TextureInfo
+ {
+ public:
+ S32 mIndex = INVALID_INDEX;
+ S32 mTexCoord = 0;
+
+ bool operator==(const TextureInfo& rhs) const;
+ bool operator!=(const TextureInfo& rhs) const;
+
+ const TextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class NormalTextureInfo : public TextureInfo
+ {
+ public:
+ F32 mScale = 1.0f;
+
+ const NormalTextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class OcclusionTextureInfo : public TextureInfo
+ {
+ public:
+ F32 mStrength = 1.0f;
+
+ const OcclusionTextureInfo& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class PbrMetallicRoughness
+ {
+ public:
+ vec4 mBaseColorFactor = vec4(1.f,1.f,1.f,1.f);
+ TextureInfo mBaseColorTexture;
+ F32 mMetallicFactor = 1.0f;
+ F32 mRoughnessFactor = 1.0f;
+ TextureInfo mMetallicRoughnessTexture;
+
+ bool operator==(const PbrMetallicRoughness& rhs) const;
+ bool operator!=(const PbrMetallicRoughness& rhs) const;
+ const PbrMetallicRoughness& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+
+ PbrMetallicRoughness mPbrMetallicRoughness;
+ NormalTextureInfo mNormalTexture;
+ OcclusionTextureInfo mOcclusionTexture;
+ TextureInfo mEmissiveTexture;
+
+ std::string mName;
+ vec3 mEmissiveFactor = vec3(0.f, 0.f, 0.f);
+ AlphaMode mAlphaMode = AlphaMode::OPAQUE;
+ F32 mAlphaCutoff = 0.5f;
+ bool mDoubleSided = false;
+ Unlit mUnlit;
+
+ const Material& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class Mesh
+ {
+ public:
+ std::vector<Primitive> mPrimitives;
+ std::vector<double> mWeights;
+ std::string mName;
+
+ const Mesh& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+
+ bool prep(Asset& asset);
+ };
+
+ class Node
+ {
+ public:
+ mat4 mMatrix = glm::identity<mat4>(); //local transform
+ mat4 mRenderMatrix; //transform for rendering
+ mat4 mAssetMatrix; //transform from local to asset space
+ mat4 mAssetMatrixInv; //transform from asset to local space
+
+ vec3 mTranslation = vec3(0,0,0);
+ quat mRotation = glm::identity<quat>();
+ vec3 mScale = vec3(1.f,1.f,1.f);
+
+ // if true, mMatrix is valid and up to date
+ bool mMatrixValid = false;
+
+ // 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 Value& src);
+ void serialize(boost::json::object& dst) const;
+
+ // Set mRenderMatrix to a transform that can be used for the current render pass
+ // modelview -- parent's render matrix
+ void updateRenderTransforms(Asset& asset, const mat4& modelview);
+
+ // update mAssetMatrix and mAssetMatrixInv
+ void updateTransforms(Asset& asset, const mat4& parentMatrix);
+
+ // 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 quat& rotation);
+
+ // Set translation of this node
+ // SIDE EFFECT: invalidates mMatrix
+ void setTranslation(const vec3& translation);
+
+ // Set scale of this node
+ // SIDE EFFECT: invalidates mMatrix
+ void setScale(const vec3& scale);
+ };
+
+ class Skin
+ {
+ public:
+ ~Skin();
+
+ S32 mInverseBindMatrices = INVALID_INDEX;
+ S32 mSkeleton = INVALID_INDEX;
+
+ U32 mUBO = 0;
+ std::vector<S32> mJoints;
+ std::string mName;
+ std::vector<mat4> mInverseBindMatricesData;
+
+ bool prep(Asset& asset);
+ void uploadMatrixPalette(Asset& asset);
+
+ const Skin& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class Scene
+ {
+ public:
+ std::vector<S32> mNodes;
+ std::string mName;
+
+ const Scene& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+
+ void updateTransforms(Asset& asset);
+ void updateRenderTransforms(Asset& asset, const mat4& modelview);
+ };
+
+ class Texture
+ {
+ public:
+ S32 mSampler = INVALID_INDEX;
+ S32 mSource = INVALID_INDEX;
+ std::string mName;
+
+ const Texture& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class Sampler
+ {
+ public:
+ S32 mMagFilter = LINEAR;
+ S32 mMinFilter = LINEAR_MIPMAP_LINEAR;
+ S32 mWrapS = REPEAT;
+ S32 mWrapT = REPEAT;
+ std::string mName;
+
+ const Sampler& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+ };
+
+ class Image
+ {
+ public:
+ std::string mName;
+ std::string mUri;
+ std::string mMimeType;
+
+ S32 mBufferView = INVALID_INDEX;
+
+ S32 mWidth = -1;
+ S32 mHeight = -1;
+ S32 mComponent = -1;
+ S32 mBits = -1;
+ S32 mPixelType = -1;
+
+ LLPointer<LLViewerFetchedTexture> mTexture;
+
+ const Image& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+
+ // save image to disk
+ // may remove image data from bufferviews and convert to
+ // file uri if necessary
+ bool save(Asset& asset, const std::string& filename);
+
+ // erase the buffer view associated with this image
+ // free any associated GLTF resources
+ // preserve only uri and name
+ void clearData(Asset& asset);
+
+ bool prep(Asset& asset);
+ };
+
+ // C++ representation of a GLTF Asset
+ class Asset
+ {
+ public:
+
+ static const std::string minVersion_default;
+ 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;
+ std::vector<std::string> mExtensionsUsed;
+ std::vector<std::string> mExtensionsRequired;
+
+ std::string mVersion;
+ std::string mGenerator;
+ std::string mMinVersion;
+ std::string mCopyright;
+
+ S32 mScene = INVALID_INDEX;
+ Value mExtras;
+
+ U32 mPendingBuffers = 0;
+
+ // local file this asset was loaded from (if any)
+ std::string mFilename;
+
+ // the last time update() was called according to gFrameTimeSeconds
+ F32 mLastUpdateTime = gFrameTimeSeconds;
+
+
+ // prepare for first time use
+ bool prep();
+
+ // 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 mat4& modelview);
+
+ // return the index of the node that the line segment intersects with, or -1 if no hit
+ // input and output values must be in this asset's local coordinate frame
+ 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
+ );
+
+ Asset() = default;
+ Asset(const Value& src);
+
+ // load from given file
+ // accepts .gltf and .glb files
+ // Any existing data will be lost
+ // returns result of prep() on success
+ bool load(std::string_view filename);
+
+ // load .glb contents from memory
+ // data - binary contents of .glb file
+ // returns result of prep() on success
+ bool loadBinary(const std::string& data);
+
+ const Asset& operator=(const Value& src);
+ void serialize(boost::json::object& dst) const;
+
+ // save the asset to the given .gltf file
+ // saves images and bins alongside the gltf file
+ bool save(const std::string& filename);
+
+ // remove the bufferview at the given index
+ // updates all bufferview indices in this Asset as needed
+ void eraseBufferView(S32 bufferView);
+
+ // return true if this Asset has been loaded as a local preview
+ // Local previews may be uploaded or exported to disk
+ bool isLocalPreview() { return !mFilename.empty(); }
+ };
+
+ Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode);
+ std::string enum_to_gltf_alpha_mode(Material::AlphaMode alpha_mode);
+ }
+}
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
new file mode 100644
index 0000000000..c26752a6b6
--- /dev/null
+++ b/indra/newview/gltf/buffer_util.h
@@ -0,0 +1,1024 @@
+#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
+
+#include "accessor.h"
+
+namespace LL
+{
+ namespace GLTF
+ {
+
+ using string_view = boost::json::string_view;
+
+ // copy one Scalar from src to dst
+ template<class S, class T>
+ inline void copyScalar(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one vec2 from src to dst
+ template<class S, class T>
+ inline void copyVec2(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one vec3 from src to dst
+ template<class S, class T>
+ inline void copyVec3(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one vec4 from src to dst
+ template<class S, class T>
+ inline void copyVec4(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one mat2 from src to dst
+ template<class S, class T>
+ inline void copyMat2(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one mat3 from src to dst
+ template<class S, class T>
+ inline void copyMat3(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ // copy one mat4 from src to dst
+ template<class S, class T>
+ inline void copyMat4(S* src, T& dst)
+ {
+ LL_ERRS() << "TODO: implement " << LL_FUNCSIG << LL_ENDL;
+ }
+
+ //=========================================================================================================
+ // concrete implementations for different types of source and destination
+ //=========================================================================================================
+
+ template<>
+ inline void copyScalar<F32, F32>(F32* src, F32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyScalar<U32, U32>(U32* src, U32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyScalar<U32, U16>(U32* src, U16& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyScalar<U16, U16>(U16* src, U16& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyScalar<U16, U32>(U16* src, U32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyScalar<U8, U16>(U8* src, U16& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyScalar<U8, U32>(U8* src, U32& dst)
+ {
+ dst = *src;
+ }
+
+ template<>
+ inline void copyVec2<F32, LLVector2>(F32* src, LLVector2& dst)
+ {
+ dst.set(src[0], src[1]);
+ }
+
+ template<>
+ inline void copyVec3<F32, vec3>(F32* src, vec3& dst)
+ {
+ dst = vec3(src[0], src[1], src[2]);
+ }
+
+ template<>
+ inline void copyVec3<F32, LLVector4a>(F32* src, LLVector4a& dst)
+ {
+ dst.load3(src);
+ }
+
+ template<>
+ inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
+ {
+ dst.set(src[0], src[1], src[2], 255);
+ }
+
+ template<>
+ inline void copyVec4<U8, LLColor4U>(U8* src, LLColor4U& dst)
+ {
+ dst.set(src[0], src[1], src[2], src[3]);
+ }
+
+ template<>
+ inline void copyVec4<U16, U64>(U16* src, U64& dst)
+ {
+ U16* data = (U16*)&dst;
+ data[0] = src[0];
+ data[1] = src[1];
+ data[2] = src[2];
+ data[3] = src[3];
+ }
+
+ template<>
+ inline void copyVec4<U16, LLColor4U>(U16* src, LLColor4U& dst)
+ {
+ dst.set(src[0], src[1], src[2], src[3]);
+ }
+
+ template<>
+ inline void copyVec4<F32, LLColor4U>(F32* src, LLColor4U& dst)
+ {
+ dst.set(src[0]*255, src[1]*255, src[2]*255, src[3]*255);
+ }
+
+ template<>
+ inline void copyVec4<F32, LLVector4a>(F32* src, LLVector4a& dst)
+ {
+ dst.loadua(src);
+ }
+
+ template<>
+ inline void copyVec4<U16, LLVector4a>(U16* src, LLVector4a& dst)
+ {
+ dst.set(src[0], src[1], src[2], src[3]);
+ }
+
+ template<>
+ inline void copyVec4<U8, LLVector4a>(U8* src, LLVector4a& dst)
+ {
+ dst.set(src[0], src[1], src[2], src[3]);
+ }
+
+ template<>
+ inline void copyVec4<F32, quat>(F32* src, quat& dst)
+ {
+ dst.x = src[0];
+ dst.y = src[1];
+ dst.z = src[2];
+ dst.w = src[3];
+ }
+
+ template<>
+ inline void copyMat4<F32, mat4>(F32* src, mat4& dst)
+ {
+ dst = glm::make_mat4(src);
+ }
+
+ //=========================================================================================================
+
+ // 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>
+ inline 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>
+ inline 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>
+ inline 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>
+ inline 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>
+ inline 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>
+ inline 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>
+ inline 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>
+ inline void copy(Asset& asset, Accessor& accessor, const S* src, LLStrider<T>& dst, S32 byteStride)
+ {
+ if (accessor.mType == Accessor::Type::SCALAR)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 1 : byteStride;
+ copyScalar((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == Accessor::Type::VEC2)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 2 : byteStride;
+ copyVec2((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == Accessor::Type::VEC3)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 3 : byteStride;
+ copyVec3((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == Accessor::Type::VEC4)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;
+ copyVec4((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == Accessor::Type::MAT2)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 4 : byteStride;
+ copyMat2((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == Accessor::Type::MAT3)
+ {
+ S32 stride = byteStride == 0 ? sizeof(S) * 9 : byteStride;
+ copyMat3((S*)src, dst, stride, accessor.mCount);
+ }
+ else if (accessor.mType == 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>
+ inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
+ {
+ const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
+ const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
+ const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
+
+ switch (accessor.mComponentType)
+ {
+ case Accessor::ComponentType::FLOAT:
+ copy(asset, accessor, (const F32*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::UNSIGNED_INT:
+ copy(asset, accessor, (const U32*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::SHORT:
+ copy(asset, accessor, (const S16*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::UNSIGNED_SHORT:
+ copy(asset, accessor, (const U16*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::BYTE:
+ copy(asset, accessor, (const S8*)src, dst, bufferView.mByteStride);
+ break;
+ case Accessor::ComponentType::UNSIGNED_BYTE:
+ copy(asset, accessor, (const U8*)src, dst, bufferView.mByteStride);
+ break;
+ default:
+ LL_ERRS("GLTF") << "Invalid component type" << LL_ENDL;
+ break;
+ }
+ }
+
+ // copy data from accessor to vector
+ template<class T>
+ inline void copy(Asset& asset, Accessor& accessor, std::vector<T>& dst)
+ {
+ dst.resize(accessor.mCount);
+ LLStrider<T> strider = dst.data();
+ copy(asset, accessor, strider);
+ }
+
+
+ //=========================================================================================================
+ // boost::json copying utilities
+ // ========================================================================================================
+
+ //====================== unspecialized base template, single value ===========================
+
+ // to/from Value
+ template<typename T>
+ inline bool copy(const Value& src, T& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ template<typename T>
+ inline bool write(const T& src, Value& dst)
+ {
+ dst = boost::json::object();
+ src.serialize(dst.as_object());
+ return true;
+ }
+
+ template<typename T>
+ inline bool copy(const Value& src, std::unordered_map<std::string, T>& dst)
+ {
+ if (src.is_object())
+ {
+ const boost::json::object& obj = src.as_object();
+ for (const auto& [key, value] : obj)
+ {
+ copy<T>(value, dst[key]);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::unordered_map<std::string, T>& src, Value& dst)
+ {
+ boost::json::object obj;
+ for (const auto& [key, value] : src)
+ {
+ Value v;
+ if (write<T>(value, v))
+ {
+ obj[key] = v;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ dst = obj;
+ return true;
+ }
+
+ // to/from array
+ template<typename T>
+ inline bool copy(const Value& src, std::vector<T>& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.get_array();
+ dst.resize(arr.size());
+ for (size_t i = 0; i < arr.size(); ++i)
+ {
+ copy(arr[i], dst[i]);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::vector<T>& src, Value& dst)
+ {
+ boost::json::array arr;
+ for (const T& t : src)
+ {
+ Value v;
+ if (write(t, v))
+ {
+ arr.push_back(v);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ dst = arr;
+ return true;
+ }
+
+ // to/from object member
+ template<typename T>
+ inline bool copy(const boost::json::object& src, string_view member, T& dst)
+ {
+ auto it = src.find(member);
+ if (it != src.end())
+ {
+ return copy(it->value(), dst);
+ }
+ return false;
+ }
+
+ // always write a member to an object without checking default
+ template<typename T>
+ inline bool write_always(const T& src, string_view member, boost::json::object& dst)
+ {
+ Value& v = dst[member];
+ if (!write(src, v))
+ {
+ dst.erase(member);
+ return false;
+ }
+ return true;
+ }
+
+
+ // to/from extension
+
+ // for internal use only, use copy_extensions instead
+ template<typename T>
+ inline bool _copy_extension(const boost::json::object& extensions, std::string_view member, T* dst)
+ {
+ if (extensions.contains(member))
+ {
+ return copy(extensions.at(member), *dst);
+ }
+
+ return false;
+ }
+
+ // Copy all extensions from src.extensions to provided destinations
+ // Usage:
+ // copy_extensions(src,
+ // "KHR_materials_unlit", &mUnlit,
+ // "KHR_materials_pbrSpecularGlossiness", &mPbrSpecularGlossiness);
+ // returns true if any of the extensions are copied
+ template<class... Types>
+ inline bool copy_extensions(const boost::json::value& src, Types... args)
+ {
+ // extract the extensions object (don't assume it exists and verify that it is an object)
+ if (src.is_object())
+ {
+ boost::json::object obj = src.get_object();
+ if (obj.contains("extensions"))
+ {
+ const boost::json::value& extensions = obj.at("extensions");
+ if (extensions.is_object())
+ {
+ const boost::json::object& ext_obj = extensions.as_object();
+ bool success = false;
+ // copy each extension, return true if any of them succeed, do not short circuit on success
+ U32 count = sizeof...(args);
+ for (U32 i = 0; i < count; i += 2)
+ {
+ if (_copy_extension(ext_obj, args...))
+ {
+ success = true;
+ }
+ }
+ return success;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // internal use aonly, use write_extensions instead
+ template<typename T>
+ inline bool _write_extension(boost::json::object& extensions, const T* src, string_view member)
+ {
+ if (src->mPresent)
+ {
+ Value v;
+ if (write(*src, v))
+ {
+ extensions[member] = v;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Write all extensions to dst.extensions
+ // Usage:
+ // write_extensions(dst,
+ // "KHR_materials_unlit", mUnlit,
+ // "KHR_materials_pbrSpecularGlossiness", mPbrSpecularGlossiness);
+ // returns true if any of the extensions are written
+ template<class... Types>
+ inline bool write_extensions(boost::json::object& dst, Types... args)
+ {
+ bool success = false;
+
+ boost::json::object extensions;
+ U32 count = sizeof...(args) - 1;
+
+ for (U32 i = 0; i < count; i += 2)
+ {
+ if (_write_extension(extensions, args...))
+ {
+ success = true;
+ }
+ }
+
+ if (success)
+ {
+ dst["extensions"] = extensions;
+ }
+
+ return success;
+ }
+
+ // conditionally write a member to an object if the member
+ // is not the default value
+ template<typename T>
+ inline bool write(const T& src, string_view member, boost::json::object& dst, const T& default_value = T())
+ {
+ if (src != default_value)
+ {
+ return write_always(src, member, dst);
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::unordered_map<std::string, T>& src, string_view member, boost::json::object& dst, const std::unordered_map<std::string, T>& default_value = std::unordered_map<std::string, T>())
+ {
+ if (!src.empty())
+ {
+ Value v;
+ if (write<T>(src, v))
+ {
+ dst[member] = v;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool write(const std::vector<T>& src, string_view member, boost::json::object& dst, const std::vector<T>& deafault_value = std::vector<T>())
+ {
+ if (!src.empty())
+ {
+ Value v;
+ if (write(src, v))
+ {
+ dst[member] = v;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<typename T>
+ inline bool copy(const Value& src, string_view member, T& dst)
+ {
+ if (src.is_object())
+ {
+ const boost::json::object& obj = src.as_object();
+ return copy(obj, member, dst);
+ }
+
+ return false;
+ }
+
+ // Accessor::ComponentType
+ template<>
+ inline bool copy(const Value& src, Accessor::ComponentType& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = (Accessor::ComponentType)src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const Accessor::ComponentType& src, Value& dst)
+ {
+ dst = (S32)src;
+ return true;
+ }
+
+ //Primitive::Mode
+ template<>
+ inline bool copy(const Value& src, Primitive::Mode& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = (Primitive::Mode)src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const Primitive::Mode& src, Value& dst)
+ {
+ dst = (S32)src;
+ return true;
+ }
+
+ // vec4
+ template<>
+ inline bool copy(const Value& src, vec4& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 4)
+ {
+ vec4 v;
+ std::error_code ec;
+
+ v.x = arr[0].to_number<F32>(ec); if (ec) return false;
+ v.y = arr[1].to_number<F32>(ec); if (ec) return false;
+ v.z = arr[2].to_number<F32>(ec); if (ec) return false;
+ v.w = arr[3].to_number<F32>(ec); if (ec) return false;
+
+ dst = v;
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const vec4& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.get_array();
+ arr.resize(4);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ arr[3] = src.w;
+ return true;
+ }
+
+ // quat
+ template<>
+ inline bool copy(const Value& src, quat& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 4)
+ {
+ std::error_code ec;
+ dst.x = arr[0].to_number<F32>(ec); if (ec) return false;
+ dst.y = arr[1].to_number<F32>(ec); if (ec) return false;
+ dst.z = arr[2].to_number<F32>(ec); if (ec) return false;
+ dst.w = arr[3].to_number<F32>(ec); if (ec) return false;
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const quat& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.get_array();
+ arr.resize(4);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ arr[3] = src.w;
+ return true;
+ }
+
+
+ // vec3
+ template<>
+ inline bool copy(const Value& src, vec3& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.as_array();
+ if (arr.size() == 3)
+ {
+ std::error_code ec;
+ vec3 t;
+ t.x = arr[0].to_number<F32>(ec); if (ec) return false;
+ t.y = arr[1].to_number<F32>(ec); if (ec) return false;
+ t.z = arr[2].to_number<F32>(ec); if (ec) return false;
+
+ dst = t;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const vec3& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.as_array();
+ arr.resize(3);
+ arr[0] = src.x;
+ arr[1] = src.y;
+ arr[2] = src.z;
+ return true;
+ }
+
+ // bool
+ template<>
+ inline bool copy(const Value& src, bool& dst)
+ {
+ if (src.is_bool())
+ {
+ dst = src.get_bool();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const bool& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // F32
+ template<>
+ inline bool copy(const Value& src, F32& dst)
+ {
+ std::error_code ec;
+ F32 t = src.to_number<F32>(ec); if (ec) return false;
+ dst = t;
+ return true;
+ }
+
+ template<>
+ inline bool write(const F32& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+
+ // U32
+ template<>
+ inline bool copy(const Value& src, U32& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const U32& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // F64
+ template<>
+ inline bool copy(const Value& src, F64& dst)
+ {
+ std::error_code ec;
+ F64 t = src.to_number<F64>(ec); if (ec) return false;
+ dst = t;
+ return true;
+ }
+
+ template<>
+ inline bool write(const F64& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // Accessor::Type
+ template<>
+ inline bool copy(const Value& src, Accessor::Type& dst)
+ {
+ if (src.is_string())
+ {
+ dst = gltf_type_to_enum(src.get_string().c_str());
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const Accessor::Type& src, Value& dst)
+ {
+ dst = enum_to_gltf_type(src);
+ return true;
+ }
+
+ // S32
+ template<>
+ inline bool copy(const Value& src, S32& dst)
+ {
+ if (src.is_int64())
+ {
+ dst = src.get_int64();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const S32& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+
+ // std::string
+ template<>
+ inline bool copy(const Value& src, std::string& dst)
+ {
+ if (src.is_string())
+ {
+ dst = src.get_string().c_str();
+ return true;
+ }
+ return false;
+ }
+
+ template<>
+ inline bool write(const std::string& src, Value& dst)
+ {
+ dst = src;
+ return true;
+ }
+
+ // mat4
+ template<>
+ inline bool copy(const Value& src, mat4& dst)
+ {
+ if (src.is_array())
+ {
+ const boost::json::array& arr = src.get_array();
+ if (arr.size() == 16)
+ {
+ // populate a temporary local in case
+ // we hit an error in the middle of the array
+ // (don't partially write a matrix)
+ mat4 t;
+ F32* p = glm::value_ptr(t);
+
+ for (U32 i = 0; i < arr.size(); ++i)
+ {
+ std::error_code ec;
+ p[i] = arr[i].to_number<F32>(ec);
+ if (ec)
+ {
+ return false;
+ }
+ }
+
+ dst = t;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ template<>
+ inline bool write(const mat4& src, Value& dst)
+ {
+ dst = boost::json::array();
+ boost::json::array& arr = dst.get_array();
+ arr.resize(16);
+ const F32* p = glm::value_ptr(src);
+ for (U32 i = 0; i < 16; ++i)
+ {
+ arr[i] = p[i];
+ }
+ return true;
+ }
+
+ // Material::AlphaMode
+ template<>
+ inline bool copy(const Value& src, Material::AlphaMode& dst)
+ {
+ if (src.is_string())
+ {
+ dst = gltf_alpha_mode_to_enum(src.get_string().c_str());
+ return true;
+ }
+ return true;
+ }
+
+ template<>
+ inline bool write(const Material::AlphaMode& src, Value& dst)
+ {
+ dst = enum_to_gltf_alpha_mode(src);
+ return true;
+ }
+
+ //
+ // ========================================================================================================
+
+ }
+}
+
+
+
+
diff --git a/indra/newview/gltf/common.h b/indra/newview/gltf/common.h
new file mode 100644
index 0000000000..4f660d7cfc
--- /dev/null
+++ b/indra/newview/gltf/common.h
@@ -0,0 +1,83 @@
+#pragma once
+
+/**
+ * @file common.h
+ * @brief LL GLTF Implementation
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#define GLM_ENABLE_EXPERIMENTAL 1
+
+#include "glm/vec2.hpp"
+#include "glm/vec3.hpp"
+#include "glm/vec4.hpp"
+#include "glm/mat4x4.hpp"
+#include "glm/gtc/type_ptr.hpp"
+#include "glm/ext/quaternion_float.hpp"
+#include "glm/gtx/quaternion.hpp"
+#include "glm/gtx/matrix_decompose.hpp"
+#include <boost/json.hpp>
+
+// Common types and constants used in the GLTF implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ using Value = boost::json::value;
+
+ using mat4 = glm::mat4;
+ using vec4 = glm::vec4;
+ using vec3 = glm::vec3;
+ using vec2 = glm::vec2;
+ using quat = glm::quat;
+
+ constexpr S32 LINEAR = 9729;
+ constexpr S32 NEAREST = 9728;
+ constexpr S32 NEAREST_MIPMAP_NEAREST = 9984;
+ constexpr S32 LINEAR_MIPMAP_NEAREST = 9985;
+ constexpr S32 NEAREST_MIPMAP_LINEAR = 9986;
+ constexpr S32 LINEAR_MIPMAP_LINEAR = 9987;
+ constexpr S32 CLAMP_TO_EDGE = 33071;
+ constexpr S32 MIRRORED_REPEAT = 33648;
+ constexpr S32 REPEAT = 10497;
+
+
+ class Asset;
+ class Material;
+ class Mesh;
+ class Node;
+ class Scene;
+ class Texture;
+ class Sampler;
+ class Image;
+ class Animation;
+ class Skin;
+ class Camera;
+ class Light;
+ class Primitive;
+ class Accessor;
+ class BufferView;
+ class Buffer;
+ }
+}
+
diff --git a/indra/newview/gltf/primitive.cpp b/indra/newview/gltf/primitive.cpp
new file mode 100644
index 0000000000..bc333aff69
--- /dev/null
+++ b/indra/newview/gltf/primitive.cpp
@@ -0,0 +1,685 @@
+/**
+ * @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 "../llviewershadermgr.h"
+
+#include "mikktspace/mikktspace.hh"
+
+#include "meshoptimizer/meshoptimizer.h"
+
+
+using namespace LL::GLTF;
+using namespace boost::json;
+
+
+// Mesh data useful for Mikktspace tangent generation (and flat normal generation)
+struct MikktMesh
+{
+ std::vector<LLVector3> p;
+ std::vector<LLVector3> n;
+ std::vector<LLVector2> tc;
+ std::vector<LLVector4> w;
+ std::vector<LLVector4> t;
+ std::vector<LLColor4U> c;
+ std::vector<U64> j;
+
+ // initialize from src primitive and make an unrolled triangle list
+ // returns false if the Primitive cannot be converted to a triangle list
+ bool copy(const Primitive* prim)
+ {
+ bool indexed = !prim->mIndexArray.empty();
+ U32 vert_count = indexed ? prim->mIndexArray.size() : prim->mPositions.size();
+
+ if (prim->mMode != Primitive::Mode::TRIANGLES)
+ {
+ LL_WARNS("GLTF") << "Unsupported primitive mode for conversion to triangles: " << (S32) prim->mMode << LL_ENDL;
+ return false;
+ }
+
+ p.resize(vert_count);
+ n.resize(vert_count);
+ tc.resize(vert_count);
+ c.resize(vert_count);
+
+ bool has_normals = !prim->mNormals.empty();
+ if (has_normals)
+ {
+ n.resize(vert_count);
+ }
+ bool has_tangents = !prim->mTangents.empty();
+ if (has_tangents)
+ {
+ t.resize(vert_count);
+ }
+ bool rigged = !prim->mWeights.empty();
+ if (rigged)
+ {
+ w.resize(vert_count);
+ j.resize(vert_count);
+ }
+
+ for (int i = 0; i < vert_count; ++i)
+ {
+ U32 idx = indexed ? prim->mIndexArray[i] : i;
+
+ p[i].set(prim->mPositions[idx].getF32ptr());
+ tc[i].set(prim->mTexCoords[idx]);
+ c[i] = prim->mColors[idx];
+
+ if (has_normals)
+ {
+ n[i].set(prim->mNormals[idx].getF32ptr());
+ }
+
+ if (rigged)
+ {
+ w[i].set(prim->mWeights[idx].getF32ptr());
+ j[i] = prim->mJoints[idx];
+ }
+ }
+
+ return true;
+ }
+
+ void genNormals()
+ {
+ U32 tri_count = p.size() / 3;
+ for (U32 i = 0; i < tri_count; ++i)
+ {
+ LLVector3 v0 = p[i * 3];
+ LLVector3 v1 = p[i * 3 + 1];
+ LLVector3 v2 = p[i * 3 + 2];
+
+ LLVector3 normal = (v1 - v0) % (v2 - v0);
+ normal.normalize();
+
+ n[i * 3] = normal;
+ n[i * 3 + 1] = normal;
+ n[i * 3 + 2] = normal;
+ }
+ }
+
+ void genTangents()
+ {
+ t.resize(p.size());
+ mikk::Mikktspace ctx(*this);
+ ctx.genTangSpace();
+ }
+
+ // write to target primitive as an indexed triangle list
+ // Only modifies runtime data, does not modify the original GLTF data
+ void write(Primitive* prim) const
+ {
+ //re-weld
+ meshopt_Stream mos[] =
+ {
+ { &p[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &n[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &t[0], sizeof(LLVector4), sizeof(LLVector4) },
+ { &tc[0], sizeof(LLVector2), sizeof(LLVector2) },
+ { &c[0], sizeof(LLColor4U), sizeof(LLColor4U) },
+ { w.empty() ? nullptr : &w[0], sizeof(LLVector4), sizeof(LLVector4) },
+ { j.empty() ? nullptr : &j[0], sizeof(U64), sizeof(U64) }
+ };
+
+ std::vector<U32> remap;
+ remap.resize(p.size());
+
+ U32 stream_count = w.empty() ? 5 : 7;
+
+ size_t vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, p.size(), p.size(), mos, stream_count);
+
+ prim->mTexCoords.resize(vert_count);
+ prim->mNormals.resize(vert_count);
+ prim->mTangents.resize(vert_count);
+ prim->mPositions.resize(vert_count);
+ prim->mColors.resize(vert_count);
+ if (!w.empty())
+ {
+ prim->mWeights.resize(vert_count);
+ prim->mJoints.resize(vert_count);
+ }
+
+ prim->mIndexArray.resize(remap.size());
+
+ for (int i = 0; i < remap.size(); ++i)
+ {
+ U32 src_idx = i;
+ U32 dst_idx = remap[i];
+
+ prim->mIndexArray[i] = dst_idx;
+
+ prim->mPositions[dst_idx].load3(p[src_idx].mV);
+ prim->mNormals[dst_idx].load3(n[src_idx].mV);
+ prim->mTexCoords[dst_idx] = tc[src_idx];
+ prim->mTangents[dst_idx].loadua(t[src_idx].mV);
+ prim->mColors[dst_idx] = c[src_idx];
+
+ if (!w.empty())
+ {
+ prim->mWeights[dst_idx].loadua(w[src_idx].mV);
+ prim->mJoints[dst_idx] = j[src_idx];
+ }
+ }
+
+ prim->mGLMode = LLRender::TRIANGLES;
+ }
+
+ uint32_t GetNumFaces()
+ {
+ return uint32_t(p.size()/3);
+ }
+
+ uint32_t GetNumVerticesOfFace(const uint32_t face_num)
+ {
+ return 3;
+ }
+
+ mikk::float3 GetPosition(const uint32_t face_num, const uint32_t vert_num)
+ {
+ F32* v = p[face_num * 3 + vert_num].mV;
+ return mikk::float3(v);
+ }
+
+ mikk::float3 GetTexCoord(const uint32_t face_num, const uint32_t vert_num)
+ {
+ F32* uv = 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 Primitive::prep(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);
+
+ for (auto& idx : mIndexArray)
+ {
+ if (idx >= mPositions.size())
+ {
+ LL_WARNS("GLTF") << "Invalid index array" << LL_ENDL;
+ return false;
+ }
+ }
+ }
+
+ U32 mask = LLVertexBuffer::MAP_VERTEX;
+
+ if (!mWeights.empty())
+ {
+ mask |= LLVertexBuffer::MAP_WEIGHT4;
+ mask |= LLVertexBuffer::MAP_JOINT;
+ }
+
+ if (mTexCoords.empty())
+ {
+ mTexCoords.resize(mPositions.size());
+ }
+
+ // TODO: support more than one texcoord set (or no texcoords)
+ mask |= LLVertexBuffer::MAP_TEXCOORD0;
+
+ if (mColors.empty())
+ {
+ mColors.resize(mPositions.size(), LLColor4U::white);
+ }
+
+ // TODO: support colorless vertex buffers
+ mask |= LLVertexBuffer::MAP_COLOR;
+
+ mShaderVariant = 0;
+
+ bool unlit = false;
+
+ // bake material basecolor into color array
+ if (mMaterial != INVALID_INDEX)
+ {
+ const Material& material = asset.mMaterials[mMaterial];
+ LLColor4 baseColor(glm::value_ptr(material.mPbrMetallicRoughness.mBaseColorFactor));
+ for (auto& dst : mColors)
+ {
+ dst = LLColor4U(baseColor * LLColor4(dst));
+ }
+
+ if (material.mUnlit.mPresent)
+ { // material uses KHR_materials_unlit
+ mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
+ unlit = true;
+ }
+ }
+
+ if (mNormals.empty() && !unlit)
+ {
+ mTangents.clear();
+
+ if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP)
+ { //no normals and no surfaces, this primitive is unlit
+ mTangents.clear();
+ mShaderVariant |= LLGLSLShader::GLTFVariant::UNLIT;
+ unlit = true;
+ }
+ else
+ {
+ // unroll into non-indexed array of flat shaded triangles
+ MikktMesh data;
+ if (!data.copy(this))
+ {
+ return false;
+ }
+
+ data.genNormals();
+ data.genTangents();
+ data.write(this);
+ }
+ }
+
+ if (mTangents.empty() && !unlit)
+ { // NOTE: must be done last because tangent generation rewrites the other arrays
+ // adapted from usage of Mikktspace in llvolume.cpp
+ if (mMode == Mode::POINTS || mMode == Mode::LINES || mMode == Mode::LINE_LOOP || mMode == Mode::LINE_STRIP)
+ {
+ // for points and lines, just make sure tangent is perpendicular to normal
+ mTangents.resize(mNormals.size());
+ LLVector4a up(0.f, 0.f, 1.f, 0.f);
+ LLVector4a left(1.f, 0.f, 0.f, 0.f);
+ for (U32 i = 0; i < mNormals.size(); ++i)
+ {
+ if (fabsf(mNormals[i].getF32ptr()[2]) < 0.999f)
+ {
+ mTangents[i] = up.cross3(mNormals[i]);
+ }
+ else
+ {
+ mTangents[i] = left.cross3(mNormals[i]);
+ }
+
+ mTangents[i].getF32ptr()[3] = 1.f;
+ }
+ }
+ else
+ {
+ MikktMesh data;
+ if (!data.copy(this))
+ {
+ return false;
+ }
+
+ data.genTangents();
+ data.write(this);
+ }
+ }
+
+ if (!mNormals.empty())
+ {
+ mask |= LLVertexBuffer::MAP_NORMAL;
+ }
+
+ if (!mTangents.empty())
+ {
+ mask |= LLVertexBuffer::MAP_TANGENT;
+ }
+
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
+ }
+
+ mVertexBuffer = new LLVertexBuffer(mask);
+ mVertexBuffer->allocateBuffer(mPositions.size(), mIndexArray.size() * 2); // double the size of the index buffer for 32-bit indices
+
+ mVertexBuffer->setBuffer();
+ mVertexBuffer->setPositionData(mPositions.data());
+ mVertexBuffer->setColorData(mColors.data());
+
+ if (!mNormals.empty())
+ {
+ mVertexBuffer->setNormalData(mNormals.data());
+ }
+ if (!mTangents.empty())
+ {
+ mVertexBuffer->setTangentData(mTangents.data());
+ }
+
+ if (!mWeights.empty())
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::RIGGED;
+ mVertexBuffer->setWeight4Data(mWeights.data());
+ mVertexBuffer->setJointData(mJoints.data());
+ }
+
+ // 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 (!mIndexArray.empty())
+ {
+ mVertexBuffer->setIndexData(mIndexArray.data());
+ }
+
+ createOctree();
+
+ mVertexBuffer->unbind();
+
+ Material& material = asset.mMaterials[mMaterial];
+ if (material.mAlphaMode == Material::AlphaMode::BLEND)
+ {
+ mShaderVariant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ }
+
+ return true;
+}
+
+void initOctreeTriangle(LLVolumeTriangle* tri, F32 scaler, S32 i0, S32 i1, S32 i2, const LLVector4a& v0, const LLVector4a& v1, const LLVector4a& v2)
+{
+ //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 == 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 == 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 == 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 == Mode::POINTS ||
+ mMode == Mode::LINES ||
+ mMode == Mode::LINE_LOOP ||
+ mMode == Mode::LINE_STRIP)
+ {
+ // nothing to do, no volume... maybe add some collision geometry around these primitive types?
+ }
+
+ 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;
+}
+
+LLRender::eGeomModes gltf_mode_to_gl_mode(Primitive::Mode mode)
+{
+ switch (mode)
+ {
+ case Primitive::Mode::POINTS:
+ return LLRender::POINTS;
+ case Primitive::Mode::LINES:
+ return LLRender::LINES;
+ case Primitive::Mode::LINE_LOOP:
+ return LLRender::LINE_LOOP;
+ case Primitive::Mode::LINE_STRIP:
+ return LLRender::LINE_STRIP;
+ case Primitive::Mode::TRIANGLES:
+ return LLRender::TRIANGLES;
+ case Primitive::Mode::TRIANGLE_STRIP:
+ return LLRender::TRIANGLE_STRIP;
+ case Primitive::Mode::TRIANGLE_FAN:
+ return LLRender::TRIANGLE_FAN;
+ default:
+ return LLRender::TRIANGLES;
+ }
+}
+
+void Primitive::serialize(boost::json::object& dst) const
+{
+ write(mMaterial, "material", dst, -1);
+ write(mMode, "mode", dst, Primitive::Mode::TRIANGLES);
+ write(mIndices, "indices", dst, INVALID_INDEX);
+ write(mAttributes, "attributes", dst);
+}
+
+const Primitive& Primitive::operator=(const Value& src)
+{
+ if (src.is_object())
+ {
+ copy(src, "material", mMaterial);
+ copy(src, "mode", mMode);
+ copy(src, "indices", mIndices);
+ copy(src, "attributes", mAttributes);
+
+ mGLMode = gltf_mode_to_gl_mode(mMode);
+ }
+ return *this;
+}
+
diff --git a/indra/newview/gltf/primitive.h b/indra/newview/gltf/primitive.h
new file mode 100644
index 0000000000..f9d7c63c65
--- /dev/null
+++ b/indra/newview/gltf/primitive.h
@@ -0,0 +1,104 @@
+#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"
+#include "boost/json.hpp"
+
+// LL GLTF Implementation
+namespace LL
+{
+ namespace GLTF
+ {
+ using Value = boost::json::value;
+ class Asset;
+
+ class Primitive
+ {
+ public:
+ enum class Mode : U8
+ {
+ POINTS,
+ LINES,
+ LINE_LOOP,
+ LINE_STRIP,
+ TRIANGLES,
+ TRIANGLE_STRIP,
+ TRIANGLE_FAN
+ };
+
+ ~Primitive();
+
+ // GPU copy of mesh data
+ LLPointer<LLVertexBuffer> mVertexBuffer;
+
+ // CPU copy of mesh data, keep these as LLVector types for compatibility with raycasting code
+ std::vector<LLVector2> mTexCoords;
+ std::vector<LLVector4a> mNormals;
+ std::vector<LLVector4a> mTangents;
+ std::vector<LLVector4a> mPositions;
+ std::vector<U64> 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;
+ Mode mMode = Mode::TRIANGLES; // default to triangles
+ LLRender::eGeomModes mGLMode = LLRender::TRIANGLES; // for use with LLRender
+ S32 mIndices = -1;
+
+ // shader variant according to LLGLSLShader::GLTFVariant flags
+ U8 mShaderVariant = 0;
+
+ std::unordered_map<std::string, S32> mAttributes;
+
+ // create octree based on vertex buffer
+ // must be called before buffer is unmapped and after buffer is populated with good data
+ 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
+ );
+
+ void serialize(boost::json::object& obj) const;
+ const Primitive& operator=(const Value& src);
+
+ bool prep(Asset& asset);
+ };
+ }
+}
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
new file mode 100644
index 0000000000..d7eb605489
--- /dev/null
+++ b/indra/newview/gltfscenemanager.cpp
@@ -0,0 +1,1139 @@
+/**
+ * @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"
+#include "llviewertexturelist.h"
+#include "llimagej2c.h"
+#include "llfloaterperms.h"
+#include "llagentbenefits.h"
+#include "llfilesystem.h"
+#include "boost/json.hpp"
+
+#define GLTF_SIM_SUPPORT 1
+
+using namespace LL;
+
+// 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,
+ false);
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFOpenSelection");
+ }
+}
+
+void GLTFSceneManager::saveAs()
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset)
+ {
+ LLFilePickerReplyThread::startPicker(
+ [](const std::vector<std::string>& filenames, LLFilePicker::ELoadFilter load_filter, LLFilePicker::ESaveFilter save_filter)
+ {
+ if (LLAppViewer::instance()->quitRequested())
+ {
+ return;
+ }
+ if (filenames.size() > 0)
+ {
+ GLTFSceneManager::instance().save(filenames[0]);
+ }
+ },
+ LLFilePicker::FFSAVE_GLTF,
+ "scene.gltf");
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFSaveSelection");
+ }
+}
+
+void GLTFSceneManager::uploadSelection()
+{
+ if (mUploadingAsset)
+ { // upload already in progress
+ LLNotificationsUtil::add("GLTFUploadInProgress");
+ return;
+ }
+
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset)
+ {
+ // make a copy of the asset prior to uploading
+ mUploadingAsset = std::make_shared<Asset>();
+ mUploadingObject = obj;
+ *mUploadingAsset = *obj->mGLTFAsset;
+
+ GLTF::Asset& asset = *mUploadingAsset;
+
+ for (auto& image : asset.mImages)
+ {
+ if (image.mTexture.notNull())
+ {
+ mPendingImageUploads++;
+
+ LLPointer<LLImageRaw> raw;
+
+ if (image.mBufferView != INVALID_INDEX)
+ {
+ BufferView& view = asset.mBufferViews[image.mBufferView];
+ Buffer& buffer = asset.mBuffers[view.mBuffer];
+
+ raw = LLViewerTextureManager::getRawImageFromMemory(buffer.mData.data() + view.mByteOffset, view.mByteLength, image.mMimeType);
+
+ image.clearData(asset);
+ }
+ else
+ {
+ raw = image.mTexture->getCachedRawImage();
+ }
+
+ if (raw.notNull())
+ {
+ LLPointer<LLImageJ2C> j2c = LLViewerTextureList::convertToUploadFile(raw);
+
+ std::string buffer;
+ buffer.assign((const char*)j2c->getData(), j2c->getDataSize());
+
+ LLUUID asset_id = LLUUID::generateNewID();
+
+ std::string name;
+ S32 idx = (S32)(&image - &asset.mImages[0]);
+
+ if (image.mName.empty())
+ {
+
+ name = llformat("Image_%d", idx);
+ }
+ else
+ {
+ name = image.mName;
+ }
+
+ LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
+ {
+ // TODO: handle failure
+ mPendingImageUploads--;
+ return false;
+ };
+
+
+ LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx, raw, j2c](LLUUID assetId, LLSD response)
+ {
+ if (mUploadingAsset && mUploadingAsset->mImages.size() > idx)
+ {
+ mUploadingAsset->mImages[idx].mUri = assetId.asString();
+ mPendingImageUploads--;
+ }
+ };
+
+ S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(j2c);
+
+ LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
+ buffer,
+ asset_id,
+ name,
+ name,
+ 0,
+ LLFolderType::FT_TEXTURE,
+ LLInventoryType::IT_TEXTURE,
+ LLAssetType::AT_TEXTURE,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost,
+ false,
+ finish,
+ failure));
+
+ upload_new_resource(uploadInfo);
+ }
+ }
+ }
+
+ // upload .bin
+ for (auto& bin : asset.mBuffers)
+ {
+ mPendingBinaryUploads++;
+
+ S32 idx = (S32)(&bin - &asset.mBuffers[0]);
+
+ std::string buffer;
+ buffer.assign((const char*)bin.mData.data(), bin.mData.size());
+
+ LLUUID asset_id = LLUUID::generateNewID();
+
+ LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
+ {
+ // TODO: handle failure
+ mPendingBinaryUploads--;
+ mUploadingAsset = nullptr;
+ mUploadingObject = nullptr;
+ LL_WARNS("GLTF") << "Failed to upload GLTF binary: " << reason << LL_ENDL;
+ LL_WARNS("GLTF") << response << LL_ENDL;
+ return false;
+ };
+
+ LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, idx](LLUUID assetId, LLSD response)
+ {
+ if (mUploadingAsset && mUploadingAsset->mBuffers.size() > idx)
+ {
+ mUploadingAsset->mBuffers[idx].mUri = assetId.asString();
+ mPendingBinaryUploads--;
+
+ // HACK: save buffer to cache to emulate a successful download
+ LLFileSystem cache(assetId, LLAssetType::AT_GLTF_BIN, LLFileSystem::WRITE);
+ auto& data = mUploadingAsset->mBuffers[idx].mData;
+
+ cache.write((const U8*)data.data(), data.size());
+ }
+ };
+#if GLTF_SIM_SUPPORT
+ S32 expected_upload_cost = 1;
+
+ LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
+ buffer,
+ asset_id,
+ "",
+ "",
+ 0,
+ LLFolderType::FT_NONE,
+ LLInventoryType::IT_GLTF_BIN,
+ LLAssetType::AT_GLTF_BIN,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost,
+ false,
+ finish,
+ failure));
+
+ upload_new_resource(uploadInfo);
+#else
+ // dummy finish
+ finish(LLUUID::generateNewID(), LLSD());
+#endif
+ }
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFUploadSelection");
+ }
+}
+
+void GLTFSceneManager::save(const std::string& filename)
+{
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+ if (obj && obj->mGLTFAsset)
+ {
+ Asset* asset = obj->mGLTFAsset.get();
+ if (!asset->save(filename))
+ {
+ LLNotificationsUtil::add("GLTFSaveFailed");
+ }
+ }
+}
+
+void GLTFSceneManager::load(const std::string& filename)
+{
+ std::shared_ptr<Asset> asset = std::make_shared<Asset>();
+
+ if (asset->load(filename))
+ {
+ gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
+ asset->updateTransforms();
+
+ // hang the asset off the currently selected object, or off of the avatar if no object is selected
+ LLViewerObject* obj = LLSelectMgr::instance().getSelection()->getFirstRootObject();
+
+ if (obj)
+ { // assign to self avatar
+ obj->mGLTFAsset = asset;
+ obj->markForUpdate();
+ if (std::find(mObjects.begin(), mObjects.end(), obj) == mObjects.end())
+ {
+ mObjects.push_back(obj);
+ }
+ }
+ }
+ else
+ {
+ LLNotificationsUtil::add("GLTFLoadFailed");
+ }
+}
+
+GLTFSceneManager::~GLTFSceneManager()
+{
+ mObjects.clear();
+}
+
+void GLTFSceneManager::renderOpaque()
+{
+ render(true);
+}
+
+void GLTFSceneManager::renderAlpha()
+{
+ render(false);
+}
+
+void GLTFSceneManager::addGLTFObject(LLViewerObject* obj, LLUUID gltf_id)
+{
+ llassert(obj->getVolume()->getParams().getSculptID() == gltf_id);
+ llassert(obj->getVolume()->getParams().getSculptType() == LL_SCULPT_TYPE_GLTF);
+
+ obj->ref();
+ gAssetStorage->getAssetData(gltf_id, LLAssetType::AT_GLTF, onGLTFLoadComplete, obj);
+}
+
+//static
+void GLTFSceneManager::onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
+{
+ LLViewerObject* obj = (LLViewerObject*)user_data;
+ llassert(asset_type == LLAssetType::AT_GLTF_BIN);
+
+ if (status == LL_ERR_NOERR)
+ {
+ if (obj)
+ {
+ // find the Buffer with the given id in the asset
+ if (obj->mGLTFAsset)
+ {
+ obj->mGLTFAsset->mPendingBuffers--;
+
+
+ if (obj->mGLTFAsset->mPendingBuffers == 0)
+ {
+ if (obj->mGLTFAsset->prep())
+ {
+ GLTFSceneManager& mgr = GLTFSceneManager::instance();
+ if (std::find(mgr.mObjects.begin(), mgr.mObjects.end(), obj) == mgr.mObjects.end())
+ {
+ GLTFSceneManager::instance().mObjects.push_back(obj);
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to prepare GLTF asset: " << id << LL_ENDL;
+ obj->mGLTFAsset = nullptr;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
+ obj->unref();
+ }
+}
+
+//static
+void GLTFSceneManager::onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
+{
+ LLViewerObject* obj = (LLViewerObject*)user_data;
+ llassert(asset_type == LLAssetType::AT_GLTF);
+
+ if (status == LL_ERR_NOERR)
+ {
+ if (obj)
+ {
+ LLFileSystem file(id, asset_type, LLFileSystem::READ);
+ std::string data;
+ data.resize(file.getSize());
+ file.read((U8*)data.data(), data.size());
+
+ boost::json::value json = boost::json::parse(data);
+
+ std::shared_ptr<Asset> asset = std::make_shared<Asset>(json);
+ obj->mGLTFAsset = asset;
+
+ for (auto& buffer : asset->mBuffers)
+ {
+ // for now just assume the buffer is already in the asset cache
+ LLUUID buffer_id;
+ if (LLUUID::parseUUID(buffer.mUri, &buffer_id))
+ {
+ asset->mPendingBuffers++;
+
+ gAssetStorage->getAssetData(buffer_id, LLAssetType::AT_GLTF_BIN, onGLTFBinLoadComplete, obj);
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Buffer URI is not a valid UUID: " << buffer.mUri << LL_ENDL;
+ obj->unref();
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF") << "Failed to load GLTF asset: " << id << LL_ENDL;
+ obj->unref();
+ }
+}
+
+void GLTFSceneManager::update()
+{
+ for (U32 i = 0; i < mObjects.size(); ++i)
+ {
+ if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr)
+ {
+ mObjects.erase(mObjects.begin() + i);
+ --i;
+ continue;
+ }
+
+ mObjects[i]->mGLTFAsset->update();
+ }
+
+ // process pending uploads
+ if (mUploadingAsset && !mGLTFUploadPending)
+ {
+ if (mPendingImageUploads == 0 && mPendingBinaryUploads == 0)
+ {
+ boost::json::object obj;
+ mUploadingAsset->serialize(obj);
+ std::string buffer = boost::json::serialize(obj, {});
+
+ LLNewBufferedResourceUploadInfo::uploadFailure_f failure = [this](LLUUID assetId, LLSD response, std::string reason)
+ {
+ // TODO: handle failure
+ LL_WARNS("GLTF") << "Failed to upload GLTF json: " << reason << LL_ENDL;
+ LL_WARNS("GLTF") << response << LL_ENDL;
+
+ mUploadingAsset = nullptr;
+ mUploadingObject = nullptr;
+ mGLTFUploadPending = false;
+ return false;
+ };
+
+ LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response)
+ {
+ LLAppViewer::instance()->postToMainCoro(
+ [=]()
+ {
+ if (mUploadingAsset)
+ {
+ // HACK: save buffer to cache to emulate a successful upload
+ LLFileSystem cache(assetId, LLAssetType::AT_GLTF, LLFileSystem::WRITE);
+
+ LL_INFOS("GLTF") << "Uploaded GLTF json: " << assetId << LL_ENDL;
+ cache.write((const U8 *) buffer.c_str(), buffer.size());
+
+ mUploadingAsset = nullptr;
+ }
+
+ if (mUploadingObject)
+ {
+ mUploadingObject->mGLTFAsset = nullptr;
+ mUploadingObject->setGLTFAsset(assetId);
+ mUploadingObject->markForUpdate();
+ mUploadingObject = nullptr;
+ }
+
+ mGLTFUploadPending = false;
+ });
+ };
+
+#if GLTF_SIM_SUPPORT
+ S32 expected_upload_cost = 1;
+ LLUUID asset_id = LLUUID::generateNewID();
+
+ mGLTFUploadPending = true;
+
+ LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared<LLNewBufferedResourceUploadInfo>(
+ buffer,
+ asset_id,
+ "",
+ "",
+ 0,
+ LLFolderType::FT_NONE,
+ LLInventoryType::IT_GLTF,
+ LLAssetType::AT_GLTF,
+ LLFloaterPerms::getNextOwnerPerms("Uploads"),
+ LLFloaterPerms::getGroupPerms("Uploads"),
+ LLFloaterPerms::getEveryonePerms("Uploads"),
+ expected_upload_cost,
+ false,
+ finish,
+ failure));
+
+ upload_new_resource(uploadInfo);
+#else
+ // dummy finish
+ finish(LLUUID::generateNewID(), LLSD());
+#endif
+ }
+ }
+}
+
+void GLTFSceneManager::render(bool opaque, bool rigged, bool unlit)
+{
+ U8 variant = 0;
+ if (rigged)
+ {
+ variant |= LLGLSLShader::GLTFVariant::RIGGED;
+ }
+ if (!opaque)
+ {
+ variant |= LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ }
+ if (unlit)
+ {
+ variant |= LLGLSLShader::GLTFVariant::UNLIT;
+ }
+
+ render(variant);
+}
+
+void GLTFSceneManager::render(U8 variant)
+{
+ // 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 boundd
+
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+
+ bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED;
+
+ for (U32 i = 0; i < mObjects.size(); ++i)
+ {
+ if (mObjects[i]->isDead() || mObjects[i]->mGLTFAsset == nullptr)
+ {
+ mObjects.erase(mObjects.begin() + i);
+ --i;
+ continue;
+ }
+
+ Asset* asset = mObjects[i]->mGLTFAsset.get();
+ gGL.pushMatrix();
+
+ LLMatrix4a mat = mObjects[i]->getGLTFAssetToAgentTransform();
+
+ LLMatrix4a modelview;
+ modelview.loadu(gGLModelView);
+
+ matMul(mat, modelview, modelview);
+
+ mat4 mdv = glm::make_mat4(modelview.getF32ptr());
+ asset->updateRenderTransforms(mdv);
+
+ if (rigged)
+ { // provide a modelview matrix that goes from asset to camera space for rigged render passes
+ // (matrix palettes are in asset space)
+ gGL.loadMatrix(glm::value_ptr(mdv));
+ }
+ render(*asset, variant);
+
+ gGL.popMatrix();
+ }
+}
+
+void GLTFSceneManager::render(Asset& asset, U8 variant)
+{
+ bool opaque = !(variant & LLGLSLShader::GLTFVariant::ALPHA_BLEND);
+ bool rigged = variant & LLGLSLShader::GLTFVariant::RIGGED;
+
+ if (opaque)
+ {
+ gGLTFPBRMetallicRoughnessProgram.bind(variant);
+ }
+ else
+ { // alpha shaders need all the shadow map setup etc
+ gPipeline.bindDeferredShader(gGLTFPBRMetallicRoughnessProgram.mGLTFVariants[variant]);
+ }
+
+ for (auto& node : asset.mNodes)
+ {
+ if (node.mSkin != INVALID_INDEX)
+ {
+ if (rigged)
+ {
+ Skin& skin = asset.mSkins[node.mSkin];
+ glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_GLTF_JOINTS, skin.mUBO);
+ }
+ }
+
+ if (node.mMesh != INVALID_INDEX)
+ {
+ Mesh& mesh = asset.mMeshes[node.mMesh];
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ if (primitive.mShaderVariant != variant)
+ {
+ continue;
+ }
+
+ if (!rigged)
+ {
+ gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
+ }
+ bool cull = true;
+ if (primitive.mMaterial != INVALID_INDEX)
+ {
+ Material& material = asset.mMaterials[primitive.mMaterial];
+ bind(asset, material);
+
+ cull = !material.mDoubleSided;
+ }
+ else
+ {
+ LLFetchedGLTFMaterial::sDefault.bind();
+ }
+
+ LLGLDisable cull_face(!cull ? GL_CULL_FACE : 0);
+
+ primitive.mVertexBuffer->setBuffer();
+ if (primitive.mVertexBuffer->getNumIndices() > 0)
+ {
+ primitive.mVertexBuffer->draw(primitive.mGLMode, primitive.mVertexBuffer->getNumIndices(), 0);
+ }
+ else
+ {
+ primitive.mVertexBuffer->drawArrays(primitive.mGLMode, 0, primitive.mVertexBuffer->getNumVerts());
+ }
+ }
+ }
+ }
+}
+
+static void bindTexture(Asset& asset, S32 uniform, Material::TextureInfo& info, LLViewerTexture* fallback)
+{
+ if (info.mIndex != INVALID_INDEX)
+ {
+ LLViewerTexture* tex = asset.mImages[asset.mTextures[info.mIndex].mSource].mTexture;
+ if (tex)
+ {
+ tex->addTextureStats(2048.f * 2048.f);
+ LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, tex);
+ }
+ else
+ {
+ LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback);
+ }
+ }
+ else
+ {
+ LLGLSLShader::sCurBoundShaderPtr->bindTexture(uniform, fallback);
+ }
+}
+
+
+void GLTFSceneManager::bind(Asset& asset, Material& material)
+{
+ // bind for rendering (derived from LLFetchedGLTFMaterial::bind)
+ // glTF 2.0 Specification 3.9.4. Alpha Coverage
+ // mAlphaCutoff is only valid for LLGLTFMaterial::ALPHA_MODE_MASK
+ F32 min_alpha = -1.0;
+
+ LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
+
+ if (!LLPipeline::sShadowRender || (material.mAlphaMode == Material::AlphaMode::BLEND))
+ {
+ if (material.mAlphaMode == Material::AlphaMode::MASK)
+ {
+ // dividing the alpha cutoff by transparency here allows the shader to compare against
+ // the alpha value of the texture without needing the transparency value
+ if (material.mPbrMetallicRoughness.mBaseColorFactor.a > 0.f)
+ {
+ min_alpha = material.mAlphaCutoff / material.mPbrMetallicRoughness.mBaseColorFactor.a;
+ }
+ else
+ {
+ min_alpha = 1024.f;
+ }
+ }
+ shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha);
+ }
+
+ bindTexture(asset, LLShaderMgr::DIFFUSE_MAP, material.mPbrMetallicRoughness.mBaseColorTexture, LLViewerFetchedTexture::sWhiteImagep);
+
+ F32 base_color_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR].getPacked(base_color_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_BASE_COLOR_TRANSFORM, 2, (F32*)base_color_packed);
+
+ if (!LLPipeline::sShadowRender)
+ {
+ bindTexture(asset, LLShaderMgr::NORMAL_MAP, material.mNormalTexture, LLViewerFetchedTexture::sFlatNormalImagep);
+ bindTexture(asset, LLShaderMgr::METALLIC_ROUGHNESS_MAP, material.mPbrMetallicRoughness.mMetallicRoughnessTexture, LLViewerFetchedTexture::sWhiteImagep);
+ bindTexture(asset, LLShaderMgr::OCCLUSION_MAP, material.mOcclusionTexture, LLViewerFetchedTexture::sWhiteImagep);
+ bindTexture(asset, LLShaderMgr::EMISSIVE_MAP, material.mEmissiveTexture, LLViewerFetchedTexture::sWhiteImagep);
+
+ // NOTE: base color factor is baked into vertex stream
+
+ shader->uniform1f(LLShaderMgr::ROUGHNESS_FACTOR, material.mPbrMetallicRoughness.mRoughnessFactor);
+ shader->uniform1f(LLShaderMgr::METALLIC_FACTOR, material.mPbrMetallicRoughness.mMetallicFactor);
+ shader->uniform3fv(LLShaderMgr::EMISSIVE_COLOR, 1, glm::value_ptr(material.mEmissiveFactor));
+
+ F32 normal_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL].getPacked(normal_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_NORMAL_TRANSFORM, 2, (F32*)normal_packed);
+
+ F32 metallic_roughness_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS].getPacked(metallic_roughness_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_METALLIC_ROUGHNESS_TRANSFORM, 2, (F32*)metallic_roughness_packed);
+
+ F32 emissive_packed[8];
+ //mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
+ LLGLTFMaterial::sDefault.mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
+ shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed);
+ }
+}
+
+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.get(), start, local_end, -1, pick_transparent, pick_rigged, pick_unselectable, node_hit, primitive_hit, &position, tex_coord, normal, tangent))
+ {
+ local_end = position;
+ if (intersection)
+ {
+ *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();
+
+ vec4 start;
+ vec4 end;
+
+ LLVector4a t;
+ agent_to_asset.affineTransform(gDebugRaycastStart, t);
+ start = glm::make_vec4(t.getF32ptr());
+ agent_to_asset.affineTransform(gDebugRaycastEnd, t);
+ end = glm::make_vec4(t.getF32ptr());
+
+ start.w = end.w = 1.0;
+
+ for (auto& node : asset->mNodes)
+ {
+ Mesh& mesh = asset->mMeshes[node.mMesh];
+
+ if (node.mMesh != INVALID_INDEX)
+ {
+ gGL.loadMatrix((F32*)glm::value_ptr(node.mRenderMatrix));
+
+ // 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 1
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
+ {
+ gGL.flush();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ // convert raycast to node local space
+ vec4 local_start = node.mAssetMatrixInv * start;
+ vec4 local_end = node.mAssetMatrixInv * end;
+
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ if (primitive.mOctree.notNull())
+ {
+ LLVector4a s, e;
+ s.load3(glm::value_ptr(local_start));
+ e.load3(glm::value_ptr(local_end));
+ renderOctreeRaycast(s, e, 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;
+ }
+
+ mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
+
+ mat4 modelview = glm::make_mat4(gGLModelView);
+
+
+ modelview = modelview * mat;
+
+ Asset* asset = obj->mGLTFAsset.get();
+
+ for (auto& node : asset->mNodes)
+ {
+ node.mRenderMatrix = modelview * node.mAssetMatrix;
+ }
+ }
+
+ for (auto& obj : mObjects)
+ {
+ if (obj->isDead() || obj->mGLTFAsset == nullptr)
+ {
+ continue;
+ }
+
+ Asset* asset = obj->mGLTFAsset.get();
+
+ 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 ? GL_TRUE : GL_FALSE);
+
+
+ gGL.pushMatrix();
+
+ for (auto& obj : mObjects)
+ {
+ if (obj->isDead() || obj->mGLTFAsset == nullptr)
+ {
+ continue;
+ }
+
+ mat4 mat = glm::make_mat4(obj->getGLTFAssetToAgentTransform().getF32ptr());
+
+ mat4 modelview = glm::make_mat4(gGLModelView);
+
+ modelview = modelview * mat;
+
+ Asset* asset = obj->mGLTFAsset.get();
+
+ for (auto& node : asset->mNodes)
+ {
+ // force update all mRenderMatrix, not just nodes with meshes
+ node.mRenderMatrix = modelview * node.mAssetMatrix;
+
+ gGL.loadMatrix(glm::value_ptr(node.mRenderMatrix));
+ // render x-axis red, y-axis green, z-axis blue
+ gGL.color4f(1.f, 0.f, 0.f, 0.5f);
+ gGL.begin(LLRender::LINES);
+ 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(glm::value_ptr(child.mMatrix[3]));
+ }
+ 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.get();
+ Node* node = &asset->mNodes[node_hit];
+ Primitive* primitive = &asset->mMeshes[node->mMesh].mPrimitives[primitive_hit];
+
+ gGL.flush();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ gGL.color3f(1, 0, 1);
+ drawBoxOutline(intersection, LLVector4a(0.1f, 0.1f, 0.1f, 0.f));
+
+ gGL.loadMatrix(glm::value_ptr(node->mRenderMatrix));
+
+
+ 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..7da413e8b2
--- /dev/null
+++ b/indra/newview/gltfscenemanager.h
@@ -0,0 +1,100 @@
+#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"
+#include "gltf/common.h"
+
+class LLVOVolume;
+class LLDrawable;
+
+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 saveAs(); // open filepicker and choose file to save selected asset to
+ void save(const std::string& filename); // save selected asset to filename (suitable for use in external programs)
+ void uploadSelection(); // decompose selected asset and upload to simulator
+
+ void update();
+ void render(bool opaque, bool rigged = false, bool unlit = false);
+
+ // render the given variant of all assets
+ // variant - bitmask according to LLGLSLShader::GLTFVariant flags
+ void render(U8 variant);
+
+ void render(LL::GLTF::Asset& asset, U8 variant);
+
+ // bind the given material for rendering
+ void bind(LL::GLTF::Asset& asset, LL::GLTF::Material& material);
+
+ void renderOpaque();
+ void renderAlpha();
+
+ 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, LL::GLTF::Asset* asset, const LLVector4a& start, const LLVector4a& end, S32 face, bool pick_transparent, bool pick_rigged, bool pick_unselectable, S32* face_hitp, S32* primitive_hitp,
+ LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
+
+ void renderDebug();
+
+ void addGLTFObject(LLViewerObject* object, LLUUID gltf_id);
+ static void onGLTFLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status);
+ static void onGLTFBinLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status);
+
+ std::vector<LLPointer<LLViewerObject>> mObjects;
+
+ std::shared_ptr<GLTF::Asset> mUploadingAsset;
+ bool mGLTFUploadPending = false;
+ LLPointer<LLViewerObject> mUploadingObject;
+ U32 mPendingImageUploads = 0;
+ U32 mPendingBinaryUploads = 0;
+ U32 mPendingGLTFUploads = 0;
+
+ U32 mJointUBO = 0;
+ };
+}
+
+
diff --git a/indra/newview/llagentbenefits.cpp b/indra/newview/llagentbenefits.cpp
index c2a1589682..e9f00f6556 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,6 +161,49 @@ 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 0;
+}
+
+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;
diff --git a/indra/newview/llagentbenefits.h b/indra/newview/llagentbenefits.h
index 1afc80a6cc..ff23241aa9 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 6bdc8752d9..25e777191f 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 5a04494663..3b3345e8b2 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -227,6 +227,7 @@
#include "pipeline.h"
#include "llgesturemgr.h"
#include "llsky.h"
+#include "llvlcomposition.h"
#include "llvlmanager.h"
#include "llviewercamera.h"
#include "lldrawpoolbump.h"
@@ -237,6 +238,8 @@
#include "llavatariconctrl.h"
#include "llgroupiconctrl.h"
#include "llviewerassetstats.h"
+#include "gltfscenemanager.h"
+
#include "workqueue.h"
using namespace LL;
@@ -1271,6 +1274,8 @@ bool LLAppViewer::init()
LLWorld::createInstance();
LLSelectMgr::createInstance();
LLViewerCamera::createInstance();
+ LL::GLTFSceneManager::createInstance();
+
#if LL_WINDOWS
if (!mSecondInstance)
@@ -2146,7 +2151,7 @@ bool LLAppViewer::cleanup()
ll_close_fail_log();
LLError::LLCallStacks::cleanup();
-
+ LL::GLTFSceneManager::deleteSingleton();
LLEnvironment::deleteSingleton();
LLSelectMgr::deleteSingleton();
LLViewerEventRecorder::deleteSingleton();
@@ -4853,6 +4858,7 @@ void LLAppViewer::idle()
if (!(logoutRequestSent() && hasSavedFinalSnapshot()))
{
gObjectList.update(gAgent);
+ LL::GLTFSceneManager::instance().update();
}
}
@@ -5189,6 +5195,11 @@ void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp)
}
}
+void LLAppViewer::postToMainCoro(const LL::WorkQueue::Work& work)
+{
+ gMainloopWork.post(work);
+}
+
void LLAppViewer::idleNameCache()
{
// Neither old nor new name cache can function before agent has a region
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 4245e3da97..a9fcd82ba6 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -226,6 +226,9 @@ public:
void updateNameLookupUrl(const LLViewerRegion* regionp);
+ // post given work to the "mainloop" work queue for handling on the main thread
+ void postToMainCoro(const LL::WorkQueue::Work& work);
+
protected:
virtual bool initWindow(); // Initialize the viewer's window.
virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system
diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
index ec9cf2c8b7..b0befa62e6 100644
--- a/indra/newview/llavatarrenderinfoaccountant.cpp
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -236,8 +236,6 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U
!avatar->isControlAvatar() && // Not part of an animated object
avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region
{
- avatar->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date
-
LLSD info = LLSD::emptyMap();
U32 avatar_complexity = avatar->getVisualComplexity();
if (avatar_complexity > 0)
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
index 38fd8d1f80..a7ac9da618 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 ea623b93fc..9ba4f7f300 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 1f1d9e2121..01a29f71a0 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 eb48653849..93fea899f3 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"
@@ -92,7 +93,7 @@ S32 LLDrawPoolAlpha::getNumPostDeferredPasses()
}
// set some common parameters on the given shader to prepare for alpha rendering
-static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool deferredEnvironment, F32 water_sign)
+static void prepare_alpha_shader(LLGLSLShader* shader, bool deferredEnvironment, F32 water_sign)
{
static LLCachedControl<F32> displayGamma(gSavedSettings, "RenderDeferredDisplayGamma");
F32 gamma = displayGamma;
@@ -131,15 +132,11 @@ static void prepare_alpha_shader(LLGLSLShader* shader, bool textureGamma, bool d
{
shader->setMinimumAlpha(MINIMUM_ALPHA);
}
- if (textureGamma)
- {
- shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
- }
//also prepare rigged variant
if (shader->mRiggedVariant && shader->mRiggedVariant != shader)
{
- prepare_alpha_shader(shader->mRiggedVariant, textureGamma, deferredEnvironment, water_sign);
+ prepare_alpha_shader(shader->mRiggedVariant, deferredEnvironment, water_sign);
}
}
@@ -170,36 +167,36 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass)
llassert(LLPipeline::sRenderDeferred);
emissive_shader = &gDeferredEmissiveProgram;
- prepare_alpha_shader(emissive_shader, true, false, water_sign);
+ prepare_alpha_shader(emissive_shader, false, water_sign);
pbr_emissive_shader = &gPBRGlowProgram;
- prepare_alpha_shader(pbr_emissive_shader, true, false, water_sign);
+ prepare_alpha_shader(pbr_emissive_shader, false, water_sign);
fullbright_shader =
(LLPipeline::sImpostorRender) ? &gDeferredFullbrightAlphaMaskProgram :
(LLPipeline::sRenderingHUDs) ? &gHUDFullbrightAlphaMaskAlphaProgram :
&gDeferredFullbrightAlphaMaskAlphaProgram;
- prepare_alpha_shader(fullbright_shader, true, true, water_sign);
+ prepare_alpha_shader(fullbright_shader, true, water_sign);
simple_shader =
(LLPipeline::sImpostorRender) ? &gDeferredAlphaImpostorProgram :
(LLPipeline::sRenderingHUDs) ? &gHUDAlphaProgram :
&gDeferredAlphaProgram;
- prepare_alpha_shader(simple_shader, false, true, water_sign); //prime simple shader (loads shadow relevant uniforms)
+ prepare_alpha_shader(simple_shader, true, water_sign); //prime simple shader (loads shadow relevant uniforms)
LLGLSLShader* materialShader = gDeferredMaterialProgram;
for (int i = 0; i < LLMaterial::SHADER_COUNT*2; ++i)
{
- prepare_alpha_shader(&materialShader[i], false, true, water_sign);
+ prepare_alpha_shader(&materialShader[i], true, water_sign);
}
pbr_shader =
(LLPipeline::sRenderingHUDs) ? &gHUDPBRAlphaProgram :
&gDeferredPBRAlphaProgram;
- prepare_alpha_shader(pbr_shader, false, true, water_sign);
+ prepare_alpha_shader(pbr_shader, true, water_sign);
// explicitly unbind here so render loop doesn't make assumptions about the last shader
// already being setup for rendering
@@ -260,6 +257,14 @@ 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
+ LL::GLTFSceneManager::instance().render(false, false);
+ LL::GLTFSceneManager::instance().render(false, true);
+ LL::GLTFSceneManager::instance().render(false, false, true);
+ LL::GLTFSceneManager::instance().render(false, true, true);
+ }
+
// If the face is more than 90% transparent, then don't update the Depth buffer for Dof
// 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);
@@ -807,6 +812,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask, bool depth_only, bool rigged)
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/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 25bce7bced..9afc705d3e 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -555,7 +555,7 @@ void LLDrawPoolAvatar::beginDeferredImpostor()
sVertexProgram = &gDeferredImpostorProgram;
specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP);
- normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL);
+ normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::NORMAL_MAP);
sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
sVertexProgram->bind();
sVertexProgram->setMinimumAlpha(0.01f);
@@ -566,7 +566,7 @@ void LLDrawPoolAvatar::endDeferredImpostor()
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR
sShaderLevel = mShaderLevel;
- sVertexProgram->disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL);
+ sVertexProgram->disableTexture(LLViewerShaderMgr::NORMAL_MAP);
sVertexProgram->disableTexture(LLViewerShaderMgr::SPECULAR_MAP);
sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP);
gPipeline.unbindDeferredShader(*sVertexProgram);
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index e26d98789e..055f99d764 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -699,10 +699,11 @@ void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id, F32
void LLBumpImageList::updateImages()
{
+ llassert(LLCoros::on_main_thread_main_coro()); // This code is not thread safe
+
for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); )
{
- bump_image_map_t::iterator curiter = iter++;
- LLViewerTexture* image = curiter->second;
+ LLViewerTexture* image = iter->second;
if( image )
{
bool destroy = true;
@@ -721,9 +722,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 303afa51db..5eb10fe335 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)
@@ -53,10 +54,13 @@ void LLDrawPoolGLTFPBR::renderDeferred(S32 pass)
{
llassert(!LLPipeline::sRenderingHUDs);
+ LL::GLTFSceneManager::instance().renderOpaque();
+
gDeferredPBROpaqueProgram.bind();
pushGLTFBatches(mRenderType);
gDeferredPBROpaqueProgram.bind(true);
+ LL::GLTFSceneManager::instance().render(true, true);
pushRiggedGLTFBatches(mRenderType + 1);
}
diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp
index 2b0ae260dc..836a90adab 100644
--- a/indra/newview/lldrawpoolsimple.cpp
+++ b/indra/newview/lldrawpoolsimple.cpp
@@ -36,43 +36,12 @@
#include "llspatialpartition.h"
#include "llviewershadermgr.h"
#include "llrender.h"
+#include "gltfscenemanager.h"
static LLTrace::BlockTimerStatHandle FTM_RENDER_SIMPLE_DEFERRED("Deferred Simple");
static LLTrace::BlockTimerStatHandle FTM_RENDER_GRASS_DEFERRED("Deferred Grass");
-static void setup_simple_shader(LLGLSLShader* shader)
-{
- shader->bind();
-}
-
-static void setup_glow_shader(LLGLSLShader* shader)
-{
- setup_simple_shader(shader);
- if (LLPipeline::sRenderDeferred && !LLPipeline::sRenderingHUDs)
- {
- shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 2.2f);
- }
- else
- {
- shader->uniform1f(LLShaderMgr::TEXTURE_GAMMA, 1.f);
- }
-}
-
-static void setup_fullbright_shader(LLGLSLShader* shader)
-{
- setup_glow_shader(shader);
-
- S32 channel = shader->enableTexture(LLShaderMgr::EXPOSURE_MAP);
- if (channel > -1)
- {
- gGL.getTexUnit(channel)->bind(&gPipeline.mExposureMap);
- }
-
- shader->uniform1f(LLViewerShaderMgr::FULLBRIGHT, 1.f);
-}
-
-
void LLDrawPoolGlow::renderPostDeferred(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL;
@@ -89,12 +58,12 @@ void LLDrawPoolGlow::renderPostDeferred(S32 pass)
gGL.setColorMask(false, true);
//first pass -- static objects
- setup_glow_shader(shader);
+ shader->bind();
pushBatches(LLRenderPass::PASS_GLOW, true, true);
// second pass -- rigged objects
shader = shader->mRiggedVariant;
- setup_glow_shader(shader);
+ shader->bind();
pushRiggedBatches(LLRenderPass::PASS_GLOW_RIGGED, true, true);
gGL.setColorMask(true, false);
@@ -133,11 +102,11 @@ void LLDrawPoolSimple::renderDeferred(S32 pass)
LLGLDisable blend(GL_BLEND);
//render static
- setup_simple_shader(&gDeferredDiffuseProgram);
+ gDeferredDiffuseProgram.bind();
pushBatches(LLRenderPass::PASS_SIMPLE, true, true);
//render rigged
- setup_simple_shader(gDeferredDiffuseProgram.mRiggedVariant);
+ gDeferredDiffuseProgram.bind(true);
pushRiggedBatches(LLRenderPass::PASS_SIMPLE_RIGGED, true, true);
}
@@ -150,11 +119,11 @@ void LLDrawPoolAlphaMask::renderDeferred(S32 pass)
LLGLSLShader* shader = &gDeferredDiffuseAlphaMaskProgram;
//render static
- setup_simple_shader(shader);
+ shader->bind();
pushMaskBatches(LLRenderPass::PASS_ALPHA_MASK, true, true);
//render rigged
- setup_simple_shader(shader->mRiggedVariant);
+ shader->bind(true);
pushRiggedMaskBatches(LLRenderPass::PASS_ALPHA_MASK_RIGGED, true, true);
}
@@ -201,13 +170,13 @@ void LLDrawPoolFullbright::renderPostDeferred(S32 pass)
gGL.setSceneBlendType(LLRender::BT_ALPHA);
// render static
- setup_fullbright_shader(shader);
+ shader->bind();
pushBatches(LLRenderPass::PASS_FULLBRIGHT, true, true);
if (!LLPipeline::sRenderingHUDs)
{
// render rigged
- setup_fullbright_shader(shader->mRiggedVariant);
+ shader->bind(true);
pushRiggedBatches(LLRenderPass::PASS_FULLBRIGHT_RIGGED, true, true);
}
}
@@ -216,6 +185,10 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; //LL_RECORD_BLOCK_TIME(FTM_RENDER_FULLBRIGHT);
+ // render unrigged unlit GLTF
+ LL::GLTFSceneManager::instance().render(true, false, true);
+ LL::GLTFSceneManager::instance().render(true, true, true);
+
LLGLSLShader* shader = nullptr;
if (LLPipeline::sRenderingHUDs)
{
@@ -229,13 +202,13 @@ void LLDrawPoolFullbrightAlphaMask::renderPostDeferred(S32 pass)
LLGLDisable blend(GL_BLEND);
// render static
- setup_fullbright_shader(shader);
+ shader->bind();
pushMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK, true, true);
if (!LLPipeline::sRenderingHUDs)
{
// render rigged
- setup_fullbright_shader(shader->mRiggedVariant);
+ shader->bind(true);
pushRiggedMaskBatches(LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK_RIGGED, true, true);
}
}
diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
index c86c4b3472..2897f3d749 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,264 @@ 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->mDetailRenderMaterials;
+
+ 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.mDetailRenderMaterials;
+ }
+ 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);
+
+ // Like for PBR materials, PBR terrain texture transforms are defined by
+ // the KHR_texture_transform spec, but with the following notable
+ // differences:
+ // 1) The PBR UV origin is defined as the Southwest corner of the region,
+ // with positive U facing East and positive V facing South.
+ // 2) There is an additional scaling factor RenderTerrainPBRScale. If
+ // we've done our math right, RenderTerrainPBRScale should not affect the
+ // overall behavior of KHR_texture_transform
+ // 3) There is only one texture transform per material, whereas
+ // KHR_texture_transform supports one texture transform per texture info.
+ // i.e. this isn't fully compliant with KHR_texture_transform, but is
+ // compliant when all texture infos used by a material have the same
+ // texture transform.
+ LLGLTFMaterial::TextureTransform::PackTight transforms_packed[terrain_material_count];
+ for (U32 i = 0; i < terrain_material_count; ++i)
+ {
+ const LLFetchedGLTFMaterial* fetched_material = (*fetched_materials)[i].get();
+ LLGLTFMaterial::TextureTransform transform;
+ if (fetched_material)
+ {
+ transform = fetched_material->mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR];
+#ifdef SHOW_ASSERT
+ // Assert condition where the contents of the texture transforms
+ // differ per texture info - we currently don't support this case.
+ for (U32 ti = 1; ti < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++ti)
+ {
+ llassert(fetched_material->mTextureTransform[0] == fetched_material->mTextureTransform[ti]);
+ }
+#endif
+ }
+ // *NOTE: Notice here we are combining the scale from
+ // RenderTerrainPBRScale into the KHR_texture_transform. This only
+ // works if the scale is uniform and no other transforms are
+ // applied to the terrain UVs.
+ transform.mScale.mV[VX] *= sPBRDetailScale;
+ transform.mScale.mV[VY] *= sPBRDetailScale;
+
+ transform.getPackedTight(transforms_packed[i]);
+ }
+ const U32 transform_param_count = LLGLTFMaterial::TextureTransform::PACK_TIGHT_SIZE * terrain_material_count;
+ constexpr U32 vec4_size = 4;
+ const U32 transform_vec4_count = (transform_param_count + (vec4_size - 1)) / vec4_size;
+ llassert(transform_vec4_count == 5); // If false, need to update shader
+ shader->uniform4fv(LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS, transform_vec4_count, (F32*)transforms_packed);
+
+ LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
+
+ //
+ // 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 2c27733f37..a8097fb58b 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 5694a77c9b..6efd503574 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 0eeabdd795..047e9a8112 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 22e23727b8..739f85d4e6 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)
+ if (!bake_target.isComplete())
{
- gPipeline.mBake.bindTarget();
- gPipeline.mBake.clear();
+ 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 5bee087b38..8884905cf0 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -1675,18 +1675,20 @@ 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();
for (shaders_iter = LLViewerShaderMgr::instance()->beginShaders(); shaders_iter != end_shaders; ++shaders_iter)
{
- if ((shaders_iter->mProgramObject != 0)
- && (gPipeline.canUseWindLightShaders()
- || shaders_iter->mShaderGroup == LLGLSLShader::SG_WATER))
+ shaders_iter->mUniformsDirty = true;
+ if (shaders_iter->mRiggedVariant)
{
- shaders_iter->mUniformsDirty = true;
+ shaders_iter->mRiggedVariant->mUniformsDirty = true;
+ }
+
+ for (auto& variant : shaders_iter->mGLTFVariants)
+ {
+ variant.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 bb89863450..52062a3ad2 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -628,13 +628,6 @@ void LLFace::renderOneWireframe(const LLColor4 &color, F32 fogCfx, bool wirefram
{
LLGLDisable depth(wireframe_selection ? 0 : GL_BLEND);
- //LLGLEnable stencil(wireframe_selection ? 0 : GL_STENCIL_TEST);
-
- if (!wireframe_selection)
- { //modify wireframe into outline selection mode
- glStencilFunc(GL_NOTEQUAL, 2, 0xffff);
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- }
LLGLEnable offset(GL_POLYGON_OFFSET_LINE);
glPolygonOffset(3.f, 3.f);
@@ -1260,16 +1253,16 @@ bool LLFace::getGeometryVolume(const LLVolume& volume,
{
color = tep->getColor();
- if (tep->getGLTFRenderMaterial())
- {
- color = tep->getGLTFRenderMaterial()->mBaseColor;
- }
+ if (tep->getGLTFRenderMaterial())
+ {
+ color = tep->getGLTFRenderMaterial()->mBaseColor;
+ }
}
if (rebuild_color)
{ //decide if shiny goes in alpha channel of color
if (tep &&
- !isInAlphaPool()) // <--- alpha channel MUST contain transparency, not shiny
+ !isInAlphaPool() && tep->getGLTFRenderMaterial() == nullptr) // <--- alpha channel MUST contain transparency, not shiny
{
LLMaterial* mat = tep->getMaterialParams().get();
@@ -1859,7 +1852,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();
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index c595b2253c..aa04221f4b 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 a70f0f9179..ee4f1b0fe5 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -34,18 +34,19 @@
#include "llshadermgr.h"
#include "pipeline.h"
+//static
+LLFetchedGLTFMaterial LLFetchedGLTFMaterial::sDefault;
+
LLFetchedGLTFMaterial::LLFetchedGLTFMaterial()
: LLGLTFMaterial()
, mExpectedFlusTime(0.f)
- , mActive(true)
- , mFetching(false)
{
}
LLFetchedGLTFMaterial::~LLFetchedGLTFMaterial()
{
-
+
}
LLFetchedGLTFMaterial& LLFetchedGLTFMaterial::operator=(const LLFetchedGLTFMaterial& rhs)
@@ -76,9 +77,7 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
{
if (mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
{
- // dividing the alpha cutoff by transparency here allows the shader to compare against
- // the alpha value of the texture without needing the transparency value
- min_alpha = mAlphaCutoff/mBaseColor.mV[3];
+ min_alpha = mAlphaCutoff;
}
shader->uniform1f(LLShaderMgr::MINIMUM_ALPHA, min_alpha);
}
@@ -143,7 +142,6 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
mTextureTransform[GLTF_TEXTURE_INFO_EMISSIVE].getPacked(emissive_packed);
shader->uniform4fv(LLShaderMgr::TEXTURE_EMISSIVE_TRANSFORM, 2, (F32*)emissive_packed);
}
-
}
LLViewerFetchedTexture* fetch_texture(const LLUUID& id)
@@ -242,10 +240,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)
{
@@ -255,54 +254,3 @@ void LLFetchedGLTFMaterial::materialComplete()
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 3352438660..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 b6ce31423f..0afb275d13 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";
@@ -510,11 +515,11 @@ bool LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename,
case FFSAVE_GLTF:
if (filename.empty())
{
- wcsncpy( mFilesW,L"untitled.glb", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
+ wcsncpy( mFilesW,L"untitled.gltf", FILENAME_BUFFER_SIZE); /*Flawfinder: ignore*/
}
- mOFN.lpstrDefExt = L"glb";
+ mOFN.lpstrDefExt = L"gltf";
mOFN.lpstrFilter =
- L"glTF Asset File (*.gltf *.glb)\0*.gltf;*.glb\0" \
+ L"glTF Asset File (*.gltf)\0*.gltf\0" \
L"\0";
break;
case FFSAVE_XML:
@@ -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;
@@ -783,7 +790,7 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension,
case LLFilePicker::FFSAVE_GLTF:
type = "\?\?\?\?";
creator = "\?\?\?\?";
- extension = "glb";
+ extension = "gltf";
break;
case LLFilePicker::FFSAVE_XML:
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 266c5217c8..75ff14f4cf 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 08389b8912..ea49c88755 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()
//-----------------------------------------------------------------------------
@@ -380,7 +390,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;
}
catch (...)
diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h
index 6facea0ad8..ed395722de 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;
- 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);
+ S32 getExpectedUploadCost() const override;
+
+ 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 5996547b94..aa5571ccc0 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() override;
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index a9ec392129..073cef3d73 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -62,11 +62,13 @@
#include "llfloaterreg.h"
#include "llfloaterregiondebugconsole.h"
#include "llfloatertelehub.h"
+#include "llgltfmateriallist.h"
#include "llinventorymodel.h"
#include "lllineeditor.h"
#include "llnamelistctrl.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
+#include "llpbrterrainfeatures.h"
#include "llregioninfomodel.h"
#include "llscrolllistitem.h"
#include "llsliderctrl.h"
@@ -86,7 +88,6 @@
#include "llviewerstats.h"
#include "llviewertexteditor.h"
#include "llviewerwindow.h"
-#include "llvlcomposition.h"
#include "lltrans.h"
#include "llagentui.h"
#include "llmeshrepository.h"
@@ -100,7 +101,6 @@
#include "llavatarnamecache.h"
#include "llenvironment.h"
-const S32 TERRAIN_TEXTURE_COUNT = 4;
const S32 CORNER_COUNT = 4;
const U32 MAX_LISTED_NAMES = 100;
@@ -264,7 +264,16 @@ bool LLFloaterRegionInfo::postBuild()
panel = new LLPanelRegionTerrainInfo;
mInfoPanels.push_back(panel);
- panel->buildFromFile("panel_region_terrain.xml");
+ static LLCachedControl<bool> feature_pbr_terrain_enabled(gSavedSettings, "RenderTerrainPBREnabled", false);
+ static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false);
+ if (!feature_pbr_terrain_transforms_enabled || !feature_pbr_terrain_enabled)
+ {
+ panel->buildFromFile("panel_region_terrain.xml");
+ }
+ else
+ {
+ panel->buildFromFile("panel_region_terrain_texture_transform.xml");
+ }
mTab->addTabPanel(panel);
mEnvironmentPanel = new LLPanelRegionEnvironment;
@@ -340,7 +349,6 @@ void LLFloaterRegionInfo::onRegionChanged()
}
}
-// static
void LLFloaterRegionInfo::requestRegionInfo()
{
LLTabContainer* tab = findChild<LLTabContainer>("region_panels");
@@ -556,6 +564,20 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
}
// static
+void LLFloaterRegionInfo::sRefreshFromRegion(LLViewerRegion* region)
+{
+ if (region != gAgent.getRegion()) { return; }
+
+ LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info");
+ if (!floater) { return; }
+
+ if (floater->getVisible() && region == gAgent.getRegion())
+ {
+ floater->refreshFromRegion(region);
+ }
+}
+
+// static
LLPanelEstateInfo* LLFloaterRegionInfo::getPanelEstate()
{
LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info");
@@ -605,6 +627,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()
{
@@ -817,6 +849,13 @@ void LLPanelRegionInfo::initCtrl(const std::string& name)
getChild<LLUICtrl>(name)->setCommitCallback(boost::bind(&LLPanelRegionInfo::onChangeAnything, this));
}
+template<typename CTRL>
+void LLPanelRegionInfo::initAndSetCtrl(CTRL*& ctrl, const std::string& name)
+{
+ initCtrl(name);
+ ctrl = findChild<CTRL>(name);
+}
+
void LLPanelRegionInfo::onClickManageTelehub()
{
LLFloaterReg::hideInstance("region_info");
@@ -1307,16 +1346,23 @@ void LLPanelRegionDebugInfo::onClickDebugConsole(void* data)
bool LLPanelRegionTerrainInfo::validateTextureSizes()
{
- static const S32 MAX_TERRAIN_TEXTURE_SIZE = 1024;
- for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ if (mMaterialTypeCtrl)
{
- std::string buffer;
- buffer = llformat("texture_detail_%d", i);
- LLTextureCtrl* texture_ctrl = getChild<LLTextureCtrl>(buffer);
+ 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)
+ {
+ 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 +1370,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 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 true;
+ return valid;
}
bool LLPanelRegionTerrainInfo::validateTextureHeights()
@@ -1370,6 +1518,27 @@ 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)
+ {
+ mTextureDetailCtrl[i] = nullptr;
+ mMaterialDetailCtrl[i] = nullptr;
+
+ mLastSetTextures[i] = default_textures[i];
+ mLastSetMaterials[i] = BLANK_MATERIAL_ASSET_ID;
+
+ mMaterialScaleUCtrl[i] = nullptr;
+ mMaterialScaleVCtrl[i] = nullptr;
+ mMaterialRotationCtrl[i] = nullptr;
+ mMaterialOffsetUCtrl[i] = nullptr;
+ mMaterialOffsetVCtrl[i] = nullptr;
+ }
+}
+
// Initialize statics
bool LLPanelRegionTerrainInfo::postBuild()
@@ -1380,11 +1549,25 @@ 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);
+ initAndSetCtrl(mTextureDetailCtrl[i], llformat("texture_detail_%d", i));
+ if (mTextureDetailCtrl[i])
+ {
+ mTextureDetailCtrl[i]->setBakeTextureEnabled(false);
+ }
+ initAndSetCtrl(mMaterialDetailCtrl[i], llformat("material_detail_%d", i));
+
+ initAndSetCtrl(mMaterialScaleUCtrl[i], llformat("terrain%dScaleU", i));
+ initAndSetCtrl(mMaterialScaleVCtrl[i], llformat("terrain%dScaleV", i));
+ initAndSetCtrl(mMaterialRotationCtrl[i], llformat("terrain%dRotation", i));
+ initAndSetCtrl(mMaterialOffsetUCtrl[i], llformat("terrain%dOffsetU", i));
+ initAndSetCtrl(mMaterialOffsetVCtrl[i], llformat("terrain%dOffsetV", i));
}
for(S32 i = 0; i < CORNER_COUNT; ++i)
@@ -1405,6 +1588,68 @@ 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 terrain tabs
+ LLTabContainer* terrain_tabs = findChild<LLTabContainer>("terrain_tabs");
+ if (terrain_tabs)
+ {
+ LLPanel* pbr_terrain_repeats_tab = findChild<LLPanel>("terrain_transform_panel");
+ if (pbr_terrain_repeats_tab)
+ {
+ terrain_tabs->setTabVisibility(pbr_terrain_repeats_tab, show_material_controls);
+ }
+ }
+
+ // Toggle visibility of labels
+ LLUICtrl* texture_label = findChild<LLUICtrl>("detail_texture_text");
+ if (texture_label) { texture_label->setVisible(show_texture_controls); }
+ 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 +1666,114 @@ 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)
+
+ 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)
{
- buffer = llformat("texture_detail_%d", i);
- texture_ctrl = getChild<LLTextureCtrl>(buffer);
- if(texture_ctrl)
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
{
- LL_DEBUGS() << "Detail Texture " << i << ": "
- << compp->getDetailTextureID(i) << LL_ENDL;
- LLUUID tmp_id(compp->getDetailTextureID(i));
- texture_ctrl->setImageAssetID(tmp_id);
+ LL_DEBUGS("Terrain", "Material") << "Reset Material swatch " << i
+ << LL_ENDL;
+ LLTextureCtrl* asset_ctrl = mMaterialDetailCtrl[i];
+ if(asset_ctrl)
+ {
+ asset_ctrl->setImageAssetID(mLastSetMaterials[i]);
+ }
}
}
+ for(S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ if (!mMaterialScaleUCtrl[i] || !mMaterialScaleVCtrl[i] || !mMaterialRotationCtrl[i] || !mMaterialOffsetUCtrl[i] || !mMaterialOffsetVCtrl[i]) { continue; }
+ const LLGLTFMaterial* mat_override = compp->getMaterialOverride(i);
+ if (!mat_override) { mat_override = &LLGLTFMaterial::sDefault; }
+
+ // Assume all texture transforms have the same value
+ const LLGLTFMaterial::TextureTransform& transform = mat_override->mTextureTransform[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR];
+ mMaterialScaleUCtrl[i]->setValue(transform.mScale.mV[VX]);
+ mMaterialScaleVCtrl[i]->setValue(transform.mScale.mV[VY]);
+ mMaterialRotationCtrl[i]->setValue(transform.mRotation * RAD_TO_DEG);
+ mMaterialOffsetUCtrl[i]->setValue(transform.mOffset.mV[VX]);
+ mMaterialOffsetVCtrl[i]->setValue(transform.mOffset.mV[VY]);
+ }
+
+ std::string buffer;
for(S32 i = 0; i < CORNER_COUNT; ++i)
{
buffer = llformat("height_start_spin_%d", i);
@@ -1450,6 +1788,9 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
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);
getChildView("bake_terrain_btn")->setEnabled(owner_or_god);
@@ -1461,22 +1802,14 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
// virtual
bool LLPanelRegionTerrainInfo::sendUpdate()
{
- LL_INFOS() << "LLPanelRegionTerrainInfo::sendUpdate" << LL_ENDL;
- std::string buffer;
- strings_t strings;
- LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ LL_INFOS() << __FUNCTION__ << LL_ENDL;
- // 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
+ LLUICtrl* apply_btn = getChild<LLUICtrl>("apply_btn");
+ if (apply_btn && !apply_btn->getEnabled())
+ {
+ LL_WARNS() << "Duplicate update, ignored" << LL_ENDL;
+ return false;
+ }
// Make sure user hasn't chosen wacky textures.
if (!validateTextureSizes())
@@ -1484,6 +1817,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,20 +1838,55 @@ 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)
+ // 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)
{
- buffer = llformat("texture_detail_%d", i);
- texture_ctrl = getChild<LLTextureCtrl>(buffer);
- if(texture_ctrl)
+ 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)
{
- LLUUID tmp_id(texture_ctrl->getImageAssetID());
- tmp_id.toString(id_str);
- buffer = llformat("%d %s", i, id_str.c_str());
- strings.push_back(buffer);
+ mLastSetMaterials[i] = tmp_id;
+ }
+ else
+ {
+ mLastSetTextures[i] = tmp_id;
}
}
sendEstateOwnerMessage(msg, "texturedetail", invoice, strings);
@@ -1536,6 +1910,51 @@ bool LLPanelRegionTerrainInfo::sendUpdate()
sendEstateOwnerMessage(msg, "texturecommit", invoice, strings);
+ // ========================================
+ // POST to ModifyRegion endpoint, if enabled
+
+ static LLCachedControl<bool> feature_pbr_terrain_transforms_enabled(gSavedSettings, "RenderTerrainPBRTransformsEnabled", false);
+ if (material_type == LLTerrainMaterials::Type::PBR && feature_pbr_terrain_transforms_enabled)
+ {
+ LLTerrainMaterials composition;
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ LLPointer<LLGLTFMaterial> mat_override = new LLGLTFMaterial();
+
+ const bool transform_controls_valid = mMaterialScaleUCtrl[i] && mMaterialScaleVCtrl[i] && mMaterialRotationCtrl[i] && mMaterialOffsetUCtrl[i] && mMaterialOffsetVCtrl[i];
+ if (transform_controls_valid)
+ {
+ // Set texture transforms for all texture infos to the same value,
+ // because the PBR terrain shader doesn't currently support
+ // different transforms per texture info. See also
+ // LLDrawPoolTerrain::renderFullShaderPBR .
+ for (U32 tt = 0; tt < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++tt)
+ {
+ LLGLTFMaterial::TextureTransform& transform = mat_override->mTextureTransform[tt];
+ transform.mScale.mV[VX] = mMaterialScaleUCtrl[i]->getValue().asReal();
+ transform.mScale.mV[VY] = mMaterialScaleVCtrl[i]->getValue().asReal();
+ transform.mRotation = mMaterialRotationCtrl[i]->getValue().asReal() * DEG_TO_RAD;
+ transform.mOffset.mV[VX] = mMaterialOffsetUCtrl[i]->getValue().asReal();
+ transform.mOffset.mV[VY] = mMaterialOffsetVCtrl[i]->getValue().asReal();
+ }
+ }
+
+ if (*mat_override == LLGLTFMaterial::sDefault) { mat_override = nullptr; }
+ composition.setMaterialOverride(i, mat_override.get());
+ }
+
+ // queueModify leads to a few messages being sent back and forth:
+ // viewer: POST ModifyRegion
+ // simulator: RegionHandshake
+ // viewer: GET ModifyRegion
+ LLViewerRegion* region = gAgent.getRegion();
+ llassert(region);
+ if (region)
+ {
+ LLPBRTerrainFeatures::queueModify(*region, composition);
+ }
+ }
+
return true;
}
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index 5915c6c8b8..1634683d90 100644
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file llfloaterregioninfo.h
* @author Aaron Brashears
* @brief Declaration of the region info and controls floater and panels.
@@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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$
*/
@@ -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,14 +77,15 @@ 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**);
// get and process region info if necessary.
static void processRegionInfo(LLMessageSystem* msg);
+ static void sRefreshFromRegion(LLViewerRegion* region);
static const LLUUID& getLastInvoice() { return sRequestInvoice; }
static void nextInvoice() { sRequestInvoice.generate(); }
@@ -98,15 +101,15 @@ public:
static LLPanelRegionEnvironment* getPanelEnvironment();
// from LLPanel
- virtual void refresh();
-
+ void refresh() override;
+
void onRegionChanged();
void requestRegionInfo();
void enableTopButtons();
void disableTopButtons();
private:
-
+
LLFloaterRegionInfo(const LLSD& seed);
~LLFloaterRegionInfo();
@@ -135,30 +138,31 @@ class LLPanelRegionInfo : public LLPanel
{
public:
LLPanelRegionInfo();
-
+
void onBtnSet();
void onChangeChildCtrl(LLUICtrl* ctrl);
void onChangeAnything();
static void onChangeText(LLLineEditor* caller, void* user_data);
-
+
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);
void disableButton(const std::string& btn_name);
-
+
void onClickManageTelehub();
-
+
protected:
void initCtrl(const std::string& name);
-
+ template<typename CTRL> void initAndSetCtrl(CTRL*& ctrl, const std::string& name);
+
// Returns true if update sent and apply button should be
// disabled.
virtual bool sendUpdate() { return true; }
-
+
typedef std::vector<std::string> strings_t;
//typedef std::vector<U32> integers_t;
void sendEstateOwnerMessage(
@@ -166,8 +170,8 @@ protected:
const std::string& request,
const LLUUID& invoice,
const strings_t& strings);
-
-
+
+
// member data
LLHost mHost;
};
@@ -178,22 +182,21 @@ protected:
class LLPanelRegionGeneralInfo : public LLPanelRegionInfo
{
-
+
public:
LLPanelRegionGeneralInfo()
: LLPanelRegionInfo() {}
~LLPanelRegionGeneralInfo() {}
-
- virtual bool refreshFromRegion(LLViewerRegion* region);
-
- // LLPanel
- virtual bool postBuild();
-
+
+ bool refreshFromRegion(LLViewerRegion* region) override;
+
+ 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 +217,13 @@ public:
LLPanelRegionDebugInfo()
: LLPanelRegionInfo(), mTargetAvatar() {}
~LLPanelRegionDebugInfo() {}
- // LLPanel
- virtual bool postBuild();
-
- virtual bool refreshFromRegion(LLViewerRegion* region);
+ bool postBuild() override;
+
+ 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);
@@ -232,7 +235,7 @@ protected:
bool callbackRestart(const LLSD& notification, const LLSD& response);
static void onClickCancelRestart(void* data);
static void onClickDebugConsole(void* data);
-
+
private:
LLUUID mTargetAvatar;
};
@@ -244,20 +247,22 @@ class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
LOG_CLASS(LLPanelRegionTerrainInfo);
public:
- LLPanelRegionTerrainInfo() : LLPanelRegionInfo() {}
+ LLPanelRegionTerrainInfo();
~LLPanelRegionTerrainInfo() {}
-
- virtual bool postBuild(); // LLPanel
-
- virtual bool refreshFromRegion(LLViewerRegion* region); // refresh local settings from region update from simulator
+
+ bool postBuild() override;
+
+ 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 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 +270,24 @@ 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];
+
+ LLSpinCtrl* mMaterialScaleUCtrl[LLTerrainMaterials::ASSET_COUNT];
+ LLSpinCtrl* mMaterialScaleVCtrl[LLTerrainMaterials::ASSET_COUNT];
+ LLSpinCtrl* mMaterialRotationCtrl[LLTerrainMaterials::ASSET_COUNT];
+ LLSpinCtrl* mMaterialOffsetUCtrl[LLTerrainMaterials::ASSET_COUNT];
+ LLSpinCtrl* mMaterialOffsetVCtrl[LLTerrainMaterials::ASSET_COUNT];
};
/////////////////////////////////////////////////////////////////////////////
@@ -276,13 +296,13 @@ class LLPanelEstateInfo : public LLPanelRegionInfo
{
public:
static void initDispatch(LLDispatcher& dispatch);
-
+
void onChangeFixedSun();
void onChangeUseGlobalTime();
void onChangeAccessOverride();
-
+
void onClickEditSky();
- void onClickEditSkyHelp();
+ void onClickEditSkyHelp();
void onClickEditDayCycle();
void onClickEditDayCycleHelp();
@@ -294,38 +314,37 @@ public:
void onKickUserCommit(const uuid_vec_t& ids);
static void onClickMessageEstate(void* data);
bool onMessageCommit(const LLSD& notification, const LLSD& response);
-
+
LLPanelEstateInfo();
~LLPanelEstateInfo() {}
-
+
void updateControls(LLViewerRegion* region);
-
+
static void updateEstateName(const std::string& name);
static void updateEstateOwnerName(const std::string& name);
- virtual bool refreshFromRegion(LLViewerRegion* region);
- virtual bool estateUpdate(LLMessageSystem* msg);
-
- // LLPanel
- virtual bool postBuild();
- virtual void updateChild(LLUICtrl* child_ctrl);
- virtual void refresh();
+ bool refreshFromRegion(LLViewerRegion* region) override;
+ bool estateUpdate(LLMessageSystem* msg) override;
+
+ bool postBuild() override;
+ void updateChild(LLUICtrl* child_ctrl) override;
+ void refresh() override;
void refreshFromEstate();
-
+
static bool isLindenEstate();
-
+
const std::string getOwnerName() const;
void setOwnerName(const std::string& name);
protected:
- virtual bool sendUpdate();
+ bool sendUpdate() override;
// confirmation dialog callback
bool callbackChangeLindenEstate(const LLSD& notification, const LLSD& response);
void commitEstateAccess();
void commitEstateManagers();
-
+
bool checkSunHourSlider(LLUICtrl* child_ctrl);
U32 mEstateID;
@@ -338,18 +357,17 @@ class LLPanelEstateCovenant : public LLPanelRegionInfo
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 +400,7 @@ public:
} EAssetStatus;
protected:
- virtual bool sendUpdate();
+ bool sendUpdate() override;
LLTextBox* mEstateNameText;
LLTextBox* mEstateOwnerText;
LLTextBox* mLastModifiedText;
@@ -401,16 +419,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 +456,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 +465,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();
@@ -461,7 +482,7 @@ private:
void onAllowedSearchEdit(const std::string& search_string);
void onAllowedGroupsSearchEdit(const std::string& search_string);
void onBannedSearchEdit(const std::string& search_string);
-
+
// Group picker callback is different, can't use core methods below
bool addAllowedGroup(const LLSD& notification, const LLSD& response);
void addAllowedGroup2(LLUUID id);
diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp
index f6ebc8eebc..08f8918e5d 100644
--- a/indra/newview/llglsandbox.cpp
+++ b/indra/newview/llglsandbox.cpp
@@ -1009,7 +1009,7 @@ F32 gpu_benchmark()
gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER));
gBenchmarkProgram.mShaderFiles.push_back(std::make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER));
gBenchmarkProgram.mShaderLevel = 1;
- if (!gBenchmarkProgram.createShader(NULL, NULL))
+ if (!gBenchmarkProgram.createShader())
{
return -1.f;
}
diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp
index a500e45a7a..215f3dd3a7 100644
--- a/indra/newview/llgltfmateriallist.cpp
+++ b/indra/newview/llgltfmateriallist.cpp
@@ -55,8 +55,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)
@@ -244,6 +242,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;
}
}
@@ -468,7 +467,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
@@ -553,7 +552,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 c1e0dd0a64..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() {}
@@ -81,14 +79,14 @@ public:
static void flushUpdates(void(*done_callback)(bool) = nullptr);
static void addSelectionUpdateCallback(void(*update_callback)(const LLUUID& object_id, S32 side));
-
+
// Queue an explicit LLSD ModifyMaterialParams update apply given override data
// overrides -- LLSD map (or array of maps) in the format:
// object_id UUID(required) id of object
// side integer(required) TE index of face to set, or -1 for all faces
// gltf_json string(optional) override data to set, empty string nulls out override data, omissions of this parameter keeps existing data
// asset_id UUID(optional) id of material asset to set, omission of this parameter keeps existing material asset id
- //
+ //
// NOTE: Unless you already have a gltf_json string you want to send, strongly prefer using queueModify
// If the queue/flush API is insufficient, extend it.
static void queueUpdate(const LLSD& data);
diff --git a/indra/newview/llgltfmaterialpreviewmgr.cpp b/indra/newview/llgltfmaterialpreviewmgr.cpp
new file mode 100644
index 0000000000..a198d1bdf4
--- /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..f320886157
--- /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..83c7b8a354
--- /dev/null
+++ b/indra/newview/llheroprobemanager.cpp
@@ -0,0 +1,628 @@
+/**
+ * @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 || !LLPipeline::sReflectionProbesEnabled || 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;
+ 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() && vo->isReflectionProbe() && vo->getReflectionProbeIsBox())
+ {
+ 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]));
+
+ cube_facing = 1 - cube_facing;
+
+ mFaceUpdateList[i] = ceilf(cube_facing * gPipeline.RenderHeroProbeConservativeUpdateMultiplier);
+ }
+
+
+ mProbes[0]->mOrigin = probe_pos;
+ }
+ else
+ {
+ mNearestHero = nullptr;
+ }
+
+ mHeroProbeStrength = 1;
+ }
+ }
+
+void LLHeroProbeManager::renderProbes()
+{
+ if (!LLPipeline::RenderMirrors || !LLPipeline::sReflectionProbesEnabled || gTeleportDisplay ||
+ LLStartUp::getStartupState() < STATE_PRECACHE)
+ {
+ return;
+ }
+
+ static LLCachedControl<S32> sDetail(gSavedSettings, "RenderHeroReflectionProbeDetail", -1);
+ static LLCachedControl<S32> sLevel(gSavedSettings, "RenderHeroReflectionProbeLevel", 3);
+
+ F32 near_clip = 0.01f;
+ if (mNearestHero != nullptr && (gPipeline.RenderHeroProbeUpdateRate == 0 || (gFrameCount % gPipeline.RenderHeroProbeUpdateRate) == 0) &&
+ !gTeleportDisplay && !gDisconnected && !LLAppViewer::instance()->logoutRequestSent())
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("hpmu - realtime");
+
+ 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..e45b0049b2
--- /dev/null
+++ b/indra/newview/llheroprobemanager.h
@@ -0,0 +1,159 @@
+/**
+ * @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();
+
+ void renderProbes();
+
+ // 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/llimprocessing.cpp b/indra/newview/llimprocessing.cpp
index d4c92ea5ae..e2e83ef42b 100644
--- a/indra/newview/llimprocessing.cpp
+++ b/indra/newview/llimprocessing.cpp
@@ -1637,23 +1637,29 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url)
{
session_id = message_data["asset_id"].asUUID();
}
- LLIMProcessing::processNewMessage(
- message_data["from_agent_id"].asUUID(),
- from_group,
- message_data["to_agent_id"].asUUID(),
- message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE,
- dialog,
- session_id,
- static_cast<U32>(message_data["timestamp"].asInteger()),
- message_data["from_agent_name"].asString(),
- message_data["message"].asString(),
- static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland
- message_data["region_id"].asUUID(),
- position,
- bin_bucket.data(),
- static_cast<S32>(bin_bucket.size()),
- sender,
- message_data["asset_id"].asUUID());
+
+ LLAppViewer::instance()->postToMainCoro([=]()
+ {
+ std::vector<U8> local_bin_bucket = bin_bucket;
+ LLHost local_sender = sender;
+ LLIMProcessing::processNewMessage(
+ message_data["from_agent_id"].asUUID(),
+ from_group,
+ message_data["to_agent_id"].asUUID(),
+ message_data.has("offline") ? static_cast<U8>(message_data["offline"].asInteger()) : IM_OFFLINE,
+ dialog,
+ session_id,
+ static_cast<U32>(message_data["timestamp"].asInteger()),
+ message_data["from_agent_name"].asString(),
+ message_data["message"].asString(),
+ static_cast<U32>((message_data.has("parent_estate_id")) ? message_data["parent_estate_id"].asInteger() : 1), // 1 - IMMainland
+ message_data["region_id"].asUUID(),
+ position,
+ local_bin_bucket.data(),
+ S32(local_bin_bucket.size()),
+ local_sender,
+ message_data["asset_id"].asUUID());
+ });
}
}
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 7f550bb511..8471b2cea1 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -843,12 +843,6 @@ bool get_is_category_and_children_removable(LLInventoryModel* model, const LLUUI
return false;
}
- const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
- if (mp_id.notNull() && gInventory.isObjectDescendentOf(folder_id, mp_id))
- {
- return false;
- }
-
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
model->collectDescendents(
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index c7e0e9d702..d57cb13362 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -889,11 +889,11 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot(
else if (root_id.notNull())
{
cat_array_t* cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
- if (cats)
+ if(cats)
{
for (auto& p_cat : *cats)
{
- if (p_cat && p_cat->getPreferredType() == preferred_type)
+ if(p_cat && p_cat->getPreferredType() == preferred_type)
{
const LLUUID& folder_id = p_cat->getUUID();
if (rv.isNull() || folder_id < rv)
@@ -1389,7 +1389,9 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask)
return mask;
}
- if (item->getType() == LLAssetType::AT_MESH)
+ if (item->getType() == LLAssetType::AT_MESH ||
+ item->getType() == LLAssetType::AT_GLTF ||
+ item->getType() == LLAssetType::AT_GLTF_BIN)
{
return mask;
}
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
index 9cbbda0b79..6ab5e05b7d 100644
--- a/indra/newview/lllocalbitmaps.cpp
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -1091,8 +1091,9 @@ bool LLLocalBitmapMgr::checkTextureDimensions(std::string filename)
return false;
}
- S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
- S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
+ // allow loading up to 4x max rez but implicitly downrez to max rez before upload
+ S32 max_width = gSavedSettings.getS32("max_texture_dimension_X")*4;
+ S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y")*4;
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
{
@@ -1137,6 +1138,20 @@ void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
}
}
+LLUUID LLLocalBitmapMgr::getTrackingID(const LLUUID& world_id) const
+{
+ for (local_list_citer iter = mBitmapList.begin(); iter != mBitmapList.end(); iter++)
+ {
+ LLLocalBitmap* unit = *iter;
+ if (unit->getWorldID() == world_id)
+ {
+ return unit->getTrackingID();
+ }
+ }
+
+ return LLUUID::null;
+}
+
LLUUID LLLocalBitmapMgr::getWorldID(const LLUUID &tracking_id) const
{
LLUUID world_id = LLUUID::null;
diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h
index e0cd9d172f..e169f96e70 100644
--- a/indra/newview/lllocalbitmaps.h
+++ b/indra/newview/lllocalbitmaps.h
@@ -135,6 +135,7 @@ public:
void delUnit(LLUUID tracking_id);
bool checkTextureDimensions(std::string filename);
+ LLUUID getTrackingID(const LLUUID& world_id) const;
LLUUID getWorldID(const LLUUID &tracking_id) const;
bool isLocal(const LLUUID& world_id) const;
std::string getFilename(const LLUUID &tracking_id) const;
diff --git a/indra/newview/lllocalgltfmaterials.cpp b/indra/newview/lllocalgltfmaterials.cpp
index 84b58b9e71..fab18f2d26 100644
--- a/indra/newview/lllocalgltfmaterials.cpp
+++ b/indra/newview/lllocalgltfmaterials.cpp
@@ -177,6 +177,8 @@ bool LLLocalGLTFMaterial::updateSelf()
}
}
+ materialBegin();
+ materialComplete(true);
updated = true;
}
@@ -201,6 +203,8 @@ bool LLLocalGLTFMaterial::updateSelf()
LLNotificationsUtil::add("LocalBitmapsUpdateFailedFinal", notif_args);
mLinkStatus = LS_BROKEN;
+ materialBegin();
+ materialComplete(false);
}
}
}
@@ -218,6 +222,8 @@ bool LLLocalGLTFMaterial::updateSelf()
LLNotificationsUtil::add("LocalBitmapsUpdateFileNotFound", notif_args);
mLinkStatus = LS_BROKEN;
+ materialBegin();
+ materialComplete(false);
}
}
diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp
index 8983b50980..2adb506c0f 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 c2f4200852..5bdf3f81b5 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,152 +570,175 @@ 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)
+ if (selectNode->mSelectedGLTFNode != -1)
{
- object->saveUnselectedChildrenRotation(child_rotations) ;
- object->saveUnselectedChildrenPosition(child_positions) ;
- }
+ 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);
+ gltf_mode = true;
}
- else
+ else if (!gltf_mode)
{
- object->setRotation(new_rot, damped);
- LLVOAvatar* avatar = object->asAvatar();
- if (avatar && avatar->isSelf()
- && LLSelectMgr::getInstance()->mAllowSelectAvatar
- && !object->getParent())
+ 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++)
+ if (!gltf_mode)
{
- 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()))
+ for (LLObjectSelection::iterator iter = mObjectSelection->begin();
+ iter != mObjectSelection->end(); iter++)
{
- LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+ LLSelectNode* selectNode = *iter;
+ LLViewerObject* object = selectNode->getObject();
+ LLViewerObject* root_object = (object == NULL) ? NULL : object->getRootEdit();
- 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
+ // 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()))
{
- new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
- old_position = object->getPositionAgent();
- }
+ LLVector3 center = gAgent.getPosAgentFromGlobal(mRotationCenter);
- new_position = (new_position - center) * mRotation; // new relative rotated position
- new_position += center;
+ LLVector3 old_position;
+ LLVector3 new_position;
- 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);
- }
+ if (selectNode->mSelectedGLTFNode != -1)
+ {
- // for individually selected child objects
- if (!object->isRootEdit() && selectNode->mIndividualSelection)
- {
- LLViewerObject* parentp = (LLViewerObject*)object->getParent();
- if (!parentp->isSelected())
+ }
+ else
{
if (object->isAttachment() && object->mDrawable.notNull())
{
- // find position relative to render position of parent
- object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
- rebuild(object);
+ // 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
{
- object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
+ 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);
}
}
}
- 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) ;
- }
}
}
@@ -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() &&
((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 f678a3750c..c11a98be50 100644
--- a/indra/newview/llmaniptranslate.cpp
+++ b/indra/newview/llmaniptranslate.cpp
@@ -656,114 +656,125 @@ bool LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
LLVector3 clamped_relative_move_f = (F32)axis_magnitude * axis_f; // scalar multiply
for (LLObjectSelection::iterator iter = mObjectSelection->begin();
- iter != mObjectSelection->end(); iter++)
+ 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)
+ if (selectNode->mSelectedGLTFNode != -1)
{
- continue;
+ // manipulating a GLTF node
+ clamped_relative_move_f -= selectNode->mLastMoveLocal;
+ object->moveGLTFNode(selectNode->mSelectedGLTFNode, clamped_relative_move_f);
+ selectNode->mLastMoveLocal += clamped_relative_move_f;
}
-
- if (!object->isRootEdit())
+ else
{
- // child objects should not update if parent is selected
- LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
- if (editable_root->isSelected())
+ // Only apply motion to root objects and objects selected
+ // as "individual".
+ if (!object->isRootEdit() && !selectNode->mIndividualSelection)
{
- // 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())
+ if (!object->isRootEdit())
{
- // 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)
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
{
- // counter-translate child objects if we are moving the root as an individual
- object->resetChildrenPosition(old_position_local - new_position_local, true) ;
+ // we will be moved properly by our parent, so skip
+ continue;
}
}
- 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)
+ 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())
{
- new_position_global.mdV[VZ] = LLWorld::getInstance()->resolveLandHeightGlobal(new_position_global) + 1.f;
- }
+ // calculate local version of relative move
+ LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ objWorldRotation.transQuat();
- if (object->isRootEdit())
- {
- new_position_global = LLWorld::getInstance()->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
- }
+ LLVector3 old_position_local = object->getPosition();
+ LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
- // 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);
+ //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
{
- 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);
- }
+ // 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;
- 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) ;
+ // 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();
}
- selectNode->mLastPositionLocal = object->getPosition();
}
}
+
LLSelectMgr::getInstance()->updateSelectionCenter();
gAgentCamera.clearFocusObject();
dialog_refresh_all(); // ??? is this necessary?
@@ -1727,11 +1738,6 @@ void LLManipTranslate::highlightIntersection(LLVector3 normal,
shader->bind();
}
- if (shader)
- {
- shader->bind();
- }
-
//draw volume/plane intersections
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 6bf495d10f..92f8ed949e 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -489,11 +489,7 @@ 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));
+ refreshUploadCost();
}
boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void* userData)
@@ -812,6 +808,37 @@ void LLMaterialEditor::resetUnsavedChanges()
}
}
+void LLMaterialEditor::refreshUploadCost()
+{
+ mExpectedUploadCost = 0;
+ if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId() && mBaseColorFetched)
+ {
+ S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mBaseColorFetched);
+ mExpectedUploadCost += upload_cost;
+ getChild<LLUICtrl>("base_color_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
+ }
+ if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId() && mMetallicRoughnessFetched)
+ {
+ S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mMetallicRoughnessFetched);
+ mExpectedUploadCost += upload_cost;
+ getChild<LLUICtrl>("metallic_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
+ }
+ if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId() && mEmissiveFetched)
+ {
+ S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mEmissiveFetched);
+ mExpectedUploadCost += upload_cost;
+ getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
+ }
+ if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId() && mNormalFetched)
+ {
+ S32 upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(mNormalFetched);
+ mExpectedUploadCost += upload_cost;
+ getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
+ }
+
+ getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost));
+}
+
void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag)
{
mUnsavedChanges |= dirty_flag;
@@ -842,26 +869,15 @@ void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag)
setCanSave(false);
}
- S32 upload_texture_count = 0;
- if (mBaseColorTextureUploadId.notNull() && mBaseColorTextureUploadId == getBaseColorId())
- {
- upload_texture_count++;
- }
- if (mMetallicTextureUploadId.notNull() && mMetallicTextureUploadId == getMetallicRoughnessId())
+ if ((dirty_flag & MATERIAL_BASE_COLOR_TEX_DIRTY)
+ || (dirty_flag & MATERIAL_NORMAL_TEX_DIRTY)
+ || (dirty_flag & MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)
+ || (dirty_flag & MATERIAL_EMISIVE_TEX_DIRTY)
+ || (dirty_flag == 0)
+ || (dirty_flag == U32_MAX))
{
- upload_texture_count++;
+ refreshUploadCost();
}
- if (mEmissiveTextureUploadId.notNull() && mEmissiveTextureUploadId == getEmissiveId())
- {
- upload_texture_count++;
- }
- if (mNormalTextureUploadId.notNull() && mNormalTextureUploadId == getNormalId())
- {
- upload_texture_count++;
- }
-
- mExpectedUploadCost = upload_texture_count * LLAgentBenefitsMgr::current().getTextureUploadCost();
- getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost));
}
void LLMaterialEditor::setCanSaveAs(bool value)
@@ -1870,7 +1886,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 +2140,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);
@@ -3497,8 +3513,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/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index be4aa219cd..232467460e 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -288,6 +288,7 @@ private:
// utility function for building a description of the imported material
// based on what we know about it.
const std::string buildMaterialDescription();
+ void refreshUploadCost();
void resetUnsavedChanges();
void markChangesUnsaved(U32 dirty_flag);
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 50a1bbc8f3..226c32c396 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));
@@ -1805,6 +1805,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);
}
@@ -2040,7 +2041,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 7f1c801d31..7e1553c80a 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 a34502cabb..ba52da0760 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -277,14 +277,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);
}
@@ -2230,11 +2228,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 655713be14..16c38bf1f0 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);
@@ -387,22 +387,31 @@ void LLPanelVolume::getState( )
// Reflection Probe
bool is_probe = volobjp && volobjp->isReflectionProbe();
+ bool is_mirror = volobjp && volobjp->getReflectionProbeIsMirror();
getChild<LLUICtrl>("Reflection Probe")->setValue(is_probe);
getChildView("Reflection Probe")->setEnabled(editable && single_volume && volobjp && !volobjp->isMesh());
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 Volume Type")->setEnabled(probe_enabled);
- getChildView("Probe Ambiance")->setEnabled(probe_enabled);
- getChildView("Probe Near Clip")->setEnabled(probe_enabled);
+ getChildView("Probe Update Type")->setEnabled(probe_enabled);
+ getChildView("Probe Volume Type")->setEnabled(probe_enabled && !is_mirror);
+ getChildView("Probe Ambiance")->setEnabled(probe_enabled && !is_mirror);
+ getChildView("Probe Near Clip")->setEnabled(probe_enabled && !is_mirror);
+ 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 +425,27 @@ 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";
+ }
+
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 +732,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 +1200,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 +1258,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,11 +1427,22 @@ 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;
+
+ self->getChildView("Probe Volume Type")->setEnabled(!is_mirror);
+
+ 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();
- bool is_box = shape_type == "Box";
+ bool is_box = shape_type == "Box" || is_mirror;
if (volobjp->setReflectionProbeIsBox(is_box))
{
diff --git a/indra/newview/llpbrterrainfeatures.cpp b/indra/newview/llpbrterrainfeatures.cpp
new file mode 100644
index 0000000000..bb771c6963
--- /dev/null
+++ b/indra/newview/llpbrterrainfeatures.cpp
@@ -0,0 +1,198 @@
+/**
+ * @file llpbrterrainfeatures.cpp
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpbrterrainfeatures.h"
+
+#include "llappviewer.h"
+#include "llgltfmaterial.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+
+LLPBRTerrainFeatures gPBRTerrainFeatures;
+
+// static
+void LLPBRTerrainFeatures::queueQuery(LLViewerRegion& region, void(*done_callback)(LLUUID, bool, const LLModifyRegion&))
+{
+ llassert(on_main_thread());
+ llassert(LLCoros::on_main_coro());
+
+ LLUUID region_id = region.getRegionID();
+
+ LLCoros::instance().launch("queryRegionCoro",
+ std::bind(&LLPBRTerrainFeatures::queryRegionCoro,
+ region.getCapability("ModifyRegion"),
+ region_id,
+ done_callback));
+}
+
+// static
+void LLPBRTerrainFeatures::queueModify(LLViewerRegion& region, const LLModifyRegion& composition)
+{
+ llassert(on_main_thread());
+ llassert(LLCoros::on_main_coro());
+
+ LLSD updates = LLSD::emptyMap();
+
+ LLSD override_updates = LLSD::emptyArray();
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ const LLGLTFMaterial* material_override = composition.getMaterialOverride(i);
+ LLSD override_update;
+ if (material_override)
+ {
+ LLGLTFMaterial::sDefault.getOverrideLLSD(*material_override, override_update);
+ }
+ else
+ {
+ override_update = LLSD::emptyMap();
+ }
+ override_updates.append(override_update);
+ }
+ updates["overrides"] = override_updates;
+
+ LLCoros::instance().launch("modifyRegionCoro",
+ std::bind(&LLPBRTerrainFeatures::modifyRegionCoro,
+ region.getCapability("ModifyRegion"),
+ updates,
+ nullptr));
+}
+
+// static
+void LLPBRTerrainFeatures::queryRegionCoro(std::string cap_url, LLUUID region_id, void(*done_callback)(LLUUID, bool, const LLModifyRegion&) )
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("queryRegionCoro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+ LLCore::HttpHeaders::ptr_t httpHeaders;
+
+ httpOpts->setFollowRedirects(true);
+
+ LL_DEBUGS("GLTF") << "Querying features via ModifyRegion endpoint" << LL_ENDL;
+
+ LLSD result = httpAdapter->getAndSuspend(httpRequest, cap_url, httpOpts, httpHeaders);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ bool success = true;
+ if (!status || !result["success"].asBoolean())
+ {
+ if (result["message"].isUndefined())
+ {
+ LL_WARNS("PBRTerrain") << "Failed to query PBR terrain features." << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("PBRTerrain") << "Failed to query PBR terrain features: " << result["message"] << LL_ENDL;
+ }
+ success = false;
+ }
+
+ LLTerrainMaterials* composition = new LLTerrainMaterials();
+
+ if (success)
+ {
+ const LLSD& overrides = result["overrides"];
+ if (!overrides.isArray() || overrides.size() < LLTerrainMaterials::ASSET_COUNT)
+ {
+ LL_WARNS("PBRTerrain") << "Invalid composition format: Missing/invalid overrides" << LL_ENDL;
+ success = false;
+ }
+ else
+ {
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ const LLSD& override_llsd = overrides[i];
+ LLPointer<LLGLTFMaterial> material_override = new LLGLTFMaterial();
+ material_override->applyOverrideLLSD(override_llsd);
+ if (*material_override == LLGLTFMaterial::sDefault)
+ {
+ material_override = nullptr;
+ }
+ composition->setMaterialOverride(i, material_override.get());
+ }
+ }
+ }
+
+ if (done_callback)
+ {
+ LLAppViewer::instance()->postToMainCoro([=]()
+ {
+ done_callback(region_id, success, *composition);
+ delete composition;
+ });
+ }
+ else
+ {
+ delete composition;
+ }
+}
+
+// static
+void LLPBRTerrainFeatures::modifyRegionCoro(std::string cap_url, LLSD updates, void(*done_callback)(bool) )
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("modifyRegionCoro", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+ LLCore::HttpHeaders::ptr_t httpHeaders;
+
+ httpOpts->setFollowRedirects(true);
+
+ LL_DEBUGS("GLTF") << "Applying features via ModifyRegion endpoint: " << updates << LL_ENDL;
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, updates, httpOpts, httpHeaders);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ bool success = true;
+ if (!status || !result["success"].asBoolean())
+ {
+ if (result["message"].isUndefined())
+ {
+ LL_WARNS("PBRTerrain") << "Failed to modify PBR terrain features." << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("PBRTerrain") << "Failed to modify PBR terrain features: " << result["message"] << LL_ENDL;
+ }
+ success = false;
+ }
+
+ if (done_callback)
+ {
+ LLAppViewer::instance()->postToMainCoro([=]()
+ {
+ done_callback(success);
+ });
+ }
+}
+
diff --git a/indra/newview/llpbrterrainfeatures.h b/indra/newview/llpbrterrainfeatures.h
new file mode 100644
index 0000000000..f29d4ebf50
--- /dev/null
+++ b/indra/newview/llpbrterrainfeatures.h
@@ -0,0 +1,48 @@
+/**
+ * @file llpbrterrainfeatures.h
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#pragma once
+
+#include <string>
+
+class LLViewerRegion;
+class LLMessageSystem;
+class LLModifyRegion;
+
+// Queries/modifies PBR terrain repeats, possibly other features in the future
+class LLPBRTerrainFeatures
+{
+public:
+ static void queueQuery(LLViewerRegion& region, void(*done_callback)(LLUUID, bool, const LLModifyRegion&));
+ static void queueModify(LLViewerRegion& region, const LLModifyRegion& composition);
+
+private:
+ static void queryRegionCoro(std::string cap_url, LLUUID region_id, void(*done_callback)(LLUUID, bool, const LLModifyRegion&) );
+ static void modifyRegionCoro(std::string cap_url, LLSD updates, void(*done_callback)(bool) );
+};
+
+extern LLPBRTerrainFeatures gPBRTerrainFeatures;
+
diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp
index f4b9d8b2c5..2604c2ba53 100644
--- a/indra/newview/llreflectionmap.cpp
+++ b/indra/newview/llreflectionmap.cpp
@@ -49,21 +49,24 @@ 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;
llassert(mCubeArray.notNull());
llassert(mCubeIndex != -1);
//llassert(LLPipeline::sRenderDeferred);
-
+
// make sure we don't walk off the edge of the render target
while (resolution > gPipeline.mRT->deferredScreen.getWidth() ||
resolution > gPipeline.mRT->deferredScreen.getHeight())
{
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()
@@ -142,7 +145,7 @@ void LLReflectionMap::autoAdjustOrigin()
LLVector3 origin(fp);
F32 height = LLWorld::instance().resolveLandHeightAgent(origin) + 2.f;
fp[2] = llmax(fp[2], height);
-
+
// make sure radius encompasses all objects
LLSimdScalar r2 = 0.0;
for (int i = 0; i < 8; ++i)
@@ -162,10 +165,10 @@ void LLReflectionMap::autoAdjustOrigin()
// make sure near clip doesn't poke through ground
fp[2] = llmax(fp[2], height+mRadius*0.5f);
-
+
}
}
- else if (mViewerObject)
+ else if (mViewerObject && !mViewerObject->isDead())
{
mPriority = 1;
mOrigin.load3(mViewerObject->getPositionAgent().mV);
@@ -234,7 +237,7 @@ F32 LLReflectionMap::getNearClip()
bool LLReflectionMap::getIsDynamic()
{
if (gSavedSettings.getS32("RenderReflectionProbeDetail") > (S32) LLReflectionMapManager::DetailLevel::STATIC_ONLY &&
- mViewerObject &&
+ mViewerObject &&
mViewerObject->getVolume())
{
return ((LLVOVolume*)mViewerObject)->getReflectionProbeIsDynamic();
@@ -244,7 +247,7 @@ bool LLReflectionMap::getIsDynamic()
}
bool LLReflectionMap::getBox(LLMatrix4& box)
-{
+{
if (mViewerObject)
{
LLVolume* volume = mViewerObject->getVolume();
@@ -267,7 +270,7 @@ bool LLReflectionMap::getBox(LLMatrix4& box)
// construct object to camera space (with scale)
mv = mv * rm * scale;
- // inverse is camera space to object unit cube
+ // inverse is camera space to object unit cube
mv = mv.inverse();
box = LLMatrix4(mv.m);
@@ -334,7 +337,7 @@ void LLReflectionMap::doOcclusion(const LLVector4a& eye)
mOccluded = false;
return;
}
-
+
if (mOcclusionQuery == 0)
{ // no query was previously issued, allocate one and issue
LL_PROFILE_ZONE_NAMED_CATEGORY_PIPELINE("rmdo - glGenQueries");
diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h
index 924cf6233f..9e888f20d0 100644
--- a/indra/newview/llreflectionmap.h
+++ b/indra/newview/llreflectionmap.h
@@ -36,14 +36,23 @@ class alignas(16) LLReflectionMap : public LLRefCount
{
LL_ALIGN_NEW
public:
- // allocate an environment map of the given resolution
+
+ enum class ProbeType
+ {
+ ALL = 0,
+ RADIANCE,
+ IRRADIANCE,
+ REFLECTION
+ };
+
+ // allocate an environment map of the given resolution
LLReflectionMap();
~LLReflectionMap();
// 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();
@@ -77,7 +86,7 @@ public:
// point at which environment map was last generated from (in agent space)
LLVector4a mOrigin;
-
+
// distance from main viewer camera
F32 mDistance = -1.f;
@@ -97,7 +106,7 @@ public:
// cube map used to sample this environment map
LLPointer<LLCubeMapArray> mCubeArray;
S32 mCubeIndex = -1; // index into cube map array or -1 if not currently stored in cube map array
-
+
// probe has had at least one full update and is ready to render
bool mComplete = false;
@@ -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 1eb0428215..cb1ab0dac2 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())
@@ -739,6 +827,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();
@@ -832,9 +921,10 @@ void LLReflectionMapManager::reset()
mReset = true;
}
-void LLReflectionMapManager::pause()
+void LLReflectionMapManager::pause(F32 duration)
{
mPaused = true;
+ mResumeTime = gFrameTimeSeconds + duration;
}
void LLReflectionMapManager::resume()
@@ -907,6 +997,8 @@ void LLReflectionMapManager::updateUniforms()
// the box probe
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];
@@ -917,6 +1009,8 @@ void LLReflectionMapManager::updateUniforms()
// 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
@@ -930,6 +1024,10 @@ void LLReflectionMapManager::updateUniforms()
GLint refBucket[256][4]; //lookup table for which index to start with for the given Z depth
// numbrer of active refmaps
GLint refmapCount;
+
+ GLint heroShape;
+ GLint heroMipCount;
+ GLint heroProbeCount;
};
mReflectionMaps.resize(mReflectionProbeCount);
@@ -959,9 +1057,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;
@@ -1017,7 +1114,6 @@ void LLReflectionMapManager::updateUniforms()
{
refmap->mRadius = refmap->mViewerObject->getScale().mV[0] * 0.5f;
}
-
}
modelview.affineTransform(refmap->mOrigin, oa);
rpd.refSphere[count].set(oa.getF32ptr());
@@ -1120,6 +1216,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)
{
@@ -1160,7 +1266,7 @@ void LLReflectionMapManager::setUniforms()
{
updateUniforms();
}
- glBindBufferBase(GL_UNIFORM_BUFFER, 1, mUBO);
+ glBindBufferBase(GL_UNIFORM_BUFFER, LLGLSLShader::UB_REFLECTION_PROBES, mUBO);
}
@@ -1267,6 +1373,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));
@@ -1324,7 +1432,6 @@ void LLReflectionMapManager::initReflectionMaps()
mDefaultProbe->mComplete = default_complete;
touch_default_probe(mDefaultProbe);
-
}
if (mVertexBuffer.isNull())
@@ -1388,3 +1495,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 bd4204468a..5c0651bc24 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -43,6 +43,8 @@ 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
@@ -65,7 +67,7 @@ public:
// add a probe for the given spatial group
LLReflectionMap* addProbe(LLSpatialGroup* group = nullptr);
-
+
// Populate "maps" with the N most relevant Reflection Maps where N is no more than maps.size()
// If less than maps.size() ReflectionMaps are available, will assign trailing elements to nullptr.
// maps -- presized array of Reflection Map pointers
@@ -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();
@@ -151,7 +160,7 @@ private:
// update the specified face of the specified probe
void updateProbeFace(LLReflectionMap* probe, U32 face);
-
+
// list of active reflection maps
std::vector<LLPointer<LLReflectionMap> > mProbes;
@@ -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 66f2e65274..b3420f9c57 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;
}
}
@@ -5213,46 +5220,57 @@ void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type)
{
return true; // skip
}
- selectNode->mSavedPositionLocal = object->getPosition();
- if (object->isAttachment())
+
+ if (selectNode->mSelectedGLTFNode != -1)
{
- if (object->isRootEdit())
+ // 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())
{
- LLXform* parent_xform = object->mDrawable->getXform()->getParent();
- if (parent_xform)
+ if (object->isRootEdit())
{
- selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
+ 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
{
- selectNode->mSavedPositionGlobal = object->getPositionGlobal();
+ 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
{
- 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->mSavedPositionGlobal = object->getPositionGlobal();
+ selectNode->mSavedRotation = object->getRotationRegion();
}
- selectNode->mSavedRotation = object->getRenderRotation();
- }
- else
- {
- selectNode->mSavedPositionGlobal = object->getPositionGlobal();
- selectNode->mSavedRotation = object->getRotationRegion();
- }
- selectNode->mSavedScale = object->getScale();
- selectNode->saveTextureScaleRatios(mManager->mTextureChannel);
+ selectNode->mSavedScale = object->getScale();
+ selectNode->saveTextureScaleRatios(mManager->mTextureChannel);
+ }
return true;
}
} func(action_type, this);
@@ -6684,7 +6702,6 @@ LLSelectNode::~LLSelectNode()
}
}
-
delete mPermissions;
mPermissions = NULL;
}
@@ -6713,6 +6730,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 bbc6d566cb..0456b109d7 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 c10a782605..42927769de 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/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp
index 4ce45608df..c619b63ef5 100644
--- a/indra/newview/llsidepaneltaskinfo.cpp
+++ b/indra/newview/llsidepaneltaskinfo.cpp
@@ -97,7 +97,6 @@ static std::string click_action_to_string_value(U8 click_action)
default:
return "Touch";
}
- return "Touch";
}
// Default constructor
diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp
index 52bd6a0315..9b4ed4c946 100644
--- a/indra/newview/llskinningutil.cpp
+++ b/indra/newview/llskinningutil.cpp
@@ -97,6 +97,12 @@ U32 LLSkinningUtil::getMeshJointCount(const LLMeshSkinInfo *skin)
return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size());
}
+S32 LLSkinningUtil::getMaxGLTFJointCount()
+{
+ // this is the maximum number of 3x4 matrices than can fit in a UBO
+ return gGLManager.mMaxUniformBlockSize / 48;
+}
+
void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin)
{
if (skin->mInvalidJointsScrubbed)
diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h
index bd2f8ea04e..aa0c0075af 100644
--- a/indra/newview/llskinningutil.h
+++ b/indra/newview/llskinningutil.h
@@ -40,6 +40,7 @@ class LLJointRiggingInfoTab;
namespace LLSkinningUtil
{
S32 getMaxJointCount();
+ S32 getMaxGLTFJointCount();
U32 getMeshJointCount(const LLMeshSkinInfo *skin);
void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin);
void initSkinningMatrixPalette(LLMatrix4a* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index c57949d834..782d57aed8 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"
@@ -1998,7 +1999,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())
@@ -2006,11 +2011,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.
//
@@ -2019,66 +2026,134 @@ 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)
+ {
+ LLVolume* volume = vol->getVolume();
+ faces = &volume->getVolumeFaces();
+ }
+ else
{
- const LLVolumeFace &face = volume->getVolumeFace(i);
+ drawable_faces = &drawablep->getFaces();
+ }
- gGL.flush();
- gGL.diffuseColor4f(1, 1, 0, 1);
- gGL.begin(LLRender::LINES);
- for (S32 j = 0; j < face.mNumVertices; ++j)
+ if (faces)
+ {
+ for (auto it = faces->begin(); it != faces->end(); ++it)
{
- LLVector4a n, p;
+ 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.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)
- {
gGL.flush();
- gGL.diffuseColor4f(0, 1, 1, 1);
+ gGL.diffuseColor4f(1, 1, 0, 1);
gGL.begin(LLRender::LINES);
for (S32 j = 0; j < face.mNumVertices; ++j)
{
- LLVector4a t, p;
+ LLVector4a n, p;
- t.setMul(face.mTangents[j], 1.0f);
- t.normalize3fast();
- t.mul(draw_length);
- p.setAdd(face.mPositions[j], t);
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_DEFAULT];
+ }
+ else
+ {
+ shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_WITH_TANGENTS];
+ }
+ shader->bind();
+
+ shader->uniform1f(LLShaderMgr::DEBUG_NORMAL_DRAW_LENGTH, draw_length);
+
+ 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();
}
}
@@ -2799,10 +2874,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)
{
}
@@ -2870,6 +2943,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())
@@ -2931,25 +3011,18 @@ void renderRaycast(LLDrawable* drawablep)
{
//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/llstartup.cpp b/indra/newview/llstartup.cpp
index 77e615b01c..dd005874a5 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -330,10 +330,18 @@ void update_texture_fetch()
void set_flags_and_update_appearance()
{
- LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
- LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op);
+ // this may be called from a coroutine but has many side effects
+ // in non-thread-safe classes, post to main loop
+ auto work = []()
+ {
+ LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
+ LLAppearanceMgr::instance().updateAppearanceFromCOF(true, true, no_op);
+
+ LLInventoryModelBackgroundFetch::instance().start();
+ };
+
+ LLAppViewer::instance()->postToMainCoro(work);
- LLInventoryModelBackgroundFetch::instance().start();
}
// Returns false to skip other idle processing. Should only return
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index cafeb8db1e..e6bced5c92 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -643,6 +643,7 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
}
}
+template<bool PBR>
bool LLSurface::idleUpdate(F32 max_update_time)
{
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
@@ -669,7 +670,7 @@ 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)
{
@@ -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 d759389585..324296a4d3 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -112,6 +112,7 @@ public:
LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
// Update methods (called during idle, normally)
+ 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 d2379da7b3..042d770550 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 82a09c152b..195250d2c0 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 cb216056de..284fff4d8c 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
@@ -635,7 +638,7 @@ bool LLFloaterTexturePicker::postBuild()
getChild<LLComboBox>("l_bake_use_texture_combo_box")->setCommitCallback(onBakeTextureSelect, this);
- setBakeTextureEnabled(true);
+ setBakeTextureEnabled(mInventoryPickType != PICK_MATERIAL);
return true;
}
@@ -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))
{
@@ -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,7 +1762,20 @@ void LLTextureCtrl::setFilterPermissionMasks(PermissionMask mask)
setDnDFilterPermMask(mask);
}
-void LLTextureCtrl::setVisible( bool visible )
+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 )
{
@@ -1850,7 +1882,7 @@ void LLTextureCtrl::showPicker(bool take_focus)
{
texture_floaterp->setSetImageAssetIDCallback(boost::bind(&LLTextureCtrl::setImageAssetID, this, _1));
- texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled);
+ texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled && mInventoryPickType != PICK_MATERIAL);
}
LLFloater* root_floater = gFloaterView->getParentFloater(this);
@@ -2080,7 +2112,7 @@ void LLTextureCtrl::setBakeTextureEnabled(bool enabled)
LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
if (floaterp)
{
- floaterp->setBakeTextureEnabled(enabled);
+ floaterp->setBakeTextureEnabled(enabled && mInventoryPickType != PICK_MATERIAL);
}
}
@@ -2157,48 +2189,70 @@ 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);
+ preview = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ preview->setBoostLevel(LLGLTexture::BOOST_PREVIEW);
}
}
-
- mTexturep = texture;
}
else//mImageAssetID == LLUUID::null
{
mTexturep = NULL;
+ mGLTFMaterial = NULL;
+ mGLTFPreview = NULL;
}
// Border
@@ -2211,15 +2265,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 +2392,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 +2420,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 daa879ca62..3c6cff4eaa 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;
- virtual void draw();
- virtual void setVisible( bool visible );
- virtual void setEnabled( bool enabled );
+ void draw() override;
+ void setVisible( bool visible ) override;
+ void setEnabled( bool enabled ) override;
- void setValid(bool valid);
+ void onVisibilityChange(bool new_visibility) override;
+
+ void setValid(bool valid);
// LLUICtrl interface
- virtual void clear();
+ void clear() override;
// Takes a UUID, wraps get/setImageAssetID
- virtual void setValue(const LLSD& value);
- virtual LLSD getValue() const;
+ 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 3145d90eeb..7b4f47e567 100644
--- a/indra/newview/lltinygltfhelper.cpp
+++ b/indra/newview/lltinygltfhelper.cpp
@@ -87,22 +87,32 @@ 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)
{
LLImageDataLock lockIn(occlusion_img);
LLImageDataLock lockOut(mr_img);
- //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)
{
@@ -150,7 +160,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;
@@ -162,14 +172,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;
@@ -180,7 +193,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();
}
@@ -190,7 +206,7 @@ LLImageRaw * LLTinyGLTFHelper::getTexture(const std::string & folder, const tiny
bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& model_in)
{
std::string exten = gDirUtilp->getExtension(filename);
-
+
if (exten == "gltf" || exten == "glb")
{
tinygltf::TinyGLTF loader;
@@ -227,7 +243,7 @@ bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& m
LL_WARNS("GLTF") << "Cannot load. File has no materials " << filename << LL_ENDL;
return false;
}
-
+
return true;
}
@@ -235,12 +251,50 @@ bool LLTinyGLTFHelper::loadModel(const std::string& filename, tinygltf::Model& m
return false;
}
+bool LLTinyGLTFHelper::saveModel(const std::string& filename, tinygltf::Model& model_in)
+{
+ std::string exten = gDirUtilp->getExtension(filename);
+
+ bool success = false;
+
+ if (exten == "gltf" || exten == "glb")
+ {
+ tinygltf::TinyGLTF writer;
+
+ std::string filename_lc = filename;
+ LLStringUtil::toLower(filename_lc);
+
+
+ bool embed_images = false;
+ bool embed_buffers = false;
+ bool pretty_print = true;
+ bool write_binary = false;
+
+
+ if (std::string::npos == filename_lc.rfind(".gltf"))
+ { // file is binary
+ embed_images = embed_buffers = write_binary = true;
+ }
+
+ success = writer.WriteGltfSceneToFile(&model_in, filename, embed_images, embed_buffers, pretty_print, write_binary);
+
+ if (!success)
+ {
+ LL_WARNS("GLTF") << "Failed to save" << LL_ENDL;
+ return false;
+ }
+ }
+
+ return success;
+}
+
bool LLTinyGLTFHelper::getMaterialFromModel(
const std::string& filename,
const tinygltf::Model& model_in,
S32 mat_index,
LLFetchedGLTFMaterial* material,
- std::string& material_name)
+ std::string& material_name,
+ bool flip)
{
llassert(material);
@@ -259,18 +313,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..a259609404 100644
--- a/indra/newview/lltinygltfhelper.h
+++ b/indra/newview/lltinygltfhelper.h
@@ -38,19 +38,19 @@ 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);
+ bool saveModel(const std::string& filename, tinygltf::Model& model_in);
bool getMaterialFromModel(
const std::string& filename,
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 3b4fb799c2..329cfa7715 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1082,51 +1082,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,
@@ -1267,7 +1287,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);
@@ -1303,7 +1323,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);
@@ -2645,7 +2665,7 @@ EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
// *HACK: In order to resolve SL-22177, we need to block drags
// from notecards and objects onto other objects.
- if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ if ((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
{
return ACCEPT_NO;
}
diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp
index 9050788ed9..cc04f9e3fc 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/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp
index 3e3347ff33..f4c618c08d 100644
--- a/indra/newview/llviewerassettype.cpp
+++ b/indra/newview/llviewerassettype.cpp
@@ -89,6 +89,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary()
addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE));
addEntry(LLViewerAssetType::AT_SETTINGS, new ViewerAssetEntry(DAD_SETTINGS));
addEntry(LLViewerAssetType::AT_MATERIAL, new ViewerAssetEntry(DAD_MATERIAL));
+ addEntry(LLViewerAssetType::AT_GLTF, new ViewerAssetEntry(DAD_GLTF));
+ addEntry(LLViewerAssetType::AT_GLTF_BIN, new ViewerAssetEntry(DAD_GLTF_BIN));
};
EDragAndDropType LLViewerAssetType::lookupDragAndDropType(EType asset_type)
diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp
index 14d24da081..337c18f218 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()
@@ -299,10 +299,18 @@ void LLResourceUploadInfo::assignDefaults()
mDescription = "(No Description)";
}
+ if (mAssetType == LLAssetType::AT_GLTF ||
+ mAssetType == LLAssetType::AT_GLTF_BIN)
+ {
+ mFolderId = LLUUID::null;
+ }
+ else
+ {
mFolderId = gInventory.findUserDefinedCategoryUUIDForType(
(mDestinationFolderType == LLFolderType::FT_NONE) ?
(LLFolderType::EType)mAssetType : mDestinationFolderType);
}
+}
std::string LLResourceUploadInfo::getDisplayName() const
{
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index d472ff7dc2..a070711727 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 bb6ba4622a..efed045b91 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)
{
@@ -414,6 +428,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;
}
@@ -659,6 +686,38 @@ 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);
+
+ // *NOTE: The GLTF spec allows for different texture infos to have their texture transforms set independently, but as a simplification, this debug setting only updates all the transforms in-sync (i.e. only one texture transform per terrain material).
+ LLGLTFMaterial::TextureTransform transform;
+ const std::string prefix = std::string("LocalTerrainTransform") + std::to_string(i + 1);
+ transform.mScale.mV[VX] = gSavedSettings.getF32(prefix + "ScaleU");
+ transform.mScale.mV[VY] = gSavedSettings.getF32(prefix + "ScaleV");
+ transform.mRotation = gSavedSettings.getF32(prefix + "Rotation") * DEG_TO_RAD;
+ transform.mOffset.mV[VX] = gSavedSettings.getF32(prefix + "OffsetU");
+ transform.mOffset.mV[VY] = gSavedSettings.getF32(prefix + "OffsetV");
+ LLPointer<LLGLTFMaterial> mat_override = new LLGLTFMaterial();
+ for (U32 info = 0; info < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++info)
+ {
+ mat_override->mTextureTransform[info] = transform;
+ }
+ if (*mat_override == LLGLTFMaterial::sDefault)
+ {
+ gLocalTerrainMaterials.setMaterialOverride(i, nullptr);
+ }
+ else
+ {
+ gLocalTerrainMaterials.setMaterialOverride(i, mat_override);
+ }
+ }
+}
////////////////////////////////////////////////////////////////////////////
LLPointer<LLControlVariable> setting_get_control(LLControlGroup& group, const std::string& setting)
@@ -693,7 +752,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);
@@ -734,6 +797,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);
@@ -837,6 +901,25 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorFarAwayDistance", handleUserImpostorDistanceChanged);
setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged);
setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged);
+ {
+ const char* transform_suffixes[] = {
+ "ScaleU",
+ "ScaleV",
+ "Rotation",
+ "OffsetU",
+ "OffsetV"
+ };
+ for (U32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ const auto asset_setting_name = std::string("LocalTerrainAsset") + std::to_string(i + 1);
+ setting_setup_signal_listener(gSavedSettings, asset_setting_name, handleLocalTerrainChanged);
+ for (const char* ts : transform_suffixes)
+ {
+ const auto transform_setting_name = std::string("LocalTerrainTransform") + std::to_string(i + 1) + ts;
+ setting_setup_signal_listener(gSavedSettings, transform_setting_name, handleLocalTerrainChanged);
+ }
+ }
+ }
setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged);
}
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 79c1f64122..09f77c0c29 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -656,6 +656,15 @@ 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)
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Update hero probes");
+ gPipeline.mHeroProbeManager.update();
+ gPipeline.mHeroProbeManager.renderProbes();
+ }
+
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("display - 1");
LLAppViewer::instance()->pingMainloopTimeout("Display:Update");
if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD))
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index efe57661a9..23931ecf03 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1667,7 +1667,7 @@ void LLViewerMediaImpl::destroyMediaSource()
cancelMimeTypeProbe();
{
- LLMutexLock lock(&mLock); // Delay tear-down while bg thread is updating
+ LLCoros::LockType lock(mLock); // Delay tear-down while bg thread is updating
if(mMediaSource)
{
mMediaSource->setDeleteOK(true) ;
@@ -2674,7 +2674,13 @@ void LLViewerMediaImpl::mimeDiscoveryCoro(std::string url)
{
if (initializeMedia(mimeType))
{
- loadURI();
+ ref();
+ LLAppViewer::instance()->postToMainCoro([this]()
+ {
+ loadURI();
+ unref();
+ });
+
}
}
@@ -2968,7 +2974,7 @@ bool LLViewerMediaImpl::preMediaTexUpdate(LLViewerMediaTexture*& media_tex, U8*&
void LLViewerMediaImpl::doMediaTexUpdate(LLViewerMediaTexture* media_tex, U8* data, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, bool sync)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
- LLMutexLock lock(&mLock); // don't allow media source tear-down during update
+ LLCoros::LockType lock(mLock); // don't allow media source tear-down during update
// wrap "data" in an LLImageRaw but do NOT make a copy
LLPointer<LLImageRaw> raw = new LLImageRaw(data, media_tex->getWidth(), media_tex->getHeight(), media_tex->getComponents(), true);
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index c8780f1c10..5753615a43 100644
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -182,7 +182,7 @@ private:
// Implementation functions not exported into header file
class LLViewerMediaImpl
- : public LLMouseHandler, public LLRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler
+ : public LLMouseHandler, public LLThreadSafeRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler
{
LOG_CLASS(LLViewerMediaImpl);
public:
@@ -432,7 +432,7 @@ private:
private:
// a single media url with some data and an impl.
std::shared_ptr<LLPluginClassMedia> mMediaSource;
- LLMutex mLock;
+ LLCoros::Mutex mLock;
F64 mZoomFactor;
LLUUID mTextureId;
bool mMovieImageHasMips;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 273aa3441a..7e58a604f6 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -139,8 +139,11 @@
#include "boost/unordered_map.hpp"
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/json.hpp>
#include "llcleanup.h"
#include "llviewershadermgr.h"
+#include "gltfscenemanager.h"
+#include "gltf/asset.h"
using namespace LLAvatarAppearanceDefines;
@@ -529,10 +532,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);
@@ -743,10 +744,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;
@@ -999,6 +1020,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;
@@ -2175,6 +2200,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 //
///////////////////
@@ -3280,6 +3319,40 @@ bool enable_os_exception()
#endif
}
+
+bool enable_gltf()
+{
+ static LLCachedControl<bool> enablegltf(gSavedSettings, "GLTFEnabled", false);
+ return enablegltf;
+}
+
+bool enable_gltf_save_as()
+{
+ if (enable_gltf())
+ {
+ LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject();
+ if (obj)
+ {
+ if (obj->mGLTFAsset && obj->mGLTFAsset->isLocalPreview())
+ {
+ return true;
+ }
+
+ LLPermissions* permissions = LLSelectMgr::getInstance()->findObjectPermissions(obj);
+ if (permissions)
+ {
+ return permissions->allowExportBy(gAgent.getID());
+ }
+ }
+ }
+ return false;
+}
+
+bool enable_gltf_upload()
+{
+ return enable_gltf_save_as();
+}
+
class LLSelfRemoveAllAttachments : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -8025,6 +8098,66 @@ 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 LLAdvancedClickGLTFOpen: public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LL::GLTFSceneManager::instance().load();
+ return true;
+ }
+};
+
+class LLAdvancedClickGLTFSaveAs : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LL::GLTFSceneManager::instance().saveAs();
+ return true;
+ }
+};
+
+class LLAdvancedClickGLTFUpload: public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LL::GLTFSceneManager::instance().uploadSelection();
+ return true;
+ }
+};
+
+class LLAdvancedClickResizeWindow : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ S32 w = 0;
+ S32 h = 0;
+
+ sscanf(userdata.asString().c_str(), "%dx%d", &w, &h);
+
+ if (w > 0 && h > 0)
+ {
+ gViewerWindow->getWindow()->setSize(LLCoordWindow(w, h));
+ }
+
+ return true;
+ }
+};
+
+
// these are used in the gl menus to set control values that require shader recompilation
class LLToggleShaderControl : public view_listener_t
{
@@ -9376,6 +9509,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")
@@ -9674,7 +9809,13 @@ 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 LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen");
+ view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs");
+ view_listener_t::addMenu(new LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload");
+ view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
+ view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode");
@@ -9977,6 +10118,9 @@ void initialize_menus()
commit.add("Pathfinding.Characters.Select", boost::bind(&LLFloaterPathfindingCharacters::openCharactersWithSelectedObjects));
enable.add("EnableSelectInPathfindingCharacters", boost::bind(&enable_object_select_in_pathfinding_characters));
enable.add("Advanced.EnableErrorOSException", boost::bind(&enable_os_exception));
+ enable.add("EnableGLTF", boost::bind(&enable_gltf));
+ enable.add("EnableGLTFSaveAs", boost::bind(&enable_gltf_save_as));
+ enable.add("EnableGLTFUpload", boost::bind(&enable_gltf_upload));
view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible");
view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel");
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 4e1372b386..e3da52fc78 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))
+ S32 expected_upload_cost = 0;
+
+ if (LLResourceUploadInfo::findAssetTypeAndCodecOfExtension(ext, asset_type, codec))
{
- 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);
+ 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/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 76fc629b33..5a32f9654d 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -507,192 +507,6 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data)
}
}
-// S32 exported_object_count = 0;
-// S32 exported_image_count = 0;
-// S32 current_object_count = 0;
-// S32 current_image_count = 0;
-
-// extern LLNotifyBox *gExporterNotify;
-// extern LLUUID gExporterRequestID;
-// extern std::string gExportDirectory;
-
-// extern LLUploadDialog *gExportDialog;
-
-// std::string gExportedFile;
-
-// std::map<LLUUID, std::string> gImageChecksums;
-
-// void export_complete()
-// {
-// LLUploadDialog::modalUploadFinished();
-// gExporterRequestID.setNull();
-// gExportDirectory = "";
-
-// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */
-// fseek(fXML, 0, SEEK_END);
-// long length = ftell(fXML);
-// fseek(fXML, 0, SEEK_SET);
-// U8 *buffer = new U8[length + 1];
-// size_t nread = fread(buffer, 1, length, fXML);
-// if (nread < (size_t) length)
-// {
-// LL_WARNS("Messaging") << "Short read" << LL_ENDL;
-// }
-// buffer[nread] = '\0';
-// fclose(fXML);
-
-// char *pos = (char *)buffer;
-// while ((pos = strstr(pos+1, "<sl:image ")) != 0)
-// {
-// char *pos_check = strstr(pos, "checksum=\"");
-
-// if (pos_check)
-// {
-// char *pos_uuid = strstr(pos_check, "\">");
-
-// if (pos_uuid)
-// {
-// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */
-// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */
-// image_uuid_str[UUID_STR_SIZE-1] = 0;
-
-// LLUUID image_uuid(image_uuid_str);
-
-// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL;
-
-// std::map<LLUUID, std::string>::iterator itor = gImageChecksums.find(image_uuid);
-// if (itor != gImageChecksums.end())
-// {
-// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL;
-// if (!itor->second.empty())
-// {
-// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */
-// }
-// }
-// }
-// }
-// }
-
-// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */
-// if (fwrite(buffer, 1, length, fXMLOut) != length)
-// {
-// LL_WARNS("Messaging") << "Short write" << LL_ENDL;
-// }
-// fclose(fXMLOut);
-
-// delete [] buffer;
-// }
-
-
-// void exported_item_complete(const LLTSCode status, void *user_data)
-// {
-// //std::string *filename = (std::string *)user_data;
-
-// if (status < LLTS_OK)
-// {
-// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL;
-// }
-// else
-// {
-// ++current_object_count;
-// if (current_image_count == exported_image_count && current_object_count == exported_object_count)
-// {
-// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL;
-
-// export_complete();
-// }
-// else
-// {
-// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
-// }
-// }
-// }
-
-// struct exported_image_info
-// {
-// LLUUID image_id;
-// std::string filename;
-// U32 image_num;
-// };
-
-// void exported_j2c_complete(const LLTSCode status, void *user_data)
-// {
-// exported_image_info *info = (exported_image_info *)user_data;
-// LLUUID image_id = info->image_id;
-// U32 image_num = info->image_num;
-// std::string filename = info->filename;
-// delete info;
-
-// if (status < LLTS_OK)
-// {
-// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL;
-// }
-// else
-// {
-// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
-// if (fIn)
-// {
-// LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C;
-// LLPointer<LLImageTGA> TargaUtility = new LLImageTGA;
-
-// fseek(fIn, 0, SEEK_END);
-// S32 length = ftell(fIn);
-// fseek(fIn, 0, SEEK_SET);
-// U8 *buffer = ImageUtility->allocateData(length);
-// if (fread(buffer, 1, length, fIn) != length)
-// {
-// LL_WARNS("Messaging") << "Short read" << LL_ENDL;
-// }
-// fclose(fIn);
-// LLFile::remove(filename);
-
-// // Convert to TGA
-// LLPointer<LLImageRaw> image = new LLImageRaw();
-
-// ImageUtility->updateData();
-// ImageUtility->decode(image, 100000.0f);
-
-// TargaUtility->encode(image);
-// U8 *data = TargaUtility->getData();
-// S32 data_size = TargaUtility->getDataSize();
-
-// std::string file_path = gDirUtilp->getDirName(filename);
-
-// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename;
-// //S32 name_len = output_file.length();
-// //strcpy(&output_file[name_len-3], "tga");
-// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */
-// char md5_hash_string[33]; /* Flawfinder: ignore */
-// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */
-// if (fOut)
-// {
-// if (fwrite(data, 1, data_size, fOut) != data_size)
-// {
-// LL_WARNS("Messaging") << "Short write" << LL_ENDL;
-// }
-// fseek(fOut, 0, SEEK_SET);
-// fclose(fOut);
-// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */
-// LLMD5 my_md5_hash(fOut);
-// my_md5_hash.hex_digest(md5_hash_string);
-// }
-
-// gImageChecksums.insert(std::pair<LLUUID, std::string>(image_id, md5_hash_string));
-// }
-// }
-
-// ++current_image_count;
-// if (current_image_count == exported_image_count && current_object_count == exported_object_count)
-// {
-// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL;
-// export_complete();
-// }
-// else
-// {
-// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
-// }
-//}
-
void process_derez_ack(LLMessageSystem*, void**)
{
if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount();
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index de05054f4b..8af0057c88 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -107,6 +107,7 @@
#include "llmeshrepository.h"
#include "llgltfmateriallist.h"
#include "llgl.h"
+#include "gltf/asset.h"
//#define DEBUG_UPDATE_TYPE
@@ -1481,6 +1482,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
// alpha was flipped so that it zero encoded better
coloru.mV[3] = 255 - coloru.mV[3];
+
mText->setColor(LLColor4(coloru));
mText->setString(temp_string);
@@ -1804,6 +1806,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];
@@ -4241,6 +4244,184 @@ const LLVector3 &LLViewerObject::getPositionAgent() const
return mPositionAgent;
}
+LLMatrix4a LLViewerObject::getGLTFAssetToAgentTransform() const
+{
+ LLMatrix4 root;
+ root.initScale(getScale());
+ root.rotate(getRenderRotation());
+ root.translate(getRenderPosition());
+
+ 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(-getRenderPosition());
+ 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 && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
+ {
+ auto& node = mGLTFAsset->mNodes[node_index];
+
+ LLMatrix4a asset_to_agent = getGLTFAssetToAgentTransform();
+ LLMatrix4a node_to_agent;
+ LLMatrix4a am;
+ am.loadu(glm::value_ptr(node.mAssetMatrix));
+ matMul(am, 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 && 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];
+ LLMatrix4a ami;
+ ami.loadu(glm::value_ptr(parent.mAssetMatrixInv));
+ matMul(agent_to_asset, ami, agent_to_node);
+ }
+
+ LLQuaternion agent_to_node_rot(agent_to_node.asMatrix4());
+ LLQuaternion new_rot;
+
+ new_rot = rotation * agent_to_node_rot;
+ new_rot.normalize();
+
+ LLVector3 pos;
+ LLQuaternion rot;
+ LLVector3 scale;
+ LLMatrix4a mat;
+ mat.loadu(glm::value_ptr(node.mMatrix));
+ decomposeMatrix(mat, pos, rot, scale);
+
+ mat.asMatrix4().initAll(scale, new_rot, pos);
+
+ node.mMatrix = glm::make_mat4(mat.getF32ptr());
+
+ mGLTFAsset->updateTransforms();
+ }
+}
+
+void LLViewerObject::moveGLTFNode(S32 node_index, const LLVector3& offset)
+{
+ if (mGLTFAsset && node_index >= 0 && node_index < mGLTFAsset->mNodes.size())
+ {
+ auto& node = mGLTFAsset->mNodes[node_index];
+
+ LLMatrix4a agent_to_asset = getAgentToGLTFAssetTransform();
+ LLMatrix4a agent_to_node;
+ LLMatrix4a ami;
+ ami.loadu(glm::value_ptr(node.mAssetMatrixInv));
+ matMul(agent_to_asset, ami, 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;
+
+ LLMatrix4a mat;
+ mat.loadu(glm::value_ptr(node.mMatrix));
+
+ matMul(trans, mat, mat);
+
+ node.mMatrix = glm::make_mat4(mat.getF32ptr());
+
+ // TODO -- only update transforms for this node and its children (or use a dirty flag)
+ mGLTFAsset->updateTransforms();
+ }
+}
+
const LLVector3 &LLViewerObject::getPositionRegion() const
{
if (!isRoot())
@@ -5228,7 +5409,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
@@ -7326,6 +7506,23 @@ void LLViewerObject::shrinkWrap()
}
}
+void LLViewerObject::setGLTFAsset(const LLUUID& id)
+{
+ //get the sculpt params and set the sculpt type and id
+ auto* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_SCULPT);
+
+ LLSculptParams* sculpt_params = (LLSculptParams*)param->data;
+ sculpt_params->setSculptTexture(id, LL_SCULPT_TYPE_GLTF);
+
+ setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, true, true);
+
+ // Update the volume
+ LLVolumeParams volume_params;
+ volume_params.setSculptID(id, LL_SCULPT_TYPE_GLTF);
+ updateVolume(volume_params);
+}
+
+
class ObjectPhysicsProperties : public LLHTTPNode
{
public:
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index d8ab0c8896..b96a1a6644 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -1,3 +1,4 @@
+
/**
* @file llviewerobject.h
* @brief Description of LLViewerObject class, which is the base class for most objects in the viewer.
@@ -46,6 +47,14 @@
#include "llrigginginfo.h"
#include "llreflectionmap.h"
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+ }
+}
+
class LLAgent; // TODO: Get rid of this.
class LLAudioSource;
class LLAudioSourceVO;
@@ -314,6 +323,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 +743,13 @@ public:
F32 mPhysicsDensity;
F32 mPhysicsRestitution;
+ // set the GLTF asset for this LLViewerObject to the specified asset id
+ // id MUST be for a GLTF asset (LLAssetType::AT_GLTF)
+ // will relesae any currently held references to a GLTF asset on id change
+ void setGLTFAsset(const LLUUID& id);
+
+ // Associated GLTF Asset
+ std::shared_ptr<LL::GLTF::Asset> mGLTFAsset;
// Pipeline classes
LLPointer<LLDrawable> mDrawable;
@@ -942,6 +970,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 57d96f4f1b..c75d590df8 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -55,6 +55,7 @@
#include "llfloaterregioninfo.h"
#include "llgltfmateriallist.h"
#include "llhttpnode.h"
+#include "llpbrterrainfeatures.h"
#include "llregioninfomodel.h"
#include "llsdutil.h"
#include "llstartup.h"
@@ -791,8 +792,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())
{
@@ -1105,6 +1107,11 @@ void LLViewerRegion::dirtyHeights()
}
}
+void LLViewerRegion::dirtyAllPatches()
+{
+ getLand().dirtyAllPatches();
+}
+
//physically delete the cache entry
void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering)
{
@@ -1155,13 +1162,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
@@ -1605,7 +1612,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)
{
@@ -1905,7 +1924,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)
{
@@ -2407,6 +2440,71 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& 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);
+ }
+
+ if (features.has("GLTFEnabled"))
+ {
+ bool enabled = features["GLTFEnabled"];
+ gSavedSettings.setBOOL("GLTFEnabled", enabled);
+ }
+ else
+ {
+ gSavedSettings.setBOOL("GLTFEnabled", false);
+ }
+
+ if (features.has("PBRTerrainTransformsEnabled"))
+ {
+ bool enabled = features["PBRTerrainTransformsEnabled"];
+ gSavedSettings.setBOOL("RenderTerrainTransformsPBREnabled", enabled);
+ }
+ else
+ {
+ gSavedSettings.setBOOL("RenderTerrainTransformsPBREnabled", false);
+ }
+ };
+
+
+ LLAppViewer::instance()->postToMainCoro(work);
}
//this is called when the parent is not cacheable.
@@ -2663,7 +2761,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)
@@ -2880,6 +2985,11 @@ void LLViewerRegion::dumpCache()
// TODO - add overrides cache too
}
+void LLViewerRegion::clearVOCacheFromMemory()
+{
+ mImpl->mCacheMap.clear();
+}
+
void LLViewerRegion::unpackRegionHandshake()
{
LLMessageSystem *msg = gMessageSystem;
@@ -2964,20 +3074,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;
@@ -3028,6 +3138,17 @@ void LLViewerRegion::unpackRegionHandshake()
{
compp->setParamsReady();
}
+
+ LLPBRTerrainFeatures::queueQuery(*this, [](LLUUID region_id, bool success, const LLModifyRegion& composition_changes)
+ {
+ if (!success) { return; }
+ LLViewerRegion* region = LLWorld::getInstance()->getRegionFromID(region_id);
+ if (!region) { return; }
+ LLVLComposition* compp = region->getComposition();
+ if (!compp) { return; }
+ compp->apply(composition_changes);
+ LLFloaterRegionInfo::sRefreshFromRegion(region);
+ });
}
@@ -3125,6 +3246,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("MapLayerGod");
capabilityNames.append("MeshUploadFlag");
capabilityNames.append("ModifyMaterialParams");
+ capabilityNames.append("ModifyRegion");
capabilityNames.append("NavMeshGenerationStatus");
capabilityNames.append("NewFileAgentInventory");
capabilityNames.append("ObjectAnimation");
@@ -3663,6 +3785,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 650b52b118..1329bdf1d5 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -168,6 +168,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; }
@@ -380,7 +383,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 5090d53140..d43d6fea37 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;
@@ -77,12 +85,15 @@ LLGLSLShader gGlowCombineProgram;
LLGLSLShader gReflectionMipProgram;
LLGLSLShader gGaussianProgram;
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,27 +226,151 @@ LLGLSLShader gDeferredSkinnedPBROpaqueProgram;
LLGLSLShader gHUDPBRAlphaProgram;
LLGLSLShader gDeferredPBRAlphaProgram;
LLGLSLShader gDeferredSkinnedPBRAlphaProgram;
+LLGLSLShader gDeferredPBRTerrainProgram;
+
+LLGLSLShader gGLTFPBRMetallicRoughnessProgram;
+
//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;
riggedShader.mFeatures.hasObjectSkinning = true;
riggedShader.mDefines = shader.mDefines; // NOTE: Must come before addPermutation
+
riggedShader.addPermutation("HAS_SKIN", "1");
riggedShader.mShaderFiles = shader.mShaderFiles;
riggedShader.mShaderLevel = shader.mShaderLevel;
riggedShader.mShaderGroup = shader.mShaderGroup;
shader.mRiggedVariant = &riggedShader;
- return riggedShader.createShader(NULL, NULL);
+ return riggedShader.createShader();
}
+
+static bool make_gltf_variant(LLGLSLShader& shader, LLGLSLShader& variant, bool alpha_blend, bool rigged, bool unlit, bool use_sun_shadow)
+{
+ variant.mName = shader.mName.c_str();
+ variant.mFeatures = shader.mFeatures;
+ variant.mShaderFiles = shader.mShaderFiles;
+ variant.mShaderLevel = shader.mShaderLevel;
+ variant.mShaderGroup = shader.mShaderGroup;
+
+ variant.mDefines = shader.mDefines; // NOTE: Must come before addPermutation
+
+ variant.addPermutation("MAX_JOINTS_PER_GLTF_OBJECT", std::to_string(LLSkinningUtil::getMaxGLTFJointCount()));
+
+ if (rigged)
+ {
+ variant.addPermutation("HAS_SKIN", "1");
+ }
+
+ if (unlit)
+ {
+ variant.addPermutation("UNLIT", "1");
+ }
+
+ if (alpha_blend)
+ {
+ variant.addPermutation("ALPHA_BLEND", "1");
+
+ variant.mFeatures.calculatesLighting = false;
+ variant.mFeatures.hasLighting = false;
+ variant.mFeatures.isAlphaLighting = true;
+ variant.mFeatures.hasSrgb = true;
+ variant.mFeatures.calculatesAtmospherics = true;
+ variant.mFeatures.hasAtmospherics = true;
+ variant.mFeatures.hasGamma = true;
+ variant.mFeatures.hasShadows = use_sun_shadow;
+ variant.mFeatures.isDeferred = true; // include deferredUtils
+ variant.mFeatures.hasReflectionProbes = true;
+
+ if (use_sun_shadow)
+ {
+ variant.addPermutation("HAS_SUN_SHADOW", "1");
+ }
+
+ bool success = variant.createShader();
+ llassert(success);
+
+ // Alpha Shader Hack
+ // See: LLRender::syncMatrices()
+ variant.mFeatures.calculatesLighting = true;
+ variant.mFeatures.hasLighting = true;
+
+ return success;
+ }
+ else
+ {
+ return variant.createShader();
+ }
+}
+
+static bool make_gltf_variants(LLGLSLShader& shader, bool use_sun_shadow)
+{
+ shader.mFeatures.mGLTF = true;
+ shader.mGLTFVariants.resize(LLGLSLShader::NUM_GLTF_VARIANTS);
+
+ for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
+ {
+ bool alpha_blend = i & LLGLSLShader::GLTFVariant::ALPHA_BLEND;
+ bool rigged = i & LLGLSLShader::GLTFVariant::RIGGED;
+ bool unlit = i & LLGLSLShader::GLTFVariant::UNLIT;
+
+ if (!make_gltf_variant(shader, shader.mGLTFVariants[i], alpha_blend, rigged, unlit, use_sun_shadow))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#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);
@@ -242,9 +378,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
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);
@@ -253,9 +387,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
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(&gHUDFullbrightProgram);
mShaderList.push_back(&gDeferredFullbrightAlphaMaskProgram);
@@ -264,31 +396,32 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
mShaderList.push_back(&gHUDFullbrightAlphaMaskAlphaProgram);
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(&gDeferredAvatarAlphaProgram);
+ 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(&gGLTFPBRMetallicRoughnessProgram);
+ 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 +476,10 @@ void LLViewerShaderMgr::setShaders()
return;
}
+ mShaderList.clear();
+
+ LLShaderMgr::sMirrorsEnabled = LLPipeline::RenderMirrors;
+
if (!gGLManager.mHasRequirements)
{
// Viewer will show 'hardware requirements' warning later
@@ -370,7 +507,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 +664,8 @@ void LLViewerShaderMgr::setShaders()
}
gPipeline.createGLBuffers();
+ finalizeShaderList();
+
reentrance = false;
}
@@ -623,6 +765,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 +807,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] ) );
@@ -723,7 +876,7 @@ bool LLViewerShaderMgr::loadShadersWater()
gWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER;
gWaterProgram.mShaderLevel = mShaderLevel[SHADER_WATER];
- success = gWaterProgram.createShader(NULL, NULL);
+ success = gWaterProgram.createShader();
llassert(success);
}
@@ -753,7 +906,7 @@ bool LLViewerShaderMgr::loadShadersWater()
}
gWaterEdgeProgram.mShaderGroup = LLGLSLShader::SG_WATER;
gWaterEdgeProgram.mShaderLevel = mShaderLevel[SHADER_WATER];
- success = gWaterEdgeProgram.createShader(NULL, NULL);
+ success = gWaterEdgeProgram.createShader();
llassert(success);
}
@@ -773,7 +926,7 @@ bool LLViewerShaderMgr::loadShadersWater()
{
gUnderWaterProgram.addPermutation("TRANSPARENT_WATER", "1");
}
- success = gUnderWaterProgram.createShader(NULL, NULL);
+ success = gUnderWaterProgram.createShader();
llassert(success);
}
@@ -822,7 +975,7 @@ bool LLViewerShaderMgr::loadShadersEffects()
gGlowProgram.mShaderFiles.push_back(make_pair("effects/glowV.glsl", GL_VERTEX_SHADER));
gGlowProgram.mShaderFiles.push_back(make_pair("effects/glowF.glsl", GL_FRAGMENT_SHADER));
gGlowProgram.mShaderLevel = mShaderLevel[SHADER_EFFECT];
- success = gGlowProgram.createShader(NULL, NULL);
+ success = gGlowProgram.createShader();
if (!success)
{
LLPipeline::sRenderGlow = false;
@@ -845,7 +998,7 @@ bool LLViewerShaderMgr::loadShadersEffects()
gGlowExtractProgram.addPermutation("HAS_NOISE", "1");
}
- success = gGlowExtractProgram.createShader(NULL, NULL);
+ success = gGlowExtractProgram.createShader();
if (!success)
{
LLPipeline::sRenderGlow = false;
@@ -917,11 +1070,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();
@@ -948,9 +1103,11 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDPBROpaqueProgram.unload();
gPBRGlowProgram.unload();
gDeferredPBROpaqueProgram.unload();
+ gGLTFPBRMetallicRoughnessProgram.unload();
gDeferredSkinnedPBROpaqueProgram.unload();
gDeferredPBRAlphaProgram.unload();
gDeferredSkinnedPBRAlphaProgram.unload();
+ gDeferredPBRTerrainProgram.unload();
return true;
}
@@ -964,13 +1121,12 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredHighlightProgram.mShaderFiles.push_back(make_pair("interface/highlightV.glsl", GL_VERTEX_SHADER));
gDeferredHighlightProgram.mShaderFiles.push_back(make_pair("deferred/highlightF.glsl", GL_FRAGMENT_SHADER));
gDeferredHighlightProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gDeferredHighlightProgram.createShader(NULL, NULL);
+ success = gDeferredHighlightProgram.createShader();
}
if (success)
{
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));
@@ -978,56 +1134,52 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredDiffuseProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
gDeferredDiffuseProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredDiffuseProgram, gDeferredSkinnedDiffuseProgram);
- success = success && gDeferredDiffuseProgram.createShader(NULL, NULL);
+ success = success && gDeferredDiffuseProgram.createShader();
}
if (success)
{
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));
gDeferredDiffuseAlphaMaskProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
gDeferredDiffuseAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredDiffuseAlphaMaskProgram, gDeferredSkinnedDiffuseAlphaMaskProgram);
- success = success && gDeferredDiffuseAlphaMaskProgram.createShader(NULL, NULL);
+ success = success && gDeferredDiffuseAlphaMaskProgram.createShader();
}
if (success)
{
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));
gDeferredNonIndexedDiffuseAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredNonIndexedDiffuseAlphaMaskProgram.createShader(NULL, NULL);
+ success = gDeferredNonIndexedDiffuseAlphaMaskProgram.createShader();
llassert(success);
}
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));
gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.createShader(NULL, NULL);
+ success = gDeferredNonIndexedDiffuseAlphaMaskNoColorProgram.createShader();
llassert(success);
}
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));
gDeferredBumpProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredBumpProgram, gDeferredSkinnedBumpProgram);
- success = success && gDeferredBumpProgram.createShader(NULL, NULL);
+ success = success && gDeferredBumpProgram.createShader();
llassert(success);
}
@@ -1044,9 +1196,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 +1243,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;
@@ -1102,7 +1261,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredMaterialProgram[i].mRiggedVariant = &gDeferredMaterialProgram[i + 0x10];
}
- success = gDeferredMaterialProgram[i].createShader(NULL, NULL);
+ success = gDeferredMaterialProgram[i].createShader();
llassert(success);
}
}
@@ -1119,7 +1278,6 @@ bool LLViewerShaderMgr::loadShadersDeferred()
if (success)
{
gDeferredPBROpaqueProgram.mName = "Deferred PBR Opaque Shader";
- gDeferredPBROpaqueProgram.mFeatures.encodesNormal = true;
gDeferredPBROpaqueProgram.mFeatures.hasSrgb = true;
gDeferredPBROpaqueProgram.mShaderFiles.clear();
@@ -1131,13 +1289,29 @@ bool LLViewerShaderMgr::loadShadersDeferred()
success = make_rigged_variant(gDeferredPBROpaqueProgram, gDeferredSkinnedPBROpaqueProgram);
if (success)
{
- success = gDeferredPBROpaqueProgram.createShader(NULL, NULL);
+ success = gDeferredPBROpaqueProgram.createShader();
}
llassert(success);
}
if (success)
{
+ gGLTFPBRMetallicRoughnessProgram.mName = "GLTF PBR Metallic Roughness Shader";
+ gGLTFPBRMetallicRoughnessProgram.mFeatures.hasSrgb = true;
+
+ gGLTFPBRMetallicRoughnessProgram.mShaderFiles.clear();
+ gGLTFPBRMetallicRoughnessProgram.mShaderFiles.push_back(make_pair("gltf/pbrmetallicroughnessV.glsl", GL_VERTEX_SHADER));
+ gGLTFPBRMetallicRoughnessProgram.mShaderFiles.push_back(make_pair("gltf/pbrmetallicroughnessF.glsl", GL_FRAGMENT_SHADER));
+ gGLTFPBRMetallicRoughnessProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+ gGLTFPBRMetallicRoughnessProgram.clearPermutations();
+
+ success = make_gltf_variants(gGLTFPBRMetallicRoughnessProgram, use_sun_shadow);
+
+ llassert(success);
+ }
+
+ if (success)
+ {
gPBRGlowProgram.mName = " PBR Glow Shader";
gPBRGlowProgram.mFeatures.hasSrgb = true;
gPBRGlowProgram.mShaderFiles.clear();
@@ -1148,7 +1322,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
success = make_rigged_variant(gPBRGlowProgram, gPBRGlowSkinnedProgram);
if (success)
{
- success = gPBRGlowProgram.createShader(NULL, NULL);
+ success = gPBRGlowProgram.createShader();
}
llassert(success);
}
@@ -1164,7 +1338,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDPBROpaqueProgram.clearPermutations();
gHUDPBROpaqueProgram.addPermutation("IS_HUD", "1");
- success = gHUDPBROpaqueProgram.createShader(NULL, NULL);
+ success = gHUDPBROpaqueProgram.createShader();
llassert(success);
}
@@ -1180,7 +1354,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;
@@ -1210,7 +1383,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
success = make_rigged_variant(*shader, gDeferredSkinnedPBRAlphaProgram);
if (success)
{
- success = shader->createShader(NULL, NULL);
+ success = shader->createShader();
}
llassert(success);
@@ -1239,7 +1412,34 @@ bool LLViewerShaderMgr::loadShadersDeferred()
shader->addPermutation("IS_HUD", "1");
shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = shader->createShader(NULL, NULL);
+ success = shader->createShader();
+ 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();
llassert(success);
}
@@ -1247,11 +1447,10 @@ bool LLViewerShaderMgr::loadShadersDeferred()
{
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];
- success = gDeferredTreeProgram.createShader(NULL, NULL);
+ success = gDeferredTreeProgram.createShader();
}
if (success)
@@ -1262,7 +1461,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredTreeShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredTreeShadowProgram.mRiggedVariant = &gDeferredSkinnedTreeShadowProgram;
- success = gDeferredTreeShadowProgram.createShader(NULL, NULL);
+ success = gDeferredTreeShadowProgram.createShader();
llassert(success);
}
@@ -1274,7 +1473,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredSkinnedTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowSkinnedV.glsl", GL_VERTEX_SHADER));
gDeferredSkinnedTreeShadowProgram.mShaderFiles.push_back(make_pair("deferred/treeShadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredSkinnedTreeShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredSkinnedTreeShadowProgram.createShader(NULL, NULL);
+ success = gDeferredSkinnedTreeShadowProgram.createShader();
llassert(success);
}
@@ -1282,13 +1481,11 @@ 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));
gDeferredImpostorProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredImpostorProgram.createShader(NULL, NULL);
+ success = gDeferredImpostorProgram.createShader();
llassert(success);
}
@@ -1306,7 +1503,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredLightProgram.clearPermutations();
- success = gDeferredLightProgram.createShader(NULL, NULL);
+ success = gDeferredLightProgram.createShader();
llassert(success);
}
@@ -1326,7 +1523,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredMultiLightProgram[i].mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredMultiLightProgram[i].addPermutation("LIGHT_COUNT", llformat("%d", i+1));
- success = gDeferredMultiLightProgram[i].createShader(NULL, NULL);
+ success = gDeferredMultiLightProgram[i].createShader();
llassert(success);
}
}
@@ -1344,7 +1541,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/spotLightF.glsl", GL_FRAGMENT_SHADER));
gDeferredSpotLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredSpotLightProgram.createShader(NULL, NULL);
+ success = gDeferredSpotLightProgram.createShader();
llassert(success);
}
@@ -1362,7 +1559,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/spotLightF.glsl", GL_FRAGMENT_SHADER));
gDeferredMultiSpotLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredMultiSpotLightProgram.createShader(NULL, NULL);
+ success = gDeferredMultiSpotLightProgram.createShader();
llassert(success);
}
@@ -1396,7 +1593,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredSunProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER));
gDeferredSunProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredSunProgram.createShader(NULL, NULL);
+ success = gDeferredSunProgram.createShader();
llassert(success);
}
@@ -1410,7 +1607,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair("deferred/blurLightF.glsl", GL_FRAGMENT_SHADER));
gDeferredBlurLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredBlurLightProgram.createShader(NULL, NULL);
+ success = gDeferredBlurLightProgram.createShader();
llassert(success);
}
@@ -1445,7 +1642,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;
@@ -1478,7 +1674,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = shader->createShader(NULL, NULL);
+ success = shader->createShader();
llassert(success);
// Hack
@@ -1507,7 +1703,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;
@@ -1538,7 +1733,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
{
shader->mRiggedVariant = shaders[1];
}
- success = shader->createShader(NULL, NULL);
+ success = shader->createShader();
llassert(success);
// End Hack
@@ -1555,14 +1750,13 @@ 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();
gDeferredAvatarEyesProgram.mShaderFiles.push_back(make_pair("deferred/avatarEyesV.glsl", GL_VERTEX_SHADER));
gDeferredAvatarEyesProgram.mShaderFiles.push_back(make_pair("deferred/diffuseF.glsl", GL_FRAGMENT_SHADER));
gDeferredAvatarEyesProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredAvatarEyesProgram.createShader(NULL, NULL);
+ success = gDeferredAvatarEyesProgram.createShader();
llassert(success);
}
@@ -1579,7 +1773,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredFullbrightProgram.mShaderFiles.push_back(make_pair("deferred/fullbrightF.glsl", GL_FRAGMENT_SHADER));
gDeferredFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredFullbrightProgram, gDeferredSkinnedFullbrightProgram);
- success = gDeferredFullbrightProgram.createShader(NULL, NULL);
+ success = gDeferredFullbrightProgram.createShader();
llassert(success);
}
@@ -1597,7 +1791,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDFullbrightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gHUDFullbrightProgram.clearPermutations();
gHUDFullbrightProgram.addPermutation("IS_HUD", "1");
- success = gHUDFullbrightProgram.createShader(NULL, NULL);
+ success = gHUDFullbrightProgram.createShader();
llassert(success);
}
@@ -1616,7 +1810,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredFullbrightAlphaMaskProgram.addPermutation("HAS_ALPHA_MASK","1");
gDeferredFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredFullbrightAlphaMaskProgram, gDeferredSkinnedFullbrightAlphaMaskProgram);
- success = success && gDeferredFullbrightAlphaMaskProgram.createShader(NULL, NULL);
+ success = success && gDeferredFullbrightAlphaMaskProgram.createShader();
llassert(success);
}
@@ -1635,7 +1829,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDFullbrightAlphaMaskProgram.addPermutation("HAS_ALPHA_MASK", "1");
gHUDFullbrightAlphaMaskProgram.addPermutation("IS_HUD", "1");
gHUDFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gHUDFullbrightAlphaMaskProgram.createShader(NULL, NULL);
+ success = gHUDFullbrightAlphaMaskProgram.createShader();
llassert(success);
}
@@ -1656,7 +1850,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredFullbrightAlphaMaskAlphaProgram.addPermutation("IS_ALPHA", "1");
gDeferredFullbrightAlphaMaskAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredFullbrightAlphaMaskAlphaProgram, gDeferredSkinnedFullbrightAlphaMaskAlphaProgram);
- success = success && gDeferredFullbrightAlphaMaskAlphaProgram.createShader(NULL, NULL);
+ success = success && gDeferredFullbrightAlphaMaskAlphaProgram.createShader();
llassert(success);
}
@@ -1677,7 +1871,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDFullbrightAlphaMaskAlphaProgram.addPermutation("IS_ALPHA", "1");
gHUDFullbrightAlphaMaskAlphaProgram.addPermutation("IS_HUD", "1");
gHUDFullbrightAlphaMaskAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = success && gHUDFullbrightAlphaMaskAlphaProgram.createShader(NULL, NULL);
+ success = success && gHUDFullbrightAlphaMaskAlphaProgram.createShader();
llassert(success);
}
@@ -1695,7 +1889,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredFullbrightShinyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredFullbrightShinyProgram.mFeatures.hasReflectionProbes = true;
success = make_rigged_variant(gDeferredFullbrightShinyProgram, gDeferredSkinnedFullbrightShinyProgram);
- success = success && gDeferredFullbrightShinyProgram.createShader(NULL, NULL);
+ success = success && gDeferredFullbrightShinyProgram.createShader();
llassert(success);
}
@@ -1714,7 +1908,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHUDFullbrightShinyProgram.mFeatures.hasReflectionProbes = true;
gHUDFullbrightShinyProgram.clearPermutations();
gHUDFullbrightShinyProgram.addPermutation("IS_HUD", "1");
- success = gHUDFullbrightShinyProgram.createShader(NULL, NULL);
+ success = gHUDFullbrightShinyProgram.createShader();
llassert(success);
}
@@ -1730,7 +1924,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredEmissiveProgram.mShaderFiles.push_back(make_pair("deferred/emissiveF.glsl", GL_FRAGMENT_SHADER));
gDeferredEmissiveProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredEmissiveProgram, gDeferredSkinnedEmissiveProgram);
- success = success && gDeferredEmissiveProgram.createShader(NULL, NULL);
+ success = success && gDeferredEmissiveProgram.createShader();
llassert(success);
}
@@ -1763,7 +1957,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredSoftenProgram.addPermutation("HAS_SSAO", "1");
}
- success = gDeferredSoftenProgram.createShader(NULL, NULL);
+ success = gDeferredSoftenProgram.createShader();
llassert(success);
}
@@ -1785,7 +1979,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHazeProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gHazeProgram.createShader(NULL, NULL);
+ success = gHazeProgram.createShader();
llassert(success);
}
@@ -1809,7 +2003,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gHazeWaterProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gHazeWaterProgram.createShader(NULL, NULL);
+ success = gHazeWaterProgram.createShader();
llassert(success);
}
@@ -1822,7 +2016,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredShadowProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredShadowProgram.mRiggedVariant = &gDeferredSkinnedShadowProgram;
- success = gDeferredShadowProgram.createShader(NULL, NULL);
+ success = gDeferredShadowProgram.createShader();
llassert(success);
}
@@ -1837,7 +2031,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredSkinnedShadowProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredSkinnedShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
// gDeferredSkinnedShadowProgram.addPermutation("DEPTH_CLAMP", "1"); // disable depth clamp for now
- success = gDeferredSkinnedShadowProgram.createShader(NULL, NULL);
+ success = gDeferredSkinnedShadowProgram.createShader();
llassert(success);
}
@@ -1851,7 +2045,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredShadowCubeProgram.mShaderFiles.push_back(make_pair("deferred/shadowF.glsl", GL_FRAGMENT_SHADER));
// gDeferredShadowCubeProgram.addPermutation("DEPTH_CLAMP", "1");
gDeferredShadowCubeProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredShadowCubeProgram.createShader(NULL, NULL);
+ success = gDeferredShadowCubeProgram.createShader();
llassert(success);
}
@@ -1869,7 +2063,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredShadowFullbrightAlphaMaskProgram.addPermutation("IS_FULLBRIGHT", "1");
gDeferredShadowFullbrightAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredShadowFullbrightAlphaMaskProgram, gDeferredSkinnedShadowFullbrightAlphaMaskProgram);
- success = success && gDeferredShadowFullbrightAlphaMaskProgram.createShader(NULL, NULL);
+ success = success && gDeferredShadowFullbrightAlphaMaskProgram.createShader();
llassert(success);
}
@@ -1883,7 +2077,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredShadowAlphaMaskProgram.mShaderFiles.push_back(make_pair("deferred/shadowAlphaMaskF.glsl", GL_FRAGMENT_SHADER));
gDeferredShadowAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
success = make_rigged_variant(gDeferredShadowAlphaMaskProgram, gDeferredSkinnedShadowAlphaMaskProgram);
- success = success && gDeferredShadowAlphaMaskProgram.createShader(NULL, NULL);
+ success = success && gDeferredShadowAlphaMaskProgram.createShader();
llassert(success);
}
@@ -1897,7 +2091,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredShadowGLTFAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredShadowGLTFAlphaMaskProgram.clearPermutations();
success = make_rigged_variant(gDeferredShadowGLTFAlphaMaskProgram, gDeferredSkinnedShadowGLTFAlphaMaskProgram);
- success = success && gDeferredShadowGLTFAlphaMaskProgram.createShader(NULL, NULL);
+ success = success && gDeferredShadowGLTFAlphaMaskProgram.createShader();
llassert(success);
}
@@ -1910,7 +2104,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredShadowGLTFAlphaBlendProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredShadowGLTFAlphaBlendProgram.clearPermutations();
success = make_rigged_variant(gDeferredShadowGLTFAlphaBlendProgram, gDeferredSkinnedShadowGLTFAlphaBlendProgram);
- success = success && gDeferredShadowGLTFAlphaBlendProgram.createShader(NULL, NULL);
+ success = success && gDeferredShadowGLTFAlphaBlendProgram.createShader();
llassert(success);
}
@@ -1923,7 +2117,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredAvatarShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarShadowV.glsl", GL_VERTEX_SHADER));
gDeferredAvatarShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarShadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredAvatarShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredAvatarShadowProgram.createShader(NULL, NULL);
+ success = gDeferredAvatarShadowProgram.createShader();
llassert(success);
}
@@ -1935,7 +2129,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredAvatarAlphaShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaShadowV.glsl", GL_VERTEX_SHADER));
gDeferredAvatarAlphaShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaShadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredAvatarAlphaShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredAvatarAlphaShadowProgram.createShader(NULL, NULL);
+ success = gDeferredAvatarAlphaShadowProgram.createShader();
llassert(success);
}
if (success)
@@ -1946,17 +2140,14 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredAvatarAlphaMaskShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaShadowV.glsl", GL_VERTEX_SHADER));
gDeferredAvatarAlphaMaskShadowProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaMaskShadowF.glsl", GL_FRAGMENT_SHADER));
gDeferredAvatarAlphaMaskShadowProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredAvatarAlphaMaskShadowProgram.createShader(NULL, NULL);
+ success = gDeferredAvatarAlphaMaskShadowProgram.createShader();
llassert(success);
}
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;
@@ -1967,7 +2158,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredTerrainProgram.mShaderFiles.push_back(make_pair("deferred/terrainV.glsl", GL_VERTEX_SHADER));
gDeferredTerrainProgram.mShaderFiles.push_back(make_pair("deferred/terrainF.glsl", GL_FRAGMENT_SHADER));
gDeferredTerrainProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredTerrainProgram.createShader(NULL, NULL);
+ success = gDeferredTerrainProgram.createShader();
llassert(success);
}
@@ -1975,12 +2166,11 @@ 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));
gDeferredAvatarProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredAvatarProgram.createShader(NULL, NULL);
+ success = gDeferredAvatarProgram.createShader();
llassert(success);
}
@@ -1993,7 +2183,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;
@@ -2015,7 +2204,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredAvatarAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredAvatarAlphaProgram.createShader(NULL, NULL);
+ success = gDeferredAvatarAlphaProgram.createShader();
llassert(success);
gDeferredAvatarAlphaProgram.mFeatures.calculatesLighting = true;
@@ -2029,10 +2218,25 @@ 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];
- success = gExposureProgram.createShader(NULL, NULL);
+ success = gExposureProgram.createShader();
+ llassert(success);
+ }
+
+ 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();
llassert(success);
}
@@ -2044,7 +2248,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gLuminanceProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gLuminanceProgram.mShaderFiles.push_back(make_pair("deferred/luminanceF.glsl", GL_FRAGMENT_SHADER));
gLuminanceProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gLuminanceProgram.createShader(NULL, NULL);
+ success = gLuminanceProgram.createShader();
llassert(success);
}
@@ -2058,7 +2262,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredGammaCorrect.glsl", GL_FRAGMENT_SHADER));
gDeferredPostGammaCorrectProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredPostGammaCorrectProgram.createShader(NULL, NULL);
+ success = gDeferredPostGammaCorrectProgram.createShader();
llassert(success);
}
@@ -2073,7 +2277,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gNoPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gNoPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredGammaCorrect.glsl", GL_FRAGMENT_SHADER));
gNoPostGammaCorrectProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gNoPostGammaCorrectProgram.createShader(NULL, NULL);
+ success = gNoPostGammaCorrectProgram.createShader();
llassert(success);
}
@@ -2088,7 +2292,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gLegacyPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gLegacyPostGammaCorrectProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredGammaCorrect.glsl", GL_FRAGMENT_SHADER));
gLegacyPostGammaCorrectProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gLegacyPostGammaCorrectProgram.createShader(NULL, NULL);
+ success = gLegacyPostGammaCorrectProgram.createShader();
llassert(success);
}
@@ -2101,7 +2305,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gFXAAProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER));
gFXAAProgram.mShaderFiles.push_back(make_pair("deferred/fxaaF.glsl", GL_FRAGMENT_SHADER));
gFXAAProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gFXAAProgram.createShader(NULL, NULL);
+ success = gFXAAProgram.createShader();
llassert(success);
}
@@ -2113,7 +2317,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredF.glsl", GL_FRAGMENT_SHADER));
gDeferredPostProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredPostProgram.createShader(NULL, NULL);
+ success = gDeferredPostProgram.createShader();
llassert(success);
}
@@ -2125,7 +2329,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredCoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredCoFProgram.mShaderFiles.push_back(make_pair("deferred/cofF.glsl", GL_FRAGMENT_SHADER));
gDeferredCoFProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredCoFProgram.createShader(NULL, NULL);
+ success = gDeferredCoFProgram.createShader();
llassert(success);
}
@@ -2137,7 +2341,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredDoFCombineProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredDoFCombineProgram.mShaderFiles.push_back(make_pair("deferred/dofCombineF.glsl", GL_FRAGMENT_SHADER));
gDeferredDoFCombineProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredDoFCombineProgram.createShader(NULL, NULL);
+ success = gDeferredDoFCombineProgram.createShader();
llassert(success);
}
@@ -2149,7 +2353,27 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoDoFF.glsl", GL_FRAGMENT_SHADER));
gDeferredPostNoDoFProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredPostNoDoFProgram.createShader(NULL, NULL);
+ success = gDeferredPostNoDoFProgram.createShader();
+ llassert(success);
+ }
+
+ 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();
llassert(success);
}
@@ -2167,7 +2391,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredWLSkyProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredWLSkyProgram.mShaderGroup = LLGLSLShader::SG_SKY;
- success = gDeferredWLSkyProgram.createShader(NULL, NULL);
+ success = gDeferredWLSkyProgram.createShader();
llassert(success);
}
@@ -2185,7 +2409,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredWLCloudProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredWLCloudProgram.mShaderGroup = LLGLSLShader::SG_SKY;
gDeferredWLCloudProgram.addConstant( LLGLSLShader::SHADER_CONST_CLOUD_MOON_DEPTH ); // SL-14113
- success = gDeferredWLCloudProgram.createShader(NULL, NULL);
+ success = gDeferredWLCloudProgram.createShader();
llassert(success);
}
@@ -2203,7 +2427,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredWLSunProgram.mShaderFiles.push_back(make_pair("deferred/sunDiscF.glsl", GL_FRAGMENT_SHADER));
gDeferredWLSunProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredWLSunProgram.mShaderGroup = LLGLSLShader::SG_SKY;
- success = gDeferredWLSunProgram.createShader(NULL, NULL);
+ success = gDeferredWLSunProgram.createShader();
llassert(success);
}
@@ -2223,7 +2447,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredWLMoonProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredWLMoonProgram.mShaderGroup = LLGLSLShader::SG_SKY;
gDeferredWLMoonProgram.addConstant( LLGLSLShader::SHADER_CONST_CLOUD_MOON_DEPTH ); // SL-14113
- success = gDeferredWLMoonProgram.createShader(NULL, NULL);
+ success = gDeferredWLMoonProgram.createShader();
llassert(success);
}
@@ -2236,7 +2460,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredStarProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gDeferredStarProgram.mShaderGroup = LLGLSLShader::SG_SKY;
gDeferredStarProgram.addConstant( LLGLSLShader::SHADER_CONST_STAR_DEPTH ); // SL-14113
- success = gDeferredStarProgram.createShader(NULL, NULL);
+ success = gDeferredStarProgram.createShader();
llassert(success);
}
@@ -2248,7 +2472,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gNormalMapGenProgram.mShaderFiles.push_back(make_pair("deferred/normgenF.glsl", GL_FRAGMENT_SHADER));
gNormalMapGenProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
gNormalMapGenProgram.mShaderGroup = LLGLSLShader::SG_SKY;
- success = gNormalMapGenProgram.createShader(NULL, NULL);
+ success = gNormalMapGenProgram.createShader();
}
if (success)
@@ -2258,7 +2482,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredGenBrdfLutProgram.mShaderFiles.push_back(make_pair("deferred/genbrdflutV.glsl", GL_VERTEX_SHADER));
gDeferredGenBrdfLutProgram.mShaderFiles.push_back(make_pair("deferred/genbrdflutF.glsl", GL_FRAGMENT_SHADER));
gDeferredGenBrdfLutProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredGenBrdfLutProgram.createShader(NULL, NULL);
+ success = gDeferredGenBrdfLutProgram.createShader();
}
if (success) {
@@ -2269,7 +2493,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gPostScreenSpaceReflectionProgram.mFeatures.hasScreenSpaceReflections = true;
gPostScreenSpaceReflectionProgram.mFeatures.isDeferred = true;
gPostScreenSpaceReflectionProgram.mShaderLevel = 3;
- success = gPostScreenSpaceReflectionProgram.createShader(NULL, NULL);
+ success = gPostScreenSpaceReflectionProgram.createShader();
}
if (success) {
@@ -2278,7 +2502,7 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredBufferVisualProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoTCV.glsl", GL_VERTEX_SHADER));
gDeferredBufferVisualProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredVisualizeBuffers.glsl", GL_FRAGMENT_SHADER));
gDeferredBufferVisualProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
- success = gDeferredBufferVisualProgram.createShader(NULL, NULL);
+ success = gDeferredBufferVisualProgram.createShader();
}
return success;
@@ -2292,13 +2516,12 @@ 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));
gObjectBumpProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
success = make_rigged_variant(gObjectBumpProgram, gSkinnedObjectBumpProgram);
- success = success && gObjectBumpProgram.createShader(NULL, NULL);
+ success = success && gObjectBumpProgram.createShader();
if (success)
{ //lldrawpoolbump assumes "texture0" has channel 0 and "texture1" has channel 1
LLGLSLShader* shader[] = { &gObjectBumpProgram, &gSkinnedObjectBumpProgram };
@@ -2326,7 +2549,7 @@ bool LLViewerShaderMgr::loadShadersObject()
gObjectAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("objects/simpleNoColorV.glsl", GL_VERTEX_SHADER));
gObjectAlphaMaskNoColorProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER));
gObjectAlphaMaskNoColorProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
- success = gObjectAlphaMaskNoColorProgram.createShader(NULL, NULL);
+ success = gObjectAlphaMaskNoColorProgram.createShader();
}
if (success)
@@ -2338,7 +2561,7 @@ bool LLViewerShaderMgr::loadShadersObject()
gImpostorProgram.mShaderFiles.push_back(make_pair("objects/impostorV.glsl", GL_VERTEX_SHADER));
gImpostorProgram.mShaderFiles.push_back(make_pair("objects/impostorF.glsl", GL_FRAGMENT_SHADER));
gImpostorProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
- success = gImpostorProgram.createShader(NULL, NULL);
+ success = gImpostorProgram.createShader();
}
if (success)
@@ -2350,7 +2573,7 @@ bool LLViewerShaderMgr::loadShadersObject()
gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewF.glsl", GL_FRAGMENT_SHADER));
gObjectPreviewProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
success = make_rigged_variant(gObjectPreviewProgram, gSkinnedObjectPreviewProgram);
- success = gObjectPreviewProgram.createShader(NULL, NULL);
+ success = gObjectPreviewProgram.createShader();
gObjectPreviewProgram.mFeatures.hasLighting = true;
gSkinnedObjectPreviewProgram.mFeatures.hasLighting = true;
}
@@ -2369,7 +2592,7 @@ bool LLViewerShaderMgr::loadShadersObject()
gPhysicsPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewPhysicsV.glsl", GL_VERTEX_SHADER));
gPhysicsPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewPhysicsF.glsl", GL_FRAGMENT_SHADER));
gPhysicsPreviewProgram.mShaderLevel = mShaderLevel[SHADER_OBJECT];
- success = gPhysicsPreviewProgram.createShader(NULL, NULL);
+ success = gPhysicsPreviewProgram.createShader();
gPhysicsPreviewProgram.mFeatures.hasLighting = false;
}
@@ -2410,7 +2633,7 @@ bool LLViewerShaderMgr::loadShadersAvatar()
gAvatarProgram.mShaderFiles.push_back(make_pair("avatar/avatarV.glsl", GL_VERTEX_SHADER));
gAvatarProgram.mShaderFiles.push_back(make_pair("avatar/avatarF.glsl", GL_FRAGMENT_SHADER));
gAvatarProgram.mShaderLevel = mShaderLevel[SHADER_AVATAR];
- success = gAvatarProgram.createShader(NULL, NULL);
+ success = gAvatarProgram.createShader();
/// Keep track of avatar levels
if (gAvatarProgram.mShaderLevel != mShaderLevel[SHADER_AVATAR])
@@ -2434,7 +2657,7 @@ bool LLViewerShaderMgr::loadShadersAvatar()
gAvatarEyeballProgram.mShaderFiles.push_back(make_pair("avatar/eyeballV.glsl", GL_VERTEX_SHADER));
gAvatarEyeballProgram.mShaderFiles.push_back(make_pair("avatar/eyeballF.glsl", GL_FRAGMENT_SHADER));
gAvatarEyeballProgram.mShaderLevel = mShaderLevel[SHADER_AVATAR];
- success = gAvatarEyeballProgram.createShader(NULL, NULL);
+ success = gAvatarEyeballProgram.createShader();
}
if( !success )
@@ -2460,7 +2683,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gHighlightProgram.mShaderFiles.push_back(make_pair("interface/highlightF.glsl", GL_FRAGMENT_SHADER));
gHighlightProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
success = make_rigged_variant(gHighlightProgram, gSkinnedHighlightProgram);
- success = success && gHighlightProgram.createShader(NULL, NULL);
+ success = success && gHighlightProgram.createShader();
}
if (success)
@@ -2470,7 +2693,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gHighlightNormalProgram.mShaderFiles.push_back(make_pair("interface/highlightNormV.glsl", GL_VERTEX_SHADER));
gHighlightNormalProgram.mShaderFiles.push_back(make_pair("interface/highlightF.glsl", GL_FRAGMENT_SHADER));
gHighlightNormalProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gHighlightNormalProgram.createShader(NULL, NULL);
+ success = gHighlightNormalProgram.createShader();
}
if (success)
@@ -2480,7 +2703,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gHighlightSpecularProgram.mShaderFiles.push_back(make_pair("interface/highlightSpecV.glsl", GL_VERTEX_SHADER));
gHighlightSpecularProgram.mShaderFiles.push_back(make_pair("interface/highlightF.glsl", GL_FRAGMENT_SHADER));
gHighlightSpecularProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gHighlightSpecularProgram.createShader(NULL, NULL);
+ success = gHighlightSpecularProgram.createShader();
}
if (success)
@@ -2490,7 +2713,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gUIProgram.mShaderFiles.push_back(make_pair("interface/uiV.glsl", GL_VERTEX_SHADER));
gUIProgram.mShaderFiles.push_back(make_pair("interface/uiF.glsl", GL_FRAGMENT_SHADER));
gUIProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gUIProgram.createShader(NULL, NULL);
+ success = gUIProgram.createShader();
}
if (success)
@@ -2500,7 +2723,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gPathfindingProgram.mShaderFiles.push_back(make_pair("interface/pathfindingV.glsl", GL_VERTEX_SHADER));
gPathfindingProgram.mShaderFiles.push_back(make_pair("interface/pathfindingF.glsl", GL_FRAGMENT_SHADER));
gPathfindingProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gPathfindingProgram.createShader(NULL, NULL);
+ success = gPathfindingProgram.createShader();
}
if (success)
@@ -2510,7 +2733,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gPathfindingNoNormalsProgram.mShaderFiles.push_back(make_pair("interface/pathfindingNoNormalV.glsl", GL_VERTEX_SHADER));
gPathfindingNoNormalsProgram.mShaderFiles.push_back(make_pair("interface/pathfindingF.glsl", GL_FRAGMENT_SHADER));
gPathfindingNoNormalsProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gPathfindingNoNormalsProgram.createShader(NULL, NULL);
+ success = gPathfindingNoNormalsProgram.createShader();
}
if (success)
@@ -2520,7 +2743,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gGlowCombineProgram.mShaderFiles.push_back(make_pair("interface/glowcombineV.glsl", GL_VERTEX_SHADER));
gGlowCombineProgram.mShaderFiles.push_back(make_pair("interface/glowcombineF.glsl", GL_FRAGMENT_SHADER));
gGlowCombineProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gGlowCombineProgram.createShader(NULL, NULL);
+ success = gGlowCombineProgram.createShader();
if (success)
{
gGlowCombineProgram.bind();
@@ -2537,7 +2760,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gGlowCombineFXAAProgram.mShaderFiles.push_back(make_pair("interface/glowcombineFXAAV.glsl", GL_VERTEX_SHADER));
gGlowCombineFXAAProgram.mShaderFiles.push_back(make_pair("interface/glowcombineFXAAF.glsl", GL_FRAGMENT_SHADER));
gGlowCombineFXAAProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gGlowCombineFXAAProgram.createShader(NULL, NULL);
+ success = gGlowCombineFXAAProgram.createShader();
if (success)
{
gGlowCombineFXAAProgram.bind();
@@ -2555,7 +2778,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gTwoTextureCompareProgram.mShaderFiles.push_back(make_pair("interface/twotexturecompareV.glsl", GL_VERTEX_SHADER));
gTwoTextureCompareProgram.mShaderFiles.push_back(make_pair("interface/twotexturecompareF.glsl", GL_FRAGMENT_SHADER));
gTwoTextureCompareProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gTwoTextureCompareProgram.createShader(NULL, NULL);
+ success = gTwoTextureCompareProgram.createShader();
if (success)
{
gTwoTextureCompareProgram.bind();
@@ -2572,7 +2795,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gOneTextureFilterProgram.mShaderFiles.push_back(make_pair("interface/onetexturefilterV.glsl", GL_VERTEX_SHADER));
gOneTextureFilterProgram.mShaderFiles.push_back(make_pair("interface/onetexturefilterF.glsl", GL_FRAGMENT_SHADER));
gOneTextureFilterProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gOneTextureFilterProgram.createShader(NULL, NULL);
+ success = gOneTextureFilterProgram.createShader();
if (success)
{
gOneTextureFilterProgram.bind();
@@ -2588,7 +2811,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gSolidColorProgram.mShaderFiles.push_back(make_pair("interface/solidcolorV.glsl", GL_VERTEX_SHADER));
gSolidColorProgram.mShaderFiles.push_back(make_pair("interface/solidcolorF.glsl", GL_FRAGMENT_SHADER));
gSolidColorProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gSolidColorProgram.createShader(NULL, NULL);
+ success = gSolidColorProgram.createShader();
if (success)
{
gSolidColorProgram.bind();
@@ -2605,7 +2828,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER));
gOcclusionProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
gOcclusionProgram.mRiggedVariant = &gSkinnedOcclusionProgram;
- success = gOcclusionProgram.createShader(NULL, NULL);
+ success = gOcclusionProgram.createShader();
}
if (success)
@@ -2616,7 +2839,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gSkinnedOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionSkinnedV.glsl", GL_VERTEX_SHADER));
gSkinnedOcclusionProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER));
gSkinnedOcclusionProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gSkinnedOcclusionProgram.createShader(NULL, NULL);
+ success = gSkinnedOcclusionProgram.createShader();
}
if (success)
@@ -2626,7 +2849,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gOcclusionCubeProgram.mShaderFiles.push_back(make_pair("interface/occlusionCubeV.glsl", GL_VERTEX_SHADER));
gOcclusionCubeProgram.mShaderFiles.push_back(make_pair("interface/occlusionF.glsl", GL_FRAGMENT_SHADER));
gOcclusionCubeProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gOcclusionCubeProgram.createShader(NULL, NULL);
+ success = gOcclusionCubeProgram.createShader();
}
if (success)
@@ -2638,7 +2861,34 @@ bool LLViewerShaderMgr::loadShadersInterface()
gDebugProgram.mRiggedVariant = &gSkinnedDebugProgram;
gDebugProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
success = make_rigged_variant(gDebugProgram, gSkinnedDebugProgram);
- success = success && gDebugProgram.createShader(NULL, NULL);
+ success = success && gDebugProgram.createShader();
+ }
+
+ if (success)
+ {
+ 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();
+ }
}
if (success)
@@ -2648,7 +2898,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gClipProgram.mShaderFiles.push_back(make_pair("interface/clipV.glsl", GL_VERTEX_SHADER));
gClipProgram.mShaderFiles.push_back(make_pair("interface/clipF.glsl", GL_FRAGMENT_SHADER));
gClipProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gClipProgram.createShader(NULL, NULL);
+ success = gClipProgram.createShader();
}
if (success)
@@ -2658,7 +2908,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gBenchmarkProgram.mShaderFiles.push_back(make_pair("interface/benchmarkV.glsl", GL_VERTEX_SHADER));
gBenchmarkProgram.mShaderFiles.push_back(make_pair("interface/benchmarkF.glsl", GL_FRAGMENT_SHADER));
gBenchmarkProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gBenchmarkProgram.createShader(NULL, NULL);
+ success = gBenchmarkProgram.createShader();
}
if (success)
@@ -2674,7 +2924,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gReflectionProbeDisplayProgram.mShaderFiles.push_back(make_pair("interface/reflectionprobeV.glsl", GL_VERTEX_SHADER));
gReflectionProbeDisplayProgram.mShaderFiles.push_back(make_pair("interface/reflectionprobeF.glsl", GL_FRAGMENT_SHADER));
gReflectionProbeDisplayProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gReflectionProbeDisplayProgram.createShader(NULL, NULL);
+ success = gReflectionProbeDisplayProgram.createShader();
}
if (success)
@@ -2684,7 +2934,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gCopyProgram.mShaderFiles.push_back(make_pair("interface/copyV.glsl", GL_VERTEX_SHADER));
gCopyProgram.mShaderFiles.push_back(make_pair("interface/copyF.glsl", GL_FRAGMENT_SHADER));
gCopyProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gCopyProgram.createShader(NULL, NULL);
+ success = gCopyProgram.createShader();
}
if (success)
@@ -2696,7 +2946,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gCopyDepthProgram.clearPermutations();
gCopyDepthProgram.addPermutation("COPY_DEPTH", "1");
gCopyDepthProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gCopyDepthProgram.createShader(NULL, NULL);
+ success = gCopyDepthProgram.createShader();
}
if (success)
@@ -2706,7 +2956,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gAlphaMaskProgram.mShaderFiles.push_back(make_pair("interface/alphamaskV.glsl", GL_VERTEX_SHADER));
gAlphaMaskProgram.mShaderFiles.push_back(make_pair("interface/alphamaskF.glsl", GL_FRAGMENT_SHADER));
gAlphaMaskProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gAlphaMaskProgram.createShader(NULL, NULL);
+ success = gAlphaMaskProgram.createShader();
}
if (success)
@@ -2720,7 +2970,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gReflectionMipProgram.mShaderFiles.push_back(make_pair("interface/splattexturerectV.glsl", GL_VERTEX_SHADER));
gReflectionMipProgram.mShaderFiles.push_back(make_pair("interface/reflectionmipF.glsl", GL_FRAGMENT_SHADER));
gReflectionMipProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gReflectionMipProgram.createShader(NULL, NULL);
+ success = gReflectionMipProgram.createShader();
}
if (success)
@@ -2734,7 +2984,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gGaussianProgram.mShaderFiles.push_back(make_pair("interface/splattexturerectV.glsl", GL_VERTEX_SHADER));
gGaussianProgram.mShaderFiles.push_back(make_pair("interface/gaussianF.glsl", GL_FRAGMENT_SHADER));
gGaussianProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gGaussianProgram.createShader(NULL, NULL);
+ success = gGaussianProgram.createShader();
}
if (success && gGLManager.mHasCubeMapArray)
@@ -2744,7 +2994,20 @@ 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];
- success = gRadianceGenProgram.createShader(NULL, NULL);
+ gRadianceGenProgram.addPermutation("PROBE_FILTER_SAMPLES", "32");
+ success = gRadianceGenProgram.createShader();
+ }
+
+ 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();
}
if (success && gGLManager.mHasCubeMapArray)
@@ -2754,7 +3017,7 @@ bool LLViewerShaderMgr::loadShadersInterface()
gIrradianceGenProgram.mShaderFiles.push_back(make_pair("interface/irradianceGenV.glsl", GL_VERTEX_SHADER));
gIrradianceGenProgram.mShaderFiles.push_back(make_pair("interface/irradianceGenF.glsl", GL_FRAGMENT_SHADER));
gIrradianceGenProgram.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
- success = gIrradianceGenProgram.createShader(NULL, NULL);
+ success = gIrradianceGenProgram.createShader();
}
if( !success )
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index c3b9ddbff1..60ce8c430b 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,23 @@ extern LLGLSLShader gPBRGlowProgram;
extern LLGLSLShader gDeferredPBROpaqueProgram;
extern LLGLSLShader gDeferredPBRAlphaProgram;
extern LLGLSLShader gHUDPBRAlphaProgram;
+
+// GLTF shaders
+extern LLGLSLShader gGLTFPBRMetallicRoughnessProgram;
+
+// Encodes detail level for dropping textures, in accordance with the GLTF spec where possible
+// 0 is highest detail, -1 drops emissive, etc
+// Dropping metallic roughness is off-spec - Reserve for potato machines as needed
+// 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 e3f9b21342..c1062b9a01 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -335,6 +335,18 @@ LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const s
return gTextureList.getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
}
+//static
+LLImageRaw* LLViewerTextureManager::getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
+{
+ return gTextureList.getRawImageFromMemory(data, size, mimetype);
+}
+
+//static
+LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype)
+{
+ return gTextureList.getImageFromMemory(data, size, mimetype);
+}
+
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host)
{
return gTextureList.getImageFromHost(image_id, f_type, host);
@@ -1658,7 +1670,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
{
@@ -1671,7 +1683,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 || (S32)mFullWidth <= mKnownDrawWidth || (S32)mFullHeight <= mKnownDrawHeight)
{
if (mFullWidth > desired_size || mFullHeight > desired_size)
@@ -3041,6 +3053,14 @@ void LLViewerLODTexture::processTextureStats()
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;
@@ -3050,7 +3070,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)
{
@@ -3095,7 +3115,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 063e4255d0..dc9182bf1b 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -397,7 +397,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() ;
@@ -419,6 +418,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) ;
@@ -440,8 +440,6 @@ private:
bool mForceCallbackFetch;
protected:
- std::string mLocalFileName;
-
S32 mOrigWidth;
S32 mOrigHeight;
@@ -692,6 +690,14 @@ public:
static LLViewerFetchedTexture* getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host) ;
+ // decode a given image data according to given mime type
+ // WARNING: caller is responsible for deleting the returned raw image
+ static LLImageRaw* getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
+
+ // decode given image data according to given mime type
+ // WARNING: caller is responsible for deleting the returned image
+ static LLViewerFetchedTexture* getFetchedTextureFromMemory(const U8* data, U32 size, std::string_view mimetype);
+
static void init() ;
static void cleanup() ;
};
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 21572ef620..b90c1868fc 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -360,7 +360,7 @@ void LLViewerTextureList::shutdown()
mImageList.clear();
- mInitialized = false; //prevent loading textures again.
+ mInitialized = false ; //prevent loading textures again.
}
void LLViewerTextureList::dump()
@@ -518,6 +518,39 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string&
return imagep;
}
+LLImageRaw* LLViewerTextureList::getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
+{
+ LLPointer<LLImageFormatted> image = LLImageFormatted::loadFromMemory(data, size, mimetype);
+
+ if (image)
+ {
+ LLImageRaw* raw_image = new LLImageRaw();
+ image->decode(raw_image, 0.f);
+ return raw_image;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+LLViewerFetchedTexture* LLViewerTextureList::getImageFromMemory(const U8* data, U32 size, std::string_view mimetype)
+{
+ LLPointer<LLImageRaw> raw_image = getRawImageFromMemory(data, size, mimetype);
+ if (raw_image.notNull())
+ {
+ LLViewerFetchedTexture* imagep = new LLViewerFetchedTexture(raw_image, FTT_LOCAL_FILE, true);
+ addImage(imagep, TEX_LIST_STANDARD);
+
+ imagep->dontDiscard();
+ imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_PREVIEW);
+ return imagep;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id,
FTType f_type,
@@ -631,8 +664,8 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id,
imagep->forceActive() ;
}
- mFastCacheList.insert(imagep);
- imagep->setInFastCacheList(true);
+ mFastCacheList.insert(imagep);
+ imagep->setInFastCacheList(true);
return imagep ;
}
@@ -1310,63 +1343,63 @@ bool LLViewerTextureList::createUploadFile(const std::string& filename,
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
try
{
- // Load the image
- LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
- if (image.isNull())
- {
- LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
- return false;
- }
- if (!image->load(filename))
- {
- image->setLastError("Couldn't load the image to be uploaded.");
- return false;
- }
- // Decompress or expand it in a raw image structure
- LLPointer<LLImageRaw> raw_image = new LLImageRaw;
- if (!image->decode(raw_image, 0.0f))
- {
- image->setLastError("Couldn't decode the image to be uploaded.");
- return false;
- }
- // Check the image constraints
- if ((image->getComponents() != 3) && (image->getComponents() != 4))
- {
- image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
- return false;
- }
- if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
- {
- std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
- min_image_dimentions,
- min_image_dimentions,
- image->getWidth(),
- image->getHeight());
- image->setLastError(reason);
- return false;
- }
- // Convert to j2c (JPEG2000) and save the file locally
- LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
- if (compressedImage.isNull())
- {
- image->setLastError("Couldn't convert the image to jpeg2000.");
- LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
- return false;
- }
- if (!compressedImage->save(out_filename))
- {
- image->setLastError("Couldn't create the jpeg2000 image for upload.");
- LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
- return false;
- }
- // Test to see if the encode and save worked
- LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
- if (!integrity_test->loadAndValidate(out_filename))
- {
- image->setLastError("The created jpeg2000 image is corrupt.");
- LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
- return false;
- }
+ // Load the image
+ LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
+ if (image.isNull())
+ {
+ LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
+ return false;
+ }
+ if (!image->load(filename))
+ {
+ image->setLastError("Couldn't load the image to be uploaded.");
+ return false;
+ }
+ // Decompress or expand it in a raw image structure
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ if (!image->decode(raw_image, 0.0f))
+ {
+ image->setLastError("Couldn't decode the image to be uploaded.");
+ return false;
+ }
+ // Check the image constraints
+ if ((image->getComponents() != 3) && (image->getComponents() != 4))
+ {
+ image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
+ return false;
+ }
+ if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
+ {
+ std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
+ min_image_dimentions,
+ min_image_dimentions,
+ image->getWidth(),
+ image->getHeight());
+ image->setLastError(reason);
+ return false;
+ }
+ // Convert to j2c (JPEG2000) and save the file locally
+ LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
+ if (compressedImage.isNull())
+ {
+ image->setLastError("Couldn't convert the image to jpeg2000.");
+ LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
+ return false;
+ }
+ if (!compressedImage->save(out_filename))
+ {
+ image->setLastError("Couldn't create the jpeg2000 image for upload.");
+ LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
+ return false;
+ }
+ // Test to see if the encode and save worked
+ LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
+ if (!integrity_test->loadAndValidate( out_filename ))
+ {
+ image->setLastError("The created jpeg2000 image is corrupt.");
+ LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
+ return false;
+ }
}
catch (...)
{
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 413209f50d..e4ebb7b0e8 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -192,6 +192,9 @@ private:
const LLUUID& force_id = LLUUID::null
);
+ LLImageRaw* getRawImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
+ LLViewerFetchedTexture* getImageFromMemory(const U8* data, U32 size, std::string_view mimetype);
+
LLViewerFetchedTexture* createImage(const LLUUID &image_id,
FTType f_type,
bool usemipmap = true,
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index afa430a496..70aee2dc43 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;
@@ -3380,9 +3382,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,
@@ -4375,6 +4379,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,
@@ -4468,7 +4474,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;
@@ -5349,7 +5355,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;
@@ -5380,6 +5386,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[] = {
@@ -5487,6 +5501,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;
set_current_modelview(saved_mod);
@@ -6180,8 +6199,8 @@ LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos,
void LLPickInfo::fetchResults()
{
-
S32 face_hit = -1;
+
LLVector4a intersection, normal;
LLVector4a tangent;
@@ -6204,8 +6223,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;
@@ -6351,6 +6370,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 f4682780cb..5cd6aacdb2 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;
@@ -375,7 +377,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
@@ -423,6 +426,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 efae321c85..c509d656e1 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llvlcomposition.cpp
* @brief Viewer-side representation of a composition layer...
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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$
*/
@@ -28,47 +28,417 @@
#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;
+
+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()
+{
+ for (S32 i = 0; i < ASSET_COUNT; ++i)
+ {
+ mMaterialTexturesSet[i] = false;
+ }
+}
+
+LLTerrainMaterials::~LLTerrainMaterials()
+{
+ unboost();
+}
+
+void LLTerrainMaterials::apply(const LLModifyRegion& other)
+{
+ for (S32 i = 0; i < LLTerrainMaterials::ASSET_COUNT; ++i)
+ {
+ const LLGLTFMaterial* other_override = other.getMaterialOverride(i);
+ LLGLTFMaterial* material_override = other_override ? new LLGLTFMaterial(*other_override) : nullptr;
+ setMaterialOverride(i, material_override);
+ }
+}
+
+bool LLTerrainMaterials::generateMaterials()
+{
+ if (texturesReady(true, true))
+ {
+ 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);
+ mDetailRenderMaterials[asset] = nullptr;
+ mMaterialTexturesSet[asset] = false;
+}
+
+const LLGLTFMaterial* LLTerrainMaterials::getMaterialOverride(S32 asset) const
+{
+ return mDetailMaterialOverrides[asset];
+}
+
+void LLTerrainMaterials::setMaterialOverride(S32 asset, LLGLTFMaterial* mat_override)
+{
+ // Non-null overrides must be nontrivial. Otherwise, please set the override to null instead.
+ llassert(!mat_override || *mat_override != LLGLTFMaterial::sDefault);
+
+ mDetailMaterialOverrides[asset] = mat_override;
+ mDetailRenderMaterials[asset] = nullptr;
+}
+
+LLTerrainMaterials::Type LLTerrainMaterials::getMaterialType()
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ const BOOL use_textures = texturesReady(false, false) || !materialsReady(false, false);
+ 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;
+}
+
+namespace
+{
+ bool material_asset_ready(LLFetchedGLTFMaterial* mat) { return mat && mat->isLoaded(); }
+};
+
+bool LLTerrainMaterials::materialsReady(bool boost, bool strict)
+{
+ bool ready[ASSET_COUNT];
+ // *NOTE: This section may boost materials/textures. Do not early-return if ready[i] is false.
+ for (S32 i = 0; i < ASSET_COUNT; i++)
+ {
+ ready[i] = false;
+ LLPointer<LLFetchedGLTFMaterial>& mat = mDetailMaterials[i];
+ if (!material_asset_ready(mat)) { continue; }
+
+ LLPointer<LLFetchedGLTFMaterial>& render_mat = mDetailRenderMaterials[i];
+ if (!render_mat)
+ {
+ render_mat = new LLFetchedGLTFMaterial();
+ *render_mat = *mat;
+ // This render_mat is effectively already loaded, because it gets its data from mat.
+
+ LLPointer<LLGLTFMaterial>& override_mat = mDetailMaterialOverrides[i];
+ if (override_mat)
+ {
+ render_mat->applyOverride(*override_mat);
+ }
+ }
+
+ ready[i] = materialTexturesReady(render_mat, mMaterialTexturesSet[i], boost, strict);
+ }
+
+#if 1
+ 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;
+}
-F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
+// Make sure to call material_asset_ready first
+// strict = true -> all materials must be sufficiently loaded
+// strict = false -> at least one material must be loaded
+// static
+bool LLTerrainMaterials::materialTexturesReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict)
{
- // Not sure if this is the right math...
- // Take weighted average of all four points (bilinear interpolation)
- F32 result;
+ llassert(mat);
- 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;
+ // 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]);
+ }
- return result;
+ // *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;
}
+// 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 (!material_asset_ready(mat)) { return false; }
+
+ return materialTexturesReady(mat, textures_set, boost, strict);
+}
+
+// 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),
mParamsReady(false)
{
- mSurfacep = surfacep;
-
// 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 +446,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 +460,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)
{
@@ -117,7 +471,7 @@ bool LLVLComposition::generateHeights(const F32 x, const F32 y,
llassert(mSurfacep);
- if (!mSurfacep || !mSurfacep->getRegion())
+ if (!mSurfacep || !mSurfacep->getRegion())
{
// We don't always have the region yet here....
return false;
@@ -149,10 +503,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);
@@ -189,7 +539,7 @@ bool LLVLComposition::generateHeights(const F32 x, const F32 y,
vec[1] = (F32)(origin_global.mdV[VY]+location.mV[VY])*xyScaleInv;
vec[2] = height*zScaleInv;
//
- // Choose material value by adding to the exact height a random value
+ // Choose material value by adding to the exact height a random value
//
vec1[0] = vec[0]*(0.2222222222f);
vec1[1] = vec[1]*(0.2222222222f);
@@ -199,7 +549,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 +559,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++)
+ return LLTerrainMaterials::generateMaterials();
+}
+
+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 (mDetailTextures[i]->getDiscardLevel() < 0)
+ if (!tex)
{
- mDetailTextures[i]->setBoostLevel(LLGLTexture::BOOST_TERRAIN); // in case we are at low detail
- mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE);
- return false;
+ if (!emissive)
+ {
+ prepare_fallback_image(raw_image);
+ }
+ else
+ {
+ llassert(!raw_image);
+ raw_image = nullptr;
+ }
+ return true;
}
- if ((mDetailTextures[i]->getDiscardLevel() != 0 &&
- (mDetailTextures[i]->getWidth() < BASE_SIZE ||
- mDetailTextures[i]->getHeight() < BASE_SIZE)))
+ if (raw_image)
{
- S32 width = mDetailTextures[i]->getFullWidth();
- S32 height = mDetailTextures[i]->getFullHeight();
- S32 min_dim = llmin(width, height);
- S32 ddiscard = 0;
+ // 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;
}
- 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;
-}
+ 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::generateTexture(const F32 x, const F32 y,
+bool LLVLComposition::generateMinimapTileLand(const F32 x, const F32 y,
const F32 width, const F32 height)
{
LL_PROFILE_ZONE_SCOPED
@@ -259,8 +682,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 +689,139 @@ 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
+ 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 < 4; i++)
+ 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)
+ 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)
{
- ddiscard++;
- min_dim /= 2;
+ tex = mDetailTextures[i];
+ tex_emissive = nullptr;
+ has_base_color_factor = false;
+ has_emissive_factor = false;
+ has_alpha = false;
+ llassert(tex);
+ }
+ else
+ {
+ LLPointer<LLFetchedGLTFMaterial>& mat = mDetailRenderMaterials[i];
+ tex = mat->mBaseColorTexture;
+ tex_emissive = mat->mEmissiveTexture;
+ base_color_factor = LLColor3(mat->mBaseColor);
+ // *HACK: Treat alpha as black
+ base_color_factor *= (mat->mBaseColor.mV[VW]);
+ emissive_factor = mat->mEmissiveColor;
+ has_base_color_factor = (base_color_factor.mV[VX] != 1.f ||
+ base_color_factor.mV[VY] != 1.f ||
+ base_color_factor.mV[VZ] != 1.f);
+ has_emissive_factor = (emissive_factor.mV[VX] != 1.f ||
+ emissive_factor.mV[VY] != 1.f ||
+ emissive_factor.mV[VZ] != 1.f);
+ has_alpha = mat->mAlphaMode != LLGLTFMaterial::ALPHA_MODE_OPAQUE;
}
- bool delete_raw = (mDetailTextures[i]->reloadRawImage(ddiscard) != NULL) ;
- if(mDetailTextures[i]->getRawImageLevel() != ddiscard)//raw iamge is not ready, will enter here again later.
+ 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)
{
- if (mDetailTextures[i]->getFetchPriority() <= 0.0f && !mDetailTextures[i]->hasSavedRawImage())
+ 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)
+ }
+ 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);
+ if (has_alpha)
{
- mDetailTextures[i]->destroyRawImage() ;
+ // 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);
}
- LL_DEBUGS("Terrain") << "cached raw data for terrain detail texture is not ready yet: " << mDetailTextures[i]->getID() << " Discard: " << ddiscard << LL_ENDL;
- return false;
+ 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
}
- mRawImages[i] = mDetailTextures[i]->getRawImage() ;
- if(delete_raw)
+ if (delete_raw_post)
{
- mDetailTextures[i]->destroyRawImage() ;
+ tex->destroyRawImage();
}
- if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE ||
- mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE ||
- mDetailTextures[i]->getComponents() != 3)
+ if (delete_raw_post_emissive)
{
- LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3);
- newraw->composite(mRawImages[i]);
- mRawImages[i] = newraw; // deletes old
+ 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 +841,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;
}
@@ -365,10 +873,10 @@ bool LLVLComposition::generateTexture(const F32 x, const F32 y,
U32 st_comps = 3;
U32 st_width = BASE_SIZE;
U32 st_height = BASE_SIZE;
-
+
if (tex_comps != st_comps)
{
- LL_WARNS("Terrain") << "Base texture comps != input texture comps" << LL_ENDL;
+ llassert(false);
return false;
}
@@ -459,29 +967,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 detail textures (will get re-boosted if rendering in high detail)
+ for (S32 i = 0; i < ASSET_COUNT; 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);
+ 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 bab0e0043a..a003f74eda 100644
--- a/indra/newview/llvlcomposition.h
+++ b/indra/newview/llvlcomposition.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llvlcomposition.h
* @brief Viewer-side representation of a composition layer...
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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$
*/
@@ -28,13 +28,84 @@
#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 LLGLTFMaterial;
+class LLFetchedGLTFMaterial;
+
+class LLModifyRegion
+{
+public:
+ virtual const LLGLTFMaterial* getMaterialOverride(S32 asset) const = 0;
+};
+
+class LLTerrainMaterials : public LLModifyRegion
+{
+public:
+ friend class LLDrawPoolTerrain;
+
+ LLTerrainMaterials();
+ virtual ~LLTerrainMaterials();
+
+ void apply(const LLModifyRegion& other);
+
+ // Heights map into textures (or materials) as 0-1 = first, 1-2 = second, etc.
+ // So we need to compress heights into this range.
+ static const S32 ASSET_COUNT = 4;
+
+ enum class Type
+ {
+ TEXTURE,
+ PBR,
+ COUNT
+ };
+
+ bool generateMaterials();
+
+ void boost();
+
+ virtual LLUUID getDetailAssetID(S32 asset);
+ virtual void setDetailAssetID(S32 asset, const LLUUID& id);
+ const LLGLTFMaterial* getMaterialOverride(S32 asset) const override;
+ virtual void setMaterialOverride(S32 asset, LLGLTFMaterial* mat_override);
+ Type getMaterialType();
+ bool texturesReady(bool boost, bool strict);
+ // 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);
+ // *NOTE: Prefer calling materialReady if mat is known to be LLFetchedGLTFMaterial
+ static bool materialTexturesReady(LLPointer<LLFetchedGLTFMaterial>& mat, bool& textures_set, bool boost, bool strict);
+
+ LLPointer<LLViewerFetchedTexture> mDetailTextures[ASSET_COUNT];
+ LLPointer<LLFetchedGLTFMaterial> mDetailMaterials[ASSET_COUNT];
+ LLPointer<LLGLTFMaterial> mDetailMaterialOverrides[ASSET_COUNT];
+ LLPointer<LLFetchedGLTFMaterial> mDetailRenderMaterials[ASSET_COUNT];
+ bool mMaterialTexturesSet[ASSET_COUNT];
+};
+
+// 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 +115,8 @@ 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);
+ bool generateTexture(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 +127,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 +139,23 @@ public:
friend class LLDrawPoolTerrain;
void setParamsReady() { mParamsReady = true; }
bool getParamsReady() const { return mParamsReady; }
+
protected:
- bool mParamsReady;
+ 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 37d7e0d2e4..2f7dbc46a9 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -4752,7 +4752,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);
}
@@ -5304,9 +5304,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);
@@ -6625,7 +6622,7 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LL
LL_DEBUGS("AnimatedObjects") << "adding attachment overrides for " << mesh_id
<< " to root object " << root_object->getID() << LL_ENDL;
}
- bool fullRig = jointCnt >= JOINT_COUNT_REQUIRED_FOR_FULLRIG;
+ bool fullRig = jointCnt>=JOINT_COUNT_REQUIRED_FOR_FULLRIG;
if ( fullRig && !mesh_overrides_loaded )
{
for (unsigned int i = 0; i < jointCnt; ++i)
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 2e347920f5..e4a7b53d9f 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 a90fb07706..5f68051d10 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 ;
}
@@ -521,8 +527,19 @@ F32 LLVOCacheEntry::getSquaredPixelThreshold(bool is_front)
return projection_threshold;
}
+extern bool gCubeSnapshot;
+
bool LLVOCacheEntry::isAnyVisible(const LLVector4a& camera_origin, const LLVector4a& local_camera_origin, F32 dist_threshold)
{
+#if 0
+ // this is ill-conceived and should be removed pending QA
+ // In the name of saving memory, we evict objects that are still within view distance from memory
+ // This results in constant paging of objects in and out of memory, leading to poor performance
+ // and many unacceptable visual glitches when rotating the camera
+
+ // Honestly, the entire VOCache partition system needs to be removed since it doubles the overhead of
+ // the spatial partition system and is redundant to the object cache, but this is a start
+ // - davep 2024.06.07
LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*)getGroup();
if(!group)
{
@@ -559,6 +576,9 @@ bool LLVOCacheEntry::isAnyVisible(const LLVector4a& camera_origin, const LLVecto
}
return vis;
+#else
+ return true;
+#endif
}
void LLVOCacheEntry::calcSceneContribution(const LLVector4a& camera_origin, bool needs_update, U32 last_update, F32 max_dist)
@@ -1261,6 +1281,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 +1361,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.
}
@@ -1478,12 +1517,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 +1532,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 +1555,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 +1583,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 +1613,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 +1687,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);
+ 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 = static_cast<U32>(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 +1781,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 +1823,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 +1835,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 +1846,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 +1865,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 +1902,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
}
-
- auto 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 a260967bf9..9679a20ae0 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/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index da4df226ee..3c2a0ef6a3 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -6444,30 +6444,36 @@ void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::ESta
<< ", proximal is " << inSpatialChannel()
<< LL_ENDL;
- for (status_observer_set_t::iterator it = mStatusObservers.begin();
- it != mStatusObservers.end();
- )
- {
- LLVoiceClientStatusObserver* observer = *it;
- observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
- // In case onError() deleted an entry.
- it = mStatusObservers.upper_bound(observer);
- }
+ // this function is called from a coroutine, shuttle application hook back to main loop
+ auto work = [=]()
+ {
+ for (status_observer_set_t::iterator it = mStatusObservers.begin();
+ it != mStatusObservers.end();
+ )
+ {
+ LLVoiceClientStatusObserver* observer = *it;
+ observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
+ // In case onError() deleted an entry.
+ it = mStatusObservers.upper_bound(observer);
+ }
- // skipped to avoid speak button blinking
- if ( status != LLVoiceClientStatusObserver::STATUS_JOINING
- && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
- && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED)
- {
- bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
+ // skipped to avoid speak button blinking
+ if (status != LLVoiceClientStatusObserver::STATUS_JOINING
+ && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL
+ && status != LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED)
+ {
+ bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
- gAgent.setVoiceConnected(voice_status);
+ gAgent.setVoiceConnected(voice_status);
- if (voice_status)
- {
- LLFirstUse::speak(true);
- }
- }
+ if (voice_status)
+ {
+ LLFirstUse::speak(true);
+ }
+ }
+ };
+
+ LLAppViewer::instance()->postToMainCoro(work);
}
void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer)
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index f143e9c759..56742751e8 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,39 +243,41 @@ 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)
+ if (!facep)
{
- U32 index_offset = facep->getGeomIndex();
+ return;
+ }
+
+ U32 index_offset = facep->getGeomIndex();
- updateMainGeometry(facep,
+ 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);
- updateNorthGeometry(facep,
- verticesp,
- normalsp,
- texCoords0p,
- texCoords1p,
- indicesp,
- index_offset);
- updateEastGeometry(facep,
- verticesp,
- normalsp,
- texCoords0p,
- texCoords1p,
- indicesp,
- index_offset);
- }
}
void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
@@ -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,34 +1036,56 @@ 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));
+ 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;
- for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
{
- LLFace* facep = *i;
+ 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);
- facep->setIndicesIndex(indices_index);
- facep->setGeomIndex(index_offset);
- facep->setVertexBuffer(buffer);
+ LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
+ patchp->getTerrainGeometry(vertices, normals, texcoords, texcoords2, indices);
- LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
- patchp->getGeometry(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;
- indices_index += facep->getIndicesCount();
- index_offset += facep->getGeomCount();
+ gen_terrain_tangents(index_offset, indices_index, vertices, normals, tangents, texcoords, indices);
}
buffer->unmapBuffer();
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index 9e24c33774..2780b97a97 100644
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -63,7 +63,7 @@ public:
/*virtual*/ bool updateGeometry(LLDrawable *drawable);
/*virtual*/ bool updateLOD();
/*virtual*/ void updateFaceSize(S32 idx);
- void getGeometry(LLStrider<LLVector3> &verticesp,
+ void getTerrainGeometry(LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index b5cda6019b..de62256134 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -89,6 +89,7 @@
#include "llsculptidsize.h"
#include "llavatarappearancedefines.h"
#include "llgltfmateriallist.h"
+#include "gltfscenemanager.h"
const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
const F32 FORCE_CULL_AREA = 8.f;
@@ -296,6 +297,11 @@ void LLVOVolume::markDead()
{
mLightTexture->removeVolume(LLRender::LIGHT_TEX, this);
}
+
+ if (mIsHeroProbe)
+ {
+ gPipeline.mHeroProbeManager.unregisterViewerObject(this);
+ }
}
LLViewerObject::markDead();
@@ -1128,6 +1134,11 @@ bool LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
}
}
+ if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_GLTF)
+ { // notify GLTFSceneManager about new GLTF object
+ LL::GLTFSceneManager::instance().addGLTFObject(this, volume_params.getSculptID());
+ }
+
return true;
}
else if (NO_LOD == lod)
@@ -1402,6 +1413,12 @@ bool LLVOVolume::calcLOD()
return false;
}
+ if (mGLTFAsset != nullptr)
+ {
+ // do not calculate LOD for GLTF objects
+ return false;
+ }
+
S32 cur_detail = 0;
F32 radius;
@@ -3410,6 +3427,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);
@@ -3458,6 +3498,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)
@@ -4378,14 +4430,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);
+ }
}
}
@@ -5567,7 +5635,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
LLVOVolume* vobj = drawablep->getVOVolume();
- if (!vobj || vobj->isDead())
+ if (!vobj || vobj->isDead() || vobj->mGLTFAsset)
{
continue;
}
@@ -6178,19 +6246,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 83057573eb..a004dab8c9 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 0eabb8983e..1148e81fd5 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 4c2a321937..b265529919 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,30 @@ 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 +1070,15 @@ void LLPipeline::refreshCachedSettings()
RenderScreenSpaceReflectionAdaptiveStepMultiplier = gSavedSettings.getF32("RenderScreenSpaceReflectionAdaptiveStepMultiplier");
RenderScreenSpaceReflectionGlossySamples = gSavedSettings.getS32("RenderScreenSpaceReflectionGlossySamples");
RenderBufferVisualization = gSavedSettings.getS32("RenderBufferVisualization");
+ if (gSavedSettings.getBOOL("RenderMirrors") != 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 +1108,6 @@ void LLPipeline::releaseGLBuffers()
releaseLUTBuffers();
mWaterDis.release();
- mBake.release();
mSceneMap.release();
@@ -1118,6 +1153,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 +1192,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();
@@ -1847,6 +1885,11 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list)
{
LLDrawable::drawable_vector_t::iterator curiter = iter++;
LLDrawable *drawablep = *curiter;
+ if (!drawablep)
+ {
+ iter = moved_list.erase(curiter);
+ continue;
+ }
bool done = true;
if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE)))
{
@@ -2230,7 +2273,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)
@@ -2401,6 +2445,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))
{
@@ -3769,6 +3833,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera, bool do_occlusion)
{
//update reflection probe uniform
mReflectionMapManager.updateUniforms();
+ mHeroProbeManager.updateUniforms();
}
U32 cur_type = 0;
@@ -4470,6 +4535,8 @@ void LLPipeline::renderDebug()
}
}
+ LL::GLTFSceneManager::instance().renderDebug();
+
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
{ //render visible selected group occlusion geometry
gDebugProgram.bind();
@@ -6239,6 +6306,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
@@ -6382,6 +6451,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;
@@ -6497,6 +6585,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
@@ -6534,7 +6631,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
{
@@ -6560,7 +6657,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
{
@@ -6705,6 +6802,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)
@@ -6718,6 +6817,16 @@ void LLPipeline::generateLuminance(LLRenderTarget* src, LLRenderTarget* dst)
mGlow[1].bindTexture(0, channel);
}
+ channel = gLuminanceProgram.enableTexture(LLShaderMgr::NORMAL_MAP);
+ 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();
@@ -6728,11 +6837,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();
@@ -6749,18 +6859,31 @@ 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;
+ }
- S32 channel = gExposureProgram.enableTexture(LLShaderMgr::DEFERRED_EMISSIVE);
+ shader->bind();
+
+ 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");
@@ -6777,7 +6900,7 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
if (probe_ambiance > 0.f)
{
- F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma())*2.f;
+ F32 hdr_scale = sqrtf(LLEnvironment::instance().getCurrentSky()->getGamma()) * 2.f;
if (hdr_scale > 1.f)
{
@@ -6785,19 +6908,24 @@ void LLPipeline::generateExposure(LLRenderTarget* src, LLRenderTarget* dst) {
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);
+ 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
@@ -6834,8 +6962,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);
@@ -7152,7 +7282,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, nullptr, -1, false, false, true, true, nullptr, nullptr, nullptr, &result);
focus_point.set(result.getF32ptr());
}
@@ -7494,7 +7624,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_
gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
}
- channel = shader.enableTexture(LLShaderMgr::DEFERRED_NORMAL, deferred_target->getUsage());
+ channel = shader.enableTexture(LLShaderMgr::NORMAL_MAP, deferred_target->getUsage());
if (channel > -1)
{
deferred_target->bindTexture(2, channel, LLTexUnit::TFO_POINT); // frag_data[2]
@@ -8207,6 +8337,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);
@@ -8528,7 +8659,7 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader)
LLRenderTarget* deferred_light_target = &mRT->deferredLight;
stop_glerror();
- shader.disableTexture(LLShaderMgr::DEFERRED_NORMAL, deferred_target->getUsage());
+ shader.disableTexture(LLShaderMgr::NORMAL_MAP, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::DEFERRED_DIFFUSE, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::DEFERRED_SPECULAR, deferred_target->getUsage());
shader.disableTexture(LLShaderMgr::DEFERRED_EMISSIVE, deferred_target->getUsage());
@@ -8611,6 +8742,17 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader)
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)
{
mReflectionMapManager.setUniforms();
@@ -10824,3 +10966,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 ed5e1f5151..1d2033b4f0 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 f7e1a76b93..cc4f80a15c 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
@@ -387,21 +387,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"
@@ -409,44 +397,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"
@@ -460,7 +427,7 @@
<text
type="string"
length="1"
- follows="left|top"
+ follows="topleft"
height="16"
layout="topleft"
name="TerrainMeshDetailText"
@@ -468,19 +435,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"
@@ -499,7 +466,7 @@
top_delta="0"
left_delta="304"
width="65">
- Low
+ Low
</text>
<slider
@@ -511,7 +478,7 @@
label="Objects:"
label_width="185"
layout="topleft"
- left="420"
+ left="30"
min_val="0"
max_val="4"
name="ObjectMeshDetail"
@@ -532,7 +499,7 @@
top_delta="0"
left_delta="304"
width="65">
- Low
+ Low
</text>
<slider
@@ -543,7 +510,7 @@
label="Flexiprims:"
label_width="185"
layout="topleft"
- left="420"
+ left="30"
name="FlexibleMeshDetail"
show_text="false"
top_delta="16"
@@ -562,8 +529,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"
@@ -572,7 +559,7 @@
height="16"
layout="topleft"
name="ShadersText"
- top_delta="20"
+ top_delta="-10"
left="400"
width="128">
Shaders
@@ -586,7 +573,7 @@
layout="topleft"
left="420"
name="TransparentWater"
- top_delta="16"
+ top_delta="18"
width="300">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -607,7 +594,7 @@
max_val="128"
name="SkyMeshDetail"
show_text="false"
- top_delta="16"
+ top_delta="22"
width="260">
<slider.commit_callback
function="Pref.UpdateSliderText"
@@ -635,7 +622,7 @@
layout="topleft"
left="420"
name="UseSSAO"
- top_delta="16"
+ top_delta="22"
width="240">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -649,7 +636,7 @@
layout="topleft"
left="420"
name="UseDoF"
- top_delta="16"
+ top_delta="20"
width="240">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -664,7 +651,7 @@
left="420"
name="RenderShadowDetailText"
text_readonly_color="LabelDisabledColor"
- top_delta="16"
+ top_delta="22"
width="128">
Shadows:
</text>
@@ -698,7 +685,7 @@
layout="topleft"
left="420"
name="ScreenSpaceReflections"
- top_delta="16"
+ top_delta="24"
width="240">
<check_box.commit_callback
function="Pref.RenderOptionUpdate" />
@@ -713,7 +700,7 @@
left="420"
name="ReflectionDetailText"
text_readonly_color="LabelDisabledColor"
- top_delta="16"
+ top_delta="22"
width="128">
Reflection Detail:
</text>
@@ -749,7 +736,7 @@
left="420"
name="ReflectionProbeText"
text_readonly_color="LabelDisabledColor"
- top_delta="16"
+ top_delta="22"
width="128">
Reflection Coverage:
</text>
@@ -795,10 +782,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 25cd921052..0e7c522f74 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1657,7 +1657,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">
@@ -2076,7 +2076,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">
@@ -2808,6 +2858,70 @@ function="World.EnvPreset"
parameter="mem_leaking" />
</menu_item_call>
</menu>
+ <menu
+ create_jump_keys="true"
+ label="GLTF"
+ name="GLTF"
+ tear_off="true">
+ <menu_item_call
+ label="Open..."
+ name="Open...">
+ <menu_item_call.on_enable
+ function="EnableGLTF"/>
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFOpen" />
+ </menu_item_call>
+ <menu_item_call
+ label="Save As..."
+ name="Save As...">
+ <menu_item_call.on_enable
+ function="EnableGLTFSaveAs"/>
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFSaveAs" />
+ </menu_item_call>
+ <menu_item_call
+ label="Upload..."
+ name="Upload...">
+ <menu_item_call.on_enable
+ function="EnableGLTFUpload"/>
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFUpload" />
+ </menu_item_call>
+ </menu>
+ <menu
+ create_jump_keys="true"
+ label="Video"
+ name="Video"
+ tear_off="true">
+ <menu_item_call
+ label="1080x1920"
+ name="1080x1920">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="1080x1920"/>
+ </menu_item_call>
+ <menu_item_call
+ label="1920x1080"
+ name="1920x1080">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="1920x1080"/>
+ </menu_item_call>
+ <menu_item_call
+ label="1280x720"
+ name="1280x720">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="1280x720"/>
+ </menu_item_call>
+ <menu_item_call
+ label="720x1280"
+ name="720x1280">
+ <menu_item_call.on_click
+ function="Advanced.ClickResizeWindow"
+ parameter="720x1280"/>
+ </menu_item_call>
+ </menu>
<menu
create_jump_keys="true"
label="Render Tests"
@@ -2854,6 +2968,12 @@ 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>
<menu
create_jump_keys="true"
@@ -2891,6 +3011,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
@@ -3333,6 +3463,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 bab0c5c72a..1584de6880 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -4011,10 +4011,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>
@@ -4022,10 +4051,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
@@ -9306,6 +9380,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">
@@ -12375,5 +12458,71 @@ are wearing now.
name="okignore"
yestext="OK"/>
</notification>
-
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFOpenSelection"
+ 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>
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFLoadFailed"
+ type="alert">
+ Failed to load GLTF file. See log for details.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFSaveFailed"
+ type="alert">
+ Failed to save GLTF file. See log for details.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFSaveSelection"
+ type="alert">
+ You must select an object that has a GLTF asset associated with it.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFUploadSelection"
+ type="alert">
+ You must select an object that has local-only GLTF asset associated with it.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="GLTFUploadInProgress"
+ type="alert">
+ Upload is currently in progress. Please try again later.
+ <tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
+ </notification>
+
</notifications>
diff --git a/indra/newview/skins/default/xui/en/panel_region_terrain.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/en/panel_region_terrain_texture_transform.xml b/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml
new file mode 100644
index 0000000000..cbcbe418cd
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_region_terrain_texture_transform.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<panel
+ border="true"
+ follows="top|left"
+ height="460"
+ help_topic="panel_region_terrain_tab"
+ label="Terrain"
+ layout="topleft"
+ left="0"
+ name="Terrain"
+ top="320"
+ width="480">
+ <text
+ follows="left|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="region_text_lbl"
+ top="10"
+ width="100">
+ Region:
+ </text>
+ <text
+ follows="left|top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ left_delta="50"
+ name="region_text"
+ top_delta="0"
+ width="400">
+ unknown
+ </text>
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="detail_texture_text"
+ left="10"
+ top="35"
+ width="170">
+ Terrain Textures
+ </text>
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="detail_material_text"
+ left="10"
+ top="35"
+ width="170">
+ Terrain Materials
+ </text>
+ <check_box
+ height="20"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ layout="topleft"
+ top_delta="1"
+ left_delta="180"
+ label="PBR Metallic Roughness"
+ name="terrain_material_type"
+ tool_tip="If checked, use PBR Metallic Roughness materials for terrain. Otherwise, use textures."
+ left_pad="2"
+ width="200" />
+ <texture_picker
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left="10"
+ name="texture_detail_0"
+ default_image_id="0bc58228-74a0-7e83-89bc-5c23464bcec5"
+ top_delta="30"
+ width="100" />
+ <texture_picker
+ follows="top"
+ height="121"
+ layout="topleft"
+ left_pad="10"
+ name="texture_detail_1"
+ default_image_id="63338ede-0037-c4fd-855b-015d77112fc8"
+ top_delta="0"
+ width="100" />
+ <texture_picker
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left_pad="10"
+ name="texture_detail_2"
+ default_image_id="303cd381-8560-7579-23f1-f0a880799740"
+ top_delta="0"
+ width="100" />
+ <texture_picker
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left_pad="10"
+ name="texture_detail_3"
+ default_image_id="53a2f406-4895-1d13-d541-d2e3b86bc19c"
+ top_delta="0"
+ width="100" />
+ <texture_picker
+ visible="false"
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left="10"
+ name="material_detail_0"
+ pick_type="material"
+ default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a"
+ top_delta="0"
+ width="100" />
+ <texture_picker
+ visible="false"
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left_pad="10"
+ name="material_detail_1"
+ pick_type="material"
+ default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a"
+ top_delta="0"
+ width="100" />
+ <texture_picker
+ visible="false"
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left_pad="10"
+ name="material_detail_2"
+ pick_type="material"
+ default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a"
+ top_delta="0"
+ width="100" />
+ <texture_picker
+ visible="false"
+ follows="left|top"
+ height="121"
+ layout="topleft"
+ left_pad="10"
+ name="material_detail_3"
+ pick_type="material"
+ default_image_id="968cbad0-4dad-d64e-71b5-72bf13ad051a"
+ top_delta="0"
+ width="100" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="height_text_lbl"
+ top_delta="104"
+ width="65">
+ 1 (Low)
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left_pad="45"
+ name="height_text_lbl2"
+ top_delta="0"
+ width="100">
+ 2
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left_pad="10"
+ name="height_text_lbl3"
+ top_delta="0"
+ width="100">
+ 3
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left_pad="10"
+ name="height_text_lbl4"
+ top_delta="0"
+ width="100">
+ 4 (High)
+ </text>
+ <layout_stack name="terrain_features_stack"
+ width="477"
+ height="230"
+ follows="all"
+ animate="false"
+ left="0"
+ top_delta="22"
+ orientation="vertical">
+ <layout_panel name="frame_settings_terrain"
+ auto_resize="true"
+ user_resize="false"
+ height="230"
+ width="467"
+ min_height="0"
+ visible="true">
+ <tab_container
+ follows="all"
+ halign="left"
+ height="230"
+ visible="true"
+ layout="topleft"
+ left="0"
+ name="terrain_tabs"
+ tab_position="top"
+ tab_width="100"
+ tab_padding_right="3"
+ top_pad="0"
+ width="700">
+ <panel
+ border="true"
+ class="panel_settings_terrain_elevation"
+ filename="panel_settings_terrain_elevation.xml"
+ label="Elevation"
+ layout="topleft"
+ left_delta="0"
+ top_pad="5"
+ name="terrain_elevation_panel" />
+ <panel
+ border="true"
+ class="panel_settings_terrain_transform"
+ filename="panel_settings_terrain_transform.xml"
+ label="Transforms"
+ layout="topleft"
+ left_delta="0"
+ top_pad="5"
+ name="terrain_transform_panel" />
+ </tab_container>
+ </layout_panel>
+ </layout_stack>
+ <button
+ enabled="true"
+ follows="left|top"
+ height="20"
+ label="Apply"
+ layout="topleft"
+ left="353"
+ name="apply_btn"
+ top_delta="290"
+ width="100" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml b/indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml
new file mode 100644
index 0000000000..89443290ce
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_settings_terrain_elevation.xml
@@ -0,0 +1,307 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<panel
+ border="true"
+ follows="all"
+ label="Elevation"
+ layout="topleft"
+ left="0"
+ name="panel_settings_terrain_elevation"
+ top="0">
+ <spinner
+ follows="left|top"
+ height="20"
+ label="Water Height"
+ label_width="120"
+ layout="topleft"
+ left="15"
+ max_val="100"
+ name="water_height_spin"
+ top_delta="18"
+ width="180" />
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.2"
+ label="Terrain Raise Limit"
+ label_width="120"
+ layout="topleft"
+ left="240"
+ max_val="100"
+ name="terrain_raise_spin"
+ top_delta="0"
+ width="180" />
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.2"
+ label="Terrain Lower Limit"
+ label_width="120"
+ layout="topleft"
+ left="240"
+ max_val="0"
+ min_val="-100"
+ name="terrain_lower_spin"
+ top_delta="20"
+ width="180" />
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="60"
+ layout="topleft"
+ left="8"
+ top_delta="-30"
+ width="460" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="height_text_lbl5"
+ top_delta="74"
+ width="300">
+ Texture Elevation Ranges
+ </text>
+ <text
+ visible="false"
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="height_text_lbl5_material"
+ top_delta="0"
+ width="300">
+ Material Elevation Ranges
+ </text>
+ <text
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="height_text_lbl10"
+ top_delta="30"
+ width="200"
+ word_wrap="true">
+ These values represent the blend range for the textures above.
+ </text>
+ <text
+ visible="false"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="10"
+ name="height_text_lbl10_material"
+ top_delta="0"
+ width="200"
+ word_wrap="true">
+ These values represent the blend range for the materials above.
+ </text>
+ <text
+ follows="left|top"
+ height="60"
+ layout="topleft"
+ left_delta="0"
+ name="height_text_lbl11"
+ top_delta="32"
+ width="200"
+ word_wrap="true">
+ Measured in meters, the LOW value is the MAXIMUM height of Texture #1, and the HIGH value is the MINIMUM height of Texture #4.
+ </text>
+ <text
+ visible="false"
+ follows="left|top"
+ height="60"
+ layout="topleft"
+ left_delta="0"
+ name="height_text_lbl11_material"
+ top_delta="0"
+ width="200"
+ word_wrap="true">
+ Measured in meters, the LOW value is the MAXIMUM height of Material #1, and the HIGH value is the MINIMUM height of Material #4.
+ </text>
+ <text
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="270"
+ name="height_text_lbl6"
+ top_delta="-62"
+ width="100">
+ Northwest
+ </text>
+ <text
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left_pad="10"
+ name="height_text_lbl7"
+ top_delta="0"
+ width="100">
+ Northeast
+ </text>
+<!-- northwest low-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="Low"
+ label_width="37"
+ layout="topleft"
+ left="230"
+ max_val="500"
+ min_val="-500"
+ name="height_start_spin_1"
+ top_delta="15"
+ width="100" />
+<!-- northeast low-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="Low"
+ label_width="37"
+ layout="topleft"
+ left_pad="10"
+ max_val="500"
+ min_val="-500"
+ name="height_start_spin_3"
+ top_delta="0"
+ width="100" />
+<!-- northwest high-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="High"
+ label_width="37"
+ layout="topleft"
+ left="230"
+ max_val="500"
+ min_val="-500"
+ name="height_range_spin_1"
+ top_delta="20"
+ width="100" />
+<!-- northeast high-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="High"
+ label_width="37"
+ layout="topleft"
+ left_pad="10"
+ max_val="500"
+ min_val="-500"
+ name="height_range_spin_3"
+ top_delta="0"
+ width="100" />
+ <text
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="270"
+ name="height_text_lbl8"
+ top_pad="10"
+ width="100">
+ Southwest
+ </text>
+ <text
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left_pad="10"
+ name="height_text_lbl9"
+ top_delta="0"
+ width="100">
+ Southeast
+ </text>
+<!-- southwest low-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="Low"
+ label_width="37"
+ layout="topleft"
+ left="230"
+ max_val="500"
+ min_val="-500"
+ name="height_start_spin_0"
+ top_delta="15"
+ width="100" />
+<!-- southeast low-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="Low"
+ label_width="37"
+ layout="topleft"
+ left_pad="10"
+ max_val="500"
+ min_val="-500"
+ name="height_start_spin_2"
+ top_delta="0"
+ width="100" />
+<!--southwest high-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="High"
+ label_width="37"
+ layout="topleft"
+ left="230"
+ max_val="500"
+ min_val="-500"
+ name="height_range_spin_0"
+ top_delta="20"
+ width="100" />
+<!-- southeast high-->
+ <spinner
+ follows="left|top"
+ height="20"
+ increment="0.5"
+ label="High"
+ label_width="37"
+ layout="topleft"
+ left_pad="10"
+ max_val="500"
+ min_val="-500"
+ name="height_range_spin_2"
+ top_delta="0"
+ width="100" />
+<!-- Terrain Download/Upload/Bake buttons -->
+ <button
+ follows="left|top"
+ height="20"
+ label="Download RAW terrain..."
+ layout="topleft"
+ left="10"
+ name="download_raw_btn"
+ tool_tip="Available only to estate owners, not managers"
+ top_delta="40"
+ width="160" />
+ <button
+ follows="left|top"
+ height="20"
+ label="Upload RAW terrain..."
+ layout="topleft"
+ left_pad="10"
+ top_delta="0"
+ name="upload_raw_btn"
+ tool_tip="Available only to estate owners, not managers"
+ width="160" />
+ <button
+ follows="left|top"
+ height="20"
+ label="Bake Terrain"
+ layout="topleft"
+ left_pad="10"
+ name="bake_terrain_btn"
+ tool_tip="Set current terrain as mid-point for raise/lower limits"
+ width="100" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml b/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml
new file mode 100644
index 0000000000..7052622813
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_settings_terrain_transform.xml
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<panel
+ border="true"
+ follows="all"
+ label="Samping"
+ layout="topleft"
+ left="0"
+ name="panel_settings_terrain_transform"
+ top="0">
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain0ScaleU_label"
+ left="10"
+ top_pad="3"
+ width="170">
+ Scale u
+ </text>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="-2"
+ name="terrain0ScaleU_horizontal"
+ width="430" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left="10"
+ min_val="-100"
+ max_val="100"
+ name="terrain0ScaleU"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain1ScaleU"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain2ScaleU"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain3ScaleU"
+ width="64" />
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain0ScaleV_label"
+ left="10"
+ top_pad="3"
+ width="170">
+ Scale v
+ </text>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="-2"
+ name="terrain0ScaleV_horizontal"
+ width="430" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale v"
+ label_width="0"
+ layout="topleft"
+ left="10"
+ min_val="-100"
+ max_val="100"
+ name="terrain0ScaleV"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain1ScaleV"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain2ScaleV"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="1"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain3ScaleV"
+ width="64" />
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain0Rotation_label"
+ left="10"
+ top_pad="3"
+ width="170">
+ Rotation
+ </text>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="-2"
+ name="terrain0Rotation_horizontal"
+ width="430" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Rotation"
+ label_width="0"
+ layout="topleft"
+ left="10"
+ min_val="-360"
+ max_val="360"
+ name="terrain0Rotation"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain1Rotation"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain2Rotation"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain3Rotation"
+ width="64" />
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain0OffsetU_label"
+ left="10"
+ top_pad="3"
+ width="170">
+ Offset y
+ </text>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="-2"
+ name="terrain0OffsetU_horizontal"
+ width="430" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Offset u"
+ label_width="0"
+ layout="topleft"
+ left="10"
+ min_val="-999"
+ max_val="999"
+ name="terrain0OffsetU"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain1OffsetU"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain2OffsetU"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain3OffsetU"
+ width="64" />
+ <text
+ type="string"
+ length="1"
+ halign="left"
+ valign="center"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ name="terrain0OffsetV_label"
+ left="10"
+ top_pad="3"
+ width="170">
+ Offset v
+ </text>
+ <view_border
+ bevel_style="none"
+ follows="top|left"
+ height="0"
+ layout="topleft"
+ left="8"
+ top_pad="-2"
+ name="terrain0OffsetV_horizontal"
+ width="430" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Offset v"
+ label_width="0"
+ layout="topleft"
+ left="10"
+ min_val="-999"
+ max_val="999"
+ name="terrain0OffsetV"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain1OffsetV"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain2OffsetV"
+ width="64" />
+ <spinner
+ follows="left|top"
+ height="19"
+ initial_value="0"
+ label="Scale u"
+ label_width="0"
+ layout="topleft"
+ left_delta="110"
+ min_val="-100"
+ max_val="100"
+ name="terrain3OffsetV"
+ width="64" />
+</panel>
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(低)