summaryrefslogtreecommitdiff
path: root/indra/newview/lltoolbrush.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/lltoolbrush.cpp
Print done when done.
Diffstat (limited to 'indra/newview/lltoolbrush.cpp')
-rw-r--r--indra/newview/lltoolbrush.cpp585
1 files changed, 585 insertions, 0 deletions
diff --git a/indra/newview/lltoolbrush.cpp b/indra/newview/lltoolbrush.cpp
new file mode 100644
index 0000000000..8255008aa0
--- /dev/null
+++ b/indra/newview/lltoolbrush.cpp
@@ -0,0 +1,585 @@
+/**
+ * @file lltoolbrush.cpp
+ * @brief Implementation of the toolbrushes
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolbrush.h"
+#include "lltoolselectland.h"
+
+#include "llgl.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llviewercontrol.h"
+#include "llfloatertools.h"
+#include "llregionposition.h"
+#include "llstatusbar.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llparcel.h"
+
+#include "llglheaders.h"
+
+const std::string REGION_BLOCKS_TERRAFORM_MSG = "This region does not allow terraforming.\n"
+ "You will need to buy land in another part of the world to terraform it.";
+
+// Globals
+LLToolBrushLand *gToolLand = NULL;
+
+///============================================================================
+/// Local function declarations, constants, enums, and typedefs
+///============================================================================
+
+const S32 LAND_BRUSH_SIZE_COUNT = 3;
+const F32 LAND_BRUSH_SIZE[LAND_BRUSH_SIZE_COUNT] = {1.0f, 2.0f, 4.0f};
+const S32 LAND_STEPS = 3;
+const F32 LAND_METERS_PER_SECOND = 1.0f;
+enum
+{
+ E_LAND_LEVEL = 0,
+ E_LAND_RAISE = 1,
+ E_LAND_LOWER = 2,
+ E_LAND_SMOOTH = 3,
+ E_LAND_NOISE = 4,
+ E_LAND_REVERT = 5,
+ E_LAND_INVALID = 6,
+};
+const LLColor4 OVERLAY_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
+
+///============================================================================
+/// Class LLToolBrushLand
+///============================================================================
+
+// constructor
+LLToolBrushLand::LLToolBrushLand()
+: LLTool("Land"),
+ mStartingZ( 0.0f ),
+ mMouseX( 0 ),
+ mMouseY(0),
+ mGotHover(FALSE),
+ mLastShowParcelOwners(FALSE),
+ mBrushSelected(FALSE)
+{
+ mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
+}
+
+void LLToolBrushLand::modifyLandAtPointGlobal(const LLVector3d &pos_global,
+ MASK mask)
+{
+ S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
+
+ determineAffectedRegions(mLastAffectedRegions, pos_global);
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ //BOOL is_changed = FALSE;
+ LLVector3 pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ LLSurface &land = regionp->getLand();
+ char action = E_LAND_LEVEL;
+ switch (radioAction)
+ {
+ case 0:
+ // // average toward mStartingZ
+ action = E_LAND_LEVEL;
+ break;
+ case 1:
+ action = E_LAND_RAISE;
+ break;
+ case 2:
+ action = E_LAND_LOWER;
+ break;
+ case 3:
+ action = E_LAND_SMOOTH;
+ break;
+ case 4:
+ action = E_LAND_NOISE;
+ break;
+ case 5:
+ action = E_LAND_REVERT;
+ break;
+ default:
+ action = E_LAND_INVALID;
+ break;
+ }
+
+ // Don't send a message to the region if nothing changed.
+ //if(!is_changed) continue;
+
+ // Now to update the patch information so it will redraw correctly.
+ LLSurfacePatch *patchp= land.resolvePatchRegion(pos_region);
+ if (patchp)
+ {
+ patchp->dirtyZ();
+ }
+
+ // Also force the property lines to update, normals to recompute, etc.
+ regionp->forceUpdate();
+
+ // tell the simulator what we've done
+ F32 seconds = 1.0f / gFPSClamped;
+ F32 x_pos = (F32)pos_region.mV[VX];
+ F32 y_pos = (F32)pos_region.mV[VY];
+ U8 brush_size = (U8)mBrushIndex;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ModifyLand);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ModifyBlock);
+ msg->addU8Fast(_PREHASH_Action, (U8)action);
+ msg->addU8Fast(_PREHASH_BrushSize, brush_size);
+ msg->addF32Fast(_PREHASH_Seconds, seconds);
+ msg->addF32Fast(_PREHASH_Height, mStartingZ);
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1);
+ msg->addF32Fast(_PREHASH_West, x_pos );
+ msg->addF32Fast(_PREHASH_South, y_pos );
+ msg->addF32Fast(_PREHASH_East, x_pos );
+ msg->addF32Fast(_PREHASH_North, y_pos );
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+void LLToolBrushLand::modifyLandInSelectionGlobal()
+{
+ if (gParcelMgr->selectionEmpty())
+ {
+ return;
+ }
+
+ if (gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolParcel)
+ {
+ // selecting land, don't do anything
+ return;
+ }
+
+ LLVector3d min;
+ LLVector3d max;
+
+ gParcelMgr->getSelection(min, max);
+
+ S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
+
+ mLastAffectedRegions.removeAllNodes();
+
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], min.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], max.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], min.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], max.mdV[VY], 0));
+
+ LLRegionPosition mid_point_region((min + max) * 0.5);
+ LLViewerRegion* center_region = mid_point_region.getRegion();
+ if (center_region)
+ {
+ LLVector3 pos_region = mid_point_region.getPositionRegion();
+ U32 grids = center_region->getLand().mGridsPerEdge;
+ S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
+ S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
+ mStartingZ = center_region->getLand().getZ(i+j*grids);
+ }
+ else
+ {
+ mStartingZ = 0.f;
+ }
+
+ // Stop if our selection include a no-terraform region
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ if (!canTerraform(regionp))
+ {
+ alertNoTerraform(regionp);
+ return;
+ }
+ }
+
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ //BOOL is_changed = FALSE;
+ LLVector3 min_region = regionp->getPosRegionFromGlobal(min);
+ LLVector3 max_region = regionp->getPosRegionFromGlobal(max);
+
+ min_region.clamp(0.f, regionp->getWidth());
+ max_region.clamp(0.f, regionp->getWidth());
+ F32 seconds = 1.0f;
+
+ LLSurface &land = regionp->getLand();
+ char action = E_LAND_LEVEL;
+ switch (radioAction)
+ {
+ case 0:
+ // // average toward mStartingZ
+ action = E_LAND_LEVEL;
+ seconds = 1.f;
+ break;
+ case 1:
+ action = E_LAND_RAISE;
+ break;
+ case 2:
+ action = E_LAND_LOWER;
+ break;
+ case 3:
+ action = E_LAND_SMOOTH;
+ seconds = 10.f;
+ break;
+ case 4:
+ action = E_LAND_NOISE;
+ seconds = 0.5f;
+ break;
+ case 5:
+ action = E_LAND_REVERT;
+ seconds = 0.5f;
+ break;
+ default:
+ //action = E_LAND_INVALID;
+ //seconds = 0.0f;
+ return;
+ break;
+ }
+
+ // Don't send a message to the region if nothing changed.
+ //if(!is_changed) continue;
+
+ // Now to update the patch information so it will redraw correctly.
+ LLSurfacePatch *patchp= land.resolvePatchRegion(min_region);
+ if (patchp)
+ {
+ patchp->dirtyZ();
+ }
+
+ // Also force the property lines to update, normals to recompute, etc.
+ regionp->forceUpdate();
+
+ // tell the simulator what we've done
+ U8 brush_size = (U8)mBrushIndex;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ModifyLand);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ModifyBlock);
+ msg->addU8Fast(_PREHASH_Action, (U8)action);
+ msg->addU8Fast(_PREHASH_BrushSize, brush_size);
+ msg->addF32Fast(_PREHASH_Seconds, seconds);
+ msg->addF32Fast(_PREHASH_Height, mStartingZ);
+
+ BOOL parcel_selected = gParcelMgr->getWholeParcelSelected();
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel_selected && selected_parcel)
+ {
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, selected_parcel->getLocalID());
+ msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1);
+ msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
+ }
+
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+void LLToolBrushLand::brush( void )
+{
+ LLVector3d spot;
+ if( gViewerWindow->mousePointOnLandGlobal( mMouseX, mMouseY, &spot ) )
+ {
+ // Round to nearest X,Y grid
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ modifyLandAtPointGlobal(spot, gKeyboard->currentMask(TRUE));
+ }
+}
+
+BOOL LLToolBrushLand::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Find the z value of the initial click.
+ LLVector3d spot;
+ if( gViewerWindow->mousePointOnLandGlobal( x, y, &spot ) )
+ {
+ // Round to nearest X,Y grid
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ LLRegionPosition region_position( spot );
+ LLViewerRegion* regionp = region_position.getRegion();
+
+ if (!canTerraform(regionp))
+ {
+ alertNoTerraform(regionp);
+ return TRUE;
+ }
+
+ LLVector3 pos_region = region_position.getPositionRegion();
+ U32 grids = regionp->getLand().mGridsPerEdge;
+ S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
+ S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
+ mStartingZ = regionp->getLand().getZ(i+j*grids);
+ mMouseX = x;
+ mMouseY = y;
+ gIdleCallbacks.addFunction( &LLToolBrushLand::onIdle, (void*)this );
+ setMouseCapture( TRUE );
+
+ gParcelMgr->setSelectionVisible(FALSE);
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+BOOL LLToolBrushLand::handleHover( S32 x, S32 y, MASK mask )
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolBrushLand ("
+ << (hasMouseCapture() ? "active":"inactive")
+ << ")" << llendl;
+ mMouseX = x;
+ mMouseY = y;
+ mGotHover = TRUE;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLLAND);
+ return TRUE;
+}
+
+BOOL LLToolBrushLand::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ mLastAffectedRegions.removeAllNodes();
+ if( hasMouseCapture() )
+ {
+ // Release the mouse
+ setMouseCapture( FALSE );
+
+ gParcelMgr->setSelectionVisible(TRUE);
+
+ gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, (void*)this );
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+void LLToolBrushLand::handleSelect()
+{
+ gEditMenuHandler = this;
+
+ gFloaterTools->setStatusText("Click and hold to modify land");
+// if (!mBrushSelected)
+ {
+ mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
+ gSavedSettings.setBOOL("ShowParcelOwners", TRUE);
+ mBrushSelected = TRUE;
+ }
+}
+
+
+void LLToolBrushLand::handleDeselect()
+{
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+ gFloaterTools->setStatusText("");
+ gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
+ gParcelMgr->setSelectionVisible(TRUE);
+ mBrushSelected = FALSE;
+}
+
+// Draw the area that will be affected.
+void LLToolBrushLand::render()
+{
+ if(mGotHover)
+ {
+ //llinfos << "LLToolBrushLand::render()" << llendl;
+ LLVector3d spot;
+ if(gViewerWindow->mousePointOnLandGlobal(mMouseX, mMouseY, &spot))
+ {
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
+ LLLinkedList<LLViewerRegion> regions;
+ determineAffectedRegions(regions, spot);
+
+ // Now, for each region, render the overlay
+ LLVector3 pos_world = gAgent.getRegion()->getPosRegionFromGlobal(spot);
+ for(LLViewerRegion* region = regions.getFirstData();
+ region != NULL;
+ region = regions.getNextData())
+ {
+ renderOverlay(region->getLand(),
+ region->getPosRegionFromGlobal(spot),
+ pos_world);
+ }
+ }
+ mGotHover = FALSE;
+ }
+}
+
+void LLToolBrushLand::renderOverlay(LLSurface& land, const LLVector3& pos_region,
+ const LLVector3& pos_world)
+{
+ glMatrixMode(GL_MODELVIEW);
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest mDepthTest(GL_TRUE);
+ glPushMatrix();
+ glColor4fv(OVERLAY_COLOR.mV);
+ glTranslatef(0.0f, 0.0f, 1.0f);
+ //glPushMatrix();
+ //glTranslatef(spot.mV[VX], spot.mV[VY], 100.0f);
+ //gl_rect_2d(0, 10, 10, 0);
+ //glPopMatrix();
+ S32 i = (S32) pos_region.mV[VX];
+ S32 j = (S32) pos_region.mV[VY];
+ S32 half_edge = llfloor(LAND_BRUSH_SIZE[mBrushIndex]);
+ //F32 dz = 0.0f;
+ //S32 dist = 0;
+ glBegin(GL_POINTS);
+ for(S32 di = -half_edge; di <= half_edge; di++)
+ {
+ if((i+di) < 0 || (i+di) >= (S32)land.mGridsPerEdge) continue;
+ for(S32 dj = -half_edge; dj <= half_edge; dj++)
+ {
+ if( (j+dj) < 0 || (j+dj) >= (S32)land.mGridsPerEdge ) continue;
+ glVertex3f(pos_world.mV[VX] + di, pos_world.mV[VY] + dj,
+ land.getZ((i+di)+(j+dj)*land.mGridsPerEdge));
+ }
+ }
+ glEnd();
+ glPopMatrix();
+}
+
+void LLToolBrushLand::determineAffectedRegions(LLLinkedList<LLViewerRegion>& regions,
+ const LLVector3d& spot ) const
+{
+ LLVector3d corner(spot);
+ corner.mdV[VX] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
+ corner.mdV[VY] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
+ LLViewerRegion* region = NULL;
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VY] += LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VX] += LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VY] -= LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+}
+
+// static
+void LLToolBrushLand::onIdle( void* brush_tool )
+{
+ LLToolBrushLand* self = reinterpret_cast<LLToolBrushLand*>(brush_tool);
+
+ if( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) == self )
+ {
+ self->brush();
+ }
+ else
+ {
+ gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, self );
+ }
+}
+
+void LLToolBrushLand::onMouseCaptureLost()
+{
+ gIdleCallbacks.deleteFunction(&LLToolBrushLand::onIdle, this);
+}
+
+// static
+void LLToolBrushLand::undo()
+{
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_UndoLand);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->sendMessage(regionp->getHost());
+ }
+}
+
+// static
+void LLToolBrushLand::redo()
+{
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RedoLand);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->sendMessage(regionp->getHost());
+ }
+}
+
+// static
+bool LLToolBrushLand::canTerraform(LLViewerRegion* regionp) const
+{
+ if (!regionp) return false;
+ if (regionp->canManageEstate()) return true;
+ return !(regionp->getRegionFlags() & REGION_FLAGS_BLOCK_TERRAFORM);
+}
+
+// static
+void LLToolBrushLand::alertNoTerraform(LLViewerRegion* regionp)
+{
+ if (!regionp) return;
+
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = regionp->getName();
+ gViewerWindow->alertXml("RegionNoTerraforming", args);
+
+}
+
+///============================================================================
+/// Local function definitions
+///============================================================================