2012-09-19 11:15:01 -04:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "environment/editors/guiRoadEditorCtrl.h"
# include "console/consoleTypes.h"
2014-11-03 22:42:51 -05:00
# include "console/engineAPI.h"
2012-09-19 11:15:01 -04:00
# include "scene/sceneManager.h"
# include "collision/collision.h"
# include "math/util/frustum.h"
# include "gfx/gfxPrimitiveBuffer.h"
# include "gfx/gfxTextureHandle.h"
# include "gfx/gfxTransformSaver.h"
# include "gfx/gfxDrawUtil.h"
# include "gfx/primBuilder.h"
# include "T3D/gameBase/gameConnection.h"
# include "gui/core/guiCanvas.h"
# include "gui/buttons/guiButtonCtrl.h"
# include "gui/worldEditor/undoActions.h"
# include "materials/materialDefinition.h"
2019-02-23 15:55:28 -06:00
# include "T3D/Scene.h"
2012-09-19 11:15:01 -04:00
IMPLEMENT_CONOBJECT ( GuiRoadEditorCtrl ) ;
ConsoleDocClass ( GuiRoadEditorCtrl ,
" @brief GUI tool that makes up the Decal Road Editor \n \n "
" Editor use only. \n \n "
" @internal "
) ;
GuiRoadEditorCtrl : : GuiRoadEditorCtrl ( )
{
// Each of the mode names directly correlates with the River Editor's
// tool palette
mSelectRoadMode = " RoadEditorSelectMode " ;
mAddRoadMode = " RoadEditorAddRoadMode " ;
mMovePointMode = " RoadEditorMoveMode " ;
mScalePointMode = " RoadEditorScaleMode " ;
mAddNodeMode = " RoadEditorAddNodeMode " ;
mInsertPointMode = " RoadEditorInsertPointMode " ;
mRemovePointMode = " RoadEditorRemovePointMode " ;
mMode = mSelectRoadMode ;
mRoadSet = NULL ;
mSelNode = - 1 ;
mHoverNode = - 1 ;
mSelRoad = NULL ;
mHoverRoad = NULL ;
mAddNodeIdx = 0 ;
mDefaultWidth = 10.0f ;
mInsertIdx = - 1 ;
mStartWidth = - 1.0f ;
mStartX = 0 ;
mNodeHalfSize . set ( 4 , 4 ) ;
mHoverSplineColor . set ( 255 , 0 , 0 , 255 ) ;
mSelectedSplineColor . set ( 0 , 255 , 0 , 255 ) ;
mHoverNodeColor . set ( 255 , 255 , 255 , 255 ) ;
2020-05-11 14:56:04 -05:00
mSavedDrag = false ;
2012-09-19 11:15:01 -04:00
mIsDirty = false ;
2026-07-03 23:38:43 -05:00
mMaterialAssetRef = Con : : getVariable ( " $DecalRoadEditor::defaultMaterialAsset " ) ;
2012-09-19 11:15:01 -04:00
}
GuiRoadEditorCtrl : : ~ GuiRoadEditorCtrl ( )
{
// nothing to do
}
void GuiRoadEditorUndoAction : : undo ( )
{
DecalRoad * road = NULL ;
if ( ! Sim : : findObject ( mObjId , road ) )
return ;
// Temporarily save the roads current data.
2026-07-03 23:38:43 -05:00
StringTableEntry materialAssetId = road - > getMaterialAssetId ( ) ;
2012-09-19 11:15:01 -04:00
F32 textureLength = road - > mTextureLength ;
F32 breakAngle = road - > mBreakAngle ;
F32 segmentsPerBatch = road - > mSegmentsPerBatch ;
Vector < RoadNode > nodes ;
nodes . merge ( road - > mNodes ) ;
// Restore the Road properties saved in the UndoAction
2026-07-03 23:38:43 -05:00
road - > mMaterialAssetRef = mMaterialAssetId ;
2012-09-19 11:15:01 -04:00
road - > mBreakAngle = breakAngle ;
road - > mSegmentsPerBatch = segmentsPerBatch ;
road - > mTextureLength = textureLength ;
road - > inspectPostApply ( ) ;
// Restore the Nodes saved in the UndoAction
road - > mNodes . clear ( ) ;
for ( U32 i = 0 ; i < mNodes . size ( ) ; i + + )
{
road - > _addNode ( mNodes [ i ] . point , mNodes [ i ] . width ) ;
}
// Regenerate the road
road - > regenerate ( ) ;
// If applicable set the selected road and node
mRoadEditor - > mSelRoad = road ;
mRoadEditor - > mSelNode = - 1 ;
// Now save the previous Road data in this UndoAction
// since an undo action must become a redo action and vice-versa
2021-01-03 08:58:53 -06:00
mMaterialAssetId = materialAssetId ;
2012-09-19 11:15:01 -04:00
mBreakAngle = breakAngle ;
mSegmentsPerBatch = segmentsPerBatch ;
mTextureLength = textureLength ;
mNodes . clear ( ) ;
mNodes . merge ( nodes ) ;
}
bool GuiRoadEditorCtrl : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
mRoadSet = DecalRoad : : getServerSet ( ) ;
GFXStateBlockDesc desc ;
desc . setCullMode ( GFXCullNone ) ;
desc . setBlend ( false ) ;
desc . setZReadWrite ( false , false ) ;
mZDisableSB = GFX - > createStateBlock ( desc ) ;
return true ;
}
void GuiRoadEditorCtrl : : initPersistFields ( )
{
2023-01-27 01:13:15 -06:00
docsURL ;
2025-03-09 11:53:23 -05:00
addFieldV ( " DefaultWidth " , TypeRangedF32 , Offset ( mDefaultWidth , GuiRoadEditorCtrl ) , & CommonValidators : : PositiveFloat ) ;
2012-09-19 11:15:01 -04:00
addField ( " HoverSplineColor " , TypeColorI , Offset ( mHoverSplineColor , GuiRoadEditorCtrl ) ) ;
addField ( " SelectedSplineColor " , TypeColorI , Offset ( mSelectedSplineColor , GuiRoadEditorCtrl ) ) ;
addField ( " HoverNodeColor " , TypeColorI , Offset ( mHoverNodeColor , GuiRoadEditorCtrl ) ) ;
addField ( " isDirty " , TypeBool , Offset ( mIsDirty , GuiRoadEditorCtrl ) ) ;
2021-01-03 08:58:53 -06:00
2026-07-03 23:38:43 -05:00
ADD_FIELD ( " materialAsset " , TypeMaterialAssetRef , Offset ( mMaterialAssetRef , GuiRoadEditorCtrl ) ) . doc ( " Default material asset used by the Road Editor on road creation. " ) ;
2021-01-03 08:58:53 -06:00
2012-09-19 11:15:01 -04:00
//addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRoadEditorCtrl) );
//addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRoadEditorCtrl) );
//addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRoadEditorCtrl) );
//addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRoadEditorCtrl) );
Parent : : initPersistFields ( ) ;
}
void GuiRoadEditorCtrl : : onSleep ( )
{
Parent : : onSleep ( ) ;
mMode = mSelectRoadMode ;
mHoverNode = - 1 ;
mHoverRoad = NULL ;
setSelectedNode ( - 1 ) ;
}
void GuiRoadEditorCtrl : : get3DCursor ( GuiCursor * & cursor ,
bool & visible ,
const Gui3DMouseEvent & event_ )
{
//cursor = mAddNodeCursor;
//visible = false;
cursor = NULL ;
visible = false ;
GuiCanvas * root = getRoot ( ) ;
if ( ! root )
return ;
S32 currCursor = PlatformCursorController : : curArrow ;
if ( root - > mCursorChanged = = currCursor )
return ;
PlatformWindow * window = root - > getPlatformWindow ( ) ;
PlatformCursorController * controller = window - > getCursorController ( ) ;
// We've already changed the cursor,
// so set it back before we change it again.
if ( root - > mCursorChanged ! = - 1 )
controller - > popCursor ( ) ;
// Now change the cursor shape
controller - > pushCursor ( currCursor ) ;
root - > mCursorChanged = currCursor ;
}
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 ;
2026-07-03 23:38:43 -05:00
if ( mMaterialAssetRef . notNull ( ) )
newRoad - > mMaterialAssetRef = mMaterialAssetRef . getAssetId ( ) ;
2012-09-19 11:15:01 -04:00
newRoad - > registerObject ( ) ;
2019-02-23 15:55:28 -06:00
// Add to scene
Scene * scene = Scene : : getRootScene ( ) ;
if ( ! scene )
Con : : errorf ( " GuiDecalRoadEditorCtrl - could not find scene to add new DecalRoad " ) ;
2012-09-19 11:15:01 -04:00
else
2019-02-23 15:55:28 -06:00
scene - > addObject ( newRoad ) ;
2012-09-19 11:15:01 -04:00
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 GuiRoadEditorCtrl : : on3DRightMouseDown ( const Gui3DMouseEvent & event )
{
//mIsPanning = true;
}
void GuiRoadEditorCtrl : : on3DRightMouseUp ( const Gui3DMouseEvent & event )
{
//mIsPanning = false;
}
void GuiRoadEditorCtrl : : on3DMouseUp ( const Gui3DMouseEvent & event )
{
mStartWidth = - 1.0f ;
mSavedDrag = false ;
mouseUnlock ( ) ;
}
void GuiRoadEditorCtrl : : on3DMouseMove ( const Gui3DMouseEvent & event )
{
if ( mSelRoad ! = NULL & & mMode = = mAddNodeMode )
{
Point3F startPnt = event . pos ;
Point3F endPnt = event . pos + event . vec * 1000.0f ;
RayInfo ri ;
if ( gServerContainer . castRay ( startPnt , endPnt , TerrainObjectType , & ri ) )
{
mSelRoad - > setNodePosition ( mSelNode , ri . point ) ;
mIsDirty = true ;
}
return ;
}
// Is cursor hovering over a road?
if ( mMode = = mSelectRoadMode )
{
mHoverRoad = NULL ;
Point3F startPnt = event . pos ;
Point3F endPnt = event . pos + event . vec * 1000.0f ;
RayInfo ri ;
if ( gServerContainer . castRay ( startPnt , endPnt , TerrainObjectType , & ri ) )
{
DecalRoad * pRoad = NULL ;
for ( SimSetIterator iter ( mRoadSet ) ; * iter ; + + iter )
{
pRoad = static_cast < DecalRoad * > ( * iter ) ;
if ( pRoad - > containsPoint ( ri . point ) )
{
mHoverRoad = pRoad ;
break ;
}
}
}
}
// Is cursor hovering over a RoadNode?
if ( mHoverRoad )
{
DecalRoad * pRoad = mHoverRoad ;
S32 hoverNodeIdx = - 1 ;
F32 hoverNodeDist = F32_MAX ;
for ( U32 i = 0 ; i < pRoad - > mNodes . size ( ) ; i + + )
{
const Point3F & nodePos = pRoad - > mNodes [ i ] . point ;
Point3F screenPos ;
project ( nodePos , & screenPos ) ;
RectI rect ( Point2I ( ( S32 ) screenPos . x , ( S32 ) screenPos . y ) - mNodeHalfSize , mNodeHalfSize * 2 ) ;
if ( rect . pointInRect ( event . mousePoint ) & & screenPos . z < hoverNodeDist )
{
hoverNodeDist = screenPos . z ;
hoverNodeIdx = i ;
}
}
mHoverNode = hoverNodeIdx ;
}
}
void GuiRoadEditorCtrl : : on3DMouseDragged ( const Gui3DMouseEvent & event )
{
// Drags are only used to transform nodes
if ( ! mSelRoad | | mSelNode = = - 1 | |
( mMode ! = mMovePointMode & & mMode ! = mScalePointMode ) )
return ;
if ( ! mSavedDrag )
{
submitUndo ( " Modify Node " ) ;
mSavedDrag = true ;
}
if ( mMode = = mScalePointMode )
{
Point3F tPos ;
if ( ! getTerrainPos ( event , tPos ) )
return ;
if ( mStartWidth = = - 1.0f )
{
mStartWidth = mSelRoad - > mNodes [ mSelNode ] . width ;
mStartX = event . mousePoint . x ;
mStartWorld = tPos ;
}
S32 deltaScreenX = event . mousePoint . x - mStartX ;
F32 worldDist = ( event . pos - mStartWorld ) . len ( ) ;
F32 deltaWorldX = ( deltaScreenX * worldDist ) / getWorldToScreenScale ( ) . y ;
F32 width = mStartWidth + deltaWorldX ;
mSelRoad - > setNodeWidth ( mSelNode , width ) ;
mIsDirty = true ;
}
else if ( mMode = = mMovePointMode )
{
Point3F tPos ;
if ( ! getTerrainPos ( event , tPos ) )
return ;
mSelRoad - > setNodePosition ( mSelNode , tPos ) ;
mIsDirty = true ;
}
Con : : executef ( this , " onNodeModified " , Con : : getIntArg ( mSelNode ) ) ;
}
void GuiRoadEditorCtrl : : on3DMouseEnter ( const Gui3DMouseEvent & event )
{
// nothing to do
}
void GuiRoadEditorCtrl : : on3DMouseLeave ( const Gui3DMouseEvent & event )
{
// nothing to do
}
bool GuiRoadEditorCtrl : : onKeyDown ( const GuiEvent & event )
{
if ( event . keyCode = = KEY_RETURN & & mMode = = mAddNodeMode )
{
// Delete the node attached to the cursor.
deleteSelectedNode ( ) ;
mMode = mAddRoadMode ;
return true ;
}
return false ;
}
void GuiRoadEditorCtrl : : updateGuiInfo ( )
{
// nothing to do
}
void GuiRoadEditorCtrl : : onRender ( Point2I offset , const RectI & updateRect )
{
PROFILE_SCOPE ( GuiRoadEditorCtrl_OnRender ) ;
Parent : : onRender ( offset , updateRect ) ;
return ;
}
void GuiRoadEditorCtrl : : renderScene ( const RectI & updateRect )
{
GFX - > setStateBlock ( mZDisableSB ) ;
// Draw the spline based from the client-side road
// because the serverside spline is not actually reliable...
// Can be incorrect if the DecalRoad is before the TerrainBlock
2019-02-23 15:55:28 -06:00
// in the scene.
2012-09-19 11:15:01 -04:00
if ( mHoverRoad & & mHoverRoad ! = mSelRoad )
{
DecalRoad * pRoad = ( DecalRoad * ) mHoverRoad - > getClientObject ( ) ;
if ( pRoad )
_drawRoadSpline ( pRoad , mHoverSplineColor ) ;
}
if ( mSelRoad )
{
DecalRoad * pRoad = ( DecalRoad * ) mSelRoad - > getClientObject ( ) ;
if ( pRoad )
_drawRoadSpline ( pRoad , mSelectedSplineColor ) ;
}
}
void GuiRoadEditorCtrl : : renderGui ( Point2I offset , const RectI & updateRect )
{
// Draw Control nodes for selected and highlighted roads
if ( mHoverRoad )
_drawRoadControlNodes ( mHoverRoad , mHoverSplineColor ) ;
if ( mSelRoad )
_drawRoadControlNodes ( mSelRoad , mSelectedSplineColor ) ;
Parent : : renderGui ( offset , updateRect ) ;
}
void GuiRoadEditorCtrl : : _drawRoadSpline ( DecalRoad * road , const ColorI & color )
{
if ( road - > mEdges . size ( ) < = 1 )
return ;
GFXTransformSaver saver ;
if ( DecalRoad : : smShowSpline )
{
// Render the center-line
PrimBuild : : color ( color ) ;
PrimBuild : : begin ( GFXLineStrip , road - > mEdges . size ( ) ) ;
for ( U32 i = 0 ; i < road - > mEdges . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( road - > mEdges [ i ] . p1 ) ;
}
PrimBuild : : end ( ) ;
}
if ( DecalRoad : : smWireframe )
{
// Left-side line
PrimBuild : : color3i ( 100 , 100 , 100 ) ;
PrimBuild : : begin ( GFXLineStrip , road - > mEdges . size ( ) ) ;
for ( U32 i = 0 ; i < road - > mEdges . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( road - > mEdges [ i ] . p0 ) ;
}
PrimBuild : : end ( ) ;
// Right-side line
PrimBuild : : begin ( GFXLineStrip , road - > mEdges . size ( ) ) ;
for ( U32 i = 0 ; i < road - > mEdges . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( road - > mEdges [ i ] . p2 ) ;
}
PrimBuild : : end ( ) ;
// Cross-sections
PrimBuild : : begin ( GFXLineList , road - > mEdges . size ( ) * 2 ) ;
for ( U32 i = 0 ; i < road - > mEdges . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( road - > mEdges [ i ] . p0 ) ;
PrimBuild : : vertex3fv ( road - > mEdges [ i ] . p2 ) ;
}
PrimBuild : : end ( ) ;
}
}
void GuiRoadEditorCtrl : : _drawRoadControlNodes ( DecalRoad * road , const ColorI & color )
{
if ( ! DecalRoad : : smShowSpline )
return ;
RectI bounds = getBounds ( ) ;
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
bool isSelected = ( road = = mSelRoad ) ;
bool isHighlighted = ( road = = mHoverRoad ) ;
for ( U32 i = 0 ; i < road - > mNodes . size ( ) ; i + + )
{
if ( false & & isSelected & & mSelNode = = i )
continue ;
const Point3F & wpos = road - > mNodes [ i ] . point ;
Point3F spos ;
project ( wpos , & spos ) ;
if ( spos . z > 1.0f )
continue ;
Point2I posi ;
posi . x = spos . x ;
posi . y = spos . y ;
if ( ! bounds . pointInRect ( posi ) )
continue ;
ColorI theColor = color ;
Point2I nodeHalfSize = mNodeHalfSize ;
if ( isHighlighted & & mHoverNode = = i )
nodeHalfSize + = Point2I ( 2 , 2 ) ;
if ( isSelected )
{
if ( mSelNode = = i )
{
theColor . set ( 0 , 0 , 255 ) ;
}
else if ( i = = 0 )
{
theColor . set ( 0 , 255 , 0 ) ;
}
else if ( i = = road - > mNodes . size ( ) - 1 )
{
theColor . set ( 255 , 0 , 0 ) ;
}
}
drawer - > drawRectFill ( posi - nodeHalfSize , posi + nodeHalfSize , theColor ) ;
}
}
bool GuiRoadEditorCtrl : : getTerrainPos ( const Gui3DMouseEvent & event , Point3F & tpos )
{
// Find clicked point on the terrain
Point3F startPnt = event . pos ;
Point3F endPnt = event . pos + event . vec * 10000.0f ;
RayInfo ri ;
bool hit ;
hit = gServerContainer . castRay ( startPnt , endPnt , TerrainObjectType , & ri ) ;
tpos = ri . point ;
return hit ;
}
void GuiRoadEditorCtrl : : deleteSelectedNode ( )
{
if ( ! mSelRoad | | mSelNode = = - 1 )
return ;
// If the road has only two nodes remaining,
// delete the whole road.
if ( mSelRoad - > mNodes . size ( ) < = 2 )
{
deleteSelectedRoad ( ) ;
}
else
{
// Only submit undo if we weren't in AddMode
if ( mMode ! = mAddNodeMode )
submitUndo ( " Delete Node " ) ;
// Delete the SelectedNode of the SelectedRoad
mSelRoad - > deleteNode ( mSelNode ) ;
mIsDirty = true ;
// We deleted the Node but not the Road (it has nodes left)
// so decrement the currently selected node.
if ( mSelRoad - > mNodes . size ( ) < = mSelNode )
mSelNode - - ;
}
}
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 GuiRoadEditorCtrl : : setMode ( String mode , bool sourceShortcut = false )
{
mMode = mode ;
if ( sourceShortcut )
2012-10-11 21:29:39 +01:00
Con : : executef ( this , " paletteSync " , mode ) ;
2012-09-19 11:15:01 -04:00
}
void GuiRoadEditorCtrl : : setSelectedRoad ( DecalRoad * road )
{
mSelRoad = road ;
if ( road ! = NULL )
Con : : executef ( this , " onRoadSelected " , road - > getIdString ( ) ) ;
else
Con : : executef ( this , " onRoadSelected " ) ;
if ( mSelRoad ! = road )
setSelectedNode ( - 1 ) ;
}
void GuiRoadEditorCtrl : : setNodeWidth ( F32 width )
{
if ( mSelRoad & & mSelNode ! = - 1 )
{
mSelRoad - > setNodeWidth ( mSelNode , width ) ;
mIsDirty = true ;
}
}
F32 GuiRoadEditorCtrl : : getNodeWidth ( )
{
if ( mSelRoad & & mSelNode ! = - 1 )
return mSelRoad - > getNodeWidth ( mSelNode ) ;
return 0.0f ;
}
2015-07-16 22:02:18 -05:00
void GuiRoadEditorCtrl : : setNodePosition ( const Point3F & pos )
2012-09-19 11:15:01 -04:00
{
if ( mSelRoad & & mSelNode ! = - 1 )
{
mSelRoad - > setNodePosition ( mSelNode , pos ) ;
mIsDirty = true ;
}
}
Point3F GuiRoadEditorCtrl : : getNodePosition ( )
{
if ( mSelRoad & & mSelNode ! = - 1 )
return mSelRoad - > getNodePosition ( mSelNode ) ;
return Point3F ( 0 , 0 , 0 ) ;
}
void GuiRoadEditorCtrl : : setSelectedNode ( S32 node )
{
//if ( mSelNode == node )
// return;
mSelNode = node ;
if ( mSelNode ! = - 1 & & mSelRoad ! = NULL )
Con : : executef ( this , " onNodeSelected " , Con : : getIntArg ( mSelNode ) , Con : : getFloatArg ( mSelRoad - > mNodes [ mSelNode ] . width ) ) ;
else
Con : : executef ( this , " onNodeSelected " , Con : : getIntArg ( - 1 ) ) ;
}
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 ;
2026-07-03 23:38:43 -05:00
action - > mMaterialAssetId = mSelRoad - > getMaterialAssetId ( ) ;
2012-09-19 11:15:01 -04:00
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 ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , deleteNode , void , ( ) , , " deleteNode() " )
2012-09-19 11:15:01 -04:00
{
object - > deleteSelectedNode ( ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , getMode , const char * , ( ) , , " " )
2012-09-19 11:15:01 -04:00
{
return object - > getMode ( ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , setMode , void , ( const char * mode ) , , " setMode( String mode ) " )
2012-09-19 11:15:01 -04:00
{
2014-11-03 22:42:51 -05:00
String newMode = ( mode ) ;
2012-09-19 11:15:01 -04:00
object - > setMode ( newMode ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , getNodeWidth , F32 , ( ) , , " " )
2012-09-19 11:15:01 -04:00
{
return object - > getNodeWidth ( ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , setNodeWidth , void , ( F32 width ) , , " " )
2012-09-19 11:15:01 -04:00
{
2014-11-03 22:42:51 -05:00
object - > setNodeWidth ( width ) ;
2012-09-19 11:15:01 -04:00
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , getNodePosition , Point3F , ( ) , , " " )
2012-09-19 11:15:01 -04:00
{
2014-11-03 22:42:51 -05:00
return object - > getNodePosition ( ) ;
2012-09-19 11:15:01 -04:00
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , setNodePosition , void , ( Point3F pos ) , , " " )
2012-09-19 11:15:01 -04:00
{
object - > setNodePosition ( pos ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , setSelectedRoad , void , ( const char * pathRoad ) , ( " " ) , " " )
2012-09-19 11:15:01 -04:00
{
2020-10-03 14:37:55 +02:00
if ( String : : compare ( pathRoad , " " ) = = 0 )
2012-09-19 11:15:01 -04:00
object - > setSelectedRoad ( NULL ) ;
else
{
DecalRoad * road = NULL ;
2014-11-03 22:42:51 -05:00
if ( Sim : : findObject ( pathRoad , road ) )
2012-09-19 11:15:01 -04:00
object - > setSelectedRoad ( road ) ;
}
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , getSelectedRoad , S32 , ( ) , , " " )
2012-09-19 11:15:01 -04:00
{
DecalRoad * road = object - > getSelectedRoad ( ) ;
if ( road )
2014-11-03 22:42:51 -05:00
return road - > getId ( ) ;
2012-09-19 11:15:01 -04:00
2021-10-04 20:04:21 -04:00
return 0 ;
2012-09-19 11:15:01 -04:00
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , getSelectedNode , S32 , ( ) , , " " )
2012-09-19 11:15:01 -04:00
{
return object - > getSelectedNode ( ) ;
}
2018-04-17 21:01:50 +02:00
DefineEngineMethod ( GuiRoadEditorCtrl , deleteRoad , void , ( ) , , " " )
2012-09-19 11:15:01 -04:00
{
object - > deleteSelectedRoad ( ) ;
}