void GuiDecalEditorCtrl::deleteSelectedDecal()
{
   if ( !mSELDecal )
      return;
	
	// Grab the mission editor undo manager.
	UndoManager *undoMan = NULL;
	if ( !Sim::findObject( "EUndoManager", undoMan ) )
	{
		Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
		return;           
	}

	// Create the UndoAction.
	DIDeleteUndoAction *action = new DIDeleteUndoAction("Delete Decal");
	action->deleteDecal( *mSELDecal );
	
	action->mEditor = this;
	// Submit it.               
	undoMan->addAction( action );
	
	if ( isMethod( "onDeleteInstance" ) )
	{
		char buffer[512];
		dSprintf(buffer, 512, "%i", mSELDecal->mId);
		Con::executef( this, "onDeleteInstance", String(buffer).c_str(), mSELDecal->mDataBlock->lookupName.c_str() );
	}

   gDecalManager->removeDecal( mSELDecal );
   mSELDecal = NULL;
}
示例#2
0
void GuiRoadEditorCtrl::submitUndo( const UTF8 *name )
{
   // Grab the mission editor undo manager.
   UndoManager *undoMan = NULL;
   if ( !Sim::findObject( "EUndoManager", undoMan ) )
   {
      Con::errorf( "GuiRoadEditorCtrl::submitUndo() - EUndoManager not found!" );
      return;           
   }

   // Setup the action.
   GuiRoadEditorUndoAction *action = new GuiRoadEditorUndoAction( name );

   action->mObjId = mSelRoad->getId();
   action->mBreakAngle = mSelRoad->mBreakAngle;
   action->mMaterialName = mSelRoad->mMaterialName;
   action->mSegmentsPerBatch = mSelRoad->mSegmentsPerBatch;   
   action->mTextureLength = mSelRoad->mTextureLength;
   action->mRoadEditor = this;

   for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ )
   {
      action->mNodes.push_back( mSelRoad->mNodes[i] );      
   }
      
   undoMan->addAction( action );
}
void GuiDecalEditorCtrl::retargetDecalDatablock( String dbFrom, String dbTo )
{
	DecalData * ptrFrom = dynamic_cast<DecalData*> ( Sim::findObject(dbFrom.c_str()) );
	DecalData * ptrTo = dynamic_cast<DecalData*> ( Sim::findObject(dbTo.c_str()) );
	
	if( !ptrFrom || !ptrTo )
		return;
	
	// Grab the mission editor undo manager.
	UndoManager *undoMan = NULL;
	if ( !Sim::findObject( "EUndoManager", undoMan ) )
	{
		Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
		return;           
	}

	// Create the UndoAction.
	DBRetargetUndoAction *action = new DBRetargetUndoAction("Retarget Decal Datablock");
	action->mEditor = this;
	action->mDBFromId = ptrFrom->getId();
	action->mDBToId = ptrTo->getId();

	Vector<DecalInstance*> mDecalQueue;
	Vector<DecalInstance *>::iterator iter;
	mDecalQueue.clear();
	const Vector<DecalSphere*> &grid = gDecalManager->getDecalDataFile()->getSphereList();
	for ( U32 i = 0; i < grid.size(); i++ )
	{
		const DecalSphere *decalSphere = grid[i];
		mDecalQueue.merge( decalSphere->mItems );
	}

	for ( iter = mDecalQueue.begin();iter != mDecalQueue.end();iter++ )
	{	
		if( !(*iter) )
			continue;

		if( (*iter)->mDataBlock->lookupName.compare( dbFrom ) == 0 )
		{
			if( (*iter)->mId != -1 )
			{
				action->retargetDecal((*iter));	
				(*iter)->mDataBlock = ptrTo;
				forceRedraw((*iter));
			}
		}
	}

	undoMan->addAction( action );
}
示例#4
0
void ForestTool::_submitUndo( UndoAction *action )
{
   AssertFatal( action, "ForestTool::_submitUndo() - No undo action!" );

   // Grab the mission editor undo manager.
   UndoManager *undoMan = NULL;
   if ( !Sim::findObject( "EUndoManager", undoMan ) )
   {
      Con::errorf( "ForestTool::_submitUndo() - EUndoManager not found!" );
      return;     
   }

   undoMan->addAction( action );

   mEditor->updateCollision();
}
void GuiRiverEditorCtrl::deleteSelectedRiver( bool undoAble )
{
   AssertFatal( mSelRiver != NULL, "GuiRiverEditorCtrl::deleteSelectedRiver() - No River IS selected" );

   // Not undoAble? Just delete it.
   if ( !undoAble )
   {
      mSelRiver->deleteObject();
      mIsDirty = true;
      Con::executef( this, "onRiverSelected" );
      mSelNode = -1;

      return;
   }

   // Grab the mission editor undo manager.
   UndoManager *undoMan = NULL;
   if ( !Sim::findObject( "EUndoManager", undoMan ) )
   {
      // Couldn't find it? Well just delete the River.
      Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown() - EUndoManager not found!" );    
      return;
   }
   else
   {
      // Create the UndoAction.
      MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted River");
      action->deleteObject( mSelRiver );
      mIsDirty = true;

      // Submit it.               
      undoMan->addAction( action );
   }

   // ScriptCallback with 'NULL' parameter for no River currently selected.
   Con::executef( this, "onRiverSelected" );

   // Clear the SelectedNode (it has been deleted along with the River).  
	setSelectedNode( -1 );
   mSelNode = -1;

   // SelectedRiver is a SimObjectPtr and will be NULL automatically.
}
void ForestEditorCtrl::deleteMeshSafe( ForestItemData *mesh )
{
   UndoManager *undoMan = NULL;
   if ( !Sim::findObject( "EUndoManager", undoMan ) )
   {
      Con::errorf( "ForestEditorCtrl::deleteMeshSafe() - EUndoManager not found." );
      return;     
   }

   // CompoundUndoAction which will delete the ForestItemData, ForestItem(s), and ForestBrushElement(s).
   CompoundUndoAction *compoundAction = new CompoundUndoAction( "Delete Forest Mesh" );
    
   // Find ForestItem(s) referencing this datablock and add their deletion
   // to the undo action.
   if ( mForest )
   {      
      Vector<ForestItem> foundItems;
      mForest->getData()->getItems( mesh, &foundItems );

      ForestDeleteUndoAction *itemAction = new ForestDeleteUndoAction( mForest->getData(), this );
      itemAction->removeItem( foundItems );
      compoundAction->addAction( itemAction );
   }

   // Find ForestBrushElement(s) referencing this datablock.
   SimGroup *brushGroup = ForestBrush::getGroup();
   sKey = mesh;
   Vector<SimObject*> foundElements;   
   brushGroup->findObjectByCallback( &findMeshReferences, foundElements );   

   // Add UndoAction to delete the ForestBrushElement(s) and the ForestItemData.
   MEDeleteUndoAction *elementAction = new MEDeleteUndoAction();
   elementAction->deleteObject( foundElements );
   elementAction->deleteObject( mesh );
   
   // Add compound action to the UndoManager. Done.
   undoMan->addAction( compoundAction );

   updateCollision();
}
示例#7
0
void GuiRoadEditorCtrl::deleteSelectedRoad( bool undoAble )
{
   AssertFatal( mSelRoad != NULL, "GuiRoadEditorCtrl::deleteSelectedRoad() - No road IS selected" );

   // Not undo-able? Just delete it.
   if ( !undoAble )
   {
      DecalRoad *lastRoad = mSelRoad;

      setSelectedRoad(NULL);

      lastRoad->deleteObject();
      mIsDirty = true;

      return;
   }

   // Grab the mission editor undo manager.
   UndoManager *undoMan = NULL;
   if ( !Sim::findObject( "EUndoManager", undoMan ) )
   {
      // Couldn't find it? Well just delete the road.
      Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );    
      return;
   }
   else
   {
      DecalRoad *lastRoad = mSelRoad;
      setSelectedRoad(NULL);

      // Create the UndoAction.
      MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road");
      action->deleteObject( lastRoad );
      mIsDirty = true;

      // Submit it.               
      undoMan->addAction( action );
   }
}
void GuiDecalEditorCtrl::deleteDecalDatablock( String lookupName )
{
	DecalData * datablock = dynamic_cast<DecalData*> ( Sim::findObject(lookupName.c_str()) );
	if( !datablock )
		return;

	// Grab the mission editor undo manager.
	UndoManager *undoMan = NULL;
	if ( !Sim::findObject( "EUndoManager", undoMan ) )
	{
		Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
		return;           
	}

	// Create the UndoAction.
	DBDeleteUndoAction *action = new DBDeleteUndoAction("Delete Decal Datablock");
	action->mEditor = this;
	action->mDatablockId = datablock->getId();
	
	Vector<DecalInstance*> mDecalQueue;
	Vector<DecalInstance *>::iterator iter;
	mDecalQueue.clear();
	const Vector<DecalSphere*> &grid = gDecalManager->getDecalDataFile()->getSphereList();

	for ( U32 i = 0; i < grid.size(); i++ )
	{
		const DecalSphere *decalSphere = grid[i];
		mDecalQueue.merge( decalSphere->mItems );
	}

	for ( iter = mDecalQueue.begin();iter != mDecalQueue.end();iter++ )
	{	
		if( !(*iter) )
			continue;

		if( (*iter)->mDataBlock->lookupName.compare( lookupName ) == 0 )
		{
			if( (*iter)->mId != -1 )
			{
				//make sure to call onDeleteInstance as well
				if ( isMethod( "onDeleteInstance" ) )
				{
					char buffer[512];
					dSprintf(buffer, 512, "%i", (*iter)->mId);
					Con::executef( this, "onDeleteInstance", String(buffer).c_str(), (*iter)->mDataBlock->lookupName.c_str() );
				}
				
				action->deleteDecal( *(*iter) );
				
				if( mSELDecal == (*iter) )
					mSELDecal = NULL;

				if( mHLDecal == (*iter) )
					mHLDecal = NULL;
			}
			gDecalManager->removeDecal( (*iter) );
		}
	}
	
	undoMan->addAction( action );

	mCurrentDecalData = NULL;
}
void GuiDecalEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
{ 
   if ( !mSELDecal )
      return;

   // Doing a drag copy of the decal?
   if ( event.modifier & SI_SHIFT && !mPerformedDragCopy )
   {
      mPerformedDragCopy = true;

		DecalInstance *newDecal = gDecalManager->addDecal(    mSELDecal->mPosition, 
                                                            mSELDecal->mNormal, 
                                                            0.0f, 
                                                            mSELDecal->mDataBlock, 
                                                            1.0f, 
                                                            -1, 
                                                            PermanentDecal | SaveDecal );

      newDecal->mTangent = mSELDecal->mTangent;
      newDecal->mSize = mSELDecal->mSize;
      newDecal->mTextureRectIdx = mSELDecal->mTextureRectIdx;

      // TODO: This is crazy... we should move this sort of tracking
      // inside of the decal manager... IdDecal flag maybe or just a
      // byproduct of PermanentDecal?
      //
		newDecal->mId = gDecalManager->mDecalInstanceVec.size();
		gDecalManager->mDecalInstanceVec.push_back( newDecal );

		selectDecal( newDecal );
			
		// Grab the mission editor undo manager.
		UndoManager *undoMan = NULL;
		if ( Sim::findObject( "EUndoManager", undoMan ) )
		{
			// Create the UndoAction.
			DICreateUndoAction *action = new DICreateUndoAction("Create Decal");
			action->addDecal( *mSELDecal );
			action->mEditor = this;
			undoMan->addAction( action );

			if ( isMethod( "onCreateInstance" ) )
			{
				char buffer[512];
				dSprintf( buffer, 512, "%i", mSELDecal->mId );
				Con::executef( this, "onCreateInstance", buffer, mSELDecal->mDataBlock->lookupName.c_str());
			}
		}
   }

   // Update the Gizmo.
   if (mGizmo->getSelection() != Gizmo::None)
   {
      mGizmo->on3DMouseDragged( event );

      // Pull out the Gizmo transform
      // and position.
      const MatrixF &gizmoMat = mGizmo->getTransform();
      const Point3F &gizmoPos = gizmoMat.getPosition();
      
      // Get the new projection vector.
      VectorF upVec, rightVec;
      gizmoMat.getColumn( 0, &rightVec );
      gizmoMat.getColumn( 2, &upVec );

      const Point3F &scale = mGizmo->getScale();

      // Assign the appropriate changed value back to the decal.
      if ( mGizmo->getMode() == ScaleMode )
      {
         // Save old size.
         const F32 oldSize = mSELDecal->mSize;

         // Set new size.
         mSELDecal->mSize = ( scale.x + scale.y ) * 0.5f;

         // See if the decal properly clips/projects at this size.  If not,
         // stick to the old size.
         mSELEdgeVerts.clear();
         if ( !gDecalManager->clipDecal( mSELDecal, &mSELEdgeVerts ) )
            mSELDecal->mSize = oldSize;
      }
      else if ( mGizmo->getMode() == MoveMode )
         mSELDecal->mPosition = gizmoPos;
      else if ( mGizmo->getMode() == RotateMode )
      {
         mSELDecal->mNormal = upVec;
         mSELDecal->mTangent = rightVec;
      }

      gDecalManager->notifyDecalModified( mSELDecal );

	   Con::executef( this, "syncNodeDetails" );
   }
}
void GuiDecalEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
{
   mPerformedDragCopy = false;

   if ( !isFirstResponder() )
      setFirstResponder();
	
	bool dblClick = ( event.mouseClickCount > 1 );

	// Gather selected decal information 
   RayInfo ri;
   bool hit = getRayInfo( event, &ri );

   Point3F start = event.pos;
   Point3F end = start + event.vec * 3000.0f; // use visible distance here??

   DecalInstance *pDecal = gDecalManager->raycast( start, end );
	
	if( mMode.compare("AddDecalMode") != 0 )
	{
		if ( mSELDecal )
		{
			// If our click hit the gizmo we are done.			
			if ( mGizmo->getSelection() != Gizmo::None )
			{
            mGizmo->on3DMouseDown( event );

				char returnBuffer[256];
				dSprintf(returnBuffer, sizeof(returnBuffer), "%f %f %f %f %f %f %f", 
				mSELDecal->mPosition.x, mSELDecal->mPosition.y, mSELDecal->mPosition.z, 
				mSELDecal->mTangent.x, mSELDecal->mTangent.y, mSELDecal->mTangent.z,
				mSELDecal->mSize);

				Con::executef( this, "prepGizmoTransform", Con::getIntArg(mSELDecal->mId), returnBuffer );

				return;
			}
		}

		if ( mHLDecal && pDecal == mHLDecal )
		{
			mHLDecal = NULL;            
			selectDecal( pDecal );   

			if ( isMethod( "onSelectInstance" ) )
			{
				char idBuf[512];
				dSprintf(idBuf, 512, "%i", pDecal->mId);
				Con::executef( this, "onSelectInstance", String(idBuf).c_str(), pDecal->mDataBlock->lookupName.c_str() );
			}

			return;
		}
		else if ( hit && !pDecal)
		{
			if ( dblClick )
				setMode( String("AddDecalMode"), true );

			return;
		}
	}
	else
	{
		// If we accidently hit a decal, then bail(probably an accident). If the use hits the decal twice,
		// then boot them into selection mode and select the decal.
		if ( mHLDecal && pDecal == mHLDecal )
		{
			if ( dblClick )
			{
				mHLDecal = NULL;            
				selectDecal( pDecal );   

				if ( isMethod( "onSelectInstance" ) )
				{
					char idBuf[512];
					dSprintf(idBuf, 512, "%i", pDecal->mId);
					Con::executef( this, "onSelectInstance", String(idBuf).c_str(), pDecal->mDataBlock->lookupName.c_str() );
				}
				setMode( String("SelectDecalMode"), true );
			}
			return;	
		}

		if ( hit && mCurrentDecalData ) // Create a new decal...
		{
			U8 flags = PermanentDecal | SaveDecal;

			DecalInstance *decalInst = gDecalManager->addDecal( ri.point, ri.normal, 0.0f, mCurrentDecalData, 1.0f, -1, flags );      
	      
			if ( decalInst )  
			{
				// Give the decal an id
				decalInst->mId = gDecalManager->mDecalInstanceVec.size();
				gDecalManager->mDecalInstanceVec.push_back(decalInst);

				selectDecal( decalInst );
				
				// Grab the mission editor undo manager.
				UndoManager *undoMan = NULL;
				if ( !Sim::findObject( "EUndoManager", undoMan ) )
				{
					Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
					return;           
				}

				// Create the UndoAction.
				DICreateUndoAction *action = new DICreateUndoAction("Create Decal");
				action->addDecal( *decalInst );
				
				action->mEditor = this;
				// Submit it.               
				undoMan->addAction( action );

				if ( isMethod( "onCreateInstance" ) )
				{
					char buffer[512];
					dSprintf(buffer, 512, "%i", decalInst->mId);
					Con::executef( this, "onCreateInstance", buffer, decalInst->mDataBlock->lookupName.c_str());
				}
			}

			return;
		}
	}

   if ( !mSELDecal )
      return;
}
示例#11
0
void GuiRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
{
   if ( !isFirstResponder() )
      setFirstResponder();

   // Get the clicked terrain position.
   Point3F tPos;
   if ( !getTerrainPos( event, tPos ) )
      return;      

   mouseLock();

   // Find any road / node at the clicked position.
   // TODO: handle overlapping roads/nodes somehow, cycle through them.
   
   DecalRoad *roadPtr = NULL;
   S32 closestNodeIdx = -1;
   F32 closestDist = F32_MAX;
   DecalRoad *closestNodeRoad = NULL;

   // First, find the closest node in any road to the clicked position.
   for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
   {
      roadPtr = static_cast<DecalRoad*>( *iter );
      U32 idx;
      if ( roadPtr->getClosestNode( tPos, idx ) )
      {
         Point3F nodePos = roadPtr->getNodePosition(idx);
         F32 dist = ( nodePos - tPos ).len();
         if ( dist < closestDist )
         {
            closestNodeIdx = idx;
            closestDist = dist;
            closestNodeRoad = roadPtr;
         }
      }
   }

   //
   // Second, determine if the screen-space node rectangle
   // contains the clicked position.

   bool nodeClicked = false;
   S32 clickedNodeIdx = -1;

   if ( closestNodeIdx != -1 )
   {
      Point3F nodePos = closestNodeRoad->getNodePosition( closestNodeIdx );

      Point3F temp;
      project( nodePos, &temp );
      Point2I screenPos( temp.x, temp.y );

      RectI nodeRect( screenPos - mNodeHalfSize, mNodeHalfSize * 2 );
      
      nodeClicked = nodeRect.pointInRect( event.mousePoint );
      if ( nodeClicked )
         clickedNodeIdx = closestNodeIdx;
   }

   //
   // Determine the clickedRoad
   //
   DecalRoad *clickedRoadPtr = NULL;
   U32 insertNodeIdx = 0;

   if ( nodeClicked && (mSelRoad == NULL || closestNodeRoad == mSelRoad) )
   {
      // If a node was clicked, the owning road is always
      // considered the clicked road.
      clickedRoadPtr = closestNodeRoad;
   }
   else
   {
      // check the selected road first
      if ( mSelRoad != NULL && mSelRoad->containsPoint( tPos, &insertNodeIdx ) )
      {
         clickedRoadPtr = mSelRoad;
         nodeClicked = false;
         clickedNodeIdx = -1;
      }
      else
      {
         // Otherwise, we must ask each road if it contains
         // the clicked pos.
         for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
         {
            roadPtr = static_cast<DecalRoad*>( *iter );
            if ( roadPtr->containsPoint( tPos, &insertNodeIdx ) )
            {
               clickedRoadPtr = roadPtr;
               break;            
            }
         }
      }
   }

	// shortcuts
   bool dblClick = ( event.mouseClickCount > 1 );
	if( dblClick )
   { 
		if( mMode == mSelectRoadMode )
		{
			setMode( mAddRoadMode, true );
			return;
		}
		if( mMode == mAddNodeMode )
		{
			// Delete the node attached to the cursor.
			deleteSelectedNode();
			mMode = mAddRoadMode;
			return;
		}
	}

	//this check is here in order to bounce back from deleting a whole road with ctrl+z
	//this check places the editor back into addroadmode
	if ( mMode == mAddNodeMode )
	{
      if ( !mSelRoad )
         mMode = mAddRoadMode;
	}

	if ( mMode == mSelectRoadMode )
	{
      // Did not click on a road or a node.
      if ( !clickedRoadPtr  )
      {
         setSelectedRoad( NULL );
         setSelectedNode( -1 );
         
         return;
      }

      // Clicked on a road that wasn't the currently selected road.
      if ( clickedRoadPtr != mSelRoad )
      {
         setSelectedRoad( clickedRoadPtr );
         setSelectedNode( -1 );
         return;
      }

      // Clicked on a node in the currently selected road that wasn't
      // the currently selected node.
      if ( nodeClicked )
      {
         setSelectedNode( clickedNodeIdx );
         return;
      }

      
      // Clicked a position on the currently selected road
      // that did not contain a node.
      //U32 newNode = clickedRoadPtr->insertNode( tPos, mDefaultWidth, insertNodeIdx );                  
      //setSelectedNode( newNode );
	}
   else if ( mMode == mAddRoadMode )
   {
		if ( nodeClicked && clickedRoadPtr )
      {
			// A double-click on a node in Normal mode means set AddNode mode.  
         if ( clickedNodeIdx == 0 )
         {
				setSelectedRoad( clickedRoadPtr );
				setSelectedNode( clickedNodeIdx );

				mAddNodeIdx = clickedNodeIdx;
            mMode = mAddNodeMode;
            mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx );
            mIsDirty = true;

				return;
         }
			else if ( clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 )
         {
				setSelectedRoad( clickedRoadPtr );
				setSelectedNode( clickedNodeIdx );

            mAddNodeIdx = U32_MAX;
            mMode = mAddNodeMode;
            mSelNode = mSelRoad->addNode( tPos, mDefaultWidth );
            mIsDirty = true;
				setSelectedNode( mSelNode );

				return;
         } 
		}

		DecalRoad *newRoad = new DecalRoad;
		

		newRoad->mMaterialName = mMaterialName;

      newRoad->registerObject();

      // Add to MissionGroup                              
      SimGroup *missionGroup;
      if ( !Sim::findObject( "MissionGroup", missionGroup ) )               
         Con::errorf( "GuiDecalRoadEditorCtrl - could not find MissionGroup to add new DecalRoad" );
      else
         missionGroup->addObject( newRoad );               

      newRoad->insertNode( tPos, mDefaultWidth, 0 );
      U32 newNode = newRoad->insertNode( tPos, mDefaultWidth, 1 );

      // Always add to the end of the road, the first node is the start.
      mAddNodeIdx = U32_MAX;
      
      setSelectedRoad( newRoad );      
      setSelectedNode( newNode );

      mMode = mAddNodeMode;

      // Disable the hover node while in addNodeMode, we
      // don't want some random node enlarged.
      mHoverNode = -1;

      // Grab the mission editor undo manager.
      UndoManager *undoMan = NULL;
      if ( !Sim::findObject( "EUndoManager", undoMan ) )
      {
         Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
         return;           
      }

      // Create the UndoAction.
      MECreateUndoAction *action = new MECreateUndoAction("Create Road");
      action->addObject( newRoad );
      
      // Submit it.               
      undoMan->addAction( action );
		
		//send a callback to script after were done here if one exists
		if ( isMethod( "onRoadCreation" ) )
         Con::executef( this, "onRoadCreation" );

		return;
   }
	else if ( mMode == mAddNodeMode )
	{
		// Oops the road got deleted, maybe from an undo action?
      // Back to NormalMode.
      if ( mSelRoad )
      {
			// A double-click on a node in Normal mode means set AddNode mode.  
         if ( clickedNodeIdx == 0 )
         {
				submitUndo( "Add Node" );
				mAddNodeIdx = clickedNodeIdx;
            mMode = mAddNodeMode;
            mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx );
            mIsDirty = true;
				setSelectedNode( mSelNode );

				return;
         }
			else
         {
				if( clickedRoadPtr && clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 )
				{
					submitUndo( "Add Node" );
					mAddNodeIdx = U32_MAX;
					mMode = mAddNodeMode;
					mSelNode = mSelRoad->addNode( tPos, mDefaultWidth );
               mIsDirty = true;
					setSelectedNode( mSelNode );

					return;
				}
				else
				{
					submitUndo( "Insert Node" );
					// A single-click on empty space while in
					// AddNode mode means insert / add a node.
					//submitUndo( "Add Node" );
					//F32 width = mSelRoad->getNodeWidth( mSelNode );
					U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx);
               mIsDirty = true;
					setSelectedNode( newNode );

					return;
				}
         } 
      }
	}
	else if ( mMode == mInsertPointMode  && mSelRoad != NULL)
	{
		if ( clickedRoadPtr == mSelRoad )
      {
			F32 w0 = mSelRoad->getNodeWidth( insertNodeIdx );
         F32 w1 = mSelRoad->getNodeWidth( insertNodeIdx + 1 );               
         F32 width = ( w0 + w1 ) * 0.5f;

         submitUndo( "Insert Node" );
         U32 newNode = mSelRoad->insertNode( tPos, width, insertNodeIdx + 1);  
         mIsDirty = true;
         setSelectedNode( newNode );

			return;
       }
	}
	else if ( mMode == mRemovePointMode  && mSelRoad != NULL)
	{
		if ( nodeClicked && clickedRoadPtr == mSelRoad )
      {
			setSelectedNode( clickedNodeIdx );
         deleteSelectedNode();
         return;
      }
	}
	else if ( mMode == mMovePointMode )
	{
		if ( nodeClicked && clickedRoadPtr == mSelRoad )
      {
			setSelectedNode( clickedNodeIdx );
         return;
      }
	}
	else if ( mMode == mScalePointMode )
	{
		if ( nodeClicked && clickedRoadPtr == mSelRoad )
      {
			setSelectedNode( clickedNodeIdx );
         return;
      }
	}
}
void GuiRiverEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
{
   mGizmo->on3DMouseDown( event );

   if ( !isFirstResponder() )
      setFirstResponder();
	
	// Get the raycast collision position
   Point3F tPos;
   if ( !getStaticPos( event, tPos ) )
		return;  

   // Construct a LineSegment from the camera position to 1000 meters away in
   // the direction clicked.
   // If that segment hits the terrain, truncate the ray to only be that length.

   // We will use a LineSegment/Sphere intersection test to determine if a RiverNode
   // was clicked.   

   Point3F startPnt = event.pos;
   Point3F endPnt = event.pos + event.vec * 1000.0f;

   RayInfo ri;   

   if ( gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri) )
      endPnt = ri.point;

   River *riverPtr = NULL;
   River *clickedRiverPtr = NULL;

   // Did we click on a river? check current selection first
   U32 insertNodeIdx = -1;
   Point3F collisionPnt;
   if ( mSelRiver != NULL && mSelRiver->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
   {
      clickedRiverPtr = mSelRiver;
   }
   else
   {
      for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
      {
         riverPtr = static_cast<River*>( *iter );
         if ( riverPtr->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
         {
            clickedRiverPtr = riverPtr;
            break;
         }
      }
   }

   // Did we click on a riverNode?
   bool nodeClicked = false;   
   S32 clickedNodeIdx = -1;
   F32 clickedNodeDist = mNodeSphereRadius;

   // If we clicked on the currently selected river, only scan its nodes
   if ( mSelRiver != NULL && clickedRiverPtr == mSelRiver )
   {
      for ( U32 i = 0; i < mSelRiver->mNodes.size(); i++ )
      {
         const Point3F &nodePos = mSelRiver->mNodes[i].point;

         Point3F screenPos;
         project( nodePos, &screenPos );

         F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
         if ( dist < clickedNodeDist )
         {
            clickedNodeDist = dist;
            clickedNodeIdx = i;
            nodeClicked = true;
         }
      }
   }
   else
   {
      for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
      {
         riverPtr = static_cast<River*>( *iter );
         
         for ( U32 i = 0; i < riverPtr->mNodes.size(); i++ )
         {
            const Point3F &nodePos = riverPtr->mNodes[i].point;

            Point3F screenPos;
            project( nodePos, &screenPos );

            F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
            if ( dist < clickedNodeDist )
            {
               // we found a hit!
               clickedNodeDist = dist;
               clickedNodeIdx = i;
               nodeClicked = true;
               clickedRiverPtr = riverPtr;
            }
         }
      }
   }
	
	// shortcuts
	bool dblClick = ( event.mouseClickCount > 1 );
	if( dblClick )
   { 
		if( mMode == mSelectRiverMode )
		{
			setMode( mAddRiverMode, true );
			return;
		}
		if( mMode == mAddNodeMode )
		{
			// Delete the node attached to the cursor.
			deleteSelectedNode();
			mMode = mAddRiverMode;
			return;
		}
	}

	//this check is here in order to bounce back from deleting a whole road with ctrl+z
	//this check places the editor back into addrivermode
	if ( mMode == mAddNodeMode )
	{
      if ( !mSelRiver )
         mMode = mAddRiverMode;
	}

	if ( mMode == mSelectRiverMode )
	{
      // Did not click on a River or a node.
      if ( !clickedRiverPtr  )
      {
         setSelectedRiver( NULL );
         setSelectedNode( -1 );
         
         return;
      }

      // Clicked on a River that wasn't the currently selected River.
      if ( clickedRiverPtr != mSelRiver )
      {
         setSelectedRiver( clickedRiverPtr );
         setSelectedNode( clickedNodeIdx );
         return;
      }

     // Clicked on a node in the currently selected River that wasn't
      // the currently selected node.
      if ( nodeClicked )
      {
         setSelectedNode( clickedNodeIdx );
         return;
      }
	}
   else if ( mMode == mAddRiverMode )
   {
		if ( nodeClicked )
      {
			// A double-click on a node in Normal mode means set AddNode mode.  
         if ( clickedNodeIdx == 0 )
         {
				setSelectedRiver( clickedRiverPtr );
				setSelectedNode( clickedNodeIdx );

				mAddNodeIdx = clickedNodeIdx;
            mMode = mAddNodeMode; 

            mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
            mIsDirty = true;

				return;
         }
			else if ( clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 )
         {
				setSelectedRiver( clickedRiverPtr );
				setSelectedNode( clickedNodeIdx );

            mAddNodeIdx = U32_MAX;
				mMode = mAddNodeMode;

            mSelNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
            mIsDirty = true;
				setSelectedNode( mSelNode );

				return;
         } 
		}

		if ( !isMethod( "createRiver" ) )
      {
			Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method does not exist." );
         return;
      }

      const char *res = Con::executef( this, "createRiver" );

      River *newRiver;
      if ( !Sim::findObject( res, newRiver ) )
      {
         Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method did not return a river object." );
         return;
      }                

      // Add to MissionGroup                              
      SimGroup *missionGroup;
      if ( !Sim::findObject( "MissionGroup", missionGroup ) )               
         Con::errorf( "GuiRiverEditorCtrl - could not find MissionGroup to add new River" );
      else
         missionGroup->addObject( newRiver );

      Point3F pos( endPnt );
      pos.z += mDefaultDepth * 0.5f;

      newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 );
      U32 newNode = newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 );

      // Always add to the end of the road, the first node is the start.
      mAddNodeIdx = U32_MAX;
      
      setSelectedRiver( newRiver );      
      setSelectedNode( newNode );

      mMode = mAddNodeMode;

      // Disable the hover node while in addNodeMode, we
      // don't want some random node enlarged.
      mHoverNode = -1;

      // Grab the mission editor undo manager.
      UndoManager *undoMan = NULL;
      if ( !Sim::findObject( "EUndoManager", undoMan ) )
      {
         Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
         return;           
      }

      // Create the UndoAction.
      MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad");
      action->addObject( newRiver );

      // Submit it.               
      undoMan->addAction( action );

		return;
   }
	else if ( mMode == mAddNodeMode )
	{
		// Oops the road got deleted, maybe from an undo action?
      // Back to NormalMode.
      if ( mSelRiver )
      {
			// A double-click on a node in Normal mode means set AddNode mode.  
         if ( clickedNodeIdx == 0 )
         {
				submitUndo( "Add Node" );
				mAddNodeIdx = clickedNodeIdx;
            mMode = mAddNodeMode;
            mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
            mIsDirty = true;
				setSelectedNode( mSelNode );

				return;
         }
			else
         {
				if( clickedRiverPtr && clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 )
				{
					submitUndo( "Add Node" );
					mAddNodeIdx = U32_MAX;
					mMode = mAddNodeMode;
					U32 newNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);  
               mIsDirty = true;
					setSelectedNode( newNode );

					return;
				}
				else
				{
					submitUndo( "Insert Node" );
					// A single-click on empty space while in
					// AddNode mode means insert / add a node.
					//submitUndo( "Add Node" );
					//F32 width = mSelRiver->getNodeWidth( mSelNode );
					U32 newNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx);
               mIsDirty = true;
					setSelectedNode( newNode );

					return;
				}
			}
		}
	}
	else if ( mMode == mInsertPointMode && mSelRiver != NULL )
	{
		if ( clickedRiverPtr == mSelRiver )
      {
			// NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node...
         // in order to handle inserting nodes in the same way as for DecalRoad

         U32 prevNodeIdx = insertNodeIdx;
         U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRiver->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1;

         const RiverNode &prevNode = mSelRiver->mNodes[prevNodeIdx];
         const RiverNode &nextNode = mSelRiver->mNodes[nextNodeIdx];

         F32 width = ( prevNode.width + nextNode.width ) * 0.5f;
         F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f;
         Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f;
         normal.normalize();

         submitUndo( "Insert Node" );
         U32 newNode = mSelRiver->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 );
         mIsDirty = true;
         setSelectedNode( newNode );

			return;
       }
	}
	else if ( mMode == mRemovePointMode && mSelRiver != NULL )
	{
		if ( nodeClicked && clickedRiverPtr == mSelRiver )
      {
			setSelectedNode( clickedNodeIdx );
         deleteSelectedNode();
         return;
      }
	}
	else if ( mMode == mMovePointMode )
	{
		if ( nodeClicked && clickedRiverPtr == mSelRiver )
      {
			setSelectedNode( clickedNodeIdx );
         return;
      }
	}
	else if ( mMode == mScalePointMode )
	{
		if ( nodeClicked && clickedRiverPtr == mSelRiver )
      {
			setSelectedNode( clickedNodeIdx );
         return;
      }
	}
	else if ( mMode == mRotatePointMode )
	{
		if ( nodeClicked && clickedRiverPtr == mSelRiver )
      {
			setSelectedNode( clickedNodeIdx );
         return;
      }
	}
}