forestSelectionTool.cpp
Engine/source/forest/editor/forestSelectionTool.cpp
Public Variables
Public Functions
ConsoleDocClass(ForestSelectionTool , "@brief Specialized selection tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> picking individual trees in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">forest.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(ForestSelectionTool , clearSelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , copySelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , cutSelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , deleteSelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , getSelectionCount , S32 , () , "" )
DefineEngineMethod(ForestSelectionTool , pasteSelection , void , () , "" )
Detailed Description
Public Variables
Frustum gDragFrustum
Public Functions
ConsoleDocClass(ForestSelectionTool , "@brief Specialized selection tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> picking individual trees in <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">forest.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(ForestSelectionTool , clearSelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , copySelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , cutSelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , deleteSelection , void , () , "" )
DefineEngineMethod(ForestSelectionTool , getSelectionCount , S32 , () , "" )
DefineEngineMethod(ForestSelectionTool , pasteSelection , void , () , "" )
IMPLEMENT_CONOBJECT(ForestSelectionTool )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "forest/editor/forestSelectionTool.h" 26 27#include "forest/forest.h" 28#include "forest/editor/forestUndo.h" 29#include "forest/editor/forestEditorCtrl.h" 30 31#include "gui/worldEditor/editTSCtrl.h" 32#include "gui/worldEditor/gizmo.h" 33#include "console/consoleTypes.h" 34#include "console/engineAPI.h" 35#include "core/util/tVector.h" 36#include "core/util/safeDelete.h" 37#include "gfx/gfxDrawUtil.h" 38#include "gui/worldEditor/worldEditor.h" 39#include "math/mMatrix.h" 40 41template <> 42MatrixF Selection<ForestItem>::getOrientation() 43{ 44 if ( size() == 1 ) 45 return first().getTransform(); 46 47 return MatrixF::Identity; 48} 49 50template <> 51Point3F Selection<ForestItem>::getOrigin() 52{ 53 Point3F centroid( Point3F::Zero ); 54 55 if ( empty() ) 56 return centroid; 57 58 Selection<ForestItem>::iterator itr = begin(); 59 60 for (; itr != end(); ++itr) 61 { 62 const MatrixF &mat = itr->getTransform(); 63 Point3F wPos; 64 mat.getColumn( 3, &wPos ); 65 66 centroid += wPos; 67 } 68 69 centroid /= (F32)size(); 70 71 return centroid; 72} 73 74template <> 75Point3F Selection<ForestItem>::getScale() 76{ 77 if ( size() == 1 ) 78 return Point3F( first().getScale() ); 79 80 return Point3F::One; 81} 82 83void ForestItemSelection::offsetObject( ForestItem &object, const Point3F &delta ) 84{ 85 if ( !mData ) 86 return; 87 88 MatrixF newMat( object.getTransform() ); 89 newMat.displace( delta ); 90 91 object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), newMat, object.getScale() ); 92} 93 94void ForestItemSelection::rotateObject( ForestItem &object, const EulerF &delta, const Point3F &origin ) 95{ 96 if ( !mData ) 97 return; 98 99 MatrixF mat = object.getTransform(); 100 101 Point3F pos; 102 mat.getColumn( 3, &pos ); 103 104 MatrixF wMat( mat ); 105 wMat.inverse(); 106 107 // get offset in obj space 108 Point3F offset = pos - origin; 109 110 if ( size() == 1 ) 111 { 112 wMat.mulV(offset); 113 114 MatrixF transform(EulerF::Zero, -offset); 115 transform.mul(MatrixF(delta)); 116 transform.mul(MatrixF(EulerF::Zero, offset)); 117 mat.mul(transform); 118 } 119 else 120 { 121 MatrixF transform( delta ); 122 Point3F wOffset; 123 transform.mulV( offset, &wOffset ); 124 125 wMat.mulV( offset ); 126 127 transform.set( EulerF::Zero, -offset ); 128 129 mat.setColumn( 3, Point3F::Zero ); 130 wMat.setColumn( 3, Point3F::Zero ); 131 132 transform.mul( wMat ); 133 transform.mul( MatrixF(delta) ); 134 transform.mul( mat ); 135 mat.mul( transform ); 136 137 mat.normalize(); 138 mat.setColumn( 3, wOffset + origin ); 139 } 140 141 object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), mat, object.getScale() ); 142} 143 144void ForestItemSelection::scaleObject( ForestItem &object, const Point3F &delta ) 145{ 146 if ( !mData ) 147 return; 148 149 object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), object.getTransform(), delta.z ); 150} 151 152 153IMPLEMENT_CONOBJECT( ForestSelectionTool ); 154 155ConsoleDocClass( ForestSelectionTool, 156 "@brief Specialized selection tool for picking individual trees in a forest.\n\n" 157 "Editor use only.\n\n" 158 "@internal" 159); 160 161ForestSelectionTool::ForestSelectionTool() 162 : Parent(), 163 mGizmo( NULL ), 164 mCurrAction( NULL ), 165 mGizmoProfile( NULL ), 166 mMouseDragged(false), 167 mUsingGizmo(false) 168{ 169 mBounds = Box3F::Invalid; 170 171 mDragRectColor.set(255,255,0); 172 mMouseDown = false; 173 mDragSelect = false; 174} 175 176ForestSelectionTool::~ForestSelectionTool() 177{ 178 SAFE_DELETE( mCurrAction ); 179} 180 181void ForestSelectionTool::setParentEditor( ForestEditorCtrl *editor ) 182{ 183 mEditor = editor; 184 mGizmo = editor->getGizmo(); 185 mGizmoProfile = mGizmo->getProfile(); 186} 187 188void ForestSelectionTool::_selectItem( const ForestItem &item ) 189{ 190 // Make sure its not already selected. 191 for ( U32 i=0; i < mSelection.size(); i++ ) 192 { 193 if ( mSelection[i].getKey() == item.getKey() ) 194 return; 195 } 196 197 mSelection.push_back( item ); 198 mBounds.intersect( item.getWorldBox() ); 199} 200 201void ForestSelectionTool::deleteSelection() 202{ 203 if (!mEditor) 204 return; 205 206 ForestDeleteUndoAction *action = new ForestDeleteUndoAction( mForest->getData(), mEditor ); 207 208 for ( U32 i=0; i < mSelection.size(); i++ ) 209 { 210 const ForestItem &item = mSelection[i]; 211 action->removeItem( item ); 212 } 213 214 clearSelection(); 215 _submitUndo( action ); 216} 217 218void ForestSelectionTool::clearSelection() 219{ 220 mSelection.clear(); 221 mBounds = Box3F::Invalid; 222} 223 224void ForestSelectionTool::cutSelection() 225{ 226 227} 228 229void ForestSelectionTool::copySelection() 230{ 231 232} 233 234void ForestSelectionTool::pasteSelection() 235{ 236 237} 238 239void ForestSelectionTool::setActiveForest( Forest *forest ) 240{ 241 mForest = forest; 242 243 if ( forest ) 244 mSelection.setForestData( forest->getData() ); 245 else 246 mSelection.setForestData( NULL ); 247} 248 249void ForestSelectionTool::on3DMouseDown( const Gui3DMouseEvent &evt ) 250{ 251 mMouseDown = true; 252 mMouseDragged = false; 253 254 mUsingGizmo = !mSelection.empty() && mGizmo->getSelection() != Gizmo::None; 255 256 if ( mUsingGizmo ) 257 { 258 mGizmo->on3DMouseDown( evt ); 259 return; 260 } 261 262 mDragSelection.clear(); 263 mDragRect.set( Point2I(evt.mousePoint), Point2I(0,0) ); 264 mDragStart = evt.mousePoint; 265 266 const bool multiSel = evt.modifier & SI_CTRL; 267 268 if ( !multiSel ) 269 clearSelection(); 270 271 if ( mHoverItem.isValid() ) 272 _selectItem( mHoverItem ); 273 274 // This should never happen... it should have been 275 // submitted and nulled in on3DMouseUp()! 276 // 277 // Yeah, unless you had a breakpoint there and on3DMouseUp never fired, 278 // in which case this is really annoying. 279 // 280 //AssertFatal( !mCurrAction, "ForestSelectionTool::on3DMouseDown() - Dangling undo action!" ); 281} 282 283void ForestSelectionTool::on3DMouseMove( const Gui3DMouseEvent &evt ) 284{ 285 // Invalidate the hover item first... we're gonna find a new one. 286 mHoverItem.makeInvalid(); 287 288 if ( !mForest ) 289 return; 290 291 if ( !mSelection.empty() ) 292 mGizmo->on3DMouseMove( evt ); 293 294 RayInfo ri; 295 ri.userData = new ForestItem; 296 Point3F startPnt = evt.pos; 297 Point3F endPnt = evt.pos + evt.vec * 1000.0f; 298 299 if ( mForest->castRayRendered( startPnt, endPnt, &ri ) ) 300 mHoverItem = (*(ForestItem*)ri.userData); 301 302 delete static_cast<ForestItem*>(ri.userData); 303} 304 305void ForestSelectionTool::on3DMouseDragged( const Gui3DMouseEvent &evt ) 306{ 307 mHoverItem.makeInvalid(); 308 309 if ( mUsingGizmo ) 310 { 311 mGizmo->on3DMouseDragged( evt ); 312 313 const Point3F &deltaRot = mGizmo->getDeltaRot(); 314 const Point3F &deltaPos = mGizmo->getOffset(); 315 const Point3F &deltaScale = mGizmo->getDeltaScale(); 316 317 if ( deltaRot.isZero() && deltaPos.isZero() && deltaScale.isZero() ) 318 return; 319 320 // Store the current item states! 321 if ( !mCurrAction ) 322 { 323 mCurrAction = new ForestUpdateAction( mForest->getData(), mEditor ); 324 for ( U32 i=0; i < mSelection.size(); i++ ) 325 { 326 const ForestItem &item = mSelection[i]; 327 mCurrAction->saveItem( item ); 328 } 329 } 330 331 switch ( mGizmo->getMode() ) 332 { 333 case MoveMode: 334 mSelection.offset( deltaPos ); break; 335 case RotateMode: 336 mSelection.rotate( deltaRot ); break; 337 case ScaleMode: 338 mSelection.scale( mGizmo->getScale() ); break; 339 default: ; 340 } 341 342 return; 343 } 344 345 mDragSelect = true; 346 mHoverItem.makeInvalid(); 347 348 // Doing a drag selection. 349 350 if ( mDragSelect ) 351 { 352 // build the drag selection on the renderScene method - make sure no neg extent! 353 mDragRect.point.x = (evt.mousePoint.x < mDragStart.x) ? evt.mousePoint.x : mDragStart.x; 354 mDragRect.extent.x = (evt.mousePoint.x > mDragStart.x) ? evt.mousePoint.x - mDragStart.x : mDragStart.x - evt.mousePoint.x; 355 mDragRect.point.y = (evt.mousePoint.y < mDragStart.y) ? evt.mousePoint.y : mDragStart.y; 356 mDragRect.extent.y = (evt.mousePoint.y > mDragStart.y) ? evt.mousePoint.y - mDragStart.y : mDragStart.y - evt.mousePoint.y; 357 return; 358 } 359} 360 361void ForestSelectionTool::on3DMouseUp( const Gui3DMouseEvent &evt ) 362{ 363 mGizmo->on3DMouseUp( evt ); 364 365 mMouseDown = false; 366 367 // If we have an undo action then we must have 368 // moved, rotated, or scaled something. 369 if ( mCurrAction ) 370 { 371 _submitUndo( mCurrAction ); 372 mCurrAction = NULL; 373 return; 374 } 375 376 // If we were performing a drag select, finalize it now. 377 if ( mDragSelect ) 378 { 379 mDragSelect = false; 380 381 clearSelection(); 382 383 for ( S32 i = 0; i < mDragSelection.size(); i++ ) 384 _selectItem( mDragSelection[i] ); 385 386 mDragSelection.clear(); 387 388 return; 389 } 390} 391 392void ForestSelectionTool::onRender3D() 393{ 394 GFXDrawUtil *drawUtil = GFX->getDrawUtil(); 395 ColorI color( 255, 255, 255, 255 ); 396 MatrixF treeMat; 397 398 GFXStateBlockDesc desc; 399 desc.setBlend( true ); 400 desc.setZReadWrite( true, false ); 401 402 if ( mHoverItem.isValid() ) 403 { 404 treeMat = mHoverItem.getTransform(); 405 drawUtil->drawObjectBox( desc, mHoverItem.getSize(), mHoverItem.getWorldBox().getCenter(), treeMat, color ); 406 } 407 408 if ( !mSelection.empty() ) 409 { 410 for ( U32 i = 0; i < mSelection.size(); i++ ) 411 { 412 const ForestItem &item = mSelection[i]; 413 treeMat = item.getTransform(); 414 drawUtil->drawObjectBox( desc, item.getSize(), item.getWorldBox().getCenter(), treeMat, color ); 415 } 416 417 mGizmo->set( mSelection.getOrientation(), mSelection.getOrigin(), mSelection.getScale() ); 418 419 mGizmo->renderGizmo( mEditor->getLastCameraQuery().cameraMatrix, mEditor->getLastCameraQuery().fov ); 420 } 421} 422 423static Frustum gDragFrustum; 424 425void ForestSelectionTool::onRender2D() 426{ 427 // Draw drag selection rect. 428 if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 ) 429 GFX->getDrawUtil()->drawRect( mDragRect, mDragRectColor ); 430 431 // update what is in the selection 432 if ( mDragSelect ) 433 mDragSelection.clear(); 434 435 // Determine selected objects based on the drag box touching 436 // a mesh if a drag operation has begun. 437 if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 ) 438 { 439 // Build the drag frustum based on the rect 440 F32 wwidth; 441 F32 wheight; 442 F32 aspectRatio = F32(mEditor->getWidth()) / F32(mEditor->getHeight()); 443 444 const CameraQuery &lastCameraQuery = mEditor->getLastCameraQuery(); 445 if(!lastCameraQuery.ortho) 446 { 447 wheight = lastCameraQuery.nearPlane * mTan(lastCameraQuery.fov / 2); 448 wwidth = aspectRatio * wheight; 449 } 450 else 451 { 452 wheight = lastCameraQuery.fov; 453 wwidth = aspectRatio * wheight; 454 } 455 456 F32 hscale = wwidth * 2 / F32(mEditor->getWidth()); 457 F32 vscale = wheight * 2 / F32(mEditor->getHeight()); 458 459 Point2I editorPosition = mEditor->getPosition(); 460 F32 left = (mDragRect.point.x - editorPosition.x) * hscale - wwidth; 461 F32 right = (mDragRect.point.x - editorPosition.x + mDragRect.extent.x) * hscale - wwidth; 462 F32 top = wheight - vscale * (mDragRect.point.y - editorPosition.y); 463 F32 bottom = wheight - vscale * (mDragRect.point.y - editorPosition.y + mDragRect.extent.y); 464 gDragFrustum.set(lastCameraQuery.ortho, left, right, top, bottom, lastCameraQuery.nearPlane, lastCameraQuery.farPlane, lastCameraQuery.cameraMatrix ); 465 466 mForest->getData()->getItems( gDragFrustum, &mDragSelection ); 467 } 468} 469 470bool ForestSelectionTool::updateGuiInfo() 471{ 472 SimObject *statusbar; 473 if ( !Sim::findObject( "EditorGuiStatusBar", statusbar ) ) 474 return false; 475 476 String text( "Forest Editor." ); 477 GizmoMode mode = mGizmoProfile->mode; 478 479 if ( mMouseDown && mGizmo->getSelection() != Gizmo::None ) 480 { 481 Point3F delta; 482 String qualifier; 483 484 if ( mode == RotateMode ) 485 delta = mGizmo->getDeltaTotalRot(); 486 else if ( mode == MoveMode ) 487 delta = mGizmo->getTotalOffset(); 488 else if ( mode == ScaleMode ) 489 delta = mGizmo->getDeltaTotalScale(); 490 491 if ( mGizmo->getAlignment() == Object && mode != ScaleMode ) 492 { 493 mSelection.getOrientation().mulV( delta ); 494 } 495 496 if ( mIsZero( delta.x, 0.0001f ) ) 497 delta.x = 0.0f; 498 if ( mIsZero( delta.y, 0.0001f ) ) 499 delta.y = 0.0f; 500 if ( mIsZero( delta.z, 0.0001f ) ) 501 delta.z = 0.0f; 502 503 if ( mode == RotateMode ) 504 { 505 delta.x = mRadToDeg( delta.x ); 506 delta.y = mRadToDeg( delta.y ); 507 delta.z = mRadToDeg( delta.z ); 508 text = String::ToString( "Delta angle ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); 509 } 510 else if ( mode == MoveMode ) 511 text = String::ToString( "Delta position ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); 512 else if ( mode == ScaleMode ) 513 text = String::ToString( "Delta scale ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); 514 } 515 else 516 { 517 if ( mode == MoveMode ) 518 text = "Move selection. SHIFT while dragging duplicates objects."; 519 else if ( mode == RotateMode ) 520 text = "Rotate selection."; 521 else if ( mode == ScaleMode ) 522 text = "Scale selection."; 523 } 524 525 Con::executef( statusbar, "setInfo", text.c_str() ); 526 527 Con::executef( statusbar, "setSelectionObjectsByCount", mSelection.size() ); 528 529 return true; 530} 531 532void ForestSelectionTool::updateGizmo() 533{ 534 mGizmoProfile->restoreDefaultState(); 535 536 const GizmoMode &mode = mGizmoProfile->mode; 537 538 if ( mode == ScaleMode ) 539 { 540 mGizmoProfile->flags &= ~<a href="/coding/class/classgizmoprofile/">GizmoProfile</a>::PlanarHandlesOn; 541 mGizmoProfile->allAxesScaleUniform = true; 542 } 543} 544 545void ForestSelectionTool::onUndoAction() 546{ 547 ForestData *data = mForest->getData(); 548 549 // Remove items from our selection that no longer exist. 550 for ( S32 i = 0; i < mSelection.size(); i++ ) 551 { 552 const ForestItem &item = data->findItem( mSelection[i].getKey(), mSelection[i].getPosition() ); 553 554 if ( item == ForestItem::Invalid ) 555 { 556 mSelection.erase_fast( i ); 557 i--; 558 } 559 else 560 mSelection[i] = item; 561 } 562 563 // Recalculate our selection bounds. 564 mBounds = Box3F::Invalid; 565 for ( S32 i = 0; i < mSelection.size(); i++ ) 566 mBounds.intersect( mSelection[i].getWorldBox() ); 567} 568 569DefineEngineMethod( ForestSelectionTool, getSelectionCount, S32, (), , "" ) 570{ 571 return object->getSelectionCount(); 572} 573 574DefineEngineMethod( ForestSelectionTool, deleteSelection, void, (), , "" ) 575{ 576 object->deleteSelection(); 577} 578 579DefineEngineMethod( ForestSelectionTool, clearSelection, void, (), , "" ) 580{ 581 object->clearSelection(); 582} 583 584DefineEngineMethod( ForestSelectionTool, cutSelection, void, (), , "" ) 585{ 586 object->cutSelection(); 587} 588 589DefineEngineMethod( ForestSelectionTool, copySelection, void, (), , "" ) 590{ 591 object->copySelection(); 592} 593 594DefineEngineMethod( ForestSelectionTool, pasteSelection, void, (), , "" ) 595{ 596 object->pasteSelection(); 597} 598 599