GRK-Projekt/dependencies/physx-4.1/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h
2021-12-27 11:28:19 +01:00

519 lines
17 KiB
C++

//
// 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(&params->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, &params->mP0.x);
// V4StoreU_Safe(v1V, &params->mP1.x);
// V4StoreU_Safe(v2V, &params->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<class ParamsT>
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<LeafFunction_BoxSweepAny>(tree, &Params);
else
processStreamOrdered<LeafFunction_BoxSweepClosest>(tree, &Params);
#endif
}
else
doBruteForceTests<LeafFunction_BoxSweepAny, LeafFunction_BoxSweepClosest>(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<LeafFunction_BoxSweepCB>(tree, &Params);
#endif
}
else
{
#ifdef SWEEP_AABB_IMPL
processStreamRayNoOrder<1, LeafFunction_BoxSweepCB>(tree, &Params);
#else
processStreamNoOrder<LeafFunction_BoxSweepCB>(tree, &Params);
#endif
}
}
else
doBruteForceTests<LeafFunction_BoxSweepCB, LeafFunction_BoxSweepCB>(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(Dist<params->mStabbedFace.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<LeafFunction_BoxSweepAnyCB>(tree, &Params);
else
processStreamOrdered<LeafFunction_BoxSweepClosestCB>(tree, &Params);
#endif
}
else
doBruteForceTests<LeafFunction_BoxSweepAnyCB, LeafFunction_BoxSweepClosestCB>(mesh->getNbTriangles(), &Params);
}