2012-09-19 15:15:01 +00: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/guiMeshRoadEditorCtrl.h"
# include "console/consoleTypes.h"
2014-11-04 03:42:51 +00:00
# include "console/engineAPI.h"
2012-09-19 15:15:01 +00:00
# include "environment/meshRoad.h"
# include "renderInstance/renderPassManager.h"
# include "collision/collision.h"
# include "math/util/frustum.h"
# include "math/mathUtils.h"
# include "gfx/gfxPrimitiveBuffer.h"
# include "gfx/gfxTextureHandle.h"
# include "gfx/gfxTransformSaver.h"
# include "gfx/primBuilder.h"
# include "gfx/gfxDrawUtil.h"
# include "scene/sceneRenderState.h"
# include "scene/sceneManager.h"
# include "gui/core/guiCanvas.h"
# include "gui/buttons/guiButtonCtrl.h"
# include "gui/worldEditor/undoActions.h"
# include "T3D/gameBase/gameConnection.h"
# include "gfx/sim/debugDraw.h"
# include "materials/materialDefinition.h"
# include "T3D/prefab.h"
2019-02-23 21:55:28 +00:00
# include "T3D/Scene.h"
2012-09-19 15:15:01 +00:00
IMPLEMENT_CONOBJECT ( GuiMeshRoadEditorCtrl ) ;
ConsoleDocClass ( GuiMeshRoadEditorCtrl ,
" @brief GUI tool that makes up the Mesh Road Editor \n \n "
" Editor use only. \n \n "
" @internal "
) ;
2020-10-03 22:19:36 +00:00
S32 _NodeIndexCmp ( U32 const * a , U32 const * b )
{
S32 a2 = ( * a ) ;
S32 b2 = ( * b ) ;
S32 diff = a2 - b2 ;
return diff < 0 ? 1 : diff > 0 ? - 1 : 0 ;
}
2012-09-19 15:15:01 +00:00
GuiMeshRoadEditorCtrl : : GuiMeshRoadEditorCtrl ( )
:
// Each of the mode names directly correlates with the Mesh Road Editor's
// tool palette
mSelectMeshRoadMode ( " MeshRoadEditorSelectMode " ) ,
mAddMeshRoadMode ( " MeshRoadEditorAddRoadMode " ) ,
mAddNodeMode ( " MeshRoadEditorAddNodeMode " ) ,
mInsertPointMode ( " MeshRoadEditorInsertPointMode " ) ,
mRemovePointMode ( " MeshRoadEditorRemovePointMode " ) ,
2016-10-14 23:16:55 +00:00
mMovePointMode ( " MeshRoadEditorMoveMode " ) ,
mScalePointMode ( " MeshRoadEditorScaleMode " ) ,
mRotatePointMode ( " MeshRoadEditorRotateMode " ) ,
2020-05-11 19:56:04 +00:00
mSavedDrag ( false ) ,
2016-10-14 23:16:55 +00:00
mIsDirty ( false ) ,
2020-10-03 22:19:36 +00:00
mSavedProfileDrag ( false ) ,
mDeselectProfileNode ( false ) ,
mProfileNode ( - 1 ) ,
mProfileColor ( 255 , 255 , 0 ) ,
2016-10-14 23:16:55 +00:00
mRoadSet ( NULL ) ,
mSelNode ( - 1 ) ,
mHoverNode ( - 1 ) ,
mAddNodeIdx ( 0 ) ,
mSelRoad ( NULL ) ,
mHoverRoad ( NULL ) ,
mMode ( mSelectMeshRoadMode ) ,
mDefaultWidth ( 10.0f ) ,
mDefaultDepth ( 5.0f ) ,
mDefaultNormal ( 0 , 0 , 1 ) ,
mNodeHalfSize ( 4 , 4 ) ,
mHoverSplineColor ( 255 , 0 , 0 , 255 ) ,
mSelectedSplineColor ( 0 , 255 , 0 , 255 ) ,
mHoverNodeColor ( 255 , 255 , 255 , 255 ) ,
2020-05-11 19:56:04 +00:00
mHasCopied ( false )
2021-01-03 14:58:53 +00:00
{
mTopMaterialAssetId = Con : : getVariable ( " $MeshRoadEditor::defaultTopMaterialAsset " ) ;
mBottomMaterialAssetId = Con : : getVariable ( " $MeshRoadEditor::defaultBottomMaterialAsset " ) ;
mSideMaterialAssetId = Con : : getVariable ( " $MeshRoadEditor::defaultSideMaterialAsset " ) ;
2012-09-19 15:15:01 +00:00
}
GuiMeshRoadEditorCtrl : : ~ GuiMeshRoadEditorCtrl ( )
{
// nothing to do
}
void GuiMeshRoadEditorUndoAction : : undo ( )
{
MeshRoad * object = NULL ;
if ( ! Sim : : findObject ( mObjId , object ) )
return ;
// Temporarily save the Roads current data.
//F32 metersPerSeg = object->mMetersPerSegment;
Vector < MeshRoadNode > nodes ;
nodes . merge ( object - > mNodes ) ;
// Restore the River properties saved in the UndoAction
//object->mMetersPerSegment = mMetersPerSegment;
// Restore the Nodes saved in the UndoAction
object - > mNodes . clear ( ) ;
for ( U32 i = 0 ; i < mNodes . size ( ) ; i + + )
{
object - > _addNode ( mNodes [ i ] . point , mNodes [ i ] . width , mNodes [ i ] . depth , mNodes [ i ] . normal ) ;
}
2020-10-03 22:19:36 +00:00
// Temporarily save the Roads current profile data.
Vector < MeshRoadProfileNode > profNodes ;
Vector < U8 > profMtrls ;
profNodes . merge ( object - > mSideProfile . mNodes ) ;
profMtrls . merge ( object - > mSideProfile . mSegMtrls ) ;
// Restore the Profile Nodes saved in the UndoAction
Point3F pos ;
object - > mSideProfile . mNodes . clear ( ) ;
object - > mSideProfile . mSegMtrls . clear ( ) ;
for ( U32 i = 0 ; i < mProfileNodes . size ( ) ; i + + )
{
MeshRoadProfileNode newNode ;
pos = mProfileNodes [ i ] . getPosition ( ) ;
newNode . setSmoothing ( mProfileNodes [ i ] . isSmooth ( ) ) ;
object - > mSideProfile . mNodes . push_back ( newNode ) ;
object - > mSideProfile . mNodes . last ( ) . setPosition ( pos . x , pos . y ) ;
if ( i )
object - > mSideProfile . mSegMtrls . push_back ( mProfileMtrls [ i - 1 ] ) ;
}
// Set the first node position to trigger packet update to client
pos . set ( 0.0f , 0.0f , 0.0f ) ;
object - > mSideProfile . setNodePosition ( 0 , pos ) ;
// Regenerate the Road
object - > mSideProfile . generateNormals ( ) ;
// If applicable set the selected Road and node
mEditor - > mProfileNode = - 1 ;
// Now save the previous Road data in this UndoAction
// since an undo action must become a redo action and vice-versa
//mMetersPerSegment = metersPerSeg;
mProfileNodes . clear ( ) ;
mProfileNodes . merge ( profNodes ) ;
mProfileMtrls . clear ( ) ;
mProfileMtrls . merge ( profMtrls ) ;
2012-09-19 15:15:01 +00:00
// Regenerate the Road
object - > regenerate ( ) ;
// If applicable set the selected Road and node
mEditor - > mSelRoad = object ;
mEditor - > mSelNode = - 1 ;
// Now save the previous Road data in this UndoAction
// since an undo action must become a redo action and vice-versa
//mMetersPerSegment = metersPerSeg;
mNodes . clear ( ) ;
mNodes . merge ( nodes ) ;
}
bool GuiMeshRoadEditorCtrl : : onAdd ( )
{
if ( ! Parent : : onAdd ( ) )
return false ;
mRoadSet = MeshRoad : : getServerSet ( ) ;
GFXStateBlockDesc desc ;
desc . fillMode = GFXFillSolid ;
desc . blendDefined = true ;
desc . blendEnable = false ;
desc . zDefined = true ;
desc . zEnable = false ;
desc . cullDefined = true ;
desc . cullMode = GFXCullNone ;
mZDisableSB = GFX - > createStateBlock ( desc ) ;
desc . zEnable = true ;
mZEnableSB = GFX - > createStateBlock ( desc ) ;
2021-01-03 14:58:53 +00:00
bindMaterialAsset ( TopMaterial ) ;
bindMaterialAsset ( BottomMaterial ) ;
bindMaterialAsset ( SideMaterial ) ;
2012-09-19 15:15:01 +00:00
return true ;
}
void GuiMeshRoadEditorCtrl : : initPersistFields ( )
{
addField ( " DefaultWidth " , TypeF32 , Offset ( mDefaultWidth , GuiMeshRoadEditorCtrl ) ) ;
addField ( " DefaultDepth " , TypeF32 , Offset ( mDefaultDepth , GuiMeshRoadEditorCtrl ) ) ;
addField ( " DefaultNormal " , TypePoint3F , Offset ( mDefaultNormal , GuiMeshRoadEditorCtrl ) ) ;
addField ( " HoverSplineColor " , TypeColorI , Offset ( mHoverSplineColor , GuiMeshRoadEditorCtrl ) ) ;
addField ( " SelectedSplineColor " , TypeColorI , Offset ( mSelectedSplineColor , GuiMeshRoadEditorCtrl ) ) ;
addField ( " HoverNodeColor " , TypeColorI , Offset ( mHoverNodeColor , GuiMeshRoadEditorCtrl ) ) ;
addField ( " isDirty " , TypeBool , Offset ( mIsDirty , GuiMeshRoadEditorCtrl ) ) ;
2021-01-03 14:58:53 +00:00
addField ( " topMaterial " , TypeMaterialAssetId , Offset ( mTopMaterialAssetId , GuiMeshRoadEditorCtrl ) , " Default Material used by the Mesh Road Editor on upper surface road creation. " ) ;
addField ( " bottomMaterial " , TypeMaterialAssetId , Offset ( mBottomMaterialAssetId , GuiMeshRoadEditorCtrl ) , " Default Material used by the Mesh Road Editor on bottom surface road creation. " ) ;
addField ( " sideMaterial " , TypeMaterialAssetId , Offset ( mSideMaterialAssetId , GuiMeshRoadEditorCtrl ) , " Default Material used by the Mesh Road Editor on side surface road creation. " ) ;
2012-09-19 15:15:01 +00:00
//addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiMeshRoadEditorCtrl) );
//addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiMeshRoadEditorCtrl) );
//addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiMeshRoadEditorCtrl) );
//addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiMeshRoadEditorCtrl) );
Parent : : initPersistFields ( ) ;
}
void GuiMeshRoadEditorCtrl : : onSleep ( )
{
Parent : : onSleep ( ) ;
mMode = mSelectMeshRoadMode ;
mHoverNode = - 1 ;
mHoverRoad = NULL ;
setSelectedNode ( - 1 ) ;
}
void GuiMeshRoadEditorCtrl : : 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 GuiMeshRoadEditorCtrl : : on3DMouseDown ( const Gui3DMouseEvent & event )
{
mHasCopied = false ;
mGizmo - > on3DMouseDown ( event ) ;
if ( ! isFirstResponder ( ) )
setFirstResponder ( ) ;
2020-10-03 22:19:36 +00:00
if ( MeshRoad : : smShowRoadProfile & & mSelRoad )
{
// Ctrl-Click = Add Node
if ( event . modifier & SI_CTRL )
{
S32 clickedNode = _getProfileNodeAtScreenPos ( & mSelRoad - > mSideProfile , event . mousePoint ) ;
if ( clickedNode ! = - 1 )
{
// If clicked node is already in list, remove it, else add it to list
if ( ! mSelProfNodeList . remove ( clickedNode ) & & clickedNode > 0 )
mSelProfNodeList . push_back ( clickedNode ) ;
return ;
}
Point3F pos ;
PlaneF xy ( mSelRoad - > mSlices [ 0 ] . p2 , - mSelRoad - > mSlices [ 0 ] . fvec ) ;
xy . intersect ( event . pos , event . vec , & pos ) ;
mSelRoad - > mSideProfile . worldToObj ( pos ) ;
U32 node = mSelRoad - > mSideProfile . clickOnLine ( pos ) ;
if ( node ! = - 1 )
{
submitUndo ( " Add Profile Node " ) ;
mSelRoad - > mSideProfile . addPoint ( node , pos ) ;
mProfileNode = node ;
mSelProfNodeList . clear ( ) ;
mSelProfNodeList . push_back ( node ) ;
mIsDirty = true ;
}
return ;
}
// Alt-Click = Delete Node
if ( event . modifier & SI_ALT )
{
S32 clickedNode = _getProfileNodeAtScreenPos ( & mSelRoad - > mSideProfile , event . mousePoint ) ;
if ( mSelProfNodeList . find_next ( clickedNode ) ! = - 1 )
{
submitUndo ( " Delete Profile Node " ) ;
mSelProfNodeList . sort ( _NodeIndexCmp ) ;
for ( U32 i = 0 ; i < mSelProfNodeList . size ( ) ; i + + )
mSelRoad - > mSideProfile . removePoint ( mSelProfNodeList [ i ] ) ;
mProfileNode = - 1 ;
mSelProfNodeList . clear ( ) ;
mIsDirty = true ;
}
else if ( clickedNode > 0 & & clickedNode < mSelRoad - > mSideProfile . mNodes . size ( ) - 1 )
{
submitUndo ( " Delete Profile Node " ) ;
mSelRoad - > mSideProfile . removePoint ( clickedNode ) ;
mProfileNode = - 1 ;
mSelProfNodeList . clear ( ) ;
mIsDirty = true ;
}
return ;
}
// Shift-Click = Toggle Node Smoothing
if ( event . modifier & SI_SHIFT )
{
S32 clickedNode = _getProfileNodeAtScreenPos ( & mSelRoad - > mSideProfile , event . mousePoint ) ;
if ( clickedNode ! = - 1 )
{
submitUndo ( " Smooth Profile Node " ) ;
if ( mSelProfNodeList . find_next ( clickedNode ) ! = - 1 )
{
for ( U32 i = 0 ; i < mSelProfNodeList . size ( ) ; i + + )
mSelRoad - > mSideProfile . toggleSmoothing ( mSelProfNodeList [ i ] ) ;
}
else
{
mSelRoad - > mSideProfile . toggleSmoothing ( clickedNode ) ;
if ( clickedNode ! = 0 )
{
mProfileNode = clickedNode ;
mSelProfNodeList . clear ( ) ;
mSelProfNodeList . push_back ( clickedNode ) ;
}
}
mIsDirty = true ;
return ;
}
Point3F pos ;
PlaneF xy ( mSelRoad - > mSlices [ 0 ] . p2 , - mSelRoad - > mSlices [ 0 ] . fvec ) ;
xy . intersect ( event . pos , event . vec , & pos ) ;
mSelRoad - > mSideProfile . worldToObj ( pos ) ;
U32 node = mSelRoad - > mSideProfile . clickOnLine ( pos ) ;
if ( node > 0 )
{
submitUndo ( " Profile Material " ) ;
mSelRoad - > mSideProfile . toggleSegMtrl ( node - 1 ) ;
mIsDirty = true ;
}
return ;
}
// Click to select/deselect nodes
S32 clickedNode = _getProfileNodeAtScreenPos ( & mSelRoad - > mSideProfile , event . mousePoint ) ;
if ( clickedNode ! = - 1 )
{
if ( mSelProfNodeList . find_next ( clickedNode ) ! = - 1 )
{
mProfileNode = clickedNode ;
mDeselectProfileNode = true ;
}
else if ( clickedNode ! = 0 )
{
mProfileNode = clickedNode ;
mSelProfNodeList . clear ( ) ;
mSelProfNodeList . push_back ( clickedNode ) ;
}
else
{
mProfileNode = - 1 ;
mSelProfNodeList . clear ( ) ;
// Reset profile if Node 0 is double-clicked
if ( event . mouseClickCount > 1 )
{
submitUndo ( " Reset Profile " ) ;
mSelRoad - > mSideProfile . resetProfile ( mSelRoad - > mSlices [ 0 ] . depth ) ;
mSelRoad - > regenerate ( ) ;
}
}
return ;
}
mProfileNode = - 1 ;
mSelProfNodeList . clear ( ) ;
}
2012-09-19 15:15:01 +00:00
// Get the raycast collision position
Point3F tPos ;
if ( ! getStaticPos ( event , tPos ) )
return ;
mouseLock ( ) ;
// 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 MeshRoadNode
// was clicked.
MeshRoad * pRoad = NULL ;
MeshRoad * pClickedRoad = NULL ;
U32 insertNodeIdx = - 1 ;
Point3F collisionPnt ;
Point3F startPnt = event . pos ;
Point3F endPnt = event . pos + event . vec * 2000.0f ;
RayInfo ri ;
if ( gServerContainer . castRay ( startPnt , endPnt , StaticShapeObjectType , & ri ) )
endPnt = ri . point ;
DebugDrawer * ddraw = DebugDrawer : : get ( ) ;
if ( false & & ddraw )
{
ddraw - > drawLine ( startPnt , endPnt , ColorI ( 255 , 0 , 0 , 255 ) ) ;
ddraw - > setLastTTL ( DebugDrawer : : DD_INFINITE ) ;
}
// Check for collision with nodes of the currently selected or highlighted MeshRoad
// Did we click on a MeshRoad? check currently selected road first
if ( mSelRoad ! = NULL & & mSelRoad - > collideRay ( event . pos , event . vec , & insertNodeIdx , & collisionPnt ) )
{
pClickedRoad = mSelRoad ;
}
else
{
for ( SimSetIterator iter ( mRoadSet ) ; * iter ; + + iter )
{
pRoad = static_cast < MeshRoad * > ( * iter ) ;
// Do not select or edit a MeshRoad within a Prefab.
if ( Prefab : : getPrefabByChild ( pRoad ) )
continue ;
if ( pRoad - > collideRay ( event . pos , event . vec , & insertNodeIdx , & collisionPnt ) )
{
pClickedRoad = pRoad ;
break ;
}
}
}
/*
else if ( gServerContainer . castRay ( startPnt , endPnt , StaticShapeObjectType , & ri ) )
{
MeshRoad * pRoad = NULL ;
pRoad = dynamic_cast < MeshRoad * > ( ri . object ) ;
if ( pRoad & & pRoad - > collideRay ( event . pos , event . vec , & insertNodeIdx , & collisionPnt ) )
pClickedRoad = pRoad ;
}
*/
// Did we click on a node?
bool nodeClicked = false ;
S32 clickedNodeIdx = - 1 ;
// If we clicked on the currently selected road, only scan its nodes
if ( mSelRoad ! = NULL & & pClickedRoad = = mSelRoad )
{
clickedNodeIdx = _getNodeAtScreenPos ( mSelRoad , event . mousePoint ) ;
nodeClicked = clickedNodeIdx ! = - 1 ;
}
else
{
for ( SimSetIterator iter ( mRoadSet ) ; * iter ; + + iter )
{
pRoad = static_cast < MeshRoad * > ( * iter ) ;
// Do not select or edit a MeshRoad within a Prefab.
if ( Prefab : : getPrefabByChild ( pRoad ) )
continue ;
clickedNodeIdx = _getNodeAtScreenPos ( pRoad , event . mousePoint ) ;
if ( clickedNodeIdx ! = - 1 )
{
nodeClicked = true ;
pClickedRoad = pRoad ;
break ;
}
}
}
// shortcuts
bool dblClick = ( event . mouseClickCount > 1 ) ;
if ( dblClick )
{
if ( mMode = = mSelectMeshRoadMode )
{
setMode ( mAddMeshRoadMode , true ) ;
return ;
}
if ( mMode = = mAddNodeMode )
{
// Delete the node attached to the cursor.
deleteSelectedNode ( ) ;
mMode = mAddMeshRoadMode ;
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 ( ! mSelRoad )
mMode = mAddMeshRoadMode ;
}
if ( mMode = = mSelectMeshRoadMode )
{
// Did not click on a MeshRoad or a node.
if ( ! pClickedRoad )
{
setSelectedRoad ( NULL ) ;
setSelectedNode ( - 1 ) ;
return ;
}
// Clicked on a MeshRoad that wasn't the currently selected River.
if ( pClickedRoad ! = mSelRoad )
{
setSelectedRoad ( pClickedRoad ) ;
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 = = mAddMeshRoadMode )
{
if ( nodeClicked )
{
// A double-click on a node in Normal mode means set AddNode mode.
if ( clickedNodeIdx = = 0 )
{
setSelectedRoad ( pClickedRoad ) ;
setSelectedNode ( clickedNodeIdx ) ;
mAddNodeIdx = clickedNodeIdx ;
mMode = mAddNodeMode ;
mSelNode = mSelRoad - > insertNode ( tPos , mDefaultWidth , mDefaultDepth , mDefaultNormal , mAddNodeIdx ) ;
mIsDirty = true ;
return ;
}
else if ( clickedNodeIdx = = pClickedRoad - > mNodes . size ( ) - 1 )
{
setSelectedRoad ( pClickedRoad ) ;
setSelectedNode ( clickedNodeIdx ) ;
mAddNodeIdx = U32_MAX ;
mMode = mAddNodeMode ;
mSelNode = mSelRoad - > addNode ( tPos , mDefaultWidth , mDefaultDepth , mDefaultNormal ) ;
mIsDirty = true ;
setSelectedNode ( mSelNode ) ;
return ;
}
}
MeshRoad * newRoad = new MeshRoad ;
2021-01-03 14:58:53 +00:00
if ( mTopMaterialAsset . notNull ( ) )
newRoad - > setTopMaterialAssetId ( mTopMaterialAssetId ) ;
if ( mBottomMaterialAsset . notNull ( ) )
newRoad - > setBottomMaterialAssetId ( mBottomMaterialAssetId ) ;
if ( mSideMaterialAsset . notNull ( ) )
newRoad - > setSideMaterialAssetId ( mSideMaterialAssetId ) ;
2012-09-19 15:15:01 +00:00
newRoad - > registerObject ( ) ;
2019-02-23 21:55:28 +00:00
// Add to scene
Scene * scene ;
scene = Scene : : getRootScene ( ) ;
if ( ! scene )
Con : : errorf ( " GuiMeshRoadEditorCtrl - could not find Scene to add new MeshRoad " ) ;
2012-09-19 15:15:01 +00:00
else
2019-02-23 21:55:28 +00:00
scene - > addObject ( newRoad ) ;
2012-09-19 15:15:01 +00:00
Point3F pos ( endPnt ) ;
pos . z + = mDefaultDepth * 0.5f ;
newRoad - > insertNode ( pos , mDefaultWidth , mDefaultDepth , mDefaultNormal , 0 ) ;
U32 newNode = newRoad - > insertNode ( pos , mDefaultWidth , mDefaultDepth , mDefaultNormal , 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 ( " GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found! " ) ;
return ;
}
// Create the UndoAction.
MECreateUndoAction * action = new MECreateUndoAction ( " Create MeshRoad " ) ;
action - > addObject ( newRoad ) ;
// Submit it.
undoMan - > addAction ( action ) ;
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 , mDefaultDepth , mDefaultNormal , mAddNodeIdx ) ;
mIsDirty = true ;
setSelectedNode ( mSelNode ) ;
return ;
}
else
{
if ( pClickedRoad & & clickedNodeIdx = = pClickedRoad - > mNodes . size ( ) - 1 )
{
submitUndo ( " Add Node " ) ;
mAddNodeIdx = U32_MAX ;
mMode = mAddNodeMode ;
U32 newNode = mSelRoad - > 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" );
U32 newNode = mSelRoad - > insertNode ( tPos , mDefaultWidth , mDefaultDepth , mDefaultNormal , mAddNodeIdx ) ;
mIsDirty = true ;
setSelectedNode ( newNode ) ;
return ;
}
}
}
}
else if ( mMode = = mInsertPointMode & & mSelRoad ! = NULL )
{
if ( pClickedRoad = = mSelRoad & & insertNodeIdx ! = - 1 )
{
// 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 fxRoad
U32 prevNodeIdx = insertNodeIdx ;
U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRoad - > mNodes . size ( ) - 1 ) ? prevNodeIdx : prevNodeIdx + 1 ;
const MeshRoadNode & prevNode = mSelRoad - > mNodes [ prevNodeIdx ] ;
const MeshRoadNode & nextNode = mSelRoad - > 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 = mSelRoad - > insertNode ( collisionPnt , width , depth , normal , insertNodeIdx + 1 ) ;
mIsDirty = true ;
setSelectedNode ( newNode ) ;
return ;
}
}
else if ( mMode = = mRemovePointMode & & mSelRoad ! = NULL )
{
if ( nodeClicked & & pClickedRoad = = mSelRoad )
{
setSelectedNode ( clickedNodeIdx ) ;
deleteSelectedNode ( ) ;
return ;
}
}
else if ( mMode = = mMovePointMode )
{
if ( nodeClicked & & pClickedRoad = = mSelRoad )
{
setSelectedNode ( clickedNodeIdx ) ;
return ;
}
}
else if ( mMode = = mScalePointMode )
{
if ( nodeClicked & & pClickedRoad = = mSelRoad )
{
setSelectedNode ( clickedNodeIdx ) ;
return ;
}
}
else if ( mMode = = mRotatePointMode )
{
if ( nodeClicked & & pClickedRoad = = mSelRoad )
{
setSelectedNode ( clickedNodeIdx ) ;
return ;
}
}
}
void GuiMeshRoadEditorCtrl : : on3DRightMouseDown ( const Gui3DMouseEvent & event )
{
//mIsPanning = true;
}
void GuiMeshRoadEditorCtrl : : on3DRightMouseUp ( const Gui3DMouseEvent & event )
{
//mIsPanning = false;
}
void GuiMeshRoadEditorCtrl : : on3DMouseUp ( const Gui3DMouseEvent & event )
{
mGizmo - > on3DMouseUp ( event ) ;
mSavedDrag = false ;
2020-10-03 22:19:36 +00:00
mSavedProfileDrag = false ;
if ( MeshRoad : : smShowRoadProfile & & mSelRoad )
{
// If we need to deselect node... this means we clicked on a selected node without dragging
if ( mDeselectProfileNode )
{
S32 clickedNode = _getProfileNodeAtScreenPos ( & mSelRoad - > mSideProfile , event . mousePoint ) ;
if ( clickedNode = = mProfileNode )
{
mProfileNode = - 1 ;
mSelProfNodeList . clear ( ) ;
}
mDeselectProfileNode = false ;
}
// Else if we dragged a node, update the road
else
{
S32 clickedNode = _getProfileNodeAtScreenPos ( & mSelRoad - > mSideProfile , event . mousePoint ) ;
if ( clickedNode = = mProfileNode )
mSelRoad - > regenerate ( ) ; // This regens the road for collision purposes on the server
}
}
2012-09-19 15:15:01 +00:00
mouseUnlock ( ) ;
}
void GuiMeshRoadEditorCtrl : : on3DMouseMove ( const Gui3DMouseEvent & event )
{
if ( mSelRoad ! = NULL & & mMode = = mAddNodeMode )
{
Point3F pos ;
mSelRoad - > disableCollision ( ) ;
if ( getStaticPos ( event , pos ) )
{
pos . z + = mSelRoad - > getNodeDepth ( mSelNode ) * 0.5f ;
mSelRoad - > setNodePosition ( mSelNode , pos ) ;
mIsDirty = true ;
}
mSelRoad - > enableCollision ( ) ;
return ;
}
if ( mSelRoad ! = NULL & & mSelNode ! = - 1 )
{
mGizmo - > on3DMouseMove ( event ) ;
//mGizmo.collideAxisGizmo( event );
//Con::printf( "SelectedAxis: %i", mGizmo.getSelectedAxis() );
}
// Is cursor hovering over a river?
if ( mMode = = mSelectMeshRoadMode )
{
mHoverRoad = NULL ;
Point3F startPnt = event . pos ;
Point3F endPnt = event . pos + event . vec * 1000.0f ;
RayInfo ri ;
if ( gServerContainer . castRay ( startPnt , endPnt , StaticShapeObjectType , & ri ) )
{
MeshRoad * pRoad = NULL ;
pRoad = dynamic_cast < MeshRoad * > ( ri . object ) ;
// Do not select or edit a MeshRoad within a Prefab.
if ( pRoad & & ! Prefab : : getPrefabByChild ( pRoad ) )
mHoverRoad = pRoad ;
}
}
// Is cursor over a node?
if ( mHoverRoad )
{
MeshRoad * pRoad = NULL ;
S32 nodeIdx = - 1 ;
for ( SimSetIterator iter ( mRoadSet ) ; * iter ; + + iter )
{
pRoad = static_cast < MeshRoad * > ( * iter ) ;
nodeIdx = _getNodeAtScreenPos ( pRoad , event . mousePoint ) ;
if ( nodeIdx ! = - 1 )
{
mHoverRoad = pRoad ;
break ;
}
}
mHoverNode = nodeIdx ;
}
}
void GuiMeshRoadEditorCtrl : : on3DMouseDragged ( const Gui3DMouseEvent & event )
{
2020-10-03 22:19:36 +00:00
if ( MeshRoad : : smShowRoadProfile & & mProfileNode > 0 & & mSelRoad )
{
// If we haven't already saved,
// save an undo action to get back to this state,
// before we make any modifications to the selected node.
if ( ! mSavedProfileDrag )
{
submitUndo ( " Modify Profile Node " ) ;
mSavedProfileDrag = true ;
mIsDirty = true ;
}
U32 idx ;
Point3F pos , diff ;
PlaneF xy ( mSelRoad - > mSlices [ 0 ] . p2 , - mSelRoad - > mSlices [ 0 ] . fvec ) ;
xy . intersect ( event . pos , event . vec , & pos ) ;
mSelRoad - > mSideProfile . worldToObj ( pos ) ;
diff = pos - mSelRoad - > mSideProfile . mNodes [ mProfileNode ] . getPosition ( ) ;
for ( U32 i = 0 ; i < mSelProfNodeList . size ( ) ; i + + )
{
idx = mSelProfNodeList [ i ] ;
pos = mSelRoad - > mSideProfile . mNodes [ idx ] . getPosition ( ) ;
pos + = diff ;
if ( pos . x < - mSelRoad - > mSlices [ 0 ] . width / 2.0f )
pos . x = - mSelRoad - > mSlices [ 0 ] . width / 2.0f + 1e-6 ;
mSelRoad - > mSideProfile . setNodePosition ( idx , pos ) ;
}
mDeselectProfileNode = false ;
return ;
}
2012-09-19 15:15:01 +00:00
// Drags are only used to transform nodes
if ( ! mSelRoad | | mSelNode = = - 1 | |
( mMode ! = mMovePointMode & & mMode ! = mScalePointMode & & mMode ! = mRotatePointMode ) )
return ;
// If we haven't already saved,
// save an undo action to get back to this state,
// before we make any modifications to the selected node.
if ( ! mSavedDrag )
{
submitUndo ( " Modify Node " ) ;
mSavedDrag = true ;
}
// If shift is held and we haven't already copied the node,
// make a copy of the selected node and select it.
if ( event . modifier & SI_SHIFT & & ! mHasCopied & & mSelRoad - > isEndNode ( mSelNode ) )
{
const MeshRoadNode & data = mSelRoad - > getNode ( mSelNode ) ;
U32 insertIdx = ( mSelNode = = 0 ) ? 0 : U32_MAX ;
U32 newNodeIdx = mSelRoad - > insertNode ( data . point , data . width , data . depth , data . normal , insertIdx ) ;
mIsDirty = true ;
mSelNode = - 1 ;
setSelectedNode ( newNodeIdx ) ;
mHasCopied = true ;
}
// Let the Gizmo handle the drag, eg, modify its transforms
mGizmo - > on3DMouseDragged ( event ) ;
if ( mGizmo - > isDirty ( ) )
{
Point3F pos = mGizmo - > getPosition ( ) ;
Point3F scale = mGizmo - > getScale ( ) ;
const MatrixF & mat = mGizmo - > getTransform ( ) ;
VectorF normal ;
mat . getColumn ( 2 , & normal ) ;
mSelRoad - > setNode ( pos , scale . x , scale . z , normal , mSelNode ) ;
mIsDirty = true ;
mGizmo - > markClean ( ) ;
}
Con : : executef ( this , " onNodeModified " , Con : : getIntArg ( mSelNode ) ) ;
}
void GuiMeshRoadEditorCtrl : : on3DMouseEnter ( const Gui3DMouseEvent & event )
{
// nothing to do
}
void GuiMeshRoadEditorCtrl : : on3DMouseLeave ( const Gui3DMouseEvent & event )
{
// nothing to do
}
bool GuiMeshRoadEditorCtrl : : onKeyDown ( const GuiEvent & event )
{
if ( event . keyCode = = KEY_RETURN & & mMode = = mAddNodeMode )
{
// Delete the node attached to the cursor.
deleteSelectedNode ( ) ;
mMode = mAddMeshRoadMode ;
return true ;
}
return false ;
}
void GuiMeshRoadEditorCtrl : : updateGuiInfo ( )
{
// nothing to do
}
void GuiMeshRoadEditorCtrl : : renderScene ( const RectI & updateRect )
{
//GFXDrawUtil *drawer = GFX->getDrawUtil();
GFX - > setStateBlock ( mZDisableSB ) ;
// get the projected size...
GameConnection * connection = GameConnection : : getConnectionToServer ( ) ;
if ( ! connection )
return ;
// Grab the camera's transform
MatrixF mat ;
connection - > getControlCameraTransform ( 0 , & mat ) ;
// Get the camera position
Point3F camPos ;
mat . getColumn ( 3 , & camPos ) ;
2020-10-03 22:19:36 +00:00
// Set up transform
if ( mSelRoad )
{
MatrixF profileMat ( true ) ;
profileMat . setRow ( 0 , mSelRoad - > mSlices [ 0 ] . rvec ) ;
profileMat . setRow ( 1 , mSelRoad - > mSlices [ 0 ] . uvec ) ;
profileMat . setRow ( 2 , - mSelRoad - > mSlices [ 0 ] . fvec ) ;
mSelRoad - > mSideProfile . setTransform ( profileMat , mSelRoad - > mSlices [ 0 ] . p2 ) ;
}
2012-09-19 15:15:01 +00:00
if ( mHoverRoad & & mHoverRoad ! = mSelRoad )
{
_drawSpline ( mHoverRoad , mHoverSplineColor ) ;
}
if ( mSelRoad )
{
_drawSpline ( mSelRoad , mSelectedSplineColor ) ;
// Render Gizmo for selected node if were in either of the three transform modes
if ( mSelNode ! = - 1 & & ( mMode = = mMovePointMode | | mMode = = mScalePointMode | | mMode = = mRotatePointMode ) )
{
if ( mMode = = mMovePointMode )
{
mGizmo - > getProfile ( ) - > mode = MoveMode ;
}
else if ( mMode = = mScalePointMode )
{
mGizmo - > getProfile ( ) - > mode = ScaleMode ;
}
else if ( mMode = = mRotatePointMode )
{
mGizmo - > getProfile ( ) - > mode = RotateMode ;
}
const MeshRoadNode & node = mSelRoad - > mNodes [ mSelNode ] ;
MatrixF objMat = mSelRoad - > getNodeTransform ( mSelNode ) ;
Point3F objScale ( node . width , 1.0f , node . depth ) ;
Point3F worldPos = node . point ;
mGizmo - > set ( objMat , worldPos , objScale ) ;
mGizmo - > renderGizmo ( mLastCameraQuery . cameraMatrix , mLastCameraQuery . fov ) ;
// Render Gizmo text
mGizmo - > renderText ( mSaveViewport , mSaveModelview , mSaveProjection ) ;
}
}
DebugDrawer : : get ( ) - > render ( ) ;
// Now draw all the 2d stuff!
GFX - > setClipRect ( updateRect ) ;
// Draw Control nodes for selecting and highlighted rivers
if ( mHoverRoad )
_drawControlNodes ( mHoverRoad , mHoverSplineColor ) ;
if ( mSelRoad )
_drawControlNodes ( mSelRoad , mSelectedSplineColor ) ;
2020-10-03 22:19:36 +00:00
if ( MeshRoad : : smShowRoadProfile )
{
char buf [ 64 ] ;
Point2I posi ;
posi . x = 10 ;
posi . y = updateRect . len_y ( ) - 80 ;
GFX - > getDrawUtil ( ) - > setBitmapModulation ( ColorI ( 128 , 128 , 128 ) ) ;
dStrcpy ( buf , " Reset Profile: Double-click Start Node " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
posi . y - = mProfile - > mFont - > getCharHeight ( ( U8 ) buf [ 0 ] ) + 4 ;
dStrcpy ( buf , " Move Node: Click and Drag Node " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
posi . y - = mProfile - > mFont - > getCharHeight ( ( U8 ) buf [ 0 ] ) + 4 ;
dStrcpy ( buf , " Select Multiple Nodes: Ctrl-click Nodes " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
posi . y - = mProfile - > mFont - > getCharHeight ( ( U8 ) buf [ 0 ] ) + 4 ;
dStrcpy ( buf , " Toggle Material: Shift-click Spline Segment " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
posi . y - = mProfile - > mFont - > getCharHeight ( ( U8 ) buf [ 0 ] ) + 4 ;
dStrcpy ( buf , " Toggle Smoothing: Shift-click Node " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
posi . y - = mProfile - > mFont - > getCharHeight ( ( U8 ) buf [ 0 ] ) + 4 ;
dStrcpy ( buf , " Delete Node: Alt-click Node " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
posi . y - = mProfile - > mFont - > getCharHeight ( ( U8 ) buf [ 0 ] ) + 4 ;
dStrcpy ( buf , " Add Node: Ctrl-click Spline " , 64 ) ;
GFX - > getDrawUtil ( ) - > drawTextN ( mProfile - > mFont , posi , buf , dStrlen ( buf ) ) ;
}
2012-09-19 15:15:01 +00:00
}
S32 GuiMeshRoadEditorCtrl : : _getNodeAtScreenPos ( const MeshRoad * pRoad , const Point2I & posi )
{
for ( U32 i = 0 ; i < pRoad - > mNodes . size ( ) ; i + + )
{
const Point3F & nodePos = pRoad - > mNodes [ i ] . point ;
Point3F screenPos ;
project ( nodePos , & screenPos ) ;
if ( screenPos . z < 0.0f )
continue ;
Point2I screenPosI ( ( S32 ) screenPos . x , ( S32 ) screenPos . y ) ;
RectI nodeScreenRect ( screenPosI - mNodeHalfSize , mNodeHalfSize * 2 ) ;
if ( nodeScreenRect . pointInRect ( posi ) )
{
// we found a hit!
return i ;
}
}
return - 1 ;
}
2020-10-03 22:19:36 +00:00
S32 GuiMeshRoadEditorCtrl : : _getProfileNodeAtScreenPos ( MeshRoadProfile * pProfile , const Point2I & posi )
{
for ( U32 i = 0 ; i < pProfile - > mNodes . size ( ) ; i + + )
{
Point3F nodePos ;
pProfile - > getNodeWorldPos ( i , nodePos ) ;
Point3F screenPos ;
project ( nodePos , & screenPos ) ;
if ( screenPos . z < 0.0f )
continue ;
Point2I screenPosI ( ( S32 ) screenPos . x , ( S32 ) screenPos . y ) ;
RectI nodeScreenRect ( screenPosI - mNodeHalfSize , mNodeHalfSize * 2 ) ;
if ( nodeScreenRect . pointInRect ( posi ) )
{
// we found a hit!
return i ;
}
}
return - 1 ;
}
2012-09-19 15:15:01 +00:00
void GuiMeshRoadEditorCtrl : : _drawSpline ( MeshRoad * river , const ColorI & color )
{
if ( river - > mSlices . size ( ) < = 1 )
return ;
if ( MeshRoad : : smShowSpline )
{
// Render the River center-line
2020-10-03 22:19:36 +00:00
if ( MeshRoad : : smShowRoadProfile )
PrimBuild : : color ( ColorI ( 100 , 100 , 100 ) ) ;
else
PrimBuild : : color ( color ) ;
PrimBuild : : begin ( GFXLineStrip , river - > mSlices . size ( ) ) ;
2012-09-19 15:15:01 +00:00
for ( U32 i = 0 ; i < river - > mSlices . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( river - > mSlices [ i ] . p1 ) ;
}
PrimBuild : : end ( ) ;
}
if ( MeshRoad : : smWireframe )
{
// Left-side line
PrimBuild : : color3i ( 100 , 100 , 100 ) ;
PrimBuild : : begin ( GFXLineStrip , river - > mSlices . size ( ) ) ;
for ( U32 i = 0 ; i < river - > mSlices . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( river - > mSlices [ i ] . p0 ) ;
}
PrimBuild : : end ( ) ;
// Right-side line
PrimBuild : : begin ( GFXLineStrip , river - > mSlices . size ( ) ) ;
for ( U32 i = 0 ; i < river - > mSlices . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( river - > mSlices [ i ] . p2 ) ;
}
PrimBuild : : end ( ) ;
// Cross-sections
PrimBuild : : begin ( GFXLineList , river - > mSlices . size ( ) * 2 ) ;
for ( U32 i = 0 ; i < river - > mSlices . size ( ) ; i + + )
{
PrimBuild : : vertex3fv ( river - > mSlices [ i ] . p0 ) ;
PrimBuild : : vertex3fv ( river - > mSlices [ i ] . p2 ) ;
}
PrimBuild : : end ( ) ;
}
2020-10-03 22:19:36 +00:00
// If we are in Profile Edit Mode, draw the profile spline and node normals
if ( MeshRoad : : smShowRoadProfile )
{
Point3F nodePos ;
Point3F normEndPos ;
U32 numSide , numTop , numBottom ;
numSide = numTop = numBottom = 0 ;
for ( U32 i = 0 ; i < river - > mSideProfile . mSegMtrls . size ( ) ; i + + )
{
switch ( river - > mSideProfile . mSegMtrls [ i ] )
{
case MeshRoad : : Side : numSide + + ; break ;
case MeshRoad : : Top : numTop + + ; break ;
case MeshRoad : : Bottom : numBottom + + ; break ;
}
}
// Render the profile spline
// Side
if ( numSide )
{
PrimBuild : : color ( mProfileColor ) ;
PrimBuild : : begin ( GFXLineList , 2 * numSide ) ;
for ( U32 i = 0 ; i < river - > mSideProfile . mSegMtrls . size ( ) ; i + + )
{
if ( river - > mSideProfile . mSegMtrls [ i ] = = MeshRoad : : Side )
{
river - > mSideProfile . getNodeWorldPos ( i , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
river - > mSideProfile . getNodeWorldPos ( i + 1 , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
}
}
PrimBuild : : end ( ) ;
}
// Top
if ( numTop )
{
PrimBuild : : color ( ColorI ( 0 , 255 , 0 ) ) ;
PrimBuild : : begin ( GFXLineList , 2 * numTop ) ;
for ( U32 i = 0 ; i < river - > mSideProfile . mSegMtrls . size ( ) ; i + + )
{
if ( river - > mSideProfile . mSegMtrls [ i ] = = MeshRoad : : Top )
{
river - > mSideProfile . getNodeWorldPos ( i , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
river - > mSideProfile . getNodeWorldPos ( i + 1 , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
}
}
PrimBuild : : end ( ) ;
}
// Bottom
if ( numBottom )
{
PrimBuild : : color ( ColorI ( 255 , 0 , 255 ) ) ;
PrimBuild : : begin ( GFXLineList , 2 * numBottom ) ;
for ( U32 i = 0 ; i < river - > mSideProfile . mSegMtrls . size ( ) ; i + + )
{
if ( river - > mSideProfile . mSegMtrls [ i ] = = MeshRoad : : Bottom )
{
river - > mSideProfile . getNodeWorldPos ( i , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
river - > mSideProfile . getNodeWorldPos ( i + 1 , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
}
}
PrimBuild : : end ( ) ;
}
// Render node normals
PrimBuild : : color ( ColorI ( 255 , 0 , 0 ) ) ;
PrimBuild : : begin ( GFXLineList , 4 * river - > mSideProfile . mNodes . size ( ) - 4 ) ;
for ( U32 i = 0 ; i < river - > mSideProfile . mNodes . size ( ) - 1 ; i + + )
{
for ( U32 j = 0 ; j < 2 ; j + + )
{
river - > mSideProfile . getNodeWorldPos ( i + j , nodePos ) ;
PrimBuild : : vertex3fv ( nodePos ) ;
river - > mSideProfile . getNormWorldPos ( 2 * i + j , normEndPos ) ;
PrimBuild : : vertex3fv ( normEndPos ) ;
}
}
PrimBuild : : end ( ) ;
}
2012-09-19 15:15:01 +00:00
// Segment
}
void GuiMeshRoadEditorCtrl : : _drawControlNodes ( MeshRoad * river , const ColorI & color )
{
if ( ! MeshRoad : : smShowSpline )
return ;
RectI bounds = getBounds ( ) ;
GFXDrawUtil * drawer = GFX - > getDrawUtil ( ) ;
bool isSelected = ( river = = mSelRoad ) ;
bool isHighlighted = ( river = = mHoverRoad ) ;
for ( U32 i = 0 ; i < river - > mNodes . size ( ) ; i + + )
{
if ( false & & isSelected & & mSelNode = = i )
continue ;
const Point3F & wpos = river - > 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 )
{
//theColor = mHoverNodeColor;
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 = = river - > mNodes . size ( ) - 1 )
{
theColor . set ( 255 , 0 , 0 ) ;
}
}
2020-10-03 22:19:36 +00:00
if ( MeshRoad : : smShowRoadProfile & & isSelected )
theColor . set ( 100 , 100 , 100 ) ;
2012-09-19 15:15:01 +00:00
drawer - > drawRectFill ( posi - nodeHalfSize , posi + nodeHalfSize , theColor ) ;
}
2020-10-03 22:19:36 +00:00
// Draw profile control nodes
if ( MeshRoad : : smShowRoadProfile & & isSelected )
{
Point3F wpos ;
Point3F spos ;
Point2I posi ;
ColorI theColor ;
for ( U32 i = 0 ; i < river - > mSideProfile . mNodes . size ( ) ; i + + )
{
river - > mSideProfile . getNodeWorldPos ( i , wpos ) ;
project ( wpos , & spos ) ;
if ( spos . z > 1.0f )
continue ;
posi . x = spos . x ;
posi . y = spos . y ;
if ( ! bounds . pointInRect ( posi ) )
continue ;
if ( i = = 0 )
theColor . set ( mProfileColor . red / 3 , mProfileColor . green / 3 , mProfileColor . blue / 3 , 255 ) ;
else
theColor . set ( mProfileColor , 255 ) ;
if ( mSelProfNodeList . find_next ( i ) ! = - 1 )
theColor . set ( 0 , 0 , 255 ) ;
drawer - > drawRectFill ( posi - mNodeHalfSize , posi + mNodeHalfSize , theColor ) ;
}
}
2012-09-19 15:15:01 +00:00
}
bool GuiMeshRoadEditorCtrl : : getStaticPos ( const Gui3DMouseEvent & event , Point3F & tpos )
{
// Find clicked point on the terrain
Point3F startPnt = event . pos ;
Point3F endPnt = event . pos + event . vec * 1000.0f ;
RayInfo ri ;
bool hit ;
hit = gServerContainer . castRay ( startPnt , endPnt , StaticShapeObjectType , & ri ) ;
tpos = ri . point ;
return hit ;
}
void GuiMeshRoadEditorCtrl : : deleteSelectedNode ( )
{
if ( ! mSelRoad | | mSelNode = = - 1 )
return ;
// If the Road has only two nodes remaining,
// delete the whole Road.
if ( mSelRoad - > mNodes . size ( ) < = 2 )
{
deleteSelectedRoad ( mMode ! = mAddNodeMode ) ;
}
else
{
if ( mMode ! = mAddNodeMode )
submitUndo ( " Delete Node " ) ;
// Delete the SelectedNode of the SelectedRoad
mSelRoad - > deleteNode ( mSelNode ) ;
// We deleted the Node but not the Road (it has nodes left)
// so decrement the currently selected node.
if ( mSelRoad - > mNodes . size ( ) < = mSelNode )
setSelectedNode ( mSelNode - 1 ) ;
else
{
// force gizmo to update to the selected nodes position
// the index didn't change but the node it refers to did.
U32 i = mSelNode ;
mSelNode = - 1 ;
setSelectedNode ( i ) ;
}
}
// If you were in addNodeMode,
// deleting a node should ends it.
//mMode = smNormalMode;
}
void GuiMeshRoadEditorCtrl : : deleteSelectedRoad ( bool undoAble )
{
AssertFatal ( mSelRoad ! = NULL , " GuiMeshRoadEditorCtrl::deleteSelectedRoad() - No Road is selected " ) ;
// Not undoAble? Just delete it.
if ( ! undoAble )
{
mSelRoad - > deleteObject ( ) ;
mIsDirty = true ;
Con : : executef ( this , " onRoadSelected " ) ;
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 Road.
Con : : errorf ( " GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found! " ) ;
return ;
}
else
{
// Create the UndoAction.
MEDeleteUndoAction * action = new MEDeleteUndoAction ( " Deleted Road " ) ;
action - > deleteObject ( mSelRoad ) ;
mIsDirty = true ;
// Submit it.
undoMan - > addAction ( action ) ;
}
// ScriptCallback with 'NULL' parameter for no Road currently selected.
Con : : executef ( this , " onRoadSelected " ) ;
// Clear the SelectedNode (it has been deleted along with the River).
setSelectedNode ( - 1 ) ;
mSelNode = - 1 ;
}
void GuiMeshRoadEditorCtrl : : setMode ( String mode , bool sourceShortcut = false )
{
mMode = mode ;
if ( sourceShortcut )
2012-10-11 20:29:39 +00:00
Con : : executef ( this , " paletteSync " , mode ) ;
2012-09-19 15:15:01 +00:00
}
void GuiMeshRoadEditorCtrl : : setSelectedRoad ( MeshRoad * road )
{
mSelRoad = road ;
if ( mSelRoad ! = NULL )
Con : : executef ( this , " onRoadSelected " , road - > getIdString ( ) ) ;
else
Con : : executef ( this , " onRoadSelected " ) ;
if ( mSelRoad ! = road )
setSelectedNode ( - 1 ) ;
}
void GuiMeshRoadEditorCtrl : : setNodeWidth ( F32 width )
{
if ( mSelRoad & & mSelNode ! = - 1 )
{
mSelRoad - > setNodeWidth ( mSelNode , width ) ;
mIsDirty = true ;
}
}
F32 GuiMeshRoadEditorCtrl : : getNodeWidth ( )
{
if ( mSelRoad & & mSelNode ! = - 1 )
return mSelRoad - > getNodeWidth ( mSelNode ) ;
return 0.0f ;
}
void GuiMeshRoadEditorCtrl : : setNodeDepth ( F32 depth )
{
if ( mSelRoad & & mSelNode ! = - 1 )
{
mSelRoad - > setNodeDepth ( mSelNode , depth ) ;
mIsDirty = true ;
}
}
F32 GuiMeshRoadEditorCtrl : : getNodeDepth ( )
{
if ( mSelRoad & & mSelNode ! = - 1 )
return mSelRoad - > getNodeDepth ( mSelNode ) ;
return 0.0f ;
}
2015-07-17 03:02:18 +00:00
void GuiMeshRoadEditorCtrl : : setNodePosition ( const Point3F & pos )
2012-09-19 15:15:01 +00:00
{
if ( mSelRoad & & mSelNode ! = - 1 )
{
mSelRoad - > setNodePosition ( mSelNode , pos ) ;
mIsDirty = true ;
}
}
Point3F GuiMeshRoadEditorCtrl : : getNodePosition ( )
{
if ( mSelRoad & & mSelNode ! = - 1 )
return mSelRoad - > getNodePosition ( mSelNode ) ;
return Point3F ( 0 , 0 , 0 ) ;
}
void GuiMeshRoadEditorCtrl : : setNodeNormal ( const VectorF & normal )
{
if ( mSelRoad & & mSelNode ! = - 1 )
{
mSelRoad - > setNodeNormal ( mSelNode , normal ) ;
mIsDirty = true ;
}
}
VectorF GuiMeshRoadEditorCtrl : : getNodeNormal ( )
{
if ( mSelRoad & & mSelNode ! = - 1 )
return mSelRoad - > getNodeNormal ( mSelNode ) ;
return VectorF : : Zero ;
}
void GuiMeshRoadEditorCtrl : : setSelectedNode ( S32 node )
{
if ( mSelNode = = node )
return ;
mSelNode = node ;
if ( mSelNode ! = - 1 )
{
2018-03-14 22:43:03 +00:00
const MeshRoadNode & curNode = mSelRoad - > mNodes [ mSelNode ] ;
2012-09-19 15:15:01 +00:00
MatrixF objMat = mSelRoad - > getNodeTransform ( mSelNode ) ;
2018-03-14 22:43:03 +00:00
Point3F objScale ( curNode . width , 1.0f , curNode . depth ) ;
Point3F worldPos = curNode . point ;
2012-09-19 15:15:01 +00:00
mGizmo - > set ( objMat , worldPos , objScale ) ;
}
if ( mSelNode ! = - 1 )
Con : : executef ( this , " onNodeSelected " , Con : : getIntArg ( mSelNode ) ) ;
else
Con : : executef ( this , " onNodeSelected " , Con : : getIntArg ( - 1 ) ) ;
}
void GuiMeshRoadEditorCtrl : : submitUndo ( const UTF8 * name )
{
// Grab the mission editor undo manager.
UndoManager * undoMan = NULL ;
if ( ! Sim : : findObject ( " EUndoManager " , undoMan ) )
{
Con : : errorf ( " GuiMeshRoadEditorCtrl::submitUndo() - EUndoManager not found! " ) ;
return ;
}
// Setup the action.
GuiMeshRoadEditorUndoAction * action = new GuiMeshRoadEditorUndoAction ( name ) ;
action - > mObjId = mSelRoad - > getId ( ) ;
//action->mMetersPerSegment = mSelRoad->mMetersPerSegment;
action - > mEditor = this ;
for ( U32 i = 0 ; i < mSelRoad - > mNodes . size ( ) ; i + + )
{
action - > mNodes . push_back ( mSelRoad - > mNodes [ i ] ) ;
}
2020-10-03 22:19:36 +00:00
// Save profile nodes and materials
for ( U32 i = 0 ; i < mSelRoad - > mSideProfile . mNodes . size ( ) ; i + + )
{
action - > mProfileNodes . push_back ( mSelRoad - > mSideProfile . mNodes [ i ] ) ;
if ( i )
action - > mProfileMtrls . push_back ( mSelRoad - > mSideProfile . mSegMtrls [ i - 1 ] ) ;
}
2012-09-19 15:15:01 +00:00
undoMan - > addAction ( action ) ;
}
void GuiMeshRoadEditorCtrl : : matchTerrainToRoad ( )
{
if ( ! mSelRoad )
return ;
// Not implemented, but a potentially useful idea.
// Move manipulate the terrain so that the MeshRoad appears to be sitting
// on top of it.
// The opposite could also be useful, manipulate the MeshRoad to line up
// with the terrain underneath it.
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , deleteNode , void , ( ) , , " deleteNode() " )
2012-09-19 15:15:01 +00:00
{
object - > deleteSelectedNode ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , getMode , const char * , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
return object - > getMode ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , setMode , void , ( const char * mode ) , , " setMode( String mode ) " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
String newMode = ( mode ) ;
2012-09-19 15:15:01 +00:00
object - > setMode ( newMode ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , getNodeWidth , F32 , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
return object - > getNodeWidth ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , setNodeWidth , void , ( F32 width ) , , " " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
object - > setNodeWidth ( width ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , getNodeDepth , F32 , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
return object - > getNodeDepth ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , setNodeDepth , void , ( F32 depth ) , , " " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
object - > setNodeDepth ( depth ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , getNodePosition , Point3F , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
return object - > getNodePosition ( ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , setNodePosition , void , ( Point3F pos ) , , " " )
2012-09-19 15:15:01 +00:00
{
object - > setNodePosition ( pos ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , getNodeNormal , Point3F , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
2014-11-04 03:42:51 +00:00
return object - > getNodeNormal ( ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , setNodeNormal , void , ( Point3F normal ) , , " " )
2012-09-19 15:15:01 +00:00
{
object - > setNodeNormal ( normal ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , setSelectedRoad , void , ( const char * objName ) , ( " " ) , " " )
2012-09-19 15:15:01 +00:00
{
2015-06-06 22:40:49 +00:00
if ( String : : isEmpty ( objName ) )
2012-09-19 15:15:01 +00:00
object - > setSelectedRoad ( NULL ) ;
else
{
MeshRoad * road = NULL ;
2014-11-04 03:42:51 +00:00
if ( Sim : : findObject ( objName , road ) )
2012-09-19 15:15:01 +00:00
object - > setSelectedRoad ( road ) ;
}
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , getSelectedRoad , S32 , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
MeshRoad * road = object - > getSelectedRoad ( ) ;
if ( ! road )
return NULL ;
2014-11-04 03:42:51 +00:00
return road - > getId ( ) ;
2012-09-19 15:15:01 +00:00
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , regenerate , void , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
MeshRoad * road = object - > getSelectedRoad ( ) ;
if ( road )
road - > regenerate ( ) ;
}
2018-04-17 19:01:50 +00:00
DefineEngineMethod ( GuiMeshRoadEditorCtrl , matchTerrainToRoad , void , ( ) , , " " )
2012-09-19 15:15:01 +00:00
{
object - > matchTerrainToRoad ( ) ;
2012-10-11 20:29:39 +00:00
}