00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "lltoolbrush.h"
00035 #include "lltoolselectland.h"
00036
00037 #include "llgl.h"
00038
00039 #include "message.h"
00040
00041 #include "llagent.h"
00042 #include "llcallbacklist.h"
00043 #include "llviewercontrol.h"
00044 #include "llfloatertools.h"
00045 #include "llregionposition.h"
00046 #include "llstatusbar.h"
00047 #include "llsurface.h"
00048 #include "llsurfacepatch.h"
00049 #include "lltoolmgr.h"
00050 #include "llui.h"
00051 #include "llviewerparcelmgr.h"
00052 #include "llviewerparceloverlay.h"
00053 #include "llviewerregion.h"
00054 #include "llviewerwindow.h"
00055 #include "llworld.h"
00056 #include "viewer.h"
00057 #include "llparcel.h"
00058
00059 #include "llglheaders.h"
00060
00061 const std::string REGION_BLOCKS_TERRAFORM_MSG = "This region does not allow terraforming.\n"
00062 "You will need to buy land in another part of the world to terraform it.";
00063
00064
00065 LLToolBrushLand *gToolLand = NULL;
00066
00070
00071 const S32 LAND_BRUSH_SIZE_COUNT = 3;
00072 const F32 LAND_BRUSH_SIZE[LAND_BRUSH_SIZE_COUNT] = {1.0f, 2.0f, 4.0f};
00073 const S32 LAND_STEPS = 3;
00074 const F32 LAND_METERS_PER_SECOND = 1.0f;
00075 enum
00076 {
00077 E_LAND_LEVEL = 0,
00078 E_LAND_RAISE = 1,
00079 E_LAND_LOWER = 2,
00080 E_LAND_SMOOTH = 3,
00081 E_LAND_NOISE = 4,
00082 E_LAND_REVERT = 5,
00083 E_LAND_INVALID = 6,
00084 };
00085 const LLColor4 OVERLAY_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
00086
00090
00091
00092 LLToolBrushLand::LLToolBrushLand()
00093 : LLTool("Land"),
00094 mStartingZ( 0.0f ),
00095 mMouseX( 0 ),
00096 mMouseY(0),
00097 mGotHover(FALSE),
00098 mLastShowParcelOwners(FALSE),
00099 mBrushSelected(FALSE)
00100 {
00101 mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
00102 }
00103
00104 void LLToolBrushLand::modifyLandAtPointGlobal(const LLVector3d &pos_global,
00105 MASK mask)
00106 {
00107 S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
00108
00109 determineAffectedRegions(mLastAffectedRegions, pos_global);
00110 for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
00111 regionp != NULL;
00112 regionp = mLastAffectedRegions.getNextData())
00113 {
00114
00115 LLVector3 pos_region = regionp->getPosRegionFromGlobal(pos_global);
00116 LLSurface &land = regionp->getLand();
00117 char action = E_LAND_LEVEL;
00118 switch (radioAction)
00119 {
00120 case 0:
00121
00122 action = E_LAND_LEVEL;
00123 break;
00124 case 1:
00125 action = E_LAND_RAISE;
00126 break;
00127 case 2:
00128 action = E_LAND_LOWER;
00129 break;
00130 case 3:
00131 action = E_LAND_SMOOTH;
00132 break;
00133 case 4:
00134 action = E_LAND_NOISE;
00135 break;
00136 case 5:
00137 action = E_LAND_REVERT;
00138 break;
00139 default:
00140 action = E_LAND_INVALID;
00141 break;
00142 }
00143
00144
00145
00146
00147
00148 LLSurfacePatch *patchp= land.resolvePatchRegion(pos_region);
00149 if (patchp)
00150 {
00151 patchp->dirtyZ();
00152 }
00153
00154
00155 regionp->forceUpdate();
00156
00157
00158 F32 seconds = 1.0f / gFPSClamped;
00159 F32 x_pos = (F32)pos_region.mV[VX];
00160 F32 y_pos = (F32)pos_region.mV[VY];
00161 U8 brush_size = (U8)mBrushIndex;
00162 LLMessageSystem* msg = gMessageSystem;
00163 msg->newMessageFast(_PREHASH_ModifyLand);
00164 msg->nextBlockFast(_PREHASH_AgentData);
00165 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00166 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00167 msg->nextBlockFast(_PREHASH_ModifyBlock);
00168 msg->addU8Fast(_PREHASH_Action, (U8)action);
00169 msg->addU8Fast(_PREHASH_BrushSize, brush_size);
00170 msg->addF32Fast(_PREHASH_Seconds, seconds);
00171 msg->addF32Fast(_PREHASH_Height, mStartingZ);
00172 msg->nextBlockFast(_PREHASH_ParcelData);
00173 msg->addS32Fast(_PREHASH_LocalID, -1);
00174 msg->addF32Fast(_PREHASH_West, x_pos );
00175 msg->addF32Fast(_PREHASH_South, y_pos );
00176 msg->addF32Fast(_PREHASH_East, x_pos );
00177 msg->addF32Fast(_PREHASH_North, y_pos );
00178 msg->sendMessage(regionp->getHost());
00179 }
00180 }
00181
00182 void LLToolBrushLand::modifyLandInSelectionGlobal()
00183 {
00184 if (gParcelMgr->selectionEmpty())
00185 {
00186 return;
00187 }
00188
00189 if (gToolMgr->getCurrentTool() == gToolParcel)
00190 {
00191
00192 return;
00193 }
00194
00195 LLVector3d min;
00196 LLVector3d max;
00197
00198 gParcelMgr->getSelection(min, max);
00199
00200 S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
00201
00202 mLastAffectedRegions.removeAllNodes();
00203
00204 determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], min.mdV[VY], 0));
00205 determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], max.mdV[VY], 0));
00206 determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], min.mdV[VY], 0));
00207 determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], max.mdV[VY], 0));
00208
00209 LLRegionPosition mid_point_region((min + max) * 0.5);
00210 LLViewerRegion* center_region = mid_point_region.getRegion();
00211 if (center_region)
00212 {
00213 LLVector3 pos_region = mid_point_region.getPositionRegion();
00214 U32 grids = center_region->getLand().mGridsPerEdge;
00215 S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
00216 S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
00217 mStartingZ = center_region->getLand().getZ(i+j*grids);
00218 }
00219 else
00220 {
00221 mStartingZ = 0.f;
00222 }
00223
00224
00225 for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
00226 regionp != NULL;
00227 regionp = mLastAffectedRegions.getNextData())
00228 {
00229 if (!canTerraform(regionp))
00230 {
00231 alertNoTerraform(regionp);
00232 return;
00233 }
00234 }
00235
00236 for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
00237 regionp != NULL;
00238 regionp = mLastAffectedRegions.getNextData())
00239 {
00240
00241 LLVector3 min_region = regionp->getPosRegionFromGlobal(min);
00242 LLVector3 max_region = regionp->getPosRegionFromGlobal(max);
00243
00244 min_region.clamp(0.f, regionp->getWidth());
00245 max_region.clamp(0.f, regionp->getWidth());
00246 F32 seconds = 1.0f;
00247
00248 LLSurface &land = regionp->getLand();
00249 char action = E_LAND_LEVEL;
00250 switch (radioAction)
00251 {
00252 case 0:
00253
00254 action = E_LAND_LEVEL;
00255 seconds = 1.f;
00256 break;
00257 case 1:
00258 action = E_LAND_RAISE;
00259 break;
00260 case 2:
00261 action = E_LAND_LOWER;
00262 break;
00263 case 3:
00264 action = E_LAND_SMOOTH;
00265 seconds = 10.f;
00266 break;
00267 case 4:
00268 action = E_LAND_NOISE;
00269 seconds = 0.5f;
00270 break;
00271 case 5:
00272 action = E_LAND_REVERT;
00273 seconds = 0.5f;
00274 break;
00275 default:
00276
00277
00278 return;
00279 break;
00280 }
00281
00282
00283
00284
00285
00286 LLSurfacePatch *patchp= land.resolvePatchRegion(min_region);
00287 if (patchp)
00288 {
00289 patchp->dirtyZ();
00290 }
00291
00292
00293 regionp->forceUpdate();
00294
00295
00296 U8 brush_size = (U8)mBrushIndex;
00297 LLMessageSystem* msg = gMessageSystem;
00298 msg->newMessageFast(_PREHASH_ModifyLand);
00299 msg->nextBlockFast(_PREHASH_AgentData);
00300 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00301 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00302 msg->nextBlockFast(_PREHASH_ModifyBlock);
00303 msg->addU8Fast(_PREHASH_Action, (U8)action);
00304 msg->addU8Fast(_PREHASH_BrushSize, brush_size);
00305 msg->addF32Fast(_PREHASH_Seconds, seconds);
00306 msg->addF32Fast(_PREHASH_Height, mStartingZ);
00307
00308 BOOL parcel_selected = gParcelMgr->getParcelSelection()->getWholeParcelSelected();
00309 LLParcel* selected_parcel = gParcelMgr->getParcelSelection()->getParcel();
00310
00311 if (parcel_selected && selected_parcel)
00312 {
00313 msg->nextBlockFast(_PREHASH_ParcelData);
00314 msg->addS32Fast(_PREHASH_LocalID, selected_parcel->getLocalID());
00315 msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
00316 msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
00317 msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
00318 msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
00319 }
00320 else
00321 {
00322 msg->nextBlockFast(_PREHASH_ParcelData);
00323 msg->addS32Fast(_PREHASH_LocalID, -1);
00324 msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
00325 msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
00326 msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
00327 msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
00328 }
00329
00330 msg->sendMessage(regionp->getHost());
00331 }
00332 }
00333
00334 void LLToolBrushLand::brush( void )
00335 {
00336 LLVector3d spot;
00337 if( gViewerWindow->mousePointOnLandGlobal( mMouseX, mMouseY, &spot ) )
00338 {
00339
00340 spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
00341 spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
00342
00343 modifyLandAtPointGlobal(spot, gKeyboard->currentMask(TRUE));
00344 }
00345 }
00346
00347 BOOL LLToolBrushLand::handleMouseDown(S32 x, S32 y, MASK mask)
00348 {
00349 BOOL handled = FALSE;
00350
00351
00352 LLVector3d spot;
00353 if( gViewerWindow->mousePointOnLandGlobal( x, y, &spot ) )
00354 {
00355
00356 spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
00357 spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
00358
00359 LLRegionPosition region_position( spot );
00360 LLViewerRegion* regionp = region_position.getRegion();
00361
00362 if (!canTerraform(regionp))
00363 {
00364 alertNoTerraform(regionp);
00365 return TRUE;
00366 }
00367
00368 LLVector3 pos_region = region_position.getPositionRegion();
00369 U32 grids = regionp->getLand().mGridsPerEdge;
00370 S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
00371 S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
00372 mStartingZ = regionp->getLand().getZ(i+j*grids);
00373 mMouseX = x;
00374 mMouseY = y;
00375 gIdleCallbacks.addFunction( &LLToolBrushLand::onIdle, (void*)this );
00376 setMouseCapture( TRUE );
00377
00378 gParcelMgr->setSelectionVisible(FALSE);
00379 handled = TRUE;
00380 }
00381
00382 return handled;
00383 }
00384
00385 BOOL LLToolBrushLand::handleHover( S32 x, S32 y, MASK mask )
00386 {
00387 lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolBrushLand ("
00388 << (hasMouseCapture() ? "active":"inactive")
00389 << ")" << llendl;
00390 mMouseX = x;
00391 mMouseY = y;
00392 mGotHover = TRUE;
00393 gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLLAND);
00394 return TRUE;
00395 }
00396
00397 BOOL LLToolBrushLand::handleMouseUp(S32 x, S32 y, MASK mask)
00398 {
00399 BOOL handled = FALSE;
00400 mLastAffectedRegions.removeAllNodes();
00401 if( hasMouseCapture() )
00402 {
00403
00404 setMouseCapture( FALSE );
00405
00406 gParcelMgr->setSelectionVisible(TRUE);
00407
00408 gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, (void*)this );
00409 handled = TRUE;
00410 }
00411
00412 return handled;
00413 }
00414
00415 void LLToolBrushLand::handleSelect()
00416 {
00417 gEditMenuHandler = this;
00418
00419 gFloaterTools->setStatusText("modifyland");
00420
00421 {
00422 mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
00423 gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
00424 mBrushSelected = TRUE;
00425 }
00426 }
00427
00428
00429 void LLToolBrushLand::handleDeselect()
00430 {
00431 if( gEditMenuHandler == this )
00432 {
00433 gEditMenuHandler = NULL;
00434 }
00435 mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
00436 gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
00437 gParcelMgr->setSelectionVisible(TRUE);
00438 mBrushSelected = FALSE;
00439 }
00440
00441
00442 void LLToolBrushLand::render()
00443 {
00444 if(mGotHover)
00445 {
00446
00447 LLVector3d spot;
00448 if(gViewerWindow->mousePointOnLandGlobal(mMouseX, mMouseY, &spot))
00449 {
00450 spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
00451 spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
00452
00453 mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
00454 LLLinkedList<LLViewerRegion> regions;
00455 determineAffectedRegions(regions, spot);
00456
00457
00458 LLVector3 pos_world = gAgent.getRegion()->getPosRegionFromGlobal(spot);
00459 for(LLViewerRegion* region = regions.getFirstData();
00460 region != NULL;
00461 region = regions.getNextData())
00462 {
00463 renderOverlay(region->getLand(),
00464 region->getPosRegionFromGlobal(spot),
00465 pos_world);
00466 }
00467 }
00468 mGotHover = FALSE;
00469 }
00470 }
00471
00472 void LLToolBrushLand::renderOverlay(LLSurface& land, const LLVector3& pos_region,
00473 const LLVector3& pos_world)
00474 {
00475 glMatrixMode(GL_MODELVIEW);
00476 LLGLSNoTexture gls_no_texture;
00477 LLGLDepthTest mDepthTest(GL_TRUE);
00478 glPushMatrix();
00479 glColor4fv(OVERLAY_COLOR.mV);
00480 glTranslatef(0.0f, 0.0f, 1.0f);
00481
00482
00483
00484
00485 S32 i = (S32) pos_region.mV[VX];
00486 S32 j = (S32) pos_region.mV[VY];
00487 S32 half_edge = llfloor(LAND_BRUSH_SIZE[mBrushIndex]);
00488
00489
00490 glBegin(GL_POINTS);
00491 for(S32 di = -half_edge; di <= half_edge; di++)
00492 {
00493 if((i+di) < 0 || (i+di) >= (S32)land.mGridsPerEdge) continue;
00494 for(S32 dj = -half_edge; dj <= half_edge; dj++)
00495 {
00496 if( (j+dj) < 0 || (j+dj) >= (S32)land.mGridsPerEdge ) continue;
00497 glVertex3f(pos_world.mV[VX] + di, pos_world.mV[VY] + dj,
00498 land.getZ((i+di)+(j+dj)*land.mGridsPerEdge));
00499 }
00500 }
00501 glEnd();
00502 glPopMatrix();
00503 }
00504
00505 void LLToolBrushLand::determineAffectedRegions(LLLinkedList<LLViewerRegion>& regions,
00506 const LLVector3d& spot ) const
00507 {
00508 LLVector3d corner(spot);
00509 corner.mdV[VX] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
00510 corner.mdV[VY] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
00511 LLViewerRegion* region = NULL;
00512 region = gWorldPointer->getRegionFromPosGlobal(corner);
00513 if(region && !regions.checkData(region))
00514 {
00515 regions.addData(region);
00516 }
00517 corner.mdV[VY] += LAND_BRUSH_SIZE[mBrushIndex];
00518 region = gWorldPointer->getRegionFromPosGlobal(corner);
00519 if(region && !regions.checkData(region))
00520 {
00521 regions.addData(region);
00522 }
00523 corner.mdV[VX] += LAND_BRUSH_SIZE[mBrushIndex];
00524 region = gWorldPointer->getRegionFromPosGlobal(corner);
00525 if(region && !regions.checkData(region))
00526 {
00527 regions.addData(region);
00528 }
00529 corner.mdV[VY] -= LAND_BRUSH_SIZE[mBrushIndex];
00530 region = gWorldPointer->getRegionFromPosGlobal(corner);
00531 if(region && !regions.checkData(region))
00532 {
00533 regions.addData(region);
00534 }
00535 }
00536
00537
00538 void LLToolBrushLand::onIdle( void* brush_tool )
00539 {
00540 LLToolBrushLand* self = reinterpret_cast<LLToolBrushLand*>(brush_tool);
00541
00542 if( gToolMgr->getCurrentTool() == self )
00543 {
00544 self->brush();
00545 }
00546 else
00547 {
00548 gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, self );
00549 }
00550 }
00551
00552 void LLToolBrushLand::onMouseCaptureLost()
00553 {
00554 gIdleCallbacks.deleteFunction(&LLToolBrushLand::onIdle, this);
00555 }
00556
00557
00558 void LLToolBrushLand::undo()
00559 {
00560 for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
00561 regionp != NULL;
00562 regionp = mLastAffectedRegions.getNextData())
00563 {
00564 gMessageSystem->newMessageFast(_PREHASH_UndoLand);
00565 gMessageSystem->nextBlockFast(_PREHASH_AgentData);
00566 gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
00567 gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00568 gMessageSystem->sendMessage(regionp->getHost());
00569 }
00570 }
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589 bool LLToolBrushLand::canTerraform(LLViewerRegion* regionp) const
00590 {
00591 if (!regionp) return false;
00592 if (regionp->canManageEstate()) return true;
00593 return !(regionp->getRegionFlags() & REGION_FLAGS_BLOCK_TERRAFORM);
00594 }
00595
00596
00597 void LLToolBrushLand::alertNoTerraform(LLViewerRegion* regionp)
00598 {
00599 if (!regionp) return;
00600
00601 LLStringBase<char>::format_map_t args;
00602 args["[REGION]"] = regionp->getName();
00603 gViewerWindow->alertXml("RegionNoTerraforming", args);
00604
00605 }
00606