summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llrender/llshadermgr.cpp23
-rw-r--r--indra/llrender/llshadermgr.h3
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl33
-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/llspatialpartition.cpp171
-rw-r--r--indra/newview/llsurface.cpp14
-rw-r--r--indra/newview/llsurface.h6
-rw-r--r--indra/newview/llsurfacepatch.cpp197
-rw-r--r--indra/newview/llsurfacepatch.h13
-rw-r--r--indra/newview/llviewermenu.cpp18
-rwxr-xr-xindra/newview/llviewerregion.cpp35
-rw-r--r--indra/newview/llviewerregion.h3
-rw-r--r--indra/newview/llviewershadermgr.cpp29
-rw-r--r--indra/newview/llviewershadermgr.h8
-rw-r--r--indra/newview/llvosurfacepatch.cpp1
-rw-r--r--indra/newview/pipeline.cpp10
-rw-r--r--indra/newview/pipeline.h2
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml7
20 files changed, 657 insertions, 77 deletions
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 14f3086eb6..33d229bcc9 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -588,11 +588,22 @@ 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");
@@ -1453,6 +1464,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 ebd682141d..4ee5c8ef47 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -316,6 +316,9 @@ public:
WATER_EDGE_FACTOR, // "water_edge"
SUN_UP_FACTOR, // "sun_up_factor"
MOONLIGHT_COLOR, // "moonlight_color"
+
+ DEBUG_NORMAL_DRAW_LENGTH, // "debug_normal_draw_length"
+
END_RESERVED_UNIFORMS
} eGLSLReservedUniforms;
// clang-format on
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 54b7dd7df0..4413cf9269 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10888,6 +10888,17 @@
<key>Value</key>
<real>8.0</real>
</map>
+ <key>RenderTerrainPBRNormalsEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>EXPERIMENTAL: Change normal gen for PBR Terrain.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>RenderTrackerBeacon</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl
new file mode 100644
index 0000000000..388042e7e0
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/normaldebugF.glsl
@@ -0,0 +1,33 @@
+/**
+ * @file normaldebugF.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$
+ */
+
+out vec4 frag_color;
+
+in vec4 vertex_color;
+
+void main()
+{
+ 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..ea04ce1cae
--- /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[];
+#if HAS_ATTRIBUTE_TANGENT == 1
+in vec4 tangent_g[];
+#endif
+
+layout(TRIANGLES) in;
+#if HAS_ATTRIBUTE_TANGENT == 1
+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();
+
+#if HAS_ATTRIBUTE_TANGENT == 1
+ // 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..d1596b9d2a
--- /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;
+#if HAS_ATTRIBUTE_TANGENT == 1
+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);
+#if HAS_ATTRIBUTE_TANGENT == 1
+ tangent_g = get_screen_normal(position.xyz, world_pos, tangent.xyz, mat);
+#endif
+}
+
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 931880a475..40e8e526d1 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"
@@ -2000,7 +2001,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())
@@ -2008,11 +2013,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.
//
@@ -2021,68 +2028,136 @@ void renderNormals(LLDrawable *drawablep)
// transform. We get that effect here by pre-applying the inverse scale (twice, because
// one forward scale will be re-applied via the MVP in the vertex shader)
- LLVector3 scale_v3 = vol->getScale();
- float scale_len = scale_v3.length();
- LLVector4a obj_scale(scale_v3.mV[VX], scale_v3.mV[VY], scale_v3.mV[VZ]);
- obj_scale.normalize3();
+ LLVector4a inv_scale;
+ float scale_len;
+ if (vol)
+ {
+ LLVector3 scale_v3 = vol->getScale();
+ LLVector4a obj_scale(scale_v3.mV[VX], scale_v3.mV[VY], scale_v3.mV[VZ]);
+ obj_scale.normalize3();
- // Normals &tangent line segments get scaled along with the object. Divide by scale length
- // to keep the as-viewed lengths (relatively) constant with the debug setting length
- float draw_length = gSavedSettings.getF32("RenderDebugNormalScale") / scale_len;
+ // Create inverse-scale vector for normals
+ inv_scale.set(1.0 / scale_v3.mV[VX], 1.0 / scale_v3.mV[VY], 1.0 / scale_v3.mV[VZ], 0.0);
+ inv_scale.mul(inv_scale); // Squared, to apply inverse scale twice
- // Create inverse-scale vector for normals
- LLVector4a inv_scale(1.0 / scale_v3.mV[VX], 1.0 / scale_v3.mV[VY], 1.0 / scale_v3.mV[VZ]);
- inv_scale.mul(inv_scale); // Squared, to apply inverse scale twice
- inv_scale.normalize3fast();
+ inv_scale.normalize3fast();
+ scale_len = scale_v3.length();
+ }
+ else
+ {
+ inv_scale.set(1.0, 1.0, 1.0, 0.0);
+ scale_len = 1.0;
+ }
gGL.pushMatrix();
- gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix);
+ if (vol)
+ {
+ gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix);
+ }
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
+ // Normals &tangent line segments get scaled along with the object. Divide by scale length
+ // to keep the as-viewed lengths (relatively) constant with the debug setting length
+ float draw_length = gSavedSettings.getF32("RenderDebugNormalScale") / scale_len;
+
+ std::vector<LLVolumeFace>* faces = nullptr;
+ std::vector<LLFace*>* drawable_faces = nullptr;
+ if (vol)
+ {
+ 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)
- {
- LLVector4a n, p;
+ if (faces)
+ {
+ for (auto it = faces->begin(); it != faces->end(); ++it)
+ {
+ const LLVolumeFace& face = *it;
+
+ gGL.flush();
+ gGL.diffuseColor4f(1, 1, 0, 1);
+ gGL.begin(LLRender::LINES);
+ for (S32 j = 0; j < face.mNumVertices; ++j)
+ {
+ LLVector4a n, p;
- 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);
+ 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();
+ 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;
- // Tangents are simple vectors and do not require reorientation via pre-scaling
- if (face.mTangents)
+ t.setMul(face.mTangents[j], 1.0f);
+ t.normalize3fast();
+ t.mul(draw_length);
+ p.setAdd(face.mPositions[j], t);
+
+ gGL.vertex3fv(face.mPositions[j].getF32ptr());
+ gGL.vertex3fv(p.getF32ptr());
+ }
+ gGL.end();
+ }
+ }
+ }
+ else if (drawable_faces)
+ {
+ // *HACK: Prepare to restore previous shader as other debug code depends on a simpler shader being present
+ llassert(LLGLSLShader::sCurBoundShaderPtr == &gDebugProgram);
+ LLGLSLShader* prev_shader = LLGLSLShader::sCurBoundShaderPtr;
+ for (auto it = drawable_faces->begin(); it != drawable_faces->end(); ++it)
{
- gGL.flush();
- gGL.diffuseColor4f(0, 1, 1, 1);
- gGL.begin(LLRender::LINES);
- for (S32 j = 0; j < face.mNumVertices; ++j)
+ LLFace* facep = *it;
+ LLFace& face = **it;
+ LLVertexBuffer* buf = face.getVertexBuffer();
+ if (!buf) { continue; }
+ U32 mask_vn = LLVertexBuffer::TYPE_VERTEX | LLVertexBuffer::TYPE_NORMAL;
+ if ((buf->getTypeMask() & mask_vn) != mask_vn) { continue; }
+
+ LLGLSLShader* shader;
+ if ((buf->getTypeMask() & LLVertexBuffer::TYPE_TANGENT) != LLVertexBuffer::TYPE_TANGENT)
{
- LLVector4a t, p;
+ shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_DEFAULT];
+ }
+ else
+ {
+ shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_WITH_TANGENTS];
+ }
+ shader->bind();
- t.setMul(face.mTangents[j], 1.0f);
- t.normalize3fast();
- t.mul(draw_length);
- p.setAdd(face.mPositions[j], t);
+ shader->uniform1f(LLShaderMgr::DEBUG_NORMAL_DRAW_LENGTH, draw_length);
- gGL.vertex3fv(face.mPositions[j].getF32ptr());
- gGL.vertex3fv(p.getF32ptr());
- }
- gGL.end();
+ LLRenderPass::applyModelMatrix(&facep->getDrawable()->getRegion()->mRenderMatrix);
+
+ buf->setBuffer();
+ // *NOTE: The render type in the vertex shader is TRIANGLES, but gets converted to LINES in the geometry shader
+ // *NOTE: For terrain normal debug, this seems to also include vertices for water, which is technically not part of the terrain. Should fix that at some point.
+ buf->drawRange(LLRender::TRIANGLES, face.getGeomIndex(), face.getGeomIndex() + face.getGeomCount()-1, face.getIndicesCount(), face.getIndicesStart());
}
- }
+ if (prev_shader)
+ {
+ prev_shader->bind();
+ }
+ }
gGL.popMatrix();
}
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index 1418499f8b..804afe6266 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -643,17 +643,18 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
}
}
-BOOL LLSurface::idleUpdate(F32 max_update_time)
+template<bool PBR>
+bool LLSurface::idleUpdate(F32 max_update_time)
{
if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
{
- return FALSE;
+ return false;
}
// Perform idle time update of non-critical stuff.
// In this case, texture and normal updates.
LLTimer update_timer;
- BOOL did_update = FALSE;
+ bool did_update = false;
// If the Z height data has changed, we need to rebuild our
// property line vertex arrays.
@@ -669,13 +670,13 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
{
std::set<LLSurfacePatch *>::iterator curiter = iter++;
LLSurfacePatch *patchp = *curiter;
- patchp->updateNormals();
+ patchp->updateNormals<PBR>();
patchp->updateVerticalStats();
if (max_update_time == 0.f || update_timer.getElapsedTimeF32() < max_update_time)
{
if (patchp->updateTexture())
{
- did_update = TRUE;
+ did_update = true;
patchp->clearDirty();
mDirtyPatchList.erase(curiter);
}
@@ -691,6 +692,9 @@ BOOL LLSurface::idleUpdate(F32 max_update_time)
return did_update;
}
+template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
+template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
+
void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch)
{
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index 33a64ae7d5..b7b47d2a1c 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -112,7 +112,8 @@ public:
LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
// Update methods (called during idle, normally)
- BOOL idleUpdate(F32 max_update_time);
+ template<bool PBR>
+ bool idleUpdate(F32 max_update_time);
BOOL containsPosition(const LLVector3 &position);
@@ -224,6 +225,9 @@ private:
static S32 sTextureSize; // Size of the surface texture
};
+extern template bool LLSurface::idleUpdate</*PBR=*/false>(F32 max_update_time);
+extern template bool LLSurface::idleUpdate</*PBR=*/true>(F32 max_update_time);
+
// . __.
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
index a6370e9ec2..92b1273041 100644
--- a/indra/newview/llsurfacepatch.cpp
+++ b/indra/newview/llsurfacepatch.cpp
@@ -243,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();
@@ -356,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();
@@ -453,6 +614,7 @@ void LLSurfacePatch::updateVerticalStats()
}
+template<bool PBR>
void LLSurfacePatch::updateNormals()
{
if (mSurfacep->mType == 'w')
@@ -470,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;
@@ -483,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;
@@ -496,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;
}
@@ -507,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;
}
@@ -584,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;
}
@@ -598,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;
@@ -615,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();
diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h
index 8c8f501dce..ec3864ce44 100644
--- a/indra/newview/llsurfacepatch.h
+++ b/indra/newview/llsurfacepatch.h
@@ -77,6 +77,7 @@ public:
void updateVerticalStats();
void updateCompositionStats();
+ template<bool PBR>
void updateNormals();
void updateEastEdge();
@@ -102,9 +103,18 @@ public:
LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset.
LLVector2 getTexCoords(const U32 x, const U32 y) const;
+ // Per-vertex normals
+ // *TODO: PBR=true is a test implementation solely for proof-of-concept.
+ // Final implementation would likely be very different and may not even use
+ // this function. If we decide to keep calcNormalFlat, remove index as it
+ // is a debug parameter for testing.
+ template<bool PBR>
void calcNormal(const U32 x, const U32 y, const U32 stride);
const LLVector3 &getNormal(const U32 x, const U32 y) const;
+ // Per-triangle normals for flat edges
+ void calcNormalFlat(LLVector3& normal_out, const U32 x, const U32 y, const U32 index /* 0 or 1 */);
+
void eval(const U32 x, const U32 y, const U32 stride,
LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1);
@@ -181,5 +191,8 @@ protected:
LLSurface *mSurfacep; // Pointer to "parent" surface
};
+extern template void LLSurfacePatch::updateNormals</*PBR=*/false>();
+extern template void LLSurfacePatch::updateNormals</*PBR=*/true>();
+
#endif // LL_LLSURFACEPATCH_H
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 9db9d97ddc..da7b1131a3 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -2122,6 +2122,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 //
///////////////////
@@ -9492,6 +9506,10 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickRenderProfile(), "Advanced.ClickRenderProfile");
view_listener_t::addMenu(new LLAdvancedClickRenderBenchmark(), "Advanced.ClickRenderBenchmark");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
+ if (gSavedSettings.get<bool>("RenderTerrainPBREnabled"))
+ {
+ view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
+ }
#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode");
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index dae476d9d7..d541bb6647 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1105,6 +1105,11 @@ void LLViewerRegion::dirtyHeights()
}
}
+void LLViewerRegion::dirtyAllPatches()
+{
+ getLand().dirtyAllPatches();
+}
+
//physically delete the cache entry
void LLViewerRegion::killCacheEntry(LLVOCacheEntry* entry, bool for_rendering)
{
@@ -1605,7 +1610,19 @@ void LLViewerRegion::idleUpdate(F32 max_update_time)
mLastUpdate = LLViewerOctreeEntryData::getCurrentFrame();
- mImpl->mLandp->idleUpdate(max_update_time);
+ static bool pbr_terrain_enabled = gSavedSettings.get<bool>("RenderTerrainPBREnabled");
+ 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)
{
@@ -1906,7 +1923,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 bool pbr_terrain_enabled = gSavedSettings.get<BOOL>("RenderTerrainPBREnabled");
+ 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)
{
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 622490c881..1c2ff9bc97 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -163,6 +163,9 @@ public:
// Call this whenever you change the height data in the region.
// (Automatically called by LLSurfacePatch's update routine)
void dirtyHeights();
+ // Call this whenever you want to force all terrain to rebuild.
+ // (For example, if a global terrain config option has changed)
+ void dirtyAllPatches();
LLViewerParcelOverlay *getParcelOverlay() const
{ return mParcelOverlay; }
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 48c54f3dcc..7e49ea3b7c 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -91,6 +91,8 @@ 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;
@@ -2694,6 +2696,33 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
if (success)
{
+ for (S32 variant = 0; variant < NORMAL_DEBUG_SHADER_COUNT; ++variant)
+ {
+ LLGLSLShader& shader = gNormalDebugProgram[variant];
+ LLGLSLShader& skinned_shader = gSkinnedNormalDebugProgram[variant];
+ shader.mName = "Normal Debug Shader";
+ shader.mShaderFiles.clear();
+ shader.mShaderFiles.push_back(make_pair("interface/normaldebugV.glsl", GL_VERTEX_SHADER));
+ // *NOTE: Geometry shaders have a reputation for being slow.
+ // Consider using compute shaders instead, which have a reputation
+ // for being fast. This geometry shader in particular seems to run
+ // fine on my machine, but I won't vouch for this in
+ // performance-critical areas. -Cosmic,2023-09-28
+ shader.mShaderFiles.push_back(make_pair("interface/normaldebugG.glsl", GL_GEOMETRY_SHADER));
+ shader.mShaderFiles.push_back(make_pair("interface/normaldebugF.glsl", GL_FRAGMENT_SHADER));
+ shader.mRiggedVariant = &skinned_shader;
+ shader.mShaderLevel = mShaderLevel[SHADER_INTERFACE];
+ if (variant == NORMAL_DEBUG_SHADER_WITH_TANGENTS)
+ {
+ shader.addPermutation("HAS_ATTRIBUTE_TANGENT", "1");
+ }
+ success = make_rigged_variant(shader, skinned_shader);
+ success = success && shader.createShader(NULL, NULL);
+ }
+ }
+
+ if (success)
+ {
gClipProgram.mName = "Clip Shader";
gClipProgram.mShaderFiles.clear();
gClipProgram.mShaderFiles.push_back(make_pair("interface/clipV.glsl", GL_VERTEX_SHADER));
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index cb1729cd1b..4273c3e70c 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -156,6 +156,14 @@ extern LLGLSLShader gRadianceGenProgram;
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;
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index 9544450a69..69b9476d38 100644
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -214,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;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index d81bcef259..d63b9d317e 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -8241,6 +8241,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);
@@ -10869,3 +10870,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 5ace90b000..fb28a0e41c 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -134,6 +134,8 @@ public:
// rebuild all LLVOVolume render batches
void rebuildDrawInfo();
+ // Rebuild all terrain
+ void rebuildTerrain();
// Clear LLFace mVertexBuffer pointers
void resetVertexBuffers(LLDrawable* drawable);
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 660f4b62c7..9b238693e0 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3297,6 +3297,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"