void Sc::ParticleSystemSim::visualizeInteractions(Cm::RenderOutput& out) { out << PxU32(PxDebugColor::eARGB_GREEN) << Cm::RenderOutput::LINES; for(PxU32 i=0; i < mParticlePacketShapes.size(); i++) { ParticlePacketShape* particleShape = mParticlePacketShapes[i]; ParticleElementRbElementInteraction** interactions = particleShape->getInteractions(); PxU32 nbInteractions = particleShape->getInteractionsCount(); while(nbInteractions--) { ParticleElementRbElementInteraction* interaction = *interactions++; PX_ASSERT(interaction->getType() == InteractionType::ePARTICLE_BODY); const PxBounds3 bounds = particleShape->getBounds(); PX_ALIGN(16, PxTransform absPos); interaction->getRbShape().getAbsPoseAligned(&absPos); out << bounds.getCenter() << absPos.p; } } }
void testBoundsMesh( const Gu::InternalTriangleMeshData& meshData, const PxTransform& world2Shape, const PxTransform& s2w, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh, const PxBounds3& worldBounds, PxcContactCellMeshCallback& callback) { // Find colliding triangles. // Setup an OBB for the fluid particle cell (in local space of shape) // assuming uniform scaling in most cases, using the pose as box rotation // if scaling is non-uniform, the bounding box is conservative PxBounds3 boundsInMesh; PX_ASSERT(!worldBounds.isEmpty()); boundsInMesh = PxBounds3::transformFast(world2Shape, worldBounds); Gu::Box vertexSpaceOBB(boundsInMesh.getCenter(), boundsInMesh.getExtents(), PxMat33(PxIdentity)); if(!idtScaleMesh) meshScaling.transformQueryBounds(vertexSpaceOBB.center, vertexSpaceOBB.extents); // Set collider flags (has to be done each time again!) Gu::RTreeMidphaseData hmd; meshData.mCollisionModel.getRTreeMidphaseData(hmd); MPT_SET_CONTEXT("flui", s2w, meshScaling); PX_UNUSED(s2w); Gu::MeshRayCollider::collideOBB(vertexSpaceOBB, true, hmd, callback); }
Bounds3 Bounds3::ToManaged(PxBounds3 bounds) { PxVec3 center = bounds.getCenter(); PxVec3 extents = bounds.getExtents(); return Bounds3(MathUtil::PxVec3ToVector3(center - extents), MathUtil::PxVec3ToVector3(center + extents)); }
void physx::Pt::collideCellsWithStaticMesh(ParticleCollData* collData, const LocalCellHash& localCellHash, const GeometryUnion& meshShape, const PxTransform& world2Shape, const PxTransform& shape2World, PxReal /*cellSize*/, PxReal /*collisionRange*/, PxReal proxRadius, const PxVec3& /*packetCorner*/) { PX_ASSERT(collData); PX_ASSERT(localCellHash.isHashValid); PX_ASSERT(localCellHash.numParticles <= PT_SUBPACKET_PARTICLE_LIMIT_COLLISION); PX_ASSERT(localCellHash.numHashEntries <= PT_LOCAL_HASH_SIZE_MESH_COLLISION); const PxTriangleMeshGeometryLL& meshShapeData = meshShape.get<const PxTriangleMeshGeometryLL>(); const TriangleMesh* meshData = meshShapeData.meshData; PX_ASSERT(meshData); // mesh bounds in world space (conservative) const PxBounds3 shapeBounds = meshData->getLocalBoundsFast().transformSafe(world2Shape.getInverse() * meshShapeData.scale); const bool idtScaleMesh = meshShapeData.scale.isIdentity(); Cm::FastVertex2ShapeScaling meshScaling; if(!idtScaleMesh) meshScaling.init(meshShapeData.scale); // process the particle cells for(PxU32 c = 0; c < localCellHash.numHashEntries; c++) { const ParticleCell& cell = localCellHash.hashEntries[c]; if(cell.numParticles == PX_INVALID_U32) continue; PxBounds3 cellBounds; cellBounds.setEmpty(); PxBounds3 cellBoundsNew(PxBounds3::empty()); PxU32* it = localCellHash.particleIndices + cell.firstParticle; const PxU32* end = it + cell.numParticles; for(; it != end; it++) { const ParticleCollData& particle = collData[*it]; cellBounds.include(particle.oldPos); cellBoundsNew.include(particle.newPos); } PX_ASSERT(!cellBoundsNew.isEmpty()); cellBoundsNew.fattenFast(proxRadius); cellBounds.include(cellBoundsNew); if(!cellBounds.intersects(shapeBounds)) continue; // early out if (inflated) cell doesn't intersect mesh bounds // opcode query: cell bounds against shape bounds in unscaled mesh space PxcContactCellMeshCallback callback(collData, &(localCellHash.particleIndices[cell.firstParticle]), cell.numParticles, *meshData, meshScaling, proxRadius, NULL, shape2World); testBoundsMesh(*meshData, world2Shape, meshScaling, idtScaleMesh, cellBounds, callback); } }
PxBounds3 Sc::ClothCore::getWorldBounds() const { const PxVec3& center = reinterpret_cast<const PxVec3&>(mLowLevelCloth->getBoundingBoxCenter()); const PxVec3& extent = reinterpret_cast<const PxVec3&>(mLowLevelCloth->getBoundingBoxScale()); PxBounds3 localBounds = PxBounds3::centerExtents(center, extent); PX_ASSERT(!localBounds.isEmpty()); return PxBounds3::transformFast(getGlobalPose(), localBounds); }
void physx::Pt::collideWithStaticHeightField(ParticleCollData* particleCollData, PxU32 numCollData, const GeometryUnion& heightFieldShape, PxReal proxRadius, const PxTransform& shape2World) { PX_ASSERT(particleCollData); const PxHeightFieldGeometryLL& hfGeom = heightFieldShape.get<const PxHeightFieldGeometryLL>(); const HeightFieldUtil hfUtil(hfGeom); for(PxU32 p = 0; p < numCollData; p++) { ParticleCollData& collData = particleCollData[p]; PxBounds3 particleBounds = PxBounds3::boundsOfPoints(collData.localOldPos, collData.localNewPos); PX_ASSERT(!particleBounds.isEmpty()); particleBounds.fattenFast(proxRadius); HeightFieldAabbTest test(particleBounds, hfUtil); HeightFieldAabbTest::Iterator itBegin = test.begin(); HeightFieldAabbTest::Iterator itEnd = test.end(); PxVec3 triangle[3]; collData.localDcNum = 0.0f; collData.localSurfaceNormal = PxVec3(0); collData.localSurfacePos = PxVec3(0); bool hasCC = (collData.localFlags & ParticleCollisionFlags::CC) > 0; PxVec3 tmpSurfaceNormal(0.0f); PxVec3 tmpSurfacePos(0.0f); PxVec3 tmpProxSurfaceNormal(0.0f); PxVec3 tmpProxSurfacePos(0.0f); PxReal tmpCCTime(collData.ccTime); PxReal tmpDistOldToSurface(0.0f); for(HeightFieldAabbTest::Iterator it = itBegin; it != itEnd; ++it) { it.getTriangleVertices(triangle); const PxVec3& origin = triangle[0]; PxVec3 e0, e1; e0 = triangle[1] - origin; e1 = triangle[2] - origin; PxU32 tmpFlags = collideWithMeshTriangle(tmpSurfaceNormal, tmpSurfacePos, tmpProxSurfaceNormal, tmpProxSurfacePos, tmpCCTime, tmpDistOldToSurface, collData.localOldPos, collData.localNewPos, origin, e0, e1, hasCC, collData.restOffset, proxRadius); updateCollShapeData(collData, hasCC, tmpFlags, tmpCCTime, tmpDistOldToSurface, tmpSurfaceNormal, tmpSurfacePos, tmpProxSurfaceNormal, tmpProxSurfacePos, shape2World); } } }
PxBounds3 RTree::refitAll(CallbackRefit& cb) { PxU8* treeNodes8 = PX_IS_X64 ? CAST_U8(get64BitBasePage()) : CAST_U8((mFlags & IS_DYNAMIC) ? NULL : mPages); PxBounds3 meshBounds = refitRecursive(treeNodes8, 0, NULL, 0, cb); for (PxU32 j = 1; j<mNumRootPages; j++) meshBounds.include( refitRecursive(treeNodes8, j, NULL, 0, cb) ); #ifdef PX_CHECKED validate(&cb); #endif return meshBounds; }
PxBounds3 NpCloth::getWorldBounds(float inflation) const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); const PxBounds3 bounds = mCloth.getWorldBounds(); PX_ASSERT(bounds.isValid()); // PT: unfortunately we can't just scale the min/max vectors, we need to go through center/extents. const PxVec3 center = bounds.getCenter(); const PxVec3 inflatedExtents = bounds.getExtents() * inflation; return PxBounds3::centerExtents(center, inflatedExtents); }
void NpSpatialIndex::overlap(const PxBounds3& aabb, PxSpatialOverlapCallback& callback) const { PX_SIMD_GUARD; PX_CHECK_AND_RETURN(aabb.isValid(), "PxSpatialIndex::overlap: aabb is not valid."); flushUpdates(); OverlapCallback cb(callback); PxBoxGeometry boxGeom(aabb.getExtents()); PxTransform xf(aabb.getCenter()); Sq::ShapeData shapeData(boxGeom, xf, 0.0f); // temporary rvalue not compatible with PX_NOCOPY mPruner->overlap(shapeData, cb); }
PxBounds3 NpArticulation::getWorldBounds(float inflation) const { NP_READ_CHECK(getOwnerScene()); PxBounds3 bounds = PxBounds3::empty(); for(PxU32 i=0; i < mArticulationLinks.size(); i++) { bounds.include(mArticulationLinks[i]->getWorldBounds()); } PX_ASSERT(bounds.isValid()); // PT: unfortunately we can't just scale the min/max vectors, we need to go through center/extents. const PxVec3 center = bounds.getCenter(); const PxVec3 inflatedExtents = bounds.getExtents() * inflation; return PxBounds3::centerExtents(center, inflatedExtents); }
bool AABBTreeOfVerticesBuilder::ComputeGlobalBox(const PxU32* primitives, PxU32 nb_prims, PxBounds3& global_box) const { // Checkings if(!primitives || !nb_prims) return false; // Initialize global box global_box.setEmpty(); // Loop through vertices for(PxU32 i=0;i<nb_prims;i++) { // Update global box global_box.include(mVertexArray[primitives[i]]); } return true; }
static void tessellateTriangle(PxU32& nbNewTris, const PxTriangle& tr, PxU32 index, TriArray& worldTriangles, IntArray& triIndicesArray, const PxBounds3& cullingBox, const CCTParams& params, PxU16& nbTessellation) { TessParams tp; tp.nbNewTris = 0; tp.index = index; tp.worldTriangles = &worldTriangles; tp.triIndicesArray = &triIndicesArray; tp.cullingBoxCenter = cullingBox.getCenter(); tp.cullingBoxExtents = cullingBox.getExtents(); tp.maxEdgeLength2 = params.mMaxEdgeLength2; tp.nbTessellation = 0; tessellateTriangle(&tp, tr.verts[0], tr.verts[1], tr.verts[2]); nbNewTris += tp.nbNewTris; nbTessellation += tp.nbTessellation; // nbTessellation += PxU16(tp.nbNewTris); }
PxBounds3 RTree::refitRecursive(PxU8* treeNodes8, PxU32 top, RTreePage* parentPage, PxU32 parentSubIndex, CallbackRefit& cb) { const PxReal eps = RTREE_INFLATION_EPSILON; RTreePage* tn = (RTreePage*)(treeNodes8 + top); PxBounds3 pageBound; for (PxU32 i = 0; i < RTREE_PAGE_SIZE; i++) { if (tn->isEmpty(i)) continue; PxU32 ptr = tn->ptrs[i]; Vec3V childMn, childMx; PxBounds3 childBound; if (ptr & 1) { // (ptr-1) clears the isLeaf bit (lowest bit) cb.recomputeBounds(ptr-1, childMn, childMx); // compute the bound around triangles V3StoreU(childMn, childBound.minimum); V3StoreU(childMx, childBound.maximum); // AP: doesn't seem worth vectorizing because of transposed layout tn->minx[i] = childBound.minimum.x - eps; // update page bounds for this leaf tn->miny[i] = childBound.minimum.y - eps; tn->minz[i] = childBound.minimum.z - eps; tn->maxx[i] = childBound.maximum.x + eps; tn->maxy[i] = childBound.maximum.y + eps; tn->maxz[i] = childBound.maximum.z + eps; } else childBound = refitRecursive(treeNodes8, ptr, tn, i, cb); if (i == 0) pageBound = childBound; else pageBound.include(childBound); } if (parentPage) { parentPage->minx[parentSubIndex] = pageBound.minimum.x - eps; parentPage->miny[parentSubIndex] = pageBound.minimum.y - eps; parentPage->minz[parentSubIndex] = pageBound.minimum.z - eps; parentPage->maxx[parentSubIndex] = pageBound.maximum.x + eps; parentPage->maxy[parentSubIndex] = pageBound.maximum.y + eps; parentPage->maxz[parentSubIndex] = pageBound.maximum.z + eps; } return pageBound; }
void NpSpatialIndex::update(PxSpatialIndexItemId id, const PxBounds3& bounds) { PX_SIMD_GUARD; PX_CHECK_AND_RETURN(bounds.isValid(), "PxSpatialIndex::update: bounds are not valid."); mPruner->updateObjects(&id, &bounds); mPendingUpdates = true; }
void NpSpatialIndex::sweep(const PxBounds3& aabb, const PxVec3& unitDir, PxReal maxDist, PxSpatialLocationCallback& callback) const { PX_SIMD_GUARD; PX_CHECK_AND_RETURN(aabb.isValid(), "PxSpatialIndex::sweep: aabb is not valid."); PX_CHECK_AND_RETURN(unitDir.isFinite() && unitDir.isNormalized(), "PxSpatialIndex::sweep: unitDir is not valid."); PX_CHECK_AND_RETURN(maxDist > 0.0f, "PxSpatialIndex::sweep: distance must be positive"); flushUpdates(); LocationCallback cb(callback); PxBoxGeometry boxGeom(aabb.getExtents()); PxTransform xf(aabb.getCenter()); Sq::ShapeData shapeData(boxGeom, xf, 0.0f); // temporary rvalue not compatible with PX_NOCOPY mPruner->sweep(shapeData, unitDir, maxDist, cb); }
static bool computeMassAndDiagInertia(Ext::InertiaTensorComputer& inertiaComp, PxVec3& diagTensor, PxQuat& orient, PxReal& massOut, PxVec3& coM, bool lockCOM, const PxRigidBody& body, const char* errorStr) { // The inertia tensor and center of mass is relative to the actor at this point. Transform to the // body frame directly if CoM is specified, else use computed center of mass if (lockCOM) { inertiaComp.translate(-coM); // base the tensor on user's desired center of mass. } else { //get center of mass - has to be done BEFORE centering. coM = inertiaComp.getCenterOfMass(); //the computed result now needs to be centered around the computed center of mass: inertiaComp.center(); } // The inertia matrix is now based on the body's center of mass desc.massLocalPose.p massOut = inertiaComp.getMass(); diagTensor = PxDiagonalize(inertiaComp.getInertia(), orient); if ((diagTensor.x > 0.0f) && (diagTensor.y > 0.0f) && (diagTensor.z > 0.0f)) return true; else { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "%s: inertia tensor has negative components (ill-conditioned input expected). Approximation for inertia tensor will be used instead.", errorStr); // keep center of mass but use the AABB as a crude approximation for the inertia tensor PxBounds3 bounds = body.getWorldBounds(); PxTransform pose = body.getGlobalPose(); bounds = PxBounds3::transformFast(pose.getInverse(), bounds); Ext::InertiaTensorComputer it(false); it.setBox(bounds.getExtents()); it.scaleDensity(massOut / it.getMass()); PxMat33 inertia = it.getInertia(); diagTensor = PxVec3(inertia.column0.x, inertia.column1.y, inertia.column2.z); orient = PxQuat(PxIdentity); return true; } }
void Sc::ParticleSystemSim::visualizeInteractions(Cm::RenderOutput& out) { out << PxU32(PxDebugColor::eARGB_GREEN) << Cm::RenderOutput::LINES; for(PxU32 i=0; i < mParticlePacketShapes.size(); i++) { ParticlePacketShape* particleShape = mParticlePacketShapes[i]; Cm::Range<ParticleElementRbElementInteraction*const> interactions = particleShape->getPacketShapeInteractions(); for (; !interactions.empty(); interactions.popFront()) { ParticleElementRbElementInteraction*const interaction = interactions.front(); PX_ASSERT(interaction->getType() == PX_INTERACTION_TYPE_PARTICLE_BODY); PxBounds3 bounds = particleShape->getBounds(); out << bounds.getCenter() << interaction->getRbShape().getAbsPose().p; } } }
void Sc::ParticleSystemSim::visualizeSpatialGrid(Cm::RenderOutput& out) { PxReal packetSize = getCore().getGridSize(); for (PxU32 i = 0; i < mParticlePacketShapes.size(); i++) { ParticlePacketShape* particleShape = mParticlePacketShapes[i]; PxBounds3 bounds = particleShape->getBounds(); PxVec3 centerGridSpace = bounds.getCenter() / packetSize; for (PxU32 d = 0; d < 3; d++) bounds.minimum[d] = Ps::floor(centerGridSpace[d]) * packetSize; for (PxU32 d = 0; d < 3; d++) bounds.maximum[d] = Ps::ceil(centerGridSpace[d]) * packetSize; out << PxU32(PxDebugColor::eARGB_BLUE) << Cm::DebugBox(bounds); } }
void obj_DroppedItem::UpdateObjectPositionAfterCreation() { if(!PhysicsObject) return; PxActor* actor = PhysicsObject->getPhysicsActor(); if(!actor) return; PxBounds3 pxBbox = actor->getWorldBounds(); PxVec3 pxCenter = pxBbox.getCenter(); // place object on the ground, to prevent excessive bouncing { PxRaycastHit hit; PxSceneQueryFilterData filter(PxFilterData(COLLIDABLE_STATIC_MASK, 0, 0, 0), PxSceneQueryFilterFlag::eSTATIC); if(g_pPhysicsWorld->raycastSingle(PxVec3(pxCenter.x, pxCenter.y, pxCenter.z), PxVec3(0, -1, 0), 50.0f, PxSceneQueryFlag::eIMPACT, hit, filter)) { SetPosition(r3dPoint3D(hit.impact.x, hit.impact.y+0.1f, hit.impact.z)); } } }
PxSpatialIndexItemId NpSpatialIndex::insert(PxSpatialIndexItem& item, const PxBounds3& bounds) { PX_SIMD_GUARD; PX_CHECK_AND_RETURN_VAL(bounds.isValid(), "PxSpatialIndex::insert: bounds are not valid.", PX_SPATIAL_INDEX_INVALID_ITEM_ID); Sq::PrunerHandle output; Sq::PrunerPayload payload; payload.data[0] = reinterpret_cast<size_t>(&item); mPruner->addObjects(&output, &bounds, &payload); mPendingUpdates = true; return output; }
void FDestructibleMeshEditorViewportClient::Draw( const FSceneView* View,FPrimitiveDrawInterface* PDI ) { FEditorViewportClient::Draw(View, PDI); #if WITH_APEX const bool DrawChunkMarker = true; UDestructibleComponent* Comp = PreviewDestructibleComp.Get(); if (Comp) { if (Comp->DestructibleMesh != NULL && Comp->DestructibleMesh->FractureSettings != NULL) { if (Comp->DestructibleMesh->ApexDestructibleAsset != NULL) { NxDestructibleAsset* Asset = Comp->DestructibleMesh->ApexDestructibleAsset; const NxRenderMeshAsset* RenderMesh = Asset->getRenderMeshAsset(); for (uint32 i=0; i < Asset->getChunkCount(); ++i) { int32 PartIdx = Asset->getPartIndex(i); int32 BoneIdx = i+1; if ( SelectedChunkIndices.Contains(i) ) { PxBounds3 PBounds = RenderMesh->getBounds(PartIdx); FVector Center = P2UVector(PBounds.getCenter()) + Comp->GetBoneLocation(Comp->GetBoneName(BoneIdx)); FVector Extent = P2UVector(PBounds.getExtents()); FBox Bounds(Center - Extent, Center + Extent); DrawWireBox(PDI, Bounds, FColor::Blue, SDPG_World); } } } } } #endif // WITH_APEX }
void ApexParticles::CreateEmitter(NxApexSDK* gApexSDK, NxApexScene* gApexScene) { NxApexEmitterAsset* emitterAsset; physx::apex::NxApexAsset* asset = reinterpret_cast<physx::apex::NxApexAsset*>(gApexSDK->getNamedResourceProvider()->getResource(NX_APEX_EMITTER_AUTHORING_TYPE_NAME, "testSpriteEmitter4ParticleFluidIos")); if (asset) { emitterAsset = static_cast<NxApexEmitterAsset*> (asset); } //NxApexEmitterAsset* emitterAsset = static_cast<NxApexEmitterAsset*> (gApexSDK->createAsset(asParams, "testMeshEmitter4ParticleIos.apb")); gApexSDK->forceLoadAssets(); NxParameterized::Interface* descParams = emitterAsset->getDefaultActorDesc(); PX_ASSERT(descParams); if (!descParams) { return; } // Set Actor pose //NxParameterized::setParamMat44( *descParams, "initialPose", pose ); NxApexEmitterActor* emitterActor; if(descParams->areParamsOK()) { emitterActor = static_cast<NxApexEmitterActor*>(emitterAsset->createApexActor(*descParams,*gApexScene)); if(emitterActor) { emitterActor->setCurrentPosition(PxVec3(0.0f, 1.0f, 0.0f)); emitterActor->startEmit( true ); //emitterActor->setLifetimeRange(physx::apex::NxRange<PxF32>(1,5)); //emitterActor->setRateRange(physx::apex::NxRange<PxF32>(10, 10)); } } PxBounds3 b; b.setInfinite(); mRenderVolume = mIofxModule->createRenderVolume(*gApexScene, b, 0, true ); emitterActor->setPreferredRenderVolume( mRenderVolume ); }
bool AABBTreeOfAABBsBuilder::ComputeGlobalBox(const PxU32* primitives, PxU32 nb_prims, PxBounds3& global_box) const { // Checkings if(!primitives || !nb_prims) return false; // Initialize global box global_box = mAABBArray[primitives[0]]; // Loop through boxes for(PxU32 i=1;i<nb_prims;i++) { // Update global box global_box.include(mAABBArray[primitives[i]]); } return true; }
void EmitterGeomSphereShellImpl::computeFillPositions(physx::Array<PxVec3>& positions, physx::Array<PxVec3>& velocities, const PxTransform& pose, const PxVec3& scale, float objRadius, PxBounds3& outBounds, QDSRand&) const { PX_UNUSED(scale); const float bigRadius = *mRadius + *mShellThickness; const float radiusSquared = bigRadius * bigRadius; const float hemisphere = *mHemisphere; const float sphereCapBaseHeight = -bigRadius + 2 * bigRadius * hemisphere; const float sphereCapBaseRadius = PxSqrt(radiusSquared - sphereCapBaseHeight * sphereCapBaseHeight); const float horizontalExtents = hemisphere < 0.5f ? bigRadius : sphereCapBaseRadius; // we're not doing anything with the velocities array PX_UNUSED(velocities); // we don't want anything outside the emitter uint32_t numX = (uint32_t)PxFloor(horizontalExtents / objRadius); numX -= numX % 2; uint32_t numY = (uint32_t)PxFloor((bigRadius - sphereCapBaseHeight) / objRadius); numY -= numY % 2; uint32_t numZ = (uint32_t)PxFloor(horizontalExtents / objRadius); numZ -= numZ % 2; for (float x = -(numX * objRadius); x <= bigRadius - objRadius; x += 2 * objRadius) { for (float y = -(numY * objRadius); y <= bigRadius - objRadius; y += 2 * objRadius) { for (float z = -(numZ * objRadius); z <= bigRadius - objRadius; z += 2 * objRadius) { const float magnitudeSquare = PxVec3(x, y, z).magnitudeSquared(); if ((magnitudeSquare > (*mRadius + objRadius) * (*mRadius + objRadius)) && (magnitudeSquare < (bigRadius - objRadius) * (bigRadius - objRadius))) { positions.pushBack(pose.transform(PxVec3(x, y, z))); outBounds.include(positions.back()); } } } } }
static PX_FORCE_INLINE bool planesAABBOverlap(const PxBounds3& a, const PxPlane* p, PxU32& out_clip_mask, PxU32 in_clip_mask) { //------------------------------------------------------------------------ // Convert the AABB from (minimum,maximum) form into (center,half-diagonal). // Note that we could get rid of these six subtractions and three // multiplications if the AABB was originally expressed in (center, // half-diagonal) form. //------------------------------------------------------------------------ PxVec3 m = a.getCenter(); // get center of AABB ((minimum+maximum)*0.5f) PxVec3 d = a.maximum; d-=m; // get positive half-diagonal (maximum - center) //------------------------------------------------------------------------ // Evaluate through all active frustum planes. We determine the relation // between the AABB and a plane by using the concept of "near" and "far" // vertices originally described by Zhang (and later by Moeller). Our // variant here uses 3 fabs ops, 6 muls, 7 adds and two floating point // comparisons per plane. The routine early-exits if the AABB is found // to be outside any of the planes. The loop also constructs a new output // clip mask. Most FPUs have a native single-cycle fabsf() operation. //------------------------------------------------------------------------ PxU32 Mask = 1; // current mask index (1,2,4,8,..) PxU32 TmpOutClipMask = 0; // initialize output clip mask into empty. while(Mask<=in_clip_mask) // keep looping while we have active planes left... { if(in_clip_mask & Mask) // if clip plane is active, process it.. { const float NP = d.x*PxAbs(p->n.x) + d.y*PxAbs(p->n.y) + d.z*PxAbs(p->n.z); const float MP = m.x*p->n.x + m.y*p->n.y + m.z*p->n.z + p->d; if(NP < MP) // near vertex behind the clip plane... return false; // .. so there is no intersection.. if((-NP) < MP) // near and far vertices on different sides of plane.. TmpOutClipMask |= Mask; // .. so update the clip mask... } Mask+=Mask; // mk = (1<<plane) p++; // advance to next plane } out_clip_mask = TmpOutClipMask; // copy output value (temp used to resolve aliasing!) return true; // indicate that AABB intersects frustum }
void CreateParticleAABB(ParticleData& particleData, const PxBounds3& aabb, const PxVec3& vel, float distance) { PxVec3 aabbDim = aabb.getExtents() * 2.0f; unsigned sideNumX = (unsigned)PxMax(1.0f, physx::shdfnd::floor(aabbDim.x / distance)); unsigned sideNumY = (unsigned)PxMax(1.0f, physx::shdfnd::floor(aabbDim.y / distance)); unsigned sideNumZ = (unsigned)PxMax(1.0f, physx::shdfnd::floor(aabbDim.z / distance)); for(unsigned i=0; i<sideNumX; i++) for(unsigned j=0; j<sideNumY; j++) for(unsigned k=0; k<sideNumZ; k++) { if(particleData.numParticles >= particleData.maxParticles) break; PxVec3 p = PxVec3(i*distance,j*distance,k*distance); p += aabb.minimum; particleData.positions[particleData.numParticles] = p; particleData.velocities[particleData.numParticles] = vel; particleData.numParticles++; } }
PxU32 PxBroadPhaseExt::createRegionsFromWorldBounds(PxBounds3* regions, const PxBounds3& globalBounds, PxU32 nbSubdiv, PxU32 upAxis) { PX_CHECK_MSG(globalBounds.isValid(), "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid bounds provided!"); PX_CHECK_MSG(upAxis<3, "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid up-axis provided!"); const PxVec3& min = globalBounds.minimum; const PxVec3& max = globalBounds.maximum; const float dx = (max.x - min.x) / float(nbSubdiv); const float dy = (max.y - min.y) / float(nbSubdiv); const float dz = (max.z - min.z) / float(nbSubdiv); PxU32 nbRegions = 0; PxVec3 currentMin, currentMax; for(PxU32 j=0;j<nbSubdiv;j++) { for(PxU32 i=0;i<nbSubdiv;i++) { if(upAxis==0) { currentMin = PxVec3(min.x, min.y + dy * float(i), min.z + dz * float(j)); currentMax = PxVec3(max.x, min.y + dy * float(i+1), min.z + dz * float(j+1)); } else if(upAxis==1) { currentMin = PxVec3(min.x + dx * float(i), min.y, min.z + dz * float(j)); currentMax = PxVec3(min.x + dx * float(i+1), max.y, min.z + dz * float(j+1)); } else if(upAxis==2) { currentMin = PxVec3(min.x + dx * float(i), min.y + dy * float(j), min.z); currentMax = PxVec3(min.x + dx * float(i+1), min.y + dy * float(j+1), max.z); } regions[nbRegions++] = PxBounds3(currentMin, currentMax); } } return nbRegions; }
bool Character::buildMotion(Acclaim::AMCData &amcData, Motion &motion, PxU32 start, PxU32 end) { using namespace Acclaim; if (mASFData == NULL) return false; motion.mNbFrames = end - start + 1; motion.mMotionData = SAMPLE_NEW(MotionData)[motion.mNbFrames]; // compute bounds of all the motion data on normalized frame PxBounds3 bounds = PxBounds3::empty(); for (PxU32 i = start; i < end; i++) { Acclaim::FrameData &frameData = amcData.mFrameData[i]; PxTransform rootTransform(PxVec3(0.0f), EulerAngleToQuat(frameData.mRootOrientation)); for (PxU32 j = 0; j < mASFData->mNbBones; j++) { PxTransform t = computeBoneTransform(rootTransform, mASFData->mBones[j], frameData.mBoneFrameData); bounds.include(t.p); } } Acclaim::FrameData& firstFrame = amcData.mFrameData[0]; Acclaim::FrameData& lastFrame = amcData.mFrameData[amcData.mNbFrames-1]; // compute direction vector motion.mDistance = mCharacterScale * (lastFrame.mRootPosition - firstFrame.mRootPosition).magnitude(); PxVec3 firstPosition = firstFrame.mRootPosition; PX_UNUSED(firstPosition); PxVec3 firstAngles = firstFrame.mRootOrientation; PxQuat firstOrientation = EulerAngleToQuat(PxVec3(0, firstAngles.y, 0)); for (PxU32 i = 0; i < motion.mNbFrames; i++) { Acclaim::FrameData& frameData = amcData.mFrameData[i+start]; MotionData &motionData = motion.mMotionData[i]; // normalize y-rot by computing inverse quat from first frame // this makes all the motion aligned in the same (+ z) direction. PxQuat currentOrientation = EulerAngleToQuat(frameData.mRootOrientation); PxQuat qdel = firstOrientation.getConjugate() * currentOrientation; PxTransform rootTransform(PxVec3(0.0f), qdel); for (PxU32 j = 0; j < mNbBones; j++) { PxTransform boneTransform = computeBoneTransform(rootTransform, mASFData->mBones[j], frameData.mBoneFrameData); motionData.mBoneTransform[j] = boneTransform; } //PxReal y = mCharacterScale * (frameData.mRootPosition.y - firstPosition.y) - bounds.minimum.y; motionData.mRootTransform = PxTransform(PxVec3(0.0f, -bounds.minimum.y, 0.0f), PxQuat(PxIdentity)); } // now make the motion cyclic by linear interpolating root position and joint angles const PxU32 windowSize = 10; for (PxU32 i = 0; i <= windowSize; i++) { PxU32 j = motion.mNbFrames - 1 - windowSize + i; PxReal t = PxReal(i) / PxReal(windowSize); MotionData& motion_i = motion.mMotionData[0]; MotionData& motion_j = motion.mMotionData[j]; // lerp root translation PxVec3 blendedRootPos = (1.0f - t) * motion_j.mRootTransform.p + t * motion_i.mRootTransform.p; for (PxU32 k = 0; k < mNbBones; k++) { PxVec3 pj = motion_j.mRootTransform.p + motion_j.mBoneTransform[k].p; PxVec3 pi = motion_i.mRootTransform.p + motion_i.mBoneTransform[k].p; PxVec3 p = (1.0f - t) * pj + t * pi; motion_j.mBoneTransform[k].p = p - blendedRootPos; } motion_j.mRootTransform.p = blendedRootPos; } return true; }
void Cct::findTouchedGeometry( const InternalCBData_FindTouchedGeom* userData, const PxExtendedBounds3& worldBounds, // ### we should also accept other volumes TriArray& worldTriangles, IntArray& triIndicesArray, IntArray& geomStream, const CCTFilter& filter, const CCTParams& params) { PX_ASSERT(userData); const PxInternalCBData_FindTouchedGeom* internalData = static_cast<const PxInternalCBData_FindTouchedGeom*>(userData); PxScene* scene = internalData->scene; Cm::RenderBuffer* renderBuffer = internalData->renderBuffer; PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset getCenter(worldBounds, Origin); // 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! PxSceneQueryFilterFlags sqFilterFlags; if(filter.mStaticShapes) sqFilterFlags |= PxSceneQueryFilterFlag::eSTATIC; if(filter.mDynamicShapes) sqFilterFlags |= PxSceneQueryFilterFlag::eDYNAMIC; if(filter.mFilterCallback) { if(filter.mPreFilter) sqFilterFlags |= PxSceneQueryFilterFlag::ePREFILTER; if(filter.mPostFilter) sqFilterFlags |= PxSceneQueryFilterFlag::ePOSTFILTER; } // ### this one is dangerous const PxBounds3 tmpBounds(toVec3(worldBounds.minimum), toVec3(worldBounds.maximum)); // LOSS OF ACCURACY // PT: unfortunate conversion forced by the PxGeometry API PxVec3 center = tmpBounds.getCenter(), extents = tmpBounds.getExtents(); PxShape* hits[100]; PxU32 size = 100; const PxSceneQueryFilterData sceneQueryFilterData = filter.mFilterData ? PxSceneQueryFilterData(*filter.mFilterData, sqFilterFlags) : PxSceneQueryFilterData(sqFilterFlags); PxI32 numberHits = scene->overlapMultiple(PxBoxGeometry(extents), PxTransform(center), hits, size, sceneQueryFilterData, filter.mFilterCallback); for(PxI32 i = 0; i < numberHits; i++) { PxShape* shape = hits[i]; if(shape == NULL) continue; // 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(internalData->cctShapeHashSet->contains(shape)) continue; // Ubi (EA) : Discarding Triggers : if(shape->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) continue; // PT: here you might want to disable kinematic objects. // Output shape to stream const PxTransform globalPose = getShapeGlobalPose(*shape); const PxGeometryType::Enum type = shape->getGeometryType(); // ### VIRTUAL! if(type==PxGeometryType::eSPHERE) outputSphereToStream(shape, globalPose, geomStream, Origin); else if(type==PxGeometryType::eCAPSULE) outputCapsuleToStream(shape, globalPose, geomStream, Origin); else if(type==PxGeometryType::eBOX) outputBoxToStream(shape, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); else if(type==PxGeometryType::eTRIANGLEMESH) outputMeshToStream(shape, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); else if(type==PxGeometryType::eHEIGHTFIELD) outputHeightFieldToStream(shape, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); else if(type==PxGeometryType::eCONVEXMESH) outputConvexToStream(shape, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds); else if(type==PxGeometryType::ePLANE) outputPlaneToStream(shape, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); } }
static void outputHeightFieldToStream( PxShape* hfShape, const PxTransform& heightfieldPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, Cm::RenderBuffer* renderBuffer) { PX_ASSERT(hfShape->getGeometryType() == PxGeometryType::eHEIGHTFIELD); // Do AABB-mesh query PxHeightFieldGeometry hfGeom; hfShape->getHeightFieldGeometry(hfGeom); PxBoxGeometry boxGeom(tmpBounds.getExtents()); PxTransform boxPose; boxPose.p = tmpBounds.getCenter(); boxPose.q = PxQuat::createIdentity(); // Collide AABB against current heightfield PxFindOverlapTriangleMeshUtil overlapUtil; const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, hfGeom, heightfieldPose); const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); TouchedMesh* touchedMesh = (TouchedMesh*)reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32)); touchedMesh->mType = TouchedGeomType::eMESH; // ptchernev: seems to work touchedMesh->mUserData = hfShape; touchedMesh->mOffset = origin; touchedMesh->mNbTris = nbTouchedTris; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); if(params.mSlopeLimit!=0.0f) { // Reserve memory for incoming triangles // PxTriangle* TouchedTriangles = reserve(worldTriangles, nbTouchedTris); // Loop through touched triangles PxU32 nbCreatedTris = 0; for(PxU32 i=0; i < nbTouchedTris; i++) { const PxU32 triangleIndex = indices[i]; // Compute triangle in world space, add to array PxTriangle currentTriangle; PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); nbCreatedTris += nbNewTris; if(!nbNewTris) { worldTriangles.pushBack(currentTriangle); triIndicesArray.pushBack(triangleIndex); nbCreatedTris++; } } touchedMesh->mNbTris = nbCreatedTris; } else { // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = reserve(worldTriangles, nbTouchedTris); // Loop through touched triangles for(PxU32 i=0; i < nbTouchedTris; i++) { const PxU32 triangleIndex = indices[i]; // Compute triangle in world space, add to array PxTriangle& currentTriangle = *TouchedTriangles++; PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; triIndicesArray.pushBack(triangleIndex); } } if(gVisualizeTouchedTris) visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, &worldTriangles[0], renderBuffer, offset, params.mUpDirection); }