summaryrefslogtreecommitdiffstats
path: root/tools/trackeditor/code/nodes
diff options
context:
space:
mode:
authorSvxy <aidan61605@gmail.com>2023-05-31 23:31:32 +0200
committerSvxy <aidan61605@gmail.com>2023-05-31 23:31:32 +0200
commiteb4b3404aa00220d659e532151dab13d642c17a3 (patch)
tree7e1107c4995489a26c4007e41b53ea8d00ab2134 /tools/trackeditor/code/nodes
downloadThe-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.gz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.bz2
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.lz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.xz
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.tar.zst
The-Simpsons-Hit-and-Run-eb4b3404aa00220d659e532151dab13d642c17a3.zip
Diffstat (limited to 'tools/trackeditor/code/nodes')
-rw-r--r--tools/trackeditor/code/nodes/NU.h34
-rw-r--r--tools/trackeditor/code/nodes/fenceline.cpp201
-rw-r--r--tools/trackeditor/code/nodes/fenceline.h30
-rw-r--r--tools/trackeditor/code/nodes/intersection.cpp213
-rw-r--r--tools/trackeditor/code/nodes/intersection.h48
-rw-r--r--tools/trackeditor/code/nodes/pedpath.cpp271
-rw-r--r--tools/trackeditor/code/nodes/pedpath.h30
-rw-r--r--tools/trackeditor/code/nodes/road.cpp409
-rw-r--r--tools/trackeditor/code/nodes/road.h56
-rw-r--r--tools/trackeditor/code/nodes/tiledisplay.cpp212
-rw-r--r--tools/trackeditor/code/nodes/tiledisplay.h35
-rw-r--r--tools/trackeditor/code/nodes/treelineshapenode.cpp1343
-rw-r--r--tools/trackeditor/code/nodes/treelineshapenode.h142
-rw-r--r--tools/trackeditor/code/nodes/walllocator.cpp551
-rw-r--r--tools/trackeditor/code/nodes/walllocator.h69
15 files changed, 3644 insertions, 0 deletions
diff --git a/tools/trackeditor/code/nodes/NU.h b/tools/trackeditor/code/nodes/NU.h
new file mode 100644
index 0000000..d43db8f
--- /dev/null
+++ b/tools/trackeditor/code/nodes/NU.h
@@ -0,0 +1,34 @@
+#include "precompiled/PCH.h"
+
+#ifndef NODE_UTIL_H
+#define NODE_UTIL_H
+
+namespace NODE_UTIL
+{
+ inline void DisableAttributes( MObject& node, bool justY = true )
+ {
+ MFnDagNode fnDagNode( node );
+
+ MObject parent = fnDagNode.parent( 0 );
+ MFnDependencyNode fnParent( parent );
+
+ if ( justY )
+ {
+ MPlug ptyPlug = fnParent.findPlug( MString( "translateY" ) );
+ ptyPlug.setLocked( true );
+ }
+ else
+ {
+ MPlug ptPlug = fnParent.findPlug( MString( "translate" ) );
+ ptPlug.setLocked( true );
+ }
+
+ MPlug spPlug = fnParent.findPlug( MString( "scale" ) );
+ spPlug.setLocked( true );
+
+ MPlug rpPlug = fnParent.findPlug( MString( "rotate" ) );
+ rpPlug.setLocked( true );
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/fenceline.cpp b/tools/trackeditor/code/nodes/fenceline.cpp
new file mode 100644
index 0000000..87c8966
--- /dev/null
+++ b/tools/trackeditor/code/nodes/fenceline.cpp
@@ -0,0 +1,201 @@
+#include "fenceline.h"
+#include "walllocator.h"
+#include "utility/mext.h"
+
+#include <toollib.hpp>
+
+MTypeId FenceLineNode::id( TEConstants::TypeIDPrefix, TEConstants::NodeIDs::FenceLine );
+const char* FenceLineNode::stringId = "FenceLine";
+
+//==============================================================================
+// FenceLineNode::FenceLineNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: FenceLineNode
+//
+//==============================================================================
+FenceLineNode::FenceLineNode() {}
+
+//==============================================================================
+// FenceLineNode::~FenceLineNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: FenceLineNode
+//
+//==============================================================================
+FenceLineNode::~FenceLineNode() {}
+
+//==============================================================================
+// FenceLineNode::creator
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void* FenceLineNode::creator()
+{
+ return new FenceLineNode();
+}
+
+//==============================================================================
+// FenceLineNode::initialize
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus FenceLineNode::initialize()
+{
+ return MS::kSuccess;
+}
+
+//==============================================================================
+// FenceLineNode::postConstructor
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void FenceLineNode::postConstructor()
+{
+ //No moving the fenceline.
+ MPlug lPlug( thisMObject(), localPosition );
+ lPlug.setLocked( true );
+
+ MPlug wPlug( thisMObject(), worldPosition );
+ wPlug.setLocked( true );
+}
+
+//This is how you export one of these.
+//==============================================================================
+// FenceLineNode::Export
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& fenceNode, tlHistory& history )
+//
+// Return: tlDataChunk
+//
+//==============================================================================
+tlDataChunk* FenceLineNode::Export( MObject& fenceNode, tlHistory& history )
+{
+ //This fenceline assumes that there are fences below it.
+ MFnDagNode fnNode( fenceNode );
+
+ if ( fnNode.typeId() == FenceLineNode::id )
+ {
+ //Create a tlDataChunk and return it filled with the appropriate data.
+ tlFenceLineChunk* fenceLine = new tlFenceLineChunk;
+
+ //Go through all it's children and add them to the chunk incrementing the
+ //count.
+
+ unsigned int childCount = 0;
+ MItDag dagIt( MItDag::kDepthFirst, MFn::kLocator );
+
+ MFnDagNode fnDag( fenceNode );
+ MObject fenceT = fnDag.parent( 0 );
+
+ dagIt.reset( fenceT );
+
+ while ( !dagIt.isDone() )
+ {
+ MFnDependencyNode fnNode( dagIt.item() );
+ MTypeId id = fnNode.typeId();
+
+ if ( id == WallLocatorNode::id )
+ {
+ //Export a wall locator;
+ tlDataChunk* newChunk = WallLocatorNode::Export( dagIt.item(), history );
+
+ //Append this to the fence line
+ fenceLine->AppendSubChunk( newChunk );
+
+ ++childCount;
+ }
+
+ dagIt.next();
+ }
+
+ if ( childCount )
+ {
+ fenceLine->SetNumWalls( childCount );
+ }
+ else
+ {
+ delete fenceLine;
+ return NULL;
+ }
+
+ return fenceLine;
+ }
+
+ assert( false );
+ return NULL;
+}
+
+//==============================================================================
+// FenceLineNode::AddWall
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& fenceLine, MObject& wall )
+//
+// Return: void
+//
+//==============================================================================
+void FenceLineNode::AddWall( MObject& fenceLine, MObject& wall )
+{
+ //Test to make sure the fenceLine passed in is an obj, not a transform.
+ MFnDagNode fnDag( fenceLine );
+ if ( fnDag.typeId() == FenceLineNode::id )
+ {
+ MExt::AddChild( fenceLine, wall );
+ }
+ else
+ {
+ if ( fenceLine.apiType() == MFn::kTransform )
+ {
+ //We need to find the FenceLine node that is the child of this transform.
+ unsigned int childCount = fnDag.childCount();
+
+ unsigned int i;
+
+ MObject child;
+ MFnDagNode fnDagChild;
+
+ for ( i = 0; i < childCount; ++i )
+ {
+ child = fnDag.child( i );
+
+ fnDagChild.setObject( child );
+
+ if ( fnDagChild.typeId() == FenceLineNode::id )
+ {
+ //This is the child.
+ MExt::AddChild( child, wall );
+ return;
+ }
+ }
+
+ MExt::DisplayError( "Tried to parent something strange to fenceLine" );
+ assert(false);
+ }
+ }
+}
+
diff --git a/tools/trackeditor/code/nodes/fenceline.h b/tools/trackeditor/code/nodes/fenceline.h
new file mode 100644
index 0000000..d174f13
--- /dev/null
+++ b/tools/trackeditor/code/nodes/fenceline.h
@@ -0,0 +1,30 @@
+#include "precompiled/PCH.h"
+
+
+#ifndef FENCELINE
+#define FENCELINE
+
+#include "main/constants.h"
+
+class tlDataChunk;
+
+class FenceLineNode : public MPxLocatorNode
+{
+public:
+ FenceLineNode();
+ ~FenceLineNode();
+
+ static void* creator();
+ static MStatus initialize();
+ virtual void postConstructor();
+
+ static void AddWall( MObject& fenceLine, MObject& wall );
+
+ //This is how you export one of these.
+ static tlDataChunk* Export( MObject& fenceNode, tlHistory& history );
+
+ static MTypeId id;
+ static const char* stringId;
+};
+
+#endif \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/intersection.cpp b/tools/trackeditor/code/nodes/intersection.cpp
new file mode 100644
index 0000000..f3dce53
--- /dev/null
+++ b/tools/trackeditor/code/nodes/intersection.cpp
@@ -0,0 +1,213 @@
+#include "precompiled/PCH.h"
+
+#include "intersection.h"
+#include "main/constants.h"
+#include "utility/glext.h"
+#include "utility/mext.h"
+#include "utility/nodehelper.h"
+
+#include "utility/transformmatrix.h"
+#include <toollib.hpp>
+
+
+MTypeId IntersectionLocatorNode::id( TEConstants::TypeIDPrefix, TEConstants::NodeIDs::Intersection );
+const char* IntersectionLocatorNode::stringId = "IntersectionLocatorNode";
+
+//Attribute
+const char* IntersectionLocatorNode::TYPE_NAME_LONG = "IntersectionType";
+const char* IntersectionLocatorNode::TYPE_NAME_SHORT = "it";
+MObject IntersectionLocatorNode::mType;
+
+const char* IntersectionLocatorNode::ROAD_LONG = "Roads";
+const char* IntersectionLocatorNode::ROAD_SHORT = "R";
+MObject IntersectionLocatorNode::mRoads;
+
+
+const int IntersectionLocatorNode::ACTIVE_COLOUR = 13;
+const int IntersectionLocatorNode::INACTIVE_COLOUR = 22;
+const float IntersectionLocatorNode::SCALE = 1.0f * TEConstants::Scale;
+
+IntersectionLocatorNode::IntersectionLocatorNode() {};
+IntersectionLocatorNode::~IntersectionLocatorNode() {};
+
+//==============================================================================
+// IntersectionLocatorNode::creator
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void* IntersectionLocatorNode::creator()
+{
+ return new IntersectionLocatorNode();
+}
+
+//==============================================================================
+// IntersectionLocatorNode::initialize
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus IntersectionLocatorNode::initialize()
+{
+ MStatus status;
+ MFnTypedAttribute fnTyped;
+ MFnMessageAttribute fnMessage;
+
+ mType = fnTyped.create( TYPE_NAME_LONG, TYPE_NAME_SHORT, MFnData::kString, MObject::kNullObj, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mType ) );
+
+ mRoads = fnMessage.create( ROAD_LONG, ROAD_SHORT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setReadable( false ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setWritable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setArray( true ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setIndexMatters( false ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mRoads ) );
+
+
+ return MS::kSuccess;
+}
+
+//==============================================================================
+// IntersectionLocatorNode::postConstructor
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void IntersectionLocatorNode::postConstructor()
+{
+}
+
+
+//==============================================================================
+// IntersectionLocatorNode::draw
+//==============================================================================
+// Description: Comment
+//
+// Parameters: draw( M3dView & view,
+// const MDagPath & path,
+// M3dView::DisplayStyle style,
+// M3dView::DisplayStatus status )
+//
+// Return: void
+//
+//==============================================================================
+void IntersectionLocatorNode::draw( M3dView & view,
+ const MDagPath & path,
+ M3dView::DisplayStyle style,
+ M3dView::DisplayStatus status )
+{
+ view.beginGL();
+ glPushAttrib( GL_CURRENT_BIT | GL_LINE_BIT | GL_POLYGON_BIT );
+
+ if ( status == M3dView::kDormant )
+ {
+ int colour = NodeHelper::OverrideNodeColour( thisMObject(), INACTIVE_COLOUR );
+
+ view.setDrawColor( colour, M3dView::kDormantColors );
+ }
+ else
+ {
+ view.setDrawColor( ACTIVE_COLOUR, M3dView::kActiveColors );
+ }
+
+ //Draw a star to represent the locator.
+ GLExt::drawI( TEConstants::Scale );
+ GLExt::drawSphere( SCALE );
+
+ glPopAttrib();
+ view.endGL();
+}
+
+//==============================================================================
+// IntersectionLocatorNode::Export
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& intersectionLocatorNode, tlHistory& history )
+//
+// Return: tlDataChunk
+//
+//==============================================================================
+tlDataChunk* IntersectionLocatorNode::Export( MObject& intersectionLocatorNode,
+ tlHistory& history )
+{
+ MFnDagNode fnNode( intersectionLocatorNode );
+
+ if ( fnNode.typeId() == IntersectionLocatorNode::id )
+ {
+ tlIntersectionChunk* intersectionChunk = new tlIntersectionChunk;
+
+ intersectionChunk->SetName( fnNode.name().asChar() );
+
+ MPoint thisPosition;
+ MExt::GetWorldPosition( &thisPosition, intersectionLocatorNode );
+
+ tlPoint point;
+ point[0] = thisPosition[0] / TEConstants::Scale;
+ point[1] = thisPosition[1] / TEConstants::Scale;
+ point[2] = -thisPosition[2] / TEConstants::Scale; //Maya vs. P3D...
+
+ intersectionChunk->SetCentre( rmt::Vector( point ) );
+
+ //GetScale...
+ MObject transform;
+ transform = fnNode.parent( 0 );
+ MFnTransform fnTransform( transform );
+
+ MDagPath dagPath;
+ MExt::FindDagNodeByName( &dagPath, fnTransform.name() );
+ TransformMatrix tm( dagPath );
+
+ double scaleX;
+ fnTransform.findPlug( MString( "sx" ) ).getValue( scaleX );
+
+ intersectionChunk->SetRadius( (float)scaleX );
+
+ MPlug typePlug = fnNode.findPlug( mType );
+ MString type;
+ typePlug.getValue( type );
+
+ if ( MString("NoStop") == type )
+ {
+ intersectionChunk->SetType( 0 );
+ }
+ else if ( MString("NWay") == type )
+ {
+ intersectionChunk->SetType( 1 );
+ }
+ else if ( MString("FourWay") == type )
+ {
+ intersectionChunk->SetType( 2 );
+ }
+ else if ( MString("NoStopN") == type )
+ {
+ intersectionChunk->SetType( 3 );
+ }
+ else //if ( MString("NWay") == type )
+ {
+ intersectionChunk->SetType( 4 );
+ }
+
+
+ return intersectionChunk;
+ }
+
+ assert( false );
+ return NULL;
+} \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/intersection.h b/tools/trackeditor/code/nodes/intersection.h
new file mode 100644
index 0000000..fade595
--- /dev/null
+++ b/tools/trackeditor/code/nodes/intersection.h
@@ -0,0 +1,48 @@
+#include "precompiled/PCH.h"
+
+#ifndef INTERSECTION_LOCATOR
+#define INTERSECTION_LOCATOR
+
+
+#include "main/constants.h"
+
+class tlDataChunk;
+
+class IntersectionLocatorNode : public MPxLocatorNode
+{
+public:
+ IntersectionLocatorNode();
+ ~IntersectionLocatorNode();
+
+ static void* creator();
+
+ virtual void draw( M3dView& view,
+ const MDagPath& path,
+ M3dView::DisplayStyle displayStyle,
+ M3dView::DisplayStatus displayStatus
+ );
+ static MStatus initialize();
+ virtual void postConstructor();
+
+ //This is how you export one of these.
+ static tlDataChunk* Export( MObject& intersectionLocatorNode, tlHistory& history );
+
+ static const char* TYPE_NAME_LONG;
+ static const char* TYPE_NAME_SHORT;
+ static MObject mType;
+
+ static const char* ROAD_LONG;
+ static const char* ROAD_SHORT;
+ static MObject mRoads; //This is an out message to all the roads this intersection connects.
+
+ static MTypeId id;
+ static const char* stringId;
+
+private:
+
+ static const int ACTIVE_COLOUR;
+ static const int INACTIVE_COLOUR;
+ static const float SCALE;
+};
+
+#endif \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/pedpath.cpp b/tools/trackeditor/code/nodes/pedpath.cpp
new file mode 100644
index 0000000..682f004
--- /dev/null
+++ b/tools/trackeditor/code/nodes/pedpath.cpp
@@ -0,0 +1,271 @@
+#include "pedpath.h"
+#include "walllocator.h"
+#include "utility/mext.h"
+
+#include <toollib.hpp>
+
+MTypeId PedPathNode::id( TEConstants::TypeIDPrefix, TEConstants::NodeIDs::PedPath );
+const char* PedPathNode::stringId = "PedPath";
+
+//==============================================================================
+// PedPathNode::PedPathNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PedPathNode
+//
+//==============================================================================
+PedPathNode::PedPathNode() {}
+
+//==============================================================================
+// PedPathNode::~PedPathNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PedPathNode
+//
+//==============================================================================
+PedPathNode::~PedPathNode() {}
+
+//==============================================================================
+// PedPathNode::creator
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void* PedPathNode::creator()
+{
+ return new PedPathNode();
+}
+
+//==============================================================================
+// PedPathNode::initialize
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus PedPathNode::initialize()
+{
+ return MS::kSuccess;
+}
+
+//==============================================================================
+// PedPathNode::postConstructor
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void PedPathNode::postConstructor()
+{
+ //No moving the pedpath.
+ MPlug lPlug( thisMObject(), localPosition );
+ lPlug.setLocked( true );
+
+ MPlug wPlug( thisMObject(), worldPosition );
+ wPlug.setLocked( true );
+}
+
+//This is how you export one of these.
+//==============================================================================
+// PedPathNode::Export
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& pedNode, tlHistory& history )
+//
+// Return: tlDataChunk
+//
+//==============================================================================
+tlDataChunk* PedPathNode::Export( MObject& pedNode, tlHistory& history )
+{
+ //This fenceline assumes that there are fences below it.
+ MFnDagNode fnNode( pedNode );
+
+ if ( fnNode.typeId() == PedPathNode::id )
+ {
+ //Create a tlDataChunk and return it filled with the appropriate data.
+ tlPedpathChunk* pedPath = new tlPedpathChunk;
+
+ //Go through all it's children and add them to the chunk incrementing the
+ //count.
+
+ unsigned int childCount = 0;
+ MItDag dagIt( MItDag::kDepthFirst, MFn::kLocator );
+
+ MFnDagNode fnDag( pedNode );
+ MObject fenceT = fnDag.parent( 0 );
+
+ dagIt.reset( fenceT );
+
+ tlDataChunk tempChunk;
+
+ while ( !dagIt.isDone() )
+ {
+ MFnDependencyNode fnNode( dagIt.item() );
+ MTypeId id = fnNode.typeId();
+
+ if ( id == WallLocatorNode::id )
+ {
+ //Export a wall locator;
+ tlWallChunk* newChunk = reinterpret_cast<tlWallChunk*>(WallLocatorNode::Export( dagIt.item(), history ));
+
+ //Append this to the fence line
+ tempChunk.AppendSubChunk( newChunk );
+
+ ++childCount;
+ }
+
+ dagIt.next();
+ }
+
+ if ( childCount )
+ {
+ tlPoint* points = new tlPoint[childCount + 1];
+
+ unsigned int i;
+// for ( i = 0; i < tempChunk.SubChunkCount(); i++ )
+// {
+// points[i] = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( i ))->GetStart();
+//
+// if ( i == tempChunk.SubChunkCount() - 1 )
+// {
+// points[childCount] = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( i ))->GetEnd();
+// }
+// }
+
+ //Okay, we need to order the points... If there was a split, the points will
+ //be badly ordered.
+ points[0] = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( 0 ))->GetStart(); //First point is always good.
+
+ //This is the testing point for the loop below.
+ tlPoint testPoint = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( 0 ))->GetEnd();
+ tempChunk.RemoveSubChunk( 0 );
+
+ unsigned int remainingChunks = childCount - 1;
+ unsigned int foundCount = 1;
+
+
+ while ( foundCount < childCount )
+ {
+ bool found = false;
+ unsigned int chunkIndex = 0;
+ for ( i = 0; i < remainingChunks; ++i )
+ {
+ if ( !found && reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( i ))->GetStart() == testPoint )
+ {
+ points[foundCount] = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( i ))->GetStart();
+ found = true;
+ foundCount++;
+
+ //heheh
+ if ( foundCount == childCount )
+ {
+ points[foundCount] = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( i ))->GetEnd();
+ foundCount++;
+ }
+
+ chunkIndex = i;
+ }
+ }
+
+ if ( found )
+ {
+ testPoint = reinterpret_cast<tlWallChunk*>(tempChunk.GetSubChunk( chunkIndex ))->GetEnd();
+
+ tempChunk.RemoveSubChunk( chunkIndex );
+ remainingChunks--;
+ }
+ else
+ {
+ MExt::DisplayError("WHOA! Big error, get Cary!!!! Looks like ped paths are screwy..");
+ break;
+ }
+ }
+
+
+
+ pedPath->SetPoints( points, childCount + 1 );
+ pedPath->SetNumPoints( childCount + 1 );
+
+ delete[] points;
+ points = NULL;
+ }
+ else
+ {
+ delete pedPath;
+ return NULL;
+ }
+
+ return pedPath;
+ }
+
+ assert( false );
+ return NULL;
+}
+
+//==============================================================================
+// PedPathNode::AddWall
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& pedPath, MObject& wall )
+//
+// Return: void
+//
+//==============================================================================
+void PedPathNode::AddWall( MObject& pedPath, MObject& wall )
+{
+ //Test to make sure the fenceLine passed in is an obj, not a transform.
+ MFnDagNode fnDag( pedPath );
+ if ( fnDag.typeId() == PedPathNode::id )
+ {
+ MExt::AddChild( pedPath, wall );
+ }
+ else
+ {
+ if ( pedPath.apiType() == MFn::kTransform )
+ {
+ //We need to find the FenceLine node that is the child of this transform.
+ unsigned int childCount = fnDag.childCount();
+
+ unsigned int i;
+
+ MObject child;
+ MFnDagNode fnDagChild;
+
+ for ( i = 0; i < childCount; ++i )
+ {
+ child = fnDag.child( i );
+
+ fnDagChild.setObject( child );
+
+ if ( fnDagChild.typeId() == PedPathNode::id )
+ {
+ //This is the child.
+ MExt::AddChild( child, wall );
+ return;
+ }
+ }
+
+ MExt::DisplayError( "Tried to parent something strange to pedPath" );
+ assert(false);
+ }
+ }
+}
+
diff --git a/tools/trackeditor/code/nodes/pedpath.h b/tools/trackeditor/code/nodes/pedpath.h
new file mode 100644
index 0000000..be8bf48
--- /dev/null
+++ b/tools/trackeditor/code/nodes/pedpath.h
@@ -0,0 +1,30 @@
+#include "precompiled/PCH.h"
+
+
+#ifndef PED_PATH
+#define PED_PATH
+
+#include "main/constants.h"
+
+class tlDataChunk;
+
+class PedPathNode : public MPxLocatorNode
+{
+public:
+ PedPathNode();
+ ~PedPathNode();
+
+ static void* creator();
+ static MStatus initialize();
+ virtual void postConstructor();
+
+ static void AddWall( MObject& fenceLine, MObject& wall );
+
+ //This is how you export one of these.
+ static tlDataChunk* Export( MObject& fenceNode, tlHistory& history );
+
+ static MTypeId id;
+ static const char* stringId;
+};
+
+#endif //PED_PATH \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/road.cpp b/tools/trackeditor/code/nodes/road.cpp
new file mode 100644
index 0000000..c414d84
--- /dev/null
+++ b/tools/trackeditor/code/nodes/road.cpp
@@ -0,0 +1,409 @@
+#include "road.h"
+#include "utility/mext.h"
+#include "utility/transformmatrix.h"
+#include <toollib.hpp>
+
+const tlPoint MVectorTotlPoint( const MVector& vector )
+{
+ tlPoint point;
+ point[0] = vector[0];
+ point[1] = vector[1];
+ point[2] = vector[2];
+
+ return point;
+}
+
+MTypeId RoadNode::id( TEConstants::TypeIDPrefix, TEConstants::NodeIDs::Road );
+const char* RoadNode::stringId = "RoadNode";
+
+const char* RoadNode::ROAD_SEG_NAME_SHORT = "RoadSegments";
+const char* RoadNode::ROAD_SEG_NAME_LONG = "rs";
+MObject RoadNode::mRoadSegments;
+
+const char* RoadNode::INTERSECTION_START_SHORT = "is";
+const char* RoadNode::INTERSECTION_START_LONG = "IntersectionStart";
+MObject RoadNode::mIntersectionStart;
+
+const char* RoadNode::INTERSECTION_END_SHORT = "ie";
+const char* RoadNode::INTERSECTION_END_LONG = "IntersectionEnd";
+MObject RoadNode::mIntersectionEnd;
+
+const char* RoadNode::DENSITY_SHORT = "den";
+const char* RoadNode::DENSITY_LONG = "density";
+MObject RoadNode::mDensity;
+
+const char* RoadNode::SPEED_SHORT = "spd";
+const char* RoadNode::SPEED_LONG = "speed";
+MObject RoadNode::mSpeed;
+
+const char* RoadNode::DIFF_SHORT = "diff";
+const char* RoadNode::DIFF_LONG = "difficulty";
+MObject RoadNode::mDiff;
+
+const char* RoadNode::SHORTCUT_SHORT = "shct";
+const char* RoadNode::SHORTCUT_LONG = "shortCut";
+MObject RoadNode::mShortcut;
+
+
+
+//==============================================================================
+// RoadNode::RoadNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: RoadNode
+//
+//==============================================================================
+RoadNode::RoadNode() {}
+
+//==============================================================================
+// RoadNode::~RoadNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: RoadNode
+//
+//==============================================================================
+RoadNode::~RoadNode() {}
+
+//==============================================================================
+// RoadNode::creator
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void* RoadNode::creator()
+{
+ return new RoadNode();
+}
+
+//==============================================================================
+// RoadNode::initialize
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus RoadNode::initialize()
+{
+ MStatus status;
+ MFnMessageAttribute fnMessage;
+
+ mRoadSegments = fnMessage.create( ROAD_SEG_NAME_LONG, ROAD_SEG_NAME_SHORT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setReadable( false ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setWritable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setArray( true ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setIndexMatters( false ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mRoadSegments ) );
+
+ mIntersectionStart = fnMessage.create( INTERSECTION_START_LONG, INTERSECTION_START_SHORT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mIntersectionStart ) );
+
+ mIntersectionEnd = fnMessage.create( INTERSECTION_END_LONG, INTERSECTION_END_SHORT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mIntersectionEnd ) );
+
+ MFnNumericAttribute fnNumeric;
+ mDensity = fnNumeric.create( DENSITY_LONG, DENSITY_SHORT, MFnNumericData::kInt, 5, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setReadable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setWritable( true ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mDensity ) );
+
+ mSpeed = fnNumeric.create( SPEED_LONG, SPEED_SHORT, MFnNumericData::kInt, 50, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setReadable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setWritable( true ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mSpeed ) );
+
+ mShortcut = fnNumeric.create( SHORTCUT_LONG, SHORTCUT_SHORT, MFnNumericData::kBoolean, false, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setReadable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setWritable( true ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mShortcut ) );
+
+ mDiff = fnNumeric.create( DIFF_LONG, DIFF_SHORT, MFnNumericData::kInt, 0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setReadable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnNumeric.setWritable( true ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mDiff ) );
+
+
+ return MS::kSuccess;
+}
+
+//==============================================================================
+// RoadNode::postConstructor
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void RoadNode::postConstructor()
+{
+ //No moving the road.
+ MPlug lPlug( thisMObject(), localPosition );
+ lPlug.setLocked( true );
+
+ MPlug wPlug( thisMObject(), worldPosition );
+ wPlug.setLocked( true );
+}
+
+//This is how you export one of these.
+//=============================================================================
+// RoadNode::Export
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& roadNode, tlHistory& history, tlDataChunk* outChunk )
+//
+// Return: tlDataChunk
+//
+//=============================================================================
+tlDataChunk* RoadNode::Export( MObject& roadNode, tlHistory& history, tlDataChunk* outChunk ) //I think this is hackish.
+{
+ //This fenceline assumes that there are fences below it.
+ MFnDagNode fnNode( roadNode );
+
+ if ( fnNode.typeId() == RoadNode::id )
+ {
+ MFnDependencyNode fnTempNode;
+
+ tlRoadChunk* roadChunk = new tlRoadChunk;
+
+ roadChunk->SetName( fnNode.name().asChar() );
+ roadChunk->SetType( 0 );
+
+ //Get the intersections.
+
+ //START
+ MPlug intersectionPlug = fnNode.findPlug( mIntersectionStart );
+ MPlugArray source, dest;
+ MExt::ResolveConnections( &source, &dest, intersectionPlug, AS_SOURCE );
+
+ if ( dest.length() == 0 )
+ {
+ MExt::DisplayError( "ERROR: Road %s has no start intersection!", fnNode.name().asChar() );
+ delete roadChunk;
+ return NULL;
+ }
+
+ fnTempNode.setObject( dest[0].node() );
+ roadChunk->SetStartIntersection( fnTempNode.name().asChar() );
+
+ //END
+ intersectionPlug = fnNode.findPlug( mIntersectionEnd );
+ MExt::ResolveConnections( &source, &dest, intersectionPlug, AS_SOURCE );
+
+ if ( dest.length() == 0 )
+ {
+ MExt::DisplayError( "ERROR: Road %s has no end intersection!", fnNode.name().asChar() );
+ delete roadChunk;
+ return NULL;
+ }
+
+ fnTempNode.setObject( dest[0].node() );
+ roadChunk->SetEndIntersection( fnTempNode.name().asChar() );
+
+ int density;
+ fnNode.findPlug( mDensity ).getValue( density );
+ roadChunk->SetDensity( density );
+
+ int speed;
+ fnNode.findPlug( mSpeed ).getValue( speed );
+ if ( speed > 255 || speed < 0 )
+ {
+ speed = 255; //No need to mask, but what the hell.
+ }
+
+ int diff;
+ fnNode.findPlug( mDiff ).getValue( diff );
+ if ( diff > 255 || diff < 0 )
+ {
+ diff = 255;
+ }
+
+ bool sc;
+ fnNode.findPlug( mShortcut ).getValue( sc );
+
+
+ //This works differently now.
+ //8 bits - speed
+ //8 bits - difficulty level
+ //1 bit - id Short cut
+ //15 bits - saved for later
+ const int SPEED_MASK = 0x000000FF;
+ const int DIFFIC_MASK = 0x0000FF00;
+ const int SC_MASK = 0x00010000;
+
+ roadChunk->SetSpeed( speed | ( diff << 8 ) | ( sc ? SC_MASK : 0 ) );
+
+ //New set all the road segment chunks.
+ MPlug roadSegPlug = fnNode.findPlug( mRoadSegments );
+ MExt::ResolveConnections( &source, &dest, roadSegPlug, AS_DEST );
+
+ MDagPath dagPath;
+ MPlug tempPlug;
+ tlRoadSegmentChunk* roadSegChunk = NULL;
+ tlRoadSegmentDataChunk* roadSegDataChunk = NULL;
+ unsigned int i;
+ for ( i = 0; i < source.length(); ++i )
+ {
+ //Create new tlRoadSegmentChunks
+ roadSegChunk = new tlRoadSegmentChunk;
+ roadSegDataChunk = new tlRoadSegmentDataChunk;
+
+ fnTempNode.setObject( source[ i ].node() );
+ MExt::FindDagNodeByName( &dagPath, fnTempNode.name() );
+ MFnMesh fnMesh( dagPath );
+
+ roadSegChunk->SetName( fnMesh.name().asChar() );
+ roadSegChunk->SetRoadSegmentData( fnMesh.name().asChar() );
+ roadSegDataChunk->SetName( fnMesh.name().asChar() );
+
+ tempPlug = fnMesh.findPlug( MString( "teType" ) );
+ int type;
+ tempPlug.getValue( type );
+ roadSegDataChunk->SetType( type < 0 ? 0 : type );
+
+ tempPlug = fnMesh.findPlug( MString( "teLanes" ) );
+ int lanes;
+ tempPlug.getValue( lanes );
+ roadSegDataChunk->SetNumLanes( lanes );
+
+ tempPlug = fnMesh.findPlug( MString( "teShoulder" ) );
+ bool shoulder;
+ tempPlug.getValue( shoulder );
+ roadSegDataChunk->SetHasShoulder( shoulder ? 1 : 0 );
+
+ MPointArray points;
+ fnMesh.getPoints( points, MSpace::kWorld );
+
+ //ORIGIN
+ tempPlug = fnMesh.findPlug( MString( "teOrigin" ) );
+ int origin;
+ tempPlug.getValue( origin );
+ assert( origin >= 0 );
+ MPoint orig;
+ orig[ 0 ] = points[origin][0] / TEConstants::Scale;
+ orig[ 1 ] = points[origin][1] / TEConstants::Scale;
+ orig[ 2 ] = -points[origin][2] / TEConstants::Scale;
+
+ //DIRECTION
+ tempPlug = fnMesh.findPlug( MString( "teRoad" ) );
+ int direction;
+ tempPlug.getValue( direction );
+ assert( direction >= 0 );
+ MPoint dir;
+ dir[ 0 ] = points[direction][0] / TEConstants::Scale;
+ dir[ 1 ] = points[direction][1] / TEConstants::Scale;
+ dir[ 2 ] = -points[direction][2] / TEConstants::Scale;
+
+ MVector vDir = dir - orig;
+ roadSegDataChunk->SetDirection( MVectorTotlPoint( vDir ) );
+
+ //TOP
+ tempPlug = fnMesh.findPlug( MString( "teTop" ) );
+ int top;
+ tempPlug.getValue( top );
+ assert( top >= 0 );
+ MPoint topPoint;
+ topPoint[ 0 ] = points[top][0] / TEConstants::Scale;
+ topPoint[ 1 ] = points[top][1] / TEConstants::Scale;
+ topPoint[ 2 ] = -points[top][2] / TEConstants::Scale;
+
+ MVector vTop = topPoint - orig;
+ roadSegDataChunk->SetTop( MVectorTotlPoint( vTop ) );
+
+ //BOTTOM
+ tempPlug = fnMesh.findPlug( MString( "teBottom" ) );
+ int bottom;
+ tempPlug.getValue( bottom );
+ assert( bottom >= 0 );
+ MPoint bot;
+ bot[ 0 ] = points[bottom][0] / TEConstants::Scale;
+ bot[ 1 ] = points[bottom][1] / TEConstants::Scale;
+ bot[ 2 ] = -points[bottom][2] / TEConstants::Scale;
+
+ MVector vBottom = bot - orig;
+ roadSegDataChunk->SetBottom( MVectorTotlPoint( vBottom ) );
+
+
+ //Lets to the SCALE and ROTATION of the segment.
+ MPointArray worldPoints;
+ fnMesh.getPoints( worldPoints, MSpace::kWorld );
+
+ //WORLD ORIGIN
+ MPoint worldOrig;
+ worldOrig[ 0 ] = worldPoints[origin][0] / TEConstants::Scale;
+ worldOrig[ 1 ] = worldPoints[origin][1] / TEConstants::Scale;
+ worldOrig[ 2 ] = -worldPoints[origin][2] / TEConstants::Scale;
+
+ //Get the parent transform matrix for the mesh.
+ MObject meshTransformObj = fnMesh.parent( 0 );
+ MFnTransform fnTransform( meshTransformObj );
+ MExt::FindDagNodeByName( &dagPath, fnTransform.name() );
+ TransformMatrix tm( dagPath );
+
+ tlMatrix hmatrix;
+ tm.GetHierarchyMatrixLHS( hmatrix );
+ //Make this p3d friendly...
+ hmatrix.element[3][0] /= TEConstants::Scale;
+ hmatrix.element[3][1] /= TEConstants::Scale;
+ hmatrix.element[3][2] /= TEConstants::Scale;
+
+ if ( hmatrix.element[3][0] == 0.0f &&
+ hmatrix.element[3][1] == 0.0f &&
+ hmatrix.element[3][2] == 0.0f )
+ {
+ //This could be a frozen
+ MExt::DisplayWarning( "%s could have it's transforms frozen! NOT GOOD! Forced to assume object was built at origin.", fnMesh.name().asChar() );
+
+ hmatrix.element[3][0] = worldOrig.x;
+ hmatrix.element[3][1] = worldOrig.y;
+ hmatrix.element[3][2] = worldOrig.z;
+ }
+
+ tlMatrix smatrix;
+ tm.GetScaleMatrixLHS( smatrix );
+
+ //MATRICIES
+ roadSegChunk->SetHierarchyMatrix( hmatrix );
+ roadSegChunk->SetScaleMatrix( smatrix );
+
+ //DONE
+ roadChunk->AppendSubChunk( roadSegChunk );
+
+ outChunk->AppendSubChunk( roadSegDataChunk );
+ }
+
+ return roadChunk;
+ }
+
+ assert( false );
+ return NULL;
+}
+
diff --git a/tools/trackeditor/code/nodes/road.h b/tools/trackeditor/code/nodes/road.h
new file mode 100644
index 0000000..a601433
--- /dev/null
+++ b/tools/trackeditor/code/nodes/road.h
@@ -0,0 +1,56 @@
+#include "precompiled/PCH.h"
+
+
+#ifndef ROAD_H
+#define ROAD_H
+
+#include "main/constants.h"
+
+class tlDataChunk;
+
+class RoadNode : public MPxLocatorNode
+{
+public:
+ RoadNode();
+ ~RoadNode();
+
+ static void* creator();
+ static MStatus initialize();
+ virtual void postConstructor();
+
+ //This is how you export one of these.
+ static tlDataChunk* Export( MObject& roadNode, tlHistory& history, tlDataChunk* outChunk );
+
+ static MTypeId id;
+ static const char* stringId;
+
+ static const char* ROAD_SEG_NAME_SHORT;
+ static const char* ROAD_SEG_NAME_LONG;
+ static MObject mRoadSegments;
+
+ static const char* INTERSECTION_START_SHORT;
+ static const char* INTERSECTION_START_LONG;
+ static MObject mIntersectionStart;
+
+ static const char* INTERSECTION_END_SHORT;
+ static const char* INTERSECTION_END_LONG;
+ static MObject mIntersectionEnd;
+
+ static const char* DENSITY_SHORT;
+ static const char* DENSITY_LONG;
+ static MObject mDensity;
+
+ static const char* SPEED_SHORT;
+ static const char* SPEED_LONG;
+ static MObject mSpeed;
+
+ static const char* DIFF_SHORT;
+ static const char* DIFF_LONG;
+ static MObject mDiff;
+
+ static const char* SHORTCUT_SHORT;
+ static const char* SHORTCUT_LONG;
+ static MObject mShortcut;
+};
+
+#endif \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/tiledisplay.cpp b/tools/trackeditor/code/nodes/tiledisplay.cpp
new file mode 100644
index 0000000..e26f7f9
--- /dev/null
+++ b/tools/trackeditor/code/nodes/tiledisplay.cpp
@@ -0,0 +1,212 @@
+#include "tiledisplay.h"
+#include "main/constants.h"
+#include "main/trackeditor.h"
+#include "utility/glext.h"
+#include "utility/mext.h"
+
+MTypeId TileDisplayNode::id( TEConstants::TypeIDPrefix, TEConstants::NodeIDs::TileDisplay );
+const char* TileDisplayNode::stringId = "TileDisplayNode";
+
+const int TileDisplayNode::ORIGIN_COLOUR = 10;
+const int TileDisplayNode::ROAD_COLOUR = 11;
+const int TileDisplayNode::TOP_COLOUR = 12;
+const int TileDisplayNode::BOTTOM_COLOUR = 13;
+const int TileDisplayNode::LANE_COLOUR = 4;
+const float TileDisplayNode::SCALE = 1.0f * TEConstants::Scale;
+const float TileDisplayNode::LINE_WIDTH = 6.0f;
+const float TileDisplayNode::Y_OFFSET = 0.5f;
+
+TileDisplayNode::TileDisplayNode() {};
+
+TileDisplayNode::~TileDisplayNode() {};
+
+//==============================================================================
+// TileDisplayNode::creator
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void* TileDisplayNode::creator()
+{
+ return new TileDisplayNode();
+}
+//==============================================================================
+// TileDisplayNode::draw
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( M3dView& view,
+// const MDagPath& path,
+// M3dView::DisplayStyle displayStyle,
+// M3dView::DisplayStatus displayStatus )
+//
+//
+// Return: void
+//
+//==============================================================================
+void TileDisplayNode::draw( M3dView& view,
+ const MDagPath& path,
+ M3dView::DisplayStyle displayStyle,
+ M3dView::DisplayStatus displayStatus
+ )
+{
+ if ( TrackEditor::GetEditMode() != TrackEditor::OFF )
+ {
+ //Display the arrows for the selected mesh.
+ MStatus status;
+ MSelectionList activeList;
+ MGlobal::getActiveSelectionList(activeList);
+
+ MItSelectionList iter( activeList, MFn::kMesh, &status);
+
+ unsigned int count = activeList.length();
+
+ //Draw
+ view.beginGL();
+ glPushAttrib( GL_CURRENT_BIT | GL_LINE_BIT | GL_POLYGON_BIT );
+
+ while ( !iter.isDone() )
+ {
+ MDagPath meshDagPath;
+ iter.getDagPath( meshDagPath );
+
+ MFnMesh fnMesh( meshDagPath );
+
+ MPlug teOriginPlug = fnMesh.findPlug( MString("teOrigin"), &status );
+
+ if ( status == MStatus::kSuccess )
+ {
+ //This is a mesh of the appropriate type
+ //Get all the vertices that are set and display arrows for them.
+ int originVert;
+ teOriginPlug.getValue( originVert );
+
+ int roadVert;
+ MPlug teRoadPlug = fnMesh.findPlug( MString("teRoad"), &status );
+ assert( status );
+ teRoadPlug.getValue( roadVert );
+
+ int topVert;
+ MPlug teTopPlug = fnMesh.findPlug( MString("teTop"), &status );
+ assert( status );
+ teTopPlug.getValue( topVert );
+
+ int bottomVert;
+ MPlug teBottomPlug = fnMesh.findPlug( MString("teBottom"), &status );
+ assert( status );
+ teBottomPlug.getValue( bottomVert );
+
+ int numLanes;
+ MPlug teLanesPlug = fnMesh.findPlug( MString("teLanes"), &status );
+ assert( status );
+ teLanesPlug.getValue( numLanes );
+
+ if ( numLanes < 1 )
+ {
+ MExt::DisplayError( "The mesh %s has a road with no lanes!", fnMesh.name().asChar() );
+ }
+
+ MPoint teOriginPoint;
+ fnMesh.getPoint( originVert, teOriginPoint, MSpace::kWorld );
+ teOriginPoint[1] += Y_OFFSET;
+
+ MPoint teRoadPoint;
+ fnMesh.getPoint( roadVert, teRoadPoint, MSpace::kWorld );
+ teRoadPoint[1] += Y_OFFSET;
+
+ MPoint teTopPoint;
+ fnMesh.getPoint( topVert, teTopPoint, MSpace::kWorld );
+ teTopPoint[1] += Y_OFFSET;
+
+ MPoint teBottomPoint;
+ fnMesh.getPoint( bottomVert, teBottomPoint, MSpace::kWorld );
+ teBottomPoint[1] += Y_OFFSET;
+
+ //When we are in render mode, we draw the lines.
+ //If this was in GL_SELECTION_MODE, we would not draw the lines, so they won't interfere
+ //with selection.
+ GLint value;
+ glGetIntegerv( GL_RENDER_MODE, &value );
+
+ if ( (value == GL_RENDER) )
+ {
+ if ( originVert >= 0 )
+ {
+ view.setDrawColor( ORIGIN_COLOUR, M3dView::kActiveColors );
+ GLExt::drawSphere( 0.5f * SCALE, teOriginPoint );
+ }
+
+ if ( roadVert >= 0 )
+ {
+ view.setDrawColor( ROAD_COLOUR, M3dView::kActiveColors );
+ GLExt::drawSphere( 0.5f * SCALE, teRoadPoint );
+ GLExt::drawLine( teOriginPoint, teRoadPoint, LINE_WIDTH );
+ }
+
+ if ( topVert >= 0 )
+ {
+ view.setDrawColor( TOP_COLOUR, M3dView::kActiveColors );
+ GLExt::drawSphere( 0.5f * SCALE, teTopPoint );
+ GLExt::drawLine( teRoadPoint, teTopPoint, LINE_WIDTH );
+ }
+
+ if ( bottomVert >= 0 )
+ {
+ view.setDrawColor( BOTTOM_COLOUR, M3dView::kActiveColors );
+ GLExt::drawSphere( 0.5f * SCALE, teBottomPoint );
+ GLExt::drawLine( teOriginPoint, teBottomPoint, LINE_WIDTH );
+ }
+
+ if ( numLanes > 0 )
+ {
+ MVector scaledVector0, scaledVector1;
+ scaledVector0 = teBottomPoint - teOriginPoint;
+ scaledVector0 /= numLanes;
+ scaledVector0 /= 2;
+
+ scaledVector1 = teTopPoint - teRoadPoint;
+ scaledVector1 /= numLanes;
+ scaledVector1 /= 2;
+
+ unsigned int i;
+ for( i = 0; i < numLanes; ++i )
+ {
+ MPoint p0, p1;
+
+ p0 = teOriginPoint + scaledVector0 + (scaledVector0 * i * 2);
+ p1 = teRoadPoint + scaledVector1 + (scaledVector1 * i * 2);
+
+ view.setDrawColor( LANE_COLOUR, M3dView::kActiveColors );
+ GLExt::drawArrow( p0, p1, LINE_WIDTH );
+ }
+ }
+ }
+ }
+
+ iter.next();
+ }
+
+ glPopAttrib();
+ view.endGL();
+ }
+}
+
+//==============================================================================
+// TileDisplayNode::initialize
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus TileDisplayNode::initialize()
+{
+ return MStatus::kSuccess;
+}
+
diff --git a/tools/trackeditor/code/nodes/tiledisplay.h b/tools/trackeditor/code/nodes/tiledisplay.h
new file mode 100644
index 0000000..631778a
--- /dev/null
+++ b/tools/trackeditor/code/nodes/tiledisplay.h
@@ -0,0 +1,35 @@
+#include "precompiled/PCH.h"
+
+
+#ifndef TILE_DSIPLAY_H
+#define TILE_DSIPLAY_H
+
+class TileDisplayNode : public MPxLocatorNode
+{
+public:
+ TileDisplayNode();
+ ~TileDisplayNode();
+
+ static void* creator();
+ virtual void draw( M3dView& view,
+ const MDagPath& path,
+ M3dView::DisplayStyle displayStyle,
+ M3dView::DisplayStatus displayStatus
+ );
+ static MStatus initialize();
+
+ static MTypeId id;
+ static const char* stringId;
+
+private:
+ static const int ORIGIN_COLOUR;
+ static const int ROAD_COLOUR;
+ static const int TOP_COLOUR;
+ static const int BOTTOM_COLOUR;
+ static const int LANE_COLOUR;
+ static const float SCALE;
+ static const float LINE_WIDTH;
+ static const float Y_OFFSET;
+};
+
+#endif \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/treelineshapenode.cpp b/tools/trackeditor/code/nodes/treelineshapenode.cpp
new file mode 100644
index 0000000..701ab9c
--- /dev/null
+++ b/tools/trackeditor/code/nodes/treelineshapenode.cpp
@@ -0,0 +1,1343 @@
+#include <windows.h>
+#include <GL/glu.h>
+
+#include <math.h>
+
+#include <radmath/radmath.hpp>
+
+#include "nodes/treelineshapenode.h"
+#include "utility/mext.h"
+#include "utility/mui.h"
+#include "main/shapeconstants.h"
+#include "main/constants.h"
+#include "main/trackeditor.h"
+
+namespace TETreeLine
+{
+
+#define DORMANT_VERTEX_COLOR 8 // purple
+#define ACTIVE_VERTEX_COLOR 16 // yellow
+
+// Vertex point size
+//
+#define POINT_SIZE 2.0
+
+void SetQuadricDrawStyle (GLUquadricObj* qobj, int token)
+{
+ if ((token==SMOOTH_SHADED)||(token==FLAT_SHADED))
+ {
+ gluQuadricNormals( qobj, GLU_SMOOTH );
+ gluQuadricTexture( qobj, true );
+ gluQuadricDrawStyle( qobj, GLU_FILL );
+ }
+ else
+ {
+ gluQuadricDrawStyle( qobj, GLU_LINE );
+ }
+}
+
+void p3dBaseShapeUI::getDrawRequestsWireframe( MDrawRequest& request, const MDrawInfo& info )
+{
+ request.setToken( WIREFRAME );
+
+ M3dView::DisplayStatus displayStatus = info.displayStatus();
+ M3dView::ColorTable activeColorTable = M3dView::kActiveColors;
+ M3dView::ColorTable dormantColorTable = M3dView::kDormantColors;
+ switch ( displayStatus )
+ {
+ case M3dView::kLead :
+ request.setColor( lead_color, activeColorTable );
+ break;
+ case M3dView::kActive :
+ request.setColor( active_color, activeColorTable );
+ break;
+ case M3dView::kActiveAffected :
+ request.setColor( active_affected_color, activeColorTable );
+ break;
+ case M3dView::kDormant :
+ request.setColor( dormant_color, dormantColorTable );
+ break;
+ case M3dView::kHilite :
+ request.setColor( hilite_color, activeColorTable );
+ break;
+ }
+}
+
+void p3dBaseShapeUI::getDrawRequestsShaded( MDrawRequest& request, const MDrawInfo& info,
+ MDrawRequestQueue& queue, MDrawData& data )
+{
+ // Need to get the material info
+ //
+ MDagPath path = info.multiPath(); // path to your dag object
+ M3dView view = info.view();; // view to draw to
+ MMaterial material = MPxSurfaceShapeUI::material( path );
+ M3dView::DisplayStatus displayStatus = info.displayStatus();
+
+ // Evaluate the material and if necessary, the texture.
+ //
+ if ( ! material.evaluateMaterial( view, path ) )
+ {
+ MExt::DisplayError( "Could not evaluate\n" );
+ }
+
+ if ( material.materialIsTextured() )
+ {
+ material.evaluateTexture( data );
+ }
+
+ request.setMaterial( material );
+
+ bool materialTransparent = false;
+ material.getHasTransparency( materialTransparent );
+ if ( materialTransparent )
+ {
+ request.setIsTransparent( true );
+ }
+
+ // create a draw request for wireframe on shaded if
+ // necessary.
+ //
+ if ( (displayStatus == M3dView::kActive) ||
+ (displayStatus == M3dView::kLead) ||
+ (displayStatus == M3dView::kHilite) )
+ {
+ MDrawRequest wireRequest = info.getPrototype( *this );
+ wireRequest.setDrawData( data );
+ getDrawRequestsWireframe( wireRequest, info );
+ wireRequest.setToken( WIREFRAME_SHADED );
+ wireRequest.setDisplayStyle( M3dView::kWireFrame );
+ queue.add( wireRequest );
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+// SHAPE NODE IMPLEMENTATION
+/////////////////////////////////////////////////////////////////////
+MTypeId TreelineShapeNode::id(TEConstants::NodeIDs::TreeLine);
+const char* TreelineShapeNode::stringId = "TreelineShapeNode";
+
+const char* TreelineShapeNode::SHADER_NAME_LONG = "material";
+const char* TreelineShapeNode::SHADER_NAME_SHORT = "mt";
+MObject TreelineShapeNode::sShader;
+
+const char* TreelineShapeNode::USCALE_NAME_LONG = "uscale";
+const char* TreelineShapeNode::USCALE_NAME_SHORT = "us";
+MObject TreelineShapeNode::sUScale;
+
+const char* TreelineShapeNode::COLOUR_NAME_LONG = "colour";
+const char* TreelineShapeNode::COLOUR_NAME_SHORT = "clr";
+MObject TreelineShapeNode::sColour;
+
+const char* TreelineShapeNode::RED_NAME_LONG = "red";
+const char* TreelineShapeNode::RED_NAME_SHORT = "r";
+MObject TreelineShapeNode::sRed;
+
+const char* TreelineShapeNode::GREEN_NAME_LONG = "green";
+const char* TreelineShapeNode::GREEN_NAME_SHORT = "g";
+MObject TreelineShapeNode::sGreen;
+
+const char* TreelineShapeNode::BLUE_NAME_LONG = "blue";
+const char* TreelineShapeNode::BLUE_NAME_SHORT = "b";
+MObject TreelineShapeNode::sBlue;
+
+const char* TreelineShapeNode::ALPHA_NAME_LONG = "alpha";
+const char* TreelineShapeNode::ALPHA_NAME_SHORT = "a";
+MObject TreelineShapeNode::sAlpha;
+
+const char* TreelineShapeNode::HEIGHT_NAME_LONG = "height";
+const char* TreelineShapeNode::HEIGHT_NAME_SHORT = "h";
+MObject TreelineShapeNode::sHeight;
+
+TreelineShapeNode::TreelineShapeNode()
+{
+}
+
+TreelineShapeNode::~TreelineShapeNode()
+{
+}
+
+//********************************************************************************************
+// Description
+//
+// When instances of this node are created internally, the MObject associated
+// with the instance is not created until after the constructor of this class
+// is called. This means that no member functions of MPxSurfaceShape can
+// be called in the constructor.
+// The postConstructor solves this problem. Maya will call this function
+// after the internal object has been created.
+// As a general rule do all of your initialization in the postConstructor.
+//********************************************************************************************
+void TreelineShapeNode::postConstructor()
+{
+ // This call allows the shape to have shading groups assigned
+ setRenderable( true );
+}
+
+//********************************************************************************************
+// Compute attribute values of the node
+//********************************************************************************************
+MStatus TreelineShapeNode::compute( const MPlug& plug, MDataBlock& datablock )
+{
+ return MS::kSuccess;
+}
+
+//********************************************************************************************
+// Capture when connections are made to this shape
+//********************************************************************************************
+MStatus TreelineShapeNode::connectionMade ( const MPlug &plug, const MPlug &otherPlug, bool asSrc )
+{
+ MObject shaderObj = otherPlug.node();
+ if ( asSrc && shaderObj.hasFn(MFn::kShadingEngine) )
+ {
+ MFnDependencyNode parentFn( plug.node() );
+ MFnDependencyNode shaderFn( shaderObj );
+
+ // connect this material with the chosen object
+
+ MStatus status;
+ MPlug parentMaterialPlug = parentFn.findPlug( SHADER_NAME_LONG, &status );
+
+ if ( !status )
+ {
+ MExt::DisplayError( "Could not assign %s to %s", shaderFn.name(), parentFn.name() );
+ }
+ else
+ {
+ parentMaterialPlug.setLocked( false );
+ parentMaterialPlug.setValue( shaderFn.name() );
+ parentMaterialPlug.setLocked( true );
+ }
+ }
+
+ // let Maya process the connection too
+ return MS::kUnknownParameter;
+}
+
+//********************************************************************************************
+// Create instance of shape
+//********************************************************************************************
+void* TreelineShapeNode::creator()
+{
+ return new TreelineShapeNode();
+}
+
+//********************************************************************************************
+// Create and initialize all attributes in maya
+//********************************************************************************************
+MStatus TreelineShapeNode::initialize()
+{
+ MStatus status;
+ MFnTypedAttribute strAttr;
+ MFnNumericAttribute numAttr;
+ MFnCompoundAttribute compAttr;
+
+ sShader = strAttr.create( SHADER_NAME_LONG, SHADER_NAME_SHORT, MFnData::kString, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( strAttr.setInternal( false ) );
+ RETURN_STATUS_ON_FAILURE( strAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( strAttr.setKeyable( false ) );
+ status = addAttribute( sShader );
+ RETURN_STATUS_ON_FAILURE( status );
+
+ sUScale = numAttr.create( USCALE_NAME_LONG, USCALE_NAME_SHORT, MFnNumericData::kDouble, 1.0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMin(0.01) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMax(100.0) );
+ RETURN_STATUS_ON_FAILURE( strAttr.setInternal( false ) );
+ RETURN_STATUS_ON_FAILURE( strAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( strAttr.setKeyable( true ) );
+ status = addAttribute( sUScale );
+ RETURN_STATUS_ON_FAILURE( status );
+
+ // Compound colour attribute
+ sRed = numAttr.create ( RED_NAME_LONG, RED_NAME_SHORT, MFnNumericData::kFloat, 1.0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMin(0.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMax(1.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setKeyable( true ) );
+
+ sGreen = numAttr.create ( GREEN_NAME_LONG, GREEN_NAME_SHORT, MFnNumericData::kFloat, 1.0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMin(0.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMax(1.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setKeyable( true ) );
+
+ sBlue = numAttr.create ( BLUE_NAME_LONG, BLUE_NAME_SHORT, MFnNumericData::kFloat, 1.0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMin(0.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMax(1.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setKeyable( true ) );
+
+ sColour = numAttr.create( COLOUR_NAME_LONG, COLOUR_NAME_SHORT, sRed, sGreen, sBlue, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setKeyable( true ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setUsedAsColor(true) );
+ status = addAttribute( sColour );
+ RETURN_STATUS_ON_FAILURE( status );
+
+ sAlpha = numAttr.create ( ALPHA_NAME_LONG, ALPHA_NAME_SHORT, MFnNumericData::kFloat, 1.0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMin(0.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMax(1.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setInternal( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setKeyable( true ) );
+ status = addAttribute( sAlpha );
+ RETURN_STATUS_ON_FAILURE( status );
+
+
+ sHeight = numAttr.create( HEIGHT_NAME_LONG, HEIGHT_NAME_SHORT, MFnNumericData::kFloat, 10.0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMin(0.1) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setMax(100.0) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setInternal( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setHidden( false ) );
+ RETURN_STATUS_ON_FAILURE( numAttr.setKeyable( true ) );
+ status = addAttribute( sHeight );
+
+ return MS::kSuccess;
+}
+
+//********************************************************************************************
+// Returns the bounding box for the shape.
+// In this case just use the radius and height attributes
+// to determine the bounding box.
+//********************************************************************************************
+MBoundingBox TreelineShapeNode::boundingBox() const
+{
+ MBoundingBox result;
+
+ MStatus status;
+ MObject obj = thisMObject();
+
+ float height = 1.0f;
+
+ MFnDagNode dagNodeFn(obj);
+ MPlug plug;
+
+ plug = dagNodeFn.findPlug( sHeight, &status );
+ plug.getValue( height );
+
+ MPlug vertices = dagNodeFn.findPlug( mControlPoints, &status );
+ assert( status );
+
+ unsigned int i;
+ for ( i = 0; i < vertices.numElements(); ++i )
+ {
+ MPoint point;
+ MPlug vertex1 = vertices.elementByLogicalIndex( i, &status );
+ assert( status );
+
+ MPlug x1 = vertex1.child( mControlValueX, &status );
+ assert( status );
+ x1.getValue( point.x );
+
+ MPlug y1 = vertex1.child( mControlValueY, &status );
+ assert( status );
+ y1.getValue( point.y );
+
+ MPlug z1 = vertex1.child( mControlValueZ, &status );
+ assert( status );
+ z1.getValue( point.z );
+
+ result.expand( point );
+
+ point.y = point.y + ( height * TEConstants::Scale );
+
+ result.expand( point );
+ }
+
+ return result;
+}
+
+//=============================================================================
+// TreelineShapeNode::componentToPlugs
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& component, MSelectionList& selectionList )
+//
+// Return: void
+//
+//=============================================================================
+void TreelineShapeNode::componentToPlugs( MObject& component,
+ MSelectionList& selectionList ) const
+{
+ if ( component.hasFn(MFn::kMeshVertComponent) )
+ {
+ MFnSingleIndexedComponent fnVtxComp( component );
+ MObject thisNode = thisMObject();
+ MPlug plug( thisNode, mControlPoints );
+ int len = fnVtxComp.elementCount();
+ for ( int i = 0; i < len; i++ )
+ {
+ MPlug vtxPlug = plug.elementByLogicalIndex( fnVtxComp.element(i) );
+ selectionList.add( vtxPlug );
+ }
+ }
+}
+
+//=============================================================================
+// TreelineShapeNode::matchComponent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const MSelectionList& item, const MAttributeSpecArray& spec, MSelectionList& list )
+//
+// Return: MPxSurfaceShape
+//
+//=============================================================================
+MPxSurfaceShape::MatchResult
+TreelineShapeNode::matchComponent( const MSelectionList& item,
+ const MAttributeSpecArray& spec,
+ MSelectionList& list )
+//
+// Description:
+//
+// Component/attribute matching method.
+// This method validates component names and indices which are
+// specified as a string and adds the corresponding component
+// to the passed in selection list.
+//
+// For instance, select commands such as "select shape1.vtx[0:7]"
+// are validated with this method and the corresponding component
+// is added to the selection list.
+//
+// Arguments
+//
+// item - DAG selection item for the object being matched
+// spec - attribute specification object
+// list - list to add components to
+//
+// Returns
+//
+// the result of the match
+//
+{
+ MPxSurfaceShape::MatchResult result = MPxSurfaceShape::kMatchOk;
+ MAttributeSpec attrSpec = spec[0];
+ int dim = attrSpec.dimensions();
+
+ // Look for attributes specifications of the form :
+ // vtx[ index ]
+ // vtx[ lower:upper ]
+ //
+ if ( (1 == spec.length()) &&
+ (dim > 0) &&
+ (attrSpec.name() == "controlPoints" ) )
+ {
+ MObject node;
+ item.getDependNode( 0, node );
+ MFnDependencyNode fnDepNode( node );
+ int numVertices = fnDepNode.findPlug( mControlPoints ).numElements();
+ MAttributeIndex attrIndex = attrSpec[0];
+
+ int upper = 0;
+ int lower = 0;
+ if ( attrIndex.hasLowerBound() ) {
+ attrIndex.getLower( lower );
+ }
+ if ( attrIndex.hasUpperBound() ) {
+ attrIndex.getUpper( upper );
+ }
+
+ // Check the attribute index range is valid
+ //
+ if ( (lower > upper) || (upper >= numVertices) ) {
+ result = MPxSurfaceShape::kMatchInvalidAttributeRange;
+ }
+ else {
+ MDagPath path;
+ item.getDagPath( 0, path );
+ MFnSingleIndexedComponent fnVtxComp;
+ MObject vtxComp = fnVtxComp.create( MFn::kMeshVertComponent );
+
+ for ( int i=lower; i<=upper; i++ )
+ {
+ fnVtxComp.addElement( i );
+ }
+ list.add( path, vtxComp );
+ }
+ }
+ else {
+ // Pass this to the parent class
+ return MPxSurfaceShape::matchComponent( item, spec, list );
+ }
+
+ return result;
+}
+
+//=============================================================================
+// TreelineShapeNode::match
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const MSelectionMask & mask, const MObjectArray& componentList )
+//
+// Return: bool
+//
+//=============================================================================
+bool TreelineShapeNode::match( const MSelectionMask & mask, const MObjectArray& componentList ) const
+//
+// Description:
+//
+// Check for matches between selection type / component list, and
+// the type of this shape / or it's components
+//
+// This is used by sets and deformers to make sure that the selected
+// components fall into the "vertex only" category.
+//
+// Arguments
+//
+// mask - selection type mask
+// componentList - possible component list
+//
+// Returns
+// true if matched any
+//
+{
+ bool result = false;
+
+ if( componentList.length() == 0 ) {
+ result = mask.intersects( MSelectionMask::kSelectMeshes );
+ }
+ else
+ {
+ for ( int i=0; i<(int)componentList.length(); i++ )
+ {
+ if ( (componentList[i].apiType() == MFn::kMeshVertComponent) &&
+ (mask.intersects(MSelectionMask::kSelectMeshVerts))
+ )
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+//=============================================================================
+// TreelineShapeNode::transformUsing
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const MMatrix & mat, const MObjectArray & componentList )
+//
+// Return: void
+//
+//=============================================================================
+void TreelineShapeNode::transformUsing( const MMatrix & mat,
+ const MObjectArray & componentList )
+//
+// Description
+//
+// Transforms the given components. This method is used by
+// the move, rotate, and scale tools in component mode.
+// Bounding box has to be updated here, so do the normals and
+// any other attributes that depend on vertex positions.
+//
+// Arguments
+//
+// mat - matrix to tranform the components by
+// componentList - list of components to be transformed
+//
+{
+ MStatus stat;
+
+ MFnDependencyNode fnDepNode( thisMObject() );
+ MPlug vertices = fnDepNode.findPlug( mControlPoints );
+
+ int len = componentList.length();
+ int i;
+ for ( i=0; i<len; i++ )
+ {
+ MObject comp = componentList[i];
+ MFnSingleIndexedComponent fnComp( comp );
+ int elemCount = fnComp.elementCount();
+ for ( int idx=0; idx<elemCount; idx++ )
+ {
+ int elemIndex = fnComp.element( idx );
+ MPlug vertex = vertices.elementByLogicalIndex( elemIndex );
+
+ MPoint point;
+ vertex.child(0).getValue( point.x );
+ vertex.child(1).getValue( point.y );
+ vertex.child(2).getValue( point.z );
+
+ point *= mat;
+
+ vertex.child(0).setValue( point.x );
+ vertex.child(1).setValue( point.y );
+ vertex.child(2).setValue( point.z );
+ }
+ }
+
+ // Moving vertices will likely change the bounding box.
+ //
+
+ // Tell maya the bounding box for this object has changed
+ // and thus "boundingBox()" needs to be called.
+ //
+ childChanged( MPxSurfaceShape::kBoundingBoxChanged );
+}
+
+//=============================================================================
+// TreelineShapeNode::closestPoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const MPoint & toThisPoint, MPoint & theClosestPoint, double tolerance )
+//
+// Return: void
+//
+//=============================================================================
+void TreelineShapeNode::closestPoint ( const MPoint& toThisPoint,
+ MPoint& theClosestPoint,
+ double tolerance )
+{
+ MStatus stat;
+
+ MFnDependencyNode fnDepNode( thisMObject() );
+ MPlug vertices = fnDepNode.findPlug( mControlPoints );
+
+ bool found = false;
+ double dist = 10000000.0;
+
+ unsigned int i;
+ for ( i = 0; i < vertices.numElements(); ++i )
+ {
+ MPlug vertex = vertices.elementByLogicalIndex( i );
+ MPoint point;
+
+ vertex.child( 0 ).getValue( point.x );
+ vertex.child( 1 ).getValue( point.y );
+ vertex.child( 2 ).getValue( point.z );
+
+ if ( point.distanceTo( toThisPoint ) < dist )
+ {
+ dist = point.distanceTo( toThisPoint );
+ theClosestPoint = point;
+ }
+ }
+}
+
+//=============================================================================
+// TreelineShapeNode::SnapTreeline
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& treeline )
+//
+// Return: void
+//
+//=============================================================================
+void TreelineShapeNode::SnapTreeline( MObject& treeline )
+{
+ MFnDependencyNode fnDepNode( treeline );
+
+ MPlug vertices = fnDepNode.findPlug( mControlPoints );
+
+ unsigned int i;
+
+ for ( i = 0; i < vertices.numElements(); ++i )
+ {
+ MPlug vertex = vertices.elementByLogicalIndex( i );
+
+ MPoint point;
+
+ vertex.child( 0 ).getValue( point.x );
+ vertex.child( 1 ).getValue( point.y );
+ vertex.child( 2 ).getValue( point.z );
+
+ MPoint intersectPoint;
+ //Look down...
+ if ( MExt::MeshIntersectAlongVector( point, MPoint(0, -100000.0, 0 ), intersectPoint ) )
+ {
+ vertex.child( 0 ).setValue( intersectPoint.x );
+ vertex.child( 1 ).setValue( intersectPoint.y );
+ vertex.child( 2 ).setValue( intersectPoint.z );
+ }
+ else
+ {
+ //Try looking up...
+ if ( MExt::MeshIntersectAlongVector( point, MPoint(0, 100000.0, 0 ), intersectPoint ) )
+ {
+ vertex.child( 0 ).setValue( intersectPoint.x );
+ vertex.child( 1 ).setValue( intersectPoint.y );
+ vertex.child( 2 ).setValue( intersectPoint.z );
+ }
+ }
+ }
+}
+
+void TreelineShapeNode::ConvertToGeometry( MObject& obj )
+{
+ MFnMesh newMesh;
+ MObject newMeshObj;
+
+ MFnDependencyNode fnDepNode( obj );
+ MPlug vertices = fnDepNode.findPlug( mControlPoints );
+
+ if ( vertices.numElements() >= 2 )
+ {
+ double height;
+ fnDepNode.findPlug( sHeight ).getValue( height );
+
+ double uScale;
+ fnDepNode.findPlug( sUScale).getValue( uScale );
+
+ MFloatArray uArray, vArray;
+ MIntArray uvCounts, uvIds;
+
+ unsigned int i = 0;
+ unsigned int j = 0;
+ do
+ {
+ //Create the new mesh...
+ MPointArray points;
+ MPoint point1, point2, point3, point4;
+
+ MPlug vertex1 = vertices.elementByLogicalIndex( i );
+ vertex1.child(0).getValue( point1.x );
+ vertex1.child(1).getValue( point1.y );
+ vertex1.child(2).getValue( point1.z );
+
+ point2 = point1;
+ point1.y += height * TEConstants::Scale;
+
+ points.append( point1 );
+ points.append( point2 );
+
+ MPlug vertex2 = vertices.elementByLogicalIndex( i + 1 );
+ vertex2.child(0).getValue( point3.x );
+ vertex2.child(1).getValue( point3.y );
+ vertex2.child(2).getValue( point3.z );
+
+ point4 = point3;
+ point4.y += height * TEConstants::Scale;
+
+ points.append( point3 );
+ points.append( point4 );
+
+ newMeshObj = newMesh.addPolygon( points );
+
+ double dist = point2.distanceTo( point3 );
+
+ float U = ceil( dist / (uScale * TEConstants::Scale ) );
+
+ uArray.append( 0 );
+ uArray.append( 0 );
+ uArray.append( U );
+ uArray.append( U );
+
+ vArray.append( 1 );
+ vArray.append( 0 );
+ vArray.append( 0 );
+ vArray.append( 1 );
+
+ uvCounts.append( 4 );
+
+ uvIds.append( 0 + j );
+ uvIds.append( 1 + j );
+ uvIds.append( 2 + j );
+ uvIds.append( 3 + j );
+
+ ++i;
+ j += 4;
+ }
+ while ( i < vertices.numElements() - 1 );
+
+ MString material;
+ fnDepNode.findPlug( sShader ).getValue( material );
+
+ if ( material.length() > 0 )
+ {
+ //Set the material to the new object.
+
+ newMesh.setUVs( uArray, vArray );
+ newMesh.assignUVs( uvCounts, uvIds );
+
+ //MEL command for assigning the texture.
+ //sets -e -forceElement pure3dSimpleShader1SG polySurface1;
+ MString meshName = newMesh.name();
+
+ MString cmd;
+
+ cmd += MString("sets -e -forceElement ") + material + MString(" ") + meshName;
+
+ MStatus status;
+ MGlobal::executeCommand( cmd, status );
+ }
+ }
+ else
+ {
+ assert(false);
+ MExt::DisplayError( "Treeline: %s is invalid for converting to geometry!", fnDepNode.name().asChar() );
+ }
+
+ if ( TrackEditor::GetDeleteTreelines() )
+ {
+ MExt::DeleteNode( obj, true );
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// UI IMPLEMENTATION
+/////////////////////////////////////////////////////////////////////
+
+TreelineShapeNodeUI::TreelineShapeNodeUI()
+{
+}
+
+TreelineShapeNodeUI::~TreelineShapeNodeUI()
+{
+}
+
+void* TreelineShapeNodeUI::creator()
+{
+ return new TreelineShapeNodeUI();
+}
+
+//********************************************************************************************
+// The draw data is used to pass geometry through the
+// draw queue. The data should hold all the information
+// needed to draw the shape.
+//********************************************************************************************
+void TreelineShapeNodeUI::getDrawRequests( const MDrawInfo & info, bool objectAndActiveOnly, MDrawRequestQueue & queue )
+{
+ MDrawData data;
+ MDrawRequest request = info.getPrototype( *this );
+ TreelineShapeNode* shapeNode = (TreelineShapeNode*)surfaceShape();
+ getDrawData( NULL, data );
+ request.setDrawData( data );
+
+ // Use display status to determine what color to draw the object
+ switch ( info.displayStyle() )
+ {
+ case M3dView::kGouraudShaded :
+ request.setToken( SMOOTH_SHADED );
+ getDrawRequestsShaded( request, info, queue, data );
+ queue.add( request );
+ break;
+
+ case M3dView::kFlatShaded :
+ request.setToken( FLAT_SHADED );
+ getDrawRequestsShaded( request, info, queue, data );
+ queue.add( request );
+ break;
+
+ default :
+ request.setToken(WIREFRAME);
+ getDrawRequestsWireframe( request, info );
+ queue.add( request );
+ break;
+
+ }
+
+ // Add draw requests for components
+ //
+ if ( !objectAndActiveOnly )
+ {
+ // Inactive components
+ //
+ if ( (info.displayStyle() == M3dView::kPoints) ||
+ (info.displayStatus() == M3dView::kHilite) )
+ {
+ MDrawRequest vertexRequest = info.getPrototype( *this );
+ vertexRequest.setDrawData( data );
+ vertexRequest.setToken( VERTICES );
+ vertexRequest.setColor( DORMANT_VERTEX_COLOR, //TODO: PICK COLOURS
+ M3dView::kActiveColors );
+
+ queue.add( vertexRequest );
+ }
+
+ // Active components
+ //
+ if ( surfaceShape()->hasActiveComponents() ) {
+
+ MDrawRequest activeVertexRequest = info.getPrototype( *this );
+ activeVertexRequest.setDrawData( data );
+ activeVertexRequest.setToken( VERTICES );
+ activeVertexRequest.setColor( ACTIVE_VERTEX_COLOR, //TODO: PICK COLOURS
+ M3dView::kActiveColors );
+
+ MObjectArray clist = surfaceShape()->activeComponents();
+ MObject vertexComponent = clist[0]; // Should filter list
+ activeVertexRequest.setComponent( vertexComponent );
+
+ queue.add( activeVertexRequest );
+ }
+ }
+
+}
+
+//********************************************************************************************
+// Actual draw call
+//********************************************************************************************
+void TreelineShapeNodeUI::drawQuad(int drawMode) const
+{
+ glPushAttrib( GL_CURRENT_BIT | GL_LINE_BIT | GL_POLYGON_BIT );
+ TreelineShapeNode* shapeNode = (TreelineShapeNode*)surfaceShape();
+ MFnDagNode dagNodeFn(shapeNode->thisMObject());
+ MPlug plug;
+
+ MStatus status;
+ float height;
+ MPlug heightPlug = dagNodeFn.findPlug( TETreeLine::TreelineShapeNode::sHeight, &status );
+ assert( status );
+
+ heightPlug.getValue( height );
+
+ MPlug vertices = dagNodeFn.findPlug( TETreeLine::TreelineShapeNode::mControlPoints, &status );
+ assert( status );
+
+ if ( vertices.numElements() >= 2 )
+ {
+ double uScale;
+ MPlug uScalePlug = dagNodeFn.findPlug( TETreeLine::TreelineShapeNode::sUScale, &status );
+ assert( status );
+ uScalePlug.getValue( uScale );
+
+ int primType;
+ if ((drawMode==SMOOTH_SHADED)||(drawMode==FLAT_SHADED))
+ {
+ glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL);
+ primType = GL_POLYGON;
+ }
+ else
+ {
+ glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE);
+ primType = GL_LINE_LOOP;
+ }
+
+ glPushMatrix();
+
+ unsigned int i = 0;
+
+ do
+ {
+ MPoint point1, point2;
+ MPlug vertex1 = vertices.elementByLogicalIndex( i, &status );
+ assert( status );
+
+ MPlug vertex2 = vertices.elementByLogicalIndex( i + 1, &status );
+ assert( status );
+
+ MPlug x1 = vertex1.child( TETreeLine::TreelineShapeNode::mControlValueX, &status );
+ assert( status );
+ x1.getValue( point1.x );
+
+ MPlug y1 = vertex1.child( TETreeLine::TreelineShapeNode::mControlValueY, &status );
+ assert( status );
+ y1.getValue( point1.y );
+
+ MPlug z1 = vertex1.child( TETreeLine::TreelineShapeNode::mControlValueZ, &status );
+ assert( status );
+ z1.getValue( point1.z );
+
+ MPlug x2 = vertex2.child( TETreeLine::TreelineShapeNode::mControlValueX, &status );
+ assert( status );
+ x2.getValue( point2.x );
+
+ MPlug y2 = vertex2.child( TETreeLine::TreelineShapeNode::mControlValueY, &status );
+ assert( status );
+ y2.getValue( point2.y );
+
+ MPlug z2 = vertex2.child( TETreeLine::TreelineShapeNode::mControlValueZ, &status );
+ assert( status );
+ z2.getValue( point2.z );
+
+ MPoint normal, vect;
+ rmt::Vector normalVec;
+ rmt::Vector v;
+
+ double dist = point1.distanceTo( point2 );
+
+ double U = ceil( dist / (uScale * TEConstants::Scale ) );
+
+ vect = point2 - point1;
+ v.Set( vect.x, vect.y, vect.z );
+ normalVec.CrossProduct( v, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+
+ glBegin(primType);
+ glNormal3f( normalVec.x, normalVec.y, normalVec.z ); //TODO: CALCULATE THIS!
+ glTexCoord2f ( 0, 1 );
+ glVertex3f( point1.x,
+ (point1.y) + (height * TEConstants::Scale),
+ point1.z );
+ glTexCoord2f ( 0, 0 );
+ glVertex3f( point1.x,
+ point1.y,
+ point1.z );
+ glTexCoord2f ( U, 0 );
+ glVertex3f( point2.x,
+ point2.y,
+ point2.z );
+ glTexCoord2f ( U, 1 );
+ glVertex3f( point2.x,
+ (point2.y) + (height * TEConstants::Scale),
+ point2.z );
+ glEnd();
+
+ ++i;
+ }
+ while ( i < vertices.numElements() - 1 );
+
+ glPopMatrix();
+
+ glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL);
+ }
+ glPopAttrib();
+}
+
+//********************************************************************************************
+// From the given draw request, get the draw data and display the quad
+//********************************************************************************************
+void TreelineShapeNodeUI::draw( const MDrawRequest & request, M3dView & view ) const
+{
+ glPushAttrib( GL_CURRENT_BIT | GL_LINE_BIT | GL_POLYGON_BIT );
+ MDagPath dagPath = request.multiPath();
+ MDrawData data = request.drawData();
+ short token = request.token();
+ bool drawTexture = false;
+
+ view.beginGL();
+
+ if ( (token == SMOOTH_SHADED) || (token == FLAT_SHADED) )
+ {
+ glEnable( GL_POLYGON_OFFSET_FILL );
+ // Set up the material
+ //
+ MMaterial material = request.material();
+ material.setMaterial(dagPath,false);
+
+ // Enable texturing
+ //
+ drawTexture = material.materialIsTextured();
+ if ( drawTexture ) glEnable(GL_TEXTURE_2D);
+
+ // Apply the texture to the current view
+ //
+ if ( drawTexture )
+ {
+ material.applyTexture( view, data );
+ }
+ }
+
+ if ( token == VERTICES )
+ {
+ drawVertices( request, view );
+ }
+ else
+ {
+ drawQuad(token);
+ }
+
+ // Turn off texture mode
+ //
+ if ( drawTexture ) glDisable(GL_TEXTURE_2D);
+
+ view.endGL();
+ glPopAttrib();
+}
+
+//********************************************************************************************
+// Select function. Gets called when the bbox for the object is selected.
+// This function just selects the object without doing any intersection tests.
+//********************************************************************************************
+bool TreelineShapeNodeUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const
+{
+ bool selected = false;
+
+ if ( selectInfo.displayStatus() == M3dView::kHilite ) {
+ selected = selectVertices( selectInfo, selectionList,worldSpaceSelectPts );
+ }
+
+ if ( !selected )
+ {
+ M3dView view = selectInfo.view();
+
+ //
+ // Re-Draw the object and see if they lie withing the selection area
+ // Sets OpenGL's render mode to select and stores
+ // selected items in a pick buffer
+ //
+ view.beginSelect();
+
+ switch ( selectInfo.displayStyle() )
+ {
+ case M3dView::kGouraudShaded :
+ drawQuad(SMOOTH_SHADED);
+ break;
+
+ case M3dView::kFlatShaded :
+ drawQuad(FLAT_SHADED);
+ break;
+
+ default :
+ drawQuad(WIREFRAME);
+ break;
+ }
+
+ if( view.endSelect() > 0 )
+ {
+ MSelectionMask priorityMask( MSelectionMask::kSelectObjectsMask );
+ MSelectionList item;
+ item.add( selectInfo.selectPath() );
+ MPoint xformedPt;
+ selectInfo.addSelection( item, xformedPt, selectionList, worldSpaceSelectPts, priorityMask, false );
+ return true;
+ }
+ }
+
+ return selected;
+}
+
+//=============================================================================
+// TreelineShapeNodeUI::drawVertices
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const MDrawRequest & request, M3dView & view )
+//
+// Return: void
+//
+//=============================================================================
+void TreelineShapeNodeUI::drawVertices( const MDrawRequest & request, M3dView & view ) const
+//
+// Description:
+//
+// Component (vertex) drawing routine
+//
+// Arguments:
+//
+// request - request to be drawn
+// view - view to draw into
+//
+{
+ glPushAttrib( GL_CURRENT_BIT | GL_LINE_BIT | GL_POLYGON_BIT );
+ MDrawData data = request.drawData();
+ TreelineShapeNode* shapeNode = (TreelineShapeNode*)surfaceShape();
+ MFnDagNode dagNodeFn(shapeNode->thisMObject());
+
+ view.beginGL();
+
+ // Query current state so it can be restored
+ //
+ bool lightingWasOn = glIsEnabled( GL_LIGHTING ) ? true : false;
+ if ( lightingWasOn ) {
+ glDisable( GL_LIGHTING );
+ }
+ float lastPointSize;
+ glGetFloatv( GL_POINT_SIZE, &lastPointSize );
+
+ // Set the point size of the vertices
+ //
+ glPointSize( POINT_SIZE );
+
+ // If there is a component specified by the draw request
+ // then loop over comp (using an MFnComponent class) and draw the
+ // active vertices, otherwise draw all vertices.
+ //
+ MObject comp = request.component();
+ if ( ! comp.isNull() )
+ {
+ MPlug vertices = dagNodeFn.findPlug( TETreeLine::TreelineShapeNode::mControlPoints );
+
+ MFnSingleIndexedComponent fnComponent( comp );
+ for ( int i=0; i<fnComponent.elementCount(); i++ )
+ {
+ int index = fnComponent.element( i );
+ glBegin( GL_POINTS );
+ MPlug vertexPlug = vertices.elementByLogicalIndex( index );
+
+ MPoint vertex;
+ vertexPlug.child( 0 ).getValue( vertex.x );
+ vertexPlug.child( 1 ).getValue( vertex.y );
+ vertexPlug.child( 2 ).getValue( vertex.z );
+
+ glVertex3f( (float)vertex[0],
+ (float)vertex[1],
+ (float)vertex[2] );
+ glEnd();
+
+ char annotation[32];
+ sprintf( annotation, "%d", index );
+ view.drawText( annotation, vertex );
+ }
+ }
+ else
+ {
+ MPlug vertices = dagNodeFn.findPlug( TETreeLine::TreelineShapeNode::mControlPoints );
+
+ for ( int i=0; i<vertices.numElements(); i++ )
+ {
+ glBegin( GL_POINTS );
+ MPlug vertexPlug = vertices.elementByLogicalIndex( i );
+
+ MPoint vertex;
+ vertexPlug.child( 0 ).getValue( vertex.x );
+ vertexPlug.child( 1 ).getValue( vertex.y );
+ vertexPlug.child( 2 ).getValue( vertex.z );
+
+ glVertex3f( (float)vertex[0],
+ (float)vertex[1],
+ (float)vertex[2] );
+ glEnd();
+ }
+ }
+ // Restore the state
+ //
+ if ( lightingWasOn ) {
+ glEnable( GL_LIGHTING );
+ }
+ glPointSize( lastPointSize );
+
+ view.endGL();
+ glPopAttrib();
+}
+
+//=============================================================================
+// TreelineShapeNodeUI::selectVertices
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts )
+//
+// Return: bool
+//
+//=============================================================================
+bool TreelineShapeNodeUI::selectVertices( MSelectInfo &selectInfo,
+ MSelectionList &selectionList,
+ MPointArray &worldSpaceSelectPts ) const
+//
+// Description:
+//
+// Vertex selection.
+//
+// Arguments:
+//
+// selectInfo - the selection state information
+// selectionList - the list of selected items to add to
+// worldSpaceSelectPts -
+//
+{
+ bool selected = false;
+ M3dView view = selectInfo.view();
+
+ MPoint xformedPoint;
+ MPoint currentPoint;
+ MPoint selectionPoint;
+ double z,previousZ = 0.0;
+ int closestPointVertexIndex = -1;
+
+ const MDagPath & path = selectInfo.multiPath();
+
+ // Create a component that will store the selected vertices
+ //
+ MFnSingleIndexedComponent fnComponent;
+ MObject surfaceComponent = fnComponent.create( MFn::kMeshVertComponent );
+ int vertexIndex;
+
+ // if the user did a single mouse click and we find > 1 selection
+ // we will use the alignmentMatrix to find out which is the closest
+ //
+ MMatrix alignmentMatrix;
+ MPoint singlePoint;
+ bool singleSelection = selectInfo.singleSelection();
+ if( singleSelection ) {
+ alignmentMatrix = selectInfo.getAlignmentMatrix();
+ }
+
+ // Get the geometry information
+ //
+ TreelineShapeNode* shapeNode = (TreelineShapeNode*)surfaceShape();
+ MFnDagNode dagNodeFn(shapeNode->thisMObject());
+
+ // Loop through all vertices of the mesh and
+ // see if they lie withing the selection area
+ //
+ MPlug vertices = dagNodeFn.findPlug( TETreeLine::TreelineShapeNode::mControlPoints );
+
+ int numVertices = vertices.numElements();
+
+ for ( vertexIndex=0; vertexIndex<numVertices; vertexIndex++ )
+ {
+ MPlug vertexPlug = vertices.elementByLogicalIndex( vertexIndex );
+
+ MPoint currentPoint;
+ vertexPlug.child( 0 ).getValue( currentPoint.x );
+ vertexPlug.child( 1 ).getValue( currentPoint.y );
+ vertexPlug.child( 2 ).getValue( currentPoint.z );
+
+ // Sets OpenGL's render mode to select and stores
+ // selected items in a pick buffer
+ //
+ view.beginSelect();
+
+ glBegin( GL_POINTS );
+
+ glVertex3f( (float)currentPoint[0],
+ (float)currentPoint[1],
+ (float)currentPoint[2] );
+ glEnd();
+
+ if ( view.endSelect() > 0 ) // Hit count > 0
+ {
+ selected = true;
+
+ if ( singleSelection ) {
+ xformedPoint = currentPoint;
+ xformedPoint.homogenize();
+ xformedPoint*= alignmentMatrix;
+ z = xformedPoint.z;
+ if ( closestPointVertexIndex < 0 || z > previousZ ) {
+ closestPointVertexIndex = vertexIndex;
+ singlePoint = currentPoint;
+ previousZ = z;
+ }
+ } else {
+ // multiple selection, store all elements
+ //
+ fnComponent.addElement( vertexIndex );
+ }
+ }
+ }
+
+ // If single selection, insert the closest point into the array
+ //
+ if ( selected && selectInfo.singleSelection() ) {
+ fnComponent.addElement(closestPointVertexIndex);
+
+ // need to get world space position for this vertex
+ //
+ selectionPoint = singlePoint;
+ selectionPoint *= path.inclusiveMatrix();
+ }
+
+ // Add the selected component to the selection list
+ //
+ if ( selected ) {
+ MSelectionList selectionItem;
+ selectionItem.add( path, surfaceComponent );
+
+ MSelectionMask mask( MSelectionMask::kSelectComponentsMask );
+ selectInfo.addSelection(
+ selectionItem, selectionPoint,
+ selectionList, worldSpaceSelectPts,
+ mask, true );
+ }
+
+ return selected;
+}
+
+} //namespace TETreeLine
+
+
diff --git a/tools/trackeditor/code/nodes/treelineshapenode.h b/tools/trackeditor/code/nodes/treelineshapenode.h
new file mode 100644
index 0000000..e1a25ac
--- /dev/null
+++ b/tools/trackeditor/code/nodes/treelineshapenode.h
@@ -0,0 +1,142 @@
+#ifndef TREELINE_SHAPE_NODE_H
+#define TREELINE_SHAPE_NODE_H
+
+#include "precompiled/PCH.h"
+
+class tlDataChunk;
+
+namespace TETreeLine
+{
+
+class p3dBaseShape : public MPxSurfaceShape
+{
+public:
+ p3dBaseShape() {}
+ virtual ~p3dBaseShape() {}
+};
+
+class p3dBaseShapeUI : public MPxSurfaceShapeUI
+{
+public:
+ p3dBaseShapeUI()
+ {
+ lead_color = 18; //green
+ active_color = 15; //white
+ active_affected_color = 8; //purple
+ dormant_color = 4; //blue
+ hilite_color = 17; //pale blue
+ }
+ virtual ~p3dBaseShapeUI() {}
+
+ virtual void getDrawRequestsWireframe( MDrawRequest&, const MDrawInfo& );
+ virtual void getDrawRequestsShaded( MDrawRequest&, const MDrawInfo&, MDrawRequestQueue&, MDrawData& data );
+
+protected:
+ int lead_color;
+ int active_color;
+ int active_affected_color;
+ int dormant_color;
+ int hilite_color;
+};
+
+/////////////////////////////////////////////////////////////////////
+//
+// Shape class - defines the non-UI part of a shape node
+//
+class TreelineShapeNode : public p3dBaseShape
+{
+public:
+ TreelineShapeNode();
+ virtual ~TreelineShapeNode();
+
+ virtual void postConstructor();
+ virtual MStatus compute( const MPlug& plug, MDataBlock& datablock );
+ virtual MStatus connectionMade ( const MPlug &plug, const MPlug &otherPlug, bool asSrc );
+ static void* creator();
+ static MStatus initialize();
+ virtual bool isBounded() const {return true;}
+ virtual MBoundingBox boundingBox() const;
+
+ virtual void componentToPlugs( MObject& component, MSelectionList& selectionList ) const;
+ virtual MPxSurfaceShape::MatchResult
+ matchComponent( const MSelectionList& item,
+ const MAttributeSpecArray& spec,
+ MSelectionList& list );
+ virtual bool match( const MSelectionMask& mask,
+ const MObjectArray& componentList ) const;
+ virtual void transformUsing( const MMatrix& mat, const MObjectArray& componentList );
+ virtual void closestPoint ( const MPoint & toThisPoint, MPoint & theClosestPoint, double tolerance );
+
+ static void SnapTreeline( MObject& treeline );
+
+ static void ConvertToGeometry( MObject& obj );
+
+ static MTypeId id;
+ static const char* stringId;
+
+private:
+ // Attributes
+ static const char* SHADER_NAME_LONG;
+ static const char* SHADER_NAME_SHORT;
+ static MObject sShader;
+
+ static const char* USCALE_NAME_LONG;
+ static const char* USCALE_NAME_SHORT;
+ static MObject sUScale;
+
+ static const char* COLOUR_NAME_LONG;
+ static const char* COLOUR_NAME_SHORT;
+ static MObject sColour;
+
+ static const char* RED_NAME_LONG;
+ static const char* RED_NAME_SHORT;
+ static MObject sRed;
+
+ static const char* GREEN_NAME_LONG;
+ static const char* GREEN_NAME_SHORT;
+ static MObject sGreen;
+
+ static const char* BLUE_NAME_LONG;
+ static const char* BLUE_NAME_SHORT;
+ static MObject sBlue;
+
+ static const char* ALPHA_NAME_LONG;
+ static const char* ALPHA_NAME_SHORT;
+ static MObject sAlpha;
+
+ static const char* HEIGHT_NAME_LONG;
+ static const char* HEIGHT_NAME_SHORT;
+ static MObject sHeight;
+};
+
+/////////////////////////////////////////////////////////////////////
+//
+// UI class - defines the UI part of a shape node
+//
+class TreelineShapeNodeUI : public p3dBaseShapeUI
+{
+public:
+ TreelineShapeNodeUI();
+ virtual ~TreelineShapeNodeUI();
+
+ virtual void getDrawRequests( const MDrawInfo & info, bool objectAndActiveOnly, MDrawRequestQueue & requests );
+ virtual void draw( const MDrawRequest & request, M3dView & view ) const;
+ virtual bool select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const;
+ static void * creator();
+
+protected:
+ void drawQuad(int drawMode) const;
+ void drawVertices( const MDrawRequest & request, M3dView & view ) const;
+ bool selectVertices( MSelectInfo &selectInfo,
+ MSelectionList &selectionList,
+ MPointArray &worldSpaceSelectPts ) const;
+};
+
+
+} //namespace TETreeLine
+
+
+
+#endif //TREELINE_SHAPE_NODE_H
+
+
diff --git a/tools/trackeditor/code/nodes/walllocator.cpp b/tools/trackeditor/code/nodes/walllocator.cpp
new file mode 100644
index 0000000..e4d5c4f
--- /dev/null
+++ b/tools/trackeditor/code/nodes/walllocator.cpp
@@ -0,0 +1,551 @@
+#include "precompiled/PCH.h"
+
+#include "walllocator.h"
+#include "main/constants.h"
+#include "utility/glext.h"
+#include "utility/mext.h"
+#include "utility/nodehelper.h"
+
+#include <toollib.hpp>
+
+
+MTypeId WallLocatorNode::id( TEConstants::TypeIDPrefix, TEConstants::NodeIDs::WallLocator );
+const char* WallLocatorNode::stringId = "WallLocatorNode";
+
+const int WallLocatorNode::ACTIVE_COLOUR = 13;
+const int WallLocatorNode::INACTIVE_COLOUR = 22;
+const float WallLocatorNode::SCALE = 1.0f * TEConstants::Scale;
+
+//This is an attribute.
+
+const char* WallLocatorNode::LEFTRIGHT_NAME_LONG = "leftRight";
+const char* WallLocatorNode::LEFTRIGHT_NAME_SHORT = "lr";
+MObject WallLocatorNode::mLeftRight;
+
+const char* WallLocatorNode::PREVNODE_NAME_LONG = "prevNode";
+const char* WallLocatorNode::PREVNODE_NAME_SHORT = "pn";
+MObject WallLocatorNode::mPrevNode;
+
+const char* WallLocatorNode::NEXTNODE_NAME_LONG = "nextNode";
+const char* WallLocatorNode::NEXTNODE_NAME_SHORT = "nn";
+MObject WallLocatorNode::mNextNode;
+
+const char* WallLocatorNode::ID_NAME_LONG = "callbackID";
+const char* WallLocatorNode::ID_NAME_SHORT = "cb";
+MObject WallLocatorNode::mCallbackId;
+
+//==============================================================================
+// WallLocatorNode::WallLocatorNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WallLocatorNode
+//
+//==============================================================================
+WallLocatorNode::WallLocatorNode() {};
+
+//==============================================================================
+// WallLocatorNode::~WallLocatorNode
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WallLocatorNode
+//
+//==============================================================================
+WallLocatorNode::~WallLocatorNode() {};
+
+//==============================================================================
+// WallLocatorNode::creator
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void* WallLocatorNode::creator()
+{
+ return new WallLocatorNode();
+}
+
+//==============================================================================
+// WallLocatorNode::initialize
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus WallLocatorNode::initialize()
+{
+ MFnMessageAttribute fnMessage;
+ MFnNumericAttribute fnNumeric;
+ MStatus status;
+
+ //Create the left/right attrib
+ mLeftRight = fnNumeric.create( LEFTRIGHT_NAME_LONG, LEFTRIGHT_NAME_SHORT, MFnNumericData::kInt, LEFT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ fnNumeric.setDefault(LEFT);
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mLeftRight ) );
+
+
+ //Create the sttribute for the previous node.
+ mPrevNode = fnMessage.create( PREVNODE_NAME_LONG, PREVNODE_NAME_SHORT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setReadable( false ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setWritable( true ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mPrevNode ) );
+
+ //Create the sttribute for the next node.
+ mNextNode = fnMessage.create( NEXTNODE_NAME_LONG, NEXTNODE_NAME_SHORT, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setReadable( true ) );
+ RETURN_STATUS_ON_FAILURE( fnMessage.setWritable( false ) );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mNextNode ) );
+
+
+ mCallbackId = fnNumeric.create( ID_NAME_LONG, ID_NAME_SHORT, MFnNumericData::kLong, 0, &status );
+ RETURN_STATUS_ON_FAILURE( status );
+ fnNumeric.setDefault( 0 );
+
+ RETURN_STATUS_ON_FAILURE( addAttribute( mCallbackId ) );
+
+ return MS::kSuccess;
+}
+
+//==============================================================================
+// WallLocatorNode::legalConnection
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( const MPlug & plug, const MPlug & otherPlug, bool asSrc, bool& result )
+//
+// Return: MStatus
+//
+//==============================================================================
+MStatus WallLocatorNode::legalConnection ( const MPlug & plug, const MPlug & otherPlug, bool asSrc, bool& result ) const
+{
+ if ( otherPlug.node() == thisMObject() )
+ {
+ result = false;
+ return MS::kSuccess;
+ }
+
+ if ( plug == mNextNode )
+ {
+ //This is the source of the connection.
+ //Therefore the connection is legal if I'm not already connected to the same node by the input.
+ MFnDependencyNode fnNode;
+ MStatus status;
+
+ fnNode.setObject( thisMObject() );
+ MPlug prevPlug = fnNode.findPlug( mPrevNode, &status );
+ assert( status );
+
+ if ( prevPlug.node() != otherPlug.node() )
+ {
+ //Go ahead and connect.
+ result = true;
+ }
+ else
+ {
+ //Already connected to this node. No 2-Node loops please.
+ result = false;
+ }
+
+ return MS::kSuccess;
+ }
+ else if ( plug == mPrevNode )
+ {
+ //This is the destination of the connection.
+ //Therefore the connection is legal if I'm not already connected to the same node by the output
+ MFnDependencyNode fnNode;
+ MStatus status;
+
+ fnNode.setObject( thisMObject() );
+ MPlug nextPlug = fnNode.findPlug( mNextNode, &status );
+ assert( status );
+
+ if ( nextPlug.node() != otherPlug.node() )
+ {
+ //Go ahead and connect.
+ result = true;
+ }
+ else
+ {
+ //Already connected to this node. No 2-Node loops please.
+ result = false;
+ }
+ return MS::kSuccess;
+ }
+
+ return MS::kUnknownParameter;
+}
+
+//==============================================================================
+// WallLocatorNode::postConstructor
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//==============================================================================
+void WallLocatorNode::postConstructor()
+{
+ //
+ // Register a callback that will notify us just prior to this node being
+ // deleted.
+ //
+ MStatus status;
+ MFnDependencyNode fnNode;
+
+ fnNode.setObject( thisMObject() );
+ MPlug plug = fnNode.findPlug( mCallbackId, &status );
+ assert( status );
+
+ int id = MNodeMessage::addNodeAboutToDeleteCallback(
+ thisMObject(),
+ NodeAboutToDeleteCallback,
+ (void*)(this),
+ &status
+ );
+
+ plug.setValue( id );
+
+ //Since this is a planar dealie, we want the Y to stay at 0...
+ MPlug lyPlug( thisMObject(), localPositionY );
+ lyPlug.setLocked( true );
+
+ MPlug wyPlug( thisMObject(), worldPositionY );
+ wyPlug.setLocked( true );
+
+ assert( status );
+}
+
+//==============================================================================
+// WallLocatorNode::draw
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( M3dView & view,
+// const MDagPath & path,
+// M3dView::DisplayStyle style,
+// M3dView::DisplayStatus status )
+//
+// Return: void
+//
+//==============================================================================
+void WallLocatorNode::draw( M3dView & view,
+ const MDagPath & path,
+ M3dView::DisplayStyle style,
+ M3dView::DisplayStatus status )
+{
+ view.beginGL();
+ glPushAttrib( GL_CURRENT_BIT | GL_LINE_BIT | GL_POLYGON_BIT );
+
+ //If there is a connected wall locator node, draw a line to it. Then draw the arrow halfway between them.
+ if ( MExt::IsConnected( thisMObject(), mNextNode ) )
+ {
+ //Can we stop the GL system from adding this to it's selection mechanism?
+
+ //When we are in render mode, we draw the lines between the nodes.
+ //If this was in GL_SELECTION_MODE, we would not draw the lines, so they won't interfere
+ //with selection.
+ GLint value;
+ glGetIntegerv( GL_RENDER_MODE, &value );
+
+ if ( (value == GL_RENDER) )
+ {
+ //Get the world position of the next node
+ MStatus st;
+ MPlugArray pa;
+ MFnDependencyNode fnNode;
+
+ fnNode.setObject( thisMObject() );
+ MPlug plug = fnNode.findPlug( mNextNode, &st );
+ assert( st );
+
+ plug.connectedTo( pa, false, true, &st );
+ assert( st );
+
+ //There is only one thing plugged into this...
+ MPlug nextPlug = pa[0];
+
+ //Got the nextNode's plug, let's get the WorldPosition of the other node.
+ MPoint nnwp;
+ MExt::GetWorldPosition( &nnwp, nextPlug.node() );
+
+ //Get the world position of this node.
+ MPoint wp;
+ MExt::GetWorldPosition( &wp, thisMObject() );
+
+ MPoint localPosNN( nnwp - wp );
+ MPoint localOrigin; // (0,0,0)
+
+ int colour = NodeHelper::OverrideNodeColour( thisMObject(), INACTIVE_COLOUR );
+
+ view.setDrawColor( colour, M3dView::kDormantColors );
+
+ GLExt::drawLine( localOrigin, localPosNN );
+
+
+ //Draw the LEFT / RIGHT line
+ MPoint wpMiddleOfLine = MExt::GetWorldPositionBetween( thisMObject(), nextPlug.node() );
+
+ MVector arrow;
+ if ( CalculateNormal( nnwp, &arrow ) )
+ {
+ MPoint arrowFrom( wpMiddleOfLine - wp );
+ double scale = ( localPosNN.distanceTo(localOrigin) / 6 );
+ if ( scale > 5 * TEConstants::Scale )
+ {
+ scale = 5 * TEConstants::Scale;
+ }
+
+ MPoint arrowTo( ( arrow * scale ) + arrowFrom );
+
+ GLExt::drawLine( arrowFrom, arrowTo, 5.0f );
+ }
+ }
+ }
+
+ if ( status == M3dView::kDormant )
+ {
+ int colour = NodeHelper::OverrideNodeColour( thisMObject(), INACTIVE_COLOUR );
+
+ view.setDrawColor( colour, M3dView::kDormantColors );
+ }
+ else
+ {
+ view.setDrawColor( ACTIVE_COLOUR, M3dView::kDormantColors );
+ }
+
+ //Draw a star to represent the locator.
+ GLExt::drawCrossHair3D( SCALE );
+
+ glPopAttrib();
+ view.endGL();
+}
+
+//==============================================================================
+// WallLocatorNode::NodeAboutToDeleteCallback
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MDGModifier& modifier, void* data )
+//
+// Return: void
+//
+//==============================================================================
+void WallLocatorNode::NodeAboutToDeleteCallback( MDGModifier& modifier, void* data )
+{
+ //
+ // Get the this pointer for the node being deleted.
+ //
+ WallLocatorNode* thisNode = (WallLocatorNode*)(data);
+ assert( thisNode );
+
+ //
+ // Get the MObject corresponding to this node.
+ //
+ MObject node = thisNode->thisMObject();
+
+ //Attach the neighbour nodes to eachother.
+ MObject nextNode;
+ MObject prevNode;
+
+ if ( MExt::IsConnected( node, mNextNode ) && MExt::IsConnected( node, mPrevNode ))
+ {
+ MStatus status;
+ MFnDependencyNode fnNode;
+ fnNode.setObject( node );
+
+ MPlug plug = fnNode.findPlug( mNextNode, &status );
+
+ MPlugArray pa;
+ plug.connectedTo( pa, false, true, &status );
+ assert( status );
+
+ MPlug nextPlug = pa[0];
+
+ nextNode = nextPlug.node();
+
+
+ fnNode.setObject( node );
+
+ plug = fnNode.findPlug( mPrevNode, &status );
+
+ plug.connectedTo( pa, true, false, &status );
+ assert( status );
+
+ MPlug prevPlug = pa[0];
+
+ prevNode = prevPlug.node();
+
+ //Remove all connections to this node.
+ MExt::DisconnectAll( node, mNextNode );
+ MExt::DisconnectAll( node, mPrevNode );
+
+
+ //Connect the nodes together... THANKS!
+ if ( prevNode != nextNode )
+ {
+ MExt::Connect( prevNode, WallLocatorNode::NEXTNODE_NAME_LONG, nextNode, WallLocatorNode::PREVNODE_NAME_LONG );
+ }
+ }
+
+ //
+ // cancel callback.
+ //
+ MStatus status;
+ MFnDependencyNode fnNode;
+ fnNode.setObject( node );
+
+ int id;
+ MPlug plug = fnNode.findPlug( mCallbackId, &status );
+ plug.getValue( id );
+
+ MMessage::removeCallback( id );
+}
+
+//==============================================================================
+// WallLocatorNode::CalculateNormal
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MPoint& nextNodeWP, MVector* normal )
+//
+// Return: bool
+//
+//==============================================================================
+bool WallLocatorNode::CalculateNormal( MPoint& nextNodeWP, MVector* normal )
+{
+ return CalculateNormal( thisMObject(), nextNodeWP, normal );
+}
+
+//==============================================================================
+// WallLocatorNode::CalculateNormal
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& thisNode, MPoint& nextNodeWP, MVector* normal )
+//
+// Return: bool
+//
+//==============================================================================
+bool WallLocatorNode::CalculateNormal( MObject& thisNode, MPoint& nextNodeWP, MVector* normal )
+{
+ //Get the world position of this node.
+ MPoint wp;
+ MExt::GetWorldPosition( &wp, thisNode );
+
+ MPoint localPosNN( nextNodeWP - wp );
+
+ MVector nextNode( localPosNN );
+
+ int isLeft = NONE;
+ MExt::Attr::Get( &isLeft, thisNode, mLeftRight );
+
+ if ( isLeft == LEFT )
+ {
+ MVector yUp( 0, -1.0f, 0 );
+ *normal = nextNode ^ yUp; //Cross product.
+ }
+ else if ( isLeft == RIGHT)
+ {
+ MVector yUp( 0, 1.0f, 0 );
+ *normal = nextNode ^ yUp; //Cross product.
+ }
+ else
+ {
+ return false;
+ }
+
+ normal->normalize();
+ return true;
+}
+
+//==============================================================================
+// WallLocatorNode::Export
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( MObject& wallLocatorNode, tlHistory& history )
+//
+// Return: tlDataChunk
+//
+//==============================================================================
+tlDataChunk* WallLocatorNode::Export( MObject& wallLocatorNode, tlHistory& history )
+{
+ MFnDagNode fnNode( wallLocatorNode );
+
+ if ( fnNode.typeId() == WallLocatorNode::id )
+ {
+ //Create a tlDataChunk and return it filled with the appropriate data.
+ tlWallChunk* wall = new tlWallChunk;
+
+ MStatus st;
+ MPlugArray pa;
+ MPlug nextPlug = fnNode.findPlug( mNextNode, &st );
+ nextPlug.connectedTo( pa, false, true, &st );
+ assert( st );
+
+ //There is only one thing plugged into this...
+ MPlug nextNodePlug = pa[0];
+ MObject nextNode = nextNodePlug.node();
+
+ MPoint thisPosition;
+ MExt::GetWorldPosition( &thisPosition, wallLocatorNode );
+
+ MPoint nextPosition;
+ MExt::GetWorldPosition( &nextPosition, nextNode );
+
+ MVector normal;
+ bool hasNormal = CalculateNormal( wallLocatorNode, nextPosition, &normal );
+
+ //Set the values.
+ tlPoint point;
+
+ point[0] = thisPosition[0] / TEConstants::Scale;
+ point[1] = thisPosition[1] / TEConstants::Scale;
+ point[2] = -thisPosition[2] / TEConstants::Scale; //Maya vs. P3D...
+ wall->SetStart( point );
+
+ point[0] = nextPosition[0] / TEConstants::Scale;
+ point[1] = nextPosition[1] / TEConstants::Scale;
+ point[2] = -nextPosition[2] / TEConstants::Scale; //Maya vs. P3D...
+ wall->SetEnd( point );
+
+ if ( hasNormal )
+ {
+ normal.normalize();
+ point[0] = normal[0];
+ point[1] = normal[1];
+ point[2] = -normal[2]; //Maya vs. P3D...
+ }
+ else
+ {
+ point[0] = 0;
+ point[1] = 0;
+ point[2] = 0;
+ }
+ wall->SetNormal( point );
+
+ return wall;
+ }
+
+ assert( false );
+ return NULL;
+} \ No newline at end of file
diff --git a/tools/trackeditor/code/nodes/walllocator.h b/tools/trackeditor/code/nodes/walllocator.h
new file mode 100644
index 0000000..31d114c
--- /dev/null
+++ b/tools/trackeditor/code/nodes/walllocator.h
@@ -0,0 +1,69 @@
+#include "precompiled/PCH.h"
+
+#ifndef WALL_LOCATOR
+#define WALL_LOCATOR
+
+
+#include "main/constants.h"
+
+class tlDataChunk;
+
+class WallLocatorNode : public MPxLocatorNode
+{
+public:
+ WallLocatorNode();
+ ~WallLocatorNode();
+
+ static void* creator();
+
+ virtual void draw( M3dView& view,
+ const MDagPath& path,
+ M3dView::DisplayStyle displayStyle,
+ M3dView::DisplayStatus displayStatus
+ );
+ static MStatus initialize();
+ virtual MStatus legalConnection ( const MPlug & plug, const MPlug & otherPlug, bool asSrc, bool& result ) const;
+ virtual void postConstructor();
+
+ //This is how you export one of these.
+ static tlDataChunk* Export( MObject& wallLocatorNode, tlHistory& history );
+ static bool CalculateNormal( MObject& thisNode, MPoint& nextNodeWP, MVector* normal );
+
+ static MTypeId id;
+ static const char* stringId;
+
+ //Custom to this object.
+ static const char* LEFTRIGHT_NAME_SHORT;
+ static const char* LEFTRIGHT_NAME_LONG;
+ static MObject mLeftRight;
+
+ enum
+ {
+ LEFT,
+ RIGHT,
+ NONE
+ };
+
+ static const char* PREVNODE_NAME_SHORT;
+ static const char* PREVNODE_NAME_LONG;
+ static MObject mPrevNode;
+
+ static const char* NEXTNODE_NAME_SHORT;
+ static const char* NEXTNODE_NAME_LONG;
+ static MObject mNextNode;
+
+ static const char* ID_NAME_SHORT;
+ static const char* ID_NAME_LONG;
+ static MObject mCallbackId;
+
+private:
+
+ static void NodeAboutToDeleteCallback( MDGModifier& modifier, void* data );
+ bool CalculateNormal( MPoint& nextNodeWP, MVector* normal );
+
+ static const int ACTIVE_COLOUR;
+ static const int INACTIVE_COLOUR;
+ static const float SCALE;
+};
+
+#endif \ No newline at end of file