// // 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 "GuSweepTriangleUtils.h" #include "GuSweepBoxTriangle_FeatureBased.h" #include "GuSweepBoxTriangle_SAT.h" #include "GuBV4_BoxOverlap_Internal.h" // PT: for box-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt. // We use: // - method 3 if the box is an AABB (SWEEP_AABB_IMPL is defined) // - method 2 if the box is an OBB (SWEEP_AABB_IMPL is undefined) #ifdef SWEEP_AABB_IMPL // PT: TODO: refactor structure (TA34704) struct RayParams { BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); #ifndef GU_BV4_USE_SLABS BV4_ALIGN16(Vec3p mData2_PaddedAligned); BV4_ALIGN16(Vec3p mFDir_PaddedAligned); BV4_ALIGN16(Vec3p mData_PaddedAligned); BV4_ALIGN16(Vec3p mLocalDir_PaddedAligned); #endif BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) }; #include "GuBV4_AABBAABBSweepTest.h" #else #include "GuBV4_BoxBoxOverlapTest.h" #endif #include "GuBV4_BoxSweep_Params.h" static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat_Padded) { const FloatV xxxV = V4GetX(p); const FloatV yyyV = V4GetY(p); const FloatV zzzV = V4GetZ(p); Vec4V ResV = V4Scale(V4LoadU_Safe(&mat_Padded.column0.x), xxxV); ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column1.x), yyyV)); ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column2.x), zzzV)); return ResV; } // PT: TODO: __fastcall removed to make it compile everywhere. Revisit. static bool /*__fastcall*/ triBoxSweep(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) { PxU32 VRef0, VRef1, VRef2; getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); const PxVec3& p0 = params->mVerts[VRef0]; const PxVec3& p1 = params->mVerts[VRef1]; const PxVec3& p2 = params->mVerts[VRef2]; // Don't bother doing the actual sweep test if the triangle is too far away if(1) { const float dp0 = p0.dot(params->mLocalDir_Padded); const float dp1 = p1.dot(params->mLocalDir_Padded); const float dp2 = p2.dot(params->mLocalDir_Padded); float TriMin = PxMin(dp0, dp1); TriMin = PxMin(TriMin, dp2); if(TriMin >= params->mOffset + params->mStabbedFace.mDistance) return false; } TrianglePadded triBoxSpace; const Vec4V transModelToBoxV = V4LoadU_Safe(¶ms->mTModelToBox_Padded.x); const Vec4V v0V = V4Add(multiply3x3V(V4LoadU_Safe(&p0.x), params->mRModelToBox_Padded), transModelToBoxV); V4StoreU_Safe(v0V, &triBoxSpace.verts[0].x); const Vec4V v1V = V4Add(multiply3x3V(V4LoadU_Safe(&p1.x), params->mRModelToBox_Padded), transModelToBoxV); V4StoreU_Safe(v1V, &triBoxSpace.verts[1].x); const Vec4V v2V = V4Add(multiply3x3V(V4LoadU_Safe(&p2.x), params->mRModelToBox_Padded), transModelToBoxV); V4StoreU_Safe(v2V, &triBoxSpace.verts[2].x); float Dist; if(triBoxSweepTestBoxSpace_inlined(triBoxSpace, params->mOriginalExtents_Padded, params->mOriginalDir_Padded*params->mStabbedFace.mDistance, params->mOneOverDir_Padded, 1.0f, Dist, params->mBackfaceCulling)) { // PT: TODO: these muls & divs may not be needed at all - we just pass the unit dir/inverse dir to the sweep code. Revisit. (TA34704) Dist *= params->mStabbedFace.mDistance; params->mOneOverDir_Padded = params->mOneOverOriginalDir / Dist; params->mStabbedFace.mDistance = Dist; params->mStabbedFace.mTriangleID = primIndex; // PT: TODO: revisit this (TA34704) params->mP0 = triBoxSpace.verts[0]; params->mP1 = triBoxSpace.verts[1]; params->mP2 = triBoxSpace.verts[2]; // V4StoreU_Safe(v0V, ¶ms->mP0.x); // V4StoreU_Safe(v1V, ¶ms->mP1.x); // V4StoreU_Safe(v2V, ¶ms->mP2.x); if(nodeSorting) { #ifdef SWEEP_AABB_IMPL #ifndef GU_BV4_USE_SLABS setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); #endif #else params->ShrinkOBB(Dist); #endif } return true; } return false; } namespace { class LeafFunction_BoxSweepClosest { public: static PX_FORCE_INLINE void doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex) { PxU32 nbToGo = getNbPrimitives(primIndex); do { triBoxSweep(params, primIndex); primIndex++; }while(nbToGo--); } }; class LeafFunction_BoxSweepAny { public: static PX_FORCE_INLINE Ps::IntBool doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex) { PxU32 nbToGo = getNbPrimitives(primIndex); do { if(triBoxSweep(params, primIndex)) return 1; primIndex++; }while(nbToGo--); return 0; } }; } // PT: TODO: refactor with sphere/capsule versions (TA34704) static PX_FORCE_INLINE bool computeImpactData(const Box& box, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const BoxSweepParams* PX_RESTRICT params, bool isDoubleSided, bool meshBothSides) { if(params->mStabbedFace.mTriangleID==PX_INVALID_U32) return false; // We didn't touch any triangle if(hit) { const float t = params->mStabbedFace.mDistance; hit->mTriangleID = params->mStabbedFace.mTriangleID; hit->mDistance = t; if(t==0.0f) { hit->mPos = PxVec3(0.0f); hit->mNormal = -dir; } else { // PT: TODO: revisit/optimize/use this (TA34704) const PxTriangle triInBoxSpace(params->mP0, params->mP1, params->mP2); PxHitFlags outFlags = PxHitFlag::Enum(0); computeBoxLocalImpact(hit->mPos, hit->mNormal, outFlags, box, params->mOriginalDir_Padded, triInBoxSpace, PxHitFlag::ePOSITION|PxHitFlag::eNORMAL, isDoubleSided, meshBothSides, t); } } return true; } template static PX_FORCE_INLINE void setupBoxSweepParams(ParamsT* PX_RESTRICT params, const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) { params->mStabbedFace.mTriangleID = PX_INVALID_U32; setupParamsFlags(params, flags); setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); prepareSweepData(localBox, localDir, maxDist, params); #ifdef SWEEP_AABB_IMPL params->mOrigin_Padded = localBox.center; #ifndef GU_BV4_USE_SLABS params->mLocalDir_PaddedAligned = localDir; setupRayData(params, maxDist, localBox.center, localDir); #endif #endif } #ifdef GU_BV4_USE_SLABS #include "GuBV4_Slabs.h" #endif #ifdef SWEEP_AABB_IMPL #include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" #include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" #ifdef GU_BV4_USE_SLABS #include "GuBV4_Slabs_KajiyaNoOrder.h" #include "GuBV4_Slabs_KajiyaOrdered.h" #endif #else #include "GuBV4_ProcessStreamOrdered_OBBOBB.h" #include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" #ifdef GU_BV4_USE_SLABS #include "GuBV4_Slabs_SwizzledNoOrder.h" #include "GuBV4_Slabs_SwizzledOrdered.h" #endif #endif #ifdef SWEEP_AABB_IMPL #define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER #define GU_BV4_PROCESS_STREAM_RAY_ORDERED #else #define GU_BV4_PROCESS_STREAM_NO_ORDER #define GU_BV4_PROCESS_STREAM_ORDERED #endif #include "GuBV4_Internal.h" #ifdef SWEEP_AABB_IMPL Ps::IntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) #else Ps::IntBool Sweep_OBB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) #endif { const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; BoxSweepParams Params; setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); if(tree.mNodes) { #ifdef SWEEP_AABB_IMPL if(Params.mEarlyExit) processStreamRayNoOrder<1, LeafFunction_BoxSweepAny>(tree, &Params); else processStreamRayOrdered<1, LeafFunction_BoxSweepClosest>(tree, &Params); #else if(Params.mEarlyExit) processStreamNoOrder(tree, &Params); else processStreamOrdered(tree, &Params); #endif } else doBruteForceTests(mesh->getNbTriangles(), &Params); return computeImpactData(localBox, localDir, hit, &Params, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); } // PT: box sweep callback version - currently not used namespace { struct BoxSweepParamsCB : BoxSweepParams { // PT: these new members are only here to call computeImpactData during traversal :( // PT: TODO: most of them may not be needed Box mBoxCB; // Box in original space (maybe not local/mesh space) PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space) const PxMat44* mWorldm_Aligned; PxU32 mFlags; SweepUnlimitedCallback mCallback; void* mUserData; float mMaxDist; bool mNodeSorting; }; class LeafFunction_BoxSweepCB { public: static PX_FORCE_INLINE Ps::IntBool doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) { PxU32 nbToGo = getNbPrimitives(primIndex); do { if(triBoxSweep(params, primIndex, params->mNodeSorting)) { // PT: TODO: in this version we must compute the impact data immediately, // which is a terrible idea in general, but I'm not sure what else I can do. SweepHit hit; const bool b = computeImpactData(params->mBoxCB, params->mDirCB, &hit, params, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); PX_ASSERT(b); // PT: then replicate part from BV4_BoxSweepSingle: if(b && params->mWorldm_Aligned) { // Move to world space // PT: TODO: optimize (TA34704) hit.mPos = params->mWorldm_Aligned->transform(hit.mPos); hit.mNormal = params->mWorldm_Aligned->rotate(hit.mNormal); } reportUnlimitedCallbackHit(params, hit); } primIndex++; }while(nbToGo--); return 0; } }; } // PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). // PT: 'worldm_Aligned' is only here to move back results to world space, but input is already in local space. #ifdef SWEEP_AABB_IMPL void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) #else void Sweep_OBB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) #endif { const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; BoxSweepParamsCB Params; Params.mBoxCB = localBox; Params.mDirCB = localDir; Params.mWorldm_Aligned = worldm_Aligned; Params.mFlags = flags; Params.mCallback = callback; Params.mUserData = userData; Params.mMaxDist = maxDist; Params.mNodeSorting = nodeSorting; setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); PX_ASSERT(!Params.mEarlyExit); if(tree.mNodes) { if(nodeSorting) { #ifdef SWEEP_AABB_IMPL processStreamRayOrdered<1, LeafFunction_BoxSweepCB>(tree, &Params); #else processStreamOrdered(tree, &Params); #endif } else { #ifdef SWEEP_AABB_IMPL processStreamRayNoOrder<1, LeafFunction_BoxSweepCB>(tree, &Params); #else processStreamNoOrder(tree, &Params); #endif } } else doBruteForceTests(mesh->getNbTriangles(), &Params); } // New callback-based box sweeps. Reuses code above, allow early exits. Some init code may be done in vain // since the leaf tests are not performed (we don't do box-sweeps-vs-tri since the box is only a BV around // the actual shape, say a convex) namespace { struct GenericSweepParamsCB : BoxSweepParams { MeshSweepCallback mCallback; void* mUserData; }; class LeafFunction_BoxSweepClosestCB { public: static PX_FORCE_INLINE void doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index) { PxU32 nbToGo = getNbPrimitives(prim_index); do { // PT: in the regular version we'd do a box-vs-triangle sweep test here // Instead we just grab the triangle and send it to the callback // // This can be used for regular "closest hit" sweeps, when the scale is not identity or // when the box is just around a more complex shape (e.g. convex). In this case we want // the calling code to compute a convex-triangle distance, and then we want to shrink // the ray/box while doing an ordered traversal. // // For "sweep all" or "sweep any" purposes we want to either report all hits or early exit // as soon as we find one. There is no need for shrinking or ordered traversals here. PxU32 VRef0, VRef1, VRef2; getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16); const PxVec3& p0 = params->mVerts[VRef0]; const PxVec3& p1 = params->mVerts[VRef1]; const PxVec3& p2 = params->mVerts[VRef2]; // Don't bother doing the actual sweep test if the triangle is too far away const float dp0 = p0.dot(params->mLocalDir_Padded); const float dp1 = p1.dot(params->mLocalDir_Padded); const float dp2 = p2.dot(params->mLocalDir_Padded); float TriMin = PxMin(dp0, dp1); TriMin = PxMin(TriMin, dp2); if(TriMin < params->mOffset + params->mStabbedFace.mDistance) { // const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; float Dist = params->mStabbedFace.mDistance; if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist)) return; // PT: TODO: we return here but the ordered path doesn't really support early exits (TA34704) if(DistmStabbedFace.mDistance) { params->mStabbedFace.mDistance = Dist; params->mStabbedFace.mTriangleID = prim_index; #ifdef SWEEP_AABB_IMPL #ifndef GU_BV4_USE_SLABS setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); #endif #else params->ShrinkOBB(Dist); #endif } } prim_index++; }while(nbToGo--); } }; class LeafFunction_BoxSweepAnyCB { public: static PX_FORCE_INLINE Ps::IntBool doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index) { PxU32 nbToGo = getNbPrimitives(prim_index); do { PxU32 VRef0, VRef1, VRef2; getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16); const PxVec3& p0 = params->mVerts[VRef0]; const PxVec3& p1 = params->mVerts[VRef1]; const PxVec3& p2 = params->mVerts[VRef2]; { // const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; float Dist = params->mStabbedFace.mDistance; if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist)) return 1; } prim_index++; }while(nbToGo--); return 0; } }; } #ifdef SWEEP_AABB_IMPL void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags) #else void GenericSweep_OBB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags) #endif { const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; GenericSweepParamsCB Params; Params.mCallback = callback; Params.mUserData = userData; setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); if(tree.mNodes) { #ifdef SWEEP_AABB_IMPL if(Params.mEarlyExit) processStreamRayNoOrder<1, LeafFunction_BoxSweepAnyCB>(tree, &Params); else processStreamRayOrdered<1, LeafFunction_BoxSweepClosestCB>(tree, &Params); #else if(Params.mEarlyExit) processStreamNoOrder(tree, &Params); else processStreamOrdered(tree, &Params); #endif } else doBruteForceTests(mesh->getNbTriangles(), &Params); }