diff options
Diffstat (limited to 'tools/trackeditor/code/nodes')
-rw-r--r-- | tools/trackeditor/code/nodes/NU.h | 34 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/fenceline.cpp | 201 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/fenceline.h | 30 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/intersection.cpp | 213 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/intersection.h | 48 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/pedpath.cpp | 271 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/pedpath.h | 30 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/road.cpp | 409 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/road.h | 56 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/tiledisplay.cpp | 212 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/tiledisplay.h | 35 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/treelineshapenode.cpp | 1343 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/treelineshapenode.h | 142 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/walllocator.cpp | 551 | ||||
-rw-r--r-- | tools/trackeditor/code/nodes/walllocator.h | 69 |
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 |