Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 43

//-----------------------------------------------------------------------------

// Copyright (c) 2012 GarageGames, LLC


//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "environment/meshRoad.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "util/catmullRom.h"
#include "math/util/quadTransforms.h"
#include "scene/simPath.h"
#include "scene/sceneRenderState.h"
#include "scene/sceneManager.h"
#include "scene/sgUtil.h"
#include "renderInstance/renderPassManager.h"
#include "T3D/gameBase/gameConnection.h"
#include "core/stream/bitStream.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDebugEvent.h"
#include "materials/materialManager.h"
#include "math/mathIO.h"
#include "math/mathUtils.h"
#include "math/util/frustum.h"
#include "gui/3d/guiTSControl.h"
#include "materials/shaderData.h"
#include "gfx/sim/gfxStateBlockData.h"
#include "gfx/sim/debugDraw.h"
#include "collision/concretePolyList.h"
#include "T3D/physics/physicsPlugin.h"
#include "T3D/physics/physicsBody.h"
#include "T3D/physics/physicsCollision.h"
#include "environment/nodeListManager.h"
#define MIN_METERS_PER_SEGMENT 1.0f
#define MIN_NODE_DEPTH 0.25f
#define MAX_NODE_DEPTH 50.0f
#define MIN_NODE_WIDTH 0.25f
#define MAX_NODE_WIDTH 50.0f
static U32 gIdxArray[6][2][3] = {
{ { 0, 4, 5 }, { 0, 5, 1 }, }, // Top Face
{ { 2, 6, 4 }, { 2, 4, 0 }, }, // Left Face
{ { 1, 5, 7 }, { 1, 7, 3 }, }, // Right Face
{ { 2, 3, 7 }, { 2, 7, 6 }, }, // Bottom Face
{ { 0, 1, 3 }, { 0, 3, 2 }, }, // Front Face
{ { 4, 6, 7 }, { 4, 7, 5 }, }, // Back Face
};
static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b)
{
const MeshRoadHitSegment *fa = (MeshRoadHitSegment*)a;
const MeshRoadHitSegment *fb = (MeshRoadHitSegment*)b;
return mSign(fb->t - fa->t);
}

//-----------------------------------------------------------------------------
// MeshRoadNodeList Struct
//-----------------------------------------------------------------------------
struct MeshRoadNodeList : public NodeListManager::NodeList
{
Vector<Point3F> mPositions;
Vector<F32> mWidths;
Vector<F32> mDepths;
Vector<VectorF> mNormals;
MeshRoadNodeList() { }
virtual ~MeshRoadNodeList() { }
};
//-----------------------------------------------------------------------------
// MeshRoadNodeEvent Class
//-----------------------------------------------------------------------------
class MeshRoadNodeEvent : public NodeListEvent
{
typedef NodeListEvent Parent;
public:
Vector<Point3F> mPositions;
Vector<F32> mWidths;
Vector<F32> mDepths;
Vector<VectorF> mNormals;
public:
MeshRoadNodeEvent() { mNodeList = NULL; }
virtual ~MeshRoadNodeEvent() { }
virtual void pack(NetConnection*, BitStream*);
virtual void unpack(NetConnection*, BitStream*);
virtual void copyIntoList(NodeListManager::NodeList* copyInto);
virtual void padListToSize();
DECLARE_CONOBJECT(MeshRoadNodeEvent);
};
void MeshRoadNodeEvent::pack(NetConnection* conn, BitStream* stream)
{
Parent::pack( conn, stream );
stream->writeInt( mPositions.size(), 16 );
for (U32 i=0; i<mPositions.size(); ++i)
{
mathWrite( *stream, mPositions[i] );
stream->write( mWidths[i] );
stream->write( mDepths[i] );
mathWrite( *stream, mNormals[i] );
}
}
void MeshRoadNodeEvent::unpack(NetConnection* conn, BitStream* stream)
{
mNodeList = new MeshRoadNodeList();
Parent::unpack( conn, stream );
U32 count = stream->readInt( 16 );
Point3F pos;
F32 width, depth;
VectorF normal;
MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
for (U32 i=0; i<count; ++i)
{
mathRead( *stream, &pos );
stream->read( &width );
stream->read( &depth );
mathRead( *stream, &normal );
list->mPositions.push_back( pos );
list->mWidths.push_back( width );
list->mDepths.push_back( depth );
list->mNormals.push_back( normal );
}
list->mTotalValidNodes = count;
// Do we have a complete list?
if (list->mPositions.size() >= mTotalNodes)
list->mListComplete = true;
}
void MeshRoadNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto)
{
MeshRoadNodeList* prevList = dynamic_cast<MeshRoadNodeList*>(copyInto);
MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
// Merge our list with the old list.
for (U32 i=mLocalListStart, index=0; i<mLocalListStart+list->mPositions.size(
); ++i, ++index)
{
prevList->mPositions[i] = list->mPositions[index];
prevList->mWidths[i] = list->mWidths[index];
prevList->mDepths[i] = list->mDepths[index];
prevList->mNormals[i] = list->mNormals[index];
}
}
void MeshRoadNodeEvent::padListToSize()
{
MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
U32 totalValidNodes = list->mTotalValidNodes;
// Pad our list front?
if (mLocalListStart)
{
MeshRoadNodeList* newlist = new MeshRoadNodeList();
newlist->mPositions.increment(mLocalListStart);
newlist->mWidths.increment(mLocalListStart);
newlist->mDepths.increment(mLocalListStart);
newlist->mNormals.increment(mLocalListStart);
newlist->mPositions.merge(list->mPositions);
newlist->mWidths.merge(list->mWidths);
newlist->mDepths.merge(list->mDepths);
newlist->mNormals.merge(list->mNormals);
delete list;
mNodeList = list = newlist;
}
// Pad our list end?
if (list->mPositions.size() < mTotalNodes)
{
U32 delta = mTotalNodes - list->mPositions.size();
list->mPositions.increment(delta);
list->mWidths.increment(delta);
list->mDepths.increment(delta);
list->mNormals.increment(delta);
}
list->mTotalValidNodes = totalValidNodes;
}
IMPLEMENT_CO_NETEVENT_V1(MeshRoadNodeEvent);
ConsoleDocClass( MeshRoadNodeEvent,
"@brief Sends messages to the Mesh Road Editor\n\n"
"Editor use only.\n\n"
"@internal"
);
//-----------------------------------------------------------------------------
// MeshRoadNodeListNotify Class
//-----------------------------------------------------------------------------
class MeshRoadNodeListNotify : public NodeListNotify
{
typedef NodeListNotify Parent;
protected:
SimObjectPtr<MeshRoad> mRoad;
public:
MeshRoadNodeListNotify( MeshRoad* road, U32 listId ) { mRoad = road; mListId
= listId; }
virtual ~MeshRoadNodeListNotify() { mRoad = NULL; }
virtual void sendNotification( NodeListManager::NodeList* list );
};
void MeshRoadNodeListNotify::sendNotification( NodeListManager::NodeList* list )
{
if (mRoad.isValid())
{
// Build the road's nodes
MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
if (roadList)
mRoad->buildNodesFromList( roadList );
}
}
//------------------------------------------------------------------------------
// MeshRoadConvex Class
//------------------------------------------------------------------------------
const MatrixF& MeshRoadConvex::getTransform() const
{
return MatrixF::Identity; //mObject->getTransform();
}
Box3F MeshRoadConvex::getBoundingBox() const
{
return box;
}
Box3F MeshRoadConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) c
onst
{
Box3F newBox = box;
newBox.minExtents.convolve(scale);
newBox.maxExtents.convolve(scale);
mat.mul(newBox);
return newBox;
}
Point3F MeshRoadConvex::support(const VectorF& vec) const
{
F32 bestDot = mDot( verts[0], vec );
const Point3F *bestP = &verts[0];
for(S32 i=1; i<4; i++)
{
F32 newD = mDot(verts[i], vec);
if(newD > bestDot)
{
bestDot = newD;
bestP = &verts[i];
}
}
return *bestP;
}
void MeshRoadConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFea
ture* cf)
{
cf->material = 0;
cf->object = mObject;
// For a tetrahedron this is pretty easy... first
// convert everything into world space.
Point3F tverts[4];
mat.mulP(verts[0], &tverts[0]);
mat.mulP(verts[1], &tverts[1]);
mat.mulP(verts[2], &tverts[2]);
mat.mulP(verts[3], &tverts[3]);
// Points...
S32 firstVert = cf->mVertexList.size();
cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0];
cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1];
cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2];
cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3];
// Edges...
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = firstVert+0;
cf->mEdgeList.last().vertex[1] = firstVert+1;
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = firstVert+1;
cf->mEdgeList.last().vertex[1] = firstVert+2;
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = firstVert+2;
cf->mEdgeList.last().vertex[1] = firstVert+0;
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = firstVert+3;
cf->mEdgeList.last().vertex[1] = firstVert+0;
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = firstVert+3;
cf->mEdgeList.last().vertex[1] = firstVert+1;
cf->mEdgeList.increment();
cf->mEdgeList.last().vertex[0] = firstVert+3;
cf->mEdgeList.last().vertex[1] = firstVert+2;
// Triangles...
cf->mFaceList.increment();
cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]);
cf->mFaceList.last().vertex[0] = firstVert+2;
cf->mFaceList.last().vertex[1] = firstVert+1;
cf->mFaceList.last().vertex[2] = firstVert+0;
cf->mFaceList.increment();
cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]);
cf->mFaceList.last().vertex[0] = firstVert+1;
cf->mFaceList.last().vertex[1] = firstVert+0;
cf->mFaceList.last().vertex[2] = firstVert+3;
cf->mFaceList.increment();
cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]);
cf->mFaceList.last().vertex[0] = firstVert+2;
cf->mFaceList.last().vertex[1] = firstVert+1;
cf->mFaceList.last().vertex[2] = firstVert+3;
cf->mFaceList.increment();
cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]);
cf->mFaceList.last().vertex[0] = firstVert+0;
cf->mFaceList.last().vertex[1] = firstVert+2;
cf->mFaceList.last().vertex[2] = firstVert+3;
}

