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