Torque3D Documentation / _generateds / pathManager.cpp

pathManager.cpp

Engine/source/scene/pathManager.cpp

More...

Classes:

Public Functions

ConsoleDocClass(PathManagerEvent , "@brief Class responsible <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the registration, transmission , and management " "of paths on client and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n\n</a>" "For internal use only, not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> use in TorqueScript or game <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">development\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n</a>" )
DefineEngineFunction(clearClientPaths , void , () , "" )
DefineEngineFunction(clearServerPaths , void , () , "" )

Detailed Description

Public Variables

 gClientPathManager 
bool gEditingMission 

For frame signal.

 gServerPathManager 
 MODULE_END 
 MODULE_INIT 
 MODULE_SHUTDOWN 

Public Functions

ConsoleDocClass(PathManagerEvent , "@brief Class responsible <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the registration, transmission , and management " "of paths on client and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n\n</a>" "For internal use only, not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> use in TorqueScript or game <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">development\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n</a>" )

DefineEngineFunction(clearClientPaths , void , () , "" )

DefineEngineFunction(clearServerPaths , void , () , "" )

IMPLEMENT_CO_NETEVENT_V1(PathManagerEvent )

  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 "gfx/gfxDevice.h"
 25#include "scene/pathManager.h"
 26#include "sim/netConnection.h"
 27#include "core/stream/bitStream.h"
 28#include "scene/simPath.h"
 29#include "math/mathIO.h"
 30#include "scene/sceneRenderState.h"
 31#include "scene/sceneManager.h"
 32#include "platform/profiler.h"
 33#include "core/module.h"
 34#include "console/engineAPI.h"
 35
 36extern bool gEditingMission;
 37
 38
 39namespace {
 40
 41U32 countNumBits(U32 n)
 42{
 43   U32 count = 0;
 44   while (n != 0) {
 45      n >>= 1;
 46      count++;
 47   }
 48
 49   return count ? count : 1;
 50}
 51
 52} // namespace {}
 53
 54
 55MODULE_BEGIN( PathManager )
 56
 57   MODULE_INIT
 58   {
 59      AssertFatal(gClientPathManager == NULL && gServerPathManager == NULL, "Error, already initialized the path manager!");
 60
 61      gClientPathManager = new PathManager(false);
 62      gServerPathManager = new PathManager(true);
 63   }
 64
 65   MODULE_SHUTDOWN
 66   {
 67      AssertFatal(gClientPathManager != NULL && gServerPathManager != NULL, "Error, path manager not initialized!");
 68
 69      delete gClientPathManager;
 70      gClientPathManager = NULL;
 71      delete gServerPathManager;
 72      gServerPathManager = NULL;
 73   }
 74
 75MODULE_END;
 76
 77
 78//--------------------------------------------------------------------------
 79//-------------------------------------- PathManagerEvent
 80//
 81class PathManagerEvent : public NetEvent
 82{
 83  public:
 84   U32  modifiedPath;
 85   bool clearPaths;
 86   PathManager::PathEntry path;
 87
 88  public:
 89   typedef NetEvent Parent;
 90   PathManagerEvent() : modifiedPath(0), clearPaths(false) { }
 91
 92   void pack(NetConnection*, BitStream*);
 93   void write(NetConnection*, BitStream*);
 94   void unpack(NetConnection*, BitStream*);
 95   void process(NetConnection*);
 96
 97   DECLARE_CONOBJECT(PathManagerEvent);
 98};
 99
100void PathManagerEvent::pack(NetConnection*, BitStream* stream)
101{
102   // Write out the modified path...
103   stream->write(modifiedPath);
104   stream->writeFlag(clearPaths);
105   stream->write(path.totalTime);
106   stream->write(path.looping);
107   stream->write(path.positions.size());
108
109
110   // This is here for safety. You can remove it if you want to try your luck at bigger sizes. -- BJG
111   AssertWarn(path.positions.size() < 1500/40, "Warning! Path size is pretty big - may cause packet overrun!");
112
113   // Each one of these is about 8 floats and 2 ints
114   // so we'll say it's about 40 bytes in size, which is where the 40 in the above calc comes from.
115   for (U32 j = 0; j < path.positions.size(); j++)
116   {
117      mathWrite(*stream, path.positions[j]);
118      mathWrite(*stream, path.rotations[j]);
119      stream->write(path.msToNext[j]);
120      stream->write(path.smoothingType[j]);
121   }
122}
123
124void PathManagerEvent::write(NetConnection*nc, BitStream *stream)
125{
126   pack(nc, stream);
127}
128
129void PathManagerEvent::unpack(NetConnection*, BitStream* stream)
130{
131   // Read in the modified path...
132
133   stream->read(&modifiedPath);
134   clearPaths = stream->readFlag();
135   stream->read(&path.totalTime);
136   stream->read(&path.looping);
137
138   U32 numPoints;
139   stream->read(&numPoints);
140   path.positions.setSize(numPoints);
141   path.rotations.setSize(numPoints);
142   path.msToNext.setSize(numPoints);
143   path.smoothingType.setSize(numPoints);
144   for (U32 j = 0; j < path.positions.size(); j++)
145   {
146      mathRead(*stream, &path.positions[j]);
147      mathRead(*stream, &path.rotations[j]);
148      stream->read(&path.msToNext[j]);
149      stream->read(&path.smoothingType[j]);
150   }
151}
152
153void PathManagerEvent::process(NetConnection*)
154{
155   if (clearPaths)
156   {
157      // Clear out all the client's paths...
158      gClientPathManager->clearPaths();
159   }
160   AssertFatal(modifiedPath <= gClientPathManager->mPaths.size(), "Error out of bounds path!");
161   if (modifiedPath == gClientPathManager->mPaths.size()) {
162      PathManager::PathEntry *pe = new PathManager::PathEntry;
163      *pe = path;
164      gClientPathManager->mPaths.push_back(pe);
165   }
166   else
167      *(gClientPathManager->mPaths[modifiedPath]) = path;
168}
169
170IMPLEMENT_CO_NETEVENT_V1(PathManagerEvent);
171
172// Will be internalized once the @internal tag is working
173ConsoleDocClass( PathManagerEvent,
174   "@brief Class responsible for the registration, transmission, and management "
175   "of paths on client and server.\n\n"
176
177   "For internal use only, not intended for use in TorqueScript or game development\n\n"
178
179   "@internal\n"
180);
181
182//--------------------------------------------------------------------------
183//-------------------------------------- PathManager Implementation
184//
185PathManager* gClientPathManager = NULL;
186PathManager* gServerPathManager = NULL;
187
188//--------------------------------------------------------------------------
189PathManager::PathManager(const bool isServer)
190{
191   VECTOR_SET_ASSOCIATION(mPaths);
192
193   mIsServer  = isServer;
194}
195
196PathManager::~PathManager()
197{
198   clearPaths();
199}
200
201void PathManager::clearPaths()
202{
203   for (U32 i = 0; i < mPaths.size(); i++)
204      delete mPaths[i];
205   mPaths.setSize(0);
206#ifdef TORQUE_DEBUG
207   // This gets rid of the memory used by the vector.
208   // Prevents it from showing up in memory leak logs.
209   mPaths.compact();
210#endif
211}
212
213DefineEngineFunction( clearServerPaths, void, ( ), , "")
214{
215   gServerPathManager->clearPaths();
216}
217
218DefineEngineFunction( clearClientPaths, void, ( ), , "")
219{
220   gClientPathManager->clearPaths();
221}
222
223//--------------------------------------------------------------------------
224U32 PathManager::allocatePathId()
225{
226   mPaths.increment();
227   mPaths.last() = new PathEntry;
228
229   return (mPaths.size() - 1);
230}
231
232
233void PathManager::updatePath(const U32              id,
234                             const Vector<Point3F>& positions,
235                             const Vector<QuatF>&   rotations,
236                             const Vector<U32>&     times,
237                             const Vector<U32>&     smoothingTypes,
238                             const bool             looping)
239{
240   AssertFatal(mIsServer == true, "PathManager::updatePath: Error, must be called on the server side");
241   AssertFatal(id < mPaths.size(), "PathManager::updatePath: error, id out of range");
242   AssertFatal(positions.size() == times.size() && positions.size() == smoothingTypes.size(), "Error, times and positions must match!");
243
244   PathEntry& rEntry = *mPaths[id];
245
246   rEntry.positions = positions;
247   rEntry.rotations = rotations;
248   rEntry.msToNext  = times;
249   rEntry.smoothingType = smoothingTypes;
250   rEntry.looping = looping;
251
252   rEntry.totalTime = 0;
253   for (S32 i = 0; i < S32(rEntry.msToNext.size()); i++)
254      rEntry.totalTime += rEntry.msToNext[i];
255
256   transmitPath(id);
257}
258
259
260//--------------------------------------------------------------------------
261void PathManager::transmitPaths(NetConnection* nc)
262{
263   AssertFatal(mIsServer, "Error, cannot call transmitPaths on client path manager!");
264
265   // Send over paths
266   for(S32 i = 0; i < mPaths.size(); i++)
267   {
268      PathManagerEvent* event = new PathManagerEvent;
269      event->clearPaths       = (i == 0);
270      event->modifiedPath = i;
271      event->path = *(mPaths[i]);
272      nc->postNetEvent(event);
273   }
274}
275
276void PathManager::transmitPath(const U32 id)
277{
278   AssertFatal(mIsServer, "Error, cannot call transmitNewPath on client path manager!");
279
280   // Post to all active clients that have already received their paths...
281   //
282   SimGroup* pClientGroup = Sim::getClientGroup();
283   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
284      NetConnection* nc = dynamic_cast<NetConnection*>(*itr);
285      if (nc && nc->missionPathsSent())
286      {
287         // Transmit the updated path...
288         PathManagerEvent* event = new PathManagerEvent;
289         event->modifiedPath     = id;
290         event->clearPaths       = false;
291         event->path             = *(mPaths[id]);
292         nc->postNetEvent(event);
293      }
294   }
295}
296
297void PathManager::getPathPosition(const U32 id,
298                                  const F64 msPosition,
299                                  Point3F&  rPosition,
300                                  QuatF &rotation)
301{
302   AssertFatal(isValidPath(id), "Error, this is not a valid path!");
303   PROFILE_START(PathManGetPos);
304
305   // Ok, query holds our path information...
306   F64 ms = msPosition;
307
308   //Looping vs. non-looping splines
309   F32 msTotal;
310   if (mPaths[id]->looping)
311      msTotal = mPaths[id]->totalTime;
312   else
313      //total time minus last nodes time
314      msTotal = mPaths[id]->totalTime - mPaths[id]->msToNext[mPaths[id]->msToNext.size() - 1];
315
316   if (ms > msTotal)
317      ms = msTotal;
318
319   S32 startNode = 0;
320   while (ms > mPaths[id]->msToNext[startNode]) {
321      ms -= mPaths[id]->msToNext[startNode];
322      startNode++;
323   }
324
325   S32 endNode;
326
327   //Looping splines
328   if (mPaths[id]->looping)
329      endNode = (startNode + 1) % mPaths[id]->positions.size();
330   //Non-looping splines
331   else
332      endNode = getMin(startNode + 1, mPaths[id]->positions.size() - 1);
333
334
335   Point3F& rStart = mPaths[id]->positions[startNode];
336   Point3F& rEnd   = mPaths[id]->positions[endNode];
337
338   F64 interp = ms / F32(mPaths[id]->msToNext[startNode]);
339   if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeLinear)
340   {
341      rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
342   }
343   else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeAccelerate)
344   {
345      interp = mSin(interp * M_PI - (M_PI / 2)) * 0.5 + 0.5;
346      rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
347   }
348   else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeSpline)
349   {
350      S32 preStart = startNode - 1;
351      S32 postEnd = endNode + 1;
352
353      //Looping splines
354      if (mPaths[id]->looping)
355      {
356         if (postEnd >= mPaths[id]->positions.size())
357            postEnd = 0;
358         if (preStart < 0)
359            preStart = mPaths[id]->positions.size() - 1;
360      }
361      //Non-looping splines
362      else
363      {
364         if (postEnd >= mPaths[id]->positions.size())
365            postEnd = mPaths[id]->positions.size() - 1;
366         if (preStart < 0)
367            preStart = 0;
368      }
369
370      Point3F p0 = mPaths[id]->positions[preStart];
371      Point3F p1 = rStart;
372      Point3F p2 = rEnd;
373      Point3F p3 = mPaths[id]->positions[postEnd];
374      rPosition.x = mCatmullrom(interp, p0.x, p1.x, p2.x, p3.x);
375      rPosition.y = mCatmullrom(interp, p0.y, p1.y, p2.y, p3.y);
376      rPosition.z = mCatmullrom(interp, p0.z, p1.z, p2.z, p3.z);
377   }
378   rotation.interpolate( mPaths[id]->rotations[startNode], mPaths[id]->rotations[endNode], interp );
379   PROFILE_END();
380}
381
382U32 PathManager::getPathTotalTime(const U32 id) const
383{
384   AssertFatal(isValidPath(id), "Error, this is not a valid path!");
385
386   return mPaths[id]->totalTime;
387}
388
389U32 PathManager::getPathNumWaypoints(const U32 id) const
390{
391   AssertFatal(isValidPath(id), "Error, this is not a valid path!");
392
393   return mPaths[id]->positions.size();
394}
395
396U32 PathManager::getWaypointTime(const U32 id, const U32 wayPoint) const
397{
398   AssertFatal(isValidPath(id), "Error, this is not a valid path!");
399   AssertFatal(wayPoint < getPathNumWaypoints(id), "Invalid waypoint!");
400
401   U32 time = 0;
402   for (U32 i = 0; i < wayPoint; i++)
403      time += mPaths[id]->msToNext[i];
404
405   return time;
406}
407
408U32 PathManager::getPathTimeBits(const U32 id)
409{
410   AssertFatal(isValidPath(id), "Error, this is not a valid path!");
411
412   return countNumBits(mPaths[id]->totalTime);
413}
414
415U32 PathManager::getPathWaypointBits(const U32 id)
416{
417   AssertFatal(isValidPath(id), "Error, this is not a valid path!");
418
419   return countNumBits(mPaths[id]->positions.size());
420}
421
422
423bool PathManager::dumpState(BitStream* stream) const
424{
425   stream->write(mPaths.size());
426
427   for (U32 i = 0; i < mPaths.size(); i++) {
428      const PathEntry& rEntry = *mPaths[i];
429      stream->write(rEntry.totalTime);
430
431      stream->write(rEntry.positions.size());
432      for (U32 j = 0; j < rEntry.positions.size(); j++) {
433         mathWrite(*stream, rEntry.positions[j]);
434         stream->write(rEntry.msToNext[j]);
435      }
436   }
437
438   return stream->getStatus() == Stream::Ok;
439}
440
441bool PathManager::readState(BitStream* stream)
442{
443   U32 i;
444   for (i = 0; i < mPaths.size(); i++)
445      delete mPaths[i];
446
447   U32 numPaths;
448   stream->read(&numPaths);
449   mPaths.setSize(numPaths);
450
451   for (i = 0; i < mPaths.size(); i++) {
452      mPaths[i] = new PathEntry;
453      PathEntry& rEntry = *mPaths[i];
454
455      stream->read(&rEntry.totalTime);
456
457      U32 numPositions;
458      stream->read(&numPositions);
459      rEntry.positions.setSize(numPositions);
460      rEntry.msToNext.setSize(numPositions);
461      for (U32 j = 0; j < rEntry.positions.size(); j++) {
462         mathRead(*stream, &rEntry.positions[j]);
463         stream->read(&rEntry.msToNext[j]);
464      }
465   }
466
467   return stream->getStatus() == Stream::Ok;
468}
469
470F64 PathManager::getClosestTimeToPoint(const U32 id, const Point3F p)
471{
472   //Ubiq: Ideally this algorithm would work directly by finding roots. However, it's a 5th order 
473   //polynomial (cannot be solved by radicals), so we're doing it iteratively instead!
474
475   //Steps:
476   //1) Termination condition: if the segment is shorter than L, return the midpoint
477   //2) Otherwise, divide the spline-segment into S sub-segments (S+1 points to test)
478   //2) Test these points against the given point and find the closest of them
479   //4) Recurse within the 2 segments surrounding the closest point
480
481   //NOTE: In a case where the spline comes near the point multiple times, the wrong local 
482   //minima of the spline may be chosen early on and then refined, like polishing a turd :)
483
484   PROFILE_START(PathManager_getClosestTimeToPoint);
485
486   F64 totalTime;
487   if (mPaths[id]->looping)
488      totalTime = mPaths[id]->totalTime;
489   else
490      totalTime = mPaths[id]->totalTime - mPaths[id]->msToNext[mPaths[id]->msToNext.size() - 1];
491
492   F64 ret = getClosestTimeToPoint(id, p, 0.0f, totalTime);
493
494   PROFILE_END();
495
496   return ret;
497}
498
499F64 PathManager::getClosestTimeToPoint(const U32 id, const Point3F p, const F64 tMin, const F64 tMax)
500{
501   F64 totalSize = tMax - tMin;
502
503   //termination condition
504   if (totalSize <= 25.0f)
505      return (tMin + tMax) / 2.0f;
506
507   U32 steps = getMax((F32)totalSize / 500.0f, 8.0f);
508   F64 stepSize = totalSize / steps;
509
510   F64 distBest = F32_MAX;
511   F64 tBest = 0;
512
513   for (U32 i = 0; i <= steps; i++)
514   {
515      F64 tTest = tMin + (stepSize * i);
516      Point3F pTest; QuatF dummy;
517      getPathPosition(id, tTest, pTest, dummy);
518
519      F64 dist = (pTest - p).lenSquared();    //no need for square root
520      if (dist < distBest)
521      {
522         tBest = tTest;
523         distBest = dist;
524      }
525   }
526
527   F64 tMinNew = tBest - stepSize;
528   F64 tMaxNew = tBest + stepSize;
529   if (mPaths[id]->looping)
530   {
531      while (tMinNew < 0)
532      {
533         tMinNew += getPathTotalTime(id);
534         tMaxNew += getPathTotalTime(id);
535      }
536      while (tMaxNew > getPathTotalTime(id))
537      {
538         tMinNew -= getPathTotalTime(id);
539         tMaxNew -= getPathTotalTime(id);
540      }
541   }
542   else
543   {
544      tMinNew = getMax(tMin, tMinNew);
545      tMaxNew = getMin(tMax, tMaxNew);
546   }
547   return getClosestTimeToPoint(id, p, tMinNew, tMaxNew);
548}
549
550