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 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( ¢er ); 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