summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/newview/llterrainpaintmap.cpp118
-rw-r--r--indra/newview/llterrainpaintmap.h38
-rw-r--r--indra/newview/llviewermenu.cpp46
-rw-r--r--indra/newview/llvlcomposition.cpp8
-rw-r--r--indra/newview/llvlcomposition.h6
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml7
6 files changed, 221 insertions, 2 deletions
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<LLTerrainPaint::ptr_t>& 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<U32> 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<LLTerrainPaint>;
+
+ U16 mStartX;
+ U16 mStartY;
+ U16 mWidthX;
+ U16 mWidthY;
+ U8 mBitDepth;
+ static const U8 COMPONENTS = 3;
+ std::vector<U8> mData;
+};
+
+class LLTerrainPaintQueue
+{
+public:
+ bool enqueue(LLTerrainPaint::ptr_t& paint);
+ bool empty() const;
+ void clear();
+
+ const std::vector<LLTerrainPaint::ptr_t>& 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<LLTerrainPaint::ptr_t> 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<LLTerrainPaint>();
+ 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 <memory>
+
#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<LLViewerTexture> 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
@@ -3826,6 +3826,13 @@ function="World.EnvPreset"
</menu_item_call>
<menu_item_call
enabled="true"
+ label="Edit Local Paintmap"
+ name="Edit Local Paintmap">
+ <menu_item_call.on_click
+ function="Advanced.TerrainEditLocalPaintMap" />
+ </menu_item_call>
+ <menu_item_call
+ enabled="true"
label="Delete Local Paintmap"
name="Delete Local Paintmap">
<menu_item_call.on_click