Torque3D Documentation / _generateds / forestCollision.cpp

forestCollision.cpp

Engine/source/forest/forestCollision.cpp

More...

Detailed Description

  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 "forest/forestCollision.h"
 26
 27#include "forest/forest.h"
 28#include "forest/forestCell.h"
 29#include "forest/ts/tsForestItemData.h"
 30#include "ts/tsShapeInstance.h"
 31#include "T3D/physics/physicsPlugin.h"
 32#include "T3D/physics/physicsBody.h"
 33#include "T3D/physics/physicsCollision.h"
 34#include "collision/concretePolyList.h"
 35#include "platform/profiler.h"
 36
 37
 38/*
 39bool Forest::castRay( const Point3F &start, const Point3F &end, RayInfo* info )
 40{
 41   //if ( !mData )
 42      //return false;
 43   //return mData->castRay( start, end, outInfo );
 44
 45   return false;
 46}
 47
 48bool Forest::castRayI( const Point3F &start, const Point3F &end, ForestRayInfo *outInfo )
 49{
 50   if ( !mData )
 51      return false;
 52
 53   return mData->castRay( start, end, outInfo );
 54}
 55
 56ScriptMethod( Forest, forestRayCast, const char*, 4, 4, "( Point3F start, Point3F end )"
 57                "Cast a ray from start to end, checking for collision against trees in this forest.\n\n"                
 58                "@returns A string containing either null, if nothing was struck, or these fields:\n"
 59                "            - The ID of the tree type datablock.\n"
 60                "            - The tree ID (not a normal object id).\n" 
 61                "            - t, The time during the raycast at which the collision occured." )                
 62{
 63
 64   Point3F start, end;
 65   dSscanf(argv[2], "%g %g %g", &start.x, &start.y, &start.z);
 66   dSscanf(argv[3], "%g %g %g", &end.x,   &end.y,   &end.z);   
 67
 68   static const U32 bufSize = 256;
 69   char *returnBuffer = Con::getReturnBuffer(bufSize);
 70   returnBuffer[0] = '0';
 71   returnBuffer[1] = '\0';
 72
 73   ForestRayInfo rinfo;
 74   if ( object->castRayI( start, end, &rinfo ) )
 75      dSprintf( returnBuffer, bufSize, "%d %d %g", rinfo.item->getData()->getId(), rinfo.key, rinfo.t );
 76
 77   return returnBuffer;
 78}
 79*/
 80
 81void ForestConvex::calculateTransform( const MatrixF &worldXfrm )
 82{
 83   mTransform = worldXfrm;
 84   return;
 85}
 86
 87Box3F ForestConvex::getBoundingBox() const
 88{
 89   // This is probably a bad idea? -- BJG
 90   return getBoundingBox( mTransform, Point3F(mScale,mScale,mScale) );
 91}
 92
 93Box3F ForestConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
 94{
 95   Box3F newBox = box;
 96   newBox.minExtents.convolve(scale);
 97   newBox.maxExtents.convolve(scale);
 98   mat.mul(newBox);
 99   return newBox;
100}
101
102Point3F ForestConvex::support(const VectorF& v) const
103{
104   TSShapeInstance *si = mData->getShapeInstance();
105
106   TSShape::ConvexHullAccelerator* pAccel =
107      si->getShape()->getAccelerator(mData->getCollisionDetails()[hullId]);
108   AssertFatal(pAccel != NULL, "Error, no accel!");
109
110   F32 currMaxDP = mDot(pAccel->vertexList[0], v);
111   U32 index = 0;
112   for (U32 i = 1; i < pAccel->numVerts; i++) 
113   {
114      F32 dp = mDot(pAccel->vertexList[i], v);
115      if (dp > currMaxDP) 
116      {
117         currMaxDP = dp;
118         index = i;
119      }
120   }
121
122   return pAccel->vertexList[index];
123}
124
125void ForestConvex::getFeatures( const MatrixF &mat, const VectorF &n, ConvexFeature *cf )
126{
127   cf->material = 0;
128   cf->mObject = mObject;
129
130   TSShapeInstance *si = mData->getShapeInstance();
131
132   TSShape::ConvexHullAccelerator* pAccel =
133      si->getShape()->getAccelerator(mData->getCollisionDetails()[hullId]);
134   AssertFatal(pAccel != NULL, "Error, no accel!");
135
136   F32 currMaxDP = mDot(pAccel->vertexList[0], n);
137   U32 index = 0;
138   U32 i;
139   for (i = 1; i < pAccel->numVerts; i++) 
140   {
141      F32 dp = mDot(pAccel->vertexList[i], n);
142      if (dp > currMaxDP) 
143      {
144         currMaxDP = dp;
145         index = i;
146      }
147   }
148
149   const U8* emitString = pAccel->emitStrings[index];
150   U32 currPos = 0;
151   U32 numVerts = emitString[currPos++];
152   for (i = 0; i < numVerts; i++) 
153   {
154      cf->mVertexList.increment();
155      index = emitString[currPos++];
156      mat.mulP(pAccel->vertexList[index], &cf->mVertexList.last());
157   }
158
159   U32 numEdges = emitString[currPos++];
160   for (i = 0; i < numEdges; i++) 
161   {
162      U32 ev0 = emitString[currPos++];
163      U32 ev1 = emitString[currPos++];
164      cf->mEdgeList.increment();
165      cf->mEdgeList.last().vertex[0] = ev0;
166      cf->mEdgeList.last().vertex[1] = ev1;
167   }
168
169   U32 numFaces = emitString[currPos++];
170   for (i = 0; i < numFaces; i++) 
171   {
172      cf->mFaceList.increment();
173      U32 plane = emitString[currPos++];
174      mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal);
175      for (U32 j = 0; j < 3; j++)
176         cf->mFaceList.last().vertex[j] = emitString[currPos++];
177   }
178}
179
180void ForestConvex::getPolyList(AbstractPolyList* list)
181{
182   list->setTransform( &mTransform, Point3F(mScale,mScale,mScale));
183   list->setObject(mObject);
184
185   TSShapeInstance *si = mData->getShapeInstance();
186
187   S32 detail = mData->getCollisionDetails()[hullId];
188   si->animate(detail);
189   si->buildPolyList(list, detail);
190}
191
192
193void Forest::buildConvex( const Box3F &box, Convex *convex )
194{
195   mConvexList->collectGarbage();
196
197   // Get all ForestItem(s) within the box.
198   Vector<ForestItem> trees;
199   mData->getItems( box, &trees );
200   if ( trees.empty() )
201      return;
202
203   for ( U32 i = 0; i < trees.size(); i++ )
204   {
205      const ForestItem &forestItem = trees[i];
206
207      Box3F realBox = box;
208      mWorldToObj.mul( realBox );
209      realBox.minExtents.convolveInverse( mObjScale );
210      realBox.maxExtents.convolveInverse( mObjScale );
211
212      // JCF: is this really necessary if we already got this ForestItem
213      // as a result from getItems?
214      if ( realBox.isOverlapped( getObjBox() ) == false )
215         continue;      
216
217      TSForestItemData *data = (TSForestItemData*)forestItem.getData();
218
219      // Find CollisionDetail(s) that are defined...
220      const Vector<S32> &details = data->getCollisionDetails();
221      for ( U32 j = 0; j < details.size(); j++ ) 
222      {
223         // JCFHACK: need to fix this if we want this to work with speedtree
224         // or other cases in which we don't have a TSForestItemData.
225         // Most likely via preventing this method and other torque collision
226         // specific stuff from ever getting called.
227         if ( details[j] == -1 ) 
228            continue;         
229
230         // See if this convex exists in the working set already...
231         Convex* cc = 0;
232         CollisionWorkingList& wl = convex->getWorkingList();
233         for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) 
234         {
235            if ( itr->mConvex->getType() == ForestConvexType )
236            {
237               ForestConvex *pConvex = static_cast<ForestConvex*>(itr->mConvex);
238
239               if ( pConvex->mObject == this &&
240                  pConvex->mForestItemKey == forestItem.getKey() &&
241                  pConvex->hullId == j )
242               {
243                  cc = itr->mConvex;
244                  break;
245               }
246            }
247         }
248         if (cc)
249            continue;
250
251         // Then we need to make one.
252         ForestConvex *cp = new ForestConvex;
253         mConvexList->registerObject(cp);
254         convex->addToWorkingList(cp);
255         cp->mObject          = this;
256         cp->mForestItemKey   = forestItem.getKey();
257         cp->mData            = data;
258         cp->mScale           = forestItem.getScale();
259         cp->hullId           = j;
260         cp->box              = forestItem.getObjBox();
261         cp->calculateTransform( forestItem.getTransform() );
262      }
263   }
264}
265
266bool Forest::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere )
267{
268   if ( context == PLC_Decal )
269      return false;
270
271   // Get all ForestItem(s) within the box.
272   Vector<ForestItem> trees;
273   if ( mData->getItems( box, &trees ) == 0 )
274      return false;
275
276   polyList->setObject(this);
277
278   bool gotPoly = false;
279
280   for ( U32 i = 0; i < trees.size(); i++ )
281      gotPoly |= trees[i].buildPolyList( polyList, box, sphere );
282
283   return gotPoly;
284}
285
286bool ForestItem::buildPolyList( AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere ) const
287{
288   TSForestItemData *data = (TSForestItemData*)mDataBlock;
289   
290   bool ret = false;
291
292   MatrixF xfm = getTransform();
293   polyList->setTransform( &xfm, Point3F(mScale,mScale,mScale) );
294
295   TSShapeInstance *si = data->getShapeInstance();
296
297   const Vector<S32> &details = data->getCollisionDetails();
298   S32 detail;
299   for  (U32 i = 0; i < details.size(); i++ ) 
300   {
301      detail = details[i];
302      if (detail != -1) 
303         ret |= si->buildPolyList( polyList, detail );         
304   }
305
306   return ret;   
307}
308
309bool Forest::collideBox( const Point3F &start, const Point3F &end, RayInfo *outInfo )
310{   
311   //Con::warnf( "Forest::collideBox() - not yet implemented!" );
312   return Parent::collideBox( start, end, outInfo );
313}
314
315bool Forest::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo )
316{
317   return castRayBase( start, end, outInfo, false );   
318}
319
320bool Forest::castRayRendered( const Point3F &start, const Point3F &end, RayInfo *outInfo )
321{
322   return castRayBase( start, end, outInfo, true );
323}
324
325bool Forest::castRayBase( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered )
326{
327   if ( !getObjBox().collideLine( start, end ) )
328      return false;
329
330   if ( mData->castRay( start, end, outInfo, rendered ) )
331   {
332      outInfo->object = this;
333      return true;
334   }
335
336   return false;
337}
338
339void Forest::updateCollision()
340{
341   if ( !mData )
342      return;
343
344   mData->buildPhysicsRep( this );
345
346   // Make the assumption that if collision needs
347   // to be updated that the zoning probably changed too.
348   if ( isClientObject() )
349      mZoningDirty = true;
350}
351
352bool ForestData::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const
353{
354   RayInfo shortest;
355   shortest.userData = outInfo->userData;
356   shortest.t = F32_MAX;
357
358   BucketTable::ConstIterator iter = mBuckets.begin();
359   for (; iter != mBuckets.end(); ++iter)
360   {
361      if ( iter->value->castRay( start, end, outInfo, rendered ) )
362      {
363         if ( outInfo->t < shortest.t )         
364            shortest = *outInfo;         
365      }
366   }
367
368   *outInfo = shortest;
369
370   return ( outInfo->t < F32_MAX );
371}
372
373bool ForestCell::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const
374{
375   // JCF: start/end are in object space of the Forest but mBounds
376   // is in world space... Luckily Forest is global bounds and always at the origin
377   // with no scale.
378   if ( !mBounds.collideLine( start, end ) )
379      return false;
380
381   RayInfo shortest;
382   shortest.userData = outInfo->userData;
383   shortest.t = F32_MAX;
384
385   if ( !isLeaf() )
386   {
387      for ( U32 i=0; i < 4; i++ )
388      {
389         if ( mSubCells[i]->castRay( start, end, outInfo, rendered ) )
390         {
391            if ( outInfo->t < shortest.t )
392               shortest = *outInfo;
393         }
394      }
395   }
396   else
397   {
398      for ( U32 i = 0; i < mItems.size(); i++ )
399      {     
400         if ( mItems[i].castRay( start, end, outInfo, rendered ) )
401         {
402            if ( outInfo->t < shortest.t )
403               shortest = *outInfo;
404         }
405      }
406   }
407
408   *outInfo = shortest;
409
410   return ( outInfo->t < F32_MAX );
411}
412
413bool ForestItem::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const
414{
415   if ( !mWorldBox.collideLine( start, end ) )
416      return false;
417
418   Point3F s, e;
419   MatrixF mat = getTransform();
420   mat.scale( Point3F(mScale) );
421   mat.inverse();
422
423   mat.mulP( start, &s );
424   mat.mulP( end, &e );
425
426   TSForestItemData *data = (TSForestItemData*)mDataBlock;
427   TSShapeInstance *si = data->getShapeInstance();
428
429   if ( !si ) 
430      return false;
431   
432   if ( rendered )
433   {
434      // Always raycast against detail level zero
435      // because it makes lots of things easier.
436      if ( !si->castRayRendered( s, e, outInfo, 0 ) )
437         return false;
438
439      if ( outInfo->userData != NULL )
440         *(ForestItem*)(outInfo->userData) = *this;
441
442      return true;
443   }
444
445   RayInfo shortest;  
446   shortest.t = 1e8;
447   bool gotMatch = false;
448   S32 detail;
449
450   const Vector<S32> &details = data->getLOSDetails();
451   for (U32 i = 0; i < details.size(); i++) 
452   {
453      detail = details[i];
454      if (detail != -1) 
455      {
456         //si->animate(data->mLOSDetails[i]);
457
458         if ( si->castRayOpcode( detail, s, e, outInfo ) )         
459         {
460            if (outInfo->t < shortest.t)
461            {
462               gotMatch = true;
463               shortest = *outInfo;      
464            }
465         }
466      }
467   }
468
469   if ( !gotMatch )
470      return false;
471
472   // Copy out the shortest time...
473   *outInfo = shortest;
474
475   if ( outInfo->userData != NULL )
476      *(ForestItem*)(outInfo->userData) = *this;
477
478   return true;   
479}
480