Merge pull request #710 from secondlife/vi-43

Add PBR terrain normal gen feature flag
meow-7.2.2
cosmic-linden 2024-02-01 09:56:21 -08:00 committed by GitHub
commit 7a5b0f5acf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 667 additions and 87 deletions

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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();
// 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
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();
if (vol)
{
gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix);
}
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
// 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
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();
gGL.pushMatrix();
gGL.multMatrix((F32 *) vol->getRelativeXform().mMatrix);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
std::vector<LLVolumeFace>* faces = nullptr;
std::vector<LLFace*>* drawable_faces = nullptr;
if (vol)
{
const LLVolumeFace &face = volume->getVolumeFace(i);
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);
gGL.vertex3fv(face.mPositions[j].getF32ptr());
gGL.vertex3fv(p.getF32ptr());
}
gGL.end();
// Tangents are simple vectors and do not require reorientation via pre-scaling
if (face.mTangents)
{
gGL.flush();
gGL.diffuseColor4f(0, 1, 1, 1);
gGL.begin(LLRender::LINES);
for (S32 j = 0; j < face.mNumVertices; ++j)
{
LLVector4a t, p;
t.setMul(face.mTangents[j], 1.0f);
t.normalize3fast();
t.mul(draw_length);
p.setAdd(face.mPositions[j], t);
gGL.vertex3fv(face.mPositions[j].getF32ptr());
gGL.vertex3fv(p.getF32ptr());
}
gGL.end();
}
LLVolume* volume = vol->getVolume();
faces = &volume->getVolumeFaces();
}
else
{
drawable_faces = &drawablep->getFaces();
}
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);
gGL.vertex3fv(face.mPositions[j].getF32ptr());
gGL.vertex3fv(p.getF32ptr());
}
gGL.end();
// Tangents are simple vectors and do not require reorientation via pre-scaling
if (face.mTangents)
{
gGL.flush();
gGL.diffuseColor4f(0, 1, 1, 1);
gGL.begin(LLRender::LINES);
for (S32 j = 0; j < face.mNumVertices; ++j)
{
LLVector4a t, p;
t.setMul(face.mTangents[j], 1.0f);
t.normalize3fast();
t.mul(draw_length);
p.setAdd(face.mPositions[j], t);
gGL.vertex3fv(face.mPositions[j].getF32ptr());
gGL.vertex3fv(p.getF32ptr());
}
gGL.end();
}
}
}
else if (drawable_faces)
{
// *HACK: Prepare to restore previous shader as other debug code depends on a simpler shader being present
llassert(LLGLSLShader::sCurBoundShaderPtr == &gDebugProgram);
LLGLSLShader* prev_shader = LLGLSLShader::sCurBoundShaderPtr;
for (auto it = drawable_faces->begin(); it != drawable_faces->end(); ++it)
{
LLFace* facep = *it;
LLFace& face = **it;
LLVertexBuffer* buf = face.getVertexBuffer();
if (!buf) { continue; }
U32 mask_vn = LLVertexBuffer::TYPE_VERTEX | LLVertexBuffer::TYPE_NORMAL;
if ((buf->getTypeMask() & mask_vn) != mask_vn) { continue; }
LLGLSLShader* shader;
if ((buf->getTypeMask() & LLVertexBuffer::TYPE_TANGENT) != LLVertexBuffer::TYPE_TANGENT)
{
shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_DEFAULT];
}
else
{
shader = &gNormalDebugProgram[NORMAL_DEBUG_SHADER_WITH_TANGENTS];
}
shader->bind();
shader->uniform1f(LLShaderMgr::DEBUG_NORMAL_DRAW_LENGTH, draw_length);
LLRenderPass::applyModelMatrix(&facep->getDrawable()->getRegion()->mRenderMatrix);
buf->setBuffer();
// *NOTE: The render type in the vertex shader is TRIANGLES, but gets converted to LINES in the geometry shader
// *NOTE: For terrain normal debug, this seems to also include vertices for water, which is technically not part of the terrain. Should fix that at some point.
buf->drawRange(LLRender::TRIANGLES, face.getGeomIndex(), face.getGeomIndex() + face.getGeomCount()-1, face.getIndicesCount(), face.getIndicesStart());
}
if (prev_shader)
{
prev_shader->bind();
}
}
gGL.popMatrix();
}

View File

@ -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)
{

View File

@ -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);
// . __.

View File

@ -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();

View File

@ -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

View File

@ -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");

View File

@ -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)
{

View File

@ -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; }

View File

@ -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;
@ -2692,6 +2694,33 @@ BOOL LLViewerShaderMgr::loadShadersInterface()
success = success && gDebugProgram.createShader(NULL, NULL);
}
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";

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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"