diff options
| author | cosmic-linden <111533034+cosmic-linden@users.noreply.github.com> | 2024-02-01 09:56:21 -0800 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-01 09:56:21 -0800 | 
| commit | 7a5b0f5acf0b8aee3e73af0eb15d8c6930b98243 (patch) | |
| tree | bb929645ac6951fe111a5e9b320c9841d67784e1 | |
| parent | 9c378ca1e74229b5b1bb25fd63be5f1cf8349184 (diff) | |
| parent | 118996225bb588dcaef415dfe18f5e4aca9c35b8 (diff) | |
Merge pull request #710 from secondlife/vi-43
Add PBR terrain normal gen feature flag
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" | 
