river.cpp

Engine/source/environment/river.cpp

More...

Classes:

Public Defines

define
MAX_NODE_DEPTH() 500.0f
define
MAX_NODE_WIDTH() 1000.0f
define
define
define
NODE_RADIUS() 15.0f

Public Variables

Public Functions

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." )

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