Torque3D Documentation / _generateds / guiRiverEditorCtrl.cpp

guiRiverEditorCtrl.cpp

Engine/source/environment/editors/guiRiverEditorCtrl.cpp

More...

Public Functions

ConsoleDocClass(GuiRiverEditorCtrl , "@brief GUI tool that makes up the <a href="/coding/class/classriver/">River</a> <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(GuiRiverEditorCtrl , deleteNode , void , () , "deleteNode()" )
DefineEngineMethod(GuiRiverEditorCtrl , getMode , const char * , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , getNodeDepth , F32 , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , getNodeNormal , Point3F , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , getNodePosition , Point3F , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , getNodeWidth , F32 , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , getSelectedRiver , S32 , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , regenerate , void , () , "" )
DefineEngineMethod(GuiRiverEditorCtrl , 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(GuiRiverEditorCtrl , setNodeDepth , void , (F32 depth) , "" )
DefineEngineMethod(GuiRiverEditorCtrl , setNodeNormal , void , (Point3F normal) , "" )
DefineEngineMethod(GuiRiverEditorCtrl , setNodePosition , void , (Point3F pos) , "" )
DefineEngineMethod(GuiRiverEditorCtrl , setNodeWidth , void , (F32 width) , "" )
DefineEngineMethod(GuiRiverEditorCtrl , setSelectedRiver , void , (const char *objName) , ("") , "" )

Detailed Description

Public Functions

ConsoleDocClass(GuiRiverEditorCtrl , "@brief GUI tool that makes up the <a href="/coding/class/classriver/">River</a> <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(GuiRiverEditorCtrl , deleteNode , void , () , "deleteNode()" )

DefineEngineMethod(GuiRiverEditorCtrl , getMode , const char * , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , getNodeDepth , F32 , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , getNodeNormal , Point3F , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , getNodePosition , Point3F , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , getNodeWidth , F32 , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , getSelectedRiver , S32 , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , regenerate , void , () , "" )

DefineEngineMethod(GuiRiverEditorCtrl , 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(GuiRiverEditorCtrl , setNodeDepth , void , (F32 depth) , "" )

DefineEngineMethod(GuiRiverEditorCtrl , setNodeNormal , void , (Point3F normal) , "" )

DefineEngineMethod(GuiRiverEditorCtrl , setNodePosition , void , (Point3F pos) , "" )

DefineEngineMethod(GuiRiverEditorCtrl , setNodeWidth , void , (F32 width) , "" )

DefineEngineMethod(GuiRiverEditorCtrl , setSelectedRiver , void , (const char *objName) , ("") , "" )

IMPLEMENT_CONOBJECT(GuiRiverEditorCtrl )

   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/guiRiverEditorCtrl.h"
  26
  27#include "console/consoleTypes.h"
  28#include "console/engineAPI.h"
  29#include "environment/river.h"
  30#include "renderInstance/renderPassManager.h"
  31#include "collision/collision.h"
  32#include "math/util/frustum.h"
  33#include "math/mathUtils.h"
  34#include "gfx/gfxPrimitiveBuffer.h"
  35#include "gfx/gfxTextureHandle.h"
  36#include "gfx/gfxTransformSaver.h"
  37#include "gfx/primBuilder.h"
  38#include "gfx/gfxDrawUtil.h"
  39#include "scene/sceneRenderState.h"
  40#include "scene/sceneManager.h"
  41#include "gui/core/guiCanvas.h"
  42#include "gui/buttons/guiButtonCtrl.h"
  43#include "gui/worldEditor/undoActions.h"
  44#include "T3D/gameBase/gameConnection.h"
  45#include "T3D/prefab.h"
  46
  47#include "T3D/Scene.h"
  48
  49IMPLEMENT_CONOBJECT(GuiRiverEditorCtrl);
  50
  51ConsoleDocClass( GuiRiverEditorCtrl,
  52   "@brief GUI tool that makes up the River Editor\n\n"
  53   "Editor use only.\n\n"
  54   "@internal"
  55);
  56
  57GuiRiverEditorCtrl::GuiRiverEditorCtrl()
  58 : mDefaultWidth( 10.0f ),
  59   mDefaultDepth( 5.0f ),
  60   mDefaultNormal( 0, 0, 1 )
  61{   
  62   // Each of the mode names directly correlates with the River Editor's
  63   // tool palette
  64   mSelectRiverMode = "RiverEditorSelectMode";
  65   mAddRiverMode = "RiverEditorAddRiverMode";
  66   mMovePointMode = "RiverEditorMoveMode";
  67   mRotatePointMode = "RiverEditorRotateMode";
  68   mScalePointMode = "RiverEditorScaleMode";
  69   mAddNodeMode = "RiverEditorAddNodeMode";
  70   mInsertPointMode = "RiverEditorInsertPointMode";
  71   mRemovePointMode = "RiverEditorRemovePointMode";
  72   
  73   mMode = mSelectRiverMode;
  74   
  75   mRiverSet = NULL;   
  76   mSelNode = -1;
  77   mSelRiver = NULL;
  78   mHoverRiver = NULL;
  79   mAddNodeIdx = 0;
  80   mHoverNode = -1;
  81
  82   mInsertIdx = -1;
  83
  84   mStartWidth = -1.0f;
  85   mStartHeight = -1.0f;
  86   mStartX = 0;
  87
  88   mSavedDrag = false;
  89   mIsDirty = false;
  90
  91   mNodeHalfSize.set(4,4);
  92
  93   mNodeSphereRadius = 15.0f;
  94   mNodeSphereFillColor.set( 15,15,100,145 );
  95   mNodeSphereLineColor.set( 25,25,25,0 );
  96   mHoverSplineColor.set( 255,0,0,255 );
  97   mSelectedSplineColor.set( 0,255,0,255 );
  98   mHoverNodeColor.set( 255,255,255,255 );
  99
 100   mStartDragMousePoint = InvalidMousePoint;
 101   //mMoveNodeCursor = NULL;
 102   //mAddNodeCursor = NULL;
 103   //mInsertNodeCursor = NULL;
 104   //mResizeNodeCursor = NULL;
 105}
 106
 107GuiRiverEditorCtrl::~GuiRiverEditorCtrl()
 108{
 109   // nothing to do
 110}
 111
 112void GuiRiverEditorUndoAction::undo()
 113{
 114   River *river = NULL;
 115   if ( !Sim::findObject( mObjId, river ) )
 116      return;
 117
 118   // Temporarily save the Rivers current data.
 119   F32 metersPerSeg = river->mMetersPerSegment;
 120   Vector<RiverNode> nodes;   
 121   nodes.merge( river->mNodes );
 122
 123   // Restore the River properties saved in the UndoAction
 124   river->mMetersPerSegment = mMetersPerSegment;
 125
 126   // Restore the Nodes saved in the UndoAction
 127   river->mNodes.clear();
 128   for ( U32 i = 0; i < mNodes.size(); i++ )
 129   {
 130      river->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal );      
 131   }
 132
 133   // Regenerate the River
 134   river->regenerate();
 135
 136   // If applicable set the selected River and node
 137   mRiverEditor->mSelRiver = river;
 138   mRiverEditor->mSelNode = -1;
 139
 140   // Now save the previous River data in this UndoAction
 141   // since an undo action must become a redo action and vice-versa
 142   mMetersPerSegment = metersPerSeg;
 143   mNodes.clear();
 144   mNodes.merge( nodes );
 145}
 146
 147bool GuiRiverEditorCtrl::onAdd()
 148{
 149   if( !Parent::onAdd() )
 150      return false;
 151
 152   mRiverSet = River::getServerSet();
 153
 154   GFXStateBlockDesc desc;
 155   desc.fillMode = GFXFillSolid;      
 156   desc.setBlend( false );
 157   desc.setZReadWrite( false, false );
 158   desc.setCullMode( GFXCullNone );   
 159
 160   mZDisableSB = GFX->createStateBlock(desc);
 161
 162   desc.setZReadWrite( true, true );
 163   mZEnableSB = GFX->createStateBlock(desc);
 164
 165   SceneManager::getPreRenderSignal().notify( this, &GuiRiverEditorCtrl::_prepRenderImage );
 166
 167   return true;
 168}
 169
 170void GuiRiverEditorCtrl::initPersistFields()
 171{
 172   addField( "DefaultWidth",        TypeF32,    Offset( mDefaultWidth, GuiRiverEditorCtrl ) );
 173   addField( "DefaultDepth",        TypeF32,    Offset( mDefaultDepth, GuiRiverEditorCtrl ) );
 174   addField( "DefaultNormal",       TypePoint3F,Offset( mDefaultNormal, GuiRiverEditorCtrl ) );
 175   addField( "HoverSplineColor",    TypeColorI, Offset( mHoverSplineColor, GuiRiverEditorCtrl ) );
 176   addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiRiverEditorCtrl ) );
 177   addField( "HoverNodeColor",      TypeColorI, Offset( mHoverNodeColor, GuiRiverEditorCtrl ) );
 178   addField( "isDirty",             TypeBool,   Offset( mIsDirty, GuiRiverEditorCtrl ) );
 179   //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRiverEditorCtrl) );
 180   //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRiverEditorCtrl) );
 181   //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRiverEditorCtrl) );
 182   //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRiverEditorCtrl) );
 183
 184   Parent::initPersistFields();
 185}
 186
 187void GuiRiverEditorCtrl::onSleep()
 188{
 189   Parent::onSleep();
 190
 191   mMode = mSelectRiverMode;  
 192   mHoverNode = -1;
 193   mHoverRiver = NULL;
 194   setSelectedNode(-1);
 195   //mSelRiver = NULL;
 196   //mSelNode = -1;
 197}
 198
 199void GuiRiverEditorCtrl::get3DCursor( GuiCursor *&cursor, 
 200                                       bool &visible, 
 201                                       const Gui3DMouseEvent &event_ )
 202{
 203   //cursor = mAddNodeCursor;
 204   //visible = false;
 205   
 206   cursor = NULL;
 207   visible = false;
 208
 209   GuiCanvas *root = getRoot();
 210   if ( !root )
 211      return;
 212
 213   S32 currCursor = PlatformCursorController::curArrow;
 214
 215   if ( root->mCursorChanged == currCursor )
 216      return;
 217
 218   PlatformWindow *window = root->getPlatformWindow();
 219   PlatformCursorController *controller = window->getCursorController();
 220   
 221   // We've already changed the cursor, 
 222   // so set it back before we change it again.
 223   if( root->mCursorChanged != -1)
 224      controller->popCursor();
 225
 226   // Now change the cursor shape
 227   controller->pushCursor(currCursor);
 228   root->mCursorChanged = currCursor;   
 229}
 230
 231void GuiRiverEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
 232{
 233   _process3DMouseDown( event );
 234
 235   mGizmo->on3DMouseDown( event );
 236
 237   if ( !isFirstResponder() )
 238      setFirstResponder();
 239}
 240
 241void GuiRiverEditorCtrl::_process3DMouseDown( const Gui3DMouseEvent& event )
 242{
 243   // Get the raycast collision position
 244   Point3F tPos;
 245   if ( !getStaticPos( event, tPos ) )
 246      return;  
 247      
 248   mouseLock();
 249
 250   // Construct a LineSegment from the camera position to 1000 meters away in
 251   // the direction clicked.
 252   // If that segment hits the terrain, truncate the ray to only be that length.
 253
 254   // We will use a LineSegment/Sphere intersection test to determine if a RiverNode
 255   // was clicked.   
 256
 257   Point3F startPnt = event.pos;
 258   Point3F endPnt = event.pos + event.vec * 1000.0f;
 259
 260   RayInfo ri;   
 261
 262   if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
 263      endPnt = ri.point;
 264
 265   River *riverPtr = NULL;
 266   River *clickedRiverPtr = NULL;
 267
 268   // Did we click on a river? check current selection first
 269   U32 insertNodeIdx = -1;
 270   Point3F collisionPnt;
 271   if ( mSelRiver != NULL && mSelRiver->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
 272   {
 273      clickedRiverPtr = mSelRiver;
 274   }
 275   else
 276   {
 277      for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
 278      {
 279         riverPtr = static_cast<River*>( *iter );
 280
 281         // Do not select or edit a River within a Prefab.
 282         if ( Prefab::getPrefabByChild(riverPtr) )
 283            continue;
 284
 285         if ( riverPtr->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
 286         {
 287            clickedRiverPtr = riverPtr;
 288            break;
 289         }
 290      }
 291   }
 292
 293   // Did we click on a riverNode?
 294   bool nodeClicked = false;   
 295   S32 clickedNodeIdx = -1;
 296   F32 clickedNodeDist = mNodeSphereRadius;
 297
 298   // If we clicked on the currently selected river, only scan its nodes
 299   if ( mSelRiver != NULL && clickedRiverPtr == mSelRiver )
 300   {
 301      for ( U32 i = 0; i < mSelRiver->mNodes.size(); i++ )
 302      {
 303         const Point3F &nodePos = mSelRiver->mNodes[i].point;
 304
 305         Point3F screenPos;
 306         project( nodePos, &screenPos );
 307
 308         F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
 309         if ( dist < clickedNodeDist )
 310         {
 311            clickedNodeDist = dist;
 312            clickedNodeIdx = i;
 313            insertNodeIdx = i;
 314            nodeClicked = true;
 315         }
 316      }
 317   }
 318   else
 319   {
 320      for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
 321      {
 322         riverPtr = static_cast<River*>( *iter );
 323
 324         // Do not select or edit a River within a Prefab.
 325         if ( Prefab::getPrefabByChild(riverPtr) )
 326            continue;
 327         
 328         for ( U32 i = 0; i < riverPtr->mNodes.size(); i++ )
 329         {
 330            const Point3F &nodePos = riverPtr->mNodes[i].point;
 331
 332            Point3F screenPos;
 333            project( nodePos, &screenPos );
 334
 335            F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
 336            if ( dist < clickedNodeDist )
 337            {
 338               // we found a hit!
 339               clickedNodeDist = dist;
 340               clickedNodeIdx = i;
 341               insertNodeIdx = i;
 342               nodeClicked = true;
 343               clickedRiverPtr = riverPtr;
 344            }
 345         }
 346      }
 347   }
 348   
 349   // shortcuts
 350   bool dblClick = ( event.mouseClickCount > 1 );
 351   if( dblClick )
 352   { 
 353      if( mMode == mSelectRiverMode )
 354      {
 355         setMode( mAddRiverMode, true );
 356         return;
 357      }
 358      if( mMode == mAddNodeMode )
 359      {
 360         // Delete the node attached to the cursor.
 361         deleteSelectedNode();
 362         mMode = mAddRiverMode;
 363         return;
 364      }
 365   }
 366
 367   //this check is here in order to bounce back from deleting a whole road with ctrl+z
 368   //this check places the editor back into addrivermode
 369   if ( mMode == mAddNodeMode )
 370   {
 371      if ( !mSelRiver )
 372         mMode = mAddRiverMode;
 373   }
 374
 375   if ( mMode == mSelectRiverMode )
 376   {
 377      // Did not click on a River or a node.
 378      if ( !clickedRiverPtr  )
 379      {
 380         setSelectedRiver( NULL );
 381         setSelectedNode( -1 );
 382         
 383         return;
 384      }
 385
 386      // Clicked on a River that wasn't the currently selected River.
 387      if ( clickedRiverPtr != mSelRiver )
 388      {
 389         setSelectedRiver( clickedRiverPtr );
 390         setSelectedNode( clickedNodeIdx );
 391         return;
 392      }
 393
 394     // Clicked on a node in the currently selected River that wasn't
 395      // the currently selected node.
 396      if ( nodeClicked )
 397      {
 398         setSelectedNode( clickedNodeIdx );
 399         return;
 400      }
 401   }
 402   else if ( mMode == mAddRiverMode )
 403   {
 404      if ( nodeClicked )
 405      {
 406         // A double-click on a node in Normal mode means set AddNode mode.  
 407         if ( clickedNodeIdx == 0 )
 408         {
 409            setSelectedRiver( clickedRiverPtr );
 410            setSelectedNode( clickedNodeIdx );
 411
 412            mAddNodeIdx = clickedNodeIdx;
 413            mMode = mAddNodeMode; 
 414
 415            mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
 416            mIsDirty = true;
 417
 418            return;
 419         }
 420         else if ( clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 )
 421         {
 422            setSelectedRiver( clickedRiverPtr );
 423            setSelectedNode( clickedNodeIdx );
 424
 425            mAddNodeIdx = U32_MAX;
 426            mMode = mAddNodeMode;
 427
 428            mSelNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
 429            mIsDirty = true;
 430            setSelectedNode( mSelNode );
 431
 432            return;
 433         } 
 434      }
 435
 436      if ( !isMethod( "createRiver" ) )
 437      {
 438         Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method does not exist." );
 439         return;
 440      }
 441
 442      const char *res = Con::executef( this, "createRiver" );
 443
 444      River *newRiver;
 445      if ( !Sim::findObject( res, newRiver ) )
 446      {
 447         Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method did not return a river object." );
 448         return;
 449      }                
 450
 451      // Add to Scene                              
 452      Scene* scene = Scene::getRootScene();
 453      if ( !scene )               
 454         Con::errorf( "GuiRiverEditorCtrl - could not find root Scene to add new River" );
 455      else
 456         scene->addObject( newRiver );
 457
 458      Point3F pos( endPnt );
 459      pos.z += mDefaultDepth * 0.5f;
 460
 461      newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 );
 462      U32 newNode = newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 );
 463
 464      // Always add to the end of the road, the first node is the start.
 465      mAddNodeIdx = U32_MAX;
 466      
 467      setSelectedRiver( newRiver );      
 468      setSelectedNode( newNode );
 469
 470      mMode = mAddNodeMode;
 471
 472      // Disable the hover node while in addNodeMode, we
 473      // don't want some random node enlarged.
 474      mHoverNode = -1;
 475
 476      // Grab the mission editor undo manager.
 477      UndoManager *undoMan = NULL;
 478      if ( !Sim::findObject( "EUndoManager", undoMan ) )
 479      {
 480         Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
 481         return;           
 482      }
 483
 484      // Create the UndoAction.
 485      MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad");
 486      action->addObject( newRiver );
 487
 488      // Submit it.               
 489      undoMan->addAction( action );
 490
 491      return;
 492   }
 493   else if ( mMode == mAddNodeMode )
 494   {
 495      // Oops the road got deleted, maybe from an undo action?
 496      // Back to NormalMode.
 497      if ( mSelRiver )
 498      {
 499         // A double-click on a node in Normal mode means set AddNode mode.  
 500         if ( clickedNodeIdx == 0 )
 501         {
 502            submitUndo( "Add Node" );
 503            mAddNodeIdx = clickedNodeIdx;
 504            mMode = mAddNodeMode;
 505            mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
 506            mIsDirty = true;
 507            setSelectedNode( mSelNode );
 508
 509            return;
 510         }
 511         else
 512         {
 513            if( clickedRiverPtr && clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 )
 514            {
 515               submitUndo( "Add Node" );
 516               mAddNodeIdx = U32_MAX;
 517               mMode = mAddNodeMode;
 518               U32 newNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);  
 519               mIsDirty = true;
 520               setSelectedNode( newNode );
 521
 522               return;
 523            }
 524            else
 525            {
 526               submitUndo( "Insert Node" );
 527               // A single-click on empty space while in
 528               // AddNode mode means insert / add a node.
 529               //submitUndo( "Add Node" );
 530               //F32 width = mSelRiver->getNodeWidth( mSelNode );
 531               U32 newNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx);
 532               mIsDirty = true;
 533               setSelectedNode( newNode );
 534
 535               return;
 536            }
 537         }
 538      }
 539   }
 540   else if ( mMode == mInsertPointMode && mSelRiver != NULL )
 541   {
 542      if ( clickedRiverPtr == mSelRiver )
 543      {
 544         // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node...
 545         // in order to handle inserting nodes in the same way as for DecalRoad
 546
 547         U32 prevNodeIdx = insertNodeIdx;
 548         U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRiver->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1;
 549
 550         const RiverNode &prevNode = mSelRiver->mNodes[prevNodeIdx];
 551         const RiverNode &nextNode = mSelRiver->mNodes[nextNodeIdx];
 552
 553         F32 width = ( prevNode.width + nextNode.width ) * 0.5f;
 554         F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f;
 555         Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f;
 556         normal.normalize();
 557
 558         submitUndo( "Insert Node" );
 559         U32 newNode = mSelRiver->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 );
 560         mIsDirty = true;
 561         setSelectedNode( newNode );
 562
 563         return;
 564       }
 565   }
 566   else if ( mMode == mRemovePointMode && mSelRiver != NULL )
 567   {
 568      if ( nodeClicked && clickedRiverPtr == mSelRiver )
 569      {
 570         setSelectedNode( clickedNodeIdx );
 571         deleteSelectedNode();
 572         return;
 573      }
 574   }
 575   else if ( mMode == mMovePointMode )
 576   {
 577      if ( nodeClicked && clickedRiverPtr == mSelRiver )
 578      {
 579         setSelectedNode( clickedNodeIdx );
 580         return;
 581      }
 582   }
 583   else if ( mMode == mScalePointMode )
 584   {
 585      if ( nodeClicked && clickedRiverPtr == mSelRiver )
 586      {
 587         setSelectedNode( clickedNodeIdx );
 588         return;
 589      }
 590   }
 591   else if ( mMode == mRotatePointMode )
 592   {
 593      if ( nodeClicked && clickedRiverPtr == mSelRiver )
 594      {
 595         setSelectedNode( clickedNodeIdx );
 596         return;
 597      }
 598   }
 599}
 600
 601void GuiRiverEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event)
 602{
 603   //mIsPanning = true;
 604}
 605
 606void GuiRiverEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event)
 607{
 608   //mIsPanning = false;
 609}
 610
 611void GuiRiverEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
 612{
 613   // Keep the Gizmo up to date.
 614   mGizmo->on3DMouseUp( event );
 615
 616   mStartWidth = -1.0f;     
 617   mStartHeight = -1.0f;
 618   mSavedDrag = false;
 619
 620   mouseUnlock();
 621}
 622
 623void GuiRiverEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
 624{
 625   if ( mSelRiver != NULL && mMode == mAddNodeMode )
 626   {
 627      Point3F pos;
 628      if ( getStaticPos( event, pos ) )         
 629      {
 630         pos.z += mSelRiver->getNodeDepth(mSelNode) * 0.5f;
 631         mSelRiver->setNodePosition( mSelNode, pos );
 632         mIsDirty = true;
 633      }
 634
 635      return;
 636   }
 637
 638   if ( mSelRiver != NULL && mSelNode != -1 )
 639      mGizmo->on3DMouseMove( event );
 640
 641   // Is cursor hovering over a river?
 642   if ( mMode == mSelectRiverMode )
 643   {
 644      mHoverRiver = NULL;
 645
 646      Point3F startPnt = event.pos;
 647      Point3F endPnt = event.pos + event.vec * 1000.0f;
 648
 649      RayInfo ri;   
 650
 651      if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
 652         endPnt = ri.point;
 653
 654      River *pRiver = NULL;
 655
 656      for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
 657      {
 658         pRiver = static_cast<River*>( *iter );
 659
 660         // Do not select or edit a River within a Prefab.
 661         if ( Prefab::getPrefabByChild(pRiver) )
 662            continue;
 663
 664         if ( pRiver->collideRay( event.pos, event.vec ) )
 665         {
 666            mHoverRiver = pRiver;
 667            break;
 668         }
 669      }      
 670   }
 671
 672   // Is cursor hovering over a RiverNode?
 673   if ( mHoverRiver )
 674   {      
 675      River *pRiver = mHoverRiver;
 676
 677      S32 hoverNodeIdx = -1;
 678      F32 hoverNodeDist = mNodeSphereRadius;
 679
 680      //for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
 681      //{
 682      //   River *pRiver = static_cast<River*>( *iter );
 683
 684         for ( U32 i = 0; i < pRiver->mNodes.size(); i++ )
 685         {
 686            const Point3F &nodePos = pRiver->mNodes[i].point;
 687
 688            Point3F screenPos;
 689            project( nodePos, &screenPos );
 690
 691            F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
 692            if ( dist < hoverNodeDist )
 693            {
 694               // we found a hit!
 695               hoverNodeDist = dist;
 696               hoverNodeIdx = i;
 697            }           
 698         }      
 699      //}  
 700
 701      mHoverNode = hoverNodeIdx;
 702   }
 703}
 704
 705void GuiRiverEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
 706{
 707   // Drags are only used to transform nodes
 708   if ( !mSelRiver || mSelNode == -1 ||
 709      ( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) )
 710      return;
 711
 712   // If we haven't already saved,
 713   // save an undo action to get back to this state,
 714   // before we make any modifications to the selected node.
 715   if ( !mSavedDrag )
 716   {
 717      submitUndo( "Modify Node" );
 718      mSavedDrag = true;
 719   }
 720
 721   // Let the gizmo handle the drag, eg, modify its transforms
 722   mGizmo->on3DMouseDragged( event );
 723   if ( mGizmo->isDirty() )
 724   {
 725      Point3F pos = mGizmo->getPosition();
 726      Point3F scale = mGizmo->getScale();      
 727      const MatrixF &mat = mGizmo->getTransform();
 728      VectorF normal;
 729      mat.getColumn( 2, &normal );
 730
 731      mSelRiver->setNode( pos, scale.x, scale.z, normal, mSelNode );
 732      mIsDirty = true;
 733   }
 734   Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) );   
 735   /*
 736   // If we are just starting a new drag,
 737   // we need to save the starting screen position of the mouse,
 738   // and the starting position of the selected node.
 739   if ( mStartDragMousePoint == InvalidMousePoint )
 740   {
 741      mStartDragMousePoint = event.mousePoint;
 742      mStartDragNodePos = mSelRiver->getNodePosition( mSelNode );
 743   }
 744
 745   MathUtils::Line clickLine;
 746   clickLine.p = event.pos;
 747   clickLine.d = event.vec;
 748
 749   MathUtils::Line axisLine;
 750   axisLine.p = mStartDragNodePos;
 751   axisLine.d = mGizmo.selectionToAxisVector( mGizmoSelection );
 752
 753   MathUtils::LineSegment segment;
 754
 755   MathUtils::mShortestSegmentBetweenLines( clickLine, axisLine, segment );
 756
 757   // Segment.p1 is the closest point on the axis line, 
 758   // We want to put the selected gizmo handle at that point,
 759   // So calculate the offset from the handle to the centerPoint to
 760   // determine the gizmo's position.
 761   mSelRiver->setNodePosition( mSelNode, segment.p1 );
 762   */
 763
 764   /*
 765   // Convert the delta (dragged mouse distance) from screen space
 766   // into world space.
 767   Point2I deltaScreen = event.mousePoint - mStartDragMousePoint;
 768
 769   F32 worldDist = ( event.pos - mStartDragNodePos ).len();      
 770   
 771   Point2F deltaWorld;
 772   deltaWorld.x = GFX->unprojectRadius( worldDist, deltaScreen.x );
 773   deltaWorld.y = GFX->unprojectRadius( worldDist, deltaScreen.y );
 774
 775   // Now modify the selected node depending on the kind of operation we are doing.
 776   if ( mGizmoSelection == Gizmo::Axis_X )
 777   {
 778      Point3F newPos = mStartDragNodePos;
 779      newPos.x += deltaWorld.x;      
 780      mSelRiver->setNodePosition( mSelNode, newPos );
 781   }
 782   else if ( mGizmoSelection == Gizmo::Axis_Y )
 783   {
 784      Point3F newPos = mStartDragNodePos;
 785      newPos.y += deltaWorld.x;      
 786      mSelRiver->setNodePosition( mSelNode, newPos );
 787   }
 788   else if ( mGizmoSelection == Gizmo::Axis_Z )
 789   {
 790      Point3F newPos = mStartDragNodePos;
 791      newPos.z += deltaWorld.y;      
 792      mSelRiver->setNodePosition( mSelNode, newPos );
 793   }
 794   */
 795
 796   /*
 797   F32 height = mStartHeight + deltaWorldX;    
 798   Con::printf( "height = %g", height );
 799
 800   mSelRiver->setNodeHeight( mSelNode, height );
 801
 802   Con::executef( this, "onNodeHeightModified", Con::getFloatArg(height) );
 803
 804
 805   if ( event.modifier & SI_PRIMARY_CTRL )
 806   {
 807      //Point3F tPos;
 808      //if ( !getStaticPos( event, tPos ) )
 809      //   return;  
 810
 811      if ( mStartHeight == -1.0f )
 812      {
 813         mStartHeight = mSelRiver->mNodes[mSelNode].point.z;
 814
 815         mStartX = event.mousePoint.x;
 816         mStartWorld = mSelRiver->mNodes[mSelNode].point;
 817      }
 818
 819      S32 deltaScreenX = event.mousePoint.x - mStartX;
 820
 821      F32 worldDist = ( event.pos - mStartWorld ).len();      
 822
 823      F32 deltaWorldX = GFX->unprojectRadius( worldDist, deltaScreenX );
 824
 825      F32 height = mStartHeight + deltaWorldX;    
 826      Con::printf( "height = %g", height );
 827
 828      mSelRiver->setNodeHeight( mSelNode, height );
 829
 830      Con::executef( this, "onNodeHeightModified", Con::getFloatArg(height) );
 831   }
 832   else if ( event.modifier & SI_SHIFT )
 833   {
 834      Point3F tPos;
 835      if ( !getStaticPos( event, tPos ) )
 836         return;   
 837
 838      if ( mStartWidth == -1.0f )
 839      {
 840         mStartWidth = mSelRiver->mNodes[mSelNode].width;
 841         
 842         mStartX = event.mousePoint.x;
 843         mStartWorld = tPos;
 844      }
 845
 846      S32 deltaScreenX = event.mousePoint.x - mStartX;
 847      
 848      F32 worldDist = ( event.pos - mStartWorld ).len();      
 849
 850      F32 deltaWorldX = GFX->unprojectRadius( worldDist, deltaScreenX );
 851
 852      F32 width = mStartWidth + deltaWorldX;      
 853
 854      mSelRiver->setNodeWidth( mSelNode, width );
 855
 856      Con::executef( this, "onNodeWidthModified", Con::getFloatArg(width) );
 857   }
 858   else
 859   {    
 860      Point3F tPos;
 861      if ( !getStaticPos( event, tPos ) )
 862         return; 
 863      else if ( mGizmoSelection == Gizmo::Axis_Y )
 864      {
 865         Point3F newPos = mStartDragNodePos;
 866         newPos.y += deltaWorld.x;      
 867         mSelRiver->setNodePosition( mSelNode, newPos );
 868      }
 869      mSelRiver->setNodePosition( mSelNode, tPos );
 870   }   
 871   */
 872}
 873
 874void GuiRiverEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
 875{
 876   // nothing to do
 877}
 878
 879void GuiRiverEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
 880{
 881   // nothing to do
 882}
 883
 884bool GuiRiverEditorCtrl::onKeyDown(const GuiEvent& event)
 885{
 886   if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode )
 887   {
 888      // Delete the node attached to the cursor.
 889      deleteSelectedNode();
 890      mMode = mAddRiverMode;
 891      return true;
 892   }
 893
 894   return false;
 895}
 896
 897void GuiRiverEditorCtrl::updateGuiInfo()
 898{
 899   // nothing to do
 900}
 901      
 902void GuiRiverEditorCtrl::onRender( Point2I offset, const RectI &updateRect )
 903{
 904   PROFILE_SCOPE( GuiRiverEditorCtrl_OnRender );
 905
 906   Parent::onRender( offset, updateRect );
 907   return;
 908}
 909      
 910void GuiRiverEditorCtrl::renderScene(const RectI & updateRect)
 911{
 912   //GFXDrawUtil *drawer = GFX->getDrawUtil();            
 913
 914   GFX->setStateBlock( mZDisableSB );
 915
 916   // get the projected size...
 917   GameConnection* connection = GameConnection::getConnectionToServer();
 918   if(!connection)
 919      return;
 920
 921   // Grab the camera's transform
 922   MatrixF mat;
 923   connection->getControlCameraTransform(0, &mat);
 924
 925   // Get the camera position
 926   Point3F camPos;
 927   mat.getColumn(3,&camPos);
 928
 929   if ( mHoverRiver && mHoverRiver != mSelRiver )
 930   {
 931      _drawRiverSpline( mHoverRiver, mHoverSplineColor );
 932   }
 933
 934   if ( mSelRiver )
 935   {
 936      _drawRiverSpline( mSelRiver, mSelectedSplineColor );            
 937
 938      // Render Gizmo for selected node if were in either of the three transform modes
 939      if ( mSelNode != -1 && ( mMode == mMovePointMode || mMode == mScalePointMode || mMode == mRotatePointMode ) )
 940      {
 941         if( mMode == mMovePointMode )
 942         {
 943            mGizmo->getProfile()->mode = MoveMode;
 944         }
 945         else if( mMode == mScalePointMode )
 946         {
 947            mGizmo->getProfile()->mode = ScaleMode;
 948         }
 949         else if( mMode == mRotatePointMode )
 950         {
 951            mGizmo->getProfile()->mode = RotateMode;
 952         }
 953
 954         const RiverNode &node = mSelRiver->mNodes[mSelNode];
 955
 956         MatrixF objMat = mSelRiver->getNodeTransform(mSelNode);      
 957         Point3F objScale( node.width, 1.0f, node.depth );
 958         Point3F worldPos = node.point;
 959
 960         mGizmo->set( objMat, worldPos, objScale );
 961
 962         mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov );
 963         
 964         // Render Gizmo text
 965         //mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection ); 
 966      }    
 967   }
 968
 969   // Now draw all the 2d stuff!
 970   GFX->setClipRect(updateRect); 
 971
 972   // Draw Control nodes for selecting and highlighted rivers
 973   if ( mHoverRiver )
 974      _drawRiverControlNodes( mHoverRiver, mHoverSplineColor );
 975   if ( mSelRiver )
 976      _drawRiverControlNodes( mSelRiver, mSelectedSplineColor );
 977} 
 978
 979void GuiRiverEditorCtrl::_drawRiverSpline( River *river, const ColorI &color )
 980{
 981   if ( river->mSlices.size() <= 1 )
 982      return;
 983
 984   if ( River::smShowSpline )
 985   {
 986      // Render the River center-line
 987      PrimBuild::color( color );
 988      PrimBuild::begin( GFXLineStrip, river->mSlices.size() );            
 989      for ( U32 i = 0; i < river->mSlices.size(); i++ )
 990      {                       
 991         PrimBuild::vertex3fv( river->mSlices[i].p1 );            
 992      }
 993      PrimBuild::end();
 994   }
 995   
 996   if ( River::smWireframe )
 997   {
 998      // Left-side line
 999      PrimBuild::color3i( 100, 100, 100 );
1000      PrimBuild::begin( GFXLineStrip, river->mSlices.size() );            
1001      for ( U32 i = 0; i < river->mSlices.size(); i++ )
1002      {                       
1003         PrimBuild::vertex3fv( river->mSlices[i].p0 );            
1004      }
1005      PrimBuild::end();
1006
1007      // Right-side line
1008      PrimBuild::begin( GFXLineStrip, river->mSlices.size() );            
1009      for ( U32 i = 0; i < river->mSlices.size(); i++ )
1010      {                       
1011         PrimBuild::vertex3fv( river->mSlices[i].p2 );            
1012      }
1013      PrimBuild::end();
1014
1015      // Cross-sections
1016      PrimBuild::begin( GFXLineList, river->mSlices.size() * 2 );            
1017      for ( U32 i = 0; i < river->mSlices.size(); i++ )
1018      {                       
1019         PrimBuild::vertex3fv( river->mSlices[i].p0 );
1020         PrimBuild::vertex3fv( river->mSlices[i].p2 );            
1021      }
1022      PrimBuild::end();
1023   }
1024   // Segment 
1025}
1026
1027void GuiRiverEditorCtrl::_drawRiverControlNodes( River *river, const ColorI &color )
1028{
1029   if ( !River::smShowSpline )
1030      return;
1031
1032   RectI bounds = getBounds();
1033
1034   GFXDrawUtil *drawer = GFX->getDrawUtil();
1035
1036   bool isSelected = ( river == mSelRiver );
1037   bool isHighlighted = ( river == mHoverRiver );
1038
1039   for ( U32 i = 0; i < river->mNodes.size(); i++ )
1040   {
1041      if ( false && isSelected && mSelNode == i  )
1042         continue;
1043
1044      const Point3F &wpos = river->mNodes[i].point;
1045
1046      Point3F spos;
1047      project( wpos, &spos );                  
1048
1049      if ( spos.z > 1.0f )
1050         continue;
1051
1052      Point2I posi;
1053      posi.x = spos.x;
1054      posi.y = spos.y;
1055
1056      if ( !bounds.pointInRect( posi ) )
1057         continue;
1058
1059      ColorI theColor = color;
1060      Point2I nodeHalfSize = mNodeHalfSize;
1061
1062      if ( isHighlighted && mHoverNode == i )
1063      {
1064         //theColor = mHoverNodeColor;
1065         nodeHalfSize += Point2I(2,2);
1066      }
1067
1068      if ( isSelected )
1069      {   
1070         if ( mSelNode == i )
1071         {
1072            theColor.set(0,0,255);
1073         }
1074         else if ( i == 0 )
1075         {
1076            theColor.set(0,255,0);
1077         }
1078         else if ( i == river->mNodes.size() - 1 )
1079         {
1080            theColor.set(255,0,0);
1081         }         
1082      }
1083
1084      drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
1085   }
1086}
1087
1088bool GuiRiverEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos )
1089{     
1090   // Find clicked point on the terrain
1091
1092   Point3F startPnt = event.pos;
1093   Point3F endPnt = event.pos + event.vec * 1000.0f;
1094
1095   RayInfo ri;
1096   bool hit;         
1097         
1098   hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri);    
1099   tpos = ri.point;
1100   
1101   return hit;
1102}
1103
1104void GuiRiverEditorCtrl::deleteSelectedNode()
1105{    
1106   if ( !mSelRiver || mSelNode == -1 )
1107      return;
1108   
1109   // If the River has only two nodes remaining,
1110   // delete the whole River.
1111   if ( mSelRiver->mNodes.size() <= 2 )
1112   {      
1113      deleteSelectedRiver( mMode != mAddNodeMode );
1114   }
1115   else
1116   {
1117      if ( mMode != mAddNodeMode )
1118         submitUndo( "Delete Node" );
1119
1120      // Delete the SelectedNode of the SelectedRiver
1121      mSelRiver->deleteNode(mSelNode);
1122      mIsDirty = true;
1123
1124      // We deleted the Node but not the River (it has nodes left)
1125      // so decrement the currently selected node.
1126      if ( mSelRiver->mNodes.size() <= mSelNode )
1127         setSelectedNode( mSelNode - 1 );
1128      else
1129      {
1130         // force gizmo to update to the selected nodes position
1131         // the index didn't change but the node it refers to did.
1132         U32 i = mSelNode;
1133         mSelNode = -1;
1134         setSelectedNode( i );
1135      }
1136   }
1137
1138   // If you were in addNodeMode, 
1139   // deleting a node should ends it.
1140   //mMode = smNormalMode;
1141}
1142
1143void GuiRiverEditorCtrl::deleteSelectedRiver( bool undoAble )
1144{
1145   AssertFatal( mSelRiver != NULL, "GuiRiverEditorCtrl::deleteSelectedRiver() - No River IS selected" );
1146
1147   // Not undoAble? Just delete it.
1148   if ( !undoAble )
1149   {
1150      mSelRiver->deleteObject();
1151      mIsDirty = true;
1152      Con::executef( this, "onRiverSelected" );
1153      mSelNode = -1;
1154
1155      return;
1156   }
1157
1158   // Grab the mission editor undo manager.
1159   UndoManager *undoMan = NULL;
1160   if ( !Sim::findObject( "EUndoManager", undoMan ) )
1161   {
1162      // Couldn't find it? Well just delete the River.
1163      Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown() - EUndoManager not found!" );    
1164      return;
1165   }
1166   else
1167   {
1168      // Create the UndoAction.
1169      MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted River");
1170      action->deleteObject( mSelRiver );
1171      mIsDirty = true;
1172
1173      // Submit it.               
1174      undoMan->addAction( action );
1175   }
1176
1177   // ScriptCallback with 'NULL' parameter for no River currently selected.
1178   Con::executef( this, "onRiverSelected" );
1179
1180   // Clear the SelectedNode (it has been deleted along with the River).  
1181   setSelectedNode( -1 );
1182   mSelNode = -1;
1183
1184   // SelectedRiver is a SimObjectPtr and will be NULL automatically.
1185}
1186
1187void GuiRiverEditorCtrl::setMode( String mode, bool sourceShortcut = false )
1188{
1189   mMode = mode;
1190
1191   if( sourceShortcut )
1192      Con::executef( this, "paletteSync", mode );
1193}
1194
1195void GuiRiverEditorCtrl::setSelectedRiver( River *river )
1196{
1197   mSelRiver = river;
1198
1199   if ( mSelRiver != NULL )
1200      Con::executef( this, "onRiverSelected", river->getIdString() );
1201   else
1202      Con::executef( this, "onRiverSelected" );
1203
1204   if ( mSelRiver != river )
1205      setSelectedNode(-1);
1206}
1207
1208void GuiRiverEditorCtrl::setNodeWidth( F32 width )
1209{
1210   if ( mSelRiver && mSelNode != -1 )
1211   {
1212      mSelRiver->setNodeWidth( mSelNode, width );
1213      mIsDirty = true;
1214   }
1215}
1216
1217F32 GuiRiverEditorCtrl::getNodeWidth()
1218{
1219   if ( mSelRiver && mSelNode != -1 )
1220      return mSelRiver->getNodeWidth( mSelNode );
1221
1222   return 0.0f;   
1223}
1224
1225void GuiRiverEditorCtrl::setNodeDepth(F32 depth)
1226{
1227   if ( mSelRiver && mSelNode != -1 )
1228   {
1229      mSelRiver->setNodeDepth( mSelNode, depth );
1230      mIsDirty = true;
1231   }
1232}
1233
1234F32 GuiRiverEditorCtrl::getNodeDepth()
1235{
1236   if ( mSelRiver && mSelNode != -1 )
1237      return mSelRiver->getNodeDepth( mSelNode );
1238
1239   return 0.0f;
1240}
1241
1242void GuiRiverEditorCtrl::setNodePosition(const Point3F& pos)
1243{
1244   if ( mSelRiver && mSelNode != -1 )
1245   {
1246      mSelRiver->setNodePosition( mSelNode, pos );
1247      mIsDirty = true;
1248   }
1249}
1250
1251Point3F GuiRiverEditorCtrl::getNodePosition()
1252{
1253   if ( mSelRiver && mSelNode != -1 )
1254      return mSelRiver->getNodePosition( mSelNode );
1255
1256   return Point3F( 0, 0, 0 );   
1257}
1258
1259void GuiRiverEditorCtrl::setNodeNormal( const VectorF &normal )
1260{
1261   if ( mSelRiver && mSelNode != -1 )
1262   {
1263      mSelRiver->setNodeNormal( mSelNode, normal );
1264      mIsDirty = true;
1265   }
1266}
1267
1268VectorF GuiRiverEditorCtrl::getNodeNormal()
1269{
1270   if ( mSelRiver && mSelNode != -1 )
1271      return mSelRiver->getNodeNormal( mSelNode );
1272
1273   return VectorF::Zero;
1274}
1275
1276void GuiRiverEditorCtrl::setSelectedNode( S32 node )
1277{
1278   //if ( mSelNode == node )
1279   //   return;
1280
1281   mSelNode = node;
1282   if ( mSelNode != -1 )
1283   {
1284      const RiverNode &curNode = mSelRiver->mNodes[mSelNode];
1285
1286      MatrixF objMat = mSelRiver->getNodeTransform(mSelNode);      
1287      Point3F objScale(curNode.width, 1.0f, curNode.depth );
1288      Point3F worldPos = curNode.point;
1289
1290      mGizmo->set( objMat, worldPos, objScale );
1291   }
1292   
1293   if ( mSelNode != -1 )
1294      Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode) );
1295   else
1296      Con::executef( this, "onNodeSelected", Con::getIntArg(-1) );
1297}
1298
1299void GuiRiverEditorCtrl::submitUndo( const UTF8 *name )
1300{
1301   // Grab the mission editor undo manager.
1302   UndoManager *undoMan = NULL;
1303   if ( !Sim::findObject( "EUndoManager", undoMan ) )
1304   {
1305      Con::errorf( "GuiRiverEditorCtrl::submitUndo() - EUndoManager not found!" );
1306      return;           
1307   }
1308
1309   // Setup the action.
1310   GuiRiverEditorUndoAction *action = new GuiRiverEditorUndoAction( name );
1311
1312   action->mObjId = mSelRiver->getId();
1313   action->mMetersPerSegment = mSelRiver->mMetersPerSegment;
1314   action->mSegmentsPerBatch = mSelRiver->mSegmentsPerBatch;   
1315   action->mRiverEditor = this;
1316
1317   for( U32 i = 0; i < mSelRiver->mNodes.size(); i++ )
1318   {
1319      action->mNodes.push_back( mSelRiver->mNodes[i] );      
1320   }
1321      
1322   undoMan->addAction( action );
1323}
1324
1325void GuiRiverEditorCtrl::_prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* state )
1326{
1327   if ( isAwake() && River::smEditorOpen && mSelRiver )
1328   {
1329      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
1330      ri->type = RenderPassManager::RIT_Editor;
1331      ri->renderDelegate.bind( this, &GuiRiverEditorCtrl::_renderSelectedRiver );
1332      ri->defaultKey = 100;
1333      state->getRenderPass()->addInst( ri );
1334   }
1335}
1336
1337void GuiRiverEditorCtrl::_renderSelectedRiver( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst )
1338{
1339   if ( !mSelRiver || !River::smEditorOpen)
1340      return;
1341
1342   GFXTransformSaver saver;
1343
1344   GFX->setStateBlock( mZEnableSB );
1345
1346   if ( River::smShowWalls && mSelRiver->mSlices.size() > 1 )
1347   {
1348      Point3F offset(0,0,1);
1349
1350      // Render the River volume      
1351      PrimBuild::begin( GFXTriangleList, 18 * mSelRiver->mSlices.size() - 1 );
1352
1353      for ( U32 i = 0; i < mSelRiver->mSlices.size() - 1; i++ )
1354      {
1355         const RiverSlice &slice = mSelRiver->mSlices[i];
1356         const RiverSlice &nextSlice = mSelRiver->mSlices[i+1];
1357
1358         // Top face
1359         //drawer->drawQuad( slice.p0, nextSlice.p0, nextSlice.p2, slice.p2, colorRed, true );
1360         //PrimBuild::color3i( 0, 0, 255 );
1361         //PrimBuild::vertex3fv( slice.p0 );
1362         //PrimBuild::vertex3fv( nextSlice.p0 );
1363         //PrimBuild::vertex3fv( nextSlice.p2 );
1364         //PrimBuild::vertex3fv( slice.p0 );
1365         //PrimBuild::vertex3fv( nextSlice.p2 );
1366         //PrimBuild::vertex3fv( slice.p2 );         
1367
1368         // Bottom face
1369         PrimBuild::color3i( 0, 255, 0 );
1370         PrimBuild::vertex3fv( slice.pb0 );
1371         PrimBuild::vertex3fv( nextSlice.pb0 );
1372         PrimBuild::vertex3fv( nextSlice.pb2 );
1373         PrimBuild::vertex3fv( slice.pb0 );
1374         PrimBuild::vertex3fv( nextSlice.pb2 );         
1375         PrimBuild::vertex3fv( slice.pb2 );         
1376
1377         // Left face
1378         PrimBuild::color3i( 255, 0, 0 );
1379         PrimBuild::vertex3fv( slice.pb0 );
1380         PrimBuild::vertex3fv( nextSlice.pb0 );         
1381         PrimBuild::vertex3fv( nextSlice.p0 );
1382         PrimBuild::vertex3fv( slice.pb0 );
1383         PrimBuild::vertex3fv( nextSlice.p0 );
1384         PrimBuild::vertex3fv( slice.p0 );         
1385
1386         // Right face
1387         PrimBuild::color3i( 255, 0, 0 );
1388         PrimBuild::vertex3fv( slice.p2 );         
1389         PrimBuild::vertex3fv( nextSlice.p2 );
1390         PrimBuild::vertex3fv( nextSlice.pb2 );
1391         PrimBuild::vertex3fv( slice.p2 );    
1392         PrimBuild::vertex3fv( nextSlice.pb2 );
1393         PrimBuild::vertex3fv( slice.pb2 );         
1394      }
1395
1396      PrimBuild::end();
1397   }
1398}
1399
1400DefineEngineMethod( GuiRiverEditorCtrl, deleteNode, void, (), , "deleteNode()" )
1401{
1402   object->deleteSelectedNode();
1403}
1404
1405DefineEngineMethod( GuiRiverEditorCtrl, getMode, const char*, (), , "" )
1406{
1407   return object->getMode();
1408}
1409
1410DefineEngineMethod( GuiRiverEditorCtrl, setMode, void, ( const char * mode ), , "setMode( String mode )" )
1411{
1412   String newMode = ( mode );
1413   object->setMode( newMode );
1414}
1415
1416DefineEngineMethod( GuiRiverEditorCtrl, getNodeWidth, F32, (), , "" )
1417{
1418   return object->getNodeWidth();
1419}
1420
1421DefineEngineMethod( GuiRiverEditorCtrl, setNodeWidth, void, ( F32 width ), , "" )
1422{
1423   object->setNodeWidth( width );
1424}
1425
1426DefineEngineMethod( GuiRiverEditorCtrl, getNodeDepth, F32, (), , "" )
1427{
1428   return object->getNodeDepth();
1429}
1430
1431DefineEngineMethod( GuiRiverEditorCtrl, setNodeDepth, void, ( F32 depth ), , "" )
1432{
1433   object->setNodeDepth( depth );
1434}
1435
1436DefineEngineMethod( GuiRiverEditorCtrl, getNodePosition, Point3F, (), , "" )
1437{
1438
1439   return  object->getNodePosition();
1440}
1441
1442DefineEngineMethod( GuiRiverEditorCtrl, setNodePosition, void, (Point3F pos), , "" )
1443{
1444   object->setNodePosition( pos );
1445}
1446
1447DefineEngineMethod( GuiRiverEditorCtrl, getNodeNormal, Point3F, (), , "" )
1448{
1449
1450   return object->getNodeNormal();
1451}
1452
1453DefineEngineMethod( GuiRiverEditorCtrl, setNodeNormal, void, (Point3F normal), , "" )
1454{
1455
1456   object->setNodeNormal( normal );
1457}
1458
1459DefineEngineMethod( GuiRiverEditorCtrl, setSelectedRiver, void, (const char * objName), (""), "" )
1460{
1461   if (String::compare( objName,"" )==0)
1462      object->setSelectedRiver(NULL);
1463   else
1464   {
1465      River *river = NULL;
1466      if ( Sim::findObject( objName, river ) )
1467         object->setSelectedRiver(river);
1468   }
1469}
1470
1471DefineEngineMethod( GuiRiverEditorCtrl, getSelectedRiver, S32, (), , "" )
1472{
1473   River *river = object->getSelectedRiver();
1474   if ( !river )
1475      return NULL;
1476
1477   return river->getId();
1478}
1479
1480DefineEngineMethod( GuiRiverEditorCtrl, regenerate, void, (), , "" )
1481{
1482   River *river = object->getSelectedRiver();
1483   if ( river )
1484      river->regenerate();
1485}
1486