meshRoad.cpp

Engine/source/environment/meshRoad.cpp

More...

Classes:

Public Defines

define
define
define
define

Public Variables

gIdxArray [6][2][3]

Public Functions

ConsoleDocClass(MeshRoad , "@brief A strip of rectangular mesh segments defined by a 3D spline " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> prototyping road-shaped objects in your <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scene.\n\n</a>" "User may <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> width and depth per node, overall spline shape in three " " dimensions, and seperate Materials <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rendering the top, bottom , and side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">surfaces.\n\n</a>" "<a href="/coding/class/classmeshroad/">MeshRoad</a> is not capable of handling intersections, branches , curbs , or other " "desirable features in a final 'road' asset and is therefore intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> " "prototyping and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">experimentation.\n\n</a>" "Materials assigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/class/classmeshroad/">MeshRoad</a> should tile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vertically.\n\n</a>" " @ingroup Terrain" )
ConsoleDocClass(MeshRoadNodeEvent , "@brief Sends messages <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the Mesh Road <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(MeshRoad , postApply , void , () , "Intended as a helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Force trigger an inspectPostApply. This will transmit " "material and other fields ( not including nodes ) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> client objects." )
DefineEngineMethod(MeshRoad , regenerate , void , () , "Intended as a helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Force <a href="/coding/class/classmeshroad/">MeshRoad</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> recreate its geometry." )
DefineEngineMethod(MeshRoad , setNodeDepth , void , (S32 idx, F32 meters) , "Intended as a helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Sets the depth in meters of a particular node." )

Detailed Description

Public Defines

MAX_NODE_DEPTH() 50.0f
MAX_NODE_WIDTH() 50.0f
MIN_METERS_PER_SEGMENT() 1.0f
MIN_NODE_DEPTH() 0.25f
MIN_NODE_WIDTH() 0.25f

Public Variables

U32 gIdxArray [6][2][3]

Public Functions

compareHitSegments(const void * a, const void * b)

ConsoleDocClass(MeshRoad , "@brief A strip of rectangular mesh segments defined by a 3D spline " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> prototyping road-shaped objects in your <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scene.\n\n</a>" "User may <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> width and depth per node, overall spline shape in three " " dimensions, and seperate Materials <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rendering the top, bottom , and side <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">surfaces.\n\n</a>" "<a href="/coding/class/classmeshroad/">MeshRoad</a> is not capable of handling intersections, branches , curbs , or other " "desirable features in a final 'road' asset and is therefore intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> " "prototyping and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">experimentation.\n\n</a>" "Materials assigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/class/classmeshroad/">MeshRoad</a> should tile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vertically.\n\n</a>" " @ingroup Terrain" )

ConsoleDocClass(MeshRoadNodeEvent , "@brief Sends messages <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the Mesh Road <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )

DefineEngineMethod(MeshRoad , postApply , void , () , "Intended as a helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Force trigger an inspectPostApply. This will transmit " "material and other fields ( not including nodes ) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> client objects." )

DefineEngineMethod(MeshRoad , regenerate , void , () , "Intended as a helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Force <a href="/coding/class/classmeshroad/">MeshRoad</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> recreate its geometry." )

DefineEngineMethod(MeshRoad , setNodeDepth , void , (S32 idx, F32 meters) , "Intended as a helper <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> developers and editor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scripts.\n</a>" "Sets the depth in meters of a particular node." )

IMPLEMENT_CO_NETEVENT_V1(MeshRoadNodeEvent )

IMPLEMENT_CO_NETOBJECT_V1(MeshRoad )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "environment/meshRoad.h"
  26
  27#include "console/consoleTypes.h"
  28#include "console/engineAPI.h"
  29#include "util/catmullRom.h"
  30#include "math/util/quadTransforms.h"
  31#include "scene/simPath.h"
  32#include "scene/sceneRenderState.h"
  33#include "scene/sceneManager.h"
  34#include "scene/sgUtil.h"
  35#include "renderInstance/renderPassManager.h"
  36#include "T3D/gameBase/gameConnection.h"
  37#include "core/stream/bitStream.h"
  38#include "gfx/gfxDrawUtil.h"
  39#include "gfx/gfxTransformSaver.h"
  40#include "gfx/primBuilder.h"
  41#include "gfx/gfxDebugEvent.h"
  42#include "materials/materialManager.h"
  43#include "math/mathIO.h"
  44#include "math/mathUtils.h"
  45#include "math/util/frustum.h"
  46#include "gui/3d/guiTSControl.h"
  47#include "materials/shaderData.h"
  48#include "gfx/sim/gfxStateBlockData.h"
  49#include "gfx/sim/debugDraw.h"
  50#include "collision/concretePolyList.h"
  51#include "T3D/physics/physicsPlugin.h"
  52#include "T3D/physics/physicsBody.h"
  53#include "T3D/physics/physicsCollision.h"
  54#include "environment/nodeListManager.h"
  55
  56#define MIN_METERS_PER_SEGMENT 1.0f
  57#define MIN_NODE_DEPTH 0.25f
  58#define MAX_NODE_DEPTH 50.0f
  59#define MIN_NODE_WIDTH 0.25f
  60#define MAX_NODE_WIDTH 50.0f
  61
  62
  63static U32 gIdxArray[6][2][3] = {
  64   { { 0, 4, 5 }, { 0, 5, 1 }, },   // Top Face
  65   { { 2, 6, 4 }, { 2, 4, 0 }, },   // Left Face
  66   { { 1, 5, 7 }, { 1, 7, 3 }, },   // Right Face
  67   { { 2, 3, 7 }, { 2, 7, 6 }, },   // Bottom Face
  68   { { 0, 1, 3 }, { 0, 3, 2 }, },   // Front Face
  69   { { 4, 6, 7 }, { 4, 7, 5 }, },   // Back Face
  70};
  71
  72static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b)
  73{
  74   const MeshRoadHitSegment *fa = (MeshRoadHitSegment*)a;
  75   const MeshRoadHitSegment *fb = (MeshRoadHitSegment*)b;
  76
  77   return mSign(fb->t - fa->t);
  78}
  79
  80
  81//-----------------------------------------------------------------------------
  82// MeshRoadNodeList Struct
  83//-----------------------------------------------------------------------------
  84
  85struct MeshRoadNodeList : public NodeListManager::NodeList
  86{
  87   Vector<Point3F>   mPositions;
  88   Vector<F32>       mWidths;
  89   Vector<F32>       mDepths;
  90   Vector<VectorF>   mNormals;
  91
  92   MeshRoadNodeList() { }
  93   virtual ~MeshRoadNodeList() { }
  94};
  95
  96//-----------------------------------------------------------------------------
  97// MeshRoadNodeEvent Class
  98//-----------------------------------------------------------------------------
  99
 100class MeshRoadNodeEvent : public NodeListEvent
 101{
 102   typedef NodeListEvent Parent;
 103
 104public:
 105   Vector<Point3F>   mPositions;
 106   Vector<F32>       mWidths;
 107   Vector<F32>       mDepths;
 108   Vector<VectorF>   mNormals;
 109
 110public:
 111   MeshRoadNodeEvent() { mNodeList = NULL; }
 112   virtual ~MeshRoadNodeEvent() { }
 113
 114   virtual void pack(NetConnection*, BitStream*);
 115   virtual void unpack(NetConnection*, BitStream*);
 116
 117   virtual void copyIntoList(NodeListManager::NodeList* copyInto);
 118   virtual void padListToSize();
 119
 120   DECLARE_CONOBJECT(MeshRoadNodeEvent);
 121};
 122
 123void MeshRoadNodeEvent::pack(NetConnection* conn, BitStream* stream)
 124{
 125   Parent::pack( conn, stream );
 126
 127   stream->writeInt( mPositions.size(), 16 );
 128
 129   for (U32 i=0; i<mPositions.size(); ++i)
 130   {
 131      mathWrite( *stream, mPositions[i] );
 132      stream->write( mWidths[i] );
 133      stream->write( mDepths[i] );
 134      mathWrite( *stream, mNormals[i] );         
 135   }
 136}
 137
 138void MeshRoadNodeEvent::unpack(NetConnection* conn, BitStream* stream)
 139{
 140   mNodeList = new MeshRoadNodeList();
 141
 142   Parent::unpack( conn, stream );
 143
 144   U32 count = stream->readInt( 16 );
 145
 146   Point3F pos;
 147   F32 width, depth;
 148   VectorF normal;
 149
 150   MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
 151
 152   for (U32 i=0; i<count; ++i)
 153   {
 154      mathRead( *stream, &pos );
 155      stream->read( &width );   
 156      stream->read( &depth );
 157      mathRead( *stream, &normal );
 158
 159      list->mPositions.push_back( pos );
 160      list->mWidths.push_back( width );
 161      list->mDepths.push_back( depth );
 162      list->mNormals.push_back( normal );
 163   }
 164
 165   list->mTotalValidNodes = count;
 166
 167   // Do we have a complete list?
 168   if (list->mPositions.size() >= mTotalNodes)
 169      list->mListComplete = true;
 170}
 171
 172void MeshRoadNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto)
 173{
 174   MeshRoadNodeList* prevList = dynamic_cast<MeshRoadNodeList*>(copyInto);
 175   MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
 176
 177   // Merge our list with the old list.
 178   for (U32 i=<a href="/coding/class/classnodelistevent/#classnodelistevent_1a05350212961bce959f29916230c0209e">mLocalListStart</a>, index=0; i<mLocalListStart+list->mPositions.size(); ++i, ++index)
 179   {
 180      prevList->mPositions[i] = list->mPositions[index];
 181      prevList->mWidths[i] = list->mWidths[index];
 182      prevList->mDepths[i] = list->mDepths[index];
 183      prevList->mNormals[i] = list->mNormals[index];
 184   }
 185}
 186
 187void MeshRoadNodeEvent::padListToSize()
 188{
 189   MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
 190
 191   U32 totalValidNodes = list->mTotalValidNodes;
 192
 193   // Pad our list front?
 194   if (mLocalListStart)
 195   {
 196      MeshRoadNodeList* newlist = new MeshRoadNodeList();
 197      newlist->mPositions.increment(mLocalListStart);
 198      newlist->mWidths.increment(mLocalListStart);
 199      newlist->mDepths.increment(mLocalListStart);
 200      newlist->mNormals.increment(mLocalListStart);
 201
 202      newlist->mPositions.merge(list->mPositions);
 203      newlist->mWidths.merge(list->mWidths);
 204      newlist->mDepths.merge(list->mDepths);
 205      newlist->mNormals.merge(list->mNormals);
 206
 207      delete list;
 208      mNodeList = list = newlist;
 209   }
 210
 211   // Pad our list end?
 212   if (list->mPositions.size() < mTotalNodes)
 213   {
 214      U32 delta = mTotalNodes - list->mPositions.size();
 215      list->mPositions.increment(delta);
 216      list->mWidths.increment(delta);
 217      list->mDepths.increment(delta);
 218      list->mNormals.increment(delta);
 219   }
 220
 221   list->mTotalValidNodes = totalValidNodes;
 222}
 223
 224IMPLEMENT_CO_NETEVENT_V1(MeshRoadNodeEvent);
 225
 226ConsoleDocClass( MeshRoadNodeEvent,
 227   "@brief Sends messages to the Mesh Road Editor\n\n"
 228   "Editor use only.\n\n"
 229   "@internal"
 230);
 231
 232//-----------------------------------------------------------------------------
 233// MeshRoadNodeListNotify Class
 234//-----------------------------------------------------------------------------
 235
 236class MeshRoadNodeListNotify : public NodeListNotify
 237{
 238   typedef NodeListNotify Parent;
 239
 240protected:
 241   SimObjectPtr<MeshRoad> mRoad;
 242
 243public:
 244   MeshRoadNodeListNotify( MeshRoad* road, U32 listId ) { mRoad = road; mListId = listId; }
 245   virtual ~MeshRoadNodeListNotify() { mRoad = NULL; }
 246
 247   virtual void sendNotification( NodeListManager::NodeList* list );
 248};
 249
 250void MeshRoadNodeListNotify::sendNotification( NodeListManager::NodeList* list )
 251{
 252   if (mRoad.isValid())
 253   {
 254      // Build the road's nodes
 255      MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
 256      if (roadList)
 257         mRoad->buildNodesFromList( roadList );
 258   }
 259}
 260
 261//------------------------------------------------------------------------------
 262// MeshRoadConvex Class
 263//------------------------------------------------------------------------------
 264
 265const MatrixF& MeshRoadConvex::getTransform() const
 266{
 267   return MatrixF::Identity; //mObject->getTransform();    
 268}
 269
 270Box3F MeshRoadConvex::getBoundingBox() const
 271{
 272   return box;
 273}
 274
 275Box3F MeshRoadConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
 276{
 277   Box3F newBox = box;
 278   newBox.minExtents.convolve(scale);
 279   newBox.maxExtents.convolve(scale);
 280   mat.mul(newBox);
 281   return newBox;
 282}
 283
 284Point3F MeshRoadConvex::support(const VectorF& vec) const
 285{
 286   F32 bestDot = mDot( verts[0], vec );
 287
 288   const Point3F *bestP = &verts[0];
 289   for(S32 i=1; i<4; i++)
 290   {
 291      F32 newD = mDot(verts[i], vec);
 292      if(newD > bestDot)
 293      {
 294         bestDot = newD;
 295         bestP = &verts[i];
 296      }
 297   }
 298
 299   return *bestP;
 300}
 301
 302void MeshRoadConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
 303{
 304   cf->material = 0;
 305   cf->object = mObject;
 306
 307   // For a tetrahedron this is pretty easy... first
 308   // convert everything into world space.
 309   Point3F tverts[4];
 310   mat.mulP(verts[0], &tverts[0]);
 311   mat.mulP(verts[1], &tverts[1]);
 312   mat.mulP(verts[2], &tverts[2]);
 313   mat.mulP(verts[3], &tverts[3]);
 314
 315   // Points...
 316   S32 firstVert = cf->mVertexList.size();
 317   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0];
 318   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1];
 319   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2];
 320   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3];
 321
 322   // Edges...
 323   cf->mEdgeList.increment();
 324   cf->mEdgeList.last().vertex[0] = firstVert+0;
 325   cf->mEdgeList.last().vertex[1] = firstVert+1;
 326
 327   cf->mEdgeList.increment();
 328   cf->mEdgeList.last().vertex[0] = firstVert+1;
 329   cf->mEdgeList.last().vertex[1] = firstVert+2;
 330
 331   cf->mEdgeList.increment();
 332   cf->mEdgeList.last().vertex[0] = firstVert+2;
 333   cf->mEdgeList.last().vertex[1] = firstVert+0;
 334
 335   cf->mEdgeList.increment();
 336   cf->mEdgeList.last().vertex[0] = firstVert+3;
 337   cf->mEdgeList.last().vertex[1] = firstVert+0;
 338
 339   cf->mEdgeList.increment();
 340   cf->mEdgeList.last().vertex[0] = firstVert+3;
 341   cf->mEdgeList.last().vertex[1] = firstVert+1;
 342
 343   cf->mEdgeList.increment();
 344   cf->mEdgeList.last().vertex[0] = firstVert+3;
 345   cf->mEdgeList.last().vertex[1] = firstVert+2;
 346
 347   // Triangles...
 348   cf->mFaceList.increment();
 349   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]);
 350   cf->mFaceList.last().vertex[0] = firstVert+2;
 351   cf->mFaceList.last().vertex[1] = firstVert+1;
 352   cf->mFaceList.last().vertex[2] = firstVert+0;
 353
 354   cf->mFaceList.increment();
 355   cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]);
 356   cf->mFaceList.last().vertex[0] = firstVert+1;
 357   cf->mFaceList.last().vertex[1] = firstVert+0;
 358   cf->mFaceList.last().vertex[2] = firstVert+3;
 359
 360   cf->mFaceList.increment();
 361   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]);
 362   cf->mFaceList.last().vertex[0] = firstVert+2;
 363   cf->mFaceList.last().vertex[1] = firstVert+1;
 364   cf->mFaceList.last().vertex[2] = firstVert+3;
 365
 366   cf->mFaceList.increment();
 367   cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]);
 368   cf->mFaceList.last().vertex[0] = firstVert+0;
 369   cf->mFaceList.last().vertex[1] = firstVert+2;
 370   cf->mFaceList.last().vertex[2] = firstVert+3;
 371}
 372
 373
 374void MeshRoadConvex::getPolyList( AbstractPolyList* list )
 375{
 376   // Transform the list into object space and set the pointer to the object
 377   //MatrixF i( mObject->getTransform() );
 378   //Point3F iS( mObject->getScale() );
 379   //list->setTransform(&i, iS);
 380
 381   list->setTransform( &MatrixF::Identity, Point3F::One );
 382   list->setObject(mObject);
 383
 384   // Points...
 385   S32 base =  list->addPoint(verts[1]);
 386   list->addPoint(verts[2]);
 387   list->addPoint(verts[0]);
 388   list->addPoint(verts[3]);
 389
 390   // Planes...
 391   list->begin(0,0);
 392   list->vertex(base + 2);
 393   list->vertex(base + 1);
 394   list->vertex(base + 0);
 395   list->plane(base + 2, base + 1, base + 0);
 396   list->end();
 397   list->begin(0,0);
 398   list->vertex(base + 2);
 399   list->vertex(base + 1);
 400   list->vertex(base + 3);
 401   list->plane(base + 2, base + 1, base + 3);
 402   list->end();
 403   list->begin(0,0);
 404   list->vertex(base + 3);
 405   list->vertex(base + 1);
 406   list->vertex(base + 0);
 407   list->plane(base + 3, base + 1, base + 0);
 408   list->end();
 409   list->begin(0,0);
 410   list->vertex(base + 2);
 411   list->vertex(base + 3);
 412   list->vertex(base + 0);
 413   list->plane(base + 2, base + 3, base + 0);
 414   list->end();
 415}
 416
 417
 418//------------------------------------------------------------------------------
 419// MeshRoadSegment Class
 420//------------------------------------------------------------------------------
 421
 422MeshRoadSegment::MeshRoadSegment()
 423{
 424   mPlaneCount = 0;
 425   columns = 0;
 426   rows = 0;
 427   numVerts = 0;
 428   numTriangles = 0;
 429
 430   startVert = 0;
 431   endVert = 0;
 432   startIndex = 0;
 433   endIndex = 0;
 434
 435   slice0 = NULL;
 436   slice1 = NULL;
 437}
 438
 439MeshRoadSegment::MeshRoadSegment( MeshRoadSlice *rs0, MeshRoadSlice *rs1, const MatrixF &roadMat )
 440{
 441   columns = 0;
 442   rows = 0;
 443   numVerts = 0;
 444   numTriangles = 0;
 445
 446   startVert = 0;
 447   endVert = 0;
 448   startIndex = 0;
 449   endIndex = 0;
 450
 451   slice0 = rs0;
 452   slice1 = rs1;
 453
 454   // Calculate the bounding box(s)
 455   worldbounds.minExtents = worldbounds.maxExtents = rs0->p0;
 456   worldbounds.extend( rs0->p2 );
 457   worldbounds.extend( rs0->pb0 );
 458   worldbounds.extend( rs0->pb2 );
 459   worldbounds.extend( rs1->p0 );
 460   worldbounds.extend( rs1->p2 );
 461   worldbounds.extend( rs1->pb0 );
 462   worldbounds.extend( rs1->pb2 );
 463
 464   objectbounds = worldbounds;
 465   roadMat.mul( objectbounds );
 466
 467   // Calculate the planes for this segment
 468   // Will be used for intersection/buoyancy tests
 469
 470   mPlaneCount = 6;
 471   mPlanes[0].set( slice0->pb0, slice0->p0, slice1->p0 ); // left   
 472   mPlanes[1].set( slice1->pb2, slice1->p2, slice0->p2 ); // right   
 473   mPlanes[2].set( slice0->pb2, slice0->p2, slice0->p0 ); // near   
 474   mPlanes[3].set( slice1->p0, slice1->p2, slice1->pb2 ); // far   
 475   mPlanes[4].set( slice1->p2, slice1->p0, slice0->p0 ); // top   
 476   mPlanes[5].set( slice0->pb0, slice1->pb0, slice1->pb2 ); // bottom
 477}
 478
 479void MeshRoadSegment::set( MeshRoadSlice *rs0, MeshRoadSlice *rs1 )
 480{
 481   columns = 0;
 482   rows = 0;
 483   numVerts = 0;
 484   numTriangles = 0;
 485
 486   startVert = 0;
 487   endVert = 0;
 488   startIndex = 0;
 489   endIndex = 0;
 490
 491   slice0 = rs0;
 492   slice1 = rs1;
 493}
 494
 495bool MeshRoadSegment::intersectBox( const Box3F &bounds ) const
 496{
 497   // This code copied from Frustum class.
 498
 499   Point3F maxPoint;
 500   F32 maxDot;
 501
 502   // Note the planes are ordered left, right, near, 
 503   // far, top, bottom for getting early rejections
 504   // from the typical horizontal scene.
 505   for ( S32 i = 0; i < mPlaneCount; i++ )
 506   {
 507      // This is pretty much as optimal as you can
 508      // get for a plane vs AABB test...
 509      // 
 510      // 4 comparisons
 511      // 3 multiplies
 512      // 2 adds
 513      // 1 negation
 514      //
 515      // It will early out as soon as it detects the
 516      // bounds is outside one of the planes.
 517
 518      if ( mPlanes[i].x > 0 )
 519         maxPoint.x = bounds.maxExtents.x;
 520      else
 521         maxPoint.x = bounds.minExtents.x;
 522
 523      if ( mPlanes[i].y > 0 )
 524         maxPoint.y = bounds.maxExtents.y;
 525      else
 526         maxPoint.y = bounds.minExtents.y;
 527
 528      if ( mPlanes[i].z > 0 )
 529         maxPoint.z = bounds.maxExtents.z;
 530      else
 531         maxPoint.z = bounds.minExtents.z;
 532
 533      maxDot = mDot( maxPoint, mPlanes[ i ] );
 534
 535      if ( maxDot <= -mPlanes[ i ].d )
 536         return false;
 537   }
 538
 539   return true;
 540}
 541
 542bool MeshRoadSegment::containsPoint( const Point3F &pnt ) const
 543{
 544   // This code from Frustum class.
 545
 546   F32 maxDot;
 547
 548   // Note the planes are ordered left, right, near, 
 549   // far, top, bottom for getting early rejections
 550   // from the typical horizontal scene.
 551   for ( S32 i = 0; i < mPlaneCount; i++ )
 552   {
 553      const PlaneF &plane = mPlanes[ i ];
 554
 555      // This is pretty much as optimal as you can
 556      // get for a plane vs point test...
 557      // 
 558      // 1 comparison
 559      // 2 multiplies
 560      // 1 adds
 561      //
 562      // It will early out as soon as it detects the
 563      // point is outside one of the planes.
 564
 565      maxDot = mDot( pnt, plane ) + plane.d;
 566      if ( maxDot < 0.0f )
 567         return false;
 568   }
 569
 570   return true;
 571}
 572
 573F32 MeshRoadSegment::distanceToSurface(const Point3F &pnt) const
 574{
 575   return mPlanes[4].distToPlane( pnt );
 576}
 577
 578//------------------------------------------------------------------------------
 579// MeshRoad Class
 580//------------------------------------------------------------------------------
 581
 582ConsoleDocClass( MeshRoad,
 583   "@brief A strip of rectangular mesh segments defined by a 3D spline "
 584   "for prototyping road-shaped objects in your scene.\n\n"
 585   
 586   "User may control width and depth per node, overall spline shape in three "
 587   "dimensions, and seperate Materials for rendering the top, bottom, and side surfaces.\n\n"
 588   
 589   "MeshRoad is not capable of handling intersections, branches, curbs, or other "
 590   "desirable features in a final 'road' asset and is therefore intended for "
 591   "prototyping and experimentation.\n\n"
 592
 593   "Materials assigned to MeshRoad should tile vertically.\n\n"
 594
 595   "@ingroup Terrain"
 596);
 597
 598bool MeshRoad::smEditorOpen = false;
 599bool MeshRoad::smShowBatches = false;
 600bool MeshRoad::smShowSpline = true;
 601bool MeshRoad::smShowRoad = true;
 602bool MeshRoad::smWireframe = true;
 603SimObjectPtr<SimSet> MeshRoad::smServerMeshRoadSet = NULL;
 604
 605GFXStateBlockRef MeshRoad::smWireframeSB;
 606
 607IMPLEMENT_CO_NETOBJECT_V1(MeshRoad);
 608
 609MeshRoad::MeshRoad()
 610: mTextureLength( 5.0f ),
 611  mBreakAngle( 3.0f ),
 612  mPhysicsRep( NULL ),
 613  mWidthSubdivisions( 0 )
 614{
 615   mConvexList = new Convex;
 616
 617   // Setup NetObject.
 618   mTypeMask |= StaticObjectType | StaticShapeObjectType;
 619   mNetFlags.set(Ghostable);
 620
 621   mMatInst[Top] = NULL;
 622   mMatInst[Bottom] = NULL;
 623   mMatInst[Side] = NULL;
 624}
 625
 626MeshRoad::~MeshRoad()
 627{   
 628   delete mConvexList;
 629   mConvexList = NULL;
 630}
 631
 632void MeshRoad::initPersistFields()
 633{
 634   addGroup( "MeshRoad" );
 635
 636      addField( "topMaterial", TypeMaterialName, Offset( mMaterialName[Top], MeshRoad ),
 637         "Material for the upper surface of the road." );
 638
 639      addField( "bottomMaterial", TypeMaterialName, Offset( mMaterialName[Bottom], MeshRoad ),
 640         "Material for the bottom surface of the road." );
 641
 642      addField( "sideMaterial", TypeMaterialName, Offset( mMaterialName[Side], MeshRoad ),
 643         "Material for the left, right, front, and back surfaces of the road." );
 644
 645      addField( "textureLength", TypeF32, Offset( mTextureLength, MeshRoad ), 
 646         "The length in meters of textures mapped to the MeshRoad." );      
 647
 648      addField( "breakAngle", TypeF32, Offset( mBreakAngle, MeshRoad ), 
 649         "Angle in degrees - MeshRoad will subdivide the spline if its curve is greater than this threshold." ); 
 650
 651      addField( "widthSubdivisions", TypeS32, Offset( mWidthSubdivisions, MeshRoad ), 
 652         "Subdivide segments widthwise this many times when generating vertices." );
 653
 654   endGroup( "MeshRoad" );
 655
 656   addGroup( "Internal" );
 657
 658      addProtectedField( "Node", TypeString, NULL, &addNodeFromField, &emptyStringProtectedGetFn, 
 659         "Do not modify, for internal use." );
 660
 661   endGroup( "Internal" );
 662
 663   Parent::initPersistFields();
 664}
 665
 666void MeshRoad::consoleInit()
 667{
 668   Parent::consoleInit();
 669
 670   Con::addVariable( "$MeshRoad::EditorOpen", TypeBool, &MeshRoad::smEditorOpen, "True if the MeshRoad editor is open, otherwise false.\n"
 671      "@ingroup Editors\n");
 672   Con::addVariable( "$MeshRoad::wireframe", TypeBool, &MeshRoad::smWireframe, "If true, will render the wireframe of the road.\n"
 673      "@ingroup Editors\n");
 674   Con::addVariable( "$MeshRoad::showBatches", TypeBool, &MeshRoad::smShowBatches, "Determines if the debug rendering of the batches cubes is displayed or not.\n"
 675      "@ingroup Editors\n");
 676   Con::addVariable( "$MeshRoad::showSpline", TypeBool, &MeshRoad::smShowSpline, "If true, the spline on which the curvature of this road is based will be rendered.\n"
 677      "@ingroup Editors\n");
 678   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"
 679      "@ingroup Editors\n");
 680}
 681
 682bool MeshRoad::addNodeFromField( void *object, const char *index, const char *data )
 683{
 684   MeshRoad *pObj = static_cast<MeshRoad*>(object);
 685
 686   //if ( !pObj->isProperlyAdded() )
 687   //{      
 688   F32 width, depth;
 689   Point3F pos, normal;      
 690   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 );      
 691   if ( result == 8 )
 692      pObj->_addNode( pos, width, depth, normal );      
 693   //}
 694
 695   return false;
 696}
 697
 698bool MeshRoad::onAdd()
 699{
 700   if ( !Parent::onAdd() ) 
 701      return false;
 702
 703   // Reset the World Box.
 704   //setGlobalBounds();
 705   resetWorldBox();
 706
 707   // Set the Render Transform.
 708   setRenderTransform(mObjToWorld);
 709
 710   // Add to ServerMeshRoadSet
 711   if ( isServerObject() )
 712   {
 713      getServerSet()->addObject( this );      
 714   }
 715
 716   if ( isClientObject() )
 717      _initMaterial();
 718
 719   // Generate the Vert/Index buffers and everything else.
 720   _regenerate();
 721
 722   // Add to Scene.
 723   addToScene();
 724
 725   return true;
 726}
 727
 728void MeshRoad::onRemove()
 729{
 730   SAFE_DELETE( mPhysicsRep );
 731
 732   mConvexList->nukeList();
 733
 734   for ( U32 i = 0; i < SurfaceCount; i++ )
 735   {
 736      SAFE_DELETE( mMatInst[i] );
 737   }
 738
 739   removeFromScene();
 740   Parent::onRemove();
 741}
 742
 743void MeshRoad::inspectPostApply()
 744{
 745   // Set Parent.
 746   Parent::inspectPostApply();
 747
 748   //if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT )
 749   //   mMetersPerSegment = MIN_METERS_PER_SEGMENT;
 750
 751   setMaskBits(MeshRoadMask);
 752}
 753
 754void MeshRoad::onStaticModified( const char* slotName, const char*newValue )
 755{
 756   Parent::onStaticModified( slotName, newValue );
 757
 758   if ( dStricmp( slotName, "breakAngle" ) == 0 )
 759   {
 760      setMaskBits( RegenMask );
 761   }
 762}
 763
 764void MeshRoad::writeFields( Stream &stream, U32 tabStop )
 765{
 766   Parent::writeFields( stream, tabStop );
 767
 768   // Now write all nodes
 769
 770   stream.write(2, "\r\n");   
 771
 772   for ( U32 i = 0; i < mNodes.size(); i++ )
 773   {
 774      const MeshRoadNode &node = mNodes[i];
 775
 776      stream.writeTabs(tabStop);
 777
 778      char buffer[1024];
 779      dMemset( buffer, 0, 1024 );
 780      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.normal.y, node.normal.z );      
 781      stream.writeLine( (const U8*)buffer );
 782   }
 783}
 784
 785bool MeshRoad::writeField( StringTableEntry fieldname, const char *value )
 786{   
 787   if ( fieldname == StringTable->insert("Node") )
 788      return false;
 789
 790   return Parent::writeField( fieldname, value );
 791}
 792
 793void MeshRoad::onEditorEnable()
 794{
 795}
 796
 797void MeshRoad::onEditorDisable()
 798{
 799}
 800
 801SimSet* MeshRoad::getServerSet()
 802{
 803   if ( !smServerMeshRoadSet )
 804   {
 805      smServerMeshRoadSet = new SimSet();
 806      smServerMeshRoadSet->registerObject( "ServerMeshRoadSet" );
 807      Sim::getRootGroup()->addObject( smServerMeshRoadSet );
 808   }
 809
 810   return smServerMeshRoadSet;
 811}
 812
 813void MeshRoad::prepRenderImage( SceneRenderState* state )
 814{
 815   if ( mNodes.size() <= 1 )
 816      return;
 817
 818   RenderPassManager *renderPass = state->getRenderPass();
 819   
 820   // Normal Road RenderInstance
 821   // Always rendered when the editor is not open
 822   // otherwise obey the smShowRoad flag
 823   if ( smShowRoad || !smEditorOpen )
 824   {
 825      MeshRenderInst coreRI;
 826      coreRI.clear();
 827      coreRI.objectToWorld = &MatrixF::Identity;
 828      coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
 829      coreRI.projection = renderPass->allocSharedXform(RenderPassManager::Projection);
 830      coreRI.type = RenderPassManager::RIT_Mesh;      
 831      
 832      BaseMatInstance *matInst;
 833      for ( U32 i = 0; i < SurfaceCount; i++ )
 834      {             
 835         matInst = state->getOverrideMaterial( mMatInst[i] );   
 836         if ( !matInst )
 837            continue;
 838
 839         // Get the lights if we haven't already.
 840         if ( matInst->isForwardLit() && !coreRI.lights[0] )
 841         {
 842            LightQuery query;
 843            query.init( getWorldSphere() );
 844            query.getLights( coreRI.lights, 8 );
 845         }
 846
 847         MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
 848         *ri = coreRI;
 849
 850         // Currently rendering whole road, fix to cull and batch
 851         // per segment.
 852
 853         // Set the correct material for rendering.
 854         ri->matInst = matInst;
 855         ri->vertBuff = &mVB[i];
 856         ri->primBuff = &mPB[i];
 857
 858         ri->prim = renderPass->allocPrim();
 859         ri->prim->type = GFXTriangleList;
 860         ri->prim->minIndex = 0;
 861         ri->prim->startIndex = 0;
 862         ri->prim->numPrimitives = mTriangleCount[i];
 863         ri->prim->startVertex = 0;
 864         ri->prim->numVertices = mVertCount[i];
 865
 866         // We sort by the material then vertex buffer.
 867         ri->defaultKey = matInst->getStateHint();
 868         ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Not 64bit safe!
 869
 870         renderPass->addInst( ri );  
 871      }
 872   }
 873
 874   // Debug RenderInstance
 875   // Only when editor is open.
 876   if ( smEditorOpen )
 877   {
 878      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
 879      ri->renderDelegate.bind( this, &MeshRoad::_debugRender );
 880      ri->type = RenderPassManager::RIT_Editor;
 881      state->getRenderPass()->addInst( ri );
 882   }
 883}
 884
 885void MeshRoad::_initMaterial()
 886{
 887   for ( U32 i = 0; i < SurfaceCount; i++ )
 888   {
 889      if ( mMatInst[i] )
 890         SAFE_DELETE( mMatInst[i] );
 891
 892      if ( mMaterial[i] )
 893         mMatInst[i] = mMaterial[i]->createMatInstance();
 894      else
 895         mMatInst[i] = MATMGR->createMatInstance( "WarningMaterial" );
 896
 897      mMatInst[i]->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>() );
 898   }
 899}
 900
 901void MeshRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* )
 902{
 903   //MeshRoadConvex convex;
 904   //buildConvex( Box3F(true), convex );
 905   //convex.render();   
 906   //GFXDrawUtil *drawer = GFX->getDrawUtil();
 907
 908   //GFX->setStateBlock( smStateBlock );
 909   return;
 910   /*
 911   U32 convexCount = mDebugConvex.size();
 912
 913   PrimBuild::begin( GFXTriangleList, convexCount * 12 );
 914   PrimBuild::color4i( 0, 0, 255, 155 );
 915
 916   for ( U32 i = 0; i < convexCount; i++ )
 917   {
 918      MeshRoadConvex *convex = mDebugConvex[i];
 919
 920      Point3F a = convex->verts[0];
 921      Point3F b = convex->verts[1];
 922      Point3F c = convex->verts[2];
 923      Point3F p = convex->verts[3];
 924
 925      //mObjToWorld.mulP(a);
 926      //mObjToWorld.mulP(b);
 927      //mObjToWorld.mulP(c);
 928      //mObjToWorld.mulP(p);
 929      
 930      PrimBuild::vertex3fv( c );
 931      PrimBuild::vertex3fv( b );
 932      PrimBuild::vertex3fv( a );      
 933
 934      PrimBuild::vertex3fv( b );
 935      PrimBuild::vertex3fv( a );
 936      PrimBuild::vertex3fv( p );
 937
 938      PrimBuild::vertex3fv( c );
 939      PrimBuild::vertex3fv( b );
 940      PrimBuild::vertex3fv( p );
 941
 942      PrimBuild::vertex3fv( a );
 943      PrimBuild::vertex3fv( c );
 944      PrimBuild::vertex3fv( p );
 945   }
 946
 947   PrimBuild::end();
 948
 949   for ( U32 i = 0; i < mSegments.size(); i++ )
 950   {
 951      ///GFX->getDrawUtil()->drawWireBox( mSegments[i].worldbounds, ColorI(255,0,0,255) );
 952   }
 953
 954   GFX->enterDebugEvent( ColorI( 255, 0, 0 ), "DecalRoad_debugRender" );
 955   GFXTransformSaver saver;
 956
 957   GFX->setStateBlock( smStateBlock );      
 958
 959   Point3F size(1,1,1);
 960   ColorI color( 255, 0, 0, 255 );
 961
 962   if ( smShowBatches )  
 963   {
 964      for ( U32 i = 0; i < mBatches.size(); i++ )   
 965      {
 966         const Box3F &box = mBatches[i].bounds;
 967         Point3F center;
 968         box.getCenter( &center );
 969
 970         GFX->getDrawUtil()->drawWireCube( ( box.maxExtents - box.minExtents ) * 0.5f, center, ColorI(255,100,100,255) );         
 971      }
 972   }
 973
 974   GFX->leaveDebugEvent();
 975   */
 976}
 977
 978U32 MeshRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
 979{  
 980   U32 retMask = Parent::packUpdate(con, mask, stream);
 981
 982   if ( stream->writeFlag( mask & MeshRoadMask ) )
 983   {
 984      // Write Object Transform.
 985      stream->writeAffineTransform( mObjToWorld );
 986
 987      // Write Materials      
 988      stream->write( mMaterialName[0] );      
 989      stream->write( mMaterialName[1] );
 990      stream->write( mMaterialName[2] );
 991
 992      stream->write( mTextureLength );      
 993      stream->write( mBreakAngle );
 994      stream->write( mWidthSubdivisions );
 995   }
 996
 997   if ( stream->writeFlag( mask & NodeMask ) )
 998   {
 999      const U32 nodeByteSize = 32; // Based on sending all of a node's parameters
1000
1001      // Test if we can fit all of our nodes within the current stream.
1002      // We make sure we leave 100 bytes still free in the stream for whatever
1003      // may follow us.
1004      S32 allowedBytes = stream->getWriteByteSize() - 100;
1005      if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) )
1006      {
1007         // All nodes should fit, so send them out now.
1008         stream->writeInt( mNodes.size(), 16 );
1009
1010         for ( U32 i = 0; i < mNodes.size(); i++ )
1011         {
1012            mathWrite( *stream, mNodes[i].point );
1013            stream->write( mNodes[i].width );
1014            stream->write( mNodes[i].depth );
1015            mathWrite( *stream, mNodes[i].normal );         
1016         }
1017      }
1018      else
1019      {
1020         // There isn't enough space left in the stream for all of the
1021         // nodes.  Batch them up into NetEvents.
1022         U32 id = gServerNodeListManager->nextListId();
1023         U32 count = 0;
1024         U32 index = 0;
1025         while (count < mNodes.size())
1026         {
1027            count += NodeListManager::smMaximumNodesPerEvent;
1028            if (count > mNodes.size())
1029            {
1030               count = mNodes.size();
1031            }
1032
1033            MeshRoadNodeEvent* event = new MeshRoadNodeEvent();
1034            event->mId = id;
1035            event->mTotalNodes = mNodes.size();
1036            event->mLocalListStart = index;
1037
1038            for (; index<count; ++index)
1039            {
1040               event->mPositions.push_back( mNodes[index].point );
1041               event->mWidths.push_back( mNodes[index].width );
1042               event->mDepths.push_back( mNodes[index].depth );
1043               event->mNormals.push_back( mNodes[index].normal );
1044            }
1045
1046            con->postNetEvent( event );
1047         }
1048
1049         stream->write( id );
1050      }
1051   }
1052
1053   stream->writeFlag( mask & RegenMask );
1054
1055   // Were done ...
1056   return retMask;
1057}
1058
1059void MeshRoad::unpackUpdate(NetConnection * con, BitStream * stream)
1060{
1061   // Unpack Parent.
1062   Parent::unpackUpdate(con, stream);
1063
1064   // MeshRoadMask
1065   if(stream->readFlag())
1066   {
1067      MatrixF     ObjectMatrix;
1068      stream->readAffineTransform(&ObjectMatrix);
1069      Parent::setTransform(ObjectMatrix);
1070
1071      // Read Materials...
1072      Material *pMat = NULL;
1073
1074      for ( U32 i = 0; i < SurfaceCount; i++ )
1075      {
1076         stream->read( &mMaterialName[i] );
1077        
1078         if ( !Sim::findObject( mMaterialName[i], pMat ) )
1079            Con::printf( "DecalRoad::unpackUpdate, failed to find Material of name %s", mMaterialName[i].c_str() );
1080         else         
1081            mMaterial[i] = pMat;         
1082      }
1083
1084      if ( isProperlyAdded() )
1085         _initMaterial(); 
1086
1087      stream->read( &mTextureLength );
1088
1089      stream->read( &mBreakAngle );
1090
1091      stream->read( &mWidthSubdivisions );
1092   }
1093
1094   // NodeMask
1095   if ( stream->readFlag() )
1096   {
1097      if (stream->readFlag())
1098      {
1099         // Nodes have been passed in this update
1100         U32 count = stream->readInt( 16 );
1101
1102         mNodes.clear();
1103
1104         Point3F pos, normal;
1105         F32 width, depth;
1106         for ( U32 i = 0; i < count; i++ )
1107         {
1108            mathRead( *stream, &pos );
1109            stream->read( &width );   
1110            stream->read( &depth );
1111            mathRead( *stream, &normal );
1112            _addNode( pos, width, depth, normal );           
1113         }
1114      }
1115      else
1116      {
1117         // Nodes will arrive as events
1118         U32 id;
1119         stream->read( &id );
1120
1121         // Check if the road's nodes made it here before we did.
1122         NodeListManager::NodeList* list = NULL;
1123         if ( gClientNodeListManager->findListById( id, &list, true) )
1124         {
1125            // Work with the completed list
1126            MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
1127            if (roadList)
1128               buildNodesFromList( roadList );
1129
1130            delete list;
1131         }
1132         else
1133         {
1134            // Nodes have not yet arrived, so register our interest in the list
1135            MeshRoadNodeListNotify* notify = new MeshRoadNodeListNotify( this, id );
1136            gClientNodeListManager->registerNotification( notify );
1137         }
1138      }
1139   }
1140
1141   if ( stream->readFlag() && isProperlyAdded() )   
1142      _regenerate();
1143}
1144
1145void MeshRoad::setTransform( const MatrixF &mat )
1146{
1147   for ( U32 i = 0; i < mNodes.size(); i++ )
1148   {
1149      mWorldToObj.mulP( mNodes[i].point );
1150      mat.mulP( mNodes[i].point );
1151   }
1152
1153   Parent::setTransform( mat );
1154
1155   if ( mPhysicsRep )
1156      mPhysicsRep->setTransform( mat );
1157
1158   // Regenerate and update the client
1159   _regenerate();
1160   setMaskBits( NodeMask | RegenMask );
1161}
1162
1163void MeshRoad::setScale( const VectorF &scale )
1164{
1165   // We ignore scale requests from the editor
1166   // right now.
1167
1168   //Parent::setScale( scale );
1169}
1170
1171void MeshRoad::buildConvex(const Box3F& box, Convex* convex)
1172{
1173   if ( mSlices.size() < 2 )
1174      return;
1175
1176   mConvexList->collectGarbage();
1177   mDebugConvex.clear();
1178
1179   Box3F realBox = box;
1180   mWorldToObj.mul(realBox);
1181   realBox.minExtents.convolveInverse(mObjScale);
1182   realBox.maxExtents.convolveInverse(mObjScale);
1183
1184   if (realBox.isOverlapped(getObjBox()) == false)
1185      return;
1186
1187   U32 segmentCount = mSegments.size();
1188
1189   // Create convex(s) for each segment
1190   for ( U32 i = 0; i < segmentCount; i++ )
1191   {
1192      const MeshRoadSegment &segment = mSegments[i];
1193
1194      // Is this segment overlapped?
1195      if ( !segment.getWorldBounds().isOverlapped( box ) )
1196         continue;
1197
1198      // Each segment has 6 faces
1199      for ( U32 j = 0; j < 6; j++ )
1200      {
1201         // Only first segment has front face
1202         if ( j == 4 && i != 0 )
1203            continue;
1204         // Only last segment has back face
1205         if ( j == 5 && i != segmentCount-1 )
1206            continue;
1207
1208         // Each face has 2 convex(s)
1209         for ( U32 k = 0; k < 2; k++ )
1210         {
1211            // See if this convex exists in the working set already...
1212            Convex* cc = 0;
1213            CollisionWorkingList& wl = convex->getWorkingList();
1214            for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) 
1215            {
1216               if ( itr->mConvex->getType() == MeshRoadConvexType )
1217               {
1218                  MeshRoadConvex *pConvex = static_cast<MeshRoadConvex*>(itr->mConvex);
1219
1220                  if ( pConvex->pRoad == this &&
1221                       pConvex->segmentId == i &&
1222                       pConvex->faceId == j &&
1223                       pConvex->triangleId == k )           
1224                  {
1225                     cc = itr->mConvex;
1226                     break;
1227                  }
1228               }
1229            }
1230            if (cc)
1231               continue;
1232
1233            // Get the triangle...
1234            U32 idx0 = gIdxArray[j][k][0];
1235            U32 idx1 = gIdxArray[j][k][1];
1236            U32 idx2 = gIdxArray[j][k][2];
1237
1238            Point3F a = segment[idx0];
1239            Point3F b = segment[idx1];
1240            Point3F c = segment[idx2];
1241            
1242            // Transform the result into object space!
1243            //mWorldToObj.mulP( a );
1244            //mWorldToObj.mulP( b );
1245            //mWorldToObj.mulP( c );            
1246
1247            PlaneF p( c, b, a );
1248            Point3F peak = ((a + b + c) / 3.0f) + (p * 0.15f);
1249
1250            // Set up the convex...
1251            MeshRoadConvex *cp = new MeshRoadConvex();
1252
1253            mConvexList->registerObject( cp );
1254            convex->addToWorkingList( cp );
1255
1256            cp->mObject    = this;
1257            cp->pRoad   = this;
1258            cp->segmentId = i;
1259            cp->faceId = j;
1260            cp->triangleId = k;
1261
1262            cp->normal = p;
1263            cp->verts[0] = c;
1264            cp->verts[1] = b;
1265            cp->verts[2] = a;
1266            cp->verts[3] = peak;
1267
1268            // Update the bounding box.
1269            Box3F &bounds = cp->box;
1270            bounds.minExtents.set( F32_MAX,  F32_MAX,  F32_MAX );
1271            bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX );
1272
1273            bounds.minExtents.setMin( a );
1274            bounds.minExtents.setMin( b );
1275            bounds.minExtents.setMin( c );
1276            bounds.minExtents.setMin( peak );
1277
1278            bounds.maxExtents.setMax( a );
1279            bounds.maxExtents.setMax( b );
1280            bounds.maxExtents.setMax( c );
1281            bounds.maxExtents.setMax( peak );
1282
1283            mDebugConvex.push_back(cp);
1284         }
1285      }
1286   }
1287}
1288
1289bool MeshRoad::buildPolyList( PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF & )
1290{
1291   if ( mSlices.size() < 2 )
1292      return false;
1293
1294   polyList->setTransform( &MatrixF::Identity, Point3F::One );
1295   polyList->setObject(this);
1296
1297   // JCF: optimize this to not always add everything.
1298
1299   return buildSegmentPolyList( polyList, 0, mSegments.size() - 1, true, true );
1300}
1301
1302bool MeshRoad::buildSegmentPolyList( AbstractPolyList* polyList, U32 startSegIdx, U32 endSegIdx, bool capFront, bool capEnd )
1303{
1304   if ( mSlices.size() < 2 )
1305      return false;
1306
1307   // Add verts
1308   for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
1309   {
1310      const MeshRoadSegment &seg = mSegments[i];
1311
1312      if ( i == startSegIdx )
1313      {
1314         polyList->addPoint( seg.slice0->p0 );
1315         polyList->addPoint( seg.slice0->p2 );
1316         polyList->addPoint( seg.slice0->pb0 );
1317         polyList->addPoint( seg.slice0->pb2 );
1318      }
1319
1320      polyList->addPoint( seg.slice1->p0 );
1321      polyList->addPoint( seg.slice1->p2 );
1322      polyList->addPoint( seg.slice1->pb0 );
1323      polyList->addPoint( seg.slice1->pb2 );
1324   }
1325
1326   // Temporaries to hold indices for the corner points of a quad.
1327   S32 p00, p01, p11, p10;
1328   S32 pb00, pb01, pb11, pb10;
1329   U32 offset = 0;
1330
1331   DebugDrawer *ddraw = NULL;//DebugDrawer::get();
1332   ClippedPolyList *cpolyList = dynamic_cast<ClippedPolyList*>(polyList);
1333   MatrixF mat;
1334   Point3F scale;
1335   if ( cpolyList )
1336      cpolyList->getTransform( &mat, &scale );
1337
1338
1339   for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
1340   {     
1341      p00 = offset;
1342      p10 = offset + 1;
1343      pb00 = offset + 2;
1344      pb10 = offset + 3;
1345      p01 = offset + 4;
1346      p11 = offset + 5;
1347      pb01 = offset + 6;
1348      pb11 = offset + 7;
1349
1350      // Top Face
1351
1352      polyList->begin( 0,0 );
1353      polyList->vertex( p00 );
1354      polyList->vertex( p01 );
1355      polyList->vertex( p11 );
1356      polyList->plane( p00, p01, p11 );
1357      polyList->end();
1358      if ( ddraw && cpolyList )
1359      {
1360         Point3F v0 = cpolyList->mVertexList[p00].point;
1361         mat.mulP( v0 );
1362         Point3F v1 = cpolyList->mVertexList[p01].point;
1363         mat.mulP( v1 );
1364         Point3F v2 = cpolyList->mVertexList[p11].point;
1365         mat.mulP( v2 );
1366         ddraw->drawTri( v0, v1, v2 );
1367         ddraw->setLastZTest( false );
1368         ddraw->setLastTTL( 0 );
1369      }
1370
1371      polyList->begin( 0,0 );
1372      polyList->vertex( p00 );
1373      polyList->vertex( p11 );
1374      polyList->vertex( p10 );
1375      polyList->plane( p00, p11, p10 );
1376      polyList->end();
1377      if ( ddraw && cpolyList )
1378      {
1379         ddraw->drawTri( cpolyList->mVertexList[p00].point, cpolyList->mVertexList[p11].point, cpolyList->mVertexList[p10].point );
1380         ddraw->setLastTTL( 0 );
1381      }
1382
1383      // Left Face
1384
1385      polyList->begin( 0,0 );
1386      polyList->vertex( pb00 );
1387      polyList->vertex( pb01 );
1388      polyList->vertex( p01 );
1389      polyList->plane( pb00, pb01, p01 );
1390      polyList->end();
1391
1392      polyList->begin( 0,0 );
1393      polyList->vertex( pb00 );
1394      polyList->vertex( p01 );
1395      polyList->vertex( p00 );
1396      polyList->plane( pb00, p01, p00 );
1397      polyList->end();   
1398
1399      // Right Face
1400
1401      polyList->begin( 0,0 );
1402      polyList->vertex( p10 );
1403      polyList->vertex( p11 );
1404      polyList->vertex( pb11 );
1405      polyList->plane( p10, p11, pb11 );
1406      polyList->end();
1407
1408      polyList->begin( 0,0 );
1409      polyList->vertex( p10 );
1410      polyList->vertex( pb11 );
1411      polyList->vertex( pb10 );
1412      polyList->plane( p10, pb11, pb10 );
1413      polyList->end();  
1414
1415      // Bottom Face
1416
1417      polyList->begin( 0,0 );
1418      polyList->vertex( pb00 );
1419      polyList->vertex( pb10 );
1420      polyList->vertex( pb11 );
1421      polyList->plane( pb00, pb10, pb11 );
1422      polyList->end();
1423
1424      polyList->begin( 0,0 );
1425      polyList->vertex( pb00 );
1426      polyList->vertex( pb11 );
1427      polyList->vertex( pb01 );
1428      polyList->plane( pb00, pb11, pb01 );
1429      polyList->end();  
1430
1431      // Front Face
1432
1433      if ( i == startSegIdx && capFront )
1434      {
1435         polyList->begin( 0,0 );
1436         polyList->vertex( p00 );
1437         polyList->vertex( p10 );
1438         polyList->vertex( pb10 );
1439         polyList->plane( p00, p10, pb10 );
1440         polyList->end();
1441
1442         polyList->begin( 0,0 );
1443         polyList->vertex( p00 );
1444         polyList->vertex( pb10 );
1445         polyList->vertex( pb00 );
1446         polyList->plane( p00, pb10, pb00 );
1447         polyList->end();  
1448      }
1449
1450      // Back Face
1451      if ( i == endSegIdx && capEnd )
1452      {
1453         polyList->begin( 0,0 );
1454         polyList->vertex( p01 );
1455         polyList->vertex( pb01 );
1456         polyList->vertex( pb11 );
1457         polyList->plane( p01, pb01, pb11 );
1458         polyList->end();
1459
1460         polyList->begin( 0,0 );
1461         polyList->vertex( p01 );
1462         polyList->vertex( pb11 );
1463         polyList->vertex( p11 );
1464         polyList->plane( p01, pb11, p11 );
1465         polyList->end();  
1466      }
1467
1468      offset += 4;
1469   }
1470
1471   return true;
1472}
1473
1474bool MeshRoad::castRay( const Point3F &s, const Point3F &e, RayInfo *info )
1475{
1476   Point3F start = s;
1477   Point3F end = e;
1478   mObjToWorld.mulP(start);
1479   mObjToWorld.mulP(end);
1480
1481   F32 out = 1.0f;   // The output fraction/percentage along the line defined by s and e
1482   VectorF norm(0.0f, 0.0f, 0.0f);     // The normal of the face intersected
1483   
1484   Vector<MeshRoadHitSegment> hitSegments;
1485
1486   for ( U32 i = 0; i < mSegments.size(); i++ )
1487   {
1488      const MeshRoadSegment &segment = mSegments[i];
1489
1490      F32 t;
1491      VectorF n;
1492
1493      if ( segment.getWorldBounds().collideLine( start, end, &t, &n ) )
1494      {
1495         hitSegments.increment();
1496         hitSegments.last().t = t;
1497         hitSegments.last().idx = i;         
1498      }
1499   }
1500
1501   dQsort( hitSegments.address(), hitSegments.size(), sizeof(MeshRoadHitSegment), compareHitSegments );
1502
1503   U32 idx0, idx1, idx2;
1504   F32 t;
1505
1506   for ( U32 i = 0; i < hitSegments.size(); i++ )
1507   {
1508      U32 segIdx = hitSegments[i].idx;
1509      const MeshRoadSegment &segment = mSegments[segIdx];
1510
1511      // Each segment has 6 faces
1512      for ( U32 j = 0; j < 6; j++ )
1513      {
1514         if ( j == 4 && segIdx != 0 )
1515            continue;
1516
1517         if ( j == 5 && segIdx != mSegments.size() - 1 )
1518            continue;
1519
1520         // Each face has 2 triangles
1521         for ( U32 k = 0; k < 2; k++ )
1522         {
1523            idx0 = gIdxArray[j][k][0];
1524            idx1 = gIdxArray[j][k][1];
1525            idx2 = gIdxArray[j][k][2];
1526
1527            const Point3F &v0 = segment[idx0];
1528            const Point3F &v1 = segment[idx1];
1529            const Point3F &v2 = segment[idx2];
1530
1531            if ( !MathUtils::mLineTriangleCollide( start, end, 
1532                                                   v2, v1, v0,
1533                                                   NULL,
1534                                                   &t ) )
1535               continue;
1536            
1537            if ( t >= 0.0f && t < 1.0f && t < out )
1538            {
1539               out = t;               
1540               norm = PlaneF( v0, v1, v2 );
1541            }
1542         }
1543      }
1544
1545      if (out >= 0.0f && out < 1.0f)
1546         break;
1547   }
1548
1549   if (out >= 0.0f && out < 1.0f)
1550   {
1551      info->t = out;
1552      info->normal = norm;
1553      info->point.interpolate(start, end, out);
1554      info->face = -1;
1555      info->object = this;
1556      info->material = this->mMatInst[0];
1557      return true;
1558   }
1559
1560   return false;
1561}
1562
1563bool MeshRoad::collideBox(const Point3F &start, const Point3F &end, RayInfo* info)
1564{   
1565   Con::warnf( "MeshRoad::collideBox() - not yet implemented!" );
1566   return Parent::collideBox( start, end, info );
1567}
1568
1569void MeshRoad::_regenerate()
1570{               
1571   if ( mNodes.size() == 0 )
1572      return;
1573
1574   const Point3F &nodePt = mNodes.first().point;
1575
1576   MatrixF mat( true );
1577   mat.setPosition( nodePt );
1578   Parent::setTransform( mat );
1579
1580   _generateSlices();
1581
1582   // Make sure we are in the correct bins given our world box.
1583   if( getSceneManager() != NULL )
1584      getSceneManager()->notifyObjectDirty( this );
1585}
1586
1587void MeshRoad::_generateSlices()
1588{      
1589   if ( mNodes.size() < 2 )
1590      return;
1591
1592   // Create the spline, initialized with the MeshRoadNode(s)
1593   U32 nodeCount = mNodes.size();
1594   MeshRoadSplineNode *splineNodes = new MeshRoadSplineNode[nodeCount];
1595
1596   for ( U32 i = 0; i < nodeCount; i++ )
1597   {
1598      MeshRoadSplineNode &splineNode = splineNodes[i];
1599      const MeshRoadNode &node = mNodes[i];
1600
1601      splineNode.x = node.point.x;
1602      splineNode.y = node.point.y;
1603      splineNode.z = node.point.z;
1604      splineNode.width = node.width;
1605      splineNode.depth = node.depth;
1606      splineNode.normal = node.normal;
1607   }
1608
1609   CatmullRom<MeshRoadSplineNode> spline;
1610   spline.initialize( nodeCount, splineNodes );
1611   delete [] splineNodes;
1612
1613   mSlices.clear();
1614      
1615   VectorF lastBreakVector(0,0,0);      
1616   MeshRoadSlice slice;
1617   MeshRoadSplineNode lastBreakNode;
1618   lastBreakNode = spline.evaluate(0.0f);
1619
1620   for ( U32 i = 1; i < mNodes.size(); i++ )
1621   {
1622      F32 t1 = spline.getTime(i);
1623      F32 t0 = spline.getTime(i-1);
1624      
1625      F32 segLength = spline.arcLength( t0, t1 );
1626
1627      U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT );
1628      numSegments = getMax( numSegments, (U32)1 );
1629      F32 tstep = ( t1 - t0 ) / numSegments; 
1630
1631      U32 startIdx = 0;
1632      U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments;
1633
1634      for ( U32 j = startIdx; j < endIdx; j++ )
1635      {
1636         F32 t = t0 + tstep * j;
1637         MeshRoadSplineNode splineNode = spline.evaluate(t);
1638
1639         VectorF toNodeVec = splineNode.getPosition() - lastBreakNode.getPosition();
1640         toNodeVec.normalizeSafe();
1641
1642         if ( lastBreakVector.isZero() )
1643            lastBreakVector = toNodeVec;
1644
1645         F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) );
1646
1647         if ( j == startIdx || 
1648            ( j == endIdx - 1 && i == mNodes.size() - 1 ) ||
1649              angle > mBreakAngle )
1650         {
1651            // Push back a spline node
1652            slice.p1.set( splineNode.x, splineNode.y, splineNode.z );            
1653            slice.width = splineNode.width;
1654            slice.depth = splineNode.depth;
1655            slice.normal = splineNode.normal;   
1656            slice.normal.normalize();
1657            slice.parentNodeIdx = i-1;
1658            slice.t = t;
1659            mSlices.push_back( slice );         
1660
1661            lastBreakVector = splineNode.getPosition() - lastBreakNode.getPosition();
1662            lastBreakVector.normalizeSafe();
1663
1664            lastBreakNode = splineNode;
1665         }          
1666      }
1667   }
1668
1669   //
1670   // Calculate uvec, fvec, and rvec for all slices
1671   //   
1672
1673   MatrixF mat(true);
1674
1675   for ( U32 i = 0; i < mSlices.size(); i++ )
1676   {
1677      calcSliceTransform( i, mat );
1678      mat.getColumn( 0, &mSlices[i].rvec );
1679      mat.getColumn( 1, &mSlices[i].fvec );
1680      mat.getColumn( 2, &mSlices[i].uvec );
1681   } 
1682
1683   //
1684   // Calculate p0/p2/pb0/pb2 for all slices
1685   //      
1686   for ( U32 i = 0; i < mSlices.size(); i++ )
1687   {
1688      MeshRoadSlice *slice = &mSlices[i];
1689      slice->p0 = slice->p1 - slice->rvec * slice->width * 0.5f;
1690      slice->p2 = slice->p1 + slice->rvec * slice->width * 0.5f;
1691      slice->pb0 = slice->p0 - slice->uvec * slice->depth;
1692      slice->pb2 = slice->p2 - slice->uvec * slice->depth;
1693   }
1694
1695   // Generate the object/world bounds
1696   Box3F box;
1697   for ( U32 i = 0; i < mSlices.size(); i++ )
1698   {
1699      const MeshRoadSlice &slice = mSlices[i];
1700
1701      if ( i == 0 )
1702      {
1703         box.minExtents = slice.p0;
1704         box.maxExtents = slice.p2;
1705         box.extend( slice.pb0 );
1706         box.extend( slice.pb2 );
1707      }
1708      else
1709      {
1710         box.extend( slice.p0 ); 
1711         box.extend( slice.p2 );
1712         box.extend( slice.pb0 );
1713         box.extend( slice.pb2 );
1714      }
1715   }
1716
1717   mWorldBox = box;
1718   resetObjectBox();
1719
1720   _generateSegments();   
1721}
1722
1723void MeshRoad::_generateSegments()
1724{
1725   SAFE_DELETE( mPhysicsRep );
1726
1727   mSegments.clear();
1728
1729   for ( U32 i = 0; i < mSlices.size() - 1; i++ )
1730   {
1731      MeshRoadSegment seg( &mSlices[i], &mSlices[i+1], getWorldTransform() );
1732
1733      mSegments.push_back( seg );
1734   }
1735
1736   if ( isClientObject() )
1737      _generateVerts();
1738
1739   if ( PHYSICSMGR )
1740   {
1741      ConcretePolyList polylist;
1742      if ( buildPolyList( PLC_Collision, &polylist, getWorldBox(), getWorldSphere() ) )
1743      {
1744         polylist.triangulate();
1745
1746         PhysicsCollision *colShape = PHYSICSMGR->createCollision();
1747         colShape->addTriangleMesh( polylist.mVertexList.address(),
1748            polylist.mVertexList.size(),
1749            polylist.mIndexList.address(),
1750            polylist.mIndexList.size() / 3,
1751            MatrixF::Identity );
1752
1753         PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
1754         mPhysicsRep = PHYSICSMGR->createBody();
1755         mPhysicsRep->init( colShape, 0, 0, this, world );
1756      }
1757   }
1758}
1759
1760void MeshRoad::_generateVerts()
1761{           
1762   const U32 widthDivisions = getMax( 0, mWidthSubdivisions );
1763   const F32 divisionStep = 1.0f / (F32)( widthDivisions + 1 );
1764   const U32 sliceCount = mSlices.size();
1765   const U32 segmentCount = mSegments.size();
1766
1767   mVertCount[Top] = ( 2 + widthDivisions ) * sliceCount;
1768   mTriangleCount[Top] = segmentCount * 2 * ( widthDivisions + 1 );
1769
1770   mVertCount[Bottom] = sliceCount * 2;
1771   mTriangleCount[Bottom] = segmentCount * 2;
1772
1773   mVertCount[Side] = sliceCount * 4;
1774   mTriangleCount[Side] = segmentCount * 4 + 4;
1775   
1776   // Calculate TexCoords for Slices
1777
1778   F32 texCoordV = 0.0f;
1779   mSlices[0].texCoordV = 0.0f;
1780
1781   for ( U32 i = 1; i < sliceCount; i++ )
1782   {
1783      MeshRoadSlice &slice = mSlices[i]; 
1784      MeshRoadSlice &prevSlice = mSlices[i-1];
1785             
1786      // Increment the textCoordV for the next slice.
1787      F32 len = ( slice.p1 - prevSlice.p1 ).len();
1788      texCoordV += len / mTextureLength;         
1789
1790      slice.texCoordV = texCoordV;
1791   }
1792
1793   // Make Vertex Buffers
1794   GFXVertexPNTT *pVert = NULL;
1795   U32 vertCounter = 0;
1796
1797   // Top Buffers...
1798
1799   mVB[Top].set( GFX, mVertCount[Top], GFXBufferTypeStatic );   
1800   pVert = mVB[Top].lock(); 
1801   vertCounter = 0;
1802   
1803   for ( U32 i = 0; i < sliceCount; i++ )
1804   {
1805      MeshRoadSlice &slice = mSlices[i];      
1806      
1807      pVert->point = slice.p0;    
1808      pVert->normal = slice.uvec;
1809      pVert->tangent = slice.fvec;
1810      pVert->texCoord.set(1,slice.texCoordV);      
1811      pVert++;
1812      vertCounter++;
1813
1814      for ( U32 j = 0; j < widthDivisions; j++ )
1815      {
1816         const F32 t = divisionStep * (F32)( j + 1 );
1817
1818         pVert->point.interpolate( slice.p0, slice.p2, t );    
1819         pVert->normal = slice.uvec;
1820         pVert->tangent = slice.fvec;
1821         pVert->texCoord.set( 1.0f - t, slice.texCoordV );      
1822         pVert++;
1823         vertCounter++;
1824      }
1825
1826      pVert->point = slice.p2;    
1827      pVert->normal = slice.uvec;
1828      pVert->tangent = slice.fvec;
1829      pVert->texCoord.set( 0, slice.texCoordV );      
1830      pVert++;
1831      vertCounter++;
1832   }
1833
1834   AssertFatal( vertCounter == mVertCount[Top], "MeshRoad, wrote incorrect number of verts in mVB[Top]!" );
1835
1836   mVB[Top].unlock();
1837
1838   // Bottom Buffer...
1839
1840   mVB[Bottom].set( GFX, mVertCount[Bottom], GFXBufferTypeStatic );   
1841   pVert = mVB[Bottom].lock(); 
1842   vertCounter = 0;
1843
1844   for ( U32 i = 0; i < sliceCount; i++ )
1845   {
1846      MeshRoadSlice &slice = mSlices[i];      
1847
1848      pVert->point = slice.pb2;    
1849      pVert->normal = -slice.uvec;
1850      pVert->tangent = slice.fvec;
1851      pVert->texCoord.set(0,slice.texCoordV);      
1852      pVert++;
1853      vertCounter++;
1854
1855      pVert->point = slice.pb0;    
1856      pVert->normal = -slice.uvec;
1857      pVert->tangent = slice.fvec;
1858      pVert->texCoord.set(1,slice.texCoordV);      
1859      pVert++;
1860      vertCounter++;
1861   }
1862
1863   AssertFatal( vertCounter == mVertCount[Bottom], "MeshRoad, wrote incorrect number of verts in mVB[Bottom]!" );
1864
1865   mVB[Bottom].unlock();
1866
1867   // Side Buffers...
1868
1869   mVB[Side].set( GFX, mVertCount[Side], GFXBufferTypeStatic );   
1870   pVert = mVB[Side].lock(); 
1871   vertCounter = 0;
1872
1873   for ( U32 i = 0; i < sliceCount; i++ )
1874   {
1875      MeshRoadSlice &slice = mSlices[i];      
1876
1877      pVert->point = slice.p0;    
1878      pVert->normal = -slice.rvec;
1879      pVert->tangent = slice.fvec;
1880      pVert->texCoord.set(1,slice.texCoordV);      
1881      pVert++;
1882      vertCounter++;
1883
1884      pVert->point = slice.p2;    
1885      pVert->normal = slice.rvec;
1886      pVert->tangent = slice.fvec;
1887      pVert->texCoord.set(1,slice.texCoordV);      
1888      pVert++;
1889      vertCounter++;
1890
1891      pVert->point = slice.pb0;    
1892      pVert->normal = -slice.rvec;
1893      pVert->tangent = slice.fvec;
1894      pVert->texCoord.set(0,slice.texCoordV);      
1895      pVert++;
1896      vertCounter++;
1897
1898      pVert->point = slice.pb2;    
1899      pVert->normal = slice.rvec;
1900      pVert->tangent = slice.fvec;
1901      pVert->texCoord.set(0,slice.texCoordV);      
1902      pVert++;
1903      vertCounter++;
1904   }
1905
1906   AssertFatal( vertCounter == mVertCount[Side], "MeshRoad, wrote incorrect number of verts in mVB[Side]!" );
1907
1908   mVB[Side].unlock();
1909
1910   // Make Primitive Buffers   
1911   U32 p00, p01, p11, p10;
1912   U32 pb00, pb01, pb11, pb10;
1913   U32 offset = 0;
1914   U16 *pIdx = NULL;   
1915   U32 curIdx = 0; 
1916
1917   // Top Primitive Buffer
1918
1919   mPB[Top].set( GFX, mTriangleCount[Top] * 3, mTriangleCount[Top], GFXBufferTypeStatic );
1920
1921   mPB[Top].lock(&pIdx);     
1922   curIdx = 0; 
1923   offset = 0;
1924
1925   const U32 rowStride = 2 + widthDivisions;
1926 
1927   for ( U32 i = 0; i < mSegments.size(); i++ )
1928   {     
1929      for ( U32 j = 0; j < widthDivisions + 1; j++ )
1930      {
1931         p00 = offset;
1932         p10 = offset + 1;
1933         p01 = offset + rowStride;
1934         p11 = offset + rowStride + 1;
1935
1936         pIdx[curIdx] = p00;
1937         curIdx++;
1938         pIdx[curIdx] = p01;
1939         curIdx++;
1940         pIdx[curIdx] = p11;
1941         curIdx++;
1942
1943         pIdx[curIdx] = p00;
1944         curIdx++;
1945         pIdx[curIdx] = p11;
1946         curIdx++;
1947         pIdx[curIdx] = p10;
1948         curIdx++;  
1949
1950         offset += 1;
1951      }
1952
1953      offset += 1;
1954   }
1955
1956
1957   AssertFatal( curIdx == mTriangleCount[Top] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Top]!" );
1958
1959   mPB[Top].unlock();
1960
1961   // Bottom Primitive Buffer
1962
1963   mPB[Bottom].set( GFX, mTriangleCount[Bottom] * 3, mTriangleCount[Bottom], GFXBufferTypeStatic );
1964
1965   mPB[Bottom].lock(&pIdx);     
1966   curIdx = 0; 
1967   offset = 0;
1968
1969   for ( U32 i = 0; i < mSegments.size(); i++ )
1970   {     
1971      p00 = offset;
1972      p10 = offset + 1;
1973      p01 = offset + 2;
1974      p11 = offset + 3;
1975
1976      pIdx[curIdx] = p00;
1977      curIdx++;
1978      pIdx[curIdx] = p01;
1979      curIdx++;
1980      pIdx[curIdx] = p11;
1981      curIdx++;
1982
1983      pIdx[curIdx] = p00;
1984      curIdx++;
1985      pIdx[curIdx] = p11;
1986      curIdx++;
1987      pIdx[curIdx] = p10;
1988      curIdx++;      
1989
1990      offset += 2;
1991   }
1992
1993   AssertFatal( curIdx == mTriangleCount[Bottom] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Bottom]!" );
1994
1995   mPB[Bottom].unlock();
1996
1997   // Side Primitive Buffer
1998
1999   mPB[Side].set( GFX, mTriangleCount[Side] * 3, mTriangleCount[Side], GFXBufferTypeStatic );
2000
2001   mPB[Side].lock(&pIdx);     
2002   curIdx = 0; 
2003   offset = 0;
2004
2005   for ( U32 i = 0; i < mSegments.size(); i++ )
2006   {     
2007      p00 = offset;
2008      p10 = offset + 1;
2009      pb00 = offset + 2;
2010      pb10 = offset + 3;
2011      p01 = offset + 4;
2012      p11 = offset + 5;
2013      pb01 = offset + 6;
2014      pb11 = offset + 7;
2015
2016      // Left Side
2017
2018      pIdx[curIdx] = pb00;
2019      curIdx++;
2020      pIdx[curIdx] = pb01;
2021      curIdx++;
2022      pIdx[curIdx] = p01;
2023      curIdx++;
2024
2025      pIdx[curIdx] = pb00;
2026      curIdx++;
2027      pIdx[curIdx] = p01;
2028      curIdx++;
2029      pIdx[curIdx] = p00;
2030      curIdx++;      
2031
2032      // Right Side
2033
2034      pIdx[curIdx] = p10;
2035      curIdx++;
2036      pIdx[curIdx] = p11;
2037      curIdx++;
2038      pIdx[curIdx] = pb11;
2039      curIdx++;
2040
2041      pIdx[curIdx] = p10;
2042      curIdx++;
2043      pIdx[curIdx] = pb11;
2044      curIdx++;
2045      pIdx[curIdx] = pb10;
2046      curIdx++;      
2047
2048      offset += 4;
2049   }
2050
2051   // Cap the front and back ends
2052   pIdx[curIdx++] = 0;
2053   pIdx[curIdx++] = 1;
2054   pIdx[curIdx++] = 3;
2055   pIdx[curIdx++] = 0;
2056   pIdx[curIdx++] = 3;
2057   pIdx[curIdx++] = 2;
2058   
2059   pIdx[curIdx++] = offset + 0;
2060   pIdx[curIdx++] = offset + 3;
2061   pIdx[curIdx++] = offset + 1;
2062   pIdx[curIdx++] = offset + 0;
2063   pIdx[curIdx++] = offset + 2;
2064   pIdx[curIdx++] = offset + 3;
2065
2066
2067   AssertFatal( curIdx == mTriangleCount[Side] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Side]!" );
2068
2069   mPB[Side].unlock();
2070}
2071
2072const MeshRoadNode& MeshRoad::getNode( U32 idx )
2073{
2074   return mNodes[idx];
2075}
2076
2077VectorF MeshRoad::getNodeNormal( U32 idx )
2078{
2079   if ( mNodes.size() - 1 < idx )
2080      return VectorF::Zero;
2081
2082   return mNodes[idx].normal;
2083}
2084
2085void MeshRoad::setNodeNormal( U32 idx, const VectorF &normal )
2086{
2087   if ( mNodes.size() - 1 < idx )
2088      return;
2089
2090   mNodes[idx].normal = normal;
2091
2092   regenerate();
2093
2094   setMaskBits( NodeMask | RegenMask );
2095}
2096
2097Point3F MeshRoad::getNodePosition( U32 idx )
2098{
2099   if ( mNodes.size() - 1 < idx )
2100      return Point3F::Zero;
2101
2102   return mNodes[idx].point;
2103}
2104
2105void MeshRoad::setNodePosition( U32 idx, const Point3F &pos )
2106{
2107   if ( mNodes.size() - 1 < idx )
2108      return;
2109
2110   mNodes[idx].point = pos;
2111
2112   regenerate();
2113
2114   setMaskBits( NodeMask | RegenMask );
2115}
2116
2117U32 MeshRoad::addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal )
2118{
2119   U32 idx = _addNode( pos, width, depth, normal );   
2120
2121   regenerate();
2122
2123   setMaskBits( NodeMask | RegenMask );
2124
2125   return idx;
2126}
2127
2128void MeshRoad::buildNodesFromList( MeshRoadNodeList* list )
2129{
2130   mNodes.clear();
2131
2132   for (U32 i=0; i<list->mPositions.size(); ++i)
2133   {
2134      _addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->mNormals[i] );
2135   }
2136
2137   _regenerate();
2138}
2139
2140U32 MeshRoad::insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
2141{
2142   U32 ret = _insertNode( pos, width, depth, normal, idx );
2143
2144   regenerate();
2145
2146   setMaskBits( NodeMask | RegenMask );
2147
2148   return ret;
2149}
2150
2151void MeshRoad::setNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
2152{
2153   if ( mNodes.size() - 1 < idx )
2154      return;
2155
2156   MeshRoadNode &node = mNodes[idx];
2157
2158   node.point = pos;
2159   node.width = width;
2160   node.depth = depth;
2161   node.normal = normal;
2162
2163   regenerate();
2164
2165   setMaskBits( NodeMask | RegenMask );
2166}
2167
2168void MeshRoad::setNodeWidth( U32 idx, F32 meters )
2169{
2170   meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH );
2171
2172   if ( mNodes.size() - 1 < idx )
2173      return;
2174
2175   mNodes[idx].width = meters;
2176   _regenerate();
2177
2178   setMaskBits( RegenMask | NodeMask );
2179}
2180
2181F32 MeshRoad::getNodeWidth( U32 idx )
2182{
2183   if ( mNodes.size() - 1 < idx )
2184      return -1.0f;
2185
2186   return mNodes[idx].width;
2187}
2188
2189void MeshRoad::setNodeDepth( U32 idx, F32 meters )
2190{
2191   meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH );
2192
2193   if ( mNodes.size() - 1 < idx )
2194      return;
2195
2196   mNodes[idx].depth = meters;
2197
2198   _regenerate();
2199
2200   setMaskBits( MeshRoadMask | RegenMask | NodeMask );
2201}
2202
2203F32 MeshRoad::getNodeDepth( U32 idx )
2204{
2205   if ( mNodes.size() - 1 < idx )
2206      return -1.0f;
2207
2208   return mNodes[idx].depth;
2209}
2210
2211MatrixF MeshRoad::getNodeTransform( U32 idx )
2212{   
2213   MatrixF mat(true);   
2214
2215   if ( mNodes.size() - 1 < idx )
2216      return mat;
2217
2218   bool hasNext = idx + 1 < mNodes.size();
2219   bool hasPrev = (S32)idx - 1 > 0;
2220
2221   const MeshRoadNode &node = mNodes[idx];   
2222
2223   VectorF fvec( 0, 1, 0 );
2224
2225   if ( hasNext )
2226   {
2227      fvec = mNodes[idx+1].point - node.point;      
2228      fvec.normalizeSafe();
2229   }
2230   else if ( hasPrev )
2231   {
2232      fvec = node.point - mNodes[idx-1].point;
2233      fvec.normalizeSafe();
2234   }
2235   else
2236      fvec = mPerp( node.normal );
2237
2238   if ( fvec.isZero() )
2239      fvec = mPerp( node.normal );
2240
2241   F32 dot = mDot( fvec, node.normal );
2242   if ( dot < -0.9f || dot > 0.9f )
2243      fvec = mPerp( node.normal );
2244
2245   VectorF rvec = mCross( fvec, node.normal );
2246   if ( rvec.isZero() )
2247      rvec = mPerp( fvec );
2248   rvec.normalize();
2249
2250   fvec = mCross( node.normal, rvec );
2251   fvec.normalize();
2252
2253   mat.setColumn( 0, rvec );
2254   mat.setColumn( 1, fvec );
2255   mat.setColumn( 2, node.normal );
2256   mat.setColumn( 3, node.point );
2257
2258   AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
2259
2260   return mat; 
2261}
2262
2263void MeshRoad::calcSliceTransform( U32 idx, MatrixF &mat )
2264{   
2265   if ( mSlices.size() - 1 < idx )
2266      return;
2267
2268   bool hasNext = idx + 1 < mSlices.size();
2269   bool hasPrev = (S32)idx - 1 >= 0;
2270
2271   const MeshRoadSlice &slice = mSlices[idx];   
2272
2273   VectorF fvec( 0, 1, 0 );
2274
2275   if ( hasNext )
2276   {
2277      fvec = mSlices[idx+1].p1 - slice.p1;      
2278      fvec.normalizeSafe();
2279   }
2280   else if ( hasPrev )
2281   {
2282      fvec = slice.p1 - mSlices[idx-1].p1;
2283      fvec.normalizeSafe();
2284   }
2285   else
2286      fvec = mPerp( slice.normal );
2287
2288   if ( fvec.isZero() )
2289      fvec = mPerp( slice.normal );
2290
2291   F32 dot = mDot( fvec, slice.normal );
2292   if ( dot < -0.9f || dot > 0.9f )
2293      fvec = mPerp( slice.normal );
2294
2295   VectorF rvec = mCross( fvec, slice.normal );
2296   if ( rvec.isZero() )
2297      rvec = mPerp( fvec );
2298   rvec.normalize();
2299
2300   fvec = mCross( slice.normal, rvec );
2301   fvec.normalize();
2302
2303   mat.setColumn( 0, rvec );
2304   mat.setColumn( 1, fvec );
2305   mat.setColumn( 2, slice.normal );
2306   mat.setColumn( 3, slice.p1 );
2307
2308   AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
2309}
2310
2311F32 MeshRoad::getRoadLength() const
2312{
2313   F32 length = 0.0f;
2314
2315   for ( U32 i = 0; i < mSegments.size(); i++ )
2316   {
2317      length += mSegments[i].length();
2318   }
2319
2320   return length;
2321}
2322
2323void MeshRoad::deleteNode( U32 idx )
2324{
2325   if ( mNodes.size() - 1 < idx )
2326      return;
2327
2328   mNodes.erase(idx);   
2329   _regenerate();
2330
2331   setMaskBits( RegenMask | NodeMask );
2332}
2333
2334U32 MeshRoad::_addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal )
2335{
2336   mNodes.increment();
2337   MeshRoadNode &node = mNodes.last();
2338
2339   node.point = pos;   
2340   node.width = width;
2341   node.depth = depth;
2342   node.normal = normal;
2343
2344   setMaskBits( NodeMask | RegenMask );
2345
2346   return mNodes.size() - 1;
2347}
2348
2349U32 MeshRoad::_insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
2350{
2351   U32 ret;
2352   MeshRoadNode *node;
2353
2354   if ( idx == U32_MAX )
2355   {
2356      mNodes.increment();
2357      node = &mNodes.last();
2358      ret = mNodes.size() - 1;
2359   }
2360   else
2361   {
2362      mNodes.insert( idx );
2363      node = &mNodes[idx];
2364      ret = idx;
2365   }
2366
2367   node->point = pos;
2368   node->depth = depth;
2369   node->width = width;     
2370   node->normal = normal;
2371
2372   return ret;
2373}
2374
2375bool MeshRoad::collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx, Point3F *collisionPnt )
2376{
2377   Point3F p0 = origin;
2378   Point3F p1 = origin + direction * 2000.0f;
2379
2380   // If the line segment does not collide with the MeshRoad's world box, 
2381   // it definitely does not collide with any part of the river.
2382   if ( !getWorldBox().collideLine( p0, p1 ) )
2383      return false;
2384
2385   if ( mSlices.size() < 2 )
2386      return false;
2387
2388   MathUtils::Quad quad;
2389   MathUtils::Ray ray;
2390   F32 t;
2391
2392   // Check each road segment (formed by a pair of slices) for collision
2393   // with the line segment.
2394   for ( U32 i = 0; i < mSlices.size() - 1; i++ )
2395   {
2396      const MeshRoadSlice &slice0 = mSlices[i];
2397      const MeshRoadSlice &slice1 = mSlices[i+1];
2398
2399      // For simplicities sake we will only test for collision between the
2400      // line segment and the Top face of the river segment.
2401
2402      // Clockwise starting with the leftmost/closest point.
2403      quad.p00 = slice0.p0;
2404      quad.p01 = slice1.p0;
2405      quad.p11 = slice1.p2;
2406      quad.p10 = slice0.p2;
2407
2408      ray.origin = origin;
2409      ray.direction = direction;
2410
2411      if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) )
2412      {
2413         if ( nodeIdx )
2414            *nodeIdx = slice0.parentNodeIdx;         
2415         if ( collisionPnt )
2416            *collisionPnt = ray.origin + ray.direction * t;
2417         return true;
2418      }
2419   }
2420
2421   return false;
2422}
2423
2424void MeshRoad::regenerate()
2425{
2426   _regenerate();
2427   setMaskBits( RegenMask );
2428}
2429
2430//-------------------------------------------------------------------------
2431// Console Methods
2432//-------------------------------------------------------------------------
2433
2434DefineEngineMethod( MeshRoad, setNodeDepth, void, ( S32 idx, F32 meters ),,
2435                   "Intended as a helper to developers and editor scripts.\n"
2436                   "Sets the depth in meters of a particular node."
2437                   )
2438{
2439   object->setNodeDepth( idx, meters );
2440}
2441
2442DefineEngineMethod( MeshRoad, regenerate, void, (),,
2443                   "Intended as a helper to developers and editor scripts.\n"
2444                   "Force MeshRoad to recreate its geometry."
2445                   )
2446{
2447   object->regenerate();
2448}
2449
2450DefineEngineMethod( MeshRoad, postApply, void, (),,
2451                   "Intended as a helper to developers and editor scripts.\n"
2452                   "Force trigger an inspectPostApply. This will transmit "
2453                   "material and other fields ( not including nodes ) to client objects."
2454                   )
2455{
2456   object->inspectPostApply();
2457}
2458