1
///////////////////////////////////////////////////////////////////////////////
2
/// @file OgreCollisionContext.cpp
3
/// @brief <TODO: insert file description here>
5
/// @author The OgreOpcode Team
7
///////////////////////////////////////////////////////////////////////////////
9
/// This file is part of OgreOpcode.
11
/// A lot of the code is based on the Nebula Opcode Collision module, see docs/Nebula_license.txt
13
/// OgreOpcode is free software; you can redistribute it and/or
14
/// modify it under the terms of the GNU Lesser General Public
15
/// License as published by the Free Software Foundation; either
16
/// version 2.1 of the License, or (at your option) any later version.
18
/// OgreOpcode is distributed in the hope that it will be useful,
19
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
20
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
/// Lesser General Public License for more details.
23
/// You should have received a copy of the GNU Lesser General Public
24
/// License along with OgreOpcode; if not, write to the Free Software
25
/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
///////////////////////////////////////////////////////////////////////////////
28
#include "OgreCollisionContext.h"
29
#include "OgreCollisionObject.h"
30
#include "OgreOpcodeMath.h"
31
#include "OgreCollisionManager.h"
35
using namespace OgreOpcode::Details;
41
inline bool intervalOverlap(Real a0, Real a1, Real b0, Real b1)
43
// Only two ways for intervals to not overlap --
44
// a's max less than b's min, or a's min greater than b's max.
45
// Otherwise they overlap.
46
// return !(a1<b0 || a0>b1);
47
// I just applied the De Morgan's law here in order to obtain short-circuit
48
return (a1>=b0) && (a0<=b1);
52
// release all owned collide objects
53
CollisionContext::~CollisionContext()
55
// remove collision objects
56
while (!owned_list.empty())
58
destroyObject(*(owned_list.begin()));
60
delete mVisualDebugger;
64
// Construct a new collide object.
65
CollisionObject *CollisionContext::createObject(const Ogre::String& name)
67
CollisionObject *co = new CollisionObject(name);
68
co->setId(unique_id++);
70
owned_list.push_back(co);
74
CollisionContext::CollisionContext(const Ogre::String& name) :
80
mVisualDebugger = new Details::OgreOpcodeDebugger(mName, CollisionManager::getSingletonPtr()->getSceneManager());
81
mRecentContactList.clear();
82
mBroadPhase = new BP_Scene(&proxList, &OgreOpcode::CollisionContext::addPair, &OgreOpcode::CollisionContext::removePair);
85
// Kill an owned collide object.
86
void CollisionContext::destroyObject(CollisionObject *collObj)
90
if(collObj->isAttached())
92
rw_attached_list_iterator itAttached = find(attached_list.begin(), attached_list.end(), collObj);
93
if (itAttached != attached_list.end())
95
attached_list.erase(itAttached);
99
rw_owned_list_iterator itOwned = find(owned_list.begin(), owned_list.end(), collObj);
100
if (itOwned != owned_list.end())
102
owned_list.erase(itOwned);
104
collObj->setAttached(false);
105
collObj->remove_broadphase();
106
collObj->setContext(0);
111
void CollisionContext::addObject(CollisionObject *collObj)
114
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Trying to add a null object.", "CollisionContext::addObject");
116
attached_list.push_back(collObj);
117
collObj->setAttached(true);
120
void CollisionContext::removeObject(CollisionObject *collObj)
124
collObj->setAttached(false);
125
collObj->remove_broadphase();
126
rw_attached_list_iterator itAttached = find(attached_list.begin(), attached_list.end(), collObj);
127
if (itAttached != attached_list.end())
129
attached_list.erase(itAttached);
134
/// Call collide on each object in the context.
135
/// After this, each object's collision array holds all collisions
136
/// this object was involved with.
137
int CollisionContext::collide(Real dt)
143
// first, clear the collision counters in all collide objects
144
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
146
(*i)->clearCollisions();
149
// then, run the broadphase
150
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
152
(*i)->do_broadphase();
155
// Loop through the Potentially Colliding Set and tell each CollisionObject to test against the other
156
for (ProxList::iterator prox_it = proxList.begin(); prox_it != proxList.end(); ++prox_it)
158
// If the shape is marked as being static, do not add it to the PCS.
159
if(!(*prox_it).obj1->getShape()->isStatic())
160
(*prox_it).obj1->addToCollideList((*prox_it).obj2);
161
if(!(*prox_it).obj2->getShape()->isStatic())
162
(*prox_it).obj2->addToCollideList((*prox_it).obj1);
165
// check the collision status for each object
166
collideReportHandler.beginFrame();
167
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
169
//if ( (*i)->needsUpdate() )
172
collideReportHandler.endFrame();
174
int num_coll = collideReportHandler.getNumCollisions();
178
/// Get all collisions an object is involved in.
179
/// Returns pointer to an internal collision array and
180
/// the number of collisions.
181
int CollisionContext::getCollisions(CollisionObject *collObj, CollisionPair **&cpPtr)
183
if (collObj->getNumCollisions() > 0)
185
return collideReportHandler.getCollisions(collObj,cpPtr);
193
CollisionObject* CollisionContext::getAttachedObject(Ogre::String name)
195
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
197
if( (*i)->getName() == name )
200
OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "'" + name + "' is not attached! Does it even exsist?", "CollisionContext::getAttachedObject");
205
const std::list<CollisionObject*>& CollisionContext::getPotentialColliders(const CollisionObject* collidee)
207
return collidee->collideList;
210
/// get reporter for for last collide() call.
211
const CollisionReporter& CollisionContext::getCollisionReport()
213
return collideReportHandler;
217
const int CollisionContext::getNumCollisions()
219
return collideReportHandler.getNumCollisions();
222
/// get reporter for for last Check...() call.
223
const CollisionReporter& CollisionContext::getCheckReport()
225
return checkReportHandler;
228
/// visualize all objects in the context.
229
void CollisionContext::visualize(bool doVisualize, bool doRadii, bool doContacts, bool doBBs, bool doShapes, bool doAABBs)
231
// if the debugger is down, just return
232
if(!mVisualDebugger) return;
234
mVisualDebugger->clearAll();
235
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
237
(*i)->getShape()->clearViz();
242
if (!attached_list.empty())
246
mVisualDebugger->beginRadii();
247
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
249
(*i)->visualizeRadii();
251
mVisualDebugger->endRadii();
255
if( collideReportHandler.getNumCollisions() > 0)
257
mVisualDebugger->beginContacts();
258
mVisualDebugger->beginContactNormals();
259
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
261
(*i)->visualizeContacts();
263
mVisualDebugger->endContactNormals();
264
mVisualDebugger->endContacts();
267
static int contactCount = 0;
268
static bool bufferContacts = true;
270
if( checkReportHandler.getNumCollisions() > 0)
272
CollisionPair **pick_report;
273
int num_picks = checkReportHandler.getAllCollisions(pick_report);
277
for(int i = 0; i < num_picks; i++)
279
for (int currColl = 0; currColl < static_cast<int>(pick_report[i]->collInfos.size()); currColl++)
283
CollisionInfo collInfo;
284
collInfo.contact = pick_report[i]->collInfos[currColl].contact;
285
collInfo.this_normal = pick_report[i]->collInfos[currColl].this_normal * 5;
286
collInfo.other_normal = pick_report[i]->collInfos[currColl].other_normal * 5;
287
mRecentContactList.push_back(collInfo);
289
mRecentContactList.pop_front();
294
if(contactCount > 10)
295
bufferContacts = false;
299
// render any collision contact points
300
if (mRecentContactList.size() > 0)
302
mVisualDebugger->beginContacts();
303
mVisualDebugger->beginContactNormals();
305
std::list<CollisionInfo>::iterator contactIter;
306
for (contactIter = mRecentContactList.begin(); contactIter != mRecentContactList.end(); ++contactIter)
308
Vector3 cnt = (*contactIter).contact;
309
mVisualDebugger->addContactLine(cnt.x-0.5f,cnt.y,cnt.z, cnt.x+0.5f,cnt.y,cnt.z);
310
mVisualDebugger->addContactLine(cnt.x,cnt.y-0.5f,cnt.z, cnt.x,cnt.y+0.5f,cnt.z);
311
mVisualDebugger->addContactLine(cnt.x,cnt.y,cnt.z-0.5f, cnt.x,cnt.y,cnt.z+0.5f);
313
Vector3 n = (*contactIter).this_normal * 5;
314
mVisualDebugger->addContactNormalsLine(cnt.x,cnt.y,cnt.z, cnt.x+n.x,cnt.y+n.y,cnt.z+n.z);
315
n = (*contactIter).other_normal * 5;
316
mVisualDebugger->addContactNormalsLine(cnt.x,cnt.y,cnt.z, cnt.x+n.x,cnt.y+n.y,cnt.z+n.z);
320
mVisualDebugger->endContactNormals();
321
mVisualDebugger->endContacts();
328
mVisualDebugger->beginBBs();
329
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
331
(*i)->visualizeBoundingBoxes();
333
mVisualDebugger->endBBs();
337
//mVisualDebugger->beginShapes();
339
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
341
// if( (*i)->hasCollisions() || (*i)->hasCheckCollisions() )
342
(*i)->getShape()->visualize(mVisualDebugger);
344
//mVisualDebugger->endShapes();
348
mVisualDebugger->beginAABBs();
350
for (attached_list_iterator i = attached_list.begin(); i != attached_list.end(); ++i)
352
if( (*i)->hasCollisions() || (*i)->hasCheckCollisions() )
353
(*i)->getShape()->visualizeAABBs(mVisualDebugger);
355
mVisualDebugger->endAABBs();
361
/// Do an instant check of a moving sphere in the collision volume.
362
/// Fills out the provided collide report array and
363
/// returns number of detected collisions.
364
/// @param p0 [in] starting position
365
/// @param v0 [in] vector to ending position
366
/// @param radius [in] radius
367
/// @param collClass [in] collision class for collision type query
368
/// @param cr_ptr [out] pointer to array of pointers to CollisionPair's
369
/// @return number of detected contacts (1 per collide object)
370
int CollisionContext::sweptSphereCheck(const Vector3& position, const Vector3& movementVector, Real radius, CollisionClass collClass, CollisionPair **& cpPtr, String ignorename)
372
// create a bounding box from the start and end position
373
Vector3 endPosition(position + movementVector);
374
Vector3 minv(n_min(position.x, endPosition.x) - radius,
375
n_min(position.y, endPosition.y) - radius,
376
n_min(position.z, endPosition.z) - radius);
377
Vector3 maxv(n_max(position.x, endPosition.x) + radius,
378
n_max(position.y, endPosition.y) + radius,
379
n_max(position.z, endPosition.z) + radius);
381
const int own_id = 0xffff;
383
// initialize collision report handler
384
checkReportHandler.beginFrame();
386
// This simply goes through all attached objects, and
387
// checks them for overlap, so ITS SLOW! Every object is
388
// tested exactly once
389
for (attached_list_iterator other = attached_list.begin(); other != attached_list.end(); ++other)
391
// see if we have overlaps in all 3 dimensions
392
if ((minv.x < (*other)->maxv.x) && (maxv.x > (*other)->minv.x) &&
393
(minv.y < (*other)->maxv.y) && (maxv.y > (*other)->minv.y) &&
394
(minv.z < (*other)->maxv.z) && (maxv.z > (*other)->minv.z))
396
// see if the candidate is in the ignore types set
397
CollisionType ct = CollisionManager::getSingletonPtr()->queryCollType(collClass,(*other)->getCollClass());
398
if (COLLTYPE_IGNORE == ct) continue;
400
checkReportHandler.mTotalObjObjTests++;
401
if((*other)->getName() != ignorename)
403
if (COLLTYPE_QUICK == ct)
405
// Trying to extract position information from provided matrices.
406
Vector3 p1 = Vector3((*other)->old_matrix[0][3], (*other)->old_matrix[1][3], (*other)->old_matrix[2][3]);
407
Vector3 v1 = Vector3(Vector3((*other)->new_matrix[0][3], (*other)->new_matrix[1][3], (*other)->new_matrix[2][3]) - p1);
409
// do the contact check between 2 moving spheres
410
sphere s0(position,radius);
411
sphere s1(p1,(*other)->getRadius());
413
checkReportHandler.mTotalBVBVTests++;
414
if (s0.intersect_sweep(movementVector,s1,v1,u0,u1))
416
if ((u0>=0.0f) && (u0<1.0f))
420
// compute the 2 midpoints at the time of collision
421
Vector3 c0(position + movementVector*u0);
422
Vector3 c1(p1 + v1*u0);
424
// compute the collide normal
426
if (d.length() > TINY)
431
d = Vector3(0.0f, 1.0f, 0.0f);
434
// fill out a collide report and add to report handler
436
cr.this_object = (*other);
437
cr.other_object = (*other);
439
CollisionInfo collInfo;
440
collInfo.contact = (d*radius) + c0;
441
collInfo.this_normal = d;
442
collInfo.other_normal = -d;
443
cr.collInfos.push_back(collInfo);
444
checkReportHandler.addCollision(cr,own_id,(*other)->id);
448
else // CONTACT and EXACT
450
// do sphere-shape collision check
451
ICollisionShape* shape = (*other)->getShape();
456
cp.this_object = (*other);
457
cp.other_object = (*other);
458
bool ret = shape->sweptSphereCheck(ct, (*other)->getTransform(), position, movementVector, radius, cp);
459
checkReportHandler.mTotalBVBVTests += cp.numBVBVTests;
460
checkReportHandler.mTotalBVPrimTests += cp.numBVPrimTests;
463
cp.this_object = (*other);
464
cp.other_object = (*other);
465
checkReportHandler.addCollision(cp, own_id, (*other)->id);
473
checkReportHandler.endFrame();
474
return checkReportHandler.getAllCollisions(cpPtr);
477
/// Test a ray against the collide objects in the collide context.
478
/// The collType will be interpreted as follows:
479
/// - COLLTYPE_IGNORE: illegal (makes no sense)
480
/// - COLLTYPE_QUICK: occlusion check only
481
/// - COLLTYPE_CONTACT: return closest contact only
482
/// - COLLTYPE_EXACT: return all contacts (unsorted)
483
/// @param line [in] the ray to test in global space
484
/// @param collType [in] the collision type
485
/// @param collClass [in] optional coll class (COLLCLASS_ALWAYS_* if no coll class filtering wanted)
486
/// @param cpPtr [out] will be filled with pointer to collide report pointers
487
/// @return number of detected contacts (1 per collide object)
488
int CollisionContext::rayCheck(const Ogre::Ray line, const Real dist, CollisionType collType, CollisionClass collClass, CollisionPair**& cpPtr)
490
assert(collType != COLLTYPE_IGNORE);
492
// create a bounding box from the line
495
bbox.grow(line.getOrigin());
496
bbox.grow(line.getPoint(dist));
497
const int ownId = 0xffff;
499
// initialize collision report handler
500
checkReportHandler.beginFrame();
502
// go through all attached collide objects
503
for (attached_list_iterator co = attached_list.begin(); co != attached_list.end(); ++co)
505
Vector3 coMin = (*co)->minv;
506
Vector3 coMax = (*co)->maxv;
507
// see if we have overlaps in all 3 dimensions
508
if (intervalOverlap(bbox.vmin.x,bbox.vmax.x,coMin.x,coMax.x) &&
509
intervalOverlap(bbox.vmin.y,bbox.vmax.y,coMin.y,coMax.y) &&
510
intervalOverlap(bbox.vmin.z,bbox.vmax.z,coMin.z,coMax.z) )
512
// see if the candidate is in the ignore types set
513
CollisionType ct = CollisionManager::getSingletonPtr()->queryCollType(collClass, (*co)->getCollClass());
514
if (COLLTYPE_IGNORE == ct)
521
ICollisionShape* shape = (*co)->getShape();
524
checkReportHandler.mTotalObjObjTests++;
526
bool ret = shape->rayCheck(collType, (*co)->getTransform(), line, dist, cp, mRayCulling);
527
checkReportHandler.mTotalBVBVTests += cp.numBVBVTests;
528
checkReportHandler.mTotalBVBVTests += cp.numBVPrimTests;
531
cp.this_object = (*co);
532
cp.other_object = (*co);
533
checkReportHandler.addCollision(cp, ownId, (*co)->id);
534
if (COLLTYPE_QUICK == collType)
543
checkReportHandler.endFrame();
545
//if (COLLTYPE_CONTACT == collType) // FIXME!
547
// // get closest contact only
548
// return checkReportHandler.getClosestCollision(line.getOrigin(), cpPtr);
552
// get all contacts (unsorted)
553
return checkReportHandler.getAllCollisions(cpPtr);
557
/// Test a sphere against the collide objects in the collide context.
558
/// The collType will be interpreted as follows:
559
/// - COLLTYPE_IGNORE: illegal (makes no sense)
560
/// - COLLTYPE_QUICK: return all contacts, do sphere-sphere check
561
/// - COLLTYPE_CONTACT: return closest contact only, sphere-shape
562
/// - COLLTYPE_EXACT: return all contacts (unsorted), sphere-shape
563
/// @param theSphere [in] the sphere to test in global space
564
/// @param collType [in] the collision type
565
/// @param collClass [in] optional coll class (COLLCLASS_ALWAYS_* if no coll class filtering wanted)
566
/// @param cpPtr [out] will be filled with pointer to collide report pointers
567
/// @return number of detected contacts (1 per collide object)
568
int CollisionContext::sphereCheck(const Sphere& theSphere, CollisionType collType, CollisionClass collClass, CollisionPair**& cpPtr)
570
assert(collType != COLLTYPE_IGNORE);
573
ball.set(theSphere.getCenter(),theSphere.getRadius());
574
// create a bounding box from the sphere
575
Vector3 vmin(ball.p.x - ball.r, ball.p.y - ball.r, ball.p.z - ball.r);
576
Vector3 vmax(ball.p.x + ball.r, ball.p.y + ball.r, ball.p.z + ball.r);
577
bbox3 bbox(vmin, vmax);
578
const int ownId = 0xffff;
580
// initialize collision report handler
581
checkReportHandler.beginFrame();
583
// go through all attached collide objects
585
for (attached_list_iterator co = attached_list.begin(); co != attached_list.end(); ++co)
587
// see if we have overlaps in all 3 dimensions
588
if ((bbox.vmin.x < (*co)->maxv.x) && (bbox.vmax.x > (*co)->minv.x) &&
589
(bbox.vmin.y < (*co)->maxv.y) && (bbox.vmax.y > (*co)->minv.y) &&
590
(bbox.vmin.z < (*co)->maxv.z) && (bbox.vmax.z > (*co)->minv.z))
592
// see if the candidate is in the ignore types set
593
CollisionType ct = CollisionManager::getSingletonPtr()->queryCollType(collClass, (*co)->getCollClass());
594
if (COLLTYPE_IGNORE == ct)
599
checkReportHandler.mTotalObjObjTests++;
600
if (COLLTYPE_QUICK == ct)
602
// do sphere-sphere collision check
603
const Matrix4 coTrans = (*co)->getTransform();
604
//s0.set(coTrans[0][3], coTrans[1][3], coTrans[2][3], (*co)->getRadius());
605
s0.set((*co)->getShape()->getCenter(), (*co)->getRadius());
606
checkReportHandler.mTotalBVBVTests++;
607
if (ball.intersects(s0))
610
cp.this_object = (*co);
611
cp.other_object = (*co);
612
checkReportHandler.addCollision(cp, ownId, (*co)->id);
617
// do sphere-shape collision check
618
ICollisionShape* shape = (*co)->getShape();
622
bool ret = shape->sphereCheck(collType, (*co)->getTransform(), theSphere, cp);
623
checkReportHandler.mTotalBVBVTests += cp.numBVBVTests;
624
checkReportHandler.mTotalBVPrimTests += cp.numBVPrimTests;
627
cp.this_object = (*co);
628
cp.other_object = (*co);
629
checkReportHandler.addCollision(cp, ownId, (*co)->id);
635
checkReportHandler.endFrame();
637
//if (COLLTYPE_CONTACT == collType)
639
// // get closest contact only
640
// return checkReportHandler.getClosestCollision(ball.p, cpPtr);
644
// get all contacts (unsorted)
645
return checkReportHandler.getAllCollisions(cpPtr);
650
void CollisionContext::update(Real dt)
652
for (attached_list_iterator co = attached_list.begin(); co != attached_list.end(); ++co)
658
/// reset position and timestamp of all attached collide objects to 0.0.
659
/// This is useful at the beginning of a level to prevent phantom collisions
660
/// (when objects are repositioned to their starting point in the level).
661
void CollisionContext::reset()
663
Matrix4 identity = Matrix4::IDENTITY;
664
for (attached_list_iterator co = attached_list.begin(); co != attached_list.end(); ++co)
666
// This must be done twice, so that old timestamp also becomes 0
667
(*co)->update(-1.0, identity);
668
(*co)->update(-1.0, identity);