// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "common/PxProfileZone.h" #include "geometry/PxSphereGeometry.h" #include "geometry/PxCapsuleGeometry.h" #include "geometry/PxBoxGeometry.h" #include "geometry/PxTriangleMeshGeometry.h" #include "geometry/PxConvexMeshGeometry.h" #include "geometry/PxHeightFieldGeometry.h" #include "geometry/PxConvexMesh.h" #include "geometry/PxMeshQuery.h" #include "extensions/PxTriangleMeshExt.h" #include "PxScene.h" #include "CctInternalStructs.h" #include "CmRenderOutput.h" #include "PsMathUtils.h" #include "GuIntersectionTriangleBox.h" #include "GuSIMDHelpers.h" static const bool gVisualizeTouchedTris = false; static const float gDebugVisOffset = 0.01f; using namespace physx; using namespace Cct; using namespace Gu; using namespace Cm; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PT: HINT: disable gUsePartialUpdates for easier visualization static void visualizeTouchedTriangles(PxU32 nbTrisToRender, PxU32 startIndex, const PxTriangle* triangles, RenderBuffer* renderBuffer, const PxVec3& offset, const PxVec3& upDirection) { if(!renderBuffer) return; PxVec3 yy = -offset; yy += upDirection * gDebugVisOffset; // PT: move slightly in the up direction for(PxU32 i=0; inbTessellation++; // PT: this one is safe, v0/v1/v2 can always be V4Loaded if(!intersectTriangleBox_Unsafe(tp->cullingBoxCenter, tp->cullingBoxExtents, v0, v1, v2)) return; PxU32 code; { const PxVec3 edge0 = v0 - v1; const PxVec3 edge1 = v1 - v2; const PxVec3 edge2 = v2 - v0; const float maxEdgeLength2 = tp->maxEdgeLength2; const bool split0 = edge0.magnitudeSquared()>maxEdgeLength2; const bool split1 = edge1.magnitudeSquared()>maxEdgeLength2; const bool split2 = edge2.magnitudeSquared()>maxEdgeLength2; code = (PxU32(split2)<<2)|(PxU32(split1)<<1)|PxU32(split0); } // PT: using Vec3p to make sure we can safely V4LoadU these vertices const Vec3p m0 = (v0 + v1)*0.5f; const Vec3p m1 = (v1 + v2)*0.5f; const Vec3p m2 = (v2 + v0)*0.5f; switch(code) { case 0: // 000: no split { tp->worldTriangles->pushBack(PxTriangle(v0, v1, v2)); tp->triIndicesArray->pushBack(tp->index); tp->nbNewTris++; } break; case 1: // 001: split edge0 { tessellateTriangleRecursive(tp, v0, m0, v2); tessellateTriangleRecursive(tp, m0, v1, v2); } break; case 2: // 010: split edge1 { tessellateTriangleRecursive(tp, v0, v1, m1); tessellateTriangleRecursive(tp, v0, m1, v2); } break; case 3: // 011: split edge0/edge1 { tessellateTriangleRecursive(tp, v0, m0, m1); tessellateTriangleRecursive(tp, v0, m1, v2); tessellateTriangleRecursive(tp, m0, v1, m1); } break; case 4: // 100: split edge2 { tessellateTriangleRecursive(tp, v0, v1, m2); tessellateTriangleRecursive(tp, v1, v2, m2); } break; case 5: // 101: split edge0/edge2 { tessellateTriangleRecursive(tp, v0, m0, m2); tessellateTriangleRecursive(tp, m0, v1, m2); tessellateTriangleRecursive(tp, m2, v1, v2); } break; case 6: // 110: split edge1/edge2 { tessellateTriangleRecursive(tp, v0, v1, m1); tessellateTriangleRecursive(tp, v0, m1, m2); tessellateTriangleRecursive(tp, m2, m1, v2); } break; case 7: // 111: split edge0/edge1/edge2 { tessellateTriangleRecursive(tp, v0, m0, m2); tessellateTriangleRecursive(tp, m0, v1, m1); tessellateTriangleRecursive(tp, m2, m1, v2); tessellateTriangleRecursive(tp, m0, m1, m2); } break; }; } static void tessellateTriangle(PxU32& nbNewTris, const TrianglePadded& 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; tessellateTriangleRecursive(&tp, tr.verts[0], tr.verts[1], tr.verts[2]); nbNewTris += tp.nbNewTris; nbTessellation += tp.nbTessellation; // nbTessellation += PxU16(tp.nbNewTris); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void outputPlaneToStream(PxShape* planeShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer) { PX_ASSERT(planeShape->getGeometryType() == PxGeometryType::ePLANE); const PxF32 length = (tmpBounds.maximum - tmpBounds.minimum).magnitude(); PxVec3 center = toVec3(origin); const PxPlane plane = PxPlaneEquationFromTransform(globalPose); PxVec3 right, up; Ps::computeBasis(plane.n, right, up); right *= length; up *= length; const PxVec3 p = plane.project(center); const PxVec3 p0 = p - right + up; const PxVec3 p1 = p - right - up; const PxVec3 p2 = p + right - up; const PxVec3 p3 = p + right + up; const PxU32 nbTouchedTris = 2; const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); touchedMesh->mType = TouchedGeomType::eMESH; touchedMesh->mTGUserData = planeShape; touchedMesh->mActor = actor; touchedMesh->mOffset = origin; touchedMesh->mNbTris = nbTouchedTris; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); triIndicesArray.pushBack(0); triIndicesArray.pushBack(1); TouchedTriangles[0].verts[0] = p0 + offset; TouchedTriangles[0].verts[1] = p1 + offset; TouchedTriangles[0].verts[2] = p2 + offset; TouchedTriangles[1].verts[0] = p0 + offset; TouchedTriangles[1].verts[1] = p2 + offset; TouchedTriangles[1].verts[2] = p3 + offset; if(gVisualizeTouchedTris) visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); } static void outputSphereToStream(PxShape* sphereShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) { PX_ASSERT(sphereShape->getGeometryType() == PxGeometryType::eSPHERE); PxExtendedSphere WorldSphere; { PxSphereGeometry sg; sphereShape->getSphereGeometry(sg); WorldSphere.radius = sg.radius; WorldSphere.center.x = PxExtended(globalPose.p.x); WorldSphere.center.y = PxExtended(globalPose.p.y); WorldSphere.center.z = PxExtended(globalPose.p.z); } TouchedSphere* PX_RESTRICT touchedSphere = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedSphere)/sizeof(PxU32))); touchedSphere->mType = TouchedGeomType::eSPHERE; touchedSphere->mTGUserData = sphereShape; touchedSphere->mActor = actor; touchedSphere->mOffset = origin; touchedSphere->mRadius = WorldSphere.radius; touchedSphere->mCenter.x = float(WorldSphere.center.x - origin.x); touchedSphere->mCenter.y = float(WorldSphere.center.y - origin.y); touchedSphere->mCenter.z = float(WorldSphere.center.z - origin.z); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void outputCapsuleToStream(PxShape* capsuleShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) { PX_ASSERT(capsuleShape->getGeometryType() == PxGeometryType::eCAPSULE); PxExtendedCapsule WorldCapsule; { PxCapsuleGeometry cg; capsuleShape->getCapsuleGeometry(cg); PxVec3 p0 = cg.halfHeight * globalPose.q.getBasisVector0(); PxVec3 p1 = -p0; p0 += globalPose.p; p1 += globalPose.p; WorldCapsule.radius = cg.radius; WorldCapsule.p0.x = PxExtended(p0.x); WorldCapsule.p0.y = PxExtended(p0.y); WorldCapsule.p0.z = PxExtended(p0.z); WorldCapsule.p1.x = PxExtended(p1.x); WorldCapsule.p1.y = PxExtended(p1.y); WorldCapsule.p1.z = PxExtended(p1.z); } TouchedCapsule* PX_RESTRICT touchedCapsule = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedCapsule)/sizeof(PxU32))); touchedCapsule->mType = TouchedGeomType::eCAPSULE; touchedCapsule->mTGUserData = capsuleShape; touchedCapsule->mActor = actor; touchedCapsule->mOffset = origin; touchedCapsule->mRadius = WorldCapsule.radius; touchedCapsule->mP0.x = float(WorldCapsule.p0.x - origin.x); touchedCapsule->mP0.y = float(WorldCapsule.p0.y - origin.y); touchedCapsule->mP0.z = float(WorldCapsule.p0.z - origin.z); touchedCapsule->mP1.x = float(WorldCapsule.p1.x - origin.x); touchedCapsule->mP1.y = float(WorldCapsule.p1.y - origin.y); touchedCapsule->mP1.z = float(WorldCapsule.p1.z - origin.z); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void outputBoxToStream( PxShape* boxShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, PxU16& nbTessellation) { PX_ASSERT(boxShape->getGeometryType() == PxGeometryType::eBOX); PxBoxGeometry bg; boxShape->getBoxGeometry(bg); //8 verts in local space. const PxF32 dx = bg.halfExtents.x; const PxF32 dy = bg.halfExtents.y; const PxF32 dz = bg.halfExtents.z; PxVec3 boxVerts[8]= { PxVec3(-dx,-dy,-dz), PxVec3(+dx,-dy,-dz), PxVec3(+dx,+dy,-dz), PxVec3(-dx,+dy,-dz), PxVec3(-dx,-dy,+dz), PxVec3(+dx,-dy,+dz), PxVec3(+dx,+dy,+dz), PxVec3(-dx,+dy,+dz) }; //Transform verts into world space. const PxVec3 pxOrigin = toVec3(origin); for(PxU32 i = 0; i < 8; i++) { boxVerts[i] = globalPose.transform(boxVerts[i]) - pxOrigin; } //Index of triangles. const PxU32 boxTris[12][3]= { {0,2,1}, {2,0,3}, //0,1,2,3 {3,6,2}, {6,3,7}, //3,2,6,7 {7,5,6}, {5,7,4}, //7,6,5,4 {4,1,5}, {1,4,0}, //4,5,1,0 {0,7,3}, {7,0,4}, //0,3,7,4 {2,5,1}, {5,2,6} //2,1,5,6 }; TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); touchedMesh->mType = TouchedGeomType::eMESH; touchedMesh->mTGUserData = boxShape; touchedMesh->mActor = actor; touchedMesh->mOffset = origin; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); if(params.mTessellation) { const PxBoxGeometry boxGeom(tmpBounds.getExtents()); const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); PxU32 nbCreatedTris = 0; for(PxU32 i=0; i<12; i++) { // Compute triangle in world space, add to array TrianglePadded currentTriangle; currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; PxU32 nbNewTris = 0; tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); nbCreatedTris += nbNewTris; } touchedMesh->mNbTris = nbCreatedTris; } else { touchedMesh->mNbTris = 12; // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = worldTriangles.reserve(12); for(PxU32 i=0; i<12; i++) { PxTriangle& currentTriangle = TouchedTriangles[i]; currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; triIndicesArray.pushBack(PX_INVALID_U32); } } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static PxU32 createInvisibleWalls(const CCTParams& params, const PxTriangle& currentTriangle, TriArray& worldTriangles, IntArray& triIndicesArray) { const PxF32 wallHeight = params.mInvisibleWallHeight; if(wallHeight==0.0f) return 0; PxU32 nbNewTris = 0; // Number of newly created tris const PxVec3& upDirection = params.mUpDirection; PxVec3 normal; currentTriangle.normal(normal); if(testSlope(normal, upDirection, params.mSlopeLimit)) { const PxVec3 upWall = upDirection*wallHeight; PxVec3 v0p = currentTriangle.verts[0] + upWall; PxVec3 v1p = currentTriangle.verts[1] + upWall; PxVec3 v2p = currentTriangle.verts[2] + upWall; // Extrude edge 0-1 PxVec3 faceNormal01; { // 0-1-0p const PxTriangle tri0_1_0p(currentTriangle.verts[0], currentTriangle.verts[1], v0p); worldTriangles.pushBack(tri0_1_0p); // 0p-1-1p const PxTriangle tri0p_1_1p(v0p, currentTriangle.verts[1], v1p); worldTriangles.pushBack(tri0p_1_1p); tri0p_1_1p.normal(faceNormal01); } // Extrude edge 1-2 PxVec3 faceNormal12; { // 1p-1-2p const PxTriangle tri1p_1_2p(v1p, currentTriangle.verts[1], v2p); worldTriangles.pushBack(tri1p_1_2p); // 2p-1-2 const PxTriangle tri2p_1_2(v2p, currentTriangle.verts[1], currentTriangle.verts[2]); worldTriangles.pushBack(tri2p_1_2); tri2p_1_2.normal(faceNormal12); } // Extrude edge 2-0 PxVec3 faceNormal20; { // 0p-2-0 const PxTriangle tri0p_2_0(v0p, currentTriangle.verts[2], currentTriangle.verts[0]); worldTriangles.pushBack(tri0p_2_0); // 0p-2p-2 const PxTriangle tri0p_2p_2(v0p, v2p, currentTriangle.verts[2]); worldTriangles.pushBack(tri0p_2p_2); tri0p_2p_2.normal(faceNormal20); } const PxU32 triIndex = PX_INVALID_U32; for(PxU32 i=0;i<6;i++) triIndicesArray.pushBack(triIndex); nbNewTris += 6; } return nbNewTris; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void outputMeshToStream( PxShape* meshShape, const PxRigidActor* actor, const PxTransform& meshPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) { PX_ASSERT(meshShape->getGeometryType() == PxGeometryType::eTRIANGLEMESH); // Do AABB-mesh query PxTriangleMeshGeometry triGeom; meshShape->getTriangleMeshGeometry(triGeom); const PxBoxGeometry boxGeom(tmpBounds.getExtents()); const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); // Collide AABB against current mesh PxMeshOverlapUtil overlapUtil; const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, triGeom, meshPose); const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); touchedMesh->mType = TouchedGeomType::eMESH; touchedMesh->mTGUserData = meshShape; touchedMesh->mActor = actor; touchedMesh->mOffset = origin; touchedMesh->mNbTris = nbTouchedTris; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); if(params.mSlopeLimit!=0.0f) { if(!params.mTessellation) { // 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 TrianglePadded currentTriangle; PxMeshQuery::getTriangle(triGeom, meshPose, 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 { const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); // 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 TrianglePadded currentTriangle; PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); nbCreatedTris += nbNewTris; if(!nbNewTris) { /* worldTriangles.pushBack(currentTriangle); triIndicesArray.pushBack(triangleIndex); nbCreatedTris++;*/ tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); nbCreatedTris += nbNewTris; // printf("Tesselate: %d new tris\n", nbNewTris); } } touchedMesh->mNbTris = nbCreatedTris; } } else { if(!params.mTessellation) { // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = worldTriangles.reserve(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(triGeom, meshPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; triIndicesArray.pushBack(triangleIndex); } } else { const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); PxU32 nbCreatedTris = 0; for(PxU32 i=0; i < nbTouchedTris; i++) { const PxU32 triangleIndex = indices[i]; // Compute triangle in world space, add to array TrianglePadded currentTriangle; PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; PxU32 nbNewTris = 0; tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); // printf("Tesselate: %d new tris\n", nbNewTris); nbCreatedTris += nbNewTris; } touchedMesh->mNbTris = nbCreatedTris; } } if(gVisualizeTouchedTris) visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void outputHeightFieldToStream( PxShape* hfShape, const PxRigidActor* actor, const PxTransform& heightfieldPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) { PX_ASSERT(hfShape->getGeometryType() == PxGeometryType::eHEIGHTFIELD); // Do AABB-mesh query PxHeightFieldGeometry hfGeom; hfShape->getHeightFieldGeometry(hfGeom); const PxBoxGeometry boxGeom(tmpBounds.getExtents()); const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); // Collide AABB against current heightfield PxMeshOverlapUtil overlapUtil; const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, hfGeom, heightfieldPose); const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); touchedMesh->mType = TouchedGeomType::eMESH; // ptchernev: seems to work touchedMesh->mTGUserData = hfShape; touchedMesh->mActor = actor; touchedMesh->mOffset = origin; touchedMesh->mNbTris = nbTouchedTris; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); if(params.mSlopeLimit!=0.0f) { if(!params.mTessellation) { // 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 TrianglePadded 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 { const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); // 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 TrianglePadded currentTriangle; PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); nbCreatedTris += nbNewTris; if(!nbNewTris) { tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); nbCreatedTris += nbNewTris; // printf("Tesselate: %d new tris\n", nbNewTris); } } touchedMesh->mNbTris = nbCreatedTris; } } else { if(!params.mTessellation) { // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = worldTriangles.reserve(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); } } else { const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); PxU32 nbCreatedTris = 0; for(PxU32 i=0; i < nbTouchedTris; i++) { const PxU32 triangleIndex = indices[i]; // Compute triangle in world space, add to array TrianglePadded currentTriangle; PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); currentTriangle.verts[0] += offset; currentTriangle.verts[1] += offset; currentTriangle.verts[2] += offset; PxU32 nbNewTris = 0; tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); // printf("Tesselate: %d new tris\n", nbNewTris); nbCreatedTris += nbNewTris; } touchedMesh->mNbTris = nbCreatedTris; } } if(gVisualizeTouchedTris) visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void outputConvexToStream(PxShape* convexShape, const PxRigidActor* actor, const PxTransform& absPose_, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) { PX_ASSERT(convexShape->getGeometryType() == PxGeometryType::eCONVEXMESH); PxConvexMeshGeometry cg; convexShape->getConvexMeshGeometry(cg); PX_ASSERT(cg.convexMesh); // Do AABB-mesh query PxU32* TF; // Collide AABB against current mesh // The overlap function doesn't exist for convexes so let's just dump all tris PxConvexMesh& cm = *cg.convexMesh; // PT: convex triangles are not exposed anymore so we need to access convex polygons & triangulate them // PT: TODO: this is copied from "DrawObjects", move this to a shared place. Actually a helper directly in PxConvexMesh would be useful. PxU32 Nb = 0; { const PxU32 nbPolys = cm.getNbPolygons(); const PxU8* polygons = cm.getIndexBuffer(); for(PxU32 i=0;i(PxAlloca(sizeof(PxU32)*Nb*3)); PxU32* t = TF; for(PxU32 i=0;i(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); touchedMesh->mType = TouchedGeomType::eMESH; touchedMesh->mTGUserData = convexShape; touchedMesh->mActor = actor; touchedMesh->mOffset = origin; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); const PxVec3* verts = cm.getVertices(); // Loop through touched triangles if(params.mTessellation) { const PxBoxGeometry boxGeom(tmpBounds.getExtents()); const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); PxU32 nbCreatedTris = 0; while(Nb--) { // Compute triangle in world space, add to array TrianglePadded currentTriangle; const PxU32 vref0 = *TF++; const PxU32 vref1 = *TF++; const PxU32 vref2 = *TF++; currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); PxU32 nbNewTris = 0; tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); nbCreatedTris += nbNewTris; } touchedMesh->mNbTris = nbCreatedTris; } else { // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = worldTriangles.reserve(Nb); touchedMesh->mNbTris = Nb; while(Nb--) { // Compute triangle in world space, add to array PxTriangle& currentTriangle = *TouchedTriangles++; const PxU32 vref0 = *TF++; const PxU32 vref1 = *TF++; const PxU32 vref2 = *TF++; currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); triIndicesArray.pushBack(PX_INVALID_U32); } } if(gVisualizeTouchedTris) visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PxU32 Cct::getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData) { PX_ASSERT(userData); const PxInternalCBData_FindTouchedGeom* internalData = static_cast(userData); PxScene* scene = internalData->scene; return scene->getSceneQueryStaticTimestamp(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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, PxU16& nbTessellation) { PX_ASSERT(userData); const PxInternalCBData_FindTouchedGeom* internalData = static_cast(userData); PxScene* scene = internalData->scene; PX_PROFILE_ZONE("CharacterController.findTouchedGeometry", PxU64(reinterpret_cast(scene))); 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! PxQueryFlags sqFilterFlags; if(filter.mStaticShapes) sqFilterFlags |= PxQueryFlag::eSTATIC; if(filter.mDynamicShapes) sqFilterFlags |= PxQueryFlag::eDYNAMIC; if(filter.mFilterCallback) { if(filter.mPreFilter) sqFilterFlags |= PxQueryFlag::ePREFILTER; if(filter.mPostFilter) sqFilterFlags |= PxQueryFlag::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 const PxVec3 center = tmpBounds.getCenter(); const PxVec3 extents = tmpBounds.getExtents(); const PxU32 size = 100; PxOverlapHit hits[size]; PxQueryFilterData sceneQueryFilterData = filter.mFilterData ? PxQueryFilterData(*filter.mFilterData, sqFilterFlags) : PxQueryFilterData(sqFilterFlags); PxOverlapBuffer hitBuffer(hits, size); sceneQueryFilterData.flags |= PxQueryFlag::eNO_BLOCK; // fix for DE8255 scene->overlap(PxBoxGeometry(extents), PxTransform(center), hitBuffer, sceneQueryFilterData, filter.mFilterCallback); PxU32 numberHits = hitBuffer.getNbAnyHits(); for(PxU32 i = 0; i < numberHits; i++) { const PxOverlapHit& hit = hitBuffer.getAnyHit(i); PxShape* shape = hit.shape; PxRigidActor* actor = hit.actor; if(!shape || !actor) 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. 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, *actor); const PxGeometryType::Enum type = shape->getGeometryType(); // ### VIRTUAL! if(type==PxGeometryType::eSPHERE) outputSphereToStream (shape, actor, globalPose, geomStream, Origin); else if(type==PxGeometryType::eCAPSULE) outputCapsuleToStream (shape, actor, globalPose, geomStream, Origin); else if(type==PxGeometryType::eBOX) outputBoxToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, nbTessellation); else if(type==PxGeometryType::eTRIANGLEMESH) outputMeshToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); else if(type==PxGeometryType::eHEIGHTFIELD) outputHeightFieldToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); else if(type==PxGeometryType::eCONVEXMESH) outputConvexToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); else if(type==PxGeometryType::ePLANE) outputPlaneToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "characterkinematic/PxControllerBehavior.h" #include "CctCharacterControllerManager.h" #include "CctObstacleContext.h" // #### hmmm, in the down case, isn't reported length too big ? It contains our artificial up component, // that might confuse the user static void fillCCTHit(PxControllerHit& hit, const SweptContact& contact, const PxVec3& dir, float length, Controller* controller) { hit.controller = controller->getPxController(); hit.worldPos = contact.mWorldPos; hit.worldNormal = contact.mWorldNormal; hit.dir = dir; hit.length = length; } static const PxU32 defaultBehaviorFlags = 0; PxU32 Cct::shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) { Controller* controller = static_cast(userData)->controller; PxControllerShapeHit hit; fillCCTHit(hit, contact, dir, length, controller); hit.shape = const_cast(reinterpret_cast(contact.mGeom->mTGUserData)); hit.actor = const_cast(contact.mGeom->mActor); hit.triangleIndex = contact.mTriangleIndex; if(controller->mReportCallback) controller->mReportCallback->onShapeHit(hit); PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.shape, *hit.actor) : defaultBehaviorFlags; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static PX_FORCE_INLINE PxU32 handleObstacleHit(const PxObstacle& touchedObstacle, const ObstacleHandle& obstacleHandle, PxControllerObstacleHit& hit, const PxInternalCBData_OnHit* internalData, Controller* controller) { hit.userData = touchedObstacle.mUserData; const_cast(internalData)->touchedObstacle = &touchedObstacle; // (*) PT: TODO: revisit const_cast(internalData)->touchedObstacleHandle = obstacleHandle; if(controller->mReportCallback) controller->mReportCallback->onObstacleHit(hit); PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; return behaviorCB ? behaviorCB->getBehaviorFlags(touchedObstacle) : defaultBehaviorFlags; } PxU32 Cct::userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) { const PxInternalCBData_OnHit* internalData = static_cast(userData); Controller* controller = internalData->controller; const PxU32 objectCode = PxU32(size_t(contact.mGeom->mTGUserData)); const UserObjectType type = decodeType(objectCode); const PxU32 index = decodeIndex(objectCode); if(type==USER_OBJECT_CCT) { PX_ASSERT(indexgetCctManager()->getNbControllers()); Controller** controllers = controller->getCctManager()->getControllers(); Controller* other = controllers[index]; PxControllersHit hit; fillCCTHit(hit, contact, dir, length, controller); hit.other = other->getPxController(); if(controller->mReportCallback) controller->mReportCallback->onControllerHit(hit); PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.other) : defaultBehaviorFlags; } else if(type==USER_OBJECT_BOX_OBSTACLE) { PX_ASSERT(internalData->obstacles); PX_ASSERT(indexobstacles->mBoxObstacles.size()); PxControllerObstacleHit hit; fillCCTHit(hit, contact, dir, length, controller); const ObstacleContext::InternalBoxObstacle& obstacle = internalData->obstacles->mBoxObstacles[index]; const PxBoxObstacle& touchedObstacle = obstacle.mData; return handleObstacleHit(touchedObstacle, obstacle.mHandle , hit, internalData, controller); } else if(type==USER_OBJECT_CAPSULE_OBSTACLE) { PX_ASSERT(internalData->obstacles); PX_ASSERT(indexobstacles->mCapsuleObstacles.size()); PxControllerObstacleHit hit; fillCCTHit(hit, contact, dir, length, controller); const ObstacleContext::InternalCapsuleObstacle& obstacle = internalData->obstacles->mCapsuleObstacles[index]; const PxCapsuleObstacle& touchedObstacle = obstacle.mData; return handleObstacleHit(touchedObstacle, obstacle.mHandle, hit, internalData, controller); } else PX_ASSERT(0); return defaultBehaviorFlags; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////