river.cpp
Engine/source/environment/river.cpp
Classes:
Public Defines
define
MAX_NODE_DEPTH() 500.0f
define
MAX_NODE_WIDTH() 1000.0f
define
MIN_METERS_PER_SEGMENT() 1.0f
define
MIN_NODE_DEPTH() 0.25f
define
MIN_NODE_WIDTH() 0.25f
define
NODE_RADIUS() 15.0f
Public Variables
Public Functions
compareHitSegments(const void * a, const void * b)
ConsoleDocClass(River , "@brief A water volume defined by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spline.\n\n</a>" "User may <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> width and depth per node and overall spline shape in three " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dimensions.\n\n</a>" "%<a href="/coding/class/classriver/">River</a> supports dynamic planar reflections (fullReflect) like all <a href="/coding/class/classwaterobject/">WaterObject</a> " " classes, but keep in mind it is not necessarily <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> planar surface. For best " "visual quality <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> %<a href="/coding/class/classriver/">River</a> should be less reflective the more it twists and " "bends. This caution only applies <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> %Rivers with fullReflect <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">on.\n\n</a>" " @see <a href="/coding/class/classwaterobject/">WaterObject</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> inherited <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">functionality.\n\n</a>" " @ingroup Water" )
ConsoleDocClass(RiverNodeEvent , "@brief Sends messages <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/class/classriver/">River</a> <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(River , 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/classriver/">River</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> recreate its geometry." )
DefineEngineMethod(River , setBatchSize , void , (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>" "BatchSize is not currently used." )
DefineEngineMethod(River , setMaxDivisionSize , void , (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>" "@see SubdivideLength field." )
DefineEngineMethod(River , setMetersPerSegment , void , (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>" "@see SegmentLength field." )
DefineEngineMethod(River , 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." )
SegmentPointCompare(const void * aptr, const void * bptr)
Detailed Description
Public Defines
MAX_NODE_DEPTH() 500.0f
MAX_NODE_WIDTH() 1000.0f
MIN_METERS_PER_SEGMENT() 1.0f
MIN_NODE_DEPTH() 0.25f
MIN_NODE_WIDTH() 0.25f
NODE_RADIUS() 15.0f
Public Variables
U32 gIdxArray [6][2][3]
Point3F sSegmentPointComparePoints [4]
Public Functions
compareHitSegments(const void * a, const void * b)
ConsoleDocClass(River , "@brief A water volume defined by <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spline.\n\n</a>" "User may <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> width and depth per node and overall spline shape in three " "<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dimensions.\n\n</a>" "%<a href="/coding/class/classriver/">River</a> supports dynamic planar reflections (fullReflect) like all <a href="/coding/class/classwaterobject/">WaterObject</a> " " classes, but keep in mind it is not necessarily <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> planar surface. For best " "visual quality <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> %<a href="/coding/class/classriver/">River</a> should be less reflective the more it twists and " "bends. This caution only applies <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> %Rivers with fullReflect <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">on.\n\n</a>" " @see <a href="/coding/class/classwaterobject/">WaterObject</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> inherited <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">functionality.\n\n</a>" " @ingroup Water" )
ConsoleDocClass(RiverNodeEvent , "@brief Sends messages <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/class/classriver/">River</a> <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(River , 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/classriver/">River</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> recreate its geometry." )
DefineEngineMethod(River , setBatchSize , void , (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>" "BatchSize is not currently used." )
DefineEngineMethod(River , setMaxDivisionSize , void , (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>" "@see SubdivideLength field." )
DefineEngineMethod(River , setMetersPerSegment , void , (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>" "@see SegmentLength field." )
DefineEngineMethod(River , 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(RiverNodeEvent )
IMPLEMENT_CO_NETOBJECT_V1(River )
SegmentPointCompare(const void * aptr, const void * bptr)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "environment/river.h" 26 27#include "console/consoleTypes.h" 28#include "console/engineAPI.h" 29#include "util/catmullRom.h" 30#include "math/util/quadTransforms.h" 31#include "scene/simPath.h" 32#include "scene/sceneRenderState.h" 33#include "scene/sceneManager.h" 34#include "materials/sceneData.h" 35#include "materials/baseMatInstance.h" 36#include "scene/sgUtil.h" 37#include "T3D/gameBase/gameConnection.h" 38#include "core/stream/bitStream.h" 39#include "gfx/gfxDrawUtil.h" 40#include "gfx/gfxTransformSaver.h" 41#include "gfx/primBuilder.h" 42#include "gfx/gfxDebugEvent.h" 43#include "gfx/gfxOcclusionQuery.h" 44#include "math/mathIO.h" 45#include "math/mathUtils.h" 46#include "math/util/frustum.h" 47#include "math/util/quadTransforms.h" 48#include "gui/3d/guiTSControl.h" 49#include "gfx/sim/debugDraw.h" 50#include "T3D/fx/particleEmitter.h" 51#include "scene/reflectionManager.h" 52#include "ts/tsShapeInstance.h" 53#include "postFx/postEffect.h" 54#include "math/util/matrixSet.h" 55#include "environment/nodeListManager.h" 56 57ConsoleDocClass( River, 58 "@brief A water volume defined by a 3D spline.\n\n" 59 60 "User may control width and depth per node and overall spline shape in three " 61 "dimensions.\n\n" 62 63 "%River supports dynamic planar reflections (fullReflect) like all WaterObject " 64 "classes, but keep in mind it is not necessarily a planar surface. For best " 65 "visual quality a %River should be less reflective the more it twists and " 66 "bends. This caution only applies to %Rivers with fullReflect on.\n\n" 67 68 "@see WaterObject for inherited functionality.\n\n" 69 70 "@ingroup Water" 71); 72 73#define MIN_METERS_PER_SEGMENT 1.0f 74#define MIN_NODE_DEPTH 0.25f 75#define MAX_NODE_DEPTH 500.0f 76#define MIN_NODE_WIDTH 0.25f 77#define MAX_NODE_WIDTH 1000.0f 78#define NODE_RADIUS 15.0f 79 80static U32 gIdxArray[6][2][3] = { 81 { { 0, 4, 5 }, { 0, 5, 1 }, }, // Top Face 82 { { 2, 6, 4 }, { 2, 4, 0 }, }, // Left Face 83 { { 1, 5, 7 }, { 1, 7, 3 }, }, // Right Face 84 { { 2, 3, 7 }, { 2, 7, 6 }, }, // Bottom Face 85 { { 0, 1, 3 }, { 0, 3, 2 }, }, // Front Face 86 { { 4, 6, 7 }, { 4, 7, 5 }, }, // Back Face 87}; 88 89struct RiverHitSegment 90{ 91 U32 idx; 92 F32 t; 93}; 94 95static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b) 96{ 97 const RiverHitSegment *fa = (RiverHitSegment*)a; 98 const RiverHitSegment *fb = (RiverHitSegment*)b; 99 100 return mSign(fb->t - fa->t); 101} 102 103static Point3F sSegmentPointComparePoints[4]; 104 105//----------------------------------------------------------------------------- 106// DecalRoadNodeList Struct 107//----------------------------------------------------------------------------- 108 109struct RiverNodeList : public NodeListManager::NodeList 110{ 111 Vector<Point3F> mPositions; 112 Vector<F32> mWidths; 113 Vector<F32> mDepths; 114 Vector<VectorF> mNormals; 115 116 RiverNodeList() { } 117 virtual ~RiverNodeList() { } 118}; 119 120//----------------------------------------------------------------------------- 121// RiverNodeEvent Class 122//----------------------------------------------------------------------------- 123 124class RiverNodeEvent : public NodeListEvent 125{ 126 typedef NodeListEvent Parent; 127 128public: 129 Vector<Point3F> mPositions; 130 Vector<F32> mWidths; 131 Vector<F32> mDepths; 132 Vector<VectorF> mNormals; 133 134public: 135 RiverNodeEvent() { mNodeList = NULL; } 136 virtual ~RiverNodeEvent() { } 137 138 virtual void pack(NetConnection*, BitStream*); 139 virtual void unpack(NetConnection*, BitStream*); 140 141 virtual void copyIntoList(NodeListManager::NodeList* copyInto); 142 virtual void padListToSize(); 143 144 DECLARE_CONOBJECT(RiverNodeEvent); 145}; 146 147void RiverNodeEvent::pack(NetConnection* conn, BitStream* stream) 148{ 149 Parent::pack( conn, stream ); 150 151 stream->writeInt( mPositions.size(), 16 ); 152 153 for (U32 i=0; i<mPositions.size(); ++i) 154 { 155 mathWrite( *stream, mPositions[i] ); 156 stream->write( mWidths[i] ); 157 stream->write( mDepths[i] ); 158 mathWrite( *stream, mNormals[i] ); 159 } 160} 161 162void RiverNodeEvent::unpack(NetConnection* conn, BitStream* stream) 163{ 164 mNodeList = new RiverNodeList(); 165 166 Parent::unpack( conn, stream ); 167 168 U32 count = stream->readInt( 16 ); 169 170 Point3F pos; 171 F32 width, depth; 172 VectorF normal; 173 174 RiverNodeList* list = static_cast<RiverNodeList*>(mNodeList); 175 176 for (U32 i=0; i<count; ++i) 177 { 178 mathRead( *stream, &pos ); 179 stream->read( &width ); 180 stream->read( &depth ); 181 mathRead( *stream, &normal ); 182 183 list->mPositions.push_back( pos ); 184 list->mWidths.push_back( width ); 185 list->mDepths.push_back( depth ); 186 list->mNormals.push_back( normal ); 187 } 188 189 list->mTotalValidNodes = count; 190 191 // Do we have a complete list? 192 if (list->mPositions.size() >= mTotalNodes) 193 list->mListComplete = true; 194} 195 196void RiverNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto) 197{ 198 RiverNodeList* prevList = dynamic_cast<RiverNodeList*>(copyInto); 199 RiverNodeList* list = static_cast<RiverNodeList*>(mNodeList); 200 201 // Merge our list with the old list. 202 for (U32 i=<a href="/coding/class/classnodelistevent/#classnodelistevent_1a05350212961bce959f29916230c0209e">mLocalListStart</a>, index=0; i<mLocalListStart+list->mPositions.size(); ++i, ++index) 203 { 204 prevList->mPositions[i] = list->mPositions[index]; 205 prevList->mWidths[i] = list->mWidths[index]; 206 prevList->mDepths[i] = list->mDepths[index]; 207 prevList->mNormals[i] = list->mNormals[index]; 208 } 209} 210 211void RiverNodeEvent::padListToSize() 212{ 213 RiverNodeList* list = static_cast<RiverNodeList*>(mNodeList); 214 215 U32 totalValidNodes = list->mTotalValidNodes; 216 217 // Pad our list front? 218 if (mLocalListStart) 219 { 220 RiverNodeList* newlist = new RiverNodeList(); 221 newlist->mPositions.increment(mLocalListStart); 222 newlist->mWidths.increment(mLocalListStart); 223 newlist->mDepths.increment(mLocalListStart); 224 newlist->mNormals.increment(mLocalListStart); 225 226 newlist->mPositions.merge(list->mPositions); 227 newlist->mWidths.merge(list->mWidths); 228 newlist->mDepths.merge(list->mDepths); 229 newlist->mNormals.merge(list->mNormals); 230 231 delete list; 232 mNodeList = list = newlist; 233 } 234 235 // Pad our list end? 236 if (list->mPositions.size() < mTotalNodes) 237 { 238 U32 delta = mTotalNodes - list->mPositions.size(); 239 list->mPositions.increment(delta); 240 list->mWidths.increment(delta); 241 list->mDepths.increment(delta); 242 list->mNormals.increment(delta); 243 } 244 245 list->mTotalValidNodes = totalValidNodes; 246} 247 248IMPLEMENT_CO_NETEVENT_V1(RiverNodeEvent); 249 250ConsoleDocClass( RiverNodeEvent, 251 "@brief Sends messages to the River Editor\n\n" 252 "Editor use only.\n\n" 253 "@internal" 254); 255//----------------------------------------------------------------------------- 256// RiverNodeListNotify Class 257//----------------------------------------------------------------------------- 258 259class RiverNodeListNotify : public NodeListNotify 260{ 261 typedef NodeListNotify Parent; 262 263protected: 264 SimObjectPtr<River> mRiver; 265 266public: 267 RiverNodeListNotify( River* river, U32 listId ) { mRiver = river; mListId = listId; } 268 virtual ~RiverNodeListNotify() { mRiver = NULL; } 269 270 virtual void sendNotification( NodeListManager::NodeList* list ); 271}; 272 273void RiverNodeListNotify::sendNotification( NodeListManager::NodeList* list ) 274{ 275 if (mRiver.isValid()) 276 { 277 // Build the road's nodes 278 RiverNodeList* riverList = dynamic_cast<RiverNodeList*>( list ); 279 if (riverList) 280 mRiver->buildNodesFromList( riverList ); 281 } 282} 283 284//------------------------------------------------------------------------------ 285// Class: RiverSegment 286//------------------------------------------------------------------------------ 287 288RiverSegment::RiverSegment() 289{ 290 mPlaneCount = 0; 291 columns = 0; 292 rows = 0; 293 numVerts = 0; 294 numTriangles = 0; 295 296 startVert = 0; 297 endVert = 0; 298 startIndex = 0; 299 endIndex = 0; 300 301 slice0 = NULL; 302 slice1 = NULL; 303} 304 305RiverSegment::RiverSegment( RiverSlice *rs0, RiverSlice *rs1 ) 306{ 307 columns = 0; 308 rows = 0; 309 numVerts = 0; 310 numTriangles = 0; 311 312 startVert = 0; 313 endVert = 0; 314 startIndex = 0; 315 endIndex = 0; 316 317 slice0 = rs0; 318 slice1 = rs1; 319 320 // Calculate the planes for this segment 321 // Will be used for intersection/buoyancy tests 322 VectorF normal; 323 mPlaneCount = 6; 324 325 sSegmentPointCompareReference = getFaceCenter(6); 326 327 // left 328 mPlanes[0] = _getBestPlane( &slice1->p0, &slice1->pb0, &slice0->pb0, &slice0->p0 ); 329 330 // right 331 mPlanes[1] = _getBestPlane( &slice0->pb2, &slice1->pb2, &slice1->p2, &slice0->p2 ); 332 333 // near 334 mPlanes[2] = _getBestPlane( &slice0->pb0, &slice0->pb2, &slice0->p2, &slice0->p0 ); 335 336 // far 337 mPlanes[3] = _getBestPlane( &slice1->pb2, &slice1->pb0, &slice1->p0, &slice1->p2 ); 338 339 // top 340 mPlanes[4] = _getBestPlane( &slice0->p2, &slice1->p2, &slice1->p0, &slice0->p0 ); 341 342 // bottom 343 mPlanes[5] = _getBestPlane( &slice0->pb2, &slice0->pb0, &slice1->pb0, &slice1->pb2 ); 344 345 // Calculate the bounding box(s) 346 worldbounds.minExtents = worldbounds.maxExtents = rs0->p0; 347 worldbounds.extend( rs0->p2 ); 348 worldbounds.extend( rs0->pb0 ); 349 worldbounds.extend( rs0->pb2 ); 350 worldbounds.extend( rs1->p0 ); 351 worldbounds.extend( rs1->p2 ); 352 worldbounds.extend( rs1->pb0 ); 353 worldbounds.extend( rs1->pb2 ); 354 355 /* 356 // Calculate tetrahedrons (for collision and buoyancy testing) 357 // This is 0 in the diagram. 358 mCubePoints[0] = cornerPoint; 359 mCubePoints[1] = cornerPoint + (VectorF( 1.0f, 0.0f, 0.0f ) * size ); 360 mCubePoints[2] = cornerPoint + (VectorF( 0.0f, 1.0f, 0.0f ) * size ); 361 mCubePoints[3] = cornerPoint + (VectorF( 1.0f, 1.0f, 0.0f ) * size ); 362 363 mCubePoints[4] = cornerPoint + (VectorF( 0.0f, 0.0f, 1.0f ); 364 mCubePoints[5] = cornerPoint + (VectorF( 1.0f, 0.0f, 1.0f ); 365 mCubePoints[6] = cornerPoint + (VectorF( 0.0f, 1.0f, 1.0f ); 366 mCubePoints[7] = cornerPoint + (VectorF( 1.0f, 1.0f, 1.0f ); 367 368 // Center tetra. 369 mTetras[0].p0 = &mCubePoints[1]; 370 mTetras[0].p1 = &mCubePoints[2]; 371 mTetras[0].p2 = &mCubePoints[4]; 372 mTetras[0].p3 = &mCubePoints[7]; 373 374 375 376 mTetras[1].p0 = &mCubePoints[0]; // this is the tip 377 mTetras[1].p1 = &mCubePoints[1]; 378 mTetras[1].p2 = &mCubePoints[2]; 379 mTetras[1].p3 = &mCubePoints[4]; 380 381 mTetras[2].p0 = &mCubePoints[3]; // tip 382 mTetras[2].p1 = &mCubePoints[2]; 383 mTetras[2].p2 = &mCubePoints[1]; 384 mTetras[2].p3 = &mCubePoints[7]; 385 386 mTetras[3].p0 = &mCubePoints[6]; // tip 387 mTetras[3].p1 = &mCubePoints[7]; 388 mTetras[3].p2 = &mCubePoints[4]; 389 mTetras[3].p3 = &mCubePoints[2]; 390 391 mTetras[4].p0 = &mCubePoints[5]; // tip 392 mTetras[4].p1 = &mCubePoints[7]; 393 mTetras[4].p2 = &mCubePoints[4]; 394 mTetras[4].p3 = &mCubePoints[3];*/ 395 396} 397 398void RiverSegment::set( RiverSlice *rs0, RiverSlice *rs1 ) 399{ 400 columns = 0; 401 rows = 0; 402 numVerts = 0; 403 numTriangles = 0; 404 405 startVert = 0; 406 endVert = 0; 407 startIndex = 0; 408 endIndex = 0; 409 410 slice0 = rs0; 411 slice1 = rs1; 412} 413 414static S32 QSORT_CALLBACK SegmentPointCompare(const void *aptr, const void *bptr) 415{ 416 const U32 a = *(const U32*)aptr; 417 const U32 b = *(const U32*)bptr; 418 419 F32 lenA = ( sSegmentPointCompareReference - sSegmentPointComparePoints[a] ).lenSquared(); 420 F32 lenB = ( sSegmentPointCompareReference - sSegmentPointComparePoints[b] ).lenSquared(); 421 return ( lenB - lenA ); 422} 423 424PlaneF RiverSegment::_getBestPlane( const Point3F *p0, const Point3F *p1, const Point3F *p2, const Point3F *p3 ) 425{ 426 sSegmentPointComparePoints[0] = *p0; 427 sSegmentPointComparePoints[1] = *p1; 428 sSegmentPointComparePoints[2] = *p2; 429 sSegmentPointComparePoints[3] = *p3; 430 431 Point3F points[4] = { 432 *p0, *p1, *p2, *p3 433 }; 434 435 U32 indices[4] = { 436 0,1,2,3 437 }; 438 439 dQsort(indices, 4, sizeof(U32), SegmentPointCompare); 440 441 // Collect the best three points (in correct winding order) 442 // To generate the plane's normal 443 Vector<Point3F> normalPnts; 444 445 for ( U32 i = 0; i < 4; i++ ) 446 { 447 if ( i == indices[3] ) 448 continue; 449 450 normalPnts.push_back(points[i]); 451 } 452 453 PlaneF plane( normalPnts[0], normalPnts[1], normalPnts[2] ); 454 return plane; 455} 456 457Point3F RiverSegment::getFaceCenter( U32 faceIdx ) const 458{ 459 Point3F center(0,0,0); 460 461 switch ( faceIdx ) 462 { 463 case 0: // left 464 center = slice1->p0 + slice0->p0 + slice0->pb0 + slice1->pb0; 465 center *= 0.25f; 466 break; 467 468 case 1: // right 469 center = slice0->p2 + slice1->p2 + slice1->pb2 + slice0->pb2; 470 center *= 0.25f; 471 break; 472 473 case 2: // near 474 center = slice0->p0 + slice0->p2 + slice0->pb2 + slice0->pb0; 475 center *= 0.25f; 476 break; 477 478 case 3: // far 479 center = slice1->pb0 + slice1->p0 + slice1->pb0 + slice1->pb2; 480 center *= 0.25f; 481 break; 482 483 case 4: // top 484 center = slice0->p0 + slice1->p0 + slice1->p2 + slice0->p2; 485 center *= 0.25f; 486 break; 487 488 case 5: // bottom 489 center = slice1->pb2 + slice1->pb0 + slice0->pb0 + slice0->pb2; 490 center *= 0.25f; 491 break; 492 493 case 6: // segment center 494 center = slice0->p0 + slice0->p2 + slice1->p0 + slice1->p2 + slice0->pb0 + slice0->pb2 + slice1->pb0 + slice1->pb2; 495 center /= 8; 496 break; 497 } 498 499 return center; 500} 501 502bool RiverSegment::intersectBox( const Box3F &bounds ) const 503{ 504 // This code copied from Frustum class. 505 506 Point3F maxPoint; 507 F32 maxDot; 508 509 // Note the planes are ordered left, right, near, 510 // far, top, bottom for getting early rejections 511 // from the typical horizontal scene. 512 for ( S32 i = 0; i < mPlaneCount; i++ ) 513 { 514 // This is pretty much as optimal as you can 515 // get for a plane vs AABB test... 516 // 517 // 4 comparisons 518 // 3 multiplies 519 // 2 adds 520 // 1 negation 521 // 522 // It will early out as soon as it detects the 523 // bounds is outside one of the planes. 524 525 if ( mPlanes[i].x > 0 ) 526 maxPoint.x = bounds.maxExtents.x; 527 else 528 maxPoint.x = bounds.minExtents.x; 529 530 if ( mPlanes[i].y > 0 ) 531 maxPoint.y = bounds.maxExtents.y; 532 else 533 maxPoint.y = bounds.minExtents.y; 534 535 if ( mPlanes[i].z > 0 ) 536 maxPoint.z = bounds.maxExtents.z; 537 else 538 maxPoint.z = bounds.minExtents.z; 539 540 maxDot = mDot( maxPoint, mPlanes[ i ] ); 541 542 if ( maxDot <= -mPlanes[ i ].d ) 543 return false; 544 } 545 546 return true; 547} 548 549bool RiverSegment::containsPoint( const Point3F &pnt ) const 550{ 551 // NOTE: this code from Frustum class. 552 553 F32 maxDot; 554 555 // Note the planes are ordered left, right, near, 556 // far, top, bottom for getting early rejections 557 // from the typical horizontal scene. 558 for ( S32 i = 0; i < mPlaneCount; i++ ) 559 { 560 const PlaneF &plane = mPlanes[ i ]; 561 562 // This is pretty much as optimal as you can 563 // get for a plane vs point test... 564 // 565 // 1 comparison 566 // 2 multiplies 567 // 1 adds 568 // 569 // It will early out as soon as it detects the 570 // point is outside one of the planes. 571 572 maxDot = mDot( pnt, plane ) + plane.d; 573 if ( maxDot < -0.1f ) 574 return false; 575 } 576 577 return true; 578} 579 580F32 RiverSegment::distanceToSurface(const Point3F &pnt) const 581{ 582 return mPlanes[4].distToPlane( pnt ); 583} 584 585bool River::smEditorOpen = false; 586bool River::smWireframe = false; 587bool River::smShowWalls = false; 588bool River::smShowNodes = false; 589bool River::smShowSpline = true; 590bool River::smShowRiver = true; 591SimObjectPtr<SimSet> River::smServerRiverSet = NULL; 592 593IMPLEMENT_CO_NETOBJECT_V1(River); 594 595 596River::River() 597 : mLowVertCount(0), 598 mHighVertCount(0), 599 mLowTriangleCount(0), 600 mHighTriangleCount(0), 601 mSegmentsPerBatch(10), 602 mMetersPerSegment(10.0f), 603 mDepthScale(1.0f), 604 mFlowMagnitude(1.0f), 605 mLodDistance( 50.0f ), 606 mMaxDivisionSize(2.5f), 607 mMinDivisionSize(0.25f), 608 mColumnCount(5) 609{ 610 mNetFlags.set( Ghostable | ScopeAlways ); 611 612 mObjScale.set( 1, 1, 1 ); 613 614 mObjBox.minExtents.set( -0.5, -0.5, -0.5 ); 615 mObjBox.maxExtents.set( 0.5, 0.5, 0.5 ); 616 617 mReflectNormalUp = false; 618 619 // We use the shader const miscParams.w to signify 620 // that this object is a River. 621 mMiscParamW = 1.0f; 622} 623 624River::~River() 625{ 626} 627 628void River::initPersistFields() 629{ 630 addGroup( "River" ); 631 632 addField( "SegmentLength", TypeF32, Offset( mMetersPerSegment, River ), 633 "Divide the River lengthwise into segments of this length in meters. " 634 "These geometric volumes are used for spacial queries like determining containment." ); 635 636 addField( "SubdivideLength", TypeF32, Offset( mMaxDivisionSize, River ), 637 "For purposes of generating the renderable geometry River segments are further subdivided " 638 "such that no quad is of greater width or length than this distance in meters." ); 639 640 addField( "FlowMagnitude", TypeF32, Offset( mFlowMagnitude, River ), 641 "Magnitude of the force vector applied to dynamic objects within the River." ); 642 643 addField( "LowLODDistance", TypeF32, Offset( mLodDistance, River ), 644 "Segments of the river at this distance in meters or greater will " 645 "render as a single unsubdivided without undulation effects." ); 646 647 endGroup( "River" ); 648 649 addGroup( "Internal" ); 650 651 addProtectedField( "Node", TypeString, NULL, &addNodeFromField, &emptyStringProtectedGetFn, "For internal use, do not modify." ); 652 653 endGroup( "Internal" ); 654 655 Parent::initPersistFields(); 656} 657 658void River::consoleInit() 659{ 660 Parent::consoleInit(); 661 662 Con::addVariable( "$River::EditorOpen", TypeBool, &River::smEditorOpen, "For editor use.\n" 663 "@ingroup Editors\n" ); 664 Con::addVariable( "$River::showWalls", TypeBool, &River::smShowWalls, "For editor use.\n" 665 "@ingroup Editors\n" ); 666 Con::addVariable( "$River::showNodes", TypeBool, &River::smShowNodes, "For editor use.\n" 667 "@ingroup Editors\n"); 668 Con::addVariable( "$River::showSpline", TypeBool, &River::smShowSpline, "For editor use.\n" 669 "@ingroup Editors\n" ); 670 Con::addVariable( "$River::showRiver", TypeBool, &River::smShowRiver, "For editor use.\n" 671 "@ingroup Editors\n" ); 672 Con::addVariable( "$River::showWireframe", TypeBool, &River::smWireframe, "For editor use.\n" 673 "@ingroup Editors\n"); 674} 675 676bool River::addNodeFromField( void *object, const char *index, const char *data ) 677{ 678 River *pObj = static_cast<River*>(object); 679 680 //if ( !pObj->isProperlyAdded() ) 681 //{ 682 F32 x,y,z,width,depth; 683 VectorF normal; 684 U32 result = dSscanf( data, "%f %f %f %f %f %f %f %f", &x, &y, &z, &width, &depth, &normal.x, &normal.y, &normal.z ); 685 if ( result == 8 ) 686 pObj->_addNode( Point3F(x,y,z), width, depth, normal ); 687 //} 688 689 return false; 690} 691 692bool River::onAdd() 693{ 694 if ( !Parent::onAdd() ) 695 return false; 696 697 // Reset the World Box. 698 //setGlobalBounds(); 699 resetWorldBox(); 700 701 // Set the Render Transform. 702 setRenderTransform(mObjToWorld); 703 704 // Add to Scene. 705 addToScene(); 706 707 if ( isServerObject() ) 708 getServerSet()->addObject( this ); 709 710 _regenerate(); 711 712 return true; 713} 714 715void River::onRemove() 716{ 717 removeFromScene(); 718 719 Parent::onRemove(); 720} 721 722void River::inspectPostApply() 723{ 724 // Set Parent. 725 Parent::inspectPostApply(); 726 727 if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT ) 728 mMetersPerSegment = MIN_METERS_PER_SEGMENT; 729 730 mMaxDivisionSize = getMax( mMaxDivisionSize, mMinDivisionSize ); 731 732 // Set fxPortal Mask. 733 setMaskBits(RiverMask</a>|<a href="/coding/class/classriver/#classriver_1a155ef8e378b8c5a68f6dbc662dec7de0a083a3101bc65d3239b7295221c0c5a39">RegenMask); 734} 735 736void River::onStaticModified( const char* slotName, const char*newValue ) 737{ 738 Parent::onStaticModified( slotName, newValue ); 739 740 if ( dStricmp( slotName, "surfMaterial" ) == 0 ) 741 setMaskBits( MaterialMask ); 742} 743 744SimSet* River::getServerSet() 745{ 746 if ( !smServerRiverSet ) 747 { 748 smServerRiverSet = new SimSet(); 749 smServerRiverSet->registerObject( "ServerRiverSet" ); 750 Sim::getRootGroup()->addObject( smServerRiverSet ); 751 } 752 753 return smServerRiverSet; 754} 755 756void River::writeFields( Stream &stream, U32 tabStop ) 757{ 758 Parent::writeFields( stream, tabStop ); 759 760 // Now write all nodes 761 762 stream.write(2, "\r\n"); 763 764 for ( U32 i = 0; i < mNodes.size(); i++ ) 765 { 766 const RiverNode &node = mNodes[i]; 767 768 stream.writeTabs(tabStop); 769 770 char buffer[1024]; 771 dMemset( buffer, 0, 1024 ); 772 dSprintf( buffer, 1024, "Node = \"%f %f %f %f %f %f %f %f\";", node.point.x, node.point.y, node.point.z, 773 node.width, 774 node.depth, 775 node.normal.x, node.normal.y, node.normal.z ); 776 stream.writeLine( (const U8*)buffer ); 777 } 778} 779 780bool River::writeField( StringTableEntry fieldname, const char *value ) 781{ 782 if ( fieldname == StringTable->insert("node") ) 783 return false; 784 785 return Parent::writeField( fieldname, value ); 786} 787 788void River::innerRender( SceneRenderState *state ) 789{ 790 GFXDEBUGEVENT_SCOPE( River_innerRender, ColorI( 255, 0, 0 ) ); 791 792 PROFILE_SCOPE( River_innerRender ); 793 794 // Setup SceneData 795 SceneData sgData; 796 sgData.init( state ); 797 sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); 798 sgData.backBuffTex = REFLECTMGR->getRefractTex(); 799 sgData.reflectTex = mPlaneReflector.reflectTex; 800 sgData.wireframe |= smWireframe; 801 802 const Point3F &camPosition = state->getCameraPosition(); 803 804 // set the material 805 806 S32 matIdx = getMaterialIndex( camPosition ); 807 808 if ( !initMaterial( matIdx ) ) 809 return; 810 811 BaseMatInstance *mat = mMatInstances[matIdx]; 812 WaterMatParams matParams = mMatParamHandles[matIdx]; 813 814 if ( !mat ) 815 return; 816 817 // setup proj/world transform 818 GFXTransformSaver saver; 819 820 setShaderParams( state, mat, matParams ); 821 822 _makeRenderBatches( camPosition ); 823 824 if ( !River::smShowRiver ) 825 return; 826 827 // If no material... we're done. 828 if ( mLowLODBatches.empty() && mHighLODBatches.empty() ) 829 return; 830 831 if ( !mHighLODBatches.empty() ) 832 _makeHighLODBuffers(); 833 834 mMatrixSet->restoreSceneViewProjection(); 835 mMatrixSet->setWorld( MatrixF::Identity ); 836 837 while( mat->setupPass( state, sgData ) ) 838 { 839 mat->setSceneInfo(state, sgData); 840 mat->setTransforms(*mMatrixSet, state); 841 842 setCustomTextures( matIdx, mat->getCurPass(), matParams ); 843 844 GFX->setVertexBuffer( mVB_low ); 845 GFX->setPrimitiveBuffer( mPB_low ); 846 847 for ( U32 i = 0; i < mLowLODBatches.size(); i++ ) 848 { 849 const RiverRenderBatch &batch = mLowLODBatches[i]; 850 851 U32 startVert = batch.startSegmentIdx * 2; 852 U32 endVert = ( batch.endSegmentIdx + 1 ) * 2 + 1; 853 U32 startIdx = batch.startSegmentIdx * 6; 854 U32 endIdx = batch.endSegmentIdx * 6 + 5; 855 856 U32 vertCount = ( endVert - startVert ) + 1; 857 U32 idxCount = ( endIdx - startIdx ) + 1; 858 U32 triangleCount = idxCount / 3; 859 860 AssertFatal( startVert < mLowVertCount, "River, bad draw call!" ); 861 AssertFatal( startVert + vertCount <= mLowVertCount, "River, bad draw call!" ); 862 AssertFatal( triangleCount <= mLowTriangleCount, "River, bad draw call!" ); 863 864 GFX->drawIndexedPrimitive( GFXTriangleList, 0, startVert, vertCount, startIdx, triangleCount ); 865 } 866 867 // Render all high detail batches. 868 // 869 // It is possible that the buffers could not be allocated because 870 // the max number of verts/indices was exceeded. We don't want to 871 // crash because that would be unhelpful for working in the editor. 872 if ( mVB_high.isValid() && mPB_high.isValid() ) 873 { 874 GFX->setVertexBuffer( mVB_high ); 875 GFX->setPrimitiveBuffer( mPB_high ); 876 877 for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) 878 { 879 const RiverRenderBatch &batch = mHighLODBatches[i]; 880 881 AssertFatal( batch.startVert < mHighVertCount, "River, bad draw call!" ); 882 AssertFatal( batch.startVert + batch.vertCount <= mHighVertCount, "River, bad draw call!" ); 883 AssertFatal( batch.triangleCount <= mHighTriangleCount, "River, bad draw call!" ); 884 AssertFatal( batch.startIndex < mHighTriangleCount * 3, "River, bad draw call!" ); 885 AssertFatal( batch.startIndex + batch.triangleCount * 3 <= mHighTriangleCount * 3, "River, bad draw call!" ); 886 887 GFX->drawIndexedPrimitive( GFXTriangleList, 888 0, 889 0, 890 batch.vertCount, 891 batch.startIndex, 892 batch.triangleCount ); 893 } 894 } 895 896 } // while( mat->setupPass( sgData ) ) 897} 898 899void River::updateUnderwaterEffect( SceneRenderState *state ) 900{ 901 // Calculate mWaterPlane before calling updateUnderwaterEffect. 902 Point3F dummy; 903 _getWaterPlane( state->getCameraPosition(), mWaterFogData.plane, dummy ); 904 905 Parent::updateUnderwaterEffect( state ); 906} 907 908void River::setShaderParams( SceneRenderState *state, BaseMatInstance* mat, const WaterMatParams& paramHandles ) 909{ 910 // Set variables that will be assigned to shader consts within WaterCommon 911 // before calling Parent::setShaderParams 912 913 mUndulateMaxDist = mLodDistance; 914 915 Parent::setShaderParams( state, mat, paramHandles ); 916 917 // Now set the rest of the shader consts that are either unique to this 918 // class or that WaterObject leaves to us to handle... 919 920 MaterialParameters* matParams = mat->getMaterialParameters(); 921 922 // set vertex shader constants 923 //----------------------------------- 924 925 matParams->setSafe(paramHandles.mGridElementSizeSC, 1.0f); 926 if ( paramHandles.mModelMatSC->isValid() ) 927 matParams->set(paramHandles.mModelMatSC, MatrixF::Identity, GFXSCT_Float4x4); 928 929 // set pixel shader constants 930 //----------------------------------- 931 932 LinearColorF c( mWaterFogData.color ); 933 matParams->setSafe(paramHandles.mBaseColorSC, c); 934 935 // By default we need to show a true reflection is fullReflect is enabled and 936 // we are above water. 937 F32 reflect = mPlaneReflector.isEnabled() && !isUnderwater( state->getCameraPosition() ); 938 939 // If we were occluded the last frame a query was fetched ( not necessarily last frame ) 940 // and we weren't updated last frame... we don't have a valid texture to show 941 // so use the cubemap / fake reflection color this frame. 942 if ( mPlaneReflector.lastUpdateMs != REFLECTMGR->getLastUpdateMs() && mPlaneReflector.isOccluded() ) 943 reflect = false; 944 945 Point4F reflectParams( mWaterPos.z, 0.0f, 1000.0f, !reflect ); 946 matParams->setSafe(paramHandles.mReflectParamsSC, reflectParams ); 947 948 matParams->setSafe(paramHandles.mReflectNormalSC, mPlaneReflector.refplane ); 949} 950 951bool River::isUnderwater( const Point3F &pnt ) const 952{ 953 return containsPoint( pnt, NULL ); 954} 955 956U32 River::packUpdate(NetConnection * con, U32 mask, BitStream * stream) 957{ 958 // Pack Parent. 959 U32 retMask = Parent::packUpdate(con, mask, stream); 960 961 if ( stream->writeFlag( mask & RiverMask ) ) 962 { 963 // Write Object Transform. 964 stream->writeAffineTransform(mObjToWorld); 965 966 stream->write( mMetersPerSegment ); 967 stream->write( mSegmentsPerBatch ); 968 stream->write( mDepthScale ); 969 stream->write( mMaxDivisionSize ); 970 stream->write( mColumnCount ); 971 972 stream->write( mFlowMagnitude ); 973 stream->write( mLodDistance ); 974 } 975 976 if ( stream->writeFlag( mask & NodeMask ) ) 977 { 978 const U32 nodeByteSize = 32; // Based on sending all of a node's parameters 979 980 // Test if we can fit all of our nodes within the current stream. 981 // We make sure we leave 100 bytes still free in the stream for whatever 982 // may follow us. 983 S32 allowedBytes = stream->getWriteByteSize() - 100; 984 if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) ) 985 { 986 // All nodes should fit, so send them out now. 987 stream->writeInt( mNodes.size(), 16 ); 988 989 for ( U32 i = 0; i < mNodes.size(); i++ ) 990 { 991 mathWrite( *stream, mNodes[i].point ); 992 stream->write( mNodes[i].width ); 993 stream->write( mNodes[i].depth ); 994 mathWrite( *stream, mNodes[i].normal ); 995 } 996 } 997 else 998 { 999 // There isn't enough space left in the stream for all of the 1000 // nodes. Batch them up into NetEvents. 1001 U32 id = gServerNodeListManager->nextListId(); 1002 U32 count = 0; 1003 U32 index = 0; 1004 while (count < mNodes.size()) 1005 { 1006 count += NodeListManager::smMaximumNodesPerEvent; 1007 if (count > mNodes.size()) 1008 { 1009 count = mNodes.size(); 1010 } 1011 1012 RiverNodeEvent* event = new RiverNodeEvent(); 1013 event->mId = id; 1014 event->mTotalNodes = mNodes.size(); 1015 event->mLocalListStart = index; 1016 1017 for (; index<count; ++index) 1018 { 1019 event->mPositions.push_back( mNodes[index].point ); 1020 event->mWidths.push_back( mNodes[index].width ); 1021 event->mDepths.push_back( mNodes[index].depth ); 1022 event->mNormals.push_back( mNodes[index].normal ); 1023 } 1024 1025 con->postNetEvent( event ); 1026 } 1027 1028 stream->write( id ); 1029 } 1030 } 1031 1032 if( stream->writeFlag( mask & ( RiverMask | InitialUpdateMask ) ) ) 1033 { 1034 // This is set to allow the user to modify the size of the water dynamically 1035 // in the editor 1036 mathWrite( *stream, mObjScale ); 1037 stream->writeAffineTransform( mObjToWorld ); 1038 } 1039 1040 stream->writeFlag( mask & RegenMask ); 1041 1042 return retMask; 1043} 1044 1045void River::unpackUpdate(NetConnection * con, BitStream * stream) 1046{ 1047 // Unpack Parent. 1048 Parent::unpackUpdate(con, stream); 1049 1050 // RiverMask 1051 if(stream->readFlag()) 1052 { 1053 MatrixF ObjectMatrix; 1054 stream->readAffineTransform(&ObjectMatrix); 1055 Parent::setTransform(ObjectMatrix); 1056 1057 stream->read( &mMetersPerSegment ); 1058 stream->read( &mSegmentsPerBatch ); 1059 stream->read( &mDepthScale ); 1060 stream->read( &mMaxDivisionSize ); 1061 stream->read( &mColumnCount ); 1062 1063 stream->read( &mFlowMagnitude ); 1064 stream->read( &mLodDistance ); 1065 } 1066 1067 // NodeMask 1068 if ( stream->readFlag() ) 1069 { 1070 if (stream->readFlag()) 1071 { 1072 // Nodes have been passed in this update 1073 U32 count = stream->readInt( 16 ); 1074 1075 mNodes.clear(); 1076 1077 Point3F pos; 1078 VectorF normal; 1079 F32 width,depth; 1080 1081 for ( U32 i = 0; i < count; i++ ) 1082 { 1083 mathRead( *stream, &pos ); 1084 stream->read( &width ); 1085 stream->read( &depth ); 1086 mathRead( *stream, &normal ); 1087 _addNode( pos, width, depth, normal ); 1088 } 1089 } 1090 else 1091 { 1092 // Nodes will arrive as events 1093 U32 id; 1094 stream->read( &id ); 1095 1096 // Check if the road's nodes made it here before we did. 1097 NodeListManager::NodeList* list = NULL; 1098 if ( gClientNodeListManager->findListById( id, &list, true) ) 1099 { 1100 // Work with the completed list 1101 RiverNodeList* riverList = dynamic_cast<RiverNodeList*>( list ); 1102 if (riverList) 1103 buildNodesFromList( riverList ); 1104 1105 delete list; 1106 } 1107 else 1108 { 1109 // Nodes have not yet arrived, so register our interest in the list 1110 RiverNodeListNotify* notify = new RiverNodeListNotify( this, id ); 1111 gClientNodeListManager->registerNotification( notify ); 1112 } 1113 } 1114 } 1115 1116 // RiverMask | InitialUpdateMask 1117 if( stream->readFlag() ) 1118 { 1119 mathRead( *stream, &mObjScale ); 1120 stream->readAffineTransform( &mObjToWorld ); 1121 } 1122 1123 // RegenMask 1124 if ( stream->readFlag() && isProperlyAdded() ) 1125 regenerate(); 1126} 1127 1128void River::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) 1129{ 1130 // Find the RiverSegment closest to the camera. 1131 F32 closestDist = F32_MAX; 1132 S32 closestSegment = 0; 1133 Point3F projPnt(0.0f, 0.0f, 0.0f); 1134 1135 VectorF normal(0,0,0); 1136 1137 for ( U32 i = 0; i < mSegments.size(); i++ ) 1138 { 1139 const RiverSegment &segment = mSegments[i]; 1140 1141 const Point3F pos = MathUtils::mClosestPointOnSegment( segment.slice0->p1, segment.slice1->p1, camPos ); 1142 1143 F32 dist = ( camPos - pos ).len(); 1144 1145 if ( dist < closestDist ) 1146 { 1147 closestDist = dist; 1148 closestSegment = i; 1149 projPnt = pos; 1150 } 1151 1152 normal += segment.getSurfaceNormal(); 1153 } 1154 1155 if ( mReflectNormalUp ) 1156 normal.set(0,0,1); 1157 else 1158 normal.normalizeSafe(); 1159 1160 outPos = projPnt; 1161 outPlane.set( projPnt, normal ); 1162} 1163 1164void River::setTransform( const MatrixF &mat ) 1165{ 1166 1167 for ( U32 i = 0; i < mNodes.size(); i++ ) 1168 { 1169 mWorldToObj.mulP( mNodes[i].point ); 1170 mat.mulP( mNodes[i].point ); 1171 } 1172 1173 /* 1174 // Get the amount of change in position. 1175 MatrixF oldMat = getTransform(); 1176 Point3F oldPos = oldMat.getPosition(); 1177 Point3F newPos = mat.getPosition(); 1178 Point3F delta = newPos - oldPos; 1179 1180 // Offset all nodes by that amount 1181 for ( U32 i = 0; i < mNodes.size(); i++ ) 1182 { 1183 mNodes[i].point += delta; 1184 } 1185 1186 // Assign the new position ( we ignore rotation ) 1187 MatrixF newMat( oldMat ); 1188 newMat.setPosition( newPos ); 1189 */ 1190 1191 Parent::setTransform( mat ); 1192 1193 // Regenerate and update the client 1194 _regenerate(); 1195 setMaskBits( NodeMask | RegenMask ); 1196} 1197 1198void River::setScale( const VectorF &scale ) 1199{ 1200 // We ignore scale requests from the editor 1201 // right now. 1202} 1203 1204bool River::castRay(const Point3F &s, const Point3F &e, RayInfo* info) 1205{ 1206 Point3F start = s; 1207 Point3F end = e; 1208 mObjToWorld.mulP(start); 1209 mObjToWorld.mulP(end); 1210 1211 F32 out = 1.0f; // The output fraction/percentage along the line defined by s and e 1212 VectorF norm(0.0f, 0.0f, 0.0f); // The normal of the face intersected 1213 1214 Vector<RiverHitSegment> hitSegments; 1215 1216 for ( U32 i = 0; i < mSegments.size(); i++ ) 1217 { 1218 const RiverSegment &segment = mSegments[i]; 1219 1220 F32 t; 1221 VectorF n; 1222 1223 if ( segment.worldbounds.collideLine( start, end, &t, &n ) ) 1224 { 1225 hitSegments.increment(); 1226 hitSegments.last().t = t; 1227 hitSegments.last().idx = i; 1228 } 1229 } 1230 1231 dQsort( hitSegments.address(), hitSegments.size(), sizeof(RiverHitSegment), compareHitSegments ); 1232 1233 U32 idx0, idx1, idx2; 1234 F32 t; 1235 1236 for ( U32 i = 0; i < hitSegments.size(); i++ ) 1237 { 1238 U32 segIdx = hitSegments[i].idx; 1239 const RiverSegment &segment = mSegments[segIdx]; 1240 1241 // Each segment has 6 faces 1242 for ( U32 j = 0; j < 6; j++ ) 1243 { 1244 if ( j == 4 && segIdx != 0 ) 1245 continue; 1246 1247 if ( j == 5 && segIdx != mSegments.size() - 1 ) 1248 continue; 1249 1250 // Each face has 2 triangles 1251 for ( U32 k = 0; k < 2; k++ ) 1252 { 1253 idx0 = gIdxArray[j][k][0]; 1254 idx1 = gIdxArray[j][k][1]; 1255 idx2 = gIdxArray[j][k][2]; 1256 1257 const Point3F &v0 = segment[idx0]; 1258 const Point3F &v1 = segment[idx1]; 1259 const Point3F &v2 = segment[idx2]; 1260 1261 if ( !MathUtils::mLineTriangleCollide( start, end, 1262 v2, v1, v0, 1263 NULL, 1264 &t ) ) 1265 continue; 1266 1267 if ( t >= 0.0f && t < 1.0f && t < out ) 1268 { 1269 out = t; 1270 1271 // optimize this, can be calculated easily within 1272 // the collision test 1273 norm = PlaneF( v0, v1, v2 ); 1274 } 1275 } 1276 } 1277 1278 if (out >= 0.0f && out < 1.0f) 1279 break; 1280 } 1281 1282 if (out >= 0.0f && out < 1.0f) 1283 { 1284 info->t = out; 1285 info->normal = norm; 1286 info->point.interpolate(start, end, out); 1287 info->face = -1; 1288 info->object = this; 1289 1290 return true; 1291 } 1292 1293 return false; 1294} 1295 1296bool River::collideBox(const Point3F &start, const Point3F &end, RayInfo* info) 1297{ 1298 return false; 1299} 1300 1301bool River::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere ) 1302{ 1303 Vector<const RiverSegment*> hitSegments; 1304 for ( U32 i = 0; i < mSegments.size(); i++ ) 1305 { 1306 const RiverSegment &segment = mSegments[i]; 1307 if ( segment.worldbounds.isOverlapped( box ) ) 1308 { 1309 hitSegments.push_back( &segment ); 1310 } 1311 } 1312 1313 if ( !hitSegments.size() ) 1314 return false; 1315 1316 polyList->setObject( this ); 1317 polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) ); 1318 1319 for ( U32 i = 0; i < hitSegments.size(); i++ ) 1320 { 1321 const RiverSegment* segment = hitSegments[i]; 1322 for ( U32 k = 0; k < 2; k++ ) 1323 { 1324 // gIdxArray[0] gives us the top plane (see table definition). 1325 U32 idx0 = gIdxArray[0][k][0]; 1326 U32 idx1 = gIdxArray[0][k][1]; 1327 U32 idx2 = gIdxArray[0][k][2]; 1328 1329 const Point3F &v0 = (*segment)[idx0]; 1330 const Point3F &v1 = (*segment)[idx1]; 1331 const Point3F &v2 = (*segment)[idx2]; 1332 1333 // Add vertices to poly list. 1334 U32 i0 = polyList->addPoint(v0); 1335 polyList->addPoint(v1); 1336 polyList->addPoint(v2); 1337 1338 // Add plane between them. 1339 polyList->begin(0, 0); 1340 polyList->vertex(i0); 1341 polyList->vertex(i0+1); 1342 polyList->vertex(i0+2); 1343 polyList->plane(i0, i0+1, i0+2); 1344 polyList->end(); 1345 } 1346 } 1347 1348 return true; 1349} 1350 1351F32 River::getWaterCoverage( const Box3F &worldBox ) const 1352{ 1353 PROFILE_SCOPE( River_GetWaterCoverage ); 1354 1355 if ( !mWorldBox.isOverlapped(worldBox) ) 1356 return 0.0f; 1357 1358 Point3F bottomPnt = worldBox.getCenter(); 1359 bottomPnt.z = worldBox.minExtents.z; 1360 1361 F32 farthest = 0.0f; 1362 1363 for ( U32 i = 0; i < mSegments.size(); i++ ) 1364 { 1365 const RiverSegment &segment = mSegments[i]; 1366 1367 if ( !segment.worldbounds.isOverlapped(worldBox) ) 1368 continue; 1369 1370 if ( !segment.intersectBox( worldBox ) ) 1371 continue; 1372 1373 F32 distance = segment.distanceToSurface( bottomPnt ); 1374 1375 if ( distance > farthest ) 1376 farthest = distance; 1377 } 1378 1379 F32 height = worldBox.maxExtents.z - worldBox.minExtents.z; 1380 F32 distance = mClampF( farthest, 0.0f, height ); 1381 F32 coverage = distance / height; 1382 1383 return coverage; 1384} 1385 1386F32 River::getSurfaceHeight( const Point2F &pos ) const 1387{ 1388 PROFILE_SCOPE( River_GetSurfaceHeight ); 1389 1390 Point3F origin( pos.x, pos.y, mWorldBox.maxExtents.z ); 1391 Point3F direction(0,0,-1); 1392 U32 nodeIdx; 1393 Point3F collisionPnt; 1394 1395 if ( !collideRay( origin, direction, &nodeIdx, &collisionPnt ) ) 1396 return -1.0f; 1397 1398 return collisionPnt.z; 1399} 1400 1401VectorF River::getFlow( const Point3F &pos ) const 1402{ 1403 PROFILE_SCOPE( River_GetFlow ); 1404 1405 for ( U32 i = 0; i < mSegments.size(); i++ ) 1406 { 1407 const RiverSegment &segment = mSegments[i]; 1408 1409 if ( !segment.containsPoint(pos) ) 1410 continue; 1411 1412 VectorF flow = segment.slice0->p1 - segment.slice1->p1; 1413 flow.normalize(); 1414 flow *= mFlowMagnitude; 1415 1416 return flow; 1417 } 1418 1419 return VectorF::Zero; 1420} 1421 1422void River::onReflectionInfoChanged() 1423{ 1424 /* 1425 if ( isClientObject() && GFX->getPixelShaderVersion() >= 1.4 ) 1426 { 1427 if ( mFullReflect ) 1428 REFLECTMGR->registerObject( this, ReflectDelegate( this, &River::updateReflection ), mReflectPriority, mReflectMaxRateMs, mReflectMaxDist ); 1429 else 1430 { 1431 REFLECTMGR->unregisterObject( this ); 1432 mReflectTex = NULL; 1433 } 1434 } 1435 */ 1436} 1437 1438void River::_regenerate() 1439{ 1440 if ( mNodes.size() == 0 ) 1441 return; 1442 1443 const Point3F &nodePt = mNodes.first().point; 1444 1445 MatrixF mat( true ); 1446 mat.setPosition( nodePt ); 1447 Parent::setTransform( mat ); 1448 1449 _generateSlices(); 1450} 1451 1452void River::_generateSlices() 1453{ 1454 if ( mNodes.size() < 2 ) 1455 return; 1456 1457 U32 nodeCount = mNodes.size(); 1458 RiverSplineNode *splineNodes = new RiverSplineNode[nodeCount]; 1459 1460 for ( U32 i = 0; i < nodeCount; i++ ) 1461 { 1462 const RiverNode &node = mNodes[i]; 1463 splineNodes[i].x = node.point.x; 1464 splineNodes[i].y = node.point.y; 1465 splineNodes[i].z = node.point.z; 1466 splineNodes[i].width = node.width; 1467 splineNodes[i].depth = node.depth; 1468 splineNodes[i].normal = node.normal; 1469 } 1470 1471 CatmullRom<RiverSplineNode> spline; 1472 spline.initialize( nodeCount, splineNodes ); 1473 delete [] splineNodes; 1474 1475 mSlices.clear(); 1476 1477 for ( U32 i = 1; i < nodeCount; i++ ) 1478 { 1479 F32 t0 = spline.getTime( i-1 ); 1480 F32 t1 = spline.getTime( i ); 1481 1482 F32 segLength = spline.arcLength( t0, t1 ); 1483 1484 U32 numSegments = mCeil( segLength / mMetersPerSegment ); 1485 numSegments = getMax( numSegments, (U32)1 ); 1486 F32 tstep = ( t1 - t0 ) / numSegments; 1487 1488 //AssertFatal( numSegments > 0, "River::_generateSlices, got zero segments!" ); 1489 1490 U32 startIdx = 0; 1491 U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; 1492 1493 for ( U32 j = startIdx; j < endIdx; j++ ) 1494 { 1495 F32 t = t0 + tstep * j; //spline.findParameterByDistance( 0.0f, i * segLen ); 1496 RiverSplineNode val = spline.evaluate(t); 1497 1498 RiverSlice slice; 1499 slice.p1.set( val.x, val.y, val.z ); 1500 slice.uvec.set( 0,0,1 ); 1501 slice.width = val.width; 1502 slice.depth = val.depth; 1503 slice.parentNodeIdx = i-1; 1504 slice.normal = val.normal; 1505 slice.normal.normalize(); 1506 mSlices.push_back( slice ); 1507 } 1508 } 1509 1510 // 1511 // Calculate fvec and rvec for all slices 1512 // 1513 RiverSlice *pSlice = NULL; 1514 RiverSlice *pNextSlice = NULL; 1515 1516 // Must do the first slice outside the loop 1517 { 1518 pSlice = &mSlices[0]; 1519 pNextSlice = &mSlices[1]; 1520 pSlice->fvec = pNextSlice->p1 - pSlice->p1; 1521 pSlice->fvec.normalize(); 1522 pSlice->rvec = mCross( pSlice->fvec, pSlice->normal ); 1523 pSlice->rvec.normalize(); 1524 pSlice->uvec = mCross( pSlice->rvec, pSlice->fvec ); 1525 pSlice->uvec.normalize(); 1526 pSlice->rvec = mCross( pSlice->fvec, pSlice->uvec ); 1527 pSlice->rvec.normalize(); 1528 } 1529 1530 for ( U32 i = 1; i < mSlices.size() - 1; i++ ) 1531 { 1532 pSlice = &mSlices[i]; 1533 pNextSlice = &mSlices[i+1]; 1534 1535 pSlice->fvec = pNextSlice->p1 - pSlice->p1; 1536 pSlice->fvec.normalize(); 1537 1538 pSlice->rvec = mCross( pSlice->fvec, pSlice->normal ); 1539 pSlice->rvec.normalize(); 1540 1541 pSlice->uvec = mCross( pSlice->rvec, pSlice->fvec ); 1542 pSlice->uvec.normalize(); 1543 1544 pSlice->rvec = mCross( pSlice->fvec, pSlice->uvec ); 1545 pSlice->rvec.normalize(); 1546 } 1547 1548 // Must do the last slice outside the loop 1549 { 1550 RiverSlice *lastSlice = &mSlices[mSlices.size()-1]; 1551 RiverSlice *prevSlice = &mSlices[mSlices.size()-2]; 1552 1553 lastSlice->fvec = prevSlice->fvec; 1554 1555 lastSlice->rvec = mCross( lastSlice->fvec, lastSlice->normal ); 1556 lastSlice->rvec.normalize(); 1557 1558 lastSlice->uvec = mCross( lastSlice->rvec, lastSlice->fvec ); 1559 lastSlice->uvec.normalize(); 1560 1561 lastSlice->rvec = mCross( lastSlice->fvec, lastSlice->uvec ); 1562 lastSlice->rvec.normalize(); 1563 } 1564 1565 1566 // 1567 // Calculate p0/p2/pb0/pb2 for all slices 1568 // 1569 for ( U32 i = 0; i < mSlices.size(); i++ ) 1570 { 1571 RiverSlice *slice = &mSlices[i]; 1572 slice->p0 = slice->p1 - slice->rvec * slice->width * 0.5f; 1573 slice->p2 = slice->p1 + slice->rvec * slice->width * 0.5f; 1574 slice->pb0 = slice->p0 - slice->uvec * slice->depth; 1575 slice->pb2 = slice->p2 - slice->uvec * slice->depth; 1576 } 1577 1578 // Generate the object/world bounds 1579 Box3F box; 1580 for ( U32 i = 0; i < mSlices.size(); i++ ) 1581 { 1582 const RiverSlice &slice = mSlices[i]; 1583 1584 if ( i == 0 ) 1585 { 1586 box.minExtents = slice.p0; 1587 box.maxExtents = slice.p2; 1588 box.extend( slice.pb0 ); 1589 box.extend( slice.pb2 ); 1590 } 1591 else 1592 { 1593 box.extend( slice.p0 ); 1594 box.extend( slice.p2 ); 1595 box.extend( slice.pb0 ); 1596 box.extend( slice.pb2 ); 1597 } 1598 } 1599 1600 mWorldBox = box; 1601 //mObjBox.minExtents -= pos; 1602 //mObjBox.maxExtents -= pos; 1603 resetObjectBox(); 1604 1605 // Make sure we are in the correct bins given our world box. 1606 if( getSceneManager() != NULL ) 1607 getSceneManager()->notifyObjectDirty( this ); 1608 1609 _generateSegments(); 1610} 1611 1612void River::_generateSegments() 1613{ 1614 mSegments.clear(); 1615 1616 for ( U32 i = 0; i < mSlices.size() - 1; i++ ) 1617 { 1618 RiverSegment seg( &mSlices[i], &mSlices[i+1] ); 1619 1620 mSegments.push_back( seg ); 1621 } 1622 1623 /* 1624 #ifdef TORQUE_DEBUG 1625 1626 for ( U32 i = 0; i < mSegments.size(); i++ ) 1627 { 1628 const RiverSegment &segment = mSegments[i]; 1629 PlaneF normal0 = MathUtils::mTriangleNormal( segment.slice0->p0, segment.slice1->p0, segment.slice1->p2 ); 1630 PlaneF normal1 = MathUtils::mTriangleNormal( segment.slice0->p0, segment.slice1->p2, segment.slice0->p2 ); 1631 AssertFatal( true || normal0 != normal1, "River::generateSegments, segment is not coplanar!" ); 1632 } 1633 1634 #endif // TORQUE_DEBUG 1635 */ 1636 1637 // We have to go back and generate normals for each slice 1638 // to be used in calculation of the reflect plane. 1639 // The slice-normal we calculate are relative to the surface normal 1640 // of the segments adjacent to the slice. 1641 /* 1642 if ( mSlices.size() >= 2 ) 1643 { 1644 mSlices[0].normal = mSegments[0].getSurfaceNormal(); 1645 for ( U32 i = 1; i < mSlices.size() - 1; i++ ) 1646 { 1647 mSlices[i].normal = ( mSegments[i-1].getSurfaceNormal() + mSegments[i].getSurfaceNormal() ) / 2; 1648 } 1649 mSlices.last().normal = mSegments.last().getSurfaceNormal(); 1650 } 1651 */ 1652 1653 _generateVerts(); 1654} 1655 1656void River::_generateVerts() 1657{ 1658 if ( isServerObject() ) 1659 return; 1660 1661 // These will depend on the level of subdivision per segment 1662 // calculated below. 1663 mHighVertCount = 0; 1664 mHighTriangleCount = 0; 1665 1666 // Calculate the number of row/column subdivisions per each 1667 // RiverSegment. 1668 1669 F32 greatestWidth = 0.1f; 1670 for ( U32 i = 0; i < mNodes.size(); i++ ) 1671 { 1672 RiverNode &node = mNodes[i]; 1673 if ( node.width > greatestWidth ) 1674 greatestWidth = node.width; 1675 } 1676 1677 mColumnCount = mCeil( greatestWidth / mMaxDivisionSize ); 1678 1679 for ( U32 i = 0; i < mSegments.size(); i++ ) 1680 { 1681 RiverSegment &segment = mSegments[i]; 1682 const RiverSlice *slice = segment.slice0; 1683 const RiverSlice *nextSlice = segment.slice1; 1684 1685 // Calculate the size of divisions in the forward direction ( p00 -> p01 ) 1686 F32 segLength = (nextSlice->p1 - slice->p1).len(); 1687 1688 // A division count of one is actually NO subdivision, 1689 // the segment corners are the only verts in this segment. 1690 U32 numRows = 1; 1691 1692 if ( segLength > 0.0f ) 1693 numRows = mCeil( segLength / mMaxDivisionSize ); 1694 1695 // The problem with calculating num columns per segment is 1696 // two adjacent - high lod segments of different width can have 1697 // verts that don't line up! So even though RiverSegment HAS a 1698 // column data member we initialize all segments in the river to 1699 // the same (River::mColumnCount) 1700 1701 // Calculate the size of divisions in the right direction ( p00 -> p10 ) 1702 // F32 segWidth = ( ( p11 - p01 ).len() + ( p10 - p00 ).len() ) * 0.5f; 1703 1704 // U32 numColumns = 5; 1705 //F32 columnSize = segWidth / numColumns; 1706 1707 //while ( columnSize > mMaxDivisionSize ) 1708 //{ 1709 // numColumns++; 1710 // columnSize = segWidth / numColumns; 1711 //} 1712 1713 // Save the calculated numb of columns / rows for this segment. 1714 segment.columns = mColumnCount; 1715 segment.rows = numRows; 1716 1717 // Save the corresponding number of verts/prims 1718 segment.numVerts = ( 1 + mColumnCount ) * ( 1 + numRows ); 1719 segment.numTriangles = mColumnCount * numRows * 2; 1720 1721 mHighVertCount += segment.numVerts; 1722 mHighTriangleCount += segment.numTriangles; 1723 } 1724 1725 // Number of low detail verts/prims. 1726 mLowVertCount = mSlices.size() * 2; 1727 mLowTriangleCount = mSegments.size() * 2; 1728 1729 // Allocate the low detail VertexBuffer, 1730 // this will stay in memory and will never need to change. 1731 mVB_low.set( GFX, mLowVertCount, GFXBufferTypeStatic ); 1732 1733 GFXWaterVertex *lowVertPtr = mVB_low.lock(); 1734 U32 vertCounter = 0; 1735 1736 // The texCoord.y value start/end for a segment 1737 // as we loop through them. 1738 F32 textCoordV = 0; 1739 1740 // 1741 // Fill the low-detail VertexBuffer 1742 // 1743 for ( U32 i = 0; i < mSlices.size(); i++ ) 1744 { 1745 RiverSlice &slice = mSlices[i]; 1746 1747 lowVertPtr->point = slice.p0; 1748 lowVertPtr->normal = slice.normal; 1749 lowVertPtr->undulateData.set( -slice.width*0.5f, textCoordV ); 1750 lowVertPtr->horizonFactor.set( 0, 0, 0, 0 ); 1751 lowVertPtr++; 1752 vertCounter++; 1753 1754 lowVertPtr->point = slice.p2; 1755 lowVertPtr->normal = slice.normal; 1756 lowVertPtr->undulateData.set( slice.width*0.5f, textCoordV ); 1757 lowVertPtr->horizonFactor.set( 0, 0, 0, 0 ); 1758 lowVertPtr++; 1759 vertCounter++; 1760 1761 // Save this so we can get it later. 1762 slice.texCoordV = textCoordV; 1763 1764 if ( i < mSlices.size() - 1 ) 1765 { 1766 // Increment the textCoordV for the next slice. 1767 F32 segLen = ( mSlices[i+1].p1 - slice.p1 ).len(); 1768 textCoordV += segLen; 1769 } 1770 } 1771 1772 AssertFatal( vertCounter == mLowVertCount, "River, wrote incorrect number of verts in mBV_low!" ); 1773 1774 // Unlock the low-detail VertexBuffer, we are done filling it. 1775 mVB_low.unlock(); 1776 1777 // 1778 // Create the low-detail prim buffer(s) 1779 // 1780 mPB_low.set( GFX, mLowTriangleCount * 3, mLowTriangleCount, GFXBufferTypeStatic ); 1781 1782 U16 *lowIdxBuff; 1783 mPB_low.lock(&lowIdxBuff); 1784 U32 curLowIdx = 0; 1785 1786 // Temporaries to hold indices for the corner points of a quad. 1787 U32 p00, p01, p11, p10; 1788 1789 U32 offset = 0; 1790 1791 // Fill the low-detail PrimitiveBuffer 1792 for ( U32 i = 0; i < mSegments.size(); i++ ) 1793 { 1794 //const RiverSegment &segment = mSegments[i]; 1795 1796 // Two triangles formed by the corner points of this segment 1797 // into the the low detail primitive buffer. 1798 p00 = offset; 1799 p01 = p00 + 2; 1800 p11 = p01 + 1; 1801 p10 = p00 + 1; 1802 1803 // Upper-Left triangle 1804 lowIdxBuff[curLowIdx] = p00; 1805 curLowIdx++; 1806 lowIdxBuff[curLowIdx] = p01; 1807 curLowIdx++; 1808 lowIdxBuff[curLowIdx] = p11; 1809 curLowIdx++; 1810 1811 // Lower-Right Triangle 1812 lowIdxBuff[curLowIdx] = p00; 1813 curLowIdx++; 1814 lowIdxBuff[curLowIdx] = p11; 1815 curLowIdx++; 1816 lowIdxBuff[curLowIdx] = p10; 1817 curLowIdx++; 1818 1819 offset += 2; 1820 } 1821 1822 AssertFatal( curLowIdx == mLowTriangleCount * 3, "River, wrote incorrect number of indices in mPB_low!" ); 1823 1824 // Unlock the low-detail PrimitiveBuffer, we are done filling it. 1825 mPB_low.unlock(); 1826} 1827 1828bool River::getClosestNode( const Point3F &pos, U32 &idx ) const 1829{ 1830 F32 closestDist = F32_MAX; 1831 1832 for ( U32 i = 0; i < mNodes.size(); i++ ) 1833 { 1834 F32 dist = ( mNodes[i].point - pos ).len(); 1835 if ( dist < closestDist ) 1836 { 1837 closestDist = dist; 1838 idx = i; 1839 } 1840 } 1841 1842 return closestDist != F32_MAX; 1843} 1844 1845bool River::containsPoint( const Point3F &worldPos, U32 *nodeIdx ) const 1846{ 1847 // If point isn't in the world box, 1848 // it's definitely not in the River. 1849 //if ( !getWorldBox().isContained( worldPos ) ) 1850 // return false; 1851 1852 // Look through all edges, does the polygon 1853 // formed from adjacent edge's contain the worldPos? 1854 for ( U32 i = 0; i < mSegments.size(); i++ ) 1855 { 1856 const RiverSegment &segment = mSegments[i]; 1857 1858 if ( segment.containsPoint( worldPos ) ) 1859 { 1860 if ( nodeIdx ) 1861 *nodeIdx = i; 1862 return true; 1863 } 1864 } 1865 1866 return false; 1867} 1868 1869F32 River::distanceToSurface( const Point3F &pnt, U32 segmentIdx ) 1870{ 1871 return mSegments[segmentIdx].distanceToSurface( pnt ); 1872} 1873 1874bool River::collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx, Point3F *collisionPnt ) const 1875{ 1876 Point3F p0 = origin; 1877 Point3F p1 = origin + direction * 2000.0f; 1878 1879 // If the line segment does not collide with the river's world box, 1880 // it definitely does not collide with any part of the river. 1881 if ( !getWorldBox().collideLine( p0, p1 ) ) 1882 return false; 1883 1884 if ( mSlices.size() < 2 ) 1885 return false; 1886 1887 MathUtils::Quad quad; 1888 MathUtils::Ray ray; 1889 F32 t; 1890 1891 // Check each river segment (formed by a pair of slices) for collision 1892 // with the line segment. 1893 for ( U32 i = 0; i < mSlices.size() - 1; i++ ) 1894 { 1895 const RiverSlice &slice0 = mSlices[i]; 1896 const RiverSlice &slice1 = mSlices[i+1]; 1897 1898 // For simplicities sake we will only test for collision between the 1899 // line segment and the Top face of the river segment. 1900 1901 // Clockwise starting with the leftmost/closest point. 1902 quad.p00 = slice0.p0; 1903 quad.p01 = slice1.p0; 1904 quad.p11 = slice1.p2; 1905 quad.p10 = slice0.p2; 1906 1907 ray.origin = origin; 1908 ray.direction = direction; 1909 1910 // NOTE: 1911 // mRayQuadCollide is designed for a "real" quad in which all four points 1912 // are coplanar which is actually not the case here. The more twist 1913 // and turn in-between two neighboring river slices the more incorrect 1914 // this calculation will be. 1915 1916 if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) ) 1917 { 1918 if ( nodeIdx ) 1919 *nodeIdx = slice0.parentNodeIdx; 1920 if ( collisionPnt ) 1921 *collisionPnt = ray.origin + ray.direction * t; 1922 return true; 1923 } 1924 } 1925 1926 return false; 1927} 1928 1929Point3F River::getNodePosition( U32 idx ) const 1930{ 1931 if ( mNodes.size() - 1 < idx ) 1932 return Point3F(); 1933 1934 return mNodes[idx].point; 1935} 1936 1937void River::setNodePosition( U32 idx, const Point3F &pos ) 1938{ 1939 if ( mNodes.size() - 1 < idx ) 1940 return; 1941 1942 mNodes[idx].point = pos; 1943 1944 regenerate(); 1945 1946 setMaskBits( NodeMask | RegenMask ); 1947} 1948 1949U32 River::addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ) 1950{ 1951 U32 idx = _addNode( pos, width, depth, normal ); 1952 1953 regenerate(); 1954 1955 setMaskBits( NodeMask | RegenMask ); 1956 1957 return idx; 1958} 1959 1960U32 River::insertNode(const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx) 1961{ 1962 U32 ret = _insertNode( pos, width, depth, normal, idx ); 1963 1964 regenerate(); 1965 1966 setMaskBits( NodeMask | RegenMask ); 1967 1968 return ret; 1969} 1970 1971void River::setNode(const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx) 1972{ 1973 if ( mNodes.size() - 1 < idx ) 1974 return; 1975 1976 RiverNode &node = mNodes[idx]; 1977 node.point = pos; 1978 node.width = width; 1979 node.depth = depth; 1980 node.normal = normal; 1981 1982 regenerate(); 1983 1984 setMaskBits( NodeMask | RegenMask ); 1985} 1986 1987void River::setNodeWidth( U32 idx, F32 meters ) 1988{ 1989 meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH ); 1990 1991 if ( mNodes.size() - 1 < idx ) 1992 return; 1993 1994 mNodes[idx].width = meters; 1995 _regenerate(); 1996 1997 setMaskBits( RegenMask | NodeMask ); 1998} 1999 2000void River::setNodeHeight( U32 idx, F32 height ) 2001{ 2002 if ( mNodes.size() - 1 < idx ) 2003 return; 2004 2005 mNodes[idx].point.z = height; 2006 _regenerate(); 2007 2008 setMaskBits( RegenMask | NodeMask ); 2009} 2010 2011F32 River::getNodeWidth( U32 idx ) const 2012{ 2013 if ( mNodes.size() - 1 < idx ) 2014 return -1.0f; 2015 2016 return mNodes[idx].width; 2017} 2018 2019void River::setNodeDepth( U32 idx, F32 meters ) 2020{ 2021 meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH ); 2022 2023 if ( mNodes.size() - 1 < idx ) 2024 return; 2025 2026 mNodes[idx].depth = meters; 2027 _regenerate(); 2028 setMaskBits( RiverMask | RegenMask | NodeMask ); 2029} 2030 2031void River::setNodeNormal( U32 idx, const VectorF &normal ) 2032{ 2033 if ( mNodes.size() - 1 < idx ) 2034 return; 2035 2036 mNodes[idx].normal = normal; 2037 2038 regenerate(); 2039 2040 setMaskBits( NodeMask | RegenMask ); 2041} 2042 2043F32 River::getNodeDepth( U32 idx ) const 2044{ 2045 if ( mNodes.size() - 1 < idx ) 2046 return -1.0f; 2047 2048 return mNodes[idx].depth; 2049} 2050 2051VectorF River::getNodeNormal( U32 idx ) const 2052{ 2053 if ( mNodes.size() - 1 < idx ) 2054 return VectorF::Zero; 2055 2056 return mNodes[idx].normal; 2057} 2058 2059MatrixF River::getNodeTransform( U32 idx ) const 2060{ 2061 MatrixF mat(true); 2062 2063 if ( mNodes.size() - 1 < idx ) 2064 return mat; 2065 2066 bool hasNext = idx + 1 < mNodes.size(); 2067 bool hasPrev = (S32)idx - 1 >= 0; 2068 2069 const RiverNode &node = mNodes[idx]; 2070 2071 VectorF fvec( 0, 1, 0 ); 2072 2073 if ( hasNext ) 2074 { 2075 fvec = mNodes[idx+1].point - node.point; 2076 fvec.normalizeSafe(); 2077 } 2078 else if ( hasPrev ) 2079 { 2080 fvec = node.point - mNodes[idx-1].point; 2081 fvec.normalizeSafe(); 2082 } 2083 else 2084 fvec = mPerp( node.normal ); 2085 2086 if ( fvec.isZero() ) 2087 fvec = mPerp( node.normal ); 2088 2089 F32 dot = mDot( fvec, node.normal ); 2090 if ( dot < -0.9f || dot > 0.9f ) 2091 fvec = mPerp( node.normal ); 2092 2093 VectorF rvec = mCross( fvec, node.normal ); 2094 if ( rvec.isZero() ) 2095 rvec = mPerp( fvec ); 2096 rvec.normalize(); 2097 2098 fvec = mCross( node.normal, rvec ); 2099 fvec.normalize(); 2100 2101 mat.setColumn( 0, rvec ); 2102 mat.setColumn( 1, fvec ); 2103 mat.setColumn( 2, node.normal ); 2104 mat.setColumn( 3, node.point ); 2105 2106 AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!"); 2107 2108 return mat; 2109} 2110 2111void River::deleteNode( U32 idx ) 2112{ 2113 if ( mNodes.size() - 1 < idx ) 2114 return; 2115 2116 mNodes.erase(idx); 2117 _regenerate(); 2118 2119 setMaskBits( RegenMask | NodeMask ); 2120} 2121 2122void River::buildNodesFromList( RiverNodeList* list ) 2123{ 2124 mNodes.clear(); 2125 2126 for (U32 i=0; i<list->mPositions.size(); ++i) 2127 { 2128 _addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->mNormals[i] ); 2129 } 2130 2131 _regenerate(); 2132} 2133 2134void River::_makeRenderBatches( const Point3F &cameraPos ) 2135{ 2136 // Loop through each segment to determine if it is either 1 [not visible], 2 [high LOD], 3 [low LOD] 2137 2138 mHighLODBatches.clear(); 2139 mLowLODBatches.clear(); 2140 2141 // Keeps track of what we batch type we are currently collecting. 2142 // -1 is uninitialized, 0 is low detail, 1 is high detail 2143 S32 lastDetail = -1; 2144 bool highDetail; 2145 2146 U32 startSegmentIdx = -1; 2147 U32 endSegmentIdx = 0; 2148 2149 F32 lodDistSquared = mLodDistance * mLodDistance; 2150 2151 for ( U32 i = 0; i < mSegments.size(); i++ ) 2152 { 2153 const RiverSegment &segment = mSegments[i]; 2154 const RiverSlice *slice = segment.slice0; 2155 const RiverSlice *nextSlice = segment.slice1; 2156 2157 // TODO: add bounds BoxF to RiverSegment 2158 const bool isVisible = true; //frustum.intersects( segment.bounds ); 2159 if ( isVisible ) 2160 { 2161 F32 dist0 = MathUtils::mTriangleDistance( slice->p0, nextSlice->p0, nextSlice->p2, cameraPos ); 2162 F32 dist1 = MathUtils::mTriangleDistance( slice->p0, nextSlice->p2, slice->p2, cameraPos ); 2163 2164 F32 dist = getMin( dist0, dist1 ); 2165 highDetail = ( dist < lodDistSquared ); 2166 if ( (highDetail && lastDetail == 0) || 2167 (!highDetail && lastDetail == 1) ) 2168 { 2169 // We hit a segment with a different lod than the previous. 2170 // Save what we have so far... 2171 2172 RiverRenderBatch batch; 2173 2174 batch.startSegmentIdx = startSegmentIdx; 2175 batch.endSegmentIdx = endSegmentIdx; 2176 2177 if ( lastDetail == 0 ) 2178 { 2179 mLowLODBatches.push_back( batch ); 2180 } 2181 else 2182 { 2183 mHighLODBatches.push_back( batch ); 2184 } 2185 2186 // Reset the batching 2187 startSegmentIdx = -1; 2188 lastDetail = -1; 2189 i--; 2190 2191 continue; 2192 } 2193 2194 // If this is the start of a set of batches. 2195 if ( startSegmentIdx == -1 ) 2196 { 2197 endSegmentIdx = startSegmentIdx = i; 2198 lastDetail = ( highDetail ) ? 1 : 0; 2199 } 2200 2201 // Else we're extending the end batch index. 2202 else 2203 ++endSegmentIdx; 2204 2205 // If this isn't the last batch then continue. 2206 if ( i < mSegments.size()-1 ) 2207 continue; 2208 } 2209 2210 // If we still don't have a start batch skip. 2211 if ( startSegmentIdx == -1 ) 2212 continue; 2213 2214 // Save what we have so far... 2215 2216 RiverRenderBatch batch; 2217 2218 batch.startSegmentIdx = startSegmentIdx; 2219 batch.endSegmentIdx = endSegmentIdx; 2220 2221 if ( lastDetail == 0 ) 2222 { 2223 mLowLODBatches.push_back( batch ); 2224 } 2225 else 2226 { 2227 mHighLODBatches.push_back( batch ); 2228 } 2229 2230 // Reset the batching. 2231 startSegmentIdx = -1; 2232 lastDetail = -1; 2233 } 2234} 2235 2236void River::_makeHighLODBuffers() 2237{ 2238 PROFILE_SCOPE( River_makeHighLODBuffers ); 2239 2240 // This is the number of verts/triangles for ALL high lod batches combined. 2241 // eg. the size for the buffers. 2242 U32 numVerts = 0; 2243 U32 numTriangles = 0; 2244 2245 for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) 2246 { 2247 RiverRenderBatch &batch = mHighLODBatches[i]; 2248 2249 for ( U32 j = batch.startSegmentIdx; j <= batch.endSegmentIdx; j++ ) 2250 { 2251 const RiverSegment &segment = mSegments[j]; 2252 2253 numTriangles += segment.numTriangles; 2254 numVerts += segment.numVerts; 2255 } 2256 } 2257 2258 if ( numVerts > GFX_MAX_DYNAMIC_VERTS || numTriangles * 3 > GFX_MAX_DYNAMIC_INDICES ) 2259 { 2260 mVB_high = NULL; 2261 mPB_high = NULL; 2262 return; 2263 } 2264 2265 mHighTriangleCount = numTriangles; 2266 mHighVertCount = numVerts; 2267 2268 mVB_high.set( GFX, numVerts, GFXBufferTypeVolatile ); 2269 GFXWaterVertex *vertPtr = mVB_high.lock(); 2270 U32 vertCounter = 0; 2271 2272 // NOTE: this will break if different segments have different number 2273 // of columns, but that will also cause T-junction triangles so just don't 2274 // do that. 2275 2276 // For each batch, loop through the segments contained by 2277 // that batch, and add their verts to the buffer. 2278 for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) 2279 { 2280 RiverRenderBatch &batch = mHighLODBatches[i]; 2281 2282 batch.startVert = vertCounter; 2283 batch.vertCount = 0; 2284 2285 VectorF lastNormal(0,0,1); 2286 2287 for ( U32 j = batch.startSegmentIdx; j <= batch.endSegmentIdx; j++ ) 2288 { 2289 // Add the verts for this segment to the buffer. 2290 RiverSegment &segment = mSegments[j]; 2291 2292 BiSqrToQuad3D squareToQuad( segment.getP00(), 2293 segment.getP10(), 2294 segment.getP11(), 2295 segment.getP01() ); 2296 2297 // We are duplicating the last row of verts in a segment on 2298 // the first row of the next segment. This could be optimized but 2299 // shouldn't cause any problems. 2300 2301 VectorF normal = segment.getSurfaceNormal(); 2302 2303 for ( U32 k = 0; k <= segment.rows; k++ ) 2304 { 2305 VectorF vertNormal = ( k == 0 && j != batch.startSegmentIdx ) ? lastNormal : normal; 2306 2307 F32 rowLen = mLerp( segment.slice0->width, segment.slice1->width, (F32)k / (F32)segment.rows ); 2308 2309 for ( U32 l = 0; l <= segment.columns; l++ ) 2310 { 2311 // We are generating a "row" of verts along the forwardDivision 2312 // Each l iteration is a step to the right along with row. 2313 2314 Point2F uv( (F32)l / (F32)segment.columns, (F32)k / (F32)segment.rows ); 2315 2316 Point3F pnt = squareToQuad.transform( uv ); 2317 2318 // Assign the Vert 2319 vertPtr->point = pnt; 2320 vertPtr->normal = vertNormal; 2321 vertPtr->undulateData.x = ( uv.x - 0.5f ) * rowLen; 2322 vertPtr->undulateData.y = ( segment.TexCoordEnd() - segment.TexCoordStart() ) * uv.y + segment.TexCoordStart(); 2323 vertPtr->horizonFactor.set( 0, 0, 0, 0 ); 2324 2325 vertPtr++; 2326 vertCounter++; 2327 batch.vertCount++; 2328 } 2329 } 2330 2331 lastNormal = normal; 2332 } 2333 } 2334 2335 AssertFatal( vertCounter == mHighVertCount, "River, wrote incorrect number of verts in mVB_high" ); 2336 2337 mVB_high.unlock(); 2338 2339 // 2340 // Do the high lod primitive buffer. 2341 // 2342 2343 mPB_high.set( GFX, numTriangles * 3, numTriangles, GFXBufferTypeVolatile ); 2344 U16 *idxBuff; 2345 mPB_high.lock(&idxBuff); 2346 U32 curIdx = 0; 2347 2348 U32 batchOffset = 0; 2349 2350 // For each high lod batch, we must add indices to the buffer 2351 // for each segment it contains ( and the count will depend on 2352 // the division level columns/rows for each segment ). 2353 2354 // Temporaries for holding the indices of a quad 2355 U32 p00, p01, p11, p10; 2356 2357 for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) 2358 { 2359 RiverRenderBatch &batch = mHighLODBatches[i]; 2360 2361 batch.indexCount = 0; 2362 batch.triangleCount = 0; 2363 batch.startIndex = curIdx; 2364 2365 U32 temp = 0; 2366 U32 segmentOffset = 0; 2367 2368 for ( U32 j = batch.startSegmentIdx; j <= batch.endSegmentIdx; j++ ) 2369 { 2370 const RiverSegment &segment = mSegments[j]; 2371 2372 // Loop through all divisions adding the indices to the 2373 // high detail primitive buffer. 2374 for ( U32 k = 0; k < segment.rows; k++ ) 2375 { 2376 for ( U32 l = 0; l < segment.columns; l++ ) 2377 { 2378 // The indices for this quad. 2379 p00 = batchOffset + segmentOffset + l + k * ( segment.columns + 1 ); 2380 p01 = p00 + segment.columns + 1; 2381 p11 = p01 + 1; 2382 p10 = p00 + 1; 2383 2384 AssertFatal( p00 <= mHighTriangleCount * 3, "River, bad draw call!" ); 2385 AssertFatal( p01 <= mHighTriangleCount * 3, "River, bad draw call!" ); 2386 AssertFatal( p11 <= mHighTriangleCount * 3, "River, bad draw call!" ); 2387 AssertFatal( p10 <= mHighTriangleCount * 3, "River, bad draw call!" ); 2388 2389 // Upper-Left triangle 2390 idxBuff[curIdx] = p00; 2391 curIdx++; 2392 idxBuff[curIdx] = p01; 2393 curIdx++; 2394 idxBuff[curIdx] = p11; 2395 curIdx++; 2396 2397 // Lower-Right Triangle 2398 idxBuff[curIdx] = p00; 2399 curIdx++; 2400 idxBuff[curIdx] = p11; 2401 curIdx++; 2402 idxBuff[curIdx] = p10; 2403 curIdx++; 2404 2405 batch.indexCount += 6; 2406 batch.triangleCount += 2; 2407 } 2408 } 2409 2410 // Increment the sliceOffset by the number of verts 2411 // used by this segment. So the next segment will index 2412 // into new verts. 2413 segmentOffset += ( segment.columns + 1 ) * ( segment.rows + 1 ); 2414 temp += ( segment.columns + 1 ) * ( segment.rows + 1 ); 2415 } 2416 2417 batchOffset += temp; 2418 } 2419 2420 // Unlock the PrimitiveBuffer, we are done filling it. 2421 mPB_high.unlock(); 2422} 2423 2424U32 River::_addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ) 2425{ 2426 mNodes.increment(); 2427 RiverNode &node = mNodes.last(); 2428 2429 node.point = pos; 2430 node.width = width; 2431 node.depth = depth; 2432 node.normal = normal; 2433 2434 setMaskBits( NodeMask | RegenMask ); 2435 2436 return mNodes.size() - 1; 2437} 2438 2439U32 River::_insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ) 2440{ 2441 U32 ret; 2442 RiverNode *node; 2443 2444 if ( idx == U32_MAX ) 2445 { 2446 mNodes.increment(); 2447 node = &mNodes.last(); 2448 ret = mNodes.size() - 1; 2449 } 2450 else 2451 { 2452 mNodes.insert( idx ); 2453 node = &mNodes[idx]; 2454 ret = idx; 2455 } 2456 2457 node->point = pos; 2458 node->depth = depth; 2459 node->width = width; 2460 node->normal = normal; 2461 2462 return ret; 2463} 2464 2465void River::setMetersPerSegment( F32 meters ) 2466{ 2467 if ( meters < MIN_METERS_PER_SEGMENT ) 2468 { 2469 Con::warnf( "River::setMetersPerSegment, specified meters (%g) is below the min meters (%g), NOT SET!", meters, MIN_METERS_PER_SEGMENT ); 2470 return; 2471 } 2472 2473 mMetersPerSegment = meters; 2474 _regenerate(); 2475 setMaskBits( RiverMask | RegenMask ); 2476} 2477 2478void River::setBatchSize( U32 size ) 2479{ 2480 // Not functional 2481 //mSegmentsPerBatch = size; 2482 //_regenerate(); 2483 //setMaskBits( RiverMask | RegenMask ); 2484} 2485 2486void River::regenerate() 2487{ 2488 _regenerate(); 2489 setMaskBits( RegenMask ); 2490} 2491 2492void River::setMaxDivisionSize( F32 meters ) 2493{ 2494 if ( meters < mMinDivisionSize ) 2495 mMaxDivisionSize = mMinDivisionSize; 2496 else 2497 mMaxDivisionSize = meters; 2498 2499 _regenerate(); 2500 setMaskBits( RiverMask | RegenMask ); 2501} 2502 2503//------------------------------------------------------------------------- 2504// Console Methods 2505//------------------------------------------------------------------------- 2506 2507DefineEngineMethod( River, regenerate, void, (),, 2508 "Intended as a helper to developers and editor scripts.\n" 2509 "Force River to recreate its geometry." 2510 ) 2511{ 2512 object->regenerate(); 2513} 2514 2515DefineEngineMethod( River, setMetersPerSegment, void, ( F32 meters ),, 2516 "Intended as a helper to developers and editor scripts.\n" 2517 "@see SegmentLength field." 2518 ) 2519{ 2520 object->setMetersPerSegment( meters ); 2521} 2522 2523DefineEngineMethod( River, setBatchSize, void, ( F32 meters ),, 2524 "Intended as a helper to developers and editor scripts.\n" 2525 "BatchSize is not currently used." 2526 ) 2527{ 2528 object->setBatchSize( meters ); 2529} 2530 2531DefineEngineMethod( River, setNodeDepth, void, ( S32 idx, F32 meters ),, 2532 "Intended as a helper to developers and editor scripts.\n" 2533 "Sets the depth in meters of a particular node." 2534 ) 2535{ 2536 object->setNodeDepth( idx, meters ); 2537} 2538 2539DefineEngineMethod( River, setMaxDivisionSize, void, ( F32 meters ),, 2540 "Intended as a helper to developers and editor scripts.\n" 2541 "@see SubdivideLength field." 2542 ) 2543{ 2544 object->setMaxDivisionSize( meters ); 2545} 2546