Torque3D Documentation / _generateds / guiRoadEditorCtrl.cpp

guiRoadEditorCtrl.cpp

Engine/source/environment/editors/guiRoadEditorCtrl.cpp

More...

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