guiRoadEditorCtrl.cpp
Engine/source/environment/editors/guiRoadEditorCtrl.cpp
Public Functions
ConsoleDocClass(GuiRoadEditorCtrl , "@brief GUI tool that makes up the Decal Road <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(GuiRoadEditorCtrl , deleteNode , void , () , "deleteNode()" )
DefineEngineMethod(GuiRoadEditorCtrl , deleteRoad , void , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getMode , const char * , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getNodePosition , Point3F , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getNodeWidth , F32 , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getSelectedNode , S32 , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getSelectedRoad , S32 , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , setMode , void , (const char *mode) , "setMode( <a href="/coding/class/classstring/">String</a> <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a> )" )
DefineEngineMethod(GuiRoadEditorCtrl , setNodePosition , void , (Point3F pos) , "" )
DefineEngineMethod(GuiRoadEditorCtrl , setNodeWidth , void , (F32 width) , "" )
DefineEngineMethod(GuiRoadEditorCtrl , setSelectedRoad , void , (const char *pathRoad) , ("") , "" )
Detailed Description
Public Functions
ConsoleDocClass(GuiRoadEditorCtrl , "@brief GUI tool that makes up the Decal Road <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(GuiRoadEditorCtrl , deleteNode , void , () , "deleteNode()" )
DefineEngineMethod(GuiRoadEditorCtrl , deleteRoad , void , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getMode , const char * , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getNodePosition , Point3F , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getNodeWidth , F32 , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getSelectedNode , S32 , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , getSelectedRoad , S32 , () , "" )
DefineEngineMethod(GuiRoadEditorCtrl , setMode , void , (const char *mode) , "setMode( <a href="/coding/class/classstring/">String</a> <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a> )" )
DefineEngineMethod(GuiRoadEditorCtrl , setNodePosition , void , (Point3F pos) , "" )
DefineEngineMethod(GuiRoadEditorCtrl , setNodeWidth , void , (F32 width) , "" )
DefineEngineMethod(GuiRoadEditorCtrl , setSelectedRoad , void , (const char *pathRoad) , ("") , "" )
IMPLEMENT_CONOBJECT(GuiRoadEditorCtrl )
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 "environment/editors/guiRoadEditorCtrl.h" 26 27#include "console/consoleTypes.h" 28#include "console/engineAPI.h" 29#include "scene/sceneManager.h" 30#include "collision/collision.h" 31#include "math/util/frustum.h" 32#include "gfx/gfxPrimitiveBuffer.h" 33#include "gfx/gfxTextureHandle.h" 34#include "gfx/gfxTransformSaver.h" 35#include "gfx/gfxDrawUtil.h" 36#include "gfx/primBuilder.h" 37#include "T3D/gameBase/gameConnection.h" 38#include "gui/core/guiCanvas.h" 39#include "gui/buttons/guiButtonCtrl.h" 40#include "gui/worldEditor/undoActions.h" 41#include "materials/materialDefinition.h" 42 43#include "T3D/Scene.h" 44 45IMPLEMENT_CONOBJECT(GuiRoadEditorCtrl); 46 47ConsoleDocClass( GuiRoadEditorCtrl, 48 "@brief GUI tool that makes up the Decal Road Editor\n\n" 49 "Editor use only.\n\n" 50 "@internal" 51); 52 53GuiRoadEditorCtrl::GuiRoadEditorCtrl() 54{ 55 // Each of the mode names directly correlates with the River Editor's 56 // tool palette 57 mSelectRoadMode = "RoadEditorSelectMode"; 58 mAddRoadMode = "RoadEditorAddRoadMode"; 59 mMovePointMode = "RoadEditorMoveMode"; 60 mScalePointMode = "RoadEditorScaleMode"; 61 mAddNodeMode = "RoadEditorAddNodeMode"; 62 mInsertPointMode = "RoadEditorInsertPointMode"; 63 mRemovePointMode = "RoadEditorRemovePointMode"; 64 65 mMode = mSelectRoadMode; 66 67 mRoadSet = NULL; 68 mSelNode = -1; 69 mHoverNode = -1; 70 mSelRoad = NULL; 71 mHoverRoad = NULL; 72 mAddNodeIdx = 0; 73 74 mDefaultWidth = 10.0f; 75 mInsertIdx = -1; 76 77 mStartWidth = -1.0f; 78 mStartX = 0; 79 80 mNodeHalfSize.set(4,4); 81 82 mHoverSplineColor.set( 255,0,0,255 ); 83 mSelectedSplineColor.set( 0,255,0,255 ); 84 mHoverNodeColor.set( 255,255,255,255 ); 85 86 mSavedDrag = false; 87 mIsDirty = false; 88 89 mMaterialAssetId = Con::getVariable("$DecalRoadEditor::defaultMaterialAsset"); 90} 91 92GuiRoadEditorCtrl::~GuiRoadEditorCtrl() 93{ 94 // nothing to do 95} 96 97void GuiRoadEditorUndoAction::undo() 98{ 99 DecalRoad *road = NULL; 100 if ( !Sim::findObject( mObjId, road ) ) 101 return; 102 103 // Temporarily save the roads current data. 104 String materialAssetId = road->mMaterialAssetId; 105 F32 textureLength = road->mTextureLength; 106 F32 breakAngle = road->mBreakAngle; 107 F32 segmentsPerBatch = road->mSegmentsPerBatch; 108 Vector<RoadNode> nodes; 109 nodes.merge( road->mNodes ); 110 111 // Restore the Road properties saved in the UndoAction 112 road->setMaterialAssetId(materialAssetId); 113 road->mBreakAngle = breakAngle; 114 road->mSegmentsPerBatch = segmentsPerBatch; 115 road->mTextureLength = textureLength; 116 road->inspectPostApply(); 117 118 // Restore the Nodes saved in the UndoAction 119 road->mNodes.clear(); 120 for ( U32 i = 0; i < mNodes.size(); i++ ) 121 { 122 road->_addNode( mNodes[i].point, mNodes[i].width ); 123 } 124 125 // Regenerate the road 126 road->regenerate(); 127 128 // If applicable set the selected road and node 129 mRoadEditor->mSelRoad = road; 130 mRoadEditor->mSelNode = -1; 131 132 // Now save the previous Road data in this UndoAction 133 // since an undo action must become a redo action and vice-versa 134 mMaterialAssetId = materialAssetId; 135 mBreakAngle = breakAngle; 136 mSegmentsPerBatch = segmentsPerBatch; 137 mTextureLength = textureLength; 138 139 mNodes.clear(); 140 mNodes.merge( nodes ); 141} 142 143bool GuiRoadEditorCtrl::onAdd() 144{ 145 if( !Parent::onAdd() ) 146 return false; 147 148 mRoadSet = DecalRoad::getServerSet(); 149 150 GFXStateBlockDesc desc; 151 desc.setCullMode( GFXCullNone ); 152 desc.setBlend(false); 153 desc.setZReadWrite( false, false ); 154 155 mZDisableSB = GFX->createStateBlock(desc); 156 157 bindMaterialAsset(Material); 158 159 return true; 160} 161 162void GuiRoadEditorCtrl::initPersistFields() 163{ 164 addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiRoadEditorCtrl ) ); 165 addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiRoadEditorCtrl ) ); 166 addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiRoadEditorCtrl ) ); 167 addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiRoadEditorCtrl ) ); 168 addField( "isDirty", TypeBool, Offset( mIsDirty, GuiRoadEditorCtrl ) ); 169 170 addField("material", TypeMaterialAssetId, Offset(mMaterialAssetId, GuiRoadEditorCtrl), "Default Material used by the Road Editor on road creation."); 171 172 //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRoadEditorCtrl) ); 173 //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRoadEditorCtrl) ); 174 //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRoadEditorCtrl) ); 175 //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRoadEditorCtrl) ); 176 177 Parent::initPersistFields(); 178} 179 180void GuiRoadEditorCtrl::onSleep() 181{ 182 Parent::onSleep(); 183 184 mMode = mSelectRoadMode; 185 mHoverNode = -1; 186 mHoverRoad = NULL; 187 setSelectedNode(-1); 188} 189 190void GuiRoadEditorCtrl::get3DCursor( GuiCursor *&cursor, 191 bool &visible, 192 const Gui3DMouseEvent &event_ ) 193{ 194 //cursor = mAddNodeCursor; 195 //visible = false; 196 197 cursor = NULL; 198 visible = false; 199 200 GuiCanvas *root = getRoot(); 201 if ( !root ) 202 return; 203 204 S32 currCursor = PlatformCursorController::curArrow; 205 206 if ( root->mCursorChanged == currCursor ) 207 return; 208 209 PlatformWindow *window = root->getPlatformWindow(); 210 PlatformCursorController *controller = window->getCursorController(); 211 212 // We've already changed the cursor, 213 // so set it back before we change it again. 214 if( root->mCursorChanged != -1) 215 controller->popCursor(); 216 217 // Now change the cursor shape 218 controller->pushCursor(currCursor); 219 root->mCursorChanged = currCursor; 220} 221 222void GuiRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) 223{ 224 if ( !isFirstResponder() ) 225 setFirstResponder(); 226 227 // Get the clicked terrain position. 228 Point3F tPos; 229 if ( !getTerrainPos( event, tPos ) ) 230 return; 231 232 mouseLock(); 233 234 // Find any road / node at the clicked position. 235 // TODO: handle overlapping roads/nodes somehow, cycle through them. 236 237 DecalRoad *roadPtr = NULL; 238 S32 closestNodeIdx = -1; 239 F32 closestDist = F32_MAX; 240 DecalRoad *closestNodeRoad = NULL; 241 242 // First, find the closest node in any road to the clicked position. 243 for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) 244 { 245 roadPtr = static_cast<DecalRoad*>( *iter ); 246 U32 idx; 247 if ( roadPtr->getClosestNode( tPos, idx ) ) 248 { 249 Point3F nodePos = roadPtr->getNodePosition(idx); 250 F32 dist = ( nodePos - tPos ).len(); 251 if ( dist < closestDist ) 252 { 253 closestNodeIdx = idx; 254 closestDist = dist; 255 closestNodeRoad = roadPtr; 256 } 257 } 258 } 259 260 // 261 // Second, determine if the screen-space node rectangle 262 // contains the clicked position. 263 264 bool nodeClicked = false; 265 S32 clickedNodeIdx = -1; 266 267 if ( closestNodeIdx != -1 ) 268 { 269 Point3F nodePos = closestNodeRoad->getNodePosition( closestNodeIdx ); 270 271 Point3F temp; 272 project( nodePos, &temp ); 273 Point2I screenPos( temp.x, temp.y ); 274 275 RectI nodeRect( screenPos - mNodeHalfSize, mNodeHalfSize * 2 ); 276 277 nodeClicked = nodeRect.pointInRect( event.mousePoint ); 278 if ( nodeClicked ) 279 clickedNodeIdx = closestNodeIdx; 280 } 281 282 // 283 // Determine the clickedRoad 284 // 285 DecalRoad *clickedRoadPtr = NULL; 286 U32 insertNodeIdx = 0; 287 288 if ( nodeClicked && (mSelRoad == NULL || closestNodeRoad == mSelRoad) ) 289 { 290 // If a node was clicked, the owning road is always 291 // considered the clicked road. 292 clickedRoadPtr = closestNodeRoad; 293 } 294 else 295 { 296 // check the selected road first 297 if ( mSelRoad != NULL && mSelRoad->containsPoint( tPos, &insertNodeIdx ) ) 298 { 299 clickedRoadPtr = mSelRoad; 300 nodeClicked = false; 301 clickedNodeIdx = -1; 302 } 303 else 304 { 305 // Otherwise, we must ask each road if it contains 306 // the clicked pos. 307 for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) 308 { 309 roadPtr = static_cast<DecalRoad*>( *iter ); 310 if ( roadPtr->containsPoint( tPos, &insertNodeIdx ) ) 311 { 312 clickedRoadPtr = roadPtr; 313 break; 314 } 315 } 316 } 317 } 318 319 // shortcuts 320 bool dblClick = ( event.mouseClickCount > 1 ); 321 if( dblClick ) 322 { 323 if( mMode == mSelectRoadMode ) 324 { 325 setMode( mAddRoadMode, true ); 326 return; 327 } 328 if( mMode == mAddNodeMode ) 329 { 330 // Delete the node attached to the cursor. 331 deleteSelectedNode(); 332 mMode = mAddRoadMode; 333 return; 334 } 335 } 336 337 //this check is here in order to bounce back from deleting a whole road with ctrl+z 338 //this check places the editor back into addroadmode 339 if ( mMode == mAddNodeMode ) 340 { 341 if ( !mSelRoad ) 342 mMode = mAddRoadMode; 343 } 344 345 if ( mMode == mSelectRoadMode ) 346 { 347 // Did not click on a road or a node. 348 if ( !clickedRoadPtr ) 349 { 350 setSelectedRoad( NULL ); 351 setSelectedNode( -1 ); 352 353 return; 354 } 355 356 // Clicked on a road that wasn't the currently selected road. 357 if ( clickedRoadPtr != mSelRoad ) 358 { 359 setSelectedRoad( clickedRoadPtr ); 360 setSelectedNode( -1 ); 361 return; 362 } 363 364 // Clicked on a node in the currently selected road that wasn't 365 // the currently selected node. 366 if ( nodeClicked ) 367 { 368 setSelectedNode( clickedNodeIdx ); 369 return; 370 } 371 372 373 // Clicked a position on the currently selected road 374 // that did not contain a node. 375 //U32 newNode = clickedRoadPtr->insertNode( tPos, mDefaultWidth, insertNodeIdx ); 376 //setSelectedNode( newNode ); 377 } 378 else if ( mMode == mAddRoadMode ) 379 { 380 if ( nodeClicked && clickedRoadPtr ) 381 { 382 // A double-click on a node in Normal mode means set AddNode mode. 383 if ( clickedNodeIdx == 0 ) 384 { 385 setSelectedRoad( clickedRoadPtr ); 386 setSelectedNode( clickedNodeIdx ); 387 388 mAddNodeIdx = clickedNodeIdx; 389 mMode = mAddNodeMode; 390 mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx ); 391 mIsDirty = true; 392 393 return; 394 } 395 else if ( clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 ) 396 { 397 setSelectedRoad( clickedRoadPtr ); 398 setSelectedNode( clickedNodeIdx ); 399 400 mAddNodeIdx = U32_MAX; 401 mMode = mAddNodeMode; 402 mSelNode = mSelRoad->addNode( tPos, mDefaultWidth ); 403 mIsDirty = true; 404 setSelectedNode( mSelNode ); 405 406 return; 407 } 408 } 409 410 DecalRoad *newRoad = new DecalRoad; 411 412 if (mMaterialAsset.notNull()) 413 newRoad->setMaterialAssetId(mMaterialAssetId); 414 415 newRoad->registerObject(); 416 417 // Add to scene 418 Scene* scene = Scene::getRootScene(); 419 if ( !scene ) 420 Con::errorf( "GuiDecalRoadEditorCtrl - could not find scene to add new DecalRoad" ); 421 else 422 scene->addObject( newRoad ); 423 424 newRoad->insertNode( tPos, mDefaultWidth, 0 ); 425 U32 newNode = newRoad->insertNode( tPos, mDefaultWidth, 1 ); 426 427 // Always add to the end of the road, the first node is the start. 428 mAddNodeIdx = U32_MAX; 429 430 setSelectedRoad( newRoad ); 431 setSelectedNode( newNode ); 432 433 mMode = mAddNodeMode; 434 435 // Disable the hover node while in addNodeMode, we 436 // don't want some random node enlarged. 437 mHoverNode = -1; 438 439 // Grab the mission editor undo manager. 440 UndoManager *undoMan = NULL; 441 if ( !Sim::findObject( "EUndoManager", undoMan ) ) 442 { 443 Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); 444 return; 445 } 446 447 // Create the UndoAction. 448 MECreateUndoAction *action = new MECreateUndoAction("Create Road"); 449 action->addObject( newRoad ); 450 451 // Submit it. 452 undoMan->addAction( action ); 453 454 //send a callback to script after were done here if one exists 455 if ( isMethod( "onRoadCreation" ) ) 456 Con::executef( this, "onRoadCreation" ); 457 458 return; 459 } 460 else if ( mMode == mAddNodeMode ) 461 { 462 // Oops the road got deleted, maybe from an undo action? 463 // Back to NormalMode. 464 if ( mSelRoad ) 465 { 466 // A double-click on a node in Normal mode means set AddNode mode. 467 if ( clickedNodeIdx == 0 ) 468 { 469 submitUndo( "Add Node" ); 470 mAddNodeIdx = clickedNodeIdx; 471 mMode = mAddNodeMode; 472 mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx ); 473 mIsDirty = true; 474 setSelectedNode( mSelNode ); 475 476 return; 477 } 478 else 479 { 480 if( clickedRoadPtr && clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 ) 481 { 482 submitUndo( "Add Node" ); 483 mAddNodeIdx = U32_MAX; 484 mMode = mAddNodeMode; 485 mSelNode = mSelRoad->addNode( tPos, mDefaultWidth ); 486 mIsDirty = true; 487 setSelectedNode( mSelNode ); 488 489 return; 490 } 491 else 492 { 493 submitUndo( "Insert Node" ); 494 // A single-click on empty space while in 495 // AddNode mode means insert / add a node. 496 //submitUndo( "Add Node" ); 497 //F32 width = mSelRoad->getNodeWidth( mSelNode ); 498 U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx); 499 mIsDirty = true; 500 setSelectedNode( newNode ); 501 502 return; 503 } 504 } 505 } 506 } 507 else if ( mMode == mInsertPointMode && mSelRoad != NULL) 508 { 509 if ( clickedRoadPtr == mSelRoad ) 510 { 511 F32 w0 = mSelRoad->getNodeWidth( insertNodeIdx ); 512 F32 w1 = mSelRoad->getNodeWidth( insertNodeIdx + 1 ); 513 F32 width = ( w0 + w1 ) * 0.5f; 514 515 submitUndo( "Insert Node" ); 516 U32 newNode = mSelRoad->insertNode( tPos, width, insertNodeIdx + 1); 517 mIsDirty = true; 518 setSelectedNode( newNode ); 519 520 return; 521 } 522 } 523 else if ( mMode == mRemovePointMode && mSelRoad != NULL) 524 { 525 if ( nodeClicked && clickedRoadPtr == mSelRoad ) 526 { 527 setSelectedNode( clickedNodeIdx ); 528 deleteSelectedNode(); 529 return; 530 } 531 } 532 else if ( mMode == mMovePointMode ) 533 { 534 if ( nodeClicked && clickedRoadPtr == mSelRoad ) 535 { 536 setSelectedNode( clickedNodeIdx ); 537 return; 538 } 539 } 540 else if ( mMode == mScalePointMode ) 541 { 542 if ( nodeClicked && clickedRoadPtr == mSelRoad ) 543 { 544 setSelectedNode( clickedNodeIdx ); 545 return; 546 } 547 } 548} 549 550void GuiRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event) 551{ 552 //mIsPanning = true; 553} 554 555void GuiRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event) 556{ 557 //mIsPanning = false; 558} 559 560void GuiRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event) 561{ 562 mStartWidth = -1.0f; 563 mSavedDrag = false; 564 mouseUnlock(); 565} 566 567void GuiRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) 568{ 569 if ( mSelRoad != NULL && mMode == mAddNodeMode ) 570 { 571 Point3F startPnt = event.pos; 572 Point3F endPnt = event.pos + event.vec * 1000.0f; 573 RayInfo ri; 574 if ( gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri) ) 575 { 576 mSelRoad->setNodePosition( mSelNode, ri.point ); 577 mIsDirty = true; 578 } 579 580 return; 581 } 582 583 // Is cursor hovering over a road? 584 if ( mMode == mSelectRoadMode ) 585 { 586 mHoverRoad = NULL; 587 588 Point3F startPnt = event.pos; 589 Point3F endPnt = event.pos + event.vec * 1000.0f; 590 591 RayInfo ri; 592 593 if ( gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri) ) 594 { 595 DecalRoad *pRoad = NULL; 596 597 for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) 598 { 599 pRoad = static_cast<DecalRoad*>( *iter ); 600 601 if ( pRoad->containsPoint( ri.point ) ) 602 { 603 mHoverRoad = pRoad; 604 break; 605 } 606 } 607 } 608 } 609 610 // Is cursor hovering over a RoadNode? 611 if ( mHoverRoad ) 612 { 613 DecalRoad *pRoad = mHoverRoad; 614 615 S32 hoverNodeIdx = -1; 616 F32 hoverNodeDist = F32_MAX; 617 618 for ( U32 i = 0; i < pRoad->mNodes.size(); i++ ) 619 { 620 const Point3F &nodePos = pRoad->mNodes[i].point; 621 622 Point3F screenPos; 623 project( nodePos, &screenPos ); 624 625 RectI rect( Point2I((S32)screenPos.x,(S32)screenPos.y) - mNodeHalfSize, mNodeHalfSize * 2 ); 626 627 if ( rect.pointInRect( event.mousePoint ) && screenPos.z < hoverNodeDist ) 628 { 629 hoverNodeDist = screenPos.z; 630 hoverNodeIdx = i; 631 } 632 } 633 634 mHoverNode = hoverNodeIdx; 635 } 636} 637 638void GuiRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) 639{ 640 // Drags are only used to transform nodes 641 if ( !mSelRoad || mSelNode == -1 || 642 ( mMode != mMovePointMode && mMode != mScalePointMode ) ) 643 return; 644 645 if ( !mSavedDrag ) 646 { 647 submitUndo( "Modify Node" ); 648 mSavedDrag = true; 649 } 650 651 if ( mMode == mScalePointMode ) 652 { 653 Point3F tPos; 654 if ( !getTerrainPos( event, tPos ) ) 655 return; 656 657 if ( mStartWidth == -1.0f ) 658 { 659 mStartWidth = mSelRoad->mNodes[mSelNode].width; 660 661 mStartX = event.mousePoint.x; 662 mStartWorld = tPos; 663 } 664 665 S32 deltaScreenX = event.mousePoint.x - mStartX; 666 667 F32 worldDist = ( event.pos - mStartWorld ).len(); 668 669 F32 deltaWorldX = ( deltaScreenX * worldDist ) / getWorldToScreenScale().y; 670 671 F32 width = mStartWidth + deltaWorldX; 672 673 mSelRoad->setNodeWidth( mSelNode, width ); 674 mIsDirty = true; 675 } 676 else if( mMode == mMovePointMode ) 677 { 678 Point3F tPos; 679 if ( !getTerrainPos( event, tPos ) ) 680 return; 681 682 mSelRoad->setNodePosition( mSelNode, tPos ); 683 mIsDirty = true; 684 } 685 686 Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) ); 687} 688 689void GuiRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event) 690{ 691 // nothing to do 692} 693 694void GuiRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event) 695{ 696 // nothing to do 697} 698 699bool GuiRoadEditorCtrl::onKeyDown(const GuiEvent& event) 700{ 701 if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode ) 702 { 703 // Delete the node attached to the cursor. 704 deleteSelectedNode(); 705 mMode = mAddRoadMode; 706 return true; 707 } 708 709 return false; 710} 711 712void GuiRoadEditorCtrl::updateGuiInfo() 713{ 714 // nothing to do 715} 716 717void GuiRoadEditorCtrl::onRender( Point2I offset, const RectI &updateRect ) 718{ 719 PROFILE_SCOPE( GuiRoadEditorCtrl_OnRender ); 720 721 Parent::onRender( offset, updateRect ); 722 return; 723} 724 725void GuiRoadEditorCtrl::renderScene(const RectI & updateRect) 726{ 727 GFX->setStateBlock( mZDisableSB ); 728 729 // Draw the spline based from the client-side road 730 // because the serverside spline is not actually reliable... 731 // Can be incorrect if the DecalRoad is before the TerrainBlock 732 // in the scene. 733 734 if ( mHoverRoad && mHoverRoad != mSelRoad ) 735 { 736 DecalRoad *pRoad = (DecalRoad*)mHoverRoad->getClientObject(); 737 if ( pRoad ) 738 _drawRoadSpline( pRoad, mHoverSplineColor ); 739 } 740 741 if ( mSelRoad ) 742 { 743 DecalRoad *pRoad = (DecalRoad*)mSelRoad->getClientObject(); 744 if ( pRoad ) 745 _drawRoadSpline( pRoad, mSelectedSplineColor ); 746 } 747} 748 749void GuiRoadEditorCtrl::renderGui( Point2I offset, const RectI &updateRect ) 750{ 751 // Draw Control nodes for selected and highlighted roads 752 if ( mHoverRoad ) 753 _drawRoadControlNodes( mHoverRoad, mHoverSplineColor ); 754 if ( mSelRoad ) 755 _drawRoadControlNodes( mSelRoad, mSelectedSplineColor ); 756 757 Parent::renderGui(offset, updateRect); 758} 759 760void GuiRoadEditorCtrl::_drawRoadSpline( DecalRoad *road, const ColorI &color ) 761{ 762 if ( road->mEdges.size() <= 1 ) 763 return; 764 765 GFXTransformSaver saver; 766 767 if ( DecalRoad::smShowSpline ) 768 { 769 // Render the center-line 770 PrimBuild::color( color ); 771 PrimBuild::begin( GFXLineStrip, road->mEdges.size() ); 772 for ( U32 i = 0; i < road->mEdges.size(); i++ ) 773 { 774 PrimBuild::vertex3fv( road->mEdges[i].p1 ); 775 } 776 PrimBuild::end(); 777 } 778 779 if ( DecalRoad::smWireframe ) 780 { 781 // Left-side line 782 PrimBuild::color3i( 100, 100, 100 ); 783 PrimBuild::begin( GFXLineStrip, road->mEdges.size() ); 784 for ( U32 i = 0; i < road->mEdges.size(); i++ ) 785 { 786 PrimBuild::vertex3fv( road->mEdges[i].p0 ); 787 } 788 PrimBuild::end(); 789 790 // Right-side line 791 PrimBuild::begin( GFXLineStrip, road->mEdges.size() ); 792 for ( U32 i = 0; i < road->mEdges.size(); i++ ) 793 { 794 PrimBuild::vertex3fv( road->mEdges[i].p2 ); 795 } 796 PrimBuild::end(); 797 798 // Cross-sections 799 PrimBuild::begin( GFXLineList, road->mEdges.size() * 2 ); 800 for ( U32 i = 0; i < road->mEdges.size(); i++ ) 801 { 802 PrimBuild::vertex3fv( road->mEdges[i].p0 ); 803 PrimBuild::vertex3fv( road->mEdges[i].p2 ); 804 } 805 PrimBuild::end(); 806 } 807} 808 809void GuiRoadEditorCtrl::_drawRoadControlNodes( DecalRoad *road, const ColorI &color ) 810{ 811 if ( !DecalRoad::smShowSpline ) 812 return; 813 814 RectI bounds = getBounds(); 815 816 GFXDrawUtil *drawer = GFX->getDrawUtil(); 817 818 bool isSelected = ( road == mSelRoad ); 819 bool isHighlighted = ( road == mHoverRoad ); 820 821 for ( U32 i = 0; i < road->mNodes.size(); i++ ) 822 { 823 if ( false && isSelected && mSelNode == i ) 824 continue; 825 826 const Point3F &wpos = road->mNodes[i].point; 827 828 Point3F spos; 829 project( wpos, &spos ); 830 831 if ( spos.z > 1.0f ) 832 continue; 833 834 Point2I posi; 835 posi.x = spos.x; 836 posi.y = spos.y; 837 838 if ( !bounds.pointInRect( posi ) ) 839 continue; 840 841 ColorI theColor = color; 842 Point2I nodeHalfSize = mNodeHalfSize; 843 844 if ( isHighlighted && mHoverNode == i ) 845 nodeHalfSize += Point2I(2,2); 846 847 if ( isSelected ) 848 { 849 if ( mSelNode == i ) 850 { 851 theColor.set(0,0,255); 852 } 853 else if ( i == 0 ) 854 { 855 theColor.set(0,255,0); 856 } 857 else if ( i == road->mNodes.size() - 1 ) 858 { 859 theColor.set(255,0,0); 860 } 861 } 862 863 drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor ); 864 } 865} 866 867bool GuiRoadEditorCtrl::getTerrainPos( const Gui3DMouseEvent & event, Point3F &tpos ) 868{ 869 // Find clicked point on the terrain 870 871 Point3F startPnt = event.pos; 872 Point3F endPnt = event.pos + event.vec * 10000.0f; 873 874 RayInfo ri; 875 bool hit; 876 877 hit = gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri); 878 tpos = ri.point; 879 880 return hit; 881} 882 883void GuiRoadEditorCtrl::deleteSelectedNode() 884{ 885 if ( !mSelRoad || mSelNode == -1 ) 886 return; 887 888 // If the road has only two nodes remaining, 889 // delete the whole road. 890 if ( mSelRoad->mNodes.size() <= 2 ) 891 { 892 deleteSelectedRoad(); 893 } 894 else 895 { 896 // Only submit undo if we weren't in AddMode 897 if ( mMode != mAddNodeMode ) 898 submitUndo( "Delete Node" ); 899 900 // Delete the SelectedNode of the SelectedRoad 901 mSelRoad->deleteNode(mSelNode); 902 mIsDirty = true; 903 904 // We deleted the Node but not the Road (it has nodes left) 905 // so decrement the currently selected node. 906 if ( mSelRoad->mNodes.size() <= mSelNode ) 907 mSelNode--; 908 } 909} 910 911void GuiRoadEditorCtrl::deleteSelectedRoad( bool undoAble ) 912{ 913 AssertFatal( mSelRoad != NULL, "GuiRoadEditorCtrl::deleteSelectedRoad() - No road IS selected" ); 914 915 // Not undo-able? Just delete it. 916 if ( !undoAble ) 917 { 918 DecalRoad *lastRoad = mSelRoad; 919 920 setSelectedRoad(NULL); 921 922 lastRoad->deleteObject(); 923 mIsDirty = true; 924 925 return; 926 } 927 928 // Grab the mission editor undo manager. 929 UndoManager *undoMan = NULL; 930 if ( !Sim::findObject( "EUndoManager", undoMan ) ) 931 { 932 // Couldn't find it? Well just delete the road. 933 Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); 934 return; 935 } 936 else 937 { 938 DecalRoad *lastRoad = mSelRoad; 939 setSelectedRoad(NULL); 940 941 // Create the UndoAction. 942 MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road"); 943 action->deleteObject( lastRoad ); 944 mIsDirty = true; 945 946 // Submit it. 947 undoMan->addAction( action ); 948 } 949} 950 951void GuiRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false ) 952{ 953 mMode = mode; 954 955 if( sourceShortcut ) 956 Con::executef( this, "paletteSync", mode ); 957} 958 959void GuiRoadEditorCtrl::setSelectedRoad( DecalRoad *road ) 960{ 961 mSelRoad = road; 962 963 if ( road != NULL ) 964 Con::executef( this, "onRoadSelected", road->getIdString() ); 965 else 966 Con::executef( this, "onRoadSelected" ); 967 968 if ( mSelRoad != road ) 969 setSelectedNode(-1); 970} 971 972void GuiRoadEditorCtrl::setNodeWidth( F32 width ) 973{ 974 if ( mSelRoad && mSelNode != -1 ) 975 { 976 mSelRoad->setNodeWidth( mSelNode, width ); 977 mIsDirty = true; 978 } 979} 980 981F32 GuiRoadEditorCtrl::getNodeWidth() 982{ 983 if ( mSelRoad && mSelNode != -1 ) 984 return mSelRoad->getNodeWidth( mSelNode ); 985 986 return 0.0f; 987} 988 989void GuiRoadEditorCtrl::setNodePosition(const Point3F& pos) 990{ 991 if ( mSelRoad && mSelNode != -1 ) 992 { 993 mSelRoad->setNodePosition( mSelNode, pos ); 994 mIsDirty = true; 995 } 996} 997 998Point3F GuiRoadEditorCtrl::getNodePosition() 999{ 1000 if ( mSelRoad && mSelNode != -1 ) 1001 return mSelRoad->getNodePosition( mSelNode ); 1002 1003 return Point3F( 0, 0, 0 ); 1004} 1005 1006void GuiRoadEditorCtrl::setSelectedNode( S32 node ) 1007{ 1008 //if ( mSelNode == node ) 1009 // return; 1010 1011 mSelNode = node; 1012 1013 if ( mSelNode != -1 && mSelRoad != NULL ) 1014 Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode), Con::getFloatArg(mSelRoad->mNodes[mSelNode].width) ); 1015 else 1016 Con::executef( this, "onNodeSelected", Con::getIntArg(-1) ); 1017} 1018 1019void GuiRoadEditorCtrl::submitUndo( const UTF8 *name ) 1020{ 1021 // Grab the mission editor undo manager. 1022 UndoManager *undoMan = NULL; 1023 if ( !Sim::findObject( "EUndoManager", undoMan ) ) 1024 { 1025 Con::errorf( "GuiRoadEditorCtrl::submitUndo() - EUndoManager not found!" ); 1026 return; 1027 } 1028 1029 // Setup the action. 1030 GuiRoadEditorUndoAction *action = new GuiRoadEditorUndoAction( name ); 1031 1032 action->mObjId = mSelRoad->getId(); 1033 action->mBreakAngle = mSelRoad->mBreakAngle; 1034 action->mMaterialAssetId = mSelRoad->mMaterialAssetId; 1035 action->mSegmentsPerBatch = mSelRoad->mSegmentsPerBatch; 1036 action->mTextureLength = mSelRoad->mTextureLength; 1037 action->mRoadEditor = this; 1038 1039 for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ ) 1040 { 1041 action->mNodes.push_back( mSelRoad->mNodes[i] ); 1042 } 1043 1044 undoMan->addAction( action ); 1045} 1046 1047DefineEngineMethod( GuiRoadEditorCtrl, deleteNode, void, (), , "deleteNode()" ) 1048{ 1049 object->deleteSelectedNode(); 1050} 1051 1052DefineEngineMethod( GuiRoadEditorCtrl, getMode, const char*, (), , "" ) 1053{ 1054 return object->getMode(); 1055} 1056 1057DefineEngineMethod( GuiRoadEditorCtrl, setMode, void, ( const char * mode ), , "setMode( String mode )" ) 1058{ 1059 String newMode = ( mode ); 1060 object->setMode( newMode ); 1061} 1062 1063DefineEngineMethod( GuiRoadEditorCtrl, getNodeWidth, F32, (), , "" ) 1064{ 1065 return object->getNodeWidth(); 1066} 1067 1068DefineEngineMethod( GuiRoadEditorCtrl, setNodeWidth, void, ( F32 width ), , "" ) 1069{ 1070 object->setNodeWidth( width ); 1071} 1072 1073DefineEngineMethod( GuiRoadEditorCtrl, getNodePosition, Point3F, (), , "" ) 1074{ 1075 1076 return object->getNodePosition(); 1077} 1078 1079DefineEngineMethod( GuiRoadEditorCtrl, setNodePosition, void, ( Point3F pos ), , "" ) 1080{ 1081 1082 object->setNodePosition( pos ); 1083} 1084 1085DefineEngineMethod( GuiRoadEditorCtrl, setSelectedRoad, void, ( const char * pathRoad ), (""), "" ) 1086{ 1087 if (String::compare( pathRoad,"")==0 ) 1088 object->setSelectedRoad(NULL); 1089 else 1090 { 1091 DecalRoad *road = NULL; 1092 if ( Sim::findObject( pathRoad, road ) ) 1093 object->setSelectedRoad(road); 1094 } 1095} 1096 1097DefineEngineMethod( GuiRoadEditorCtrl, getSelectedRoad, S32, (), , "" ) 1098{ 1099 DecalRoad *road = object->getSelectedRoad(); 1100 if ( road ) 1101 return road->getId(); 1102 1103 return NULL; 1104} 1105 1106DefineEngineMethod( GuiRoadEditorCtrl, getSelectedNode, S32, (), , "" ) 1107{ 1108 return object->getSelectedNode(); 1109} 1110 1111DefineEngineMethod( GuiRoadEditorCtrl, deleteRoad, void, (), , "" ) 1112{ 1113 object->deleteSelectedRoad(); 1114} 1115