forestCollision.cpp
Engine/source/forest/forestCollision.cpp
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