void MeshRoadConvex::getPolyList( AbstractPolyList* list )


{
// Transform the list into object space and set the pointer to the object
//MatrixF i( mObject->getTransform() );
//Point3F iS( mObject->getScale() );
//list->setTransform(&i, iS);
list->setTransform( &MatrixF::Identity, Point3F::One );
list->setObject(mObject);
// Points...
S32 base = list->addPoint(verts[1]);
list->addPoint(verts[2]);
list->addPoint(verts[0]);
list->addPoint(verts[3]);
// Planes...
list->begin(0,0);
list->vertex(base + 2);
list->vertex(base + 1);
list->vertex(base + 0);
list->plane(base + 2, base + 1, base + 0);
list->end();
list->begin(0,0);
list->vertex(base + 2);
list->vertex(base + 1);
list->vertex(base + 3);
list->plane(base + 2, base + 1, base + 3);
list->end();
list->begin(0,0);
list->vertex(base + 3);
list->vertex(base + 1);
list->vertex(base + 0);
list->plane(base + 3, base + 1, base + 0);
list->end();
list->begin(0,0);
list->vertex(base + 2);
list->vertex(base + 3);
list->vertex(base + 0);
list->plane(base + 2, base + 3, base + 0);
list->end();
}
//------------------------------------------------------------------------------
// MeshRoadSegment Class
//------------------------------------------------------------------------------
MeshRoadSegment::MeshRoadSegment()
{
mPlaneCount = 0;
columns = 0;
rows = 0;
numVerts = 0;
numTriangles = 0;
startVert = 0;
endVert = 0;
startIndex = 0;
endIndex = 0;
slice0 = NULL;
slice1 = NULL;
}
MeshRoadSegment::MeshRoadSegment( MeshRoadSlice *rs0, MeshRoadSlice *rs1, const
MatrixF &roadMat )
{
columns = 0;
rows = 0;
numVerts = 0;
numTriangles = 0;
startVert = 0;
endVert = 0;
startIndex = 0;
endIndex = 0;
slice0 = rs0;
slice1 = rs1;
// Calculate the bounding box(s)
worldbounds.minExtents = worldbounds.maxExtents = rs0->p0;
worldbounds.extend( rs0->p2 );
worldbounds.extend( rs0->pb0 );
worldbounds.extend( rs0->pb2 );
worldbounds.extend( rs1->p0 );
worldbounds.extend( rs1->p2 );
worldbounds.extend( rs1->pb0 );
worldbounds.extend( rs1->pb2 );
objectbounds = worldbounds;
roadMat.mul( objectbounds );
// Calculate the planes for this segment
// Will be used for intersection/buoyancy tests
mPlaneCount = 6;
mPlanes[0].set( slice0->pb0, slice0->p0, slice1->p0 ); // left
mPlanes[1].set( slice1->pb2, slice1->p2, slice0->p2 ); // right
mPlanes[2].set( slice0->pb2, slice0->p2, slice0->p0 ); // near
mPlanes[3].set( slice1->p0, slice1->p2, slice1->pb2 ); // far
mPlanes[4].set( slice1->p2, slice1->p0, slice0->p0 ); // top
mPlanes[5].set( slice0->pb0, slice1->pb0, slice1->pb2 ); // bottom
}
void MeshRoadSegment::set( MeshRoadSlice *rs0, MeshRoadSlice *rs1 )
{
columns = 0;
rows = 0;
numVerts = 0;
numTriangles = 0;
startVert = 0;
endVert = 0;
startIndex = 0;
endIndex = 0;
slice0 = rs0;
slice1 = rs1;
}
bool MeshRoadSegment::intersectBox( const Box3F &bounds ) const
{
// This code copied from Frustum class.
Point3F maxPoint;
F32 maxDot;
// Note the planes are ordered left, right, near,
// far, top, bottom for getting early rejections
// from the typical horizontal scene.
for ( S32 i = 0; i < mPlaneCount; i++ )
{
// This is pretty much as optimal as you can
// get for a plane vs AABB test...
//
// 4 comparisons
// 3 multiplies
// 2 adds
// 1 negation
//
// It will early out as soon as it detects the
// bounds is outside one of the planes.
if ( mPlanes[i].x > 0 )
maxPoint.x = bounds.maxExtents.x;
else
maxPoint.x = bounds.minExtents.x;
if ( mPlanes[i].y > 0 )
maxPoint.y = bounds.maxExtents.y;
else
maxPoint.y = bounds.minExtents.y;
if ( mPlanes[i].z > 0 )
maxPoint.z = bounds.maxExtents.z;
else
maxPoint.z = bounds.minExtents.z;
maxDot = mDot( maxPoint, mPlanes[ i ] );
if ( maxDot <= -mPlanes[ i ].d )
return false;
}
return true;
}
bool MeshRoadSegment::containsPoint( const Point3F &pnt ) const
{
// This code from Frustum class.
F32 maxDot;
// Note the planes are ordered left, right, near,
// far, top, bottom for getting early rejections
// from the typical horizontal scene.
for ( S32 i = 0; i < mPlaneCount; i++ )
{
const PlaneF &plane = mPlanes[ i ];
// This is pretty much as optimal as you can
// get for a plane vs point test...
//
// 1 comparison
// 2 multiplies
// 1 adds
//
// It will early out as soon as it detects the
// point is outside one of the planes.
maxDot = mDot( pnt, plane ) + plane.d;
if ( maxDot < 0.0f )
return false;
}
return true;
}
F32 MeshRoadSegment::distanceToSurface(const Point3F &pnt) const
{
return mPlanes[4].distToPlane( pnt );
}
//------------------------------------------------------------------------------
// MeshRoad Class
//------------------------------------------------------------------------------
ConsoleDocClass( MeshRoad,
"@brief A strip of rectangular mesh segments defined by a 3D spline "
"for prototyping road-shaped objects in your scene.\n\n"
"User may control width and depth per node, overall spline shape in three "
"dimensions, and seperate Materials for rendering the top, bottom, and side s
urfaces.\n\n"
"MeshRoad is not capable of handling intersections, branches, curbs, or other
"
"desirable features in a final 'road' asset and is therefore intended for "
"prototyping and experimentation.\n\n"
"Materials assigned to MeshRoad should tile vertically.\n\n"
"@ingroup Terrain"
);
bool MeshRoad::smEditorOpen = false;
bool MeshRoad::smShowBatches = false;
bool MeshRoad::smShowSpline = true;
bool MeshRoad::smShowRoad = true;
bool MeshRoad::smWireframe = true;
SimObjectPtr<SimSet> MeshRoad::smServerMeshRoadSet = NULL;
GFXStateBlockRef MeshRoad::smWireframeSB;
IMPLEMENT_CO_NETOBJECT_V1(MeshRoad);
MeshRoad::MeshRoad()
: mTextureLength( 5.0f ),
mBreakAngle( 3.0f ),
mPhysicsRep( NULL ),
mWidthSubdivisions( 0 )
{
mConvexList = new Convex;
// Setup NetObject.
mTypeMask |= StaticObjectType | StaticShapeObjectType;
mNetFlags.set(Ghostable);
mMatInst[Top] = NULL;
mMatInst[Bottom] = NULL;
mMatInst[Side] = NULL;
}
MeshRoad::~MeshRoad()
{
delete mConvexList;
mConvexList = NULL;
}
void MeshRoad::initPersistFields()
{
addGroup( "MeshRoad" );
addField( "topMaterial", TypeMaterialName, Offset( mMaterialName[Top], Mes
hRoad ),
"Material for the upper surface of the road." );
addField( "bottomMaterial", TypeMaterialName, Offset( mMaterialName[Bottom
], MeshRoad ),
"Material for the bottom surface of the road." );
addField( "sideMaterial", TypeMaterialName, Offset( mMaterialName[Side], M
eshRoad ),
"Material for the left, right, front, and back surfaces of the road." )
;
addField( "textureLength", TypeF32, Offset( mTextureLength, MeshRoad ),
"The length in meters of textures mapped to the MeshRoad." );
addField( "breakAngle", TypeF32, Offset( mBreakAngle, MeshRoad ),
"Angle in degrees - MeshRoad will subdivide the spline if its curve is
greater than this threshold." );
addField( "widthSubdivisions", TypeS32, Offset( mWidthSubdivisions, MeshRo
ad ),
"Subdivide segments widthwise this many times when generating vertices.
" );
endGroup( "MeshRoad" );
addGroup( "Internal" );
addProtectedField( "Node", TypeString, NULL, &addNodeFromField, &emptyStri
ngProtectedGetFn,
"Do not modify, for internal use." );
endGroup( "Internal" );
Parent::initPersistFields();
}
void MeshRoad::consoleInit()
{
Parent::consoleInit();
Con::addVariable( "$MeshRoad::EditorOpen", TypeBool, &MeshRoad::smEditorOpen,
"True if the MeshRoad editor is open, otherwise false.\n"
"@ingroup Editors\n");
Con::addVariable( "$MeshRoad::wireframe", TypeBool, &MeshRoad::smWireframe, "
If true, will render the wireframe of the road.\n"
"@ingroup Editors\n");
Con::addVariable( "$MeshRoad::showBatches", TypeBool, &MeshRoad::smShowBatche
s, "Determines if the debug rendering of the batches cubes is displayed or not.\
n"
"@ingroup Editors\n");
Con::addVariable( "$MeshRoad::showSpline", TypeBool, &MeshRoad::smShowSpline,
"If true, the spline on which the curvature of this road is based will be rende
red.\n"
"@ingroup Editors\n");
Con::addVariable( "$MeshRoad::showRoad", TypeBool, &MeshRoad::smShowRoad, "If
true, the road will be rendered. When in the editor, roads are always rendered
regardless of this flag.\n"
"@ingroup Editors\n");
}
bool MeshRoad::addNodeFromField( void *object, const char *index, const char *da
ta )
{
MeshRoad *pObj = static_cast<MeshRoad*>(object);
//if ( !pObj->isProperlyAdded() )
//{
F32 width, depth;
Point3F pos, normal;
U32 result = dSscanf( data, "%g %g %g %g %g %g %g %g", &pos.x, &pos.y, &pos.z
, &width, &depth, &normal.x, &normal.y, &normal.z );
if ( result == 8 )
pObj->_addNode( pos, width, depth, normal );
//}
return false;
}
bool MeshRoad::onAdd()
{
if ( !Parent::onAdd() )
return false;
// Reset the World Box.
//setGlobalBounds();
resetWorldBox();
// Set the Render Transform.
setRenderTransform(mObjToWorld);
// Add to ServerMeshRoadSet
if ( isServerObject() )
{
getServerSet()->addObject( this );
}
if ( isClientObject() )
_initMaterial();
// Generate the Vert/Index buffers and everything else.
_regenerate();
// Add to Scene.
addToScene();
return true;
}
void MeshRoad::onRemove()
{
SAFE_DELETE( mPhysicsRep );
mConvexList->nukeList();
for ( U32 i = 0; i < SurfaceCount; i++ )
{
SAFE_DELETE( mMatInst[i] );
}
removeFromScene();
Parent::onRemove();
}
void MeshRoad::inspectPostApply()
{
// Set Parent.
Parent::inspectPostApply();
//if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT )
// mMetersPerSegment = MIN_METERS_PER_SEGMENT;
setMaskBits(MeshRoadMask);
}
void MeshRoad::onStaticModified( const char* slotName, const char*newValue )
{
Parent::onStaticModified( slotName, newValue );
if ( dStricmp( slotName, "breakAngle" ) == 0 )
{
setMaskBits( RegenMask );
}
}
void MeshRoad::writeFields( Stream &stream, U32 tabStop )
{
Parent::writeFields( stream, tabStop );
// Now write all nodes
stream.write(2, "\r\n");
for ( U32 i = 0; i < mNodes.size(); i++ )
{
const MeshRoadNode &node = mNodes[i];
stream.writeTabs(tabStop);
char buffer[1024];
dMemset( buffer, 0, 1024 );
dSprintf( buffer, 1024, "Node = \"%g %g %g %g %g %g %g %g\";", node.point.
x, node.point.y, node.point.z, node.width, node.depth, node.normal.x, node.norma
l.y, node.normal.z );
stream.writeLine( (const U8*)buffer );
}
}
bool MeshRoad::writeField( StringTableEntry fieldname, const char *value )
{
if ( fieldname == StringTable->insert("Node") )
return false;
return Parent::writeField( fieldname, value );
}
void MeshRoad::onEditorEnable()
{
}
void MeshRoad::onEditorDisable()
{
}
SimSet* MeshRoad::getServerSet()
{
if ( !smServerMeshRoadSet )
{
smServerMeshRoadSet = new SimSet();
smServerMeshRoadSet->registerObject( "ServerMeshRoadSet" );
Sim::getRootGroup()->addObject( smServerMeshRoadSet );
}
return smServerMeshRoadSet;
}
void MeshRoad::prepRenderImage( SceneRenderState* state )
{
if ( mNodes.size() <= 1 )
return;
RenderPassManager *renderPass = state->getRenderPass();
// Normal Road RenderInstance
// Always rendered when the editor is not open
// otherwise obey the smShowRoad flag
if ( smShowRoad || !smEditorOpen )
{
MeshRenderInst coreRI;
coreRI.clear();
coreRI.objectToWorld = &MatrixF::Identity;
coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::Vie
w);
coreRI.projection = renderPass->allocSharedXform(RenderPassManager::Projec
tion);
coreRI.type = RenderPassManager::RIT_Mesh;
BaseMatInstance *matInst;
for ( U32 i = 0; i < SurfaceCount; i++ )
{
matInst = state->getOverrideMaterial( mMatInst[i] );
if ( !matInst )
continue;
// Get the lights if we haven't already.
if ( matInst->isForwardLit() && !coreRI.lights[0] )
{
LightQuery query;
query.init( getWorldSphere() );
query.getLights( coreRI.lights, 8 );
}
MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
*ri = coreRI;
// Currently rendering whole road, fix to cull and batch
// per segment.
// Set the correct material for rendering.
ri->matInst = matInst;
ri->vertBuff = &mVB[i];
ri->primBuff = &mPB[i];
ri->prim = renderPass->allocPrim();
ri->prim->type = GFXTriangleList;
ri->prim->minIndex = 0;
ri->prim->startIndex = 0;
ri->prim->numPrimitives = mTriangleCount[i];
ri->prim->startVertex = 0;
ri->prim->numVertices = mVertCount[i];
// We sort by the material then vertex buffer.
ri->defaultKey = matInst->getStateHint();
ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Not 64bit safe!
renderPass->addInst( ri );
}
}
// Debug RenderInstance
// Only when editor is open.
if ( smEditorOpen )
{
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>
();
ri->renderDelegate.bind( this, &MeshRoad::_debugRender );
ri->type = RenderPassManager::RIT_Editor;
state->getRenderPass()->addInst( ri );
}
}
void MeshRoad::_initMaterial()
{
for ( U32 i = 0; i < SurfaceCount; i++ )
{
if ( mMatInst[i] )
SAFE_DELETE( mMatInst[i] );
if ( mMaterial[i] )
mMatInst[i] = mMaterial[i]->createMatInstance();
else
mMatInst[i] = MATMGR->createMatInstance( "WarningMaterial" );
mMatInst[i]->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVer
texPNTT>() );
}
}
void MeshRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, Base
MatInstance* )
{
//MeshRoadConvex convex;
//buildConvex( Box3F(true), convex );
//convex.render();
//GFXDrawUtil *drawer = GFX->getDrawUtil();
//GFX->setStateBlock( smStateBlock );
return;
/*
U32 convexCount = mDebugConvex.size();
PrimBuild::begin( GFXTriangleList, convexCount * 12 );
PrimBuild::color4i( 0, 0, 255, 155 );
for ( U32 i = 0; i < convexCount; i++ )
{
MeshRoadConvex *convex = mDebugConvex[i];
Point3F a = convex->verts[0];
Point3F b = convex->verts[1];
Point3F c = convex->verts[2];
Point3F p = convex->verts[3];
//mObjToWorld.mulP(a);
//mObjToWorld.mulP(b);
//mObjToWorld.mulP(c);
//mObjToWorld.mulP(p);
PrimBuild::vertex3fv( c );
PrimBuild::vertex3fv( b );
PrimBuild::vertex3fv( a );
PrimBuild::vertex3fv( b );
PrimBuild::vertex3fv( a );
PrimBuild::vertex3fv( p );
PrimBuild::vertex3fv( c );
PrimBuild::vertex3fv( b );
PrimBuild::vertex3fv( p );
PrimBuild::vertex3fv( a );
PrimBuild::vertex3fv( c );
PrimBuild::vertex3fv( p );
}
PrimBuild::end();
for ( U32 i = 0; i < mSegments.size(); i++ )
{
///GFX->getDrawUtil()->drawWireBox( mSegments[i].worldbounds, ColorI(255,0
,0,255) );
}
GFX->enterDebugEvent( ColorI( 255, 0, 0 ), "DecalRoad_debugRender" );
GFXTransformSaver saver;
GFX->setStateBlock( smStateBlock );
Point3F size(1,1,1);
ColorI color( 255, 0, 0, 255 );
if ( smShowBatches )
{
for ( U32 i = 0; i < mBatches.size(); i++ )
{
const Box3F &box = mBatches[i].bounds;
Point3F center;
box.getCenter( &center );
GFX->getDrawUtil()->drawWireCube( ( box.maxExtents - box.minExtents ) *
0.5f, center, ColorI(255,100,100,255) );
}
}
GFX->leaveDebugEvent();
*/
}
U32 MeshRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if ( stream->writeFlag( mask & MeshRoadMask ) )
{
// Write Object Transform.
stream->writeAffineTransform( mObjToWorld );
// Write Materials
stream->write( mMaterialName[0] );
stream->write( mMaterialName[1] );
stream->write( mMaterialName[2] );
stream->write( mTextureLength );
stream->write( mBreakAngle );
stream->write( mWidthSubdivisions );
}
if ( stream->writeFlag( mask & NodeMask ) )
{
const U32 nodeByteSize = 32; // Based on sending all of a node's parameter
s
// Test if we can fit all of our nodes within the current stream.
// We make sure we leave 100 bytes still free in the stream for whatever
// may follow us.
S32 allowedBytes = stream->getWriteByteSize() - 100;
if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) )
{
// All nodes should fit, so send them out now.
stream->writeInt( mNodes.size(), 16 );
for ( U32 i = 0; i < mNodes.size(); i++ )
{
mathWrite( *stream, mNodes[i].point );
stream->write( mNodes[i].width );
stream->write( mNodes[i].depth );
mathWrite( *stream, mNodes[i].normal );
}
}
else
{
// There isn't enough space left in the stream for all of the
// nodes. Batch them up into NetEvents.
U32 id = gServerNodeListManager->nextListId();
U32 count = 0;
U32 index = 0;
while (count < mNodes.size())
{
count += NodeListManager::smMaximumNodesPerEvent;
if (count > mNodes.size())
{
count = mNodes.size();
}
MeshRoadNodeEvent* event = new MeshRoadNodeEvent();
event->mId = id;
event->mTotalNodes = mNodes.size();
event->mLocalListStart = index;
for (; index<count; ++index)
{
event->mPositions.push_back( mNodes[index].point );
event->mWidths.push_back( mNodes[index].width );
event->mDepths.push_back( mNodes[index].depth );
event->mNormals.push_back( mNodes[index].normal );
}
con->postNetEvent( event );
}
stream->write( id );
}
}
stream->writeFlag( mask & RegenMask );
// Were done ...
return retMask;
}
void MeshRoad::unpackUpdate(NetConnection * con, BitStream * stream)
{
// Unpack Parent.
Parent::unpackUpdate(con, stream);
// MeshRoadMask
if(stream->readFlag())
{
MatrixF ObjectMatrix;
stream->readAffineTransform(&ObjectMatrix);
Parent::setTransform(ObjectMatrix);
// Read Materials...
Material *pMat = NULL;
for ( U32 i = 0; i < SurfaceCount; i++ )
{
stream->read( &mMaterialName[i] );
if ( !Sim::findObject( mMaterialName[i], pMat ) )
Con::printf( "DecalRoad::unpackUpdate, failed to find Material of na
me %s", mMaterialName[i].c_str() );
else
mMaterial[i] = pMat;
}
if ( isProperlyAdded() )
_initMaterial();
stream->read( &mTextureLength );
stream->read( &mBreakAngle );
stream->read( &mWidthSubdivisions );
}
// NodeMask
if ( stream->readFlag() )
{
if (stream->readFlag())
{
// Nodes have been passed in this update
U32 count = stream->readInt( 16 );
mNodes.clear();
Point3F pos, normal;
F32 width, depth;
for ( U32 i = 0; i < count; i++ )
{
mathRead( *stream, &pos );
stream->read( &width );
stream->read( &depth );
mathRead( *stream, &normal );
_addNode( pos, width, depth, normal );
}
}
else
{
// Nodes will arrive as events
U32 id;
stream->read( &id );
// Check if the road's nodes made it here before we did.
NodeListManager::NodeList* list = NULL;
if ( gClientNodeListManager->findListById( id, &list, true) )
{
// Work with the completed list
MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list )
;
if (roadList)
buildNodesFromList( roadList );
delete list;
}
else
{
// Nodes have not yet arrived, so register our interest in the list
MeshRoadNodeListNotify* notify = new MeshRoadNodeListNotify( this, i
d );
gClientNodeListManager->registerNotification( notify );
}
}
}
if ( stream->readFlag() && isProperlyAdded() )
_regenerate();
}
void MeshRoad::setTransform( const MatrixF &mat )
{
for ( U32 i = 0; i < mNodes.size(); i++ )
{
mWorldToObj.mulP( mNodes[i].point );
mat.mulP( mNodes[i].point );
}
Parent::setTransform( mat );
if ( mPhysicsRep )
mPhysicsRep->setTransform( mat );
// Regenerate and update the client
_regenerate();
setMaskBits( NodeMask | RegenMask );
}
void MeshRoad::setScale( const VectorF &scale )
{
// We ignore scale requests from the editor
// right now.
//Parent::setScale( scale );
}
void MeshRoad::buildConvex(const Box3F& box, Convex* convex)
{
if ( mSlices.size() < 2 )
return;
mConvexList->collectGarbage();
mDebugConvex.clear();
Box3F realBox = box;
mWorldToObj.mul(realBox);
realBox.minExtents.convolveInverse(mObjScale);
realBox.maxExtents.convolveInverse(mObjScale);
if (realBox.isOverlapped(getObjBox()) == false)
return;
U32 segmentCount = mSegments.size();
// Create convex(s) for each segment
for ( U32 i = 0; i < segmentCount; i++ )
{
const MeshRoadSegment &segment = mSegments[i];
// Is this segment overlapped?
if ( !segment.getWorldBounds().isOverlapped( box ) )
continue;
// Each segment has 6 faces
for ( U32 j = 0; j < 6; j++ )
{
// Only first segment has front face
if ( j == 4 && i != 0 )
continue;
// Only last segment has back face
if ( j == 5 && i != segmentCount-1 )
continue;
// Each face has 2 convex(s)
for ( U32 k = 0; k < 2; k++ )
{
// See if this convex exists in the working set already...
Convex* cc = 0;
CollisionWorkingList& wl = convex->getWorkingList();
for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr =
itr->wLink.mNext )
{
if ( itr->mConvex->getType() == MeshRoadConvexType )
{
MeshRoadConvex *pConvex = static_cast<MeshRoadConvex*>(itr->mC
onvex);
if ( pConvex->pRoad == this &&
pConvex->segmentId == i &&
pConvex->faceId == j &&
pConvex->triangleId == k )
{
cc = itr->mConvex;
break;
}
}
}
if (cc)
continue;
// Get the triangle...
U32 idx0 = gIdxArray[j][k][0];
U32 idx1 = gIdxArray[j][k][1];
U32 idx2 = gIdxArray[j][k][2];
Point3F a = segment[idx0];
Point3F b = segment[idx1];
Point3F c = segment[idx2];
// Transform the result into object space!
//mWorldToObj.mulP( a );
//mWorldToObj.mulP( b );
//mWorldToObj.mulP( c );
PlaneF p( c, b, a );
Point3F peak = ((a + b + c) / 3.0f) + (p * 0.15f);
// Set up the convex...
MeshRoadConvex *cp = new MeshRoadConvex();
mConvexList->registerObject( cp );
convex->addToWorkingList( cp );
cp->mObject = this;
cp->pRoad = this;
cp->segmentId = i;
cp->faceId = j;
cp->triangleId = k;
cp->normal = p;
cp->verts[0] = c;
cp->verts[1] = b;
cp->verts[2] = a;
cp->verts[3] = peak;
// Update the bounding box.
Box3F &bounds = cp->box;
bounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX );
bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX );
bounds.minExtents.setMin( a );
bounds.minExtents.setMin( b );
bounds.minExtents.setMin( c );
bounds.minExtents.setMin( peak );
bounds.maxExtents.setMax( a );
bounds.maxExtents.setMax( b );
bounds.maxExtents.setMax( c );
bounds.maxExtents.setMax( peak );
mDebugConvex.push_back(cp);
}
}
}
}
bool MeshRoad::buildPolyList( PolyListContext, AbstractPolyList* polyList, const
Box3F &box, const SphereF & )
{
if ( mSlices.size() < 2 )
return false;
polyList->setTransform( &MatrixF::Identity, Point3F::One );
polyList->setObject(this);
// JCF: optimize this to not always add everything.
return buildSegmentPolyList( polyList, 0, mSegments.size() - 1, true, true );
}
bool MeshRoad::buildSegmentPolyList( AbstractPolyList* polyList, U32 startSegIdx
, U32 endSegIdx, bool capFront, bool capEnd )
{
if ( mSlices.size() < 2 )
return false;
// Add verts
for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
{
const MeshRoadSegment &seg = mSegments[i];
if ( i == startSegIdx )
{
polyList->addPoint( seg.slice0->p0 );
polyList->addPoint( seg.slice0->p2 );
polyList->addPoint( seg.slice0->pb0 );
polyList->addPoint( seg.slice0->pb2 );
}
polyList->addPoint( seg.slice1->p0 );
polyList->addPoint( seg.slice1->p2 );
polyList->addPoint( seg.slice1->pb0 );
polyList->addPoint( seg.slice1->pb2 );
}
// Temporaries to hold indices for the corner points of a quad.
S32 p00, p01, p11, p10;
S32 pb00, pb01, pb11, pb10;
U32 offset = 0;
DebugDrawer *ddraw = NULL;//DebugDrawer::get();
ClippedPolyList *cpolyList = dynamic_cast<ClippedPolyList*>(polyList);
MatrixF mat;
Point3F scale;
if ( cpolyList )
cpolyList->getTransform( &mat, &scale );

for ( U32 i = startSegIdx; i <= endSegIdx; i++ )


{
p00 = offset;
p10 = offset + 1;
pb00 = offset + 2;
pb10 = offset + 3;
p01 = offset + 4;
p11 = offset + 5;
pb01 = offset + 6;
pb11 = offset + 7;
// Top Face
polyList->begin( 0,0 );
polyList->vertex( p00 );
polyList->vertex( p01 );
polyList->vertex( p11 );
polyList->plane( p00, p01, p11 );
polyList->end();
if ( ddraw && cpolyList )
{
Point3F v0 = cpolyList->mVertexList[p00].point;
mat.mulP( v0 );
Point3F v1 = cpolyList->mVertexList[p01].point;
mat.mulP( v1 );
Point3F v2 = cpolyList->mVertexList[p11].point;
mat.mulP( v2 );
ddraw->drawTri( v0, v1, v2 );
ddraw->setLastZTest( false );
ddraw->setLastTTL( 0 );
}
polyList->begin( 0,0 );
polyList->vertex( p00 );
polyList->vertex( p11 );
polyList->vertex( p10 );
polyList->plane( p00, p11, p10 );
polyList->end();
if ( ddraw && cpolyList )
{
ddraw->drawTri( cpolyList->mVertexList[p00].point, cpolyList->mVertexLi
st[p11].point, cpolyList->mVertexList[p10].point );
ddraw->setLastTTL( 0 );
}
// Left Face
polyList->begin( 0,0 );
polyList->vertex( pb00 );
polyList->vertex( pb01 );
polyList->vertex( p01 );
polyList->plane( pb00, pb01, p01 );
polyList->end();
polyList->begin( 0,0 );
polyList->vertex( pb00 );
polyList->vertex( p01 );
polyList->vertex( p00 );
polyList->plane( pb00, p01, p00 );
polyList->end();
// Right Face
polyList->begin( 0,0 );
polyList->vertex( p10 );
polyList->vertex( p11 );
polyList->vertex( pb11 );
polyList->plane( p10, p11, pb11 );
polyList->end();
polyList->begin( 0,0 );
polyList->vertex( p10 );
polyList->vertex( pb11 );
polyList->vertex( pb10 );
polyList->plane( p10, pb11, pb10 );
polyList->end();
// Bottom Face
polyList->begin( 0,0 );
polyList->vertex( pb00 );
polyList->vertex( pb10 );
polyList->vertex( pb11 );
polyList->plane( pb00, pb10, pb11 );
polyList->end();
polyList->begin( 0,0 );
polyList->vertex( pb00 );
polyList->vertex( pb11 );
polyList->vertex( pb01 );
polyList->plane( pb00, pb11, pb01 );
polyList->end();
// Front Face
if ( i == startSegIdx && capFront )
{
polyList->begin( 0,0 );
polyList->vertex( p00 );
polyList->vertex( p10 );
polyList->vertex( pb10 );
polyList->plane( p00, p10, pb10 );
polyList->end();
polyList->begin( 0,0 );
polyList->vertex( p00 );
polyList->vertex( pb10 );
polyList->vertex( pb00 );
polyList->plane( p00, pb10, pb00 );
polyList->end();
}
// Back Face
if ( i == endSegIdx && capEnd )
{
polyList->begin( 0,0 );
polyList->vertex( p01 );
polyList->vertex( pb01 );
polyList->vertex( pb11 );
polyList->plane( p01, pb01, pb11 );
polyList->end();
polyList->begin( 0,0 );
polyList->vertex( p01 );
polyList->vertex( pb11 );
polyList->vertex( p11 );
polyList->plane( p01, pb11, p11 );
polyList->end();
}
offset += 4;
}
return true;
}
bool MeshRoad::castRay( const Point3F &s, const Point3F &e, RayInfo *info )
{
Point3F start = s;
Point3F end = e;
mObjToWorld.mulP(start);
mObjToWorld.mulP(end);
F32 out = 1.0f; // The output fraction/percentage along the line defined by
s and e
VectorF norm(0.0f, 0.0f, 0.0f); // The normal of the face intersected
Vector<MeshRoadHitSegment> hitSegments;
for ( U32 i = 0; i < mSegments.size(); i++ )
{
const MeshRoadSegment &segment = mSegments[i];
F32 t;
VectorF n;
if ( segment.getWorldBounds().collideLine( start, end, &t, &n ) )
{
hitSegments.increment();
hitSegments.last().t = t;
hitSegments.last().idx = i;
}
}
dQsort( hitSegments.address(), hitSegments.size(), sizeof(MeshRoadHitSegment)
, compareHitSegments );
U32 idx0, idx1, idx2;
F32 t;
for ( U32 i = 0; i < hitSegments.size(); i++ )
{
U32 segIdx = hitSegments[i].idx;
const MeshRoadSegment &segment = mSegments[segIdx];
// Each segment has 6 faces
for ( U32 j = 0; j < 6; j++ )
{
if ( j == 4 && segIdx != 0 )
continue;
if ( j == 5 && segIdx != mSegments.size() - 1 )
continue;
// Each face has 2 triangles
for ( U32 k = 0; k < 2; k++ )
{
idx0 = gIdxArray[j][k][0];
idx1 = gIdxArray[j][k][1];
idx2 = gIdxArray[j][k][2];
const Point3F &v0 = segment[idx0];
const Point3F &v1 = segment[idx1];
const Point3F &v2 = segment[idx2];
if ( !MathUtils::mLineTriangleCollide( start, end,
v2, v1, v0,
NULL,
&t ) )
continue;
if ( t >= 0.0f && t < 1.0f && t < out )
{
out = t;
norm = PlaneF( v0, v1, v2 );
}
}
}
if (out >= 0.0f && out < 1.0f)
break;
}
if (out >= 0.0f && out < 1.0f)
{
info->t = out;
info->normal = norm;
info->point.interpolate(start, end, out);
info->face = -1;
info->object = this;
info->material = this->mMatInst[0];
return true;
}
return false;
}
bool MeshRoad::collideBox(const Point3F &start, const Point3F &end, RayInfo* inf
o)
{
Con::warnf( "MeshRoad::collideBox() - not yet implemented!" );
return Parent::collideBox( start, end, info );
}
void MeshRoad::_regenerate()
{
if ( mNodes.size() == 0 )
return;
const Point3F &nodePt = mNodes.first().point;
MatrixF mat( true );
mat.setPosition( nodePt );
Parent::setTransform( mat );
_generateSlices();
// Make sure we are in the correct bins given our world box.
if( getSceneManager() != NULL )
getSceneManager()->notifyObjectDirty( this );
}
void MeshRoad::_generateSlices()
{
if ( mNodes.size() < 2 )
return;
// Create the spline, initialized with the MeshRoadNode(s)
U32 nodeCount = mNodes.size();
MeshRoadSplineNode *splineNodes = new MeshRoadSplineNode[nodeCount];
for ( U32 i = 0; i < nodeCount; i++ )
{
MeshRoadSplineNode &splineNode = splineNodes[i];
const MeshRoadNode &node = mNodes[i];
splineNode.x = node.point.x;
splineNode.y = node.point.y;
splineNode.z = node.point.z;
splineNode.width = node.width;
splineNode.depth = node.depth;
splineNode.normal = node.normal;
}
CatmullRom<MeshRoadSplineNode> spline;
spline.initialize( nodeCount, splineNodes );
delete [] splineNodes;
mSlices.clear();
VectorF lastBreakVector(0,0,0);
MeshRoadSlice slice;
MeshRoadSplineNode lastBreakNode;
lastBreakNode = spline.evaluate(0.0f);
for ( U32 i = 1; i < mNodes.size(); i++ )
{
F32 t1 = spline.getTime(i);
F32 t0 = spline.getTime(i-1);
F32 segLength = spline.arcLength( t0, t1 );
U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT );
numSegments = getMax( numSegments, (U32)1 );
F32 tstep = ( t1 - t0 ) / numSegments;
U32 startIdx = 0;
U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments;
for ( U32 j = startIdx; j < endIdx; j++ )
{
F32 t = t0 + tstep * j;
MeshRoadSplineNode splineNode = spline.evaluate(t);
VectorF toNodeVec = splineNode.getPosition() - lastBreakNode.getPositio
n();
toNodeVec.normalizeSafe();
if ( lastBreakVector.isZero() )
lastBreakVector = toNodeVec;
F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) );
if ( j == startIdx ||
( j == endIdx - 1 && i == mNodes.size() - 1 ) ||
angle > mBreakAngle )
{
// Push back a spline node
slice.p1.set( splineNode.x, splineNode.y, splineNode.z );
slice.width = splineNode.width;
slice.depth = splineNode.depth;
slice.normal = splineNode.normal;
slice.normal.normalize();
slice.parentNodeIdx = i-1;
slice.t = t;
mSlices.push_back( slice );
lastBreakVector = splineNode.getPosition() - lastBreakNode.getPositi
on();
lastBreakVector.normalizeSafe();
lastBreakNode = splineNode;
}
}
}
//
// Calculate uvec, fvec, and rvec for all slices
//
MatrixF mat(true);
for ( U32 i = 0; i < mSlices.size(); i++ )
{
calcSliceTransform( i, mat );
mat.getColumn( 0, &mSlices[i].rvec );
mat.getColumn( 1, &mSlices[i].fvec );
mat.getColumn( 2, &mSlices[i].uvec );
}
//
// Calculate p0/p2/pb0/pb2 for all slices
//
for ( U32 i = 0; i < mSlices.size(); i++ )
{
MeshRoadSlice *slice = &mSlices[i];
slice->p0 = slice->p1 - slice->rvec * slice->width * 0.5f;
slice->p2 = slice->p1 + slice->rvec * slice->width * 0.5f;
slice->pb0 = slice->p0 - slice->uvec * slice->depth;
slice->pb2 = slice->p2 - slice->uvec * slice->depth;
}
// Generate the object/world bounds
Box3F box;
for ( U32 i = 0; i < mSlices.size(); i++ )
{
const MeshRoadSlice &slice = mSlices[i];
if ( i == 0 )
{
box.minExtents = slice.p0;
box.maxExtents = slice.p2;
box.extend( slice.pb0 );
box.extend( slice.pb2 );
}
else
{
box.extend( slice.p0 );
box.extend( slice.p2 );
box.extend( slice.pb0 );
box.extend( slice.pb2 );
}
}
mWorldBox = box;
resetObjectBox();
_generateSegments();
}
void MeshRoad::_generateSegments()
{
SAFE_DELETE( mPhysicsRep );
mSegments.clear();
for ( U32 i = 0; i < mSlices.size() - 1; i++ )
{
MeshRoadSegment seg( &mSlices[i], &mSlices[i+1], getWorldTransform() );
mSegments.push_back( seg );
}
if ( isClientObject() )
_generateVerts();
if ( PHYSICSMGR )
{
ConcretePolyList polylist;
if ( buildPolyList( PLC_Collision, &polylist, getWorldBox(), getWorldSpher
e() ) )
{
polylist.triangulate();
PhysicsCollision *colShape = PHYSICSMGR->createCollision();
colShape->addTriangleMesh( polylist.mVertexList.address(),
polylist.mVertexList.size(),
polylist.mIndexList.address(),
polylist.mIndexList.size() / 3,
MatrixF::Identity );
PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server"
: "client" );
mPhysicsRep = PHYSICSMGR->createBody();
mPhysicsRep->init( colShape, 0, 0, this, world );
}
}
}
void MeshRoad::_generateVerts()
{
const U32 widthDivisions = getMax( 0, mWidthSubdivisions );
const F32 divisionStep = 1.0f / (F32)( widthDivisions + 1 );
const U32 sliceCount = mSlices.size();
const U32 segmentCount = mSegments.size();
mVertCount[Top] = ( 2 + widthDivisions ) * sliceCount;
mTriangleCount[Top] = segmentCount * 2 * ( widthDivisions + 1 );
mVertCount[Bottom] = sliceCount * 2;
mTriangleCount[Bottom] = segmentCount * 2;
mVertCount[Side] = sliceCount * 4;
mTriangleCount[Side] = segmentCount * 4 + 4;
// Calculate TexCoords for Slices
F32 texCoordV = 0.0f;
mSlices[0].texCoordV = 0.0f;
for ( U32 i = 1; i < sliceCount; i++ )
{
MeshRoadSlice &slice = mSlices[i];
MeshRoadSlice &prevSlice = mSlices[i-1];
// Increment the textCoordV for the next slice.
F32 len = ( slice.p1 - prevSlice.p1 ).len();
texCoordV += len / mTextureLength;
slice.texCoordV = texCoordV;
}
// Make Vertex Buffers
GFXVertexPNTT *pVert = NULL;
U32 vertCounter = 0;
// Top Buffers...
mVB[Top].set( GFX, mVertCount[Top], GFXBufferTypeStatic );
pVert = mVB[Top].lock();
vertCounter = 0;
for ( U32 i = 0; i < sliceCount; i++ )
{
MeshRoadSlice &slice = mSlices[i];
pVert->point = slice.p0;
pVert->normal = slice.uvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(1,slice.texCoordV);
pVert++;
vertCounter++;
for ( U32 j = 0; j < widthDivisions; j++ )
{
const F32 t = divisionStep * (F32)( j + 1 );
pVert->point.interpolate( slice.p0, slice.p2, t );
pVert->normal = slice.uvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set( 1.0f - t, slice.texCoordV );
pVert++;
vertCounter++;
}
pVert->point = slice.p2;
pVert->normal = slice.uvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set( 0, slice.texCoordV );
pVert++;
vertCounter++;
}
AssertFatal( vertCounter == mVertCount[Top], "MeshRoad, wrote incorrect numbe
r of verts in mVB[Top]!" );
mVB[Top].unlock();
// Bottom Buffer...
mVB[Bottom].set( GFX, mVertCount[Bottom], GFXBufferTypeStatic );
pVert = mVB[Bottom].lock();
vertCounter = 0;
for ( U32 i = 0; i < sliceCount; i++ )
{
MeshRoadSlice &slice = mSlices[i];
pVert->point = slice.pb2;
pVert->normal = -slice.uvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(0,slice.texCoordV);
pVert++;
vertCounter++;
pVert->point = slice.pb0;
pVert->normal = -slice.uvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(1,slice.texCoordV);
pVert++;
vertCounter++;
}
AssertFatal( vertCounter == mVertCount[Bottom], "MeshRoad, wrote incorrect nu
mber of verts in mVB[Bottom]!" );
mVB[Bottom].unlock();
// Side Buffers...
mVB[Side].set( GFX, mVertCount[Side], GFXBufferTypeStatic );
pVert = mVB[Side].lock();
vertCounter = 0;
for ( U32 i = 0; i < sliceCount; i++ )
{
MeshRoadSlice &slice = mSlices[i];
pVert->point = slice.p0;
pVert->normal = -slice.rvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(1,slice.texCoordV);
pVert++;
vertCounter++;
pVert->point = slice.p2;
pVert->normal = slice.rvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(1,slice.texCoordV);
pVert++;
vertCounter++;
pVert->point = slice.pb0;
pVert->normal = -slice.rvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(0,slice.texCoordV);
pVert++;
vertCounter++;
pVert->point = slice.pb2;
pVert->normal = slice.rvec;
pVert->tangent = slice.fvec;
pVert->texCoord.set(0,slice.texCoordV);
pVert++;
vertCounter++;
}
AssertFatal( vertCounter == mVertCount[Side], "MeshRoad, wrote incorrect numb
er of verts in mVB[Side]!" );
mVB[Side].unlock();
// Make Primitive Buffers
U32 p00, p01, p11, p10;
U32 pb00, pb01, pb11, pb10;
U32 offset = 0;
U16 *pIdx = NULL;
U32 curIdx = 0;
// Top Primitive Buffer
mPB[Top].set( GFX, mTriangleCount[Top] * 3, mTriangleCount[Top], GFXBufferTyp
eStatic );
mPB[Top].lock(&pIdx);
curIdx = 0;
offset = 0;
const U32 rowStride = 2 + widthDivisions;
for ( U32 i = 0; i < mSegments.size(); i++ )
{
for ( U32 j = 0; j < widthDivisions + 1; j++ )
{
p00 = offset;
p10 = offset + 1;
p01 = offset + rowStride;
p11 = offset + rowStride + 1;
pIdx[curIdx] = p00;
curIdx++;
pIdx[curIdx] = p01;
curIdx++;
pIdx[curIdx] = p11;
curIdx++;
pIdx[curIdx] = p00;
curIdx++;
pIdx[curIdx] = p11;
curIdx++;
pIdx[curIdx] = p10;
curIdx++;
offset += 1;
}
offset += 1;
}

AssertFatal( curIdx == mTriangleCount[Top] * 3, "MeshRoad, wrote incorrect nu


mber of indices in mPB[Top]!" );
mPB[Top].unlock();
// Bottom Primitive Buffer
mPB[Bottom].set( GFX, mTriangleCount[Bottom] * 3, mTriangleCount[Bottom], GFX
BufferTypeStatic );
mPB[Bottom].lock(&pIdx);
curIdx = 0;
offset = 0;
for ( U32 i = 0; i < mSegments.size(); i++ )
{
p00 = offset;
p10 = offset + 1;
p01 = offset + 2;
p11 = offset + 3;
pIdx[curIdx] = p00;
curIdx++;
pIdx[curIdx] = p01;
curIdx++;
pIdx[curIdx] = p11;
curIdx++;
pIdx[curIdx] = p00;
curIdx++;
pIdx[curIdx] = p11;
curIdx++;
pIdx[curIdx] = p10;
curIdx++;
offset += 2;
}
AssertFatal( curIdx == mTriangleCount[Bottom] * 3, "MeshRoad, wrote incorrect
number of indices in mPB[Bottom]!" );
mPB[Bottom].unlock();
// Side Primitive Buffer
mPB[Side].set( GFX, mTriangleCount[Side] * 3, mTriangleCount[Side], GFXBuffer
TypeStatic );
mPB[Side].lock(&pIdx);
curIdx = 0;
offset = 0;
for ( U32 i = 0; i < mSegments.size(); i++ )
{
p00 = offset;
p10 = offset + 1;
pb00 = offset + 2;
pb10 = offset + 3;
p01 = offset + 4;
p11 = offset + 5;
pb01 = offset + 6;
pb11 = offset + 7;
// Left Side
pIdx[curIdx] = pb00;
curIdx++;
pIdx[curIdx] = pb01;
curIdx++;
pIdx[curIdx] = p01;
curIdx++;
pIdx[curIdx] = pb00;
curIdx++;
pIdx[curIdx] = p01;
curIdx++;
pIdx[curIdx] = p00;
curIdx++;
// Right Side
pIdx[curIdx] = p10;
curIdx++;
pIdx[curIdx] = p11;
curIdx++;
pIdx[curIdx] = pb11;
curIdx++;
pIdx[curIdx] = p10;
curIdx++;
pIdx[curIdx] = pb11;
curIdx++;
pIdx[curIdx] = pb10;
curIdx++;
offset += 4;
}
// Cap the front and back ends
pIdx[curIdx++] = 0;
pIdx[curIdx++] = 1;
pIdx[curIdx++] = 3;
pIdx[curIdx++] = 0;
pIdx[curIdx++] = 3;
pIdx[curIdx++] = 2;
pIdx[curIdx++] = offset + 0;
pIdx[curIdx++] = offset + 3;
pIdx[curIdx++] = offset + 1;
pIdx[curIdx++] = offset + 0;
pIdx[curIdx++] = offset + 2;
pIdx[curIdx++] = offset + 3;

AssertFatal( curIdx == mTriangleCount[Side] * 3, "MeshRoad, wrote incorrect n


umber of indices in mPB[Side]!" );
mPB[Side].unlock();
}
const MeshRoadNode& MeshRoad::getNode( U32 idx )
{
return mNodes[idx];
}
VectorF MeshRoad::getNodeNormal( U32 idx )
{
if ( mNodes.size() - 1 < idx )
return VectorF::Zero;
return mNodes[idx].normal;
}
void MeshRoad::setNodeNormal( U32 idx, const VectorF &normal )
{
if ( mNodes.size() - 1 < idx )
return;
mNodes[idx].normal = normal;
regenerate();
setMaskBits( NodeMask | RegenMask );
}
Point3F MeshRoad::getNodePosition( U32 idx )
{
if ( mNodes.size() - 1 < idx )
return Point3F::Zero;
return mNodes[idx].point;
}
void MeshRoad::setNodePosition( U32 idx, const Point3F &pos )
{
if ( mNodes.size() - 1 < idx )
return;
mNodes[idx].point = pos;
regenerate();
setMaskBits( NodeMask | RegenMask );
}
U32 MeshRoad::addNode( const Point3F &pos, const F32 &width, const F32 &depth, c
onst VectorF &normal )
{
U32 idx = _addNode( pos, width, depth, normal );
regenerate();
setMaskBits( NodeMask | RegenMask );
return idx;
}
void MeshRoad::buildNodesFromList( MeshRoadNodeList* list )
{
mNodes.clear();
for (U32 i=0; i<list->mPositions.size(); ++i)
{
_addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->m
Normals[i] );
}
_regenerate();
}
U32 MeshRoad::insertNode( const Point3F &pos, const F32 &width, const F32 &depth
, const VectorF &normal, const U32 &idx )
{
U32 ret = _insertNode( pos, width, depth, normal, idx );
regenerate();
setMaskBits( NodeMask | RegenMask );
return ret;
}
void MeshRoad::setNode( const Point3F &pos, const F32 &width, const F32 &depth,
const VectorF &normal, const U32 &idx )
{
if ( mNodes.size() - 1 < idx )
return;
MeshRoadNode &node = mNodes[idx];
node.point = pos;
node.width = width;
node.depth = depth;
node.normal = normal;
regenerate();
setMaskBits( NodeMask | RegenMask );
}
void MeshRoad::setNodeWidth( U32 idx, F32 meters )
{
meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH );
if ( mNodes.size() - 1 < idx )
return;
mNodes[idx].width = meters;
_regenerate();
setMaskBits( RegenMask | NodeMask );
}
F32 MeshRoad::getNodeWidth( U32 idx )
{
if ( mNodes.size() - 1 < idx )
return -1.0f;
return mNodes[idx].width;
}
void MeshRoad::setNodeDepth( U32 idx, F32 meters )
{
meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH );
if ( mNodes.size() - 1 < idx )
return;
mNodes[idx].depth = meters;
_regenerate();
setMaskBits( MeshRoadMask | RegenMask | NodeMask );
}
F32 MeshRoad::getNodeDepth( U32 idx )
{
if ( mNodes.size() - 1 < idx )
return -1.0f;
return mNodes[idx].depth;
}
MatrixF MeshRoad::getNodeTransform( U32 idx )
{
MatrixF mat(true);
if ( mNodes.size() - 1 < idx )
return mat;
bool hasNext = idx + 1 < mNodes.size();
bool hasPrev = (S32)idx - 1 > 0;
const MeshRoadNode &node = mNodes[idx];
VectorF fvec( 0, 1, 0 );
if ( hasNext )
{
fvec = mNodes[idx+1].point - node.point;
fvec.normalizeSafe();
}
else if ( hasPrev )
{
fvec = node.point - mNodes[idx-1].point;
fvec.normalizeSafe();
}
else
fvec = mPerp( node.normal );
if ( fvec.isZero() )
fvec = mPerp( node.normal );
F32 dot = mDot( fvec, node.normal );
if ( dot < -0.9f || dot > 0.9f )
fvec = mPerp( node.normal );
VectorF rvec = mCross( fvec, node.normal );
if ( rvec.isZero() )
rvec = mPerp( fvec );
rvec.normalize();
fvec = mCross( node.normal, rvec );
fvec.normalize();
mat.setColumn( 0, rvec );
mat.setColumn( 1, fvec );
mat.setColumn( 2, node.normal );
mat.setColumn( 3, node.point );
AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
return mat;
}
void MeshRoad::calcSliceTransform( U32 idx, MatrixF &mat )
{
if ( mSlices.size() - 1 < idx )
return;
bool hasNext = idx + 1 < mSlices.size();
bool hasPrev = (S32)idx - 1 >= 0;
const MeshRoadSlice &slice = mSlices[idx];
VectorF fvec( 0, 1, 0 );
if ( hasNext )
{
fvec = mSlices[idx+1].p1 - slice.p1;
fvec.normalizeSafe();
}
else if ( hasPrev )
{
fvec = slice.p1 - mSlices[idx-1].p1;
fvec.normalizeSafe();
}
else
fvec = mPerp( slice.normal );
if ( fvec.isZero() )
fvec = mPerp( slice.normal );
F32 dot = mDot( fvec, slice.normal );
if ( dot < -0.9f || dot > 0.9f )
fvec = mPerp( slice.normal );
VectorF rvec = mCross( fvec, slice.normal );
if ( rvec.isZero() )
rvec = mPerp( fvec );
rvec.normalize();
fvec = mCross( slice.normal, rvec );
fvec.normalize();
mat.setColumn( 0, rvec );
mat.setColumn( 1, fvec );
mat.setColumn( 2, slice.normal );
mat.setColumn( 3, slice.p1 );
AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
}
F32 MeshRoad::getRoadLength() const
{
F32 length = 0.0f;
for ( U32 i = 0; i < mSegments.size(); i++ )
{
length += mSegments[i].length();
}
return length;
}
void MeshRoad::deleteNode( U32 idx )
{
if ( mNodes.size() - 1 < idx )
return;
mNodes.erase(idx);
_regenerate();
setMaskBits( RegenMask | NodeMask );
}
U32 MeshRoad::_addNode( const Point3F &pos, const F32 &width, const F32 &depth,
const VectorF &normal )
{
mNodes.increment();
MeshRoadNode &node = mNodes.last();
node.point = pos;
node.width = width;
node.depth = depth;
node.normal = normal;
setMaskBits( NodeMask | RegenMask );
return mNodes.size() - 1;
}
U32 MeshRoad::_insertNode( const Point3F &pos, const F32 &width, const F32 &dept
h, const VectorF &normal, const U32 &idx )
{
U32 ret;
MeshRoadNode *node;
if ( idx == U32_MAX )
{
mNodes.increment();
node = &mNodes.last();
ret = mNodes.size() - 1;
}
else
{
mNodes.insert( idx );
node = &mNodes[idx];
ret = idx;
}
node->point = pos;
node->depth = depth;
node->width = width;
node->normal = normal;
return ret;
}
bool MeshRoad::collideRay( const Point3F &origin, const Point3F &direction, U32
*nodeIdx, Point3F *collisionPnt )
{
Point3F p0 = origin;
Point3F p1 = origin + direction * 2000.0f;
// If the line segment does not collide with the MeshRoad's world box,
// it definitely does not collide with any part of the river.
if ( !getWorldBox().collideLine( p0, p1 ) )
return false;
if ( mSlices.size() < 2 )
return false;
MathUtils::Quad quad;
MathUtils::Ray ray;
F32 t;
// Check each road segment (formed by a pair of slices) for collision
// with the line segment.
for ( U32 i = 0; i < mSlices.size() - 1; i++ )
{
const MeshRoadSlice &slice0 = mSlices[i];
const MeshRoadSlice &slice1 = mSlices[i+1];
// For simplicities sake we will only test for collision between the
// line segment and the Top face of the river segment.
// Clockwise starting with the leftmost/closest point.
quad.p00 = slice0.p0;
quad.p01 = slice1.p0;
quad.p11 = slice1.p2;
quad.p10 = slice0.p2;
ray.origin = origin;
ray.direction = direction;
if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) )
{
if ( nodeIdx )
*nodeIdx = slice0.parentNodeIdx;
if ( collisionPnt )
*collisionPnt = ray.origin + ray.direction * t;
return true;
}
}
return false;
}
void MeshRoad::regenerate()
{
_regenerate();
setMaskBits( RegenMask );
}
//-------------------------------------------------------------------------
// Console Methods
//-------------------------------------------------------------------------
DefineEngineMethod( MeshRoad, setNodeDepth, void, ( S32 idx, F32 meters ),,
"Intended as a helper to developers and editor scripts.\n"
"Sets the depth in meters of a particular node."
)
{
object->setNodeDepth( idx, meters );
}
DefineEngineMethod( MeshRoad, regenerate, void, (),,
"Intended as a helper to developers and editor scripts.\n"
"Force MeshRoad to recreate its geometry."
)
{
object->regenerate();
}
DefineEngineMethod( MeshRoad, postApply, void, (),,
"Intended as a helper to developers and editor scripts.\n"
"Force trigger an inspectPostApply. This will transmit "
"material and other fields ( not including nodes ) to client
objects."
)
{
object->inspectPostApply();
}

You might also like