From 4811e4263216a2f282cfe146e247578b50005e42 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Thu, 8 Aug 2024 11:04:35 -0700 Subject: (WIP) Local paintmap modification test --- indra/newview/llterrainpaintmap.cpp | 118 ++++++++++++++++++++- indra/newview/llterrainpaintmap.h | 38 +++++++ indra/newview/llviewermenu.cpp | 46 ++++++++ indra/newview/llvlcomposition.cpp | 8 ++ indra/newview/llvlcomposition.h | 6 ++ indra/newview/skins/default/xui/en/menu_viewer.xml | 7 ++ 6 files changed, 221 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 8ccde74c93..6979464cd3 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -38,18 +38,31 @@ #include "llsurface.h" #include "llsurfacepatch.h" #include "llviewercamera.h" +#include "llviewercontrol.h" #include "llviewerregion.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -// static -bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +namespace +{ +#ifdef SHOW_ASSERT +void check_tex(const LLViewerTexture& tex) { llassert(tex.getComponents() == 3); llassert(tex.getWidth() > 0 && tex.getHeight() > 0); llassert(tex.getWidth() == tex.getHeight()); llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); +} +#endif +} // namespace + +// static +bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -283,3 +296,104 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& return success; } + +// TODO: Decide when to apply the paint queue - ideally once per frame per region +// Applies paints and then clears the paint queue +// *NOTE The paint queue is also cleared when setting the paintmap texture +void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue) +{ + if (queue.empty()) { return; } + +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + + gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); + + const std::vector& queue_list = queue.get(); + for (size_t i = 0; i < queue_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + queue.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint = queue_list[i]; + + if (paint->mData.empty()) { continue; } + constexpr GLint level = 0; + if ((paint->mStartX >= tex.getWidth() - 1) || (paint->mStartY >= tex.getHeight() - 1)) { continue; } + constexpr GLint miplevel = 0; + const S32 x_offset = paint->mStartX; + const S32 y_offset = paint->mStartY; + const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset); + const U8* pixels = paint->mData.data(); + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels); + stop_glerror(); + } + + // Generating mipmaps at the end... + glGenerateMipmap(GL_TEXTURE_2D); + stop_glerror(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + queue.clear(); +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) +{ + llassert(paint); + if (!paint) { return false; } + + // The paint struct should be pre-validated before this code is reached. + llassert(!paint->mData.empty()); + // The internal paint map image is currently 8 bits, so that's the maximum + // allowed bit depth. + llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); + llassert(paint->mData.size() == (LLTerrainPaint::COMPONENTS * paint->mWidthX * paint->mWidthY)); + llassert(paint->mWidthX > 0); + llassert(paint->mWidthY > 0); +#ifdef SHOW_ASSERT + static LLCachedControl max_texture_width(gSavedSettings, "RenderMaxTextureResolution", 2048); +#endif + llassert(paint->mWidthX <= max_texture_width); + llassert(paint->mWidthY <= max_texture_width); + llassert(paint->mStartX < max_texture_width); + llassert(paint->mStartY < max_texture_width); + + mList.push_back(paint); + return true; +} + +bool LLTerrainPaintQueue::empty() const +{ + return mList.empty(); +} + +void LLTerrainPaintQueue::clear() +{ + mList.clear(); +} + +void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) +{ + llassert(target_bit_depth > 0 && target_bit_depth <= 8); + llassert(index < mList.size()); + + LLTerrainPaint::ptr_t& paint = mList[index]; + if (paint->mBitDepth == target_bit_depth) { return; } + + const F32 old_bit_max = F32((1 << paint->mBitDepth) - 1); + const F32 new_bit_max = F32((1 << target_bit_depth) - 1); + const F32 bit_conversion_factor = new_bit_max / old_bit_max; + + for (U8& color : paint->mData) + { + color = (U8)llround(F32(color) * bit_conversion_factor); + } + + paint->mBitDepth = target_bit_depth; +} diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 66827862c5..9189f12cbd 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -28,6 +28,7 @@ class LLViewerRegion; class LLViewerTexture; +class LLTerrainPaintQueue; class LLTerrainPaintMap { @@ -39,4 +40,41 @@ public: // to type TERRAIN_PAINT_TYPE_PBR_PAINTMAP. // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + + static void applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue); +}; + +// Enqueued paint operations, in texture coordinates. +// data is always RGB, with each U8 storing one color in the provided bit depth. +class LLTerrainPaint +{ +public: + using ptr_t = std::shared_ptr; + + U16 mStartX; + U16 mStartY; + U16 mWidthX; + U16 mWidthY; + U8 mBitDepth; + static const U8 COMPONENTS = 3; + std::vector mData; +}; + +class LLTerrainPaintQueue +{ +public: + bool enqueue(LLTerrainPaint::ptr_t& paint); + bool empty() const; + void clear(); + + const std::vector& get() const { return mList; } + + // Convert mBitDepth for the LLTerrainPaint in the queue at index + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + void convertBitDepths(size_t index, U8 target_bit_depth); + +private: + std::vector mList; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 10506e0e74..fa6e8870b3 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -117,6 +117,7 @@ #include "lltoolmgr.h" #include "lltoolpie.h" #include "lltoolselectland.h" +#include "llterrainpaintmap.h" #include "lltrans.h" #include "llviewerdisplay.h" //for gWindowResized #include "llviewergenericmessage.h" @@ -1448,6 +1449,50 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t } }; +class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerTexture* tex = gLocalTerrainMaterials.getPaintMap(); + if (!tex) + { + LL_WARNS() << "No local paint map available to edit" << LL_ENDL; + return false; + } + + LLTerrainPaintQueue& paint_queue = gLocalTerrainMaterials.getPaintQueue(); + + // Enqueue a paint + // Overrides an entire region patch with the material in the last slot + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + LLTerrainPaint::ptr_t paint = std::make_shared(); + const U16 width = U16(tex->getWidth() / 16); + paint->mStartX = width - 1; + paint->mStartY = width - 1; + paint->mWidthX = width; + paint->mWidthY = width; + constexpr U8 bit_depth = 5; + paint->mBitDepth = bit_depth; + constexpr U8 max_value = (1 << bit_depth) - 1; + const size_t pixel_count = width * width; + paint->mData.resize(LLTerrainPaint::COMPONENTS * pixel_count); + for (size_t pixel = 0; pixel < pixel_count; ++pixel) + { + paint->mData[(LLTerrainPaint::COMPONENTS*pixel) + LLTerrainPaint::COMPONENTS - 1] = max_value; + } + paint_queue.enqueue(paint); + + // Apply the paint queue ad-hoc right here for now. + // *TODO: Eventually the paint queue should be applied at a predictable + // time in the viewer frame loop. + LLTerrainPaintMap::applyPaintQueue(*tex, paint_queue); + + return true; + } +}; + class LLAdvancedTerrainDeleteLocalPaintMap : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -9813,6 +9858,7 @@ void initialize_menus() // Develop > Terrain view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); view_listener_t::addMenu(new LLAdvancedTerrainCreateLocalPaintMap(), "Advanced.TerrainCreateLocalPaintMap"); + view_listener_t::addMenu(new LLAdvancedTerrainEditLocalPaintMap(), "Advanced.TerrainEditLocalPaintMap"); view_listener_t::addMenu(new LLAdvancedTerrainDeleteLocalPaintMap(), "Advanced.TerrainDeleteLocalPaintMap"); // Advanced > UI diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 077e6e6cb1..d87658ba89 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -317,10 +317,18 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() return mPaintMap.get(); } +LLTerrainPaintQueue& LLTerrainMaterials::getPaintQueue() +{ + return mPaintQueue; +} + void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); + const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; + // The paint map has changed, so edits are no longer valid + mPaintQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index f15f9bff6a..972a46d8db 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -27,10 +27,13 @@ #ifndef LL_LLVLCOMPOSITION_H #define LL_LLVLCOMPOSITION_H +#include + #include "llviewerlayer.h" #include "llviewershadermgr.h" #include "llviewertexture.h" #include "llpointer.h" +#include "llterrainpaintmap.h" #include "llimage.h" @@ -87,6 +90,8 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Paint queue for current paint map + LLTerrainPaintQueue& getPaintQueue(); protected: void unboost(); @@ -105,6 +110,7 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; + LLTerrainPaintQueue mPaintQueue; }; // Local materials to override all regions diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index e1d33e5bc3..74ced74178 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3824,6 +3824,13 @@ function="World.EnvPreset" + + + Date: Fri, 9 Aug 2024 17:23:43 -0700 Subject: secondlife/viewer#1883: (WIP) Alpha paint queue --- indra/llrender/llglslshader.cpp | 22 ++ indra/llrender/llglslshader.h | 2 + indra/llrender/llshadermgr.cpp | 3 +- indra/llrender/llshadermgr.h | 1 + .../shaders/class1/interface/pbrTerrainBakeF.glsl | 2 +- .../shaders/class1/interface/terrainStampF.glsl | 44 +++ .../shaders/class1/interface/terrainStampV.glsl | 39 +++ indra/newview/lldrawpoolterrain.cpp | 8 +- indra/newview/llterrainpaintmap.cpp | 326 ++++++++++++++++++++- indra/newview/llterrainpaintmap.h | 28 +- indra/newview/llviewermenu.cpp | 33 ++- indra/newview/llviewershadermgr.cpp | 19 ++ indra/newview/llviewershadermgr.h | 1 + indra/newview/llvlcomposition.cpp | 8 +- indra/newview/llvlcomposition.h | 13 +- 15 files changed, 510 insertions(+), 39 deletions(-) create mode 100644 indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl create mode 100644 indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl (limited to 'indra') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 6ba5463acd..bbbce1965e 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1138,6 +1138,28 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextu return uniform; } +// For LLImageGL-wrapped textures created via GL elsewhere with our API only. Use with caution. +S32 LLGLSLShader::bindTextureImageGL(S32 uniform, LLImageGL* texture, LLTexUnit::eTextureType mode) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + + if (uniform < 0 || uniform >= (S32)mTexture.size()) + { + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); + return -1; + } + + uniform = mTexture[uniform]; + + if (uniform > -1) + { + gGL.getTexUnit(uniform)->bind(texture); + } + + return uniform; +} + S32 LLGLSLShader::bindTexture(S32 uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode, U32 index) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 2d669c70a9..c24daaf686 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -264,6 +264,8 @@ public: // You can reuse the return value to unbind a texture when required. S32 bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); S32 bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + // For LLImageGL-wrapped textures created via GL elsewhere with our API only. Use with caution. + S32 bindTextureImageGL(S32 uniform, LLImageGL* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); S32 bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR); S32 bindTexture(S32 uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR, U32 index = 0); S32 unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 6097b09d96..796805e2a5 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1190,8 +1190,9 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("gltf_material_id"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) + mReservedUniforms.push_back("terrain_stamp_scale"); - llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS +1); + llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_STAMP_SCALE +1); mReservedUniforms.push_back("viewport"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 1bae0cd8a0..ff07ce454b 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -67,6 +67,7 @@ public: GLTF_MATERIAL_ID, // "gltf_material_id" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) + TERRAIN_STAMP_SCALE, // "terrain_stamp_scale" VIEWPORT, // "viewport" LIGHT_POSITION, // "light_position" diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl index cf20653a0f..a79a56d725 100644 --- a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl @@ -1,5 +1,5 @@ /** - * @file terrainBakeF.glsl + * @file pbrTerrainBakeF.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl new file mode 100644 index 0000000000..e79e9010e6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl @@ -0,0 +1,44 @@ +/** + * @file terrainStampF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +// Paint texture to stamp into the "paintmap" +uniform sampler2D diffuseMap; + +in vec2 vary_texcoord0; + +void main() +{ + vec4 col = texture(diffuseMap, vary_texcoord0); + if (col.a <= 0.0f) + { + discard; + } + + frag_color = max(col, vec4(0)); +} diff --git a/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl new file mode 100644 index 0000000000..294fa6be26 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl @@ -0,0 +1,39 @@ +/** + * @file terrainStampV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +uniform mat4 modelview_projection_matrix; +uniform vec2 terrain_stamp_scale; + +in vec3 position; + +out vec2 vary_texcoord0; + +void main() +{ + gl_Position = modelview_projection_matrix * vec4(position, 1.0); + // Positions without transforms are treated as UVs for the purpose of this shader. + vary_texcoord0.xy = terrain_stamp_scale * position.xy; +} + diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 5e676bc5b3..57def49539 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -296,7 +296,7 @@ void LLDrawPoolTerrain::renderFullShaderTextures() // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL1); @@ -557,7 +557,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool use_local_materials) // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE) { @@ -769,7 +769,7 @@ void LLDrawPoolTerrain::renderFull4TU() } LLVertexBuffer::unbind(); - // Disable multitexture + // Disable textures gGL.getTexUnit(3)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(3)->disable(); gGL.getTexUnit(3)->activate(); @@ -949,7 +949,7 @@ void LLDrawPoolTerrain::renderFull2TU() // Restore blend state gGL.setSceneBlendType(LLRender::BT_ALPHA); - // Disable multitexture + // Disable textures gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(1)->disable(); diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 6979464cd3..7dc09a4748 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -31,6 +31,8 @@ // library includes #include "llglslshader.h" #include "llrendertarget.h" +#include "llrender2dutils.h" +#include "llshadermgr.h" #include "llvertexbuffer.h" // newview includes @@ -89,8 +91,8 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& // Bind the debug shader and render terrain to tex // Use a scratch render target because its dimensions may exceed the standard bake target, and this is a one-off bake LLRenderTarget scratch_target; - const S32 dim = llmin(tex.getWidth(), tex.getHeight()); - scratch_target.allocate(dim, dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, LLTexUnit::eTextureMipGeneration::TMG_NONE); if (!scratch_target.isComplete()) { @@ -117,6 +119,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const F32 region_half_width = region_width / 2.0f; const F32 region_camera_height = surface.getMaxZ() + DEFAULT_NEAR_PLANE; LLViewerCamera camera; + // TODO: Huh... I just realized this view vector is not completely vertical const LLVector3 region_center = LLVector3(region_half_width, region_half_width, 0.0) + region.getOriginAgent(); const LLVector3 camera_origin = LLVector3(0.0f, 0.0f, region_camera_height) + region_center; camera.lookAt(camera_origin, region_center, LLVector3::y_axis); @@ -250,6 +253,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& S32 alpha_ramp = shader.enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); LLPointer alpha_ramp_texture = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD_2D); + // TODO: Consider using LLGLSLShader::bindTexture gGL.getTexUnit(alpha_ramp)->bind(alpha_ramp_texture); gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); @@ -263,6 +267,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const U32 vertex_offset = n * patch_index; llassert(index_offset + ni <= region_indices); llassert(vertex_offset + n <= region_vertices); + // TODO: Try a single big drawRange and see if that still works buf->drawRange(LLRender::TRIANGLES, vertex_offset, vertex_offset + n - 1, ni, index_offset); } } @@ -282,7 +287,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& gGL.flush(); LLVertexBuffer::unbind(); // Final step: Copy the output to the terrain paintmap - const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, dim, dim); + const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, tex.getWidth(), tex.getHeight()); if (!success) { LL_WARNS() << "Failed to copy framebuffer to paintmap" << LL_ENDL; @@ -297,10 +302,10 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& return success; } -// TODO: Decide when to apply the paint queue - ideally once per frame per region +// *TODO: Decide when to apply the paint queue - ideally once per frame per region // Applies paints and then clears the paint queue // *NOTE The paint queue is also cleared when setting the paintmap texture -void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue) +void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue) { if (queue.empty()) { return; } @@ -310,6 +315,11 @@ void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueu gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); + // glTexSubImage2D replaces all pixels in the rectangular region. That + // makes it unsuitable for alpha. + llassert(queue.getComponents() == LLTerrainPaint::RGB); + constexpr GLenum pixformat = GL_RGB; + const std::vector& queue_list = queue.get(); for (size_t i = 0; i < queue_list.size(); ++i) { @@ -328,8 +338,10 @@ void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueu const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset); const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset); const U8* pixels = paint->mData.data(); - constexpr GLenum pixformat = GL_RGB; constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + // *TODO: Performance suggestion: Use the sub-image utility function + // that LLImageGL::setSubImage uses to split texture updates into + // lines, if that's faster. glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels); stop_glerror(); } @@ -343,7 +355,284 @@ void LLTerrainPaintMap::applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueu queue.clear(); } -bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) +namespace +{ + +// A general-purpose vertex buffer of a quad for stamping textures on the z=0 +// plane. +// *NOTE: Because we know the vertex XY coordinates go from 0 to 1 +// pre-transform, UVs can be calculated from the vertices +LLVertexBuffer& get_paint_triangle_buffer() +{ + static LLPointer buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); + static bool initialized = false; + if (!initialized) + { + // Two triangles forming a square from (0,0) to (1,1) + buf->allocateBuffer(/*vertices =*/ 4, /*indices =*/ 6); + LLStrider indices; + LLStrider vertices; + buf->getVertexStrider(vertices); + buf->getIndexStrider(indices); + // y + // 2....3 + // ^ . . + // | 0....1 + // | + // -------> x + // + // triangle 1: 0,1,2 + // triangle 2: 1,3,2 + (*(vertices++)).set(0.0f, 0.0f, 0.0f); + (*(vertices++)).set(1.0f, 0.0f, 0.0f); + (*(vertices++)).set(0.0f, 1.0f, 0.0f); + (*(vertices++)).set(1.0f, 1.0f, 0.0f); + *(indices++) = 0; + *(indices++) = 1; + *(indices++) = 2; + *(indices++) = 1; + *(indices++) = 3; + *(indices++) = 2; + buf->unmapBuffer(); + } + return *buf; +} + +}; + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the paints, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = &get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glh::matrix4f(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glh::matrix4f model; + { + model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each RGBA paint to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector& queue_in_list = queue_in.get(); + for (size_t i = 0; i < queue_in_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for paint operations (this + // could change in the future). + queue_in.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint_in = queue_in_list[i]; + + // Modelview matrix for the current paint + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + glh::matrix4f model; + { + model.set_scale(glh::vec3f(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model.set_translate(glh::vec3f(paint_in->mStartX, paint_in->mStartY, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + // Generate temporary stamp texture from paint contents. + // Our stamp image needs to be a power of two. + // Because the paint data may not cover a whole power-of-two region, + // allocate a bigger 2x2 image if needed, but set the image data later + // for a subset of the image. + // Pixel data outside this subset is left undefined. We will use + // TERRAIN_STAMP_SCALE in the stamp shader to define the subset of the + // image we care about. + const U32 width_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthX)))); + const U32 height_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthY)))); + LLPointer stamp_image; + { + // Create image object (dimensions not yet initialized in GL) + U32 stamp_tex_name; + LLImageGL::generateTextures(1, &stamp_tex_name); + const U32 components = paint_in->mComponents; + constexpr LLGLenum target = GL_TEXTURE_2D; + const LLGLenum internal_format = paint_in->mComponents == 4 ? GL_RGBA8 : GL_RGB8; + const LLGLenum format = paint_in->mComponents == 4 ? GL_RGBA : GL_RGB; + constexpr LLGLenum type = GL_UNSIGNED_BYTE; + stamp_image = new LLImageGL(stamp_tex_name, components, target, internal_format, format, type, LLTexUnit::TAM_WRAP); + // Nearest-neighbor filtering to reduce surprises + stamp_image->setFilteringOption(LLTexUnit::TFO_POINT); + + // Initialize the image dimensions in GL + constexpr U8* undefined_data_for_now = nullptr; + gGL.getTexUnit(0)->bind(stamp_image, false, true); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_rounded, height_rounded, 0, format, type, undefined_data_for_now); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + stamp_image->setSize(width_rounded, height_rounded, components); + stamp_image->setDiscardLevel(0); + + // Manually set a subset of the image in GL + const U8* data = paint_in->mData.data(); + const S32 data_width = paint_in->mWidthX; + const S32 data_height = paint_in->mWidthY; + constexpr S32 origin = 0; + // width = data_width; height = data_height. i.e.: Copy the full + // contents of data into the image. + stamp_image->setSubImage(data, data_width, data_height, origin, origin, /*width=*/data_width, /*height=*/data_height); + } + + // Apply ("stamp") the paint to the render target + { + shader.bindTextureImageGL(LLShaderMgr::DIFFUSE_MAP, stamp_image); + const F32 width_fraction = F32(paint_in->mWidthX) / F32(width_rounded); + const F32 height_fraction = F32(paint_in->mWidthY) / F32(height_rounded); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, width_fraction, height_fraction); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared(); + { + paint_out->mStartX = paint_in->mStartX; + paint_out->mStartY = paint_in->mStartY; + paint_out->mWidthX = paint_in->mWidthX; + paint_out->mWidthY = paint_in->mWidthY; + paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mComponents = LLTerrainPaint::RGB; + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = llmin(paint_out->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint_out->mWidthY, tex.getHeight() - y_offset); + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + queue_out.convertBitDepths(queue_out.size()-1, 5); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) +: mComponents(components) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(const LLTerrainPaintQueue& other) +{ + *this = other; + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(const LLTerrainPaintQueue& other) +{ + mComponents = other.mComponents; + mList = other.mList; + return *this; +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) { llassert(paint); if (!paint) { return false; } @@ -353,7 +642,7 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) // The internal paint map image is currently 8 bits, so that's the maximum // allowed bit depth. llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); - llassert(paint->mData.size() == (LLTerrainPaint::COMPONENTS * paint->mWidthX * paint->mWidthY)); + llassert(paint->mData.size() == (mComponents * paint->mWidthX * paint->mWidthY)); llassert(paint->mWidthX > 0); llassert(paint->mWidthY > 0); #ifdef SHOW_ASSERT @@ -364,10 +653,29 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint) llassert(paint->mStartX < max_texture_width); llassert(paint->mStartY < max_texture_width); - mList.push_back(paint); + if (!dry_run) { mList.push_back(paint); } return true; } +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& paint_queue) +{ + constexpr bool dry_run = true; + for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) + { + if (!enqueue(paint), dry_run) { return false; } + } + for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) + { + enqueue(paint); + } + return true; +} + +size_t LLTerrainPaintQueue::size() const +{ + return mList.size(); +} + bool LLTerrainPaintQueue::empty() const { return mList.empty(); diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 9189f12cbd..b4d706b107 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,6 +26,8 @@ #pragma once +#include "llviewerprecompiledheaders.h" + class LLViewerRegion; class LLViewerTexture; class LLTerrainPaintQueue; @@ -41,14 +43,15 @@ public: // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); - static void applyPaintQueue(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); }; // Enqueued paint operations, in texture coordinates. -// data is always RGB, with each U8 storing one color in the provided bit depth. -class LLTerrainPaint +// mData is always RGB or RGBA (determined by mComponents), with each U8 +// storing one color with a max value of (1 >> mBitDepth) - 1 +struct LLTerrainPaint { -public: using ptr_t = std::shared_ptr; U16 mStartX; @@ -56,25 +59,38 @@ public: U16 mWidthX; U16 mWidthY; U8 mBitDepth; - static const U8 COMPONENTS = 3; + U8 mComponents; + const static U8 RGB = 3; + const static U8 RGBA = 4; std::vector mData; }; class LLTerrainPaintQueue { public: - bool enqueue(LLTerrainPaint::ptr_t& paint); + // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) + LLTerrainPaintQueue(U8 components); + LLTerrainPaintQueue(const LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(const LLTerrainPaintQueue& other); + + bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); + bool enqueue(LLTerrainPaintQueue& paint_queue); + size_t size() const; bool empty() const; void clear(); const std::vector& get() const { return mList; } + U8 getComponents() const { return mComponents; } // Convert mBitDepth for the LLTerrainPaint in the queue at index + // If mBitDepth is already equal to target_bit_depth, no conversion takes + // place. // It is currently the responsibility of the paint queue to convert // incoming bits to the right bit depth for the paintmap (this could // change in the future). void convertBitDepths(size_t index, U8 target_bit_depth); private: + U8 mComponents; std::vector mList; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index fa6e8870b3..c4bb5eaa2e 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1435,6 +1435,7 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); dim = llclamp(dim, 16, max_resolution); dim = 1 << U32(std::ceil(std::log2(dim))); + // TODO: Could probably get away with not using image raw here, now that we aren't writing bits via the CPU (see load_exr for example) LLPointer image_raw = new LLImageRaw(dim,dim,3); LLPointer tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); @@ -1460,7 +1461,7 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t return false; } - LLTerrainPaintQueue& paint_queue = gLocalTerrainMaterials.getPaintQueue(); + LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); // Enqueue a paint // Overrides an entire region patch with the material in the last slot @@ -1477,17 +1478,31 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t paint->mBitDepth = bit_depth; constexpr U8 max_value = (1 << bit_depth) - 1; const size_t pixel_count = width * width; - paint->mData.resize(LLTerrainPaint::COMPONENTS * pixel_count); - for (size_t pixel = 0; pixel < pixel_count; ++pixel) + const U8 components = LLTerrainPaint::RGBA; + paint->mComponents = components; + paint->mData.resize(components * pixel_count); + for (size_t h = 0; h < paint->mWidthY; ++h) { - paint->mData[(LLTerrainPaint::COMPONENTS*pixel) + LLTerrainPaint::COMPONENTS - 1] = max_value; + for (size_t w = 0; w < paint->mWidthX; ++w) + { + const size_t pixel = (h * paint->mWidthX) + w; + // Solid blue color + paint->mData[(components*pixel) + components - 2] = max_value; // blue + // Alpha gradient from 0.0 to 1.0 along w + const U8 alpha = U8(F32(max_value) * F32(w+1) / F32(paint->mWidthX)); + paint->mData[(components*pixel) + components - 1] = alpha; // alpha + } } - paint_queue.enqueue(paint); + paint_request_queue.enqueue(paint); - // Apply the paint queue ad-hoc right here for now. - // *TODO: Eventually the paint queue should be applied at a predictable - // time in the viewer frame loop. - LLTerrainPaintMap::applyPaintQueue(*tex, paint_queue); + // Apply the paint queues ad-hoc right here for now. + // *TODO: Eventually the paint queue(s) should be applied at a + // predictable time in the viewer frame loop. + // TODO: In hindsight... maybe we *should* bind the paintmap to the render buffer. That makes a lot more sense, and we wouldn't have to reduce its resolution by settling for the bake buffer. If we do that, make a comment above convertPaintQueueRGBAToRGB that the texture is modified! + LLTerrainPaintQueue paint_send_queue = LLTerrainPaintMap::convertPaintQueueRGBAToRGB(*tex, paint_request_queue); + LLTerrainPaintQueue& paint_map_queue = gLocalTerrainMaterials.getPaintMapQueue(); + paint_map_queue.enqueue(paint_send_queue); + LLTerrainPaintMap::applyPaintQueueRGB(*tex, paint_map_queue); return true; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a8fe221d98..8e7af28d41 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -100,6 +100,7 @@ LLGLSLShader gReflectionProbeDisplayProgram; LLGLSLShader gCopyProgram; LLGLSLShader gCopyDepthProgram; LLGLSLShader gPBRTerrainBakeProgram; +LLGLSLShader gTerrainStampProgram; //object shaders LLGLSLShader gObjectPreviewProgram; @@ -3170,6 +3171,24 @@ bool LLViewerShaderMgr::loadShadersInterface() } } + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + llassert(success); + } + if (success) { gAlphaMaskProgram.mName = "Alpha Mask Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index b08796025a..e654967c46 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -175,6 +175,7 @@ extern LLGLSLShader gReflectionProbeDisplayProgram; extern LLGLSLShader gCopyProgram; extern LLGLSLShader gCopyDepthProgram; extern LLGLSLShader gPBRTerrainBakeProgram; +extern LLGLSLShader gTerrainStampProgram; //output tex0[tc0] - tex1[tc1] extern LLGLSLShader gTwoTextureCompareProgram; diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index d87658ba89..ca76d93cd7 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -317,18 +317,14 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() return mPaintMap.get(); } -LLTerrainPaintQueue& LLTerrainMaterials::getPaintQueue() -{ - return mPaintQueue; -} - void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; // The paint map has changed, so edits are no longer valid - mPaintQueue.clear(); + mPaintRequestQueue.clear(); + mPaintMapQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 972a46d8db..3f1124a8ac 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -90,8 +90,14 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); - // Paint queue for current paint map - LLTerrainPaintQueue& getPaintQueue(); + // Queue of client-triggered paint operations that need to be converted + // into a form that can be sent to the server. + // Paints in this queue are in RGBA format. + LLTerrainPaintQueue& getPaintRequestQueue() { return mPaintRequestQueue; } + // Paint queue for current paint map - this queue gets applied directly to + // the paint map. Paints within are assumed to have already been sent to + // the server. Paints in this queue are in RGB format. + LLTerrainPaintQueue& getPaintMapQueue() { return mPaintMapQueue; } protected: void unboost(); @@ -110,7 +116,8 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; - LLTerrainPaintQueue mPaintQueue; + LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; + LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; // Local materials to override all regions -- cgit v1.2.3 From 642ace3c75d08cc55370374f5a091f0c8ff41a4c Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 16 Aug 2024 17:27:57 -0700 Subject: secondlife/viewer#1883: (WIP) (not yet working) Brush queue --- indra/newview/llterrainpaintmap.cpp | 349 ++++++++++++++++++++++++++++++++---- indra/newview/llterrainpaintmap.h | 90 ++++++++-- indra/newview/llviewermenu.cpp | 40 ++++- indra/newview/llvlcomposition.h | 5 + 4 files changed, 434 insertions(+), 50 deletions(-) (limited to 'indra') diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 7dc09a4748..6a57605325 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -575,12 +575,16 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur paint_out->mWidthY = paint_in->mWidthY; paint_out->mBitDepth = 8; // Will be reduced to 5 bits later paint_out->mComponents = LLTerrainPaint::RGB; +#ifdef SHOW_ASSERT + paint_out->assert_confined_to(tex); +#endif + paint_out->confine_to(tex); paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); constexpr GLint miplevel = 0; const S32 x_offset = paint_out->mStartX; const S32 y_offset = paint_out->mStartY; - const S32 width = llmin(paint_out->mWidthX, tex.getWidth() - x_offset); - const S32 height = llmin(paint_out->mWidthY, tex.getHeight() - y_offset); + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; constexpr GLenum pixformat = GL_RGB; constexpr GLenum pixtype = GL_UNSIGNED_BYTE; llassert(paint_out->mData.size() <= std::numeric_limits::max()); @@ -613,22 +617,294 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur return queue_out; } +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) +{ +#ifdef SHOW_ASSERT + check_tex(tex); +#endif + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = &get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glh::matrix4f(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glh::matrix4f model; + { + model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each brush stroke to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector& brush_list = queue_in.get(); + for (size_t i = 0; i < brush_list.size(); ++i) + { + const LLTerrainBrush::ptr_t& brush_in = brush_list[i]; + + // Modelview matrix for the current brush + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + // *NOTE: Brush path information is in region space. It will need to be + // converted to paintmap pixel space before it makes sense. + F32 brush_width_x; + F32 brush_width_y; + F32 brush_start_x; + F32 brush_start_y; + { + F32 min_x = brush_in->mPath[0].mV[VX]; + F32 max_x = min_x; + F32 min_y = brush_in->mPath[0].mV[VY]; + F32 max_y = min_y; + for (size_t i = 1; i < brush_in->mPath.size(); ++i) + { + const F32 x = brush_in->mPath[i].mV[VX]; + const F32 y = brush_in->mPath[i].mV[VY]; + min_x = llmin(min_x, x); + max_x = llmax(max_x, x); + min_y = llmin(min_y, y); + max_y = llmax(max_y, y); + } + brush_width_x = brush_in->mBrushSize + (max_x - min_x); + brush_width_y = brush_in->mBrushSize + (max_y - min_y); + brush_start_x = min_x - (brush_in->mBrushSize / 2.0f); + brush_start_y = min_y - (brush_in->mBrushSize / 2.0f); + // Convert brush path information to paintmap pixel space from region + // space. + brush_width_x *= tex.getWidth() / region.getWidth(); + brush_width_y *= tex.getHeight() / region.getWidth(); + brush_start_x *= tex.getWidth() / region.getWidth(); + brush_start_y *= tex.getHeight() / region.getWidth(); + } + glh::matrix4f model; + { + model.set_scale(glh::vec3f(brush_width_x, brush_width_y, 1.0f)); + model.set_translate(glh::vec3f(brush_start_x, brush_start_y, 0.0f)); + } + glh::matrix4f modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(modelview.m); + + // Apply the "brush" to the render target + { + // TODO: Use different shader for this - currently this is using the stamp shader. The white image is just a placeholder for now + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared(); + { + paint_out->mStartX = U16(floor(brush_start_x)); + paint_out->mStartY = U16(floor(brush_start_y)); + const F32 dX = brush_start_x - F32(paint_out->mStartX); + const F32 dY = brush_start_y - F32(paint_out->mStartY); + paint_out->mWidthX = U16(ceil(brush_width_x + dX)); + paint_out->mWidthY = U16(ceil(brush_width_y + dY)); + paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mComponents = LLTerrainPaint::RGB; + // The brush strokes are expected to sometimes partially venture + // outside of the paintmap bounds. + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + queue_out.convertBitDepths(queue_out.size()-1, 5); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +template +LLTerrainQueue::LLTerrainQueue(LLTerrainQueue& other) +{ + *this = other; +} + +template +LLTerrainQueue& LLTerrainQueue::operator=(LLTerrainQueue& other) +{ + mList = other.mList; + return *this; +} + +template +bool LLTerrainQueue::enqueue(std::shared_ptr& t, bool dry_run) +{ + if (!dry_run) { mList.push_back(t); } + return true; +} + +template +bool LLTerrainQueue::enqueue(std::vector>& list) +{ + constexpr bool dry_run = true; + for (T::ptr_t& t : list) + { + if (!enqueue(t), dry_run) { return false; } + } + for (T::ptr_t& t : list) + { + enqueue(t); + } + return true; +} + +template +size_t LLTerrainQueue::size() const +{ + return mList.size(); +} + +template +bool LLTerrainQueue::empty() const +{ + return mList.empty(); +} + +template +void LLTerrainQueue::clear() +{ + mList.clear(); +} + +void LLTerrainPaint::assert_confined_to(const LLTexture& tex) const +{ + llassert(mStartX >= 0 && mStartX < tex.getWidth()); + llassert(mStartY >= 0 && mStartY < tex.getHeight()); + llassert(mWidthX <= tex.getWidth() - mStartX); + llassert(mWidthY <= tex.getHeight() - mStartY); +} + +void LLTerrainPaint::confine_to(const LLTexture& tex) +{ + mStartX = llmax(mStartX, 0); + mStartY = llmax(mStartY, 0); + mWidthX = llmin(mWidthX, tex.getWidth() - mStartX); + mWidthY = llmin(mWidthY, tex.getHeight() - mStartY); + assert_confined_to(tex); +} + LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) : mComponents(components) { llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); } -LLTerrainPaintQueue::LLTerrainPaintQueue(const LLTerrainPaintQueue& other) +LLTerrainPaintQueue::LLTerrainPaintQueue(LLTerrainPaintQueue& other) +: LLTerrainQueue(other) +, mComponents(other.mComponents) { - *this = other; llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); } -LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(const LLTerrainPaintQueue& other) +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(LLTerrainPaintQueue& other) { + LLTerrainQueue::operator=(other); mComponents = other.mComponents; - mList = other.mList; return *this; } @@ -653,37 +929,12 @@ bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) llassert(paint->mStartX < max_texture_width); llassert(paint->mStartY < max_texture_width); - if (!dry_run) { mList.push_back(paint); } - return true; + return LLTerrainQueue::enqueue(paint, dry_run); } -bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& paint_queue) +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& queue) { - constexpr bool dry_run = true; - for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) - { - if (!enqueue(paint), dry_run) { return false; } - } - for (LLTerrainPaint::ptr_t& paint : paint_queue.mList) - { - enqueue(paint); - } - return true; -} - -size_t LLTerrainPaintQueue::size() const -{ - return mList.size(); -} - -bool LLTerrainPaintQueue::empty() const -{ - return mList.empty(); -} - -void LLTerrainPaintQueue::clear() -{ - mList.clear(); + return LLTerrainQueue::enqueue(queue.mList); } void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) @@ -705,3 +956,33 @@ void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) paint->mBitDepth = target_bit_depth; } + +LLTerrainBrushQueue::LLTerrainBrushQueue() +: LLTerrainQueue() +{ +} + +LLTerrainBrushQueue::LLTerrainBrushQueue(LLTerrainBrushQueue& other) +: LLTerrainQueue(other) +{ +} + +LLTerrainBrushQueue& LLTerrainBrushQueue::operator=(LLTerrainBrushQueue& other) +{ + LLTerrainQueue::operator=(other); + return *this; +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run) +{ + llassert(brush->mBrushSize > 0); + llassert(!brush->mPath.empty()); + llassert(brush->mPathOffset < brush->mPath.size()); + llassert(brush->mPathOffset < 2); // Harmless, but doesn't do anything useful, so might be a sign of implementation error + return LLTerrainQueue::enqueue(brush, dry_run); +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue) +{ + return LLTerrainQueue::enqueue(queue.mList); +} diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index b4d706b107..cffdad80a2 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -28,10 +28,14 @@ #include "llviewerprecompiledheaders.h" +class LLTexture; class LLViewerRegion; class LLViewerTexture; class LLTerrainPaintQueue; +class LLTerrainBrushQueue; +// TODO: Terrain painting across regions. Assuming painting is confined to one +// region for now. class LLTerrainPaintMap { public: @@ -43,8 +47,34 @@ public: // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + // This operation clears the queue + // TODO: Decide if clearing the queue is needed - seems inconsistent static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); + // TODO: Implement (it's similar to convertPaintQueueRGBAToRGB but different shader + need to calculate the dimensions + need a different vertex buffer for each brush stroke) + static LLTerrainPaintQueue convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in); +}; + +template +class LLTerrainQueue +{ +public: + LLTerrainQueue() = default; + LLTerrainQueue(LLTerrainQueue& other); + LLTerrainQueue& operator=(LLTerrainQueue& other); + + bool enqueue(std::shared_ptr& t, bool dry_run = false); + size_t size() const; + bool empty() const; + void clear(); + + const std::vector>& get() const { return mList; } + +protected: + bool enqueue(std::vector>& list); + + std::vector> mList; }; // Enqueued paint operations, in texture coordinates. @@ -63,23 +93,27 @@ struct LLTerrainPaint const static U8 RGB = 3; const static U8 RGBA = 4; std::vector mData; + + // Asserts that this paint's start/width fit within the bounds of the + // provided texture dimensions. + void assert_confined_to(const LLTexture& tex) const; + // Confines this paint's start/width so it fits within the bounds of the + // provided texture dimensions. + // Does not allocate mData. + void confine_to(const LLTexture& tex); }; -class LLTerrainPaintQueue +class LLTerrainPaintQueue : public LLTerrainQueue { public: + LLTerrainPaintQueue() = delete; // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) LLTerrainPaintQueue(U8 components); - LLTerrainPaintQueue(const LLTerrainPaintQueue& other); - LLTerrainPaintQueue& operator=(const LLTerrainPaintQueue& other); + LLTerrainPaintQueue(LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(LLTerrainPaintQueue& other); bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); - bool enqueue(LLTerrainPaintQueue& paint_queue); - size_t size() const; - bool empty() const; - void clear(); - - const std::vector& get() const { return mList; } + bool enqueue(LLTerrainPaintQueue& queue); U8 getComponents() const { return mComponents; } // Convert mBitDepth for the LLTerrainPaint in the queue at index @@ -92,5 +126,41 @@ public: private: U8 mComponents; - std::vector mList; +}; + +struct LLTerrainBrush +{ + using ptr_t = std::shared_ptr; + + // Width of the brush in region space. The brush is a square texture with + // alpha. + F32 mBrushSize; + // Brush path points in region space, excluding the vertical axis, which + // does not contribute to the paint map. + std::vector mPath; + // Offset of the brush path to actually start drawing at. An offset of 0 + // indicates that a brush stroke has just started (i.e. the user just + // pressed down the mouse button). An offset greater than 0 indicates the + // continuation of a brush stroke. Skipped entries in mPath are not drawn + // directly, but are used for stroke orientation and path interpolation. + // TODO: For the initial implementation, mPathOffset will be 0 and mPath + // will be of length of at most 1, leading to discontinuous paint paths. + // Then, mPathOffset may be 1 or 0, 1 indicating the continuation of a + // stroke with linear interpolation. It is unlikely that we will implement + // anything more sophisticated than that for now. + U8 mPathOffset; + // Indicates if this is the end of the brush stroke. Can occur if the mouse + // button is lifted, or if the mouse temporarily stops while held down. + bool mPathEnd; +}; + +class LLTerrainBrushQueue : public LLTerrainQueue +{ +public: + LLTerrainBrushQueue(); + LLTerrainBrushQueue(LLTerrainBrushQueue& other); + LLTerrainBrushQueue& operator=(LLTerrainBrushQueue& other); + + bool enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run = false); + bool enqueue(LLTerrainBrushQueue& queue); }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index c4bb5eaa2e..dd6b6d1ca1 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1461,17 +1461,38 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t return false; } + LLTerrainBrushQueue& brush_queue = gLocalTerrainMaterials.getBrushQueue(); LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "No current region for calculating paint operations" << LL_ENDL; + return false; + } + // TODO: Create the brush + // Just a dab for now + LLTerrainBrush::ptr_t brush = std::make_shared(); + brush->mBrushSize = 16.0f; + brush->mPath.emplace_back(17.0f, 17.0f); + brush->mPathOffset = 0; + brush->mPathEnd = true; + brush_queue.enqueue(brush); + LLTerrainPaintQueue brush_as_paint_queue = LLTerrainPaintMap::convertBrushQueueToPaintRGB(*region, *tex, brush_queue); + //paint_send_queue.enqueue(brush_as_paint_queue); // TODO: What was this line for? (it might also be a leftover line from an unfinished edit) + + // TODO: Keep this around for later testing (i.e. when reducing framebuffer size and the offsets that requires) +#if 0 // Enqueue a paint - // Overrides an entire region patch with the material in the last slot + // Modifies a subsection of the region paintmap with the material in + // the last slot. // It is currently the responsibility of the paint queue to convert // incoming bits to the right bit depth for the paintmap (this could // change in the future). LLTerrainPaint::ptr_t paint = std::make_shared(); - const U16 width = U16(tex->getWidth() / 16); - paint->mStartX = width - 1; - paint->mStartY = width - 1; + const U16 width = 33; + paint->mStartX = 1; + paint->mStartY = 1; paint->mWidthX = width; paint->mWidthY = width; constexpr U8 bit_depth = 5; @@ -1488,12 +1509,19 @@ class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t const size_t pixel = (h * paint->mWidthX) + w; // Solid blue color paint->mData[(components*pixel) + components - 2] = max_value; // blue - // Alpha gradient from 0.0 to 1.0 along w - const U8 alpha = U8(F32(max_value) * F32(w+1) / F32(paint->mWidthX)); + //// Alpha grid: 1.0 if odd for either dimension, 0.0 otherwise + //const U8 alpha = U8(F32(max_value) * F32(bool(w % 2) || bool(h % 2))); + //paint->mData[(components*pixel) + components - 1] = alpha; // alpha + // Alpha "frame" + const bool edge = w == 0 || h == 0 || w == (paint->mWidthX - 1) || h == (paint->mWidthY - 1); + const bool near_edge_frill = ((w == 1 || w == (paint->mWidthX - 2)) && (h % 2 == 0)) || + ((h == 1 || h == (paint->mWidthY - 2)) && (w % 2 == 0)); + const U8 alpha = U8(F32(max_value) * F32(edge || near_edge_frill)); paint->mData[(components*pixel) + components - 1] = alpha; // alpha } } paint_request_queue.enqueue(paint); +#endif // Apply the paint queues ad-hoc right here for now. // *TODO: Eventually the paint queue(s) should be applied at a diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 3f1124a8ac..9a5d74adda 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -90,6 +90,10 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Queue of client-triggered brush operations that need to be converted + // into a form that can be sent to the server. + // TODO: Consider getting rid of mPaintRequestQueue, as it's not really needed (brushes go directly to RGB queue) + LLTerrainBrushQueue& getBrushQueue() { return mBrushQueue; } // Queue of client-triggered paint operations that need to be converted // into a form that can be sent to the server. // Paints in this queue are in RGBA format. @@ -116,6 +120,7 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer mPaintMap; + LLTerrainBrushQueue mBrushQueue; LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; -- cgit v1.2.3 From 81cc4fa7d61e838413b912a4ed2d957cc65bcb46 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 14:30:29 -0700 Subject: secondlife/viewer#1883: (WIP/Alpha/WOMM) Fix some compiler and runtime errors --- indra/newview/llterrainpaintmap.cpp | 52 ++++++++++++++++++------------------- indra/newview/llviewercontrol.cpp | 1 + indra/newview/llviewermenu.cpp | 8 ++++-- indra/newview/llviewershadermgr.cpp | 38 +++++++++++++++++---------- 4 files changed, 57 insertions(+), 42 deletions(-) (limited to 'indra') diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 6a57605325..d36e4ea657 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -447,7 +447,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // View matrix // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. // Camera is centered in the middle of the framebuffer. - glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + glm::mat4 view(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION)); { LLViewerCamera camera; const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); @@ -456,7 +456,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); GLfloat ogl_matrix[16]; camera.getOpenGLTransform(ogl_matrix); - view *= glh::matrix4f(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); } LLGLDisable stencil(GL_STENCIL_TEST); @@ -471,14 +471,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // First, apply the paint map as the background { - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); - model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); // We care about the whole paintmap, which is already a power of two. @@ -505,14 +505,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // Modelview matrix for the current paint // View matrix is already computed. Just need the model matrix. // Orthographic projection matrix is already updated - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); - model.set_translate(glh::vec3f(paint_in->mStartX, paint_in->mStartY, 0.0f)); + model = glm::scale(model, glm::vec3(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model = glm::translate(model, glm::vec3(paint_in->mStartX, paint_in->mStartY, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); // Generate temporary stamp texture from paint contents. // Our stamp image needs to be a power of two. @@ -663,7 +663,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // View matrix // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. // Camera is centered in the middle of the framebuffer. - glh::matrix4f view((GLfloat *) OGL_TO_CFR_ROTATION); + glm::mat4 view(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION)); { LLViewerCamera camera; const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); @@ -672,7 +672,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); GLfloat ogl_matrix[16]; camera.getOpenGLTransform(ogl_matrix); - view *= glh::matrix4f(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); } LLGLDisable stencil(GL_STENCIL_TEST); @@ -687,14 +687,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // First, apply the paint map as the background { - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); - model.set_translate(glh::vec3f(0.0f, 0.0f, 0.0f)); + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); // We care about the whole paintmap, which is already a power of two. @@ -748,14 +748,14 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe brush_start_x *= tex.getWidth() / region.getWidth(); brush_start_y *= tex.getHeight() / region.getWidth(); } - glh::matrix4f model; + glm::mat4 model; { - model.set_scale(glh::vec3f(brush_width_x, brush_width_y, 1.0f)); - model.set_translate(glh::vec3f(brush_start_x, brush_start_y, 0.0f)); + model = glm::scale(model, glm::vec3(brush_width_x, brush_width_y, 1.0f)); + model = glm::translate(model, glm::vec3(brush_start_x, brush_start_y, 0.0f)); } - glh::matrix4f modelview = view * model; + glm::mat4 modelview = view * model; gGL.matrixMode(LLRender::MM_MODELVIEW); - gGL.loadMatrix(modelview.m); + gGL.loadMatrix(glm::value_ptr(modelview)); // Apply the "brush" to the render target { @@ -842,11 +842,11 @@ template bool LLTerrainQueue::enqueue(std::vector>& list) { constexpr bool dry_run = true; - for (T::ptr_t& t : list) + for (auto& t : list) { if (!enqueue(t), dry_run) { return false; } } - for (T::ptr_t& t : list) + for (auto& t : list) { enqueue(t); } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index f8a315d4d8..184c0e7d8b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -911,6 +911,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); { + setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged); const char* transform_suffixes[] = { "ScaleU", diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index dd6b6d1ca1..b39bf4fdbd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1430,6 +1430,12 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t return false; } + // This calls gLocalTerrainMaterials.setPaintType + // It also ensures the terrain bake shader is compiled (handleSetShaderChanged), so call this first + // *TODO: Fix compile errors in shader so it can be used for all platforms. Then we can unhide the shader from behind this setting and remove the hook to handleSetShaderChanged. This advanced setting is intended to be used as a local setting for testing terrain, not a feature flag, but it is currently used like a feature flag as a temporary hack. + // *TODO: Ideally we would call setPaintType *after* the paint map is well-defined. The terrain draw pool should be able to handle an undefined paint map in the meantime. + gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); + U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution"); // Ensure a reasonable image size of power two const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); @@ -1439,8 +1445,6 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t LLPointer image_raw = new LLImageRaw(dim,dim,3); LLPointer tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); - // This calls gLocalTerrainMaterials.setPaintType - gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); // If baking the paintmap failed, set the paintmap to nullptr. This // causes LLDrawPoolTerrain to use a blank paintmap instead. if (!success) { tex = nullptr; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 8e7af28d41..a6d397c039 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -3171,22 +3171,32 @@ bool LLViewerShaderMgr::loadShadersInterface() } } - if (success) + if (gSavedSettings.getBOOL("LocalTerrainPaintEnabled")) { - LLGLSLShader* shader = &gTerrainStampProgram; - U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); - // LLTerrainPaintMap currently uses an RGB8 texture internally - bit_depth = llclamp(bit_depth, 1, 8); - shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); - shader->mShaderFiles.clear(); - shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); - shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); - shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; - const U32 value_range = (1 << bit_depth) - 1; - shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); - success = success && shader->createShader(); - llassert(success); + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + //llassert(success); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << shader->mName << "', disabling!" << LL_ENDL; + gSavedSettings.setBOOL("RenderCanUseTerrainBakeShaders", false); + // continue as if this shader never happened + success = true; + } + } } if (success) -- cgit v1.2.3 From 303bf1977b802f5a091bec66b65dfb6c2f29b761 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 14:50:27 -0700 Subject: secondlife/viewer#1883: Fix terrain paintmap bit depth hardcoded in places and increase default bit depth to 8 --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llterrainpaintmap.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index babb8424dc..ebf1968007 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15367,7 +15367,7 @@ Type U32 Value - 5 + 8 TerrainPaintResolution diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index d36e4ea657..ec40f299a4 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -573,7 +573,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur paint_out->mStartY = paint_in->mStartY; paint_out->mWidthX = paint_in->mWidthX; paint_out->mWidthY = paint_in->mWidthY; - paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later paint_out->mComponents = LLTerrainPaint::RGB; #ifdef SHOW_ASSERT paint_out->assert_confined_to(tex); @@ -602,7 +602,8 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // round-trip which will reduce the bit depth, making the // pre-conversion step not necessary. queue_out.enqueue(paint_out); - queue_out.convertBitDepths(queue_out.size()-1, 5); + LLCachedControl bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); } queue_in.clear(); @@ -775,7 +776,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe const F32 dY = brush_start_y - F32(paint_out->mStartY); paint_out->mWidthX = U16(ceil(brush_width_x + dX)); paint_out->mWidthY = U16(ceil(brush_width_y + dY)); - paint_out->mBitDepth = 8; // Will be reduced to 5 bits later + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later paint_out->mComponents = LLTerrainPaint::RGB; // The brush strokes are expected to sometimes partially venture // outside of the paintmap bounds. @@ -803,7 +804,8 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe // round-trip which will reduce the bit depth, making the // pre-conversion step not necessary. queue_out.enqueue(paint_out); - queue_out.convertBitDepths(queue_out.size()-1, 5); + LLCachedControl bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); } queue_in.clear(); -- cgit v1.2.3 From d505eb4fce23e573c5155d1a9ba2857f549ca281 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Mon, 7 Oct 2024 15:57:50 -0700 Subject: secondlife/viewer#1883: Fix logic error caught by clang --- indra/newview/llterrainpaintmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index ec40f299a4..09caba8faa 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -846,7 +846,7 @@ bool LLTerrainQueue::enqueue(std::vector>& list) constexpr bool dry_run = true; for (auto& t : list) { - if (!enqueue(t), dry_run) { return false; } + if (!enqueue(t, dry_run)) { return false; } } for (auto& t : list) { -- cgit v1.2.3 From f88401c97a892ea4a6fce13ee15dad1bb976d992 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 11 Oct 2024 09:54:47 -0700 Subject: secondlife/viewer#1883: Review feedback --- indra/newview/llterrainpaintmap.cpp | 18 ++++++------------ indra/newview/llterrainpaintmap.h | 6 +++--- indra/newview/llvlcomposition.h | 2 -- 3 files changed, 9 insertions(+), 17 deletions(-) (limited to 'indra') diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 09caba8faa..52376e3bf0 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -56,15 +56,15 @@ void check_tex(const LLViewerTexture& tex) llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); } +#else +#define check_tex(tex) #endif } // namespace // static bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -309,9 +309,7 @@ void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQ { if (queue.empty()) { return; } -#ifdef SHOW_ASSERT check_tex(tex); -#endif gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); @@ -362,7 +360,7 @@ namespace // plane. // *NOTE: Because we know the vertex XY coordinates go from 0 to 1 // pre-transform, UVs can be calculated from the vertices -LLVertexBuffer& get_paint_triangle_buffer() +LLVertexBuffer* get_paint_triangle_buffer() { static LLPointer buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); static bool initialized = false; @@ -395,7 +393,7 @@ LLVertexBuffer& get_paint_triangle_buffer() *(indices++) = 2; buf->unmapBuffer(); } - return *buf; + return buf.get(); } }; @@ -403,9 +401,7 @@ LLVertexBuffer& get_paint_triangle_buffer() // static LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead @@ -429,7 +425,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; - LLVertexBuffer* buf = &get_paint_triangle_buffer(); + LLVertexBuffer* buf = get_paint_triangle_buffer(); // Update projection matrix and viewport // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. @@ -621,9 +617,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTextur // static LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) { -#ifdef SHOW_ASSERT check_tex(tex); -#endif // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer @@ -646,7 +640,7 @@ LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewe const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; - LLVertexBuffer* buf = &get_paint_triangle_buffer(); + LLVertexBuffer* buf = get_paint_triangle_buffer(); // Update projection matrix and viewport // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index cffdad80a2..6c321dc9c6 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,7 +26,7 @@ #pragma once -#include "llviewerprecompiledheaders.h" +#include class LLTexture; class LLViewerRegion; @@ -90,8 +90,8 @@ struct LLTerrainPaint U16 mWidthY; U8 mBitDepth; U8 mComponents; - const static U8 RGB = 3; - const static U8 RGBA = 4; + static constexpr U8 RGB = 3; + static constexpr U8 RGBA = 4; std::vector mData; // Asserts that this paint's start/width fit within the bounds of the diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index 9a5d74adda..21fd484375 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -27,8 +27,6 @@ #ifndef LL_LLVLCOMPOSITION_H #define LL_LLVLCOMPOSITION_H -#include - #include "llviewerlayer.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -- cgit v1.2.3 From 07e33dfac78b170d235069fd035771016d3e0403 Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Fri, 11 Oct 2024 11:50:28 -0700 Subject: secondlife/viewer#1883: Fix compiler errors --- indra/newview/llterrainpaintmap.cpp | 3 +++ indra/newview/llterrainpaintmap.h | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'indra') diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 52376e3bf0..a858f92d8e 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -982,3 +982,6 @@ bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue) { return LLTerrainQueue::enqueue(queue.mList); } + +template class LLTerrainQueue; +template class LLTerrainQueue; diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 6c321dc9c6..d4859b747b 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -27,6 +27,10 @@ #pragma once #include +#include + +#include "stdtypes.h" +#include "v2math.h" class LLTexture; class LLViewerRegion; -- cgit v1.2.3