void SweepTest::FindTouchedCCTs( NxU32 nb_boxes, const NxExtendedBounds3* boxes, const void** box_user_data, NxU32 nb_capsules, const NxExtendedCapsule* capsules, const void** capsule_user_data, const NxExtendedBounds3& world_box) { NxExtendedVec3 Origin; // Will be TouchedGeom::mOffset world_box.getCenter(Origin); // Find touched boxes, i.e. other box controllers for(NxU32 i=0;i<nb_boxes;i++) { if(!world_box.intersect(boxes[i])) continue; TouchedUserBox* UserBox = (TouchedUserBox*)reserve(mGeomStream, sizeof(TouchedUserBox)/sizeof(NxU32)); UserBox->mType = TOUCHED_USER_BOX; UserBox->mUserData = box_user_data[i]; UserBox->mOffset = Origin; UserBox->mBox = boxes[i]; } // Find touched capsules, i.e. other capsule controllers NxExtendedVec3 Center; NxVec3 Extents; world_box.getCenter(Center); world_box.getExtents(Extents); NxMat33 Idt; Idt.id(); for(NxU32 i=0;i<nb_capsules;i++) { // Do a quick AABB check first, to avoid calling the SDK too much const NxF32 r = capsules[i].radius; if((capsules[i].p0.x - r > world_box.max.x) || (world_box.min.x > capsules[i].p1.x + r)) continue; if((capsules[i].p0.y - r > world_box.max.y) || (world_box.min.y > capsules[i].p1.y + r)) continue; if((capsules[i].p0.z - r > world_box.max.z) || (world_box.min.z > capsules[i].p1.z + r)) continue; // Do a box-capsule intersect, or skip it? => better to skip it, not really useful now /* NxCapsule tmp; tmp.radius = capsules[i].radius; tmp.p0.x = float(capsules[i].p0.x); tmp.p0.y = float(capsules[i].p0.y); tmp.p0.z = float(capsules[i].p0.z); tmp.p1.x = float(capsules[i].p1.x); tmp.p1.y = float(capsules[i].p1.y); tmp.p1.z = float(capsules[i].p1.z); float d2 = gUtilLib->NxSegmentOBBSqrDist(tmp, NxVec3(float(Center.x), float(Center.y), float(Center.z)), Extents, Idt, NULL, NULL); if(d2<capsules[i].radius*capsules[i].radius)*/ { TouchedUserCapsule* UserCapsule = (TouchedUserCapsule*)reserve(mGeomStream, sizeof(TouchedUserCapsule)/sizeof(NxU32)); UserCapsule->mType = TOUCHED_USER_CAPSULE; UserCapsule->mUserData = capsule_user_data[i]; UserCapsule->mOffset = Origin; UserCapsule->mCapsule = capsules[i]; } } }
void CCTDebugData::addAABB(const NxExtendedBounds3& bounds, NxU32 color) { NxExtendedVec3 center; NxVec3 extents; bounds.getCenter(center); bounds.getExtents(extents); NxBounds3 tmp; tmp.setCenterExtents(NxVec3((float)center.x, (float)center.y, (float)center.z), extents); addAABB(tmp, color, false); }
void SweptCapsule::ComputeTemporalBox(const SweepTest& test, NxExtendedBounds3& box, const NxExtendedVec3& center, const NxVec3& direction) const { NxVec3 mExtents(mRadius, mRadius, mRadius); mExtents[test.mUpDirection] += mHeight*0.5f; //mExtents *= 2.0f; const NxVec3 SkinExtents = mExtents + NxVec3(test.mSkinWidth, test.mSkinWidth, test.mSkinWidth); NxExtendedVec3 tmp = center; tmp += direction; NxExtendedBounds3 DestBox; DestBox.setCenterExtents(tmp, SkinExtents); box.setCenterExtents(center, SkinExtents); box.add(DestBox); }
bool BoxController::getWorldBox(NxExtendedBounds3& box) const { box.setCenterExtents(position, extents); return true; }
void SweepTest::UpdateTouchedGeoms( void* user_data, const SweptVolume& swept_volume, NxU32 nb_boxes, const NxExtendedBounds3* boxes, const void** box_user_data, NxU32 nb_capsules, const NxExtendedCapsule* capsules, const void** capsule_user_data, NxU32 group_flags, const NxExtendedBounds3& world_box, const NxGroupsMask* groupsMask) { /* - if this is the first iteration (new frame) we have to redo the dynamic objects & the CCTs. The static objects can be cached. - if this is not, we can cache everything */ // PT: using "world_box" instead of "mCachedTBV" seems to produce TTP 6207 //#define DYNAMIC_BOX world_box #define DYNAMIC_BOX mCachedTBV bool NewCachedBox = false; // If the input box is inside the cached box, nothing to do if(world_box.isInside(mCachedTBV)) { if(mFirstUpdate) { mFirstUpdate = false; // Only redo the dynamic mGeomStream.erase(&mGeomStream[mNbCachedStatic]); mWorldTriangles.erase(&mWorldTriangles[mNbCachedT]); mWorldEdgeNormals.erase(&mWorldEdgeNormals[mNbCachedEN]); mEdgeFlags.erase(&mEdgeFlags[mNbCachedF]); FindTouchedGeometry(user_data, DYNAMIC_BOX, mWorldTriangles, swept_volume.GetType()==SWEPT_BOX ? &mWorldEdgeNormals : NULL, mEdgeFlags, mGeomStream, group_flags, false, true, groupsMask); FindTouchedCCTs( nb_boxes, boxes, box_user_data, nb_capsules, capsules, capsule_user_data, DYNAMIC_BOX ); gNbPartialUpdates++; } } else { NewCachedBox = true; // Cache BV used for the query mCachedTBV = world_box; // Grow the volume a bit. The temporal box here doesn't take sliding & collision response into account. // In bad cases it is possible to eventually touch a portion of space not covered by this volume. Just // in case, we grow the initial volume slightly. Then, additional tests are performed within the loop // to make sure the TBV is always correct. There's a tradeoff between the original (artificial) growth // of the volume, and the number of TBV recomputations performed at runtime... if(1) { mCachedTBV.scale(mVolumeGrowth); } else { NxExtendedVec3 center; mCachedTBV.getCenter(center); NxVec3 extents; mCachedTBV.getExtents(extents); /* NxVec3 scale(mVolumeGrowth, mVolumeGrowth, mVolumeGrowth); scale[mUpDirection] = 1.0f;*/ /* NxVec3 scale(1.0f, 1.0f, 1.0f); scale[mUpDirection] = mVolumeGrowth; extents.x *= scale.x; extents.y *= scale.y; extents.z *= scale.z;*/ extents.x *= 1.8f; extents.y += 1.0f; extents.z *= 1.8f; mCachedTBV.setCenterExtents(center, extents); } // Gather triangles touched by this box. This covers multiple meshes. mWorldTriangles.clear(); mWorldEdgeNormals.clear(); mEdgeFlags.clear(); mGeomStream.clear(); mCachedTriIndexIndex = 0; mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; gNbFullUpdates++; FindTouchedGeometry(user_data, mCachedTBV, mWorldTriangles, swept_volume.GetType()==SWEPT_BOX ? &mWorldEdgeNormals : NULL, mEdgeFlags, mGeomStream, group_flags, true, false, groupsMask); mNbCachedStatic = mGeomStream.size(); mNbCachedT = mWorldTriangles.size(); mNbCachedEN = mWorldEdgeNormals.size(); mNbCachedF = mEdgeFlags.size(); FindTouchedGeometry(user_data, DYNAMIC_BOX, mWorldTriangles, swept_volume.GetType()==SWEPT_BOX ? &mWorldEdgeNormals : NULL, mEdgeFlags, mGeomStream, group_flags, false, true, groupsMask); // We can't early exit when no tris are touched since we also have to handle the boxes FindTouchedCCTs( nb_boxes, boxes, box_user_data, nb_capsules, capsules, capsule_user_data, DYNAMIC_BOX ); mFirstUpdate = false; } if(debugData) { debugData->addAABB(mCachedTBV, NewCachedBox ? NX_ARGB_RED : NX_ARGB_GREEN); debugData->addAABB(world_box, NX_ARGB_YELLOW); } }
bool CapsuleController::getWorldBox(NxExtendedBounds3& box) const { // box.setCenterExtents(position, NxVec3(radius, height, radius)); box.setCenterExtents(position, NxVec3(radius, radius+height*0.5f, radius)); return true; }
bool FindTouchedGeometry( void* user_data, const NxExtendedBounds3& worldBounds, // ### we should also accept other volumes TriArray& world_triangles, TriArray* world_edge_normals, IntArray& edge_flags, IntArray& geom_stream, NxU32 group_flags, bool static_shapes, bool dynamic_shapes, const NxGroupsMask* groupsMask) { NX_ASSERT(user_data); NxScene* scene = (NxScene*)user_data; NxExtendedVec3 Origin; // Will be TouchedGeom::mOffset worldBounds.getCenter(Origin); // Reserve a stack buffer big enough to hold all shapes in the world. This is a lazy approach that is // acceptable here since the total number of shapes is limited to 64K anyway, which would "only" consume // 256 Kb on the stack (hence, stack overflow is unlikely). // ### TODO: the new callback mechanism would allow us to use less memory here // NxU32 total = scene->getNbStaticShapes() + scene->getNbDynamicShapes(); NxU32 total = scene->getTotalNbShapes(); NxShape** buffer = (NxShape**)NxAlloca(total*sizeof(NxShape*)); // Find touched *boxes* i.e. touched objects' AABBs in the world // We collide against dynamic shapes too, to get back dynamic boxes/etc // TODO: add active groups in interface! NxU32 Flags = 0; if(static_shapes) Flags |= NX_STATIC_SHAPES; if(dynamic_shapes) Flags |= NX_DYNAMIC_SHAPES; // ### this one is dangerous NxBounds3 tmpBounds; // LOSS OF ACCURACY tmpBounds.min.x = (float)worldBounds.min.x; tmpBounds.min.y = (float)worldBounds.min.y; tmpBounds.min.z = (float)worldBounds.min.z; tmpBounds.max.x = (float)worldBounds.max.x; tmpBounds.max.y = (float)worldBounds.max.y; tmpBounds.max.z = (float)worldBounds.max.z; NxU32 nbTouchedBoxes = scene->overlapAABBShapes(tmpBounds, NxShapesType(Flags), total, buffer, NULL, group_flags, groupsMask); // Early exit if no AABBs found if(!nbTouchedBoxes) return false; NX_ASSERT(nbTouchedBoxes<=total); // Else we just trashed some stack memory // Loop through touched world AABBs NxShape** touched = buffer; while(nbTouchedBoxes--) { // Get current shape NxShape* shape = *touched++; // Filtering // Discard all CCT shapes, i.e. kinematic actors we created ourselves. We don't need to collide with them since they're surrounded // by the real CCT volume - and collisions with those are handled elsewhere. We use the userData field for filtering because that's // really our only valid option (filtering groups are already used by clients and we don't have control over them, clients might // create other kinematic actors that we may want to keep here, etc, etc) if(size_t(shape->userData)=='CCTS') continue; // Discard if not collidable // PT: this shouldn't be possible at this point since: // - the SF flag is only used for compounds // - the AF flag is already tested in scene query // - we shouldn't get compound shapes here if(shape->getFlag(NX_SF_DISABLE_COLLISION)) continue; // Ubi (EA) : Discarding Triggers : if ( shape->getFlag(NX_TRIGGER_ENABLE) ) continue; // PT: here you might want to disable kinematic objects. // Output shape to stream NxShapeType type = shape->getType(); if(type==NX_SHAPE_SPHERE) outputSphereToStream((NxSphereShape*)shape, shape, geom_stream, Origin); else if(type==NX_SHAPE_CAPSULE) outputCapsuleToStream((NxCapsuleShape*)shape, shape, geom_stream, Origin); else if(type==NX_SHAPE_BOX) outputBoxToStream((NxBoxShape*)shape, shape, geom_stream, Origin); else if(type==NX_SHAPE_MESH) outputMeshToStream((NxTriangleMeshShape*)shape, shape, geom_stream, world_triangles, world_edge_normals, edge_flags, Origin, tmpBounds); else if(type==NX_SHAPE_HEIGHTFIELD) outputHeightFieldToStream((NxHeightFieldShape*)shape, shape, geom_stream, world_triangles, world_edge_normals, edge_flags, Origin, tmpBounds); else if(type==NX_SHAPE_CONVEX) outputConvexToStream((NxConvexShape*)shape, shape, geom_stream, world_triangles, world_edge_normals, edge_flags, Origin, tmpBounds); } return true; }