summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCosmic Linden <cosmic@lindenlab.com>2024-07-11 15:54:24 -0700
committerCosmic Linden <cosmic@lindenlab.com>2024-08-06 16:28:47 -0700
commit6aa9110b2ae346da1a396500cae1b41e8e2d5b75 (patch)
tree93c83eb1a2c515feef3e00c349f33e16e06d56d2
parentae2ff22542a5882616778ffab50612f8ad41b71e (diff)
secondlife/viewer#1883: Local-only PBR terrain paintmap with developer tools
-rw-r--r--indra/llrender/llshadermgr.cpp3
-rw-r--r--indra/llrender/llshadermgr.h3
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings.xml33
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl31
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl46
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl38
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl62
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeV.glsl42
-rw-r--r--indra/newview/lldrawpoolterrain.cpp58
-rw-r--r--indra/newview/lldrawpoolterrain.h3
-rw-r--r--indra/newview/llsurface.h3
-rw-r--r--indra/newview/llsurfacepatch.cpp10
-rw-r--r--indra/newview/llsurfacepatch.h4
-rw-r--r--indra/newview/llterrainpaintmap.cpp285
-rw-r--r--indra/newview/llterrainpaintmap.h42
-rw-r--r--indra/newview/llviewercontrol.cpp4
-rw-r--r--indra/newview/llviewermenu.cpp82
-rw-r--r--indra/newview/llviewershadermgr.cpp74
-rw-r--r--indra/newview/llviewershadermgr.h11
-rw-r--r--indra/newview/llviewertexture.cpp1
-rw-r--r--indra/newview/llvlcomposition.cpp11
-rw-r--r--indra/newview/llvlcomposition.h12
-rw-r--r--indra/newview/llvosurfacepatch.cpp74
-rw-r--r--indra/newview/llvosurfacepatch.h12
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml38
26 files changed, 848 insertions, 136 deletions
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index bc542d325e..512ef340f9 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1409,6 +1409,7 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("detail_3");
mReservedUniforms.push_back("alpha_ramp");
+ mReservedUniforms.push_back("paint_map");
mReservedUniforms.push_back("detail_0_base_color");
mReservedUniforms.push_back("detail_1_base_color");
@@ -1433,6 +1434,8 @@ void LLShaderMgr::initAttribsAndUniforms()
mReservedUniforms.push_back("emissiveColors");
mReservedUniforms.push_back("minimum_alphas");
+ mReservedUniforms.push_back("region_scale");
+
mReservedUniforms.push_back("origin");
mReservedUniforms.push_back("display_gamma");
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index 921ef20997..2b76ef664b 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -267,6 +267,7 @@ public:
TERRAIN_DETAIL3, // "detail_3"
TERRAIN_ALPHARAMP, // "alpha_ramp"
+ TERRAIN_PAINTMAP, // "paint_map"
TERRAIN_DETAIL0_BASE_COLOR, // "detail_0_base_color" (GLTF)
TERRAIN_DETAIL1_BASE_COLOR, // "detail_1_base_color" (GLTF)
@@ -291,6 +292,8 @@ public:
TERRAIN_EMISSIVE_COLORS, // "emissiveColors" (GLTF)
TERRAIN_MINIMUM_ALPHAS, // "minimum_alphas" (GLTF)
+ REGION_SCALE, // "region_scale" (GLTF)
+
SHINY_ORIGIN, // "origin"
DISPLAY_GAMMA, // "display_gamma"
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index a34354d287..e7857ff09f 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -578,6 +578,7 @@ set(viewer_SOURCE_FILES
llsyswellwindow.cpp
llteleporthistory.cpp
llteleporthistorystorage.cpp
+ llterrainpaintmap.cpp
lltexturecache.cpp
lltexturectrl.cpp
lltexturefetch.cpp
@@ -1235,6 +1236,7 @@ set(viewer_HEADER_FILES
lltable.h
llteleporthistory.h
llteleporthistorystorage.h
+ llterrainpaintmap.h
lltexturecache.h
lltexturectrl.h
lltexturefetch.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9e5d3f09bd..5c3706bb6c 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -14966,6 +14966,39 @@
<key>Value</key>
<real>0.0</real>
</map>
+ <key>LocalTerrainPaintEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>Enables local paintmap if LocalTerrainAsset1, etc are set</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>TerrainPaintBitDepth</key>
+ <map>
+ <key>Comment</key>
+ <string>Bit depth for future terrain paint map operations. Min: 1. Max: 8. Takes effect when the paint map is created or modified. Modifications to an existing paintmap of different bit depth will have lower precision.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>5</integer>
+ </map>
+ <key>TerrainPaintResolution</key>
+ <map>
+ <key>Comment</key>
+ <string>Resolution of the terrain paint map in pixels. Rounded to a power of two. Min: 16. Max: RenderMaxTextureResolution. Takes effect when the paint map is created.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>2048</integer>
+ </map>
<key>PathfindingRetrieveNeighboringRegion</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
index 6ca35419f2..410c447c64 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainF.glsl
@@ -30,6 +30,9 @@
#define TERRAIN_PBR_DETAIL_NORMAL -2
#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3
+#define TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE 0
+#define TERRAIN_PAINT_TYPE_PBR_PAINTMAP 1
+
#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
#define TerrainCoord vec4[3]
#elif TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 1
@@ -48,6 +51,7 @@ struct TerrainMix
};
TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal);
+TerrainMix get_terrain_usage_from_weight3(vec3 weight3);
struct PBRMix
{
@@ -97,7 +101,11 @@ PBRMix mix_pbr(PBRMix mix1, PBRMix mix2, float mix2_weight);
out vec4 frag_data[4];
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
uniform sampler2D alpha_ramp;
+#elif TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_PBR_PAINTMAP
+uniform sampler2D paint_map;
+#endif
// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#additional-textures
uniform sampler2D detail_0_base_color;
@@ -133,19 +141,25 @@ 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_signs[4];
#endif
+
+// vary_texcoord* are used for terrain composition, vary_coords are used for terrain UVs
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
in vec4 vary_texcoord0;
in vec4 vary_texcoord1;
+#elif TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_PBR_PAINTMAP
+in vec2 vary_texcoord;
+#endif
+#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
void mirrorClip(vec3 position);
@@ -171,11 +185,16 @@ void main()
// Make sure we clip the terrain if we're in a mirror.
mirrorClip(vary_position);
+ TerrainMix tm;
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
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);
+ tm = get_terrain_mix_weights(alpha1, alpha2, alphaFinal);
+#elif TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_PBR_PAINTMAP
+ tm = get_terrain_usage_from_weight3(texture(paint_map, vary_texcoord).xyz);
+#endif
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_OCCLUSION)
// RGB = Occlusion, Roughness, Metal
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl
index 5098de717e..dc43007dca 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainUtilF.glsl
@@ -51,7 +51,12 @@
#define TERRAIN_PBR_DETAIL_NORMAL -2
#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3
+#define TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE 0
+#define TERRAIN_PAINT_TYPE_PBR_PAINTMAP 1
+
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
in vec3 vary_vertex_normal;
+#endif
vec3 srgb_to_linear(vec3 c);
@@ -202,6 +207,45 @@ TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal)
return tm;
}
+// A paintmap weight applier for 4 swatches. The input saves a channel by not
+// storing swatch 1, and assuming the weights of the 4 swatches add to 1.
+// The components of weight3 should be between 0 and 1
+// The sum of the components of weight3 should be between 0 and 1
+TerrainMix get_terrain_usage_from_weight3(vec3 weight3)
+{
+ // These steps ensure the output weights add to between 0 and 1
+ weight3.xyz = max(vec3(0.0), weight3.xyz);
+ weight3.xyz /= max(1.0, weight3.x + weight3.y + weight3.z);
+
+ TerrainMix tm;
+
+ // Extract the first weight from the other weights
+ tm.weight.x = 1.0 - (weight3.x + weight3.y + weight3.z);
+ tm.weight.yzw = weight3.xyz;
+ ivec4 usage = max(ivec4(0), ivec4(ceil(tm.weight)));
+
+ tm.type = (usage.x * MIX_X) |
+ (usage.y * MIX_Y) |
+ (usage.z * MIX_Z) |
+ (usage.w * MIX_W);
+ return tm;
+}
+
+// Inverse of get_terrain_usage_from_weight3, excluding usage flags
+// The components of weight should be between 0 and 1
+// The sum of the components of weight should be 1
+vec3 get_weight3_from_terrain_weight(vec4 weight)
+{
+ // These steps ensure the input weights add to 1
+ weight = max(vec4(0.0), weight);
+ weight.x += 1.0 - sign(weight.x + weight.y + weight.z + weight.w);
+ weight /= weight.x + weight.y + weight.z + weight.w;
+
+ // Then return the input weights with the first weight truncated
+ return weight.yzw;
+}
+
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
TerrainTriplanar _t_triplanar()
{
float sharpness = TERRAIN_TRIPLANAR_BLEND_FACTOR;
@@ -219,6 +263,8 @@ TerrainTriplanar _t_triplanar()
((usage.z) * SAMPLE_Z);
return tw;
}
+#endif
+
// Assume weights add to 1
float terrain_mix(TerrainMix tm, vec4 tms4)
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
index c90b2b5926..6791a22a76 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbrterrainV.glsl
@@ -28,31 +28,47 @@
#define TERRAIN_PBR_DETAIL_NORMAL -2
#define TERRAIN_PBR_DETAIL_METALLIC_ROUGHNESS -3
+#define TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE 0
+#define TERRAIN_PAINT_TYPE_PBR_PAINTMAP 1
+
uniform mat3 normal_matrix;
uniform mat4 texture_matrix0;
uniform mat4 modelview_matrix;
uniform mat4 modelview_projection_matrix;
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_PBR_PAINTMAP
+uniform float region_scale;
+#endif
in vec3 position;
in vec3 normal;
in vec4 tangent;
in vec4 diffuse_color;
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
in vec2 texcoord1;
+#endif
-out vec3 vary_vertex_normal; // Used by pbrterrainUtilF.glsl
+out vec3 vary_position;
out vec3 vary_normal;
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
+out vec3 vary_vertex_normal; // Used by pbrterrainUtilF.glsl
+#endif
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
out vec3 vary_tangents[4];
flat out float vary_signs[4];
#endif
+
+// vary_texcoord* are used for terrain composition, vary_coords are used for terrain UVs
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
out vec4 vary_texcoord0;
out vec4 vary_texcoord1;
+#elif TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_PBR_PAINTMAP
+out vec2 vary_texcoord;
+#endif
#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
@@ -69,7 +85,9 @@ void main()
vary_position = (modelview_matrix*vec4(position.xyz, 1.0)).xyz;
vec3 n = normal_matrix * normal;
+#if TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT == 3
vary_vertex_normal = normal;
+#endif
vec3 t = normal_matrix * tangent.xyz;
#if (TERRAIN_PBR_DETAIL >= TERRAIN_PBR_DETAIL_NORMAL)
@@ -110,9 +128,9 @@ void main()
// Transform and pass tex coords
{
vec4[2] ttt;
+#define transform_xy() terrain_texture_transform(position.xy, 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)
@@ -157,26 +175,30 @@ void main()
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);
+ vary_coords[0].xy = transform_xy();
// 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);
+ vary_coords[0].zw = transform_xy();
// 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);
+ vary_coords[1].xy = transform_xy();
// 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);
+ vary_coords[1].zw = transform_xy();
#endif
}
- vec4 tc = vec4(texcoord1,0,1);
+#if TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
+ vec2 tc = texcoord1.xy;
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);
+#elif TERRAIN_PAINT_TYPE == TERRAIN_PAINT_TYPE_PBR_PAINTMAP
+ vary_texcoord = position.xy / region_scale;
+#endif
}
diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl
new file mode 100644
index 0000000000..cf20653a0f
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl
@@ -0,0 +1,62 @@
+/**
+ * @file terrainBakeF.glsl
+ *
+ * $LicenseInfo:firstyear=2007&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$
+ */
+
+/*[EXTRA_CODE_HERE]*/
+
+out vec4 frag_color;
+
+struct TerrainMix
+{
+ vec4 weight;
+ int type;
+};
+
+TerrainMix get_terrain_mix_weights(float alpha1, float alpha2, float alphaFinal);
+
+uniform sampler2D alpha_ramp;
+
+// vary_texcoord* are used for terrain composition
+in vec4 vary_texcoord0;
+in vec4 vary_texcoord1;
+
+void main()
+{
+ TerrainMix tm;
+ 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;
+
+ tm = get_terrain_mix_weights(alpha1, alpha2, alphaFinal);
+
+ // tm.weight.x can be ignored. The paintmap saves a channel by not storing
+ // swatch 1, and assuming the weights of the 4 swatches add to 1.
+ // TERRAIN_PAINT_PRECISION emulates loss of precision at lower bit depth
+ // when a corresponding low-bit image format is not available. Because
+ // integral values at one depth cannot be precisely represented at another
+ // bit depth, rounding is required. To maximize numerical stability for
+ // future conversions, bit depth conversions should round to the nearest
+ // integer, not floor or ceil.
+ frag_color = max(vec4(round(tm.weight.yzw * TERRAIN_PAINT_PRECISION) / TERRAIN_PAINT_PRECISION, 1.0), vec4(0));
+}
diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeV.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeV.glsl
new file mode 100644
index 0000000000..fd150b54ad
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeV.glsl
@@ -0,0 +1,42 @@
+/**
+ * @file pbrTerrainBakeV.glsl
+ *
+ * $LicenseInfo:firstyear=2007&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$
+ */
+
+uniform mat4 modelview_projection_matrix;
+
+in vec3 position;
+in vec2 texcoord1;
+
+out vec4 vary_texcoord0;
+out vec4 vary_texcoord1;
+
+void main()
+{
+ gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
+ vec2 tc = texcoord1.xy;
+ 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/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
index 7d2912d81f..5e676bc5b3 100644
--- a/indra/newview/lldrawpoolterrain.cpp
+++ b/indra/newview/lldrawpoolterrain.cpp
@@ -219,7 +219,9 @@ void LLDrawPoolTerrain::renderFullShader()
else
{
// Use materials
- sShader = &gDeferredPBRTerrainProgram;
+ U32 paint_type = use_local_materials ? gLocalTerrainMaterials.getPaintType() : compp->getPaintType();
+ paint_type = llclamp(paint_type, 0, TERRAIN_PAINT_TYPE_COUNT);
+ sShader = &gDeferredPBRTerrainProgram[paint_type];
sShader->bind();
renderFullShaderPBR(use_local_materials);
}
@@ -326,7 +328,7 @@ void LLDrawPoolTerrain::renderFullShaderTextures()
}
// *TODO: Investigate use of bindFast for PBR terrain textures
-void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
+void LLDrawPoolTerrain::renderFullShaderPBR(bool use_local_materials)
{
// Hack! Get the region that this draw pool is rendering from!
LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
@@ -339,7 +341,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
llassert(shader_material_count == terrain_material_count);
#endif
- if (local_materials)
+ if (use_local_materials)
{
// Override region terrain with the global local override terrain
fetched_materials = &gLocalTerrainMaterials.mDetailRenderMaterials;
@@ -351,6 +353,9 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
if (!materials[i]) { materials[i] = &LLGLTFMaterial::sDefault; }
}
+ U32 paint_type = use_local_materials ? gLocalTerrainMaterials.getPaintType() : compp->getPaintType();
+ paint_type = llclamp(paint_type, 0, TERRAIN_PAINT_TYPE_COUNT);
+
S32 detail_basecolor[terrain_material_count];
S32 detail_normal[terrain_material_count];
S32 detail_metalrough[terrain_material_count];
@@ -481,11 +486,31 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
LLSettingsWater::ptr_t pwater = LLEnvironment::instance().getCurrentWater();
//
- // Alpha Ramp
+ // Alpha Ramp or paint map
//
- S32 alpha_ramp = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
- gGL.getTexUnit(alpha_ramp)->bind(m2DAlphaRampImagep);
- gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+ S32 alpha_ramp = -1;
+ S32 paint_map = -1;
+ if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE)
+ {
+ alpha_ramp = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
+ gGL.getTexUnit(alpha_ramp)->bind(m2DAlphaRampImagep);
+ gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+ }
+ else if (paint_type == TERRAIN_PAINT_TYPE_PBR_PAINTMAP)
+ {
+ paint_map = sShader->enableTexture(LLViewerShaderMgr::TERRAIN_PAINTMAP);
+ LLViewerTexture* tex_paint_map = use_local_materials ? gLocalTerrainMaterials.getPaintMap() : compp->getPaintMap();
+ // If no paintmap is available, fall back to rendering just material slot 1 (by binding the appropriate image)
+ if (!tex_paint_map) { tex_paint_map = LLViewerTexture::sBlackImagep.get(); }
+ // This is a paint map for four materials, but we save a channel by
+ // storing the paintmap as the "difference" between slot 1 and the
+ // other 3 slots.
+ llassert(tex_paint_map->getComponents() == 3);
+ gGL.getTexUnit(paint_map)->bind(tex_paint_map);
+ gGL.getTexUnit(paint_map)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+
+ shader->uniform1f(LLShaderMgr::REGION_SCALE, regionp->getWidth());
+ }
//
// GLTF uniforms
@@ -534,11 +559,22 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool local_materials)
// Disable multitexture
- sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
+ if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE)
+ {
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
- gGL.getTexUnit(alpha_ramp)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.getTexUnit(alpha_ramp)->disable();
- gGL.getTexUnit(alpha_ramp)->activate();
+ gGL.getTexUnit(alpha_ramp)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(alpha_ramp)->disable();
+ gGL.getTexUnit(alpha_ramp)->activate();
+ }
+ else if (paint_type == TERRAIN_PAINT_TYPE_PBR_PAINTMAP)
+ {
+ sShader->disableTexture(LLViewerShaderMgr::TERRAIN_PAINTMAP);
+
+ gGL.getTexUnit(paint_map)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(paint_map)->disable();
+ gGL.getTexUnit(paint_map)->activate();
+ }
for (U32 i = 0; i < terrain_material_count; ++i)
{
diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h
index a8097fb58b..5380463d01 100644
--- a/indra/newview/lldrawpoolterrain.h
+++ b/indra/newview/lldrawpoolterrain.h
@@ -38,7 +38,6 @@ 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
};
@@ -80,7 +79,7 @@ protected:
void renderFull4TU();
void renderFullShader();
void renderFullShaderTextures();
- void renderFullShaderPBR(bool local_materials = false);
+ void renderFullShaderPBR(bool use_local_materials = false);
void drawLoop();
private:
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index 68295225b6..10a104730b 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -110,6 +110,7 @@ public:
LLSurfacePatch *resolvePatchRegion(const F32 x, const F32 y) const;
LLSurfacePatch *resolvePatchRegion(const LLVector3 &position_region) const;
LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
+ LLSurfacePatch *getPatch(const S32 x, const S32 y) const;
// Update methods (called during idle, normally)
template<bool PBR>
@@ -176,8 +177,6 @@ protected:
void createPatchData(); // Allocates memory for patches.
void destroyPatchData(); // Deallocates memory for patches.
- LLSurfacePatch *getPatch(const S32 x, const S32 y) const;
-
protected:
LLVector3d mOriginGlobal; // In absolute frame
LLSurfacePatch *mPatchList; // Array of all patches
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
index 0550889a9b..4315c4c6b0 100644
--- a/indra/newview/llsurfacepatch.cpp
+++ b/indra/newview/llsurfacepatch.cpp
@@ -201,13 +201,13 @@ LLVector2 LLSurfacePatch::getTexCoords(const U32 x, const U32 y) const
void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 *vertex, LLVector3 *normal,
- LLVector2 *tex0, LLVector2 *tex1)
+ LLVector2 *tex1) const
{
if (!mSurfacep || !mSurfacep->getRegion() || !mSurfacep->getGridsPerEdge() || !mVObjp)
{
return; // failsafe
}
- llassert_always(vertex && normal && tex0 && tex1);
+ llassert_always(vertex && normal && tex1);
U32 surface_stride = mSurfacep->getGridsPerEdge();
U32 point_offset = x + y*surface_stride;
@@ -220,12 +220,6 @@ void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3
pos_agent.mV[VZ] = *(mDataZ + point_offset);
*vertex = pos_agent-mVObjp->getRegion()->getOriginAgent();
- LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent();
- // *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);
const F32 xyScale = 4.9215f*7.f; //0.93284f;
diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h
index 195250d2c0..f4831487c1 100644
--- a/indra/newview/llsurfacepatch.h
+++ b/indra/newview/llsurfacepatch.h
@@ -116,7 +116,7 @@ public:
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);
+ LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex1) const;
@@ -146,6 +146,8 @@ public:
void dirty(); // Mark this surface patch as dirty...
void clearDirty() { mDirty = false; }
+ bool isHeightsGenerated() const { return mHeightsGenerated; }
+
void clearVObj();
public:
diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp
new file mode 100644
index 0000000000..4381d14546
--- /dev/null
+++ b/indra/newview/llterrainpaintmap.cpp
@@ -0,0 +1,285 @@
+/**
+ * @file llterrainpaintmap.cpp
+ * @brief Utilities for managing terrain paint maps
+ *
+ * $LicenseInfo:firstyear=2001&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 "llterrainpaintmap.h"
+
+#include "llviewerprecompiledheaders.h"
+
+// library includes
+#include "llglslshader.h"
+#include "llrendertarget.h"
+#include "llvertexbuffer.h"
+
+// newview includes
+#include "llrender.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "llviewershadermgr.h"
+#include "llviewertexture.h"
+
+// static
+bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex)
+{
+ llassert(tex.getComponents() == 3);
+ llassert(tex.getWidth() > 0 && tex.getHeight() > 0);
+ llassert(tex.getWidth() == tex.getHeight());
+ llassert(tex.getPrimaryFormat() == GL_RGB);
+ llassert(tex.getGLTexture());
+
+ const LLSurface& surface = region.getLand();
+ const U32 patch_count = surface.getPatchesPerEdge();
+
+ // *TODO: mHeightsGenerated isn't guaranteed to be true. Assume terrain is
+ // loaded for now. Would be nice to fix the loading issue or find a better
+ // heuristic to determine that the terrain is sufficiently loaded.
+#if 0
+ // Don't proceed if the region heightmap isn't loaded
+ for (U32 rj = 0; rj < patch_count; ++rj)
+ {
+ for (U32 ri = 0; ri < patch_count; ++ri)
+ {
+ const LLSurfacePatch* patch = surface.getPatch(ri, rj);
+ if (!patch->isHeightsGenerated())
+ {
+ LL_WARNS() << "Region heightmap not fully loaded" << LL_ENDL;
+ return false;
+ }
+ }
+ }
+#endif
+
+ // Bind the debug shader and render terrain to tex
+ // Use a scratch render target because its dimensions may exceed the standard bake target, and this is a one-off bake
+ LLRenderTarget scratch_target;
+ const S32 dim = llmin(tex.getWidth(), tex.getHeight());
+ scratch_target.allocate(dim, dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE,
+ LLTexUnit::eTextureMipGeneration::TMG_NONE);
+ if (!scratch_target.isComplete())
+ {
+ llassert(false);
+ LL_WARNS() << "Failed to allocate render target" << LL_ENDL;
+ return false;
+ }
+ gGL.getTexUnit(0)->disable();
+ stop_glerror();
+
+ scratch_target.bindTarget();
+ glClearColor(0, 0, 0, 0);
+ scratch_target.clear();
+
+ // Render terrain heightmap to paint map via shader
+
+ // Set up viewport, camera, and orthographic projection matrix. Position
+ // the camera such that the camera points straight down, and the region
+ // completely covers the "screen". Since orthographic projection does not
+ // distort, we arbitrarily choose the near plane and far plane to cover the
+ // full span of region heights, plus a small amount of padding to account
+ // for rounding errors.
+ const F32 region_width = region.getWidth();
+ const F32 region_half_width = region_width / 2.0f;
+ const F32 region_camera_height = surface.getMaxZ() + DEFAULT_NEAR_PLANE;
+ LLViewerCamera camera;
+ const LLVector3 region_center = LLVector3(region_half_width, region_half_width, 0.0) + region.getOriginAgent();
+ const LLVector3 camera_origin = LLVector3(0.0f, 0.0f, region_camera_height) + region_center;
+ camera.lookAt(camera_origin, region_center, LLVector3::y_axis);
+ camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth()));
+ const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0);
+ glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight());
+ // Manually get modelview matrix from camera orientation.
+ glh::matrix4f modelview((GLfloat *) OGL_TO_CFR_ROTATION);
+ GLfloat ogl_matrix[16];
+ camera.getOpenGLTransform(ogl_matrix);
+ modelview *= glh::matrix4f(ogl_matrix);
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ gGL.loadMatrix(modelview.m);
+ // Override the projection matrix from the camera
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.pushMatrix();
+ gGL.loadIdentity();
+ llassert(camera_origin.mV[VZ] >= surface.getMaxZ());
+ const F32 region_high_near = camera_origin.mV[VZ] - surface.getMaxZ();
+ constexpr F32 far_plane_delta = 0.25f;
+ const F32 region_low_far = camera_origin.mV[VZ] - surface.getMinZ() + far_plane_delta;
+ gGL.ortho(-region_half_width, region_half_width, -region_half_width, region_half_width, region_high_near, region_low_far);
+ // No need to call camera.setPerspective because we don't need the clip planes. It would be inaccurate due to the perspective rendering anyway.
+
+ // Need to get the full resolution vertices in order to get an accurate
+ // paintmap. It's not sufficient to iterate over the surface patches, as
+ // they may be at lower LODs.
+ // The functionality here is a subset of
+ // LLVOSurfacePatch::getTerrainGeometry. Unlike said function, we don't
+ // care about stride length since we're always rendering at full
+ // resolution. We also don't care about normals/tangents because those
+ // don't contribute to the paintmap.
+ // *NOTE: The actual getTerrainGeometry fits the terrain vertices snugly
+ // under the 16-bit indices limit. For the sake of simplicity, that has not
+ // been replicated here.
+ std::vector<LLPointer<LLDrawInfo>> infos;
+ // Vertex and index counts adapted from LLVOSurfacePatch::getGeomSizesMain,
+ // with additional vertices added as we are including the north and east
+ // edges here.
+ const U32 patch_size = (U32)surface.getGridsPerPatchEdge();
+ constexpr U32 stride = 1;
+ const U32 vert_size = (patch_size / stride) + 1;
+ const U32 n = vert_size * vert_size;
+ const U32 ni = 6 * (vert_size - 1) * (vert_size - 1);
+ const U32 region_vertices = n * patch_count * patch_count;
+ const U32 region_indices = ni * patch_count * patch_count;
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
+ }
+ LLPointer<LLVertexBuffer> buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD1);
+ {
+ buf->allocateBuffer(region_vertices, region_indices*2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
+ buf->setBuffer();
+ U32 vertex_total = 0;
+ std::vector<U32> index_array(region_indices);
+ std::vector<LLVector4a> positions(region_vertices);
+ std::vector<LLVector2> texcoords1(region_vertices);
+ auto idx = index_array.begin();
+ auto pos = positions.begin();
+ auto tex1 = texcoords1.begin();
+ for (U32 rj = 0; rj < patch_count; ++rj)
+ {
+ for (U32 ri = 0; ri < patch_count; ++ri)
+ {
+ const U32 index_offset = vertex_total;
+ for (U32 j = 0; j < (vert_size - 1); ++j)
+ {
+ for (U32 i = 0; i < (vert_size - 1); ++i)
+ {
+ // y
+ // 2....3
+ // ^ . .
+ // | 0....1
+ // |
+ // -------> x
+ //
+ // triangle 1: 0,1,2
+ // triangle 2: 1,3,2
+ // 0: vert0
+ // 1: vert0 + 1
+ // 2: vert0 + vert_size
+ // 3: vert0 + vert_size + 1
+ const U32 vert0 = index_offset + i + (j*vert_size);
+ *idx++ = vert0;
+ *idx++ = vert0 + 1;
+ *idx++ = vert0 + vert_size;
+ *idx++ = vert0 + 1;
+ *idx++ = vert0 + vert_size + 1;
+ *idx++ = vert0 + vert_size;
+ }
+ }
+
+ const LLSurfacePatch* patch = surface.getPatch(ri, rj);
+ for (U32 j = 0; j < vert_size; ++j)
+ {
+ for (U32 i = 0; i < vert_size; ++i)
+ {
+ LLVector3 scratch3;
+ LLVector3 pos3;
+ LLVector2 tex1_temp;
+ patch->eval(i, j, stride, &pos3, &scratch3, &tex1_temp);
+ (*pos++).set(pos3.mV[VX], pos3.mV[VY], pos3.mV[VZ]);
+ *tex1++ = tex1_temp;
+ vertex_total++;
+ }
+ }
+ }
+ }
+ buf->setIndexData(index_array.data(), 0, (U32)index_array.size());
+ buf->setPositionData(positions.data(), 0, (U32)positions.size());
+ buf->setTexCoord1Data(texcoords1.data(), 0, (U32)texcoords1.size());
+ buf->unmapBuffer();
+ buf->unbind();
+ }
+
+ // Draw the region in agent space at full resolution
+ {
+
+ LLGLSLShader::unbind();
+ // *NOTE: A theoretical non-PBR terrain bake program would be
+ // *slightly* different, due the texture terrain shader not having an
+ // alpha ramp threshold (TERRAIN_RAMP_MIX_THRESHOLD)
+ LLGLSLShader& shader = gPBRTerrainBakeProgram;
+ shader.bind();
+
+ LLGLDisable stencil(GL_STENCIL_TEST);
+ LLGLDisable scissor(GL_SCISSOR_TEST);
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS);
+
+ S32 alpha_ramp = shader.enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
+ LLPointer<LLViewerTexture> alpha_ramp_texture = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD_2D);
+ gGL.getTexUnit(alpha_ramp)->bind(alpha_ramp_texture);
+ gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
+
+ buf->setBuffer();
+ for (U32 rj = 0; rj < patch_count; ++rj)
+ {
+ for (U32 ri = 0; ri < patch_count; ++ri)
+ {
+ const U32 patch_index = ri + (rj * patch_count);
+ const U32 index_offset = ni * patch_index;
+ const U32 vertex_offset = n * patch_index;
+ llassert(index_offset + ni <= region_indices);
+ llassert(vertex_offset + n <= region_vertices);
+ buf->drawRange(LLRender::TRIANGLES, vertex_offset, vertex_offset + n - 1, ni, index_offset);
+ }
+ }
+
+ shader.disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP);
+
+ gGL.getTexUnit(alpha_ramp)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(alpha_ramp)->disable();
+ gGL.getTexUnit(alpha_ramp)->activate();
+
+ shader.unbind();
+ }
+
+ gGL.matrixMode(LLRender::MM_PROJECTION);
+ gGL.popMatrix();
+
+ gGL.flush();
+ LLVertexBuffer::unbind();
+ // Final step: Copy the output to the terrain paintmap
+ const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, dim, dim);
+ if (!success)
+ {
+ LL_WARNS() << "Failed to copy framebuffer to paintmap" << LL_ENDL;
+ }
+ glGenerateMipmap(GL_TEXTURE_2D);
+ stop_glerror();
+
+ scratch_target.flush();
+
+ LLGLSLShader::unbind();
+
+ return success;
+}
diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h
new file mode 100644
index 0000000000..66827862c5
--- /dev/null
+++ b/indra/newview/llterrainpaintmap.h
@@ -0,0 +1,42 @@
+/**
+ * @file llterrainpaintmap.h
+ * @brief Utilities for managing terrain paint maps
+ *
+ * $LicenseInfo:firstyear=2001&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
+
+class LLViewerRegion;
+class LLViewerTexture;
+
+class LLTerrainPaintMap
+{
+public:
+
+ // Convert a region's heightmap and composition into a paint map texture which
+ // approximates how the terrain would be rendered with the heightmap.
+ // In effect, this allows converting terrain of type TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
+ // to type TERRAIN_PAINT_TYPE_PBR_PAINTMAP.
+ // Returns true if successful
+ static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex);
+};
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 0e1f4c09c7..c1bf31ff9a 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -719,6 +719,8 @@ void handleLocalTerrainChanged(const LLSD& newValue)
{
gLocalTerrainMaterials.setMaterialOverride(i, mat_override);
}
+ const bool paint_enabled = gSavedSettings.getBOOL("LocalTerrainPaintEnabled");
+ gLocalTerrainMaterials.setPaintType(paint_enabled ? TERRAIN_PAINT_TYPE_PBR_PAINTMAP : TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE);
}
}
////////////////////////////////////////////////////////////////////////////
@@ -909,6 +911,7 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged);
setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged);
{
+ setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged);
const char* transform_suffixes[] = {
"ScaleU",
"ScaleV",
@@ -927,6 +930,7 @@ void settings_setup_listeners()
}
}
}
+ setting_setup_signal_listener(gSavedSettings, "TerrainPaintBitDepth", handleSetShaderChanged);
setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged);
}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 0c8dd6dff9..aa14820d55 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -42,6 +42,7 @@
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llviewereventrecorder.h"
+#include "v4coloru.h"
// newview includes
#include "llagent.h"
@@ -105,6 +106,7 @@
#include "llsidepanelappearance.h"
#include "llspellcheckmenuhandler.h"
#include "llstatusbar.h"
+#include "llterrainpaintmap.h"
#include "lltextureview.h"
#include "lltoolbarview.h"
#include "lltoolcomp.h"
@@ -122,6 +124,7 @@
#include "llviewerparcelmgr.h"
#include "llviewerstats.h"
#include "llviewerstatsrecorder.h"
+#include "llvlcomposition.h"
#include "llvoavatarself.h"
#include "llvoicevivox.h"
#include "llworld.h"
@@ -1387,6 +1390,65 @@ class LLAdvancedResetInterestLists : public view_listener_t
};
+/////////////
+// TERRAIN //
+/////////////
+
+class LLAdvancedRebuildTerrain : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ gPipeline.rebuildTerrain();
+ return true;
+ }
+};
+
+class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region)
+ {
+ LL_WARNS() << "Agent not in a region" << LL_ENDL;
+ return false;
+ }
+
+ U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution");
+ // Ensure a reasonable image size of power two
+ const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution");
+ dim = llclamp(dim, 16, max_resolution);
+ dim = 1 << U32(std::ceil(std::log2(dim)));
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
+ LLPointer<LLViewerTexture> tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true);
+ const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex);
+ // This calls gLocalTerrainMaterials.setPaintType
+ gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true);
+ // If baking the paintmap failed, set the paintmap to nullptr. This
+ // causes LLDrawPoolTerrain to use a blank paintmap instead.
+ if (!success) { tex = nullptr; }
+ gLocalTerrainMaterials.setPaintMap(tex);
+
+ return true;
+ }
+};
+
+class LLAdvancedTerrainDeleteLocalPaintMap : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ // This calls gLocalTerrainMaterials.setPaintType
+ gSavedSettings.setBOOL("LocalTerrainPaintEnabled", false);
+ gLocalTerrainMaterials.setPaintMap(nullptr);
+
+ return true;
+ }
+};
+
+
+/////////////
+
+
class LLAdvancedBuyCurrencyTest : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -2242,20 +2304,6 @@ 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 //
///////////////////
@@ -9851,7 +9899,6 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickGLTFEdit(), "Advanced.ClickGLTFEdit");
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");
@@ -9868,6 +9915,11 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedCheckInterestList360Mode(), "Advanced.CheckInterestList360Mode");
view_listener_t::addMenu(new LLAdvancedResetInterestLists(), "Advanced.ResetInterestLists");
+ // Develop > Terrain
+ view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
+ view_listener_t::addMenu(new LLAdvancedTerrainCreateLocalPaintMap(), "Advanced.TerrainCreateLocalPaintMap");
+ view_listener_t::addMenu(new LLAdvancedTerrainDeleteLocalPaintMap(), "Advanced.TerrainDeleteLocalPaintMap");
+
// Advanced > UI
commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser
commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2)); // this one opens the Web Content floater
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 96354ea18f..073a1787d5 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -100,6 +100,7 @@ LLGLSLShader gBenchmarkProgram;
LLGLSLShader gReflectionProbeDisplayProgram;
LLGLSLShader gCopyProgram;
LLGLSLShader gCopyDepthProgram;
+LLGLSLShader gPBRTerrainBakeProgram;
//object shaders
LLGLSLShader gObjectPreviewProgram;
@@ -226,7 +227,7 @@ LLGLSLShader gDeferredSkinnedPBROpaqueProgram;
LLGLSLShader gHUDPBRAlphaProgram;
LLGLSLShader gDeferredPBRAlphaProgram;
LLGLSLShader gDeferredSkinnedPBRAlphaProgram;
-LLGLSLShader gDeferredPBRTerrainProgram;
+LLGLSLShader gDeferredPBRTerrainProgram[TERRAIN_PAINT_TYPE_COUNT];
LLGLSLShader gGLTFPBRMetallicRoughnessProgram;
@@ -432,7 +433,10 @@ void LLViewerShaderMgr::finalizeShaderList()
mShaderList.push_back(&gGLTFPBRMetallicRoughnessProgram);
mShaderList.push_back(&gDeferredAvatarProgram);
mShaderList.push_back(&gDeferredTerrainProgram);
- mShaderList.push_back(&gDeferredPBRTerrainProgram);
+ for (U32 paint_type = 0; paint_type < TERRAIN_PAINT_TYPE_COUNT; ++paint_type)
+ {
+ mShaderList.push_back(&gDeferredPBRTerrainProgram[paint_type]);
+ }
mShaderList.push_back(&gDeferredDiffuseAlphaMaskProgram);
mShaderList.push_back(&gDeferredNonIndexedDiffuseAlphaMaskProgram);
mShaderList.push_back(&gDeferredTreeProgram);
@@ -1129,7 +1133,10 @@ bool LLViewerShaderMgr::loadShadersDeferred()
gDeferredSkinnedPBROpaqueProgram.unload();
gDeferredPBRAlphaProgram.unload();
gDeferredSkinnedPBRAlphaProgram.unload();
- gDeferredPBRTerrainProgram.unload();
+ for (U32 paint_type = 0; paint_type < TERRAIN_PAINT_TYPE_COUNT; ++paint_type)
+ {
+ gDeferredPBRTerrainProgram[paint_type].unload();
+ }
return true;
}
@@ -1443,25 +1450,31 @@ bool LLViewerShaderMgr::loadShadersDeferred()
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.calculatesAtmospherics = true;
- gDeferredPBRTerrainProgram.mFeatures.hasAtmospherics = true;
- gDeferredPBRTerrainProgram.mFeatures.hasGamma = true;
- gDeferredPBRTerrainProgram.mFeatures.hasTransport = true;
- gDeferredPBRTerrainProgram.mFeatures.isPBRTerrain = true;
+ for (U32 paint_type = 0; paint_type < TERRAIN_PAINT_TYPE_COUNT; ++paint_type)
+ {
+ LLGLSLShader* shader = &gDeferredPBRTerrainProgram[paint_type];
+ shader->mName = llformat("Deferred PBR Terrain Shader %d %s %s",
+ detail,
+ (paint_type == TERRAIN_PAINT_TYPE_PBR_PAINTMAP ? "paintmap" : "heightmap-with-noise"),
+ (mapping == 1 ? "flat" : "triplanar"));
+ shader->mFeatures.hasSrgb = true;
+ shader->mFeatures.isAlphaLighting = true;
+ shader->mFeatures.calculatesAtmospherics = true;
+ shader->mFeatures.hasAtmospherics = true;
+ shader->mFeatures.hasGamma = true;
+ shader->mFeatures.hasTransport = true;
+ shader->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);
+ shader->mShaderFiles.clear();
+ shader->mShaderFiles.push_back(make_pair("deferred/pbrterrainV.glsl", GL_VERTEX_SHADER));
+ shader->mShaderFiles.push_back(make_pair("deferred/pbrterrainF.glsl", GL_FRAGMENT_SHADER));
+ shader->mShaderLevel = mShaderLevel[SHADER_DEFERRED];
+ shader->addPermutation("TERRAIN_PBR_DETAIL", llformat("%d", detail));
+ shader->addPermutation("TERRAIN_PAINT_TYPE", llformat("%d", paint_type));
+ shader->addPermutation("TERRAIN_PLANAR_TEXTURE_SAMPLE_COUNT", llformat("%d", mapping));
+ success = success && shader->createShader();
+ llassert(success);
+ }
}
if (success)
@@ -2959,6 +2972,25 @@ bool LLViewerShaderMgr::loadShadersInterface()
if (success)
{
+ LLGLSLShader* shader = &gPBRTerrainBakeProgram;
+ U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth");
+ // LLTerrainPaintMap currently uses an RGB8 texture internally
+ bit_depth = llclamp(bit_depth, 1, 8);
+ shader->mName = llformat("Terrain Bake Shader RGB%o", bit_depth);
+ shader->mFeatures.isPBRTerrain = true;
+
+ shader->mShaderFiles.clear();
+ shader->mShaderFiles.push_back(make_pair("interface/pbrTerrainBakeV.glsl", GL_VERTEX_SHADER));
+ shader->mShaderFiles.push_back(make_pair("interface/pbrTerrainBakeF.glsl", GL_FRAGMENT_SHADER));
+ shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+ const U32 value_range = (1 << bit_depth) - 1;
+ shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range));
+ success = success && shader->createShader();
+ llassert(success);
+ }
+
+ if (success)
+ {
gAlphaMaskProgram.mName = "Alpha Mask Shader";
gAlphaMaskProgram.mShaderFiles.clear();
gAlphaMaskProgram.mShaderFiles.push_back(make_pair("interface/alphamaskV.glsl", GL_VERTEX_SHADER));
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 60ce8c430b..af47014a43 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -174,6 +174,7 @@ extern LLGLSLShader gBenchmarkProgram;
extern LLGLSLShader gReflectionProbeDisplayProgram;
extern LLGLSLShader gCopyProgram;
extern LLGLSLShader gCopyDepthProgram;
+extern LLGLSLShader gPBRTerrainBakeProgram;
//output tex0[tc0] - tex1[tc1]
extern LLGLSLShader gTwoTextureCompareProgram;
@@ -304,5 +305,13 @@ enum TerrainPBRDetail : S32
TERRAIN_PBR_DETAIL_BASE_COLOR = -4,
TERRAIN_PBR_DETAIL_MIN = -4,
};
-extern LLGLSLShader gDeferredPBRTerrainProgram;
+enum TerrainPaintType : U32
+{
+ // Use LLVLComposition::mDatap (heightmap) generated by generateHeights, plus noise from TERRAIN_ALPHARAMP
+ TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE = 0,
+ // Use paint map if PBR terrain, otherwise fall back to TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE
+ TERRAIN_PAINT_TYPE_PBR_PAINTMAP = 1,
+ TERRAIN_PAINT_TYPE_COUNT = 2,
+};
+extern LLGLSLShader gDeferredPBRTerrainProgram[TERRAIN_PAINT_TYPE_COUNT];
#endif
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 9648a9af18..1369e3d306 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -42,6 +42,7 @@
#include "llstl.h"
#include "message.h"
#include "lltimer.h"
+#include "v4coloru.h"
// viewer includes
#include "llimagegl.h"
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index f5f058a080..077e6e6cb1 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -312,6 +312,17 @@ bool LLTerrainMaterials::makeMaterialsReady(bool boost, bool strict)
return one_ready;
}
+LLViewerTexture* LLTerrainMaterials::getPaintMap()
+{
+ return mPaintMap.get();
+}
+
+void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map)
+{
+ llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP);
+ mPaintMap = paint_map;
+}
+
// Boost the texture loading priority
// Return true when ready to use (i.e. texture is sufficiently loaded)
// static
diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h
index 2637d77183..f15f9bff6a 100644
--- a/indra/newview/llvlcomposition.h
+++ b/indra/newview/llvlcomposition.h
@@ -28,6 +28,8 @@
#define LL_LLVLCOMPOSITION_H
#include "llviewerlayer.h"
+#include "llviewershadermgr.h"
+#include "llviewertexture.h"
#include "llpointer.h"
#include "llimage.h"
@@ -44,6 +46,7 @@ public:
virtual const LLGLTFMaterial* getMaterialOverride(S32 asset) const = 0;
};
+// The subset of the composition used by local terrain debug materials (gLocalTerrainMaterials)
class LLTerrainMaterials : public LLModifyRegion
{
public:
@@ -79,6 +82,12 @@ public:
// strict = false -> at least one material must be loaded
bool makeMaterialsReady(bool boost, bool strict);
+ // See TerrainPaintType
+ U32 getPaintType() const { return mPaintType; }
+ void setPaintType(U32 paint_type) { mPaintType = paint_type; }
+ LLViewerTexture* getPaintMap();
+ void setPaintMap(LLViewerTexture* paint_map);
+
protected:
void unboost();
static bool makeTextureReady(LLPointer<LLViewerFetchedTexture>& tex, bool boost);
@@ -93,6 +102,9 @@ protected:
LLPointer<LLGLTFMaterial> mDetailMaterialOverrides[ASSET_COUNT];
LLPointer<LLFetchedGLTFMaterial> mDetailRenderMaterials[ASSET_COUNT];
bool mMaterialTexturesSet[ASSET_COUNT];
+
+ U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE;
+ LLPointer<LLViewerTexture> mPaintMap;
};
// Local materials to override all regions
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index 471174cf5d..b76717e660 100644
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -245,7 +245,6 @@ bool LLVOSurfacePatch::updateLOD()
void LLVOSurfacePatch::getTerrainGeometry(LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp)
{
@@ -260,21 +259,18 @@ void LLVOSurfacePatch::getTerrainGeometry(LLStrider<LLVector3> &verticesp,
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);
@@ -283,7 +279,6 @@ void LLVOSurfacePatch::getTerrainGeometry(LLStrider<LLVector3> &verticesp,
void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp,
U32 &index_offset)
@@ -322,10 +317,9 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
{
x = i * render_stride;
y = j * render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
}
@@ -387,7 +381,6 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp,
U32 &index_offset)
@@ -421,10 +414,9 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
x = i * render_stride;
y = 16 - render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -433,10 +425,9 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
{
x = i * render_stride;
y = 16;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -469,10 +460,9 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
x = i * render_stride;
y = 16 - render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -482,10 +472,9 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
x = i * render_stride;
y = 16;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -525,10 +514,9 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
x = i * north_stride;
y = 16 - render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -538,10 +526,9 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
x = i * north_stride;
y = 16;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -577,7 +564,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp,
U32 &index_offset)
@@ -606,10 +592,9 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
x = 16 - render_stride;
y = i * render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -618,10 +603,9 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
{
x = 16;
y = i * render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -654,10 +638,9 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
x = 16 - render_stride;
y = i * render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
// Iterate through the east patch's points
@@ -666,10 +649,9 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
x = 16;
y = i * render_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -708,10 +690,9 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
x = 16 - render_stride;
y = i * east_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
// Iterate through the east patch's points
@@ -720,10 +701,9 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
x = 16;
y = i * east_stride;
- mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords1p.get());
verticesp++;
normalsp++;
- texCoords0p++;
texCoords1p++;
}
@@ -992,8 +972,8 @@ void gen_terrain_tangents(U16 strider_vertex_count,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
LLStrider<LLVector4a> &tangentsp,
- LLStrider<LLVector2> &texCoords0p,
- LLStrider<U16> &indicesp)
+ LLStrider<U16> &indicesp,
+ F32 region_width)
{
LL_PROFILE_ZONE_SCOPED;
@@ -1010,7 +990,10 @@ void gen_terrain_tangents(U16 strider_vertex_count,
F32 *n = normalsp[v].mV;
normals[v] = LLVector4a(n[0], n[1], n[2], 1.f);
tangents[v] = tangentsp[v];
- texcoords[v] = texCoords0p[v];
+
+ // Calculate texcoords on-the-fly using the terrain positions
+ texcoords[v].mV[VX] = verticesp[v].mV[VX] / region_width;
+ texcoords[v].mV[VY] = verticesp[v].mV[VY] / region_width;
}
for (U32 i = 0; i < strider_index_count; ++i)
{
@@ -1039,14 +1022,12 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
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));
@@ -1056,7 +1037,6 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
{
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;
@@ -1069,7 +1049,7 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
facep->setVertexBuffer(buffer);
LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
- patchp->getTerrainGeometry(vertices, normals, texcoords, texcoords2, indices);
+ patchp->getTerrainGeometry(vertices, normals, texcoords2, indices);
indices_index += facep->getIndicesCount();
index_offset += facep->getGeomCount();
@@ -1082,10 +1062,20 @@ void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
LLStrider<LLVector3> vertices = vertices_start;
LLStrider<LLVector3> normals = normals_start;
LLStrider<LLVector4a> tangents = tangents_start;
- LLStrider<LLVector2> texcoords = texcoords_start;
LLStrider<U16> indices = indices_start;
- gen_terrain_tangents(index_offset, indices_index, vertices, normals, tangents, texcoords, indices);
+ F32 region_width = 256.0f;
+ if (mFaceList.empty())
+ {
+ llassert(false);
+ }
+ else
+ {
+ const LLViewerRegion* regionp = mFaceList[0]->getViewerObject()->getRegion();
+ llassert(regionp == mFaceList.back()->getViewerObject()->getRegion()); // Assume this spatial group is confined to one region
+ region_width = regionp->getWidth();
+ }
+ gen_terrain_tangents(index_offset, indices_index, vertices, normals, tangents, indices, region_width);
}
buffer->unmapBuffer();
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index 2780b97a97..af5f05774b 100644
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -41,14 +41,6 @@ class LLVOSurfacePatch : public LLStaticViewerObject
public:
static F32 sLODFactor;
- enum
- {
- VERTEX_DATA_MASK = (1 << LLVertexBuffer::TYPE_VERTEX) |
- (1 << LLVertexBuffer::TYPE_NORMAL) |
- (1 << LLVertexBuffer::TYPE_TEXCOORD0) |
- (1 << LLVertexBuffer::TYPE_TEXCOORD1)
- };
-
LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
/*virtual*/ void markDead();
@@ -65,7 +57,6 @@ public:
/*virtual*/ void updateFaceSize(S32 idx);
void getTerrainGeometry(LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp);
@@ -118,21 +109,18 @@ protected:
void updateMainGeometry(LLFace *facep,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp,
U32 &index_offset);
void updateNorthGeometry(LLFace *facep,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp,
U32 &index_offset);
void updateEastGeometry(LLFace *facep,
LLStrider<LLVector3> &verticesp,
LLStrider<LLVector3> &normalsp,
- LLStrider<LLVector2> &texCoords0p,
LLStrider<LLVector2> &texCoords1p,
LLStrider<U16> &indicesp,
U32 &index_offset);
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 941f0c6bb7..98cf03b30e 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3435,13 +3435,6 @@ 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"
@@ -3662,6 +3655,37 @@ function="World.EnvPreset"
function="Advanced.ResetInterestLists" />
</menu_item_call>
</menu>
+ <!-- terrain -->
+ <menu
+ create_jump_keys="true"
+ label="Terrain"
+ name="DevelopTerrain"
+ tear_off="true">
+ <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"
+ label="Create Local Paintmap"
+ name="Create Local Paintmap">
+ <menu_item_call.on_click
+ function="Advanced.TerrainCreateLocalPaintMap" />
+ </menu_item_call>
+ <menu_item_call
+ enabled="true"
+ label="Delete Local Paintmap"
+ name="Delete Local Paintmap">
+ <menu_item_call.on_click
+ function="Advanced.TerrainDeleteLocalPaintMap" />
+ </menu_item_call>
+ </menu>
<menu
create_jump_keys="true"
label="UI"