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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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 href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</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//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28
  29#include "platform/platform.h"
  30#include "environment/meshRoad.h"
  31
  32#include "console/consoleTypes.h"
  33#include "console/engineAPI.h"
  34#include "util/catmullRom.h"
  35#include "math/util/quadTransforms.h"
  36#include "scene/simPath.h"
  37#include "scene/sceneRenderState.h"
  38#include "scene/sceneManager.h"
  39#include "scene/sgUtil.h"
  40#include "renderInstance/renderPassManager.h"
  41#include "T3D/gameBase/gameConnection.h"
  42#include "core/stream/bitStream.h"
  43#include "gfx/gfxDrawUtil.h"
  44#include "gfx/gfxTransformSaver.h"
  45#include "gfx/primBuilder.h"
  46#include "gfx/gfxDebugEvent.h"
  47#include "materials/materialManager.h"
  48#include "math/mathIO.h"
  49#include "math/mathUtils.h"
  50#include "math/util/frustum.h"
  51#include "gui/3d/guiTSControl.h"
  52#include "materials/shaderData.h"
  53#include "gfx/sim/gfxStateBlockData.h"
  54#include "gfx/sim/debugDraw.h"
  55#include "collision/concretePolyList.h"
  56#include "T3D/physics/physicsPlugin.h"
  57#include "T3D/physics/physicsBody.h"
  58#include "T3D/physics/physicsCollision.h"
  59#include "environment/nodeListManager.h"
  60#ifdef TORQUE_AFX_ENABLED
  61#include "afx/ce/afxZodiacMgr.h"
  62#endif
  63
  64#define MIN_METERS_PER_SEGMENT 1.0f
  65#define MIN_NODE_DEPTH 0.25f
  66#define MAX_NODE_DEPTH 50.0f
  67#define MIN_NODE_WIDTH 0.25f
  68#define MAX_NODE_WIDTH 50.0f
  69
  70
  71static U32 gIdxArray[6][2][3] = {
  72   { { 0, 4, 5 }, { 0, 5, 1 }, },   // Top Face
  73   { { 2, 6, 4 }, { 2, 4, 0 }, },   // Left Face
  74   { { 1, 5, 7 }, { 1, 7, 3 }, },   // Right Face
  75   { { 2, 3, 7 }, { 2, 7, 6 }, },   // Bottom Face
  76   { { 0, 1, 3 }, { 0, 3, 2 }, },   // Front Face
  77   { { 4, 6, 7 }, { 4, 7, 5 }, },   // Back Face
  78};
  79
  80static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b)
  81{
  82   const MeshRoadHitSegment *fa = (MeshRoadHitSegment*)a;
  83   const MeshRoadHitSegment *fb = (MeshRoadHitSegment*)b;
  84
  85   F32 diff = fb->t - fa->t;
  86
  87   return (diff > 0) ? 1 : (diff < 0) ? -1 : 0;
  88}
  89
  90
  91//-----------------------------------------------------------------------------
  92// MeshRoadNodeList Struct
  93//-----------------------------------------------------------------------------
  94
  95struct MeshRoadNodeList : public NodeListManager::NodeList
  96{
  97   Vector<Point3F>   mPositions;
  98   Vector<F32>       mWidths;
  99   Vector<F32>       mDepths;
 100   Vector<VectorF>   mNormals;
 101
 102   MeshRoadNodeList() { }
 103   virtual ~MeshRoadNodeList() { }
 104};
 105
 106//-----------------------------------------------------------------------------
 107// MeshRoadNodeEvent Class
 108//-----------------------------------------------------------------------------
 109
 110class MeshRoadNodeEvent : public NodeListEvent
 111{
 112   typedef NodeListEvent Parent;
 113
 114public:
 115   Vector<Point3F>   mPositions;
 116   Vector<F32>       mWidths;
 117   Vector<F32>       mDepths;
 118   Vector<VectorF>   mNormals;
 119
 120public:
 121   MeshRoadNodeEvent() { mNodeList = NULL; }
 122   virtual ~MeshRoadNodeEvent() { }
 123
 124   virtual void pack(NetConnection*, BitStream*);
 125   virtual void unpack(NetConnection*, BitStream*);
 126
 127   virtual void copyIntoList(NodeListManager::NodeList* copyInto);
 128   virtual void padListToSize();
 129
 130   DECLARE_CONOBJECT(MeshRoadNodeEvent);
 131};
 132
 133void MeshRoadNodeEvent::pack(NetConnection* conn, BitStream* stream)
 134{
 135   Parent::pack( conn, stream );
 136
 137   stream->writeInt( mPositions.size(), 16 );
 138
 139   for (U32 i=0; i<mPositions.size(); ++i)
 140   {
 141      mathWrite( *stream, mPositions[i] );
 142      stream->write( mWidths[i] );
 143      stream->write( mDepths[i] );
 144      mathWrite( *stream, mNormals[i] );         
 145   }
 146}
 147
 148void MeshRoadNodeEvent::unpack(NetConnection* conn, BitStream* stream)
 149{
 150   mNodeList = new MeshRoadNodeList();
 151
 152   Parent::unpack( conn, stream );
 153
 154   U32 count = stream->readInt( 16 );
 155
 156   Point3F pos;
 157   F32 width, depth;
 158   VectorF normal;
 159
 160   MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
 161
 162   for (U32 i=0; i<count; ++i)
 163   {
 164      mathRead( *stream, &pos );
 165      stream->read( &width );   
 166      stream->read( &depth );
 167      mathRead( *stream, &normal );
 168
 169      list->mPositions.push_back( pos );
 170      list->mWidths.push_back( width );
 171      list->mDepths.push_back( depth );
 172      list->mNormals.push_back( normal );
 173   }
 174
 175   list->mTotalValidNodes = count;
 176
 177   // Do we have a complete list?
 178   if (list->mPositions.size() >= mTotalNodes)
 179      list->mListComplete = true;
 180}
 181
 182void MeshRoadNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto)
 183{
 184   MeshRoadNodeList* prevList = dynamic_cast<MeshRoadNodeList*>(copyInto);
 185   MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
 186
 187   // Merge our list with the old list.
 188   for (U32 i=<a href="/coding/class/classnodelistevent/#classnodelistevent_1a05350212961bce959f29916230c0209e">mLocalListStart</a>, index=0; i<mLocalListStart+list->mPositions.size(); ++i, ++index)
 189   {
 190      prevList->mPositions[i] = list->mPositions[index];
 191      prevList->mWidths[i] = list->mWidths[index];
 192      prevList->mDepths[i] = list->mDepths[index];
 193      prevList->mNormals[i] = list->mNormals[index];
 194   }
 195}
 196
 197void MeshRoadNodeEvent::padListToSize()
 198{
 199   MeshRoadNodeList* list = static_cast<MeshRoadNodeList*>(mNodeList);
 200
 201   U32 totalValidNodes = list->mTotalValidNodes;
 202
 203   // Pad our list front?
 204   if (mLocalListStart)
 205   {
 206      MeshRoadNodeList* newlist = new MeshRoadNodeList();
 207      newlist->mPositions.increment(mLocalListStart);
 208      newlist->mWidths.increment(mLocalListStart);
 209      newlist->mDepths.increment(mLocalListStart);
 210      newlist->mNormals.increment(mLocalListStart);
 211
 212      newlist->mPositions.merge(list->mPositions);
 213      newlist->mWidths.merge(list->mWidths);
 214      newlist->mDepths.merge(list->mDepths);
 215      newlist->mNormals.merge(list->mNormals);
 216
 217      delete list;
 218      mNodeList = list = newlist;
 219   }
 220
 221   // Pad our list end?
 222   if (list->mPositions.size() < mTotalNodes)
 223   {
 224      U32 delta = mTotalNodes - list->mPositions.size();
 225      list->mPositions.increment(delta);
 226      list->mWidths.increment(delta);
 227      list->mDepths.increment(delta);
 228      list->mNormals.increment(delta);
 229   }
 230
 231   list->mTotalValidNodes = totalValidNodes;
 232}
 233
 234IMPLEMENT_CO_NETEVENT_V1(MeshRoadNodeEvent);
 235
 236ConsoleDocClass( MeshRoadNodeEvent,
 237   "@brief Sends messages to the Mesh Road Editor\n\n"
 238   "Editor use only.\n\n"
 239   "@internal"
 240);
 241
 242//-----------------------------------------------------------------------------
 243// MeshRoadNodeListNotify Class
 244//-----------------------------------------------------------------------------
 245
 246class MeshRoadNodeListNotify : public NodeListNotify
 247{
 248   typedef NodeListNotify Parent;
 249
 250protected:
 251   SimObjectPtr<MeshRoad> mRoad;
 252
 253public:
 254   MeshRoadNodeListNotify( MeshRoad* road, U32 listId ) { mRoad = road; mListId = listId; }
 255   virtual ~MeshRoadNodeListNotify() { mRoad = NULL; }
 256
 257   virtual void sendNotification( NodeListManager::NodeList* list );
 258};
 259
 260void MeshRoadNodeListNotify::sendNotification( NodeListManager::NodeList* list )
 261{
 262   if (mRoad.isValid())
 263   {
 264      // Build the road's nodes
 265      MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
 266      if (roadList)
 267         mRoad->buildNodesFromList( roadList );
 268   }
 269}
 270
 271//-------------------------------------------------------------------------
 272// MeshRoadProfile Class
 273//-------------------------------------------------------------------------
 274
 275MeshRoadProfile::MeshRoadProfile()
 276{
 277   mRoad = NULL;
 278
 279   // Set transformation matrix to identity
 280   mObjToSlice.identity();
 281   mSliceToObj.identity();
 282}
 283
 284S32 MeshRoadProfile::clickOnLine(Point3F &p)
 285{
 286   Point3F newProfilePt;
 287   Point3F ptOnSegment;
 288   F32 dist = 0.0f;
 289   F32 minDist = 99999.0f;
 290   U32 idx = 0;
 291
 292   for(U32 i=0; i < mNodes.size()-1; i++)
 293   {
 294      ptOnSegment = MathUtils::mClosestPointOnSegment(mNodes[i].getPosition(), mNodes[i+1].getPosition(), p);
 295
 296      dist = (p - ptOnSegment).len();
 297
 298      if(dist < minDist)
 299      {
 300         minDist = dist;
 301         newProfilePt = ptOnSegment;
 302         idx = i+1;
 303      }
 304   }
 305
 306   if(minDist <= 0.1f)
 307   {
 308      p.set(newProfilePt.x, newProfilePt.y, newProfilePt.z);
 309
 310      return idx;
 311   }
 312
 313   return -1;
 314}
 315
 316void MeshRoadProfile::addPoint(U32 nodeId, Point3F &p)
 317{
 318   if(nodeId < mNodes.size() && nodeId != 0)
 319   {
 320      p.z = 0.0f;
 321      mNodes.insert(nodeId, p);
 322      mSegMtrls.insert(nodeId-1, mSegMtrls[nodeId-1]);
 323      mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
 324      generateNormals();
 325   }
 326}
 327
 328void MeshRoadProfile::removePoint(U32 nodeId)
 329{
 330   if(nodeId > 0 && nodeId < mNodes.size()-1)
 331   {
 332      mNodes.erase(nodeId);
 333      mSegMtrls.remove(nodeId-1);
 334      mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
 335      generateNormals();
 336   }
 337}
 338
 339void MeshRoadProfile::setNodePosition(U32 nodeId, Point3F pos)
 340{
 341   if(nodeId < mNodes.size())
 342   {
 343      mNodes[nodeId].setPosition(pos.x, pos.y);
 344      mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
 345      generateNormals();
 346   }
 347}
 348
 349void MeshRoadProfile::toggleSmoothing(U32 nodeId)
 350{
 351   if(nodeId > 0 && nodeId < mNodes.size()-1)
 352   {
 353      mNodes[nodeId].setSmoothing(!mNodes[nodeId].isSmooth());
 354      mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
 355      generateNormals();
 356   }
 357}
 358
 359void MeshRoadProfile::toggleSegMtrl(U32 seg)
 360{
 361   if(seg < mSegMtrls.size())
 362   {
 363      switch(mSegMtrls[seg])
 364      {
 365      case MeshRoad::Side:    mSegMtrls[seg] = MeshRoad::Top;     break;
 366      case MeshRoad::Top:     mSegMtrls[seg] = MeshRoad::Bottom;  break;
 367      case MeshRoad::Bottom:  mSegMtrls[seg] = MeshRoad::Side;    break;
 368      }
 369
 370      mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
 371   }
 372}
 373
 374void MeshRoadProfile::generateNormals()
 375{
 376   VectorF t, b, n, t2, n2;
 377   Point3F averagePt;
 378
 379   mNodeNormals.clear();
 380
 381   // Loop through all profile line segments
 382   for(U32 i=0; i < mNodes.size()-1; i++)
 383   {
 384      // Calculate normal for each node in line segment
 385      for(U32 j=0; j<2; j++)
 386      {
 387         // Smoothed Node: Average the node with nodes before and after.
 388         // Direction between the node and the average is the smoothed normal.
 389         if( mNodes[i+j].isSmooth() )
 390         {
 391            b = Point3F(0.0f, 0.0f, 1.0f);
 392            t = mNodes[i+j-1].getPosition() - mNodes[i+j].getPosition();
 393            n = mCross(t, b);
 394            n.normalizeSafe();
 395
 396            t2 = mNodes[i+j].getPosition() - mNodes[i+j+1].getPosition();
 397            n2 = mCross(t2, b);
 398            n2.normalizeSafe();
 399
 400            n += n2;
 401         }
 402         // Non-smoothed Node: Normal is perpendicular to segment.
 403         else
 404         {
 405            b = Point3F(0.0f, 0.0f, 1.0f);
 406            t = mNodes[i].getPosition() - mNodes[i+1].getPosition();
 407            n = mCross(t, b);
 408         }
 409
 410         n.normalizeSafe();
 411         mNodeNormals.push_back(n);
 412      }
 413   }
 414}
 415
 416void MeshRoadProfile::generateEndCap(F32 width)
 417{
 418   Point3F pt;
 419
 420   mCap.newPoly();
 421
 422   for ( U32 i = 0; i < mNodes.size(); i++ )
 423   {
 424      pt = mNodes[i].getPosition();
 425      mCap.addVert(pt);
 426   }
 427
 428   for ( S32 i = mNodes.size()-1; i >= 0; i-- )
 429   {
 430      pt = mNodes[i].getPosition();
 431      pt.x = -pt.x - width;
 432      mCap.addVert(pt);
 433   }
 434
 435   mCap.decompose();
 436}
 437
 438void MeshRoadProfile::setProfileDepth(F32 depth)
 439{
 440   Point3F curPos = mNodes[mNodes.size()-1].getPosition();
 441
 442   mNodes[mNodes.size()-1].setPosition(curPos.x, -depth);
 443}
 444
 445void MeshRoadProfile::setTransform(const MatrixF &mat, const Point3F &p)
 446{
 447   mObjToSlice.identity();
 448   mSliceToObj.identity();
 449
 450   mObjToSlice *= mat;
 451   mSliceToObj *= mObjToSlice.inverse();
 452   mSliceToObj.transpose();
 453
 454   mStartPos = p;
 455}
 456
 457void MeshRoadProfile::getNodeWorldPos(U32 nodeId, Point3F &p)
 458{
 459   if(nodeId < mNodes.size())
 460   {
 461      p = mNodes[nodeId].getPosition();
 462      mObjToSlice.mulP(p);
 463      p += mStartPos;
 464   }
 465}
 466
 467void MeshRoadProfile::getNormToSlice(U32 normId, VectorF &n)
 468{
 469   if(normId < mNodeNormals.size())
 470   {
 471      n = mNodeNormals[normId];
 472      mObjToSlice.mulP(n);
 473   }
 474}
 475
 476void MeshRoadProfile::getNormWorldPos(U32 normId, Point3F &p)
 477{
 478   if(normId < mNodeNormals.size())
 479   {
 480      U32 nodeId = normId/2 + (U32)(mFmod(normId,2.0f));
 481      p = mNodes[nodeId].getPosition();
 482      p += 0.5f * mNodeNormals[normId];      // Length = 0.5 units
 483      mObjToSlice.mulP(p);
 484      p += mStartPos;
 485   }
 486}
 487
 488void MeshRoadProfile::worldToObj(Point3F &p)
 489{
 490   p -= mStartPos;
 491   mSliceToObj.mulP(p);
 492   p.z = 0.0f;
 493}
 494
 495void MeshRoadProfile::objToWorld(Point3F &p)
 496{
 497   mObjToSlice.mulP(p);
 498   p += mStartPos;
 499}
 500
 501F32 MeshRoadProfile::getProfileLen()
 502{
 503   F32 sum = 0.0f;
 504   Point3F segmentVec;
 505
 506   for(U32 i=0; i < mNodes.size()-1; i++)
 507   {
 508      segmentVec = mNodes[i+1].getPosition() - mNodes[i].getPosition();
 509      sum += segmentVec.len();
 510   }
 511
 512   return sum;
 513}
 514
 515F32 MeshRoadProfile::getNodePosPercent(U32 nodeId)
 516{
 517   nodeId = mFmod(nodeId, mNodes.size());
 518
 519   if(nodeId == 0)
 520      return 0.0f;
 521   else if(nodeId == mNodes.size()-1)
 522      return 1.0f;
 523
 524   F32 totLen = getProfileLen();
 525   F32 sum = 0.0f;
 526   Point3F segmentVec;
 527
 528   for(U32 i=0; i < nodeId; i++)
 529   {
 530      segmentVec = mNodes[i+1].getPosition() - mNodes[i].getPosition();
 531      sum += segmentVec.len();
 532   }
 533
 534   return sum/totLen;
 535}
 536
 537void MeshRoadProfile::resetProfile(F32 defaultDepth)
 538{
 539   Point3F pos(0.0f, 0.0f, 0.0f);
 540
 541   mNodes.clear();
 542   mNodes.push_back(pos);
 543
 544   pos.y = -defaultDepth;
 545   mNodes.push_back(pos);
 546
 547   mSegMtrls.clear();
 548   mSegMtrls.push_back(MeshRoad::Side);
 549
 550   mRoad->setMaskBits(MeshRoad::ProfileMask | MeshRoad::RegenMask);
 551   generateNormals();
 552}
 553
 554//------------------------------------------------------------------------------
 555// MeshRoadConvex Class
 556//------------------------------------------------------------------------------
 557
 558const MatrixF& MeshRoadConvex::getTransform() const
 559{
 560   return MatrixF::Identity; //mObject->getTransform();    
 561}
 562
 563Box3F MeshRoadConvex::getBoundingBox() const
 564{
 565   return box;
 566}
 567
 568Box3F MeshRoadConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
 569{
 570   Box3F newBox = box;
 571   newBox.minExtents.convolve(scale);
 572   newBox.maxExtents.convolve(scale);
 573   mat.mul(newBox);
 574   return newBox;
 575}
 576
 577Point3F MeshRoadConvex::support(const VectorF& vec) const
 578{
 579   F32 bestDot = mDot( verts[0], vec );
 580
 581   const Point3F *bestP = &verts[0];
 582   for(S32 i=1; i<4; i++)
 583   {
 584      F32 newD = mDot(verts[i], vec);
 585      if(newD > bestDot)
 586      {
 587         bestDot = newD;
 588         bestP = &verts[i];
 589      }
 590   }
 591
 592   return *bestP;
 593}
 594
 595void MeshRoadConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf)
 596{
 597   cf->material = 0;
 598   cf->mObject = mObject;
 599
 600   // For a tetrahedron this is pretty easy... first
 601   // convert everything into world space.
 602   Point3F tverts[4];
 603   mat.mulP(verts[0], &tverts[0]);
 604   mat.mulP(verts[1], &tverts[1]);
 605   mat.mulP(verts[2], &tverts[2]);
 606   mat.mulP(verts[3], &tverts[3]);
 607
 608   // Points...
 609   S32 firstVert = cf->mVertexList.size();
 610   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0];
 611   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1];
 612   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2];
 613   cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3];
 614
 615   // Edges...
 616   cf->mEdgeList.increment();
 617   cf->mEdgeList.last().vertex[0] = firstVert+0;
 618   cf->mEdgeList.last().vertex[1] = firstVert+1;
 619
 620   cf->mEdgeList.increment();
 621   cf->mEdgeList.last().vertex[0] = firstVert+1;
 622   cf->mEdgeList.last().vertex[1] = firstVert+2;
 623
 624   cf->mEdgeList.increment();
 625   cf->mEdgeList.last().vertex[0] = firstVert+2;
 626   cf->mEdgeList.last().vertex[1] = firstVert+0;
 627
 628   cf->mEdgeList.increment();
 629   cf->mEdgeList.last().vertex[0] = firstVert+3;
 630   cf->mEdgeList.last().vertex[1] = firstVert+0;
 631
 632   cf->mEdgeList.increment();
 633   cf->mEdgeList.last().vertex[0] = firstVert+3;
 634   cf->mEdgeList.last().vertex[1] = firstVert+1;
 635
 636   cf->mEdgeList.increment();
 637   cf->mEdgeList.last().vertex[0] = firstVert+3;
 638   cf->mEdgeList.last().vertex[1] = firstVert+2;
 639
 640   // Triangles...
 641   cf->mFaceList.increment();
 642   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]);
 643   cf->mFaceList.last().vertex[0] = firstVert+2;
 644   cf->mFaceList.last().vertex[1] = firstVert+1;
 645   cf->mFaceList.last().vertex[2] = firstVert+0;
 646
 647   cf->mFaceList.increment();
 648   cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]);
 649   cf->mFaceList.last().vertex[0] = firstVert+1;
 650   cf->mFaceList.last().vertex[1] = firstVert+0;
 651   cf->mFaceList.last().vertex[2] = firstVert+3;
 652
 653   cf->mFaceList.increment();
 654   cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]);
 655   cf->mFaceList.last().vertex[0] = firstVert+2;
 656   cf->mFaceList.last().vertex[1] = firstVert+1;
 657   cf->mFaceList.last().vertex[2] = firstVert+3;
 658
 659   cf->mFaceList.increment();
 660   cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]);
 661   cf->mFaceList.last().vertex[0] = firstVert+0;
 662   cf->mFaceList.last().vertex[1] = firstVert+2;
 663   cf->mFaceList.last().vertex[2] = firstVert+3;
 664}
 665
 666
 667void MeshRoadConvex::getPolyList( AbstractPolyList* list )
 668{
 669   // Transform the list into object space and set the pointer to the object
 670   //MatrixF i( mObject->getTransform() );
 671   //Point3F iS( mObject->getScale() );
 672   //list->setTransform(&i, iS);
 673
 674   list->setTransform( &MatrixF::Identity, Point3F::One );
 675   list->setObject(mObject);
 676
 677   // Points...
 678   S32 base =  list->addPoint(verts[1]);
 679   list->addPoint(verts[2]);
 680   list->addPoint(verts[0]);
 681   list->addPoint(verts[3]);
 682
 683   // Planes...
 684   list->begin(0,0);
 685   list->vertex(base + 2);
 686   list->vertex(base + 1);
 687   list->vertex(base + 0);
 688   list->plane(base + 2, base + 1, base + 0);
 689   list->end();
 690   list->begin(0,0);
 691   list->vertex(base + 2);
 692   list->vertex(base + 1);
 693   list->vertex(base + 3);
 694   list->plane(base + 2, base + 1, base + 3);
 695   list->end();
 696   list->begin(0,0);
 697   list->vertex(base + 3);
 698   list->vertex(base + 1);
 699   list->vertex(base + 0);
 700   list->plane(base + 3, base + 1, base + 0);
 701   list->end();
 702   list->begin(0,0);
 703   list->vertex(base + 2);
 704   list->vertex(base + 3);
 705   list->vertex(base + 0);
 706   list->plane(base + 2, base + 3, base + 0);
 707   list->end();
 708}
 709
 710
 711//------------------------------------------------------------------------------
 712// MeshRoadSegment Class
 713//------------------------------------------------------------------------------
 714
 715MeshRoadSegment::MeshRoadSegment()
 716{
 717   mPlaneCount = 0;
 718   columns = 0;
 719   rows = 0;
 720   numVerts = 0;
 721   numTriangles = 0;
 722
 723   startVert = 0;
 724   endVert = 0;
 725   startIndex = 0;
 726   endIndex = 0;
 727
 728   slice0 = NULL;
 729   slice1 = NULL;
 730}
 731
 732MeshRoadSegment::MeshRoadSegment( MeshRoadSlice *rs0, MeshRoadSlice *rs1, const MatrixF &roadMat )
 733{
 734   columns = 0;
 735   rows = 0;
 736   numVerts = 0;
 737   numTriangles = 0;
 738
 739   startVert = 0;
 740   endVert = 0;
 741   startIndex = 0;
 742   endIndex = 0;
 743
 744   slice0 = rs0;
 745   slice1 = rs1;
 746
 747   // Calculate the bounding box(s)
 748   worldbounds.minExtents = worldbounds.maxExtents = rs0->p0;
 749
 750   for(U32 i=0; i < rs0->verts.size(); i++)
 751      worldbounds.extend( rs0->verts[i] );
 752
 753   for(U32 i=0; i < rs1->verts.size(); i++)
 754      worldbounds.extend( rs1->verts[i] );
 755
 756   objectbounds = worldbounds;
 757   roadMat.mul( objectbounds );
 758
 759   // Calculate the planes for this segment
 760   // Will be used for intersection/buoyancy tests
 761
 762   mPlaneCount = 6;
 763   mPlanes[0].set( slice0->pb0, slice0->p0, slice1->p0 ); // left   
 764   mPlanes[1].set( slice1->pb2, slice1->p2, slice0->p2 ); // right   
 765   mPlanes[2].set( slice0->pb2, slice0->p2, slice0->p0 ); // near   
 766   mPlanes[3].set( slice1->p0, slice1->p2, slice1->pb2 ); // far   
 767   mPlanes[4].set( slice1->p2, slice1->p0, slice0->p0 ); // top   
 768   mPlanes[5].set( slice0->pb0, slice1->pb0, slice1->pb2 ); // bottom
 769}
 770
 771void MeshRoadSegment::set( MeshRoadSlice *rs0, MeshRoadSlice *rs1 )
 772{
 773   columns = 0;
 774   rows = 0;
 775   numVerts = 0;
 776   numTriangles = 0;
 777
 778   startVert = 0;
 779   endVert = 0;
 780   startIndex = 0;
 781   endIndex = 0;
 782
 783   slice0 = rs0;
 784   slice1 = rs1;
 785}
 786
 787bool MeshRoadSegment::intersectBox( const Box3F &bounds ) const
 788{
 789   // This code copied from Frustum class.
 790
 791   Point3F maxPoint;
 792   F32 maxDot;
 793
 794   // Note the planes are ordered left, right, near, 
 795   // far, top, bottom for getting early rejections
 796   // from the typical horizontal scene.
 797   for ( S32 i = 0; i < mPlaneCount; i++ )
 798   {
 799      // This is pretty much as optimal as you can
 800      // get for a plane vs AABB test...
 801      // 
 802      // 4 comparisons
 803      // 3 multiplies
 804      // 2 adds
 805      // 1 negation
 806      //
 807      // It will early out as soon as it detects the
 808      // bounds is outside one of the planes.
 809
 810      if ( mPlanes[i].x > 0 )
 811         maxPoint.x = bounds.maxExtents.x;
 812      else
 813         maxPoint.x = bounds.minExtents.x;
 814
 815      if ( mPlanes[i].y > 0 )
 816         maxPoint.y = bounds.maxExtents.y;
 817      else
 818         maxPoint.y = bounds.minExtents.y;
 819
 820      if ( mPlanes[i].z > 0 )
 821         maxPoint.z = bounds.maxExtents.z;
 822      else
 823         maxPoint.z = bounds.minExtents.z;
 824
 825      maxDot = mDot( maxPoint, mPlanes[ i ] );
 826
 827      if ( maxDot <= -mPlanes[ i ].d )
 828         return false;
 829   }
 830
 831   return true;
 832}
 833
 834bool MeshRoadSegment::containsPoint( const Point3F &pnt ) const
 835{
 836   // This code from Frustum class.
 837
 838   F32 maxDot;
 839
 840   // Note the planes are ordered left, right, near, 
 841   // far, top, bottom for getting early rejections
 842   // from the typical horizontal scene.
 843   for ( S32 i = 0; i < mPlaneCount; i++ )
 844   {
 845      const PlaneF &plane = mPlanes[ i ];
 846
 847      // This is pretty much as optimal as you can
 848      // get for a plane vs point test...
 849      // 
 850      // 1 comparison
 851      // 2 multiplies
 852      // 1 adds
 853      //
 854      // It will early out as soon as it detects the
 855      // point is outside one of the planes.
 856
 857      maxDot = mDot( pnt, plane ) + plane.d;
 858      if ( maxDot < 0.0f )
 859         return false;
 860   }
 861
 862   return true;
 863}
 864
 865F32 MeshRoadSegment::distanceToSurface(const Point3F &pnt) const
 866{
 867   return mPlanes[4].distToPlane( pnt );
 868}
 869
 870//------------------------------------------------------------------------------
 871// MeshRoad Class
 872//------------------------------------------------------------------------------
 873
 874ConsoleDocClass( MeshRoad,
 875   "@brief A strip of rectangular mesh segments defined by a 3D spline "
 876   "for prototyping road-shaped objects in your scene.\n\n"
 877   
 878   "User may control width and depth per node, overall spline shape in three "
 879   "dimensions, and seperate Materials for rendering the top, bottom, and side surfaces.\n\n"
 880   
 881   "MeshRoad is not capable of handling intersections, branches, curbs, or other "
 882   "desirable features in a final 'road' asset and is therefore intended for "
 883   "prototyping and experimentation.\n\n"
 884
 885   "Materials assigned to MeshRoad should tile vertically.\n\n"
 886
 887   "@ingroup Terrain"
 888);
 889
 890bool MeshRoad::smEditorOpen = false;
 891bool MeshRoad::smShowBatches = false;
 892bool MeshRoad::smShowSpline = true;
 893bool MeshRoad::smShowRoad = true;
 894bool MeshRoad::smShowRoadProfile = false;
 895bool MeshRoad::smWireframe = true;
 896SimObjectPtr<SimSet> MeshRoad::smServerMeshRoadSet = NULL;
 897
 898GFXStateBlockRef MeshRoad::smWireframeSB;
 899
 900IMPLEMENT_CO_NETOBJECT_V1(MeshRoad);
 901
 902MeshRoad::MeshRoad()
 903: mTextureLength( 5.0f ),
 904  mBreakAngle( 3.0f ),
 905  mWidthSubdivisions( 0 ),
 906  mPhysicsRep( NULL )
 907{
 908   mConvexList = new Convex;
 909
 910   // Setup NetObject.
 911   mTypeMask |= StaticObjectType | StaticShapeObjectType;
 912   mNetFlags.set(Ghostable);
 913
 914   mMatInst[Top] = NULL;
 915   mMatInst[Bottom] = NULL;
 916   mMatInst[Side] = NULL;
 917   mTypeMask |= TerrainLikeObjectType;
 918   for (U32 i = 0; i < SurfaceCount; i++)
 919   {
 920      mVertCount[i] = 0;
 921      mTriangleCount[i] = 0;
 922   }
 923
 924   initMaterialAsset(TopMaterial);
 925   initMaterialAsset(BottomMaterial);
 926   initMaterialAsset(SideMaterial);
 927
 928   mSideProfile.mRoad = this;
 929}
 930
 931MeshRoad::~MeshRoad()
 932{   
 933   delete mConvexList;
 934   mConvexList = NULL;
 935}
 936
 937void MeshRoad::initPersistFields()
 938{
 939   addGroup( "MeshRoad" );
 940
 941      addProtectedField("TopMaterial", TypeMaterialName, Offset(mTopMaterialName, MeshRoad), MeshRoad::_setTopMaterialName, & defaultProtectedGetFn, "Material for the upper surface of the road.", AbstractClassRep::FIELD_HideInInspectors); \
 942      addProtectedField("TopMaterialAsset", TypeMaterialAssetId, Offset(mTopMaterialAssetId, MeshRoad), MeshRoad::_setTopMaterialAsset, & defaultProtectedGetFn, "Material for the upper surface of the road.");
 943
 944      addProtectedField("BottomMaterial", TypeMaterialName, Offset(mBottomMaterialName, MeshRoad), MeshRoad::_setBottomMaterialName, & defaultProtectedGetFn, "Material for the bottom surface of the road.", AbstractClassRep::FIELD_HideInInspectors); \
 945      addProtectedField("BottomMaterialAsset", TypeMaterialAssetId, Offset(mBottomMaterialAssetId, MeshRoad), MeshRoad::_setBottomMaterialAsset, & defaultProtectedGetFn, "Material for the bottom surface of the road.");
 946
 947      addProtectedField("SideMaterial", TypeMaterialName, Offset(mSideMaterialName, MeshRoad), MeshRoad::_setSideMaterialName, & defaultProtectedGetFn, "Material for the left, right, front, and back surfaces of the road.", AbstractClassRep::FIELD_HideInInspectors); \
 948      addProtectedField("SideMaterialAsset", TypeMaterialAssetId, Offset(mSideMaterialAssetId, MeshRoad), MeshRoad::_setSideMaterialAsset, & defaultProtectedGetFn, "Material for the left, right, front, and back surfaces of the road.");
 949
 950      addField( "textureLength", TypeF32, Offset( mTextureLength, MeshRoad ), 
 951         "The length in meters of textures mapped to the MeshRoad." );      
 952
 953      addField( "breakAngle", TypeF32, Offset( mBreakAngle, MeshRoad ), 
 954         "Angle in degrees - MeshRoad will subdivide the spline if its curve is greater than this threshold." ); 
 955
 956      addField( "widthSubdivisions", TypeS32, Offset( mWidthSubdivisions, MeshRoad ), 
 957         "Subdivide segments widthwise this many times when generating vertices." );
 958
 959   endGroup( "MeshRoad" );
 960
 961   addGroup( "Internal" );
 962
 963      addProtectedField( "Node", TypeString, NULL, &addNodeFromField, &emptyStringProtectedGetFn, 
 964         "Do not modify, for internal use." );
 965
 966      addProtectedField( "ProfileNode", TypeString, NULL, &addProfileNodeFromField, &emptyStringProtectedGetFn,
 967         "Do not modify, for internal use." );
 968
 969   endGroup( "Internal" );
 970
 971   Parent::initPersistFields();
 972}
 973
 974void MeshRoad::consoleInit()
 975{
 976   Parent::consoleInit();
 977
 978   Con::addVariable( "$MeshRoad::EditorOpen", TypeBool, &MeshRoad::smEditorOpen, "True if the MeshRoad editor is open, otherwise false.\n"
 979      "@ingroup Editors\n");
 980   Con::addVariable( "$MeshRoad::wireframe", TypeBool, &MeshRoad::smWireframe, "If true, will render the wireframe of the road.\n"
 981      "@ingroup Editors\n");
 982   Con::addVariable( "$MeshRoad::showBatches", TypeBool, &MeshRoad::smShowBatches, "Determines if the debug rendering of the batches cubes is displayed or not.\n"
 983      "@ingroup Editors\n");
 984   Con::addVariable( "$MeshRoad::showSpline", TypeBool, &MeshRoad::smShowSpline, "If true, the spline on which the curvature of this road is based will be rendered.\n"
 985      "@ingroup Editors\n");
 986   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"
 987      "@ingroup Editors\n");
 988   Con::addVariable( "$MeshRoad::showRoadProfile", TypeBool, &MeshRoad::smShowRoadProfile, "If true, the road profile will be shown in the editor.\n"
 989      "@ingroup Editors\n");
 990}
 991
 992bool MeshRoad::addNodeFromField( void *object, const char *index, const char *data )
 993{
 994   MeshRoad *pObj = static_cast<MeshRoad*>(object);
 995
 996   //if ( !pObj->isProperlyAdded() )
 997   //{      
 998   F32 width, depth;
 999   Point3F pos, normal;      
1000   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 );      
1001   if ( result == 8 )
1002      pObj->_addNode( pos, width, depth, normal );      
1003   //}
1004
1005   return false;
1006}
1007
1008bool MeshRoad::addProfileNodeFromField( void* obj, const char *index, const char* data )
1009{
1010   MeshRoad *pObj = static_cast<MeshRoad*>(obj);
1011
1012   F32 x, y;
1013   U32 smooth, mtrl;
1014
1015   U32 result = dSscanf( data, "%g %g %d %d", &x, &y, &smooth, &mtrl );
1016   if ( result == 4 )
1017   {
1018      if(!pObj->mSideProfile.mNodes.empty())
1019         pObj->mSideProfile.mSegMtrls.push_back(mtrl);
1020
1021      MeshRoadProfileNode node;
1022      node.setPosition(x, y);
1023      node.setSmoothing(smooth != 0);
1024      pObj->mSideProfile.mNodes.push_back(node);
1025   }
1026
1027   return false;
1028}
1029
1030bool MeshRoad::onAdd()
1031{
1032   if ( !Parent::onAdd() ) 
1033      return false;
1034
1035   // Reset the World Box.
1036   //setGlobalBounds();
1037   resetWorldBox();
1038
1039   // Set the Render Transform.
1040   setRenderTransform(mObjToWorld);
1041
1042   // Add to ServerMeshRoadSet
1043   if ( isServerObject() )
1044   {
1045      getServerSet()->addObject( this );      
1046   }
1047
1048   if ( isClientObject() )
1049      _initMaterial();
1050
1051   // If this road was not created from a file, give profile two default nodes
1052   if(mSideProfile.mNodes.empty())
1053   {
1054      // Initialize with two nodes in vertical line with unit length
1055      MeshRoadProfileNode node1(Point3F(0.0f, 0.0f, 0.0f));
1056      MeshRoadProfileNode node2(Point3F(0.0f, -5.0f, 0.0f));
1057
1058      mSideProfile.mNodes.push_back(node1);
1059      mSideProfile.mNodes.push_back(node2);
1060
1061      // Both node normals are straight to the right, perpendicular to the profile line
1062      VectorF norm(1.0f, 0.0f, 0.0f);
1063
1064      mSideProfile.mNodeNormals.push_back(norm);
1065      mSideProfile.mNodeNormals.push_back(norm);
1066
1067      mSideProfile.mSegMtrls.push_back(MeshRoad::Side);
1068   }
1069   else
1070      mSideProfile.generateNormals();
1071
1072   // Generate the Vert/Index buffers and everything else.
1073   _regenerate();
1074
1075   // Add to Scene.
1076   addToScene();
1077
1078   return true;
1079}
1080
1081void MeshRoad::onRemove()
1082{
1083   SAFE_DELETE( mPhysicsRep );
1084
1085   mConvexList->nukeList();
1086
1087   for ( U32 i = 0; i < SurfaceCount; i++ )
1088   {
1089      SAFE_DELETE( mMatInst[i] );
1090   }
1091
1092   removeFromScene();
1093   Parent::onRemove();
1094}
1095
1096void MeshRoad::inspectPostApply()
1097{
1098   // Set Parent.
1099   Parent::inspectPostApply();
1100
1101   //if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT )
1102   //   mMetersPerSegment = MIN_METERS_PER_SEGMENT;
1103
1104   setMaskBits(MeshRoadMask);
1105}
1106
1107void MeshRoad::onStaticModified( const char* slotName, const char*newValue )
1108{
1109   Parent::onStaticModified( slotName, newValue );
1110
1111   if ( dStricmp( slotName, "breakAngle" ) == 0 )
1112   {
1113      setMaskBits( RegenMask );
1114   }
1115}
1116
1117void MeshRoad::writeFields( Stream &stream, U32 tabStop )
1118{
1119   Parent::writeFields( stream, tabStop );
1120
1121   // Now write all nodes
1122
1123   stream.write(2, "\r\n");   
1124
1125   for ( U32 i = 0; i < mNodes.size(); i++ )
1126   {
1127      const MeshRoadNode &node = mNodes[i];
1128
1129      stream.writeTabs(tabStop);
1130
1131      char buffer[1024];
1132      dMemset( buffer, 0, 1024 );
1133      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 );      
1134      stream.writeLine( (const U8*)buffer );
1135   }
1136
1137   stream.write(2, "\r\n");
1138
1139   Point3F nodePos;
1140   U8 smooth, mtrl;
1141
1142   for ( U32 i = 0; i < mSideProfile.mNodes.size(); i++ )
1143   {
1144      nodePos = mSideProfile.mNodes[i].getPosition();
1145
1146      if(i)
1147         mtrl = mSideProfile.mSegMtrls[i-1];
1148      else
1149         mtrl = 0;
1150
1151      if(mSideProfile.mNodes[i].isSmooth())
1152         smooth = 1;
1153      else
1154         smooth = 0;
1155
1156      stream.writeTabs(tabStop);
1157
1158      char buffer[1024];
1159      dMemset( buffer, 0, 1024 );
1160      dSprintf( buffer, 1024, "ProfileNode = \"%.6f %.6f %d %d\";", nodePos.x, nodePos.y, smooth, mtrl);
1161      stream.writeLine( (const U8*)buffer );
1162   }
1163}
1164
1165bool MeshRoad::writeField( StringTableEntry fieldname, const char *value )
1166{   
1167   if ( fieldname == StringTable->insert("Node") )
1168      return false;
1169
1170   if ( fieldname == StringTable->insert("ProfileNode") )
1171      return false;
1172
1173   return Parent::writeField( fieldname, value );
1174}
1175
1176void MeshRoad::onEditorEnable()
1177{
1178}
1179
1180void MeshRoad::onEditorDisable()
1181{
1182}
1183
1184SimSet* MeshRoad::getServerSet()
1185{
1186   if ( !smServerMeshRoadSet )
1187   {
1188      smServerMeshRoadSet = new SimSet();
1189      smServerMeshRoadSet->registerObject( "ServerMeshRoadSet" );
1190      Sim::getRootGroup()->addObject( smServerMeshRoadSet );
1191   }
1192
1193   return smServerMeshRoadSet;
1194}
1195
1196void MeshRoad::prepRenderImage( SceneRenderState* state )
1197{
1198   if ( mNodes.size() <= 1 )
1199      return;
1200
1201   RenderPassManager *renderPass = state->getRenderPass();
1202   
1203   // Normal Road RenderInstance
1204   // Always rendered when the editor is not open
1205   // otherwise obey the smShowRoad flag
1206   if ( smShowRoad || !smEditorOpen )
1207   {
1208#ifdef TORQUE_AFX_ENABLED
1209      afxZodiacMgr::renderMeshRoadZodiacs(state, this);
1210#endif
1211      MeshRenderInst coreRI;
1212      coreRI.clear();
1213      coreRI.objectToWorld = &MatrixF::Identity;
1214      coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
1215      coreRI.projection = renderPass->allocSharedXform(RenderPassManager::Projection);
1216      coreRI.type = RenderPassManager::RIT_Mesh;      
1217      
1218      BaseMatInstance *matInst;
1219      for ( U32 i = 0; i < SurfaceCount; i++ )
1220      {             
1221         matInst = state->getOverrideMaterial( mMatInst[i] );   
1222         if ( !matInst )
1223            continue;
1224
1225         // Get the lights if we haven't already.
1226         if ( matInst->isForwardLit() && !coreRI.lights[0] )
1227         {
1228            LightQuery query;
1229            query.init( getWorldSphere() );
1230            query.getLights( coreRI.lights, 8 );
1231         }
1232
1233         MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
1234         *ri = coreRI;
1235
1236         // Currently rendering whole road, fix to cull and batch
1237         // per segment.
1238
1239         // Set the correct material for rendering.
1240         ri->matInst = matInst;
1241         ri->vertBuff = &mVB[i];
1242         ri->primBuff = &mPB[i];
1243
1244         ri->prim = renderPass->allocPrim();
1245         ri->prim->type = GFXTriangleList;
1246         ri->prim->minIndex = 0;
1247         ri->prim->startIndex = 0;
1248         ri->prim->numPrimitives = mTriangleCount[i];
1249         ri->prim->startVertex = 0;
1250         ri->prim->numVertices = mVertCount[i];
1251
1252         // We sort by the material then vertex buffer.
1253         ri->defaultKey = matInst->getStateHint();
1254         ri->defaultKey2 = (uintptr_t)ri->vertBuff; // Not 64bit safe!
1255
1256         renderPass->addInst( ri );  
1257      }
1258   }
1259
1260   // Debug RenderInstance
1261   // Only when editor is open.
1262   if ( smEditorOpen )
1263   {
1264      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
1265      ri->renderDelegate.bind( this, &MeshRoad::_debugRender );
1266      ri->type = RenderPassManager::RIT_Editor;
1267      state->getRenderPass()->addInst( ri );
1268   }
1269}
1270
1271void MeshRoad::_initMaterial()
1272{
1273   if (mTopMaterialAsset.notNull())
1274   {
1275      if (!mMatInst[Top] || !String(mTopMaterialAsset->getMaterialDefinitionName()).equal(mMatInst[Top]->getMaterial()->getName(), String::NoCase))
1276      {
1277         SAFE_DELETE(mMatInst[Top]);
1278
1279         Material* tMat = nullptr;
1280         if (!Sim::findObject(mTopMaterialAsset->getMaterialDefinitionName(), tMat))
1281            Con::errorf("MeshRoad::_initMaterial - Material %s was not found.", mTopMaterialAsset->getMaterialDefinitionName());
1282
1283         mMaterial[Top] = tMat;
1284
1285         if (mMaterial[Top])
1286            mMatInst[Top] = mMaterial[Top]->createMatInstance();
1287         else
1288            mMatInst[Top] = MATMGR->createMatInstance("WarningMaterial");
1289
1290         mMatInst[Top]->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>());
1291      }
1292   }
1293
1294   if (mBottomMaterialAsset.notNull())
1295   {
1296      if (!mMatInst[Bottom] || !String(mBottomMaterialAsset->getMaterialDefinitionName()).equal(mMatInst[Bottom]->getMaterial()->getName(), String::NoCase))
1297      {
1298
1299         SAFE_DELETE(mMatInst[Bottom]);
1300
1301         Material* tMat = nullptr;
1302         if (!Sim::findObject(mBottomMaterialAsset->getMaterialDefinitionName(), tMat))
1303            Con::errorf("MeshRoad::_initMaterial - Material %s was not found.", mBottomMaterialAsset->getMaterialDefinitionName());
1304
1305         mMaterial[Bottom] = tMat;
1306
1307         if (mMaterial[Bottom])
1308            mMatInst[Bottom] = mMaterial[Bottom]->createMatInstance();
1309         else
1310            mMatInst[Bottom] = MATMGR->createMatInstance("WarningMaterial");
1311
1312         mMatInst[Bottom]->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>());
1313      }
1314   }
1315
1316   if (mSideMaterialAsset.notNull())
1317   {
1318      if (!mMatInst[Side] || !String(mSideMaterialAsset->getMaterialDefinitionName()).equal(mMatInst[Side]->getMaterial()->getName(), String::NoCase))
1319      {
1320         SAFE_DELETE(mMatInst[Side]);
1321
1322         Material* tMat = nullptr;
1323         if (!Sim::findObject(mSideMaterialAsset->getMaterialDefinitionName(), tMat))
1324            Con::errorf("MeshRoad::_initMaterial - Material %s was not found.", mSideMaterialAsset->getMaterialDefinitionName());
1325
1326         mMaterial[Side] = tMat;
1327
1328         if (mMaterial[Side])
1329            mMatInst[Side] = mMaterial[Side]->createMatInstance();
1330         else
1331            mMatInst[Side] = MATMGR->createMatInstance("WarningMaterial");
1332
1333         mMatInst[Side]->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat<GFXVertexPNTT>());
1334      }
1335   }
1336}
1337
1338void MeshRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* )
1339{
1340   //MeshRoadConvex convex;
1341   //buildConvex( Box3F(true), convex );
1342   //convex.render();   
1343   //GFXDrawUtil *drawer = GFX->getDrawUtil();
1344
1345   //GFX->setStateBlock( smStateBlock );
1346   return;
1347   /*
1348   U32 convexCount = mDebugConvex.size();
1349
1350   PrimBuild::begin( GFXTriangleList, convexCount * 12 );
1351   PrimBuild::color4i( 0, 0, 255, 155 );
1352
1353   for ( U32 i = 0; i < convexCount; i++ )
1354   {
1355      MeshRoadConvex *convex = mDebugConvex[i];
1356
1357      Point3F a = convex->verts[0];
1358      Point3F b = convex->verts[1];
1359      Point3F c = convex->verts[2];
1360      Point3F p = convex->verts[3];
1361
1362      //mObjToWorld.mulP(a);
1363      //mObjToWorld.mulP(b);
1364      //mObjToWorld.mulP(c);
1365      //mObjToWorld.mulP(p);
1366      
1367      PrimBuild::vertex3fv( c );
1368      PrimBuild::vertex3fv( b );
1369      PrimBuild::vertex3fv( a );      
1370
1371      PrimBuild::vertex3fv( b );
1372      PrimBuild::vertex3fv( a );
1373      PrimBuild::vertex3fv( p );
1374
1375      PrimBuild::vertex3fv( c );
1376      PrimBuild::vertex3fv( b );
1377      PrimBuild::vertex3fv( p );
1378
1379      PrimBuild::vertex3fv( a );
1380      PrimBuild::vertex3fv( c );
1381      PrimBuild::vertex3fv( p );
1382   }
1383
1384   PrimBuild::end();
1385
1386   for ( U32 i = 0; i < mSegments.size(); i++ )
1387   {
1388      ///GFX->getDrawUtil()->drawWireBox( mSegments[i].worldbounds, ColorI(255,0,0,255) );
1389   }
1390
1391   GFX->enterDebugEvent( ColorI( 255, 0, 0 ), "DecalRoad_debugRender" );
1392   GFXTransformSaver saver;
1393
1394   GFX->setStateBlock( smStateBlock );      
1395
1396   Point3F size(1,1,1);
1397   ColorI color( 255, 0, 0, 255 );
1398
1399   if ( smShowBatches )  
1400   {
1401      for ( U32 i = 0; i < mBatches.size(); i++ )   
1402      {
1403         const Box3F &box = mBatches[i].bounds;
1404         Point3F center;
1405         box.getCenter( &center );
1406
1407         GFX->getDrawUtil()->drawWireCube( ( box.maxExtents - box.minExtents ) * 0.5f, center, ColorI(255,100,100,255) );         
1408      }
1409   }
1410
1411   GFX->leaveDebugEvent();
1412   */
1413}
1414
1415U32 MeshRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
1416{  
1417   U32 retMask = Parent::packUpdate(con, mask, stream);
1418
1419   if ( stream->writeFlag( mask & MeshRoadMask ) )
1420   {
1421      // Write Object Transform.
1422      stream->writeAffineTransform( mObjToWorld );
1423
1424      // Write Materials
1425      packMaterialAsset(con, TopMaterial);
1426      packMaterialAsset(con, BottomMaterial);
1427      packMaterialAsset(con, SideMaterial);
1428
1429      stream->write( mTextureLength );      
1430      stream->write( mBreakAngle );
1431      stream->write( mWidthSubdivisions );
1432   }
1433
1434   if ( stream->writeFlag( mask & ProfileMask ) )
1435   {
1436      stream->writeInt( mSideProfile.mNodes.size(), 16 );
1437
1438      for( U32 i = 0; i < mSideProfile.mNodes.size(); i++ )
1439      {
1440         mathWrite( *stream, mSideProfile.mNodes[i].getPosition() );
1441         stream->writeFlag( mSideProfile.mNodes[i].isSmooth() );
1442
1443         if(i)
1444            stream->writeInt(mSideProfile.mSegMtrls[i-1], 3);
1445         else
1446            stream->writeInt(0, 3);
1447      }
1448   }
1449
1450   if ( stream->writeFlag( mask & NodeMask ) )
1451   {
1452      const U32 nodeByteSize = 32; // Based on sending all of a node's parameters
1453
1454      // Test if we can fit all of our nodes within the current stream.
1455      // We make sure we leave 100 bytes still free in the stream for whatever
1456      // may follow us.
1457      S32 allowedBytes = stream->getWriteByteSize() - 100;
1458      if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) )
1459      {
1460         // All nodes should fit, so send them out now.
1461         stream->writeInt( mNodes.size(), 16 );
1462
1463         for ( U32 i = 0; i < mNodes.size(); i++ )
1464         {
1465            mathWrite( *stream, mNodes[i].point );
1466            stream->write( mNodes[i].width );
1467            stream->write( mNodes[i].depth );
1468            mathWrite( *stream, mNodes[i].normal );         
1469         }
1470      }
1471      else
1472      {
1473         // There isn't enough space left in the stream for all of the
1474         // nodes.  Batch them up into NetEvents.
1475         U32 id = gServerNodeListManager->nextListId();
1476         U32 count = 0;
1477         U32 index = 0;
1478         while (count < mNodes.size())
1479         {
1480            count += NodeListManager::smMaximumNodesPerEvent;
1481            if (count > mNodes.size())
1482            {
1483               count = mNodes.size();
1484            }
1485
1486            MeshRoadNodeEvent* event = new MeshRoadNodeEvent();
1487            event->mId = id;
1488            event->mTotalNodes = mNodes.size();
1489            event->mLocalListStart = index;
1490
1491            for (; index<count; ++index)
1492            {
1493               event->mPositions.push_back( mNodes[index].point );
1494               event->mWidths.push_back( mNodes[index].width );
1495               event->mDepths.push_back( mNodes[index].depth );
1496               event->mNormals.push_back( mNodes[index].normal );
1497            }
1498
1499            con->postNetEvent( event );
1500         }
1501
1502         stream->write( id );
1503      }
1504   }
1505
1506   stream->writeFlag( mask & RegenMask );
1507
1508   // Were done ...
1509   return retMask;
1510}
1511
1512void MeshRoad::unpackUpdate(NetConnection * con, BitStream * stream)
1513{
1514   // Unpack Parent.
1515   Parent::unpackUpdate(con, stream);
1516
1517   // MeshRoadMask
1518   if(stream->readFlag())
1519   {
1520      MatrixF     ObjectMatrix;
1521      stream->readAffineTransform(&ObjectMatrix);
1522      Parent::setTransform(ObjectMatrix);
1523
1524      unpackMaterialAsset(con, TopMaterial);
1525      unpackMaterialAsset(con, BottomMaterial);
1526      unpackMaterialAsset(con, SideMaterial);
1527
1528      if ( isProperlyAdded() )
1529         _initMaterial(); 
1530
1531      stream->read( &mTextureLength );
1532
1533      stream->read( &mBreakAngle );
1534
1535      stream->read( &mWidthSubdivisions );
1536   }
1537
1538   // ProfileMask
1539   if(stream->readFlag())
1540   {
1541      Point3F pos;
1542
1543      mSideProfile.mNodes.clear();
1544      mSideProfile.mSegMtrls.clear();
1545
1546      U32 count = stream->readInt( 16 );
1547
1548      for( U32 i = 0; i < count; i++)
1549      {
1550         mathRead( *stream, &pos );
1551         MeshRoadProfileNode node(pos);
1552         node.setSmoothing( stream->readFlag() );
1553         mSideProfile.mNodes.push_back(node);
1554
1555         if(i)
1556            mSideProfile.mSegMtrls.push_back(stream->readInt(3));
1557         else
1558            stream->readInt(3);
1559      }
1560
1561      mSideProfile.generateNormals();
1562   }
1563
1564   // NodeMask
1565   if ( stream->readFlag() )
1566   {
1567      if (stream->readFlag())
1568      {
1569         // Nodes have been passed in this update
1570         U32 count = stream->readInt( 16 );
1571
1572         mNodes.clear();
1573
1574         Point3F pos, normal;
1575         F32 width, depth;
1576         for ( U32 i = 0; i < count; i++ )
1577         {
1578            mathRead( *stream, &pos );
1579            stream->read( &width );   
1580            stream->read( &depth );
1581            mathRead( *stream, &normal );
1582            _addNode( pos, width, depth, normal );           
1583         }
1584      }
1585      else
1586      {
1587         // Nodes will arrive as events
1588         U32 id;
1589         stream->read( &id );
1590
1591         // Check if the road's nodes made it here before we did.
1592         NodeListManager::NodeList* list = NULL;
1593         if ( gClientNodeListManager->findListById( id, &list, true) )
1594         {
1595            // Work with the completed list
1596            MeshRoadNodeList* roadList = dynamic_cast<MeshRoadNodeList*>( list );
1597            if (roadList)
1598               buildNodesFromList( roadList );
1599
1600            delete list;
1601         }
1602         else
1603         {
1604            // Nodes have not yet arrived, so register our interest in the list
1605            MeshRoadNodeListNotify* notify = new MeshRoadNodeListNotify( this, id );
1606            gClientNodeListManager->registerNotification( notify );
1607         }
1608      }
1609   }
1610
1611   if ( stream->readFlag() && isProperlyAdded() )   
1612      _regenerate();
1613}
1614
1615void MeshRoad::setTransform( const MatrixF &mat )
1616{
1617   for ( U32 i = 0; i < mNodes.size(); i++ )
1618   {
1619      mWorldToObj.mulP( mNodes[i].point );
1620      mat.mulP( mNodes[i].point );
1621   }
1622
1623   Parent::setTransform( mat );
1624
1625   if ( mPhysicsRep )
1626      mPhysicsRep->setTransform( mat );
1627
1628   // Regenerate and update the client
1629   _regenerate();
1630   setMaskBits( NodeMask | RegenMask );
1631}
1632
1633void MeshRoad::setScale( const VectorF &scale )
1634{
1635   // We ignore scale requests from the editor
1636   // right now.
1637
1638   //Parent::setScale( scale );
1639}
1640
1641void MeshRoad::buildConvex(const Box3F& box, Convex* convex)
1642{
1643   if ( mSlices.size() < 2 )
1644      return;
1645
1646   mConvexList->collectGarbage();
1647   mDebugConvex.clear();
1648
1649   Box3F realBox = box;
1650   mWorldToObj.mul(realBox);
1651   realBox.minExtents.convolveInverse(mObjScale);
1652   realBox.maxExtents.convolveInverse(mObjScale);
1653
1654   if (realBox.isOverlapped(getObjBox()) == false)
1655      return;
1656
1657   U32 segmentCount = mSegments.size();
1658
1659   U32 numConvexes ;
1660   U32 halfConvexes;
1661   U32 nextSegOffset = 2*mSideProfile.mNodes.size();
1662   U32 leftSideOffset = nextSegOffset/2;
1663   U32 k2, capIdx1, capIdx2, capIdx3;
1664
1665   // Create convex(s) for each segment
1666   for ( U32 i = 0; i < segmentCount; i++ )
1667   {
1668      const MeshRoadSegment &segment = mSegments[i];
1669
1670      // Is this segment overlapped?
1671      if ( !segment.getWorldBounds().isOverlapped( box ) )
1672         continue;
1673
1674      // Each segment has 6 faces
1675      for ( U32 j = 0; j < 6; j++ )
1676      {
1677         // Only first segment has front face
1678         if ( j == 4 && i != 0 )
1679            continue;
1680         // Only last segment has back face
1681         if ( j == 5 && i != segmentCount-1 )
1682            continue;
1683
1684         // The top and bottom sides have 2 convex(s)
1685         // The left, right, front, and back sides depend on the user-defined profile
1686         switch(j)
1687         {
1688            case 0:  numConvexes = 2; break;                                     // Top
1689            case 1:                                                              // Left
1690            case 2:  numConvexes = 2* (mSideProfile.mNodes.size()-1); break;     // Right
1691            case 3:  numConvexes = 2; break;                                     // Bottom
1692            case 4:                                                              // Front
1693            case 5:  numConvexes = mSideProfile.mCap.getNumTris(); break;        // Back
1694            default: numConvexes = 0;
1695         }
1696
1697         halfConvexes = numConvexes/2;
1698
1699         for ( U32 k = 0; k < numConvexes; k++ )
1700         {
1701            // See if this convex exists in the working set already...
1702            Convex* cc = 0;
1703            CollisionWorkingList& wl = convex->getWorkingList();
1704            for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) 
1705            {
1706               if ( itr->mConvex->getType() == MeshRoadConvexType )
1707               {
1708                  MeshRoadConvex *pConvex = static_cast<MeshRoadConvex*>(itr->mConvex);
1709
1710                  if ( pConvex->pRoad == this &&
1711                       pConvex->segmentId == i &&
1712                       pConvex->faceId == j &&
1713                       pConvex->triangleId == k )           
1714                  {
1715                     cc = itr->mConvex;
1716                     break;
1717                  }
1718               }
1719            }
1720            if (cc)
1721               continue;
1722
1723            Point3F a, b, c;
1724
1725            // Top or Bottom
1726            if(j == 0 || j == 3)
1727            {
1728               // Get the triangle...
1729               U32 idx0 = gIdxArray[j][k][0];
1730               U32 idx1 = gIdxArray[j][k][1];
1731               U32 idx2 = gIdxArray[j][k][2];
1732
1733               a = segment[idx0];
1734               b = segment[idx1];
1735               c = segment[idx2];
1736            }
1737            // Left Side
1738            else if(j == 1)
1739            {
1740               if(k >= halfConvexes)
1741               {
1742                  k2 = k + leftSideOffset - halfConvexes;
1743                  a = segment.slice1->verts[k2];
1744                  b = segment.slice0->verts[k2];
1745                  c = segment.slice1->verts[k2 + 1];
1746               }
1747               else
1748               {
1749                  k2 = k + leftSideOffset;
1750                  a = segment.slice0->verts[k2];
1751                  b = segment.slice0->verts[k2 + 1];
1752                  c = segment.slice1->verts[k2 + 1];
1753               }
1754            }
1755            // Right Side
1756            else if(j == 2)
1757            {
1758//             a.set(2*k, 2*k, 0.0f);
1759//             b.set(2*k, 2*k, 2.0f);
1760//             c.set(2*(k+1), 2*(k+1), 0.0f);
1761
1762               if(k >= halfConvexes)
1763               {
1764                  k2 = k - halfConvexes;
1765                  a = segment.slice1->verts[k2];
1766                  b = segment.slice1->verts[k2 + 1];
1767                  c = segment.slice0->verts[k2];
1768               }
1769               else
1770               {
1771                  a = segment.slice0->verts[k];
1772                  b = segment.slice1->verts[k + 1];
1773                  c = segment.slice0->verts[k + 1];
1774               }
1775            }
1776            // Front
1777            else if(j == 4)
1778            {
1779               k2 = nextSegOffset + leftSideOffset - 1;
1780
1781               capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
1782               capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
1783               capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
1784
1785               if(capIdx1 >= leftSideOffset)
1786                  capIdx1 = k2 - capIdx1;
1787
1788               if(capIdx2 >= leftSideOffset)
1789                  capIdx2 = k2 - capIdx2;
1790
1791               if(capIdx3 >= leftSideOffset)
1792                  capIdx3 = k2 - capIdx3;
1793
1794               a = segment.slice0->verts[capIdx1];
1795               b = segment.slice0->verts[capIdx2];
1796               c = segment.slice0->verts[capIdx3];
1797            }
1798            // Back
1799            else
1800            {
1801               k2 = nextSegOffset + leftSideOffset - 1;
1802
1803               capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
1804               capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
1805               capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
1806
1807               if(capIdx1 >= leftSideOffset)
1808                  capIdx1 = k2 - capIdx1;
1809
1810               if(capIdx2 >= leftSideOffset)
1811                  capIdx2 = k2 - capIdx2;
1812
1813               if(capIdx3 >= leftSideOffset)
1814                  capIdx3 = k2 - capIdx3;
1815
1816               a = segment.slice1->verts[capIdx3];
1817               b = segment.slice1->verts[capIdx2];
1818               c = segment.slice1->verts[capIdx1];
1819            }
1820            
1821            // Transform the result into object space!
1822            //mWorldToObj.mulP( a );
1823            //mWorldToObj.mulP( b );
1824            //mWorldToObj.mulP( c );            
1825
1826            PlaneF p( c, b, a );
1827            Point3F peak = ((a + b + c) / 3.0f) + (p * 0.15f);
1828
1829            // Set up the convex...
1830            MeshRoadConvex *cp = new MeshRoadConvex();
1831
1832            mConvexList->registerObject( cp );
1833            convex->addToWorkingList( cp );
1834
1835            cp->mObject    = this;
1836            cp->pRoad   = this;
1837            cp->segmentId = i;
1838            cp->faceId = j;
1839            cp->triangleId = k;
1840
1841            cp->normal = p;
1842            cp->verts[0] = c;
1843            cp->verts[1] = b;
1844            cp->verts[2] = a;
1845            cp->verts[3] = peak;
1846
1847            // Update the bounding box.
1848            Box3F &bounds = cp->box;
1849            bounds.minExtents.set( F32_MAX,  F32_MAX,  F32_MAX );
1850            bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX );
1851
1852            bounds.minExtents.setMin( a );
1853            bounds.minExtents.setMin( b );
1854            bounds.minExtents.setMin( c );
1855            bounds.minExtents.setMin( peak );
1856
1857            bounds.maxExtents.setMax( a );
1858            bounds.maxExtents.setMax( b );
1859            bounds.maxExtents.setMax( c );
1860            bounds.maxExtents.setMax( peak );
1861
1862            mDebugConvex.push_back(cp);
1863         }
1864      }
1865   }
1866}
1867
1868bool MeshRoad::buildPolyList( PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF & )
1869{
1870   if ( mSlices.size() < 2 )
1871      return false;
1872
1873   polyList->setTransform( &MatrixF::Identity, Point3F::One );
1874   polyList->setObject(this);
1875
1876   // JCF: optimize this to not always add everything.
1877
1878   return buildSegmentPolyList( polyList, 0, mSegments.size() - 1, true, true );
1879}
1880
1881bool MeshRoad::buildSegmentPolyList( AbstractPolyList* polyList, U32 startSegIdx, U32 endSegIdx, bool capFront, bool capEnd )
1882{
1883   if ( mSlices.size() < 2 )
1884      return false;
1885
1886   // Add verts
1887   for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
1888   {
1889      const MeshRoadSegment &seg = mSegments[i];
1890
1891      if ( i == startSegIdx )
1892      {
1893         for(U32 j = 0; j < seg.slice0->verts.size(); j++)
1894            polyList->addPoint( seg.slice0->verts[j] );
1895      }
1896
1897      for(U32 j = 0; j < seg.slice1->verts.size(); j++)
1898         polyList->addPoint( seg.slice1->verts[j] );
1899   }
1900
1901   // Temporaries to hold indices for the corner points of a quad.
1902   S32 p00, p01, p11, p10;
1903   S32 pb00, pb01, pb11, pb10;
1904   U32 offset = 0;
1905   S32 a, b, c;
1906   U32 mirror;
1907
1908   DebugDrawer *ddraw = NULL;//DebugDrawer::get();
1909   ClippedPolyList *cpolyList = dynamic_cast<ClippedPolyList*>(polyList);
1910   MatrixF mat;
1911   Point3F scale;
1912   if ( cpolyList )
1913      cpolyList->getTransform( &mat, &scale );
1914
1915   U32 nextSegOffset = 2*mSideProfile.mNodes.size();
1916   U32 leftSideOffset = nextSegOffset/2;
1917
1918   for ( U32 i = startSegIdx; i <= endSegIdx; i++ )
1919   {
1920      p00 = offset + leftSideOffset;
1921      p10 = offset;
1922      pb00 = offset + nextSegOffset - 1;
1923      pb10 = offset + leftSideOffset - 1;
1924      p01 = offset + nextSegOffset + leftSideOffset;
1925      p11 = offset + nextSegOffset;
1926      pb01 = offset + 2*nextSegOffset - 1;
1927      pb11 = offset + nextSegOffset + leftSideOffset - 1;
1928
1929      // Top Face
1930
1931      polyList->begin( 0,0 );
1932      polyList->vertex( p00 );
1933      polyList->vertex( p01 );
1934      polyList->vertex( p11 );
1935      polyList->plane( p00, p01, p11 );
1936      polyList->end();
1937
1938      if ( ddraw && cpolyList )
1939      {
1940         Point3F v0 = cpolyList->mVertexList[p00].point;
1941         mat.mulP( v0 );
1942         Point3F v1 = cpolyList->mVertexList[p01].point;
1943         mat.mulP( v1 );
1944         Point3F v2 = cpolyList->mVertexList[p11].point;
1945         mat.mulP( v2 );
1946         ddraw->drawTri( v0, v1, v2 );
1947         ddraw->setLastZTest( false );
1948         ddraw->setLastTTL( 0 );
1949      }
1950
1951      polyList->begin( 0,0 );
1952      polyList->vertex( p00 );
1953      polyList->vertex( p11 );
1954      polyList->vertex( p10 );
1955      polyList->plane( p00, p11, p10 );
1956      polyList->end();
1957      if ( ddraw && cpolyList )
1958      {
1959         ddraw->drawTri( cpolyList->mVertexList[p00].point, cpolyList->mVertexList[p11].point, cpolyList->mVertexList[p10].point );
1960         ddraw->setLastTTL( 0 );
1961      }
1962
1963      if (buildPolyList_TopSurfaceOnly)
1964      {
1965         offset += 4;
1966         continue;
1967      }
1968      // Left Face
1969      for(U32 j = leftSideOffset; j < nextSegOffset-1; j++)
1970      {
1971         a = offset + j;
1972         b = a + nextSegOffset + 1;
1973         c = b - 1;
1974
1975         polyList->begin( 0,0 );
1976         polyList->vertex( a );
1977         polyList->vertex( b );
1978         polyList->vertex( c);
1979         polyList->plane( a, b, c );
1980         polyList->end();
1981
1982         a = offset + j;
1983         b = a + 1;
1984         c = a + nextSegOffset + 1;
1985
1986         polyList->begin( 0,0 );
1987         polyList->vertex( a );
1988         polyList->vertex( b );
1989         polyList->vertex( c );
1990         polyList->plane( a, b, c );
1991         polyList->end();
1992      }
1993
1994      // Right Face
1995      for(U32 j = 0; j < leftSideOffset-1; j++)
1996      {
1997         a = offset + j;
1998         b = a + nextSegOffset;
1999         c = b + 1;
2000
2001         polyList->begin( 0,0 );
2002         polyList->vertex( a );
2003         polyList->vertex( b );
2004         polyList->vertex( c);
2005         polyList->plane( a, b, c );
2006         polyList->end();
2007
2008         a = offset + j;
2009         b = a + nextSegOffset + 1;
2010         c = a + 1;
2011
2012         polyList->begin( 0,0 );
2013         polyList->vertex( a );
2014         polyList->vertex( b );
2015         polyList->vertex( c );
2016         polyList->plane( a, b, c );
2017         polyList->end();
2018      }
2019
2020      // Bottom Face
2021
2022      polyList->begin( 0,0 );
2023      polyList->vertex( pb00 );
2024      polyList->vertex( pb10 );
2025      polyList->vertex( pb11 );
2026      polyList->plane( pb00, pb10, pb11 );
2027      polyList->end();
2028
2029      polyList->begin( 0,0 );
2030      polyList->vertex( pb00 );
2031      polyList->vertex( pb11 );
2032      polyList->vertex( pb01 );
2033      polyList->plane( pb00, pb11, pb01 );
2034      polyList->end();  
2035
2036      // Front Face
2037
2038      if ( i == startSegIdx && capFront )
2039      {
2040         mirror = nextSegOffset + leftSideOffset - 1;
2041
2042         for(U32 j = 0; j < mSideProfile.mCap.getNumTris(); j++)
2043         {
2044            a = mSideProfile.mCap.getTriIdx(j, 0);
2045            b = mSideProfile.mCap.getTriIdx(j, 1);
2046            c = mSideProfile.mCap.getTriIdx(j, 2);
2047
2048            if(a >= leftSideOffset)
2049               a = mirror - a;
2050
2051            if(b >= leftSideOffset)
2052               b = mirror - b;
2053
2054            if(c >= leftSideOffset)
2055               c = mirror - c;
2056
2057            polyList->begin( 0,0 );
2058            polyList->vertex( a );
2059            polyList->vertex( b );
2060            polyList->vertex( c );
2061            polyList->plane( a, b, c );
2062            polyList->end();
2063         }
2064      }
2065
2066      // Back Face
2067      if ( i == endSegIdx && capEnd )
2068      {
2069         mirror = nextSegOffset + leftSideOffset - 1;
2070
2071         for(U32 j = 0; j < mSideProfile.mCap.getNumTris(); j++)
2072         {
2073            a = mSideProfile.mCap.getTriIdx(j, 0);
2074            b = mSideProfile.mCap.getTriIdx(j, 1);
2075            c = mSideProfile.mCap.getTriIdx(j, 2);
2076
2077            if(a >= leftSideOffset)
2078               a = offset + nextSegOffset + mirror - a;
2079
2080            if(b >= leftSideOffset)
2081               b = offset + nextSegOffset + mirror - b;
2082
2083            if(c >= leftSideOffset)
2084               c = offset + nextSegOffset + mirror - c;
2085
2086            polyList->begin( 0,0 );
2087            polyList->vertex( c );
2088            polyList->vertex( b );
2089            polyList->vertex( a );
2090            polyList->plane( c, b, a );
2091            polyList->end();
2092         }
2093      }
2094
2095      offset += nextSegOffset;
2096   }
2097
2098   return true;
2099}
2100
2101bool MeshRoad::castRay( const Point3F &s, const Point3F &e, RayInfo *info )
2102{
2103   Point3F start = s;
2104   Point3F end = e;
2105   mObjToWorld.mulP(start);
2106   mObjToWorld.mulP(end);
2107
2108   F32 out = 1.0f;   // The output fraction/percentage along the line defined by s and e
2109   VectorF norm(0.0f, 0.0f, 0.0f);     // The normal of the face intersected
2110   
2111   Vector<MeshRoadHitSegment> hitSegments;
2112
2113   for ( U32 i = 0; i < mSegments.size(); i++ )
2114   {
2115      const MeshRoadSegment &segment = mSegments[i];
2116
2117      F32 t;
2118      VectorF n;
2119
2120      if ( segment.getWorldBounds().collideLine( start, end, &t, &n ) )
2121      {
2122         hitSegments.increment();
2123         hitSegments.last().t = t;
2124         hitSegments.last().idx = i;         
2125      }
2126   }
2127
2128   dQsort( hitSegments.address(), hitSegments.size(), sizeof(MeshRoadHitSegment), compareHitSegments );
2129
2130   U32 idx0, idx1, idx2;
2131   F32 t;
2132
2133   for ( U32 i = 0; i < hitSegments.size(); i++ )
2134   {
2135      U32 segIdx = hitSegments[i].idx;
2136      const MeshRoadSegment &segment = mSegments[segIdx];
2137
2138      U32 numConvexes ;
2139      U32 halfConvexes;
2140      U32 nextSegOffset = 2*mSideProfile.mNodes.size();
2141      U32 leftSideOffset = nextSegOffset/2;
2142      U32 k2, capIdx1, capIdx2, capIdx3;
2143
2144      // Each segment has 6 faces
2145      for ( U32 j = 0; j < 6; j++ )
2146      {
2147         if ( j == 4 && segIdx != 0 )
2148            continue;
2149
2150         if ( j == 5 && segIdx != mSegments.size() - 1 )
2151            continue;
2152
2153         // The top and bottom sides have 2 convex(s)
2154         // The left, right, front, and back sides depend on the user-defined profile
2155         switch(j)
2156         {
2157         case 0:  numConvexes = 2; break;                                  // Top
2158         case 1:                                                           // Left
2159         case 2:  numConvexes = 2* (mSideProfile.mNodes.size()-1); break;  // Right
2160         case 3:  numConvexes = 2; break;                                  // Bottom
2161         case 4:                                                           // Front
2162         case 5:  numConvexes = mSideProfile.mCap.getNumTris(); break;     // Back
2163         default: numConvexes = 0;
2164         }
2165
2166         halfConvexes = numConvexes/2;
2167
2168         // Each face has 2 triangles
2169         for ( U32 k = 0; k < numConvexes; k++ )
2170         {
2171            const Point3F *a = NULL;
2172            const Point3F *b = NULL;
2173            const Point3F *c = NULL;
2174
2175            // Top or Bottom
2176            if(j == 0 || j == 3)
2177            {
2178               idx0 = gIdxArray[j][k][0];
2179               idx1 = gIdxArray[j][k][1];
2180               idx2 = gIdxArray[j][k][2];
2181
2182               a = &segment[idx0];
2183               b = &segment[idx1];
2184               c = &segment[idx2];
2185            }
2186            // Left Side
2187            else if(j == 1)
2188            {
2189               if(k >= halfConvexes)
2190               {
2191                  k2 = k + leftSideOffset - halfConvexes;
2192                  a = &segment.slice1->verts[k2];
2193                  b = &segment.slice0->verts[k2];
2194                  c = &segment.slice1->verts[k2 + 1];
2195               }
2196               else
2197               {
2198                  k2 = k + leftSideOffset;
2199                  a = &segment.slice0->verts[k2];
2200                  b = &segment.slice0->verts[k2 + 1];
2201                  c = &segment.slice1->verts[k2 + 1];
2202               }
2203            }
2204            // Right Side
2205            else if(j == 2)
2206            {
2207               if(k >= halfConvexes)
2208               {
2209                  k2 = k - halfConvexes;
2210                  a = &segment.slice1->verts[k2];
2211                  b = &segment.slice1->verts[k2 + 1];
2212                  c = &segment.slice0->verts[k2];
2213               }
2214               else
2215               {
2216                  a = &segment.slice0->verts[k];
2217                  b = &segment.slice1->verts[k + 1];
2218                  c = &segment.slice0->verts[k + 1];
2219               }
2220            }
2221            // Front
2222            else if(j == 4)
2223            {
2224               k2 = nextSegOffset + leftSideOffset - 1;
2225
2226               capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
2227               capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
2228               capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
2229
2230               if(capIdx1 >= leftSideOffset)
2231                  capIdx1 = k2 - capIdx1;
2232
2233               if(capIdx2 >= leftSideOffset)
2234                  capIdx2 = k2 - capIdx2;
2235
2236               if(capIdx3 >= leftSideOffset)
2237                  capIdx3 = k2 - capIdx3;
2238
2239               a = &segment.slice0->verts[capIdx1];
2240               b = &segment.slice0->verts[capIdx2];
2241               c = &segment.slice0->verts[capIdx3];
2242            }
2243            // Back
2244            else
2245            {
2246               k2 = nextSegOffset + leftSideOffset - 1;
2247
2248               capIdx1 = mSideProfile.mCap.getTriIdx(k, 0);
2249               capIdx2 = mSideProfile.mCap.getTriIdx(k, 1);
2250               capIdx3 = mSideProfile.mCap.getTriIdx(k, 2);
2251
2252               if(capIdx1 >= leftSideOffset)
2253                  capIdx1 = k2 - capIdx1;
2254
2255               if(capIdx2 >= leftSideOffset)
2256                  capIdx2 = k2 - capIdx2;
2257
2258               if(capIdx3 >= leftSideOffset)
2259                  capIdx3 = k2 - capIdx3;
2260
2261               a = &segment.slice1->verts[capIdx3];
2262               b = &segment.slice1->verts[capIdx2];
2263               c = &segment.slice1->verts[capIdx1];
2264            }
2265
2266            if ( !MathUtils::mLineTriangleCollide( start, end, 
2267                                                   *c, *b, *a,
2268                                                   NULL,
2269                                                   &t ) )
2270               continue;
2271            
2272            if ( t >= 0.0f && t < 1.0f && t < out )
2273            {
2274               out = t;               
2275               norm = PlaneF( *a, *b, *c );
2276            }
2277         }
2278      }
2279
2280      if (out >= 0.0f && out < 1.0f)
2281         break;
2282   }
2283
2284   if (out >= 0.0f && out < 1.0f)
2285   {
2286      info->t = out;
2287      info->normal = norm;
2288      info->point.interpolate(start, end, out);
2289      info->face = -1;
2290      info->object = this;
2291      info->material = this->mMatInst[0];
2292      return true;
2293   }
2294
2295   return false;
2296}
2297
2298bool MeshRoad::collideBox(const Point3F &start, const Point3F &end, RayInfo* info)
2299{   
2300   Con::warnf( "MeshRoad::collideBox() - not yet implemented!" );
2301   return Parent::collideBox( start, end, info );
2302}
2303
2304void MeshRoad::_regenerate()
2305{               
2306   if ( mNodes.size() == 0 )
2307      return;
2308
2309   if ( mSideProfile.mNodes.size() == 2 && mSideProfile.mNodes[1].getPosition().x == 0.0f)
2310      mSideProfile.setProfileDepth(mNodes[0].depth);
2311
2312   const Point3F &nodePt = mNodes.first().point;
2313
2314   MatrixF mat( true );
2315   mat.setPosition( nodePt );
2316   Parent::setTransform( mat );
2317
2318   _generateSlices();
2319
2320   // Make sure we are in the correct bins given our world box.
2321   if( getSceneManager() != NULL )
2322      getSceneManager()->notifyObjectDirty( this );
2323}
2324
2325void MeshRoad::_generateSlices()
2326{      
2327   if ( mNodes.size() < 2 )
2328      return;
2329
2330   // Create the spline, initialized with the MeshRoadNode(s)
2331   U32 nodeCount = mNodes.size();
2332   MeshRoadSplineNode *splineNodes = new MeshRoadSplineNode[nodeCount];
2333
2334   for ( U32 i = 0; i < nodeCount; i++ )
2335   {
2336      MeshRoadSplineNode &splineNode = splineNodes[i];
2337      const MeshRoadNode &node = mNodes[i];
2338
2339      splineNode.x = node.point.x;
2340      splineNode.y = node.point.y;
2341      splineNode.z = node.point.z;
2342      splineNode.width = node.width;
2343      splineNode.depth = node.depth;
2344      splineNode.normal = node.normal;
2345   }
2346
2347   CatmullRom<MeshRoadSplineNode> spline;
2348   spline.initialize( nodeCount, splineNodes );
2349   delete [] splineNodes;
2350
2351   mSlices.clear();
2352      
2353   VectorF lastBreakVector(0,0,0);      
2354   MeshRoadSlice slice;
2355   MeshRoadSplineNode lastBreakNode;
2356   lastBreakNode = spline.evaluate(0.0f);
2357
2358   for ( U32 i = 1; i < mNodes.size(); i++ )
2359   {
2360      F32 t1 = spline.getTime(i);
2361      F32 t0 = spline.getTime(i-1);
2362      
2363      F32 segLength = spline.arcLength( t0, t1 );
2364
2365      U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT );
2366      numSegments = getMax( numSegments, (U32)1 );
2367      F32 tstep = ( t1 - t0 ) / numSegments; 
2368
2369      U32 startIdx = 0;
2370      U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments;
2371
2372      for ( U32 j = startIdx; j < endIdx; j++ )
2373      {
2374         F32 t = t0 + tstep * j;
2375         MeshRoadSplineNode splineNode = spline.evaluate(t);
2376
2377         VectorF toNodeVec = splineNode.getPosition() - lastBreakNode.getPosition();
2378         toNodeVec.normalizeSafe();
2379
2380         if ( lastBreakVector.isZero() )
2381            lastBreakVector = toNodeVec;
2382
2383         F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) );
2384
2385         if ( j == startIdx || 
2386            ( j == endIdx - 1 && i == mNodes.size() - 1 ) ||
2387              angle > mBreakAngle )
2388         {
2389            // Push back a spline node
2390            slice.p1.set( splineNode.x, splineNode.y, splineNode.z );            
2391            slice.width = splineNode.width;
2392            slice.depth = splineNode.depth;
2393            slice.normal = splineNode.normal;   
2394            slice.normal.normalize();
2395            slice.parentNodeIdx = i-1;
2396            slice.t = t;
2397            mSlices.push_back( slice );         
2398
2399            lastBreakVector = splineNode.getPosition() - lastBreakNode.getPosition();
2400            lastBreakVector.normalizeSafe();
2401
2402            lastBreakNode = splineNode;
2403         }          
2404      }
2405   }
2406   
2407   MatrixF mat(true);
2408   Box3F box;
2409
2410   U32 lastProfileNode = mSideProfile.mNodes.size() - 1;
2411   F32 depth = mSideProfile.mNodes[lastProfileNode].getPosition().y;
2412   F32 bttmOffset = mSideProfile.mNodes[lastProfileNode].getPosition().x;
2413
2414   for ( U32 i = 0; i < mSlices.size(); i++ )
2415   {
2416      // Calculate uvec, fvec, and rvec for all slices
2417      calcSliceTransform( i, mat );
2418      MeshRoadSlice *slicePtr = &mSlices[i];
2419      mat.getColumn( 0, &slicePtr->rvec );
2420      mat.getColumn( 1, &slicePtr->fvec );
2421      mat.getColumn( 2, &slicePtr->uvec );
2422
2423      // Calculate p0/p2/pb0/pb2 for all slices
2424      slicePtr->p0 = slicePtr->p1 - slicePtr->rvec * slicePtr->width * 0.5f;
2425      slicePtr->p2 = slicePtr->p1 + slicePtr->rvec * slicePtr->width * 0.5f;
2426      slicePtr->pb0 = slicePtr->p0 + slicePtr->uvec * depth - slicePtr->rvec * bttmOffset;
2427      slicePtr->pb2 = slicePtr->p2 + slicePtr->uvec * depth + slicePtr->rvec * bttmOffset;
2428
2429     // Generate or extend the object/world bounds
2430      if ( i == 0 )
2431      {
2432         box.minExtents = slicePtr->p0;
2433         box.maxExtents = slicePtr->p2;
2434         box.extend(slicePtr->pb0 );
2435         box.extend(slicePtr->pb2 );
2436      }
2437      else
2438      {
2439         box.extend(slicePtr->p0 );
2440         box.extend(slicePtr->p2 );
2441         box.extend(slicePtr->pb0 );
2442         box.extend(slicePtr->pb2 );
2443      }
2444
2445      // Right side
2446      Point3F pos;
2447      VectorF norm;
2448
2449      MatrixF profileMat1(true);
2450      profileMat1.setRow(0, slicePtr->rvec);
2451      profileMat1.setRow(1, slicePtr->uvec);
2452      profileMat1.setRow(2, -slicePtr->fvec);
2453
2454      // Left side
2455      MatrixF profileMat2(true);
2456      profileMat2.setRow(0, -slicePtr->rvec);
2457      profileMat2.setRow(1, slicePtr->uvec);
2458      profileMat2.setRow(2, slicePtr->fvec);
2459
2460      for(U32 i = 0; i < 2; i++)
2461      {
2462         if(i)
2463            mSideProfile.setTransform(profileMat2, slicePtr->p0);
2464         else
2465            mSideProfile.setTransform(profileMat1, slicePtr->p2);
2466
2467         // Retain original per-node depth functionality
2468         if(mSideProfile.mNodes.size() == 2 && mSideProfile.mNodes[1].getPosition().y == -mSlices[0].depth)
2469         {
2470            mSideProfile.getNodeWorldPos(0, pos);
2471            slicePtr->verts.push_back(pos);
2472            box.extend( pos );
2473
2474            pos.z -= slicePtr->depth;
2475            slicePtr->verts.push_back(pos);
2476            box.extend( pos );
2477
2478            if(i)
2479               slicePtr->pb0 = pos;
2480            else
2481               slicePtr->pb2 = pos;
2482
2483            mSideProfile.getNormToSlice(0, norm);
2484            slicePtr->norms.push_back(norm);
2485
2486            mSideProfile.getNormToSlice(1, norm);
2487            slicePtr->norms.push_back(norm);
2488         }
2489         // New profile functionality
2490         else
2491         {
2492            for(U32 j = 0; j < mSideProfile.mNodes.size(); j++)
2493            {
2494               mSideProfile.getNodeWorldPos(j, pos);
2495               slicePtr->verts.push_back(pos);
2496               box.extend( pos );
2497            }
2498
2499            for(U32 j = 0; j < mSideProfile.mNodeNormals.size(); j++)
2500            {
2501               mSideProfile.getNormToSlice(j, norm);
2502               slicePtr->norms.push_back(norm);
2503            }
2504         }
2505      }
2506   } 
2507
2508   mWorldBox = box;
2509   resetObjectBox();
2510
2511   _generateSegments();   
2512}
2513
2514void MeshRoad::_generateSegments()
2515{
2516   SAFE_DELETE( mPhysicsRep );
2517
2518   mSegments.clear();
2519
2520   for ( U32 i = 0; i < mSlices.size() - 1; i++ )
2521   {
2522      MeshRoadSegment seg( &mSlices[i], &mSlices[i+1], getWorldTransform() );
2523
2524      mSegments.push_back( seg );
2525   }
2526
2527   //mSideProfile.generateEndCap(mSlices[0].width);
2528
2529   if ( isClientObject() )
2530      _generateVerts();
2531
2532   if ( PHYSICSMGR )
2533   {
2534      ConcretePolyList polylist;
2535      if ( buildPolyList( PLC_Collision, &polylist, getWorldBox(), getWorldSphere() ) )
2536      {
2537         polylist.triangulate();
2538
2539         PhysicsCollision *colShape = PHYSICSMGR->createCollision();
2540         colShape->addTriangleMesh( polylist.mVertexList.address(),
2541            polylist.mVertexList.size(),
2542            polylist.mIndexList.address(),
2543            polylist.mIndexList.size() / 3,
2544            MatrixF::Identity );
2545
2546         PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
2547         mPhysicsRep = PHYSICSMGR->createBody();
2548         mPhysicsRep->init( colShape, 0, 0, this, world );
2549      }
2550   }
2551}
2552
2553void MeshRoad::_generateVerts()
2554{           
2555   const U32 widthDivisions = getMax( 0, mWidthSubdivisions );
2556   const F32 divisionStep = 1.0f / (F32)( widthDivisions + 1 );
2557   const U32 sliceCount = mSlices.size();
2558   const U32 segmentCount = mSegments.size();
2559
2560   U32 numProfSide, numProfTop, numProfBottom;
2561
2562   numProfSide = numProfTop = numProfBottom = 0;
2563
2564   // Find how many profile segments are set to side, top, and bottom materials
2565   for ( U32 i = 0; i < mSideProfile.mSegMtrls.size(); i++)
2566   {
2567      switch(mSideProfile.mSegMtrls[i])
2568      {
2569      case Side:     numProfSide++;    break;
2570      case Top:      numProfTop++;     break;
2571      case Bottom:   numProfBottom++;  break;
2572      }
2573   }
2574
2575   F32 profLen = mSideProfile.getProfileLen();
2576
2577   mVertCount[Top] = ( 2 + widthDivisions ) * sliceCount;
2578   mVertCount[Top] += sliceCount * numProfTop * 4;
2579   mTriangleCount[Top] = segmentCount * 2 * ( widthDivisions + 1 );
2580   mTriangleCount[Top] += segmentCount * numProfTop * 4;
2581
2582   mVertCount[Bottom] = sliceCount * 2;
2583   mVertCount[Bottom] += sliceCount * numProfBottom * 4;
2584   mTriangleCount[Bottom] = segmentCount * 2;
2585   mTriangleCount[Bottom] += segmentCount * numProfBottom * 4;
2586
2587   mVertCount[Side] = sliceCount * numProfSide * 4;               // side verts
2588   mVertCount[Side] += mSideProfile.mNodes.size() * 4;            // end cap verts
2589   mTriangleCount[Side] = segmentCount * numProfSide * 4;         // side tris
2590   mTriangleCount[Side] += mSideProfile.mCap.getNumTris() * 2;    // end cap tris
2591   
2592   // Calculate TexCoords for Slices
2593
2594   F32 texCoordV = 0.0f;
2595   mSlices[0].texCoordV = 0.0f;
2596
2597   for ( U32 i = 1; i < sliceCount; i++ )
2598   {
2599      MeshRoadSlice &slice = mSlices[i]; 
2600      MeshRoadSlice &prevSlice = mSlices[i-1];
2601             
2602      // Increment the textCoordV for the next slice.
2603      F32 len = ( slice.p1 - prevSlice.p1 ).len();
2604      texCoordV += len / mTextureLength;         
2605
2606      slice.texCoordV = texCoordV;
2607   }
2608
2609   // Make Vertex Buffers
2610   GFXVertexPNTT *pVert = NULL;
2611   U32 vertCounter = 0;
2612
2613   // Top Buffers...
2614
2615   mVB[Top].set( GFX, mVertCount[Top], GFXBufferTypeStatic );   
2616   pVert = mVB[Top].lock(); 
2617   vertCounter = 0;
2618   
2619   for ( U32 i = 0; i < sliceCount; i++ )
2620   {
2621      MeshRoadSlice &slice = mSlices[i];      
2622      
2623      pVert->point = slice.p0;    
2624      pVert->normal = slice.uvec;
2625      pVert->tangent = slice.fvec;
2626      pVert->texCoord.set(1,slice.texCoordV);      
2627      pVert++;
2628      vertCounter++;
2629
2630      for ( U32 j = 0; j < widthDivisions; j++ )
2631      {
2632         const F32 t = divisionStep * (F32)( j + 1 );
2633
2634         pVert->point.interpolate( slice.p0, slice.p2, t );    
2635         pVert->normal = slice.uvec;
2636         pVert->tangent = slice.fvec;
2637         pVert->texCoord.set( 1.0f - t, slice.texCoordV );      
2638         pVert++;
2639         vertCounter++;
2640      }
2641
2642      pVert->point = slice.p2;    
2643      pVert->normal = slice.uvec;
2644      pVert->tangent = slice.fvec;
2645      pVert->texCoord.set( 0, slice.texCoordV );      
2646      pVert++;
2647      vertCounter++;
2648   }
2649
2650   if(numProfTop)
2651   {
2652      for ( U32 i = 0; i < sliceCount; i++ )
2653      {
2654         MeshRoadSlice &slice = mSlices[i];
2655
2656         // Right Side
2657         for ( U32 j = 0; j < mSideProfile.mNodes.size()-1; j++)
2658         {
2659            if(mSideProfile.mSegMtrls[j] == Top)
2660            {
2661               // Vertex 1
2662               pVert->point = slice.verts[j];
2663               pVert->normal = slice.norms[2*j];
2664               pVert->tangent = slice.fvec;
2665               pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2666               pVert++;
2667               vertCounter++;
2668
2669               // Vertex 2
2670               pVert->point = slice.verts[j+1];
2671               pVert->normal = slice.norms[2*j+1];
2672               pVert->tangent = slice.fvec;
2673               pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2674               pVert++;
2675               vertCounter++;
2676            }
2677         }
2678
2679         // Left Side
2680         for( U32 j = mSideProfile.mNodes.size(); j < 2*mSideProfile.mNodes.size()-1; j++)
2681         {
2682            if(mSideProfile.mSegMtrls[j-mSideProfile.mNodes.size()] == Top)
2683            {
2684               // Vertex 1
2685               pVert->point = slice.verts[j];
2686               pVert->normal = slice.norms[2*j-2];
2687               pVert->tangent = slice.fvec;
2688               pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2689               pVert++;
2690               vertCounter++;
2691
2692               // Vertex 2
2693               pVert->point = slice.verts[j+1];
2694               pVert->normal = slice.norms[2*j-1];
2695               pVert->tangent = slice.fvec;
2696               pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2697               pVert++;
2698               vertCounter++;
2699            }
2700         }
2701      }
2702   }
2703
2704   AssertFatal( vertCounter == mVertCount[Top], "MeshRoad, wrote incorrect number of verts in mVB[Top]!" );
2705
2706   mVB[Top].unlock();
2707
2708   // Bottom Buffer...
2709
2710   mVB[Bottom].set( GFX, mVertCount[Bottom], GFXBufferTypeStatic );   
2711   pVert = mVB[Bottom].lock(); 
2712   vertCounter = 0;
2713
2714   for ( U32 i = 0; i < sliceCount; i++ )
2715   {
2716      MeshRoadSlice &slice = mSlices[i];      
2717
2718      pVert->point = slice.pb2;    
2719      pVert->normal = -slice.uvec;
2720      pVert->tangent = slice.fvec;
2721      pVert->texCoord.set(0,slice.texCoordV);      
2722      pVert++;
2723      vertCounter++;
2724
2725      pVert->point = slice.pb0;    
2726      pVert->normal = -slice.uvec;
2727      pVert->tangent = slice.fvec;
2728      pVert->texCoord.set(1,slice.texCoordV);      
2729      pVert++;
2730      vertCounter++;
2731   }
2732
2733   if(numProfBottom)
2734   {
2735      for ( U32 i = 0; i < sliceCount; i++ )
2736      {
2737         MeshRoadSlice &slice = mSlices[i];
2738
2739         // Right Side
2740         for ( U32 j = 0; j < mSideProfile.mNodes.size()-1; j++)
2741         {
2742            if(mSideProfile.mSegMtrls[j] == Bottom)
2743            {
2744               // Vertex 1
2745               pVert->point = slice.verts[j];
2746               pVert->normal = slice.norms[2*j];
2747               pVert->tangent = slice.fvec;
2748               pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2749               pVert++;
2750               vertCounter++;
2751
2752               // Vertex 2
2753               pVert->point = slice.verts[j+1];
2754               pVert->normal = slice.norms[2*j+1];
2755               pVert->tangent = slice.fvec;
2756               pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2757               pVert++;
2758               vertCounter++;
2759            }
2760         }
2761
2762         // Left Side
2763         for( U32 j = mSideProfile.mNodes.size(); j < 2*mSideProfile.mNodes.size()-1; j++)
2764         {
2765            if(mSideProfile.mSegMtrls[j-mSideProfile.mNodes.size()] == Bottom)
2766            {
2767               // Vertex 1
2768               pVert->point = slice.verts[j];
2769               pVert->normal = slice.norms[2*j-2];
2770               pVert->tangent = slice.fvec;
2771               pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2772               pVert++;
2773               vertCounter++;
2774
2775               // Vertex 2
2776               pVert->point = slice.verts[j+1];
2777               pVert->normal = slice.norms[2*j-1];
2778               pVert->tangent = slice.fvec;
2779               pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2780               pVert++;
2781               vertCounter++;
2782            }
2783         }
2784      }
2785   }
2786
2787   AssertFatal( vertCounter == mVertCount[Bottom], "MeshRoad, wrote incorrect number of verts in mVB[Bottom]!" );
2788
2789   mVB[Bottom].unlock();
2790
2791   // Side Buffers...
2792
2793   mVB[Side].set( GFX, mVertCount[Side], GFXBufferTypeStatic );   
2794   pVert = mVB[Side].lock(); 
2795   vertCounter = 0;
2796
2797   if(numProfSide)
2798   {
2799      for ( U32 i = 0; i < sliceCount; i++ )
2800      {
2801         MeshRoadSlice &slice = mSlices[i];
2802
2803         // Right Side
2804         for( U32 j = 0; j < mSideProfile.mNodes.size()-1; j++)
2805         {
2806            if(mSideProfile.mSegMtrls[j] == Side)
2807            {
2808               // Segment Vertex 1
2809               pVert->point = slice.verts[j];
2810               pVert->normal = slice.norms[2*j];
2811               pVert->tangent = slice.fvec;
2812               pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2813               pVert++;
2814               vertCounter++;
2815
2816               // Segment Vertex 2
2817               pVert->point = slice.verts[j+1];
2818               pVert->normal = slice.norms[2*j+1];
2819               pVert->tangent = slice.fvec;
2820               pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2821               pVert++;
2822               vertCounter++;
2823            }
2824         }
2825
2826         // Left Side
2827         for( U32 j = mSideProfile.mNodes.size(); j < 2*mSideProfile.mNodes.size()-1; j++)
2828         {
2829            if(mSideProfile.mSegMtrls[j-mSideProfile.mNodes.size()] == Side)
2830            {
2831               // Segment Vertex 1
2832               pVert->point = slice.verts[j];
2833               pVert->normal = slice.norms[2*j-2];
2834               pVert->tangent = slice.fvec;
2835               pVert->texCoord.set(mSideProfile.getNodePosPercent(j)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2836               pVert++;
2837               vertCounter++;
2838
2839               // Segment Vertex 2
2840               pVert->point = slice.verts[j+1];
2841               pVert->normal = slice.norms[2*j-1];
2842               pVert->tangent = slice.fvec;
2843               pVert->texCoord.set(mSideProfile.getNodePosPercent(j+1)*profLen/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>,slice.texCoordV);
2844               pVert++;
2845               vertCounter++;
2846            }
2847         }
2848      }
2849   }
2850
2851   // Cap verts
2852   Point3F pos;
2853   VectorF norm;
2854   VectorF tang;
2855
2856   for( U32 i = 0; i < mSlices.size(); i += mSlices.size()-1)
2857   {
2858      MeshRoadSlice &slice = mSlices[i];
2859
2860      // Back cap
2861      if(i)
2862      {
2863         norm = slice.fvec;
2864         tang = -slice.rvec;
2865      }
2866      // Front cap
2867      else
2868      {
2869         norm = -slice.fvec;
2870         tang = slice.rvec;
2871      }
2872
2873      // Right side
2874      for( U32 j = 0; j < mSideProfile.mNodes.size(); j++)
2875      {
2876         pVert->point = slice.verts[j];
2877         pVert->normal = norm;
2878         pVert->tangent = tang;
2879         pos = mSideProfile.mNodes[j].getPosition();
2880         pVert->texCoord.set(pos.x/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>, pos.y/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>);
2881         pVert++;
2882         vertCounter++;
2883      }
2884
2885      // Left side
2886      for( U32 j = 2*mSideProfile.mNodes.size()-1; j >= mSideProfile.mNodes.size(); j--)
2887      {
2888         pVert->point = slice.verts[j];
2889         pVert->normal = norm;
2890         pVert->tangent = tang;
2891         pos = mSideProfile.mNodes[j-mSideProfile.mNodes.size()].getPosition();
2892         pos.x = -pos.x - slice.width;
2893         pVert->texCoord.set(pos.x/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>, pos.y/<a href="/coding/class/classmeshroad/#classmeshroad_1a3a153061286cd8a7fd6cb8628b4cebe3">mTextureLength</a>);
2894         pVert++;
2895         vertCounter++;
2896      }
2897   }
2898
2899   AssertFatal( vertCounter == mVertCount[Side], "MeshRoad, wrote incorrect number of verts in mVB[Side]!" );
2900
2901   mVB[Side].unlock();
2902
2903   // Make Primitive Buffers   
2904   U32 p00, p01, p11, p10;
2905   U32 pb00, pb01, pb11, pb10;
2906   U32 offset = 0;
2907   U16 *pIdx = NULL;   
2908   U32 curIdx = 0; 
2909
2910   // Top Primitive Buffer
2911
2912   mPB[Top].set( GFX, mTriangleCount[Top] * 3, mTriangleCount[Top], GFXBufferTypeStatic );
2913
2914   mPB[Top].lock(&pIdx);     
2915   curIdx = 0; 
2916   offset = 0;
2917
2918   const U32 rowStride = 2 + widthDivisions;
2919 
2920   for ( U32 i = 0; i < mSegments.size(); i++ )
2921   {     
2922      for ( U32 j = 0; j < widthDivisions + 1; j++ )
2923      {
2924         p00 = offset;
2925         p10 = offset + 1;
2926         p01 = offset + rowStride;
2927         p11 = offset + rowStride + 1;
2928
2929         pIdx[curIdx] = p00;
2930         curIdx++;
2931         pIdx[curIdx] = p01;
2932         curIdx++;
2933         pIdx[curIdx] = p11;
2934         curIdx++;
2935
2936         pIdx[curIdx] = p00;
2937         curIdx++;
2938         pIdx[curIdx] = p11;
2939         curIdx++;
2940         pIdx[curIdx] = p10;
2941         curIdx++;  
2942
2943         offset += 1;
2944      }
2945
2946      offset += 1;
2947   }
2948
2949   offset += 2;
2950
2951   if(numProfTop)
2952   {
2953      U32 nextSegOffset = 4 * numProfTop;
2954
2955      for ( U32 i = 0; i < segmentCount; i++ )
2956      {
2957         // Loop through profile segments on right side
2958         for( U32 j = 0; j < numProfTop; j++)
2959         {
2960            // Profile Segment Face 1
2961            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
2962            curIdx++;
2963            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
2964            curIdx++;
2965            pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
2966            curIdx++;
2967
2968            // Profile Segment Face 2
2969            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
2970            curIdx++;
2971            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
2972            curIdx++;
2973            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
2974            curIdx++;
2975         }
2976
2977         // Loop through profile segments on left side
2978         for( U32 j = numProfTop; j < 2*numProfTop; j++)
2979         {
2980            // Profile Segment Face 1
2981            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
2982            curIdx++;
2983            pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
2984            curIdx++;
2985            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
2986            curIdx++;
2987
2988            // Profile Segment Face 2
2989            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
2990            curIdx++;
2991            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
2992            curIdx++;
2993            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
2994            curIdx++;
2995         }
2996      }
2997   }
2998
2999   AssertFatal( curIdx == mTriangleCount[Top] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Top]!" );
3000
3001   mPB[Top].unlock();
3002
3003   // Bottom Primitive Buffer
3004
3005   mPB[Bottom].set( GFX, mTriangleCount[Bottom] * 3, mTriangleCount[Bottom], GFXBufferTypeStatic );
3006
3007   mPB[Bottom].lock(&pIdx);     
3008   curIdx = 0; 
3009   offset = 0;
3010
3011   for ( U32 i = 0; i < mSegments.size(); i++ )
3012   {     
3013      p00 = offset;
3014      p10 = offset + 1;
3015      p01 = offset + 2;
3016      p11 = offset + 3;
3017
3018      pIdx[curIdx] = p00;
3019      curIdx++;
3020      pIdx[curIdx] = p01;
3021      curIdx++;
3022      pIdx[curIdx] = p11;
3023      curIdx++;
3024
3025      pIdx[curIdx] = p00;
3026      curIdx++;
3027      pIdx[curIdx] = p11;
3028      curIdx++;
3029      pIdx[curIdx] = p10;
3030      curIdx++;      
3031
3032      offset += 2;
3033   }
3034
3035   offset += 2;
3036
3037   if(numProfBottom)
3038   {
3039      U32 nextSegOffset = 4 * numProfBottom;
3040
3041      for ( U32 i = 0; i < segmentCount; i++ )
3042      {
3043         // Loop through profile segments on right side
3044         for( U32 j = 0; j < numProfBottom; j++)
3045         {
3046            // Profile Segment Face 1
3047            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
3048            curIdx++;
3049            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
3050            curIdx++;
3051            pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
3052            curIdx++;
3053
3054            // Profile Segment Face 2
3055            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
3056            curIdx++;
3057            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
3058            curIdx++;
3059            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
3060            curIdx++;
3061         }
3062
3063         // Loop through profile segments on left side
3064         for( U32 j = numProfBottom; j < 2*numProfBottom; j++)
3065         {
3066            // Profile Segment Face 1
3067            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
3068            curIdx++;
3069            pIdx[curIdx] = nextSegOffset*i + 2*j + 1 + offset;
3070            curIdx++;
3071            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
3072            curIdx++;
3073
3074            // Profile Segment Face 2
3075            pIdx[curIdx] = nextSegOffset*i + 2*j + offset;
3076            curIdx++;
3077            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + 1 + offset;
3078            curIdx++;
3079            pIdx[curIdx] = nextSegOffset*i + 2*j + nextSegOffset + offset;
3080            curIdx++;
3081         }
3082      }
3083   }
3084
3085   AssertFatal( curIdx == mTriangleCount[Bottom] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Bottom]!" );
3086
3087   mPB[Bottom].unlock();
3088
3089   // Side Primitive Buffer
3090
3091   mPB[Side].set( GFX, mTriangleCount[Side] * 3, mTriangleCount[Side], GFXBufferTypeStatic );
3092
3093   mPB[Side].lock(&pIdx);     
3094   curIdx = 0; 
3095   offset = 4 * numProfSide;
3096
3097   if(numProfSide)
3098   {
3099      for ( U32 i = 0; i < mSegments.size(); i++ )
3100      {
3101         // Loop through profile segments on right side
3102         for( U32 j = 0; j < numProfSide; j++)
3103         {
3104            // Profile Segment Face 1
3105            pIdx[curIdx] = offset*i + 2*j;
3106            curIdx++;
3107            pIdx[curIdx] = offset*i + 2*j + offset + 1;
3108            curIdx++;
3109            pIdx[curIdx] = offset*i + 2*j + 1;
3110            curIdx++;
3111
3112            // Profile Segment Face 2
3113            pIdx[curIdx] = offset*i + 2*j;
3114            curIdx++;
3115            pIdx[curIdx] = offset*i + 2*j + offset;
3116            curIdx++;
3117            pIdx[curIdx] = offset*i + 2*j + offset + 1;
3118            curIdx++;
3119         }
3120
3121         // Loop through profile segments on left side
3122         for( U32 j = numProfSide; j < 2*numProfSide; j++)
3123         {
3124            // Profile Segment Face 1
3125            pIdx[curIdx] = offset*i + 2*j;
3126            curIdx++;
3127            pIdx[curIdx] = offset*i + 2*j + 1;
3128            curIdx++;
3129            pIdx[curIdx] = offset*i + 2*j + offset + 1;
3130            curIdx++;
3131
3132            // Profile Segment Face 2
3133            pIdx[curIdx] = offset*i + 2*j;
3134            curIdx++;
3135            pIdx[curIdx] = offset*i + 2*j + offset + 1;
3136            curIdx++;
3137            pIdx[curIdx] = offset*i + 2*j + offset;
3138            curIdx++;
3139         }
3140      }
3141   }
3142
3143   // Cap the front
3144   offset = sliceCount * numProfSide * 4;
3145
3146   for ( U32 i = 0; i < mSideProfile.mCap.getNumTris(); i++ )
3147   {
3148      pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 0) + offset;
3149      curIdx++;
3150      pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 1) + offset;
3151      curIdx++;
3152      pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 2) + offset;
3153      curIdx++;
3154   }
3155
3156   // Cap the back
3157   offset += mSideProfile.mNodes.size() * 2;
3158
3159   for ( U32 i = 0; i < mSideProfile.mCap.getNumTris(); i++ )
3160   {
3161      pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 2) + offset;
3162      curIdx++;
3163      pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 1) + offset;
3164      curIdx++;
3165      pIdx[curIdx] = mSideProfile.mCap.getTriIdx(i, 0) + offset;
3166      curIdx++;
3167   }
3168
3169   AssertFatal( curIdx == mTriangleCount[Side] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Side]!" );
3170
3171   mPB[Side].unlock();
3172}
3173
3174const MeshRoadNode& MeshRoad::getNode( U32 idx )
3175{
3176   return mNodes[idx];
3177}
3178
3179VectorF MeshRoad::getNodeNormal( U32 idx )
3180{
3181   if ( mNodes.size() - 1 < idx )
3182      return VectorF::Zero;
3183
3184   return mNodes[idx].normal;
3185}
3186
3187void MeshRoad::setNodeNormal( U32 idx, const VectorF &normal )
3188{
3189   if ( mNodes.size() - 1 < idx )
3190      return;
3191
3192   mNodes[idx].normal = normal;
3193
3194   regenerate();
3195
3196   setMaskBits( NodeMask | RegenMask );
3197}
3198
3199Point3F MeshRoad::getNodePosition( U32 idx )
3200{
3201   if ( mNodes.size() - 1 < idx )
3202      return Point3F::Zero;
3203
3204   return mNodes[idx].point;
3205}
3206
3207void MeshRoad::setNodePosition( U32 idx, const Point3F &pos )
3208{
3209   if ( mNodes.size() - 1 < idx )
3210      return;
3211
3212   mNodes[idx].point = pos;
3213
3214   regenerate();
3215
3216   setMaskBits( NodeMask | RegenMask );
3217}
3218
3219U32 MeshRoad::addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal )
3220{
3221   U32 idx = _addNode( pos, width, depth, normal );   
3222
3223   regenerate();
3224
3225   setMaskBits( NodeMask | RegenMask );
3226
3227   return idx;
3228}
3229
3230void MeshRoad::buildNodesFromList( MeshRoadNodeList* list )
3231{
3232   mNodes.clear();
3233
3234   for (U32 i=0; i<list->mPositions.size(); ++i)
3235   {
3236      _addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->mNormals[i] );
3237   }
3238
3239   _regenerate();
3240}
3241
3242U32 MeshRoad::insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
3243{
3244   U32 ret = _insertNode( pos, width, depth, normal, idx );
3245
3246   regenerate();
3247
3248   setMaskBits( NodeMask | RegenMask );
3249
3250   return ret;
3251}
3252
3253void MeshRoad::setNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
3254{
3255   if ( mNodes.size() - 1 < idx )
3256      return;
3257
3258   MeshRoadNode &node = mNodes[idx];
3259
3260   node.point = pos;
3261   node.width = width;
3262   node.depth = depth;
3263   node.normal = normal;
3264
3265   regenerate();
3266
3267   setMaskBits( NodeMask | RegenMask );
3268}
3269
3270void MeshRoad::setNodeWidth( U32 idx, F32 meters )
3271{
3272   meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH );
3273
3274   if ( mNodes.size() - 1 < idx )
3275      return;
3276
3277   mNodes[idx].width = meters;
3278   _regenerate();
3279
3280   setMaskBits( RegenMask | NodeMask );
3281}
3282
3283F32 MeshRoad::getNodeWidth( U32 idx )
3284{
3285   if ( mNodes.size() - 1 < idx )
3286      return -1.0f;
3287
3288   return mNodes[idx].width;
3289}
3290
3291void MeshRoad::setNodeDepth( U32 idx, F32 meters )
3292{
3293   meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH );
3294
3295   if ( mNodes.size() - 1 < idx )
3296      return;
3297
3298   mNodes[idx].depth = meters;
3299
3300   _regenerate();
3301
3302   setMaskBits( MeshRoadMask | RegenMask | NodeMask );
3303}
3304
3305F32 MeshRoad::getNodeDepth( U32 idx )
3306{
3307   if ( mNodes.size() - 1 < idx )
3308      return -1.0f;
3309
3310   return mNodes[idx].depth;
3311}
3312
3313MatrixF MeshRoad::getNodeTransform( U32 idx )
3314{   
3315   MatrixF mat(true);   
3316
3317   if ( mNodes.size() - 1 < idx )
3318      return mat;
3319
3320   bool hasNext = idx + 1 < mNodes.size();
3321   bool hasPrev = (S32)idx - 1 > 0;
3322
3323   const MeshRoadNode &node = mNodes[idx];   
3324
3325   VectorF fvec( 0, 1, 0 );
3326
3327   if ( hasNext )
3328   {
3329      fvec = mNodes[idx+1].point - node.point;      
3330      fvec.normalizeSafe();
3331   }
3332   else if ( hasPrev )
3333   {
3334      fvec = node.point - mNodes[idx-1].point;
3335      fvec.normalizeSafe();
3336   }
3337   else
3338      fvec = mPerp( node.normal );
3339
3340   if ( fvec.isZero() )
3341      fvec = mPerp( node.normal );
3342
3343   F32 dot = mDot( fvec, node.normal );
3344   if ( dot < -0.9f || dot > 0.9f )
3345      fvec = mPerp( node.normal );
3346
3347   VectorF rvec = mCross( fvec, node.normal );
3348   if ( rvec.isZero() )
3349      rvec = mPerp( fvec );
3350   rvec.normalize();
3351
3352   fvec = mCross( node.normal, rvec );
3353   fvec.normalize();
3354
3355   mat.setColumn( 0, rvec );
3356   mat.setColumn( 1, fvec );
3357   mat.setColumn( 2, node.normal );
3358   mat.setColumn( 3, node.point );
3359
3360   AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
3361
3362   return mat; 
3363}
3364
3365void MeshRoad::calcSliceTransform( U32 idx, MatrixF &mat )
3366{   
3367   if ( mSlices.size() - 1 < idx )
3368      return;
3369
3370   bool hasNext = idx + 1 < mSlices.size();
3371   bool hasPrev = (S32)idx - 1 >= 0;
3372
3373   const MeshRoadSlice &slice = mSlices[idx];   
3374
3375   VectorF fvec( 0, 1, 0 );
3376
3377   if ( hasNext )
3378   {
3379      fvec = mSlices[idx+1].p1 - slice.p1;      
3380      fvec.normalizeSafe();
3381   }
3382   else if ( hasPrev )
3383   {
3384      fvec = slice.p1 - mSlices[idx-1].p1;
3385      fvec.normalizeSafe();
3386   }
3387   else
3388      fvec = mPerp( slice.normal );
3389
3390   if ( fvec.isZero() )
3391      fvec = mPerp( slice.normal );
3392
3393   F32 dot = mDot( fvec, slice.normal );
3394   if ( dot < -0.9f || dot > 0.9f )
3395      fvec = mPerp( slice.normal );
3396
3397   VectorF rvec = mCross( fvec, slice.normal );
3398   if ( rvec.isZero() )
3399      rvec = mPerp( fvec );
3400   rvec.normalize();
3401
3402   fvec = mCross( slice.normal, rvec );
3403   fvec.normalize();
3404
3405   mat.setColumn( 0, rvec );
3406   mat.setColumn( 1, fvec );
3407   mat.setColumn( 2, slice.normal );
3408   mat.setColumn( 3, slice.p1 );
3409
3410   AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!");
3411}
3412
3413F32 MeshRoad::getRoadLength() const
3414{
3415   F32 length = 0.0f;
3416
3417   for ( U32 i = 0; i < mSegments.size(); i++ )
3418   {
3419      length += mSegments[i].length();
3420   }
3421
3422   return length;
3423}
3424
3425void MeshRoad::deleteNode( U32 idx )
3426{
3427   if ( mNodes.size() - 1 < idx )
3428      return;
3429
3430   mNodes.erase(idx);   
3431   _regenerate();
3432
3433   setMaskBits( RegenMask | NodeMask );
3434}
3435
3436U32 MeshRoad::_addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal )
3437{
3438   mNodes.increment();
3439   MeshRoadNode &node = mNodes.last();
3440
3441   node.point = pos;   
3442   node.width = width;
3443   node.depth = depth;
3444   node.normal = normal;
3445
3446   setMaskBits( NodeMask | RegenMask );
3447
3448   return mNodes.size() - 1;
3449}
3450
3451U32 MeshRoad::_insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx )
3452{
3453   U32 ret;
3454   MeshRoadNode *node;
3455
3456   if ( idx == U32_MAX )
3457   {
3458      mNodes.increment();
3459      node = &mNodes.last();
3460      ret = mNodes.size() - 1;
3461   }
3462   else
3463   {
3464      mNodes.insert( idx );
3465      node = &mNodes[idx];
3466      ret = idx;
3467   }
3468
3469   node->point = pos;
3470   node->depth = depth;
3471   node->width = width;     
3472   node->normal = normal;
3473
3474   return ret;
3475}
3476
3477bool MeshRoad::collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx, Point3F *collisionPnt )
3478{
3479   Point3F p0 = origin;
3480   Point3F p1 = origin + direction * 2000.0f;
3481
3482   // If the line segment does not collide with the MeshRoad's world box, 
3483   // it definitely does not collide with any part of the river.
3484   if ( !getWorldBox().collideLine( p0, p1 ) )
3485      return false;
3486
3487   if ( mSlices.size() < 2 )
3488      return false;
3489
3490   MathUtils::Quad quad;
3491   MathUtils::Ray ray;
3492   F32 t;
3493
3494   // Check each road segment (formed by a pair of slices) for collision
3495   // with the line segment.
3496   for ( U32 i = 0; i < mSlices.size() - 1; i++ )
3497   {
3498      const MeshRoadSlice &slice0 = mSlices[i];
3499      const MeshRoadSlice &slice1 = mSlices[i+1];
3500
3501      // For simplicities sake we will only test for collision between the
3502      // line segment and the Top face of the river segment.
3503
3504      // Clockwise starting with the leftmost/closest point.
3505      quad.p00 = slice0.p0;
3506      quad.p01 = slice1.p0;
3507      quad.p11 = slice1.p2;
3508      quad.p10 = slice0.p2;
3509
3510      ray.origin = origin;
3511      ray.direction = direction;
3512
3513      if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) )
3514      {
3515         if ( nodeIdx )
3516            *nodeIdx = slice0.parentNodeIdx;         
3517         if ( collisionPnt )
3518            *collisionPnt = ray.origin + ray.direction * t;
3519         return true;
3520      }
3521   }
3522
3523   return false;
3524}
3525
3526void MeshRoad::regenerate()
3527{
3528   _regenerate();
3529   setMaskBits( RegenMask );
3530}
3531
3532//-------------------------------------------------------------------------
3533// Console Methods
3534//-------------------------------------------------------------------------
3535
3536DefineEngineMethod( MeshRoad, setNodeDepth, void, ( S32 idx, F32 meters ),,
3537                   "Intended as a helper to developers and editor scripts.\n"
3538                   "Sets the depth in meters of a particular node."
3539                   )
3540{
3541   object->setNodeDepth( idx, meters );
3542}
3543
3544DefineEngineMethod( MeshRoad, regenerate, void, (),,
3545                   "Intended as a helper to developers and editor scripts.\n"
3546                   "Force MeshRoad to recreate its geometry."
3547                   )
3548{
3549   object->regenerate();
3550}
3551
3552DefineEngineMethod( MeshRoad, postApply, void, (),,
3553                   "Intended as a helper to developers and editor scripts.\n"
3554                   "Force trigger an inspectPostApply. This will transmit "
3555                   "material and other fields ( not including nodes ) to client objects."
3556                   )
3557{
3558   object->inspectPostApply();
3559}
3560bool MeshRoad::buildPolyList_TopSurfaceOnly = false;
3561
3562bool MeshRoad::buildTopPolyList(PolyListContext plc, AbstractPolyList* polyList)
3563{
3564   static Box3F box_prox; static SphereF ball_prox;
3565
3566   buildPolyList_TopSurfaceOnly = true;
3567   bool result = buildPolyList(plc, polyList, box_prox, ball_prox);
3568   buildPolyList_TopSurfaceOnly = false;
3569
3570   return result;
3571}
3572
3573