diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/GeomUtils | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/GeomUtils')
267 files changed, 75588 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuAxes.h b/PhysX_3.4/Source/GeomUtils/headers/GuAxes.h new file mode 100644 index 00000000..b39d4d3b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuAxes.h @@ -0,0 +1,81 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_AXES_H +#define GU_AXES_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ +namespace Gu +{ + enum PointComponent + { + X_ = 0, + Y_ = 1, + Z_ = 2, + W_ = 3, + + PC_FORCE_DWORD = 0x7fffffff + }; + + enum AxisOrder + { + AXES_XYZ = (X_)|(Y_<<2)|(Z_<<4), + AXES_XZY = (X_)|(Z_<<2)|(Y_<<4), + AXES_YXZ = (Y_)|(X_<<2)|(Z_<<4), + AXES_YZX = (Y_)|(Z_<<2)|(X_<<4), + AXES_ZXY = (Z_)|(X_<<2)|(Y_<<4), + AXES_ZYX = (Z_)|(Y_<<2)|(X_<<4), + + AXES_FORCE_DWORD = 0x7fffffff + }; + + class Axes + { + public: + + PX_FORCE_INLINE Axes(AxisOrder order) + { + mAxis0 = PxU32((order ) & 3); + mAxis1 = PxU32((order>>2) & 3); + mAxis2 = PxU32((order>>4) & 3); + } + PX_FORCE_INLINE ~Axes() {} + + PxU32 mAxis0; + PxU32 mAxis1; + PxU32 mAxis2; + }; +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuBox.h b/PhysX_3.4/Source/GeomUtils/headers/GuBox.h new file mode 100644 index 00000000..36e7ef13 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuBox.h @@ -0,0 +1,246 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOX_H +#define GU_BOX_H + +/** \addtogroup geomutils +@{ +*/ + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "CmScaling.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + PX_PHYSX_COMMON_API void computeOBBPoints(PxVec3* PX_RESTRICT pts, const PxVec3& center, const PxVec3& extents, const PxVec3& base0, const PxVec3& base1, const PxVec3& base2); + + + /** + \brief Represents an oriented bounding box. + + As a center point, extents(radii) and a rotation. i.e. the center of the box is at the center point, + the box is rotated around this point with the rotation and it is 2*extents in width, height and depth. + */ + + /** + Box geometry + + The rot member describes the world space orientation of the box. + The center member gives the world space position of the box. + The extents give the local space coordinates of the box corner in the positive octant. + Dimensions of the box are: 2*extent. + Transformation to world space is: worldPoint = rot * localPoint + center + Transformation to local space is: localPoint = T(rot) * (worldPoint - center) + Where T(M) denotes the transpose of M. + */ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API Box + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE Box() + { + } + + /** + \brief Constructor + + \param origin Center of the OBB + \param extent Extents/radii of the obb. + \param base rotation to apply to the obb. + */ + //! Construct from center, extent and rotation + PX_FORCE_INLINE Box(const PxVec3& origin, const PxVec3& extent, const PxMat33& base) : rot(base), center(origin), extents(extent) + {} + + //! Copy constructor + PX_FORCE_INLINE Box(const Box& other) : rot(other.rot), center(other.center), extents(other.extents) + {} + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~Box() + { + } + + //! Assignment operator + PX_FORCE_INLINE const Box& operator=(const Box& other) + { + rot = other.rot; + center = other.center; + extents = other.extents; + return *this; + } + + /** + \brief Setups an empty box. + */ + PX_INLINE void setEmpty() + { + center = PxVec3(0); + extents = PxVec3(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL); + rot = PxMat33(PxIdentity); + } + + /** + \brief Checks the box is valid. + + \return true if the box is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f + if(extents.x < 0.0f) return false; + if(extents.y < 0.0f) return false; + if(extents.z < 0.0f) return false; + return true; + } + +///////////// + PX_FORCE_INLINE void setAxes(const PxVec3& axis0, const PxVec3& axis1, const PxVec3& axis2) + { + rot.column0 = axis0; + rot.column1 = axis1; + rot.column2 = axis2; + } + + PX_FORCE_INLINE PxVec3 rotate(const PxVec3& src) const + { + return rot * src; + } + + PX_FORCE_INLINE PxVec3 rotateInv(const PxVec3& src) const + { + return rot.transformTranspose(src); + } + + PX_FORCE_INLINE PxVec3 transform(const PxVec3& src) const + { + return rot * src + center; + } + + PX_FORCE_INLINE PxTransform getTransform() const + { + return PxTransform(center, PxQuat(rot)); + } + + PX_INLINE PxVec3 computeAABBExtent() const + { + const PxReal a00 = PxAbs(rot[0][0]); + const PxReal a01 = PxAbs(rot[0][1]); + const PxReal a02 = PxAbs(rot[0][2]); + + const PxReal a10 = PxAbs(rot[1][0]); + const PxReal a11 = PxAbs(rot[1][1]); + const PxReal a12 = PxAbs(rot[1][2]); + + const PxReal a20 = PxAbs(rot[2][0]); + const PxReal a21 = PxAbs(rot[2][1]); + const PxReal a22 = PxAbs(rot[2][2]); + + const PxReal ex = extents.x; + const PxReal ey = extents.y; + const PxReal ez = extents.z; + + return PxVec3( a00 * ex + a10 * ey + a20 * ez, + a01 * ex + a11 * ey + a21 * ez, + a02 * ex + a12 * ey + a22 * ez); + } + + /** + Computes the obb points. + \param pts [out] 8 box points + */ + PX_FORCE_INLINE void computeBoxPoints(PxVec3* PX_RESTRICT pts) const + { + Gu::computeOBBPoints(pts, center, extents, rot.column0, rot.column1, rot.column2); + } + + void create(const Gu::Capsule& capsule); + + PxMat33 rot; + PxVec3 center; + PxVec3 extents; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::Box) == 60); + + //! A padded version of Gu::Box, to safely load its data using SIMD + class BoxPadded : public Box + { + public: + PX_FORCE_INLINE BoxPadded() {} + PX_FORCE_INLINE ~BoxPadded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::BoxPadded) == 64); + + //! Transforms a shape space AABB to a vertex space AABB (conservative). + PX_FORCE_INLINE void computeVertexSpaceAABB(Gu::Box& vertexSpaceOBB, const PxBounds3& worldBounds, const PxTransform& world2Shape, const Cm::FastVertex2ShapeScaling& scaling, bool idtScaleMesh) + { + PX_ASSERT(!worldBounds.isEmpty()); + const PxBounds3 boundsInMesh = PxBounds3::transformFast(world2Shape, worldBounds); // transform bounds from world to shape (excluding mesh scale) + + vertexSpaceOBB.rot = PxMat33(PxIdentity); + if(idtScaleMesh) + { + vertexSpaceOBB.center = boundsInMesh.getCenter(); + vertexSpaceOBB.extents = boundsInMesh.getExtents(); + } + else + { + const PxBounds3 bounds = PxBounds3::basisExtent(scaling.getShape2VertexSkew() * boundsInMesh.getCenter(), scaling.getShape2VertexSkew(), boundsInMesh.getExtents()); + vertexSpaceOBB.center = bounds.getCenter(); + vertexSpaceOBB.extents = bounds.getExtents(); + } + } + +#if PX_VC + #pragma warning(pop) +#endif + +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuDistanceSegmentBox.h b/PhysX_3.4/Source/GeomUtils/headers/GuDistanceSegmentBox.h new file mode 100644 index 00000000..6f648d23 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuDistanceSegmentBox.h @@ -0,0 +1,57 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_BOX_H +#define GU_DISTANCE_SEGMENT_BOX_H + +#include "foundation/PxMat33.h" +#include "GuSegment.h" +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + + //! Compute the smallest distance from the (finite) line segment to the box. + PX_PHYSX_COMMON_API PxReal distanceSegmentBoxSquared( const PxVec3& segmentPoint0, const PxVec3& segmentPoint1, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* segmentParam = NULL, + PxVec3* boxParam = NULL); + + PX_FORCE_INLINE PxReal distanceSegmentBoxSquared(const Gu::Segment& segment, const Gu::Box& box, PxReal* t = NULL, PxVec3* p = NULL) + { + return distanceSegmentBoxSquared(segment.p0, segment.p1, box.center, box.extents, box.rot, t, p); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuDistanceSegmentSegment.h b/PhysX_3.4/Source/GeomUtils/headers/GuDistanceSegmentSegment.h new file mode 100644 index 00000000..5e729ca7 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuDistanceSegmentSegment.h @@ -0,0 +1,65 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_SEGMENT_H +#define GU_DISTANCE_SEGMENT_SEGMENT_H + +#include "common/PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + // This version fixes accuracy issues (e.g. TTP 4617), but needs to do 2 square roots in order + // to find the normalized direction and length of the segments, and then + // a division in order to renormalize the output + + PX_PHYSX_COMMON_API PxReal distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& dir0, PxReal extent0, + const PxVec3& origin1, const PxVec3& dir1, PxReal extent1, + PxReal* s=NULL, PxReal* t=NULL); + + PX_PHYSX_COMMON_API PxReal distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& extent0, + const PxVec3& origin1, const PxVec3& extent1, + PxReal* s=NULL, PxReal* t=NULL); + + PX_FORCE_INLINE PxReal distanceSegmentSegmentSquared( const Gu::Segment& segment0, + const Gu::Segment& segment1, + PxReal* s=NULL, PxReal* t=NULL) + { + return distanceSegmentSegmentSquared( segment0.p0, segment0.computeDirection(), + segment1.p0, segment1.computeDirection(), + s, t); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuIntersectionBoxBox.h b/PhysX_3.4/Source/GeomUtils/headers/GuIntersectionBoxBox.h new file mode 100644 index 00000000..6b318061 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuIntersectionBoxBox.h @@ -0,0 +1,54 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_BOX_BOX_H +#define GU_INTERSECTION_BOX_BOX_H + +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + PX_PHYSX_COMMON_API bool intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test); + + PX_FORCE_INLINE bool intersectOBBAABB(const Gu::Box& obb, const PxBounds3& aabb) + { + PxVec3 center = aabb.getCenter(); + PxVec3 extents = aabb.getExtents(); + return intersectOBBOBB(obb.extents, obb.center, obb.rot, extents, center, PxMat33(PxIdentity), true); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuIntersectionTriangleBox.h b/PhysX_3.4/Source/GeomUtils/headers/GuIntersectionTriangleBox.h new file mode 100644 index 00000000..4eb4e068 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuIntersectionTriangleBox.h @@ -0,0 +1,91 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_TRIANGLE_BOX_H +#define GU_INTERSECTION_TRIANGLE_BOX_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "foundation/PxMat33.h" + +namespace physx +{ +namespace Gu +{ + class Box; + class BoxPadded; + + /** + Tests if a triangle overlaps a box (AABB). This is the reference non-SIMD code. + + \param center [in] the box center + \param extents [in] the box extents + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox_ReferenceCode(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); + + /** + Tests if a triangle overlaps a box (AABB). This is the optimized SIMD code. + + WARNING: the function has various SIMD requirements, left to the calling code: + - function will load 4 bytes after 'center'. Make sure it's safe to load from there. + - function will load 4 bytes after 'extents'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p0'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p1'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p2'. Make sure it's safe to load from there. + If you can't guarantee these requirements, please use the non-SIMD reference code instead. + + \param center [in] the box center. + \param extents [in] the box extents + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox_Unsafe(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); + + /** + Tests if a triangle overlaps a box (OBB). + + There are currently no SIMD-related requirements for p0, p1, p2. + + \param box [in] the box + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox(const BoxPadded& box, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); +} // namespace Gu +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuRaycastTests.h b/PhysX_3.4/Source/GeomUtils/headers/GuRaycastTests.h new file mode 100644 index 00000000..86d70caf --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuRaycastTests.h @@ -0,0 +1,72 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_RAYCAST_TESTS_H +#define GU_RAYCAST_TESTS_H + +#include "CmPhysXCommon.h" +#include "foundation/PxSimpleTypes.h" +#include "PxQueryReport.h" +#include "PxGeometry.h" + +namespace physx +{ + // PT: TODO: why is PxHitFlag::eMESH_MULTIPLE used in the ray-vs-hf function, but not in the ray-vs-mesh function? + + // PT: we use a define to be able to quickly change the signature of all raycast functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom geometry object to raycast against + // \param[in] pose pose of geometry object + // \param[in] rayOrigin ray's origin + // \param[in] rayDir ray's unit dir + // \param[in] maxDist ray's length/max distance + // \param[in] hitFlags query behavior flags + // \param[in] maxHits max number of hits = size of 'hits' buffer + // \param[out] hits result buffer where to write raycast hits + #define GU_RAY_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, \ + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits + namespace Gu + { + // PT: function pointer for Geom-indexed raycast functions + // See GU_RAY_FUNC_PARAMS for function parameters details. + // \return number of hits written to 'hits' result buffer + // \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits. + typedef PxU32 (*RaycastFunc) (GU_RAY_FUNC_PARAMS); + + // PT: typedef for a bundle of all raycast functions, i.e. the function table itself (indexed by geom-type). + typedef RaycastFunc GeomRaycastTable[PxGeometryType::eGEOMETRY_COUNT]; + + // PT: retrieves the raycast function table (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomRaycastTable& getRaycastFuncTable(); + + } // namespace Gu +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuSIMDHelpers.h b/PhysX_3.4/Source/GeomUtils/headers/GuSIMDHelpers.h new file mode 100644 index 00000000..2aada303 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuSIMDHelpers.h @@ -0,0 +1,98 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SIMD_HELPERS_H +#define GU_SIMD_HELPERS_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "geometry/PxTriangle.h" +#include "foundation/PxMat33.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + //! A padded version of PxTriangle, to safely load its data using SIMD + class TrianglePadded : public PxTriangle + { + public: + PX_FORCE_INLINE TrianglePadded() {} + PX_FORCE_INLINE ~TrianglePadded() {} + PxU32 padding; + }; + + // PT: wrapper helper class to make sure we can safely load a PxVec3 using SIMD loads + // PT: TODO: refactor with PxVec3Pad + class Vec3p : public PxVec3 + { + public: + PX_FORCE_INLINE Vec3p() {} + PX_FORCE_INLINE ~Vec3p() {} + PX_FORCE_INLINE Vec3p(const PxVec3& p) : PxVec3(p) {} + PX_FORCE_INLINE Vec3p(float f) : PxVec3(f) {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Vec3p) == 16); + + //! A padded version of PxMat33, to safely load its data using SIMD + class PxMat33Padded : public PxMat33 + { + public: + explicit PX_FORCE_INLINE PxMat33Padded(const PxQuat& q) + { + using namespace Ps::aos; + const QuatV qV = V4LoadU(&q.x); + Vec3V column0V, column1V, column2V; + QuatGetMat33V(qV, column0V, column1V, column2V); +#if PX_ANDROID || (PX_LINUX && PX_ARM) + V3StoreU(column0V, column0); + V3StoreU(column1V, column1); + V3StoreU(column2V, column2); +#else + V4StoreU(column0V, &column0.x); + V4StoreU(column1V, &column1.x); + V4StoreU(column2V, &column2.x); +#endif + } + PX_FORCE_INLINE ~PxMat33Padded() {} + PX_FORCE_INLINE void operator=(const PxMat33& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + } + PxU32 padding; + }; + +} // namespace Gu +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/headers/GuSegment.h b/PhysX_3.4/Source/GeomUtils/headers/GuSegment.h new file mode 100644 index 00000000..d5f62122 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/headers/GuSegment.h @@ -0,0 +1,182 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SEGMENT_H +#define GU_SEGMENT_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "Ps.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + /** + \brief Represents a line segment. + + Line segment geometry + In some cases this structure will be used to represent the infinite line that passes point0 and point1. + */ + class Segment + { + public: + /** + \brief Constructor + */ + PX_INLINE Segment() + { + } + + /** + \brief Constructor + */ + PX_INLINE Segment(const PxVec3& _p0, const PxVec3& _p1) : p0(_p0), p1(_p1) + { + } + + /** + \brief Copy constructor + */ + PX_INLINE Segment(const Segment& seg) : p0(seg.p0), p1(seg.p1) + { + } + + /** + \brief Destructor + */ + PX_INLINE ~Segment() + { + } + + //! Assignment operator + PX_INLINE Segment& operator=(const Segment& other) + { + p0 = other.p0; + p1 = other.p1; + return *this; + } + + //! Equality operator + PX_INLINE bool operator==(const Segment& other) const + { + return (p0==other.p0 && p1==other.p1); + } + + //! Inequality operator + PX_INLINE bool operator!=(const Segment& other) const + { + return (p0!=other.p0 || p1!=other.p1); + } + + PX_INLINE const PxVec3& getOrigin() const + { + return p0; + } + + //! Return the vector from point0 to point1 + PX_INLINE PxVec3 computeDirection() const + { + return p1 - p0; + } + + //! Return the vector from point0 to point1 + PX_INLINE void computeDirection(PxVec3& dir) const + { + dir = p1 - p0; + } + + //! Return the center of the segment segment + PX_INLINE PxVec3 computeCenter() const + { + return (p0 + p1)*0.5f; + } + + PX_INLINE PxF32 computeLength() const + { + return (p1-p0).magnitude(); + } + + PX_INLINE PxF32 computeSquareLength() const + { + return (p1-p0).magnitudeSquared(); + } + + // PT: TODO: remove this one + //! Return the square of the length of vector from point0 to point1 + PX_INLINE PxReal lengthSquared() const + { + return ((p1 - p0).magnitudeSquared()); + } + + // PT: TODO: remove this one + //! Return the length of vector from point0 to point1 + PX_INLINE PxReal length() const + { + return ((p1 - p0).magnitude()); + } + + /* PX_INLINE void setOriginDirection(const PxVec3& origin, const PxVec3& direction) + { + p0 = p1 = origin; + p1 += direction; + }*/ + + /** + \brief Computes a point on the segment + + \param[out] pt point on segment + \param[in] t point's parameter [t=0 => pt = mP0, t=1 => pt = mP1] + */ + PX_INLINE void computePoint(PxVec3& pt, PxF32 t) const + { + pt = p0 + t * (p1 - p0); + } + + // PT: TODO: remove this one + //! Return the point at parameter t along the line: point0 + t*(point1-point0) + PX_INLINE PxVec3 getPointAt(PxReal t) const + { + return (p1 - p0)*t + p0; + } + + PxVec3 p0; //!< Start of segment + PxVec3 p1; //!< End of segment + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::Segment) == 24); +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp b/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp new file mode 100644 index 00000000..13560311 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuBounds.cpp @@ -0,0 +1,641 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBounds.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "GuInternal.h" +#include "CmUtils.h" +#include "GuConvexMesh.h" +#include "GuConvexMeshData.h" +#include "GuTriangleMesh.h" +#include "GuHeightFieldData.h" +#include "GuHeightField.h" +#include "PsFoundation.h" +#include "GuConvexUtilsInternal.h" +#include "GuBoxConversion.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxMat33& rot, const PxVec3& pos, const CenterExtentsPadded& bounds) +{ + c = rot.transform(bounds.mCenter) + pos; + ext = Cm::basisExtent(rot.column0, rot.column1, rot.column2, bounds.mExtents); +} + +// PT: this one may have duplicates in GuBV4_BoxSweep_Internal.h & GuBV4_Raycast.cpp +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33Padded& mat_Padded) +{ + Vec4V ResV = V4Scale(V4LoadU(&mat_Padded.column0.x), V4GetX(p)); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column1.x), V4GetY(p))); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column2.x), V4GetZ(p))); + return ResV; +} + +static PX_FORCE_INLINE void transformNoEmptyTestV(Vec3p& c, Vec3p& ext, const PxMat33Padded& rot, const PxVec3& pos, const CenterExtentsPadded& bounds) +{ + const Vec4V boundsCenterV = V4LoadU(&bounds.mCenter.x); // PT: this load is safe since extents follow center in the class + + // PT: unfortunately we can't V4LoadU 'pos' directly (it can come directly from users!). So we have to live with this for now: + const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pos.x)); + // PT: but eventually we'd like to use the "unsafe" version (e.g. by switching p&q in PxTransform), which would save 6 instructions on Win32 + const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), posV); +// const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), V4LoadU(&pos.x)); // ### unsafe + V4StoreU(cV, &c.x); + + // extended basis vectors + const Vec4V boundsExtentsV = V4LoadU(&bounds.mExtents.x); // PT: this load is safe since bounds are padded + const Vec4V c0V = V4Scale(V4LoadU(&rot.column0.x), V4GetX(boundsExtentsV)); + const Vec4V c1V = V4Scale(V4LoadU(&rot.column1.x), V4GetY(boundsExtentsV)); + const Vec4V c2V = V4Scale(V4LoadU(&rot.column2.x), V4GetZ(boundsExtentsV)); + + // find combination of base vectors that produces max. distance for each component = sum of abs() + Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V)); + extentsV = V4Add(extentsV, V4Abs(c2V)); + V4StoreU(extentsV, &ext.x); +} + +static PX_FORCE_INLINE PxU32 isNonIdentity(const PxVec3& scale) +{ + #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 + const PxU32* binary = reinterpret_cast<const PxU32*>(&scale.x); + return (binary[0] - IEEE_1_0)|(binary[1] - IEEE_1_0)|(binary[2] - IEEE_1_0); +} + +// PT: please don't inline this one - 300+ lines of rarely used code +static void computeScaledMatrix(PxMat33Padded& rot, const PxMeshScale& scale) +{ + rot = rot * scale.toMat33(); +} + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxTransform& transform, const PxMeshScale& scale, const CenterExtentsPadded& bounds) +{ + PxMat33Padded rot(transform.q); + + if(isNonIdentity(scale.scale)) + computeScaledMatrix(rot, scale); + + transformNoEmptyTestV(c, ext, rot, transform.p, bounds); +} + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxVec3& pos, const PxMat33Padded& rot, const PxMeshScale& scale, const CenterExtentsPadded& bounds) +{ + if(scale.isIdentity()) + transformNoEmptyTest(c, ext, rot, pos, bounds); + else + transformNoEmptyTest(c, ext, rot * scale.toMat33(), pos, bounds); +} + +static void computeMeshBounds(const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent) +{ + transformNoEmptyTest(origin, extent, pose, meshScale, *localSpaceBounds); +} + +static void computePlaneBounds(PxBounds3& bounds, const PxTransform& pose, float contactOffset, float inflation) +{ + // PT: A plane is infinite, so usually the bounding box covers the whole world. + // Now, in particular cases when the plane is axis-aligned, we can take + // advantage of this to compute a smaller bounding box anyway. + + // PT: we use PX_MAX_BOUNDS_EXTENTS to be compatible with PxBounds3::setMaximal, + // and to make sure that the value doesn't collide with the BP's sentinels. + const PxF32 bigValue = PX_MAX_BOUNDS_EXTENTS; +// const PxF32 bigValue = 1000000.0f; + PxVec3 minPt = PxVec3(-bigValue, -bigValue, -bigValue); + PxVec3 maxPt = PxVec3(bigValue, bigValue, bigValue); + + const PxVec3 planeNormal = pose.q.getBasisVector0(); + const PxPlane plane(pose.p, planeNormal); + + const float nx = PxAbs(planeNormal.x); + const float ny = PxAbs(planeNormal.y); + const float nz = PxAbs(planeNormal.z); + const float epsilon = 1e-6f; + const float oneMinusEpsilon = 1.0f - epsilon; + if(nx>oneMinusEpsilon && ny<epsilon && nz<epsilon) + { + if(planeNormal.x>0.0f) maxPt.x = -plane.d + contactOffset; + else minPt.x = plane.d - contactOffset; + } + else if(nx<epsilon && ny>oneMinusEpsilon && nz<epsilon) + { + if(planeNormal.y>0.0f) maxPt.y = -plane.d + contactOffset; + else minPt.y = plane.d - contactOffset; + } + else if(nx<epsilon && ny<epsilon && nz>oneMinusEpsilon) + { + if(planeNormal.z>0.0f) maxPt.z = -plane.d + contactOffset; + else minPt.z = plane.d - contactOffset; + } + + // PT: it is important to compute the min/max form directly without going through the + // center/extents intermediate form. With PX_MAX_BOUNDS_EXTENTS, those back-and-forth + // computations destroy accuracy. + + // PT: inflation actually destroys the bounds really. We keep it to please UTs but this is broken (DE10595). + // (e.g. for SQ 1% of PX_MAX_BOUNDS_EXTENTS is still a huge number, effectively making the AABB infinite and defeating the point of the above computation) + if(inflation!=1.0f) + { + const PxVec3 c = (maxPt + minPt)*0.5f; + const PxVec3 e = (maxPt - minPt)*0.5f*inflation; + minPt = c - e; + maxPt = c + e; + } + + bounds.minimum = minPt; + bounds.maximum = maxPt; +} + +static PX_FORCE_INLINE void inflateBounds(PxBounds3& bounds, const Vec3p& origin, const Vec3p& extents, float contactOffset, float inflation) +{ + Vec4V extentsV = V4LoadU(&extents.x); + extentsV = V4Add(extentsV, V4Load(contactOffset)); + extentsV = V4Scale(extentsV, FLoad(inflation)); + + const Vec4V originV = V4LoadU(&origin.x); + const Vec4V minV = V4Sub(originV, extentsV); + const Vec4V maxV = V4Add(originV, extentsV); + + StoreBounds(bounds, minV, maxV); +} + +static PX_FORCE_INLINE Vec4V basisExtentV(const PxMat33Padded& basis, const PxVec3& extent, float offset, float inflation) +{ + // extended basis vectors + const Vec4V c0V = V4Scale(V4LoadU(&basis.column0.x), FLoad(extent.x)); + const Vec4V c1V = V4Scale(V4LoadU(&basis.column1.x), FLoad(extent.y)); + const Vec4V c2V = V4Scale(V4LoadU(&basis.column2.x), FLoad(extent.z)); + + // find combination of base vectors that produces max. distance for each component = sum of abs() + Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V)); + extentsV = V4Add(extentsV, V4Abs(c2V)); + extentsV = V4Add(extentsV, V4Load(offset)); + extentsV = V4Scale(extentsV, FLoad(inflation)); + return extentsV; +} + +void Gu::computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& pose, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation, bool extrudeHeightfields) +{ + PX_ASSERT(contactOffset==0.0f || inflation==1.0f); + + // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space. + // Sphere, Capsule & Plane will compute world space bounds directly. + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + PX_ASSERT(!localSpaceBounds); + + const PxSphereGeometry& shape = static_cast<const PxSphereGeometry&>(geometry); + const PxVec3 extents((shape.radius+contactOffset)*inflation); + bounds.minimum = pose.p - extents; + bounds.maximum = pose.p + extents; + } + break; + + case PxGeometryType::ePLANE: + { + PX_ASSERT(!localSpaceBounds); + + computePlaneBounds(bounds, pose, contactOffset, inflation); + } + break; + + case PxGeometryType::eCAPSULE: + { + PX_ASSERT(!localSpaceBounds); + + const PxCapsuleGeometry& shape = static_cast<const PxCapsuleGeometry& >(geometry); + const PxVec3 d = pose.q.getBasisVector0(); + PxVec3 extents; + for(PxU32 ax = 0; ax<3; ax++) + extents[ax] = (PxAbs(d[ax]) * shape.halfHeight + shape.radius + contactOffset)*inflation; + bounds.minimum = pose.p - extents; + bounds.maximum = pose.p + extents; + } + break; + + case PxGeometryType::eBOX: + { + PX_ASSERT(!localSpaceBounds); + + const PxBoxGeometry& shape = static_cast<const PxBoxGeometry& >(geometry); + + const Vec3p origin(pose.p); + + const PxMat33Padded basis(pose.q); + + const Vec4V extentsV = basisExtentV(basis, shape.halfExtents, contactOffset, inflation); + + const Vec4V originV = V4LoadU(&origin.x); + const Vec4V minV = V4Sub(originV, extentsV); + const Vec4V maxV = V4Add(originV, extentsV); + + StoreBounds(bounds, minV, maxV); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast<const PxConvexMeshGeometry& >(geometry); + const Gu::ConvexHullData& hullData = static_cast<const Gu::ConvexMesh*>(shape.convexMesh)->getHull(); + + const bool useTightBounds = shape.meshFlags & PxConvexMeshGeometryFlag::eTIGHT_BOUNDS; + if(useTightBounds) + { + PxMat33Padded rot(pose.q); + + if(isNonIdentity(shape.scale.scale)) + computeScaledMatrix(rot, shape.scale); + + PxU32 nb = hullData.mNbHullVertices; + const PxVec3* v = hullData.getHullVertices(); + Vec4V minV; + Vec4V maxV; + + { + const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot); + v++; + + minV = vertexV; + maxV = vertexV; + nb--; + } + + while(nb--) + { + const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot); + v++; + + minV = V4Min(minV, vertexV); + maxV = V4Max(maxV, vertexV); + } + + const Vec4V offsetV = V4Load(contactOffset); + minV = V4Sub(minV, offsetV); + maxV = V4Add(maxV, offsetV); + + const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pose.p.x)); + maxV = V4Add(maxV, posV); + minV = V4Add(minV, posV); + + // Inflation + { + const Vec4V centerV = V4Scale(V4Add(maxV, minV), FLoad(0.5f)); + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f*inflation)); + maxV = V4Add(centerV, extentsV); + minV = V4Sub(centerV, extentsV); + } + + StoreBounds(bounds, minV, maxV); + } + else + { + Vec3p origin, extents; + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extents); + + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + Vec3p origin, extents; + const PxTriangleMeshGeometry& shape = static_cast<const PxTriangleMeshGeometry& >(geometry); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast<const Gu::TriangleMesh*>(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extents); + + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + const PxHeightFieldGeometry& shape = static_cast<const PxHeightFieldGeometry& >(geometry); + const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity)); + + const Gu::HeightFieldData& data = static_cast<const Gu::HeightField*>(shape.heightField)->getData(); + //Get the center and extents of the hf. + CenterExtentsPadded cep; + if(localSpaceBounds) + { + cep = *localSpaceBounds; + } + else + { + cep = static_cast<Gu::HeightField*>(shape.heightField)->getData().getPaddedBounds(); + } + + //Add on the thickness. + //The thickness is an absolute quantity and is not to be multiplied by the height-scale of the hf. + //To enforce this we need to divide the thickness by the scale because computeMeshBounds multiplies by the scale. + //Another way of expressing this is that computeMeshBounds uses unscaled coordinates as input so we need to express the thickness + //in unscaled coordinates too so that we may legally add it to the extents. + bounds.minimum = cep.getMin(); + bounds.maximum = cep.getMax(); + + if (extrudeHeightfields) + { + const PxF32 thickness = data.thickness; + const PxReal thicknessScaled = thickness / shape.heightScale; + if (thicknessScaled < 0.f) + bounds.minimum.y += thicknessScaled; + else + bounds.maximum.y += thicknessScaled; + } + + //Recompute the center and extent after adding on the thickness. + cep.setMinMax(bounds.minimum, bounds.maximum); + + //Compute and inflate the bounds from the pose, scale and center/extents. + Vec3p origin, extents; + computeMeshBounds(pose, &cep, scale, origin, extents); + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_ASSERT(0); + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type."); + } + } +} + +// PT: TODO: refactor this with regular function +PxF32 Gu::computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds) +{ + // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space. + // Sphere, Capsule & Plane will compute world space bounds directly. + + const PxReal inSphereRatio = 0.75f; + + //The CCD thresholds are as follows: + //(1) sphere = inSphereRatio * radius + //(2) plane = inf (we never need CCD against this shape) + //(3) capsule = inSphereRatio * radius + //(4) box = inSphereRatio * (box minimum extent axis) + //(5) convex = inSphereRatio * convex in-sphere * min scale + //(6) triangle mesh = 0.f (polygons have 0 thickness) + //(7) heightfields = 0.f (polygons have 0 thickness) + + //The decision to enter CCD depends on the sum of the shapes' CCD thresholds. One of the 2 shapes must be a + //sphere/capsule/box/convex so the sum of the CCD thresholds will be non-zero. + + switch (geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + PX_ASSERT(!localSpaceBounds); + + const PxSphereGeometry& shape = static_cast<const PxSphereGeometry&>(geometry); + origin = pose.p; + extent = PxVec3(shape.radius, shape.radius, shape.radius); + return shape.radius*inSphereRatio; + } + case PxGeometryType::ePLANE: + { + PX_ASSERT(!localSpaceBounds); + + PxBounds3 bounds; + computePlaneBounds(bounds, pose, 0.0f, 1.0f); + origin = bounds.getCenter(); + extent = bounds.getExtents(); + return PX_MAX_REAL; + } + case PxGeometryType::eCAPSULE: + { + PX_ASSERT(!localSpaceBounds); + + const PxCapsuleGeometry& shape = static_cast<const PxCapsuleGeometry&>(geometry); + origin = pose.p; + const PxVec3 d = pose.q.getBasisVector0(); + for(PxU32 ax = 0; ax<3; ax++) + extent[ax] = PxAbs(d[ax]) * shape.halfHeight + shape.radius; + return shape.radius * inSphereRatio; + } + + case PxGeometryType::eBOX: + { + PX_ASSERT(!localSpaceBounds); + + const PxBoxGeometry& shape = static_cast<const PxBoxGeometry&>(geometry); + + const PxMat33 rot(pose.q); + extent = Cm::basisExtent(rot.column0, rot.column1, rot.column2, shape.halfExtents); + + origin = pose.p; + + return PxMin(PxMin(shape.halfExtents.x, shape.halfExtents.y), shape.halfExtents.z)*inSphereRatio; + } + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast<const PxConvexMeshGeometry&>(geometry); + const Gu::ConvexHullData& hullData = static_cast<const Gu::ConvexMesh*>(shape.convexMesh)->getHull(); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extent); + return PxMin(shape.scale.scale.z, PxMin(shape.scale.scale.x, shape.scale.scale.y)) * hullData.mInternal.mRadius * inSphereRatio; + } + + case PxGeometryType::eTRIANGLEMESH: + { + const PxTriangleMeshGeometry& shape = static_cast<const PxTriangleMeshGeometry&>(geometry); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast<const Gu::TriangleMesh*>(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extent); + return 0.0f; + } + + case PxGeometryType::eHEIGHTFIELD: + { + const PxHeightFieldGeometry& shape = static_cast<const PxHeightFieldGeometry&>(geometry); + const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity)); + const Gu::HeightFieldData& data = static_cast<const Gu::HeightField*>(shape.heightField)->getData(); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &data.getPaddedBounds(), scale, origin, extent); + return 0.f; + } + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_ASSERT(0); + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type."); + } + } + return PX_MAX_REAL; +} + + +static PX_FORCE_INLINE void computeBoxExtentsAroundCapsule(PxVec3& extents, const PxCapsuleGeometry& capsuleGeom, float inflation) +{ + extents.x = (capsuleGeom.radius + capsuleGeom.halfHeight) * inflation; + extents.y = capsuleGeom.radius * inflation; + extents.z = capsuleGeom.radius * inflation; +} + +static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape) + +static void computeMeshBounds(const PxVec3& pos, const PxMat33Padded& rot, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent) +{ + Ps::prefetchLine(localSpaceBounds); // PT: this one helps reducing L2 misses in transformNoEmptyTest + transformNoEmptyTest(origin, extent, pos, rot, meshScale, *localSpaceBounds); +} + +// PT: warning: this writes 4 bytes after the end of 'bounds'. Calling code must ensure it is safe to do so. +static PX_FORCE_INLINE void computeMinMaxBounds(PxBounds3* PX_RESTRICT bounds, const Vec3p& c, const Vec3p& e, float prunerInflation, float offset) +{ + const Vec4V extentsV = V4Scale(V4Add(V4LoadU(&e.x), V4Load(offset)), FLoad(prunerInflation)); + const Vec4V centerV = V4LoadU(&c.x); + const Vec4V minV = V4Sub(centerV, extentsV); + const Vec4V maxV = V4Add(centerV, extentsV); + V4StoreU(minV, &bounds->minimum.x); + V4StoreU(maxV, &bounds->maximum.x); +} + +ShapeData::ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation) +{ + using namespace physx::shdfnd::aos; + + // PT: this cast to matrix is already done in GeometryUnion::computeBounds (e.g. for boxes). So we do it first, + // then we'll pass the matrix directly to computeBoundsShapeData, to avoid the double conversion. + const bool isOBB = PxAbs(t.q.w) < 0.999999f; + if(isOBB) + { + // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards + buildFrom(mGuBox, t.q); + } + else + { + mGuBox.rot = PxMat33(PxIdentity); + } + + // PT: can't use V4Load here since there's no guarantee on 't.p' + // PT: must store 'center' after 'rot' now + mGuBox.center = t.p; + + // Compute AABB, used by the BucketPruner as cullBox + switch(g.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& shape = static_cast<const PxSphereGeometry&>(g); + computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, PxVec3(0.0f), SQ_PRUNER_INFLATION, shape.radius+inflation); + + // + + reinterpret_cast<Sphere&>(mGuSphere) = Sphere(t.p, shape.radius); + } + break; + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& shape = static_cast<const PxCapsuleGeometry&>(g); + const Vec3p extents = mGuBox.rot.column0.abs() * shape.halfHeight; + computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, extents, SQ_PRUNER_INFLATION, shape.radius+inflation); + + // + + Capsule& dstWorldCapsule = reinterpret_cast<Capsule&>(mGuCapsule); // store a narrow phase version copy + getCapsule(dstWorldCapsule, shape, t); + + mGuBox.extents.x = shape.halfHeight; + + // compute PxBoxGeometry pruner geom around input capsule geom; transform remains unchanged + + computeBoxExtentsAroundCapsule(mPrunerBoxGeomExtents, shape, SQ_PRUNER_INFLATION); + } + break; + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& shape = static_cast<const PxBoxGeometry&>(g); + // PT: cast is safe because 'rot' followed by other members + Vec4V extentsV = basisExtentV(static_cast<const PxMat33Padded&>(mGuBox.rot), shape.halfExtents, inflation, SQ_PRUNER_INFLATION); + + // PT: c/e-to-m/M conversion + const Vec4V centerV = V4LoadU(&mGuBox.center.x); + const Vec4V minV = V4Sub(centerV, extentsV); + const Vec4V maxV = V4Add(centerV, extentsV); + V4StoreU(minV, &mPrunerInflatedAABB.minimum.x); + V4StoreU(maxV, &mPrunerInflatedAABB.maximum.x); // PT: WARNING: writes past end of class + + // + + mGuBox.extents = shape.halfExtents; // PT: TODO: use SIMD + mPrunerBoxGeomExtents = shape.halfExtents*SQ_PRUNER_INFLATION; + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast<const PxConvexMeshGeometry&>(g); + + const ConvexMesh* cm = static_cast<const ConvexMesh*>(shape.convexMesh); + const ConvexHullData* hullData = &cm->getHull(); + + // PT: cast is safe since 'rot' is followed by other members of the box + Vec3p center, extents; + computeMeshBounds(mGuBox.center, static_cast<const PxMat33Padded&>(mGuBox.rot), &hullData->getPaddedBounds(), shape.scale, center, extents); + + computeMinMaxBounds(&mPrunerInflatedAABB, center, extents, SQ_PRUNER_INFLATION, inflation); + + // + + Box prunerBox; + computeOBBAroundConvex(prunerBox, shape, cm, t); + mGuBox.rot = prunerBox.rot; // PT: TODO: optimize this copy + + // AP: pruners are now responsible for growing the OBB by 1% for overlap/sweep/GJK accuracy + mPrunerBoxGeomExtents = prunerBox.extents*SQ_PRUNER_INFLATION; + mGuBox.center = prunerBox.center; + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("PhysX internal error: Invalid shape in ShapeData contructor."); + } + + // PT: WARNING: these writes must stay after the above code + mIsOBB = PxU32(isOBB); + mType = PxU16(g.getType()); +} + + + + diff --git a/PhysX_3.4/Source/GeomUtils/src/GuBounds.h b/PhysX_3.4/Source/GeomUtils/src/GuBounds.h new file mode 100644 index 00000000..165cd89f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuBounds.h @@ -0,0 +1,157 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOUNDS_H +#define GU_BOUNDS_H + +#include "foundation/PxBounds3.h" +#include "foundation/PxFlags.h" +#include "GuSIMDHelpers.h" +#include <stddef.h> +#include "PxGeometry.h" +#include "GuBox.h" +#include "GuCenterExtents.h" +#include "GuSphere.h" +#include "GuCapsule.h" + +namespace physx +{ +class PxGeometry; + +namespace Gu +{ + +//For spheres, planes, capsules and boxes just set localSpaceBounds to NULL. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to the relevant pointer if it has already been pre-fetched. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to NULL if it has not already been pre-fetched. computeBounds will synchronously +//prefetch the local space bounds if localSpaceBounds is NULL. +//'contactOffset' and 'inflation' should not be used at the same time, i.e. either contactOffset==0.0f, or inflation==1.0f +PX_PHYSX_COMMON_API void computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& transform, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation, bool extrudeHeightfields); //AABB in world space. + +//For spheres, planes, capsules and boxes just set localSpaceBounds to NULL. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to the relevant pointer if it has not already been pre-fetched. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to NULL if it has not already been pre-fetched. computeBounds will synchronously +//prefetch the local space bounds if localSpaceBounds is NULL. +PX_PHYSX_COMMON_API PxF32 computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& transform, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds); //AABB in world space. + + +PX_FORCE_INLINE PxBounds3 computeBounds(const PxGeometry& geometry, const PxTransform& pose, bool extrudeHeightfields) +{ + PxBounds3 bounds; + computeBounds(bounds, geometry, pose, 0.0f, NULL, 1.0f, extrudeHeightfields); + return bounds; +} + +class ShapeData +{ +public: + + PX_PHYSX_COMMON_API ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation); + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxVec3& getPrunerBoxGeomExtentsInflated() const { return mPrunerBoxGeomExtents; } + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxVec3& getPrunerWorldPos() const { return mGuBox.center; } + + PX_FORCE_INLINE const PxBounds3& getPrunerInflatedWorldAABB() const { return mPrunerInflatedAABB; } + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxMat33& getPrunerWorldRot33() const { return mGuBox.rot; } + + // PT: this one only used by overlaps so far (for sphere shape, pruner level) + PX_FORCE_INLINE const Gu::Sphere& getGuSphere() const + { + PX_ASSERT(mType == PxGeometryType::eSPHERE); + return reinterpret_cast<const Gu::Sphere&>(mGuSphere); + } + + // PT: this one only used by sweeps so far (for box shape, NP level) + PX_FORCE_INLINE const Gu::Box& getGuBox() const + { + PX_ASSERT(mType == PxGeometryType::eBOX); + return mGuBox; + } + + // PT: this one used by sweeps (NP level) and overlaps (pruner level) - for capsule shape + PX_FORCE_INLINE const Gu::Capsule& getGuCapsule() const + { + PX_ASSERT(mType == PxGeometryType::eCAPSULE); + return reinterpret_cast<const Gu::Capsule&>(mGuCapsule); + } + + PX_FORCE_INLINE float getCapsuleHalfHeight() const + { + PX_ASSERT(mType == PxGeometryType::eCAPSULE); + return mGuBox.extents.x; + } + + PX_FORCE_INLINE PxU32 isOBB() const { return PxU32(mIsOBB); } + PX_FORCE_INLINE PxGeometryType::Enum getType() const { return PxGeometryType::Enum(mType); } + + PX_NOCOPY(ShapeData) +private: + + // PT: box: pre-inflated box extents + // capsule: pre-inflated extents of box-around-capsule + // convex: pre-inflated extents of box-around-convex + // sphere: not used + PxVec3 mPrunerBoxGeomExtents; // used for pruners. This volume encloses but can differ from the original shape + + // PT: + // + // box center = unchanged copy of initial shape's position, except for convex (position of box around convex) + // SIMD code will load it as a V4 (safe because member is not last of Gu structure) + // + // box rot = precomputed PxMat33 version of initial shape's rotation, except for convex (rotation of box around convex) + // SIMD code will load it as V4s (safe because member is not last of Gu structure) + // + // box extents = non-inflated initial box extents for box shape, half-height for capsule, otherwise not used + Gu::Box mGuBox; + + PxBounds3 mPrunerInflatedAABB; // precomputed AABB for the pruner shape + PxU16 mIsOBB; // true for OBB, false for AABB. Also used as padding for mPrunerInflatedAABB, don't move. + PxU16 mType; // shape's type + + // these union Gu shapes are only precomputed for narrow phase (not pruners), can be different from mPrunerVolume + // so need separate storage + union + { + PxU8 mGuCapsule[sizeof(Gu::Capsule)]; // 28 + PxU8 mGuSphere[sizeof(Gu::Sphere)]; // 16 + }; +}; + +// PT: please make sure it fits in "one" cache line +PX_COMPILE_TIME_ASSERT(sizeof(ShapeData)==128); + +} // namespace Gu + +} +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuBox.cpp b/PhysX_3.4/Source/GeomUtils/src/GuBox.cpp new file mode 100644 index 00000000..5b91d443 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuBox.cpp @@ -0,0 +1,128 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "GuBoxConversion.h" +#include "GuCapsule.h" +#include "GuInternal.h" +#include "CmMatrix34.h" +#include "PsMathUtils.h" + +using namespace physx; + +void Gu::Box::create(const Gu::Capsule& capsule) +{ + // Box center = center of the two LSS's endpoints + center = capsule.computeCenter(); + + PxVec3 dir = capsule.p1 - capsule.p0; + const float d = dir.magnitude(); + rot.column0 = dir / d; + + // Box extents + extents.x = capsule.radius + (d * 0.5f); + extents.y = capsule.radius; + extents.z = capsule.radius; + + // Box orientation + Ps::computeBasis(rot.column0, rot.column1, rot.column2); +} + + +/** +Returns edges. +\return 24 indices (12 edges) indexing the list returned by ComputePoints() +*/ +const PxU8* Gu::getBoxEdges() +{ + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + static PxU8 Indices[] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 7, 6, 6, 5, 5, 4, 4, 7, + 1, 5, 6, 2, + 3, 7, 4, 0 + }; + return Indices; +} + + +void Gu::computeOBBPoints(PxVec3* PX_RESTRICT pts, const PxVec3& center, const PxVec3& extents, const PxVec3& base0, const PxVec3& base1, const PxVec3& base2) +{ + PX_ASSERT(pts); + + // "Rotated extents" + const PxVec3 axis0 = base0 * extents.x; + const PxVec3 axis1 = base1 * extents.y; + const PxVec3 axis2 = base2 * extents.z; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + // Original code: 24 vector ops + /* pts[0] = box.center - Axis0 - Axis1 - Axis2; + pts[1] = box.center + Axis0 - Axis1 - Axis2; + pts[2] = box.center + Axis0 + Axis1 - Axis2; + pts[3] = box.center - Axis0 + Axis1 - Axis2; + pts[4] = box.center - Axis0 - Axis1 + Axis2; + pts[5] = box.center + Axis0 - Axis1 + Axis2; + pts[6] = box.center + Axis0 + Axis1 + Axis2; + pts[7] = box.center - Axis0 + Axis1 + Axis2;*/ + + // Rewritten: 12 vector ops + pts[0] = pts[3] = pts[4] = pts[7] = center - axis0; + pts[1] = pts[2] = pts[5] = pts[6] = center + axis0; + + PxVec3 tmp = axis1 + axis2; + pts[0] -= tmp; + pts[1] -= tmp; + pts[6] += tmp; + pts[7] += tmp; + + tmp = axis1 - axis2; + pts[2] += tmp; + pts[3] += tmp; + pts[4] -= tmp; + pts[5] -= tmp; +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/GuCCTSweepTests.cpp b/PhysX_3.4/Source/GeomUtils/src/GuCCTSweepTests.cpp new file mode 100644 index 00000000..d01a3a42 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuCCTSweepTests.cpp @@ -0,0 +1,440 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "PxSphereGeometry.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistancePointBox.h" +#include "GuSweepBoxSphere.h" +#include "GuSweepCapsuleBox.h" +#include "GuSweepBoxBox.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" +#include "PsVecMath.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace Ps::aos; + +static const bool gValidateBoxRadiusComputation = false; + +/////////////////////////////////////////// + +bool sweepCapsule_BoxGeom_Precise(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(inflation); + PX_UNUSED(capsulePose_); + PX_UNUSED(capsuleGeom_); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + if (lss.p0 == lss.p1) // The capsule is actually a sphere + { + //TODO: Check if this is really faster than using a "sphere-aware" version of sweepCapsuleBox + + Box box; buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + if(!sweepBoxSphere(box, lss.radius, lss.p0, unitDir, distance, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.normal = -sweepHit.normal; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + if(hitFlags & PxHitFlag::ePOSITION && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + const PxVec3 newSphereCenter = lss.p0 + unitDir * sweepHit.distance; + PxVec3 closest; + const PxReal d = distancePointBoxSquared(newSphereCenter, box.center, box.extents, box.rot, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + closest = box.rotate(closest); + sweepHit.position = closest + box.center; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + if(!sweepCapsuleBox(lss, pose, boxGeom.halfExtents, unitDir, distance, sweepHit.position, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + Capsule movedCaps = lss; + movedCaps.p0 += unitDir * sweepHit.distance; + movedCaps.p1 += unitDir * sweepHit.distance; + + Box box; + buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + + PxVec3 closest; + const PxReal d = distanceSegmentBoxSquared(movedCaps, box, NULL, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + closest = pose.q.rotate(closest); + sweepHit.position = closest + pose.p; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_SphereGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + // PT: move to relative space + const Box relBox(box.center - pose.p, box.extents, box.rot); + + const PxReal sphereRadius = sphereGeom.radius + inflation; + + if(!sweepBoxSphere(relBox, sphereRadius, PxVec3(0), -unitDir, distance, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + const PxVec3 motion = sweepHit.distance * unitDir; + const PxVec3 newSphereCenter = - motion; + PxVec3 closest; + const PxReal d = distancePointBoxSquared(newSphereCenter, relBox.center, relBox.extents, relBox.rot, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + sweepHit.position = relBox.rotate(closest) + box.center + motion; // PT: undo move to local space here + sweepHit.flags |= PxHitFlag::ePOSITION; + } + return true; +} + +bool sweepBox_CapsuleGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_UNUSED(inflation); + PX_UNUSED(boxGeom_); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + // PT: move to relative space + const PxVec3 delta = box.center - pose.p; + Box relBox(delta, box.extents, box.rot); + + Capsule capsule; + const PxVec3 halfHeightVector = getCapsuleHalfHeightVector(pose, capsuleGeom); + capsule.p0 = halfHeightVector; + capsule.p1 = -halfHeightVector; + capsule.radius = capsuleGeom.radius; + + // PT: TODO: remove this. We convert to PxTansform here but inside sweepCapsuleBox we convert back to a matrix. + const PxTransform boxWorldPose(delta, boxPose_.q); + + PxVec3 n; + if(!sweepCapsuleBox(capsule, boxWorldPose, relBox.extents, -unitDir, distance, sweepHit.position, sweepHit.distance, n, hitFlags)) + return false; + + sweepHit.normal = -n; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + relBox.center += (unitDir * sweepHit.distance); + PxVec3 closest; + const PxReal d = distanceSegmentBoxSquared(capsule, relBox, NULL, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + sweepHit.position = relBox.transform(closest) + pose.p; // PT: undo move to local space here + sweepHit.flags |= PxHitFlag::ePOSITION; + } + return true; +} + +bool sweepBox_BoxGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(inflation); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + // PT: move to local space + const Box relBox(box.center - pose.p, box.extents, box.rot); + Box staticBox; buildFrom(staticBox, PxVec3(0), boxGeom.halfExtents, pose.q); + + if(!sweepBoxBox(relBox, staticBox, unitDir, distance, hitFlags, sweepHit)) + return false; + + if(sweepHit.distance!=0.0f) + sweepHit.position += pose.p; // PT: undo move to local space + return true; +} + +// PT: test: new version for CCT, based on code for general sweeps. Just to check it works or not with rotations +// TODO: refactor this and the similar code in sweptBox for box-vs-mesh. Not so easy though. +static bool sweepBoxVsTriangles(PxU32 nbTris, const PxTriangle* triangles, const Box& box, const PxVec3& unitDir, const PxReal distance, PxSweepHit& sweepHit, + PxHitFlags hitFlags, bool isDoubleSided, const PxU32* cachedIndex) +{ + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localMotion = localDir * distance; + + bool status = false; + sweepHit.distance = distance; //was PX_MAX_F32, but that may trigger an assert in the caller! + + const PxVec3 oneOverMotion( + localDir.x!=0.0f ? 1.0f/localMotion.x : 0.0f, + localDir.y!=0.0f ? 1.0f/localMotion.y : 0.0f, + localDir.z!=0.0f ? 1.0f/localMotion.z : 0.0f); + +// PT: experimental code, don't clean up before I test it more and validate it + +// Project box +/*float boxRadius0 = + PxAbs(dir.x) * box.extents.x + + PxAbs(dir.y) * box.extents.y + + PxAbs(dir.z) * box.extents.z;*/ + +float boxRadius = + PxAbs(localDir.x) * box.extents.x + + PxAbs(localDir.y) * box.extents.y + + PxAbs(localDir.z) * box.extents.z; + +if(gValidateBoxRadiusComputation) // PT: run this to check the box radius is correctly computed +{ + PxVec3 boxVertices2[8]; + box.computeBoxPoints(boxVertices2); + float dpmin = FLT_MAX; + float dpmax = -FLT_MAX; + for(int i=0;i<8;i++) + { + const float dp = boxVertices2[i].dot(unitDir); + if(dp<dpmin) dpmin = dp; + if(dp>dpmax) dpmax = dp; + } + const float goodRadius = (dpmax-dpmin)/2.0f; + PX_UNUSED(goodRadius); +} + +const float dpc0 = box.center.dot(unitDir); +float localMinDist = 1.0f; +#if PX_DEBUG + PxU32 totalTestsExpected = nbTris; + PxU32 totalTestsReal = 0; + PX_UNUSED(totalTestsExpected); + PX_UNUSED(totalTestsReal); +#endif + + const PxU32 idx = cachedIndex ? *cachedIndex : 0; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0;ii<nbTris;ii++) + { + const PxU32 triangleIndex = getTriangleIndex(ii, idx); + + const PxTriangle& tri = triangles[triangleIndex]; + + if(!cullTriangle(tri.verts, unitDir, boxRadius, localMinDist*distance, dpc0)) + continue; + +#if PX_DEBUG + totalTestsReal++; +#endif + // Move to box space + const PxTriangle currentTriangle( + worldToBox.transform(tri.verts[0]), + worldToBox.transform(tri.verts[1]), + worldToBox.transform(tri.verts[2])); + + PxF32 t = PX_MAX_F32; // could be better! + if(triBoxSweepTestBoxSpace(currentTriangle, box.extents, localMotion, oneOverMotion, localMinDist, t, doBackfaceCulling)) + { + if(t < localMinDist) + { + // PT: test if shapes initially overlap + if(t==0.0f) + return setInitialOverlapResults(sweepHit, unitDir, triangleIndex); + + localMinDist = t; + sweepHit.distance = t * distance; + sweepHit.faceIndex = triangleIndex; + status = true; + + // PT: TODO: optimize this.... already computed in triBoxSweepTestBoxSpace... + currentTriangle.denormalizedNormal(bestTriNormal); + + if(hitFlags & PxHitFlag::eMESH_ANY) + break; + } + } + } + + if(status) + { + sweepHit.flags = PxHitFlag::eDISTANCE; + + // PT: TODO: refactor with computeBoxLocalImpact (TA34704) + if(hitFlags & (PxHitFlag::eNORMAL|PxHitFlag::ePOSITION)) + { + const PxTriangle& tri = triangles[sweepHit.faceIndex]; + + // Move to box space + const PxTriangle currentTriangle( + worldToBox.transform(tri.verts[0]), + worldToBox.transform(tri.verts[1]), + worldToBox.transform(tri.verts[2])); + + computeBoxTriImpactData(sweepHit.position, sweepHit.normal, box.extents, localDir, currentTriangle, sweepHit.distance); + + if(hitFlags & PxHitFlag::eNORMAL) + { + PxVec3 localNormal = sweepHit.normal; // PT: both local space & local variable + localNormal.normalize(); + + if(shouldFlipNormal(localNormal, meshBothSides, isDoubleSided, bestTriNormal, localDir)) + localNormal = -localNormal; + + sweepHit.normal = box.rotate(localNormal); + sweepHit.flags |= PxHitFlag::eNORMAL; + } + + if(hitFlags & PxHitFlag::ePOSITION) + { + sweepHit.position = box.rotate(sweepHit.position) + box.center; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + } + return status; +} + +bool sweepBox_HeightFieldGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(inflation); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxHeightFieldGeometry& heightFieldGeom = static_cast<const PxHeightFieldGeometry&>(geom); + + // Compute swept box + Box sweptBox; + computeSweptBox(sweptBox, box.extents, box.center, box.rot, unitDir, distance); + + //### Temp hack until we can directly collide the OBB against the HF + const PxTransform sweptBoxTR = sweptBox.getTransform(); + const PxBounds3 bounds = PxBounds3::poseExtent(sweptBoxTR, sweptBox.extents); + + sweepHit.distance = PX_MAX_F32; + + struct LocalReport : EntityReport<PxU32> + { + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; i<nb; i++) + { + const PxU32 triangleIndex = indices[i]; + + PxTriangle currentTriangle; // in world space + mHFUtil->getTriangle(*mPose, currentTriangle, NULL, NULL, triangleIndex, true, true); + + PxSweepHit sweepHit_; + const bool b = sweepBoxVsTriangles(1, ¤tTriangle, mBox, mDir, mDist, sweepHit_, mHitFlags, mIsDoubleSided, NULL); + if(b && sweepHit_.distance<mHit->distance) + { + *mHit = sweepHit_; + mHit->faceIndex = triangleIndex; + mStatus = true; + } + } + return true; + } + + const HeightFieldUtil* mHFUtil; + const PxTransform* mPose; + PxSweepHit* mHit; + bool mStatus; + Box mBox; + PxVec3 mDir; + float mDist; + PxHitFlags mHitFlags; + bool mIsDoubleSided; + } myReport; + + HeightFieldUtil hfUtil(heightFieldGeom); + + myReport.mBox = box; + myReport.mDir = unitDir; + myReport.mDist = distance; + myReport.mHitFlags = hitFlags; + myReport.mHFUtil = &hfUtil; + myReport.mStatus = false; + myReport.mPose = &pose; + myReport.mHit = &sweepHit; + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + myReport.mIsDoubleSided = (heightFieldGeom.heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || meshBothSides; + + hfUtil.overlapAABBTriangles(pose, bounds, GuHfQueryFlags::eWORLD_SPACE, &myReport); + + return myReport.mStatus; +} + +bool Gu::sweepBoxTriangles_Precise(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) +{ + PX_UNUSED(inflation); + + Box box; + buildFrom(box, pose.p, geom.halfExtents, pose.q); + + return sweepBoxVsTriangles(nbTris, triangles, box, unitDir, distance, hit, hitFlags, doubleSided, cachedIndex); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/GuCapsule.cpp new file mode 100644 index 00000000..dc26d1cf --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuCapsule.cpp @@ -0,0 +1,64 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "GuInternal.h" +#include "GuBox.h" +#include "GuCapsule.h" + +using namespace physx; + +/** +* Computes an OBB surrounding the capsule. +* \param box [out] the OBB +*/ +void Gu::computeBoxAroundCapsule(const Gu::Capsule& capsule, Gu::Box& box) +{ + // Box center = center of the two capsule's endpoints + box.center = capsule.computeCenter(); + + // Box extents + const PxF32 d = (capsule.p0 - capsule.p1).magnitude(); + box.extents.x = capsule.radius + (d * 0.5f); + box.extents.y = capsule.radius; + box.extents.z = capsule.radius; + + // Box orientation + if(d==0.0f) + { + box.rot = PxMat33(PxIdentity); + } + else + { + PxVec3 dir, right, up; + Ps::computeBasis(capsule.p0, capsule.p1, dir, right, up); + box.setAxes(dir, right, up); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuCapsule.h b/PhysX_3.4/Source/GeomUtils/src/GuCapsule.h new file mode 100644 index 00000000..3bf7c529 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuCapsule.h @@ -0,0 +1,92 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CAPSULE_H +#define GU_CAPSULE_H + +/** \addtogroup geomutils +@{ +*/ + +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + +/** +\brief Represents a capsule. +*/ + class Capsule : public Segment + { + public: + /** + \brief Constructor + */ + PX_INLINE Capsule() + { + } + + /** + \brief Constructor + + \param seg Line segment to create capsule from. + \param _radius Radius of the capsule. + */ + PX_INLINE Capsule(const Segment& seg, PxF32 _radius) : Segment(seg), radius(_radius) + { + } + + /** + \brief Constructor + + \param _p0 First segment point + \param _p1 Second segment point + \param _radius Radius of the capsule. + */ + PX_INLINE Capsule(const PxVec3& _p0, const PxVec3& _p1, PxF32 _radius) : Segment(_p0, _p1), radius(_radius) + { + } + + /** + \brief Destructor + */ + PX_INLINE ~Capsule() + { + } + + PxF32 radius; + }; +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuCenterExtents.h b/PhysX_3.4/Source/GeomUtils/src/GuCenterExtents.h new file mode 100644 index 00000000..2174bbd9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuCenterExtents.h @@ -0,0 +1,131 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CENTER_EXTENTS_H +#define GU_CENTER_EXTENTS_H + +/** \addtogroup geomutils +@{ +*/ + +#include "CmMatrix34.h" +#include "CmUtils.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Gu +{ + class CenterExtents : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE CenterExtents() {} + PX_FORCE_INLINE CenterExtents(const PxBounds3& b) { mCenter = b.getCenter(); mExtents = b.getExtents(); } + PX_FORCE_INLINE ~CenterExtents() {} + + PX_FORCE_INLINE void getMin(PxVec3& min) const { min = mCenter - mExtents; } + PX_FORCE_INLINE void getMax(PxVec3& max) const { max = mCenter + mExtents; } + + PX_FORCE_INLINE float getMin(PxU32 axis) const { return mCenter[axis] - mExtents[axis]; } + PX_FORCE_INLINE float getMax(PxU32 axis) const { return mCenter[axis] + mExtents[axis]; } + + PX_FORCE_INLINE PxVec3 getMin() const { return mCenter - mExtents; } + PX_FORCE_INLINE PxVec3 getMax() const { return mCenter + mExtents; } + + PX_FORCE_INLINE void setMinMax(const PxVec3& min, const PxVec3& max) + { + mCenter = (max + min)*0.5f; + mExtents = (max - min)*0.5f; + } + + PX_FORCE_INLINE PxU32 isInside(const CenterExtents& box) const + { + if(box.getMin(0)>getMin(0)) return 0; + if(box.getMin(1)>getMin(1)) return 0; + if(box.getMin(2)>getMin(2)) return 0; + if(box.getMax(0)<getMax(0)) return 0; + if(box.getMax(1)<getMax(1)) return 0; + if(box.getMax(2)<getMax(2)) return 0; + return 1; + } + + PX_FORCE_INLINE void setEmpty() + { + mExtents = PxVec3(-PX_MAX_BOUNDS_EXTENTS); + } + + PX_FORCE_INLINE bool isEmpty() const + { + return Cm::isEmpty(mCenter, mExtents); + } + + PX_FORCE_INLINE bool isFinite() const + { + return mCenter.isFinite() && mExtents.isFinite(); + } + + PX_FORCE_INLINE bool isValid() const + { + return Cm::isValid(mCenter, mExtents); + } + + PX_FORCE_INLINE PxBounds3 transformFast(const PxMat33& matrix) const + { + PX_ASSERT(isValid()); + return PxBounds3::basisExtent(matrix * mCenter, matrix, mExtents); + } + + PX_INLINE PxBounds3 transformSafe(const Cm::Matrix34& matrix) const + { + if(isEmpty()) + return PxBounds3::centerExtents(mCenter, mExtents); + else + return Cm::basisExtent(matrix.transform(mCenter), matrix.m.column0, matrix.m.column1, matrix.m.column2, mExtents); + } + + PxVec3 mCenter; + PxVec3 mExtents; + }; + + //! A padded version of CenterExtents, to safely load its data using SIMD + class CenterExtentsPadded : public CenterExtents + { + public: + PX_FORCE_INLINE CenterExtentsPadded() {} + PX_FORCE_INLINE ~CenterExtentsPadded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(CenterExtentsPadded) == 7*4); + +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuDebug.cpp b/PhysX_3.4/Source/GeomUtils/src/GuDebug.cpp new file mode 100644 index 00000000..26000832 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuDebug.cpp @@ -0,0 +1,264 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDebug.h" +#include "GuHeightFieldUtil.h" +#include "GuTriangleMesh.h" +#include "GuConvexMesh.h" +#include "PxVisualizationParameter.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxGeometryQuery.h" +#include "PxMeshQuery.h" + +using namespace physx; +using namespace Gu; + +#if PX_ENABLE_DEBUG_VISUALIZATION + +static void visualizeSphere(const PxSphereGeometry& geometry, Cm::RenderOutput& out, const PxTransform& absPose) +{ + const PxU32 scolor = PxU32(PxDebugColor::eARGB_MAGENTA); + + out << scolor << absPose << Cm::DebugCircle(100, geometry.radius); + + PxMat44 rotPose(absPose); + Ps::swap(rotPose.column1, rotPose.column2); + rotPose.column1 = -rotPose.column1; + out << scolor << rotPose << Cm::DebugCircle(100, geometry.radius); + + Ps::swap(rotPose.column0, rotPose.column2); + rotPose.column0 = -rotPose.column0; + out << scolor << rotPose << Cm::DebugCircle(100, geometry.radius); +} + +static void visualizePlane(const PxPlaneGeometry& /*geometry*/, Cm::RenderOutput& out, const PxTransform& absPose) +{ + const PxU32 scolor = PxU32(PxDebugColor::eARGB_MAGENTA); + + PxMat44 rotPose(absPose); + Ps::swap(rotPose.column1, rotPose.column2); + rotPose.column1 = -rotPose.column1; + + Ps::swap(rotPose.column0, rotPose.column2); + rotPose.column0 = -rotPose.column0; + for(PxReal radius = 2.0f; radius < 20.0f ; radius += 2.0f) + out << scolor << rotPose << Cm::DebugCircle(100, radius*radius); +} + +static void visualizeCapsule(const PxCapsuleGeometry& geometry, Cm::RenderOutput& out, const PxTransform& absPose) +{ + out << PxU32(PxDebugColor::eARGB_MAGENTA); + out.outputCapsule(geometry.radius, geometry.halfHeight, absPose); +} + +static void visualizeBox(const PxBoxGeometry& geometry, Cm::RenderOutput& out, const PxTransform& absPose) +{ + out << PxU32(PxDebugColor::eARGB_MAGENTA); + out << absPose << Cm::DebugBox(geometry.halfExtents); +} + +static void visualizeConvexMesh(const PxConvexMeshGeometry& geometry, Cm::RenderOutput& out, const PxTransform& absPose) +{ + (static_cast<const ConvexMesh*>(geometry.convexMesh))->debugVisualize(out, absPose, geometry.scale); +} + +static void visualizeTriangleMesh(const PxTriangleMeshGeometry& geometry, Cm::RenderOutput& out, const PxTransform& absPose, + const PxBounds3& cullbox, const PxU64 mask, const PxReal fscale, const PxU32 numMaterials) +{ + (static_cast<const TriangleMesh*>(geometry.triangleMesh))->debugVisualize(out, absPose, geometry.scale, cullbox, mask, fscale, numMaterials); +} + +static void visualizeHeightField(const PxHeightFieldGeometry& hfGeometry, + Cm::RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, + const PxU64 mask) +{ + const HeightField* heightfield = static_cast<const HeightField*>(hfGeometry.heightField); + const bool cscale = !!(mask & (PxU64(1) << PxVisualizationParameter::eCULL_BOX)); + + const PxDebugColor::Enum colors[] = + { + PxDebugColor::eARGB_BLACK, + PxDebugColor::eARGB_RED, + PxDebugColor::eARGB_GREEN, + PxDebugColor::eARGB_BLUE, + PxDebugColor::eARGB_YELLOW, + PxDebugColor::eARGB_MAGENTA, + PxDebugColor::eARGB_CYAN, + PxDebugColor::eARGB_WHITE, + PxDebugColor::eARGB_GREY, + PxDebugColor::eARGB_DARKRED, + PxDebugColor::eARGB_DARKGREEN, + PxDebugColor::eARGB_DARKBLUE, + }; + const PxU32 colorCount = sizeof(colors)/sizeof(PxDebugColor::Enum); + + if (mask & (PxU64(1) << PxVisualizationParameter::eCOLLISION_SHAPES)) + { + + // PT: TODO: the debug viz for HFs is minimal at the moment... + PxU32 scolor = PxU32(PxDebugColor::eARGB_YELLOW); + const PxMat44 midt = PxMat44(PxIdentity); + + HeightFieldUtil hfUtil(hfGeometry); + + const PxU32 nbRows = heightfield->getNbRowsFast(); + const PxU32 nbColumns = heightfield->getNbColumnsFast(); + const PxU32 nbVerts = nbRows * nbColumns; + const PxU32 nbTriangles = 2 * nbVerts; + + out << midt << scolor; // PT: no need to output the same matrix/color for each triangle + + if(cscale) + { + const PxTransform pose0((cullbox.maximum + cullbox.minimum)*0.5f); + const PxBoxGeometry boxGeometry((cullbox.maximum - cullbox.minimum)*0.5f); + + const PxTransform pose1(absPose); + + PxU32* results = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); + + bool overflow = false; + PxU32 nbTouchedTris = PxMeshQuery::findOverlapHeightField(boxGeometry, pose0, hfGeometry, pose1, results, nbTriangles, 0, overflow); + + PxDebugLine* segments = out.reserveSegments(nbTouchedTris*3); + + for (PxU32 i=0; i<nbTouchedTris; i++) + { + const PxU32 index= results[i]; + PxTriangle currentTriangle; + PxMeshQuery::getTriangle(hfGeometry, pose1, index, currentTriangle); + + //The check has been done in the findOverlapHeightField + //if(heightfield->isValidTriangle(index) && heightfield->getTriangleMaterial(index) != PxHeightFieldMaterial::eHOLE) + { + const PxU16 localMaterialIndex = heightfield->getTriangleMaterialIndex(index); + scolor = colors[localMaterialIndex % colorCount]; + + segments[0] = PxDebugLine(currentTriangle.verts[0], currentTriangle.verts[1], scolor); + segments[1] = PxDebugLine(currentTriangle.verts[1], currentTriangle.verts[2], scolor); + segments[2] = PxDebugLine(currentTriangle.verts[2], currentTriangle.verts[0], scolor); + segments+=3; + } + } + PX_FREE(results); + } + else + { + + // PT: transform vertices only once + PxVec3* tmpVerts = reinterpret_cast<PxVec3*>(PX_ALLOC(sizeof(PxVec3)*nbVerts, "PxVec3")); + // PT: TODO: optimize the following line + for(PxU32 i=0;i<nbVerts;i++) + tmpVerts[i] = absPose.transform(hfUtil.hf2shapep(heightfield->getVertex(i))); + + for(PxU32 i=0; i<nbTriangles; i++) + { + // PT: TODO: optimize away the useless divisions/modulos in the lines below + if(heightfield->isValidTriangle(i) && heightfield->getTriangleMaterial(i) != PxHeightFieldMaterial::eHOLE) + { + PxU32 vi0, vi1, vi2; + heightfield->getTriangleVertexIndices(i, vi0, vi1, vi2); + const PxU16 localMaterialIndex = heightfield->getTriangleMaterialIndex(i); + out << colors[localMaterialIndex % colorCount]; + + const PxVec3& vw0 = tmpVerts[vi0]; + const PxVec3& vw1 = tmpVerts[vi1]; + const PxVec3& vw2 = tmpVerts[vi2]; + + out.outputSegment(vw0, vw1); + out.outputSegment(vw1, vw2); + out.outputSegment(vw2, vw0); + } + } + PX_FREE(tmpVerts); + } + } +} + +namespace physx +{ +namespace Gu +{ + +void Debug::visualize(const PxGeometry& geometry, + Cm::RenderOutput& out, + const PxTransform& absPose, + const PxBounds3& cullbox, + const PxU64 mask, + const PxReal fscale, + const PxU32 numMaterials) +{ + + const bool cull((mask & (PxU64(1) << PxVisualizationParameter::eCULL_BOX)) != 0); + const bool collisionShapes((mask & (PxU64(1) << PxVisualizationParameter::eCOLLISION_SHAPES)) != 0); + + if(cull && !cullbox.intersects(PxGeometryQuery::getWorldBounds(geometry, absPose, 0.0f))) + return; + + // triangle meshes can render active edges, but for other types we can just early out if there are no collision shapes + if(!collisionShapes && geometry.getType() != PxGeometryType::eTRIANGLEMESH) + return; + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: + visualizeSphere(static_cast<const PxSphereGeometry&>(geometry), out, absPose); + break; + case PxGeometryType::eBOX: + visualizeBox(static_cast<const PxBoxGeometry&>(geometry), out, absPose); + break; + case PxGeometryType::ePLANE: + visualizePlane(static_cast<const PxPlaneGeometry&>(geometry), out, absPose); + break; + case PxGeometryType::eCAPSULE: + visualizeCapsule(static_cast<const PxCapsuleGeometry&>(geometry), out, absPose); + break; + case PxGeometryType::eCONVEXMESH: + visualizeConvexMesh(static_cast<const PxConvexMeshGeometry&>(geometry), out, absPose); + break; + case PxGeometryType::eTRIANGLEMESH: + visualizeTriangleMesh(static_cast<const PxTriangleMeshGeometry&>(geometry), out, absPose, cullbox, mask, fscale, numMaterials); + break; + case PxGeometryType::eHEIGHTFIELD: + visualizeHeightField(static_cast<const PxHeightFieldGeometry&>(geometry), out, absPose, cullbox, mask); + break; + case PxGeometryType::eINVALID: + break; + case PxGeometryType::eGEOMETRY_COUNT: + break; + } +} +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuDebug.h b/PhysX_3.4/Source/GeomUtils/src/GuDebug.h new file mode 100644 index 00000000..10ad4fb1 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuDebug.h @@ -0,0 +1,62 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DEBUG_H +#define GU_DEBUG_H + +#include "CmRenderOutput.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +#if PX_ENABLE_DEBUG_VISUALIZATION + +class PxGeometry; +namespace Gu +{ + class Debug + { + + public : + + PX_PHYSX_COMMON_API static void visualize(const PxGeometry& geometry, + Cm::RenderOutput& out, + const PxTransform& absPose, + const PxBounds3& cullbox, + const PxU64 mask, + const PxReal fscale, + const PxU32 numMaterials); + }; +} + +#endif + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuGeometryQuery.cpp b/PhysX_3.4/Source/GeomUtils/src/GuGeometryQuery.cpp new file mode 100644 index 00000000..d2de3dac --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuGeometryQuery.cpp @@ -0,0 +1,351 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxGeometryQuery.h" +#include "GuInternal.h" +#include "GuOverlapTests.h" +#include "GuSweepTests.h" +#include "GuRaycastTests.h" +#include "GuBoxConversion.h" +#include "GuTriangleMesh.h" +#include "GuMTD.h" +#include "GuBounds.h" +#include "GuDistancePointSegment.h" +#include "GuConvexMesh.h" +#include "GuDistancePointBox.h" +#include "PsFPU.h" +#include "PxSphereGeometry.h" +#include "PxBoxGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxHeightFieldGeometry.h" + +using namespace physx; +using namespace Gu; + +extern GeomSweepFuncs gGeomSweepFuncs; +extern GeomOverlapTable gGeomOverlapMethodTable[]; +extern RaycastFunc gRaycastMap[PxGeometryType::eGEOMETRY_COUNT]; + +bool PxGeometryQuery::isValid(const PxGeometry& geom) +{ + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + if(!sphereGeom.isValid()) + return false; + break; + } + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + if(!capsuleGeom.isValid()) + return false; + break; + } + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + if(!boxGeom.isValid()) + return false; + break; + } + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom); + if(!convexGeom.isValid()) + return false; + break; + } + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return true; +} + +bool PxGeometryQuery::sweep(const PxVec3& unitDir, const PxReal distance, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1, + PxSweepHit& sweepHit, PxHitFlags hitFlags, + const PxReal inflation) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxGeometryQuery::sweep(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxGeometryQuery::sweep(): pose1 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxGeometryQuery::sweep(): unitDir is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(distance), "PxGeometryQuery::sweep(): distance is not valid.", false); + PX_CHECK_AND_RETURN_VAL((distance >= 0.0f && !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) || distance > 0.0f, + "PxGeometryQuery::sweep(): sweep distance must be >=0 or >0 with eASSUME_NO_INITIAL_OVERLAP.", 0); +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geom0)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry 0 is not valid"); + return false; + } + if(!PxGeometryQuery::isValid(geom1)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry 1 is not valid"); + return false; + } +#endif // PX_CHECKED + + const GeomSweepFuncs& sf = gGeomSweepFuncs; + + switch(geom0.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + + const Capsule worldCapsule(pose0.p, pose0.p, sphereGeom.radius); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + + return func(geom1, pose1, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + + Capsule worldCapsule; + getCapsule(worldCapsule, capsuleGeom, pose0); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + + return func(geom1, pose1, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + + Box box; + buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom1.getType()] : sf.boxMap[geom1.getType()]; + + return func(geom1, pose1, boxGeom, pose0, box, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0); + + const SweepConvexFunc func = sf.convexMap[geom1.getType()]; + + return func(geom1, pose1, convexGeom, pose0, unitDir, distance, sweepHit, hitFlags, inflation); + } + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxGeometryQuery::sweep(): first geometry object parameter must be sphere, capsule, box or convex geometry."); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PxGeometryQuery::overlap( const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1) +{ + PX_SIMD_GUARD; + return Gu::overlap(geom0, pose0, geom1, pose1, gGeomOverlapMethodTable); +} + +/////////////////////////////////////////////////////////////////////////////// +PxU32 PxGeometryQuery::raycast( const PxVec3& rayOrigin, const PxVec3& rayDir, + const PxGeometry& geom, const PxTransform& pose, + PxReal maxDist, PxHitFlags hitFlags, + PxU32 maxHits, PxRaycastHit* PX_RESTRICT rayHits) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(rayDir.isFinite(), "PxGeometryQuery::raycast(): rayDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(rayOrigin.isFinite(), "PxGeometryQuery::raycast(): rayOrigin is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::raycast(): pose is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(maxDist >= 0.0f, "PxGeometryQuery::raycast(): maxDist is negative.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDist), "PxGeometryQuery::raycast(): maxDist is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f, "PxGeometryQuery::raycast(): ray direction must be unit vector.", false); + + const RaycastFunc func = gRaycastMap[geom.getType()]; + return func(geom, pose, rayOrigin, rayDir, maxDist, hitFlags, maxHits, rayHits); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool pointConvexDistance(PxVec3& normal_, PxVec3& closestPoint_, PxReal& sqDistance, const PxVec3& pt, const ConvexMesh* convexMesh, const PxMeshScale& meshScale, const PxTransform& convexPose); + +PxReal PxGeometryQuery::pointDistance(const PxVec3& point, const PxGeometry& geom, const PxTransform& pose, PxVec3* closestPoint) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::pointDistance(): pose is not valid.", false); + + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + const PxReal r = sphereGeom.radius; + + PxVec3 delta = point - pose.p; + const PxReal d = delta.magnitude(); + if(d<=r) + return 0.0f; + + if(closestPoint) + { + delta /= d; + *closestPoint = pose.p + delta * r; + } + + return (d - r)*(d - r); + } + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsGeom = static_cast<const PxCapsuleGeometry&>(geom); + + Capsule capsule; + getCapsule(capsule, capsGeom, pose); + + const PxReal r = capsGeom.radius; + + PxReal param; + const PxReal sqDistance = distancePointSegmentSquared(capsule, point, ¶m); + if(sqDistance<=r*r) + return 0.0f; + + const PxReal d = physx::intrinsics::sqrt(sqDistance); + + if(closestPoint) + { + const PxVec3 cp = capsule.getPointAt(param); + + PxVec3 delta = point - cp; + delta.normalize(); + + *closestPoint = cp + delta * r; + } + return (d - r)*(d - r); + } + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + Box obb; + buildFrom(obb, pose.p, boxGeom.halfExtents, pose.q); + + PxVec3 boxParam; + const PxReal sqDistance = distancePointBoxSquared(point, obb, &boxParam); + if(closestPoint && sqDistance!=0.0f) + { + *closestPoint = obb.transform(boxParam); + } + return sqDistance; + } + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom); + + PxVec3 normal, cp; + PxReal sqDistance; + const bool intersect = pointConvexDistance(normal, cp, sqDistance, point, static_cast<ConvexMesh*>(convexGeom.convexMesh), convexGeom.scale, pose); + if(!intersect && closestPoint) + *closestPoint = cp; + return sqDistance; + } + case PxGeometryType::ePLANE: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxGeometryQuery::pointDistance(): geometry object parameter must be sphere, capsule box or convex geometry."); + break; + } + return -1.0f; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxBounds3 PxGeometryQuery::getWorldBounds(const PxGeometry& geom, const PxTransform& pose, float inflation) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::getWorldBounds(): pose is not valid.", PxBounds3::empty()); + + PxBounds3 bounds; + Gu::computeBounds(bounds, geom, pose, 0.0f, NULL, inflation, false); + PX_ASSERT(bounds.isValid()); + return bounds; +} + +/////////////////////////////////////////////////////////////////////////////// + +extern GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +bool PxGeometryQuery::computePenetration( PxVec3& mtd, PxF32& depth, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxGeometryQuery::computePenetration(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxGeometryQuery::computePenetration(): pose1 is not valid.", false); + + if(geom0.getType() > geom1.getType()) + { + GeomMTDFunc mtdFunc = gGeomMTDMethodTable[geom1.getType()][geom0.getType()]; + PX_ASSERT(mtdFunc); + if(!mtdFunc(mtd, depth, geom1, pose1, geom0, pose0)) + return false; + mtd = -mtd; + return true; + } + else + { + GeomMTDFunc mtdFunc = gGeomMTDMethodTable[geom0.getType()][geom1.getType()]; + PX_ASSERT(mtdFunc); + return mtdFunc(mtd, depth, geom0, pose0, geom1, pose1); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuGeometryUnion.cpp b/PhysX_3.4/Source/GeomUtils/src/GuGeometryUnion.cpp new file mode 100644 index 00000000..d3b0b3f3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuGeometryUnion.cpp @@ -0,0 +1,120 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" + +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +static PX_FORCE_INLINE Gu::ConvexMesh& getConvexMesh(PxConvexMesh* pxcm) +{ + return *static_cast<Gu::ConvexMesh*>(pxcm); +} + +static PX_FORCE_INLINE Gu::TriangleMesh& getTriangleMesh(PxTriangleMesh* pxtm) +{ + return *static_cast<Gu::TriangleMesh*>(pxtm); +} + +static PX_FORCE_INLINE Gu::HeightField& getHeightField(PxHeightField* pxhf) +{ + return *static_cast<Gu::HeightField*>(pxhf); +} + +// PT: TODO: optimize all these data copies +void Gu::GeometryUnion::set(const PxGeometry& g) +{ + switch(g.getType()) + { + case PxGeometryType::eBOX: + { + reinterpret_cast<PxBoxGeometry&>(mGeometry) = static_cast<const PxBoxGeometry&>(g); + } + break; + + case PxGeometryType::eCAPSULE: + { + reinterpret_cast<PxCapsuleGeometry&>(mGeometry) = static_cast<const PxCapsuleGeometry&>(g); + } + break; + + case PxGeometryType::eSPHERE: + { + reinterpret_cast<PxSphereGeometry&>(mGeometry) = static_cast<const PxSphereGeometry&>(g); + reinterpret_cast<PxCapsuleGeometry&>(mGeometry).halfHeight = 0.0f; //AM: make sphere geometry also castable as a zero height capsule. + } + break; + + case PxGeometryType::ePLANE: + { + reinterpret_cast<PxPlaneGeometry&>(mGeometry) = static_cast<const PxPlaneGeometry&>(g); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + reinterpret_cast<PxConvexMeshGeometry&>(mGeometry) = static_cast<const PxConvexMeshGeometry&>(g); + reinterpret_cast<PxConvexMeshGeometryLL&>(mGeometry).hullData = &(::getConvexMesh(get<PxConvexMeshGeometryLL>().convexMesh).getHull()); + reinterpret_cast<PxConvexMeshGeometryLL&>(mGeometry).gpuCompatible = ::getConvexMesh(get<PxConvexMeshGeometryLL>().convexMesh).isGpuCompatible(); + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + reinterpret_cast<PxTriangleMeshGeometry&>(mGeometry) = static_cast<const PxTriangleMeshGeometry&>(g); + reinterpret_cast<PxTriangleMeshGeometryLL&>(mGeometry).meshData = &(::getTriangleMesh(get<PxTriangleMeshGeometryLL>().triangleMesh)); + reinterpret_cast<PxTriangleMeshGeometryLL&>(mGeometry).materialIndices = (::getTriangleMesh(get<PxTriangleMeshGeometryLL>().triangleMesh).getMaterials()); + reinterpret_cast<PxTriangleMeshGeometryLL&>(mGeometry).materials = MaterialIndicesStruct(); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + reinterpret_cast<PxHeightFieldGeometry&>(mGeometry) = static_cast<const PxHeightFieldGeometry&>(g); + reinterpret_cast<PxHeightFieldGeometryLL&>(mGeometry).heightFieldData = &::getHeightField(get<PxHeightFieldGeometryLL>().heightField).getData(); + reinterpret_cast<PxHeightFieldGeometryLL&>(mGeometry).materials = MaterialIndicesStruct(); + } + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("geometry type not handled"); + break; + } +} + + + + diff --git a/PhysX_3.4/Source/GeomUtils/src/GuGeometryUnion.h b/PhysX_3.4/Source/GeomUtils/src/GuGeometryUnion.h new file mode 100644 index 00000000..97f9a461 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuGeometryUnion.h @@ -0,0 +1,246 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GEOMETRY_UNION_H +#define GU_GEOMETRY_UNION_H + +#include "foundation/PxBounds3.h" +#include "GuSIMDHelpers.h" +#include <stddef.h> +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PsAllocator.h" +#include "GuBox.h" +#include "GuCenterExtents.h" +#include "GuSphere.h" +#include "GuCapsule.h" + +namespace physx +{ + +namespace Gu +{ + struct ConvexHullData; + class TriangleMesh; + struct HeightFieldData; + class GeometryUnion; +} + + +// +// Summary of our material approach: +// +// On the API level, materials are accessed via pointer. Internally we store indices into the material table. +// The material table is stored in the SDK and the materials are shared among scenes. To make this threadsafe, +// we have the following approach: +// +// - Every scene has a copy of the SDK master material table +// - At the beginning of a simulation step, the scene material table gets synced to the master material table. +// - While the simulation is running, the scene table does not get touched. +// - Each shape stores the indices of its material(s). When the simulation is not running and a user requests the +// materials of the shape, the indices are used to fetch the material from the master material table. When the +// the simulation is running then the same indices are used internally to fetch the materials from the scene +// material table. If a user changes the materials of a shape while the simulation is running, the index list +// will not get touched, instead the new materials get buffered and synced at the end of the simulation. +// - This whole scheme only works as long as the position of a material in the material table does not change +// when other materials get deleted/inserted. The data structure of the material table makes sure that is the case. +// + +struct PX_PHYSX_COMMON_API MaterialIndicesStruct +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +// PX_SERIALIZATION + MaterialIndicesStruct(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + MaterialIndicesStruct() : indices(NULL), numIndices(0) + { + } + + ~MaterialIndicesStruct() + { + } + + void allocate(PxU16 size) + { + indices = reinterpret_cast<PxU16*>(PX_ALLOC(sizeof(PxU16) * size, "MaterialIndicesStruct::allocate")); + numIndices = size; + } + + void deallocate() + { + PX_FREE(indices); + numIndices = 0; + } + PxU16* indices; // the remap table for material index + PxU16 numIndices; // the size of the remap table + PxU16 pad; // pad for serialization +#if PX_P64_FAMILY + PxU32 pad64; // pad for serialization +#endif +}; + +struct PxConvexMeshGeometryLL: public PxConvexMeshGeometry +{ + const Gu::ConvexHullData* hullData; + bool gpuCompatible; +}; + +struct PxTriangleMeshGeometryLL: public PxTriangleMeshGeometry +{ + const Gu::TriangleMesh* meshData; + const PxU16* materialIndices; + MaterialIndicesStruct materials; +}; + +struct PxHeightFieldGeometryLL : public PxHeightFieldGeometry +{ + const Gu::HeightFieldData* heightFieldData; + MaterialIndicesStruct materials; +}; + +// We sometimes overload capsule code for spheres, so every sphere should have +// valid capsule data (height = 0). This is preferable to a typedef so that we +// can maintain traits separately for a sphere, but some care is required to deal +// with the fact that when a reference to a capsule is extracted, it may have its +// type field set to eSPHERE + +template <typename T> +struct PxcGeometryTraits +{ + enum {TypeID = PxGeometryType::eINVALID }; +}; +template <typename T> struct PxcGeometryTraits<const T> { enum { TypeID = PxcGeometryTraits<T>::TypeID }; }; + +template <> struct PxcGeometryTraits<PxBoxGeometry> { enum { TypeID = PxGeometryType::eBOX }; }; +template <> struct PxcGeometryTraits<PxSphereGeometry> { enum { TypeID = PxGeometryType::eSPHERE }; }; +template <> struct PxcGeometryTraits<PxCapsuleGeometry> { enum { TypeID = PxGeometryType::eCAPSULE }; }; +template <> struct PxcGeometryTraits<PxPlaneGeometry> { enum { TypeID = PxGeometryType::ePLANE }; }; +template <> struct PxcGeometryTraits<PxConvexMeshGeometryLL> { enum { TypeID = PxGeometryType::eCONVEXMESH }; }; +template <> struct PxcGeometryTraits<PxTriangleMeshGeometryLL> { enum { TypeID = PxGeometryType::eTRIANGLEMESH }; }; +template <> struct PxcGeometryTraits<PxHeightFieldGeometryLL> { enum { TypeID = PxGeometryType::eHEIGHTFIELD }; }; +template<class T> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry); +template<> PX_CUDA_CALLABLE PX_INLINE void checkType<PxCapsuleGeometry>(const Gu::GeometryUnion& geometry); +template<> PX_CUDA_CALLABLE PX_INLINE void checkType<const PxCapsuleGeometry>(const Gu::GeometryUnion& geometry); + + +namespace Gu +{ + +class InvalidGeometry : public PxGeometry +{ +public: + PX_CUDA_CALLABLE PX_INLINE InvalidGeometry() : PxGeometry(PxGeometryType::eINVALID) {} +}; + +class PX_PHYSX_COMMON_API GeometryUnion +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + GeometryUnion(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_CUDA_CALLABLE PX_INLINE GeometryUnion() { reinterpret_cast<InvalidGeometry&>(mGeometry) = InvalidGeometry(); } + PX_CUDA_CALLABLE PX_INLINE GeometryUnion(const PxGeometry& g) { set(g); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxGeometry& getGeometry() const { return reinterpret_cast<const PxGeometry&>(mGeometry); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometryType::Enum getType() const { return reinterpret_cast<const PxGeometry&>(mGeometry).getType(); } + + PX_CUDA_CALLABLE void set(const PxGeometry& g); + + template<class Geom> PX_CUDA_CALLABLE PX_FORCE_INLINE Geom& get() + { + checkType<Geom>(*this); + return reinterpret_cast<Geom&>(mGeometry); + } + + template<class Geom> PX_CUDA_CALLABLE PX_FORCE_INLINE const Geom& get() const + { + checkType<Geom>(*this); + return reinterpret_cast<const Geom&>(mGeometry); + } + +private: + + union { + void* alignment; // PT: Makes sure the class is at least aligned to pointer size. See DE6803. + PxU8 box[sizeof(PxBoxGeometry)]; + PxU8 sphere[sizeof(PxSphereGeometry)]; + PxU8 capsule[sizeof(PxCapsuleGeometry)]; + PxU8 plane[sizeof(PxPlaneGeometry)]; + PxU8 convex[sizeof(PxConvexMeshGeometryLL)]; + PxU8 mesh[sizeof(PxTriangleMeshGeometryLL)]; + PxU8 heightfield[sizeof(PxHeightFieldGeometryLL)]; + PxU8 invalid[sizeof(InvalidGeometry)]; + } mGeometry; +}; +} + + +template<class T> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(PxU32(geometry.getType()) == PxU32(PxcGeometryTraits<T>::TypeID)); + PX_UNUSED(geometry); +} + +template<> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType<PxCapsuleGeometry>(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(geometry.getType() == PxGeometryType::eCAPSULE || geometry.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(geometry); +} + +template<> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType<const PxCapsuleGeometry>(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(geometry.getType()== PxGeometryType::eCAPSULE || geometry.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(geometry); +} + +// the shape structure relies on punning capsules and spheres +PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxCapsuleGeometry, radius) == PX_OFFSET_OF(PxSphereGeometry, radius)); +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuInternal.cpp b/PhysX_3.4/Source/GeomUtils/src/GuInternal.cpp new file mode 100644 index 00000000..4e5e25a3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuInternal.cpp @@ -0,0 +1,159 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxBounds3.h" +#include "PsIntrinsics.h" +#include "GuInternal.h" +#include "GuBox.h" +#include "GuVecPlane.h" +#include "PsMathUtils.h" +#include "PxCapsuleGeometry.h" +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +using namespace physx; + +/** +Computes the aabb points. +\param pts [out] 8 box points +*/ +void Gu::computeBoxPoints(const PxBounds3& bounds, PxVec3* PX_RESTRICT pts) +{ + PX_ASSERT(pts); + + // Get box corners + const PxVec3& minimum = bounds.minimum; + const PxVec3& maximum = bounds.maximum; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + // Generate 8 corners of the bbox + pts[0] = PxVec3(minimum.x, minimum.y, minimum.z); + pts[1] = PxVec3(maximum.x, minimum.y, minimum.z); + pts[2] = PxVec3(maximum.x, maximum.y, minimum.z); + pts[3] = PxVec3(minimum.x, maximum.y, minimum.z); + pts[4] = PxVec3(minimum.x, minimum.y, maximum.z); + pts[5] = PxVec3(maximum.x, minimum.y, maximum.z); + pts[6] = PxVec3(maximum.x, maximum.y, maximum.z); + pts[7] = PxVec3(minimum.x, maximum.y, maximum.z); +} + +PxPlane Gu::getPlane(const PxTransform& pose) +{ + const PxVec3 n = pose.q.getBasisVector0(); + return PxPlane(n, -pose.p.dot(n)); +} + +void Gu::computeBoundsAroundVertices(PxBounds3& bounds, PxU32 nbVerts, const PxVec3* PX_RESTRICT verts) +{ + // PT: we can safely V4LoadU the first N-1 vertices. We must V3LoadU the last vertex, to make sure we don't read + // invalid memory. Since we have to special-case that last vertex anyway, we reuse that code to also initialize + // the minV/maxV values (bypassing the need for a 'setEmpty()' initialization). + + if(!nbVerts) + { + bounds.setEmpty(); + return; + } + + PxU32 nbSafe = nbVerts-1; + + // PT: read last (unsafe) vertex using V3LoadU, initialize minV/maxV + const Vec4V lastVertexV = Vec4V_From_Vec3V(V3LoadU(&verts[nbSafe].x)); + Vec4V minV = lastVertexV; + Vec4V maxV = lastVertexV; + + // PT: read N-1 first (safe) vertices using V4LoadU + while(nbSafe--) + { + const Vec4V vertexV = V4LoadU(&verts->x); + verts++; + + minV = V4Min(minV, vertexV); + maxV = V4Max(maxV, vertexV); + } + + StoreBounds(bounds, minV, maxV); +} + +void Gu::computeSweptBox(Gu::Box& dest, const PxVec3& extents, const PxVec3& center, const PxMat33& rot, const PxVec3& unitDir, const PxReal distance) +{ + PxVec3 R1, R2; + Ps::computeBasis(unitDir, R1, R2); + + PxReal dd[3]; + dd[0] = PxAbs(rot.column0.dot(unitDir)); + dd[1] = PxAbs(rot.column1.dot(unitDir)); + dd[2] = PxAbs(rot.column2.dot(unitDir)); + PxReal dmax = dd[0]; + PxU32 ax0=1; + PxU32 ax1=2; + if(dd[1]>dmax) + { + dmax=dd[1]; + ax0=0; + ax1=2; + } + if(dd[2]>dmax) + { + dmax=dd[2]; + ax0=0; + ax1=1; + } + if(dd[ax1]<dd[ax0]) + Ps::swap(ax0, ax1); + + R1 = rot[ax0]; + R1 -= (R1.dot(unitDir))*unitDir; // Project to plane whose normal is dir + R1.normalize(); + R2 = unitDir.cross(R1); + + dest.setAxes(unitDir, R1, R2); + + PxReal offset[3]; + offset[0] = distance; + offset[1] = distance*(unitDir.dot(R1)); + offset[2] = distance*(unitDir.dot(R2)); + + for(PxU32 r=0; r<3; r++) + { + const PxVec3& R = dest.rot[r]; + dest.extents[r] = offset[r]*0.5f + PxAbs(rot.column0.dot(R))*extents.x + PxAbs(rot.column1.dot(R))*extents.y + PxAbs(rot.column2.dot(R))*extents.z; + } + + dest.center = center + unitDir*distance*0.5f; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuInternal.h b/PhysX_3.4/Source/GeomUtils/src/GuInternal.h new file mode 100644 index 00000000..611df543 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuInternal.h @@ -0,0 +1,151 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GEOM_UTILS_INTERNAL_H +#define GU_GEOM_UTILS_INTERNAL_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "PxCapsuleGeometry.h" +#include "PxBoxGeometry.h" +#include "PsMathUtils.h" +#include "PsUtilities.h" + +#define GU_EPSILON_SAME_DISTANCE 1e-3f + +namespace physx +{ +namespace Gu +{ + class Box; + + // PT: TODO: now that the Gu files are not exposed to users anymore, we should move back capsule-related functions + // to GuCapsule.h, etc + + PX_PHYSX_COMMON_API const PxU8* getBoxEdges(); + + PX_PHYSX_COMMON_API void computeBoxPoints(const PxBounds3& bounds, PxVec3* PX_RESTRICT pts); + PX_PHYSX_COMMON_API void computeBoundsAroundVertices(PxBounds3& bounds, PxU32 nbVerts, const PxVec3* PX_RESTRICT verts); + + void computeBoxAroundCapsule(const Capsule& capsule, Box& box); + + PxPlane getPlane(const PxTransform& pose); + + PX_FORCE_INLINE PxVec3 getCapsuleHalfHeightVector(const PxTransform& transform, const PxCapsuleGeometry& capsuleGeom) + { + return transform.q.getBasisVector0() * capsuleGeom.halfHeight; + } + + PX_FORCE_INLINE void getCapsuleSegment(const PxTransform& transform, const PxCapsuleGeometry& capsuleGeom, Gu::Segment& segment) + { + const PxVec3 tmp = getCapsuleHalfHeightVector(transform, capsuleGeom); + segment.p0 = transform.p + tmp; + segment.p1 = transform.p - tmp; + } + + PX_FORCE_INLINE void getCapsule(Gu::Capsule& capsule, const PxCapsuleGeometry& capsuleGeom, const PxTransform& pose) + { + getCapsuleSegment(pose, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + } + + void computeSweptBox(Gu::Box& box, const PxVec3& extents, const PxVec3& center, const PxMat33& rot, const PxVec3& unitDir, const PxReal distance); + + /** + * PT: computes "alignment value" used to select the "best" triangle in case of identical impact distances (for sweeps). + * This simply computes how much a triangle is aligned with a given sweep direction. + * Captured in a function to make sure it is always computed correctly, i.e. working for double-sided triangles. + * + * \param triNormal [in] triangle's normal + * \param unitDir [in] sweep direction (normalized) + * \return alignment value in [-1.0f, 0.0f]. -1.0f for fully aligned, 0.0f for fully orthogonal. + */ + PX_FORCE_INLINE PxReal computeAlignmentValue(const PxVec3& triNormal, const PxVec3& unitDir) + { + // PT: initial dot product gives the angle between the two, with "best" triangles getting a +1 or -1 score + // depending on their winding. We take the absolute value to ignore the impact of winding. We negate the result + // to make the function compatible with the initial code, which assumed single-sided triangles and expected -1 + // for best triangles. + return -PxAbs(triNormal.dot(unitDir)); + } + + /** + * PT: sweeps: determines if a newly touched triangle is "better" than best one so far. + * In this context "better" means either clearly smaller impact distance, or a similar impact + * distance but a normal more aligned with the sweep direction. + * + * \param triImpactDistance [in] new triangle's impact distance + * \param triAlignmentValue [in] new triangle's alignment value (as computed by computeAlignmentValue) + * \param bestImpactDistance [in] current best triangle's impact distance + * \param bestAlignmentValue [in] current best triangle's alignment value (as computed by computeAlignmentValue) + * \param maxDistance [in] maximum distance of the query, hit cannot be longer than this maxDistance + * \param distEpsilon [in] tris have "similar" impact distances if the difference is smaller than 2*distEpsilon + * \return true if new triangle is better + */ + PX_FORCE_INLINE bool keepTriangle( float triImpactDistance, float triAlignmentValue, + float bestImpactDistance, float bestAlignmentValue, float maxDistance, + float distEpsilon) + { + // Reject triangle if further than the maxDistance + if(triImpactDistance > maxDistance) + return false; + + // PT: make it a relative epsilon to make sure it still works with large distances + distEpsilon *= PxMax(1.0f, PxMax(triImpactDistance, bestImpactDistance)); + + // If new distance is more than epsilon closer than old distance + if(triImpactDistance < bestImpactDistance - distEpsilon) + return true; + + // If new distance is no more than epsilon farther than oldDistance and "face is more opposing than previous" + if(triImpactDistance < bestImpactDistance+distEpsilon && triAlignmentValue < bestAlignmentValue) + return true; + + // If alignment value is the same, but the new triangle is closer than the best distance + if(triAlignmentValue == bestAlignmentValue && triImpactDistance < bestImpactDistance) + return true; + + // If initial overlap happens, keep the triangle + if(triImpactDistance == 0.0f) + return true; + + return false; + } + + #define StoreBounds(bounds, minV, maxV) \ + V4StoreU(minV, &bounds.minimum.x); \ + PX_ALIGN(16, PxVec4) max4; \ + V4StoreA(maxV, &max4.x); \ + bounds.maximum = PxVec3(max4.x, max4.y, max4.z); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuMTD.cpp b/PhysX_3.4/Source/GeomUtils/src/GuMTD.cpp new file mode 100644 index 00000000..7bbbfb15 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuMTD.cpp @@ -0,0 +1,1457 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuMTD.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentBox.h" + + +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuInternal.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuBoxConversion.h" +#include "GuGeometryUnion.h" +#include "GuShapeConvex.h" +#include "GuPCMShapeConvex.h" +#include "GuPCMContactGen.h" +#include "GuConvexMesh.h" +#include "GuGJK.h" + +#include "PsUtilities.h" +#include "PsVecTransform.h" +#include "PsMathUtils.h" +#include "PxMeshScale.h" +#include "PxConvexMeshGeometry.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE PxF32 manualNormalize(PxVec3& mtd, const PxVec3& normal, PxReal lenSq) +{ + const PxF32 len = PxSqrt(lenSq); + + // We do a *manual* normalization to check for singularity condition + if(lenSq < 1e-6f) + mtd = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + mtd = normal * 1.0f / len; + + return len; +} + +static PX_FORCE_INLINE float validateDepth(float depth) +{ + // PT: penetration depth must always be positive or null, but FPU accuracy being what it is, we sometimes + // end up with very small, epsilon-sized negative depths. We clamp those to zero, since they don't indicate + // real bugs in the MTD functions. However anything larger than epsilon is wrong, and caught with an assert. + const float epsilon = 1.e-3f; + + //ML: because we are shrunking the shape in this moment, so the depth might be larger than eps, this condition is no longer valid + //PX_ASSERT(depth>=-epsilon); + PX_UNUSED(epsilon); + return PxMax(depth, 0.0f); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: the function names should follow the order in which the PxGeometryTypes are listed, +// i.e. computeMTD_Type0Type1 with Type0<=Type1. This is to guarantee that the proper results +// (following the desired convention) are returned from the PxGeometryQuery-level call. + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SphereSphere(PxVec3& mtd, PxF32& depth, const Sphere& sphere0, const Sphere& sphere1) +{ + const PxVec3 delta = sphere0.center - sphere1.center; + const PxReal d2 = delta.magnitudeSquared(); + const PxReal radiusSum = sphere0.radius + sphere1.radius; + + if(d2 > radiusSum*radiusSum) + return false; + + const PxF32 d = manualNormalize(mtd, delta, d2); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SphereCapsule(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Capsule& capsule) +{ + const PxReal radiusSum = sphere.radius + capsule.radius; + + PxReal u; + const PxReal d2 = distancePointSegmentSquared(capsule, sphere.center, &u); + + if(d2 > radiusSum*radiusSum) + return false; + + const PxVec3 normal = sphere.center - capsule.getPointAt(u); + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = manualNormalize(mtd, normal, lenSq); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + + +//This version is ported 1:1 from novodex +static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, + PxReal sphereRadius, + const PxVec3& boxExtents, +// const PxcCachedTransforms& boxCacheTransform, + const PxTransform& boxTransform, + PxVec3& point, + PxVec3& normal, + PxReal& separation, + PxReal contactDistance) +{ + + //returns true on contact + const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; + PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. + + //check if delta is outside ABB - and clip the vector to the ABB. + bool outside = false; + + if (dRot.x < -boxExtents.x) + { + outside = true; + dRot.x = -boxExtents.x; + } + else if (dRot.x > boxExtents.x) + { + outside = true; + dRot.x = boxExtents.x; + } + + if (dRot.y < -boxExtents.y) + { + outside = true; + dRot.y = -boxExtents.y; + } + else if (dRot.y > boxExtents.y) + { + outside = true; + dRot.y = boxExtents.y; + } + + if (dRot.z < -boxExtents.z) + { + outside = true; + dRot.z =-boxExtents.z; + } + else if (dRot.z > boxExtents.z) + { + outside = true; + dRot.z = boxExtents.z; + } + + if (outside) //if clipping was done, sphere center is outside of box. + { + point = boxTransform.rotate(dRot); //get clipped delta back in world coords. + normal = delta - point; //what we clipped away. + const PxReal lenSquared = normal.magnitudeSquared(); + const PxReal inflatedDist = sphereRadius + contactDistance; + if (lenSquared > inflatedDist * inflatedDist) + return false; //disjoint + + //normalize to make it into the normal: + separation = PxRecipSqrt(lenSquared); + normal *= separation; + separation *= lenSquared; + //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. + //we could also use point here, which has same direction. + //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. + //We'll just use vertex face for now, this info isn't really being used anyway. + //contact point is point on surface of cube closest to sphere center. + point += boxTransform.p; + separation -= sphereRadius; + return true; + } + else + { + //center is in box, we definitely have a contact. + PxVec3 locNorm; //local coords contact normal + + PxVec3 absdRot; + absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); + PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. + + //find smallest element of distToSurface + if (distToSurface.y < distToSurface.x) + { + if (distToSurface.y < distToSurface.z) + { + //y + locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); + separation = -distToSurface.y; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + else + { + if (distToSurface.x < distToSurface.z) + { + //x + locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); + separation = -distToSurface.x; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + point = sphereOrigin; + normal = boxTransform.rotate(locNorm); + separation -= sphereRadius; + return true; + } +} + +static bool computeMTD_SphereBox(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Box& box) +{ + PxVec3 point; + if(!ContactSphereBox( sphere.center, sphere.radius, + box.extents, PxTransform(box.center, PxQuat(box.rot)), + point, mtd, depth, 0.0f)) + return false; + depth = validateDepth(-depth); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_CapsuleCapsule(PxVec3& mtd, PxF32& depth, const Capsule& capsule0, const Capsule& capsule1) +{ + PxReal s,t; + const PxReal d2 = distanceSegmentSegmentSquared(capsule0, capsule1, &s, &t); + + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + if(d2 > radiusSum*radiusSum) + return false; + + const PxVec3 normal = capsule0.getPointAt(s) - capsule1.getPointAt(t); + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = manualNormalize(mtd, normal, lenSq); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + + +static PX_FORCE_INLINE void reorderMTD(PxVec3& mtd, const PxVec3& center0, const PxVec3& center1) +{ + const PxVec3 witness = center0 - center1; + if(mtd.dot(witness) < 0.0f) + mtd = -mtd; +} + +static PX_FORCE_INLINE void projectBox(PxReal& min, PxReal& max, const PxVec3& axis, const Box& box) +{ + const PxReal boxCen = box.center.dot(axis); + const PxReal boxExt = + PxAbs(box.rot.column0.dot(axis)) * box.extents.x + + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; + + min = boxCen - boxExt; + max = boxCen + boxExt; +} + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project box + PxReal Min1, Max1; + projectBox(Min1, Max1, axis, box); + + // Test projections + if(max0<Min1 || Max1<min0) + return false; + + const PxReal d0 = max0 - Min1; + PX_ASSERT(d0>=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static bool PxcCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) +{ + PxVec3 Sep(0.0f); + PxReal PenDepth = PX_MAX_REAL; + + // Test normals + for(PxU32 i=0;i<3;i++) + { + PxReal d; + if(!PxcTestAxis(box.rot[i], segment, radius, box, d)) + return false; + + if(d<PenDepth) + { + PenDepth = d; + Sep = box.rot[i]; + } + } + + // Test edges + PxVec3 CapsuleAxis(segment.p1 - segment.p0); + CapsuleAxis = CapsuleAxis.getNormalized(); + for(PxU32 i=0;i<3;i++) + { + PxVec3 Cross = CapsuleAxis.cross(box.rot[i]); + if(!Ps::isAlmostZero(Cross)) + { + Cross = Cross.getNormalized(); + PxReal d; + if(!PxcTestAxis(Cross, segment, radius, box, d)) + return false; + + if(d<PenDepth) + { + PenDepth = d; + Sep = Cross; + } + } + } + + reorderMTD(Sep, segment.computeCenter(), box.center); + + if(t) + *t = validateDepth(PenDepth); + if(pp) + *pp = Sep; + + return true; +} + +static bool computeMTD_CapsuleBox(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const Box& box) +{ + PxReal t; + PxVec3 onBox; + + const PxReal d2 = distanceSegmentBoxSquared(capsule.p0, capsule.p1, box.center, box.extents, box.rot, &t, &onBox); + + if(d2 > capsule.radius*capsule.radius) + return false; + + if(d2 != 0.0f) + { + // PT: the capsule segment doesn't intersect the box => distance-based version + const PxVec3 onSegment = capsule.getPointAt(t); + onBox = box.center + box.rot.transform(onBox); + + PxVec3 normal = onSegment - onBox; + PxReal normalLen = normal.magnitude(); + + if(normalLen != 0.0f) + { + normal *= 1.0f/normalLen; + + mtd = normal; + depth = validateDepth(capsule.radius - PxSqrt(d2)); + return true; + } + } + + // PT: the capsule segment intersects the box => penetration-based version + return PxcCapsuleOBBOverlap3(capsule, capsule.radius, box, &depth, &mtd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool PxcTestAxis(const PxVec3& axis, const Box& box0, const Box& box1, PxReal& depth) +{ + // Project box + PxReal min0, max0; + projectBox(min0, max0, axis, box0); + + // Project box + PxReal Min1, Max1; + projectBox(Min1, Max1, axis, box1); + + // Test projections + if(max0<Min1 || Max1<min0) + return false; + + const PxReal d0 = max0 - Min1; + PX_ASSERT(d0>=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static PX_FORCE_INLINE bool testBoxBoxAxis(PxVec3& mtd, PxF32& depth, const PxVec3& axis, const Box& box0, const Box& box1) +{ + PxF32 d; + if(!PxcTestAxis(axis, box0, box1, d)) + return false; + if(d<depth) + { + depth = d; + mtd = axis; + } + return true; +} + +static bool computeMTD_BoxBox(PxVec3& _mtd, PxF32& _depth, const Box& box0, const Box& box1) +{ + PxVec3 mtd; + PxF32 depth = PX_MAX_F32; + + if(!testBoxBoxAxis(mtd, depth, box0.rot.column0, box0, box1)) + return false; + if(!testBoxBoxAxis(mtd, depth, box0.rot.column1, box0, box1)) + return false; + if(!testBoxBoxAxis(mtd, depth, box0.rot.column2, box0, box1)) + return false; + + if(!testBoxBoxAxis(mtd, depth, box1.rot.column0, box0, box1)) + return false; + if(!testBoxBoxAxis(mtd, depth, box1.rot.column1, box0, box1)) + return false; + if(!testBoxBoxAxis(mtd, depth, box1.rot.column2, box0, box1)) + return false; + + for(PxU32 j=0;j<3;j++) + { + for(PxU32 i=0;i<3;i++) + { + PxVec3 cross = box0.rot[i].cross(box1.rot[j]); + if(!Ps::isAlmostZero(cross)) + { + cross = cross.getNormalized(); + + if(!testBoxBoxAxis(mtd, depth, cross, box0, box1)) + return false; + } + } + } + + + reorderMTD(mtd, box1.center, box0.center); + + _mtd = -mtd; + _depth = validateDepth(depth); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +using namespace physx::shdfnd::aos; + +bool pointConvexDistance(PxVec3& normal_, PxVec3& closestPoint_, PxReal& sqDistance, const PxVec3& pt, const ConvexMesh* convexMesh, const PxMeshScale& meshScale, const PxTransform& convexPose) +{ + const PxTransform transform0(pt); + + PxVec3 onSegment, onConvex; + + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + Vec3V closA, closB, normalV; + GjkStatus status; + FloatV dist; + { + const ConvexHullData* hullData = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + const ConvexHullV convexHull_(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); + + const PsMatTransformV aToB(convexPose.transformInv(transform0)); + + //const CapsuleV capsule(zeroV, zeroV, FZero());//this is a point + const CapsuleV capsule_(aToB.p, FZero());//this is a point + LocalConvex<CapsuleV> capsule(capsule_); + LocalConvex<ConvexHullV> convexHull(convexHull_); + + status = gjk<LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(capsule, convexHull, aToB.p, FMax(), closA, closB, normalV, dist); + } + + bool intersect = status == GJK_CONTACT; + if(intersect) + { + sqDistance = 0.0f; + } + else + { + const FloatV sqDist = FMul(dist, dist); + FStore(sqDist, &sqDistance); + V3StoreU(normalV, normal_); + V3StoreU(closB, closestPoint_); + + normal_ = convexPose.rotate(normal_); + closestPoint_ = convexPose.transform(closestPoint_); + } + + return intersect; +} + +static bool computeMTD_SphereConvex(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + PxReal d2; + const ConvexMesh* convexMesh = static_cast<const ConvexMesh*>(convexGeom.convexMesh); + PxVec3 dummy; + if(!pointConvexDistance(mtd, dummy, d2, sphere.center, convexMesh, convexGeom.scale, convexPose)) + { + if(d2 > sphere.radius*sphere.radius) + return false; + + depth = validateDepth(sphere.radius - PxSqrt(d2)); + mtd = -mtd; + return true; + } + + // PT: if we reach this place, the sphere center touched the convex => switch to penetration-based code + PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); + const HullPolygonData* polygons = convexMesh->getPolygons(); + const PxVec3 localSphereCenter = convexPose.transformInv(sphere.center); + PxReal dmax = -PX_MAX_F32; + while(nbPolygons--) + { + const HullPolygonData& polygon = *polygons++; + const PxF32 d = polygon.mPlane.distance(localSphereCenter); + if(d>dmax) + { + dmax = d; + mtd = convexPose.rotate(polygon.mPlane.n); + } + } + depth = validateDepth(sphere.radius - dmax); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +//ML : capsule will be in the local space of convexHullV +static bool internalComputeMTD_CapsuleConvex(const CapsuleV& capsule, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData; + getPCMConvexData(convexHullV, idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl<ConvexHullV>)]; + + SupportLocal* map = (idtScale ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullV>)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); + + return computeMTD(capsule, polyData, map, penetrationDepth, normal); +} + +static bool computeMTD_CapsuleConvex(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const PxTransform& capsulePose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const FloatV capsuleHalfHeight = FLoad(capsule.length()*0.5f); + const FloatV capsuleRadius = FLoad(capsule.radius); + + const Vec3V zeroV = V3Zero(); + // Convex mesh + const ConvexMesh* convexMesh = static_cast<const ConvexMesh*>(convexGeom.convexMesh); + const ConvexHullData* hull = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //~Convex mesh + + + const QuatV q0 = QuatVLoadU(&capsulePose.q.x); + const Vec3V p0 = V3LoadU(&capsulePose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + Vec3V normal = zeroV; + FloatV penetrationDepth = FZero(); + + CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + const bool idtScale = convexGeom.scale.isIdentity(); + bool hasContacts = internalComputeMTD_CapsuleConvex(capsuleV, idtScale, convexHullV, transf1, penetrationDepth, normal); + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + + return hasContacts; +} + +/////////////////////////////////////////////////////////////////////////////// +static bool internalComputeMTD_BoxConvex(const PxVec3 halfExtents, const BoxV& box, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData0; + PCMPolygonalBox polyBox0(halfExtents); + polyBox0.getPolygonalData(&polyData0); + polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; + + PolygonalData polyData1; + getPCMConvexData(convexHullV, idtScale, polyData1); + + Mat33V identity = M33Identity(); + SupportLocalImpl<BoxV> map0(box, transf0, identity, identity, true); + + PxU8 buff[sizeof(SupportLocalImpl<ConvexHullV>)]; + + SupportLocal* map1 = (idtScale ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullV>)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); + + return computeMTD(polyData0, polyData1, &map0, map1, penetrationDepth, normal); + +} + +static bool computeMTD_BoxConvex(PxVec3& mtd, PxF32& depth, const Box& box, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const Vec3V zeroV = V3Zero(); + const PxTransform boxPose = box.getTransform(); + const Vec3V boxExtents = V3LoadU(box.extents); + BoxV boxV(zeroV, boxExtents); + + // Convex mesh + const ConvexMesh* convexMesh = static_cast<const ConvexMesh*>(convexGeom.convexMesh); + const ConvexHullData* hull = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //~Convex mesh + + + const QuatV q0 = QuatVLoadU(&boxPose.q.x); + const Vec3V p0 = V3LoadU(&boxPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + Vec3V normal=zeroV; + FloatV penetrationDepth=FZero(); + + const bool idtScale = convexGeom.scale.isIdentity(); + bool hasContacts = internalComputeMTD_BoxConvex(box.extents, boxV, idtScale, convexHullV, transf0, transf1, penetrationDepth, normal); + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + + return hasContacts; + +} + + +static bool internalComputeMTD_ConvexConvex(const bool idtScale0, const bool idtScale1, ConvexHullV& convexHullV0, ConvexHullV& convexHullV1, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData0, polyData1; + getPCMConvexData(convexHullV0, idtScale0, polyData0); + getPCMConvexData(convexHullV1, idtScale1, polyData1); + + PxU8 buff0[sizeof(SupportLocalImpl<ConvexHullV>)]; + PxU8 buff1[sizeof(SupportLocalImpl<ConvexHullV>)]; + + SupportLocal* map0 = (idtScale0 ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff0, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHullV0), transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff0, SupportLocalImpl<ConvexHullV>)(convexHullV0, transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0))); + + SupportLocal* map1 = (idtScale1 ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff1, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHullV1), transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff1, SupportLocalImpl<ConvexHullV>)(convexHullV1, transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1))); + + return computeMTD(polyData0, polyData1, map0, map1, penetrationDepth, normal); +} + +/////////////////////////////////////////////////////////////////////////////// +static bool computeMTD_ConvexConvex(PxVec3& mtd, PxF32& depth, const PxConvexMeshGeometry& convexGeom0, const PxTransform& convexPose0, const PxConvexMeshGeometry& convexGeom1, const PxTransform& convexPose1) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + // Convex mesh + const ConvexMesh* convexMesh0 = static_cast<const ConvexMesh*>(convexGeom0.convexMesh); + const ConvexHullData* hull0 = &convexMesh0->getHull(); + const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); + ConvexHullV convexHullV0(hull0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); + //~Convex mesh + + // Convex mesh + const ConvexMesh* convexMesh1 = static_cast<const ConvexMesh*>(convexGeom1.convexMesh); + const ConvexHullData* hull1 = &convexMesh1->getHull(); + const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); + ConvexHullV convexHullV1(hull1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); + //~Convex mesh + + const QuatV q0 = QuatVLoadU(&convexPose0.q.x); + const Vec3V p0 = V3LoadU(&convexPose0.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose1.q.x); + const Vec3V p1 = V3LoadU(&convexPose1.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + Vec3V normal = zeroV; + FloatV penetrationDepth = FZero(); + + + const bool idtScale0 = convexGeom0.scale.isIdentity(); + const bool idtScale1 = convexGeom1.scale.isIdentity(); + + bool hasContacts = internalComputeMTD_ConvexConvex(idtScale0, idtScale1, convexHullV0, convexHullV1, transf0, transf1, penetrationDepth, normal); + + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + return hasContacts; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SpherePlane(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxPlane& plane) +{ + const PxReal d = plane.distance(sphere.center); + if(d>sphere.radius) + return false; + + mtd = plane.n; + depth = validateDepth(sphere.radius - d); + return true; +} + +static bool computeMTD_PlaneBox(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Box& box) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + PxReal dmin = plane.distance(pts[0]); + for(PxU32 i=1;i<8;i++) + { + const PxReal d = plane.distance(pts[i]); + dmin = physx::intrinsics::selectMin(dmin, d); + } + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +static bool computeMTD_PlaneCapsule(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Capsule& capsule) +{ + const PxReal d0 = plane.distance(capsule.p0); + const PxReal d1 = plane.distance(capsule.p1); + const PxReal dmin = physx::intrinsics::selectMin(d0, d1) - capsule.radius; + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +static bool computeMTD_PlaneConvex(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const ConvexMesh* convexMesh = static_cast<const ConvexMesh*>(convexGeom.convexMesh); + PxU32 nbVerts = convexMesh->getNbVerts(); + const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); + + PxReal dmin = plane.distance(convexPose.transform(verts[0])); + for(PxU32 i=1;i<nbVerts;i++) + { + const PxReal d = plane.distance(convexPose.transform(verts[i])); + dmin = physx::intrinsics::selectMin(dmin, d); + } + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool processContacts(PxVec3& mtd, PxF32& depth, PxU32 nbContacts, const ContactPoint* contacts) +{ + if(nbContacts) + { + PxVec3 mn(0.0f), mx(0.0f); + for(PxU32 i=0; i<nbContacts; i++) + { + const ContactPoint& ct = contacts[i]; + PxVec3 depenetration = ct.separation * ct.normal; + + mn = mn.minimum(depenetration); + mx = mx.maximum(depenetration); + } + + // even if we are already moving in separation direction, we should still depenetrate + // so no dot velocity test + // here we attempt to equalize the separations pushing in opposing directions along each axis + PxVec3 mn1, mx1; + mn1.x = (mn.x == 0.0f) ? mx.x : mn.x; + mn1.y = (mn.y == 0.0f) ? mx.y : mn.y; + mn1.z = (mn.z == 0.0f) ? mx.z : mn.z; + mx1.x = (mx.x == 0.0f) ? mn.x : mx.x; + mx1.y = (mx.y == 0.0f) ? mn.y : mx.y; + mx1.z = (mx.z == 0.0f) ? mn.z : mx.z; + PxVec3 sepDir((mn1 + mx1)*0.5f); + + if(sepDir.magnitudeSquared() < 1e-10f) + { + + return false; + + } + mtd = -sepDir.getNormalized(); + depth = sepDir.magnitude(); + } + return true; +} + +static bool computeMTD_SphereMesh(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose) +{ + GeometryUnion shape0; + shape0.set(PxSphereGeometry(sphere.radius)); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactSphereMesh(shape0, shape1, PxTransform(sphere.center), meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_CapsuleMesh(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose) +{ + PxReal halfHeight; + const PxTransform capsuleTransform = PxTransformFromSegment(capsule.p0, capsule.p1, &halfHeight); + + GeometryUnion shape0; + shape0.set(PxCapsuleGeometry(capsule.radius, halfHeight)); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactCapsuleMesh(shape0, shape1, capsuleTransform, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_BoxMesh(PxVec3& mtd, PxF32& depth, const Box& box, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose) +{ + const PxTransform boxPose(box.center, PxQuat(box.rot)); + + GeometryUnion shape0; + shape0.set(PxBoxGeometry(box.extents)); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactBoxMesh(shape0, shape1, boxPose, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_ConvexMesh(PxVec3& mtd, PxF32& depth, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose) +{ + GeometryUnion shape0; + shape0.set(convexGeom); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactConvexMesh(shape0, shape1, convexPose, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_SphereHeightField(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxHeightFieldGeometry& meshGeom, const PxTransform& meshPose) +{ + GeometryUnion shape0; + shape0.set(PxSphereGeometry(sphere.radius)); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + const PxTransform spherePose(sphere.center); + + if(!contactSphereHeightfield(shape0, shape1, spherePose, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_CapsuleHeightField(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const PxHeightFieldGeometry& meshGeom, const PxTransform& meshPose) +{ + PxReal halfHeight; + const PxTransform capsuleTransform = PxTransformFromSegment(capsule.p0, capsule.p1, &halfHeight); + + GeometryUnion shape0; + shape0.set(PxCapsuleGeometry(capsule.radius, halfHeight)); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactCapsuleHeightfield(shape0, shape1, capsuleTransform, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_BoxHeightField(PxVec3& mtd, PxF32& depth, const Box& box, const PxHeightFieldGeometry& meshGeom, const PxTransform& meshPose) +{ + const PxTransform boxPose(box.center, PxQuat(box.rot)); + + GeometryUnion shape0; + shape0.set(PxBoxGeometry(box.extents)); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactBoxHeightfield(shape0, shape1, boxPose, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + +static bool computeMTD_ConvexHeightField(PxVec3& mtd, PxF32& depth, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, const PxHeightFieldGeometry& meshGeom, const PxTransform& meshPose) +{ + GeometryUnion shape0; + shape0.set(convexGeom); + + GeometryUnion shape1; + shape1.set(meshGeom); + + Cache cache; + + ContactBuffer contactBuffer; + contactBuffer.reset(); + + if(!contactConvexHeightfield(shape0, shape1, convexPose, meshPose, NarrowPhaseParams(0.0f, 0.0f, 1.0f), cache, contactBuffer, NULL)) + return false; + + if(!processContacts(mtd, depth, contactBuffer.count, contactBuffer.contacts)) + return false; + + return contactBuffer.count!=0; +} + + +static bool GeomMTDCallback_NotSupported(GU_MTD_FUNC_PARAMS) +{ + PX_ALWAYS_ASSERT_MESSAGE("NOT SUPPORTED"); + PX_UNUSED(mtd); PX_UNUSED(depth); PX_UNUSED(geom0); PX_UNUSED(geom1); PX_UNUSED(pose0); PX_UNUSED(pose1); + + return false; +} + +static bool GeomMTDCallback_SphereSphere(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eSPHERE); + + const PxSphereGeometry& sphereGeom0 = static_cast<const PxSphereGeometry&>(geom0); + const PxSphereGeometry& sphereGeom1 = static_cast<const PxSphereGeometry&>(geom1); + + return computeMTD_SphereSphere(mtd, depth, Sphere(pose0.p, sphereGeom0.radius), Sphere(pose1.p, sphereGeom1.radius)); +} + +static bool GeomMTDCallback_SpherePlane(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); + PX_UNUSED(geom1); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + return computeMTD_SpherePlane(mtd, depth, Sphere(pose0.p, sphereGeom.radius), getPlane(pose1)); +} + +static bool GeomMTDCallback_SphereCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom1); + + Capsule capsule; + getCapsuleSegment(pose1, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_SphereCapsule(mtd, depth, Sphere(pose0.p, sphereGeom.radius), capsule); +} + +static bool GeomMTDCallback_SphereBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom1); + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_SphereBox(mtd, depth, Sphere(pose0.p, sphereGeom.radius), obb); +} + +static bool GeomMTDCallback_SphereConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + return computeMTD_SphereConvex(mtd, depth, Sphere(pose0.p, sphereGeom.radius), convexGeom, pose1); +} + +static bool GeomMTDCallback_SphereMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + return computeMTD_SphereMesh(mtd, depth, Sphere(pose0.p, sphereGeom.radius), meshGeom, pose1); +} + +static bool GeomMTDCallback_PlaneCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(geom0); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom1); + + Capsule capsule; + getCapsuleSegment(pose1, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_PlaneCapsule(mtd, depth, getPlane(pose0), capsule); +} + +static bool GeomMTDCallback_PlaneBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(geom0); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom1); + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_PlaneBox(mtd, depth, getPlane(pose0), obb); +} + +static bool GeomMTDCallback_PlaneConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + PX_UNUSED(geom0); + + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + return computeMTD_PlaneConvex(mtd, depth, getPlane(pose0), convexGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + + const PxCapsuleGeometry& capsuleGeom0 = static_cast<const PxCapsuleGeometry&>(geom0); + const PxCapsuleGeometry& capsuleGeom1 = static_cast<const PxCapsuleGeometry&>(geom1); + + Capsule capsule0; + getCapsuleSegment(pose0, capsuleGeom0, capsule0); + capsule0.radius = capsuleGeom0.radius; + + Capsule capsule1; + getCapsuleSegment(pose1, capsuleGeom1, capsule1); + capsule1.radius = capsuleGeom1.radius; + + return computeMTD_CapsuleCapsule(mtd, depth, capsule0, capsule1); +} + +static bool GeomMTDCallback_CapsuleBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_CapsuleBox(mtd, depth, capsule, obb); +} + +static bool GeomMTDCallback_CapsuleConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleConvex(mtd, depth, capsule, pose0, convexGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleMesh(mtd, depth, capsule, meshGeom, pose1); +} + +static bool GeomMTDCallback_BoxBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxBoxGeometry& boxGeom0 = static_cast<const PxBoxGeometry&>(geom0); + const PxBoxGeometry& boxGeom1 = static_cast<const PxBoxGeometry&>(geom1); + + Box obb0; + buildFrom(obb0, pose0.p, boxGeom0.halfExtents, pose0.q); + + Box obb1; + buildFrom(obb1, pose1.p, boxGeom1.halfExtents, pose1.q); + + return computeMTD_BoxBox(mtd, depth, obb0, obb1); +} + +static bool GeomMTDCallback_BoxConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxConvex(mtd, depth, obb, convexGeom, pose1); +} + +static bool GeomMTDCallback_BoxMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxMesh(mtd, depth, obb, meshGeom, pose1); +} + +static bool GeomMTDCallback_ConvexConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxConvexMeshGeometry& convexGeom0 = static_cast<const PxConvexMeshGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom1 = static_cast<const PxConvexMeshGeometry&>(geom1); + + return computeMTD_ConvexConvex(mtd, depth, convexGeom0, pose0, convexGeom1, pose1); +} + +static bool GeomMTDCallback_ConvexMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + return computeMTD_ConvexMesh(mtd, depth, convexGeom, pose0, meshGeom, pose1); +} + +static bool GeomMTDCallback_SphereHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + const Sphere sphere(pose0.p, sphereGeom.radius); + + return computeMTD_SphereHeightField(mtd, depth, sphere, meshGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleHeightField(mtd, depth, capsule, meshGeom, pose1); +} + +static bool GeomMTDCallback_BoxHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxHeightField(mtd, depth, obb, meshGeom, pose1); +} + +static bool GeomMTDCallback_ConvexHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + return computeMTD_ConvexHeightField(mtd, depth, convexGeom, pose0, meshGeom, pose1); +} + +Gu::GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT] = +{ + //PxGeometryType::eSPHERE + { + GeomMTDCallback_SphereSphere, //PxGeometryType::eSPHERE + GeomMTDCallback_SpherePlane, //PxGeometryType::ePLANE + GeomMTDCallback_SphereCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_SphereBox, //PxGeometryType::eBOX + GeomMTDCallback_SphereConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_SphereHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + GeomMTDCallback_NotSupported, //PxGeometryType::ePLANE + GeomMTDCallback_PlaneCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_PlaneBox, //PxGeometryType::eBOX + GeomMTDCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + GeomMTDCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_CapsuleBox, //PxGeometryType::eBOX + GeomMTDCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_CapsuleHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + GeomMTDCallback_BoxBox, //PxGeometryType::eBOX + GeomMTDCallback_BoxConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_BoxHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + GeomMTDCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_ConvexHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, +}; diff --git a/PhysX_3.4/Source/GeomUtils/src/GuMTD.h b/PhysX_3.4/Source/GeomUtils/src/GuMTD.h new file mode 100644 index 00000000..826799da --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuMTD.h @@ -0,0 +1,60 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MTD_H +#define GU_MTD_H + +#include "PxGeometry.h" + +namespace physx +{ +namespace Gu +{ + // PT: we use a define to be able to quickly change the signature of all MTD functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[out] mtd computed depenetration dir + // \param[out] depth computed depenetration depth + // \param[in] geom0 first geometry object + // \param[in] pose0 pose of first geometry object + // \param[in] geom1 second geometry object + // \param[in] pose1 pose of second geometry object + // \param[in] cache optional cached data for triggers + #define GU_MTD_FUNC_PARAMS PxVec3& mtd, PxF32& depth, \ + const PxGeometry& geom0, const PxTransform& pose0, \ + const PxGeometry& geom1, const PxTransform& pose1 + + // PT: function pointer for Geom-indexed MTD functions + // See GU_MTD_FUNC_PARAMS for function parameters details. + // \return true if an overlap was found, false otherwise + // \note depenetration vector D is equal to mtd * depth. It should be applied to the 1st object, to get out of the 2nd object. + typedef bool (*GeomMTDFunc) (GU_MTD_FUNC_PARAMS); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuMeshFactory.cpp b/PhysX_3.4/Source/GeomUtils/src/GuMeshFactory.cpp new file mode 100644 index 00000000..82cd556f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuMeshFactory.cpp @@ -0,0 +1,687 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "GuMeshFactory.h" +#include "PxHeightFieldDesc.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" +#include "GuConvexMesh.h" +#include "GuHeightField.h" +#include "GuConvexMeshData.h" +#include "CmUtils.h" +#include "GuMeshData.h" +#include "PsFoundation.h" + +#if PX_SUPPORT_GPU_PHYSX + #define GU_MESH_FACTORY_GPU_NOTIFICATION(notificationMethod, argument) notificationMethod(argument); +#else + #define GU_MESH_FACTORY_GPU_NOTIFICATION(notificationMethod, argument) +#endif + +using namespace physx; +using namespace Gu; + +// PT: TODO: refactor all this with a dedicated container + +GuMeshFactory::GuMeshFactory() +: mTriangleMeshes (PX_DEBUG_EXP("mesh factory triangle mesh hash")) +, mConvexMeshes (PX_DEBUG_EXP("mesh factory convex mesh hash")) +, mHeightFields (PX_DEBUG_EXP("mesh factory height field hash")) +, mFactoryListeners(PX_DEBUG_EXP("FactoryListeners")) + +{ +} + +GuMeshFactory::~GuMeshFactory() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::release() +{ + // Release all objects in case the user didn't do it + + while(mTriangleMeshes.size()) + { + Gu::TriangleMesh* mesh = mTriangleMeshes.getEntries()[0]; + PX_ASSERT(mesh->getRefCount()==1); + GU_MESH_FACTORY_GPU_NOTIFICATION(notifyReleaseTriangleMesh, *mesh); + mesh->release(); + } + + while(mConvexMeshes.size()) + { + Gu::ConvexMesh* mesh = mConvexMeshes.getEntries()[0]; + PX_ASSERT(mesh->getRefCount()==1); + GU_MESH_FACTORY_GPU_NOTIFICATION(notifyReleaseConvexMesh, *mesh); + mesh->release(); + } + + while(mHeightFields.size()) + { + Gu::HeightField* mesh = mHeightFields.getEntries()[0]; + PX_ASSERT(mesh->getRefCount()==1); + GU_MESH_FACTORY_GPU_NOTIFICATION(notifyReleaseHeightField, *mesh); + mesh->release(); + } + + PX_DELETE(this); +} + +namespace +{ + template<typename TDataType> + inline void notifyReleaseFactoryItem( Ps::Array<GuMeshFactoryListener*>& listeners, const TDataType* type, PxType typeID) + { + PxU32 numListeners = listeners.size(); + for ( PxU32 idx = 0; idx < numListeners; ++idx ) + listeners[idx]->onGuMeshFactoryBufferRelease( type, typeID); + } + + template <typename T> void addToHash(Ps::CoalescedHashSet<T*>& hash, T* element, Ps::Mutex* mutex) + { + if(!element) + return; + + if(mutex) + mutex->lock(); + + hash.insert(element); + + if(mutex) + mutex->unlock(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addTriangleMesh(TriangleMesh* np, bool lock) +{ + addToHash(mTriangleMeshes, np, lock ? &mTrackingMutex : NULL); +} + +PxTriangleMesh* GuMeshFactory::createTriangleMesh(TriangleMeshData& data) +{ + TriangleMesh* np; + + if(data.mType==PxMeshMidPhase::eBVH33) + { + PX_NEW_SERIALIZED(np, RTreeTriangleMesh)(*this, data); + } + else if(data.mType==PxMeshMidPhase::eBVH34) + { + PX_NEW_SERIALIZED(np, BV4TriangleMesh)(*this, data); + } + else return NULL; + + if(np) + addTriangleMesh(np); + + return np; +} + +// data injected by cooking lib for runtime cooking +PxTriangleMesh* GuMeshFactory::createTriangleMesh(void* data) +{ + return createTriangleMesh(*reinterpret_cast<TriangleMeshData*>(data)); +} + +static TriangleMeshData* loadMeshData(PxInputStream& stream) +{ + // Import header + PxU32 version; + bool mismatch; + if(!readHeader('M', 'E', 'S', 'H', version, mismatch, stream)) + return NULL; + + PxU32 midphaseID = PxMeshMidPhase::eBVH33; // Default before version 14 + if(version>=14) // this refers to PX_MESH_VERSION + { + midphaseID = readDword(mismatch, stream); + } + + // Check if old (incompatible) mesh format is loaded + if (version <= 9) // this refers to PX_MESH_VERSION + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Loading triangle mesh failed: " + "Deprecated mesh cooking format. Please recook your mesh in a new cooking format."); + PX_ALWAYS_ASSERT_MESSAGE("Obsolete cooked mesh found. Mesh version has been updated, please recook your meshes."); + return NULL; + } + + // Import serialization flags + const PxU32 serialFlags = readDword(mismatch, stream); + + // Import misc values + if (version <= 12) // this refers to PX_MESH_VERSION + { + // convexEdgeThreshold was removed in 3.4.0 + readFloat(mismatch, stream); + } + + TriangleMeshData* data; + if(midphaseID==PxMeshMidPhase::eBVH33) + data = PX_NEW(RTreeTriangleData); + else if(midphaseID==PxMeshMidPhase::eBVH34) + data = PX_NEW(BV4TriangleData); + else return NULL; + + // Import mesh + PxVec3* verts = data->allocateVertices(readDword(mismatch, stream)); + const PxU32 nbTris = readDword(mismatch, stream); + bool force32 = (serialFlags & (IMSF_8BIT_INDICES|IMSF_16BIT_INDICES)) == 0; + + //ML: this will allocate CPU triangle indices and GPU triangle indices if we have GRB data built + void* tris = data->allocateTriangles(nbTris, force32, serialFlags & IMSF_GRB_DATA); + + stream.read(verts, sizeof(PxVec3)*data->mNbVertices); + if(mismatch) + { + for(PxU32 i=0;i<data->mNbVertices;i++) + { + flip(verts[i].x); + flip(verts[i].y); + flip(verts[i].z); + } + } + //TODO: stop support for format conversion on load!! + const PxU32 nbIndices = 3*data->mNbTriangles; + if(serialFlags & IMSF_8BIT_INDICES) + { + PxU8 x; + if(data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast<PxU16*>(tris); + for(PxU32 i=0;i<nbIndices;i++) + { + stream.read(&x, sizeof(PxU8)); + *tris16++ = x; + } + } + else + { + PxU32* tris32 = reinterpret_cast<PxU32*>(tris); + for(PxU32 i=0;i<nbIndices;i++) + { + stream.read(&x, sizeof(PxU8)); + *tris32++ = x; + } + } + } + else if(serialFlags & IMSF_16BIT_INDICES) + { + if(data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast<PxU16*>(tris); + stream.read(tris16, nbIndices*sizeof(PxU16)); + if(mismatch) + { + for(PxU32 i=0;i<nbIndices;i++) + flip(tris16[i]); + } + } + else + { + PxU32* tris32 = reinterpret_cast<PxU32*>(tris); + PxU16 x; + for(PxU32 i=0;i<nbIndices;i++) + { + stream.read(&x, sizeof(PxU16)); + if(mismatch) + flip(x); + + *tris32++ = x; + } + } + + } + else + { + if(data->has16BitIndices()) + { + PxU32 x; + PxU16* tris16 = reinterpret_cast<PxU16*>(tris); + for(PxU32 i=0;i<nbIndices;i++) + { + stream.read(&x, sizeof(PxU32)); + if(mismatch) + flip(x); + *tris16++ = Ps::to16(x); + } + } + else + { + PxU32* tris32 = reinterpret_cast<PxU32*>(tris); + stream.read(tris32, nbIndices*sizeof(PxU32)); + + if(mismatch) + { + for(PxU32 i=0;i<nbIndices;i++) + flip(tris32[i]); + } + } + } + + if(serialFlags & IMSF_MATERIALS) + { + PxU16* materials = data->allocateMaterials(); + stream.read(materials, sizeof(PxU16)*data->mNbTriangles); + if(mismatch) + { + for(PxU32 i=0;i<data->mNbTriangles;i++) + flip(materials[i]); + } + } + if(serialFlags & IMSF_FACE_REMAP) + { + PxU32* remap = data->allocateFaceRemap(); + readIndices(readDword(mismatch, stream), data->mNbTriangles, remap, stream, mismatch); + } + + if(serialFlags & IMSF_ADJACENCIES) + { + PxU32* adj = data->allocateAdjacencies(); + stream.read(adj, sizeof(PxU32)*data->mNbTriangles*3); + if(mismatch) + { + for(PxU32 i=0;i<data->mNbTriangles*3;i++) + flip(adj[i]); + } + } + + // PT: TODO better + if(midphaseID==PxMeshMidPhase::eBVH33) + { + if(!static_cast<RTreeTriangleData*>(data)->mRTree.load(stream, version)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "RTree binary image load error."); + PX_DELETE(data); + return NULL; + } + } + else if(midphaseID==PxMeshMidPhase::eBVH34) + { + BV4TriangleData* bv4data = static_cast<BV4TriangleData*>(data); + if(!bv4data->mBV4Tree.load(stream, version)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV4 binary image load error."); + PX_DELETE(data); + return NULL; + } + + bv4data->mMeshInterface.setNbTriangles(nbTris); + bv4data->mMeshInterface.setNbVertices(data->mNbVertices); + if(data->has16BitIndices()) + bv4data->mMeshInterface.setPointers(NULL, reinterpret_cast<IndTri16*>(tris), verts); + else + bv4data->mMeshInterface.setPointers(reinterpret_cast<IndTri32*>(tris), NULL, verts); + bv4data->mBV4Tree.mMeshInterface = &bv4data->mMeshInterface; + } + else PX_ASSERT(0); + + // Import local bounds + data->mGeomEpsilon = readFloat(mismatch, stream); + data->mAABB.minimum.x = readFloat(mismatch, stream); + data->mAABB.minimum.y = readFloat(mismatch, stream); + data->mAABB.minimum.z = readFloat(mismatch, stream); + data->mAABB.maximum.x = readFloat(mismatch, stream); + data->mAABB.maximum.y = readFloat(mismatch, stream); + data->mAABB.maximum.z = readFloat(mismatch, stream); + + PxU32 nb = readDword(mismatch, stream); + if(nb) + { + PX_ASSERT(nb==data->mNbTriangles); + data->allocateExtraTrigData(); + // No need to convert those bytes + stream.read(data->mExtraTrigData, nb*sizeof(PxU8)); + } + + if(serialFlags & IMSF_GRB_DATA) + { + data->mGRB_meshAdjVerticiesTotal = readDword(mismatch, stream); + + + //read grb triangle indices + PX_ASSERT(data->mGRB_triIndices); + + if (serialFlags & IMSF_8BIT_INDICES) + { + PxU8 x; + if (data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast<PxU16*>(data->mGRB_triIndices); + for (PxU32 i = 0; i<nbIndices; i++) + { + stream.read(&x, sizeof(PxU8)); + *tris16++ = x; + } + } + else + { + PxU32* tris32 = reinterpret_cast<PxU32*>(data->mGRB_triIndices); + for (PxU32 i = 0; i<nbIndices; i++) + { + stream.read(&x, sizeof(PxU8)); + *tris32++ = x; + } + } + } + else if (serialFlags & IMSF_16BIT_INDICES) + { + if (data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast<PxU16*>(data->mGRB_triIndices); + stream.read(tris16, nbIndices*sizeof(PxU16)); + if (mismatch) + { + for (PxU32 i = 0; i<nbIndices; i++) + flip(tris16[i]); + } + } + else + { + PxU32* tris32 = reinterpret_cast<PxU32*>(data->mGRB_triIndices); + PxU16 x; + for (PxU32 i = 0; i<nbIndices; i++) + { + stream.read(&x, sizeof(PxU16)); + if (mismatch) + flip(x); + + *tris32++ = x; + } + } + + } + else + { + if (data->has16BitIndices()) + { + PxU32 x; + PxU16* tris16 = reinterpret_cast<PxU16*>(data->mGRB_triIndices); + for (PxU32 i = 0; i<nbIndices; i++) + { + stream.read(&x, sizeof(PxU32)); + if (mismatch) + flip(x); + *tris16++ = Ps::to16(x); + } + } + else + { + PxU32* tris32 = reinterpret_cast<PxU32*>(data->mGRB_triIndices); + stream.read(tris32, nbIndices*sizeof(PxU32)); + + if (mismatch) + { + for (PxU32 i = 0; i<nbIndices; i++) + flip(tris32[i]); + } + } + } + + + data->mGRB_triAdjacencies = static_cast<void *>(PX_NEW(PxU32)[data->mNbTriangles*4]); + data->mGRB_vertValency = PX_NEW(PxU32)[data->mNbVertices]; + data->mGRB_adjVertStart = PX_NEW(PxU32)[data->mNbVertices]; + data->mGRB_adjVertices = PX_NEW(PxU32)[data->mGRB_meshAdjVerticiesTotal]; + data->mGRB_faceRemap = PX_NEW(PxU32)[data->mNbTriangles]; + + stream.read(data->mGRB_triAdjacencies, sizeof(PxU32)*data->mNbTriangles*4); + stream.read(data->mGRB_vertValency, sizeof(PxU32)*data->mNbVertices); + stream.read(data->mGRB_adjVertStart, sizeof(PxU32)*data->mNbVertices); + stream.read(data->mGRB_adjVertices, sizeof(PxU32)*data->mGRB_meshAdjVerticiesTotal); + stream.read(data->mGRB_faceRemap, sizeof(PxU32)*data->mNbTriangles); + + if(mismatch) + { + for(PxU32 i=0;i<data->mNbTriangles*4;i++) + flip(reinterpret_cast<PxU32 *>(data->mGRB_triIndices)[i]); + + for(PxU32 i=0;i<data->mNbTriangles*4;i++) + flip(reinterpret_cast<PxU32 *>(data->mGRB_triAdjacencies)[i]); + for(PxU32 i=0;i<data->mNbVertices;i++) + { + flip(data->mGRB_vertValency[i]); + flip(data->mGRB_adjVertStart[i]); + } + for(PxU32 i=0;i<data->mGRB_meshAdjVerticiesTotal;i++) + flip(data->mGRB_adjVertices[i]); + + } + + //read BV32 + data->mGRB_BV32Tree = PX_NEW(BV32Tree); + BV32Tree* bv32Tree = static_cast<BV32Tree*>(data->mGRB_BV32Tree); + if (!bv32Tree->load(stream, version)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV32 binary image load error."); + PX_DELETE(data); + return NULL; + } + } + + return data; +} + +PxTriangleMesh* GuMeshFactory::createTriangleMesh(PxInputStream& desc) +{ + TriangleMeshData* data = ::loadMeshData(desc); + if(!data) + return NULL; + PxTriangleMesh* m = createTriangleMesh(*data); + PX_DELETE(data); + return m; +} + +bool GuMeshFactory::removeTriangleMesh(PxTriangleMesh& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + TriangleMesh* gu = static_cast<TriangleMesh*>(&m); + + bool found = mTriangleMeshes.erase(gu); + + if(found) + { + GU_MESH_FACTORY_GPU_NOTIFICATION(notifyReleaseTriangleMesh, m) + } + + return found; +} + +PxU32 GuMeshFactory::getNbTriangleMeshes() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mTriangleMeshes.size(); +} + +PxU32 GuMeshFactory::getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mTriangleMeshes.getEntries(), mTriangleMeshes.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addConvexMesh(ConvexMesh* np, bool lock) +{ + addToHash(mConvexMeshes, np, lock ? &mTrackingMutex : NULL); +} + +// data injected by cooking lib for runtime cooking +PxConvexMesh* GuMeshFactory::createConvexMesh(void* data) +{ + return createConvexMesh(*reinterpret_cast<Gu::ConvexHullData*>(data)); +} + +PxConvexMesh* GuMeshFactory::createConvexMesh(Gu::ConvexHullData& data) +{ + Gu::ConvexMesh *np; + PX_NEW_SERIALIZED(np, Gu::ConvexMesh)(*this, data); + if (np) + addConvexMesh(np); + + return np; +} + +PxConvexMesh* GuMeshFactory::createConvexMesh(PxInputStream& desc) +{ + ConvexMesh* np; + PX_NEW_SERIALIZED(np, ConvexMesh); + if(!np) + return NULL; + + np->setMeshFactory(this); + + if(!np->load(desc)) + { + np->decRefCount(); + return NULL; + } + + addConvexMesh(np); + return np; +} + +bool GuMeshFactory::removeConvexMesh(PxConvexMesh& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + ConvexMesh* gu = static_cast<ConvexMesh*>(&m); + + bool found = mConvexMeshes.erase(gu); + if(found) + { + GU_MESH_FACTORY_GPU_NOTIFICATION(notifyReleaseConvexMesh, m) + } + + return found; +} + +PxU32 GuMeshFactory::getNbConvexMeshes() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mConvexMeshes.size(); +} + +PxU32 GuMeshFactory::getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mConvexMeshes.getEntries(), mConvexMeshes.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addHeightField(HeightField* np, bool lock) +{ + addToHash(mHeightFields, np, lock ? &mTrackingMutex : NULL); +} + +PxHeightField* GuMeshFactory::createHeightField(void* heightFieldMeshData) +{ + HeightField* np; + PX_NEW_SERIALIZED(np, HeightField)(*this, *reinterpret_cast<Gu::HeightFieldData*>(heightFieldMeshData)); + if(np) + addHeightField(np); + + return np; +} + +PxHeightField* GuMeshFactory::createHeightField(PxInputStream& stream) +{ + HeightField* np; + PX_NEW_SERIALIZED(np, HeightField)(this); + if(!np) + return NULL; + + if(!np->load(stream)) + { + np->decRefCount(); + return NULL; + } + + addHeightField(np); + return np; +} + +bool GuMeshFactory::removeHeightField(PxHeightField& hf) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + HeightField* gu = static_cast<HeightField*>(&hf); + + bool found = mHeightFields.erase(gu); + if(found) + { + GU_MESH_FACTORY_GPU_NOTIFICATION(notifyReleaseHeightField, hf) + } + + return found; +} + +PxU32 GuMeshFactory::getNbHeightFields() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mHeightFields.size(); +} + +PxU32 GuMeshFactory::getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mHeightFields.getEntries(), mHeightFields.size()); +} + +void GuMeshFactory::addFactoryListener( GuMeshFactoryListener& listener ) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mFactoryListeners.pushBack( &listener ); +} + +void GuMeshFactory::removeFactoryListener( GuMeshFactoryListener& listener ) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + for ( PxU32 idx = 0; idx < mFactoryListeners.size(); ++idx ) + { + if ( mFactoryListeners[idx] == &listener ) + { + mFactoryListeners.replaceWithLast( idx ); + --idx; + } + } +} + +void GuMeshFactory::notifyFactoryListener(const PxBase* base, PxType typeID) +{ + notifyReleaseFactoryItem(mFactoryListeners, base, typeID); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/PhysX_3.4/Source/GeomUtils/src/GuMeshFactory.h b/PhysX_3.4/Source/GeomUtils/src/GuMeshFactory.h new file mode 100644 index 00000000..4fba8b7c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuMeshFactory.h @@ -0,0 +1,135 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MESH_FACTORY_H +#define GU_MESH_FACTORY_H + +#include "foundation/PxIO.h" +#include "PxTriangleMesh.h" +#include "PxConvexMesh.h" +#include "PxHeightField.h" + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PsMutex.h" +#include "PsArray.h" + +#include "PsUserAllocated.h" +#include "PsHashSet.h" + +namespace physx +{ + +class PxHeightFieldDesc; + +namespace Gu +{ + class ConvexMesh; + class HeightField; + class TriangleMesh; + class TriangleMeshData; + struct ConvexHullData; +} + +class GuMeshFactoryListener +{ +protected: + virtual ~GuMeshFactoryListener(){} +public: + virtual void onGuMeshFactoryBufferRelease(const PxBase* object, PxType type) = 0; +}; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif +class PX_PHYSX_COMMON_API GuMeshFactory : public Ps::UserAllocated +{ + PX_NOCOPY(GuMeshFactory) +public: + GuMeshFactory(); +protected: + virtual ~GuMeshFactory(); + +public: + void release(); + + // Triangle mehes + void addTriangleMesh(Gu::TriangleMesh* np, bool lock=true); + PxTriangleMesh* createTriangleMesh(PxInputStream& stream); + PxTriangleMesh* createTriangleMesh(void* triangleMeshData); + bool removeTriangleMesh(PxTriangleMesh&); + PxU32 getNbTriangleMeshes() const; + PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Convexes + void addConvexMesh(Gu::ConvexMesh* np, bool lock=true); + PxConvexMesh* createConvexMesh(PxInputStream&); + PxConvexMesh* createConvexMesh(void* convexMeshData); + bool removeConvexMesh(PxConvexMesh&); + PxU32 getNbConvexMeshes() const; + PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Heightfields + void addHeightField(Gu::HeightField* np, bool lock=true); + PxHeightField* createHeightField(void* heightFieldMeshData); + PxHeightField* createHeightField(PxInputStream&); + bool removeHeightField(PxHeightField&); + PxU32 getNbHeightFields() const; + PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + void addFactoryListener( GuMeshFactoryListener& listener ); + void removeFactoryListener( GuMeshFactoryListener& listener ); + void notifyFactoryListener(const PxBase*, PxType typeID); + +protected: + + PxTriangleMesh* createTriangleMesh(Gu::TriangleMeshData& data); + PxConvexMesh* createConvexMesh(Gu::ConvexHullData& data); + +#if PX_SUPPORT_GPU_PHYSX + virtual void notifyReleaseTriangleMesh(const PxTriangleMesh&) {} + virtual void notifyReleaseHeightField(const PxHeightField&) {} + virtual void notifyReleaseConvexMesh(const PxConvexMesh&) {} +#endif + + mutable Ps::Mutex mTrackingMutex; +private: + Ps::CoalescedHashSet<Gu::TriangleMesh*> mTriangleMeshes; + Ps::CoalescedHashSet<Gu::ConvexMesh*> mConvexMeshes; + Ps::CoalescedHashSet<Gu::HeightField*> mHeightFields; + + Ps::Array<GuMeshFactoryListener*> mFactoryListeners; +}; +#if PX_VC + #pragma warning(pop) +#endif +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuMetaData.cpp b/PhysX_3.4/Source/GeomUtils/src/GuMetaData.cpp new file mode 100644 index 00000000..c08f3c29 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuMetaData.cpp @@ -0,0 +1,598 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxIO.h" +#include "GuHeightField.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData2.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" +#include "GuGeometryUnion.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" + +using namespace physx; +using namespace Ps; +using namespace Cm; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_Valency(PxOutputStream& stream) +{ +// 4 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Valency) + PX_DEF_BIN_METADATA_ITEM(stream, Valency, PxU16, mCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Valency, PxU16, mOffset, 0) +} + +static void getBinaryMetaData_BigConvexRawData(PxOutputStream& stream) +{ +// 24 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BigConvexRawData) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU16, mSubdiv, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU16, mNbSamples, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU8, mSamples, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU32, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU32, mNbAdjVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, Valency, mValencies, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU8, mAdjacentVerts, PxMetaDataFlag::ePTR) +} + +void BigConvexData::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_Valency(stream); + getBinaryMetaData_BigConvexRawData(stream); + +// 28 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BigConvexData) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexData, BigConvexRawData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexData, void, mVBuffer, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mData.mSamples + // PT: can't use one array of PxU16 since we don't want to flip those bytes during conversion. + // PT: We only align the first array for DE1340, but the second one shouldn't be aligned since + // both are written as one unique block of memory. + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbSamples, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbSamples, 0, 0) + + // mData.mValencies + // PT: same here, we must only align the first array + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, Valency, mData.mNbVerts, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, BigConvexData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbAdjVerts, 0, 0) +} + +static void getBinaryMetaData_InternalObjectsData(PxOutputStream& stream) +{ +// 16 bytes + PX_DEF_BIN_METADATA_CLASS(stream, InternalObjectsData) + PX_DEF_BIN_METADATA_ITEM(stream, InternalObjectsData, PxReal, mRadius, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, InternalObjectsData, PxReal, mExtents, 0) +} + +static void getBinaryMetaData_HullPolygonData(PxOutputStream& stream) +{ +// 20 bytes + PX_DEF_BIN_METADATA_CLASS(stream, HullPolygonData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, HullPolygonData, PxReal, mPlane, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU16, mVRef8, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU8, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU8, mMinIndex, 0) +} + +static void getBinaryMetaData_ConvexHullData(PxOutputStream& stream) +{ +// 64 bytes + PX_DEF_BIN_METADATA_CLASS(stream, ConvexHullData) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxVec3, mCenterOfMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, HullPolygonData, mPolygons, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, BigConvexRawData, mBigConvexRawData, PxMetaDataFlag::ePTR) + //ML: the most significant bit of mNbEdges is used to indicate whether we have grb data or not. However, we don't support grb data + //in serialization so we have to mask the most significant bit and force the contact gen run on CPU code path + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU16, mNbEdges, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU8, mNbHullVertices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU8, mNbPolygons, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, InternalObjectsData, mInternal, 0) +} + +void Gu::ConvexMesh::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_InternalObjectsData(stream); + getBinaryMetaData_HullPolygonData(stream); + getBinaryMetaData_ConvexHullData(stream); + BigConvexData::getBinaryMetaData(stream); + +// 136 bytes + PX_DEF_BIN_METADATA_VCLASS(stream,ConvexMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream,ConvexMesh, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream,ConvexMesh, RefCountable) + + // + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, ConvexHullData, mHullData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxU32, mNb, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, BigConvexData, mBigConvexData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxReal, mMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxMat33, mInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mHullData.mPolygons (Gu::HullPolygonData, PxVec3, PxU8*2, PxU8) + // PT: we only align the first array since the other ones are contained within it + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, HullPolygonData, mHullData.mNbPolygons, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxVec3, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbEdges, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbEdges, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mNb, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, ConvexMesh, 4) + // mBigConvexData + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, Gu::ConvexMesh, BigConvexData, mBigConvexData, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxHeightFieldSample(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxHeightFieldSample) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxI16, height, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxBitAndByte, materialIndex0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxBitAndByte, materialIndex1, 0) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBitAndByte, PxU8) +} + +static void getBinaryMetaData_HeightFieldData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxHeightFieldFlags, PxU16) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxHeightFieldFormat::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, HeightFieldData) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU32, rows, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU32, columns, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, rowLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, colLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, nbColumns, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldSample, samples, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, thickness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, convexEdgeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldFlags, flags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU16, paddAfterFlags, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldFormat::Enum, format, 0) +} + +void Gu::HeightField::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxHeightFieldSample(stream); + getBinaryMetaData_HeightFieldData(stream); + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxMaterialTableIndex, PxU16) + + PX_DEF_BIN_METADATA_VCLASS(stream, HeightField) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, HeightField, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, HeightField, RefCountable) + + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, HeightFieldData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mSampleStride, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mNbSamples, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxReal, mMinHeight, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxReal, mMaxHeight, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mModifyCount, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mData.samples + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, HeightField, PxHeightFieldSample, mNbSamples, PX_SERIAL_ALIGN, 0) // PT: ### try to remove mNbSamples later +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_RTreePage(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, RTreePage) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, minx, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, miny, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, minz, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxx, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxy, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxz, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxU32, ptrs, 0, RTREE_N) +} + +void RTree::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_RTreePage(stream); + +// 96 bytes + PX_DEF_BIN_METADATA_CLASS(stream, RTree) + + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mBoundsMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mBoundsMax, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mInvDiagonal, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mDiagonalScaler, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mPageSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mNumRootPages, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mNumLevels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mTotalNodes, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mTotalPages, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, RTreePage, mPages, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mPages + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream,RTree, RTreePage, mTotalPages, 128, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void SourceMesh::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, SourceMesh) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxVec3, mVerts, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mNbTris, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, void, mTriangles32, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, void, mTriangles16, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mRemap, PxMetaDataFlag::ePTR) +} + +static void getBinaryMetaData_BVDataPacked(PxOutputStream& stream) +{ +#ifdef GU_BV4_QUANTIZED_TREE + PX_DEF_BIN_METADATA_CLASS(stream, QuantizedAABB) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[0].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[0].mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[1].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[1].mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[2].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[2].mCenter, 0) +#else + PX_DEF_BIN_METADATA_CLASS(stream, CenterExtents) + PX_DEF_BIN_METADATA_ITEM(stream, CenterExtents, PxVec3, mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, CenterExtents, PxVec3, mExtents, 0) +#endif + + PX_DEF_BIN_METADATA_CLASS(stream, BVDataPacked) +#ifdef GU_BV4_QUANTIZED_TREE + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPacked, QuantizedAABB, mAABB, 0) +#else + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPacked, CenterExtents, mAABB, 0) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPacked, PxU32, mData, 0) +} + +void BV4Tree::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_BVDataPacked(stream); + + PX_DEF_BIN_METADATA_CLASS(stream, LocalBounds) + PX_DEF_BIN_METADATA_ITEM(stream, LocalBounds, PxVec3, mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, LocalBounds, float, mExtentsMagnitude, 0) + +// 96 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BV4Tree) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, void, mMeshInterface, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, LocalBounds, mLocalBounds, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxU32, mNbNodes, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, BVDataPacked, mNodes, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxU32, mInitData, 0) + +#ifdef GU_BV4_QUANTIZED_TREE + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxVec3, mCenterOrMinCoeff, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxVec3, mExtentsOrMaxCoeff, 0) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, bool, mUserAllocated, 0) + PX_DEF_BIN_METADATA_ITEMS(stream, BV4Tree, bool, mPadding, PxMetaDataFlag::ePADDING, 3) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BV4Tree, BVDataPacked, mNbNodes, 16, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Gu::TriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ +// 320 => 304 => 272 => 256 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, TriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, TriangleMesh, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, TriangleMesh, RefCountable) + +// 224 => 208 => 192 bytes + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mNbVertices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mNbTriangles, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxVec3, mVertices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mTriangles, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU8, mExtraTrigData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxReal, mGeomEpsilon, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU8, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU16, mMaterialIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mFaceRemap, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mAdjacencies, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_triIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_triAdjacencies, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_vertValency, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_adjVertStart, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_adjVertices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_meshAdjVerticiesTotal, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_faceRemap, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_BV32Tree, PxMetaDataFlag::ePTR) + + + //------ Extra-data ------ + + // mVertices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxVec3, mVertices, mNbVertices, 0, PX_SERIAL_ALIGN) + + // mTriangles + // PT: quite tricky here: we exported either an array of PxU16s or an array of PxU32s. We trick the converter by + // pretending we exported both, with the same control variable (m16BitIndices) but opposed control flags. Also there's + // no way to capture "mNumTriangles*3" using the macros, so we just pretend we exported 3 buffers instead of 1. + // But since in reality it's all the same buffer, only the first one is declared as aligned. + + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, 0) + + // mExtraTrigData + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU8, mExtraTrigData, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mMaterialIndices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU16, mMaterialIndices, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mFaceRemap + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mFaceRemap, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mAdjacencies + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, TriangleMesh, PxU32, mPaddingFromInternalMesh, PxMetaDataFlag::ePADDING) +#endif +} + +void Gu::RTreeTriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ + RTree::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, RTreeTriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RTreeTriangleMesh, TriangleMesh) + + PX_DEF_BIN_METADATA_ITEM(stream, RTreeTriangleMesh, RTree, mRTree, 0) +} + +void Gu::BV4TriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ + SourceMesh::getBinaryMetaData(stream); + BV4Tree::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, BV4TriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, BV4TriangleMesh, TriangleMesh) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4TriangleMesh, SourceMesh, mMeshInterface, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4TriangleMesh, BV4Tree, mBV4Tree, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void MaterialIndicesStruct::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, MaterialIndicesStruct) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, indices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, numIndices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, pad, PxMetaDataFlag::ePADDING) +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU32, pad64, PxMetaDataFlag::ePADDING) +#endif + + //------ Extra-data ------ + // indices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, MaterialIndicesStruct, PxU16, indices, numIndices, 0, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Gu::GeometryUnion::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxGeometryType::Enum, PxU32) + + // The various PxGeometry classes are all public, so I can't really put the meta-data function in there. And then + // I can't access their protected members. So we use the same trick as for the ShapeContainer + class ShadowConvexMeshGeometry : public PxConvexMeshGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_TYPEDEF(stream_, PxConvexMeshGeometryFlags, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowConvexMeshGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxMeshScale, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxConvexMesh, convexMesh, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxConvexMeshGeometryFlags, meshFlags, 0) + PX_DEF_BIN_METADATA_ITEMS(stream_, ShadowConvexMeshGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING, 3) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, ConvexHullData, hullData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, bool, gpuCompatible, 0) + } + }; + ShadowConvexMeshGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxConvexMeshGeometryLL, ShadowConvexMeshGeometry) + + ///////////////// + + class ShadowTriangleMeshGeometry : public PxTriangleMeshGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_TYPEDEF(stream_, PxMeshGeometryFlags, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowTriangleMeshGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxMeshScale, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxMeshGeometryFlags, meshFlags, 0) + PX_DEF_BIN_METADATA_ITEMS(stream_, ShadowTriangleMeshGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING, 3) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxTriangleMesh, triangleMesh, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, TriangleMesh, meshData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxU16, materialIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, MaterialIndicesStruct, materials, 0) + } + }; + ShadowTriangleMeshGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxTriangleMeshGeometryLL, ShadowTriangleMeshGeometry) + + ///////////////// + + class ShadowHeightFieldGeometry : public PxHeightFieldGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowHeightFieldGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxHeightField, heightField, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, heightScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, rowScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, columnScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxMeshGeometryFlags, heightFieldFlags, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, ShadowHeightFieldGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, HeightField, heightFieldData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, MaterialIndicesStruct, materials, 0) + } + }; + ShadowHeightFieldGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxHeightFieldGeometryLL, ShadowHeightFieldGeometry) + + ///////////////// + + class ShadowPlaneGeometry : public PxPlaneGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowPlaneGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowPlaneGeometry, PxGeometryType::Enum, mType, 0) + } + }; + ShadowPlaneGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxPlaneGeometry, ShadowPlaneGeometry) + + ///////////////// + + class ShadowSphereGeometry : public PxSphereGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowSphereGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowSphereGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowSphereGeometry, PxReal, radius, 0) + } + }; + ShadowSphereGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSphereGeometry, ShadowSphereGeometry) + + ///////////////// + + class ShadowCapsuleGeometry : public PxCapsuleGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowCapsuleGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxReal, radius, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxReal, halfHeight, 0) + } + }; + ShadowCapsuleGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxCapsuleGeometry, ShadowCapsuleGeometry) + + ///////////////// + + class ShadowBoxGeometry : public PxBoxGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowBoxGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowBoxGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowBoxGeometry, PxVec3, halfExtents,0) + } + }; + ShadowBoxGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBoxGeometry, ShadowBoxGeometry) + + /* + - geom union offset & size + - control type offset & size + - type-to-class mapping + */ + +// 44 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Gu::GeometryUnion) + + PX_DEF_BIN_METADATA_UNION(stream, Gu::GeometryUnion, mGeometry) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxSphereGeometry, PxGeometryType::eSPHERE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxPlaneGeometry, PxGeometryType::ePLANE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxCapsuleGeometry, PxGeometryType::eCAPSULE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxBoxGeometry, PxGeometryType::eBOX) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxConvexMeshGeometryLL, PxGeometryType::eCONVEXMESH) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxTriangleMeshGeometryLL,PxGeometryType::eTRIANGLEMESH) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxHeightFieldGeometryLL, PxGeometryType::eHEIGHTFIELD) +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuOverlapTests.cpp b/PhysX_3.4/Source/GeomUtils/src/GuOverlapTests.cpp new file mode 100644 index 00000000..ebeca456 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuOverlapTests.cpp @@ -0,0 +1,695 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuOverlapTests.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionSphereBox.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuInternal.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecBox.h" +#include "GuConvexMesh.h" +#include "GuHillClimbing.h" +#include "GuGJK.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; + +// PT: TODO: why don't we use ShapeData for overlaps? + +//returns the maximal vertex in shape space +// PT: this function should be removed. We already have 2 different project hull functions in PxcShapeConvex & GuGJKObjectSupport, this one looks like a weird mix of both! +static PxVec3 projectHull_( const ConvexHullData& hull, + float& minimum, float& maximum, + const PxVec3& localDir, // expected to be normalized + const PxMat33& vert2ShapeSkew) +{ + PX_ASSERT(localDir.isNormalized()); + + //use property that x|My == Mx|y for symmetric M to avoid having to transform vertices. + const PxVec3 vertexSpaceDir = vert2ShapeSkew * localDir; + + const PxVec3* Verts = hull.getHullVertices(); + const PxVec3* bestVert = NULL; + + if(!hull.mBigConvexRawData) // Brute-force, local space. Experiments show break-even point is around 32 verts. + { + PxU32 NbVerts = hull.mNbHullVertices; + float min_ = PX_MAX_F32; + float max_ = -PX_MAX_F32; + while(NbVerts--) + { + const float dp = (*Verts).dot(vertexSpaceDir); + min_ = physx::intrinsics::selectMin(min_, dp); + if(dp > max_) { max_ = dp; bestVert = Verts; } + + Verts++; + } + minimum = min_; + maximum = max_; + + PX_ASSERT(bestVert != NULL); + + return vert2ShapeSkew * *bestVert; + } + else //*/if(1) // This version is better for objects with a lot of vertices + { + const PxU32 Offset = ComputeCubemapNearestOffset(vertexSpaceDir, hull.mBigConvexRawData->mSubdiv); + PxU32 MinID = hull.mBigConvexRawData->mSamples[Offset]; + PxU32 MaxID = hull.mBigConvexRawData->getSamples2()[Offset]; + + localSearch(MinID, -vertexSpaceDir, Verts, hull.mBigConvexRawData); + localSearch(MaxID, vertexSpaceDir, Verts, hull.mBigConvexRawData); + + minimum = (Verts[MinID].dot(vertexSpaceDir)); + maximum = (Verts[MaxID].dot(vertexSpaceDir)); + + PX_ASSERT(maximum >= minimum); + + return vert2ShapeSkew * Verts[MaxID]; + } +} + +static bool intersectSphereConvex(const PxTransform& sphereTransform, float radius, const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hullData = &mesh.getHull(); + const FloatV sphereRadius = FLoad(radius); + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + + const PsMatTransformV aToB(convexGlobalPose.transformInv(sphereTransform)); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); + CapsuleV capsule(aToB.p, sphereRadius); + + Vec3V contactA, contactB, normal; + FloatV dist; + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<ConvexHullV> convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + + GjkStatus status = gjk(convexA, convexB, initialSearchDir, FZero(), contactA, contactB, normal, dist); + + return status == GJK_CONTACT; +} + +static bool intersectCapsuleConvex( const PxCapsuleGeometry& capsGeom, const PxTransform& capsGlobalPose, + const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hull = &mesh.getHull(); + + const FloatV capsuleHalfHeight = FLoad(capsGeom.halfHeight); + const FloatV capsuleRadius = FLoad(capsGeom.radius); + + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + + const PsMatTransformV aToB(convexGlobalPose.transformInv(capsGlobalPose)); + + ConvexHullV convexHull(hull, zeroV, vScale, vQuat, meshScale.isIdentity()); + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + Vec3V contactA, contactB, normal; + FloatV dist; + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<ConvexHullV> convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + + GjkStatus status = gjk(convexA, convexB, initialSearchDir, FZero(), contactA, contactB, normal, dist); + + return status == GJK_CONTACT; +} + +static bool intersectBoxConvex(const PxBoxGeometry& boxGeom, const PxTransform& boxGlobalPose, + const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + // AP: see archived non-GJK version in //sw/physx/dev/pterdiman/graveyard/contactConvexBox.cpp + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hull = &mesh.getHull(); + + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + const Vec3V boxExtents = V3LoadU(boxGeom.halfExtents); + const PsMatTransformV aToB(convexGlobalPose.transformInv(boxGlobalPose)); + + ConvexHullV convexHull(hull, zeroV, vScale, vQuat, meshScale.isIdentity()); + BoxV box(zeroV, boxExtents); + + Vec3V contactA, contactB, normal; + FloatV dist; + RelativeConvex<BoxV> convexA(box, aToB); + LocalConvex<ConvexHullV> convexB(convexHull); + + GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + + //PX_PRINTF("BOX status = %i, overlap = %i, PxVec3(%f, %f, %f)\n", status, overlap, boxGlobalPose.p.x, boxGlobalPose.p.y, boxGlobalPose.p.z); + + return status == GJK_CONTACT; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxVec3* getCachedAxis(TriggerCache* cache) +{ + if(cache && cache->state==TRIGGER_OVERLAP) + return &cache->dir; + else + return NULL; +} + +static PX_FORCE_INLINE bool updateTriggerCache(bool overlap, TriggerCache* cache) +{ + if(cache) + { + if(overlap) + cache->state = TRIGGER_OVERLAP; + else + cache->state = TRIGGER_DISJOINT; + } + return overlap; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Sphere-vs-shape + +static bool GeomOverlapCallback_SphereSphere(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eSPHERE); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom0 = static_cast<const PxSphereGeometry&>(geom0); + const PxSphereGeometry& sphereGeom1 = static_cast<const PxSphereGeometry&>(geom1); + + const PxVec3 delta = pose1.p - pose0.p; + const PxReal r = sphereGeom0.radius + sphereGeom1.radius; + return delta.magnitudeSquared() <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SpherePlane(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); + PX_UNUSED(cache); + PX_UNUSED(geom1); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + + return getPlane(pose1).distance(pose0.p) <= sphereGeom.radius; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SphereCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom1); + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(pose1, capsuleGeom); + const PxReal r = sphereGeom.radius + capsuleGeom.radius; + + return distancePointSegmentSquared(capsuleHalfHeightVector, -capsuleHalfHeightVector, pose0.p - pose1.p) <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SphereBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom1); + + // PT: TODO: remove this useless conversion + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return intersectSphereBox(Sphere(pose0.p, sphereGeom.radius), obb); +} + +static bool GeomOverlapCallback_SphereConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0,0,1.f); + + const bool overlap = intersectSphereConvex(pose0, sphereGeom.radius, + *cm, + convexGeom.scale, pose1, + &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Plane-vs-shape + +static bool GeomOverlapCallback_PlaneCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast<const PxPlaneGeometry&>(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom1); + + // PT: TODO: remove this useless conversion + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose1); + + const PxPlane plane = getPlane(pose0); + + // We handle the capsule-plane collision with 2 sphere-plane collisions. + // Seems ok so far, since plane is infinite. + + if(plane.distance(capsule.p0) <= capsule.radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + + if(plane.distance(capsule.p1) <= capsule.radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + + return false; +} + +/*static bool intersectPlaneBox(const PxPlane& plane, const Box& box) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + for(PxU32 i=0;i<8;i++) + { + if(plane.distance(pts[i]) <= 0.0f) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + } + return false; +}*/ + +static bool GeomOverlapCallback_PlaneBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast<const PxPlaneGeometry&>(geom0); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom1); + + // I currently use the same code as for contact generation but maybe we could do something faster (in theory testing + // only 2 pts is enough). + + const Matrix34 absPose(pose1); + const PxPlane worldPlane = getPlane(pose0); + + for(int vx=-1; vx<=1; vx+=2) + for(int vy=-1; vy<=1; vy+=2) + for(int vz=-1; vz<=1; vz+=2) + { + const PxVec3 v = absPose.transform(PxVec3(PxReal(vx),PxReal(vy),PxReal(vz)).multiply(boxGeom.halfExtents)); + + if(worldPlane.distance(v) <= 0.0f) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + } + return false; +} + +static bool GeomOverlapCallback_PlaneConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast<const PxPlaneGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + //find plane normal in shape space of convex: + const PxTransform plane2convex = pose1.getInverse().transform(pose0); + + const PxPlane shapeSpacePlane = getPlane(plane2convex); + + PxReal minimum, maximum; + projectHull_(cm->getHull(), minimum, maximum, shapeSpacePlane.n, convexGeom.scale.toMat33()); + + return (minimum <= -shapeSpacePlane.d); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Capsule-vs-shape + +static bool GeomOverlapCallback_CapsuleCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom0 = static_cast<const PxCapsuleGeometry&>(geom0); + const PxCapsuleGeometry& capsuleGeom1 = static_cast<const PxCapsuleGeometry&>(geom1); + + // PT: move computation to local space for improved accuracy + const PxVec3 delta = pose1.p - pose0.p; + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector0 = getCapsuleHalfHeightVector(pose0, capsuleGeom0); + const PxVec3 capsuleHalfHeightVector1 = getCapsuleHalfHeightVector(pose1, capsuleGeom1); + + const PxReal squareDist = distanceSegmentSegmentSquared(-capsuleHalfHeightVector0, capsuleHalfHeightVector0*2.0f, + delta-capsuleHalfHeightVector1, capsuleHalfHeightVector1*2.0f); + const PxReal r = capsuleGeom0.radius + capsuleGeom1.radius; + return squareDist <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_CapsuleBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom1); + + // PT: move computation to local space for improved accuracy + const PxVec3 delta = pose1.p - pose0.p; + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(pose0, capsuleGeom); + + // PT: TODO: remove this useless conversion + const PxMat33 obbRot(pose1.q); + + // PT: objects are defined as closed, so we return 'true' in case of equality + return distanceSegmentBoxSquared(capsuleHalfHeightVector, -capsuleHalfHeightVector, delta, boxGeom.halfExtents, obbRot) <= capsuleGeom.radius*capsuleGeom.radius; +} + +static bool GeomOverlapCallback_CapsuleConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0,0,1.0f); + + const bool overlap = intersectCapsuleConvex(capsuleGeom, pose0, *cm, convexGeom.scale, pose1, &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Box-vs-shape + +static bool GeomOverlapCallback_BoxBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom0 = static_cast<const PxBoxGeometry&>(geom0); + const PxBoxGeometry& boxGeom1 = static_cast<const PxBoxGeometry&>(geom1); + + // PT: TODO: remove this useless conversion + return intersectOBBOBB( boxGeom0.halfExtents, pose0.p, PxMat33Padded(pose0.q), + boxGeom1.halfExtents, pose1.p, PxMat33Padded(pose1.q), true); +} + +static bool GeomOverlapCallback_BoxConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom1); + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0.0f, 0.0f, 1.0f); + + const bool overlap = intersectBoxConvex(boxGeom, pose0, *cm, convexGeom.scale, pose1, &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Convex-vs-shape +static bool GeomOverlapCallback_ConvexConvex(GU_OVERLAP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const Vec3V zeroV = V3Zero(); + const PxConvexMeshGeometry& convexGeom0 = static_cast<const PxConvexMeshGeometry&>(geom0); + const PxConvexMeshGeometry& convexGeom1 = static_cast<const PxConvexMeshGeometry&>(geom1); + const ConvexMesh* cm0 = static_cast<ConvexMesh*>(convexGeom0.convexMesh); + const ConvexMesh* cm1 = static_cast<ConvexMesh*>(convexGeom1.convexMesh); + + bool overlap; + { + const ConvexHullData* hullData0 = &cm0->getHull(); + const ConvexHullData* hullData1 = &cm1->getHull(); + + const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); + const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); + + const QuatV q0 = QuatVLoadU(&pose0.q.x); + const Vec3V p0 = V3LoadU(&pose0.p.x); + + const QuatV q1 = QuatVLoadU(&pose1.q.x); + const Vec3V p1 = V3LoadU(&pose1.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + const PsMatTransformV aToB(transf1.transformInv(transf0)); + + ConvexHullV convexHull0(hullData0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); + ConvexHullV convexHull1(hullData1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); + + Vec3V contactA, contactB, normal; + FloatV dist; + RelativeConvex<ConvexHullV> convexA(convexHull0, aToB); + LocalConvex<ConvexHullV> convexB(convexHull1); + + GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + overlap = (status == GJK_CONTACT); + } + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool GeomOverlapCallback_NotSupported(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ALWAYS_ASSERT_MESSAGE("NOT SUPPORTED"); + PX_UNUSED(cache); + PX_UNUSED(pose0); + PX_UNUSED(pose1); + PX_UNUSED(geom0); + PX_UNUSED(geom1); + return false; +} + +static bool GeomOverlapCallback_HeightfieldUnregistered(GU_OVERLAP_FUNC_PARAMS) +{ + PX_UNUSED(cache); + PX_UNUSED(geom0); + PX_UNUSED(geom1); + PX_UNUSED(pose0); + PX_UNUSED(pose1); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Overlap test called with height fields unregistered "); + return false; +} + +bool GeomOverlapCallback_SphereMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_CapsuleMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_BoxMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_ConvexMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_SphereHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_CapsuleHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_BoxHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_ConvexHeightfield (GU_OVERLAP_FUNC_PARAMS); + +GeomOverlapTable gGeomOverlapMethodTable[] = +{ + //PxGeometryType::eSPHERE + { + GeomOverlapCallback_SphereSphere, //PxGeometryType::eSPHERE + GeomOverlapCallback_SpherePlane, //PxGeometryType::ePLANE + GeomOverlapCallback_SphereCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_SphereBox, //PxGeometryType::eBOX + GeomOverlapCallback_SphereConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + GeomOverlapCallback_NotSupported, //PxGeometryType::ePLANE + GeomOverlapCallback_PlaneCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_PlaneBox, //PxGeometryType::eBOX + GeomOverlapCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + GeomOverlapCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_CapsuleBox, //PxGeometryType::eBOX + GeomOverlapCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + GeomOverlapCallback_BoxBox, //PxGeometryType::eBOX + GeomOverlapCallback_BoxConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + GeomOverlapCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, +}; + +const GeomOverlapTable* Gu::getOverlapFuncTable() +{ + return gGeomOverlapMethodTable; +} + +void registerHeightFields_Raycasts(); +void registerHeightFields_Sweeps(); +void Gu::registerHeightFields() +{ + registerHeightFields_Raycasts(); + registerHeightFields_Sweeps(); + + gGeomOverlapMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_SphereHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_CapsuleHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_BoxHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_ConvexHeightfield; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuOverlapTests.h b/PhysX_3.4/Source/GeomUtils/src/GuOverlapTests.h new file mode 100644 index 00000000..76e697c0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuOverlapTests.h @@ -0,0 +1,117 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_OVERLAP_TESTS_H +#define GU_OVERLAP_TESTS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxAssert.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" +#include "PsFoundation.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + class Sphere; + + PX_PHYSX_COMMON_API bool checkOverlapAABB_triangleGeom (const PxGeometry& triGeom, const PxTransform& pose, const PxBounds3& box); + PX_PHYSX_COMMON_API bool checkOverlapAABB_heightFieldGeom (const PxGeometry& hfGeom, const PxTransform& pose, const PxBounds3& box); + + // PT: this is just a shadow of what it used to be. We currently don't use TRIGGER_INSIDE anymore, but I leave it for now, + // since I really want to put this back the way it was before. + enum TriggerStatus + { + TRIGGER_DISJOINT, + TRIGGER_INSIDE, + TRIGGER_OVERLAP + }; + + // PT: currently only used for convex triggers + struct TriggerCache + { + PxVec3 dir; + PxU16 state; + PxU16 gjkState; //gjk succeed or fail + }; + + // PT: we use a define to be able to quickly change the signature of all overlap functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom0 first geometry object + // \param[in] pose0 pose of first geometry object + // \param[in] geom1 second geometry object + // \param[in] pose1 pose of second geometry object + // \param[in] cache optional cached data for triggers + #define GU_OVERLAP_FUNC_PARAMS const PxGeometry& geom0, const PxTransform& pose0, \ + const PxGeometry& geom1, const PxTransform& pose1, \ + Gu::TriggerCache* cache + + // PT: function pointer for Geom-indexed overlap functions + // See GU_OVERLAP_FUNC_PARAMS for function parameters details. + // \return true if an overlap was found, false otherwise + typedef bool (*GeomOverlapFunc) (GU_OVERLAP_FUNC_PARAMS); + + // PT: typedef for a bundle of all overlap functions, i.e. the function table itself (indexed by geom-type). + typedef GeomOverlapFunc GeomOverlapTable[PxGeometryType::eGEOMETRY_COUNT]; + + // PT: retrieves the overlap function table (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomOverlapTable* getOverlapFuncTable(); + + // dynamic registration of height fields + PX_PHYSX_COMMON_API void registerHeightFields(); + + PX_FORCE_INLINE bool overlap( const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1, + const GeomOverlapTable* PX_RESTRICT overlapFuncs) + { + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "Gu::overlap(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "Gu::overlap(): pose1 is not valid.", false); + + if(geom0.getType() > geom1.getType()) + { + GeomOverlapFunc overlapFunc = overlapFuncs[geom1.getType()][geom0.getType()]; + PX_ASSERT(overlapFunc); + return overlapFunc(geom1, pose1, geom0, pose0, NULL); + } + else + { + GeomOverlapFunc overlapFunc = overlapFuncs[geom0.getType()][geom1.getType()]; + PX_ASSERT(overlapFunc); + return overlapFunc(geom0, pose0, geom1, pose1, NULL); + } + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuRaycastTests.cpp b/PhysX_3.4/Source/GeomUtils/src/GuRaycastTests.cpp new file mode 100644 index 00000000..e67ea751 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuRaycastTests.cpp @@ -0,0 +1,562 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuMidphaseInterface.h" +#include "GuInternal.h" +#include "PxSphereGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRayPlane.h" +#include "GuHeightFieldUtil.h" +#include "GuDistancePointSegment.h" +#include "GuConvexMesh.h" +#include "CmScaling.h" + +using namespace physx; +using namespace Gu; + +////////////////////////////////////////////////// raycasts ////////////////////////////////////////////////////////////////// +PxU32 raycast_box(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_ASSERT(maxHits && hits); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + const PxTransform& absPose = pose; + + PxVec3 localOrigin = rayOrigin - absPose.p; + localOrigin = absPose.q.rotateInv(localOrigin); + + const PxVec3 localDir = absPose.q.rotateInv(rayDir); + + PxVec3 localImpact; + PxReal t; + PxU32 rval = rayAABBIntersect2(-boxGeom.halfExtents, boxGeom.halfExtents, localOrigin, localDir, localImpact, t); + if(!rval) + return 0; + + if(t>maxDist) + return 0; + + hits->distance = t; //worldRay.orig.distance(hit.worldImpact); //should be the same, assuming ray dir was normalized!! + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + PxHitFlags outFlags = PxHitFlag::eDISTANCE; + if((hitFlags & PxHitFlag::ePOSITION)) + { + outFlags |= PxHitFlag::ePOSITION; + if(t!=0.0f) + hits->position = absPose.transform(localImpact); + else + hits->position = rayOrigin; + } + + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + + //Because rayAABBIntersect2 set t = 0 if start point inside shape + if(t == 0) + { + hits->normal = -rayDir; + } + else + { + //local space normal is: + rval--; + PxVec3 n(0.0f); + n[rval] = PxReal((localImpact[rval] > 0.0f) ? 1.0f : -1.0f); + hits->normal = absPose.q.rotate(n); + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + return 1; +} + +PxU32 raycast_sphere(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + PX_ASSERT(maxHits && hits); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + if(!intersectRaySphere(rayOrigin, rayDir, maxDist, pose.p, sphereGeom.radius, hits->distance, &hits->position)) + return 0; + + /* // PT: should be useless now + hit.distance = worldRay.orig.distance(hit.worldImpact); + if(hit.distance>maxDist) + return false; + */ + // PT: we can't avoid computing the position here since it's needed to compute the normal anyway + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + // Compute additional information if needed + PxHitFlags outFlags = PxHitFlag::eDISTANCE|PxHitFlag::ePOSITION; + if(hitFlags & PxHitFlag::eNORMAL) + { + // User requested impact normal + //Because intersectRaySphere set distance = 0 if start point inside shape + if(hits->distance == 0.0f) + { + hits->normal = -rayDir; + } + else + { + hits->normal = hits->position - pose.p; + hits->normal.normalize(); + } + outFlags |= PxHitFlag::eNORMAL; + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + + return 1; +} + +PxU32 raycast_capsule(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_ASSERT(maxHits && hits); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + // TODO: PT: could we simplify this ? + Capsule capsule; + getCapsuleSegment(pose, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + PxReal t; + if(!intersectRayCapsule(rayOrigin, rayDir, capsule, t)) + return 0; + + if(t>maxDist) + return 0; + + // PT: we can't avoid computing the position here since it's needed to compute the normal anyway + hits->position = rayOrigin + rayDir*t; // PT: will be rayOrigin for t=0.0f (i.e. what the spec wants) + hits->distance = t; + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + // Compute additional information if needed + PxHitFlags outFlags = PxHitFlag::eDISTANCE|PxHitFlag::ePOSITION; + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + + if(t==0.0f) + { + hits->normal = -rayDir; + } + else + { + PxReal capsuleT; + distancePointSegmentSquared(capsule, hits->position, &capsuleT); + capsule.computePoint(hits->normal, capsuleT); + hits->normal = hits->position - hits->normal; //this should never be zero. It should have a magnitude of the capsule radius. + hits->normal.normalize(); + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + + return 1; +} + +PxU32 raycast_plane(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(hitFlags); + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_ASSERT(maxHits && hits); + PX_UNUSED(geom); +// const PxPlaneGeometry& planeGeom = static_cast<const PxPlaneGeometry&>(geom); + + // Perform backface culling so that we can pick objects beyond planes + const PxPlane plane = getPlane(pose); + if(rayDir.dot(plane.n)>=0.0f) + return false; + + PxReal distanceAlongLine; + if(!intersectRayPlane(rayOrigin, rayDir, plane, distanceAlongLine, &hits->position)) + return 0; + + /* + PxReal test = worldRay.orig.distance(hit.worldImpact); + + PxReal dd; + PxVec3 pp; + PxSegmentPlaneIntersect(worldRay.orig, worldRay.orig+worldRay.dir*1000.0f, plane, dd, pp); + */ + + if(distanceAlongLine<0.0f) + return 0; + + if(distanceAlongLine>maxDist) + return 0; + + hits->distance = distanceAlongLine; + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + hits->flags = PxHitFlag::eDISTANCE|PxHitFlag::ePOSITION|PxHitFlag::eNORMAL; + hits->normal = plane.n; + return 1; +} + +PxU32 raycast_convexMesh(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + PX_ASSERT(maxHits && hits); + PX_ASSERT(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f); + + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + PxRaycastHit& hit = *hits; + + //scaling: transform the ray to vertex space + const Cm::Matrix34 world2vertexSkew = convexGeom.scale.getInverse() * pose.getInverse(); + + //ConvexMesh* cmesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + const PxU32 nPolys = convexMesh->getNbPolygonsFast(); + const HullPolygonData* PX_RESTRICT polysEA = convexMesh->getPolygons(); + const HullPolygonData* polys = polysEA; + + const PxVec3 vrayOrig = world2vertexSkew.transform(rayOrigin); + const PxVec3 vrayDir = world2vertexSkew.rotate(rayDir); + + /* + Purely convex planes based algorithm + Iterate all planes of convex, with following rules: + * determine of ray origin is inside them all or not. + * planes parallel to ray direction are immediate early out if we're on the outside side (plane normal is sep axis) + * else + - for all planes the ray direction "enters" from the front side, track the one furthest along the ray direction (A) + - for all planes the ray direction "exits" from the back side, track the one furthest along the negative ray direction (B) + if the ray origin is outside the convex and if along the ray, A comes before B, the directed line stabs the convex at A + */ + bool originInsideAllPlanes = true; + PxReal latestEntry = -FLT_MAX; + PxReal earliestExit = FLT_MAX; +// PxU32 bestPolygonIndex = 0; + hit.faceIndex = 0xffffffff; + + for(PxU32 i=0;i<nPolys;i++) + { + const HullPolygonData& poly = polys[i]; + const PxPlane& vertSpacePlane = poly.mPlane; + + const PxReal distToPlane = vertSpacePlane.distance(vrayOrig); + const PxReal dn = vertSpacePlane.n.dot(vrayDir); + const PxReal distAlongRay = -distToPlane/dn; // PT: TODO: potential divide by zero here! + + // PT: TODO: this is computed again in the last branch! + if(distToPlane > 0.0f) + originInsideAllPlanes = false; //origin not behind plane == ray starts outside the convex. + + if(dn > 1E-7f) //the ray direction "exits" from the back side + { + earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay); + } + else if(dn < -1E-7f) //the ray direction "enters" from the front side + { + if(distAlongRay > latestEntry) + { + latestEntry = distAlongRay; + hit.faceIndex = i; + } + } + else + { + //plane normal and ray dir are orthogonal + if(distToPlane > 0.0f) + return 0; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex! + } + } + + if(originInsideAllPlanes) //ray starts inside convex + { + hit.distance = 0.0f; + hit.faceIndex = 0xffffffff; + hit.u = 0.0f; + hit.v = 0.0f; + hit.position = rayOrigin; + hit.normal = -rayDir; + hit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + return 1; + } + + // AP: changed to latestEntry < maxDist-1e-5f so that we have a conservatively negative result near end of ray + if(latestEntry < earliestExit && latestEntry > 0.0f && latestEntry < maxDist-1e-5f) + { + PxHitFlags outFlags = PxHitFlag::eDISTANCE | PxHitFlag::eFACE_INDEX; + if(hitFlags & PxHitFlag::ePOSITION) + { + outFlags |= PxHitFlag::ePOSITION; + const PxVec3 pointOnPlane = vrayOrig + latestEntry * vrayDir; + hit.position = pose.transform(convexGeom.scale.toMat33() * pointOnPlane); + } + hit.distance = latestEntry; + hit.u = 0.0f; + hit.v = 0.0f; + hit.normal = PxVec3(0.0f); + + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + //when we have nonuniform scaling we actually have to transform by the transpose of the inverse of vertex2worldSkew.M == transpose of world2vertexSkew: + hit.normal = world2vertexSkew.rotateTranspose(polys[hit.faceIndex].mPlane.n); + hit.normal.normalize(); + } + hit.flags = outFlags; + return 1; + } + return 0; +} + +PxU32 raycast_triangleMesh(GU_RAY_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + PX_ASSERT(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f); + + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + return Midphase::raycastTriangleMesh(meshData, meshGeom, pose, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits); +} + +namespace +{ + struct HFTraceSegmentCallback + { + PX_NOCOPY(HFTraceSegmentCallback) + public: + PxRaycastHit* mHits; + const PxU32 mMaxHits; + PxU32 mNbHits; + const HeightFieldUtil& mUtil; + const PxTransform& mPose; + const PxVec3& mRayDir; + const PxVec3& mLocalRayDir; + const PxVec3& mLocalRayOrig; + const PxHitFlags mHitFlags; + const bool mIsDoubleSided; + + HFTraceSegmentCallback( PxRaycastHit* hits, PxU32 maxHits, const PxHitFlags hitFlags, const HeightFieldUtil& hfUtil, const PxTransform& pose, + const PxVec3& rayDir, const PxVec3& localRayDir, const PxVec3& localRayOrig, + bool isDoubleSided) : + mHits (hits), + mMaxHits (maxHits), + mNbHits (0), + mUtil (hfUtil), + mPose (pose), + mRayDir (rayDir), + mLocalRayDir (localRayDir), + mLocalRayOrig (localRayOrig), + mHitFlags (hitFlags), + mIsDoubleSided (isDoubleSided) + { + PX_ASSERT(maxHits > 0); + } + + PX_FORCE_INLINE bool onEvent(PxU32 , PxU32*) + { + return true; + } + + PX_FORCE_INLINE bool underFaceHit(const HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; // true means continue traversal + } + + PxAgain faceHit(const HeightFieldUtil&, const PxVec3& aHitPoint, PxU32 aTriangleIndex, PxReal u, PxReal v) + { + // traversal is strictly sorted so there's no need to sort hits + if(mNbHits >= mMaxHits) + return false; // false = stop traversal + + PxRaycastHit& hit = mHits[mNbHits++]; + hit.position = aHitPoint; + hit.faceIndex = aTriangleIndex; + hit.u = u; + hit.v = v; + hit.flags = PxHitFlag::eUV | PxHitFlag::eFACE_INDEX; // UVs and face index are always set + + if(mHitFlags & PxHitFlag::eNORMAL) + { + // We need the normal for the dot product. + PxVec3 normal = mPose.q.rotate(mUtil.getNormalAtShapePoint(hit.position.x, hit.position.z)); + normal.normalize(); + if(mIsDoubleSided && normal.dot(mRayDir) > 0.0f) // comply with normal spec for double sided (should always face opposite rayDir) + hit.normal = -normal; + else + hit.normal = normal; + hit.flags |= PxHitFlag::eNORMAL; + } + + if(mHitFlags & PxHitFlag::eDISTANCE) + { + hit.distance = (hit.position - mLocalRayOrig).dot(mLocalRayDir); + hit.flags |= PxHitFlag::eDISTANCE; + } + + if(mHitFlags & PxHitFlag::ePOSITION) + { + hit.position = mPose.transform(hit.position); + hit.flags |= PxHitFlag::ePOSITION; + } + return (mNbHits < mMaxHits); // true = continue traversal, false = stop traversal + } + }; +} + +PxU32 raycast_heightField(GU_RAY_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_ASSERT(maxHits && hits); + PX_UNUSED(maxHits); + + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); + + const PxTransform invAbsPose = pose.getInverse(); + const PxVec3 localRayOrig = invAbsPose.transform(rayOrigin); + const PxVec3 localRayDir = invAbsPose.rotate(rayDir); + + const bool isDoubleSided = hfGeom.heightFieldFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + const HeightFieldUtil hfUtil(hfGeom); + + PxVec3 normRayDir = localRayDir; + normRayDir.normalizeSafe(); // nothing will happen if length is < PX_NORMALIZATION_EPSILON + + // pretest if we intersect HF bounds. If no early exit, if yes move the origin and shorten the maxDist + // to deal with precision issues with large maxDist + PxBounds3 hfLocalBounds; + hfUtil.computeLocalBounds(hfLocalBounds); + + PxVec3 localImpact; + PxReal t; // closest intersection, t==0 hit inside + PxU32 rval = rayAABBIntersect2(hfLocalBounds.minimum, hfLocalBounds.maximum, localRayOrig, localRayDir, localImpact, t); + // early exit we miss the AABB + if (!rval) + return 0; + if (t > maxDist) + return 0; + + // PT: if eMESH_ANY is used then eMESH_MULTIPLE won't be, and we'll stop the query after 1 hit is found. There is no difference + // between 'any hit' and 'closest hit' for HFs since hits are reported in order. + HFTraceSegmentCallback callback(hits, hitFlags.isSet(PxHitFlag::eMESH_MULTIPLE) ? maxHits : 1, hitFlags, hfUtil, pose, + rayDir, localRayDir, localRayOrig, + isDoubleSided); // make sure we return only 1 hit without eMESH_MULTIPLE + + PxReal offset = 0.0f; + PxReal maxDistOffset = maxDist; + PxVec3 localRayOrigOffset = localRayOrig; + + // if we don't start inside the AABB box, offset the start pos, because of precision issues with large maxDist + if(t > 0.0f) + { + offset = t - GU_RAY_SURFACE_OFFSET; + // move the rayOrig to offset start pos + localRayOrigOffset = localRayOrig + normRayDir*offset; + } + + // shorten the maxDist of the offset that was cut off and clip it + // we pick either the original maxDist, if maxDist is huge we clip it + maxDistOffset = PxMin(maxDist - offset, GU_RAY_SURFACE_OFFSET + 2.0f * PxMax(hfLocalBounds.maximum.x - hfLocalBounds.minimum.x, PxMax(hfLocalBounds.maximum.y - hfLocalBounds.minimum.y, hfLocalBounds.maximum.z - hfLocalBounds.minimum.z))); + + hfUtil.traceSegment<HFTraceSegmentCallback, false, false>(localRayOrigOffset, normRayDir, maxDistOffset, + &callback, hfLocalBounds, !bothSides); + return callback.mNbHits; +} + +static PxU32 raycast_heightField_unregistered(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(rayOrigin); + PX_UNUSED(rayDir); + PX_UNUSED(maxDist); + PX_UNUSED(hitFlags); + PX_UNUSED(maxHits); + PX_UNUSED(hits); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Raycast test called with height fields unregistered "); + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PT: table is not static because it's accessed as 'extern' within Gu (bypassing the function call). +RaycastFunc gRaycastMap[PxGeometryType::eGEOMETRY_COUNT] = +{ + raycast_sphere, + raycast_plane, + raycast_capsule, + raycast_box, + raycast_convexMesh, + raycast_triangleMesh, + raycast_heightField_unregistered +}; + +// PT: the function is used by external modules (Np, CCT, Sq) +const Gu::GeomRaycastTable& Gu::getRaycastFuncTable() +{ + return gRaycastMap; +} + +void registerHeightFields_Raycasts() +{ + gRaycastMap[PxGeometryType::eHEIGHTFIELD] = raycast_heightField; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSerialize.cpp b/PhysX_3.4/Source/GeomUtils/src/GuSerialize.cpp new file mode 100644 index 00000000..e7f3b68b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSerialize.cpp @@ -0,0 +1,380 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "PsUtilities.h" +#include "GuSerialize.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +void physx::readChunk(PxI8& a, PxI8& b, PxI8& c, PxI8& d, PxInputStream& stream) +{ + stream.read(&a, sizeof(PxI8)); + stream.read(&b, sizeof(PxI8)); + stream.read(&c, sizeof(PxI8)); + stream.read(&d, sizeof(PxI8)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU16 physx::readWord(bool mismatch, PxInputStream& stream) +{ + PxU16 d; + stream.read(&d, sizeof(PxU16)); + + if(mismatch) + flip(d); + return d; +} + +PxU32 physx::readDword(bool mismatch, PxInputStream& stream) +{ + PxU32 d; + stream.read(&d, sizeof(PxU32)); + + if(mismatch) + flip(d); + return d; +} + +PxF32 physx::readFloat(bool mismatch, PxInputStream& stream) +{ + union + { + PxU32 d; + PxF32 f; + } u; + + stream.read(&u.d, sizeof(PxU32)); + + if(mismatch) + flip(u.d); + return u.f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void physx::writeWord(PxU16 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxU16)); +} + +void physx::writeDword(PxU32 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxU32)); +} + +void physx::writeFloat(PxF32 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxF32)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool physx::readFloatBuffer(PxF32* dest, PxU32 nbFloats, bool mismatch, PxInputStream& stream) +{ + stream.read(dest, sizeof(PxF32)*nbFloats); + if(mismatch) + { + for(PxU32 i=0;i<nbFloats;i++) + flip(dest[i]); + } + return true; +} + +void physx::writeFloatBuffer(const PxF32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + while(nb--) + { + PxF32 f = *src++; + if(mismatch) + flip(f); + stream.write(&f, sizeof(PxF32)); + } +} + +void physx::writeWordBuffer(const PxU16* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + while(nb--) + { + PxU16 w = *src++; + if(mismatch) + flip(w); + stream.write(&w, sizeof(PxU16)); + } +} + +void physx::readWordBuffer(PxU16* dest, PxU32 nb, bool mismatch, PxInputStream& stream) +{ + stream.read(dest, sizeof(PxU16)*nb); + if(mismatch) + { + for(PxU32 i=0;i<nb;i++) + { + flip(dest[i]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool physx::writeHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32 version, bool mismatch, PxOutputStream& stream) +{ + // Store endianness + PxI8 streamFlags = Ps::littleEndian(); + if(mismatch) + streamFlags^=1; + + // Export header + writeChunk('N', 'X', 'S', streamFlags, stream); // "Novodex stream" identifier + writeChunk(a, b, c, d, stream); // Chunk identifier + writeDword(version, mismatch, stream); + return true; +} + +bool Gu::WriteHeader(PxU8 a, PxU8 b, PxU8 c, PxU8 d, PxU32 version, bool mismatch, PxOutputStream& stream) +{ + // Store endianness + PxU8 streamFlags = PxU8(Ps::littleEndian()); + if(mismatch) + streamFlags^=1; + + // Export header + writeChunk('I', 'C', 'E', PxI8(streamFlags), stream); // ICE identifier + writeChunk(PxI8(a), PxI8(b), PxI8(c), PxI8(d), stream); // Chunk identifier + writeDword(version, mismatch, stream); + return true; +} + +bool physx::readHeader(PxI8 a_, PxI8 b_, PxI8 c_, PxI8 d_, PxU32& version, bool& mismatch, PxInputStream& stream) +{ + // Import header + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='N' || b!='X' || c!='S') + return false; + + const PxI8 fileLittleEndian = d&1; + mismatch = fileLittleEndian!=Ps::littleEndian(); + + readChunk(a, b, c, d, stream); + if(a!=a_ || b!=b_ || c!=c_ || d!=d_) + return false; + + version = readDword(mismatch, stream); + return true; +} + +bool Gu::ReadHeader(PxU8 a_, PxU8 b_, PxU8 c_, PxU8 d_, PxU32& version, bool& mismatch, PxInputStream& stream) +{ + // Import header + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='I' || b!='C' || c!='E') + return false; + + const PxU8 FileLittleEndian = PxU8(d&1); + mismatch = FileLittleEndian!=Ps::littleEndian(); + + readChunk(a, b, c, d, stream); + if(a!=a_ || b!=b_ || c!=c_ || d!=d_) + return false; + + version = readDword(mismatch, stream); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::computeMaxIndex(const PxU32* indices, PxU32 nbIndices) +{ + PxU32 maxIndex=0; + while(nbIndices--) + { + PxU32 currentIndex = *indices++; + if(currentIndex>maxIndex) + maxIndex = currentIndex; + } + return maxIndex; +} +PxU16 physx::computeMaxIndex(const PxU16* indices, PxU32 nbIndices) +{ + PxU16 maxIndex=0; + while(nbIndices--) + { + PxU16 currentIndex = *indices++; + if(currentIndex>maxIndex) + maxIndex = currentIndex; + } + return maxIndex; +} + +void physx::storeIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + for(PxU32 i=0;i<nbIndices;i++) + { + PxU8 data = PxU8(indices[i]); + stream.write(&data, sizeof(PxU8)); + } + } + else if(maxIndex<=0xffff) + { + for(PxU32 i=0;i<nbIndices;i++) + writeWord(Ps::to16(indices[i]), platformMismatch, stream); + } + else + { + writeIntBuffer(indices, nbIndices, platformMismatch, stream); + } +} + +void physx::readIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + PxU8 data; + for(PxU32 i=0;i<nbIndices;i++) + { + stream.read(&data, sizeof(PxU8)); + indices[i] = data; + } + } + else if(maxIndex<=0xffff) + { + for(PxU32 i=0;i<nbIndices;i++) + indices[i] = readWord(platformMismatch, stream); + } + else + { + readIntBuffer(indices, nbIndices, platformMismatch, stream); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Gu::StoreIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + for(PxU32 i=0;i<nbIndices;i++) + { + PxU8 data = PxU8(indices[i]); + stream.write(&data, sizeof(PxU8)); + } + } + else if(maxIndex<=0xffff) + { + for(PxU32 i=0;i<nbIndices;i++) + writeWord(Ps::to16(indices[i]), platformMismatch, stream); + } + else + { +// WriteDwordBuffer(indices, nbIndices, platformMismatch, stream); + for(PxU32 i=0;i<nbIndices;i++) + writeDword(indices[i], platformMismatch, stream); + } +} + +void Gu::ReadIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + PxU8* tmp = reinterpret_cast<PxU8*>(PxAlloca(nbIndices*sizeof(PxU8))); + stream.read(tmp, nbIndices*sizeof(PxU8)); + for(PxU32 i=0;i<nbIndices;i++) + indices[i] = tmp[i]; +// for(PxU32 i=0;i<nbIndices;i++) +// indices[i] = stream.ReadByte(); + } + else if(maxIndex<=0xffff) + { + PxU16* tmp = reinterpret_cast<PxU16*>(PxAlloca(nbIndices*sizeof(PxU16))); + readWordBuffer(tmp, nbIndices, platformMismatch, stream); + for(PxU32 i=0;i<nbIndices;i++) + indices[i] = tmp[i]; +// for(PxU32 i=0;i<nbIndices;i++) +// indices[i] = ReadWord(platformMismatch, stream); + } + else + { + ReadDwordBuffer(indices, nbIndices, platformMismatch, stream); + } +} + +void Gu::StoreIndices(PxU16 maxIndex, PxU32 nbIndices, const PxU16* indices, PxOutputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + for(PxU32 i=0;i<nbIndices;i++) + { + PxU8 data = PxU8(indices[i]); + stream.write(&data, sizeof(PxU8)); + } + } + else + { + for(PxU32 i=0;i<nbIndices;i++) + writeWord(indices[i], platformMismatch, stream); + } +} + +void Gu::ReadIndices(PxU16 maxIndex, PxU32 nbIndices, PxU16* indices, PxInputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + PxU8* tmp = reinterpret_cast<PxU8*>(PxAlloca(nbIndices*sizeof(PxU8))); + stream.read(tmp, nbIndices*sizeof(PxU8)); + for(PxU32 i=0;i<nbIndices;i++) + indices[i] = tmp[i]; +// for(PxU32 i=0;i<nbIndices;i++) +// indices[i] = stream.ReadByte(); + } + else + { + PxU16* tmp = reinterpret_cast<PxU16*>(PxAlloca(nbIndices*sizeof(PxU16))); + readWordBuffer(tmp, nbIndices, platformMismatch, stream); + for(PxU32 i=0;i<nbIndices;i++) + indices[i] = tmp[i]; +// for(PxU32 i=0;i<nbIndices;i++) +// indices[i] = ReadWord(platformMismatch, stream); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSerialize.h b/PhysX_3.4/Source/GeomUtils/src/GuSerialize.h new file mode 100644 index 00000000..345fc6e9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSerialize.h @@ -0,0 +1,154 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SERIALIZE_H +#define GU_SERIALIZE_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxIO.h" +#include "CmPhysXCommon.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + PX_INLINE void flip(PxU16& v) + { + PxU8* b = reinterpret_cast<PxU8*>(&v); + PxU8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + PX_INLINE void flip(PxI16& v) + { + PxI8* b = reinterpret_cast<PxI8*>(&v); + PxI8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + PX_INLINE void flip(PxU32& v) + { + PxU8* b = reinterpret_cast<PxU8*>(&v); + + PxU8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + // MS: It is important to modify the value directly and not use a temporary variable or a return + // value. The reason for this is that a flipped float might have a bit pattern which indicates + // an invalid float. If such a float is assigned to another float, the bit pattern + // can change again (maybe to map invalid floats to a common invalid pattern?). + // When reading the float and flipping again, the changed bit pattern will result in a different + // float than the original one. + PX_INLINE void flip(PxF32& v) + { + PxU8* b = reinterpret_cast<PxU8*>(&v); + + PxU8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + PX_INLINE void writeChunk(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxOutputStream& stream) + { + stream.write(&a, sizeof(PxI8)); + stream.write(&b, sizeof(PxI8)); + stream.write(&c, sizeof(PxI8)); + stream.write(&d, sizeof(PxI8)); + } + + void readChunk(PxI8& a, PxI8& b, PxI8& c, PxI8& d, PxInputStream& stream); + + PxU16 readWord(bool mismatch, PxInputStream& stream); +PX_PHYSX_COMMON_API PxU32 readDword(bool mismatch, PxInputStream& stream); + PxF32 readFloat(bool mismatch, PxInputStream& stream); + +PX_PHYSX_COMMON_API void writeWord(PxU16 value, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeDword(PxU32 value, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeFloat(PxF32 value, bool mismatch, PxOutputStream& stream); + + bool readFloatBuffer(PxF32* dest, PxU32 nbFloats, bool mismatch, PxInputStream& stream); +PX_PHYSX_COMMON_API void writeFloatBuffer(const PxF32* src, PxU32 nb, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeWordBuffer(const PxU16* src, PxU32 nb, bool mismatch, PxOutputStream& stream); + void readWordBuffer(PxU16* dest, PxU32 nb, bool mismatch, PxInputStream& stream); + +PX_PHYSX_COMMON_API bool writeHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32 version, bool mismatch, PxOutputStream& stream); + bool readHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32& version, bool& mismatch, PxInputStream& stream); + +PX_INLINE bool readIntBuffer(PxU32* dest, PxU32 nbInts, bool mismatch, PxInputStream& stream) +{ + return readFloatBuffer(reinterpret_cast<PxF32*>(dest), nbInts, mismatch, stream); +} + +PX_INLINE void writeIntBuffer(const PxU32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + writeFloatBuffer(reinterpret_cast<const PxF32*>(src), nb, mismatch, stream); +} + +PX_INLINE bool ReadDwordBuffer(PxU32* dest, PxU32 nb, bool mismatch, PxInputStream& stream) +{ + return readFloatBuffer(reinterpret_cast<float*>(dest), nb, mismatch, stream); +} + +PX_INLINE void WriteDwordBuffer(const PxU32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + writeFloatBuffer(reinterpret_cast<const float*>(src), nb, mismatch, stream); +} + +PX_PHYSX_COMMON_API PxU32 computeMaxIndex(const PxU32* indices, PxU32 nbIndices); +PX_PHYSX_COMMON_API PxU16 computeMaxIndex(const PxU16* indices, PxU32 nbIndices); +PX_PHYSX_COMMON_API void storeIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch); +PX_PHYSX_COMMON_API void readIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch); + + +// PT: TODO: copied from IceSerialize.h, still needs to be refactored/cleaned up. +namespace Gu +{ + PX_PHYSX_COMMON_API bool WriteHeader(PxU8 a, PxU8 b, PxU8 c, PxU8 d, PxU32 version, bool mismatch, PxOutputStream& stream); + PX_PHYSX_COMMON_API bool ReadHeader(PxU8 a_, PxU8 b_, PxU8 c_, PxU8 d_, PxU32& version, bool& mismatch, PxInputStream& stream); + + PX_PHYSX_COMMON_API void StoreIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch); + void ReadIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch); + + PX_PHYSX_COMMON_API void StoreIndices(PxU16 maxIndex, PxU32 nbIndices, const PxU16* indices, PxOutputStream& stream, bool platformMismatch); + void ReadIndices(PxU16 maxIndex, PxU32 nbIndices, PxU16* indices, PxInputStream& stream, bool platformMismatch); +} + + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSphere.h b/PhysX_3.4/Source/GeomUtils/src/GuSphere.h new file mode 100644 index 00000000..fb827552 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSphere.h @@ -0,0 +1,108 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SPHERE_H +#define GU_SPHERE_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +/** +\brief Represents a sphere defined by its center point and radius. +*/ +namespace Gu +{ + class Sphere + { + public: + /** + \brief Constructor + */ + PX_INLINE Sphere() + { + } + + /** + \brief Constructor + */ + PX_INLINE Sphere(const PxVec3& _center, PxF32 _radius) : center(_center), radius(_radius) + { + } + /** + \brief Copy constructor + */ + PX_INLINE Sphere(const Sphere& sphere) : center(sphere.center), radius(sphere.radius) + { + } + /** + \brief Destructor + */ + PX_INLINE ~Sphere() + { + } + + PX_INLINE void set(const PxVec3& _center, float _radius) { center = _center; radius = _radius; } + + /** + \brief Checks the sphere is valid. + + \return true if the sphere is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + return radius >= 0.0f; + } + + /** + \brief Tests if a point is contained within the sphere. + + \param[in] p the point to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const PxVec3& p) const + { + return (center-p).magnitudeSquared() <= radius*radius; + } + + PxVec3 center; //!< Sphere's center + PxF32 radius; //!< Sphere's radius + }; +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSweepMTD.cpp b/PhysX_3.4/Source/GeomUtils/src/GuSweepMTD.cpp new file mode 100644 index 00000000..d94198b3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSweepMTD.cpp @@ -0,0 +1,1183 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "PxConvexMeshGeometry.h" +#include "GuConvexMesh.h" +#include "GuSweepSharedTests.h" +#include "GuConvexUtilsInternal.h" +#include "GuTriangleMesh.h" +#include "GuVecBox.h" +#include "GuVecTriangle.h" +#include "GuVecConvexHullNoScale.h" +#include "GuMidphaseInterface.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSweepMTD.h" +#include "GuPCMShapeConvex.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistancePointSegment.h" +#include "GuInternal.h" +#include "GuConvexEdgeFlags.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE void getScaledTriangle(const PxTriangleMeshGeometry& triGeom, const Cm::Matrix34& vertex2worldSkew, bool flipsNormal, PxTriangle& triangle, PxTriangleID triangleIndex) +{ + TriangleMesh* tm = static_cast<TriangleMesh*>(triGeom.triangleMesh); + tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, flipsNormal); +} + +#define BATCH_TRIANGLE_NUMBER 32u + +struct MTDTriangle : public PxTriangle +{ +public: + PxU8 extraTriData;//active edge flag data +}; + +struct MeshMTDGenerationCallback : MeshHitCallback<PxRaycastHit> +{ +public: + + Ps::Array<PxU32>& container; + + MeshMTDGenerationCallback(Ps::Array<PxU32>& tempContainer) + : MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE), container(tempContainer) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal&, const PxU32*) + { + container.pushBack(hit.faceIndex); + + return true; + } + + void operator=(const MeshMTDGenerationCallback&) {} +}; + +static bool getMTDPerTriangle(const MeshPersistentContact* manifoldContacts, const PxU32 numContacts, const PxU32 triangleIndex, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& deepestPen) +{ + using namespace Ps::aos; + + FloatV deepest = V4GetW(manifoldContacts[0].mLocalNormalPen); + PxU32 index = 0; + for(PxU32 k=1; k<numContacts; ++k) + { + const FloatV pen = V4GetW(manifoldContacts[k].mLocalNormalPen); + if(FAllGrtr(deepest, pen)) + { + deepest = pen; + index = k; + } + } + + if(FAllGrtr(deepestPen, deepest)) + { + PX_ASSERT(triangleIndex == manifoldContacts[index].mFaceIndex); + faceIndex = triangleIndex; + deepestPen = deepest; + normal = Vec3V_From_Vec4V(manifoldContacts[index].mLocalNormalPen); + closestA = manifoldContacts[index].mLocalPointB; + closestB = manifoldContacts[index].mLocalPointA; + return true; + } + + return false; +} +static void midPhaseQuery(const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, const Box& bound, Ps::Array<PxU32>& tempContainer) +{ + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + Box vertexSpaceBox; + computeVertexSpaceOBB(vertexSpaceBox, bound, pose, meshGeom.scale); + + MeshMTDGenerationCallback callback(tempContainer); + Midphase::intersectOBB(meshData, vertexSpaceBox, callback, true); +} + +// PT: TODO: refactor with EntityReportContainerCallback +struct MidPhaseQueryLocalReport : EntityReport<PxU32> +{ + MidPhaseQueryLocalReport(Ps::Array<PxU32>& _container) : container(_container) + { + + } + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; i<nb; i++) + container.pushBack(indices[i]); + return true; + } + + Ps::Array<PxU32>& container; + +private: + MidPhaseQueryLocalReport operator=(MidPhaseQueryLocalReport& report); +} ; + +static void midPhaseQuery(const HeightFieldUtil& hfUtil, const PxTransform& pose, const PxBounds3& bounds, Ps::Array<PxU32>& tempContainer, PxU32 flags) +{ + MidPhaseQueryLocalReport localReport(tempContainer); + hfUtil.overlapAABBTriangles(pose, bounds, flags, &localReport); +} + +static bool calculateMTD( const CapsuleV& capsuleV, const Ps::aos::FloatVArg inflatedRadiusV, const bool isDoubleSide, const MTDTriangle* triangles, const PxU32 nbTriangles, const PxU32 startIndex, MeshPersistentContact* manifoldContacts, + PxU32& numContacts, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& mtd) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + bool hadContacts = false; + FloatV deepestPen = mtd; + + for(PxU32 j=0; j<nbTriangles; ++j) + { + numContacts = 0; + + const MTDTriangle& curTri = triangles[j]; + TriangleV triangleV; + triangleV.verts[0] = V3LoadU(curTri.verts[0]); + triangleV.verts[1] = V3LoadU(curTri.verts[1]); + triangleV.verts[2] = V3LoadU(curTri.verts[2]); + const PxU8 triFlag = curTri.extraTriData; + + const Vec3V triangleNormal = triangleV.normal(); + const Vec3V v = V3Sub(capsuleV.getCenter(), triangleV.verts[0]); + const FloatV dotV = V3Dot(triangleNormal, v); + + // Backface culling + const bool culled = !isDoubleSide && (FAllGrtr(zero, dotV)); + if(culled) + continue; + + PCMCapsuleVsMeshContactGeneration::processTriangle(triangleV, j+startIndex, capsuleV, inflatedRadiusV, triFlag, manifoldContacts, numContacts); + + if(numContacts ==0) + continue; + + hadContacts = true; + + getMTDPerTriangle(manifoldContacts, numContacts, j + startIndex, normal, closestA, closestB, faceIndex, deepestPen); + } + + mtd = deepestPen; + return hadContacts; +} + +bool physx::Gu::computeCapsule_TriangleMeshMTD( const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, CapsuleV& capsuleV, PxReal inflatedRadius, + bool isDoubleSided, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + + TriangleMesh* triMesh = static_cast<TriangleMesh*>(triMeshGeom.triangleMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + const bool flipsNormal = triMeshGeom.scale.hasNegativeDeterminant(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + //inflated the capsule by 15% in case of some disagreement between sweep and mtd calculation. If sweep said initial overlap, but mtd has a positive separation, + //we are still be able to return a valid normal but we should zero the distance. + const FloatV inflatedRadiusV = FLoad(inflatedRadius*1.15f); + + bool foundInitial = false; + const PxU32 iterations = 4; + + const Cm::Matrix34 vertexToWorldSkew = pose * triMeshGeom.scale; + + Ps::Array<PxU32> tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i<iterations; ++i) + { + tempContainer.forceSize_Unsafe(0); + Capsule inflatedCapsule; + V3StoreU(capsuleV.p0, inflatedCapsule.p0); + V3StoreU(capsuleV.p1, inflatedCapsule.p1); + inflatedCapsule.radius = inflatedRadius; + + Box capsuleBox; + computeBoxAroundCapsule(inflatedCapsule, capsuleBox); + midPhaseQuery(triMeshGeom, pose, capsuleBox, tempContainer); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + const PxU32* indices = tempContainer.begin(); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER - 1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k<nbTrigs; k++) + { + //triangle world space + const PxU32 triangleIndex1 = indices[startIndex+k]; + ::getScaledTriangle(triMeshGeom, vertexToWorldSkew, flipsNormal, triangles[k], triangleIndex1); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(capsuleV, inflatedRadiusV, isDoubleSided, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = indices[triangleIndex]; + + foundInitial = true; + + //move the capsule to depenetrate it + + distV = FSub(mtd, capsuleV.radius); + if(FAllGrtrOrEq(FZero(), distV)) + { + Vec3V center = capsuleV.getCenter(); + const Vec3V t = V3Scale(normal, distV); + translation = V3Sub(translation, t); + center = V3Sub(center, t); + capsuleV.setCenter(center); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(closestA, hit.position); + V3StoreU(normal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + normal = V3Normalize(translation); + distV = FNeg(V3Length(translation)); + + if(foundInitial) + { + FStore(distV, &hit.distance); + V3StoreU(closestA, hit.position); + V3StoreU(normal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + + +bool physx::Gu::computeCapsule_HeightFieldMTD(const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + //inflated the capsule by 1% in case of some disagreement between sweep and mtd calculation.If sweep said initial overlap, but mtd has a positive separation, + //we are still be able to return a valid normal but we should zero the distance. + const FloatV inflatedRadiusV = FLoad(inflatedRadius*1.01f); + + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array<PxU32> tempContainer; + tempContainer.reserve(128); + + const HeightFieldUtil hfUtil(heightFieldGeom); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i<iterations; ++i) + { + tempContainer.empty(); + Capsule inflatedCapsule; + V3StoreU(capsuleV.p0, inflatedCapsule.p0); + V3StoreU(capsuleV.p1, inflatedCapsule.p1); + inflatedCapsule.radius = inflatedRadius; + + Box capsuleBox; + computeBoxAroundCapsule(inflatedCapsule, capsuleBox); + const PxTransform capsuleBoxTransform = capsuleBox.getTransform(); + const PxBounds3 bounds = PxBounds3::poseExtent(capsuleBoxTransform, capsuleBox.extents); + midPhaseQuery(hfUtil, pose, bounds, tempContainer, flags); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER - 1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k<nbTrigs; k++) + { + //triangle vertex space + const PxU32 triangleIndex1 = tempContainer[startIndex+k]; + hfUtil.getTriangle(pose, triangles[k], NULL, NULL, triangleIndex1, true); + triangles[k].extraTriData = ETD_CONVEX_EDGE_ALL; + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(capsuleV, inflatedRadiusV, isDoubleSided, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = FSub(mtd, capsuleV.radius); + if(FAllGrtrOrEq(FZero(), distV)) + { + //move the capsule to depenetrate it + Vec3V center = capsuleV.getCenter(); + const Vec3V t = V3Scale(normal, distV); + translation = V3Sub(translation, t); + center = V3Sub(center, t); + capsuleV.setCenter(center); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(closestA, hit.position); + V3StoreU(normal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + normal = V3Normalize(translation); + distV = FNeg(V3Length(translation)); + if(foundInitial) + { + FStore(distV, &hit.distance); + V3StoreU(closestA, hit.position); + V3StoreU(normal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool calculateMTD( const PolygonalData& polyData, SupportLocal* polyMap, Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, bool isDoubleSided, const Ps::aos::FloatVArg inflation, const MTDTriangle* triangles, const PxU32 nbTriangles, const PxU32 startIndex, + MeshPersistentContact* manifoldContacts, PxU32& numContacts, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& mtd) +{ + using namespace Ps::aos; + + bool hadContacts = false; + FloatV deepestPen = mtd; + + for(PxU32 j=0; j<nbTriangles; ++j) + { + numContacts = 0; + const MTDTriangle& curTri = triangles[j]; + const PxU8 triFlag = curTri.extraTriData; + + PCMConvexVsMeshContactGeneration::processTriangle(polyData, polyMap, curTri.verts, j+startIndex, triFlag, inflation, isDoubleSided, convexTransform, meshToConvex, manifoldContacts, numContacts); + + if(numContacts ==0) + continue; + + hadContacts = true; + getMTDPerTriangle(manifoldContacts, numContacts, j+startIndex, normal, closestA, closestB, faceIndex, deepestPen); + } + + mtd = deepestPen; + + return hadContacts; +} + +bool physx::Gu::computeBox_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const Box& _box, const PxTransform& boxTransform, PxReal inflation, bool isDoubleSided, PxSweepHit& hit) +{ + using namespace Ps::aos; + + TriangleMesh* triMesh = static_cast<TriangleMesh*>(triMeshGeom.triangleMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + + const Vec3V zeroV = V3Zero(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array<PxU32> tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + Box box = _box; + + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV minMargin = CalculatePCMBoxMargin(boxExtents); + const FloatV inflationV = FAdd(FLoad(inflation), minMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + box.extents += PxVec3(boundInflation); + const BoxV boxV(zeroV, boxExtents); + + Vec3V boxCenter = V3LoadU(box.center); + + //create the polyData based on the original data + PolygonalData polyData; + const PCMPolygonalBox polyBox(_box.extents); + polyBox.getPolygonalData(&polyData); + + const Mat33V identity = M33Identity(); + + const Cm::Matrix34 meshToWorldSkew = pose * triMeshGeom.scale; + + PsTransformV boxTransformV(p0, q0);//box + + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i<iterations; ++i) + { + tempContainer.forceSize_Unsafe(0); + + midPhaseQuery(triMeshGeom, pose, box, tempContainer); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + boxTransformV.p = boxCenter; + SupportLocalImpl<BoxV> boxMap(boxV, boxTransformV, identity, identity, true); + + boxMap.setShapeSpaceCenterofMass(zeroV); + // Move to AABB space + Cm::Matrix34 WorldToBox; + computeWorldToBoxMatrix(WorldToBox, box); + const Cm::Matrix34 meshToBox = WorldToBox*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToBox.m.column0), V3LoadU(meshToBox.m.column1), V3LoadU(meshToBox.m.column2)); + const Ps::aos::PsMatTransformV meshToConvex(V3LoadU(meshToBox.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER - 1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k<nbTrigs; k++) + { + //triangle vertex space + const PxU32 triangleIndex1 = tempContainer[startIndex+k]; + triMesh->getLocalTriangle(triangles[k], triangleIndex1, triMeshGeom.scale.hasNegativeDeterminant()); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, &boxMap, boxTransformV, meshToConvex, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = boxTransformV.rotate(normal); + worldContactA = boxTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + boxCenter = V3Sub(boxCenter, t); + V3StoreU(boxCenter, box.center); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + worldNormal = V3Normalize(translation); + distV = FNeg(V3Length(translation)); + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +bool physx::Gu::computeBox_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const Box& _box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array<PxU32> tempContainer; + tempContainer.reserve(128); + const HeightFieldUtil hfUtil(heightFieldGeom); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + Box box = _box; + + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV minMargin = CalculatePCMBoxMargin(boxExtents); + const FloatV inflationV = FAdd(FLoad(inflation), minMargin); + //const FloatV inflationV = FLoad(inflation); + + PxReal boundInflation; + FStore(inflationV, &boundInflation); + box.extents += PxVec3(boundInflation); + + const BoxV boxV(zeroV, boxExtents); + + Vec3V boxCenter = V3LoadU(box.center); + + //create the polyData based on the original box + PolygonalData polyData; + const PCMPolygonalBox polyBox(_box.extents); + polyBox.getPolygonalData(&polyData); + + const Mat33V identity = M33Identity(); + + const Cm::Matrix34 meshToWorldSkew(pose); + + PsTransformV boxTransformV(p0, q0);//box + + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i<iterations; ++i) + { + tempContainer.forceSize_Unsafe(0); + + const PxBounds3 bounds = PxBounds3::poseExtent(box.getTransform(), box.extents); + midPhaseQuery(hfUtil, pose, bounds, tempContainer, flags); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + boxTransformV.p = boxCenter; + SupportLocalImpl<BoxV> boxMap(boxV, boxTransformV, identity, identity, true); + boxMap.setShapeSpaceCenterofMass(zeroV); + // Move to AABB space + Cm::Matrix34 WorldToBox; + computeWorldToBoxMatrix(WorldToBox, box); + const Cm::Matrix34 meshToBox = WorldToBox*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToBox.m.column0), V3LoadU(meshToBox.m.column1), V3LoadU(meshToBox.m.column2)); + const Ps::aos::PsMatTransformV meshToConvex(V3LoadU(meshToBox.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k<nbTrigs; k++) + { + //triangle vertex space + const PxU32 triangleIndex1 = tempContainer[startIndex+k]; + hfUtil.getTriangle(pose, triangles[k], NULL, NULL, triangleIndex1, false, false); + triangles[k].extraTriData = ETD_CONVEX_EDGE_ALL; + } + + //ML: mtd has back face culling, so if the box's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, &boxMap, boxTransformV, meshToConvex, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = boxTransformV.rotate(normal); + worldContactA = boxTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + //worldContactB = boxTransformV.transform(closestB); + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + boxCenter = V3Sub(boxCenter, t); + V3StoreU(boxCenter, box.center); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + worldNormal = V3Normalize(translation); + distV = FNeg(V3Length(translation)); + + if(foundInitial) + { + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool physx::Gu::computeConvex_TriangleMeshMTD( const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + + TriangleMesh* triMesh = static_cast<TriangleMesh*>(triMeshGeom.triangleMesh); + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 2; + + ConvexHullData* hullData = &cm->getHull(); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + const PxVec3 _shapeSpaceCenterOfMass = convexScaling * hullData->mCenterOfMass; + const Vec3V shapeSpaceCenterOfMass = V3LoadU(_shapeSpaceCenterOfMass); + + const QuatV q0 = QuatVLoadU(&convexPose.q.x); + const Vec3V p0 = V3LoadU(&convexPose.p.x); + PsTransformV convexTransformV(p0, q0); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, idtScaleConvex); + PX_ALIGN(16, PxU8 convexBuff[sizeof(SupportLocalImpl<ConvexHullV>)]); + + const FloatV convexMargin = CalculatePCMConvexMargin(hullData, vScale); + const FloatV inflationV = FAdd(FLoad(inflation), convexMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + Ps::Array<PxU32> tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + const Cm::Matrix34 meshToWorldSkew = pose * triMeshGeom.scale; + + PolygonalData polyData; + getPCMConvexData(convexHull, idtScaleConvex, polyData); + + FloatV mtd; + FloatV distV; + Vec3V center = p0; + PxTransform tempConvexPose = convexPose; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + + for(PxU32 i=0; i<iterations; ++i) + { + tempContainer.forceSize_Unsafe(0); + + //ML:: construct convex hull data + V3StoreU(center, tempConvexPose.p); + convexTransformV.p = center; + SupportLocal* convexMap = (idtScaleConvex ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHull), convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl<ConvexHullV>)(convexHull, convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex))); + + convexMap->setShapeSpaceCenterofMass(shapeSpaceCenterOfMass); + + Box hullOBB; + computeOBBAroundConvex(hullOBB, convexGeom, cm, tempConvexPose); + + hullOBB.extents += PxVec3(boundInflation); + + midPhaseQuery(triMeshGeom, pose, hullOBB, tempContainer); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + // Move to AABB space + const Cm::Matrix34 worldToConvex(tempConvexPose.getInverse()); + const Cm::Matrix34 meshToConvex = worldToConvex*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToConvex.m.column0), V3LoadU(meshToConvex.m.column1), V3LoadU(meshToConvex.m.column2)); + const Ps::aos::PsMatTransformV meshToConvexV(V3LoadU(meshToConvex.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k<nbTrigs; k++) + { + //triangle vertex space + const PxU32 triangleIndex1 = tempContainer[startIndex+k]; + triMesh->getLocalTriangle(triangles[k], triangleIndex1, triMeshGeom.scale.hasNegativeDeterminant()); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, convexMap, convexTransformV, meshToConvexV, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = convexTransformV.rotate(normal); + worldContactA = convexTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + center = V3Sub(center, t); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + worldNormal = V3Normalize(translation); + distV = FNeg(V3Length(translation)); + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +bool physx::Gu::computeConvex_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const HeightFieldUtil hfUtil(heightFieldGeom); + + const Vec3V zeroV = V3Zero(); + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 2; + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + ConvexHullData* hullData = &cm->getHull(); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + const PxVec3 _shapeSpaceCenterOfMass = convexScaling * hullData->mCenterOfMass; + const Vec3V shapeSpaceCenterOfMass = V3LoadU(_shapeSpaceCenterOfMass); + + const QuatV q0 = QuatVLoadU(&convexPose.q.x); + const Vec3V p0 = V3LoadU(&convexPose.p.x); + PsTransformV convexTransformV(p0, q0); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, idtScaleConvex); + PX_ALIGN(16, PxU8 convexBuff[sizeof(SupportLocalImpl<ConvexHullV>)]); + + const FloatV convexMargin = CalculatePCMConvexMargin(hullData, vScale); + const FloatV inflationV = FAdd(FLoad(inflation), convexMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + Ps::Array<PxU32> tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + PolygonalData polyData; + getPCMConvexData(convexHull, idtScaleConvex, polyData); + + FloatV mtd; + FloatV distV; + Vec3V center = p0; + PxTransform tempConvexPose = convexPose; + const Cm::Matrix34 meshToWorldSkew(pose); + + for(PxU32 i=0; i<iterations; ++i) + { + tempContainer.forceSize_Unsafe(0); + + //ML:: construct convex hull data + + V3StoreU(center, tempConvexPose.p); + convexTransformV.p = center; + + SupportLocal* convexMap = (idtScaleConvex ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHull), convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl<ConvexHullV>)(convexHull, convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex))); + + convexMap->setShapeSpaceCenterofMass(shapeSpaceCenterOfMass); + + Box hullOBB; + computeOBBAroundConvex(hullOBB, convexGeom, cm, tempConvexPose); + hullOBB.extents += PxVec3(boundInflation); + + const PxBounds3 bounds = PxBounds3::basisExtent(hullOBB.center, hullOBB.rot, hullOBB.extents); + + midPhaseQuery(hfUtil, pose, bounds, tempContainer, flags); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + + if(!nbTriangles) + break; + + // Move to AABB space + const Cm::Matrix34 worldToConvex(tempConvexPose.getInverse()); + const Cm::Matrix34 meshToConvex = worldToConvex*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToConvex.m.column0), V3LoadU(meshToConvex.m.column1), V3LoadU(meshToConvex.m.column2)); + const Ps::aos::PsMatTransformV meshToConvexV(V3LoadU(meshToConvex.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k<nbTrigs; k++) + { + //triangle vertex space + const PxU32 triangleIndex1 = tempContainer[startIndex+k]; + hfUtil.getTriangle(pose, triangles[k], NULL, NULL, triangleIndex1, false, false); + triangles[k].extraTriData = ETD_CONVEX_EDGE_ALL; + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, convexMap, convexTransformV, meshToConvexV, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = convexTransformV.rotate(normal); + worldContactA = convexTransformV.transform(closestA); + + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + center = V3Sub(center, t); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + worldNormal = V3Normalize(translation); + distV = FNeg(V3Length(translation)); + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computeSphere_SphereMTD(const Sphere& sphere0, const Sphere& sphere1, PxSweepHit& hit) +{ + const PxVec3 delta = sphere1.center - sphere0.center; + const PxReal d2 = delta.magnitudeSquared(); + const PxReal radiusSum = sphere0.radius + sphere1.radius; + + const PxReal d = PxSqrt(d2); + hit.normal = delta / d; + hit.distance = d - radiusSum ; + hit.position = sphere0.center + hit.normal * sphere0.radius; + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computeSphere_CapsuleMTD( const Sphere& sphere, const Capsule& capsule, PxSweepHit& hit) +{ + const PxReal radiusSum = sphere.radius + capsule.radius; + + PxReal u; + distancePointSegmentSquared(capsule, sphere.center, &u); + + const PxVec3 normal = capsule.getPointAt(u) - sphere.center; + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = PxSqrt(lenSq); + hit.normal = normal / d; + hit.distance = d - radiusSum; + hit.position = sphere.center + hit.normal * sphere.radius; + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computeCapsule_CapsuleMTD(const Capsule& capsule0, const Capsule& capsule1, PxSweepHit& hit) +{ + using namespace Ps::aos; + + PxReal s,t; + distanceSegmentSegmentSquared(capsule0, capsule1, &s, &t); + + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + const PxVec3 pointAtCapsule0 = capsule0.getPointAt(s); + const PxVec3 pointAtCapsule1 = capsule1.getPointAt(t); + + const PxVec3 normal = pointAtCapsule0 - pointAtCapsule1; + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 len = PxSqrt(lenSq); + hit.normal = normal / len; + hit.distance = len - radiusSum; + hit.position = pointAtCapsule1 + hit.normal * capsule1.radius; + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computePlane_CapsuleMTD(const PxPlane& plane, const Capsule& capsule, PxSweepHit& hit) +{ + const PxReal d0 = plane.distance(capsule.p0); + const PxReal d1 = plane.distance(capsule.p1); + PxReal dmin; + PxVec3 point; + if(d0 < d1) + { + dmin = d0; + point = capsule.p0; + } + else + { + dmin = d1; + point = capsule.p1; + } + + hit.normal = plane.n; + hit.distance = dmin - capsule.radius; + hit.position = point - hit.normal * dmin; + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computePlane_BoxMTD(const PxPlane& plane, const Box& box, PxSweepHit& hit) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + PxReal dmin = plane.distance(pts[0]); + PxU32 index = 0; + for(PxU32 i=1;i<8;i++) + { + const PxReal d = plane.distance(pts[i]); + if(dmin > d) + { + index = i; + dmin = d; + } + } + hit.normal = plane.n; + hit.distance = dmin; + hit.position = pts[index] - plane.n*dmin; + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computePlane_ConvexMTD(const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxSweepHit& hit) +{ + const ConvexMesh* convexMesh = static_cast<const ConvexMesh*>(convexGeom.convexMesh); + const Cm::FastVertex2ShapeScaling convexScaling(convexGeom.scale); + PxU32 nbVerts = convexMesh->getNbVerts(); + const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); + + PxVec3 worldPointMin = convexPose.transform(convexScaling * verts[0]); + PxReal dmin = plane.distance(worldPointMin); + for(PxU32 i=1;i<nbVerts;i++) + { + const PxVec3 worldPoint = convexPose.transform(convexScaling * verts[i]); + const PxReal d = plane.distance(worldPoint); + if(dmin > d) + { + dmin = d; + worldPointMin = worldPoint; + } + } + + hit.normal = plane.n; + hit.distance = dmin; + hit.position = worldPointMin - plane.n * dmin; + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSweepMTD.h b/PhysX_3.4/Source/GeomUtils/src/GuSweepMTD.h new file mode 100644 index 00000000..d4bf7f55 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSweepMTD.h @@ -0,0 +1,89 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_MTD_H +#define GU_SWEEP_MTD_H + +namespace physx +{ + class PxConvexMeshGeometry; + class PxTriangleMeshGeometry; + class PxGeometry; + class PxHeightFieldGeometry; + +namespace Gu +{ + class Sphere; + class Capsule; + + bool computeCapsule_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, Gu::CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, PxSweepHit& hit); + + bool computeCapsule_HeightFieldMTD(const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, Gu::CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeBox_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const Gu::Box& box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit); + + bool computeBox_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const Gu::Box& box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeConvex_TriangleMeshMTD( const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexTransform, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit); + + bool computeConvex_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeSphere_SphereMTD(const Sphere& sphere0, const Sphere& sphere1, PxSweepHit& hit); + bool computeSphere_CapsuleMTD(const Sphere& sphere, const Capsule& capsule, PxSweepHit& hit); + + bool computeCapsule_CapsuleMTD(const Capsule& capsule0, const Capsule& capsule1, PxSweepHit& hit); + + bool computePlane_CapsuleMTD(const PxPlane& plane, const Capsule& capsule, PxSweepHit& hit); + bool computePlane_BoxMTD(const PxPlane& plane, const Box& box, PxSweepHit& hit); + bool computePlane_ConvexMTD(const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxSweepHit& hit); + + // PT: wrapper just to avoid duplicating these lines. + PX_FORCE_INLINE void setupSweepHitForMTD(PxSweepHit& sweepHit, bool hasContacts, const PxVec3& unitDir) + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } +} + +} + + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSweepSharedTests.cpp b/PhysX_3.4/Source/GeomUtils/src/GuSweepSharedTests.cpp new file mode 100644 index 00000000..162de3f5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSweepSharedTests.cpp @@ -0,0 +1,726 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "CmScaling.h" +#include "GuConvexMesh.h" +#include "GuIntersectionRayPlane.h" +#include "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuSweepMTD.h" +#include "PxConvexMeshGeometry.h" +#include "PxSphereGeometry.h" +#include "GuSweepSphereCapsule.h" +#include "GuSweepCapsuleCapsule.h" +#include "GuSweepTriangleUtils.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuInternal.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +static const PxReal gEpsilon = .01f; + +static PxU32 computeSweepConvexPlane( + const PxConvexMeshGeometry& convexGeom, ConvexHullData* hullData, const PxU32& nbPolys, const PxTransform& pose, + const PxVec3& impact_, const PxVec3& unitDir) +{ + PX_ASSERT(nbPolys); + + const PxVec3 impact = impact_ - unitDir * gEpsilon; + + const PxVec3 localPoint = pose.transformInv(impact); + const PxVec3 localDir = pose.rotateInv(unitDir); + + const FastVertex2ShapeScaling scaling(convexGeom.scale); + + PxU32 minIndex = 0; + PxReal minD = PX_MAX_REAL; + for(PxU32 j=0; j<nbPolys; j++) + { + const PxPlane& pl = hullData->mPolygons[j].mPlane; + + PxPlane plane; + scaling.transformPlaneToShapeSpace(pl.n, pl.d, plane.n, plane.d); + + PxReal d = plane.distance(localPoint); + if(d<0.0f) + continue; + + const PxReal tweak = plane.n.dot(localDir) * gEpsilon; + d += tweak; + + if(d<minD) + { + minIndex = j; + minD = d; + } + } + return minIndex; +} + +static PX_FORCE_INLINE bool computeFaceIndex(PxSweepHit& sweepHit, const PxHitFlags hitFlags, const PxConvexMeshGeometry& convexGeom, ConvexHullData* hullData, const PxTransform& pose, const PxVec3& unitDir) +{ + if(hitFlags & PxHitFlag::eFACE_INDEX) + { + // PT: compute closest polygon using the same tweak as in swept-capsule-vs-mesh + sweepHit.faceIndex = computeSweepConvexPlane(convexGeom, hullData, hullData->mNbPolygons, pose, sweepHit.position, unitDir); + sweepHit.flags |= PxHitFlag::eFACE_INDEX; + } + return true; +} + +static PX_FORCE_INLINE bool hasInitialOverlap(PxSweepHit& sweepHit, const PxVec3& unitDir, + const FloatVArg toi, + const Vec3VArg normal, const Vec3VArg closestA, + const PsTransformV& convexPose, + const bool isMtd, const bool impactPointOnTheOtherShape) +{ + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + const FloatV zero = FZero(); + if(FAllGrtrOrEq(zero, toi)) + { + //ML: initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const FloatV length = toi; + const Vec3V worldPointA = convexPose.transform(closestA); + const Vec3V worldNormal = V3Normalize(convexPose.rotate(normal)); + if(impactPointOnTheOtherShape) + { + const Vec3V destWorldPointA = V3NegScaleSub(worldNormal, length, worldPointA); + V3StoreU(worldNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + } + else + { + const Vec3V destNormal = V3Neg(worldNormal); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(worldPointA, sweepHit.position); + } + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + sweepHit.faceIndex = 0xffffffff; + return true; + } + return false; +} + +///////////////////////////////////////////////// sweepCapsule/Sphere ////////////////////////////////////////////////////// +bool sweepCapsule_SphereGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + const Sphere sphere(pose.p, sphereGeom.radius+inflation); + + if(!sweepSphereCapsule(sphere, lss, -unitDir, distance, sweepHit.distance, sweepHit.position, sweepHit.normal, hitFlags)) + return false; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + if(isMtd) + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + + if(sweepHit.distance == 0.f) + { + //intialOverlap + if(lss.p0 == lss.p1) + { + //sphere + return computeSphere_SphereMTD(sphere, Sphere(lss.p0, lss.radius), sweepHit); + } + else + { + //capsule + return computeSphere_CapsuleMTD(sphere, lss, sweepHit); + } + } + } + else + { + if(sweepHit.distance!=0.0f) + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + else + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + } + return true; +} + +bool sweepCapsule_PlaneGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(geom); +// const PxPlaneGeometry& planeGeom = static_cast<const PxPlaneGeometry&>(geom); + + const PxPlane& worldPlane = getPlane(pose); + + const PxF32 capsuleRadius = lss.radius + inflation; + + PxU32 index = 0; + PxVec3 pts[2]; + + PxReal minDp = PX_MAX_REAL; + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + // Find extreme point on the capsule + // AP: removed if (lss.p0 == lss.p1 clause because it wasn't properly computing minDp) + pts[0] = lss.p0; + pts[1] = lss.p1; + for(PxU32 i=0; i<2; i++) + { + const PxReal dp = pts[i].dot(worldPlane.n); + if(dp<minDp) + { + minDp = dp; + index = i; + } + } + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + if(isMtd) + { + //initial overlap with the plane + if(minDp <= capsuleRadius - worldPlane.d) + { + sweepHit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL| PxHitFlag::ePOSITION; + return computePlane_CapsuleMTD(worldPlane, lss, sweepHit); + } + } + else + { + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // test if the capsule initially overlaps with plane + if(minDp <= capsuleRadius - worldPlane.d) + { + sweepHit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL; + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + return true; + } + } + } + + const PxVec3 ptOnCapsule = pts[index] - worldPlane.n*capsuleRadius; + + // Raycast extreme vertex against plane + bool hitPlane = intersectRayPlane(ptOnCapsule, unitDir, worldPlane, sweepHit.distance, &sweepHit.position); + if(hitPlane && sweepHit.distance > 0 && sweepHit.distance <= distance) + { + sweepHit.normal = worldPlane.n; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return true; + } + return false; +} + +bool sweepCapsule_CapsuleGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + Capsule staticCapsule; + getCapsule(staticCapsule, capsuleGeom, pose); + staticCapsule.radius +=inflation; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + PxU16 outFlags; + if(!sweepCapsuleCapsule(lss, staticCapsule, -unitDir, distance, sweepHit.distance, sweepHit.position, sweepHit.normal, hitFlags, outFlags)) + return false; + + sweepHit.flags = PxHitFlags(outFlags); + if(sweepHit.distance == 0.0f) + { + //initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + return computeCapsule_CapsuleMTD(lss, staticCapsule, sweepHit); + } + } + return true; +} + +bool sweepCapsule_ConvexGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + + using namespace Ps::aos; + + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + const FloatV dist = FLoad(distance); + const Vec3V worldDir = V3LoadU(unitDir); + + const PsTransformV capPose = loadTransformU(capsulePose_); + const PsTransformV convexPose = loadTransformU(pose); + + const PsMatTransformV aToB(convexPose.transformInv(capPose)); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); + const FloatV capsuleRadius = FLoad(lss.radius); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + CapsuleV capsule(aToB.p, aToB.rotate( V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const Vec3V dir = convexPose.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<ConvexHullV> convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(!gjkRaycastPenetration< LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, lss.radius + inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, true)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = convexPose.transform(closestA); + const FloatV length = FMul(dist, toi); + const Vec3V destNormal = V3Normalize(convexPose.rotate(normal)); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, convexGeom, hullData, pose, unitDir); +} + +///////////////////////////////////////////////// sweepBox ////////////////////////////////////////////////////// + +bool sweepBox_PlaneGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(geom); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + +// const PxPlaneGeometry& planeGeom = static_cast<const PxPlaneGeometry&>(geom); + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + PxPlane worldPlane = getPlane(pose); + worldPlane.d -=inflation; + + // Find extreme point on the box + PxVec3 boxPts[8]; + box.computeBoxPoints(boxPts); + PxU32 index = 0; + PxReal minDp = PX_MAX_REAL; + for(PxU32 i=0;i<8;i++) + { + const PxReal dp = boxPts[i].dot(worldPlane.n); + + if(dp<minDp) + { + minDp = dp; + index = i; + } + } + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + if(isMtd) + { + // test if box initially overlap with plane + if(minDp <= -worldPlane.d) + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + //compute Mtd; + return computePlane_BoxMTD(worldPlane, box, sweepHit); + } + } + else + { + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // test if box initially overlap with plane + if(minDp <= -worldPlane.d) + { + sweepHit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL; + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + return true; + } + } + } + + // Raycast extreme vertex against plane + bool hitPlane = intersectRayPlane(boxPts[index], unitDir, worldPlane, sweepHit.distance, &sweepHit.position); + if(hitPlane && sweepHit.distance > 0 && sweepHit.distance <= distance) + { + sweepHit.normal = worldPlane.n; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return true; + } + return false; +} + +bool sweepBox_ConvexGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxGeom_); + + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const PsTransformV boxPose = loadTransformU(boxPose_); + const PsTransformV convexPose = loadTransformU(pose); + + const PsMatTransformV aToB(convexPose.transformInv(boxPose)); + + const Vec3V boxExtents = V3LoadU(box.extents); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + ShrunkBoxV boxV(zeroV, boxExtents); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexPose.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + RelativeConvex<BoxV> convexA(boxV, aToB); + LocalConvex<ConvexHullV> convexB(convexHull); + if(!gjkRaycastPenetration< RelativeConvex<BoxV>,LocalConvex<ConvexHullV> >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, true)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destNormal = V3Normalize(convexPose.rotate(normal)); + const FloatV length = FMul(dist, toi); + const Vec3V worldPointA = convexPose.transform(closestA); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, convexGeom, hullData, pose, unitDir); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Gu::sweepCapsuleTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxCapsuleGeometry)) +{ + Capsule capsule; + getCapsule(capsule, geom, pose); + capsule.radius +=inflation; + + // Compute swept box + Box capsuleBox; + computeBoxAroundCapsule(capsule, capsuleBox); + + BoxPadded sweptBounds; + computeSweptBox(sweptBounds, capsuleBox.extents, capsuleBox.center, capsuleBox.rot, unitDir, distance); + + PxVec3 triNormal; + return sweepCapsuleTriangles_Precise(nbTris, triangles, capsule, unitDir, distance, cachedIndex, hit, triNormal, hitFlags, doubleSided, &sweptBounds); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepConvex_SphereGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero= FZero(); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + const FloatV sphereRadius = FLoad(sphereGeom.radius); + + const PsTransformV sphereTransf = loadTransformU(pose); + const PsTransformV convexTransf = loadTransformU(convexPose); + + const PsMatTransformV aToB(convexTransf.transformInv(sphereTransf)); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexTransf.rotateInv(V3Scale(worldDir, dist)); + + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //CapsuleV capsule(zeroV, sphereRadius); + CapsuleV capsule(aToB.p, sphereRadius); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<ConvexHullV> convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(!gjkRaycastPenetration< LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, false)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destNormal = V3Neg(V3Normalize(convexTransf.rotate(normal))); + const FloatV length = FMul(dist, toi); + const Vec3V destWorldPointA = convexTransf.transform(closestA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_PlaneGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(hitFlags); + PX_UNUSED(geom); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + const PxVec3* PX_RESTRICT hullVertices = hullData->getHullVertices(); + PxU32 numHullVertices = hullData->mNbHullVertices; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + const FastVertex2ShapeScaling convexScaling(convexGeom.scale); + + PxPlane plane = getPlane(pose); + plane.d -=inflation; + + sweepHit.distance = distance; + bool status = false; + bool initialOverlap = false; + while(numHullVertices--) + { + const PxVec3& vertex = *hullVertices++; + const PxVec3 worldPt = convexPose.transform(convexScaling * vertex); + float t; + PxVec3 pointOnPlane; + if(intersectRayPlane(worldPt, unitDir, plane, t, &pointOnPlane)) + { + if(plane.distance(worldPt) <= 0.0f) + { + initialOverlap = true; + break; + //// Convex touches plane + //sweepHit.distance = 0.0f; + //sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + //sweepHit.normal = -unitDir; + //return true; + } + + if(t > 0.0f && t <= sweepHit.distance) + { + sweepHit.distance = t; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + sweepHit.position = pointOnPlane; + sweepHit.normal = plane.n; + status = true; + } + } + } + + if(initialOverlap) + { + if(isMtd) + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return computePlane_ConvexMTD(plane, convexGeom, convexPose, sweepHit); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + sweepHit.normal = -unitDir; + return true; + } + } + return status; +} + +bool sweepConvex_CapsuleGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose); + + // remove PxHitFlag::eFACE_INDEX, not neeeded to compute. + PxHitFlags tempHitFlags = hitFlags; + tempHitFlags &= ~PxHitFlag::eFACE_INDEX; + + if(!sweepCapsule_ConvexGeom(convexGeom, convexPose, capsuleGeom, pose, capsule, -unitDir, distance, sweepHit, tempHitFlags, inflation)) + return false; + + if(sweepHit.flags & PxHitFlag::ePOSITION) + sweepHit.position += unitDir * sweepHit.distance; + + sweepHit.normal = -sweepHit.normal; + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_BoxGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + Box box; + buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + + // remove PxHitFlag::eFACE_INDEX, not neeeded to compute. + PxHitFlags tempHitFlags = hitFlags; + tempHitFlags &= ~PxHitFlag::eFACE_INDEX; + + if(!sweepBox_ConvexGeom(convexGeom, convexPose, boxGeom, pose, box, -unitDir, distance, sweepHit, tempHitFlags, inflation)) + return false; + + if(sweepHit.flags & PxHitFlag::ePOSITION) + sweepHit.position += unitDir * sweepHit.distance; + + sweepHit.normal = -sweepHit.normal; + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_ConvexGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& otherConvexGeom = static_cast<const PxConvexMeshGeometry&>(geom); + ConvexMesh& otherConvexMesh = *static_cast<ConvexMesh*>(otherConvexGeom.convexMesh); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + ConvexHullData* otherHullData = &otherConvexMesh.getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const Vec3V otherVScale = V3LoadU_SafeReadW(otherConvexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV otherVQuat = QuatVLoadU(&otherConvexGeom.scale.rotation.x); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + const PsTransformV otherTransf = loadTransformU(pose); + const PsTransformV convexTransf = loadTransformU(convexPose); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexTransf.rotateInv(V3Scale(worldDir, dist)); + + const PsMatTransformV aToB(convexTransf.transformInv(otherTransf)); + + ConvexHullV otherConvexHull(otherHullData, zeroV, otherVScale, otherVQuat, otherConvexGeom.scale.isIdentity()); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + RelativeConvex<ConvexHullV> convexA(otherConvexHull, aToB); + LocalConvex<ConvexHullV> convexB(convexHull); + if(!gjkRaycastPenetration< RelativeConvex<ConvexHullV>, LocalConvex<ConvexHullV> >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, false)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = convexTransf.transform(closestA); + const Vec3V destNormal = V3Neg(V3Normalize(convexTransf.rotate(normal))); + const FloatV length = FMul(dist, toi); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(worldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, otherConvexGeom, otherHullData, pose, unitDir); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSweepSharedTests.h b/PhysX_3.4/Source/GeomUtils/src/GuSweepSharedTests.h new file mode 100644 index 00000000..7efc8032 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSweepSharedTests.h @@ -0,0 +1,56 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SHARED_TESTS_H +#define GU_SWEEP_SHARED_TESTS_H + +#include "CmPhysXCommon.h" +#include "GuBoxConversion.h" + +namespace physx +{ +PX_FORCE_INLINE void computeWorldToBoxMatrix(physx::Cm::Matrix34& worldToBox, const physx::Gu::Box& box) +{ + physx::Cm::Matrix34 boxToWorld; + physx::buildMatrixFromBox(boxToWorld, box); + worldToBox = boxToWorld.getInverseRT(); +} + +PX_FORCE_INLINE PxU32 getTriangleIndex(PxU32 i, PxU32 cachedIndex) +{ + PxU32 triangleIndex; + if(i==0) triangleIndex = cachedIndex; + else if(i==cachedIndex) triangleIndex = 0; + else triangleIndex = i; + return triangleIndex; +} +} + + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSweepTests.cpp b/PhysX_3.4/Source/GeomUtils/src/GuSweepTests.cpp new file mode 100644 index 00000000..d6650aef --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSweepTests.cpp @@ -0,0 +1,605 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepTests.h" +#include "PxSphereGeometry.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "GuVecTriangle.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" +#include "PsFoundation.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +bool sweepCapsule_BoxGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(hitFlags); + + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); + const FloatV dist = FLoad(distance); + const Vec3V worldDir = V3LoadU(unitDir); + + const PsTransformV capPos = loadTransformU(capsulePose_); + const PsTransformV boxPos = loadTransformU(pose); + + const PsMatTransformV aToB(boxPos.transformInv(capPos)); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); + const FloatV capsuleRadius = FLoad(lss.radius); + + ShrunkBoxV box(zeroV, boxExtents0); + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + const Vec3V dir = boxPos.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi = FMax(); + Vec3V closestA, normal;//closestA and normal is in the local space of box + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<BoxV> convexB(box); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); + if(!gjkRaycastPenetration<LocalConvex<CapsuleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, + closestA, lss.radius + inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + if(FAllGrtrOrEq(zero, toi)) + { + //initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = toi; + const Vec3V destWorldPointA = V3NegScaleSub(destNormal, length, worldPointA); + V3StoreU(destWorldPointA, sweepHit.position); + V3StoreU(destNormal, sweepHit.normal); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = FMul(dist, toi); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_SphereGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(hitFlags); + PX_UNUSED(boxGeom_); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const FloatV sphereRadius = FLoad(sphereGeom.radius); + + const PsTransformV spherePos = loadTransformU(pose); + const PsTransformV boxPos = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxPos.transformInv(spherePos)); + + ShrunkBoxV boxV(zeroV, boxExtents); + CapsuleV capsuleV(aToB.p, sphereRadius); + + //transform into b space + const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); + LocalConvex<CapsuleV> convexA(capsuleV); + LocalConvex<BoxV> convexB(boxV); + if(!gjkRaycastPenetration< LocalConvex<CapsuleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + //initial overlap + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); + const FloatV length = toi; + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); + const FloatV length = FMul(worldDist, toi); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool sweepBox_CapsuleGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_UNUSED(hitFlags); + PX_UNUSED(boxGeom_); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom.halfHeight); + const FloatV capsuleRadius = FLoad(capsuleGeom.radius); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const PsTransformV capPos = loadTransformU(pose); + const PsTransformV boxPos = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxPos.transformInv(capPos)); + + ShrunkBoxV boxV(zeroV, boxExtents); + CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + //transform into b space + const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); + LocalConvex<CapsuleV> convexA(capsuleV); + LocalConvex<BoxV> convexB(boxV); + if(!gjkRaycastPenetration< LocalConvex<CapsuleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, capsuleGeom.radius+inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + //initial overlap + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + //initial overlap is toi < 0 + const FloatV length = toi; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + return true; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = FMul(worldDist, toi); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool sweepBox_BoxGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(boxGeom_); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); + const Vec3V boxExtents1 = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const PsTransformV boxTrans0 = loadTransformU(pose); + const PsTransformV boxTrans1 = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxTrans1.transformInv(boxTrans0)); + + ShrunkBoxV box0(zeroV, boxExtents0); + ShrunkBoxV box1(zeroV, boxExtents1); + + //transform into b space + const Vec3V dir = boxTrans1.rotateInv(V3Scale(unitDirV, worldDist)); + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + RelativeConvex<BoxV> convexA(box0, aToB); + LocalConvex<BoxV> convexB(box1); + if(!gjkRaycastPenetration<RelativeConvex<BoxV>, LocalConvex<BoxV> >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const FloatV length = toi; + const Vec3V destWorldPointA = boxTrans1.transform(closestA); + const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxTrans1.transform(closestA); + const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); + const FloatV length = FMul(worldDist, toi); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool Gu::sweepBoxTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) +{ + PX_UNUSED(hitFlags); + + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !doubleSided && !meshBothSides; + + Box box; + buildFrom(box, pose.p, geom.halfExtents, pose.q); + + PxSweepHit sweepHit; + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localMotion = localDir * distance; + + const Vec3V base0 = V3LoadU(worldToBox.m.column0); + const Vec3V base1 = V3LoadU(worldToBox.m.column1); + const Vec3V base2 = V3LoadU(worldToBox.m.column2); + const Mat33V matV(base0, base1, base2); + const Vec3V p = V3LoadU(worldToBox.p); + const PsMatTransformV worldToBoxV(p, matV); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const Vec3V boxDir = V3LoadU(localDir); + const FloatV inflationV = FLoad(inflation); + const Vec3V absBoxDir = V3Abs(boxDir); + const FloatV boxRadiusV = FAdd(V3Dot(absBoxDir, boxExtents), inflationV); + BoxV boxV(zeroV, boxExtents); + +#if PX_DEBUG + PxU32 totalTestsExpected = nbTris; + PxU32 totalTestsReal = 0; + PX_UNUSED(totalTestsExpected); + PX_UNUSED(totalTestsReal); +#endif + + Vec3V boxLocalMotion = V3LoadU(localMotion); + Vec3V minClosestA = zeroV, minNormal = zeroV; + PxU32 minTriangleIndex = 0; + PxVec3 bestTriNormal(0.0f); + FloatV dist = FLoad(distance); + + const PsTransformV boxPos = loadTransformU(pose); + + bool status = false; + + const PxU32 idx = cachedIndex ? *cachedIndex : 0; + + for(PxU32 ii=0;ii<nbTris;ii++) + { + const PxU32 triangleIndex = getTriangleIndex(ii, idx); + + const Vec3V localV0 = V3LoadU(triangles[triangleIndex].verts[0]); + const Vec3V localV1 = V3LoadU(triangles[triangleIndex].verts[1]); + const Vec3V localV2 = V3LoadU(triangles[triangleIndex].verts[2]); + + const Vec3V triV0 = worldToBoxV.transform(localV0); + const Vec3V triV1 = worldToBoxV.transform(localV1); + const Vec3V triV2 = worldToBoxV.transform(localV2); + + const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1)); + + if(doBackfaceCulling && FAllGrtrOrEq(V3Dot(triNormal, boxLocalMotion), zero)) // backface culling + continue; + + const FloatV dp0 = V3Dot(triV0, boxDir); + const FloatV dp1 = V3Dot(triV1, boxDir); + const FloatV dp2 = V3Dot(triV2, boxDir); + + const FloatV dp = FMin(dp0, FMin(dp1, dp2)); + + const Vec3V dpV = V3Merge(dp0, dp1, dp2); + + const FloatV temp1 = FAdd(boxRadiusV, dist); + const BoolV con0 = FIsGrtr(dp, temp1); + const BoolV con1 = V3IsGrtr(zeroV, dpV); + + if(BAllEqTTTT(BOr(con0, con1))) + continue; + +#if PX_DEBUG + totalTestsReal++; +#endif + + TriangleV triangleV(triV0, triV1, triV2); + + FloatV lambda; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex<TriangleV> convexA(triangleV); + LocalConvex<BoxV> convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + if(gjkRaycastPenetration<LocalConvex<TriangleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, boxLocalMotion, lambda, normal, closestA, inflation, false)) + { + //hitCount++; + + if(FAllGrtrOrEq(zero, lambda)) + { + hit.distance = 0.0f; + hit.faceIndex = triangleIndex; + hit.normal = -unitDir; + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::eDISTANCE; + return true; + } + + dist = FMul(dist, lambda); + boxLocalMotion = V3Scale(boxDir, dist); + minClosestA = closestA; + minNormal = normal; + minTriangleIndex = triangleIndex; + V3StoreU(triNormal, bestTriNormal); + status = true; + if(hitFlags & PxHitFlag::eMESH_ANY) + break; + } + } + + if(!status) + return false; + + hit.faceIndex = minTriangleIndex; + const Vec3V destNormal = V3Neg(V3Normalize(boxPos.rotate(minNormal))); + const Vec3V destWorldPointA = boxPos.transform(minClosestA); + V3StoreU(destNormal, hit.normal); + V3StoreU(destWorldPointA, hit.position); + FStore(dist, &hit.distance); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(hit.normal, meshBothSides, doubleSided, bestTriNormal, unitDir)) + hit.normal = -hit.normal; + + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eDISTANCE; + return true; +} + +bool sweepCapsule_SphereGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_PlaneGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_CapsuleGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_BoxGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_BoxGeom_Precise (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_ConvexGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_MeshGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_HeightFieldGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); + +bool sweepBox_SphereGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_SphereGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_PlaneGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_CapsuleGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_CapsuleGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_BoxGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_BoxGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_ConvexGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_MeshGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_HeightFieldGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_HeightFieldGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS); + +bool sweepConvex_SphereGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_PlaneGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_CapsuleGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_BoxGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_ConvexGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_MeshGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_HeightFieldGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); + +static bool sweepCapsule_HeightfieldUnregistered(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(lss); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} +static bool sweepBox_HeightfieldUnregistered(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(box); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} +static bool sweepConvex_HeightfieldUnregistered(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(convexGeom); + PX_UNUSED(convexPose); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} + +Gu::GeomSweepFuncs gGeomSweepFuncs = +{ + { + sweepCapsule_SphereGeom, + sweepCapsule_PlaneGeom, + sweepCapsule_CapsuleGeom, + sweepCapsule_BoxGeom, + sweepCapsule_ConvexGeom, + sweepCapsule_MeshGeom, + sweepCapsule_HeightfieldUnregistered + }, + { + sweepCapsule_SphereGeom, + sweepCapsule_PlaneGeom, + sweepCapsule_CapsuleGeom, + sweepCapsule_BoxGeom_Precise, + sweepCapsule_ConvexGeom, + sweepCapsule_MeshGeom , + sweepCapsule_HeightfieldUnregistered + }, + { + sweepBox_SphereGeom, + sweepBox_PlaneGeom, + sweepBox_CapsuleGeom, + sweepBox_BoxGeom, + sweepBox_ConvexGeom, + sweepBox_MeshGeom, + sweepBox_HeightfieldUnregistered + }, + { + sweepBox_SphereGeom_Precise, + sweepBox_PlaneGeom, + sweepBox_CapsuleGeom_Precise, + sweepBox_BoxGeom_Precise, + sweepBox_ConvexGeom, + sweepBox_MeshGeom, + sweepBox_HeightfieldUnregistered + }, + { + sweepConvex_SphereGeom, // 0 + sweepConvex_PlaneGeom, // 1 + sweepConvex_CapsuleGeom, // 2 + sweepConvex_BoxGeom, // 3 + sweepConvex_ConvexGeom, // 4 + sweepConvex_MeshGeom, // 5 + sweepConvex_HeightfieldUnregistered // 6 + } +}; + +PX_PHYSX_COMMON_API const GeomSweepFuncs& Gu::getSweepFuncTable() +{ + return gGeomSweepFuncs; +} + +void registerHeightFields_Sweeps() +{ + gGeomSweepFuncs.capsuleMap[PxGeometryType::eHEIGHTFIELD] = sweepCapsule_HeightFieldGeom; + gGeomSweepFuncs.preciseCapsuleMap[PxGeometryType::eHEIGHTFIELD] = sweepCapsule_HeightFieldGeom; + gGeomSweepFuncs.boxMap[PxGeometryType::eHEIGHTFIELD] = sweepBox_HeightFieldGeom; + gGeomSweepFuncs.preciseBoxMap[PxGeometryType::eHEIGHTFIELD] = sweepBox_HeightFieldGeom_Precise; + gGeomSweepFuncs.convexMap[PxGeometryType::eHEIGHTFIELD] = sweepConvex_HeightFieldGeom; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/GuSweepTests.h b/PhysX_3.4/Source/GeomUtils/src/GuSweepTests.h new file mode 100644 index 00000000..8b42c3b9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/GuSweepTests.h @@ -0,0 +1,136 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_TESTS_H +#define GU_SWEEP_TESTS_H + +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" +#include "PxGeometry.h" + +namespace physx +{ + class PxConvexMeshGeometry; + class PxCapsuleGeometry; + class PxTriangle; + class PxBoxGeometry; + + // PT: TODO: unify this with raycast calls (names and order of params) + + // PT: we use defines to be able to quickly change the signature of all sweep functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom geometry object to sweep against + // \param[in] pose pose of geometry object + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + + // PT: sweep parameters for capsule + #define GU_CAPSULE_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxCapsuleGeometry& capsuleGeom_, const PxTransform& capsulePose_, const Gu::Capsule& lss, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + + // PT: sweep parameters for box + #define GU_BOX_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxBoxGeometry& boxGeom_, const PxTransform& boxPose_, const Gu::Box& box, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + + // PT: sweep parameters for convex + #define GU_CONVEX_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + namespace Gu + { + class Capsule; + class Box; + + // PT: function pointer for Geom-indexed capsule sweep functions + // See GU_CAPSULE_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepCapsuleFunc) (GU_CAPSULE_SWEEP_FUNC_PARAMS); + + // PT: function pointer for Geom-indexed box sweep functions + // See GU_BOX_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepBoxFunc) (GU_BOX_SWEEP_FUNC_PARAMS); + + // PT: function pointer for Geom-indexed box sweep functions + // See GU_CONVEX_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepConvexFunc) (GU_CONVEX_SWEEP_FUNC_PARAMS); + + // PT: typedef for bundles of all sweep functions, i.e. the function tables themselves (indexed by geom-type). + typedef SweepCapsuleFunc GeomSweepCapsuleTable [PxGeometryType::eGEOMETRY_COUNT]; + typedef SweepBoxFunc GeomSweepBoxTable [PxGeometryType::eGEOMETRY_COUNT]; + typedef SweepConvexFunc GeomSweepConvexTable [PxGeometryType::eGEOMETRY_COUNT]; + + struct GeomSweepFuncs + { + GeomSweepCapsuleTable capsuleMap; + GeomSweepCapsuleTable preciseCapsuleMap; + GeomSweepBoxTable boxMap; + GeomSweepBoxTable preciseBoxMap; + GeomSweepConvexTable convexMap; + }; + // PT: grabs all sweep function tables at once (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomSweepFuncs& getSweepFuncTable(); + + // PT: signature for sweep-vs-triangles functions. + // We use defines to be able to quickly change the signature of all sweep functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] nbTris number of triangles in input array + // \param[in] triangles array of triangles to sweep the shape against + // \param[in] doubleSided true if input triangles are double-sided + // \param[in] x geom to sweep against input triangles + // \param[in] pose pose of geom x + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] hit hit result + // \param[in] cachedIndex optional initial triangle index (must be <nbTris) + // \param[in] inflation optional inflation value for swept shape + // \param[in] hitFlags query behavior flags + #define GU_SWEEP_TRIANGLES_FUNC_PARAMS(x) PxU32 nbTris, const PxTriangle* triangles, bool doubleSided, \ + const x& geom, const PxTransform& pose, \ + const PxVec3& unitDir, const PxReal distance, \ + PxSweepHit& hit, const PxU32* cachedIndex, \ + const PxReal inflation, PxHitFlags hitFlags + + bool sweepCapsuleTriangles (GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxCapsuleGeometry)); + bool sweepBoxTriangles (GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)); + bool sweepBoxTriangles_Precise (GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)); + + } // namespace Gu +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepConvexMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepConvexMesh.cpp new file mode 100644 index 00000000..3d3d9e36 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepConvexMesh.cpp @@ -0,0 +1,733 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "Ps.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecTriangle.h" +#include "GuVecShrunkConvexHull.h" +#include "GuVecShrunkBox.h" +#include "GuGJKRaycast.h" +#include "GuCCDSweepConvexMesh.h" +#include "GuHeightFieldUtil.h" +#include "PsInlineArray.h" +#include "GuEntityReport.h" +#include "PxContact.h" +#include "GuDistancePointTriangle.h" +#include "GuBox.h" +#include "GuInternal.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuMidphaseInterface.h" +#include "GuTriangleVertexPointers.h" + + + +namespace physx +{ +namespace Gu +{ + +PxReal SweepShapeTriangle(GU_TRIANGLE_SWEEP_METHOD_ARGS); + +using namespace Ps::aos; + +namespace +{ +struct AccumCallback: public MeshHitCallback<PxRaycastHit> +{ + PX_NOCOPY(AccumCallback) + public: + + Ps::InlineArray<PxU32, 64>& mResult; + AccumCallback(Ps::InlineArray<PxU32, 64>& result) + : MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE), + mResult(result) + { + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal&, const PxU32*) + { + mResult.pushBack(hit.faceIndex); + return true; + } +}; + +// PT: TODO: refactor with MidPhaseQueryLocalReport +struct EntityReportContainerCallback : public EntityReport<PxU32> +{ + Ps::InlineArray<PxU32, 64>& container; + EntityReportContainerCallback(Ps::InlineArray<PxU32,64>& container_) : container(container_) + { + container.forceSize_Unsafe(0); + } + virtual ~EntityReportContainerCallback() {} + + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; i<nb; i++) + container.pushBack(indices[i]); + return true; + } +private: + EntityReportContainerCallback& operator=(const EntityReportContainerCallback&); +}; + +class ConvexTriangles +{ +public: + ConvexTriangles(const PxTriangleMeshGeometryLL& shapeMesh, + const Cm::FastVertex2ShapeScaling& skew, // object is not copied, beware! + const PxU32* trigsInGroup, + PxU32 numTrigsInGroup, + PxU32* trigIndexDestBuffer);//trigIndexDestBuffer should be at least numTrigsInGroup long. + + void getBounds(PxBounds3& bounds, const physx::PxTransform& transform) const; + + //non-virtuals: + PX_FORCE_INLINE const TriangleMesh* getMeshData() const { return shapeMesh.meshData; } + + PxVec3 getPolygonNormal(PxU32 index) const; + + +private: + ConvexTriangles& operator=(const ConvexTriangles&); + void calcCenterAndBounds(const physx::PxTransform& transform) const; + + const PxTriangleMeshGeometryLL& shapeMesh; + const Cm::FastVertex2ShapeScaling& mVertex2ShapeSkew; + const PxU32* trigsInGroup; + PxU32 numTrigsInGroup; + PxU32* trigIndexDestBuffer; + mutable HullPolygonData selectedPolygon; + + mutable PxBounds3 bounds; + mutable PxVec3 mCenter; //average of vertices rather than center of bounds! + mutable bool haveCenterAndBounds; +}; + +ConvexTriangles::ConvexTriangles(const PxTriangleMeshGeometryLL& md, + const Cm::FastVertex2ShapeScaling& skew, + const PxU32* tg, PxU32 ntg, PxU32 * tb) +: shapeMesh(md), mVertex2ShapeSkew(skew), trigsInGroup(tg), numTrigsInGroup(ntg), trigIndexDestBuffer(tb), bounds(PxBounds3::empty()), mCenter(0.0f), haveCenterAndBounds(false) +{ +} + + +void ConvexTriangles::getBounds(PxBounds3& b, const physx::PxTransform& transform) const +{ + calcCenterAndBounds(transform); + b = bounds; +} + +void ConvexTriangles::calcCenterAndBounds(const physx::PxTransform& transform) const //computes bounds in shape space +{ + //NOTE: we have code that does this in a loop inside PxcContactHullMeshPenetrationFallback -- a relatively expensive weighted average of the faces. + //see if we really need to be that expensive! + + //shound be done in ctor: + PX_ASSERT(bounds.isEmpty()); + PX_ASSERT(mCenter.isZero()); + + for (PxU32 i = 0; i < numTrigsInGroup; i++) + { + const PxU32 triangleIndex = trigsInGroup[i]; + PxVec3 v0l, v1l, v2l; + TriangleVertexPointers::getTriangleVerts(getMeshData(), triangleIndex, v0l, v1l, v2l); + + //TODO: this does a lot of redundant work because shared vertices get tested multiple times. + //Still, its not a lot of work so any overhead of optimized data access may not be worth it. + + //gotta take bounds in shape space because building it in vertex space and transforming it out would skew it. + + //unrolled loop of 3 + const PxVec3 v0 = transform.transform(mVertex2ShapeSkew * v0l); + mCenter += v0; + bounds.include(v0); + + const PxVec3 v1 = transform.transform(mVertex2ShapeSkew * v1l); + mCenter += v1; + bounds.include(v1); + + const PxVec3 v2 = transform.transform(mVertex2ShapeSkew * v2l); + mCenter += v2; + bounds.include(v2); + } + + mCenter *= 1.0f / (numTrigsInGroup * 3); + haveCenterAndBounds = true; +} + +PxVec3 ConvexTriangles::getPolygonNormal(PxU32 index) const +{ + PX_ASSERT(index < numTrigsInGroup); + const PxU32 triangleIndex = trigsInGroup[index]; + PxVec3 v0l, v1l, v2l; + TriangleVertexPointers::getTriangleVerts(getMeshData(), triangleIndex, v0l, v1l, v2l); + const bool flipNormal = mVertex2ShapeSkew.flipsNormal(); + const PxVec3 t0 = mVertex2ShapeSkew * v0l; + const PxVec3 t1 = mVertex2ShapeSkew * (flipNormal ? v2l : v1l); + const PxVec3 t2 = mVertex2ShapeSkew * (flipNormal ? v1l : v2l); + const PxVec3 v0 = t0 - t1; + const PxVec3 v1 = t0 - t2; + const PxVec3 nor = v0.cross(v1); + return nor.getNormalized(); +} + +} + + + +PxReal SweepAnyShapeHeightfield(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(toiEstimate); + HeightFieldUtil hfUtil(shape1.mGeometry->get<const physx::PxHeightFieldGeometryLL>()); + + Ps::InlineArray<PxU32,64> tempContainer; + + EntityReportContainerCallback callback(tempContainer); + + PxVec3 trA = transform0.p - lastTm0.p; + PxVec3 trB = transform1.p - lastTm1.p; + + PxVec3 relTr = trA - trB; + PxVec3 halfRelTr = relTr * 0.5f; + + const PxVec3 ext = shape0.mExtents + halfRelTr.abs() + PxVec3(restDistance); + const PxVec3 cent = shape0.mCenter + halfRelTr; + + PxBounds3 bounds0(cent - ext, cent + ext); + + hfUtil.overlapAABBTriangles(transform1, bounds0, GuHfQueryFlags::eWORLD_SPACE, &callback); + + Ps::Array<PxU32> orderedContainer(tempContainer.size()); + + Ps::Array<PxU32> distanceEntries(tempContainer.size()); + + PxU32* orderedList = orderedContainer.begin(); + PxF32* distances = reinterpret_cast<PxF32*>(distanceEntries.begin()); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + PxReal minTOI = PX_MAX_REAL; + + PxU32 numTrigs = tempContainer.size(); + PxU32* trianglesIndices = tempContainer.begin(); + + PxU32 count = 0; + for(PxU32 a = 0; a < numTrigs; ++a) + { + PxTriangle tri; + hfUtil.getTriangle(shape1.mPrevTransform, tri, 0, 0, trianglesIndices[a], true, true); + + PxVec3 resultNormal = -(tri.verts[1]-tri.verts[0]).cross(tri.verts[2]-tri.verts[0]); + resultNormal.normalize(); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(tri.verts[0]); + bounds.include(tri.verts[1]); + bounds.include(tri.verts[2]); + + PxF32 toi = sweepAABBAABB(origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + PxU32 index = 0; + if(toi <= 1.f) + { + for(PxU32 b = count; b > 0; --b) + { + if(distances[b-1] <= toi) + { + //shuffle down and swap + index = b; + break; + } + PX_ASSERT(b > 0); + PX_ASSERT(b < numTrigs); + distances[b] = distances[b-1]; + orderedList[b] = orderedList[b-1]; + } + PX_ASSERT(index < numTrigs); + orderedList[index] = trianglesIndices[a]; + distances[index] = toi; + count++; + } + } + } + + + + worldNormal = PxVec3(PxReal(0)); + worldPoint = PxVec3(PxReal(0)); + Cm::FastVertex2ShapeScaling idScale; + PxU32 ccdFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + PxVec3 sphereCenter(shape0.mPrevTransform.p); + PxF32 inSphereRadius = shape0.mFastMovingThreshold; + PxF32 inRadSq = inSphereRadius * inSphereRadius; + + + PxVec3 sphereCenterInTr1 = transform1.transformInv(sphereCenter); + PxVec3 sphereCenterInTr1T0 = transform1.transformInv(lastTm0.p); + + PxVec3 tempWorldNormal(0.f), tempWorldPoint(0.f); + + for (PxU32 ti = 0; ti < count; ti++) + { + PxTriangle tri; + hfUtil.getTriangle(lastTm1, tri, 0, 0, orderedList[ti], false, false); + + PxVec3 resultNormal, resultPoint; + + TriangleV triangle(V3LoadU(tri.verts[0]), V3LoadU(tri.verts[1]), V3LoadU(tri.verts[2])); + + //do sweep + + PxReal res = SweepShapeTriangle( + *shape0.mGeometry, *shape1.mGeometry, transform0, transform1, lastTm0, lastTm1, restDistance, + resultNormal, resultPoint, Cm::FastVertex2ShapeScaling(), triangle, + 0.f); + + if(res <= 0.f) + { + res = 0.f; + + const PxVec3 v0 = tri.verts[1] - tri.verts[0] ; + const PxVec3 v1 = tri.verts[2] - tri.verts[0]; + + //Now we have a 0 TOI, lets see if the in-sphere hit it! + + PxF32 distanceSq = distancePointTriangleSquared( sphereCenterInTr1, tri.verts[0], v0, v1); + + if(distanceSq < inRadSq) + { + const PxVec3 nor = v0.cross(v1); + const PxF32 distance = PxSqrt(distanceSq); + res = distance - inSphereRadius; + const PxF32 d = nor.dot(tri.verts[0]); + const PxF32 dd = nor.dot(sphereCenterInTr1T0); + if((dd - d) > 0.f) + { + //back side, penetration + res = -(2.f * inSphereRadius - distance); + } + } + } + + if (res < minTOI) + { + const PxVec3 v0 = tri.verts[1] - tri.verts[0] ; + const PxVec3 v1 = tri.verts[2] - tri.verts[0]; + + PxVec3 resultNormal1 = v0.cross(v1); + resultNormal1.normalize(); + //if(norDotRel > 1e-6f) + { + tempWorldNormal = resultNormal1; + tempWorldPoint = resultPoint; + minTOI = res; + ccdFaceIndex = orderedList[ti]; + } + } + + } + + worldNormal = transform1.rotate(tempWorldNormal); + worldPoint = tempWorldPoint; + + outCCDFaceIndex = ccdFaceIndex; + + return minTOI; +} + + +PxReal SweepEstimateAnyShapeHeightfield(GU_SWEEP_ESTIMATE_ARGS) +{ + HeightFieldUtil hfUtil(shape1.mGeometry->get<const physx::PxHeightFieldGeometryLL>()); + + Ps::InlineArray<PxU32,64> tempContainer; + + EntityReportContainerCallback callback(tempContainer); + + PxVec3 trA = transform0.p - lastTr0.p; + PxVec3 trB = transform1.p - lastTr1.p; + + PxVec3 relTr = trA - trB; + PxVec3 halfRelTr = relTr * 0.5f; + + const PxVec3 extents = shape0.mExtents + halfRelTr.abs() + PxVec3(restDistance); + const PxVec3 center = shape0.mCenter + halfRelTr; + + + PxBounds3 bounds0(center - extents, center + extents); + + + hfUtil.overlapAABBTriangles(transform1, bounds0, GuHfQueryFlags::eWORLD_SPACE, &callback); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents; + + PxReal minTOI = PX_MAX_REAL; + + PxU32 numTrigs = tempContainer.size(); + PxU32* trianglesIndices = tempContainer.begin(); + + for(PxU32 a = 0; a < numTrigs; ++a) + { + + PxTriangle tri; + hfUtil.getTriangle(shape1.mPrevTransform, tri, 0, 0, trianglesIndices[a], true, true); + + + + PxVec3 resultNormal = -(tri.verts[1]-tri.verts[0]).cross(tri.verts[2]-tri.verts[0]); + resultNormal.normalize(); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(tri.verts[0]); + bounds.include(tri.verts[1]); + bounds.include(tri.verts[2]); + + PxF32 toi = sweepAABBAABB(origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + minTOI = PxMin(minTOI, toi); + } + } + + return minTOI; +} + + + +PxReal SweepAnyShapeMesh(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(toiEstimate); + // this is the trimesh midphase for convex vs mesh sweep. shape0 is the convex shape. + + // Get actual shape data + const PxTriangleMeshGeometryLL& shapeMesh = shape1.mGeometry->get<const PxTriangleMeshGeometryLL>(); + + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + /*---------------------------------------------------*\ + | + | STEP1: OPCODE Geometry collection + | + \*---------------------------------------------------*/ + + PxVec3 trA = transform0.p - lastTm0.p; + PxVec3 trB = transform1.p - lastTm1.p; + + PxVec3 relTr = trA - trB; + PxVec3 unitDir = relTr; + PxReal length = unitDir.normalize(); + + PxMat33 matRot(lastTm0.q); + + + //1) Compute the swept bounds + Box sweptBox; + computeSweptBox(sweptBox, shape0.mExtents, shape0.mCenter, matRot, unitDir, length); + + Box vertexSpaceBox; + if (shapeMesh.scale.isIdentity()) + vertexSpaceBox = transformBoxOrthonormal(sweptBox, transform1.getInverse()); + else + computeVertexSpaceOBB(vertexSpaceBox, sweptBox, transform1, shapeMesh.scale); + + + vertexSpaceBox.extents += PxVec3(restDistance); + + Ps::InlineArray<PxU32, 64> tempContainer; + + AccumCallback callback(tempContainer); + + // AP scaffold: early out opportunities, should probably use fat raycast + Midphase::intersectOBB(shapeMesh.meshData, vertexSpaceBox, callback, true); + + if (tempContainer.size() == 0) + return PX_MAX_REAL; + + // Intersection found, fetch triangles + PxU32 numTrigs = tempContainer.size(); + const PxU32* triangleIndices = tempContainer.begin(); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + Ps::InlineArray<PxU32, 64> orderedContainer; + orderedContainer.resize(tempContainer.size()); + + Ps::InlineArray<PxU32, 64> distanceEntries; + distanceEntries.resize(tempContainer.size()); + + PxU32* orderedList = orderedContainer.begin(); + PxF32* distances = reinterpret_cast<PxF32*>(distanceEntries.begin()); + + PxReal minTOI = PX_MAX_REAL; + + + PxU32 count = 0; + for(PxU32 a = 0; a < numTrigs; ++a) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &triangleIndices[a], 1, &unused); + + PxVec3 resultNormal = -transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + PxBounds3 bounds; + convexPartOfMesh1.getBounds(bounds, lastTm1); + //OK, we have all 3 vertices, now calculate bounds... + + PxF32 toi = sweepAABBAABB(origin, extent, bounds.getCenter(), bounds.getExtents() + PxVec3(0.02f, 0.02f, 0.02f), trA, trB); + + PxU32 index = 0; + if(toi <= 1.f) + { + for(PxU32 b = count; b > 0; --b) + { + if(distances[b-1] <= toi) + { + //shuffle down and swap + index = b; + break; + } + PX_ASSERT(b > 0); + PX_ASSERT(b < numTrigs); + distances[b] = distances[b-1]; + orderedList[b] = orderedList[b-1]; + } + PX_ASSERT(index < numTrigs); + orderedList[index] = triangleIndices[a]; + distances[index] = toi; + count++; + } + } + } + + + + PxVec3 tempWorldNormal(0.f), tempWorldPoint(0.f); + + Cm::FastVertex2ShapeScaling idScale; + PxU32 ccdFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + PxVec3 sphereCenter(lastTm1.p); + PxF32 inSphereRadius = shape0.mFastMovingThreshold; + //PxF32 inRadSq = inSphereRadius * inSphereRadius; + + PxVec3 sphereCenterInTransform1 = transform1.transformInv(sphereCenter); + + PxVec3 sphereCenterInTransform0p = transform1.transformInv(lastTm0.p); + + + for (PxU32 ti = 0; ti < count /*&& PxMax(minTOI, 0.f) >= distances[ti]*/; ti++) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &orderedList[ti], 1, &unused); + + PxVec3 resultNormal, resultPoint, v0l, v1l, v2l; + TriangleVertexPointers::getTriangleVerts(shapeMesh.meshData, orderedList[ti], v0l, v1l, v2l); + const bool flipNormal = meshScaling.flipsNormal(); + + const PxVec3 v0 = meshScaling * v0l; + const PxVec3 v1 = meshScaling * (flipNormal ? v2l : v1l); + const PxVec3 v2 = meshScaling * (flipNormal ? v1l : v2l); + + TriangleV triangle(V3LoadU(v0), V3LoadU(v1), V3LoadU(v2)); + + //do sweep + PxReal res = SweepShapeTriangle( + *shape0.mGeometry, *shape1.mGeometry, transform0, transform1, lastTm0, lastTm1, restDistance, + resultNormal, resultPoint, Cm::FastVertex2ShapeScaling(), triangle, + 0.f); + + resultNormal = -resultNormal; + + if(res <= 0.f) + { + res = 0.f; + + PxF32 inRad = inSphereRadius + restDistance; + PxF32 inRadSq = inRad*inRad; + + const PxVec3 vv0 = v1 - v0 ; + const PxVec3 vv1 = v2 - v0; + const PxVec3 nor = vv0.cross(vv1); + + //Now we have a 0 TOI, lets see if the in-sphere hit it! + + PxF32 distanceSq = distancePointTriangleSquared( sphereCenterInTransform1, v0, vv0, vv1); + + if(distanceSq < inRadSq) + { + const PxF32 distance = PxSqrt(distanceSq); + res = distance - inRad; + const PxF32 d = nor.dot(v0); + const PxF32 dd = nor.dot(sphereCenterInTransform0p); + if((dd - d) < 0.f) + { + //back side, penetration + res = -(2.f * inRad - distance); + } + } + PX_ASSERT(PxIsFinite(res)); + resultNormal = convexPartOfMesh1.getPolygonNormal(0); + } + + if (res < minTOI) + { + tempWorldNormal = resultNormal;//convexPartOfMesh1.getPolygonNormal(0);//transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + tempWorldPoint = resultPoint; + minTOI = res; + ccdFaceIndex = orderedList[ti]; + } + + } + + worldNormal = transform1.rotate(tempWorldNormal); + worldPoint = tempWorldPoint; + outCCDFaceIndex = ccdFaceIndex; + return minTOI; +} + + +/** +\brief This code performs a conservative estimate of the TOI of a shape v mesh. +*/ +PxReal SweepEstimateAnyShapeMesh(GU_SWEEP_ESTIMATE_ARGS) +{ + // this is the trimesh midphase for convex vs mesh sweep. shape0 is the convex shape. + // Get actual shape data + const PxTriangleMeshGeometryLL& shapeMesh = shape1.mGeometry->get<const PxTriangleMeshGeometryLL>(); + + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + + /*---------------------------------------------------*\ + | + | STEP1: OPCODE Geometry collection + | + \*---------------------------------------------------*/ + + PxVec3 trA = transform0.p - lastTr0.p; + PxVec3 trB = transform1.p - lastTr1.p; + + PxVec3 relTr = trA - trB; + PxVec3 unitDir = relTr; + PxReal length = unitDir.normalize(); + + PxMat33 matRot(lastTr0.q); + + //1) Compute the swept bounds + Box sweptBox; + computeSweptBox(sweptBox, shape0.mExtents, shape0.mCenter, matRot, unitDir, length); + + Box vertexSpaceBox; + computeVertexSpaceOBB(vertexSpaceBox, sweptBox, transform1, shapeMesh.scale); + + vertexSpaceBox.extents += PxVec3(restDistance); + + // TODO: implement a cached mode that fetches the trigs from a cache rather than per opcode if there is little motion. + + struct CB : MeshHitCallback<PxRaycastHit> + { + PxReal minTOI; + PxReal sumFastMovingThresh; + const PxTriangleMeshGeometryLL& shapeMesh; + const Cm::FastVertex2ShapeScaling& meshScaling; + const PxVec3& relTr; + const PxVec3& trA; + const PxVec3& trB; + const PxTransform& transform1; + const PxVec3& origin; + const PxVec3& extent; + + CB(PxReal aSumFast, const PxTriangleMeshGeometryLL& aShapeMesh, const Cm::FastVertex2ShapeScaling& aMeshScaling, + const PxVec3& aRelTr, const PxVec3& atrA, const PxVec3& atrB, const PxTransform& aTransform1, const PxVec3& aOrigin, const PxVec3& aExtent) + : MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE), + sumFastMovingThresh(aSumFast), shapeMesh(aShapeMesh), meshScaling(aMeshScaling), relTr(aRelTr), trA(atrA), trB(atrB), + transform1(aTransform1), origin(aOrigin), extent(aExtent) + { + minTOI = PX_MAX_REAL; + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal& shrunkMaxT, const PxU32*) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &hit.faceIndex, 1, &unused); + PxVec3 resultNormal = -transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + if(relTr.dot(resultNormal) >= sumFastMovingThresh) + { + PxBounds3 bounds; + convexPartOfMesh1.getBounds(bounds, transform1); + //OK, we have all 3 vertices, now calculate bounds... + + PX_ASSERT(trB.x == 0.f); + PX_ASSERT(trB.y == 0.f); + PX_ASSERT(trB.z == 0.f); + PxF32 toi = sweepAABBAABB( + origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + minTOI = PxMin(minTOI, toi); + shrunkMaxT = minTOI; + } + + return (minTOI > 0.0f); // stop traversal if minTOI == 0.0f + } + + void operator=(const CB&) {} + }; + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + CB callback(fastMovingThreshold, shapeMesh, meshScaling, relTr, trA, trB, transform1, origin, extent); + Midphase::intersectOBB(shapeMesh.meshData, vertexSpaceBox, callback, true); + + return callback.minTOI; +} + + +} +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepConvexMesh.h b/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepConvexMesh.h new file mode 100644 index 00000000..8c520610 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepConvexMesh.h @@ -0,0 +1,178 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_CCD_SWEEP_H +#define GU_CCD_SWEEP_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecTransform.h" +#include "GuGeometryUnion.h" +#include "CmScaling.h" + +#define GU_TRIANGLE_SWEEP_METHOD_ARGS \ + const Gu::GeometryUnion& shape0, \ + const Gu::GeometryUnion& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTm0, \ + const PxTransform& lastTm1, \ + PxReal restDistance, \ + PxVec3& worldNormal, \ + PxVec3& worldPoint, \ + const Cm::FastVertex2ShapeScaling& meshScaling, \ + Gu::TriangleV& triangle, \ + const PxF32 toiEstimate + + +#define GU_SWEEP_METHOD_ARGS \ + const Gu::CCDShape& shape0, \ + const Gu::CCDShape& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTm0, \ + const PxTransform& lastTm1, \ + PxReal restDistance, \ + PxVec3& worldNormal, \ + PxVec3& worldPoint, \ + const PxF32 toiEstimate, \ + PxU32& outCCDFaceIndex, \ + const PxReal fastMovingThreshold + + +#define GU_SWEEP_ESTIMATE_ARGS \ + const CCDShape& shape0, \ + const CCDShape& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTr0, \ + const PxTransform& lastTr1, \ + const PxReal restDistance, \ + const PxReal fastMovingThreshold + + +#define GU_SWEEP_METHOD_ARGS_UNUSED \ + const Gu::CCDShape& /*shape0*/, \ + const Gu::CCDShape& /*shape1*/, \ + const PxTransform& /*transform0*/, \ + const PxTransform& /*transform1*/, \ + const PxTransform& /*lastTm0*/, \ + const PxTransform& /*lastTm1*/, \ + PxReal /*restDistance*/, \ + PxVec3& /*worldNormal*/, \ + PxVec3& /*worldPoint*/, \ + const PxF32 /*toiEstimate*/, \ + PxU32& /*outCCDFaceIndex*/, \ + const PxReal /*fastMovingThreshold*/ + +namespace physx +{ +namespace Gu +{ + struct CCDShape + { + const Gu::GeometryUnion* mGeometry; + PxReal mFastMovingThreshold; //The CCD threshold for this shape + PxTransform mPrevTransform; //This shape's previous transform + PxTransform mCurrentTransform; //This shape's current transform + PxVec3 mExtents; //The extents of this shape's AABB + PxVec3 mCenter; //The center of this shape's AABB + PxU32 mUpdateCount; //How many times this shape has been updated in the CCD. This is correlated with the CCD body's update count. + }; + + PX_FORCE_INLINE PxF32 sweepAABBAABB(const PxVec3& centerA, const PxVec3& extentsA, const PxVec3& centerB, const PxVec3& extentsB, const PxVec3& trA, const PxVec3& trB) + { + //Sweep 2 AABBs against each other, return the TOI when they hit else PX_MAX_REAL if they don't hit + const PxVec3 cAcB = centerA - centerB; + const PxVec3 sumExtents = extentsA + extentsB; + + //Initial hit + if(PxAbs(cAcB.x) <= sumExtents.x && + PxAbs(cAcB.y) <= sumExtents.y && + PxAbs(cAcB.z) <= sumExtents.z) + return 0.f; + + //No initial hit - perform the sweep + const PxVec3 relTr = trB - trA; + PxF32 tfirst = 0.f; + PxF32 tlast = 1.f; + + const PxVec3 aMax = centerA + extentsA; + const PxVec3 aMin = centerA - extentsA; + const PxVec3 bMax = centerB + extentsB; + const PxVec3 bMin = centerB - extentsB; + + const PxF32 eps = 1e-6f; + + for(PxU32 a = 0; a < 3; ++a) + { + if(relTr[a] < -eps) + { + if(bMax[a] < aMin[a]) + return PX_MAX_REAL; + if(aMax[a] < bMin[a]) + tfirst = PxMax((aMax[a] - bMin[a])/relTr[a], tfirst); + if(bMax[a] > aMin[a]) + tlast = PxMin((aMin[a] - bMax[a])/relTr[a], tlast); + } + else if(relTr[a] > eps) + { + if(bMin[a] > aMax[a]) + return PX_MAX_REAL; + if(bMax[a] < aMin[a]) + tfirst = PxMax((aMin[a] - bMax[a])/relTr[a], tfirst); + if(aMax[a] > bMin[a]) + tlast = PxMin((aMax[a] - bMin[a])/relTr[a], tlast); + } + else + { + if(bMax[a] < aMin[a] || bMin[a] > aMax[a]) + return PX_MAX_REAL; + } + + //No hit + if(tfirst > tlast) + return PX_MAX_REAL; + } + //There was a hit so return the TOI + return tfirst; + } + + PX_PHYSX_COMMON_API PxReal SweepShapeShape(GU_SWEEP_METHOD_ARGS); + + PX_PHYSX_COMMON_API PxReal SweepEstimateAnyShapeHeightfield(GU_SWEEP_ESTIMATE_ARGS); + + PX_PHYSX_COMMON_API PxReal SweepEstimateAnyShapeMesh(GU_SWEEP_ESTIMATE_ARGS); + + +} +} +#endif + diff --git a/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepPrimitives.cpp b/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepPrimitives.cpp new file mode 100644 index 00000000..0a8f55f3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/ccd/GuCCDSweepPrimitives.cpp @@ -0,0 +1,290 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "Ps.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecTriangle.h" +#include "GuVecShrunkConvexHull.h" +#include "GuVecShrunkBox.h" +#include "GuGJKRaycast.h" +#include "GuCCDSweepConvexMesh.h" +#include "GuGJKType.h" + +namespace physx +{ +namespace Gu +{ + +using namespace Ps::aos; + +template<typename Geom> PX_FORCE_INLINE PxReal getRadius(const PxGeometry&) +{ + return 0; +} + +template<> PX_FORCE_INLINE PxReal getRadius<CapsuleV>(const PxGeometry& g) +{ + PX_ASSERT(g.getType() == PxGeometryType::eCAPSULE || g.getType() == PxGeometryType::eSPHERE); + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxSphereGeometry, radius) == PX_OFFSET_OF(PxCapsuleGeometry, radius)); + return static_cast<const PxSphereGeometry&>(g).radius; +} + + + +template<class ConvexA, class ConvexB> +static PxReal CCDSweep(ConvexA& a, ConvexB& b, const PxTransform& transform0, const PxTransform& transform1, const PxTransform& lastTm0, const PxTransform& lastTm1, + const Ps::aos::FloatV& toiEstimate, PxVec3& worldPoint, PxVec3& worldNormal, PxReal inflation = 0.f) +{ + PX_UNUSED(toiEstimate); //KS - TODO - can we use this again? + using namespace Ps::aos; + + const Vec3V zero = V3Zero(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&lastTm0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&lastTm1.p.x); + + const PsTransformV tr0(p0, q0); + const PsTransformV tr1(p1, q1); + + const PsMatTransformV aToB(tr1.transformInv(tr0)); + + const Vec3V trans0p = V3LoadU(transform0.p); + const Vec3V trans1p = V3LoadU(transform1.p); + const Vec3V trA = V3Sub(trans0p, p0); + const Vec3V trB = V3Sub(trans1p, p1); + const Vec3V relTr = tr1.rotateInv(V3Sub(trB, trA)); + + FloatV lambda; + Vec3V closestA, normal; + const FloatV initialLambda = FZero(); + const RelativeConvex<ConvexA> convexA(a, aToB); + const LocalConvex<ConvexB> convexB(b); + if(gjkRaycastPenetration<RelativeConvex<ConvexA>, LocalConvex<ConvexB> >(convexA, convexB, aToB.p, initialLambda, zero, relTr, lambda, normal, closestA, inflation, true)) + { + //Adjust closestA because it will be on the surface of convex a in its initial position (s). If the TOI > 0, we need to move + //the point along the sweep direction to get the world-space hit position. + PxF32 res; + FStore(lambda, &res); + closestA = V3ScaleAdd(trA, FMax(lambda, FZero()), tr1.transform(closestA)); + normal = tr1.rotate(normal); + + V3StoreU(normal, worldNormal); + V3StoreU(closestA, worldPoint); + return res; + } + return PX_MAX_REAL; +} + + + +// +// lookup table for geometry-vs-geometry sweeps +// + + +PxReal UnimplementedSweep (GU_SWEEP_METHOD_ARGS_UNUSED) +{ + return PX_MAX_REAL; //no impact +} + +template<typename Geom0, typename Geom1> +PxReal SweepGeomGeom(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(outCCDFaceIndex); + PX_UNUSED(fastMovingThreshold); + + const PxGeometry& g0 = shape0.mGeometry->getGeometry(); + const PxGeometry& g1 = shape1.mGeometry->getGeometry(); + + //Geom0 geom0(g0); + //Geom1 geom1(g1); + typename Shrink<Geom0>::Type geom0(g0); + typename Shrink<Geom1>::Type geom1(g1); + + return CCDSweep(geom0, geom1, transform0, transform1, lastTm0, lastTm1, FLoad(toiEstimate), worldPoint, worldNormal, restDistance+getRadius<Geom0>(g0)+getRadius<Geom1>(g1) ); +} + +typedef PxReal (*SweepMethod) (GU_SWEEP_METHOD_ARGS); + +PxReal SweepAnyShapeHeightfield(GU_SWEEP_METHOD_ARGS); +PxReal SweepAnyShapeMesh(GU_SWEEP_METHOD_ARGS); + +SweepMethod g_SweepMethodTable[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT] = +{ + //PxGeometryType::eSPHERE + { + SweepGeomGeom<CapsuleV, CapsuleV>, //PxGeometryType::eSPHERE + UnimplementedSweep, //PxGeometryType::ePLANE + SweepGeomGeom<CapsuleV, CapsuleV>, //PxGeometryType::eCAPSULE + SweepGeomGeom<CapsuleV, BoxV>, //PxGeometryType::eBOX + SweepGeomGeom<CapsuleV, ConvexHullV>, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD //TODO + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + UnimplementedSweep, //PxGeometryType::ePLANE + UnimplementedSweep, //PxGeometryType::eCAPSULE + UnimplementedSweep, //PxGeometryType::eBOX + UnimplementedSweep, //PxGeometryType::eCONVEXMESH + UnimplementedSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + SweepGeomGeom<CapsuleV, CapsuleV>, //PxGeometryType::eCAPSULE + SweepGeomGeom<CapsuleV, BoxV>, //PxGeometryType::eBOX + SweepGeomGeom<CapsuleV, ConvexHullV>, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + SweepGeomGeom<BoxV, BoxV>, //PxGeometryType::eBOX + SweepGeomGeom<BoxV, ConvexHullV>, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + SweepGeomGeom<ConvexHullV, ConvexHullV>, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + UnimplementedSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, +}; + + +PxReal SweepShapeShape(GU_SWEEP_METHOD_ARGS) +{ + PxGeometryType::Enum type0 = shape0.mGeometry->getType(); + PxGeometryType::Enum type1 = shape1.mGeometry->getType(); + + return g_SweepMethodTable[type0][type1](shape0, shape1, transform0, transform1, lastTm0, lastTm1, + restDistance, worldNormal, worldPoint, toiEstimate, outCCDFaceIndex, fastMovingThreshold); + +} + +// +// lookup table for sweeps agains triangles +// + +PxReal UnimplementedTriangleSweep(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(shape1); + PX_UNUSED(transform0); + PX_UNUSED(transform1); + PX_UNUSED(lastTm0); + PX_UNUSED(lastTm1); + PX_UNUSED(restDistance); + PX_UNUSED(worldNormal); + PX_UNUSED(worldPoint); + PX_UNUSED(meshScaling); + PX_UNUSED(triangle); + PX_UNUSED(toiEstimate); + + return 1e10f; //no impact +} + +template<typename Geom> +PxReal SweepGeomTriangles(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(meshScaling); + PX_UNUSED(shape1); + + const PxGeometry& g = shape0.getGeometry(); + //Geom geom(g); + typename Shrink<Geom>::Type geom(g); + + return CCDSweep<TriangleV, Geom>(triangle, geom, transform1, transform0, lastTm1, lastTm0, FLoad(toiEstimate), worldPoint, worldNormal, restDistance+getRadius<Geom>(g) ); +} + +typedef PxReal (*TriangleSweepMethod) (GU_TRIANGLE_SWEEP_METHOD_ARGS); +TriangleSweepMethod g_TriangleSweepMethodTable[PxGeometryType::eGEOMETRY_COUNT] = +{ + SweepGeomTriangles<CapsuleV>, //PxGeometryType::eSPHERE + UnimplementedTriangleSweep, //PxGeometryType::ePLANE + SweepGeomTriangles<CapsuleV>, //PxGeometryType::eCAPSULE + SweepGeomTriangles<BoxV>, //PxGeometryType::eBOX + SweepGeomTriangles<ConvexHullV>, //PxGeometryType::eCONVEXMESH + UnimplementedTriangleSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedTriangleSweep, //PxGeometryType::eHEIGHTFIELD +}; + +PxReal SweepShapeTriangle(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + const PxGeometryType::Enum type0 = shape0.getType(); + TriangleSweepMethod method = g_TriangleSweepMethodTable[type0]; + return method(shape0, shape1, transform0, transform1, lastTm0, lastTm1, restDistance, worldNormal, worldPoint, meshScaling, triangle, toiEstimate); +} + +} +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuBarycentricCoordinates.cpp b/PhysX_3.4/Source/GeomUtils/src/common/GuBarycentricCoordinates.cpp new file mode 100644 index 00000000..c7972a98 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuBarycentricCoordinates.cpp @@ -0,0 +1,87 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBarycentricCoordinates.h" + +using namespace physx; +using namespace Ps::aos; + +void Gu::barycentricCoordinates(const Vec3VArg p, const Vec3VArg a, const Vec3VArg b, FloatV& v) +{ + const Vec3V v0 = V3Sub(a, p); + const Vec3V v1 = V3Sub(b, p); + const Vec3V d = V3Sub(v1, v0); + const FloatV denominator = V3Dot(d, d); + const FloatV numerator = V3Dot(V3Neg(v0), d); + v = FDiv(numerator, denominator); +} + +void Gu::barycentricCoordinates(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, Ps::aos::FloatV& v, Ps::aos::FloatV& w) +{ + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + + const Vec3V n = V3Cross(ab, ac); + + const VecCrossV crossA = V3PrepareCross(V3Sub(a, p)); + const VecCrossV crossB = V3PrepareCross(V3Sub(b, p)); + const VecCrossV crossC = V3PrepareCross(V3Sub(c, p)); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + const FloatV totalArea =FAdd(va, FAdd(vb, vc)); + const FloatV zero = FZero(); + const FloatV denom = FSel(FIsEq(totalArea, zero), zero, FRecip(totalArea)); + v = FMul(vb, denom); + w = FMul(vc, denom); + +} + +/* + v0 = b - a; + v1 = c - a; + v2 = p - a; +*/ +void Gu::barycentricCoordinates(const Vec3VArg v0, const Vec3VArg v1, const Vec3VArg v2, FloatV& v, FloatV& w) +{ + const FloatV d00 = V3Dot(v0, v0); + const FloatV d01 = V3Dot(v0, v1); + const FloatV d11 = V3Dot(v1, v1); + const FloatV d20 = V3Dot(v2, v0); + const FloatV d21 = V3Dot(v2, v1); + const FloatV denom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + v = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), denom); + w = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), denom); +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuBarycentricCoordinates.h b/PhysX_3.4/Source/GeomUtils/src/common/GuBarycentricCoordinates.h new file mode 100644 index 00000000..fb4178af --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuBarycentricCoordinates.h @@ -0,0 +1,93 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BARYCENTRIC_COORDINATES_H +#define GU_BARYCENTRIC_COORDINATES_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + //calculate the barycentric coorinates for a point in a segment + void barycentricCoordinates(const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + Ps::aos::FloatV& v); + + //calculate the barycentric coorinates for a point in a triangle + void barycentricCoordinates(const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& v, + Ps::aos::FloatV& w); + + void barycentricCoordinates(const Ps::aos::Vec3VArg v0, + const Ps::aos::Vec3VArg v1, + const Ps::aos::Vec3VArg v2, + Ps::aos::FloatV& v, + Ps::aos::FloatV& w); + + PX_INLINE Ps::aos::BoolV isValidTriangleBarycentricCoord(const Ps::aos::FloatVArg v, const Ps::aos::FloatVArg w) + { + using namespace Ps::aos; + const FloatV zero = FNeg(FEps()); + const FloatV one = FAdd(FOne(), FEps()); + + const BoolV con0 = BAnd(FIsGrtrOrEq(v, zero), FIsGrtrOrEq(one, v)); + const BoolV con1 = BAnd(FIsGrtrOrEq(w, zero), FIsGrtrOrEq(one, w)); + const BoolV con2 = FIsGrtr(one, FAdd(v, w)); + return BAnd(con0, BAnd(con1, con2)); + } + + PX_INLINE Ps::aos::BoolV isValidTriangleBarycentricCoord2(const Ps::aos::Vec4VArg vwvw) + { + using namespace Ps::aos; + const Vec4V eps = V4Splat(FEps()); + const Vec4V zero =V4Neg(eps); + const Vec4V one = V4Add(V4One(), eps); + + const Vec4V v0v1v0v1 = V4PermXZXZ(vwvw); + const Vec4V w0w1w0w1 = V4PermYWYW(vwvw); + + const BoolV con0 = BAnd(V4IsGrtrOrEq(v0v1v0v1, zero), V4IsGrtrOrEq(one, v0v1v0v1)); + const BoolV con1 = BAnd(V4IsGrtrOrEq(w0w1w0w1, zero), V4IsGrtrOrEq(one, w0w1w0w1)); + const BoolV con2 = V4IsGrtr(one, V4Add(v0v1v0v1, w0w1w0w1)); + return BAnd(con0, BAnd(con1, con2)); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuBoxConversion.h b/PhysX_3.4/Source/GeomUtils/src/common/GuBoxConversion.h new file mode 100644 index 00000000..e125e682 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuBoxConversion.h @@ -0,0 +1,121 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOX_CONVERSION_H +#define GU_BOX_CONVERSION_H + +#include "GuBox.h" +#include "PsMathUtils.h" +#include "CmMatrix34.h" +#include "PsVecMath.h" + +namespace physx +{ + // PT: builds rot from quat. WARNING: writes 4 bytes after 'dst.rot'. + PX_FORCE_INLINE void buildFrom(Gu::Box& dst, const PxQuat& q) + { + using namespace Ps::aos; + const QuatV qV = V4LoadU(&q.x); + Vec3V column0, column1, column2; + QuatGetMat33V(qV, column0, column1, column2); + // PT: TODO: investigate if these overlapping stores are a problem + V4StoreU(Vec4V_From_Vec3V(column0), &dst.rot.column0.x); + V4StoreU(Vec4V_From_Vec3V(column1), &dst.rot.column1.x); + V4StoreU(Vec4V_From_Vec3V(column2), &dst.rot.column2.x); + } + + PX_FORCE_INLINE void buildFrom(Gu::Box& dst, const PxVec3& center, const PxVec3& extents, const PxQuat& q) + { + using namespace Ps::aos; + // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards + buildFrom(dst, q); + dst.center = center; + dst.extents = extents; + } + + PX_FORCE_INLINE void buildMatrixFromBox(Cm::Matrix34& mat34, const Gu::Box& box) + { + mat34.m = box.rot; + mat34.p = box.center; + } + + // SD: function is now the same as FastVertex2ShapeScaling::transformQueryBounds + // PT: lots of LHS in that one. TODO: revisit... + PX_INLINE Gu::Box transform(const Cm::Matrix34& transfo, const Gu::Box& box) + { + Gu::Box ret; + PxMat33& obbBasis = ret.rot; + + obbBasis.column0 = transfo.rotate(box.rot.column0 * box.extents.x); + obbBasis.column1 = transfo.rotate(box.rot.column1 * box.extents.y); + obbBasis.column2 = transfo.rotate(box.rot.column2 * box.extents.z); + + ret.center = transfo.transform(box.center); + ret.extents = Ps::optimizeBoundingBox(obbBasis); + return ret; + } + + PX_INLINE Gu::Box transformBoxOrthonormal(const Gu::Box& box, const PxTransform& t) + { + Gu::Box ret; + PxMat33& obbBasis = ret.rot; + obbBasis.column0 = t.rotate(box.rot.column0); + obbBasis.column1 = t.rotate(box.rot.column1); + obbBasis.column2 = t.rotate(box.rot.column2); + ret.center = t.transform(box.center); + ret.extents = box.extents; + return ret; + } + + /** + \brief recomputes the OBB after an arbitrary transform by a 4x4 matrix. + \param mtx [in] the transform matrix + \param obb [out] the transformed OBB + */ + PX_INLINE void rotate(const Gu::Box& src, const Cm::Matrix34& mtx, Gu::Box& obb) + { + // The extents remain constant + obb.extents = src.extents; + // The center gets x-formed + obb.center = mtx.transform(src.center); + // Combine rotations + obb.rot = mtx.m * src.rot; + } + +// PT: TODO: move this to a better place + PX_FORCE_INLINE void getInverse(PxMat33& dstRot, PxVec3& dstTrans, const PxMat33& srcRot, const PxVec3& srcTrans) + { + const PxMat33 invRot = srcRot.getInverse(); + dstTrans = invRot.transform(-srcTrans); + dstRot = invRot; + } + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuEdgeCache.h b/PhysX_3.4/Source/GeomUtils/src/common/GuEdgeCache.h new file mode 100644 index 00000000..8efad75a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuEdgeCache.h @@ -0,0 +1,85 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EDGECACHE_H +#define GU_EDGECACHE_H + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsHash.h" + +namespace physx +{ +namespace Gu +{ + class EdgeCache + { +#define NUM_EDGES_IN_CACHE 64 //must be power of 2. 32 lines result in 10% extra work (due to cache misses), 64 lines in 6% extra work, 128 lines in 4%. + public: + EdgeCache() + { + PxMemZero(cacheLines, NUM_EDGES_IN_CACHE*sizeof(CacheLine)); + } + + PxU32 hash(PxU32 key) const + { + return (NUM_EDGES_IN_CACHE - 1) & Ps::hash(key); //Only a 16 bit hash would be needed here. + } + + bool isInCache(PxU8 vertex0, PxU8 vertex1) + { + PX_ASSERT(vertex1 >= vertex0); + PxU16 key = PxU16((vertex0 << 8) | vertex1); + PxU32 h = hash(key); + CacheLine& cl = cacheLines[h]; + if (cl.fullKey == key) + { + return true; + } + else //cache the line now as it's about to be processed + { + cl.fullKey = key; + return false; + } + } + + private: + struct CacheLine + { + PxU16 fullKey; + }; + CacheLine cacheLines[NUM_EDGES_IN_CACHE]; +#undef NUM_EDGES_IN_CACHE + }; +} + +} + +#endif + diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuEdgeListData.h b/PhysX_3.4/Source/GeomUtils/src/common/GuEdgeListData.h new file mode 100644 index 00000000..dea26f16 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuEdgeListData.h @@ -0,0 +1,153 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EDGE_LIST_DATA_H +#define GU_EDGE_LIST_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + +/*! +NOTICE! + +This is a data-code separated version of PxPhysics::EdgeList. + +It is to be shared between high and low level code, so make sure both are recompiled +if any change is done here. +*/ + +// Flags +enum EdgeType +{ + PX_EDGE_UNDEFINED, + + PX_EDGE_BOUNDARY, //!< Edge belongs to a single triangle + PX_EDGE_INTERNAL, //!< Edge belongs to exactly two triangles + PX_EDGE_SINGULAR, //!< Edge belongs to three or more triangles + + PX_EDGE_FORCE_DWORD = 0x7fffffff +}; + +enum EdgeFlag +{ + PX_EDGE_ACTIVE = (1<<0) +}; + + +// Data + + +//! Basic edge-data +struct EdgeData +{ + PxU32 Ref0; //!< First vertex reference + PxU32 Ref1; //!< Second vertex reference +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeData) == 8); + + +//! Basic edge-data using 8-bit references +struct Edge8Data +{ + PxU8 Ref0; //!< First vertex reference + PxU8 Ref1; //!< Second vertex reference +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::Edge8Data) == 2); + + +//! A count/offset pair = an edge descriptor +struct EdgeDescData +{ + PxU16 Flags; + PxU16 Count; + PxU32 Offset; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeDescData) == 8); + + +//! Edge<->triangle mapping +struct EdgeTriangleData +{ + PxU32 mLink[3]; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeTriangleData) == 12); + + +struct EdgeListData +{ + // The edge list + PxU32 mNbEdges; //!< Number of edges in the list + Gu::EdgeData* mEdges; //!< List of edges + // Faces to edges + PxU32 mNbFaces; //!< Number of faces for which we have data + Gu::EdgeTriangleData* mEdgeFaces; //!< Array of edge-triangles referencing mEdges + // Edges to faces + Gu::EdgeDescData* mEdgeToTriangles; //!< An EdgeDesc structure for each edge + PxU32* mFacesByEdges; //!< A pool of face indices +}; +#if PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeListData) == 48); +#else +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeListData) == 24); +#endif + + +// Accessors + +enum +{ + MSH_EDGE_LINK_MASK = 0x0fffffff, + MSH_ACTIVE_EDGE_MASK = 0x80000000, + MSH_ACTIVE_VERTEX_MASK = 0x40000000 +}; + +class EdgeTriangleAC +{ +public: + PX_INLINE static PxU32 GetEdge01(const Gu::EdgeTriangleData& data) { return data.mLink[0] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge12(const Gu::EdgeTriangleData& data) { return data.mLink[1] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge20(const Gu::EdgeTriangleData& data) { return data.mLink[2] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge(const Gu::EdgeTriangleData& data, PxU32 i) { return data.mLink[i] & MSH_EDGE_LINK_MASK; } + + PX_INLINE static Ps::IntBool HasActiveEdge01(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[0] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge12(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[1] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge20(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[2] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge(const Gu::EdgeTriangleData& data, PxU32 i) { return Ps::IntBool(data.mLink[i] & MSH_ACTIVE_EDGE_MASK); } +}; + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuSeparatingAxes.cpp b/PhysX_3.4/Source/GeomUtils/src/common/GuSeparatingAxes.cpp new file mode 100644 index 00000000..79d2de3c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuSeparatingAxes.cpp @@ -0,0 +1,64 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSeparatingAxes.h" + +using namespace physx; + +union FloatInt +{ + float f; + PxU32 i; +}; + +bool Gu::SeparatingAxes::addAxis(const PxVec3& axis) +{ + PxU32 numAxes = getNumAxes(); + const PxVec3* PX_RESTRICT axes = getAxes(); + const PxVec3* PX_RESTRICT axes_end = axes + numAxes; + while(axes<axes_end) + { + if(PxAbs(axis.dot(*axes))>0.9999f) + return false; + axes++; + } + +#ifdef SEP_AXIS_FIXED_MEMORY + if(mNbAxes<SEP_AXIS_FIXED_MEMORY) + { + mAxes[mNbAxes++] = axis; + return true; + } + + return false; +#else + mAxes.pushBack(axis); + return true; +#endif +} diff --git a/PhysX_3.4/Source/GeomUtils/src/common/GuSeparatingAxes.h b/PhysX_3.4/Source/GeomUtils/src/common/GuSeparatingAxes.h new file mode 100644 index 00000000..208a9e1a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/common/GuSeparatingAxes.h @@ -0,0 +1,91 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SEPARATINGAXES_H +#define GU_SEPARATINGAXES_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ +namespace Gu +{ + // PT: this is a number of axes. Multiply by sizeof(PxVec3) for size in bytes. + #define SEP_AXIS_FIXED_MEMORY 256 + + // This class holds a list of potential separating axes. + // - the orientation is irrelevant so V and -V should be the same vector + // - the scale is irrelevant so V and n*V should be the same vector + // - a given separating axis should appear only once in the class +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API SeparatingAxes + { + public: + PX_INLINE SeparatingAxes() : mNbAxes(0) {} + + bool addAxis(const PxVec3& axis); + + PX_FORCE_INLINE const PxVec3* getAxes() const + { + return mAxes; + } + + PX_FORCE_INLINE PxU32 getNumAxes() const + { + return mNbAxes; + } + + PX_FORCE_INLINE void reset() + { + mNbAxes = 0; + } + + private: + PxU32 mNbAxes; + PxVec3 mAxes[SEP_AXIS_FIXED_MEMORY]; + }; +#if PX_VC + #pragma warning(pop) +#endif + + enum PxcSepAxisType + { + SA_NORMAL0, // Normal of object 0 + SA_NORMAL1, // Normal of object 1 + SA_EE // Cross product of edges + }; + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp new file mode 100644 index 00000000..3b12a326 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactBoxBox.cpp @@ -0,0 +1,703 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" +#include "CmMatrix34.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define MAX_NB_CTCS 8 + 12*5 + 6*4 +#define ABS_GREATER(x, y) (PxAbs(x) > (y)) +#define ABS_SMALLER_EQUAL(x, y) (PxAbs(x) <= (y)) +//#define AIR(x) ((PxU32&)(x)&SIGN_BITMASK) +//#define ABS_GREATER(x, y) (AIR(x) > IR(y)) +//#define ABS_SMALLER_EQUAL(x, y) (AIR(x) <= IR(y)) + +#if PX_X86 && !PX_OSX + + // Some float optimizations ported over from novodex. + + //returns non zero if the value is negative. + #define PXC_IS_NEGATIVE(x) (((PxU32&)(x)) & 0x80000000) + +#else + + //On most platforms using the integer rep is worse(produces LHSs) since the CPU has more registers. + + //returns non zero if the value is negative. + #define PXC_IS_NEGATIVE(x) ((x) < 0.0f) + +#endif + + +enum +{ + AXIS_A0, AXIS_A1, AXIS_A2, + AXIS_B0, AXIS_B1, AXIS_B2 +}; + +struct VertexInfo +{ + PxVec3 pos; + bool penetrate; + bool area; +}; + + +/*static PxI32 doBoxBoxContactGeneration(PxVec3 ctcPts[MAX_NB_CTCS], PxReal depths[MAX_NB_CTCS], PxVec3* ctcNrm, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance);*/ + +static PxI32 doBoxBoxContactGeneration(ContactBuffer& contactBuffer, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance); + +namespace physx +{ + +namespace Gu +{ +bool contactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + // Get actual shape data + const PxBoxGeometry& shapeBox0 = shape0.get<const PxBoxGeometry>(); + const PxBoxGeometry& shapeBox1 = shape1.get<const PxBoxGeometry>(); + + PxU32 pd = PxU32(cache.mPairData); + PxI32 Nb = doBoxBoxContactGeneration(contactBuffer, + shapeBox0.halfExtents, shapeBox1.halfExtents, + pd, + Cm::Matrix34(transform0), Cm::Matrix34(transform1), + params.mContactDistance); + + cache.mPairData = Ps::to8(pd); + + if(!Nb) + { + cache.mPairData = 0; // Mark as separated for temporal coherence + return false; // WARNING: the contact stream code below used to output stuff even for 0 contacts (!). Now we just return here. + } + return true; +} +}//Gu +}//physx + +// face => 4 vertices of a face of the cube (i.e. a quad) +static PX_FORCE_INLINE PxReal IsInYZ(const PxReal y, const PxReal z, const VertexInfo** PX_RESTRICT face) +{ + // Warning, indices have been remapped. We're now actually like this: + // + // 3+------+2 + // | | | + // | *--| + // | (y,z)| + // 0+------+1 + PxReal PreviousY = face[3]->pos.y; + PxReal PreviousZ = face[3]->pos.z; + + // Loop through quad vertices + for(PxI32 i=0; i<4; i++) + { + const PxReal CurrentY = face[i]->pos.y; + const PxReal CurrentZ = face[i]->pos.z; + + // |CurrentY - PreviousY y - PreviousY| + // |CurrentZ - PreviousZ z - PreviousZ| + // => similar to backface culling, check each one of the 4 triangles are consistent, in which case + // the point is within the parallelogram. + if((CurrentY - PreviousY)*(z - PreviousZ) - (CurrentZ - PreviousZ)*(y - PreviousY) >= 0.0f) return -1.0f; + + PreviousY = CurrentY; + PreviousZ = CurrentZ; + } + + PxReal x = face[0]->pos.x; + { + const PxReal ay = y - face[0]->pos.y; + const PxReal az = z - face[0]->pos.z; + + PxVec3 b = face[1]->pos - face[0]->pos; // ### could be precomputed ? + x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ? + + b = face[3]->pos - face[0]->pos; // ### could be precomputed ? + x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ? + } + + return x; +} + +// Test with respect to the quad defined by (0,-y1,-z1) and (0,y1,z1) +// +------+ y1 y +// | | | +// | * | | +// | | | +// +------+ -y1 *-----z +static PxI32 generateContacts(//PxVec3 ctcPts[], PxReal depths[], + ContactBuffer& contactBuffer, const PxVec3& contactNormal, + PxReal y1, PxReal z1, const PxVec3& box2, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance) +{ +// PxI32 NbContacts=0; + contactBuffer.reset(); + y1 += contactDistance; + z1 += contactDistance; + + const Cm::Matrix34 trans1to0 = transform0.getInverseRT() * transform1; + + VertexInfo vtx[8]; // The 8 cube vertices +// PxI32 i; + + // 6+------+7 + // /| /| + // / | / | + // / 4+---/--+5 + // 2+------+3 / y z + // | / | / | / + // |/ |/ |/ + // 0+------+1 *---x + + { + const PxVec3 ex = trans1to0.m.column0 * box2.x; + const PxVec3 ey = trans1to0.m.column1 * box2.y; + const PxVec3 ez = trans1to0.m.column2 * box2.z; + + /* + vtx[0].pos = mat.pos - ex - ey - ez; + vtx[1].pos = mat.pos + ex - ey - ez; + vtx[2].pos = mat.pos - ex + ey - ez; + vtx[3].pos = mat.pos + ex + ey - ez; + vtx[4].pos = mat.pos - ex - ey + ez; + vtx[5].pos = mat.pos + ex - ey + ez; + vtx[6].pos = mat.pos - ex + ey + ez; + vtx[7].pos = mat.pos + ex + ey + ez; + */ + + // 12 vector ops = 12*3 = 36 FPU ops + vtx[0].pos = vtx[2].pos = vtx[4].pos = vtx[6].pos = trans1to0.p - ex; + vtx[1].pos = vtx[3].pos = vtx[5].pos = vtx[7].pos = trans1to0.p + ex; + + PxVec3 e = ey+ez; + vtx[0].pos -= e; + vtx[1].pos -= e; + vtx[6].pos += e; + vtx[7].pos += e; + + e = ey-ez; + vtx[2].pos += e; + vtx[3].pos += e; + vtx[4].pos -= e; + vtx[5].pos -= e; + } + + // Create vertex info for 8 vertices + for(PxU32 i=0; i<8; i++) + { + // Vertex suivant + VertexInfo& p = vtx[i]; + // test the point with respect to the x = 0 plane + // if(p.pos.x < 0) + if(p.pos.x < -contactDistance) //if(PXC_IS_NEGATIVE(p.pos.x)) + { + p.area = false; + p.penetrate = false; + continue; + } + + { + // we penetrated the quad plane + p.penetrate = true; + // test to see if we are in the quad + // PxAbs => thus we test Y with respect to -Y1 and +Y1 (same for Z) + // if(PxAbs(p->pos.y) <= y1 && PxAbs(p->pos.z) <= z1) + if(ABS_SMALLER_EQUAL(p.pos.y, y1) && ABS_SMALLER_EQUAL(p.pos.z, z1)) + { + // the point is inside the quad + p.area=true; + // Since we are testing with respect to x = 0, the penetration is directly the x coordinate. +// depths[NbContacts] = p.pos.x; + + // We take the vertex as the impact point +// ctcPts[NbContacts++] = p.pos; + contactBuffer.contact(p.pos, contactNormal, -p.pos.x); + } + else + { + p.area=false; + } + } + } + + // Teste 12 edges on the quad + static const PxI32 indices[]={ 0,1, 1,3, 3,2, 2,0, 4,5, 5,7, 7,6, 6,4, 0,4, 1,5, 2,6, 3,7, }; + const PxI32* runningLine = indices; + const PxI32* endLine = runningLine+24; + while(runningLine!=endLine) + { + // The two vertices of the current edge + const VertexInfo* p1 = &vtx[*runningLine++]; + const VertexInfo* p2 = &vtx[*runningLine++]; + + // Penetrate|Area|Penetrate|Area => 16 cases + + // We only take the edges that at least penetrated the quad's plane into account. + if(p1->penetrate || p2->penetrate) + // if(p1->penetrate + p2->penetrate) // One branch only + { + // If at least one of the two vertices is not in the quad... + if(!p1->area || !p2->area) + // if(!p1->area + !p2->area) // One branch only + { + // Test y + if(p1->pos.y > p2->pos.y) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; } + // Impact on the +Y1 edge of the quad + if(p1->pos.y < +y1 && p2->pos.y >= +y1) + // => a point under Y1, the other above + { + // Case 1 + PxReal a = (+y1 - p1->pos.y)/(p2->pos.y - p1->pos.y); + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y1, z); + contactBuffer.contact(PxVec3(x, y1, z), contactNormal, -x); + } + } + } + // Impact on the edge -Y1 of the quad + if(p1->pos.y < -y1 && p2->pos.y >= -y1) + { + // Case 2 + PxReal a = (-y1 - p1->pos.y)/(p2->pos.y - p1->pos.y); + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, -y1, z); + contactBuffer.contact(PxVec3(x, -y1, z), contactNormal, -x); + } + } + } + + // Test z + if(p1->pos.z > p2->pos.z) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; } + // Impact on the edge +Z1 of the quad + if(p1->pos.z < +z1 && p2->pos.z >= +z1) + { + // Case 3 + PxReal a = (+z1 - p1->pos.z)/(p2->pos.z - p1->pos.z); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y, z1); + contactBuffer.contact(PxVec3(x, y, z1), contactNormal, -x); + } + } + } + // Impact on the edge -Z1 of the quad + if(p1->pos.z < -z1 && p2->pos.z >= -z1) + { + // Case 4 + PxReal a = (-z1 - p1->pos.z)/(p2->pos.z - p1->pos.z); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y, -z1); + contactBuffer.contact(PxVec3(x, y, -z1), contactNormal, -x); + } + } + } + } + + // The case where one point penetrates the plane, and the other is not in the quad. + if((!p1->penetrate && !p2->area) || (!p2->penetrate && !p1->area)) + { + // Case 5 + PxReal a = (-p1->pos.x)/(p2->pos.x - p1->pos.x); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { +// depths[NbContacts] = 0; +// ctcPts[NbContacts++] = PxVec3(0, y, z); + contactBuffer.contact(PxVec3(0, y, z), contactNormal, 0); + } + } + } + + } + } + + { + // 6 quads => 6 faces of the cube + static const PxI32 face[][4]={ {0,1,3,2}, {1,5,7,3}, {5,4,6,7}, {4,0,2,6}, {2,3,7,6}, {0,4,5,1} }; + PxI32 addflg=0; + for(PxU32 i=0; i<6 && addflg!=0x0f; i++) + { + const PxI32* p = face[i]; + const VertexInfo* q[4]; + if((q[0]=&vtx[p[0]])->penetrate && (q[1]=&vtx[p[1]])->penetrate && (q[2]=&vtx[p[2]])->penetrate && (q[3]=&vtx[p[3]])->penetrate) + { + if(!q[0]->area || !q[1]->area || !q[2]->area || !q[3]->area) + { + if(!(addflg&1)) { PxReal x = IsInYZ(-y1, -z1, q); if(x>=0.0f) { addflg|=1; contactBuffer.contact(PxVec3(x, -y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, -z1);*/ } } + if(!(addflg&2)) { PxReal x = IsInYZ(+y1, -z1, q); if(x>=0.0f) { addflg|=2; contactBuffer.contact(PxVec3(x, +y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, -z1);*/ } } + if(!(addflg&4)) { PxReal x = IsInYZ(-y1, +z1, q); if(x>=0.0f) { addflg|=4; contactBuffer.contact(PxVec3(x, -y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, +z1);*/ } } + if(!(addflg&8)) { PxReal x = IsInYZ(+y1, +z1, q); if(x>=0.0f) { addflg|=8; contactBuffer.contact(PxVec3(x, +y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, +z1);*/ } } + } + } + } + } + +// for(i=0; i<NbContacts; i++) + for(PxU32 i=0; i<contactBuffer.count; i++) +// ctcPts[i] = transform0.transform(ctcPts[i]); // local to world + contactBuffer.contacts[i].point = transform0.transform(contactBuffer.contacts[i].point); // local to world + + //PX_ASSERT(NbContacts); //if this did not make contacts then something went wrong in theory, but even the old code without distances had this flaw! +// return NbContacts; + return PxI32(contactBuffer.count); +} + +//static PxI32 doBoxBoxContactGeneration(PxVec3 ctcPts[MAX_NB_CTCS], PxReal depths[MAX_NB_CTCS], PxVec3* ctcNrm, +static PxI32 doBoxBoxContactGeneration(ContactBuffer& contactBuffer, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance) +{ + PxReal aafC[3][3]; // matrix C = A^T B, c_{ij} = Dot(A_i,B_j) + PxReal aafAbsC[3][3]; // |c_{ij}| + PxReal afAD[3]; // Dot(A_i,D) + + PxReal d1[6]; + PxReal overlap[6]; + + PxVec3 kD = transform1.p - transform0.p; + + const PxVec3& axis00 = transform0.m.column0; + const PxVec3& axis01 = transform0.m.column1; + const PxVec3& axis02 = transform0.m.column2; + const PxVec3& axis10 = transform1.m.column0; + const PxVec3& axis11 = transform1.m.column1; + const PxVec3& axis12 = transform1.m.column2; + + // Perform Class I tests + + aafC[0][0] = axis00.dot(axis10); + aafC[0][1] = axis00.dot(axis11); + aafC[0][2] = axis00.dot(axis12); + afAD[0] = axis00.dot(kD); + aafAbsC[0][0] = 1e-6f + PxAbs(aafC[0][0]); + aafAbsC[0][1] = 1e-6f + PxAbs(aafC[0][1]); + aafAbsC[0][2] = 1e-6f + PxAbs(aafC[0][2]); + d1[AXIS_A0] = afAD[0]; + PxReal d0 = extents0.x + extents1.x*aafAbsC[0][0] + extents1.y*aafAbsC[0][1] + extents1.z*aafAbsC[0][2]; + overlap[AXIS_A0] = d0 - PxAbs(d1[AXIS_A0]) + contactDistance; + if(PXC_IS_NEGATIVE(overlap[AXIS_A0])) return 0; + + aafC[1][0] = axis01.dot(axis10); + aafC[1][1] = axis01.dot(axis11); + aafC[1][2] = axis01.dot(axis12); + afAD[1] = axis01.dot(kD); + aafAbsC[1][0] = 1e-6f + PxAbs(aafC[1][0]); + aafAbsC[1][1] = 1e-6f + PxAbs(aafC[1][1]); + aafAbsC[1][2] = 1e-6f + PxAbs(aafC[1][2]); + d1[AXIS_A1] = afAD[1]; + d0 = extents0.y + extents1.x*aafAbsC[1][0] + extents1.y*aafAbsC[1][1] + extents1.z*aafAbsC[1][2]; + overlap[AXIS_A1] = d0 - PxAbs(d1[AXIS_A1]) + contactDistance; + if(PXC_IS_NEGATIVE(overlap[AXIS_A1])) return 0; + + aafC[2][0] = axis02.dot(axis10); + aafC[2][1] = axis02.dot(axis11); + aafC[2][2] = axis02.dot(axis12); + afAD[2] = axis02.dot(kD); + aafAbsC[2][0] = 1e-6f + PxAbs(aafC[2][0]); + aafAbsC[2][1] = 1e-6f + PxAbs(aafC[2][1]); + aafAbsC[2][2] = 1e-6f + PxAbs(aafC[2][2]); + d1[AXIS_A2] = afAD[2]; + d0 = extents0.z + extents1.x*aafAbsC[2][0] + extents1.y*aafAbsC[2][1] + extents1.z*aafAbsC[2][2]; + overlap[AXIS_A2] = d0 - PxAbs(d1[AXIS_A2]) + contactDistance; + if(PXC_IS_NEGATIVE(overlap[AXIS_A2])) return 0; + + // Perform Class II tests + + d1[AXIS_B0] = axis10.dot(kD); + d0 = extents1.x + extents0.x*aafAbsC[0][0] + extents0.y*aafAbsC[1][0] + extents0.z*aafAbsC[2][0]; + overlap[AXIS_B0] = d0 - PxAbs(d1[AXIS_B0]) + contactDistance; + if(PXC_IS_NEGATIVE(overlap[AXIS_B0])) return 0; + + d1[AXIS_B1] = axis11.dot(kD); + d0 = extents1.y + extents0.x*aafAbsC[0][1] + extents0.y*aafAbsC[1][1] + extents0.z*aafAbsC[2][1]; + overlap[AXIS_B1] = d0 - PxAbs(d1[AXIS_B1]) + contactDistance; + if(PXC_IS_NEGATIVE(overlap[AXIS_B1])) return 0; + + d1[AXIS_B2] = axis12.dot(kD); + d0 = extents1.z + extents0.x*aafAbsC[0][2] + extents0.y*aafAbsC[1][2] + extents0.z*aafAbsC[2][2]; + overlap[AXIS_B2] = d0 - PxAbs(d1[AXIS_B2]) + contactDistance; + if(PXC_IS_NEGATIVE(overlap[AXIS_B2])) return 0; + + // Perform Class III tests - we don't need to store distances for those ones. + // We only test those axes when objects are likely to be separated, i.e. when they where previously non-colliding. For stacks, we'll have + // to do full contact generation anyway, and those tests are useless - so we skip them. This is similar to what I did in Opcode. + if(!collisionData) // separated or first run + { + PxReal d = afAD[2]*aafC[1][0] - afAD[1]*aafC[2][0]; + d0 = contactDistance + extents0.y*aafAbsC[2][0] + extents0.z*aafAbsC[1][0] + extents1.y*aafAbsC[0][2] + extents1.z*aafAbsC[0][1]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[2]*aafC[1][1] - afAD[1]*aafC[2][1]; + d0 = contactDistance + extents0.y*aafAbsC[2][1] + extents0.z*aafAbsC[1][1] + extents1.x*aafAbsC[0][2] + extents1.z*aafAbsC[0][0]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[2]*aafC[1][2] - afAD[1]*aafC[2][2]; + d0 = contactDistance + extents0.y*aafAbsC[2][2] + extents0.z*aafAbsC[1][2] + extents1.x*aafAbsC[0][1] + extents1.y*aafAbsC[0][0]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[0]*aafC[2][0] - afAD[2]*aafC[0][0]; + d0 = contactDistance + extents0.x*aafAbsC[2][0] + extents0.z*aafAbsC[0][0] + extents1.y*aafAbsC[1][2] + extents1.z*aafAbsC[1][1]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[0]*aafC[2][1] - afAD[2]*aafC[0][1]; + d0 = contactDistance + extents0.x*aafAbsC[2][1] + extents0.z*aafAbsC[0][1] + extents1.x*aafAbsC[1][2] + extents1.z*aafAbsC[1][0]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[0]*aafC[2][2] - afAD[2]*aafC[0][2]; + d0 = contactDistance + extents0.x*aafAbsC[2][2] + extents0.z*aafAbsC[0][2] + extents1.x*aafAbsC[1][1] + extents1.y*aafAbsC[1][0]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[1]*aafC[0][0] - afAD[0]*aafC[1][0]; + d0 = contactDistance + extents0.x*aafAbsC[1][0] + extents0.y*aafAbsC[0][0] + extents1.y*aafAbsC[2][2] + extents1.z*aafAbsC[2][1]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[1]*aafC[0][1] - afAD[0]*aafC[1][1]; + d0 = contactDistance + extents0.x*aafAbsC[1][1] + extents0.y*aafAbsC[0][1] + extents1.x*aafAbsC[2][2] + extents1.z*aafAbsC[2][0]; + if(ABS_GREATER(d, d0)) return 0; + + d = afAD[1]*aafC[0][2] - afAD[0]*aafC[1][2]; + d0 = contactDistance + extents0.x*aafAbsC[1][2] + extents0.y*aafAbsC[0][2] + extents1.x*aafAbsC[2][1] + extents1.y*aafAbsC[2][0]; + if(ABS_GREATER(d, d0)) return 0; + } + + /* djs - tempUserData can be zero when it gets here + - maybe if there was no previous axis? + - which causes stack corruption, and thence a crash, in .NET + + PT: right! At first tempUserData wasn't ever supposed to be zero, but then I used that + value to mark separation of boxes, and forgot to update the code below. Now I think + the test is redundant with the one performed above, and the line could eventually + be merged in the previous block. I'll do that later when removing all the #defines. + */ + // NB: the "16" here has nothing to do with MAX_NB_CTCS. Don't touch. + if(collisionData) // if initialized & not previously separated + overlap[collisionData-1] *= 0.999f; // Favorise previous axis .999 is too little. + + PxReal minimum = PX_MAX_REAL; + PxI32 minIndex = 0; + + for(PxU32 i=AXIS_A0; i<6; i++) + { + PxReal d = overlap[i]; + + if(d>=0.0f && d<minimum) { minimum=d; minIndex=PxI32(i); } // >=0 !! otherwise bug at sep = 0 + } + + collisionData = PxU32(minIndex + 1); // Leave "0" for separation + +#if PX_X86 + const PxU32 sign = PXC_IS_NEGATIVE(d1[minIndex]); +#else + const PxU32 sign = PxU32(PXC_IS_NEGATIVE(d1[minIndex])); +#endif + Cm::Matrix34 trs; + PxVec3 ctcNrm; + + switch(minIndex) + { + default: + return 0; + + case AXIS_A0: +// *ctcNrm = axis00; + if(sign) + { + ctcNrm = axis00; + trs.m = transform0.m; + trs.p = transform0.p - extents0.x*axis00; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis00; + + trs.m.column0 = -axis00; + trs.m.column1 = -axis01; + trs.m.column2 = axis02; + trs.p = transform0.p + extents0.x*axis00; + } +// return generateContacts(ctcPts, depths, extents0.y, extents0.z, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.y, extents0.z, extents1, trs, transform1, contactDistance); + + case AXIS_A1: +// *ctcNrm = axis01; + trs.m.column2 = axis00; // Factored out + if(sign) + { + ctcNrm = axis01; + trs.m.column0 = axis01; + trs.m.column1 = axis02; + trs.p = transform0.p - extents0.y*axis01; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis01; + + trs.m.column0 = -axis01; + trs.m.column1 = -axis02; + trs.p = transform0.p + extents0.y*axis01; + } +// return generateContacts(ctcPts, depths, extents0.z, extents0.x, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.z, extents0.x, extents1, trs, transform1, contactDistance); + + case AXIS_A2: +// *ctcNrm = axis02; + trs.m.column2 = axis01; // Factored out + + if(sign) + { + ctcNrm = axis02; + trs.m.column0 = axis02; + trs.m.column1 = axis00; + trs.p = transform0.p - extents0.z*axis02; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis02; + + trs.m.column0 = -axis02; + trs.m.column1 = -axis00; + trs.p = transform0.p + extents0.z*axis02; + } +// return generateContacts(ctcPts, depths, extents0.x, extents0.y, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.x, extents0.y, extents1, trs, transform1, contactDistance); + + case AXIS_B0: +// *ctcNrm = axis10; + if(sign) + { + ctcNrm = axis10; + trs.m.column0 = -axis10; + trs.m.column1 = -axis11; + trs.m.column2 = axis12; + trs.p = transform1.p + extents1.x*axis10; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis10; + trs.m = transform1.m; + trs.p = transform1.p - extents1.x*axis10; + + } +// return generateContacts(ctcPts, depths, extents1.y, extents1.z, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.y, extents1.z, extents0, trs, transform0, contactDistance); + + case AXIS_B1: +// *ctcNrm = axis11; + trs.m.column2 = axis10; // Factored out + if(sign) + { + ctcNrm = axis11; + trs.m.column0 = -axis11; + trs.m.column1 = -axis12; + trs.p = transform1.p + extents1.y*axis11; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis11; + + trs.m.column0 = axis11; + trs.m.column1 = axis12; + trs.m.column2 = axis10; + trs.p = transform1.p - extents1.y*axis11; + } +// return generateContacts(ctcPts, depths, extents1.z, extents1.x, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.z, extents1.x, extents0, trs, transform0, contactDistance); + + case AXIS_B2: +// *ctcNrm = axis12; + trs.m.column2 = axis11; // Factored out + + if(sign) + { + ctcNrm = axis12; + trs.m.column0 = -axis12; + trs.m.column1 = -axis10; + trs.p = transform1.p + extents1.z*axis12; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis12; + + trs.m.column0 = axis12; + trs.m.column1 = axis10; + trs.p = transform1.p - extents1.z*axis12; + } + +// return generateContacts(ctcPts, depths, extents1.x, extents1.y, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.x, extents1.y, extents0, trs, transform0, contactDistance); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp new file mode 100644 index 00000000..75e737d4 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleBox.cpp @@ -0,0 +1,457 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactBuffer.h" +#include "GuIntersectionRayBox.h" +#include "GuDistanceSegmentBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "PsMathUtils.h" +#include "PsUtilities.h" +#include "GuGeometryUnion.h" +#include "GuBoxConversion.h" + +using namespace physx; +using namespace Gu; + +/*namespace Gu +{ +const PxU8* getBoxEdges(); +}*/ + +///////// + /*#include "CmRenderOutput.h" + #include "PxsContext.h" + static void gVisualizeBox(const Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat33 rot(box.base.column0, box.base.column1, box.base.column2); + PxMat44 m(rot, box.origin); + + DebugBox db(box.extent); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m; + out << db; + } + static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; + }*/ +///////// + + +static const PxReal fatBoxEdgeCoeff = 0.01f; + +static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) return true; // collision found + + return false; // no collision +} + + +static bool GuTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project box + PxReal Min1, Max1; + { + const PxReal BoxCen = box.center.dot(axis); + const PxReal BoxExt = + PxAbs(box.rot.column0.dot(axis)) * box.extents.x + + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; + + Min1 = BoxCen - BoxExt; + Max1 = BoxCen + BoxExt; + } + + // Test projections + if(max0<Min1 || Max1<min0) + return false; + + const PxReal d0 = max0 - Min1; + PX_ASSERT(d0>=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + + +static bool GuCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) +{ + PxVec3 Sep(PxReal(0)); + PxReal PenDepth = PX_MAX_REAL; + + // Test normals + for(PxU32 i=0;i<3;i++) + { + PxReal d; + if(!GuTestAxis(box.rot[i], segment, radius, box, d)) + return false; + + if(d<PenDepth) + { + PenDepth = d; + Sep = box.rot[i]; + } + } + + // Test edges + PxVec3 CapsuleAxis(segment.p1 - segment.p0); + CapsuleAxis = CapsuleAxis.getNormalized(); + for(PxU32 i=0;i<3;i++) + { + PxVec3 Cross = CapsuleAxis.cross(box.rot[i]); + if(!Ps::isAlmostZero(Cross)) + { + Cross = Cross.getNormalized(); + PxReal d; + if(!GuTestAxis(Cross, segment, radius, box, d)) + return false; + + if(d<PenDepth) + { + PenDepth = d; + Sep = Cross; + } + } + } + + const PxVec3 Witness = segment.computeCenter() - box.center; + + if(Sep.dot(Witness) < 0.0f) + Sep = -Sep; + + if(t) + *t = PenDepth; + if(pp) + *pp = Sep; + + return true; +} + + +static void GuGenerateVFContacts( ContactBuffer& contactBuffer, + // + const Segment& segment, + const PxReal radius, + // + const Box& worldBox, + // + const PxVec3& normal, + const PxReal contactDistance) +{ + const PxVec3 Max = worldBox.extents; + const PxVec3 Min = -worldBox.extents; + + const PxVec3 tmp2 = - worldBox.rot.transformTranspose(normal); + + const PxVec3* PX_RESTRICT Ptr = &segment.p0; + for(PxU32 i=0;i<2;i++) + { + const PxVec3& Pos = Ptr[i]; + + const PxVec3 tmp = worldBox.rot.transformTranspose(Pos - worldBox.center); + PxReal tnear, tfar; + int Res = intersectRayAABB(Min, Max, tmp, tmp2, tnear, tfar); + + if(Res!=-1 && tnear < radius + contactDistance) + { + contactBuffer.contact(Pos - tnear * normal, normal, tnear - radius); + } + } +} + + +// PT: this looks similar to PxcGenerateEEContacts2 but it is mandatory to properly handle thin capsules. +static void GuGenerateEEContacts( ContactBuffer& contactBuffer, + // + const Segment& segment, + const PxReal radius, + // + const Box& worldBox, + // + const PxVec3& normal) +{ + const PxU8* PX_RESTRICT Indices = getBoxEdges(); + + PxVec3 Pts[8]; + worldBox.computeBoxPoints(Pts); + + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + // PT: precomputed part of edge-edge intersection test +// const PxVec3 v1 = segment.p1 - segment.p0; + const PxVec3 v1 = s1 - s0; + PxPlane plane; + plane.n = v1.cross(normal); +// plane.d = -(plane.normal|segment.p0); + plane.d = -(plane.n.dot(s0)); + + PxU32 ii,jj; + Ps::closestAxis(plane.n, ii, jj); + + const float coeff = 1.0f /(v1[ii]*normal[jj]-v1[jj]*normal[ii]); + + + for(PxU32 i=0;i<12;i++) + { +// PxVec3 p1 = Pts[*Indices++]; +// PxVec3 p2 = Pts[*Indices++]; +// Ps::makeFatEdge(p1, p2, fatBoxEdgeCoeff); // PT: TODO: make fat segment instead + const PxVec3& p1 = Pts[*Indices++]; + const PxVec3& p2 = Pts[*Indices++]; + +// PT: keep original code in case something goes wrong +// PxReal dist; +// PxVec3 ip; +// if(intersectEdgeEdge(p1, p2, -normal, segment.p0, segment.p1, dist, ip)) +// contactBuffer.contact(ip, normal, - (radius + dist)); + + PxReal dist; + PxVec3 ip; + + + if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip)) +// if(intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip)) + { + contactBuffer.contact(ip-normal*dist, normal, - (radius + dist)); +// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable +// return; + } + } +} + + +static void GuGenerateEEContacts2( ContactBuffer& contactBuffer, + // + const Segment& segment, + const PxReal radius, + // + const Box& worldBox, + // + const PxVec3& normal, + const PxReal contactDistance) +{ + const PxU8* PX_RESTRICT Indices = getBoxEdges(); + + PxVec3 Pts[8]; + worldBox.computeBoxPoints(Pts); + + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + // PT: precomputed part of edge-edge intersection test +// const PxVec3 v1 = segment.p1 - segment.p0; + const PxVec3 v1 = s1 - s0; + PxPlane plane; + plane.n = -(v1.cross(normal)); +// plane.d = -(plane.normal|segment.p0); + plane.d = -(plane.n.dot(s0)); + + PxU32 ii,jj; + Ps::closestAxis(plane.n, ii, jj); + + const float coeff = 1.0f /(v1[jj]*normal[ii]-v1[ii]*normal[jj]); + + for(PxU32 i=0;i<12;i++) + { +// PxVec3 p1 = Pts[*Indices++]; +// PxVec3 p2 = Pts[*Indices++]; +// Ps::makeFatEdge(p1, p2, fatBoxEdgeCoeff); // PT: TODO: make fat segment instead + const PxVec3& p1 = Pts[*Indices++]; + const PxVec3& p2 = Pts[*Indices++]; + +// PT: keep original code in case something goes wrong +// PxReal dist; +// PxVec3 ip; +// bool contact = intersectEdgeEdge(p1, p2, normal, segment.p0, segment.p1, dist, ip); +// if(contact && dist < radius + contactDistance) +// contactBuffer.contact(ip, normal, dist - radius); + + PxReal dist; + PxVec3 ip; +// bool contact = intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip); + bool contact = intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip); + if(contact && dist < radius + contactDistance) + { + contactBuffer.contact(ip-normal*dist, normal, dist - radius); +// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable +// return; + } + } +} + +namespace physx +{ +namespace Gu +{ +bool contactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>(); + + // PT: TODO: move computations to local space + + // Capsule data + Segment worldSegment; + getCapsuleSegment(transform0, shapeCapsule, worldSegment); + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + // Box data + Box worldBox; + buildFrom(worldBox, transform1.p, shapeBox.halfExtents, transform1.q); + + // Collision detection + PxReal t; + PxVec3 onBox; + const PxReal squareDist = distanceSegmentBoxSquared(worldSegment.p0, worldSegment.p1, worldBox.center, worldBox.extents, worldBox.rot, &t, &onBox); + + if(squareDist >= inflatedRadius*inflatedRadius) + return false; + + PX_ASSERT(contactBuffer.count==0); + + if(squareDist != 0.0f) + { + // PT: the capsule segment doesn't intersect the box => distance-based version + const PxVec3 onSegment = worldSegment.getPointAt(t); + onBox = worldBox.center + worldBox.rot.transform(onBox); + + PxVec3 normal = onSegment - onBox; + PxReal normalLen = normal.magnitude(); + + if(normalLen > 0.0f) + { + normal *= 1.0f/normalLen; + + // PT: generate VF contacts for segment's vertices vs box + GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + GuGenerateEEContacts2(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); + + // PT: run VF case for box-vertex-vs-capsule only if we don't have any contact yet + if(!contactBuffer.count) + contactBuffer.contact(onBox, normal, sqrtf(squareDist) - shapeCapsule.radius); + } + else + { + // On linux we encountered the following: + // For a case where a segment endpoint lies on the surface of a box, the squared distance between segment and box was tiny but still larger than 0. + // However, the computation of the normal length was exactly 0. In that case we should have switched to the penetration based version so we do it now + // instead. + goto PenetrationBasedCode; + } + } + else + { + PenetrationBasedCode: + + // PT: the capsule segment intersects the box => penetration-based version + + // PT: compute penetration vector (MTD) + PxVec3 sepAxis; + PxReal depth; + if(!GuCapsuleOBBOverlap3(worldSegment, shapeCapsule.radius, worldBox, &depth, &sepAxis)) return false; + + // PT: generate VF contacts for segment's vertices vs box + GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis); + + if(!contactBuffer.count) + { + contactBuffer.contact(worldSegment.computeCenter(), sepAxis, -(shapeCapsule.radius + depth)); + return true; + } + } + return true; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp new file mode 100644 index 00000000..f2de1078 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleCapsule.cpp @@ -0,0 +1,155 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactBuffer.h" +#include "GuDistanceSegmentSegment.h" +#include "GuContactMethodImpl.h" +#include "GuInternal.h" +#include "GuGeometryUnion.h" + +using namespace physx; + +namespace physx +{ +namespace Gu +{ +bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom0 = shape0.get<const PxCapsuleGeometry>(); + const PxCapsuleGeometry& capsuleGeom1 = shape1.get<const PxCapsuleGeometry>(); + + // PT: get capsules in local space + PxVec3 dir[2]; + Segment segment[2]; + { + const PxVec3 capsuleLocalSegment0 = getCapsuleHalfHeightVector(transform0, capsuleGeom0); + const PxVec3 capsuleLocalSegment1 = getCapsuleHalfHeightVector(transform1, capsuleGeom1); + + const PxVec3 delta = transform1.p - transform0.p; + segment[0].p0 = capsuleLocalSegment0; + segment[0].p1 = -capsuleLocalSegment0; + dir[0] = -capsuleLocalSegment0*2.0f; + segment[1].p0 = capsuleLocalSegment1 + delta; + segment[1].p1 = -capsuleLocalSegment1 + delta; + dir[1] = -capsuleLocalSegment1*2.0f; + } + + // PT: compute distance between capsules' segments + PxReal s,t; + const PxReal squareDist = distanceSegmentSegmentSquared(segment[0], segment[1], &s, &t); + const PxReal radiusSum = capsuleGeom0.radius + capsuleGeom1.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + const PxReal inflatedSumSquared = inflatedSum*inflatedSum; + + if(squareDist >= inflatedSumSquared) + return false; + + // PT: TODO: optimize this away + PxReal segLen[2]; + segLen[0] = dir[0].magnitude(); + segLen[1] = dir[1].magnitude(); + + if (segLen[0]) dir[0] *= 1.0f / segLen[0]; + if (segLen[1]) dir[1] *= 1.0f / segLen[1]; + + if (PxAbs(dir[0].dot(dir[1])) > 0.9998f) //almost parallel, ca. 1 degree difference --> generate two contact points at ends + { + PxU32 numCons = 0; + + PxReal segLenEps[2]; + segLenEps[0] = segLen[0] * 0.001f;//0.1% error is ok. + segLenEps[1] = segLen[1] * 0.001f; + + //project the two end points of each onto the axis of the other and take those 4 points. + //we could also generate a single normal at the single closest point, but this would be 'unstable'. + + for (PxU32 destShapeIndex = 0; destShapeIndex < 2; destShapeIndex ++) + { + for (PxU32 startEnd = 0; startEnd < 2; startEnd ++) + { + const PxU32 srcShapeIndex = 1-destShapeIndex; + //project start/end of srcShapeIndex onto destShapeIndex. + PxVec3 pos[2]; + pos[destShapeIndex] = startEnd ? segment[srcShapeIndex].p1 : segment[srcShapeIndex].p0; + const PxReal p = dir[destShapeIndex].dot(pos[destShapeIndex] - segment[destShapeIndex].p0); + if (p >= -segLenEps[destShapeIndex] && p <= (segLen[destShapeIndex] + segLenEps[destShapeIndex])) + { + pos[srcShapeIndex] = p * dir[destShapeIndex] + segment[destShapeIndex].p0; + + PxVec3 normal = pos[1] - pos[0]; + + const PxReal normalLenSq = normal.magnitudeSquared(); + if (normalLenSq > 1e-6f && normalLenSq < inflatedSumSquared) + { + const PxReal distance = PxSqrt(normalLenSq); + normal *= 1.0f/distance; + PxVec3 point = pos[1] - normal * (srcShapeIndex ? capsuleGeom1 : capsuleGeom0).radius; + point += transform0.p; + contactBuffer.contact(point, normal, distance - radiusSum); + numCons++; + } + } + } + } + + if (numCons) //if we did not have contacts, then we may have the case where they are parallel, but are stacked end to end, in which case the old code will generate good contacts. + return true; + } + + // Collision response + PxVec3 pos1 = segment[0].getPointAt(s); + PxVec3 pos2 = segment[1].getPointAt(t); + + PxVec3 normal = pos1 - pos2; + + const PxReal normalLenSq = normal.magnitudeSquared(); + if (normalLenSq < 1e-6f) + { + // PT: TODO: revisit this. "FW" sounds old. + // Zero normal -> pick the direction of segment 0. + // Not always accurate but consistent with FW. + if (segLen[0] > 1e-6f) + normal = dir[0]; + else + normal = PxVec3(1.0f, 0.0f, 0.0f); + } + else + { + normal *= PxRecipSqrt(normalLenSq); + } + + pos1 += transform0.p; + contactBuffer.contact(pos1 - normal * capsuleGeom0.radius, normal, PxSqrt(squareDist) - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp new file mode 100644 index 00000000..981b76ea --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleConvex.cpp @@ -0,0 +1,587 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuConvexMesh.h" +#include "GuConvexHelper.h" +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuVecConvexHull.h" +#include "GuVecCapsule.h" +#include "GuInternal.h" +#include "GuGJK.h" +#include "GuGeometryUnion.h" + +using namespace physx; +using namespace Gu; + +/////////// +// #include "CmRenderOutput.h" +// #include "PxsContext.h" +// static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +// { +// PxMat44 m = PxMat44::identity(); +// +// Cm::RenderOutput& out = context.mRenderOutput; +// out << color << m << Cm::RenderOutput::LINES << a << b; +// } +/////////// + +static const PxReal fatConvexEdgeCoeff = 0.01f; + +static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip, float limit) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) + return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; + if(dist<limit) + return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) + return true; // collision found + + return false; // no collision +} + +static bool GuTestAxis(const PxVec3& axis, const Gu::Segment& segment, PxReal radius, + const PolygonalData& polyData, const Cm::FastVertex2ShapeScaling& scaling, + const Cm::Matrix34& worldTM, + PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project convex + PxReal Min1, Max1; + (polyData.mProjectHull)(polyData, axis, worldTM, scaling, Min1, Max1); + + // Test projections + if(max0<Min1 || Max1<min0) + return false; + + const PxReal d0 = max0 - Min1; + PX_ASSERT(d0>=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static bool GuCapsuleConvexOverlap(const Gu::Segment& segment, PxReal radius, + const PolygonalData& polyData, + const Cm::FastVertex2ShapeScaling& scaling, + const PxTransform& transform, + PxReal* t, PxVec3* pp, bool isSphere) +{ + // TODO: + // - test normal & edge in same loop + // - local space + // - use precomputed face value + // - optimize projection + + PxVec3 Sep(0,0,0); + PxReal PenDepth = PX_MAX_REAL; + + PxU32 nbPolys = polyData.mNbPolygons; + const Gu::HullPolygonData* polys = polyData.mPolygons; + + const Cm::Matrix34 worldTM(transform); + + // Test normals + for(PxU32 i=0;i<nbPolys;i++) + { + const Gu::HullPolygonData& poly = polys[i]; + const PxPlane& vertSpacePlane = poly.mPlane; + + const PxVec3 worldNormal = worldTM.rotate(vertSpacePlane.n); + + PxReal d; + if(!GuTestAxis(worldNormal, segment, radius, polyData, scaling, worldTM, d)) + return false; + + if(d<PenDepth) + { + PenDepth = d; + Sep = worldNormal; + } + } + + // Test edges + if(!isSphere) + { + PxVec3 CapsuleAxis(segment.p1 - segment.p0); + CapsuleAxis = CapsuleAxis.getNormalized(); + for(PxU32 i=0;i<nbPolys;i++) + { + const Gu::HullPolygonData& poly = polys[i]; + const PxPlane& vertSpacePlane = poly.mPlane; + + const PxVec3 worldNormal = worldTM.rotate(vertSpacePlane.n); + + PxVec3 Cross = CapsuleAxis.cross(worldNormal); + if(!Ps::isAlmostZero(Cross)) + { + Cross = Cross.getNormalized(); + PxReal d; + if(!GuTestAxis(Cross, segment, radius, polyData, scaling, worldTM, d)) + return false; + + if(d<PenDepth) + { + PenDepth = d; + Sep = Cross; + } + } + } + } + + const PxVec3 Witness = segment.computeCenter() - transform.transform(polyData.mCenter); + + if(Sep.dot(Witness) < 0.0f) + Sep = -Sep; + + if(t) *t = PenDepth; + if(pp) *pp = Sep; + + return true; +} + + + + + +static bool raycast_convexMesh2( const PolygonalData& polyData, + const PxVec3& vrayOrig, const PxVec3& vrayDir, + PxReal maxDist, PxF32& t) +{ + PxU32 nPolys = polyData.mNbPolygons; + const Gu::HullPolygonData* PX_RESTRICT polys = polyData.mPolygons; + + /* + Purely convex planes based algorithm + Iterate all planes of convex, with following rules: + * determine of ray origin is inside them all or not. + * planes parallel to ray direction are immediate early out if we're on the outside side (plane normal is sep axis) + * else + - for all planes the ray direction "enters" from the front side, track the one furthest along the ray direction (A) + - for all planes the ray direction "exits" from the back side, track the one furthest along the negative ray direction (B) + if the ray origin is outside the convex and if along the ray, A comes before B, the directed line stabs the convex at A + */ + PxReal latestEntry = -FLT_MAX; + PxReal earliestExit = FLT_MAX; + + while(nPolys--) + { + const Gu::HullPolygonData& poly = *polys++; + const PxPlane& vertSpacePlane = poly.mPlane; + + const PxReal distToPlane = vertSpacePlane.distance(vrayOrig); + const PxReal dn = vertSpacePlane.n.dot(vrayDir); + const PxReal distAlongRay = -distToPlane/dn; + + if (dn > 1E-7f) //the ray direction "exits" from the back side + { + earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay); + } + else if (dn < -1E-7f) //the ray direction "enters" from the front side + { +/* if (distAlongRay > latestEntry) + { + latestEntry = distAlongRay; + }*/ + latestEntry = physx::intrinsics::selectMax(latestEntry, distAlongRay); + } + else + { + //plane normal and ray dir are orthogonal + if(distToPlane > 0.0f) + return false; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex! + } + } + + if(latestEntry < earliestExit && latestEntry != -FLT_MAX && latestEntry < maxDist-1e-5f) + { + t = latestEntry; + return true; + } + return false; +} + +// PT: version based on Gu::raycast_convexMesh to handle scaling, but modified to make sure it works when ray starts inside the convex +static void GuGenerateVFContacts2(ContactBuffer& contactBuffer, + // + const PxTransform& convexPose, + const PolygonalData& polyData, // Convex data + const PxMeshScale& scale, + // + PxU32 nbPts, + const PxVec3* PX_RESTRICT points, + const PxReal radius, // Capsule's radius + // + const PxVec3& normal, + const PxReal contactDistance) +{ + PX_ASSERT(PxAbs(normal.magnitudeSquared()-1)<1e-4f); + + //scaling: transform the ray to vertex space + const Cm::Matrix34 world2vertexSkew = scale.getInverse() * convexPose.getInverse(); + + const PxVec3 vrayDir = world2vertexSkew.rotate( -normal ); + + const PxReal maxDist = contactDistance + radius; + + for(PxU32 i=0;i<nbPts;i++) + { + const PxVec3& rayOrigin = points[i]; + + const PxVec3 vrayOrig = world2vertexSkew.transform( rayOrigin ); + PxF32 t; + if(raycast_convexMesh2(polyData, vrayOrig, vrayDir, maxDist, t)) + { + contactBuffer.contact(rayOrigin - t * normal, normal, t - radius); + } + } +} + +static void GuGenerateEEContacts( ContactBuffer& contactBuffer, + // + const Gu::Segment& segment, + const PxReal radius, + const PxReal contactDistance, + // + const PolygonalData& polyData, + const PxTransform& transform, + const Cm::FastVertex2ShapeScaling& scaling, + // + const PxVec3& normal) +{ + PxU32 numPolygons = polyData.mNbPolygons; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData.mPolygons; + const PxU8* PX_RESTRICT vertexData = polyData.mPolygonVertexRefs; + + ConvexEdge edges[512]; + PxU32 nbEdges = findUniqueConvexEdges(512, edges, numPolygons, polygons, vertexData); + + // + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatConvexEdgeCoeff); + + // PT: precomputed part of edge-edge intersection test +// const PxVec3 v1 = segment.p1 - segment.p0; + const PxVec3 v1 = s1 - s0; + PxPlane plane; + plane.n = v1.cross(normal); +// plane.d = -(plane.normal|segment.p0); + plane.d = -(plane.n.dot(s0)); + + PxU32 ii,jj; + Ps::closestAxis(plane.n, ii, jj); + + const float coeff = 1.0f /(v1[ii]*normal[jj]-v1[jj]*normal[ii]); + + // + + const PxVec3* PX_RESTRICT verts = polyData.mVerts; + for(PxU32 i=0;i<nbEdges;i++) + { + const PxU8 vi0 = edges[i].vref0; + const PxU8 vi1 = edges[i].vref1; + +// PxVec3 p1 = transform.transform(verts[vi0]); +// PxVec3 p2 = transform.transform(verts[vi1]); +// Ps::makeFatEdge(p1, p2, fatConvexEdgeCoeff); // PT: TODO: make fat segment instead + const PxVec3 p1 = transform.transform(scaling * verts[vi0]); + const PxVec3 p2 = transform.transform(scaling * verts[vi1]); + + PxReal dist; + PxVec3 ip; +// if(intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip)) +// if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip, -FLT_MAX)) + if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip, -radius-contactDistance)) +// if(intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, normal, p1, p2, dist, ip, 0)) + { + contactBuffer.contact(ip-normal*dist, normal, - (radius + dist)); +// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable +// return; + } + } +} + +static void GuGenerateEEContacts2b(ContactBuffer& contactBuffer, + // + const Gu::Segment& segment, + const PxReal radius, + // + const Cm::Matrix34& transform, + const PolygonalData& polyData, + const Cm::FastVertex2ShapeScaling& scaling, + // + const PxVec3& normal, + const PxReal contactDistance) +{ + // TODO: + // - local space + + const PxVec3 localDir = transform.rotateTranspose(normal); + PxU32 polyIndex = (polyData.mSelectClosestEdgeCB)(polyData, scaling, localDir); + + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatConvexEdgeCoeff); + + // PT: precomputed part of edge-edge intersection test +// const PxVec3 v1 = segment.p1 - segment.p0; + const PxVec3 v1 = s1 - s0; + PxPlane plane; + plane.n = -(v1.cross(normal)); +// plane.d = -(plane.normal|segment.p0); + plane.d = -(plane.n.dot(s0)); + + PxU32 ii,jj; + Ps::closestAxis(plane.n, ii, jj); + + const float coeff = 1.0f /(v1[jj]*normal[ii]-v1[ii]*normal[jj]); + // + + const PxVec3* PX_RESTRICT verts = polyData.mVerts; + + const Gu::HullPolygonData& polygon = polyData.mPolygons[polyIndex]; + const PxU8* PX_RESTRICT vRefBase = polyData.mPolygonVertexRefs + polygon.mVRef8; + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { +// const PxVec3 p1 = transform.transform(verts[vRefBase[a]]); +// const PxVec3 p2 = transform.transform(verts[vRefBase[b]]); + const PxVec3 p1 = transform.transform(scaling * verts[vRefBase[a]]); + const PxVec3 p2 = transform.transform(scaling * verts[vRefBase[b]]); + + PxReal dist; + PxVec3 ip; +// bool contact = intersectEdgeEdgePreca(segment.p0, segment.p1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip); + bool contact = intersectEdgeEdgePreca(s0, s1, v1, plane, ii, jj, coeff, -normal, p1, p2, dist, ip, 0.0f); + if(contact && dist < radius + contactDistance) + { + contactBuffer.contact(ip-normal*dist, normal, dist - radius); +// if(contactBuffer.count==2) // PT: we only need 2 contacts to be stable +// return; + } + + a = b; + b++; + } +} + +namespace physx +{ +namespace Gu +{ +bool contactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>(); + + PxVec3 onSegment, onConvex; + PxReal distance; + PxVec3 normal_; + { + Gu::ConvexMesh* cm = static_cast<Gu::ConvexMesh*>(shapeConvex.convexMesh); + + using namespace Ps::aos; + Vec3V closA, closB, normalV; + GjkStatus status; + FloatV dist; + { + + const Vec3V zeroV = V3Zero(); + const Gu::ConvexHullData* hullData = &cm->getHull(); + + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const PsMatTransformV aToB(transform1.transformInv(transform0)); + + Gu::ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, shapeConvex.scale.isIdentity()); + + //transform capsule(a) into the local space of convexHull(b), treat capsule as segment + Gu::CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), FZero()); + + + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<ConvexHullV> convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(convexA.getCenter(), convexB.getCenter()); + + status = gjk<LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(convexA, convexB, initialSearchDir, FMax(),closA, closB, normalV, dist); + } + + + if(status == GJK_CONTACT) + distance = 0.f; + else + { + //const FloatV sqDist = FMul(dist, dist); + V3StoreU(closB, onConvex); + FStore(dist, &distance); + V3StoreU(normalV, normal_); + onConvex = transform1.transform(onConvex); + normal_ = transform1.rotate(normal_); + } + } + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + if(distance >= inflatedRadius) + return false; + + Gu::Segment worldSegment; + getCapsuleSegment(transform0, shapeCapsule, worldSegment); + + const bool isSphere = worldSegment.p0 == worldSegment.p1; + const PxU32 nbPts = PxU32(isSphere ? 1 : 2); + + PX_ASSERT(contactBuffer.count==0); + + Cm::FastVertex2ShapeScaling convexScaling; + const bool idtConvexScale = shapeConvex.scale.isIdentity(); + if(!idtConvexScale) + convexScaling.init(shapeConvex.scale); + + PolygonalData polyData; + getPolygonalData_Convex(&polyData, shapeConvex.hullData, convexScaling); + +// if(0) + if(distance > 0.f) + { + // PT: the capsule segment doesn't intersect the convex => distance-based version + PxVec3 normal = -normal_; + + // PT: generate VF contacts for segment's vertices vs convex + GuGenerateVFContacts2( contactBuffer, + transform1, polyData, shapeConvex.scale, + nbPts, &worldSegment.p0, shapeCapsule.radius, + normal, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + if(!isSphere) + { + const Cm::Matrix34 worldTM(transform1); + GuGenerateEEContacts2b(contactBuffer, worldSegment, shapeCapsule.radius, + worldTM, polyData, convexScaling, + normal, params.mContactDistance); + } + + // PT: run VF case for convex-vertex-vs-capsule only if we don't have any contact yet + if(!contactBuffer.count) + { +// gVisualizeLine(onConvex, onConvex + normal, context, PxDebugColor::eARGB_RED); + //PxReal distance = PxSqrt(sqDistance); + contactBuffer.contact(onConvex, normal, distance - shapeCapsule.radius); + } + } + else + { + // PT: the capsule segment intersects the convex => penetration-based version +//printf("Penetration-based:\n"); + + // PT: compute penetration vector (MTD) + PxVec3 SepAxis; + if(!GuCapsuleConvexOverlap(worldSegment, shapeCapsule.radius, polyData, convexScaling, transform1, NULL, &SepAxis, isSphere)) + { +//printf("- no overlap\n"); + return false; + } + + // PT: generate VF contacts for segment's vertices vs convex + GuGenerateVFContacts2( contactBuffer, + transform1, polyData, shapeConvex.scale, + nbPts, &worldSegment.p0, shapeCapsule.radius, + SepAxis, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts +//printf("- %d VF contacts\n", contactBuffer.count); + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + if(!isSphere) + { + GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, params.mContactDistance, polyData, transform1, convexScaling, SepAxis); +//printf("- %d total contacts\n", contactBuffer.count); + } + } + return true; +} + +} +} diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp new file mode 100644 index 00000000..da282865 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactCapsuleMesh.cpp @@ -0,0 +1,636 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionEdgeEdge.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "GuFeatureCode.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuConvexEdgeFlags.h" +#include "GuGeometryUnion.h" +#include "GuSIMDHelpers.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; + +#define DEBUG_RENDER_MESHCONTACTS 0 + +#if DEBUG_RENDER_MESHCONTACTS +#include "PxPhysics.h" +#include "PxScene.h" +#endif + +#define USE_AABB_TRI_CULLING + +//#define USE_CAPSULE_TRI_PROJ_CULLING +//#define USE_CAPSULE_TRI_SAT_CULLING +#define VISUALIZE_TOUCHED_TRIS 0 +#define VISUALIZE_CULLING_BOX 0 + +#if VISUALIZE_TOUCHED_TRIS +#include "CmRenderOutput.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; +} +static void gVisualizeTri(const PxVec3& a, const PxVec3& b, const PxVec3& c, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +} + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; +#endif + +static const float fatBoxEdgeCoeff = 0.01f; + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, + const PxVec3* PX_RESTRICT triVerts, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project triangle + float Min1, Max1; + { + Min1 = Max1 = triVerts[0].dot(axis); + const PxReal dp1 = triVerts[1].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp1); + Max1 = physx::intrinsics::selectMax(Max1, dp1); + const PxReal dp2 = triVerts[2].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp2); + Max1 = physx::intrinsics::selectMax(Max1, dp2); + } + + // Test projections + if(max0<Min1 || Max1<min0) + return false; + + const PxReal d0 = max0 - Min1; + PX_ASSERT(d0>=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleNormal(const PxVec3* PX_RESTRICT triVerts) +{ + return ((triVerts[0]-triVerts[1]).cross(triVerts[0]-triVerts[2])).getNormalized(); +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleCenter(const PxVec3* PX_RESTRICT triVerts) +{ + static const PxReal inv3 = 1.0f / 3.0f; + return (triVerts[0] + triVerts[1] + triVerts[2]) * inv3; +} + +static bool PxcCapsuleTriOverlap3(PxU8 edgeFlags, const Segment& segment, PxReal radius, const PxVec3* PX_RESTRICT triVerts, + PxReal* PX_RESTRICT t=NULL, PxVec3* PX_RESTRICT pp=NULL) +{ + PxReal penDepth = PX_MAX_REAL; + + // Test normal + PxVec3 sep = PxcComputeTriangleNormal(triVerts); + if(!PxcTestAxis(sep, segment, radius, triVerts, penDepth)) + return false; + + // Test edges + // ML:: use the active edge flag instead of the concave flag + const PxU32 activeEdgeFlag[] = {ETD_CONVEX_EDGE_01, ETD_CONVEX_EDGE_12, ETD_CONVEX_EDGE_20}; + const PxVec3 capsuleAxis = (segment.p1 - segment.p0).getNormalized(); + for(PxU32 i=0;i<3;i++) + { + //bool active =((edgeFlags & ignoreEdgeFlag[i]) == 0); + + if(edgeFlags & activeEdgeFlag[i]) + { + + const PxVec3 e0 = triVerts[i]; +// const PxVec3 e1 = triVerts[(i+1)%3]; + const PxVec3 e1 = triVerts[Ps::getNextIndex3(i)]; + const PxVec3 edge = e0 - e1; + + PxVec3 cross = capsuleAxis.cross(edge); + if(!Ps::isAlmostZero(cross)) + { + cross = cross.getNormalized(); + PxReal d; + if(!PxcTestAxis(cross, segment, radius, triVerts, d)) + return false; + if(d<penDepth) + { + penDepth = d; + sep = cross; + } + } + } + } + + const PxVec3 capsuleCenter = segment.computeCenter(); + const PxVec3 triCenter = PxcComputeTriangleCenter(triVerts); + const PxVec3 witness = capsuleCenter - triCenter; + + if(sep.dot(witness) < 0.0f) + sep = -sep; + + if(t) *t = penDepth; + if(pp) *pp = sep; + + return true; +} + +static void PxcGenerateVFContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, + const PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, + PxU32 triangleIndex, PxReal contactDistance) +{ + const PxVec3* PX_RESTRICT Ptr = &segment.p0; + for(PxU32 i=0;i<2;i++) + { + const PxVec3& Pos = Ptr[i]; + PxReal t,u,v; + if(intersectRayTriangleCulling(Pos, -normal, triVerts[0], triVerts[1], triVerts[2], t, u, v, 1e-3f) && t < radius + contactDistance) + { + const PxVec3 Hit = meshAbsPose.transform(Pos - t * normal); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(Hit, wn, t-radius, triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << Hit << (Hit + wn * 10.0f); + #endif + } + } +} + +// PT: PxcGenerateEEContacts2 uses a segment-triangle distance function, which breaks when the segment +// intersects the triangle, in which case you need to switch to a penetration-depth computation. +// If you don't do this thin capsules don't work. +static void PxcGenerateEEContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], -normal, s0, s1, dist, ip)) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, - (radius + dist), triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +static void PxcGenerateEEContacts2( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex, PxReal contactDistance) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], normal, s0, s1, dist, ip) && dist < radius+contactDistance) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, dist - radius, triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +namespace +{ +struct CapsuleMeshContactGeneration +{ + ContactBuffer& mContactBuffer; + const Cm::Matrix34 mMeshAbsPose; + const Segment& mMeshCapsule; +#ifdef USE_AABB_TRI_CULLING + Vec3p mBC; + Vec3p mBE; +#endif + PxReal mInflatedRadius; + PxReal mContactDistance; + PxReal mShapeCapsuleRadius; + + CapsuleMeshContactGeneration(ContactBuffer& contactBuffer, const PxTransform& transform1, const Segment& meshCapsule, PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius) : + mContactBuffer (contactBuffer), + mMeshAbsPose (Cm::Matrix34(transform1)), + mMeshCapsule (meshCapsule), + mInflatedRadius (inflatedRadius), + mContactDistance (contactDistance), + mShapeCapsuleRadius (shapeCapsuleRadius) + { + PX_ASSERT(contactBuffer.count==0); +#ifdef USE_AABB_TRI_CULLING + mBC = (meshCapsule.p0 + meshCapsule.p1)*0.5f; + const Vec3p be = (meshCapsule.p0 - meshCapsule.p1)*0.5f; + mBE.x = fabsf(be.x) + inflatedRadius; + mBE.y = fabsf(be.y) + inflatedRadius; + mBE.z = fabsf(be.z) + inflatedRadius; +#endif + } + + void processTriangle(PxU32 triangleIndex, const TrianglePadded& tri, PxU8 extraData/*, const PxU32* vertInds*/) + { +#ifdef USE_AABB_TRI_CULLING + #if VISUALIZE_CULLING_BOX + { + Cm::RenderOutput& out = context.mRenderOutput; + PxTransform idt = PxTransform(PxIdentity); + out << idt; + out << 0xffffffff; + out << Cm::DebugBox(mBC, mBE, true); + } + #endif +#endif + const PxVec3& p0 = tri.verts[0]; + const PxVec3& p1 = tri.verts[1]; + const PxVec3& p2 = tri.verts[2]; + +#ifdef USE_AABB_TRI_CULLING + // PT: this one is safe because triangle class is padded + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox_Unsafe(mBC, mBE, p0, p1, p2)) + return; +#endif + +#ifdef USE_CAPSULE_TRI_PROJ_CULLING + PxVec3 triCenter = (p0 + p1 + p2)*0.33333333f; + PxVec3 delta = mBC - triCenter; + + PxReal depth; + if(!PxcTestAxis(delta, mMeshCapsule, mInflatedRadius, tri.verts, depth)) + return; +#endif + +#if VISUALIZE_TOUCHED_TRIS + gVisualizeTri(p0, p1, p2, context, PxDebugColor::eARGB_RED); +#endif + +#ifdef USE_CAPSULE_TRI_SAT_CULLING + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; +#endif + + PxReal t,u,v; + const PxVec3 p1_p0 = p1 - p0; + const PxVec3 p2_p0 = p2 - p0; + const PxReal squareDist = distanceSegmentTriangleSquared(mMeshCapsule, p0, p1_p0, p2_p0, &t, &u, &v); + + // PT: do cheaper test first! + if(squareDist >= mInflatedRadius*mInflatedRadius) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the segment-triangle distance test if it's cheaper + const PxVec3 planeNormal = p1_p0.cross(p2_p0); + const PxF32 planeD = planeNormal.dot(p0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mBC) < planeD) + return; + + if(squareDist > 0.001f*0.001f) + { + // Contact information + PxVec3 normal; + if(selectNormal(extraData, u, v)) + { + normal = planeNormal.getNormalized(); + } + else + { + const PxVec3 pointOnTriangle = Ps::computeBarycentricPoint(p0, p1, p2, u, v); + + const PxVec3 pointOnSegment = mMeshCapsule.getPointAt(t); + normal = pointOnSegment - pointOnTriangle; + const PxReal l = normal.magnitude(); + if(l == 0.0f) + return; + normal = normal / l; + } + + PxcGenerateEEContacts2(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + } + else + { + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; + + PxcGenerateEEContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex, mContactDistance); + } + } + +private: + CapsuleMeshContactGeneration& operator=(const CapsuleMeshContactGeneration&); +}; + +struct CapsuleMeshContactGenerationCallback_NoScale : MeshHitCallback<PxRaycastHit> +{ + CapsuleMeshContactGeneration mGeneration; + const TriangleMesh* mMeshData; + + CapsuleMeshContactGenerationCallback_NoScale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE), + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mMeshData (meshData) + { + PX_ASSERT(contactBuffer.count==0); + } + + PX_FORCE_INLINE PxAgain processTriangle(const PxRaycastHit& hit, const TrianglePadded& tri) + { + const PxU32 triangleIndex = hit.faceIndex; + + //ML::set all the edges to be active, if the mExtraTrigData exist, we overwrite this flag + const PxU8 extraData = getConvexEdgeFlags(mMeshData->getExtraTrigData(), triangleIndex); + mGeneration.processTriangle(triangleIndex, tri, extraData); + return true; + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + // PT: TODO: revisit this, avoid the copy + tri.verts[0] = v0; + tri.verts[1] = v1; + tri.verts[2] = v2; + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_NoScale& operator=(const CapsuleMeshContactGenerationCallback_NoScale&); +}; + +struct CapsuleMeshContactGenerationCallback_Scale : CapsuleMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mScaling; + + CapsuleMeshContactGenerationCallback_Scale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, const Cm::FastVertex2ShapeScaling& scaling, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + CapsuleMeshContactGenerationCallback_NoScale(contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius, meshData), + mScaling (scaling) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + getScaledVertices(tri.verts, v0, v1, v2, false, mScaling); + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_Scale& operator=(const CapsuleMeshContactGenerationCallback_Scale&); +}; + +} + +// PT: computes local capsule without going to world-space +static PX_FORCE_INLINE Segment computeLocalCapsule(const PxTransform& transform0, const PxTransform& transform1, const PxCapsuleGeometry& shapeCapsule) +{ + const PxVec3 halfHeight = getCapsuleHalfHeightVector(transform0, shapeCapsule); + const PxVec3 delta = transform1.p - transform0.p; + return Segment( + transform1.rotateInv(halfHeight - delta), + transform1.rotateInv(-halfHeight - delta)); +} + +bool Gu::contactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + const TriangleMesh* meshData = shapeMesh.meshData; + + //bound the capsule in shape space by an OBB: + Box queryBox; + { + const Capsule queryCapsule(meshCapsule, inflatedRadius); + queryBox.create(queryCapsule); + } + + if(shapeMesh.scale.isIdentity()) + { + CapsuleMeshContactGenerationCallback_NoScale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, params.mContactDistance, shapeCapsule.radius, meshData); + + // PT: TODO: switch to capsule query here + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + CapsuleMeshContactGenerationCallback_Scale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, meshScaling, params.mContactDistance, shapeCapsule.radius, meshData); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //apply the skew transform to the box: + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + return contactBuffer.count > 0; +} + +namespace +{ +struct CapsuleHeightfieldContactGenerationCallback : EntityReport<PxU32> +{ + CapsuleMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + const PxTransform& mTransform1; + + CapsuleHeightfieldContactGenerationCallback( + ContactBuffer& contactBuffer, + const PxTransform& transform1, HeightFieldUtil& hfUtil, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius + ) : + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mHfUtil (hfUtil), + mTransform1 (transform1) + { + PX_ASSERT(contactBuffer.count==0); + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + TrianglePadded currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + { + triFlags |= 1 << (a+3); + } + } + + mGeneration.processTriangle(triangleIndex, currentTriangle, triFlags); + } + return true; + } + +private: + CapsuleHeightfieldContactGenerationCallback& operator=(const CapsuleHeightfieldContactGenerationCallback&); +}; +} + +bool Gu::contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + // We must be in local space to use the cache + + const HeightField& hf = *static_cast<HeightField*>(shapeMesh.heightField); + HeightFieldUtil hfUtil(shapeMesh, hf); + + CapsuleHeightfieldContactGenerationCallback callback( + contactBuffer, transform1, hfUtil, meshCapsule, inflatedRadius, params.mContactDistance, shapeCapsule.radius); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //bound the capsule in shape space by an OBB: + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + return contactBuffer.count > 0; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp new file mode 100644 index 00000000..898152ae --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexConvex.cpp @@ -0,0 +1,1033 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsMathUtils.h" +#include "GuContactPolygonPolygon.h" +#include "GuGeometryUnion.h" +#include "GuConvexHelper.h" +#include "GuInternal.h" +#include "GuSeparatingAxes.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "PsFPU.h" + +// csigg: the single reference of gEnableOptims (below) has been +// replaced with the actual value to prevent ios64 compiler crash. +// static const int gEnableOptims = 1; +#define CONVEX_CONVEX_ROUGH_FIRST_PASS +#define TEST_INTERNAL_OBJECTS +#ifdef TEST_INTERNAL_OBJECTS + #define USE_BOX_DATA +#endif + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; +using namespace intrinsics; + +#ifdef TEST_INTERNAL_OBJECTS +#ifdef USE_BOX_DATA +PX_FORCE_INLINE void BoxSupport(const float extents[3], const PxVec3& sv, float p[3]) +{ + const PxU32* iextents = reinterpret_cast<const PxU32*>(extents); + const PxU32* isv = reinterpret_cast<const PxU32*>(&sv); + PxU32* ip = reinterpret_cast<PxU32*>(p); + + ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); + ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); + ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); +} +#endif + +#if PX_DEBUG +static const PxReal testInternalObjectsEpsilon = 1.0e-3f; +#endif + +#ifdef DO_NOT_REMOVE + PX_FORCE_INLINE void testInternalObjects_( const PxVec3& delta_c, const PxVec3& axis, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& tr0, const Cm::Matrix34& tr1, + float& dmin, float contactDistance) + { + { +/* float projected0 = axis.dot(tr0.p); + float projected1 = axis.dot(tr1.p); + float min0 = projected0 - polyData0.mInternal.mRadius; + float max0 = projected0 + polyData0.mInternal.mRadius; + float min1 = projected1 - polyData1.mInternal.mRadius; + float max1 = projected1 + polyData1.mInternal.mRadius; +*/ + float MinMaxRadius = polyData0.mInternal.mRadius + polyData1.mInternal.mRadius; + PxVec3 delta = tr0.p - tr1.p; + const PxReal dp = axis.dot(delta); +// const PxReal dp2 = axis.dot(delta_c); + +// const PxReal d0 = max0 - min1; +// const PxReal d0 = projected0 + polyData0.mInternal.mRadius - projected1 + polyData1.mInternal.mRadius; +// const PxReal d0 = projected0 - projected1 + polyData0.mInternal.mRadius + polyData1.mInternal.mRadius; +// const PxReal d0 = projected0 - projected1 + MinMaxRadius; +// const PxReal d0 = axis.dot(tr0.p) - axis.dot(tr1.p) + MinMaxRadius; +// const PxReal d0 = axis.dot(tr0.p - tr1.p) + MinMaxRadius; +// const PxReal d0 = MinMaxRadius + axis.dot(delta); + const PxReal d0 = MinMaxRadius + dp; + +// const PxReal d1 = max1 - min0; +// const PxReal d1 = projected1 + polyData1.mInternal.mRadius - projected0 + polyData0.mInternal.mRadius; +// const PxReal d1 = projected1 - projected0 + polyData1.mInternal.mRadius + polyData0.mInternal.mRadius; +// const PxReal d1 = projected1 - projected0 + MinMaxRadius; +// const PxReal d1 = axis.dot(tr1.p) - axis.dot(tr0.p) + MinMaxRadius; +// const PxReal d1 = axis.dot(tr1.p - tr0.p) + MinMaxRadius; +// const PxReal d1 = MinMaxRadius - axis.dot(delta); + const PxReal d1 = MinMaxRadius - dp; + + dmin = selectMin(d0, d1); + return; + } + + #ifdef USE_BOX_DATA + const PxVec3 localAxis0 = tr0.rotateTranspose(axis); + const PxVec3 localAxis1 = tr1.rotateTranspose(axis); + + const float dp = delta_c.dot(axis); + + float p0[3]; + BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + float p1[3]; + BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1); + + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z; + + const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius); + #else + const float dp = delta_c.dot(axis); + const float MinRadius = polyData0.mInternal.mRadius; + const float MaxRadius = polyData1.mInternal.mRadius; + #endif + const float MinMaxRadius = MaxRadius + MinRadius; + const float d0 = MinMaxRadius + dp; + const float d1 = MinMaxRadius - dp; + + dmin = selectMin(d0, d1); + } +#endif + +static PX_FORCE_INLINE bool testInternalObjects( const PxVec3& delta_c, const PxVec3& axis, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& tr0, const Cm::Matrix34& tr1, + float dmin) +{ +#ifdef USE_BOX_DATA + const PxVec3 localAxis0 = tr0.rotateTranspose(axis); + const PxVec3 localAxis1 = tr1.rotateTranspose(axis); + + const float dp = delta_c.dot(axis); + + float p0[3]; + BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + float p1[3]; + BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1); + + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z; + + const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius); +#else + const float dp = delta_c.dot(axis); + const float MinRadius = polyData0.mInternal.mRadius; + const float MaxRadius = polyData1.mInternal.mRadius; +#endif + const float MinMaxRadius = MaxRadius + MinRadius; + const float d0 = MinMaxRadius + dp; + const float d1 = MinMaxRadius - dp; + + const float depth = selectMin(d0, d1); + if(depth>dmin) + return false; + return true; +} +#endif + +PX_FORCE_INLINE float PxcMultiplyAdd3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world) +{ + return (p1.x + p0.x) * world.m.column0[i] + (p1.y + p0.y) * world.m.column1[i] + (p1.z + p0.z) * world.m.column2[i] + 2.0f * world.p[i]; +} + +PX_FORCE_INLINE float PxcMultiplySub3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world) +{ + return (p1.x - p0.x) * world.m.column0[i] + (p1.y - p0.y) * world.m.column1[i] + (p1.z - p0.z) * world.m.column2[i]; +} + +static PX_FORCE_INLINE bool testNormal( const PxVec3& axis, PxReal min0, PxReal max0, + const PolygonalData& polyData1, + const Cm::Matrix34& m1to0, + const Cm::FastVertex2ShapeScaling& scaling1, + PxReal& depth, PxReal contactDistance) +{ + //The separating axis we want to test is a face normal of hull0 + PxReal min1, max1; + (polyData1.mProjectHull)(polyData1, axis, m1to0, scaling1, min1, max1); + + if(max0+contactDistance<min1 || max1+contactDistance<min0) + return false; + + const PxReal d0 = max0 - min1; + const PxReal d1 = max1 - min0; + depth = selectMin(d0, d1); + return true; +} + +static PX_FORCE_INLINE bool testSeparatingAxis( const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const PxVec3& axis, PxReal& depth, PxReal contactDistance) +{ + PxReal min0, max0; + (polyData0.mProjectHull)(polyData0, axis, world0, scaling0, min0, max0); + + return testNormal(axis, min0, max0, polyData1, world1, scaling1, depth, contactDistance); +} + +static bool PxcSegmentAABBIntersect(const PxVec3& p0, const PxVec3& p1, + const PxVec3& minimum, const PxVec3& maximum, + const Cm::Matrix34& world) +{ + PxVec3 boxExtent, diff, dir; + PxReal fAWdU[3]; + + dir.x = PxcMultiplySub3x4(0, p0, p1, world); + boxExtent.x = maximum.x - minimum.x; + diff.x = (PxcMultiplyAdd3x4(0, p0, p1, world) - (maximum.x+minimum.x)); + fAWdU[0] = PxAbs(dir.x); + if(PxAbs(diff.x)> boxExtent.x + fAWdU[0]) return false; + + dir.y = PxcMultiplySub3x4(1, p0, p1, world); + boxExtent.y = maximum.y - minimum.y; + diff.y = (PxcMultiplyAdd3x4(1, p0, p1, world) - (maximum.y+minimum.y)); + fAWdU[1] = PxAbs(dir.y); + if(PxAbs(diff.y)> boxExtent.y + fAWdU[1]) return false; + + dir.z = PxcMultiplySub3x4(2, p0, p1, world); + boxExtent.z = maximum.z - minimum.z; + diff.z = (PxcMultiplyAdd3x4(2, p0, p1, world) - (maximum.z+minimum.z)); + fAWdU[2] = PxAbs(dir.z); + if(PxAbs(diff.z)> boxExtent.z + fAWdU[2]) return false; + + PxReal f; + f = dir.y * diff.z - dir.z*diff.y; if(PxAbs(f)>boxExtent.y*fAWdU[2] + boxExtent.z*fAWdU[1]) return false; + f = dir.z * diff.x - dir.x*diff.z; if(PxAbs(f)>boxExtent.x*fAWdU[2] + boxExtent.z*fAWdU[0]) return false; + f = dir.x * diff.y - dir.y*diff.x; if(PxAbs(f)>boxExtent.x*fAWdU[1] + boxExtent.y*fAWdU[0]) return false; + return true; +} + +#define EXPERIMENT + +/* +Edge culling can clearly be improved : in ConvexTest02 for example, edges don't even touch the other mesh sometimes. +*/ +static void PxcFindSeparatingAxes( SeparatingAxes& sa, const PxU32* PX_RESTRICT indices, PxU32 numPolygons, + const PolygonalData& polyData, + const Cm::Matrix34& world0, const PxPlane& plane, + const Cm::Matrix34& m0to1, const PxBounds3& aabb, PxReal contactDistance, + const Cm::FastVertex2ShapeScaling& scaling) +{ +// EdgeCache edgeCache; // PT: TODO: check this is actually useful + const PxVec3* PX_RESTRICT vertices = polyData.mVerts; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData.mPolygons; + const PxU8* PX_RESTRICT vrefsBase = polyData.mPolygonVertexRefs; + + while(numPolygons--) + { + //Get current polygon + const Gu::HullPolygonData& P = polygons[*indices++]; + const PxU8* PX_RESTRICT VData = vrefsBase + P.mVRef8; + + // Loop through polygon vertices == polygon edges + PxU32 numVerts = P.mNbVerts; + +#ifdef EXPERIMENT + PxVec3 p0,p1; + PxU8 VRef0 = VData[0]; + p0 = scaling * vertices[VRef0]; + bool b0 = plane.distance(p0) <= contactDistance; +#endif + + for(PxU32 j = 0; j < numVerts; j++) + { + PxU32 j1 = j+1; + if(j1 >= numVerts) j1 = 0; + +#ifndef EXPERIMENT + PxU8 VRef0 = VData[j]; +#endif + PxU8 VRef1 = VData[j1]; + +// Ps::order(VRef0, VRef1); //make sure edge (a,b) == edge (b,a) +// if (edgeCache.isInCache(VRef0, VRef1)) +// continue; + + //transform points //TODO: once this works we could transform plan instead, that is more efficient!! + +#ifdef EXPERIMENT + p1 = scaling * vertices[VRef1]; +#else + const PxVec3 p0 = scaling * vertices[VRef0]; + const PxVec3 p1 = scaling * vertices[VRef1]; +#endif + + // Cheap but effective culling! +#ifdef EXPERIMENT + bool b1 = plane.distance(p1) <= contactDistance; + if(b0 || b1) +#else + if(plane.signedDistanceHessianNormalForm(p0) <= contactDistance || + plane.signedDistanceHessianNormalForm(p1) <= contactDistance) +#endif + { + if(PxcSegmentAABBIntersect(p0, p1, aabb.minimum, aabb.maximum, m0to1)) + { + // Create current edge. We're only interested in different edge directions so we normalize. + const PxVec3 currentEdge = world0.rotate(p0 - p1).getNormalized(); + sa.addAxis(currentEdge); + } + } +#ifdef EXPERIMENT + VRef0 = VRef1; + p0 = p1; + b0 = b1; +#endif + } + } +} + +static bool PxcTestFacesSepAxesBackface(const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m1to0, const PxVec3& delta, + PxReal& dmin, PxVec3& sep, PxU32& id, PxU32* PX_RESTRICT indices_, PxU32& numIndices, + PxReal contactDistance, float toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , const PxVec3& worldDelta +#endif + ) +{ + PX_UNUSED(toleranceLength); // Only used in Debug + id = PX_INVALID_U32; + PxU32* indices = indices_; + + const PxU32 num = polyData0.mNbPolygons; + const PxVec3* PX_RESTRICT vertices = polyData0.mVerts; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons; + + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceDelta = scaling0 % delta; + + // PT: prefetch polygon data + { + const PxU32 dataSize = num*sizeof(Gu::HullPolygonData); + for(PxU32 offset=0; offset < dataSize; offset+=128) + Ps::prefetchLine(polygons, offset); + } + + for(PxU32 i=0; i < num; i++) + { + const Gu::HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + + // Do backface-culling + if(PL.n.dot(vertSpaceDelta) < 0.0f) + continue; + + //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) + PxVec3 shapeSpaceNormal = scaling0 % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); // PT: We need to find a way to skip this normalize + +#ifdef TEST_INTERNAL_OBJECTS + +/* +const PxVec3 worldNormal_ = world0.rotate(shapeSpaceNormal); +PxReal d0; +bool test0 = PxcTestSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, worldNormal_, d0, contactDistance); + +PxReal d1; +const float invMagnitude0 = 1.0f / magnitude; +bool test1 = PxcTestNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude0, P.getMax() * invMagnitude0, polyData1, m1to0, scaling1, d1, contactDistance); + +PxReal d2; +testInternalObjects_(worldDelta, worldNormal_, polyData0, polyData1, world0, world1, d2, contactDistance); +*/ + + const PxVec3 worldNormal = world0.rotate(shapeSpaceNormal); + if(!testInternalObjects(worldDelta, worldNormal, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + *indices++ = i; + + //////////////////// + /* + //gotta transform minimum and maximum from vertex to shape space! + //I think they transform like the 'd' of the plane, basically by magnitude division! + //unfortunately I am not certain of that, so let's convert them to a point in vertex space, transform that, and then convert back to a plane d. + + //let's start by transforming the plane's d: + //a point on the plane: + + PxVec3 vertSpaceDPoint = PL.normal * -PL.d; + + //make sure this is on the plane: + PxReal distZero = PL.signedDistanceHessianNormalForm(vertSpaceDPoint); //should be zero + + //transform: + + PxVec3 shapeSpaceDPoint = cache.mVertex2ShapeSkew[skewIndex] * vertSpaceDPoint; + + //make into a d offset again by projecting along the plane: + PxcPlane shapeSpacePlane(shapeSpaceNormal, shapeSpaceDPoint); + + //see what D is!! + + + //NOTE: for boxes scale[0] is always id so this is all redundant. Hopefully for convex convex it will become useful! + */ + + //////////////////// + + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + return false; + + if(d < dmin) + { +#ifdef TEST_INTERNAL_OBJECTS + sep = worldNormal; +#else + sep = world0.rotate(shapeSpaceNormal); +#endif + dmin = d; + id = i; + } + } + + numIndices = PxU32(indices - indices_); + + PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version + + return true; +} + +// PT: isolating this piece of code allows us to better see what happens in PIX. For some reason it looks +// like isolating it creates *less* LHS than before, but I don't understand why. There's still a lot of +// bad stuff there anyway +//PX_FORCE_INLINE +static void prepareData(PxPlane& witnessPlane0, PxPlane& witnessPlane1, + PxBounds3& aabb0, PxBounds3& aabb1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxPlane& vertSpacePlane0, const PxPlane& vertSpacePlane1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + PxReal contactDistance + ) +{ + scaling0.transformPlaneToShapeSpace(vertSpacePlane0.n, vertSpacePlane0.d, witnessPlane0.n, witnessPlane0.d); + scaling1.transformPlaneToShapeSpace(vertSpacePlane1.n, vertSpacePlane1.d, witnessPlane1.n, witnessPlane1.d); + + //witnessPlane0 = witnessPlane0.getTransformed(m0to1); + //witnessPlane1 = witnessPlane1.getTransformed(m1to0); + const PxVec3 newN0 = m0to1.rotate(witnessPlane0.n); + witnessPlane0 = PxPlane(newN0, witnessPlane0.d - m0to1.p.dot(newN0)); + const PxVec3 newN1 = m1to0.rotate(witnessPlane1.n); + witnessPlane1 = PxPlane(newN1, witnessPlane1.d - m1to0.p.dot(newN1)); + + aabb0 = hullBounds0; + aabb1 = hullBounds1; + + //gotta inflate // PT: perfect LHS recipe here.... + const PxVec3 inflate(contactDistance); + aabb0.minimum -= inflate; + aabb1.minimum -= inflate; + aabb0.maximum += inflate; + aabb1.maximum += inflate; +} + +static bool PxcBruteForceOverlapBackface( const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta, + PxU32& id0, PxU32& id1, + PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, float toleranceLength) +{ + const PxVec3 localDelta0 = world0.rotateTranspose(delta); + PxU32* PX_RESTRICT indices0 = reinterpret_cast<PxU32*>(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); + + PxU32 numIndices0; + PxReal dmin0 = PX_MAX_REAL; + PxVec3 vec0; + if(!PxcTestFacesSepAxesBackface(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localDelta0, dmin0, vec0, id0, indices0, numIndices0, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , -delta +#endif + )) + return false; + + const PxVec3 localDelta1 = world1.rotateTranspose(delta); + + PxU32* PX_RESTRICT indices1 = reinterpret_cast<PxU32*>(PxAlloca(polyData1.mNbPolygons*sizeof(PxU32))); + + PxU32 numIndices1; + PxReal dmin1 = PX_MAX_REAL; + PxVec3 vec1; + if(!PxcTestFacesSepAxesBackface(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, -localDelta1, dmin1, vec1, id1, indices1, numIndices1, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , delta +#endif + )) + return false; + + PxReal dmin = dmin0; + PxVec3 vec = vec0; + code = SA_NORMAL0; + + if(dmin1 < dmin) + { + dmin = dmin1; + vec = vec1; + code = SA_NORMAL1; + } + + PX_ASSERT(id0!=PX_INVALID_U32); + PX_ASSERT(id1!=PX_INVALID_U32); + + // Brute-force find a separating axis + SeparatingAxes mSA0; + SeparatingAxes mSA1; + mSA0.reset(); + mSA1.reset(); + PxPlane witnessPlane0; + PxPlane witnessPlane1; + PxBounds3 aabb0, aabb1; + + prepareData(witnessPlane0, witnessPlane1, + aabb0, aabb1, + hullBounds0, hullBounds1, + polyData0.mPolygons[id0].mPlane, + polyData1.mPolygons[id1].mPlane, + scaling0, scaling1, + m0to1, m1to0, + contactDistance); + + // Find possibly separating axes + PxcFindSeparatingAxes(mSA0, indices0, numIndices0, polyData0, world0, witnessPlane1, m0to1, aabb1, contactDistance, scaling0); + PxcFindSeparatingAxes(mSA1, indices1, numIndices1, polyData1, world1, witnessPlane0, m1to0, aabb0, contactDistance, scaling1); +// PxcFindSeparatingAxes(context.mSA0, &id0, 1, hull0, world0, witnessPlane1, m0to1, aabbMin1, aabbMax1); +// PxcFindSeparatingAxes(context.mSA1, &id1, 1, hull1, world1, witnessPlane0, m1to0, aabbMin0, aabbMax0); + + const PxU32 numEdges0 = mSA0.getNumAxes(); + const PxVec3* PX_RESTRICT edges0 = mSA0.getAxes(); + + const PxU32 numEdges1 = mSA1.getNumAxes(); + const PxVec3* PX_RESTRICT edges1 = mSA1.getAxes(); + + // Worst case = convex test 02 with big meshes: 23 & 23 edges => 23*23 = 529 tests! + // printf("%d - %d\n", NbEdges0, NbEdges1); // maximum = ~20 in test scenes. + + for(PxU32 i=0; i < numEdges0; i++) + { + const PxVec3& edge0 = edges0[i]; + for(PxU32 j=0; j < numEdges1; j++) + { + const PxVec3& edge1 = edges1[j]; + + PxVec3 sepAxis = edge0.cross(edge1); + if(!Ps::isAlmostZero(sepAxis)) + { + sepAxis = sepAxis.getNormalized(); + +#ifdef TEST_INTERNAL_OBJECTS + if(!testInternalObjects(-delta, sepAxis, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + if(testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance)) + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + PxReal d; + if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance)) + return false; + + if(d<dmin) + { + dmin = d; + vec = sepAxis; + code = SA_EE; + } + } + } + } + + depth = dmin; + sep = vec; + + return true; +} + +static bool GuTestFacesSepAxesBackfaceRoughPass( + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m1to0, const PxVec3& /*witness*/, const PxVec3& delta, + PxReal& dmin, PxVec3& sep, PxU32& id, + PxReal contactDistance, float toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , const PxVec3& worldDelta +#endif + ) +{ + PX_UNUSED(toleranceLength); // Only used in Debug + id = PX_INVALID_U32; + + const PxU32 num = polyData0.mNbPolygons; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons; + const PxVec3* PX_RESTRICT vertices = polyData0.mVerts; + + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceDelta = scaling0 % delta; + + for(PxU32 i=0; i < num; i++) + { + const Gu::HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + + if(PL.n.dot(vertSpaceDelta) < 0.0f) + continue; //backface-cull + + PxVec3 shapeSpaceNormal = scaling0 % PL.n; //normals transform with inverse transpose + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); // PT: We need to find a way to skip this normalize + +#ifdef TEST_INTERNAL_OBJECTS + const PxVec3 worldNormal = world0.rotate(shapeSpaceNormal); + if(!testInternalObjects(worldDelta, worldNormal, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, /*hull1,*/ polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, /*hull1,*/ polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + return false; + + if(d < dmin) + { +#ifdef TEST_INTERNAL_OBJECTS + sep = worldNormal; +#else + sep = world0.rotate(shapeSpaceNormal); +#endif + dmin = d; + id = i; + } + } + + PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version + + return true; +} + +static bool GuBruteForceOverlapBackfaceRoughPass( const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta, + PxU32& id0, PxU32& id1, + PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, PxReal toleranceLength) +{ + PxReal dmin0 = PX_MAX_REAL; + PxReal dmin1 = PX_MAX_REAL; + PxVec3 vec0, vec1; + + const PxVec3 localDelta0 = world0.rotateTranspose(delta); + const PxVec3 localCenter1in0 = m1to0.transform(polyData1.mCenter); + if(!GuTestFacesSepAxesBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localCenter1in0, localDelta0, dmin0, vec0, id0, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , -delta +#endif + )) + return false; + + const PxVec3 localDelta1 = world1.rotateTranspose(delta); + const PxVec3 localCenter0in1 = m0to1.transform(polyData0.mCenter); + if(!GuTestFacesSepAxesBackfaceRoughPass(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, localCenter0in1, -localDelta1, dmin1, vec1, id1, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , delta +#endif + )) + return false; + + PxReal dmin = dmin0; + PxVec3 vec = vec0; + code = SA_NORMAL0; + + if(dmin1 < dmin) + { + dmin = dmin1; + vec = vec1; + code = SA_NORMAL1; + } + + PX_ASSERT(id0!=PX_INVALID_U32); + PX_ASSERT(id1!=PX_INVALID_U32); + + depth = dmin; + sep = vec; + + return true; +} + +static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + bool idtScale0, bool idtScale1); + +namespace physx +{ + +namespace Gu +{ +// Box-convex contact generation +// this can no longer share code with convex-convex because that case needs scaling for both shapes, while this only needs it for one, which may be significant perf wise. +// +// PT: duplicating the full convex-vs-convex codepath is a lot worse. Look at it this way: if scaling is really "significant perf wise" then we made the whole convex-convex +// codepath significantly slower, and this is a lot more important than just box-vs-convex. The proper approach is to share the code and make sure scaling is NOT a perf hit. +// PT: please leave this function in the same translation unit as PxcContactHullHull. + +bool contactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + + Cm::FastVertex2ShapeScaling idtScaling; + + const PxBounds3 boxBounds(-shapeBox.halfExtents, shapeBox.halfExtents); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + /////// + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 convexBounds; + PolygonalData polyData1; + const bool idtScale = getConvexData(shape1, convexScaling, convexBounds, polyData1); + + return GuContactHullHull( polyData0, polyData1, boxBounds, convexBounds, + transform0, transform1, params, contactBuffer, + idtScaling, convexScaling, true, idtScale); +} + +// PT: please leave this function in the same translation unit as PxcContactHullHull. +bool contactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + Cm::FastVertex2ShapeScaling scaling0, scaling1; + PxBounds3 convexBounds0, convexBounds1; + PolygonalData polyData0, polyData1; + const bool idtScale0 = getConvexData(shape0, scaling0, convexBounds0, polyData0); + const bool idtScale1 = getConvexData(shape1, scaling1, convexBounds1, polyData1); + + return GuContactHullHull( polyData0, polyData1, convexBounds0, convexBounds1, + transform0, transform1, params, contactBuffer, + scaling0, scaling1, idtScale0, idtScale1); +} +}//Gu +}//physx + +static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + bool idtScale0, bool idtScale1) +{ + // Compute matrices + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + const PxVec3 worldCenter0 = world0.transform(polyData0.mCenter); + const PxVec3 worldCenter1 = world1.transform(polyData1.mCenter); + + const PxVec3 deltaC = worldCenter1 - worldCenter0; + + PxReal depth; + + /////////////////////////////////////////////////////////////////////////// + + // Early-exit test: quickly discard obviously non-colliding cases. + // PT: we used to skip this when objects were touching, which provided a small speedup (saving 2 full hull projections). + // We may want to add this back at some point in the future, if possible. + if(true) //AM: now we definitely want to test the cached axis to get the depth value for the cached axis, even if we had a contact before. + { + if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, deltaC, depth, params.mContactDistance)) + //there was no contact previously and we reject using the same prev. axis. + return false; + } + + /////////////////////////////////////////////////////////////////////////// + + // Compute relative transforms + PxTransform t0to1 = transform1.transformInv(transform0); + PxTransform t1to0 = transform0.transformInv(transform1); + + PxU32 c0(0x7FFF); + PxU32 c1(0x7FFF); + PxVec3 worldNormal; + + const Cm::Matrix34 m0to1(t0to1); + const Cm::Matrix34 m1to0(t1to0); + +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + // PT: it is a bad idea to skip the rough pass, for two reasons: + // 1) performance. The rough pass is obviously a lot faster. + // 2) stability. The "skipIt" optimization relies on the rough pass being present to catch the cases where it fails. If you disable the rough pass + // "skipIt" can skip the whole work, contacts get lost, and we never "try again" ==> explosions +// bool TryRoughPass = (contactDistance == 0.0f); //Rough first pass doesn't work with dist based for some reason. + for(int TryRoughPass = 1 /*gEnableOptims*/; 0 <= TryRoughPass; --TryRoughPass) + { +#endif + + { + PxcSepAxisType code; + PxU32 id0, id1; + //PxReal depth; +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + bool Status; + if(TryRoughPass) + { + Status = GuBruteForceOverlapBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength); + } + else + { + Status = PxcBruteForceOverlapBackface( +// hull0, hull1, + hullBounds0, hullBounds1, + polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength); + } + + if(!Status) +#else + if(!PxcBruteForceOverlapBackface( +// hull0, hull1, + hullBounds0, hullBounds1, + polyData0, polyData1, + world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, contactDistance)) +#endif + return false; + + if(deltaC.dot(worldNormal) < 0.0f) + worldNormal = -worldNormal; + +/* +worldNormal = -partialSep; +depth = -partialDepth; +code = SA_EE; +*/ + + if(code==SA_NORMAL0) + { + c0 = id0; + c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal)); + } + else if(code==SA_NORMAL1) + { + c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal)); + c1 = id1; + } + else if(code==SA_EE) + { + c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal)); + c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal)); + } + } + + const Gu::HullPolygonData& HP0 = polyData0.mPolygons[c0]; + const Gu::HullPolygonData& HP1 = polyData1.mPolygons[c1]; + // PT: prefetching those guys saves ~600.000 cycles in convex-convex benchmark + Ps::prefetchLine(&HP0.mPlane); + Ps::prefetchLine(&HP1.mPlane); + + //ok, we have a new depth value. convert to real distance. +// PxReal separation = -depth; //depth was either computed in initial cached-axis check, or better, when skipIt was false, in sep axis search. +// if (separation < 0.0f) +// separation = 0.0f; //we don't want to store penetration values. + const PxReal separation = fsel(depth, 0.0f, -depth); + + PxVec3 worldNormal0; + PX_ALIGN(16, PxPlane) shapeSpacePlane0; + if(idtScale0) + { + V4StoreA(V4LoadU(&HP0.mPlane.n.x), &shapeSpacePlane0.n.x); + worldNormal0 = world0.rotate(HP0.mPlane.n); + } + else + { + scaling0.transformPlaneToShapeSpace(HP0.mPlane.n,HP0.mPlane.d,shapeSpacePlane0.n,shapeSpacePlane0.d); + worldNormal0 = world0.rotate(shapeSpacePlane0.n); + } + + PxVec3 worldNormal1; + PX_ALIGN(16, PxPlane) shapeSpacePlane1; + if(idtScale1) + { + V4StoreA(V4LoadU(&HP1.mPlane.n.x), &shapeSpacePlane1.n.x); + worldNormal1 = world1.rotate(HP1.mPlane.n); + } + else + { + scaling1.transformPlaneToShapeSpace(HP1.mPlane.n,HP1.mPlane.d,shapeSpacePlane1.n,shapeSpacePlane1.d); + worldNormal1 = world1.rotate(shapeSpacePlane1.n); + } + + Ps::IntBool flag; + { + const PxReal d0 = PxAbs(worldNormal0.dot(worldNormal)); + const PxReal d1 = PxAbs(worldNormal1.dot(worldNormal)); + bool f = d0>d1; //which face normal is the separating axis closest to. + flag = f; + } + +////////////////////NEW DIST HANDLING////////////////////// + PX_ASSERT(separation >= 0.0f); //be sure this got fetched somewhere in the chaos above. + + const PxReal cCCDEpsilon = params.mMeshContactMargin; + const PxReal contactGenPositionShift = separation + cCCDEpsilon; //if we're at a distance, shift so we're in penetration. + + const PxVec3 contactGenPositionShiftVec = worldNormal * -contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. + + //note: for some reason this has to change sign! + + //this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that + //the solver converges to MSP penetration, while we want it to converge to 0 penetration. + //to real distance: + + //The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. + const PxVec3 newp = world0.p - contactGenPositionShiftVec; + const Cm::Matrix34 world0_Tweaked(world0.m.column0, world0.m.column1, world0.m.column2, newp); + PxTransform shifted0(newp, transform0.q); + + t0to1 = transform1.transformInv(shifted0); + t1to0 = shifted0.transformInv(transform1); + Cm::Matrix34 m0to1_Tweaked; + Cm::Matrix34 m1to0_Tweaked; + m0to1_Tweaked.set(t0to1.q); m0to1_Tweaked.p = t0to1.p; + m1to0_Tweaked.set(t1to0.q); m1to0_Tweaked.p = t1to0.p; + +////////////////////////////////////////////////// + //pretransform convex polygon if we have scaling! + PxVec3* scaledVertices0; + PxU8* stackIndices0; + GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, idtScale0, HP0.mNbVerts, scaling0, polyData0.mVerts, polyData0.getPolygonVertexRefs(HP0)) + + //pretransform convex polygon if we have scaling! + PxVec3* scaledVertices1; + PxU8* stackIndices1; + GET_SCALEX_CONVEX(scaledVertices1, stackIndices1, idtScale1, HP1.mNbVerts, scaling1, polyData1.mVerts, polyData1.getPolygonVertexRefs(HP1)) + + // So we need to switch: + // - HP0, HP1 + // - scaledVertices0, scaledVertices1 + // - stackIndices0, stackIndices1 + // - world0, world1 + // - shapeSpacePlane0, shapeSpacePlane1 + // - worldNormal0, worldNormal1 + // - m0to1, m1to0 + // - true, false + const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); + const PxMat33 RotT1 = findRotationMatrixFromZ(shapeSpacePlane1.n); + + if(flag) + { + if(contactPolygonPolygonExt(HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0, + HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1, + worldNormal0, m0to1_Tweaked, m1to0_Tweaked, + PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX, + contactBuffer, + true, contactGenPositionShiftVec, contactGenPositionShift)) + return true; + } + else + { + if(contactPolygonPolygonExt(HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1, + HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0, + worldNormal1, m1to0_Tweaked, m0to1_Tweaked, + PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX, + contactBuffer, + false, contactGenPositionShiftVec, contactGenPositionShift)) + return true; + } + +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + } +#endif + return false; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp new file mode 100644 index 00000000..58142f46 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactConvexMesh.cpp @@ -0,0 +1,1449 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuConvexUtilsInternal.h" +#include "GuInternal.h" +#include "GuContactPolygonPolygon.h" +#include "GuConvexEdgeFlags.h" +#include "GuSeparatingAxes.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuConvexHelper.h" +#include "GuTriangleCache.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "GuGeometryUnion.h" +#include "GuIntersectionTriangleBox.h" +#include "CmUtils.h" +#include "PsAllocator.h" +#include "GuBox.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; +using namespace intrinsics; + +#define LOCAL_TOUCHED_TRIG_SIZE 192 + +//#define USE_TRIANGLE_NORMAL +#define TEST_INTERNAL_OBJECTS + +static PX_FORCE_INLINE void projectTriangle(const PxVec3& localSpaceDirection, const PxVec3* PX_RESTRICT triangle, PxReal& min1, PxReal& max1) +{ + const PxReal dp0 = triangle[0].dot(localSpaceDirection); + const PxReal dp1 = triangle[1].dot(localSpaceDirection); + min1 = selectMin(dp0, dp1); + max1 = selectMax(dp0, dp1); + + const PxReal dp2 = triangle[2].dot(localSpaceDirection); + min1 = selectMin(min1, dp2); + max1 = selectMax(max1, dp2); +} + +#ifdef TEST_INTERNAL_OBJECTS + +static PX_FORCE_INLINE void boxSupport(const float extents[3], const PxVec3& sv, float p[3]) +{ + const PxU32* iextents = reinterpret_cast<const PxU32*>(extents); + const PxU32* isv = reinterpret_cast<const PxU32*>(&sv); + PxU32* ip = reinterpret_cast<PxU32*>(p); + + ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); + ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); + ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); +} + +#if PX_DEBUG +static const PxReal testInternalObjectsEpsilon = 1.0e-3f; +#endif + +static PX_FORCE_INLINE bool testInternalObjects(const PxVec3& localAxis0, + const PolygonalData& polyData0, + const PxVec3* PX_RESTRICT triangleInHullSpace, + float dmin) +{ + PxReal min1, max1; + projectTriangle(localAxis0, triangleInHullSpace, min1, max1); + + const float dp = polyData0.mCenter.dot(localAxis0); + + float p0[3]; + boxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float bestRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const PxReal min0 = dp - bestRadius; + const PxReal max0 = dp + bestRadius; + + const PxReal d0 = max0 - min1; + const PxReal d1 = max1 - min0; + + const float depth = selectMin(d0, d1); + if(depth>dmin) + return false; + return true; +} +#endif + +static PX_FORCE_INLINE bool testNormal( const PxVec3& sepAxis, PxReal min0, PxReal max0, + const PxVec3* PX_RESTRICT triangle, + PxReal& depth, PxReal contactDistance) +{ + PxReal min1, max1; + projectTriangle(sepAxis, triangle, min1, max1); + + if(max0+contactDistance<min1 || max1+contactDistance<min0) + return false; + + const PxReal d0 = max0 - min1; + const PxReal d1 = max1 - min0; + depth = selectMin(d0, d1); + return true; +} + +static PX_FORCE_INLINE bool testSepAxis(const PxVec3& sepAxis, + const PolygonalData& polyData0, + const PxVec3* PX_RESTRICT triangle, + const Cm::Matrix34& m0to1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal& depth, PxReal contactDistance) +{ + PxReal min0, max0; + (polyData0.mProjectHull)(polyData0, sepAxis, m0to1, convexScaling, min0, max0); + + PxReal min1, max1; + projectTriangle(sepAxis, triangle, min1, max1); + + if(max0+contactDistance < min1 || max1+contactDistance < min0) + return false; + + const PxReal d0 = max0 - min1; + const PxReal d1 = max1 - min0; + depth = selectMin(d0, d1); + return true; +} + +static bool testFacesSepAxesBackface( const PolygonalData& polyData0, + const Cm::Matrix34& /*world0*/, const Cm::Matrix34& /*world1*/, + const Cm::Matrix34& m0to1, const PxVec3& witness, + const PxVec3* PX_RESTRICT triangle, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxU32& numHullIndices, + PxU32* hullIndices_, PxReal& dmin, PxVec3& sep, PxU32& id, PxReal contactDistance, + bool idtConvexScale + ) +{ + id = PX_INVALID_U32; + + const PxU32 numHullPolys = polyData0.mNbPolygons; + const HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons; + const PxVec3* PX_RESTRICT vertices = polyData0.mVerts; + const PxVec3& trans = m0to1.p; + { + PxU32* hullIndices = hullIndices_; + + // PT: when the center of one object is inside the other object (deep penetrations) this discards everything! + // PT: when this happens, the backup procedure is used to come up with good results anyway. + // PT: it's worth having a special codepath here for identity scales, to skip all the normalizes/division. Lot faster without. + + if(idtConvexScale) + { + for(PxU32 i=0; i<numHullPolys; i++) + { + const HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + +#ifdef USE_TRIANGLE_NORMAL + if(PL.normal.dot(witness) > 0.0f) +#else + // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? + if(PL.distance(witness) < 0.0f) +#endif + continue; //backface culled + + *hullIndices++ = i; + + const PxVec3 sepAxis = m0to1.rotate(PL.n); + const PxReal dp = sepAxis.dot(trans); + + PxReal d; + if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + } + } + else + { +#ifndef USE_TRIANGLE_NORMAL + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceWitness = convexScaling % witness; +#endif + for(PxU32 i=0; i<numHullPolys; i++) + { + const HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + +#ifdef USE_TRIANGLE_NORMAL + if(PL.normal.dot(witness) > 0.0f) +#else + // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? + if(PL.distance(vertSpaceWitness) < 0.0f) +#endif + continue; //backface culled + + //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) + PxVec3 shapeSpaceNormal = convexScaling % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); + + *hullIndices++ = i; + const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal); + PxReal d; + const PxReal dp = sepAxis.dot(trans); + + const float oneOverM = 1.0f / magnitude; + if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + } + } + numHullIndices = PxU32(hullIndices - hullIndices_); + } + + // Backup + if(id == PX_INVALID_U32) + { + if(idtConvexScale) + { + for(PxU32 i=0; i<numHullPolys; i++) + { + const HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + + const PxVec3 sepAxis = m0to1.rotate(PL.n); + const PxReal dp = sepAxis.dot(trans); + + PxReal d; + if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + hullIndices_[i] = i; + } + } + else + { + for(PxU32 i=0; i<numHullPolys; i++) + { + const HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + + PxVec3 shapeSpaceNormal = convexScaling % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); + + const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal); + + PxReal d; + const PxReal dp = sepAxis.dot(trans); + + const float oneOverM = 1.0f / magnitude; + if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + hullIndices_[i] = i; + } + } + numHullIndices = numHullPolys; + } + return true; +} + +static PX_FORCE_INLINE bool edgeCulling(const PxPlane& plane, const PxVec3& p0, const PxVec3& p1, PxReal contactDistance) +{ + return plane.distance(p0)<=contactDistance || plane.distance(p1)<=contactDistance; +} + +static bool performEETests( + const PolygonalData& polyData0, + const PxU8 triFlags, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + const PxVec3* PX_RESTRICT triangle, + PxU32 numHullIndices, const PxU32* PX_RESTRICT hullIndices, + const PxPlane& localTriPlane, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxVec3& vec, PxReal& dmin, PxReal contactDistance, PxReal toleranceLength, + PxU32 id0, PxU32 /*triangleIndex*/) +{ + PX_UNUSED(toleranceLength); // Only used in Debug + + // Cull candidate triangle edges vs to hull plane + PxU32 nbTriangleAxes = 0; + PxVec3 triangleAxes[3]; + { + const HullPolygonData& P = polyData0.mPolygons[id0]; + const PxPlane& vertSpacePlane = P.mPlane; + + const PxVec3 newN = m1to0.rotate(vertSpacePlane.n); + PxPlane hullWitness(convexScaling * newN, vertSpacePlane.d - m1to0.p.dot(newN)); //technically not a fully xformed plane, just use property of x|My == Mx|y for symmetric M. + + if((triFlags & ETD_CONVEX_EDGE_01) && edgeCulling(hullWitness, triangle[0], triangle[1], contactDistance)) + triangleAxes[nbTriangleAxes++] = (triangle[0] - triangle[1]); + + if((triFlags & ETD_CONVEX_EDGE_12) && edgeCulling(hullWitness, triangle[1], triangle[2], contactDistance)) + triangleAxes[nbTriangleAxes++] = (triangle[1] - triangle[2]); + + if((triFlags & ETD_CONVEX_EDGE_20) && edgeCulling(hullWitness, triangle[2], triangle[0], contactDistance)) + triangleAxes[nbTriangleAxes++] = (triangle[2] - triangle[0]); + } + + //PxcPlane vertexSpacePlane = localTriPlane.getTransformed(m1to0); + //vertexSpacePlane.normal = convexScaling * vertexSpacePlane.normal; //technically not a fully xformed plane, just use property of x|My == Mx|y for symmetric M. + const PxVec3 newN = m1to0.rotate(localTriPlane.n); + PxPlane vertexSpacePlane(convexScaling * newN, localTriPlane.d - m1to0.p.dot(newN)); + + const PxVec3* PX_RESTRICT hullVerts = polyData0.mVerts; + + SeparatingAxes SA; + SA.reset(); + + const PxU8* PX_RESTRICT vrefBase0 = polyData0.mPolygonVertexRefs; + const HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons; + while(numHullIndices--) + { + const HullPolygonData& P = polygons[*hullIndices++]; + const PxU8* PX_RESTRICT data = vrefBase0 + P.mVRef8; + + PxU32 numEdges = nbTriangleAxes; + const PxVec3* edges = triangleAxes; + + // TODO: cheap edge culling as in convex/convex! + while(numEdges--) + { + const PxVec3& currentPolyEdge = *edges++; + + // Loop through polygon vertices == polygon edges; + PxU32 numVerts = P.mNbVerts; + for(PxU32 j = 0; j < numVerts; j++) + { + PxU32 j1 = j+1; + if(j1>=numVerts) j1 = 0; + + const PxU32 VRef0 = data[j]; + const PxU32 VRef1 = data[j1]; + + if(edgeCulling(vertexSpacePlane, hullVerts[VRef0], hullVerts[VRef1], contactDistance)) + { + const PxVec3 currentHullEdge = m0to1.rotate(convexScaling * (hullVerts[VRef0] - hullVerts[VRef1])); //matrix mult is distributive! + + PxVec3 sepAxis = currentHullEdge.cross(currentPolyEdge); + if(!Ps::isAlmostZero(sepAxis)) + SA.addAxis(sepAxis.getNormalized()); + } + } + } + } + + dmin = PX_MAX_REAL; + PxU32 numAxes = SA.getNumAxes(); + const PxVec3* PX_RESTRICT axes = SA.getAxes(); + +#ifdef TEST_INTERNAL_OBJECTS + PxVec3 triangleInHullSpace[3]; + if(numAxes) + { + triangleInHullSpace[0] = m1to0.transform(triangle[0]); + triangleInHullSpace[1] = m1to0.transform(triangle[1]); + triangleInHullSpace[2] = m1to0.transform(triangle[2]); + } +#endif + + while(numAxes--) + { + const PxVec3& currentAxis = *axes++; + +#ifdef TEST_INTERNAL_OBJECTS + const PxVec3 localAxis0 = m1to0.rotate(currentAxis); + if(!testInternalObjects(localAxis0, polyData0, triangleInHullSpace, dmin)) + { + #if PX_DEBUG + PxReal dtest; + if(testSepAxis(currentAxis, polyData0, triangle, m0to1, convexScaling, dtest, contactDistance)) + { + PX_ASSERT(dtest + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + PxReal d; + if(!testSepAxis(currentAxis, polyData0, triangle, + m0to1, convexScaling, d, contactDistance)) + { + return false; + } + + if(d < dmin) + { + dmin = d; + vec = currentAxis; + } + } + return true; +} + +static bool triangleConvexTest( const PolygonalData& polyData0, + const PxU8 triFlags, + PxU32 index, const PxVec3* PX_RESTRICT localPoints, + const PxPlane& localPlane, + const PxVec3& groupCenterHull, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, PxReal toleranceLength, + PxVec3& groupAxis, PxReal& groupMinDepth, bool& faceContact, + bool idtConvexScale + ) +{ + PxU32 id0 = PX_INVALID_U32; + PxReal dmin0 = PX_MAX_REAL; + PxVec3 vec0; + + PxU32 numHullIndices = 0; + PxU32* PX_RESTRICT const hullIndices = reinterpret_cast<PxU32*>(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); + + // PT: we test the hull normals first because they don't need any hull projection. If we can early exit thanks + // to those, we completely avoid all hull projections. + bool status = testFacesSepAxesBackface(polyData0, world0, world1, m0to1, groupCenterHull, localPoints, + convexScaling, numHullIndices, hullIndices, dmin0, vec0, id0, contactDistance, idtConvexScale); + if(!status) + return false; + + groupAxis = PxVec3(0); + groupMinDepth = PX_MAX_REAL; + + const PxReal eps = 0.0001f; // DE7748 + + //Test in mesh-space + PxVec3 sepAxis; + PxReal depth; + + { + // Test triangle normal + PxReal d; + if(!testSepAxis(localPlane.n, polyData0, localPoints, + m0to1, convexScaling, d, contactDistance)) + return false; + + if(d<dmin0+eps) +// if(d<dmin0) + { + depth = d; + sepAxis = localPlane.n; + faceContact = true; + } + else + { + depth = dmin0; + sepAxis = vec0; + faceContact = false; + } + } + + if(depth < groupMinDepth) + { + groupMinDepth = depth; + groupAxis = world1.rotate(sepAxis); + } + + if(!performEETests(polyData0, triFlags, m0to1, m1to0, + localPoints, + numHullIndices, hullIndices, localPlane, + convexScaling, sepAxis, depth, contactDistance, toleranceLength, + id0, index)) + return false; + + if(depth < groupMinDepth) + { + groupMinDepth = depth; + groupAxis = world1.rotate(sepAxis); + faceContact = false; + } + + return true; +} + +namespace +{ + struct ConvexMeshContactGeneration + { + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& mDelayedContacts; + Gu::CacheMap<Gu::CachedEdge, 128> mEdgeCache; + Gu::CacheMap<Gu::CachedVertex, 128> mVertCache; + + const Cm::Matrix34 m0to1; + const Cm::Matrix34 m1to0; + + PxVec3 mHullCenterMesh; + PxVec3 mHullCenterWorld; + + const PolygonalData& mPolyData0; + const Cm::Matrix34& mWorld0; + const Cm::Matrix34& mWorld1; + + const Cm::FastVertex2ShapeScaling& mConvexScaling; + + PxReal mContactDistance; + PxReal mToleranceLength; + bool mIdtMeshScale, mIdtConvexScale; + PxReal mCCDEpsilon; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + bool mAnyHits; + + ConvexMeshContactGeneration( + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer + ); + + void processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + void generateLastContacts(); + + bool generateContacts( + const PxPlane& localPlane, + const PxVec3* PX_RESTRICT localPoints, + const PxVec3& triCenter, PxVec3& groupAxis, + PxReal groupMinDepth, PxU32 index) const; + + private: + ConvexMeshContactGeneration& operator=(const ConvexMeshContactGeneration&); + }; + +// 17 entries. 1088/17 = 64 triangles in the local array +struct SavedContactData +{ + PxU32 mTriangleIndex; + PxVec3 mVerts[3]; + PxU32 mInds[3]; + PxVec3 mGroupAxis; + PxReal mGroupMinDepth; +}; +} + +ConvexMeshContactGeneration::ConvexMeshContactGeneration( + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer +) : + mDelayedContacts(delayedContacts), + m0to1 (t0to1), + m1to0 (t1to0), + mPolyData0 (polyData0), + mWorld0 (world0), + mWorld1 (world1), + mConvexScaling (convexScaling), + mContactDistance(contactDistance), + mToleranceLength(toleranceLength), + mIdtConvexScale (idtConvexScale), + mCCDEpsilon (cCCDEpsilon), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer) +{ + delayedContacts.forceSize_Unsafe(0); + mAnyHits = false; + + // Hull center in local space + const PxVec3& hullCenterLocal = mPolyData0.mCenter; + // Hull center in mesh space + mHullCenterMesh = m0to1.transform(hullCenterLocal); + // Hull center in world space + mHullCenterWorld = mWorld0.transform(hullCenterLocal); +} + +struct ConvexMeshContactGenerationCallback : MeshHitCallback<PxRaycastHit> +{ + ConvexMeshContactGeneration mGeneration; + const Cm::FastVertex2ShapeScaling& mMeshScaling; + const PxU8* PX_RESTRICT mExtraTrigData; + bool mIdtMeshScale; + const TriangleMesh* mMeshData; + const BoxPadded& mBox; + + ConvexMeshContactGenerationCallback( + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const TriangleMesh* meshData, + const PxU8* PX_RESTRICT extraTrigData, + const Cm::FastVertex2ShapeScaling& meshScaling, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtMeshScale, bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, + const BoxPadded& box + ) : + MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE), + mGeneration (delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, transform1, contactBuffer), + mMeshScaling (meshScaling), + mExtraTrigData (extraTrigData), + mIdtMeshScale (idtMeshScale), + mMeshData (meshData), + mBox (box) + { + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + // PT: this one is safe because incoming vertices from midphase are always safe to V4Load (by design) + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox(mBox, v0, v1, v2)) + return true; + + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, mIdtMeshScale, mMeshScaling); + + const PxU32 triangleIndex = hit.faceIndex; + + const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); + mGeneration.processTriangle(verts, triangleIndex, extraData, vinds); + return true; + } + +protected: + ConvexMeshContactGenerationCallback &operator=(const ConvexMeshContactGenerationCallback &); +}; + +bool ConvexMeshContactGeneration::generateContacts( + const PxPlane& localPlane, + const PxVec3* PX_RESTRICT localPoints, + const PxVec3& triCenter, PxVec3& groupAxis, + PxReal groupMinDepth, PxU32 index) const +{ + const PxVec3 worldGroupCenter = mWorld1.transform(triCenter); + const PxVec3 deltaC = mHullCenterWorld - worldGroupCenter; + if(deltaC.dot(groupAxis) < 0.0f) + groupAxis = -groupAxis; + + const PxU32 id = (mPolyData0.mSelectClosestEdgeCB)(mPolyData0, mConvexScaling, mWorld0.rotateTranspose(-groupAxis)); + + const HullPolygonData& HP = mPolyData0.mPolygons[id]; + PX_ALIGN(16, PxPlane) shapeSpacePlane0; + if(mIdtConvexScale) + V4StoreA(V4LoadU(&HP.mPlane.n.x), &shapeSpacePlane0.n.x); + else + mConvexScaling.transformPlaneToShapeSpace(HP.mPlane.n, HP.mPlane.d, shapeSpacePlane0.n, shapeSpacePlane0.d); + + const PxVec3 hullNormalWorld = mWorld0.rotate(shapeSpacePlane0.n); + + const PxReal d0 = PxAbs(hullNormalWorld.dot(groupAxis)); + + const PxVec3 triNormalWorld = mWorld1.rotate(localPlane.n); + const PxReal d1 = PxAbs(triNormalWorld.dot(groupAxis)); + const bool d0biggerd1 = d0 > d1; + +////////////////////NEW DIST HANDLING////////////////////// + //TODO: skip this if there is no dist involved! + +PxReal separation = - groupMinDepth; //convert to real distance. + +separation = fsel(separation, separation, 0.0f); //don't do anything when penetrating! + +//printf("\nseparation = %f", separation); + +PxReal contactGenPositionShift = separation + mCCDEpsilon; //if we're at a distance, shift so we're within penetration. + +PxVec3 contactGenPositionShiftVec = groupAxis * contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. + +//note: for some reason this has to change sign! + +//this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that +//the solver converges to MSP penetration, while we want it to converge to 0 penetration. +//to real distance: +// PxReal polyPolySeparationShift = separation; //(+ or - depending on which way normal goes) + +//The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. + +//TODO: make these overwrite orig location if its safe to do so. + +Cm::Matrix34 world0_(mWorld0); +PxTransform transform0_(mTransform0); + +world0_.p -= contactGenPositionShiftVec; +transform0_.p = world0_.p; //reset this too. + +PxTransform t0to1_ = mTransform1.transformInv(transform0_); +PxTransform t1to0_ = transform0_.transformInv(mTransform1); +Cm::Matrix34 m0to1_(t0to1_); +Cm::Matrix34 m1to0_(t1to0_); + + PxVec3* scaledVertices0; + PxU8* stackIndices0; + GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, mIdtConvexScale, HP.mNbVerts, mConvexScaling, mPolyData0.mVerts, mPolyData0.getPolygonVertexRefs(HP)) + + const PxU8 indices[3] = {0, 1, 2}; + + const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); + const PxMat33 RotT1 = findRotationMatrixFromZ(localPlane.n); + + if(d0biggerd1) + { + if(contactPolygonPolygonExt( + HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, + RotT0, + 3, localPoints, indices, mWorld1, localPlane, + RotT1, + hullNormalWorld, m0to1_, m1to0_, PXC_CONTACT_NO_FACE_INDEX, index, + mContactBuffer, + true, + contactGenPositionShiftVec, contactGenPositionShift)) + { + return true; + } + } + else + { + if(contactPolygonPolygonExt( + 3, localPoints, indices, mWorld1, localPlane, + RotT1, + HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, + RotT0, + triNormalWorld, m1to0_, m0to1_, PXC_CONTACT_NO_FACE_INDEX, index, + mContactBuffer, + false, + contactGenPositionShiftVec, contactGenPositionShift)) + { + return true; + } + } + return false; +} + +enum FeatureCode +{ + FC_VERTEX0, + FC_VERTEX1, + FC_VERTEX2, + FC_EDGE01, + FC_EDGE12, + FC_EDGE20, + FC_FACE, + + FC_UNDEFINED +}; + +static FeatureCode computeFeatureCode(const PxVec3& point, const PxVec3* verts) +{ + const PxVec3& triangleOrigin = verts[0]; + const PxVec3 triangleEdge0 = verts[1] - verts[0]; + const PxVec3 triangleEdge1 = verts[2] - verts[0]; + + const PxVec3 kDiff = triangleOrigin - point; + const PxReal fA00 = triangleEdge0.magnitudeSquared(); + const PxReal fA01 = triangleEdge0.dot(triangleEdge1); + const PxReal fA11 = triangleEdge1.magnitudeSquared(); + const PxReal fB0 = kDiff.dot(triangleEdge0); + const PxReal fB1 = kDiff.dot(triangleEdge1); + const PxReal fDet = PxAbs(fA00*fA11 - fA01*fA01); + const PxReal u = fA01*fB1-fA11*fB0; + const PxReal v = fA01*fB0-fA00*fB1; + + FeatureCode fc = FC_UNDEFINED; + + if(u + v <= fDet) + { + if(u < 0.0f) + { + if(v < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + if(-fB0 >= fA00) + fc = FC_VERTEX1; + else + fc = FC_EDGE01; + } + else + { + if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB1 >= fA11) + fc = FC_VERTEX2; + else + fc = FC_EDGE20; + } + } + else // region 3 + { + if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB1 >= fA11) + fc = FC_VERTEX2; + else + fc = FC_EDGE20; + } + } + else if(v < 0.0f) // region 5 + { + if(fB0 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB0 >= fA00) + fc = FC_VERTEX1; + else + fc = FC_EDGE01; + } + else // region 0 + { + // minimum at interior PxVec3 + if(fDet==0.0f) + fc = FC_VERTEX0; + else + fc = FC_FACE; + } + } + else + { + PxReal fTmp0, fTmp1, fNumer, fDenom; + + if(u < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX1; + else + fc = FC_EDGE12; + } + else + { + if(fTmp1 <= 0.0f) + fc = FC_VERTEX2; + else if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else + fc = FC_EDGE20; + } + } + else if(v < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX2; + else + fc = FC_EDGE12; + } + else + { + if(fTmp1 <= 0.0f) + fc = FC_VERTEX1; + else if(fB0 >= 0.0f) + fc = FC_VERTEX0; + else + fc = FC_EDGE01; + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + fc = FC_VERTEX2; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX1; + else + fc = FC_EDGE12; + } + } + } + return fc; +} + + +//static bool validateVertex(PxU32 vref, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) +//{ +// PxU32 previous = 0xffffffff; +// for(PxU32 i=0;i<count;i++) +// { +// if(contacts[i].internalFaceIndex1==previous) +// continue; +// previous = contacts[i].internalFaceIndex1; +// +// const TriangleIndices T(meshData, contacts[i].internalFaceIndex1); +// if( T.mVRefs[0]==vref +// || T.mVRefs[1]==vref +// || T.mVRefs[2]==vref) +// return false; +// } +// return true; +//} + + +//static PX_FORCE_INLINE bool testEdge(PxU32 vref0, PxU32 vref1, PxU32 tvref0, PxU32 tvref1) +//{ +// if(tvref0>tvref1) +// Ps::swap(tvref0, tvref1); +// +// if(tvref0==vref0 && tvref1==vref1) +// return false; +// return true; +//} + +//static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) +//{ +// if(vref0>vref1) +// Ps::swap(vref0, vref1); +// +// PxU32 previous = 0xffffffff; +// for(PxU32 i=0;i<count;i++) +// { +// if(contacts[i].internalFaceIndex1==previous) +// continue; +// previous = contacts[i].internalFaceIndex1; +// +// const TriangleIndices T(meshData, contacts[i].internalFaceIndex1); +// +///* if(T.mVRefs[0]==vref0 || T.mVRefs[0]==vref1) +// return false; +// if(T.mVRefs[1]==vref0 || T.mVRefs[1]==vref1) +// return false; +// if(T.mVRefs[2]==vref0 || T.mVRefs[2]==vref1) +// return false;*/ +// // PT: wow, this was wrong??? ###FIX +// if(!testEdge(vref0, vref1, T.mVRefs[0], T.mVRefs[1])) +// return false; +// if(!testEdge(vref0, vref1, T.mVRefs[1], T.mVRefs[2])) +// return false; +// if(!testEdge(vref0, vref1, T.mVRefs[2], T.mVRefs[0])) +// return false; +// } +// return true; +//} + +//static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32* vertIndices, const PxU32 nbIndices) +//{ +// if(vref0>vref1) +// Ps::swap(vref0, vref1); +// +// for(PxU32 i=0;i<nbIndices;i+=3) +// { +// if(!testEdge(vref0, vref1, vertIndices[i+0], vertIndices[i+1])) +// return false; +// if(!testEdge(vref0, vref1, vertIndices[i+1], vertIndices[i+2])) +// return false; +// if(!testEdge(vref0, vref1, vertIndices[i+2], vertIndices[i+0])) +// return false; +// } +// return true; +//} + +//#endif + +void ConvexMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + const PxPlane localPlane(verts[0], verts[1], verts[2]); + + // Backface culling + if(localPlane.distance(mHullCenterMesh)<0.0f) +// if(localPlane.normal.dot(mHullCenterMesh - T.mVerts[0]) <= 0.0f) + return; + + ////////////////////////////////////////////////////////////////////////// + + const PxVec3 triCenter = (verts[0] + verts[1] + verts[2])*(1.0f/3.0f); + + // Group center in hull space +#ifdef USE_TRIANGLE_NORMAL + const PxVec3 groupCenterHull = m1to0.rotate(localPlane.normal); +#else + const PxVec3 groupCenterHull = m1to0.transform(triCenter); +#endif + ////////////////////////////////////////////////////////////////////////// + + PxVec3 groupAxis; + PxReal groupMinDepth; + bool faceContact; + if(!triangleConvexTest( mPolyData0, triFlags, + triangleIndex, verts, + localPlane, + groupCenterHull, + mWorld0, mWorld1, m0to1, m1to0, + mConvexScaling, + mContactDistance, mToleranceLength, + groupAxis, groupMinDepth, faceContact, + mIdtConvexScale + )) + return; + + if(faceContact) + { + // PT: generate face contacts immediately to save memory & avoid recomputing triangle data later + if(generateContacts( + localPlane, + verts, + triCenter, groupAxis, + groupMinDepth, triangleIndex)) + { + mAnyHits = true; + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1])); + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[2])); + mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2])); + mVertCache.addData(CachedVertex(vertInds[0])); + mVertCache.addData(CachedVertex(vertInds[1])); + mVertCache.addData(CachedVertex(vertInds[2])); + } + else + { + int stop=1; + (void)stop; + } + } + else + { + const PxU32 nb = sizeof(SavedContactData)/sizeof(PxU32); + + // PT: no "pushBack" please (useless data copy + LHS) + PxU32 newSize = nb + mDelayedContacts.size(); + mDelayedContacts.reserve(newSize); + SavedContactData* PX_RESTRICT cd = reinterpret_cast<SavedContactData*>(mDelayedContacts.end()); + mDelayedContacts.forceSize_Unsafe(newSize); + + cd->mTriangleIndex = triangleIndex; + cd->mVerts[0] = verts[0]; + cd->mVerts[1] = verts[1]; + cd->mVerts[2] = verts[2]; + cd->mInds[0] = vertInds[0]; + cd->mInds[1] = vertInds[1]; + cd->mInds[2] = vertInds[2]; + cd->mGroupAxis = groupAxis; + cd->mGroupMinDepth = groupMinDepth; + } +} + +void ConvexMeshContactGeneration::generateLastContacts() +{ + // Process delayed contacts + PxU32 nbEntries = mDelayedContacts.size(); + if(nbEntries) + { + nbEntries /= sizeof(SavedContactData)/sizeof(PxU32); + + // PT: TODO: replicate this fix in sphere-vs-mesh ###FIX + //const PxU32 count = mContactBuffer.count; + //const ContactPoint* PX_RESTRICT contacts = mContactBuffer.contacts; + + const SavedContactData* PX_RESTRICT cd = reinterpret_cast<const SavedContactData*>(mDelayedContacts.begin()); + for(PxU32 i=0;i<nbEntries;i++) + { + const SavedContactData& currentContact = cd[i]; + + const PxU32 triangleIndex = currentContact.mTriangleIndex; + + // PT: unfortunately we must recompute this triangle-data here. + // PT: TODO: find a way not to +// const TriangleVertices T(*mMeshData, mMeshScaling, triangleIndex); +// const TriangleIndices T(*mMeshData, triangleIndex); + + const PxU32 ref0 = currentContact.mInds[0]; + const PxU32 ref1 = currentContact.mInds[1]; + const PxU32 ref2 = currentContact.mInds[2]; + + // PT: TODO: why bother with the feature code at all? Use edge cache directly? +// const FeatureCode FC = computeFeatureCode(mHullCenterMesh, T.mVerts); + const FeatureCode FC = computeFeatureCode(mHullCenterMesh, currentContact.mVerts); + + bool generateContact = false; + switch(FC) + { + // PT: trying the same as in sphere-mesh here + case FC_VERTEX0: + generateContact = !mVertCache.contains(CachedVertex(ref0)); + break; + case FC_VERTEX1: + generateContact =!mVertCache.contains(CachedVertex(ref1)); + break; + case FC_VERTEX2: + generateContact = !mVertCache.contains(CachedVertex(ref2)); + break; + case FC_EDGE01: + generateContact = !mEdgeCache.contains(CachedEdge(ref0, ref1)); + break; + case FC_EDGE12: + generateContact = !mEdgeCache.contains(CachedEdge(ref1, ref2)); + break; + case FC_EDGE20: + generateContact = !mEdgeCache.contains(CachedEdge(ref0, ref2)); + break; + case FC_FACE: + generateContact = true; + break; + case FC_UNDEFINED: + break; + }; + + if(!generateContact) + continue; + +// const PxcPlane localPlane(T.mVerts[0], T.mVerts[1], T.mVerts[2]); + const PxPlane localPlane(currentContact.mVerts[0], currentContact.mVerts[1], currentContact.mVerts[2]); +// const PxVec3 triCenter = (T.mVerts[0] + T.mVerts[1] + T.mVerts[2])*(1.0f/3.0f); + const PxVec3 triCenter = (currentContact.mVerts[0] + currentContact.mVerts[1] + currentContact.mVerts[2])*(1.0f/3.0f); + + PxVec3 groupAxis = currentContact.mGroupAxis; + if(generateContacts( + localPlane, +// T.mVerts, + currentContact.mVerts, + triCenter, groupAxis, + currentContact.mGroupMinDepth, triangleIndex)) + { + mAnyHits = true; + + //We don't add the edges to the data - this is important because we don't want to reject triangles + //because we generated an edge contact with an adjacent triangle + /*mEdgeCache.addData(CachedEdge(ref0, ref1)); + mEdgeCache.addData(CachedEdge(ref0, ref2)); + mEdgeCache.addData(CachedEdge(ref1, ref2)); + mVertCache.addData(CachedVertex(ref0)); + mVertCache.addData(CachedVertex(ref1)); + mVertCache.addData(CachedVertex(ref2));*/ + + } + } + } +} + +///////////// + +static bool contactHullMesh2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxTriangleMeshGeometryLL& shape1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale) +{ + //Just a sanity-check in debug-mode + PX_ASSERT(shape1.getType() == PxGeometryType::eTRIANGLEMESH); + //////////////////// + + // Compute matrices + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + // Compute relative transforms + const PxTransform t0to1 = transform1.transformInv(transform0); + const PxTransform t1to0 = transform0.transformInv(transform1); + + BoxPadded hullOBB; + computeHullOBB(hullOBB, hullAABB, params.mContactDistance, world0, world1, meshScaling, idtMeshScale); + + // Setup the collider + const TriangleMesh* PX_RESTRICT meshData = shape1.meshData; + + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE> delayedContacts; + + ConvexMeshContactGenerationCallback blockCallback( + delayedContacts, + t0to1, t1to0, polyData0, world0, world1, meshData, meshData->getExtraTrigData(), meshScaling, + convexScaling, params.mContactDistance, params.mToleranceLength, + idtMeshScale, idtConvexScale, params.mMeshContactMargin, + transform0, transform1, + contactBuffer, hullOBB + ); + + Midphase::intersectOBB(meshData, hullOBB, blockCallback, false); + + blockCallback.mGeneration.generateLastContacts(); + + return blockCallback.mGeneration.mAnyHits; +} + +///////////// + +bool Gu::contactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData0; + const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); + + return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, meshScaling, idtScaleConvex, idtScaleMesh); +} + +bool Gu::contactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling idtScaling; + return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, meshScaling, true, idtScaleMesh); +} + +///////////// + +namespace +{ +struct ConvexVsHeightfieldContactGenerationCallback : EntityReport<PxU32> +{ + ConvexMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + ConvexVsHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer + ) : mGeneration(delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, + transform1, contactBuffer), + mHfUtil(hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + PxTriangle currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mGeneration.mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + triFlags |= (1 << (a+3)); + } + mGeneration.processTriangle(currentTriangle.verts, triangleIndex, triFlags, vertIndices); + } + return true; + } + +protected: + ConvexVsHeightfieldContactGenerationCallback &operator=(const ConvexVsHeightfieldContactGenerationCallback &); +}; +} + +static bool contactHullHeightfield2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxHeightFieldGeometry& shape1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale) +{ + //We need to create a callback that fills triangles from the HF + + const HeightField& hf = *static_cast<HeightField*>(shape1.heightField); + + HeightFieldUtil hfUtil(shape1, hf); + + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + //////////////////// + + // Compute relative transforms + const PxTransform t0to1 = transform1.transformInv(transform0); + const PxTransform t1to0 = transform0.transformInv(transform1); + + Ps::InlineArray<PxU32, LOCAL_CONTACTS_SIZE> delayedContacts; + + ConvexVsHeightfieldContactGenerationCallback blockCallback(hfUtil, delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, params.mContactDistance, params.mToleranceLength, + idtConvexScale, params.mMeshContactMargin, transform0, transform1, contactBuffer); + + hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return blockCallback.mGeneration.mAnyHits; +} + +bool Gu::contactConvexHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods + const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>(); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData0; + const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); + const PxVec3 inflation(params.mContactDistance); + hullAABB.minimum -= inflation; + hullAABB.maximum += inflation; + + return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, idtScaleConvex); +} + +bool Gu::contactBoxHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods + const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>(); + + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + const PxVec3 inflatedExtents = shapeBox.halfExtents + PxVec3(params.mContactDistance); + + const PxBounds3 hullAABB = PxBounds3(-inflatedExtents, inflatedExtents); + + const Cm::FastVertex2ShapeScaling idtScaling; + + return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, true); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h new file mode 100644 index 00000000..86101dc3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactMethodImpl.h @@ -0,0 +1,174 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTMETHODIMPL_H +#define GU_CONTACTMETHODIMPL_H + +#include "foundation/PxAssert.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "collision/PxCollisionDefs.h" + +namespace physx +{ +namespace Cm +{ + class RenderOutput; +} + +namespace Gu +{ + class GeometryUnion; + class ContactBuffer; + struct NarrowPhaseParams; + class PersistentContactManifold; + class MultiplePersistentContactManifold; + + enum ManifoldFlags + { + IS_MANIFOLD = (1<<0), + IS_MULTI_MANIFOLD = (1<<1) + }; + + struct Cache : public PxCache + { + Cache() + { + } + + PX_FORCE_INLINE void setManifold(void* manifold) + { + mCachedData = reinterpret_cast<PxU8*>(manifold); + mManifoldFlags |= IS_MANIFOLD; + } + + PX_FORCE_INLINE void setMultiManifold(void* manifold) + { + mCachedData = reinterpret_cast<PxU8*>(manifold); + mManifoldFlags |= IS_MANIFOLD|IS_MULTI_MANIFOLD; + } + + PX_FORCE_INLINE PxU8 isManifold() const + { + return PxU8(mManifoldFlags & IS_MANIFOLD); + } + + PX_FORCE_INLINE PxU8 isMultiManifold() const + { + return PxU8(mManifoldFlags & IS_MULTI_MANIFOLD); + } + + PX_FORCE_INLINE PersistentContactManifold& getManifold() + { + PX_ASSERT(isManifold()); + PX_ASSERT(!isMultiManifold()); + PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0); + return *reinterpret_cast<PersistentContactManifold*>(mCachedData); + } + + PX_FORCE_INLINE MultiplePersistentContactManifold& getMultipleManifold() + { + PX_ASSERT(isManifold()); + PX_ASSERT(isMultiManifold()); + PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0); + return *reinterpret_cast<MultiplePersistentContactManifold*>(mCachedData); + } + }; +} + +#define GU_CONTACT_METHOD_ARGS \ + const Gu::GeometryUnion& shape0, \ + const Gu::GeometryUnion& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const Gu::NarrowPhaseParams& params, \ + Gu::Cache& cache, \ + Gu::ContactBuffer& contactBuffer, \ + Cm::RenderOutput* renderOutput + +namespace Gu +{ + PX_PHYSX_COMMON_API bool contactSphereSphere(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactSphereBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexConvex(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSphereMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexMesh(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSphereHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexHeightfield(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSpherePlane(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS); + + //Legacy heightfield code path + PX_PHYSX_COMMON_API bool legacyContactSphereHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool legacyContactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool legacyContactBoxHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool legacyContactConvexHeightfield(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp new file mode 100644 index 00000000..bd46b296 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneBox.cpp @@ -0,0 +1,128 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "CmMatrix34.h" + +#include "PsUtilities.h" +#include "foundation/PxUnionCast.h" + +namespace physx +{ +namespace Gu +{ +bool contactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get<const PxPlaneGeometry>(); + const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>(); + + const PxVec3 negPlaneNormal = -transform0.q.getBasisVector0(); + + //Make sure we have a normalized plane + //PX_ASSERT(PxAbs(shape0.mNormal.magnitudeSquared() - 1.0f) < 0.000001f); + + Cm::Matrix34 boxMatrix(transform1); + Cm::Matrix34 boxToPlane(transform0.transformInv(transform1)); + + PxVec3 point; + + PX_ASSERT(contactBuffer.count==0); + +/* for(int vx=-1; vx<=1; vx+=2) + for(int vy=-1; vy<=1; vy+=2) + for(int vz=-1; vz<=1; vz+=2) + { + //point = boxToPlane.transform(PxVec3(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz)); + //PxReal planeEq = point.x; + //Optimized a bit + point.set(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz); + const PxReal planeEq = boxToPlane.m.column0.x*point.x + boxToPlane.m.column1.x*point.y + boxToPlane.m.column2.x*point.z + boxToPlane.p.x; + + if(planeEq <= contactDistance) + { + contactBuffer.contact(boxMatrix.transform(point), negPlaneNormal, planeEq); + + //no point in making more than 4 contacts. + if (contactBuffer.count >= 6) //was: 4) actually, with strong interpenetration more than just the bottom surface goes through, + //and we want to find the *deepest* 4 vertices, really. + return true; + } + }*/ + + // PT: the above code is shock full of LHS/FCMPs. And there's no point in limiting the number of contacts to 6 when the max possible is 8. + + const PxReal limit = params.mContactDistance - boxToPlane.p.x; + const PxReal dx = shapeBox.halfExtents.x; + const PxReal dy = shapeBox.halfExtents.y; + const PxReal dz = shapeBox.halfExtents.z; + const PxReal bxdx = boxToPlane.m.column0.x * dx; + const PxReal bxdy = boxToPlane.m.column1.x * dy; + const PxReal bxdz = boxToPlane.m.column2.x * dz; + + PxReal depths[8]; + depths[0] = bxdx + bxdy + bxdz - limit; + depths[1] = bxdx + bxdy - bxdz - limit; + depths[2] = bxdx - bxdy + bxdz - limit; + depths[3] = bxdx - bxdy - bxdz - limit; + depths[4] = - bxdx + bxdy + bxdz - limit; + depths[5] = - bxdx + bxdy - bxdz - limit; + depths[6] = - bxdx - bxdy + bxdz - limit; + depths[7] = - bxdx - bxdy - bxdz - limit; + + //const PxU32* binary = reinterpret_cast<const PxU32*>(depths); + const PxU32* binary = PxUnionCast<PxU32*, PxF32*>(depths); + + if(binary[0] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, dz)), negPlaneNormal, depths[0] + params.mContactDistance); + if(binary[1] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, -dz)), negPlaneNormal, depths[1] + params.mContactDistance); + if(binary[2] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, dz)), negPlaneNormal, depths[2] + params.mContactDistance); + if(binary[3] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, -dz)), negPlaneNormal, depths[3] + params.mContactDistance); + if(binary[4] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, dz)), negPlaneNormal, depths[4] + params.mContactDistance); + if(binary[5] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, -dz)), negPlaneNormal, depths[5] + params.mContactDistance); + if(binary[6] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, dz)), negPlaneNormal, depths[6] + params.mContactDistance); + if(binary[7] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, -dz)), negPlaneNormal, depths[7] + params.mContactDistance); + + return contactBuffer.count > 0; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp new file mode 100644 index 00000000..92a917a3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneCapsule.cpp @@ -0,0 +1,80 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuInternal.h" +#include "GuSegment.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get<const PxPlaneGeometry>(); + const PxCapsuleGeometry& shapeCapsule = shape1.get<const PxCapsuleGeometry>(); + + const PxTransform capsuleToPlane = transform0.transformInv(transform1); + + //Capsule in plane space + Segment segment; + getCapsuleSegment(capsuleToPlane, shapeCapsule, segment); + + const PxVec3 negPlaneNormal = transform0.q.getBasisVector0(); + + bool contact = false; + + const PxReal separation0 = segment.p0.x - shapeCapsule.radius; + const PxReal separation1 = segment.p1.x - shapeCapsule.radius; + if(separation0 <= params.mContactDistance) + { + const PxVec3 temp(segment.p0.x - shapeCapsule.radius, segment.p0.y, segment.p0.z); + const PxVec3 point = transform0.transform(temp); + contactBuffer.contact(point, -negPlaneNormal, separation0); + contact = true; + } + + if(separation1 <= params.mContactDistance) + { + const PxVec3 temp(segment.p1.x - shapeCapsule.radius, segment.p1.y, segment.p1.z); + const PxVec3 point = transform0.transform(temp); + contactBuffer.contact(point, -negPlaneNormal, separation1); + contact = true; + } + return contact; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp new file mode 100644 index 00000000..682186c3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPlaneConvex.cpp @@ -0,0 +1,101 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuConvexMeshData.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "CmScaling.h" + + +namespace physx +{ +namespace Gu +{ +bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get<const PxPlaneGeometry>(); + const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>(); + + const PxVec3* PX_RESTRICT hullVertices = shapeConvex.hullData->getHullVertices(); + PxU32 numHullVertices = shapeConvex.hullData->mNbHullVertices; +// Ps::prefetch128(hullVertices); + + // Plane is implicitly <1,0,0> 0 in localspace + Cm::Matrix34 convexToPlane (transform0.transformInv(transform1)); + PxMat33 convexToPlane_rot(convexToPlane[0], convexToPlane[1], convexToPlane[2] ); + + bool idtScale = shapeConvex.scale.isIdentity(); + Cm::FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor + if(!idtScale) + convexScaling.init(shapeConvex.scale); + + convexToPlane = Cm::Matrix34( convexToPlane_rot * convexScaling.getVertex2ShapeSkew(), convexToPlane[3] ); + + //convexToPlane = context.mVertex2ShapeSkew[1].getVertex2WorldSkew(convexToPlane); + + const Cm::Matrix34 planeToW (transform0); + + // This is rather brute-force + + bool status = false; + + const PxVec3 contactNormal = -planeToW.m.column0; + + while(numHullVertices--) + { + const PxVec3& vertex = *hullVertices++; +// if(numHullVertices) +// Ps::prefetch128(hullVertices); + + const PxVec3 pointInPlane = convexToPlane.transform(vertex); //TODO: this multiply could be factored out! + if(pointInPlane.x <= params.mContactDistance) + { +// const PxVec3 pointInW = planeToW.transform(pointInPlane); +// contactBuffer.contact(pointInW, -planeToW.m.column0, pointInPlane.x); + status = true; + Gu::ContactPoint* PX_RESTRICT pt = contactBuffer.contact(); + if(pt) + { + pt->normal = contactNormal; + pt->point = planeToW.transform(pointInPlane); + pt->separation = pointInPlane.x; + pt->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + } + } + return status; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp new file mode 100644 index 00000000..33190da8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.cpp @@ -0,0 +1,861 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxMemory.h" +#include "GuContactPolygonPolygon.h" +#include "GuShapeConvex.h" +#include "GuContactBuffer.h" +#include "PsMathUtils.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +#define CONTACT_REDUCTION + +/* +void gVisualizeLocalLine(const PxVec3& a, const PxVec3& b, const Cm::Matrix34& m, PxsContactManager& manager) //temp debug +{ + Cm::RenderOutput out = manager.getContext()->getRenderOutput(); + out << 0xffffff << m << Cm::RenderOutput::LINES << a << b; +} +*/ + +#ifdef CONTACT_REDUCTION +static PX_FORCE_INLINE PxReal dot2D(const PxVec3& v0, const PxVec3& v1) +{ + return v0.x * v1.x + v0.y * v1.y; +} + +static void ContactReductionAllIn( ContactBuffer& contactBuffer, PxU32 nbExistingContacts, PxU32 numIn, + const PxMat33& rotT, + const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices) +{ + // Number of contacts created by current call + const PxU32 nbNewContacts = contactBuffer.count - nbExistingContacts; + + if(nbNewContacts<=4) + return; // no reduction for less than 4 verts + + // We have 3 different numbers here: + // - numVerts = number of vertices in the convex polygon we're dealing with + // - numIn = number of those that were "inside" the other convex polygon (should be <= numVerts) + // - contactBuffer.count = total number of contacts *from both polygons* (that's the catch here) + // The fast path can only be chosen when the contact buffer contains all the verts from current polygon, + // i.e. when contactBuffer.count == numIn == numVerts + + Gu::ContactPoint* PX_RESTRICT ctcs = contactBuffer.contacts + nbExistingContacts; + + if(numIn == nbNewContacts) + { + // Codepath 1: all vertices generated a contact + + PxReal deepestSeparation = ctcs[0].separation; + PxU32 deepestIndex = 0; + for(PxU32 i=1; i<nbNewContacts; ++i) + { + if(deepestSeparation > ctcs[i].separation) + { + deepestSeparation = ctcs[i].separation; + deepestIndex = i; + } + } + + PxU32 index = 0; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + bool needsExtraPoint = true; + for(PxU32 i=0;i<4;i++) + { + const PxU32 contactIndex = index>>16; + ctcs[i] = ctcs[contactIndex]; + if(contactIndex==deepestIndex) + needsExtraPoint = false; + index += step; + } + + if(needsExtraPoint) + { + ctcs[4] = ctcs[deepestIndex]; + contactBuffer.count = nbExistingContacts + 5; + } + else + { + contactBuffer.count = nbExistingContacts + 4; + } + +/* PT: TODO: investigate why this one does not work + PxU32 index = deepestIndex<<16; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + for(PxU32 i=0;i<4;i++) + { + PxU32 contactIndex = index>>16; + if(contactIndex>=numIn) + contactIndex -= numIn; + ctcs[i] = ctcs[contactIndex]; + index += step; + } + contactBuffer.count = nbExistingContacts + 4;*/ + } + else + { + // Codepath 2: all vertices are "in" but only some of them generated a contact + + // WARNING: this path doesn't work when the buffer contains vertices from both polys. + + // TODO: precompute those axes + const PxU32 nbAxes = 8; + PxVec3 dirs[nbAxes]; + float angle = 0.0f; + const float angleStep = Ps::degToRad(180.0f/float(nbAxes)); + for(PxU32 i=0;i<nbAxes;i++) + { + dirs[i] = PxVec3(cosf(angle), sinf(angle), 0.0f); + angle += angleStep; + } + + float dpmin[nbAxes]; + float dpmax[nbAxes]; + for(PxU32 i=0;i<nbAxes;i++) + { + dpmin[i] = PX_MAX_F32; + dpmax[i] = -PX_MAX_F32; + } + + for(PxU32 i=0;i<nbNewContacts;i++) + { + const PxVec3& p = vertices[indices[i]]; + + // Transform to 2D + const PxVec3 p2d = rotT.transform(p); + + for(PxU32 j=0;j<nbAxes;j++) + { + const float dp = dot2D(dirs[j], p2d); + dpmin[j] = physx::intrinsics::selectMin(dpmin[j], dp); + dpmax[j] = physx::intrinsics::selectMax(dpmax[j], dp); + } + } + + PxU32 bestAxis = 0; + float maxVariance = dpmax[0] - dpmin[0]; + for(PxU32 i=1;i<nbAxes;i++) + { + const float variance = dpmax[i] - dpmin[i]; + if(variance>maxVariance) + { + maxVariance = variance; + bestAxis = i; + } + } + + const PxVec3 u = dirs[bestAxis]; + const PxVec3 v = PxVec3(-u.y, u.x, 0.0f); + // PxVec3(1.0f, 0.0f, 0.0f) => PxVec3(0.0f, 1.0f, 0.0f) + // PxVec3(0.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 0.0f, 0.0f) + // PxVec3(-1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, -1.0f, 0.0f) + // PxVec3(1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 1.0f, 0.0f) + + float dpminu = PX_MAX_F32; + float dpmaxu = -PX_MAX_F32; + float dpminv = PX_MAX_F32; + float dpmaxv = -PX_MAX_F32; + PxU32 indexMinU = 0; + PxU32 indexMaxU = 0; + PxU32 indexMinV = 0; + PxU32 indexMaxV = 0; + + for(PxU32 i=0;i<nbNewContacts;i++) + { + const PxVec3& p = vertices[indices[i]]; + + // Transform to 2D + const PxVec3 p2d = rotT.transform(p); + + const float dpu = dot2D(u, p2d); + const float dpv = dot2D(v, p2d); + + if(dpu<dpminu) + { + dpminu=dpu; + indexMinU = i; + } + if(dpu>dpmaxu) + { + dpmaxu=dpu; + indexMaxU = i; + } + + if(dpv<dpminv) + { + dpminv=dpv; + indexMinV = i; + } + if(dpv>dpmaxv) + { + dpmaxv=dpv; + indexMaxV = i; + } + } + + if(indexMaxU == indexMinU) + indexMaxU = 0xffffffff; + if(indexMinV == indexMinU || indexMinV == indexMaxU) + indexMinV = 0xffffffff; + if(indexMaxV == indexMinU || indexMaxV == indexMaxU || indexMaxV == indexMinV) + indexMaxV = 0xffffffff; + + PxU32 newCount = 0; + for(PxU32 i=0;i<nbNewContacts;i++) + { + if( i==indexMinU + || i==indexMaxU + || i==indexMinV + || i==indexMaxV) + { + ctcs[newCount++] = ctcs[i]; + } + } + contactBuffer.count = nbExistingContacts + newCount; + } +} +#endif + +// PT: please leave that function in the same translation unit as the calling code +/*static*/ PxMat33 Gu::findRotationMatrixFromZ(const PxVec3& to) +{ + PxMat33 result; + + const PxReal e = to.z; + const PxReal f = PxAbs(e); + + if(f <= 0.9999f) + { + // PT: please keep the normal case first for PS3 branch prediction + + // Normal case, to and from are not parallel or anti-parallel + const PxVec3 v = Ps::cross001(to); + const PxReal h = 1.0f/(1.0f + e); /* optimization by Gottfried Chen */ + const PxReal hvx = h * v.x; + const PxReal hvz = h * v.z; + const PxReal hvxy = hvx * v.y; + const PxReal hvxz = hvx * v.z; + const PxReal hvyz = hvz * v.y; + + result(0,0) = e + hvx*v.x; + result(0,1) = hvxy - v.z; + result(0,2) = hvxz + v.y; + + result(1,0) = hvxy + v.z; + result(1,1) = e + h*v.y*v.y; + result(1,2) = hvyz - v.x; + + result(2,0) = hvxz - v.y; + result(2,1) = hvyz + v.x; + result(2,2) = e + hvz*v.z; + } + else + { + //Vectors almost parallel + // PT: TODO: simplify code below + PxVec3 from(0.0f, 0.0f, 1.0f); + PxVec3 absFrom(0.0f, 0.0f, 1.0f); + + if(absFrom.x < absFrom.y) + { + if(absFrom.x < absFrom.z) + absFrom = PxVec3(1.0f, 0.0f, 0.0f); + else + absFrom = PxVec3(0.0f, 0.0f, 1.0f); + } + else + { + if(absFrom.y < absFrom.z) + absFrom = PxVec3(0.0f, 1.0f, 0.0f); + else + absFrom = PxVec3(0.0f, 0.0f, 1.0f); + } + + PxVec3 u, v; + u.x = absFrom.x - from.x; u.y = absFrom.y - from.y; u.z = absFrom.z - from.z; + v.x = absFrom.x - to.x; v.y = absFrom.y - to.y; v.z = absFrom.z - to.z; + + const PxReal c1 = 2.0f / u.dot(u); + const PxReal c2 = 2.0f / v.dot(v); + const PxReal c3 = c1 * c2 * u.dot(v); + + for(unsigned int i = 0; i < 3; i++) + { + for(unsigned int j = 0; j < 3; j++) + { + result(i,j) = - c1*u[i]*u[j] - c2*v[i]*v[j] + c3*v[i]*u[j]; + } + result(i,i) += 1.0f; + } + } + return result; +} + +// PT: using this specialized version avoids doing an explicit transpose, which reduces LHS +PX_FORCE_INLINE Cm::Matrix34 transformTranspose(const PxMat33& a, const Cm::Matrix34& b) +{ + return Cm::Matrix34(a.transformTranspose(b.m.column0), a.transformTranspose(b.m.column1), a.transformTranspose(b.m.column2), a.transformTranspose(b.p)); +} + +// Helper function to transform x/y coordinate of point. +PX_FORCE_INLINE void transform2D(float& x, float& y, const PxVec3& src, const Cm::Matrix34& mat) +{ + x = src.x * mat.m.column0.x + src.y * mat.m.column1.x + src.z * mat.m.column2.x + mat.p.x; + y = src.x * mat.m.column0.y + src.y * mat.m.column1.y + src.z * mat.m.column2.y + mat.p.y; +} + +// Helper function to transform x/y coordinate of point. Use transposed matrix +PX_FORCE_INLINE void transform2DT(float& x, float& y, const PxVec3& src, const PxMat33& mat) +{ + x = mat.column0.dot(src); + y = mat.column1.dot(src); +} + +// Helper function to transform z coordinate of point. +PX_FORCE_INLINE PxReal transformZ(const PxVec3& src, const Cm::Matrix34& mat) +{ + return src.x * mat.m.column0.z + src.y * mat.m.column1.z + src.z * mat.m.column2.z + mat.p.z; +} + +static void transformVertices( float& minX, float& minY, + float& maxX, float& maxY, + float* PX_RESTRICT verts2D, + PxU32 nb, const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices, const PxMat33& RotT) +{ + // PT: using local variables is important to reduce LHS. + float lminX = FLT_MAX; + float lminY = FLT_MAX; + float lmaxX = -FLT_MAX; + float lmaxY = -FLT_MAX; + + // PT: project points, compute min & max at the same time + for(PxU32 i=0; i<nb; i++) + { + float x,y; + transform2DT(x, y, vertices[indices[i]], RotT); + lminX = physx::intrinsics::selectMin(lminX, x); + lminY = physx::intrinsics::selectMin(lminY, y); + lmaxX = physx::intrinsics::selectMax(lmaxX, x); + lmaxY = physx::intrinsics::selectMax(lmaxY, y); + verts2D[i*2+0] = x; + verts2D[i*2+1] = y; + } + + // DE702 + // Compute center of polygon + const float cx = (lminX + lmaxX)*0.5f; + const float cy = (lminY + lmaxY)*0.5f; + // We'll scale the polygon by epsilon + const float epsilon = 1.e-6f; + // Adjust bounds to take care of scaling + lminX -= epsilon; + lminY -= epsilon; + lmaxX += epsilon; + lmaxY += epsilon; + //~DE702 + + // PT: relocate polygon to positive quadrant + for(PxU32 i=0; i<nb; i++) + { + const float x = verts2D[i*2+0]; + const float y = verts2D[i*2+1]; + + // PT: original code suffering from DE702 (relocation) +// verts2D[i*2+0] = x - lminX; +// verts2D[i*2+1] = y - lminY; + + // PT: theoretically proper DE702 fix (relocation + scaling) + const float dx = x - cx; + const float dy = y - cy; +// const float coeff = epsilon * physx::intrinsics::recipSqrt(dx*dx+dy*dy); +// verts2D[i*2+0] = x - lminX + dx * coeff; +// verts2D[i*2+1] = y - lminY + dy * coeff; + + // PT: approximate but faster DE702 fix. We multiply by epsilon so this is good enough. + verts2D[i*2+0] = x - lminX + physx::intrinsics::fsel(dx, epsilon, -epsilon); + verts2D[i*2+1] = y - lminY + physx::intrinsics::fsel(dy, epsilon, -epsilon); + } + lmaxX -= lminX; + lmaxY -= lminY; + + minX = lminX; + minY = lminY; + maxX = lmaxX; + maxY = lmaxY; +} + +//! Dedicated triangle version +PX_FORCE_INLINE bool pointInTriangle2D( float px, float pz, + float p0x, float p0z, + float e10x, float e10z, + float e20x, float e20z) +{ + const float a = e10x*e10x + e10z*e10z; + const float b = e10x*e20x + e10z*e20z; + const float c = e20x*e20x + e20z*e20z; + const float ac_bb = (a*c)-(b*b); + + const float vpx = px - p0x; + const float vpz = pz - p0z; + + const float d = vpx*e10x + vpz*e10z; + const float e = vpx*e20x + vpz*e20z; + + const float x = (d*c) - (e*b); + const float y = (e*a) - (d*b); + const float z = x + y - ac_bb; + + // Same as: if(x>0.0f && y>0.0f && z<0.0f) return TRUE; + // else return FALSE; +// return (( IR(z) & ~(IR(x)|IR(y)) ) & SIGN_BITMASK) != 0; + if(x>0.0f && y>0.0f && z<0.0f) return true; + else return false; +} + + + enum OutCode + { + OUT_XP = (1<<0), + OUT_XN = (1<<1), + OUT_YP = (1<<2), + OUT_YN = (1<<3) + }; + +static +//PX_FORCE_INLINE +bool PointInConvexPolygon2D_OutCodes(const float* PX_RESTRICT pgon2D, PxU32 numVerts, const PxReal tx, const PxReal ty, const PxReal maxX, const PxReal maxY, PxU8& outCodes) +{ + PxU32 out = 0; + if(tx<0.0f) out |= OUT_XN; + if(ty<0.0f) out |= OUT_YN; + if(tx>maxX) out |= OUT_XP; + if(ty>maxY) out |= OUT_YP; + outCodes = PxU8(out); + if(out) + return false; + + if(numVerts==3) + return pointInTriangle2D( tx, ty, + pgon2D[0], pgon2D[1], + pgon2D[2] - pgon2D[0], + pgon2D[3] - pgon2D[1], + pgon2D[4] - pgon2D[0], + pgon2D[5] - pgon2D[1]); + +#define X 0 +#define Y 1 + + const PxReal* PX_RESTRICT vtx0_ = pgon2D + (numVerts-1)*2; + const PxReal* PX_RESTRICT vtx1_ = pgon2D; + + const int* PX_RESTRICT ivtx0 = reinterpret_cast<const int*>(vtx0_); + const int* PX_RESTRICT ivtx1 = reinterpret_cast<const int*>(vtx1_); + //const int itx = (int&)tx; + //const int ity = (int&)ty; +// const int ity = PX_SIR(ty); + const int* tmp = reinterpret_cast<const int*>(&ty); + const int ity = *tmp; + + // get test bit for above/below X axis + int yflag0 = ivtx0[Y] >= ity; + + int InsideFlag = 0; + + while(numVerts--) + { + const int yflag1 = ivtx1[Y] >= ity; + if(yflag0 != yflag1) + { + const PxReal* PX_RESTRICT vtx0 = reinterpret_cast<const PxReal*>(ivtx0); + const PxReal* PX_RESTRICT vtx1 = reinterpret_cast<const PxReal*>(ivtx1); + if( ((vtx1[Y]-ty) * (vtx0[X]-vtx1[X]) > (vtx1[X]-tx) * (vtx0[Y]-vtx1[Y])) == yflag1 ) + { + if(InsideFlag == 1) return false; + + InsideFlag++; + } + } + yflag0 = yflag1; + ivtx0 = ivtx1; + ivtx1 += 2; + } +#undef X +#undef Y + + return InsideFlag & 1; +} + +// Helper function to detect contact between two edges +PX_FORCE_INLINE bool EdgeEdgeContactSpecial(const PxVec3& v1, const PxPlane& plane, + const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, + PxReal& dist, PxVec3& ip, unsigned int i, unsigned int j, float coeff) +{ + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp > 0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = (p4-p3); + temp = plane.n.dot(v2); + if(temp == 0.0f) // ### epsilon would be better + return false; + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist < 0.0f) + return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) + return true; // collision found + + return false; //no collision +} + +//This one can also handle 2 vertex 'polygons' (useful for capsule surface segments) and can shift the results before contact generation. +bool Gu::contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0, //polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& rotT0, + // + PxU32 numVerts1, const PxVec3* PX_RESTRICT vertices1, const PxU8* PX_RESTRICT indices1, //polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& rotT1, + // + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0, //transforms between polygons + PxU32 /*polyIndex0*/, PxU32 polyIndex1, //feature indices for contact callback + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, PxReal sepShift) // shape order, result shift +{ + const PxVec3 n = flipNormal ? -worldSepAxis : worldSepAxis; + + PX_ASSERT(indices0 != NULL && indices1 != NULL); + + // - optimize "from to" computation + // - do the raycast case && EE tests in same space as 2D case... + // - project all edges at the same time ? + PxU32 NumIn = 0; + bool status = false; + + void* PX_RESTRICT stackMemory; + { + const PxU32 maxNumVert = PxMax(numVerts0, numVerts1); + stackMemory = PxAlloca(maxNumVert * sizeof(PxVec3)); + } + + const PxU32 size0 = numVerts0 * sizeof(bool); + bool* PX_RESTRICT flags0 = reinterpret_cast<bool*>(PxAlloca(size0)); + PxU8* PX_RESTRICT outCodes0 = reinterpret_cast<PxU8*>(PxAlloca(size0)); +// Ps::memZero(flags0, size0); +// Ps::memZero(outCodes0, size0); + + const PxU32 size1 = numVerts1 * sizeof(bool); + bool* PX_RESTRICT flags1 = reinterpret_cast<bool*>(PxAlloca(size1)); + PxU8* PX_RESTRICT outCodes1 = reinterpret_cast<PxU8*>(PxAlloca(size1)); +// Ps::memZero(flags1, size1); +// Ps::memZero(outCodes1, size1); + +#ifdef CONTACT_REDUCTION + // We want to do contact reduction on newly created contacts, not on all the already existing ones... + PxU32 nbExistingContacts = contactBuffer.count; + PxU32 nbCurrentContacts=0; + PxU8 indices[ContactBuffer::MAX_CONTACTS]; +#endif + + { + //polygon 1 + float* PX_RESTRICT verts2D = NULL; + float minX=0, minY=0; + float maxX=0, maxY=0; + + const PxVec3 localDir = -world1.rotateTranspose(worldSepAxis); //contactNormal in hull1 space + //that's redundant, its equal to -localPlane1.d + const Cm::Matrix34 t0to2D = transformTranspose(rotT1, transform0to1); //transform from hull0 to RotT + + PxReal dn = localDir.dot(localPlane1.n); //if the contactNormal == +-(normal of poly0) is NOT orthogonal to poly1 ...this is just to protect the division below. + + // PT: TODO: if "numVerts1>2" we may skip more + if (numVerts1 > 2 //no need to test whether we're 'inside' ignore capsule segments and points +// if(!(-1E-7 < dn && dn < 1E-7)) + && dn >= 1E-7f) // PT: it should never be negative so this unique test is enough + { + dn = 1.0f / dn; + const float ld1 = -localPlane1.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast<float*>(stackMemory); + //Project points + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts1, vertices1, indices1, rotT1); + } + + for(PxU32 i=0; i < numVerts0; i++) //for all vertices of poly0 + { + const PxVec3& p = vertices0[indices0[i]]; + const float p0_z = transformZ(p, t0to2D); //transform ith vertex of poly0 to RotT + + const PxVec3 pIn1 = transform0to1.transform(p); //transform vertex to hull1 space, in which we have the poly1 vertices. + + const PxReal dd = (p0_z - ld1) * dn; //(p0_z + localPlane1.d) is the depth of the vertex behind the triangle measured along the triangle's normal. + //we convert this to being measured along the 'contact normal' using the division. + +// if(dd < 0.0f) //if the penetrating vertex will have a penetration along the contact normal: +// PX_ASSERT(dd <= 0.0f); // PT: dn is always positive, so dd is always negative + { + float px, py; + transform2DT(px, py, pIn1 - dd*localDir, rotT1); //project vertex into poly1 plane along CONTACT NORMAL - not the polygon's normal. + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts1, px-minX, py-minY, maxX, maxY, outCodes0[i]); + flags0[i] = res; + if(res) + { + NumIn++; + + if(p0_z < ld1) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices0[i]; +#endif + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? posShift : PxVec3(0.0f)); + ctc->separation = dd + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + } + } + else + { + PxMemZero(flags0, size0); + PxMemZero(outCodes0, size0); + } + + if(NumIn == numVerts0) + { + //All vertices0 are inside polygon 1 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + return status; + } + +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + +#ifdef CONTACT_REDUCTION + nbExistingContacts = contactBuffer.count; + nbCurrentContacts = 0; +#endif + NumIn = 0; + verts2D = NULL; + + //Polygon 0 + const Cm::Matrix34 t1to2D = transformTranspose(rotT0, transform1to0); + + if (numVerts0 > 2) //no need to test whether we're 'inside' ignore capsule segments and points + { + const float ld0 = -localPlane0.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast<float*>(stackMemory); + //Project vertices + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts0, vertices0, indices0, rotT0); + } + + for(PxU32 i=0; i < numVerts1; i++) + { + const PxVec3& p = vertices1[indices1[i]]; + + float px, py; + transform2D(px, py, p, t1to2D); + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts0, px-minX, py-minY, maxX, maxY, outCodes1[i]); + flags1[i] = res; + if(res) + { + NumIn++; + + const float pz = transformZ(p, t1to2D); + if(pz < ld0) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + // PT: in theory, with this contact point we should use "worldSepAxis" as a contact normal. + // However we want to output the same normal for all contact points not to break friction + // patches!!! In theory again, it should be exactly the same since the contact point at + // time of impact is supposed to be the same on both bodies. In practice however, and with + // a depth-based engine, this is not the case. So the contact point here is not exactly + // right, but preserving the friction patch seems more important. + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices1[i]; +#endif + ctc->normal = n; + ctc->point = world1.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = (pz - ld0) + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + + if(NumIn == numVerts1) + { + //all vertices 1 are inside polygon 0 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + return status; + } +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + } + else + { + PxMemZero(flags1, size1); + PxMemZero(outCodes1, size1); + } + } + + //Edge/edge case + //Calculation done in space 0 + PxVec3* PX_RESTRICT verts1in0 = reinterpret_cast<PxVec3*>(stackMemory); + for(PxU32 i=0; i<numVerts1; i++) + { + verts1in0[i] = transform1to0.transform(vertices1[indices1[i]]); + } + + if (numVerts0 >= 2 && numVerts1 >= 2)//useless if one of them is degenerate. + for(PxU32 j=0; j<numVerts1; j++) + { + PxU32 j1 = j+1; + if(j1 >= numVerts1) j1 = 0; + +// if(!(flags1[j] ^ flags1[j1])) +// continue; + if(flags1[j] && flags1[j1]) + continue; + if(outCodes1[j]&outCodes1[j1]) + continue; + + const PxVec3& p0 = verts1in0[j]; + const PxVec3& p1 = verts1in0[j1]; + +// gVisualizeLocalLine(vertices1[indices1[j]], vertices1[indices1[j1]], world1, callback.getManager()); + + const PxVec3 v1 = p1-p0; + const PxVec3 planeNormal = v1.cross(localPlane0.n); + const PxPlane plane(planeNormal, -(planeNormal.dot(p0))); + + // find largest 2D plane projection + PxU32 _i, _j; + Ps::closestAxis(planeNormal, _i, _j); + + const PxReal coeff = 1.0f / (v1[_i]*localPlane0.n[_j]-v1[_j]*localPlane0.n[_i]); + + for(PxU32 i=0; i<numVerts0; i++) + { + PxU32 i1 = i+1; + if(i1 >= numVerts0) i1 = 0; + +// if(!(flags0[i] ^ flags0[i1])) +// continue; + if(flags0[i] && flags0[i1]) + continue; + if(outCodes0[i]&outCodes0[i1]) + continue; + + const PxVec3& p0b = vertices0[indices0[i]]; + const PxVec3& p1b = vertices0[indices0[i1]]; + +// gVisualizeLocalLine(p0b, p1b, world0, callback.getManager()); + + PxReal dist; + PxVec3 p; + + if(EdgeEdgeContactSpecial(v1, plane, p0, p1, localPlane0.n, p0b, p1b, dist, p, _i, _j, coeff)) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function +/* p = world0.transform(p); + + //contacts are generated on the edges of polygon 1 + //we only have to shift the position of polygon 1 if flipNormal is false, because + //in this case convex 0 gets passed as polygon 1, and it is convex 0 that was shifted. + if (!flipNormal) + p += posShift; + + contactBuffer.contact(p, n, -dist + sepShift, polyIndex0, polyIndex1, convexID);*/ + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = -dist + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + return status; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h new file mode 100644 index 00000000..41f5bf6f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactPolygonPolygon.h @@ -0,0 +1,69 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTPOLYGONPOLYGON_H +#define GU_CONTACTPOLYGONPOLYGON_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ +class ContactBuffer; + +PX_PHYSX_COMMON_API PxMat33 findRotationMatrixFromZ(const PxVec3& to); + +PX_PHYSX_COMMON_API bool contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0,//polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& RotT0, + + PxU32 numVerts1, const PxVec3* vertices1, const PxU8* indices1,//polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& RotT1, + + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0,//transforms between polygons + PxU32 polyIndex0, PxU32 polyIndex1, //face indices for contact callback, + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, float sepShift + ); // shape order, post gen shift. +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp new file mode 100644 index 00000000..58da6764 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereBox.cpp @@ -0,0 +1,181 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" + +using namespace physx; + +//This version is ported 1:1 from novodex +static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, + PxReal sphereRadius, + const PxVec3& boxExtents, +// const PxcCachedTransforms& boxCacheTransform, + const PxTransform& boxTransform, + PxVec3& point, + PxVec3& normal, + PxReal& separation, + PxReal contactDistance) +{ +// const PxTransform& boxTransform = boxCacheTransform.getShapeToWorld(); + + //returns true on contact + const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; + PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. + + //check if delta is outside ABB - and clip the vector to the ABB. + bool outside = false; + + if (dRot.x < -boxExtents.x) + { + outside = true; + dRot.x = -boxExtents.x; + } + else if (dRot.x > boxExtents.x) + { + outside = true; + dRot.x = boxExtents.x; + } + + if (dRot.y < -boxExtents.y) + { + outside = true; + dRot.y = -boxExtents.y; + } + else if (dRot.y > boxExtents.y) + { + outside = true; + dRot.y = boxExtents.y; + } + + if (dRot.z < -boxExtents.z) + { + outside = true; + dRot.z =-boxExtents.z; + } + else if (dRot.z > boxExtents.z) + { + outside = true; + dRot.z = boxExtents.z; + } + + if (outside) //if clipping was done, sphere center is outside of box. + { + point = boxTransform.rotate(dRot); //get clipped delta back in world coords. + normal = delta - point; //what we clipped away. + const PxReal lenSquared = normal.magnitudeSquared(); + const PxReal inflatedDist = sphereRadius + contactDistance; + if (lenSquared > inflatedDist * inflatedDist) + return false; //disjoint + + //normalize to make it into the normal: + separation = PxRecipSqrt(lenSquared); + normal *= separation; + separation *= lenSquared; + //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. + //we could also use point here, which has same direction. + //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. + //We'll just use vertex face for now, this info isn't really being used anyway. + //contact point is point on surface of cube closest to sphere center. + point += boxTransform.p; + separation -= sphereRadius; + return true; + } + else + { + //center is in box, we definitely have a contact. + PxVec3 locNorm; //local coords contact normal + + /*const*/ PxVec3 absdRot; + absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); + /*const*/ PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. + + //find smallest element of distToSurface + if (distToSurface.y < distToSurface.x) + { + if (distToSurface.y < distToSurface.z) + { + //y + locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); + separation = -distToSurface.y; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + else + { + if (distToSurface.x < distToSurface.z) + { + //x + locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); + separation = -distToSurface.x; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + point = sphereOrigin; + normal = boxTransform.rotate(locNorm); + separation -= sphereRadius; + return true; + } +} + +namespace physx +{ +namespace Gu +{ +bool contactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = shape0.get<const PxSphereGeometry>(); + const PxBoxGeometry& boxGeom = shape1.get<const PxBoxGeometry>(); + + PxVec3 normal; + PxVec3 point; + PxReal separation; + if(!ContactSphereBox(transform0.p, sphereGeom.radius, boxGeom.halfExtents, transform1, point, normal, separation, params.mContactDistance)) + return false; + + contactBuffer.contact(point, normal, separation); + return true; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp new file mode 100644 index 00000000..24a97d6f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereCapsule.cpp @@ -0,0 +1,82 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDistancePointSegment.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuInternal.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = shape0.get<const PxSphereGeometry>(); + const PxCapsuleGeometry& capsuleGeom = shape1.get<const PxCapsuleGeometry>(); + + // PT: get capsule in local space + const PxVec3 capsuleLocalSegment = getCapsuleHalfHeightVector(transform1, capsuleGeom); + const Segment localSegment(capsuleLocalSegment, -capsuleLocalSegment); + + // PT: get sphere in capsule space + const PxVec3 sphereCenterInCapsuleSpace = transform0.p - transform1.p; + + const PxReal radiusSum = sphereGeom.radius + capsuleGeom.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + + // PT: compute distance between sphere center & capsule's segment + PxReal u; + const PxReal squareDist = distancePointSegmentSquared(localSegment, sphereCenterInCapsuleSpace, &u); + if(squareDist >= inflatedSum*inflatedSum) + return false; + + // PT: compute contact normal + PxVec3 normal = sphereCenterInCapsuleSpace - localSegment.getPointAt(u); + + // We do a *manual* normalization to check for singularity condition + const PxReal lenSq = normal.magnitudeSquared(); + if(lenSq==0.0f) + normal = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + normal *= PxRecipSqrt(lenSq); + + // PT: compute contact point + const PxVec3 point = sphereCenterInCapsuleSpace + transform1.p - normal * sphereGeom.radius; + + // PT: output unique contact + contactBuffer.contact(point, normal, PxSqrt(squareDist) - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp new file mode 100644 index 00000000..582b2df9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereMesh.cpp @@ -0,0 +1,615 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDistancePointTriangle.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "GuFeatureCode.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBox.h" +#include "PsSort.h" + +#include "CmRenderOutput.h" + +#define DEBUG_RENDER_MESHCONTACTS 0 + +using namespace physx; +using namespace Gu; + +static const bool gDrawTouchedTriangles = false; + +static void outputErrorMessage() +{ +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Dropping contacts in sphere vs mesh: exceeded limit of 64 "); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: a customized version that also returns the feature code +static PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t, FeatureCode& fc) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + fc = FC_VERTEX0; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + fc = FC_VERTEX1; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + fc = FC_EDGE01; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + fc = FC_VERTEX2; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + fc = FC_EDGE20; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + fc = FC_EDGE12; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + fc = FC_FACE; + return a + ab*v + ac*w; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: we use a separate structure to make sorting faster +struct SortKey +{ + float mSquareDist; + PxU32 mIndex; + + PX_FORCE_INLINE bool operator < (const SortKey& data) const + { + return mSquareDist < data.mSquareDist; + } +}; + +struct TriangleData +{ + PxVec3 mDelta; + FeatureCode mFC; + PxU32 mTriangleIndex; + PxU32 mVRef[3]; +}; + +struct CachedTriangleIndices +{ + PxU32 mVRef[3]; +}; + +static PX_FORCE_INLINE bool validateSquareDist(PxReal squareDist) +{ + return squareDist>0.0001f; +} + +static bool validateEdge(PxU32 vref0, PxU32 vref1, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + const PxU32 vi0 = inds.mVRef[0]; + const PxU32 vi1 = inds.mVRef[1]; + const PxU32 vi2 = inds.mVRef[2]; + + if(vi0==vref0) + { + if(vi1==vref1 || vi2==vref1) + return false; + } + else if(vi1==vref0) + { + if(vi0==vref1 || vi2==vref1) + return false; + } + else if(vi2==vref0) + { + if(vi1==vref1 || vi0==vref1) + return false; + } + } + return true; +} + +static bool validateVertex(PxU32 vref, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + if(inds.mVRef[0]==vref || inds.mVRef[1]==vref || inds.mVRef[2]==vref) + return false; + } + return true; +} + +namespace +{ + class NullAllocator + { + public: + PX_FORCE_INLINE NullAllocator() { } + PX_FORCE_INLINE void* allocate(size_t, const char*, int) { return NULL; } + PX_FORCE_INLINE void deallocate(void*) { } + }; + +struct SphereMeshContactGeneration +{ + const PxSphereGeometry& mShapeSphere; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + const PxVec3& mSphereCenterShape1Space; + PxF32 mInflatedRadius2; + PxU32 mNbDelayed; + TriangleData mSavedData[ContactBuffer::MAX_CONTACTS]; + SortKey mSortKey[ContactBuffer::MAX_CONTACTS]; + PxU32 mNbCachedTris; + CachedTriangleIndices mCachedTris[ContactBuffer::MAX_CONTACTS]; + Cm::RenderOutput* mRenderOutput; + + SphereMeshContactGeneration(const PxSphereGeometry& shapeSphere, const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput) : + mShapeSphere (shapeSphere), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer), + mSphereCenterShape1Space (sphereCenterShape1Space), + mInflatedRadius2 (inflatedRadius*inflatedRadius), + mNbDelayed (0), + mNbCachedTris (0), + mRenderOutput (renderOutput) + { + } + + PX_FORCE_INLINE void cacheTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2) + { + const PxU32 nb = mNbCachedTris++; + mCachedTris[nb].mVRef[0] = ref0; + mCachedTris[nb].mVRef[1] = ref1; + mCachedTris[nb].mVRef[2] = ref2; + } + + PX_FORCE_INLINE void addContact(const PxVec3& d, PxReal squareDist, PxU32 triangleIndex) + { + float dist; + PxVec3 delta; + if(validateSquareDist(squareDist)) + { + // PT: regular contact. Normalize 'delta'. + dist = PxSqrt(squareDist); + delta = d / dist; + } + else + { + // PT: singular contact: 'd' is the non-unit triangle's normal in this case. + dist = 0.0f; + delta = -d.getNormalized(); + } + + const PxVec3 worldNormal = -mTransform1.rotate(delta); + + const PxVec3 localHit = mSphereCenterShape1Space + mShapeSphere.radius*delta; + const PxVec3 hit = mTransform1.transform(localHit); + + if(!mContactBuffer.contact(hit, worldNormal, dist - mShapeSphere.radius, triangleIndex)) + outputErrorMessage(); + } + + void processTriangle(PxU32 triangleIndex, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxU32* vertInds) + { + // PT: compute closest point between sphere center and triangle + PxReal u, v; + FeatureCode fc; + const PxVec3 cp = closestPtPointTriangle(mSphereCenterShape1Space, v0, v1, v2, u, v, fc); + + // PT: compute 'delta' vector between closest point and sphere center + const PxVec3 delta = cp - mSphereCenterShape1Space; + const PxReal squareDist = delta.magnitudeSquared(); + if(squareDist >= mInflatedRadius2) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the pt-triangle distance test if it's cheaper + // PT: TODO: e0/e1 already computed in closestPtPointTriangle + const PxVec3 e0 = v1 - v0; + const PxVec3 e1 = v2 - v0; + const PxVec3 planeNormal = e0.cross(e1); + const PxF32 planeD = planeNormal.dot(v0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mSphereCenterShape1Space) < planeD) + return; + + // PT: for a regular contact, 'delta' is non-zero (and so is 'squareDist'). However when the sphere's center exactly touches + // the triangle, then both 'delta' and 'squareDist' become zero. This needs to be handled as a special case to avoid dividing + // by zero. We will use the triangle's normal as a contact normal in this special case. + // + // 'validateSquareDist' is called twice because there are conflicting goals here. We could call it once now and already + // compute the proper data for generating the contact. But this would mean doing a square-root and a division right here, + // even when the contact is not actually needed in the end. We could also call it only once in "addContact', but the plane's + // normal would not always be available (in case of delayed contacts), and thus it would need to be either recomputed (slower) + // or stored within 'TriangleData' (using more memory). Calling 'validateSquareDist' twice is a better option overall. + PxVec3 d; + if(validateSquareDist(squareDist)) + d = delta; + else + d = planeNormal; + + if(fc==FC_FACE) + { + addContact(d, squareDist, triangleIndex); + + if(mNbCachedTris<ContactBuffer::MAX_CONTACTS) + cacheTriangle(vertInds[0], vertInds[1], vertInds[2]); + } + else + { + if(mNbDelayed<ContactBuffer::MAX_CONTACTS) + { + const PxU32 index = mNbDelayed++; + mSortKey[index].mSquareDist = squareDist; + mSortKey[index].mIndex = index; + + TriangleData* saved = mSavedData + index; + saved->mDelta = d; + saved->mVRef[0] = vertInds[0]; + saved->mVRef[1] = vertInds[1]; + saved->mVRef[2] = vertInds[2]; + saved->mFC = fc; + saved->mTriangleIndex = triangleIndex; + } + else outputErrorMessage(); + } + } + + void generateLastContacts() + { + const PxU32 count = mNbDelayed; + if(!count) + return; + + Ps::sort(mSortKey, count, Ps::Less<SortKey>(), NullAllocator(), ContactBuffer::MAX_CONTACTS); + + TriangleData* touchedTris = mSavedData; + for(PxU32 i=0;i<count;i++) + { + const TriangleData& data = touchedTris[mSortKey[i].mIndex]; + + const PxU32 ref0 = data.mVRef[0]; + const PxU32 ref1 = data.mVRef[1]; + const PxU32 ref2 = data.mVRef[2]; + + bool generateContact = false; + + switch(data.mFC) + { + case FC_VERTEX0: + generateContact = ::validateVertex(ref0, mCachedTris, mNbCachedTris); + break; + + case FC_VERTEX1: + generateContact = ::validateVertex(ref1, mCachedTris, mNbCachedTris); + break; + + case FC_VERTEX2: + generateContact = ::validateVertex(ref2, mCachedTris, mNbCachedTris); + break; + + case FC_EDGE01: + generateContact = ::validateEdge(ref0, ref1, mCachedTris, mNbCachedTris); + break; + + case FC_EDGE12: + generateContact = ::validateEdge(ref1, ref2, mCachedTris, mNbCachedTris); + break; + + case FC_EDGE20: + generateContact = ::validateEdge(ref0, ref2, mCachedTris, mNbCachedTris); + break; + + case FC_FACE: + case FC_UNDEFINED: + PX_ASSERT(0); // PT: should not be possible + break; + }; + + if(generateContact) + addContact(data.mDelta, mSortKey[i].mSquareDist, data.mTriangleIndex); + + if(mNbCachedTris<ContactBuffer::MAX_CONTACTS) + cacheTriangle(ref0, ref1, ref2); + else + outputErrorMessage(); + } + } + +private: + SphereMeshContactGeneration& operator=(const SphereMeshContactGeneration&); +}; + +struct SphereMeshContactGenerationCallback_NoScale : MeshHitCallback<PxRaycastHit> +{ + SphereMeshContactGeneration mGeneration; + const TriangleMesh& mMeshData; + + SphereMeshContactGenerationCallback_NoScale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer, + const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : MeshHitCallback<PxRaycastHit> (CallbackMode::eMULTIPLE), + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshData (meshData) + { + } + + virtual ~SphereMeshContactGenerationCallback_NoScale() + { + mGeneration.generateLastContacts(); + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(v0); + const PxVec3 wp1 = mGeneration.mTransform1.transform(v1); + const PxVec3 wp2 = mGeneration.mTransform1.transform(v2); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, v0, v1, v2, vinds); + return true; + } + +protected: + SphereMeshContactGenerationCallback_NoScale &operator=(const SphereMeshContactGenerationCallback_NoScale &); +}; + +struct SphereMeshContactGenerationCallback_Scale : SphereMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mMeshScaling; + + SphereMeshContactGenerationCallback_Scale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, const Cm::FastVertex2ShapeScaling& meshScaling, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : SphereMeshContactGenerationCallback_NoScale(meshData, shapeSphere, + transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshScaling (meshScaling) + { + } + + virtual ~SphereMeshContactGenerationCallback_Scale() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, false, mMeshScaling); + + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(verts[0]); + const PxVec3 wp1 = mGeneration.mTransform1.transform(verts[1]); + const PxVec3 wp2 = mGeneration.mTransform1.transform(verts[2]); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, verts[0], verts[1], verts[2], vinds); + return true; + } +protected: + SphereMeshContactGenerationCallback_Scale &operator=(const SphereMeshContactGenerationCallback_Scale &); +}; + +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Gu::contactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + // We must be in local space to use the cache + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const TriangleMesh* meshData = shapeMesh.meshData; + + // mesh scale is not baked into cached verts + if(shapeMesh.scale.isIdentity()) + { + SphereMeshContactGenerationCallback_NoScale callback( + *meshData, shapeSphere, transform0, transform1, + contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + // PT: TODO: switch to sphere query here + const Box obb(sphereCenterInMeshSpace, PxVec3(inflatedRadius), PxMat33(PxIdentity)); + Midphase::intersectOBB(meshData, obb, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + SphereMeshContactGenerationCallback_Scale callback( + *meshData, shapeSphere, transform0, transform1, + meshScaling, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + PxVec3 obbCenter = sphereCenterInMeshSpace; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + } + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ +struct SphereHeightfieldContactGenerationCallback : EntityReport<PxU32> +{ + SphereMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + SphereHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, + const PxTransform& transform1, + ContactBuffer& contactBuffer, + const PxVec3& sphereCenterInMeshSpace, + PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput + ) : + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput), + mHfUtil (hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + while(nb--) + { + const PxU32 triangleIndex = *indices++; + PxU32 vertIndices[3]; + PxTriangle currentTriangle; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, NULL, triangleIndex, false, false); + + mGeneration.processTriangle(triangleIndex, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], vertIndices); + } + return true; + } +protected: + SphereHeightfieldContactGenerationCallback &operator=(const SphereHeightfieldContactGenerationCallback &); +}; +} + +bool Gu::contactSphereHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get<const PxHeightFieldGeometryLL>(); + const HeightField& hf = *static_cast<HeightField*>(shapeMesh.heightField); + + HeightFieldUtil hfUtil(shapeMesh, hf); + + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const PxVec3 inflatedRV3(inflatedRadius); + + const PxBounds3 bounds(sphereCenterInMeshSpace - inflatedRV3, sphereCenterInMeshSpace + inflatedRV3); + + SphereHeightfieldContactGenerationCallback blockCallback(hfUtil, shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp new file mode 100644 index 00000000..38446117 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSpherePlane.cpp @@ -0,0 +1,68 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape1); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + //const PxPlaneGeometry& shapePlane = shape1.get<const PxPlaneGeometry>(); + + //Sphere in plane space + const PxVec3 sphere = transform1.transformInv(transform0.p); + + //Make sure we have a normalized plane + //The plane is implicitly n=<1,0,0> d=0 (in plane-space) + //PX_ASSERT(PxAbs(shape1.mNormal.magnitudeSquared() - 1.0f) < 0.000001f); + + //Separation + const PxReal separation = sphere.x - shapeSphere.radius; + + if(separation<=params.mContactDistance) + { + const PxVec3 normal = transform1.q.getBasisVector0(); + const PxVec3 point = transform0.p - normal * shapeSphere.radius; + contactBuffer.contact(point, normal, separation); + return true; + } + return false; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp new file mode 100644 index 00000000..6146b1d2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuContactSphereSphere.cpp @@ -0,0 +1,68 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom0 = shape0.get<const PxSphereGeometry>(); + const PxSphereGeometry& sphereGeom1 = shape1.get<const PxSphereGeometry>(); + + PxVec3 delta = transform0.p - transform1.p; + + const PxReal distanceSq = delta.magnitudeSquared(); + const PxReal radiusSum = sphereGeom0.radius + sphereGeom1.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + if(distanceSq >= inflatedSum*inflatedSum) + return false; + + // We do a *manual* normalization to check for singularity condition + const PxReal magn = PxSqrt(distanceSq); + if(magn<=0.00001f) + delta = PxVec3(1.0f, 0.0f, 0.0f); // PT: spheres are exactly overlapping => can't create normal => pick up random one + else + delta *= 1.0f/magn; + + // PT: TODO: why is this formula different from the original code? + const PxVec3 contact = delta * ((sphereGeom0.radius + magn - sphereGeom1.radius)*-0.5f) + transform0.p; + + contactBuffer.contact(contact, delta, magn - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp new file mode 100644 index 00000000..f9bbd7e9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.cpp @@ -0,0 +1,128 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuConvexEdgeFlags.h" +#include "GuFeatureCode.h" + +using namespace physx; +using namespace Gu; + +static FeatureCode computeFeatureCode(PxReal u, PxReal v) +{ + // Analysis + if(u==0.0f) + { + if(v==0.0f) + { + // Vertex 0 + return FC_VERTEX0; + } + else if(v==1.0f) + { + // Vertex 2 + return FC_VERTEX2; + } + else + { + // Edge 0-2 + return FC_EDGE20; + } + } + else if(u==1.0f) + { + if(v==0.0f) + { + // Vertex 1 + return FC_VERTEX1; + } + } + else + { + if(v==0.0f) + { + // Edge 0-1 + return FC_EDGE01; + } + else + { + if((u+v)>=0.9999f) + { + // Edge 1-2 + return FC_EDGE12; + } + else + { + // Face + return FC_FACE; + } + } + } + return FC_UNDEFINED; +} + + +bool Gu::selectNormal(PxU8 data, PxReal u, PxReal v) +{ + bool useFaceNormal = false; + const FeatureCode FC = computeFeatureCode(u, v); + switch(FC) + { + case FC_VERTEX0: + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20))) + useFaceNormal = true; + break; + case FC_VERTEX1: + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12))) + useFaceNormal = true; + break; + case FC_VERTEX2: + if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20))) + useFaceNormal = true; + break; + case FC_EDGE01: + if(!(data & Gu::ETD_CONVEX_EDGE_01)) + useFaceNormal = true; + break; + case FC_EDGE12: + if(!(data & Gu::ETD_CONVEX_EDGE_12)) + useFaceNormal = true; + break; + case FC_EDGE20: + if(!(data & Gu::ETD_CONVEX_EDGE_20)) + useFaceNormal = true; + break; + case FC_FACE: + useFaceNormal = true; + break; + case FC_UNDEFINED: + break; + }; + return useFaceNormal; +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h new file mode 100644 index 00000000..541f2a67 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuFeatureCode.h @@ -0,0 +1,56 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_FEATURE_CODE_H +#define GU_FEATURE_CODE_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + enum FeatureCode + { + FC_VERTEX0, + FC_VERTEX1, + FC_VERTEX2, + FC_EDGE01, + FC_EDGE12, + FC_EDGE20, + FC_FACE, + + FC_UNDEFINED + }; + + bool selectNormal(PxU8 data, PxReal u, PxReal v); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp new file mode 100644 index 00000000..8429fde3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactBoxHeightField.cpp @@ -0,0 +1,476 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" +#include "GuHeightFieldUtil.h" +#include "CmRenderBuffer.h" +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuLegacyTraceLineCallback.h" +#include "PsFPU.h" + +#include "CmMatrix34.h" + +using namespace physx; + +#define DISTANCE_BASED_TEST + +///////// +#if 0 + #include "CmRenderOutput.h" + #include "PxsContext.h" + static void gVisualizeBox(const Gu::Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat33 rot(box.rot.column0, box.rot.column1, box.rot.column2); + PxMat44 m(rot, box.center); + + Cm::DebugBox db(box.extents); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m; + out << db; + } + static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat44 m = PxMat44(PxIdentity); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; + } +#endif +///////// + +// ptchernev TODO: make sure these are ok before shipping +static const bool gCompileBoxVertex = true; +static const bool gCompileEdgeEdge = true; +static const bool gCompileHeightFieldVertex = true; + +static const PxReal signs[24] = +{ + -1,-1,-1, + -1,-1, 1, + -1, 1,-1, + -1, 1, 1, + 1,-1,-1, + 1,-1, 1, + 1, 1,-1, + 1, 1, 1, +}; + +static const PxU8 edges[24] = +{ + 0,1, + 1,3, + 3,2, + 2,0, + 4,5, + 5,7, + 7,6, + 6,4, + 0,4, + 1,5, + 2,6, + 3,7, +}; + +static bool GuDepenetrateBox( const PxVec3& point, + const PxVec3& safeNormal, + const PxVec3& dimensions, + float /*contactDistance*/, + PxVec3& normal, + PxReal& distance) +{ + PxVec3 faceNormal(PxReal(0)); + PxReal distance1 = -PX_MAX_REAL; // cant be more + PxReal distance2 = -PX_MAX_REAL; // cant be more + PxI32 poly1 = -1; + PxI32 poly2 = -2; + + for (PxU32 poly = 0; poly < 6; poly++) + { + PxU32 dim = poly % 3; + + PxReal sign = (poly > 2) ? -PxReal(1) : PxReal(1); + + PxVec3 n(PxVec3(0)); + + n[dim] = sign; + PxReal proj = n[dim] * safeNormal[dim]; + PxReal d = n[dim] * (point[dim] - sign * dimensions[dim]); + +#ifdef DISTANCE_BASED_TEST + // PT: I'm not really sure about contactDistance here + // AP: enabling this causes jitter in DE2740 + //d -= contactDistance; +#endif + + if (d >= 0) + return false; + + if (proj > 0) + { + if (d > distance1) // less penetration + { + distance1 = d; + faceNormal = n; + poly1 = PxI32(poly); + } + + // distance2 / d = 1 / proj + PxReal tmp = d / proj; + if (tmp > distance2) + { + distance2 = tmp; + poly2 = PxI32(poly); + } + } + } + + if (poly1 == poly2) + { + PX_ASSERT(faceNormal.magnitudeSquared() != 0.0f); + normal = faceNormal; + distance = -distance1; + } + else + { + normal = safeNormal; + distance = -distance2; + } + + return true; +} + +//Box-Heightfield and Convex-Heightfield do not support positive values for contactDistance, +//and if in this case we would emit contacts normally, we'd cause things to jitter. +//as a workaround we add contactDistance to the distance values that we emit in contacts. +//this has the effect that the biasing will work exactly as if we had specified a legacy skinWidth of (contactDistance - restDistance) + +namespace physx +{ +namespace Gu +{ +bool legacyContactBoxHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + //PXC_WARN_ONCE(contactDistance > 0.0f, "PxcContactBoxHeightField: Box-Heightfield does not support distance based contact generation! Ignoring contactOffset > 0!"); + + // Get actual shape data + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>(); + + const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField); + const Gu::HeightFieldUtil hfUtil(hfGeom, hf); + + PX_ASSERT(contactBuffer.count==0); + + Cm::Matrix34 boxShapeAbsPose(transform0); + Cm::Matrix34 hfShapeAbsPose(transform1); + + const PxMat33& left = hfShapeAbsPose.m; + const PxMat33& right = boxShapeAbsPose.m; + + Cm::Matrix34 boxShape2HfShape(left.getInverse()* right, left.getInverse()*(boxShapeAbsPose.p - hfShapeAbsPose.p)); + + // Get box vertices. + PxVec3 boxVertices[8]; + PxVec3 boxVertexNormals[8]; + for(PxU32 i=0; i<8; i++) + { + boxVertices[i] = PxVec3(shapeBox.halfExtents.x*signs[3*i], shapeBox.halfExtents.y*signs[3*i+1], shapeBox.halfExtents.z*signs[3*i+2]); + + boxVertexNormals[i] = PxVec3(signs[3*i], signs[3*i+1], signs[3*i+2]); + boxVertexNormals[i].normalize(); + } + + // Transform box vertices to HeightFieldShape space. + PxVec3 boxVerticesInHfShape[8]; + PxVec3 boxVertexNormalsInHfShape[8]; + for(PxU32 i=0; i<8; i++) + { + boxVerticesInHfShape[i] = boxShape2HfShape.transform(boxVertices[i]); + boxVertexNormalsInHfShape[i] = boxShape2HfShape.rotate(boxVertexNormals[i]); + } + + // bounds of box on HeightField. + PxVec3 aabbMin(boxVerticesInHfShape[0]); + PxVec3 aabbMax(boxVerticesInHfShape[0]); + for(PxU32 i=1; i<8; i++) + { + for(PxU32 dim = 0; dim < 3; dim++) + { + aabbMin[dim] = PxMin(aabbMin[dim], boxVerticesInHfShape[i][dim]); + aabbMax[dim] = PxMax(aabbMax[dim], boxVerticesInHfShape[i][dim]); + } + } + + const bool thicknessNegOrNull = (hf.getThicknessFast() <= 0.0f); // PT: don't do this each time! FCMPs are slow. + + // Compute the height field extreme over the bounds area. +// PxReal hfExtreme = thicknessNegOrNull ? -PX_MAX_REAL : PX_MAX_REAL; + // PT: we already computed those! +// const PxReal oneOverRowScale = 1.0f / shapeHeightField.rowScale; +// const PxReal oneOverColumnScale = 1.0f / shapeHeightField.columnScale; + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + + PxU32 minRow; + PxU32 maxRow; + PxU32 minColumn; + PxU32 maxColumn; + + if (hfGeom.rowScale < 0) + { + minRow = hf.getMinRow(aabbMax.x * oneOverRowScale); + maxRow = hf.getMaxRow(aabbMin.x * oneOverRowScale); + } + else + { + minRow = hf.getMinRow(aabbMin.x * oneOverRowScale); + maxRow = hf.getMaxRow(aabbMax.x * oneOverRowScale); + } + + if (hfGeom.columnScale < 0) + { + minColumn = hf.getMinColumn(aabbMax.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(aabbMin.z * oneOverColumnScale); + } + else + { + minColumn = hf.getMinColumn(aabbMin.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(aabbMax.z * oneOverColumnScale); + } + + PxReal hfExtreme = hf.computeExtreme(minRow, maxRow, minColumn, maxColumn); + + //hfExtreme *= hfShape.getHeightScale(); + hfExtreme *= hfGeom.heightScale; + + // Return if convex is on the wrong side of the extreme. + if (thicknessNegOrNull) + { + if (aabbMin.y > hfExtreme) return false; + } + else + { + if (aabbMax.y < hfExtreme) return false; + } + + // Test box vertices. + if (gCompileBoxVertex) + { + for(PxU32 i=0; i<8; i++) + { + const int32_t* tmp = reinterpret_cast<const int32_t*>(&boxVertexNormalsInHfShape[i].y); + // PT: orientation culling + if(*tmp>0) +// if(PX_SIR(boxVertexNormalsInHfShape[i].y)>0) + continue; + + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; +#if 0 + PxVec3 pt = boxShapeAbsPose.transform(boxVertices[i]); + PxVec3 worldNormal = boxShapeAbsPose.rotate(boxVertexNormals[i]); + //gVisualizeLine(pt, pt+PxVec3(1.0f,0.0f,0.0f), context, PxDebugColor::eARGB_RED); + //gVisualizeLine(pt, pt+PxVec3(0.0f,1.0f,0.0f), context, PxDebugColor::eARGB_GREEN); + //gVisualizeLine(pt, pt+PxVec3(0.0f,0.0f,1.0f), context, PxDebugColor::eARGB_BLUE); + gVisualizeLine(pt, pt+worldNormal, context, PxDebugColor::eARGB_MAGENTA); +#endif + +//////// SAME CODE AS IN CONVEX-HF + const bool insideExtreme = + thicknessNegOrNull ? (boxVertexInHfShape.y < hfExtreme + params.mContactDistance) : (boxVertexInHfShape.y > hfExtreme-params.mContactDistance); + + //if (insideExtreme && hfShape.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z)) + if (insideExtreme && hfUtil.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z)) + { + //PxReal y = hfShape.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); +// const PxReal y = hfUtil.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + + // PT: compute this once, reuse results (3 times!) + // PT: TODO: also reuse this in EE tests + PxReal fracX, fracZ; + const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates( + boxVertexInHfShape.x * oneOverRowScale, boxVertexInHfShape.z * oneOverColumnScale, fracX, fracZ); + + const PxReal y = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ); + + const PxReal dy = boxVertexInHfShape.y - y; +#ifdef DISTANCE_BASED_TEST + if (hf.isDeltaHeightInsideExtent(dy, params.mContactDistance)) +#else + if (hf.isDeltaHeightInsideExtent(dy)) +#endif + { + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ); + if (faceIndex != 0xffffffff) + { +// PxcMaterialIndex material = hfShape.getTriangleMaterial(feature); + PxVec3 n; + //n = hfShape.getNormalAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); +// n = hfUtil.getNormalAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + n = hfUtil.getNormalAtShapePoint2(vertexIndex, fracX, fracZ); + n = n.getNormalized(); + + contactBuffer +#ifdef DISTANCE_BASED_TEST + .contact(boxShapeAbsPose.transform(boxVertices[i]), hfShapeAbsPose.rotate(n), n.y*dy/* + contactDistance*/, faceIndex); +#else + .contact(boxShapeAbsPose.transform(boxVertices[i]), hfShapeAbsPose.rotate(n), n.y*dy + contactDistance, feature);//add contactDistance to compensate for fact that we don't support dist based contacts! See comment at start of funct. +#endif + } + } + } +////////~SAME CODE AS IN CONVEX-HF + } + } + + // Test box edges. + if (gCompileEdgeEdge) + { + // create helper class for the trace segment + GuContactHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + for(PxU32 i=0; i<12; i++) + { +// PT: orientation culling +//float worldNormalY = (boxVertexNormalsInHfShape[edges[2*i]].y + boxVertexNormalsInHfShape[edges[2*i+1]].y)*0.5f; +float worldNormalY = boxVertexNormalsInHfShape[edges[2*i]].y + boxVertexNormalsInHfShape[edges[2*i+1]].y; +if(worldNormalY>0.0f) + continue; + + + const PxVec3& v0 = boxVerticesInHfShape[edges[2*i]]; + const PxVec3& v1 = boxVerticesInHfShape[edges[2*i+1]]; + +#if 0 + PxVec3 pt0 = boxShapeAbsPose.transform(boxVertices[edges[2*i]]); + PxVec3 pt1 = boxShapeAbsPose.transform(boxVertices[edges[2*i+1]]); + + PxVec3 worldNormal0 = boxShapeAbsPose.rotate(boxVertexNormals[edges[2*i]]); + PxVec3 worldNormal1 = boxShapeAbsPose.rotate(boxVertexNormals[edges[2*i+1]]); + + PxVec3 pt = (pt0 + pt1)*0.5f; + PxVec3 worldNormal = (worldNormal0 + worldNormal1)*0.5f; + + gVisualizeLine(pt, pt+worldNormal, context, PxDebugColor::eARGB_CYAN); +#endif + + + + if (hf.getThicknessFast()) // PT: errr...? not the same test as in the convex code? + { + if ((v0.y > hfExtreme) && (v1.y > hfExtreme)) continue; + } + else + { + if ((v0.y < hfExtreme) && (v1.y < hfExtreme)) continue; + } + GuContactTraceSegmentCallback cb(v1 - v0, + contactBuffer, + hfShapeAbsPose, params.mContactDistance/*, context.mRenderOutput*/); + + //context.mRenderOutput << PxVec3(1,0,0) << Gu::Debug::convertToPxMat44(transform1) + // << Cm::RenderOutput::LINES << v0+PxVec3(0.01f) << v1+PxVec3(0.01f); + //hfShape.traceSegment<PxcContactTraceSegmentCallback>(v0, v1, &cb); + traceSegmentHelper.traceSegment(v0, v1, &cb); + } + } + + // Test HeightField vertices. + if (gCompileHeightFieldVertex) + { + // Iterate over all HeightField vertices inside the bounds. + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + + //if (!hfShape.isCollisionVertex(vertexIndex)) continue; + if (!hfUtil.isCollisionVertex(vertexIndex, row, column)) continue; + + // Check if hf vertex is inside the box. + //PxVec3 hfVertex; + //hfVertex.set(hfShape.getRowScale() * row, hfShape.getHeightScale() * hfShape.getHeight(vertexIndex), hfShape.getColumnScale() * column); +// const PxVec3 hfVertex(shapeHeightField.rowScale * row, shapeHeightField.columnScale * hfShape.getHeight(vertexIndex), shapeHeightField.columnScale * column); + const PxVec3 hfVertex(hfGeom.rowScale * row, hfGeom.heightScale * hf.getHeight(vertexIndex), hfGeom.columnScale * column); + + const PxVec3 hfVertexInBoxShape = boxShape2HfShape.transformTranspose(hfVertex); + if ((PxAbs(hfVertexInBoxShape.x) - shapeBox.halfExtents.x - params.mContactDistance < 0) + && (PxAbs(hfVertexInBoxShape.y) - shapeBox.halfExtents.y - params.mContactDistance < 0) + && (PxAbs(hfVertexInBoxShape.z) - shapeBox.halfExtents.z - params.mContactDistance < 0)) + { + + // ptchernev: should have done this in HeightFieldShape + // check if this really is a collision vertex + //PxVec3 hfVertexNormal = thicknessNegOrNull ? hfShape.getVertexNormal(vertexIndex) : -hfShape.getVertexNormal(vertexIndex); +// PxVec3 hfVertexNormal = thicknessNegOrNull ? hfUtil.getVertexNormal(vertexIndex) : -hfUtil.getVertexNormal(vertexIndex); + const PxVec3 nrm = hfUtil.getVertexNormal(vertexIndex, row, column); + PxVec3 hfVertexNormal = thicknessNegOrNull ? nrm : -nrm; + hfVertexNormal = hfVertexNormal.getNormalized(); + const PxVec3 hfVertexNormalInBoxShape = boxShape2HfShape.rotateTranspose(hfVertexNormal); + PxVec3 normal; + PxReal depth; + if (!GuDepenetrateBox(hfVertexInBoxShape, -hfVertexNormalInBoxShape, shapeBox.halfExtents, params.mContactDistance, normal, depth)) + { + continue; + } + +// PxMat33 rot(boxShape2HfShape[0],boxShape2HfShape[1],boxShape2HfShape[2]); +// PxVec3 normalInHfShape = rot * (-normal); + PxVec3 normalInHfShape = boxShape2HfShape.rotate(-normal); + //hfShape.clipShapeNormalToVertexVoronoi(normalInHfShape, vertexIndex); + hfUtil.clipShapeNormalToVertexVoronoi(normalInHfShape, vertexIndex, row, column); + if (normalInHfShape.dot(hfVertexNormal) < PX_EPS_REAL) + { + // hmm, I dont think this can happen + continue; + } + normalInHfShape = normalInHfShape.getNormalized(); + const PxU32 faceIndex = hfUtil.getVertexFaceIndex(vertexIndex, row, column); + contactBuffer + .contact(hfShapeAbsPose.transform(hfVertex), hfShapeAbsPose.rotate(normalInHfShape), +#ifdef DISTANCE_BASED_TEST + -depth, +#else + -depth + contactDistance, //add contactDistance to compensate for fact that we don't support dist based contacts! See comment at start of funct. +#endif + faceIndex); + } + } + } + } + + return contactBuffer.count > 0; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp new file mode 100644 index 00000000..a21a281d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactCapsuleHeightField.cpp @@ -0,0 +1,279 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuGeometryUnion.h" +#include "GuHeightFieldData.h" +#include "GuHeightFieldUtil.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuInternal.h" + +#define DO_EDGE_EDGE 1 +#define DEBUG_HFNORMAL 0 +#define DEBUG_HFNORMALV 0 +#define DEBUG_RENDER_HFCONTACTS 0 + +#if DEBUG_RENDER_HFCONTACTS +#include "PxPhysics.h" +#include "PxScene.h" +#endif + +using namespace physx; +using namespace Gu; + +bool GuContactSphereHeightFieldShared(GU_CONTACT_METHOD_ARGS, bool isCapsule); + +namespace physx +{ +namespace Gu +{ +bool legacyContactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS) +{ + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>(); + + const HeightField& hf = *static_cast<HeightField*>(hfGeom.heightField); + const HeightFieldUtil hfUtil(hfGeom, hf); + + const PxReal radius = shapeCapsule.radius; + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + const PxReal radiusSquared = inflatedRadius * inflatedRadius; + const PxReal halfHeight = shapeCapsule.halfHeight; + const PxReal eps = PxReal(0.1)*radius; + const PxReal epsSqr = eps*eps; + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxReal radiusOverRowScale = inflatedRadius * PxAbs(oneOverRowScale); + const PxReal radiusOverColumnScale = inflatedRadius * PxAbs(oneOverColumnScale); + const PxTransform capsuleShapeToHfShape = transform1.transformInv(transform0); + + PxVec3 verticesInHfShape[2]; + verticesInHfShape[0] = capsuleShapeToHfShape.transform(PxVec3(-halfHeight, 0, 0)); + verticesInHfShape[1] = capsuleShapeToHfShape.transform(PxVec3(halfHeight, 0, 0)); + + PX_ASSERT(contactBuffer.count==0); + Gu::GeometryUnion u; + u.set(PxSphereGeometry(radius)); + PxTransform ts0(transform1.transform(verticesInHfShape[0])), ts1(transform1.transform(verticesInHfShape[1])); + GuContactSphereHeightFieldShared(u, shape1, ts0, transform1, params, cache, contactBuffer, renderOutput, true); + GuContactSphereHeightFieldShared(u, shape1, ts1, transform1, params, cache, contactBuffer, renderOutput, true); + + Segment worldCapsule; + worldCapsule.p0 = -getCapsuleHalfHeightVector(transform0, shapeCapsule); + worldCapsule.p1 = -worldCapsule.p0; + worldCapsule.p0 += transform0.p; + worldCapsule.p1 += transform0.p; + + const Segment capsuleSegmentInHfShape(verticesInHfShape[0], verticesInHfShape[1]); + + const PxU32 numCapsuleVertexContacts = contactBuffer.count; // remember how many contacts were stored as capsule vertex vs hf + + // test capsule edges vs HF + PxVec3 v0h = hfUtil.shape2hfp(verticesInHfShape[0]), v1h = hfUtil.shape2hfp(verticesInHfShape[1]); + const PxU32 absMinRow = hf.getMinRow(PxMin(v0h.x - radiusOverRowScale, v1h.x - radiusOverRowScale)); + const PxU32 absMaxRow = hf.getMaxRow(PxMax(v0h.x + radiusOverRowScale, v1h.x + radiusOverRowScale)); + const PxU32 absMinCol = hf.getMinColumn(PxMin(v0h.z - radiusOverColumnScale, v1h.z - radiusOverColumnScale)); + const PxU32 absMaxCol = hf.getMaxColumn(PxMax(v0h.z + radiusOverColumnScale, v1h.z + radiusOverColumnScale)); + if (DO_EDGE_EDGE) + for(PxU32 row = absMinRow; row <= absMaxRow; row++) + { + for(PxU32 column = absMinCol; column <= absMaxCol; column++) + { + //PxU32 vertexIndex = row * hfShape.getNbColumnsFast() + column; + const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + const PxU32 firstEdge = 3 * vertexIndex; + + // omg I am sorry about this code but I can't find a simpler way: + // last column will only test edge 2 + // last row will only test edge 0 + // and most importantly last row and column will not go inside the for + const PxU32 minEi = PxU32((column == absMaxCol) ? 2 : 0); + const PxU32 maxEi = PxU32((row == absMaxRow) ? 1 : 3); + // perform capsule edge vs HF edge collision + for (PxU32 ei = minEi; ei < maxEi; ei++) + { + const PxU32 edgeIndex = firstEdge + ei; + + PX_ASSERT(vertexIndex == edgeIndex / 3); + PX_ASSERT(row == vertexIndex / hf.getNbColumnsFast()); + PX_ASSERT(column == vertexIndex % hf.getNbColumnsFast()); + + // look up the face indices adjacent to the current edge + PxU32 adjFaceIndices[2]; + const PxU32 adjFaceCount = hf.getEdgeTriangleIndices(edgeIndex, adjFaceIndices); + bool doCollision = false; + if(adjFaceCount == 2) + { + doCollision = hf.getMaterialIndex0(adjFaceIndices[0] >> 1) != PxHeightFieldMaterial::eHOLE + || hf.getMaterialIndex1(adjFaceIndices[1] >> 1) != PxHeightFieldMaterial::eHOLE; + } + else if(adjFaceCount == 1) + { + doCollision = (hf.getMaterialIndex0(adjFaceIndices[0] >> 1) != PxHeightFieldMaterial::eHOLE); + } + + if(doCollision) + { + PxVec3 origin; + PxVec3 direction; + hfUtil.getEdge(edgeIndex, vertexIndex, row, column, origin, direction); + + PxReal s, t; + const PxReal ll = distanceSegmentSegmentSquared( + capsuleSegmentInHfShape.p0, capsuleSegmentInHfShape.computeDirection(), origin, direction, &s, &t); + if ((ll < radiusSquared) && (t >= 0) && (t <= 1)) + { + + // We only want to test the vertices for either rows or columns. + // In this case we have chosen rows (ei == 0). + if (ei != 0 && (t == 0 || t == 1)) + continue; + + const PxVec3 pointOnCapsuleInHfShape = capsuleSegmentInHfShape.getPointAt(s); + const PxVec3 pointOnEdge = origin + t * direction; + const PxVec3 d = pointOnCapsuleInHfShape - pointOnEdge; + //if (hfShape.isDeltaHeightOppositeExtent(d.y)) + if (hf.isDeltaHeightOppositeExtent(d.y)) + { + + // Check if the current edge's normal is within any of it's 2 adjacent faces' Voronoi regions + // If it is, force the normal to that region's face normal + PxReal l; + PxVec3 n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1, ll, pointOnEdge.x, pointOnEdge.z, epsSqr, l); + PxVec3 localN = transform1.rotateInv(n); + for (PxU32 j = 0; j < adjFaceCount; j++) + { + const PxVec3 adjNormal = hfUtil.hf2shapen(hf.getTriangleNormalInternal(adjFaceIndices[j])).getNormalized(); + PxU32 triCell = adjFaceIndices[j] >> 1; + PxU32 triRow = triCell/hf.getNbColumnsFast(); + PxU32 triCol = triCell%hf.getNbColumnsFast(); + PxVec3 tv0, tv1, tv2, tvc; + hf.getTriangleVertices(adjFaceIndices[j], triRow, triCol, tv0, tv1, tv2); + tvc = hfUtil.hf2shapep((tv0+tv1+tv2)/3.0f); // compute adjacent triangle center + PxVec3 perp = adjNormal.cross(direction).getNormalized(); // adj face normal cross edge dir + if (perp.dot(tvc-origin) < 0.0f) // make sure perp is pointing toward the center of the triangle + perp = -perp; + // perp is now a vector sticking out of the edge of the triangle (also the test edge) pointing toward the center + // perpendicular to the normal (in triangle plane) + if (perp.dot(localN) > 0.0f) // if the normal is in perp halfspace, clamp it to Voronoi region + { + n = transform1.rotate(adjNormal); + break; + } + } + + const PxVec3 worldPoint = worldCapsule.getPointAt(s); + const PxVec3 p = worldPoint - n * radius; + PxU32 adjTri = adjFaceIndices[0]; + if(adjFaceCount == 2) + { + const PxU16 m0 = hf.getMaterialIndex0(adjFaceIndices[0] >> 1); + if(m0 == PxHeightFieldMaterial::eHOLE) + adjTri = adjFaceIndices[1]; + } + contactBuffer.contact(p, n, l-radius, adjTri); + #if DEBUG_HFNORMAL + printf("n=%.5f %.5f %.5f; d=%.5f\n", n.x, n.y, n.z, l-radius); + #if DEBUG_RENDER_HFCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << p << (p + n * 10.0f); + #endif + #endif + } + } + } + } + + // also perform capsule edge vs HF vertex collision + if (hfUtil.isCollisionVertex(vertexIndex, row, column)) + { + PxVec3 vertex(row * hfGeom.rowScale, hfGeom.heightScale * hf.getHeight(vertexIndex), column * hfGeom.columnScale); + PxReal s; + const PxReal ll = distancePointSegmentSquared(capsuleSegmentInHfShape, vertex, &s); + if (ll < radiusSquared) + { + const PxVec3 pointOnCapsuleInHfShape = capsuleSegmentInHfShape.getPointAt(s); + const PxVec3 d = pointOnCapsuleInHfShape - vertex; + //if (hfShape.isDeltaHeightOppositeExtent(d.y)) + if (hf.isDeltaHeightOppositeExtent(d.y)) + { + // we look through all prior capsule vertex vs HF face contacts and see + // if any of those share a face with hf_edge for the currently considered capsule_edge/hf_vertex contact + bool normalFromFace = false; + PxVec3 n; + PxReal l = 1.0f; + for (PxU32 iVertexContact = 0; iVertexContact < numCapsuleVertexContacts; iVertexContact++) + { + const ContactPoint& cp = contactBuffer.contacts[iVertexContact]; + PxU32 vi0, vi1, vi2; + hf.getTriangleVertexIndices(cp.internalFaceIndex1, vi0, vi1, vi2); + + const PxU32 vi = vertexIndex; + if ((cp.forInternalUse == 0) // if this is a face contact + && (vi == vi0 || vi == vi1 || vi == vi2)) // with one of the face's vertices matching this one + { + n = cp.normal; // then copy the normal from this contact + l = PxAbs(d.dot(n)); + normalFromFace = true; + break; + } + } + + if (!normalFromFace) + n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1, ll, vertex.x, vertex.z, epsSqr, l); + + const PxVec3 worldPoint = worldCapsule.getPointAt(s); + + const PxU32 faceIndex = hfUtil.getVertexFaceIndex(vertexIndex, row, column); + + const PxVec3 p = worldPoint - n * radius; + contactBuffer.contact(p, n, l-radius, faceIndex); + #if DEBUG_HFNORMAL + printf("n=%.5f %.5f %.5f; d=%.5f\n", n.x, n.y, n.z, l-radius); + #if DEBUG_RENDER_HFCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << p << (p + n * 10.0f); + #endif + #endif + } + } // if ll < radiusSquared + } // if isCollisionVertex + } // forEach HF column intersecting with capsule edge AABB + } // forEach HF row intersecting with capsule edge AABB + + return contactBuffer.count>0; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp new file mode 100644 index 00000000..4103714c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactConvexHeightField.cpp @@ -0,0 +1,430 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuLegacyTraceLineCallback.h" +#include "GuConvexMeshData.h" +#include "GuEdgeCache.h" +#include "GuConvexHelper.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; + +#define DISTANCE_BASED_TEST + +// ptchernev TODO: make sure these are ok before shipping +static const bool gCompileConvexVertex = true; +static const bool gCompileEdgeEdge = true; +static const bool gCompileHeightFieldVertex = true; + +/** +\param point vertex tested for penetration (in local hull space) +\param safeNormal if none of the faces are good this becomes the normal +\param normal the direction to translate vertex to depenetrate +\param distance the distance along normal to translate vertex to depenetrate +*/ +static bool GuDepenetrateConvex( const PxVec3& point, + const PxVec3& safeNormal, + const Gu::ConvexHullData& hull, + float contactDistance, + PxVec3& normal, + PxReal& distance, + const Cm::FastVertex2ShapeScaling& scaling, + bool isConvexScaleIdentity) +{ + PxVec3 faceNormal(PxReal(0)); + PxReal distance1 = -PX_MAX_REAL; // cant be more + PxReal distance2 = -PX_MAX_REAL; // cant be more + PxI32 poly1 = -1; + PxI32 poly2 = -2; + +// const Cm::FastVertex2ShapeScaling& scaling = context.mVertex2ShapeSkew[0]; + + for (PxU32 poly = 0; poly < hull.mNbPolygons; poly++) + { + PX_ALIGN(16, PxPlane) shapeSpacePlane; + if(isConvexScaleIdentity) + { + V4StoreA(V4LoadU(&hull.mPolygons[poly].mPlane.n.x), &shapeSpacePlane.n.x); + } + else + { + const PxPlane& vertSpacePlane = hull.mPolygons[poly].mPlane; + scaling.transformPlaneToShapeSpace(vertSpacePlane.n, vertSpacePlane.d, shapeSpacePlane.n, shapeSpacePlane.d);//transform plane into shape space + } + +#ifdef DISTANCE_BASED_TEST + // PT: I'm not really sure about contactDistance here + const PxReal d = shapeSpacePlane.distance(point) - contactDistance; +#else + const PxReal d = shapeSpacePlane.distance(point); +#endif + + if (d >= 0) + { + // no penetration at all + return false; + } + + //const PxVec3& n = plane.normal; + const PxReal proj = shapeSpacePlane.n.dot(safeNormal); + if (proj > 0) + { + if (d > distance1) // less penetration + { + distance1 = d; + faceNormal = shapeSpacePlane.n; + poly1 = PxI32(poly); + } + + // distance2 / d = 1 / proj + const PxReal tmp = d / proj; + if (tmp > distance2) + { + distance2 = tmp; + poly2 = PxI32(poly); + } + } + } + + if (poly1 == poly2) + { + PX_ASSERT(faceNormal.magnitudeSquared() != 0.0f); + normal = faceNormal; + distance = -distance1; + } + else + { + normal = safeNormal; + distance = -distance2; + } + + return true; +} + +//Box-Heightfield and Convex-Heightfield do not support positive values for contactDistance, +//and if in this case we would emit contacts normally, we'd cause things to jitter. +//as a workaround we add contactDistance to the distance values that we emit in contacts. +//this has the effect that the biasing will work exactly as if we had specified a legacy skinWidth of (contactDistance - restDistance) + +#include "GuContactMethodImpl.h" + +namespace physx +{ +namespace Gu +{ +bool legacyContactConvexHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + +#ifndef DISTANCE_BASED_TEST + PX_WARN_ONCE(contactDistance > 0.0f, "PxcContactConvexHeightField: Convex-Heightfield does not support distance based contact generation! Ignoring contactOffset > 0!"); +#endif + + // Get actual shape data + const PxConvexMeshGeometryLL& shapeConvex = shape0.get<const PxConvexMeshGeometryLL>(); + const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>(); + + Cm::Matrix34 convexShapeAbsPose(transform0); + Cm::Matrix34 hfShapeAbsPose(transform1); + + PX_ASSERT(contactBuffer.count==0); + + const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField); + const Gu::HeightFieldUtil hfUtil(hfGeom, hf); + + const bool isConvexScaleIdentity = shapeConvex.scale.isIdentity(); + Cm::FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor + if(!isConvexScaleIdentity) + convexScaling.init(shapeConvex.scale); + + const PxMat33& left = hfShapeAbsPose.m; + const PxMat33& right = convexShapeAbsPose.m; + + Cm::Matrix34 convexShape2HfShape(left.getInverse()* right, left.getInverse()*(convexShapeAbsPose.p - hfShapeAbsPose.p)); + Cm::Matrix34 convexVertex2World( right * convexScaling.getVertex2ShapeSkew(),convexShapeAbsPose.p ); + + // Allocate space for transformed vertices. + const Gu::ConvexHullData* PX_RESTRICT hull = shapeConvex.hullData; + PxVec3* PX_RESTRICT convexVerticesInHfShape = reinterpret_cast<PxVec3*>(PxAlloca(hull->mNbHullVertices*sizeof(PxVec3))); + + // Transform vertices to height field shape + PxMat33 convexShape2HfShape_rot(convexShape2HfShape[0],convexShape2HfShape[1],convexShape2HfShape[2]); + Cm::Matrix34 convexVertices2HfShape(convexShape2HfShape_rot*convexScaling.getVertex2ShapeSkew(), convexShape2HfShape[3]); + const PxVec3* const PX_RESTRICT hullVerts = hull->getHullVertices(); + for(PxU32 i = 0; i<hull->mNbHullVertices; i++) + convexVerticesInHfShape[i] = convexVertices2HfShape.transform(hullVerts[i]); + + PxVec3 convexBoundsInHfShapeMin( PX_MAX_REAL, PX_MAX_REAL, PX_MAX_REAL); + PxVec3 convexBoundsInHfShapeMax(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL); + + // Compute bounds of convex in hf space + for(PxU32 i = 0; i<hull->mNbHullVertices; i++) + { + const PxVec3& v = convexVerticesInHfShape[i]; + + convexBoundsInHfShapeMin.x = PxMin(convexBoundsInHfShapeMin.x, v.x); + convexBoundsInHfShapeMin.y = PxMin(convexBoundsInHfShapeMin.y, v.y); + convexBoundsInHfShapeMin.z = PxMin(convexBoundsInHfShapeMin.z, v.z); + + convexBoundsInHfShapeMax.x = PxMax(convexBoundsInHfShapeMax.x, v.x); + convexBoundsInHfShapeMax.y = PxMax(convexBoundsInHfShapeMax.y, v.y); + convexBoundsInHfShapeMax.z = PxMax(convexBoundsInHfShapeMax.z, v.z); + } + + const bool thicknessNegOrNull = (hf.getThicknessFast() <= 0.0f); // PT: don't do this each time! FCMPs are slow. + + // Compute the height field extreme over the bounds area. + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + + PxU32 minRow; + PxU32 maxRow; + PxU32 minColumn; + PxU32 maxColumn; + + minRow = hf.getMinRow(convexBoundsInHfShapeMin.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShapeMax.x * oneOverRowScale); + + minColumn = hf.getMinColumn(convexBoundsInHfShapeMin.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShapeMax.z * oneOverColumnScale); + + PxReal hfExtreme = hf.computeExtreme(minRow, maxRow, minColumn, maxColumn); + + hfExtreme *= hfGeom.heightScale; + + + // Return if convex is on the wrong side of the extreme. + if (thicknessNegOrNull) + { + if (convexBoundsInHfShapeMin.y > hfExtreme) return false; + } + else + { + if (convexBoundsInHfShapeMax.y < hfExtreme) return false; + } + + // Test convex vertices + if (gCompileConvexVertex) + { + for(PxU32 i=0; i<hull->mNbHullVertices; i++) + { + const PxVec3& convexVertexInHfShape = convexVerticesInHfShape[i]; + + //////// SAME CODE AS IN BOX-HF + const bool insideExtreme = thicknessNegOrNull ? (convexVertexInHfShape.y < hfExtreme) : (convexVertexInHfShape.y > hfExtreme); + + if (insideExtreme && hfUtil.isShapePointOnHeightField(convexVertexInHfShape.x, convexVertexInHfShape.z)) + { + // PT: compute this once, reuse results (3 times!) + // PT: TODO: also reuse this in EE tests + PxReal fracX, fracZ; + const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates(convexVertexInHfShape.x * oneOverRowScale, convexVertexInHfShape.z * oneOverColumnScale, fracX, fracZ); + + const PxReal y = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ); + + const PxReal dy = convexVertexInHfShape.y - y; + #ifdef DISTANCE_BASED_TEST + if (hf.isDeltaHeightInsideExtent(dy, params.mContactDistance/**2.0f*/)) + #else + if (hf.isDeltaHeightInsideExtent(dy)) + #endif + { + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ); + if (faceIndex != 0xffffffff) + { + PxVec3 n; + n = hfUtil.getNormalAtShapePoint2(vertexIndex, fracX, fracZ); + n = n.getNormalized(); + + contactBuffer + #ifdef DISTANCE_BASED_TEST + .contact(convexVertex2World.transform(hullVerts[i]), hfShapeAbsPose.rotate(n), n.y*dy/* - contactDistance*/, faceIndex); + #else + .contact(convexVertex2World.transform(hullVerts[i]), hfShapeAbsPose.rotate(n), n.y*dy + contactDistance, faceIndex);//add contactDistance to compensate for fact that we don't support dist based contacts! See comment at start of funct. + #endif + } + } + } + ////////~SAME CODE AS IN BOX-HF + } + } + + // Test convex edges + if (gCompileEdgeEdge) + { + // create helper class for the trace segment + GuContactHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + if(1) + { + PxU32 numPolygons = hull->mNbPolygons; + const Gu::HullPolygonData* polygons = hull->mPolygons; + const PxU8* vertexData = hull->getVertexData8(); + + ConvexEdge edges[512]; + PxU32 nbEdges = findUniqueConvexEdges(512, edges, numPolygons, polygons, vertexData); + + for(PxU32 i=0;i<nbEdges;i++) + { + const PxVec3 convexNormalInHfShape = convexVertices2HfShape.rotate(edges[i].normal); + if(convexNormalInHfShape.y>0.0f) + continue; + + const PxU8 vi0 = edges[i].vref0; + const PxU8 vi1 = edges[i].vref1; + + const PxVec3& sv0 = convexVerticesInHfShape[vi0]; + const PxVec3& sv1 = convexVerticesInHfShape[vi1]; + + if (thicknessNegOrNull) + { + if ((sv0.y > hfExtreme) && (sv1.y > hfExtreme)) continue; + } + else + { + if ((sv0.y < hfExtreme) && (sv1.y < hfExtreme)) continue; + } + GuContactTraceSegmentCallback cb(sv1 - sv0, contactBuffer, hfShapeAbsPose, params.mContactDistance); + traceSegmentHelper.traceSegment(sv0, sv1, &cb); + } + } + else + { + Gu::EdgeCache edgeCache; + PxU32 numPolygons = hull->mNbPolygons; + const Gu::HullPolygonData* polygons = hull->mPolygons; + const PxU8* vertexData = hull->getVertexData8(); + while (numPolygons--) + { + const Gu::HullPolygonData& polygon = *polygons++; + const PxU8* vRefBase = vertexData + polygon.mVRef8; + + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = vRefBase[a]; + PxU8 vi1 = vRefBase[b]; + + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + a = b; + b++; + + // avoid processing edges 2x if possible (this will typically have cache misses about 5% of the time + // leading to 5% redundant work). + if (edgeCache.isInCache(vi0, vi1)) + continue; + + const PxVec3& sv0 = convexVerticesInHfShape[vi0]; + const PxVec3& sv1 = convexVerticesInHfShape[vi1]; + + if (thicknessNegOrNull) + { + if ((sv0.y > hfExtreme) && (sv1.y > hfExtreme)) + continue; + } + else + { + if ((sv0.y < hfExtreme) && (sv1.y < hfExtreme)) + continue; + } + GuContactTraceSegmentCallback cb(sv1 - sv0, contactBuffer, hfShapeAbsPose, params.mContactDistance); + traceSegmentHelper.traceSegment(sv0, sv1, &cb); + } + } + } + } + + // Test height field vertices + if (gCompileHeightFieldVertex) + { + // Iterate over HeightField vertices inside the projected box bounds. + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + + if (!hfUtil.isCollisionVertex(vertexIndex, row, column)) + continue; + + const PxVec3 hfVertex(hfGeom.rowScale * row, hfGeom.heightScale * hf.getHeight(vertexIndex), hfGeom.columnScale * column); + + const PxVec3 nrm = hfUtil.getVertexNormal(vertexIndex, row, column); + PxVec3 hfVertexNormal = thicknessNegOrNull ? nrm : -nrm; + hfVertexNormal = hfVertexNormal.getNormalized(); + const PxVec3 hfVertexNormalInConvexShape = convexShape2HfShape.rotateTranspose(hfVertexNormal); + PxVec3 hfVertexInConvexShape = convexShape2HfShape.transformTranspose(hfVertex); + PxReal depth; + PxVec3 normal; + if (!GuDepenetrateConvex(hfVertexInConvexShape, -hfVertexNormalInConvexShape, *hull, params.mContactDistance, normal, depth, + convexScaling, + isConvexScaleIdentity)) + { + continue; + } + PxVec3 normalInHfShape = convexShape2HfShape.rotate(-normal); + hfUtil.clipShapeNormalToVertexVoronoi(normalInHfShape, vertexIndex, row, column); + if (normalInHfShape.dot(hfVertexNormal) < PX_EPS_REAL) // AP scaffold: verify this is impossible + continue; + + normalInHfShape = normalInHfShape.getNormalized(); + PxU32 faceIndex = hfUtil.getVertexFaceIndex(vertexIndex, row, column); + contactBuffer + .contact(hfShapeAbsPose.transform(hfVertex), hfShapeAbsPose.rotate(normalInHfShape), + #ifdef DISTANCE_BASED_TEST + -depth, + #else + //add contactDistance to compensate for fact that we don't support dist based contacts! + // See comment at start of funct. + -depth + contactDistance, + #endif + faceIndex); + } + } + } + + return contactBuffer.count>0; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp new file mode 100644 index 00000000..5c9ba896 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyContactSphereHeightField.cpp @@ -0,0 +1,290 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" +#include "GuHeightFieldData.h" +#include "GuHeightFieldUtil.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + +#define DEBUG_RENDER_HFCONTACTS 0 +#if DEBUG_RENDER_HFCONTACTS +#include "PxPhysics.h" +#include "PxScene.h" +#endif + +using namespace physx; +using namespace Gu; + +// Sphere-heightfield contact generation + +// this code is shared between capsule vertices and sphere +bool GuContactSphereHeightFieldShared(GU_CONTACT_METHOD_ARGS, bool isCapsule) +{ +#if 1 + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxHeightFieldGeometryLL& hfGeom = shape1.get<const PxHeightFieldGeometryLL>(); + + const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(hfGeom.heightField); + const Gu::HeightFieldUtil hfUtil(hfGeom, hf); + + const PxReal radius = shapeSphere.radius; + const PxReal eps = PxReal(0.1) * radius; + + const PxVec3 sphereInHfShape = transform1.transformInv(transform0.p); + + PX_ASSERT(isCapsule || contactBuffer.count==0); + + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + + // check if the sphere is below the HF surface + if (hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z)) + { + + PxReal fracX, fracZ; + const PxU32 vertexIndex = hfUtil.getHeightField().computeCellCoordinates(sphereInHfShape.x * oneOverRowScale, sphereInHfShape.z * oneOverColumnScale, fracX, fracZ); + + // The sphere origin projects within the bounds of the heightfield in the X-Z plane +// const PxReal sampleHeight = hfShape.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + const PxReal sampleHeight = hfUtil.getHeightAtShapePoint2(vertexIndex, fracX, fracZ); + + const PxReal deltaHeight = sphereInHfShape.y - sampleHeight; + //if (hfShape.isDeltaHeightInsideExtent(deltaHeight, eps)) + if (hf.isDeltaHeightInsideExtent(deltaHeight, eps)) + { + // The sphere origin is 'below' the heightfield surface + // Actually there is an epsilon involved to make sure the + // 'above' surface calculations can deliver a good normal + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePointNoTest2(vertexIndex, fracX, fracZ); + if (faceIndex != 0xffffffff) + { + + //hfShape.getAbsPoseFast().M.getColumn(1, hfShapeUp); + const PxVec3 hfShapeUp = transform1.q.getBasisVector1(); + + if (hf.getThicknessFast() <= 0) + contactBuffer.contact(transform0.p, hfShapeUp, deltaHeight-radius, faceIndex); + else + contactBuffer.contact(transform0.p, -hfShapeUp, -deltaHeight-radius, faceIndex); + + return true; + } + + return false; + } + + } + + const PxReal epsSqr = eps * eps; + + const PxReal inflatedRadius = radius + params.mContactDistance; + const PxReal inflatedRadiusSquared = inflatedRadius * inflatedRadius; + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape); + + const PxReal radiusOverRowScale = inflatedRadius * PxAbs(oneOverRowScale); + const PxReal radiusOverColumnScale = inflatedRadius * PxAbs(oneOverColumnScale); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + // this assert is here because the following code depends on it for reasonable performance for high-count situations + PX_COMPILE_TIME_ASSERT(ContactBuffer::MAX_CONTACTS == 64); + + const PxU32 nbColumns = hf.getNbColumnsFast(); + +#define HFU Gu::HeightFieldUtil + PxU32 numFaceContacts = 0; + for (PxU32 i = 0; i<2; i++) + { + const bool facesOnly = (i == 0); + // first we go over faces-only meaning only contacts directly in Voronoi regions of faces + // at second pass we consider edges and vertices and clamp the normals to adjacent feature's normal + // if there was a prior contact. it is equivalent to clipping the normal to it's feature's Voronoi region + + for (PxU32 r = minRow; r < maxRow; r++) + { + for (PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 closestPoints[11]; + PxU32 closestFeatures[11]; + PxU32 npcp = hfUtil.findClosestPointsOnCell( + r, c, sphereInHfShape, closestPoints, closestFeatures, facesOnly, !facesOnly, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + PX_ASSERT(closestFeatures[pi] != 0xffffffff); + const PxVec3 d = sphereInHfShape - closestPoints[pi]; + + if (hf.isDeltaHeightOppositeExtent(d.y)) // See if we are 'above' the heightfield + { + const PxReal dMagSq = d.magnitudeSquared(); + + if (dMagSq > inflatedRadiusSquared) + // Too far above + continue; + + PxReal dMag = -1.0f; // dMag is sqrt(sMadSq) and comes up as a byproduct of other calculations in computePointNormal + PxVec3 n; // n is in world space, rotated by transform1 + PxU32 featureType = HFU::getFeatureType(closestFeatures[pi]); + if (featureType == HFU::eEDGE) + { + PxU32 edgeIndex = HFU::getFeatureIndex(closestFeatures[pi]); + PxU32 adjFaceIndices[2]; + const PxU32 adjFaceCount = hf.getEdgeTriangleIndices(edgeIndex, adjFaceIndices); + PxVec3 origin; + PxVec3 direction; + const PxU32 vertexIndex = edgeIndex / 3; + const PxU32 row = vertexIndex / nbColumns; + const PxU32 col = vertexIndex % nbColumns; + hfUtil.getEdge(edgeIndex, vertexIndex, row, col, origin, direction); + n = hfUtil.computePointNormal( + hfGeom.heightFieldFlags, d, transform1, dMagSq, + closestPoints[pi].x, closestPoints[pi].z, epsSqr, dMag); + PxVec3 localN = transform1.rotateInv(n); + // clamp the edge's normal to its Voronoi region + for (PxU32 j = 0; j < adjFaceCount; j++) + { + const PxVec3 adjNormal = hfUtil.hf2shapen(hf.getTriangleNormalInternal(adjFaceIndices[j])).getNormalized(); + PxU32 triCell = adjFaceIndices[j] >> 1; + PxU32 triRow = triCell/hf.getNbColumnsFast(); + PxU32 triCol = triCell%hf.getNbColumnsFast(); + PxVec3 tv0, tv1, tv2, tvc; + hf.getTriangleVertices(adjFaceIndices[j], triRow, triCol, tv0, tv1, tv2); + tvc = hfUtil.hf2shapep((tv0+tv1+tv2)/3.0f); // compute adjacent triangle center + PxVec3 perp = adjNormal.cross(direction).getNormalized(); // adj face normal cross edge dir + if (perp.dot(tvc-origin) < 0.0f) // make sure perp is pointing toward the center of the triangle + perp = -perp; + // perp is now a vector sticking out of the edge of the triangle (also the test edge) pointing toward the center + // perpendicular to the normal (in triangle plane) + if (perp.dot(localN) > 0.0f) // if the normal is in perp halfspace, clamp it to Voronoi region + { + n = transform1.rotate(adjNormal); + break; + } + } + } else if(featureType == HFU::eVERTEX) + { + // AP: these contacts are rare so hopefully it's ok + const PxU32 bufferCount = contactBuffer.count; + const PxU32 vertIndex = HFU::getFeatureIndex(closestFeatures[pi]); + EdgeData adjEdges[8]; + const PxU32 row = vertIndex / nbColumns; + const PxU32 col = vertIndex % nbColumns; + const PxU32 numAdjEdges = ::getVertexEdgeIndices(hf, vertIndex, row, col, adjEdges); + for (PxU32 iPrevEdgeContact = numFaceContacts; iPrevEdgeContact < bufferCount; iPrevEdgeContact++) + { + if (contactBuffer.contacts[iPrevEdgeContact].forInternalUse != HFU::eEDGE) + continue; // skip non-edge contacts (can be other vertex contacts) + + for (PxU32 iAdjEdge = 0; iAdjEdge < numAdjEdges; iAdjEdge++) + // does adjacent edge index for this vertex match a previously encountered edge index? + if (adjEdges[iAdjEdge].edgeIndex == contactBuffer.contacts[iPrevEdgeContact].internalFaceIndex1) + { + // if so, clamp the normal for this vertex to that edge's normal + n = contactBuffer.contacts[iPrevEdgeContact].normal; + dMag = PxSqrt(dMagSq); + break; + } + } + } + + if (dMag == -1.0f) + n = hfUtil.computePointNormal(hfGeom.heightFieldFlags, d, transform1, + dMagSq, closestPoints[pi].x, closestPoints[pi].z, epsSqr, dMag); + + PxVec3 p = transform0.p - n * radius; + #if DEBUG_RENDER_HFCONTACTS + printf("n=%.5f %.5f %.5f; ", n.x, n.y, n.z); + if (n.y < 0.8f) + int a = 1; + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << p << (p + n * 10.0f); + #endif + + // temporarily use the internalFaceIndex0 slot in the contact buffer for featureType + contactBuffer.contact( + p, n, dMag - radius, PxU16(featureType), HFU::getFeatureIndex(closestFeatures[pi])); + } + } + } + } + if (facesOnly) + numFaceContacts = contactBuffer.count; + } // for facesOnly = true to false + + if (!isCapsule) // keep the face labels as internalFaceIndex0 if this function is called from inside capsule/HF function + for (PxU32 k = 0; k < numFaceContacts; k++) + contactBuffer.contacts[k].forInternalUse = 0xFFFF; + + for (PxU32 k = numFaceContacts; k < contactBuffer.count; k++) + { + PX_ASSERT(contactBuffer.contacts[k].forInternalUse != HFU::eFACE); + PX_ASSERT(contactBuffer.contacts[k].forInternalUse <= HFU::eVERTEX); + PxU32 featureIndex = contactBuffer.contacts[k].internalFaceIndex1; + if (contactBuffer.contacts[k].forInternalUse == HFU::eEDGE) + contactBuffer.contacts[k].internalFaceIndex1 = hfUtil.getEdgeFaceIndex(featureIndex); + else if (contactBuffer.contacts[k].forInternalUse == HFU::eVERTEX) + { + PxU32 row = featureIndex/hf.getNbColumnsFast(); + PxU32 col = featureIndex%hf.getNbColumnsFast(); + contactBuffer.contacts[k].internalFaceIndex1 = hfUtil.getVertexFaceIndex(featureIndex, row, col); + } + } +#undef HFU + + return contactBuffer.count>0; +#endif +} + +namespace physx +{ +namespace Gu +{ +bool legacyContactSphereHeightfield(GU_CONTACT_METHOD_ARGS) +{ + return GuContactSphereHeightFieldShared(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput, false); +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h new file mode 100644 index 00000000..d89cfb24 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/contact/GuLegacyTraceLineCallback.h @@ -0,0 +1,160 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_CONTACTTRACESEGMENTCALLBACK_H +#define GU_CONTACTTRACESEGMENTCALLBACK_H + +#include "CmMatrix34.h" +#include "GuGeometryUnion.h" + +#include "GuHeightFieldUtil.h" +#include "CmRenderOutput.h" +#include "GuContactBuffer.h" + +namespace physx +{ +namespace Gu +{ +#define DISTANCE_BASED_TEST + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct GuContactTraceSegmentCallback +{ + PxVec3 mLine; + ContactBuffer& mContactBuffer; + Cm::Matrix34 mTransform; + PxReal mContactDistance; + PxU32 mPrevTriangleIndex; // currently only used to indicate first callback +// Cm::RenderOutput& mRO; + + PX_INLINE GuContactTraceSegmentCallback( + const PxVec3& line, Gu::ContactBuffer& contactBuffer, + Cm::Matrix34 transform, PxReal contactDistance + /*, Cm::RenderOutput& ro*/) + : mLine(line), mContactBuffer(contactBuffer), mTransform(transform), + mContactDistance(contactDistance), mPrevTriangleIndex(0xFFFFffff)//, mRO(ro) + { + } + + bool onEvent(PxU32 , PxU32* ) + { + return true; + } + + PX_INLINE bool faceHit(const Gu::HeightFieldUtil& /*hfUtil*/, const PxVec3& /*hitPoint*/, PxU32 /*triangleIndex*/, PxReal, PxReal) { return true; } + + // x,z is the point of projected face entry intercept in hf coords, rayHeight is at that same point + PX_INLINE bool underFaceHit( + const Gu::HeightFieldUtil& hfUtil, const PxVec3& triangleNormal, + const PxVec3& crossedEdge, PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex) + { + if (mPrevTriangleIndex == 0xFFFFffff) // we only record under-edge contacts so we need at least 2 face hits to have the edge + { + mPrevTriangleIndex = triangleIndex; + //mPrevTriangleNormal = hfUtil.getTriangleNormal(triangleIndex); + return true; + } + + const Gu::HeightField& hf = hfUtil.getHeightField(); + PxF32 y = hfUtil.getHeightAtShapePoint(x, z); // TODO: optmization opportunity - this can be derived cheaply inside traceSegment + PxF32 dy = rayHeight - y; + + if (!hf.isDeltaHeightInsideExtent(dy, mContactDistance)) + return true; + + // add contact + PxVec3 n = crossedEdge.cross(mLine); + if (n.y < 0) // Make sure cross product is facing correctly before clipping + n = -n; + + if (n.y < 0) // degenerate case + return true; + + const PxReal ll = n.magnitudeSquared(); + if (ll > 0) // normalize + n *= PxRecipSqrt(ll); + else // degenerate case + return true; + + // Scale delta height so it becomes the "penetration" along the normal + dy *= n.y; + if (hf.getThicknessFast() > 0) + { + n = -n; + dy = -dy; + } + + // compute the contact point + const PxVec3 point(x, rayHeight, z); + //mRO << PxVec3(1,0,0) << Gu::Debug::convertToPxMat44(mTransform) + // << Cm::RenderOutput::LINES << point << point + triangleNormal; +#ifdef DISTANCE_BASED_TEST + mContactBuffer.contact( + mTransform.transform(point), mTransform.rotate(triangleNormal), dy, triangleIndex); +#else + // add gContactDistance to compensate for fact that we don't support dist based contacts in box/convex-hf! + // See comment at start of those functs. + mContactBuffer.contact( + mTransform.transform(point), mTransform.rotate(triangleNormal), + dy + mContactDistance, PXC_CONTACT_NO_FACE_INDEX, triangleIndex); +#endif + mPrevTriangleIndex = triangleIndex; + //mPrevTriangleNormal = triangleNormal; + return true; + } + +private: + GuContactTraceSegmentCallback& operator=(const GuContactTraceSegmentCallback&); +}; + +class GuContactHeightfieldTraceSegmentHelper +{ + PX_NOCOPY(GuContactHeightfieldTraceSegmentHelper) +public: + GuContactHeightfieldTraceSegmentHelper(const HeightFieldUtil& hfUtil) + : mHfUtil(hfUtil) + { + mHfUtil.computeLocalBounds(mLocalBounds); + } + + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& aP1, GuContactTraceSegmentCallback* aCallback) const + { + mHfUtil.traceSegment<GuContactTraceSegmentCallback, true, false>(aP0, aP1 - aP0, 1.0f, aCallback, mLocalBounds, true, NULL); + } + +private: + const HeightFieldUtil& mHfUtil; + PxBounds3 mLocalBounds; +}; + +}//Gu +}//physx + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData.cpp new file mode 100644 index 00000000..7874c526 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData.cpp @@ -0,0 +1,203 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "GuSerialize.h" +#include "GuBigConvexData2.h" +#include "GuCubeIndex.h" +#include "PsIntrinsics.h" +#include "CmUtils.h" +#include "PsUtilities.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; + +BigConvexData::BigConvexData() : mVBuffer(NULL) +{ + mData.mSubdiv = 0; + mData.mNbSamples = 0; + mData.mSamples = NULL; + + ////// + + mData.mNbVerts = 0; + mData.mNbAdjVerts = 0; + mData.mValencies = NULL; + mData.mAdjacentVerts = NULL; +} + +BigConvexData::~BigConvexData() +{ + PX_FREE(mData.mSamples); + + /////////// + + if(mVBuffer) + { + PX_FREE(mVBuffer); + } + else + { + // Allocated from somewhere else!! + PX_FREE(mData.mValencies); + PX_FREE(mData.mAdjacentVerts); + } +} + +void BigConvexData::CreateOffsets() +{ + // Create offsets (radix style) + mData.mValencies[0].mOffset = 0; + for(PxU32 i=1;i<mData.mNbVerts;i++) + mData.mValencies[i].mOffset = PxU16(mData.mValencies[i-1].mOffset + mData.mValencies[i-1].mCount); +} + +bool BigConvexData::VLoad(PxInputStream& stream) +{ + // Import header + PxU32 Version; + bool Mismatch; + if(!ReadHeader('V', 'A', 'L', 'E', Version, Mismatch, stream)) + return false; + + mData.mNbVerts = readDword(Mismatch, stream); + mData.mNbAdjVerts = readDword(Mismatch, stream); + + PX_FREE(mVBuffer); + + // PT: align Gu::Valency? + const PxU32 numVerts = (mData.mNbVerts+3)&~3; + const PxU32 TotalSize = sizeof(Gu::Valency)*numVerts + sizeof(PxU8)*mData.mNbAdjVerts; + mVBuffer = PX_ALLOC(TotalSize, "BigConvexData data"); + mData.mValencies = reinterpret_cast<Gu::Valency*>(mVBuffer); + mData.mAdjacentVerts = (reinterpret_cast<PxU8*>(mVBuffer)) + sizeof(Gu::Valency)*numVerts; + + PX_ASSERT(0 == (size_t(mData.mAdjacentVerts) & 0xf)); + PX_ASSERT(Version==2); + + { + PxU16* temp = reinterpret_cast<PxU16*>(mData.mValencies); + + PxU32 MaxIndex = readDword(Mismatch, stream); + ReadIndices(Ps::to16(MaxIndex), mData.mNbVerts, temp, stream, Mismatch); + + // We transform from: + // + // |5555|4444|3333|2222|1111|----|----|----|----|----| + // + // to: + // + // |5555|4444|4444|2222|3333|----|2222|----|1111|----| + // + for(PxU32 i=0;i<mData.mNbVerts;i++) + mData.mValencies[mData.mNbVerts-i-1].mCount = temp[mData.mNbVerts-i-1]; + } + stream.read(mData.mAdjacentVerts, mData.mNbAdjVerts); + + // Recreate offsets + CreateOffsets(); + + return true; +} + +PxU32 BigConvexData::ComputeOffset(const PxVec3& dir) const +{ + return ComputeCubemapOffset(dir, mData.mSubdiv); +} + +PxU32 BigConvexData::ComputeNearestOffset(const PxVec3& dir) const +{ + return ComputeCubemapNearestOffset(dir, mData.mSubdiv); +} + +bool BigConvexData::Load(PxInputStream& stream) +{ + // Import header + PxU32 Version; + bool Mismatch; + if(!ReadHeader('S', 'U', 'P', 'M', Version, Mismatch, stream)) + return false; + + // Load base gaussmap +// if(!GaussMap::Load(stream)) return false; + + // Import header + if(!ReadHeader('G', 'A', 'U', 'S', Version, Mismatch, stream)) + return false; + + // Import basic info + mData.mSubdiv = Ps::to16(readDword(Mismatch, stream)); + mData.mNbSamples = Ps::to16(readDword(Mismatch, stream)); + + // Load map data + mData.mSamples = reinterpret_cast<PxU8*>(PX_ALLOC(sizeof(PxU8)*mData.mNbSamples*2, "BigConvex Samples Data")); + + // These byte buffers shouldn't need converting + stream.read(mData.mSamples, sizeof(PxU8)*mData.mNbSamples*2); + + //load the valencies + return VLoad(stream); +} + +// PX_SERIALIZATION +void BigConvexData::exportExtraData(PxSerializationContext& stream) +{ + if(mData.mSamples) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData.mSamples, sizeof(PxU8)*mData.mNbSamples*2); + } + + if(mData.mValencies) + { + stream.alignData(PX_SERIAL_ALIGN); + PxU32 numVerts = (mData.mNbVerts+3)&~3; + const PxU32 TotalSize = sizeof(Gu::Valency)*numVerts + sizeof(PxU8)*mData.mNbAdjVerts; + stream.writeData(mData.mValencies, TotalSize); + } +} + +void BigConvexData::importExtraData(PxDeserializationContext& context) +{ + if(mData.mSamples) + mData.mSamples = context.readExtraData<PxU8, PX_SERIAL_ALIGN>(PxU32(mData.mNbSamples*2)); + + if(mData.mValencies) + { + context.alignExtraData(); + PxU32 numVerts = (mData.mNbVerts+3)&~3; + mData.mValencies = context.readExtraData<Gu::Valency>(numVerts); + mData.mAdjacentVerts = context.readExtraData<PxU8>(mData.mNbAdjVerts); + + } +} +//~PX_SERIALIZATION + diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData.h new file mode 100644 index 00000000..835f463b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData.h @@ -0,0 +1,98 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BIG_CONVEX_DATA_H +#define GU_BIG_CONVEX_DATA_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class BigConvexDataBuilder; +class PxcHillClimb; +class BigConvexData; + +// Data + +namespace Gu +{ + +struct Valency +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxU16 mCount; + PxU16 mOffset; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::Valency) == 4); + +struct BigConvexRawData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + // Support vertex map + PxU16 mSubdiv; // "Gaussmap" subdivision + PxU16 mNbSamples; // Total #samples in gaussmap PT: this is not even needed at runtime! + + PxU8* mSamples; + PX_FORCE_INLINE const PxU8* getSamples2() const + { + return mSamples + mNbSamples; + } + //~Support vertex map + + // Valencies data + PxU32 mNbVerts; //!< Number of vertices + PxU32 mNbAdjVerts; //!< Total number of adjacent vertices ### PT: this is useless at runtime and should not be stored here + Gu::Valency* mValencies; //!< A list of mNbVerts valencies (= number of neighbors) + PxU8* mAdjacentVerts; //!< List of adjacent vertices + //~Valencies data +}; +#if PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Gu::BigConvexRawData) == 40); +#else +PX_COMPILE_TIME_ASSERT(sizeof(Gu::BigConvexRawData) == 24); +#endif + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData2.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData2.h new file mode 100644 index 00000000..be2e8a50 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuBigConvexData2.h @@ -0,0 +1,95 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BIG_CONVEX_DATA2_H +#define GU_BIG_CONVEX_DATA2_H + +#include "GuBigConvexData.h" +#include "PxMetaData.h" + +namespace physx +{ + class PxSerializationContext; + class PxDeserializationContext; + + class PX_PHYSX_COMMON_API BigConvexData : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + BigConvexData(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + BigConvexData(); + ~BigConvexData(); + // Support vertex map + bool Load(PxInputStream& stream); + + PxU32 ComputeOffset(const PxVec3& dir) const; + PxU32 ComputeNearestOffset(const PxVec3& dir) const; + + // Data access + PX_INLINE PxU32 GetSubdiv() const { return mData.mSubdiv; } + PX_INLINE PxU32 GetNbSamples() const { return mData.mNbSamples; } + //~Support vertex map + + // Valencies + // Data access + PX_INLINE PxU32 GetNbVerts() const { return mData.mNbVerts; } + PX_INLINE const Gu::Valency* GetValencies() const { return mData.mValencies; } + PX_INLINE PxU16 GetValency(PxU32 i) const { return mData.mValencies[i].mCount; } + PX_INLINE PxU16 GetOffset(PxU32 i) const { return mData.mValencies[i].mOffset; } + PX_INLINE const PxU8* GetAdjacentVerts() const { return mData.mAdjacentVerts; } + + PX_INLINE PxU16 GetNbNeighbors(PxU32 i) const { return mData.mValencies[i].mCount; } + PX_INLINE const PxU8* GetNeighbors(PxU32 i) const { return &mData.mAdjacentVerts[mData.mValencies[i].mOffset]; } + +// PX_SERIALIZATION + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); +//~PX_SERIALIZATION + Gu::BigConvexRawData mData; + protected: + void* mVBuffer; + // Internal methods + void CreateOffsets(); + bool VLoad(PxInputStream& stream); + //~Valencies + friend class BigConvexDataBuilder; + }; + +} + +#endif // BIG_CONVEX_DATA_H + diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexEdgeFlags.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexEdgeFlags.h new file mode 100644 index 00000000..66f55ace --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexEdgeFlags.h @@ -0,0 +1,56 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_EDGE_FLAGS_H +#define GU_CONVEX_EDGE_FLAGS_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + enum ExtraTrigDataFlag + { + ETD_CONVEX_EDGE_01 = (1<<3), // PT: important value, don't change + ETD_CONVEX_EDGE_12 = (1<<4), // PT: important value, don't change + ETD_CONVEX_EDGE_20 = (1<<5), // PT: important value, don't change + + ETD_CONVEX_EDGE_ALL = ETD_CONVEX_EDGE_01|ETD_CONVEX_EDGE_12|ETD_CONVEX_EDGE_20 + }; + + // PT: helper function to make sure we use the proper default flags everywhere + PX_FORCE_INLINE PxU8 getConvexEdgeFlags(const PxU8* extraTrigData, PxU32 triangleIndex) + { + return extraTrigData ? extraTrigData[triangleIndex] : PxU8(ETD_CONVEX_EDGE_ALL); + } +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexHelper.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexHelper.cpp new file mode 100644 index 00000000..48bd7641 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexHelper.cpp @@ -0,0 +1,137 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuConvexHelper.h" +#include "GuGeometryUnion.h" +#include "GuInternal.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +// PT: we can't call alloca in a function and we want to avoid defines or duplicating the code. This makes it a bit tricky to write. +void Gu::getScaledConvex( PxVec3*& scaledVertices, PxU8*& scaledIndices, PxVec3* dstVertices, PxU8* dstIndices, + bool idtConvexScale, const PxVec3* srcVerts, const PxU8* srcIndices, PxU32 nbVerts, const Cm::FastVertex2ShapeScaling& convexScaling) +{ + //pretransform convex polygon if we have scaling! + if(idtConvexScale) // PT: the scale is always 1 for boxes so no need to test the type + { + scaledVertices = const_cast<PxVec3*>(srcVerts); + scaledIndices = const_cast<PxU8*>(srcIndices); + } + else + { + scaledIndices = dstIndices; + scaledVertices = dstVertices; + for(PxU32 i=0; i<nbVerts; i++) + { + scaledIndices[i] = Ps::to8(i); //generate trivial indexing. + scaledVertices[i] = convexScaling * srcVerts[srcIndices[i]]; + } + } +} + +bool Gu::getConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData) +{ + const PxConvexMeshGeometryLL& shapeConvex = shape.get<const PxConvexMeshGeometryLL>(); + + const bool idtScale = shapeConvex.scale.isIdentity(); + if(!idtScale) + scaling.init(shapeConvex.scale); + + // PT: this version removes all the FCMPs and almost all LHS. This is temporary until + // the legacy 3x3 matrix totally vanishes but meanwhile do NOT do useless matrix conversions, + // it's a perfect recipe for LHS. + PX_ASSERT(!shapeConvex.hullData->mAABB.isEmpty()); + bounds = shapeConvex.hullData->mAABB.transformFast(scaling.getVertex2ShapeSkew()); + + getPolygonalData_Convex(&polyData, shapeConvex.hullData, scaling); + + // PT: non-uniform scaling invalidates the "internal objects" optimization, since our internal sphere + // might become an ellipsoid or something. Just disable the optimization if scaling is used... + if(!idtScale) + polyData.mInternal.reset(); + + return idtScale; +} + +PxU32 Gu::findUniqueConvexEdges(PxU32 maxNbEdges, ConvexEdge* PX_RESTRICT edges, PxU32 numPolygons, const Gu::HullPolygonData* PX_RESTRICT polygons, const PxU8* PX_RESTRICT vertexData) +{ + PxU32 nbEdges = 0; + + while(numPolygons--) + { + const HullPolygonData& polygon = *polygons++; + const PxU8* vRefBase = vertexData + polygon.mVRef8; + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = vRefBase[a]; + PxU8 vi1 = vRefBase[b]; + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + bool found=false; + for(PxU32 i=0;i<nbEdges;i++) + { + if(edges[i].vref0==vi0 && edges[i].vref1==vi1) + { + found = true; + edges[i].normal += polygon.mPlane.n; + break; + } + } + if(!found) + { + if(nbEdges==maxNbEdges) + { + PX_ALWAYS_ASSERT_MESSAGE("Internal error: max nb edges reached. This shouldn't be possible..."); + return nbEdges; + } + + edges[nbEdges].vref0 = vi0; + edges[nbEdges].vref1 = vi1; + edges[nbEdges].normal = polygon.mPlane.n; + nbEdges++; + } + + a = b; + b++; + } + } + return nbEdges; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexHelper.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexHelper.h new file mode 100644 index 00000000..5e93eab2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexHelper.h @@ -0,0 +1,66 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEXHELPER_H +#define GU_CONVEXHELPER_H + +#include "GuShapeConvex.h" + +namespace physx +{ +namespace Gu +{ + class GeometryUnion; + + /////////////////////////////////////////////////////////////////////////// + + PX_PHYSX_COMMON_API void getScaledConvex( PxVec3*& scaledVertices, PxU8*& scaledIndices, PxVec3* dstVertices, PxU8* dstIndices, + bool idtConvexScale, const PxVec3* srcVerts, const PxU8* srcIndices, PxU32 nbVerts, const Cm::FastVertex2ShapeScaling& convexScaling); + + // PT: calling this correctly isn't trivial so let's macroize it. At least we limit the damage since it immediately calls a real function. + #define GET_SCALEX_CONVEX(scaledVertices, stackIndices, idtScaling, nbVerts, scaling, srcVerts, srcIndices) \ + getScaledConvex(scaledVertices, stackIndices, \ + idtScaling ? NULL : reinterpret_cast<PxVec3*>(PxAlloca(nbVerts * sizeof(PxVec3))), \ + idtScaling ? NULL : reinterpret_cast<PxU8*>(PxAlloca(nbVerts * sizeof(PxU8))), \ + idtScaling, srcVerts, srcIndices, nbVerts, scaling); + + PX_PHYSX_COMMON_API bool getConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData); + + struct ConvexEdge + { + PxU8 vref0; + PxU8 vref1; + PxVec3 normal; // warning: non-unit vector! + }; + + PX_PHYSX_COMMON_API PxU32 findUniqueConvexEdges(PxU32 maxNbEdges, ConvexEdge* PX_RESTRICT edges, PxU32 numPolygons, const Gu::HullPolygonData* PX_RESTRICT polygons, const PxU8* PX_RESTRICT vertexData); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMesh.cpp new file mode 100644 index 00000000..53d67504 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMesh.cpp @@ -0,0 +1,456 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PxVisualizationParameter.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "GuConvexMesh.h" +#include "GuTriangle32.h" +#include "GuBigConvexData2.h" +#include "GuSerialize.h" +#include "GuMeshFactory.h" +#include "CmUtils.h" +#include "PxMeshScale.h" +#include "PsAllocator.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +// PX_SERIALIZATION +#include "PsIntrinsics.h" +//~PX_SERIALIZATION + +bool Gu::ConvexMesh::getPolygonData(PxU32 i, PxHullPolygon& data) const +{ + if(i>=mHullData.mNbPolygons) + return false; + + const HullPolygonData& poly = mHullData.mPolygons[i]; + data.mPlane[0] = poly.mPlane.n.x; + data.mPlane[1] = poly.mPlane.n.y; + data.mPlane[2] = poly.mPlane.n.z; + data.mPlane[3] = poly.mPlane.d; + data.mNbVerts = poly.mNbVerts; + data.mIndexBase = poly.mVRef8; + return true; +} + +/// ====================================== + +static void initConvexHullData(Gu::ConvexHullData& data) +{ + data.mAABB.setEmpty(); + data.mCenterOfMass = PxVec3(0); + data.mNbEdges = PxBitAndWord(); + data.mNbHullVertices = 0; + data.mNbPolygons = 0; + data.mPolygons = NULL; + data.mBigConvexRawData = NULL; + data.mInternal.mRadius = 0.0f; + data.mInternal.mExtents[0] = data.mInternal.mExtents[1] = data.mInternal.mExtents[2] = 0.0f; +} + +Gu::ConvexMesh::ConvexMesh() +: PxConvexMesh(PxConcreteType::eCONVEX_MESH, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNb (0) +, mBigConvexData (NULL) +, mMass (0) +, mInertia (PxMat33(PxIdentity)) +{ + initConvexHullData(mHullData); +} + +Gu::ConvexMesh::ConvexMesh(GuMeshFactory& factory, ConvexHullData& data) +: PxConvexMesh(PxConcreteType::eCONVEX_MESH, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNb(0) +, mBigConvexData(NULL) +, mMass(0) +, mInertia(PxMat33(PxIdentity)) +, mMeshFactory(&factory) +{ + mHullData = data; +} + +Gu::ConvexMesh::~ConvexMesh() +{ +// PX_SERIALIZATION + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) +//~PX_SERIALIZATION + { + PX_DELETE_POD(mHullData.mPolygons); + PX_DELETE_AND_RESET(mBigConvexData); + } +} + +bool Gu::ConvexMesh::isGpuCompatible() const +{ + return mHullData.mNbHullVertices <= 64 && + mHullData.mPolygons[0].mNbVerts < 32 && + mHullData.mNbEdges.isBitSet(); +} + +// PX_SERIALIZATION +void Gu::ConvexMesh::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 bufferSize = computeBufferSize(mHullData, getNb()); + stream.writeData(mHullData.mPolygons, bufferSize); + + if(mBigConvexData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mBigConvexData, sizeof(BigConvexData)); + + mBigConvexData->exportExtraData(stream); + } +} + +void Gu::ConvexMesh::importExtraData(PxDeserializationContext& context) +{ + const PxU32 bufferSize = computeBufferSize(mHullData, getNb()); + mHullData.mPolygons = reinterpret_cast<Gu::HullPolygonData*>(context.readExtraData<PxU8, PX_SERIAL_ALIGN>(bufferSize)); + + if(mBigConvexData) + { + mBigConvexData = context.readExtraData<BigConvexData, PX_SERIAL_ALIGN>(); + new(mBigConvexData)BigConvexData(PxEmpty); + mBigConvexData->importExtraData(context); + mHullData.mBigConvexRawData = &mBigConvexData->mData; + } +} + +Gu::ConvexMesh* Gu::ConvexMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + ConvexMesh* obj = new (address) ConvexMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(ConvexMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +static bool convexHullLoad(Gu::ConvexHullData& data, PxInputStream& stream, PxBitAndDword& bufferSize) +{ + PxU32 version; + bool Mismatch; + if(!ReadHeader('C', 'L', 'H', 'L', version, Mismatch, stream)) + return false; + + if(!ReadHeader('C', 'V', 'H', 'L', version, Mismatch, stream)) + return false; + + PxU32 Nb; + + // Import figures + { + PxU32 tmp[4]; + ReadDwordBuffer(tmp, 4, Mismatch, stream); + data.mNbHullVertices = Ps::to8(tmp[0]); + data.mNbEdges = Ps::to16(tmp[1]); + data.mNbPolygons = Ps::to8(tmp[2]); + Nb = tmp[3]; + } + + //AM: In practice the old aligner approach wastes 20 bytes and there is no reason to 20 byte align this data. + //I changed the code to just 4 align for the time being. + //On consoles if anything we will need to make this stuff 16 byte align vectors to have any sense, which will have to be done by padding data structures. + PX_ASSERT(sizeof(Gu::HullPolygonData) % sizeof(PxReal) == 0); //otherwise please pad it. + PX_ASSERT(sizeof(PxVec3) % sizeof(PxReal) == 0); + + PxU32 bytesNeeded = computeBufferSize(data, Nb); + + PX_FREE(data.mPolygons); // Load() can be called for an existing convex mesh. In that case we need to free + // the memory first. + + bufferSize = Nb; + void* mDataMemory = PX_ALLOC(bytesNeeded, "ConvexHullData data"); + + PxU8* address = reinterpret_cast<PxU8*>(mDataMemory); + + data.mPolygons = reinterpret_cast<Gu::HullPolygonData*>(address); address += sizeof(Gu::HullPolygonData) * data.mNbPolygons; + PxVec3* mDataHullVertices = reinterpret_cast<PxVec3*>(address); address += sizeof(PxVec3) * data.mNbHullVertices; + PxU8* mDataFacesByEdges8 = address; address += sizeof(PxU8) * data.mNbEdges * 2; + PxU8* mDataFacesByVertices8 = address; address += sizeof(PxU8) * data.mNbHullVertices * 3; + PxU16* mEdges = reinterpret_cast<PxU16*>(address); address += data.mNbEdges.isBitSet() ? (sizeof(PxU16) * data.mNbEdges * 2) : 0; + PxU8* mDataVertexData8 = address; address += sizeof(PxU8) * Nb; // PT: leave that one last, so that we don't need to serialize "Nb" + + PX_ASSERT(!(size_t(mDataHullVertices) % sizeof(PxReal))); + PX_ASSERT(!(size_t(data.mPolygons) % sizeof(PxReal))); + PX_ASSERT(size_t(address)<=size_t(mDataMemory)+bytesNeeded); + + // Import vertices + readFloatBuffer(&mDataHullVertices->x, PxU32(3*data.mNbHullVertices), Mismatch, stream); + + if(version<=6) + { + PxU16 useUnquantizedNormals = readWord(Mismatch, stream); + PX_UNUSED(useUnquantizedNormals); + } + + // Import polygons + stream.read(data.mPolygons, data.mNbPolygons*sizeof(Gu::HullPolygonData)); + + if(Mismatch) + { + for(PxU32 i=0;i<data.mNbPolygons;i++) + flipData(data.mPolygons[i]); + } + + stream.read(mDataVertexData8, Nb); + stream.read(mDataFacesByEdges8, PxU32(data.mNbEdges*2)); + if(version <= 5) + { + //KS - we need to compute faces-by-vertices here + + bool noPlaneShift = false; + for(PxU32 i=0; i< data.mNbHullVertices; ++i) + { + PxU32 count = 0; + PxU8 inds[3]; + for(PxU32 j=0; j<data.mNbPolygons; ++j) + { + Gu::HullPolygonData& polygon = data.mPolygons[j]; + for(PxU32 k=0; k< polygon.mNbVerts; ++k) + { + PxU8 index = mDataVertexData8[polygon.mVRef8 + k]; + if(i == index) + { + //Found a polygon + inds[count++] = Ps::to8(j); + break; + } + } + if(count == 3) + break; + } + //We have 3 indices + //PX_ASSERT(count == 3); + //Do something here + if(count == 3) + { + mDataFacesByVertices8[i*3+0] = inds[0]; + mDataFacesByVertices8[i*3+1] = inds[1]; + mDataFacesByVertices8[i*3+2] = inds[2]; + } + else + { + noPlaneShift = true; + break; + } + } + + + if(noPlaneShift) + { + for(PxU32 a = 0; a < data.mNbHullVertices; ++a) + { + mDataFacesByVertices8[a*3] = 0xFF; + mDataFacesByVertices8[a*3+1] = 0xFF; + mDataFacesByVertices8[a*3+2] = 0xFF; + } + } + + } + else + stream.read(mDataFacesByVertices8, PxU32(data.mNbHullVertices * 3)); + + if (data.mNbEdges.isBitSet()) + { + if (version <= 7) + { + for (PxU32 a = 0; a < PxU32(data.mNbEdges * 2); ++a) + { + mEdges[a] = 0xFFFF; + } + } + else + { + readWordBuffer(mEdges, PxU32(data.mNbEdges * 2), Mismatch, stream); + } + } + return true; +} + +bool Gu::ConvexMesh::load(PxInputStream& stream) +{ + // Import header + PxU32 version; + bool mismatch; + if(!readHeader('C', 'V', 'X', 'M', version, mismatch, stream)) + return false; + + // Check if old (incompatible) mesh format is loaded + if (version < PX_CONVEX_VERSION) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Loading convex mesh failed: " + "Deprecated mesh cooking format."); + return false; + } + + // Import serialization flags + PxU32 serialFlags = readDword(mismatch, stream); + PX_UNUSED(serialFlags); + + if(!convexHullLoad(mHullData, stream, mNb)) + return false; + + // Import local bounds + float tmp[8]; + readFloatBuffer(tmp, 8, mismatch, stream); +// geomEpsilon = tmp[0]; + mHullData.mAABB = PxBounds3(PxVec3(tmp[1], tmp[2], tmp[3]), PxVec3(tmp[4],tmp[5],tmp[6])); + + // Import mass info + mMass = tmp[7]; + if(mMass!=-1.0f) + { + readFloatBuffer(&mInertia(0,0), 9, mismatch, stream); + readFloatBuffer(&mHullData.mCenterOfMass.x, 3, mismatch, stream); + } + + // Import gaussmaps + PxF32 gaussMapFlag = readFloat(mismatch, stream); + if(gaussMapFlag != -1.0f) + { + PX_ASSERT(gaussMapFlag == 1.0f); //otherwise file is corrupt + + PX_DELETE_AND_RESET(mBigConvexData); + PX_NEW_SERIALIZED(mBigConvexData,BigConvexData); + + if(mBigConvexData) + { + mBigConvexData->Load(stream); + mHullData.mBigConvexRawData = &mBigConvexData->mData; + } + } + +/* + printf("\n\n"); + printf("COM: %f %f %f\n", massInfo.centerOfMass.x, massInfo.centerOfMass.y, massInfo.centerOfMass.z); + printf("BND: %f %f %f\n", mHullData.aabb.getCenter().x, mHullData.aabb.getCenter().y, mHullData.aabb.getCenter().z); + printf("CNT: %f %f %f\n", mHullData.mCenterxx.x, mHullData.mCenterxx.y, mHullData.mCenterxx.z); + printf("COM-BND: %f BND-CNT: %f, CNT-COM: %f\n", (massInfo.centerOfMass - mHullData.aabb.getCenter()).magnitude(), (mHullData.aabb.getCenter() - mHullData.mCenterxx).magnitude(), (mHullData.mCenterxx - massInfo.centerOfMass).magnitude()); +*/ + +// TEST_INTERNAL_OBJECTS + mHullData.mInternal.mRadius = readFloat(mismatch, stream); + mHullData.mInternal.mExtents[0] = readFloat(mismatch, stream); + mHullData.mInternal.mExtents[1] = readFloat(mismatch, stream); + mHullData.mInternal.mExtents[2] = readFloat(mismatch, stream); + + PX_ASSERT(PxVec3(mHullData.mInternal.mExtents[0], mHullData.mInternal.mExtents[1], mHullData.mInternal.mExtents[2]).isFinite()); + PX_ASSERT(mHullData.mInternal.mExtents[0] != 0.0f); + PX_ASSERT(mHullData.mInternal.mExtents[1] != 0.0f); + PX_ASSERT(mHullData.mInternal.mExtents[2] != 0.0f); +//~TEST_INTERNAL_OBJECTS + return true; +} + + + +void Gu::ConvexMesh::release() +{ + decRefCount(); +} + +void Gu::ConvexMesh::onRefCountZero() +{ + if ((!getBufferSize()) || mMeshFactory->removeConvexMesh(*this)) // when the mesh failed to load properly, it will not have been added to the convex array + { + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, PxConcreteType::eCONVEX_MESH); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::ConvexMesh::release: double deletion detected!"); +} + +void Gu::ConvexMesh::acquireReference() +{ + incRefCount(); +} + +PxU32 Gu::ConvexMesh::getReferenceCount() const +{ + return getRefCount(); +} + +void Gu::ConvexMesh::getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const +{ + mass = Gu::ConvexMesh::getMass(); + localInertia = Gu::ConvexMesh::getInertia(); + localCenterOfMass = Gu::ConvexMesh::getHull().mCenterOfMass; +} + +PxBounds3 Gu::ConvexMesh::getLocalBounds() const +{ + PX_ASSERT(mHullData.mAABB.isValid()); + return PxBounds3::centerExtents(mHullData.mAABB.mCenter, mHullData.mAABB.mExtents); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmMatrix34.h" +#include "GuDebug.h" +void Gu::ConvexMesh::debugVisualize(Cm::RenderOutput& out, const PxTransform& pose, const PxMeshScale& scale) const +{ + const PxU32 scolor = PxU32(PxDebugColor::eARGB_MAGENTA); + + const PxVec3* vertices = mHullData.getHullVertices(); + const PxU8* indexBuffer = mHullData.getVertexData8(); + const PxU32 nbPolygons = getNbPolygonsFast(); + + const PxMat44 m44(PxMat33(pose.q) * scale.toMat33(), pose.p); + + out << m44 << scolor; // PT: no need to output this for each segment! + + for (PxU32 i = 0; i < nbPolygons; i++) + { + const PxU32 pnbVertices = mHullData.mPolygons[i].mNbVerts; + + PxVec3 begin = m44.transform(vertices[indexBuffer[0]]); // PT: transform it only once before the loop starts + for (PxU32 j = 1; j < pnbVertices; j++) + { + PxVec3 end = m44.transform(vertices[indexBuffer[j]]); + out.outputSegment(begin, end); + begin = end; + } + out.outputSegment(begin, m44.transform(vertices[indexBuffer[0]])); + + indexBuffer += pnbVertices; + } +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMesh.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMesh.h new file mode 100644 index 00000000..cf75ec31 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMesh.h @@ -0,0 +1,189 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_COLLISION_CONVEXMESH_H +#define GU_COLLISION_CONVEXMESH_H + +#include "foundation/PxBitAndData.h" +#include "PxConvexMesh.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "GuConvexMeshData.h" + +// PX_SERIALIZATION +#include "PxMetaData.h" +#include "CmRenderOutput.h" +//~PX_SERIALIZATION + +namespace physx +{ + +class BigConvexData; +class GuMeshFactory; +class PxMeshScale; + + +namespace Gu +{ + struct HullPolygonData; + + PX_INLINE PxU32 computeBufferSize(const Gu::ConvexHullData& data, PxU32 nb) + { + PxU32 bytesNeeded = sizeof(Gu::HullPolygonData) * data.mNbPolygons; + bytesNeeded += sizeof(PxVec3) * data.mNbHullVertices; + bytesNeeded += sizeof(PxU8) * data.mNbEdges * 2; // mFacesByEdges8 + bytesNeeded += sizeof(PxU8) * data.mNbHullVertices * 3; // mFacesByVertices8; + bytesNeeded += data.mNbEdges.isBitSet() ? (sizeof(PxU16) * data.mNbEdges * 2) : 0; // mEdges; + bytesNeeded += sizeof(PxU8) * nb; // mVertexData8 + + //4 align the whole thing! + const PxU32 mod = bytesNeeded % sizeof(PxReal); + if (mod) + bytesNeeded += sizeof(PxReal) - mod; + return bytesNeeded; + } + + // 0: includes raycast map + // 1: discarded raycast map + // 2: support map not always there + // 3: support stackless trees for non-recursive collision queries + // 4: no more opcode model + // 5: valencies table and gauss map combined, only exported over a vertex count treshold that depends on the platform cooked for. + // 6: removed support for edgeData16. + // 7: removed support for edge8Data. + // 8: removed support for triangles. + + // 9: removed local sphere. + //10: removed geometric center. + //11: removed mFlags, and mERef16 from Poly; nbVerts is just a byte. + //12: removed explicit minimum, maximum from Poly + //13: internal objects + #define PX_CONVEX_VERSION 13 + + class ConvexMesh : public PxConvexMesh, public Ps::UserAllocated, public Cm::RefCountable + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + PX_PHYSX_COMMON_API ConvexMesh(PxBaseFlags baseFlags) : PxConvexMesh(baseFlags), Cm::RefCountable(PxEmpty), mHullData(PxEmpty), mNb(PxEmpty) + { + mNb.setBit(); + } + + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& stream); + PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context); + PX_PHYSX_COMMON_API virtual void onRefCountZero(); + PX_PHYSX_COMMON_API static ConvexMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + void resolveReferences(PxDeserializationContext&) {} + virtual void requires(PxProcessPxBaseCallback&){} + //~PX_SERIALIZATION + PX_PHYSX_COMMON_API ConvexMesh(); + + ConvexMesh(GuMeshFactory& factory, ConvexHullData& data); + + PX_PHYSX_COMMON_API bool load(PxInputStream& stream); + + // PxConvexMesh + PX_PHYSX_COMMON_API virtual void release(); + PX_PHYSX_COMMON_API virtual PxU32 getNbVertices() const { return mHullData.mNbHullVertices; } + PX_PHYSX_COMMON_API virtual const PxVec3* getVertices() const { return mHullData.getHullVertices(); } + PX_PHYSX_COMMON_API virtual const PxU8* getIndexBuffer() const { return mHullData.getVertexData8(); } + PX_PHYSX_COMMON_API virtual PxU32 getNbPolygons() const { return mHullData.mNbPolygons; } + PX_PHYSX_COMMON_API virtual bool getPolygonData(PxU32 i, PxHullPolygon& data) const; + PX_PHYSX_COMMON_API virtual bool isGpuCompatible() const; + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const; + PX_PHYSX_COMMON_API virtual void acquireReference(); + + PX_PHYSX_COMMON_API virtual void getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const; + PX_PHYSX_COMMON_API virtual PxBounds3 getLocalBounds() const; + //~PxConvexMesh + + PX_FORCE_INLINE PxU32 getNbVerts() const { return mHullData.mNbHullVertices; } + PX_FORCE_INLINE const PxVec3* getVerts() const { return mHullData.getHullVertices(); } + PX_FORCE_INLINE PxU32 getNbPolygonsFast() const { return mHullData.mNbPolygons; } + PX_FORCE_INLINE const HullPolygonData& getPolygon(PxU32 i) const { return mHullData.mPolygons[i]; } + PX_FORCE_INLINE const HullPolygonData* getPolygons() const { return mHullData.mPolygons; } + PX_FORCE_INLINE PxU32 getNbEdges() const { return mHullData.mNbEdges; } + + PX_FORCE_INLINE const ConvexHullData& getHull() const { return mHullData; } + PX_FORCE_INLINE ConvexHullData& getHull() { return mHullData; } + PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mHullData.mAABB; } + PX_FORCE_INLINE PxReal getMass() const { return mMass; } + PX_FORCE_INLINE void setMass(PxReal mass) { mMass = mass; } + PX_FORCE_INLINE const PxMat33& getInertia() const { return mInertia; } + PX_FORCE_INLINE void setInertia(const PxMat33& inertia) { mInertia = inertia; } + + PX_FORCE_INLINE BigConvexData* getBigConvexData() const { return mBigConvexData; } + PX_FORCE_INLINE void setBigConvexData(BigConvexData* bcd) { mBigConvexData = bcd; } + + PX_FORCE_INLINE PxU32 getBufferSize() const { return computeBufferSize(mHullData, getNb()); } + + PX_PHYSX_COMMON_API virtual ~ConvexMesh(); + + PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; } + + protected: + ConvexHullData mHullData; + PxBitAndDword mNb; // ### PT: added for serialization. Try to remove later? + + BigConvexData* mBigConvexData; //!< optional, only for large meshes! PT: redundant with ptr in chull data? Could also be end of other buffer + PxReal mMass; //this is mass assuming a unit density that can be scaled by instances! + PxMat33 mInertia; //in local space of mesh! +private: + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization + + PX_FORCE_INLINE PxU32 getNb() const { return mNb; } + PX_FORCE_INLINE PxU32 ownsMemory() const { return PxU32(!mNb.isBitSet()); } + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + /** + \brief Perform convex mesh geometry debug visualization + + \param out Debug renderer. + \param pose World position. + \param scale Scale to apply. + */ + void debugVisualize( Cm::RenderOutput& out, const PxTransform& pose, const PxMeshScale& scale) const; + +#endif + }; + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMeshData.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMeshData.h new file mode 100644 index 00000000..60558b05 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexMeshData.h @@ -0,0 +1,215 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_MESH_DATA_H +#define GU_CONVEX_MESH_DATA_H + +#include "foundation/PxPlane.h" +#include "PsIntrinsics.h" +#include "GuSerialize.h" +#include "GuCenterExtents.h" +#include "foundation/PxBitAndData.h" + +// Data definition + +namespace physx +{ +namespace Gu +{ + struct BigConvexRawData; + + struct HullPolygonData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // PT: this structure won't be allocated with PX_NEW because polygons aren't allocated alone (with a dedicated alloc). + // Instead they are part of a unique allocation/buffer containing all data for the ConvexHullData class (polygons, followed by + // hull vertices, edge data, etc). As a result, ctors for embedded classes like PxPlane won't be called. + + PxPlane mPlane; //!< Plane equation for this polygon //Could drop 4th elem as it can be computed from any vertex as: d = - p.dot(n); + PxU16 mVRef8; //!< Offset of vertex references in hull vertex data (CS: can we assume indices are tightly packed and offsets are ascending?? DrawObjects makes and uses this assumption) + PxU8 mNbVerts; //!< Number of vertices/edges in the polygon + PxU8 mMinIndex; //!< Index of the polygon vertex that has minimal projection along this plane's normal. + + PX_FORCE_INLINE PxReal getMin(const PxVec3* PX_RESTRICT hullVertices) const //minimum of projection of the hull along this plane normal + { + return mPlane.n.dot(hullVertices[mMinIndex]); + } + + PX_FORCE_INLINE PxReal getMax() const { return -mPlane.d; } //maximum of projection of the hull along this plane normal + }; + + PX_FORCE_INLINE void flipData(Gu::HullPolygonData& data) + { + flip(data.mPlane.n.x); + flip(data.mPlane.n.y); + flip(data.mPlane.n.z); + flip(data.mPlane.d); + flip(data.mVRef8); + } + // PT: if this one breaks, please make sure the 'flipData' function is properly updated. + PX_COMPILE_TIME_ASSERT(sizeof(Gu::HullPolygonData) == 20); + +// TEST_INTERNAL_OBJECTS + struct InternalObjectsData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxReal mRadius; + PxReal mExtents[3]; + + PX_FORCE_INLINE void reset() + { + mRadius = 0.0f; + mExtents[0] = 0.0f; + mExtents[1] = 0.0f; + mExtents[2] = 0.0f; + } + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::InternalObjectsData) == 16); +//~TEST_INTERNAL_OBJECTS + + struct ConvexHullData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; //!< bounds TODO: compute this on the fly from first 6 vertices in the vertex array. We'll of course need to sort the most extreme ones to the front. + PxVec3 mCenterOfMass; //in local space of mesh! + + // PT: WARNING: mNbHullVertices *must* appear before mBigConvexRawData for ConvX to be able to do "big raw data" surgery + + PxBitAndWord mNbEdges; //!<the highest bit indicate whether we have grb data, the other 15 bits indicate the number of edges in this convex hull + + PxU8 mNbHullVertices; //!< Number of vertices in the convex hull + PxU8 mNbPolygons; //!< Number of planar polygons composing the hull + + HullPolygonData* mPolygons; //!< Array of mNbPolygons structures + BigConvexRawData* mBigConvexRawData; //!< Hill climbing data, only for large convexes! else NULL. + +// TEST_INTERNAL_OBJECTS + InternalObjectsData mInternal; +//~TEST_INTERNAL_OBJECTS + + PX_FORCE_INLINE ConvexHullData(const PxEMPTY) : mNbEdges(PxEmpty) + { + } + + PX_FORCE_INLINE ConvexHullData() + { + } + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert at the end of file + return static_cast<const CenterExtentsPadded&>(mAABB); + } + + PX_FORCE_INLINE const PxVec3* getHullVertices() const //!< Convex hull vertices + { + const char* tmp = reinterpret_cast<const char*>(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + return reinterpret_cast<const PxVec3*>(tmp); + } + + PX_FORCE_INLINE const PxU8* getFacesByEdges8() const //!< for each edge, gives 2 adjacent polygons; used by convex-convex code to come up with all the convex' edge normals. + { + const char* tmp = reinterpret_cast<const char*>(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + return reinterpret_cast<const PxU8*>(tmp); + } + + PX_FORCE_INLINE const PxU8* getFacesByVertices8() const //!< for each edge, gives 2 adjacent polygons; used by convex-convex code to come up with all the convex' edge normals. + { + const char* tmp = reinterpret_cast<const char*>(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + return reinterpret_cast<const PxU8*>(tmp); + } + + //If we don't build the convex hull with grb data, we will return NULL pointer + PX_FORCE_INLINE const PxU16* getVerticesByEdges16() const //!< Vertex indices indexed by unique edges + { + if (mNbEdges.isBitSet()) + { + const char* tmp = reinterpret_cast<const char*>(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + tmp += sizeof(PxU8) * mNbHullVertices * 3; + return reinterpret_cast<const PxU16*>(tmp); + } + return NULL; + } + + PX_FORCE_INLINE const PxU8* getVertexData8() const //!< Vertex indices indexed by hull polygons + { + const char* tmp = reinterpret_cast<const char*>(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + tmp += sizeof(PxU8) * mNbHullVertices * 3; + if (mNbEdges.isBitSet()) + tmp += sizeof(PxU16) * mNbEdges * 2; + return reinterpret_cast<const PxU8*>(tmp); + } + + }; + #if PX_P64_FAMILY + PX_COMPILE_TIME_ASSERT(sizeof(Gu::ConvexHullData) == 72); + #else + PX_COMPILE_TIME_ASSERT(sizeof(Gu::ConvexHullData) == 64); + #endif + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::ConvexHullData, mCenterOfMass)>=PX_OFFSET_OF(Gu::ConvexHullData, mAABB)+4); + +} // namespace Gu + +} + +//#pragma PX_POP_PACK + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexSupportTable.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexSupportTable.cpp new file mode 100644 index 00000000..912e5594 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexSupportTable.cpp @@ -0,0 +1,44 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecBox.h" + +namespace physx +{ + const Ps::aos::BoolV boxVertexTable[8] = { + Ps::aos::BFFFF(),//--- + Ps::aos::BTFFF(),//+-- + Ps::aos::BFTFF(),//-+- + Ps::aos::BTTFF(),//++- + Ps::aos::BFFTF(),//--+ + Ps::aos::BTFTF(),//+-+ + Ps::aos::BFTTF(),//-++ + Ps::aos::BTTTF(),//+++ + }; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexSupportTable.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexSupportTable.h new file mode 100644 index 00000000..017dfc01 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexSupportTable.h @@ -0,0 +1,162 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_SUPPORT_TABLE_H +#define GU_CONVEX_SUPPORT_TABLE_H + +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ +namespace Gu +{ + + class TriangleV; + class CapsuleV; + class BoxV; + class ConvexHullV; + class ShrunkConvexHullV; + class ConvexHullNoScaleV; + class ShrunkConvexHullNoScaleV; + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + class SupportLocal + { + public: + Ps::aos::Vec3V shapeSpaceCenterOfMass; + const Ps::aos::PsTransformV& transform; + const Ps::aos::Mat33V& vertex2Shape; + const Ps::aos::Mat33V& shape2Vertex; + const bool isIdentityScale; + + SupportLocal(const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true): transform(_transform), + vertex2Shape(_vertex2Shape), shape2Vertex(_shape2Vertex), isIdentityScale(_isIdentityScale) + { + } + + void setShapeSpaceCenterofMass(const Ps::aos::Vec3VArg _shapeSpaceCenterOfMass) + { + shapeSpaceCenterOfMass = _shapeSpaceCenterOfMass; + } + virtual ~SupportLocal() {} + virtual Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const = 0; + virtual void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const = 0; + virtual void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts)const = 0; + + protected: + SupportLocal& operator=(const SupportLocal&); + }; +#if PX_VC + #pragma warning(pop) +#endif + + template <typename Convex> + class SupportLocalImpl : public SupportLocal + { + + public: + const Convex& conv; + SupportLocalImpl(const Convex& _conv, const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true) : + SupportLocal(_transform, _vertex2Shape, _shape2Vertex, _isIdentityScale), conv(_conv) + { + } + + Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const + { + //return conv.supportVertsLocal(dir); + return conv.supportLocal(dir); + } + + void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const + { + return conv.supportLocal(dir, min, max); + } + + + void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts) const + { + conv.populateVerts(inds, numInds, originalVerts, verts); + } + + PX_FORCE_INLINE void populateVertsFast(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts) const + { + conv.populateVerts(inds, numInds, originalVerts, verts); + } + + protected: + SupportLocalImpl& operator=(const SupportLocalImpl&); + + }; + + template <typename Convex, typename ShrunkConvex> + class SupportLocalShrunkImpl : public SupportLocal + { + SupportLocalShrunkImpl& operator=(const SupportLocalShrunkImpl&); + public: + const Convex& conv; + const ShrunkConvex& shrunkConvex; + SupportLocalShrunkImpl(const Convex& _conv, const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true) : + SupportLocal(_transform, _vertex2Shape, _shape2Vertex, _isIdentityScale), conv(_conv), + shrunkConvex(static_cast<const ShrunkConvex&>(static_cast<const ConvexV&>(_conv))) //ML: The types may or may not be related but they will share the base class (ConvexV) so to avoid + //a compiler warning if we use reinterpret_cast unnecessarily, we static cast to base, then back up to + //the derived type. We guarantee that all types that are converted between are data compatible + { + } + + Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const + { + //return conv.supportVertsLocal(dir); + return conv.supportLocal(dir); + } + + void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const + { + return conv.supportLocal(dir, min, max); + } + + void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts) const + { + conv.populateVerts(inds, numInds, originalVerts, verts); + } + + PX_COMPILE_TIME_ASSERT(sizeof(Convex) == sizeof(ShrunkConvex)); + + }; + +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexUtilsInternal.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexUtilsInternal.cpp new file mode 100644 index 00000000..e7b6109f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexUtilsInternal.cpp @@ -0,0 +1,78 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuConvexUtilsInternal.h" + +#include "foundation/PxBounds3.h" +#include "CmScaling.h" +#include "GuBoxConversion.h" +#include "PxConvexMeshGeometry.h" +#include "GuConvexMesh.h" + +using namespace physx; +using namespace Gu; + +void Gu::computeHullOBB(Box& hullOBB, const PxBounds3& hullAABB, float offset, + const Cm::Matrix34& convexPose, + const Cm::Matrix34& meshPose, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh) +{ + // transform bounds = mesh space + Cm::Matrix34 m0to1 = meshPose.transformTranspose(convexPose); + + hullOBB.extents = hullAABB.getExtents() + PxVec3(offset); + hullOBB.center = m0to1.transform(hullAABB.getCenter()); + hullOBB.rot = m0to1.m; + + if(!idtScaleMesh) + meshScaling.transformQueryBounds(hullOBB.center, hullOBB.extents, hullOBB.rot); +} + +void Gu::computeVertexSpaceOBB(Box& dst, const Box& src, const PxTransform& meshPose, const PxMeshScale& meshScale) +{ + // AP scaffold failure in x64 debug in GuConvexUtilsInternal.cpp + //PX_ASSERT("Performance warning - this path shouldn't execute for identity mesh scale." && !meshScale.isIdentity()); + + dst = transform(meshScale.getInverse() * Cm::Matrix34(meshPose.getInverse()), src); +} + +void Gu::computeOBBAroundConvex( + Box& obb, const PxConvexMeshGeometry& convexGeom, const PxConvexMesh* cm, const PxTransform& convexPose) +{ + const CenterExtents& aabb = static_cast<const Gu::ConvexMesh*>(cm)->getLocalBoundsFast(); + + if(convexGeom.scale.isIdentity()) + { + const PxMat33 m(convexPose.q); + obb = Gu::Box(m.transform(aabb.mCenter) + convexPose.p, aabb.mExtents, m); + } + else + { + obb = transform(Cm::Matrix34(convexPose) * convexGeom.scale.toMat33(), Box(aabb.mCenter, aabb.mExtents, PxMat33(PxIdentity))); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexUtilsInternal.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexUtilsInternal.h new file mode 100644 index 00000000..533fa956 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuConvexUtilsInternal.h @@ -0,0 +1,67 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_UTILS_INTERNALS_H +#define GU_CONVEX_UTILS_INTERNALS_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +class PxMeshScale; +class PxConvexMeshGeometry; +class PxConvexMesh; + +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ + class Box; + + void computeHullOBB( + Gu::Box& hullOBB, const PxBounds3& hullAABB, float offset, const Cm::Matrix34& world0, + const Cm::Matrix34& world1, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh); + + // src = input + // computes a box in vertex space (including skewed scale) from src world box + void computeVertexSpaceOBB(Gu::Box& dst, const Gu::Box& src, const PxTransform& meshPose, const PxMeshScale& meshScale); + + PX_PHYSX_COMMON_API void computeOBBAroundConvex( + Gu::Box& obb, const PxConvexMeshGeometry& convexGeom, const PxConvexMesh* cm, const PxTransform& convexPose); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuCubeIndex.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuCubeIndex.h new file mode 100644 index 00000000..dad4f7d2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuCubeIndex.h @@ -0,0 +1,155 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CUBE_INDEX_H +#define GU_CUBE_INDEX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsFPU.h" + +namespace physx +{ + + enum CubeIndex + { + CUBE_RIGHT, + CUBE_LEFT, + CUBE_TOP, + CUBE_BOTTOM, + CUBE_FRONT, + CUBE_BACK, + + CUBE_FORCE_DWORD = 0x7fffffff + }; + + /* + It's pretty straightforwards in concept (though the execution in hardware is + a bit crufty and complex). You use a 3D texture coord to look up a texel in + a cube map. First you find which of the axis has the largest value (i.e. + X,Y,Z), and then the sign of that axis decides which face you are going to + use. Which is why the faces are called +X, -X, +Y, -Y, +Z, -Z - after their + principle axis. Then you scale the vector so that the largest value is +/-1. + Then use the other two as 2D coords to look up your texel (with a 0.5 scale + & offset). + + For example, vector (0.4, -0.2, -0.5). Largest value is the Z axis, and it's + -ve, so we're reading from the -Z map. Scale so that this Z axis is +/-1, + and you get the vector (0.8, -0.4, -1.0). So now use the other two values to + look up your texel. So we look up texel (0.8, -0.4). The scale & offset move + the -1->+1 range into the usual 0->1 UV range, so we actually look up texel + (0.9, 0.3). The filtering is extremely complex, especially where three maps + meet, but that's a hardware problem :-) + */ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Cubemap lookup function. + * + * To transform returned uvs into mapping coordinates : + * u += 1.0f; u *= 0.5f; + * v += 1.0f; v *= 0.5f; + * + * \fn CubemapLookup(const PxVec3& direction, float& u, float& v) + * \param direction [in] a direction vector + * \param u [out] impact coordinate on the unit cube, in [-1,1] + * \param v [out] impact coordinate on the unit cube, in [-1,1] + * \return cubemap texture index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PX_INLINE CubeIndex CubemapLookup(const PxVec3& direction, float& u, float& v); + + PX_INLINE PxU32 ComputeCubemapOffset(const PxVec3& dir, PxU32 subdiv) + { + float u,v; + const CubeIndex CI = CubemapLookup(dir, u, v); + + // Remap to [0, subdiv[ + const float Coeff = 0.5f * float(subdiv-1); + u += 1.0f; u *= Coeff; + v += 1.0f; v *= Coeff; + + // Compute offset + return PxU32(CI)*(subdiv*subdiv) + PxU32(u)*subdiv + PxU32(v); + } + + + PX_INLINE PxU32 ComputeCubemapNearestOffset(const PxVec3& dir, PxU32 subdiv) + { + float u,v; + const CubeIndex CI = CubemapLookup(dir, u, v); + + // Remap to [0, subdiv] + const float Coeff = 0.5f * float(subdiv-1); + u += 1.0f; u *= Coeff; + v += 1.0f; v *= Coeff; + + // Compute offset + return PxU32(CI)*(subdiv*subdiv) + PxU32(u + 0.5f)*subdiv + PxU32(v + 0.5f); + } + + + PX_INLINE CubeIndex CubemapLookup(const PxVec3& direction, float& u, float& v) + { + const PxU32* binary = reinterpret_cast<const PxU32*>(&direction.x); + + const PxU32 absPx = binary[0] & ~PX_SIGN_BITMASK; + const PxU32 absNy = binary[1] & ~PX_SIGN_BITMASK; + const PxU32 absNz = binary[2] & ~PX_SIGN_BITMASK; + + PxU32 Index1 = 0; //x biggest axis + PxU32 Index2 = 1; + PxU32 Index3 = 2; + if( (absNy > absPx) & (absNy > absNz)) + { + //y biggest + Index2 = 2; + Index3 = 0; + Index1 = 1; + } + else if(absNz > absPx) + { + //z biggest + Index2 = 0; + Index3 = 1; + Index1 = 2; + } + + const PxF32* data = &direction.x; + const float Coeff = 1.0f / fabsf(data[Index1]); + u = data[Index2] * Coeff; + v = data[Index3] * Coeff; + + const PxU32 Sign = binary[Index1]>>31; + return CubeIndex(Sign|(Index1+Index1)); + } + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuHillClimbing.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuHillClimbing.cpp new file mode 100644 index 00000000..c945385f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuHillClimbing.cpp @@ -0,0 +1,96 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxVec3.h" +#include "foundation/PxAssert.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "GuHillClimbing.h" +#include "GuBigConvexData2.h" + +namespace physx +{ + +void localSearch(PxU32& id, const PxVec3& dir, const PxVec3* verts, const Gu::BigConvexRawData* val) +{ + // WARNING: there is a problem on x86 with a naive version of this code, where truncation + // of values from 80 bits to 32 bits as they're stored in memory means that iteratively moving to + // an adjacent vertex of greater support can go into an infinite loop. So we use a version which + // never vists a version twice. Note - this might not be enough for GJK, since local + // termination of the support function might not be enough to ensure convergence of GJK itself. + + // if we got here, we'd better have vertices and valencies + PX_ASSERT(verts && val); + + class TinyBitMap + { + public: + PxU32 m[8]; + PX_FORCE_INLINE TinyBitMap() { m[0] = m[1] = m[2] = m[3] = m[4] = m[5] = m[6] = m[7] = 0; } + PX_FORCE_INLINE void set(PxU8 v) { m[v>>5] |= 1<<(v&31); } + PX_FORCE_INLINE bool get(PxU8 v) const { return (m[v>>5] & 1<<(v&31)) != 0; } + }; + + TinyBitMap visited; + + const Gu::Valency* Valencies = val->mValencies; + const PxU8* Adj = val->mAdjacentVerts; + + PX_ASSERT(Valencies && Adj); + + // Get the initial value and the initial vertex + float MaxVal = dir.dot(verts[id]); + PxU32 NextVtx = id; + + do + { + PxU16 NbNeighbors = Valencies[NextVtx].mCount; + const PxU8* Run = Adj + Valencies[NextVtx].mOffset; + id = NextVtx; + while(NbNeighbors--) + { + const PxU8 Neighbor = *Run++; + + if(!visited.get(Neighbor)) + { + visited.set(Neighbor); + + const float CurVal = dir.dot(verts[Neighbor]); + + if(CurVal>MaxVal) + { + MaxVal = CurVal; + NextVtx = Neighbor; + } + } + } + } while(NextVtx!=id); +} + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuHillClimbing.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuHillClimbing.h new file mode 100644 index 00000000..5eb9d31a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuHillClimbing.h @@ -0,0 +1,46 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HILL_CLIMBING_H +#define GU_HILL_CLIMBING_H + +#include "Ps.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + namespace Gu + { + struct BigConvexRawData; + } + + void localSearch(PxU32& id, const PxVec3& dir, const PxVec3* verts, const Gu::BigConvexRawData* val); +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuShapeConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/convex/GuShapeConvex.cpp new file mode 100644 index 00000000..606cafab --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuShapeConvex.cpp @@ -0,0 +1,516 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuShapeConvex.h" +#include "GuBigConvexData.h" +#include "GuEdgeListData.h" +#include "GuInternal.h" + +#include "CmMatrix34.h" +#include "GuHillClimbing.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE PxU32 selectClosestPolygon(PxReal& maxDp_, PxU32 numPolygons, const Gu::HullPolygonData* polys, const PxVec3& axis) +{ + float maxDp = polys[0].mPlane.n.dot(axis); + PxU32 closest = 0; + + // Loop through polygons + for(PxU32 i=1; i <numPolygons; i++) + { + // Catch current polygon and test its plane + const PxReal dp = polys[i].mPlane.n.dot(axis); + if(dp>maxDp) + { + maxDp = dp; + closest = i; + } + } + maxDp_ = maxDp; + return closest; +} + +static PxU32 SelectClosestEdgeCB_Convex(const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localSpaceDirection) +{ + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; + + const Gu::HullPolygonData* PX_RESTRICT polys = data.mPolygons; + + PxReal maxDp; + // ##might not be needed + PxU32 closest = ::selectClosestPolygon(maxDp, data.mNbPolygons, polys, vertexSpaceDirection); + + // Since the convex is closed, at least some poly must satisfy this + PX_ASSERT(maxDp>=0); + + const PxU32 numEdges = data.mNbEdges; + const PxU8* const edgeToFace = data.mFacesByEdges; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + PxReal maxDpSq = maxDp * maxDp; + for(PxU32 i=0; i < numEdges; i++) + { + const PxU8 f0 = edgeToFace[i*2]; + const PxU8 f1 = edgeToFace[i*2+1]; + + // unnormalized edge normal + const PxVec3 edgeNormal = polys[f0].mPlane.n + polys[f1].mPlane.n; + const PxReal enMagSq = edgeNormal.magnitudeSquared(); + //Test normal of current edge - squared test is valid if dp and maxDp both >= 0 + const float dp = edgeNormal.dot(vertexSpaceDirection); + if(dp>=0.0f && dp*dp>maxDpSq*enMagSq) + { + maxDpSq = dp*dp/enMagSq; + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const PxU8* FBE = edgeToFace; + + const PxU32 f0 = FBE[closestEdge*2]; + const PxU32 f1 = FBE[closestEdge*2+1]; + + const PxReal dp0 = polys[f0].mPlane.n.dot(vertexSpaceDirection); + const PxReal dp1 = polys[f1].mPlane.n.dot(vertexSpaceDirection); + if(dp0>dp1) + closest = f0; + else + closest = f1; + } + return closest; +} + +// Hull projection callback for "small" hulls +static void HullProjectionCB_SmallConvex(const PolygonalData& data, const PxVec3& dir, + const Cm::Matrix34& world, + const Cm::FastVertex2ShapeScaling& scaling, + PxReal& min, PxReal& max) +{ + const PxVec3 localSpaceDirection = world.rotateTranspose(dir); + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; + + // PT: prevents aliasing + PxReal minimum = PX_MAX_REAL; + PxReal maximum = -PX_MAX_REAL; + + //brute-force, localspace + { + const PxVec3* PX_RESTRICT verts = data.mVerts; + PxU32 numVerts = data.mNbVerts; + while(numVerts--) + { + const PxReal dp = (*verts++).dot(vertexSpaceDirection); + minimum = physx::intrinsics::selectMin(minimum, dp); + maximum = physx::intrinsics::selectMax(maximum, dp); + } + + } + + const PxReal offset = world.p.dot(dir); + min = minimum + offset; + max = maximum + offset; +} + +static PxU32 computeNearestOffset(const PxU32 subdiv, const PxVec3& dir) +{ + // ComputeCubemapNearestOffset(const Point& dir, udword subdiv) + + // PT: ok so why exactly was the code duplicated here? + // PxU32 CI = CubemapLookup(dir,u,v) + + PxU32 index; + PxReal coeff; + // find largest axis + PxReal absNx = PxAbs(dir.x); + PxReal absNy = PxAbs(dir.y); + PxReal absNz = PxAbs(dir.z); + + if( absNy > absNx && + absNy > absNz) + { + //y biggest + index = 1; + coeff = 1.0f/absNy; + } + else if(absNz > absNx) + { + index = 2; + coeff = 1.0f/absNz; + } + else + { + index = 0; + coeff = 1.0f/absNx; + } + + union + { + PxU32 aU32; + PxReal aFloat; + } conv; + + conv.aFloat = dir[index]; + PxU32 sign = conv.aU32>>31; + + const PxU32 index2 = Ps::getNextIndex3(index); + const PxU32 index3 = Ps::getNextIndex3(index2); + PxReal u = dir[index2] * coeff; + PxReal v = dir[index3] * coeff; + + PxU32 CI = (sign | (index+index)); + + //Remap to [0, subdiv[ + coeff = 0.5f * PxReal(subdiv-1); + u += 1.0f; u *= coeff; + v += 1.0f; v *= coeff; + + //Round to nearest + PxU32 ui = PxU32(u); + PxU32 vi = PxU32(v); + + PxReal du = u - PxReal(ui); + PxReal dv = v - PxReal(vi); + if(du>0.5f) ui++; + if(dv>0.5f) vi++; + + //Compute offset + return CI*(subdiv*subdiv) + ui*subdiv + vi; +} + +// Hull projection callback for "big" hulls +static void HullProjectionCB_BigConvex(const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum) +{ + const PxVec3* PX_RESTRICT verts = data.mVerts; + + const PxVec3 localSpaceDirection = world.rotateTranspose(dir); + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; //NB: triangles are always shape 1! eek! + + // This version is better for objects with a lot of vertices + const Gu::BigConvexRawData* bigData = data.mBigData; + PxU32 minID = 0, maxID = 0; + { + const PxU32 offset = computeNearestOffset(bigData->mSubdiv, -vertexSpaceDirection); + minID = bigData->mSamples[offset]; + maxID = bigData->getSamples2()[offset]; + } + + // Do hillclimbing! + localSearch(minID, -vertexSpaceDirection, verts, bigData); + localSearch(maxID, vertexSpaceDirection, verts, bigData); + + const PxReal offset = world.p.dot(dir); + minimum = offset + verts[minID].dot(vertexSpaceDirection); + maximum = offset + verts[maxID].dot(vertexSpaceDirection); + PX_ASSERT(maximum >= minimum); +} + +void Gu::getPolygonalData_Convex(PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling) +{ + dst->mCenter = scaling * src->mCenterOfMass; + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + +// TEST_INTERNAL_OBJECTS + dst->mInternal = src->mInternal; +//~TEST_INTERNAL_OBJECTS + + dst->mBigData = src->mBigConvexRawData; + + // This threshold test doesnt cost much and many customers cook on PC and use this on 360. + // 360 has a much higher threshold than PC(and it makes a big difference) + // PT: the cool thing is that this test is now done once by contact generation call, not once by hull projection + if(!src->mBigConvexRawData) + dst->mProjectHull = HullProjectionCB_SmallConvex; + else + dst->mProjectHull = HullProjectionCB_BigConvex; + dst->mSelectClosestEdgeCB = SelectClosestEdgeCB_Convex; +} + +// Box emulating convex mesh + +// Face0: 0-1-2-3 +// Face1: 1-5-6-2 +// Face2: 5-4-7-6 +// Face3: 4-0-3-7 +// Face4; 3-2-6-7 +// Face5: 4-5-1-0 + +// 7+------+6 0 = --- +// /| /| 1 = +-- +// / | / | 2 = ++- +// / 4+---/--+5 3 = -+- +// 3+------+2 / y z 4 = --+ +// | / | / | / 5 = +-+ +// |/ |/ |/ 6 = +++ +// 0+------+1 *---x 7 = -++ + +static const PxU8 gPxcBoxPolygonData[] = { + 0, 1, 2, 3, + 1, 5, 6, 2, + 5, 4, 7, 6, + 4, 0, 3, 7, + 3, 2, 6, 7, + 4, 5, 1, 0, +}; + +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) +static PxVec3 gPxcBoxEdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; +#undef INVSQRT2 + +// ### needs serious checkings + // Flags(16), Count(16), Offset(32); +static Gu::EdgeDescData gPxcBoxEdgeDesc[] = { + {Gu::PX_EDGE_ACTIVE, 2, 0}, + {Gu::PX_EDGE_ACTIVE, 2, 2}, + {Gu::PX_EDGE_ACTIVE, 2, 4}, + {Gu::PX_EDGE_ACTIVE, 2, 6}, + {Gu::PX_EDGE_ACTIVE, 2, 8}, + {Gu::PX_EDGE_ACTIVE, 2, 10}, + {Gu::PX_EDGE_ACTIVE, 2, 12}, + {Gu::PX_EDGE_ACTIVE, 2, 14}, + {Gu::PX_EDGE_ACTIVE, 2, 16}, + {Gu::PX_EDGE_ACTIVE, 2, 18}, + {Gu::PX_EDGE_ACTIVE, 2, 20}, + {Gu::PX_EDGE_ACTIVE, 2, 22}, +}; + +// ### needs serious checkings +static PxU8 gPxcBoxFaceByEdge[] = { + 0,5, // Edge 0-1 + 0,1, // Edge 1-2 + 0,4, // Edge 2-3 + 0,3, // Edge 3-0 + 2,4, // Edge 7-6 + 1,2, // Edge 6-5 + 2,5, // Edge 5-4 + 2,3, // Edge 4-7 + 1,5, // Edge 1-5 + 1,4, // Edge 6-2 + 3,4, // Edge 3-7 + 3,5, // Edge 4-0 +}; + +static PxU32 SelectClosestEdgeCB_Box(const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localDirection) +{ + PX_UNUSED(scaling); + + PxReal maxDp; + // ##might not be needed + PxU32 closest = ::selectClosestPolygon(maxDp, 6, data.mPolygons, localDirection); + + PxU32 numEdges = 12; + const PxVec3* PX_RESTRICT edgeNormals = gPxcBoxEdgeNormals; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + for(PxU32 i=0; i < numEdges; i++) + { + //Test normal of current edge + const float dp = edgeNormals[i].dot(localDirection); + if(dp>maxDp) + { + maxDp = dp; + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const Gu::EdgeDescData* PX_RESTRICT ED = gPxcBoxEdgeDesc; + const PxU8* PX_RESTRICT FBE = gPxcBoxFaceByEdge; + + PX_ASSERT(ED[closestEdge].Count==2); + const PxU32 f0 = FBE[ED[closestEdge].Offset]; + const PxU32 f1 = FBE[ED[closestEdge].Offset+1]; + + const PxReal dp0 = data.mPolygons[f0].mPlane.n.dot(localDirection); + const PxReal dp1 = data.mPolygons[f1].mPlane.n.dot(localDirection); + if(dp0>dp1) + closest = f0; + else + closest = f1; + } + + return closest; +} + +static PX_FORCE_INLINE void projectBox(PxVec3& p, const PxVec3& localDir, const PxVec3& extents) +{ + // PT: the original code didn't have branches or FPU comparisons. Why rewrite it ? +// p.x = (localDir.x >= 0) ? extents.x : -extents.x; +// p.y = (localDir.y >= 0) ? extents.y : -extents.y; +// p.z = (localDir.z >= 0) ? extents.z : -extents.z; + p.x = physx::intrinsics::fsel(localDir.x, extents.x, -extents.x); + p.y = physx::intrinsics::fsel(localDir.y, extents.y, -extents.y); + p.z = physx::intrinsics::fsel(localDir.z, extents.z, -extents.z); +} + +static void HullProjectionCB_Box(const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum) +{ + PX_UNUSED(scaling); + + const PxVec3 localDir = world.rotateTranspose(dir); + + PxVec3 p; + projectBox(p, localDir, *data.mHalfSide); + + const PxReal offset = world.p.dot(dir); + const PxReal tmp = p.dot(localDir); + maximum = offset + tmp; + minimum = offset - tmp; +} + +PolygonalBox::PolygonalBox(const PxVec3& halfSide) : mHalfSide(halfSide) +{ + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + PxVec3 minimum = -mHalfSide; + PxVec3 maximum = mHalfSide; + // Generate 8 corners of the bbox + mVertices[0] = PxVec3(minimum.x, minimum.y, minimum.z); + mVertices[1] = PxVec3(maximum.x, minimum.y, minimum.z); + mVertices[2] = PxVec3(maximum.x, maximum.y, minimum.z); + mVertices[3] = PxVec3(minimum.x, maximum.y, minimum.z); + mVertices[4] = PxVec3(minimum.x, minimum.y, maximum.z); + mVertices[5] = PxVec3(maximum.x, minimum.y, maximum.z); + mVertices[6] = PxVec3(maximum.x, maximum.y, maximum.z); + mVertices[7] = PxVec3(minimum.x, maximum.y, maximum.z); + + //Setup the polygons + for(PxU8 i=0; i < 6; i++) + { + mPolygons[i].mNbVerts = 4; + mPolygons[i].mVRef8 = PxU16(i*4); + } + + // ### planes needs *very* careful checks + // X axis + mPolygons[1].mPlane.n = PxVec3(1.0f, 0.0f, 0.0f); + mPolygons[1].mPlane.d = -mHalfSide.x; + mPolygons[3].mPlane.n = PxVec3(-1.0f, 0.0f, 0.0f); + mPolygons[3].mPlane.d = -mHalfSide.x; + + mPolygons[1].mMinIndex = 0; + mPolygons[3].mMinIndex = 1; + +// mPolygons[1].mMinObsolete = -mHalfSide.x; +// mPolygons[3].mMinObsolete = -mHalfSide.x; + + PX_ASSERT(mPolygons[1].getMin(mVertices) == -mHalfSide.x); + PX_ASSERT(mPolygons[3].getMin(mVertices) == -mHalfSide.x); + + + // Y axis + mPolygons[4].mPlane.n = PxVec3(0.f, 1.0f, 0.0f); + mPolygons[4].mPlane.d = -mHalfSide.y; + mPolygons[5].mPlane.n = PxVec3(0.0f, -1.0f, 0.0f); + mPolygons[5].mPlane.d = -mHalfSide.y; + + mPolygons[4].mMinIndex = 0; + mPolygons[5].mMinIndex = 2; +// mPolygons[4].mMinObsolete = -mHalfSide.y; +// mPolygons[5].mMinObsolete = -mHalfSide.y; + + PX_ASSERT(mPolygons[4].getMin(mVertices) == -mHalfSide.y); + PX_ASSERT(mPolygons[5].getMin(mVertices) == -mHalfSide.y); + + // Z axis + mPolygons[2].mPlane.n = PxVec3(0.f, 0.0f, 1.0f); + mPolygons[2].mPlane.d = -mHalfSide.z; + mPolygons[0].mPlane.n = PxVec3(0.0f, 0.0f, -1.0f); + mPolygons[0].mPlane.d = -mHalfSide.z; + + mPolygons[2].mMinIndex = 0; + mPolygons[0].mMinIndex = 4; +// mPolygons[2].mMinObsolete = -mHalfSide.z; +// mPolygons[0].mMinObsolete = -mHalfSide.z; + PX_ASSERT(mPolygons[2].getMin(mVertices) == -mHalfSide.z); + PX_ASSERT(mPolygons[0].getMin(mVertices) == -mHalfSide.z); +} + +void PolygonalBox::getPolygonalData(PolygonalData* PX_RESTRICT dst) const +{ + dst->mCenter = PxVec3(0.0f, 0.0f, 0.0f); + dst->mNbVerts = 8; + dst->mNbPolygons = 6; + dst->mPolygons = mPolygons; + dst->mNbEdges = 0; + dst->mVerts = mVertices; + dst->mPolygonVertexRefs = gPxcBoxPolygonData; + dst->mFacesByEdges = NULL; + dst->mInternal.mRadius = 0.0f; + dst->mInternal.mExtents[0] = 0.0f; + dst->mInternal.mExtents[1] = 0.0f; + dst->mInternal.mExtents[2] = 0.0f; +// dst->mBigData = NULL; + dst->mHalfSide = &mHalfSide; + dst->mProjectHull = HullProjectionCB_Box; + dst->mSelectClosestEdgeCB = SelectClosestEdgeCB_Box; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/convex/GuShapeConvex.h b/PhysX_3.4/Source/GeomUtils/src/convex/GuShapeConvex.h new file mode 100644 index 00000000..9906caf0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/convex/GuShapeConvex.h @@ -0,0 +1,100 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SHAPECONVEX_H +#define GU_SHAPECONVEX_H + +#include "GuConvexMeshData.h" +#include "CmScaling.h" + +namespace physx +{ +namespace Gu +{ + struct PolygonalData; + typedef void (*HullPrefetchCB) (PxU32 numVerts, const PxVec3* PX_RESTRICT verts); + typedef void (*HullProjectionCB) (const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world2hull, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum); + typedef PxU32 (*SelectClosestEdgeCB) (const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localDirection); + + struct PolygonalData + { + // Data + PxVec3 mCenter; + PxU32 mNbVerts; + PxU32 mNbPolygons; + PxU32 mNbEdges; + const Gu::HullPolygonData* mPolygons; + const PxVec3* mVerts; + const PxU8* mPolygonVertexRefs; + const PxU8* mFacesByEdges; + const PxU16* mVerticesByEdges; + + Gu::InternalObjectsData mInternal; + union + { + const Gu::BigConvexRawData* mBigData; // Only for big convexes + const PxVec3* mHalfSide; // Only for boxes + }; + + // Code + HullProjectionCB mProjectHull; + SelectClosestEdgeCB mSelectClosestEdgeCB; + + PX_FORCE_INLINE const PxU8* getPolygonVertexRefs(const Gu::HullPolygonData& poly) const + { + return mPolygonVertexRefs + poly.mVRef8; + } + }; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API PolygonalBox + { + public: + PolygonalBox(const PxVec3& halfSide); + + void getPolygonalData(PolygonalData* PX_RESTRICT dst) const; + + const PxVec3& mHalfSide; + PxVec3 mVertices[8]; + Gu::HullPolygonData mPolygons[6]; + private: + PolygonalBox& operator=(const PolygonalBox&); + }; +#if PX_VC + #pragma warning(pop) +#endif + + void getPolygonalData_Convex(PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointBox.cpp b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointBox.cpp new file mode 100644 index 00000000..3ce79cbd --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointBox.cpp @@ -0,0 +1,66 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDistancePointBox.h" + +using namespace physx; + +PxReal Gu::distancePointBoxSquared( const PxVec3& point, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxVec3* boxParam) +{ + // Compute coordinates of point in box coordinate system + const PxVec3 diff = point - boxOrigin; + + PxVec3 closest( boxBase.column0.dot(diff), + boxBase.column1.dot(diff), + boxBase.column2.dot(diff)); + + // Project test point onto box + PxReal sqrDistance = 0.0f; + for(PxU32 ax=0; ax<3; ax++) + { + if(closest[ax] < -boxExtent[ax]) + { + const PxReal delta = closest[ax] + boxExtent[ax]; + sqrDistance += delta*delta; + closest[ax] = -boxExtent[ax]; + } + else if(closest[ax] > boxExtent[ax]) + { + const PxReal delta = closest[ax] - boxExtent[ax]; + sqrDistance += delta*delta; + closest[ax] = boxExtent[ax]; + } + } + + if(boxParam) *boxParam = closest; + + return sqrDistance; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointBox.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointBox.h new file mode 100644 index 00000000..0f570a2a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointBox.h @@ -0,0 +1,70 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_BOX_H +#define GU_DISTANCE_POINT_BOX_H + +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + /** + Return the square of the minimum distance from the surface of the box to the given point. + \param point The point + \param boxOrigin The origin of the box + \param boxExtent The extent of the box + \param boxBase The orientation of the box + \param boxParam Set to coordinates of the closest point on the box in its local space + */ + PxReal distancePointBoxSquared( const PxVec3& point, + const PxVec3& boxOrigin, + const PxVec3& boxExtent, + const PxMat33& boxBase, + PxVec3* boxParam=NULL); + + /** + Return the square of the minimum distance from the surface of the box to the given point. + \param point The point + \param box The box + \param boxParam Set to coordinates of the closest point on the box in its local space + */ + PX_FORCE_INLINE PxReal distancePointBoxSquared( const PxVec3& point, + const Gu::Box& box, + PxVec3* boxParam=NULL) + { + return distancePointBoxSquared(point, box.center, box.extents, box.rot, boxParam); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointSegment.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointSegment.h new file mode 100644 index 00000000..f804e72d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointSegment.h @@ -0,0 +1,90 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_SEGMENT_H +#define GU_DISTANCE_POINT_SEGMENT_H + +#include "PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + // dir = p1 - p0 + PX_FORCE_INLINE PxReal distancePointSegmentSquaredInternal(const PxVec3& p0, const PxVec3& dir, const PxVec3& point, PxReal* param=NULL) + { + PxVec3 diff = point - p0; + PxReal fT = diff.dot(dir); + + if(fT<=0.0f) + { + fT = 0.0f; + } + else + { + const PxReal sqrLen = dir.magnitudeSquared(); + if(fT>=sqrLen) + { + fT = 1.0f; + diff -= dir; + } + else + { + fT /= sqrLen; + diff -= fT*dir; + } + } + + if(param) + *param = fT; + + return diff.magnitudeSquared(); + } + + /** + A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1 + Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1. + Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1. + */ + PX_FORCE_INLINE PxReal distancePointSegmentSquared(const PxVec3& p0, const PxVec3& p1, const PxVec3& point, PxReal* param=NULL) + { + return distancePointSegmentSquaredInternal(p0, p1 - p0, point, param); + } + + PX_INLINE PxReal distancePointSegmentSquared(const Gu::Segment& segment, const PxVec3& point, PxReal* param=NULL) + { + return distancePointSegmentSquared(segment.p0, segment.p1, point, param); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangle.cpp b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangle.cpp new file mode 100644 index 00000000..906f4400 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangle.cpp @@ -0,0 +1,353 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxVec3.h" +#include "GuDistancePointTriangle.h" +#include "GuDistancePointTriangleSIMD.h" + +using namespace physx; + +// Based on Christer Ericson's book +PxVec3 Gu::closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + return a + ab*v + ac*w; +} + +//Ps::aos::FloatV Gu::distancePointTriangleSquared( const Ps::aos::Vec3VArg p, +// const Ps::aos::Vec3VArg a, +// const Ps::aos::Vec3VArg b, +// const Ps::aos::Vec3VArg c, +// Ps::aos::FloatV& u, +// Ps::aos::FloatV& v, +// Ps::aos::Vec3V& closestP) +//{ +// using namespace Ps::aos; +// +// const FloatV zero = FZero(); +// const FloatV one = FOne(); +// //const Vec3V zero = V3Zero(); +// const Vec3V ab = V3Sub(b, a); +// const Vec3V ac = V3Sub(c, a); +// const Vec3V bc = V3Sub(c, b); +// const Vec3V ap = V3Sub(p, a); +// const Vec3V bp = V3Sub(p, b); +// const Vec3V cp = V3Sub(p, c); +// +// const FloatV d1 = V3Dot(ab, ap); // snom +// const FloatV d2 = V3Dot(ac, ap); // tnom +// const FloatV d3 = V3Dot(ab, bp); // -sdenom +// const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 +// const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 +// const FloatV d6 = V3Dot(ac, cp); // -tdenom +// const FloatV unom = FSub(d4, d3); +// const FloatV udenom = FSub(d5, d6); +// +// //check if p in vertex region outside a +// const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 +// const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 +// const BoolV con0 = BAnd(con00, con01); // vertex region a +// const FloatV u0 = zero; +// const FloatV v0 = zero; +// +// //check if p in vertex region outside b +// const BoolV con10 = FIsGrtrOrEq(d3, zero); +// const BoolV con11 = FIsGrtrOrEq(d3, d4); +// const BoolV con1 = BAnd(con10, con11); // vertex region b +// const FloatV u1 = one; +// const FloatV v1 = zero; +// +// //check if p in vertex region outside c +// const BoolV con20 = FIsGrtrOrEq(d6, zero); +// const BoolV con21 = FIsGrtrOrEq(d6, d5); +// const BoolV con2 = BAnd(con20, con21); // vertex region c +// const FloatV u2 = zero; +// const FloatV v2 = one; +// +// //check if p in edge region of AB +// const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); +// +// const BoolV con30 = FIsGrtr(zero, vc); +// const BoolV con31 = FIsGrtrOrEq(d1, zero); +// const BoolV con32 = FIsGrtr(zero, d3); +// const BoolV con3 = BAnd(con30, BAnd(con31, con32)); +// const FloatV sScale = FDiv(d1, FSub(d1, d3)); +// const Vec3V closest3 = V3Add(a, V3Scale(ab, sScale)); +// const FloatV u3 = sScale; +// const FloatV v3 = zero; +// +// //check if p in edge region of BC +// const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); +// const BoolV con40 = FIsGrtr(zero, va); +// const BoolV con41 = FIsGrtrOrEq(d4, d3); +// const BoolV con42 = FIsGrtrOrEq(d5, d6); +// const BoolV con4 = BAnd(con40, BAnd(con41, con42)); +// const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); +// const Vec3V closest4 = V3Add(b, V3Scale(bc, uScale)); +// const FloatV u4 = FSub(one, uScale); +// const FloatV v4 = uScale; +// +// //check if p in edge region of AC +// const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); +// const BoolV con50 = FIsGrtr(zero, vb); +// const BoolV con51 = FIsGrtrOrEq(d2, zero); +// const BoolV con52 = FIsGrtr(zero, d6); +// const BoolV con5 = BAnd(con50, BAnd(con51, con52)); +// const FloatV tScale = FDiv(d2, FSub(d2, d6)); +// const Vec3V closest5 = V3Add(a, V3Scale(ac, tScale)); +// const FloatV u5 = zero; +// const FloatV v5 = tScale; +// +// //P must project inside face region. Compute Q using Barycentric coordinates +// const FloatV denom = FRecip(FAdd(va, FAdd(vb, vc))); +// const FloatV t = FMul(vb, denom); +// const FloatV w = FMul(vc, denom); +// const Vec3V bCom = V3Scale(ab, t); +// const Vec3V cCom = V3Scale(ac, w); +// const Vec3V closest6 = V3Add(a, V3Add(bCom, cCom)); +// const FloatV u6 = t; +// const FloatV v6 = w; +// +// const Vec3V closest= V3Sel(con0, a, V3Sel(con1, b, V3Sel(con2, c, V3Sel(con3, closest3, V3Sel(con4, closest4, V3Sel(con5, closest5, closest6)))))); +// u = FSel(con0, u0, FSel(con1, u1, FSel(con2, u2, FSel(con3, u3, FSel(con4, u4, FSel(con5, u5, u6)))))); +// v = FSel(con0, v0, FSel(con1, v1, FSel(con2, v2, FSel(con3, v3, FSel(con4, v4, FSel(con5, v5, v6)))))); +// closestP = closest; +// +// const Vec3V vv = V3Sub(p, closest); +// +// return V3Dot(vv, vv); +//} + + +Ps::aos::FloatV Gu::distancePointTriangleSquared( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& u, + Ps::aos::FloatV& v, + Ps::aos::Vec3V& closestP) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + const FloatV one = FOne(); + //const Vec3V zero = V3Zero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + u = zero; + v = zero; + const Vec3V vv = V3Sub(p, a); + closestP = a; + return V3Dot(vv, vv); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + u = one; + v = zero; + const Vec3V vv = V3Sub(p, b); + closestP = b; + return V3Dot(vv, vv); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + u = zero; + v = one; + const Vec3V vv = V3Sub(p, c); + closestP = c; + return V3Dot(vv, vv); + } + + //check if p in edge region of AB + const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3Add(a, V3Scale(ab, sScale)); + u = sScale; + v = zero; + const Vec3V vv = V3Sub(p, closest3); + closestP = closest3; + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3Add(b, V3Scale(bc, uScale)); + u = FSub(one, uScale); + v = uScale; + const Vec3V vv = V3Sub(p, closest4); + closestP = closest4; + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3Add(a, V3Scale(ac, tScale)); + u = zero; + v = tScale; + const Vec3V vv = V3Sub(p, closest5); + closestP = closest5; + return V3Dot(vv, vv); + } + + //P must project inside face region. Compute Q using Barycentric coordinates + const FloatV denom = FRecip(FAdd(va, FAdd(vb, vc))); + const FloatV t = FMul(vb, denom); + const FloatV w = FMul(vc, denom); + const Vec3V bCom = V3Scale(ab, t); + const Vec3V cCom = V3Scale(ac, w); + const Vec3V closest6 = V3Add(a, V3Add(bCom, cCom)); + u = t; + v = w; + closestP = closest6; + + const Vec3V vv = V3Sub(p, closest6); + + return V3Dot(vv, vv); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangle.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangle.h new file mode 100644 index 00000000..d5d961eb --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangle.h @@ -0,0 +1,125 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_TRIANGLE_H +#define GU_DISTANCE_POINT_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + // PT: special version: + // - inlined + // - doesn't compute (s,t) output params + // - expects precomputed edges in input + PX_FORCE_INLINE PxVec3 closestPtPointTriangle2(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, const PxVec3& ab, const PxVec3& ac) + { + // Check if P in vertex region outside A + //const PxVec3 ab = b - a; + //const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + return a; // Barycentric coords 1,0,0 + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + return b; // Barycentric coords 0,1,0 + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + return c; // Barycentric coords 0,0,1 + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + return a + ab*v + ac*w; + } + + PX_PHYSX_COMMON_API PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t); + + PX_FORCE_INLINE PxReal distancePointTriangleSquared(const PxVec3& point, + const PxVec3& triangleOrigin, + const PxVec3& triangleEdge0, + const PxVec3& triangleEdge1, + PxReal* param0=NULL, + PxReal* param1=NULL) + { + const PxVec3 pt0 = triangleEdge0 + triangleOrigin; + const PxVec3 pt1 = triangleEdge1 + triangleOrigin; + float s,t; + const PxVec3 cp = closestPtPointTriangle(point, triangleOrigin, pt0, pt1, s, t); + if(param0) + *param0 = s; + if(param1) + *param1 = t; + return (cp - point).magnitudeSquared(); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangleSIMD.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangleSIMD.h new file mode 100644 index 00000000..0e66f121 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistancePointTriangleSIMD.h @@ -0,0 +1,54 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_TRIANGLE_SIMD_H +#define GU_DISTANCE_POINT_TRIANGLE_SIMD_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + + PX_PHYSX_COMMON_API Ps::aos::FloatV distancePointTriangleSquared( const Ps::aos::Vec3VArg point, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& u, + Ps::aos::FloatV& v, + Ps::aos::Vec3V& closestP); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentBox.cpp b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentBox.cpp new file mode 100644 index 00000000..71f0be67 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentBox.cpp @@ -0,0 +1,549 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDistanceSegmentBox.h" +#include "GuDistancePointBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionRayBox.h" + +using namespace physx; + +static void face(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, const PxVec3& rkPmE, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxVec3 kPpE; + PxReal fLSqr, fInv, fTmp, fParam, fT, fDelta; + + kPpE[i1] = rkPnt[i1] + extents[i1]; + kPpE[i2] = rkPnt[i2] + extents[i2]; + if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0]) + { + if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0]) + { + // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0) + if(pfLParam) + { + rkPnt[i0] = extents[i0]; + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv; + rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv; + *pfLParam = -rkPmE[i0]*fInv; + } + } + else + { + // v[i1] >= -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp <= 2.0f*fLSqr*extents[i1]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } + } + else + { + if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] ) + { + // v[i1] < -e[i1], v[i2] >= -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + } + else + { + // v[i1] < -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp >= 0.0f) + { + // v[i1]-edge is closest + if ( fTmp <= 2.0f*fLSqr*extents[i1] ) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + return; + } + + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp >= 0.0f) + { + // v[i2]-edge is closest + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + return; + } + + // (v[i1],v[i2])-corner is closest + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } +} + +static void caseNoZeros(PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxVec3 kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z); + + PxReal fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz; + + fProdDxPy = rkDir.x*kPmE.y; + fProdDyPx = rkDir.y*kPmE.x; + if(fProdDyPx >= fProdDxPy) + { + fProdDzPx = rkDir.z*kPmE.x; + fProdDxPz = rkDir.x*kPmE.z; + if(fProdDzPx >= fProdDxPz) + { + // line intersects x = e0 + face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } + else + { + fProdDzPy = rkDir.z*kPmE.y; + fProdDyPz = rkDir.y*kPmE.z; + if(fProdDzPy >= fProdDyPz) + { + // line intersects y = e1 + face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } +} + +static void case0(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxReal fPmE0 = rkPnt[i0] - extents[i0]; + PxReal fPmE1 = rkPnt[i1] - extents[i1]; + PxReal fProd0 = rkDir[i1]*fPmE0; + PxReal fProd1 = rkDir[i0]*fPmE1; + PxReal fDelta, fInvLSqr, fInv; + + if(fProd0 >= fProd1) + { + // line intersects P[i0] = e[i0] + rkPnt[i0] = extents[i0]; + + PxReal fPpE1 = rkPnt[i1] + extents[i1]; + fDelta = fProd0 - rkDir[i0]*fPpE1; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i1] = -extents[i1]; + *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= fProd0*fInv; + *pfLParam = -fPmE0*fInv; + } + } + } + else + { + // line intersects P[i1] = e[i1] + rkPnt[i1] = extents[i1]; + + PxReal fPpE0 = rkPnt[i0] + extents[i0]; + fDelta = fProd1 - rkDir[i1]*fPpE0; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i0] = -extents[i0]; + *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i1]; + rkPnt[i0] -= fProd1*fInv; + *pfLParam = -fPmE1*fInv; + } + } + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if ( rkPnt[i2] > extents[i2] ) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void case00(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxReal fDelta; + + if(pfLParam) + *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0]; + + rkPnt[i0] = extents[i0]; + + if(rkPnt[i1] < -extents[i1]) + { + fDelta = rkPnt[i1] + extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i1]; + } + else if(rkPnt[i1] > extents[i1]) + { + fDelta = rkPnt[i1] - extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = extents[i1]; + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if(rkPnt[i2] > extents[i2]) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void case000(PxVec3& rkPnt, const PxVec3& extents, PxReal& rfSqrDistance) +{ + PxReal fDelta; + + if(rkPnt.x < -extents.x) + { + fDelta = rkPnt.x + extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = -extents.x; + } + else if(rkPnt.x > extents.x) + { + fDelta = rkPnt.x - extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = extents.x; + } + + if(rkPnt.y < -extents.y) + { + fDelta = rkPnt.y + extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = -extents.y; + } + else if(rkPnt.y > extents.y) + { + fDelta = rkPnt.y - extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = extents.y; + } + + if(rkPnt.z < -extents.z) + { + fDelta = rkPnt.z + extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = -extents.z; + } + else if(rkPnt.z > extents.z) + { + fDelta = rkPnt.z - extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = extents.z; + } +} + +//! Compute the smallest distance from the (infinite) line to the box. +static PxReal distanceLineBoxSquared(const PxVec3& lineOrigin, const PxVec3& lineDirection, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* lineParam, + PxVec3* boxParam) +{ + const PxVec3& axis0 = boxBase.column0; + const PxVec3& axis1 = boxBase.column1; + const PxVec3& axis2 = boxBase.column2; + + // compute coordinates of line in box coordinate system + const PxVec3 diff = lineOrigin - boxOrigin; + PxVec3 pnt(diff.dot(axis0), diff.dot(axis1), diff.dot(axis2)); + PxVec3 dir(lineDirection.dot(axis0), lineDirection.dot(axis1), lineDirection.dot(axis2)); + + // Apply reflections so that direction vector has nonnegative components. + bool reflect[3]; + for(unsigned int i=0;i<3;i++) + { + if(dir[i]<0.0f) + { + pnt[i] = -pnt[i]; + dir[i] = -dir[i]; + reflect[i] = true; + } + else + { + reflect[i] = false; + } + } + + PxReal sqrDistance = 0.0f; + + if(dir.x>0.0f) + { + if(dir.y>0.0f) + { + if(dir.z>0.0f) caseNoZeros(pnt, dir, boxExtent, lineParam, sqrDistance); // (+,+,+) + else case0(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,+,0) + } + else + { + if(dir.z>0.0f) case0(0, 2, 1, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,0,+) + else case00(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,0,0) + } + } + else + { + if(dir.y>0.0f) + { + if(dir.z>0.0f) case0(1, 2, 0, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,+,+) + else case00(1, 0, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,+,0) + } + else + { + if(dir.z>0.0f) case00(2, 0, 1, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,0,+) + else + { + case000(pnt, boxExtent, sqrDistance); // (0,0,0) + if(lineParam) + *lineParam = 0.0f; + } + } + } + + if(boxParam) + { + // undo reflections + for(unsigned int i=0;i<3;i++) + { + if(reflect[i]) + pnt[i] = -pnt[i]; + } + + *boxParam = pnt; + } + + return sqrDistance; +} + +//! Compute the smallest distance from the (finite) line segment to the box. +PxReal Gu::distanceSegmentBoxSquared( const PxVec3& segmentPoint0, const PxVec3& segmentPoint1, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* segmentParam, + PxVec3* boxParam) +{ + // compute coordinates of line in box coordinate system + + PxReal lp; + PxVec3 bp; + PxReal sqrDistance = distanceLineBoxSquared(segmentPoint0, segmentPoint1 - segmentPoint0, boxOrigin, boxExtent, boxBase, &lp, &bp); + if(lp>=0.0f) + { + if(lp<=1.0f) + { + if(segmentParam) + *segmentParam = lp; + if(boxParam) + *boxParam = bp; + return sqrDistance; + } + else + { + if(segmentParam) + *segmentParam = 1.0f; + return Gu::distancePointBoxSquared(segmentPoint1, boxOrigin, boxExtent, boxBase, boxParam); + } + } + else + { + if(segmentParam) + *segmentParam = 0.0f; + return Gu::distancePointBoxSquared(segmentPoint0, boxOrigin, boxExtent, boxBase, boxParam); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentSegment.cpp b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentSegment.cpp new file mode 100644 index 00000000..bb9fd314 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentSegment.cpp @@ -0,0 +1,576 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +using namespace physx; +using namespace Ps; +using namespace aos; + +static const float ZERO_TOLERANCE = 1e-06f; + +// S0 = origin + extent * dir; +// S1 = origin - extent * dir; +PxReal Gu::distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& dir0, PxReal extent0, + const PxVec3& origin1, const PxVec3& dir1, PxReal extent1, + PxReal* param0, PxReal* param1) +{ + const PxVec3 kDiff = origin0 - origin1; + const PxReal fA01 = -dir0.dot(dir1); + const PxReal fB0 = kDiff.dot(dir0); + const PxReal fB1 = -kDiff.dot(dir1); + const PxReal fC = kDiff.magnitudeSquared(); + const PxReal fDet = PxAbs(1.0f - fA01*fA01); + PxReal fS0, fS1, fSqrDist, fExtDet0, fExtDet1, fTmpS0, fTmpS1; + + if (fDet >= ZERO_TOLERANCE) + { + // segments are not parallel + fS0 = fA01*fB1-fB0; + fS1 = fA01*fB0-fB1; + fExtDet0 = extent0*fDet; + fExtDet1 = extent1*fDet; + + if (fS0 >= -fExtDet0) + { + if (fS0 <= fExtDet0) + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 0 (interior) + { + // minimum at two interior points of 3D lines + PxReal fInvDet = 1.0f/fDet; + fS0 *= fInvDet; + fS1 *= fInvDet; + fSqrDist = fS0*(fS0+fA01*fS1+2.0f*fB0) + fS1*(fA01*fS0+fS1+2.0f*fB1)+fC; + } + else // region 3 (side) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + } + } + else // region 7 (side) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + } + } + else + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 1 (side) + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0)+fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + else // region 2 (corner) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + else // region 8 (corner) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 > extent1) + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 >= -extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + } + else + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 5 (side) + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0)+fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + else // region 4 (corner) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 > extent0) + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 >= -extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + else // region 6 (corner) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 > extent0) + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 >= -extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + } + else + { + // The segments are parallel. + PxReal fE0pE1 = extent0 + extent1; + PxReal fSign = (fA01 > 0.0f ? -1.0f : 1.0f); + PxReal b0Avr = 0.5f*(fB0 - fSign*fB1); + PxReal fLambda = -b0Avr; + if(fLambda < -fE0pE1) + { + fLambda = -fE0pE1; + } + else if(fLambda > fE0pE1) + { + fLambda = fE0pE1; + } + + fS1 = -fSign*fLambda*extent1/fE0pE1; + fS0 = fLambda + fSign*fS1; + fSqrDist = fLambda*(fLambda + 2.0f*b0Avr) + fC; + } + + if(param0) + *param0 = fS0; + if(param1) + *param1 = fS1; + + // account for numerical round-off error + return physx::intrinsics::selectMax(0.0f, fSqrDist); +} + +PxReal Gu::distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& extent0, + const PxVec3& origin1, const PxVec3& extent1, + PxReal* param0, + PxReal* param1) +{ + // Some conversion is needed between the old & new code + // Old: + // segment (s0, s1) + // origin = s0 + // extent = s1 - s0 + // + // New: + // s0 = origin + extent * dir; + // s1 = origin - extent * dir; + + // dsequeira: is this really sensible? We use a highly optimized Wild Magic routine, + // then use a segment representation that requires an expensive conversion to/from... + + PxVec3 dir0 = extent0; + const PxVec3 center0 = origin0 + extent0*0.5f; + PxReal length0 = extent0.magnitude(); //AM: change to make it work for degenerate (zero length) segments. + const bool b0 = length0 != 0.0f; + PxReal oneOverLength0 = 0.0f; + if(b0) + { + oneOverLength0 = 1.0f / length0; + dir0 *= oneOverLength0; + length0 *= 0.5f; + } + + PxVec3 dir1 = extent1; + const PxVec3 center1 = origin1 + extent1*0.5f; + PxReal length1 = extent1.magnitude(); + const bool b1 = length1 != 0.0f; + PxReal oneOverLength1 = 0.0f; + if(b1) + { + oneOverLength1 = 1.0f / length1; + dir1 *= oneOverLength1; + length1 *= 0.5f; + } + + // the return param vals have -extent = s0, extent = s1 + + const PxReal d2 = distanceSegmentSegmentSquared(center0, dir0, length0, + center1, dir1, length1, + param0, param1); + + //ML : This is wrong for some reason, I guess it has precision issue + //// renormalize into the 0 = s0, 1 = s1 range + //if (param0) + // *param0 = b0 ? ((*param0) * oneOverLength0 * 0.5f + 0.5f) : 0.0f; + //if (param1) + // *param1 = b1 ? ((*param1) * oneOverLength1 * 0.5f + 0.5f) : 0.0f; + + if(param0) + *param0 = b0 ? ((length0 + (*param0))*oneOverLength0) : 0.0f; + if(param1) + *param1 = b1 ? ((length1 + (*param1))*oneOverLength1) : 0.0f; + + return d2; +} + +/* + S0 = origin + extent * dir; + S1 = origin + extent * dir; + dir is the vector from start to end point + p1 is the start point of segment1 + d1 is the direction vector(q1 - p1) + p2 is the start point of segment2 + d2 is the direction vector(q2 - p2) +*/ + +FloatV Gu::distanceSegmentSegmentSquared( const Vec3VArg p1, + const Vec3VArg d1, + const Vec3VArg p2, + const Vec3VArg d2, + FloatV& s, + FloatV& t) +{ + const FloatV zero = FZero(); + const FloatV one = FOne(); + const FloatV eps = FEps(); + + const Vec3V r = V3Sub(p1, p2); + const Vec4V combinedDot = V3Dot4(d1, d1, d2, d2, d1, d2, d1, r); + const Vec4V combinedRecip = V4Sel(V4IsGrtr(combinedDot, V4Splat(eps)), V4Recip(combinedDot), V4Splat(zero)); + const FloatV a = V4GetX(combinedDot); + const FloatV e = V4GetY(combinedDot); + const FloatV b = V4GetZ(combinedDot); + const FloatV c = V4GetW(combinedDot); + const FloatV aRecip = V4GetX(combinedRecip);//FSel(FIsGrtr(a, eps), FRecip(a), zero); + const FloatV eRecip = V4GetY(combinedRecip);//FSel(FIsGrtr(e, eps), FRecip(e), zero); + + const FloatV f = V3Dot(d2, r); + + /* + s = (b*f - c*e)/(a*e - b*b); + t = (a*f - b*c)/(a*e - b*b); + + s = (b*t - c)/a; + t = (b*s + f)/e; + */ + + //if segments not parallel, the general non-degenerated case, compute closest point on two segments and clamp to segment1 + const FloatV denom = FSub(FMul(a, e), FMul(b, b)); + const FloatV temp = FSub(FMul(b, f), FMul(c, e)); + const FloatV s0 = FClamp(FDiv(temp, denom), zero, one); + + //if segment is parallel, demon < eps + const BoolV con2 = FIsGrtr(eps, denom);//FIsEq(denom, zero); + const FloatV sTmp = FSel(con2, FHalf(), s0); + + //compute point on segment2 closest to segment1 + //const FloatV tTmp = FMul(FAdd(FMul(b, sTmp), f), eRecip); + const FloatV tTmp = FMul(FScaleAdd(b, sTmp, f), eRecip); + + //if t is in [zero, one], done. otherwise clamp t + const FloatV t2 = FClamp(tTmp, zero, one); + + //recompute s for the new value + const FloatV comp = FMul(FSub(FMul(b,t2), c), aRecip); + const FloatV s2 = FClamp(comp, zero, one); + + s = s2; + t = t2; + + const Vec3V closest1 = V3ScaleAdd(d1, s2, p1);//V3Add(p1, V3Scale(d1, tempS)); + const Vec3V closest2 = V3ScaleAdd(d2, t2, p2);//V3Add(p2, V3Scale(d2, tempT)); + const Vec3V vv = V3Sub(closest1, closest2); + return V3Dot(vv, vv); +} + + + + +/* + segment (p, d) and segment (p02, d02) + segment (p, d) and segment (p12, d12) + segment (p, d) and segment (p22, d22) + segment (p, d) and segment (p32, d32) +*/ +Vec4V Gu::distanceSegmentSegmentSquared4( const Vec3VArg p, const Vec3VArg d0, + const Vec3VArg p02, const Vec3VArg d02, + const Vec3VArg p12, const Vec3VArg d12, + const Vec3VArg p22, const Vec3VArg d22, + const Vec3VArg p32, const Vec3VArg d32, + Vec4V& s, Vec4V& t) +{ + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + const Vec4V eps = V4Eps(); + const Vec4V half = V4Splat(FHalf()); + + const Vec4V d0X = V4Splat(V3GetX(d0)); + const Vec4V d0Y = V4Splat(V3GetY(d0)); + const Vec4V d0Z = V4Splat(V3GetZ(d0)); + const Vec4V pX = V4Splat(V3GetX(p)); + const Vec4V pY = V4Splat(V3GetY(p)); + const Vec4V pZ = V4Splat(V3GetZ(p)); + + Vec4V d024 = Vec4V_From_Vec3V(d02); + Vec4V d124 = Vec4V_From_Vec3V(d12); + Vec4V d224 = Vec4V_From_Vec3V(d22); + Vec4V d324 = Vec4V_From_Vec3V(d32); + + Vec4V p024 = Vec4V_From_Vec3V(p02); + Vec4V p124 = Vec4V_From_Vec3V(p12); + Vec4V p224 = Vec4V_From_Vec3V(p22); + Vec4V p324 = Vec4V_From_Vec3V(p32); + + Vec4V d0123X, d0123Y, d0123Z; + Vec4V p0123X, p0123Y, p0123Z; + + PX_TRANSPOSE_44_34(d024, d124, d224, d324, d0123X, d0123Y, d0123Z); + PX_TRANSPOSE_44_34(p024, p124, p224, p324, p0123X, p0123Y, p0123Z); + + const Vec4V rX = V4Sub(pX, p0123X); + const Vec4V rY = V4Sub(pY, p0123Y); + const Vec4V rZ = V4Sub(pZ, p0123Z); + + //TODO - store this in a transposed state and avoid so many dot products? + + const FloatV dd = V3Dot(d0, d0); + + const Vec4V e = V4MulAdd(d0123Z, d0123Z, V4MulAdd(d0123X, d0123X, V4Mul(d0123Y, d0123Y))); + const Vec4V b = V4MulAdd(d0Z, d0123Z, V4MulAdd(d0X, d0123X, V4Mul(d0Y, d0123Y))); + const Vec4V c = V4MulAdd(d0Z, rZ, V4MulAdd(d0X, rX, V4Mul(d0Y, rY))); + const Vec4V f = V4MulAdd(d0123Z, rZ, V4MulAdd(d0123X, rX, V4Mul(d0123Y, rY))); + + const Vec4V a(V4Splat(dd)); + + const Vec4V aRecip(V4Recip(a)); + const Vec4V eRecip(V4Recip(e)); + + //if segments not parallell, compute closest point on two segments and clamp to segment1 + const Vec4V denom = V4Sub(V4Mul(a, e), V4Mul(b, b)); + const Vec4V temp = V4Sub(V4Mul(b, f), V4Mul(c, e)); + const Vec4V s0 = V4Clamp(V4Div(temp, denom), zero, one); + + //test whether segments are parallel + const BoolV con2 = V4IsGrtrOrEq(eps, denom); + const Vec4V sTmp = V4Sel(con2, half, s0); + + //compute point on segment2 closest to segment1 + const Vec4V tTmp = V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip); + + //if t is in [zero, one], done. otherwise clamp t + const Vec4V t2 = V4Clamp(tTmp, zero, one); + + //recompute s for the new value + const Vec4V comp = V4Mul(V4Sub(V4Mul(b,t2), c), aRecip); + const BoolV aaNearZero = V4IsGrtrOrEq(eps, a); // check if aRecip is valid (aa>eps) + const Vec4V s2 = V4Sel(aaNearZero, V4Zero(), V4Clamp(comp, zero, one)); + + /* s = V4Sel(con0, zero, V4Sel(con1, cd, s2)); + t = V4Sel(con1, zero, V4Sel(con0, cg, t2)); */ + s = s2; + t = t2; + + const Vec4V closest1X = V4MulAdd(d0X, s2, pX); + const Vec4V closest1Y = V4MulAdd(d0Y, s2, pY); + const Vec4V closest1Z = V4MulAdd(d0Z, s2, pZ); + + const Vec4V closest2X = V4MulAdd(d0123X, t2, p0123X); + const Vec4V closest2Y = V4MulAdd(d0123Y, t2, p0123Y); + const Vec4V closest2Z = V4MulAdd(d0123Z, t2, p0123Z); + + const Vec4V vvX = V4Sub(closest1X, closest2X); + const Vec4V vvY = V4Sub(closest1Y, closest2Y); + const Vec4V vvZ = V4Sub(closest1Z, closest2Z); + + const Vec4V vd = V4MulAdd(vvX, vvX, V4MulAdd(vvY, vvY, V4Mul(vvZ, vvZ))); + + return vd; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentSegmentSIMD.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentSegmentSIMD.h new file mode 100644 index 00000000..289b9830 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentSegmentSIMD.h @@ -0,0 +1,57 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_SEGMENT_SIMD_H +#define GU_DISTANCE_SEGMENT_SEGMENT_SIMD_H + +#include "common/PxPhysXCommonConfig.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + PX_PHYSX_COMMON_API Ps::aos::FloatV distanceSegmentSegmentSquared( const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg d1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg d2, + Ps::aos::FloatV& param0, + Ps::aos::FloatV& param1); + + /* + This function do four segment segment closest point test in one go + */ + Ps::aos::Vec4V distanceSegmentSegmentSquared4( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg d, + const Ps::aos::Vec3VArg p02, const Ps::aos::Vec3VArg d02, + const Ps::aos::Vec3VArg p12, const Ps::aos::Vec3VArg d12, + const Ps::aos::Vec3VArg p22, const Ps::aos::Vec3VArg d22, + const Ps::aos::Vec3VArg p32, const Ps::aos::Vec3VArg d32, + Ps::aos::Vec4V& s, Ps::aos::Vec4V& t); +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangle.cpp b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangle.cpp new file mode 100644 index 00000000..6811d26b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangle.cpp @@ -0,0 +1,541 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuDistanceSegmentTriangleSIMD.h" +#include "GuDistancePointTriangle.h" +#include "GuDistancePointTriangleSIMD.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" +#include "GuBarycentricCoordinates.h" + +using namespace physx; +using namespace Gu; + +// ptchernev: +// The Magic Software code uses a relative error test for parallel case. +// The Novodex code does not presumably as an optimization. +// Since the Novodex code is working in the trunk I see no reason +// to reintroduce the relative error test here. + +// PT: this might just be because the relative error test has been added +// after we grabbed the code. I don't remember making this change. A good +// idea would be NOT to refactor Magic's code, to easily grab updated +// versions from the website............................................. + +// ptchernev: +// The code has been modified to use a relative error test since the absolute +// test would break down for small geometries. (TTP 4021) + +static PX_FORCE_INLINE void updateClosestHit( PxReal fSqrDist0, PxReal fR0, PxReal fS0, PxReal fT0, + PxReal& fSqrDist, PxReal& fR, PxReal& fS, PxReal& fT) +{ + if(fSqrDist0 < fSqrDist) + { + fSqrDist = fSqrDist0; + fR = fR0; + fS = fS0; + fT = fT0; + } +} + +PxReal Gu::distanceSegmentTriangleSquared( const PxVec3& origin, const PxVec3& dir, + const PxVec3& p0, const PxVec3& triEdge0, const PxVec3& triEdge1, + PxReal* t, PxReal* u, PxReal* v) +{ + const PxReal fA00 = dir.magnitudeSquared(); + if(fA00 < 1e-6f*1e-6f) + { + if(t) + *t = 0.0f; + return distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, u, v); + } + const PxVec3 kDiff = p0 - origin; + const PxReal fA01 = -(dir.dot(triEdge0)); + const PxReal fA02 = -(dir.dot(triEdge1)); + const PxReal fA11 = triEdge0.magnitudeSquared(); + const PxReal fA12 = triEdge0.dot(triEdge1); + const PxReal fA22 = triEdge1.dot(triEdge1); + const PxReal fB0 = -(kDiff.dot(dir)); + const PxReal fB1 = kDiff.dot(triEdge0); + const PxReal fB2 = kDiff.dot(triEdge1); + const PxReal fCof00 = fA11*fA22-fA12*fA12; + const PxReal fCof01 = fA02*fA12-fA01*fA22; + const PxReal fCof02 = fA01*fA12-fA02*fA11; + const PxReal fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; + + PxReal fSqrDist, fSqrDist0, fR, fS, fT, fR0, fS0, fT0; + + // Set up for a relative error test on the angle between ray direction + // and triangle normal to determine parallel/nonparallel status. + const PxVec3 kNormal = triEdge0.cross(triEdge1); + const PxReal fDot = kNormal.dot(dir); + if(fDot*fDot >= 1e-6f*dir.magnitudeSquared()*kNormal.magnitudeSquared()) + { + const PxReal fCof11 = fA00*fA22-fA02*fA02; + const PxReal fCof12 = fA02*fA01-fA00*fA12; + const PxReal fCof22 = fA00*fA11-fA01*fA01; + const PxReal fInvDet = fDet == 0.0f ? 0.0f : 1.0f/fDet; + const PxReal fRhs0 = -fB0*fInvDet; + const PxReal fRhs1 = -fB1*fInvDet; + const PxReal fRhs2 = -fB2*fInvDet; + + fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; + fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; + fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; + + if(fR < 0.0f) + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4m + { + // minimum on face s=0 or t=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3m + { + // minimum on face s=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 5m + { + // minimum on face t=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 0m + { + // minimum on face r=0 + fSqrDist = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS, &fT); + fR = 0.0f; + } + } + else + { + if(fS < 0.0f) // region 2m + { + // minimum on face s=0 or s+t=1 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6m + { + // minimum on face t=0 or s+t=1 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1m + { + // minimum on face s+t=1 or r=0 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + } + else if(fR <= 1.0f) + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4 + { + // minimum on face s=0 or t=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3 + { + // minimum on face s=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + } + else if(fT < 0.0f) // region 5 + { + // minimum on face t=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + } + else // region 0 + { + // global minimum is interior, done + fSqrDist = fR*(fA00*fR+fA01*fS+fA02*fT+2.0f*fB0) + +fS*(fA01*fR+fA11*fS+fA12*fT+2.0f*fB1) + +fT*(fA02*fR+fA12*fS+fA22*fT+2.0f*fB2) + +kDiff.magnitudeSquared(); + } + } + else + { + if(fS < 0.0f) // region 2 + { + // minimum on face s=0 or s+t=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6 + { + // minimum on face t=0 or s+t=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1 + { + // minimum on face s+t=1 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + } + } + else // fR > 1 + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4p + { + // minimum on face s=0 or t=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3p + { + // minimum on face s=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 5p + { + // minimum on face t=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 0p + { + // minimum face on r=1 + const PxVec3 kPt = origin+dir; + fSqrDist = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS, &fT); + fR = 1.0f; + } + } + else + { + if(fS < 0.0f) // region 2p + { + // minimum on face s=0 or s+t=1 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6p + { + // minimum on face t=0 or s+t=1 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1p + { + // minimum on face s+t=1 or r=1 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + } + } + else + { + // segment and triangle are parallel + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR0, &fT0); + fS0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1 - triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + + if(t) *t = fR; + if(u) *u = fS; + if(v) *v = fT; + + // account for numerical round-off error + return physx::intrinsics::selectMax(0.0f, fSqrDist); +} + + +/* + closest0 is the closest point on segment pq + closest1 is the closest point on triangle abc +*/ +Ps::aos::FloatV Gu::distanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + //const FloatV one = FOne(); + //const FloatV parallelTolerance = FloatV_From_F32(PX_PARALLEL_TOLERANCE); + + const Vec3V pq = V3Sub(q, p); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + //This is used to calculate the barycentric coordinate + const FloatV d00 = V3Dot(ab,ab); + const FloatV d01 = V3Dot(ab, ac); + const FloatV d11 = V3Dot(ac, ac); + const FloatV tDenom = FSub(FMul(d00, d11), FMul(d01, d01)); + + const FloatV bdenom = FSel(FIsGrtr(tDenom, zero), FRecip(tDenom), zero); + + const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector + + //compute the closest point of p and triangle plane abc + const FloatV dist3 = V3Dot(ap, n); + const FloatV sqDist3 = FMul(dist3, dist3); + + + //compute the closest point of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, n); + const FloatV sqDist4 = FMul(dist4, dist4); + const FloatV dMul = FMul(dist3, dist4); + const BoolV con = FIsGrtr(zero, dMul); + + + // intersect with the plane + if(BAllEqTTTT(con)) + { + //compute the intersect point + const FloatV nom = FNeg(V3Dot(n, ap)); + const FloatV denom = FRecip(V3Dot(n, pq)); + const FloatV t = FMul(nom, denom); + const Vec3V ip = V3ScaleAdd(pq, t, p);//V3Add(p, V3Scale(pq, t)); + const Vec3V v2 = V3Sub(ip, a); + const FloatV d20 = V3Dot(v2, ab); + const FloatV d21 = V3Dot(v2, ac); + const FloatV v0 = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), bdenom); + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + if(BAllEqTTTT(con0)) + { + closest0 = closest1 = ip; + return zero; + } + } + + + Vec4V t40, t41; + const Vec4V sqDist44 = distanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); + + const FloatV t00 = V4GetX(t40); + const FloatV t10 = V4GetY(t40); + const FloatV t20 = V4GetZ(t40); + + const FloatV t01 = V4GetX(t41); + const FloatV t11 = V4GetY(t41); + const FloatV t21 = V4GetZ(t41); + + const FloatV sqDist0(V4GetX(sqDist44)); + const FloatV sqDist1(V4GetY(sqDist44)); + const FloatV sqDist2(V4GetZ(sqDist44)); + + const Vec3V closestP00 = V3ScaleAdd(pq, t00, p); + const Vec3V closestP01 = V3ScaleAdd(ab, t01, a); + + const Vec3V closestP10 = V3ScaleAdd(pq, t10, p); + const Vec3V closestP11 = V3ScaleAdd(bc, t11, b); + + const Vec3V closestP20 = V3ScaleAdd(pq, t20, p); + const Vec3V closestP21 = V3ScaleAdd(ac, t21, a); + + + //Get the closest point of all edges + const BoolV con20 = FIsGrtr(sqDist1, sqDist0); + const BoolV con21 = FIsGrtr(sqDist2, sqDist0); + const BoolV con2 = BAnd(con20,con21); + const BoolV con30 = FIsGrtrOrEq(sqDist0, sqDist1); + const BoolV con31 = FIsGrtr(sqDist2, sqDist1); + const BoolV con3 = BAnd(con30, con31); + const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); + //const FloatV tValue = FSel(con2, t00, FSel(con3, t10, t20)); + const Vec3V closestPE0 = V3Sel(con2, closestP00, V3Sel(con3, closestP10, closestP20)); // closestP on segment + const Vec3V closestPE1 = V3Sel(con2, closestP01, V3Sel(con3, closestP11, closestP21)); // closestP on triangle + + + const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); + const Vec3V closestP30 = p; + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const FloatV pD20 = V3Dot(pV20, ab); + const FloatV pD21 = V3Dot(pV20, ac); + const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); + + //check closestP3 is inside the triangle + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + + + + const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); + const Vec3V closestP40 = q; + + //Compute the barycentric coordinate for project point of q + const Vec3V qV20 = V3Sub(closestP41, a); + const FloatV qD20 = V3Dot(qV20, ab); + const FloatV qD21 = V3Dot(qV20, ac); + const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); + const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); + + const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); + + /* + p is interior point but not q + */ + const BoolV d0 = FIsGrtr(sqDistPE, sqDist3); + const Vec3V c00 = V3Sel(d0, closestP30, closestPE0); + const Vec3V c01 = V3Sel(d0, closestP31, closestPE1); + + /* + q is interior point but not p + */ + const BoolV d1 = FIsGrtr(sqDistPE, sqDist4); + const Vec3V c10 = V3Sel(d1, closestP40, closestPE0); + const Vec3V c11 = V3Sel(d1, closestP41, closestPE1); + + /* + p and q are interior point + */ + const BoolV d2 = FIsGrtr(sqDist4, sqDist3); + const Vec3V c20 = V3Sel(d2, closestP30, closestP40); + const Vec3V c21 = V3Sel(d2, closestP31, closestP41); + + const BoolV cond2 = BAnd(con0, con1); + + const Vec3V closestP0 = V3Sel(cond2, c20, V3Sel(con0, c00, V3Sel(con1, c10, closestPE0))); + const Vec3V closestP1 = V3Sel(cond2, c21, V3Sel(con0, c01, V3Sel(con1, c11, closestPE1))); + + const Vec3V vv = V3Sub(closestP1, closestP0); + closest0 = closestP0; + closest1 = closestP1; + return V3Dot(vv, vv); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangle.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangle.h new file mode 100644 index 00000000..f626bc35 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangle.h @@ -0,0 +1,63 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_TRIANGLE_H +#define GU_DISTANCE_SEGMENT_TRIANGLE_H + +#include "PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + + PX_PHYSX_COMMON_API PxReal distanceSegmentTriangleSquared( + const PxVec3& segmentOrigin, const PxVec3& segmentExtent, + const PxVec3& triangleOrigin, const PxVec3& triangleEdge0, const PxVec3& triangleEdge1, + PxReal* t=NULL, PxReal* u=NULL, PxReal* v=NULL); + + PX_INLINE PxReal distanceSegmentTriangleSquared( + const Gu::Segment& segment, + const PxVec3& triangleOrigin, + const PxVec3& triangleEdge0, + const PxVec3& triangleEdge1, + PxReal* t=NULL, + PxReal* u=NULL, + PxReal* v=NULL) + { + return distanceSegmentTriangleSquared( + segment.p0, segment.computeDirection(), triangleOrigin, triangleEdge0, triangleEdge1, t, u, v); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangleSIMD.h b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangleSIMD.h new file mode 100644 index 00000000..28d758d3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/distance/GuDistanceSegmentTriangleSIMD.h @@ -0,0 +1,54 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_TRIANGLE_SIMD_H +#define GU_DISTANCE_SEGMENT_TRIANGLE_SIMD_H + +#include "PxPhysXCommonConfig.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + + /* + closest0 is the closest point on segment pq + closest1 is the closest point on triangle abc + */ + PX_PHYSX_COMMON_API Ps::aos::FloatV distanceSegmentTriangleSquared( + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPA.cpp b/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPA.cpp new file mode 100644 index 00000000..44d0507b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPA.cpp @@ -0,0 +1,698 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuEPA.h" +#include "GuEPAFacet.h" +#include "GuGJKSimplex.h" +#include "CmPriorityQueue.h" +#include "PsAllocator.h" + +#define EPA_DEBUG 0 + +namespace physx +{ +namespace Gu +{ + using namespace Ps::aos; + + class ConvexV; + + struct FacetDistanceComparator + { + bool operator()(const Facet* left, const Facet* right) const + { + return *left < *right; + } + }; + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + + class EPA + { + public: + EPA(){} + GjkStatus PenetrationDepth(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3V* PX_RESTRICT Q, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const PxI32 size, Ps::aos::Vec3V& pa, Ps::aos::Vec3V& pb, Ps::aos::Vec3V& normal, Ps::aos::FloatV& penDepth, const bool takeCoreShape = false); + bool expandPoint(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg lowerBound, const FloatVArg upperBound); + bool expandSegment(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg lowerBound, const FloatVArg upperBound); + bool expandTriangle(PxI32& numVerts, const FloatVArg lowerBound, const FloatVArg upperBound); + + Facet* addFacet(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::FloatVArg lower2, const Ps::aos::FloatVArg upper2); + + bool originInTetrahedron(const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg p3, const Ps::aos::Vec3VArg p4); + + Cm::InlinePriorityQueue<Facet*, MaxFacets, FacetDistanceComparator> heap; + Ps::aos::Vec3V aBuf[MaxSupportPoints]; + Ps::aos::Vec3V bBuf[MaxSupportPoints]; + Facet facetBuf[MaxFacets]; + EdgeBuffer edgeBuffer; + EPAFacetManager facetManager; + + private: + PX_NOCOPY(EPA) + }; +#if PX_VC + #pragma warning(pop) +#endif + + PX_FORCE_INLINE bool EPA::originInTetrahedron(const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg p3, const Ps::aos::Vec3VArg p4) + { + using namespace Ps::aos; + return BAllEqFFFF(PointOutsideOfPlane4(p1, p2, p3, p4)) == 1; + } + + + static PX_FORCE_INLINE void doSupport(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3VArg dir, Ps::aos::Vec3V& supportA, Ps::aos::Vec3V& supportB, Ps::aos::Vec3V& support) + { + const Vec3V tSupportA = a.support(V3Neg(dir)); + const Vec3V tSupportB = b.support(dir); + //avoid LHS + supportA = tSupportA; + supportB = tSupportB; + support = V3Sub(tSupportA, tSupportB); + } + + static FloatV calculatePlaneDist(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf) + { + const Vec3V pa0(aBuf[i0]); + const Vec3V pa1(aBuf[i1]); + const Vec3V pa2(aBuf[i2]); + + const Vec3V pb0(bBuf[i0]); + const Vec3V pb1(bBuf[i1]); + const Vec3V pb2(bBuf[i2]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + const Vec3V v1 = V3Sub(p1, p0); + const Vec3V v2 = V3Sub(p2, p0); + const Vec3V v3 = V3Sub(p2, p1); + + const FloatV v1v1 = V3Dot(v1, v1); + const FloatV v2v2 = V3Dot(v2, v2); + const Vec3V v = V3Sel(FIsGrtr(v1v1, v2v2), v2, v1); + + const Vec3V planeNormal = V3Normalize(V3Cross(v, v3)); + return V3Dot(planeNormal, p0); + } + + + GjkStatus epaPenetration(const GjkConvex& a, const GjkConvex& b, PxU8* PX_RESTRICT aInd, PxU8* PX_RESTRICT bInd, PxU8 _size, Ps::aos::Vec3V& contactA, Ps::aos::Vec3V& contactB, Ps::aos::Vec3V& normal, Ps::aos::FloatV& penetrationDepth, const bool takeCoreShape) + { + using namespace Ps::aos; + const BoolV bTrue = BTTTT(); + const Vec3V zeroV = V3Zero(); + + PxU32 size=_size; + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + + //ML: eps2 is the square value of an epsilon value which applied in the termination condition for two shapes overlap. EPA will terminate based on sq(v) < eps2 and indicate that two shapes are overlap. + //we calculate the eps2 based on 10% of the minimum margin of two shapes + const FloatV eps = FMul(minMargin, FLoad(0.1f)); + const FloatV eps2 = FMul(eps, eps); + + Vec3V A[4]; + Vec3V B[4]; + Vec3V Q[4]; + Vec3V v(zeroV); + BoolV bNotTerminated = bTrue; + BoolV bCon = bTrue; + FloatV sDist = FMax(); + FloatV minDist= sDist; + Vec3V supportA, supportB, support; + FloatV marginDif = FZero(); + + if(_size != 0) + { + //ML: we construct a simplex based on the gjk simplex indices + for(PxU32 i=0; i<_size; ++i) + { + supportA = a.supportPoint(aInd[i], &marginDif); + supportB = b.supportPoint(bInd[i], &marginDif); + //avoid LHS in xbox + A[i] = supportA; + B[i] = supportB; + Q[i] = V3Sub(supportA, supportB); + } + + //ML: if any of the gjk shapes is a shrunk shape, we need to recreate the simplex for the original shape to work on; otherwise, we can just skip this process + //and go to the epa code + if(!takeCoreShape) + { + v = GJKCPairDoSimplex(Q, A, B, Q[size-1], size); + sDist = V3Dot(v, v); + bNotTerminated = FIsGrtr(sDist, eps2); + } + else + { + bNotTerminated = BFFFF(); + } + } + else + { + const Vec3V _initialSearchDir = V3Sub(a.getCenter(), b.getCenter()); + v = V3Sel(FIsGrtr(V3Dot(_initialSearchDir, _initialSearchDir), FZero()), _initialSearchDir, V3UnitX()); + } + + + while(BAllEqTTTT(bNotTerminated)) + { + minDist = sDist; + //calculate the support point + //pair->doSupport(v, A[size], B[size], Q[size]); + supportA = a.support(V3Neg(v)); + supportB = b.support(v); + support = V3Sub(supportA, supportB); + A[size] = supportA; + B[size] = supportB; + Q[size++] = support; + + v = GJKCPairDoSimplex(Q, A, B, support, size); + sDist = V3Dot(v, v); + bCon = FIsGrtr(minDist, sDist); + bNotTerminated = BAnd(FIsGrtr(sDist, eps2), bCon); + } + + EPA epa; + + return epa.PenetrationDepth(a, b, Q, A, B, PxI32(size), contactA, contactB, normal, penetrationDepth, takeCoreShape); + } + + //ML: this function returns the signed distance of a point to a plane + PX_FORCE_INLINE Ps::aos::FloatV Facet::getPlaneDist(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf) const + { + const Vec3V pa0(aBuf[m_indices[0]]); + const Vec3V pb0(bBuf[m_indices[0]]); + const Vec3V p0 = V3Sub(pa0, pb0); + + return V3Dot(m_planeNormal, V3Sub(p, p0)); + } + + //ML: This function: + // (1)calculates the distance from orign((0, 0, 0)) to a triangle plane + // (2) rejects triangle if the triangle is degenerate (two points are identical) + // (3) rejects triangle to be added into the heap if the plane distance is outside of the boundary[lower, upper] + PX_EPA_FORCE_INLINE Ps::aos::BoolV Facet::isValid2(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, + const Ps::aos::FloatVArg lower, const Ps::aos::FloatVArg upper) + { + using namespace Ps::aos; + const FloatV eps = FEps(); + + const Vec3V pa0(aBuf[i0]); + const Vec3V pa1(aBuf[i1]); + const Vec3V pa2(aBuf[i2]); + + const Vec3V pb0(bBuf[i0]); + const Vec3V pb1(bBuf[i1]); + const Vec3V pb2(bBuf[i2]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + + const Vec3V v1 = V3Sub(p1, p0); + const Vec3V v2 = V3Sub(p2, p0); + const Vec3V v3 = V3Sub(p2, p1); + + const FloatV v1v1 = V3Dot(v1, v1); + const FloatV v2v2 = V3Dot(v2, v2); + + const Vec3V v = V3Sel(FIsGrtr(v1v1, v2v2), v2, v1); + const Vec3V denormalizedNormal = V3Cross(v, v3); + FloatV norValue = V3Dot(denormalizedNormal, denormalizedNormal); + //if norValue < eps, this triangle is degenerate + const BoolV con = FIsGrtr(norValue, eps); + norValue = FSel(con, norValue, FOne()); + + const Vec3V planeNormal = V3Scale(denormalizedNormal, FRsqrt(norValue)); + const FloatV planeDist = V3Dot(planeNormal, p0); + m_planeNormal = planeNormal; + + FStore(planeDist, &m_planeDist); + + return BAnd(con, BAnd(FIsGrtrOrEq(planeDist, lower), FIsGrtrOrEq(upper, planeDist))); + } + + //ML: if the triangle is valid(not degenerate and within lower and upper bound), we need to add it into the heap. Otherwise, we just return + //the triangle so that the facet can be linked to other facets in the expanded polytope. + Facet* EPA::addFacet(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::FloatVArg lower2, const Ps::aos::FloatVArg upper2) + { + using namespace Ps::aos; + PX_ASSERT(i0 != i1 && i0 != i2 && i1 != i2); + //ML: we move the control in the calling code so we don't need to check weather we will run out of facets or not + PX_ASSERT(facetManager.getNumUsedID() < MaxFacets); + + const PxU32 facetId = facetManager.getNewID(); + Ps::prefetchLine(&facetBuf[facetId], 128); + + Facet * facet = PX_PLACEMENT_NEW(&facetBuf[facetId],Facet(i0, i1, i2)); + facet->m_FacetId = PxU8(facetId); + + const BoolV validTriangle = facet->isValid2(i0, i1, i2, aBuf, bBuf, lower2, upper2); + + if(BAllEqTTTT(validTriangle)) + { + heap.push(facet); + facet->m_inHeap = true; + } + else + { + facet->m_inHeap = false; + } + return facet; + } + + //ML: this function performs a flood fill over the boundary of the current polytope. + void Facet::silhouette(const PxU32 _index, const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager) + { + using namespace Ps::aos; + const FloatV zero = FZero(); + Edge stack[MaxFacets]; + stack[0] = Edge(this, _index); + PxI32 size = 1; + while(size--) + { + Facet* const PX_RESTRICT f = stack[size].m_facet; + const PxU32 index = stack[size].m_index; + PX_ASSERT(f->Valid()); + + if(!f->m_obsolete) + { + //ML: if the point is above the facet, the facet has an reflex edge, which will make the polytope concave. Therefore, we need to + //remove this facet and make sure the expanded polytope is convex. + const FloatV pointPlaneDist = f->getPlaneDist(w, aBuf, bBuf); + + if(FAllGrtr(zero, pointPlaneDist)) + { + //ML: facet isn't visible from w (we don't have a reflex edge), this facet will be on the boundary and part of the new polytope so that + //we will push it into our edgeBuffer + edgeBuffer.Insert(f, index); + } + else + { + //ML:facet is visible from w, therefore, we need to remove this facet from the heap and push its adjacent facets onto the stack + f->m_obsolete = true; // Facet is visible from w + const PxU32 next(incMod3(index)); + const PxU32 next2(incMod3(next)); + stack[size++] = Edge(f->m_adjFacets[next2],PxU32(f->m_adjEdges[next2])); + stack[size++] = Edge(f->m_adjFacets[next], PxU32(f->m_adjEdges[next])); + + PX_ASSERT(size <= MaxFacets); + if(!f->m_inHeap) + { + //if the facet isn't in the heap, we can release that memory + manager.deferredFreeID(f->m_FacetId); + } + } + } + } + } + + //ML: this function perform flood fill for the adjancent facet and store the boundary facet into the edgeBuffer + void Facet::silhouette(const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager) + { + m_obsolete = true; + for(PxU32 a = 0; a < 3; ++a) + { + m_adjFacets[a]->silhouette(PxU32(m_adjEdges[a]), w, aBuf, bBuf, edgeBuffer, manager); + } + } + + bool EPA::expandPoint(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg lowerBound, const FloatVArg upperBound) + { + const Vec3V x = V3UnitX(); + Vec3V q0 = V3Sub(aBuf[0], bBuf[0]); + Vec3V q1; + doSupport(a, b, x, aBuf[1], bBuf[1], q1); + if (V3AllEq(q0, q1)) + return false; + return expandSegment(a, b, numVerts, lowerBound, upperBound); + } + + //ML: this function use the segement to create a triangle + bool EPA::expandSegment(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg lowerBound, const FloatVArg upperBound) + { + const Vec3V q0 = V3Sub(aBuf[0], bBuf[0]); + const Vec3V q1 = V3Sub(aBuf[1], bBuf[1]); + const Vec3V v = V3Sub(q1, q0); + const Vec3V absV = V3Abs(v); + + const FloatV x = V3GetX(absV); + const FloatV y = V3GetY(absV); + const FloatV z = V3GetZ(absV); + + Vec3V axis = V3UnitX(); + const BoolV con0 = BAnd(FIsGrtr(x, y), FIsGrtr(z, y)); + if (BAllEqTTTT(con0)) + { + axis = V3UnitY(); + } + else if(FAllGrtr(x, z)) + { + axis = V3UnitZ(); + } + + const Vec3V n = V3Normalize(V3Cross(axis, v)); + Vec3V q2; + doSupport(a, b, n, aBuf[2], bBuf[2], q2); + + return expandTriangle(numVerts, lowerBound, upperBound); + } + + bool EPA::expandTriangle(PxI32& numVerts, const FloatVArg lowerBound, const FloatVArg upperBound) + { + numVerts = 3; + + Facet * PX_RESTRICT f0 = addFacet(0, 1, 2, lowerBound, upperBound); + Facet * PX_RESTRICT f1 = addFacet(1, 0, 2, lowerBound, upperBound); + + if(f0 == NULL || f1 == NULL || heap.empty()) + return false; + + f0->link(0, f1, 0); + f0->link(1, f1, 2); + f0->link(2, f1, 1); + + return true; + } + + + //ML: this function calculates contact information. If takeCoreShape flag is true, this means the two closest points will be on the core shape used in the support functions. + //For example, we treat sphere/capsule as a point/segment in the support function for GJK/EPA, so that the core shape for sphere/capsule is a point/segment. For PCM, we need + //to take the point from the core shape because this will allows us recycle the contacts more stably. For SQ sweeps, we need to take the point on the surface of the sphere/capsule + //when we calculate MTD because this is what will be reported to the user. Therefore, the takeCoreShape flag will be set to be false in SQ. + static void calculateContactInformation(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, const Facet* facet, const GjkConvex& a, const GjkConvex& b, Vec3V& pa, Vec3V& pb, Vec3V& normal, FloatV& penDepth, const bool takeCoreShape) + { + const FloatV zero = FZero(); + Vec3V _pa, _pb; + facet->getClosestPoint(aBuf, bBuf, _pa, _pb); + //dist > 0 means two shapes are penetrated. If dist < 0(when origin isn't inside the polytope), two shapes status are unknown + const FloatV dist = facet->getPlaneDist(); + //planeNormal is pointing from B to A, however, the normal we are expecting is from A to B which match the GJK margin intersect case, therefore, + //we need to flip the normal + const Vec3V n =V3Neg(facet->getPlaneNormal()); + + if(takeCoreShape) + { + pa = _pa; + pb = _pb; + normal = n; + penDepth = FNeg(dist); + } + else + { + + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + + const FloatV marginA = FSel(aQuadratic, a.getMargin(), zero); + const FloatV marginB = FSel(bQuadratic, b.getMargin(), zero); + const FloatV sumMargin = FAdd(marginA, marginB); + pa = V3NegScaleSub(n, marginA, _pa); + pb = V3ScaleAdd(n, marginB, _pb); + normal = n; + penDepth = FNeg(FAdd(dist, sumMargin)); + +#if EPA_DEBUG + const Vec3V v = V3Sub(_pb, _pa); + const FloatV length = V3Length(v); + const Vec3V cn = V3ScaleInv(v, length); + const Vec3V cnn = V3Sel(FIsGrtr(dist, zero), cn, V3Neg(cn)); + //make sure planeNormal and n are pointing to the same direction + const FloatV dir = V3Dot(cnn, normal); + PX_ASSERT(FAllGrtr(dir, zero)); + //planeDist might be zero if the origin didn't include in the polytope + const FloatV dif = V3Sub(length, FAbs(penDepth)); + PX_ASSERT(FAllGrtr(FLoad(1e-5f), FAbs(dif))); +#endif + } + } + + //ML: This function returns one of three status codes: + //(1)EPA_FAIL: the algorithm failed to create a valid polytope(the origin wasn't inside the polytope) from the input simplex + //(2)EPA_CONTACT : the algorithm found the MTD amd converged successfully. + //(3)EPA_DEGENERATE: the algorithm cannot make further progress and the result is unknown. + GjkStatus EPA::PenetrationDepth(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3V* PX_RESTRICT /*Q*/, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const PxI32 size, Ps::aos::Vec3V& pa, Ps::aos::Vec3V& pb, Ps::aos::Vec3V& normal, Ps::aos::FloatV& penDepth, const bool takeCoreShape) + { + + using namespace Ps::aos; + + Ps::prefetchLine(&facetBuf[0]); + Ps::prefetchLine(&facetBuf[0], 128); + + const FloatV zero = FZero(); + + const FloatV _max = FMax(); + FloatV upper_bound(_max); + FloatV lower_bound(FNeg(_max)); + + aBuf[0]=A[0]; aBuf[1]=A[1]; aBuf[2]=A[2]; aBuf[3]=A[3]; + bBuf[0]=B[0]; bBuf[1]=B[1]; bBuf[2]=B[2]; bBuf[3]=B[3]; + + PxI32 numVertsLocal = 0; + + heap.clear(); + + //if the simplex isn't a tetrahedron, we need to construct one before we can expand it + switch (size) + { + case 1: + { + // Touching contact. Yes, we have a collision and the penetration will be zero + if(!expandPoint(a, b, numVertsLocal, lower_bound, upper_bound)) + return EPA_FAIL; + break; + } + case 2: + { + // We have a line segment inside the Minkowski sum containing the + // origin. we need to construct two back to back triangles which link to each other + if(!expandSegment(a, b, numVertsLocal, lower_bound, upper_bound)) + return EPA_FAIL; + break; + } + case 3: + { + // We have a triangle inside the Minkowski sum containing + // the origin. We need to construct two back to back triangles which link to each other + if(!expandTriangle(numVertsLocal, lower_bound, upper_bound)) + return EPA_FAIL; + + break; + + } + case 4: + { + //check for input face normal. All face normals in this tetrahedron should be all pointing either inwards or outwards. If all face normals are pointing outward, we are good to go. Otherwise, we need to + //shuffle the input vertexes and make sure all face normals are pointing outward + const FloatV planeDistf0 = calculatePlaneDist(0, 1, 2, aBuf, bBuf); + + if(FAllGrtr(zero, planeDistf0)) + { + //shuffle the input vertexes + const Vec3V tempA0 = aBuf[2]; + const Vec3V tempB0 = bBuf[2]; + aBuf[2] = aBuf[1]; + bBuf[2] = bBuf[1]; + aBuf[1] = tempA0; + bBuf[1] = tempB0; + } + Facet * PX_RESTRICT f0 = addFacet(0, 1, 2, lower_bound, upper_bound); + Facet * PX_RESTRICT f1 = addFacet(0, 3, 1, lower_bound, upper_bound); + Facet * PX_RESTRICT f2 = addFacet(0, 2, 3, lower_bound, upper_bound); + Facet * PX_RESTRICT f3 = addFacet(1, 3, 2, lower_bound, upper_bound); + PX_ASSERT(f0->m_planeDist >= 0.f); + PX_ASSERT(f1->m_planeDist >= 0.f); + PX_ASSERT(f2->m_planeDist >= 0.f); + PX_ASSERT(f3->m_planeDist >= 0.f); + + if((f0==NULL) || (f1==NULL) || (f2==NULL) || (f3==NULL) || heap.empty()) + return EPA_FAIL; + + + f0->link(0, f1, 2); + f0->link(1, f3, 2); + f0->link(2, f2, 0); + f1->link(0, f2, 2); + f1->link(1, f3, 0); + f2->link(1, f3, 1); + numVertsLocal = 4; + + break; + } + } + + const FloatV eps = FEps(); + const FloatV tenPerc = FLoad(0.1f); + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + const FloatV eps2 = FMul(minMargin, tenPerc); + + Facet* PX_RESTRICT facet = NULL; + Facet* PX_RESTRICT bestFacet = NULL; + + bool hasMoreFacets = false; + Vec3V tempa, tempb, q; + + do + { + + facetManager.processDeferredIds(); + facet = heap.pop(); //get the shortest distance triangle of origin from the list + facet->m_inHeap = false; + + if (!facet->isObsolete()) + { + bestFacet = facet; + Ps::prefetchLine(edgeBuffer.m_pEdges); + Ps::prefetchLine(edgeBuffer.m_pEdges,128); + Ps::prefetchLine(edgeBuffer.m_pEdges,256); + + const Vec3V planeNormal = facet->getPlaneNormal(); + const FloatV planeDist = facet->getPlaneDist(); + + tempa = a.support(planeNormal); + tempb = b.support(V3Neg(planeNormal)); + + q = V3Sub(tempa, tempb); + + Ps::prefetchLine(&aBuf[numVertsLocal],128); + Ps::prefetchLine(&bBuf[numVertsLocal],128); + + //calculate the distance from support point to the origin along the plane normal. Because the support point is search along + //the plane normal, which means the distance should be positive. However, if the origin isn't contained in the polytope, dist + //might be negative + const FloatV dist = V3Dot(q, planeNormal); + //update the upper bound to the minimum between exisiting upper bound and the distance if distance is positive + upper_bound = FSel(FIsGrtrOrEq(dist, zero), FMin(upper_bound, dist), upper_bound); + //lower bound is the plane distance, which will be the smallest among all the facets in the prority queue + lower_bound = planeDist; + //if the plane distance and the upper bound is within a small tolerance, which means we found the MTD + const BoolV con0 = FIsGrtrOrEq(eps2, FAbs(FSub(upper_bound, lower_bound))); + + if(BAllEqTTTT(con0)) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, pa, pb, normal, penDepth, takeCoreShape); + return EPA_CONTACT; + } + + //if planeDist is the same as dist, which means the support point we get out from the Mincowsky sum is one of the point + //in the triangle facet, we should exist because we can't progress further. + const FloatV dif = FSub(dist, planeDist); + const BoolV degeneratedCondition = FIsGrtr(eps, dif); + if(BAllEqTTTT(degeneratedCondition)) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, pa, pb, normal, penDepth, takeCoreShape); + return EPA_DEGENERATE; + } + + aBuf[numVertsLocal]=tempa; + bBuf[numVertsLocal]=tempb; + + const PxU32 index =PxU32(numVertsLocal++); + + // Compute the silhouette cast by the new vertex + // Note that the new vertex is on the positive side + // of the current facet, so the current facet will + // not be in the polytope. Start local search + // from this facet. + + edgeBuffer.MakeEmpty(); + + facet->silhouette(q, aBuf, bBuf, edgeBuffer, facetManager); + + if (edgeBuffer.IsEmpty()) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, pa, pb, normal, penDepth, takeCoreShape); + return EPA_DEGENERATE; + } + + Edge* PX_RESTRICT edge=edgeBuffer.Get(0); + + PxU32 bufferSize=edgeBuffer.Size(); + + //check to see whether we have enough space in the facet manager to create new facets + if(bufferSize > facetManager.getNumRemainingIDs()) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, pa, pb, normal, penDepth, takeCoreShape); + return EPA_DEGENERATE; + } + + Facet *firstFacet = addFacet(edge->getTarget(), edge->getSource(),index, lower_bound, upper_bound); + PX_ASSERT(firstFacet); + firstFacet->link(0, edge->getFacet(), edge->getIndex()); + + Facet * PX_RESTRICT lastFacet = firstFacet; + + bool degenerate = false; + for(PxU32 i=1; (i<bufferSize) && (!degenerate); ++i) + { + edge=edgeBuffer.Get(i); + Facet* PX_RESTRICT newFacet = addFacet(edge->getTarget(), edge->getSource(),index, lower_bound, upper_bound); + PX_ASSERT(newFacet); + const bool b0 = newFacet->link(0, edge->getFacet(), edge->getIndex()); + const bool b1 = newFacet->link(2, lastFacet, 1); + degenerate = degenerate || !b0 || !b1; + lastFacet = newFacet; + } + + if(degenerate) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, pa, pb, normal, penDepth, takeCoreShape); + return EPA_DEGENERATE; + } + + firstFacet->link(2, lastFacet, 1); + } + facetManager.freeID(facet->m_FacetId); + + hasMoreFacets = (heap.size() > 0); + //after polytope expansion, we don't have a better facet to work with so that we should process the best facet + //this sometime won't produce the MTD but the result seems close enough so we accept it as contact. + //test case in BenchMark_GJKEPABoxConvex + if(hasMoreFacets && FAllGrtr( heap.top()->getPlaneDist(), upper_bound)) + { + calculateContactInformation(aBuf, bBuf, bestFacet, a, b, pa, pb, normal, penDepth, takeCoreShape); + return EPA_CONTACT; + } + } + while(hasMoreFacets && numVertsLocal != MaxSupportPoints); + + calculateContactInformation(aBuf, bBuf, facet, a, b, pa, pb, normal, penDepth, takeCoreShape); + + return EPA_DEGENERATE; + } +} + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPA.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPA.h new file mode 100644 index 00000000..79f934e6 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPA.h @@ -0,0 +1,58 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EPA_H +#define GU_EPA_H + +#include "GuGJKUtil.h" +#include "GuGJKType.h" + +namespace physx +{ + +namespace Gu +{ + //ML: The main entry point for EPA. + // + //This function returns one of three status codes: + //(1)EPA_FAIL: the algorithm failed to create a valid polytope(the origin wasn't inside the polytope) from the input simplex. + //(2)EPA_CONTACT : the algorithm found the MTD and converged successfully. + //(3)EPA_DEGENERATE: the algorithm cannot make further progress and the result is unknown. + + GjkStatus epaPenetration(const GjkConvex& a, const GjkConvex& b, // two convexes, in the same space + PxU8* PX_RESTRICT aInd, PxU8* PX_RESTRICT bInd, // warm start index points to create an initial simplex that EPA will work on + PxU8 _size, // count of warm-start indices + Ps::aos::Vec3V& contactA, Ps::aos::Vec3V& contactB, // a point on each body: when B is translated by normal*penetrationDepth, these are coincident + Ps::aos::Vec3V& normal, Ps::aos::FloatV& depth, // MTD normal & penetration depth + const bool takeCoreShape = false); // indicates whether we take support point from the core shape of the convexes +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPAFacet.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPAFacet.h new file mode 100644 index 00000000..5e2c6cde --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuEPAFacet.h @@ -0,0 +1,292 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EPA_FACET_H +#define GU_EPA_FACET_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "PsFPU.h" +#include "PsUtilities.h" +#include "CmIDPool.h" + +#if (defined __GNUC__ && defined _DEBUG) +#define PX_EPA_FORCE_INLINE +#else +#define PX_EPA_FORCE_INLINE PX_FORCE_INLINE +#endif + +namespace physx +{ +#define MaxEdges 32 +#define MaxFacets 64 +#define MaxSupportPoints 64 + +namespace Gu +{ + const PxU32 lookUp[3] = {1, 2, 0}; + + PX_FORCE_INLINE PxU32 incMod3(PxU32 i) { return lookUp[i]; } + + class EdgeBuffer; + class Edge; + typedef Cm::InlineDeferredIDPool<MaxFacets> EPAFacetManager; + + class Facet + { + public: + + Facet() + { + } + + PX_FORCE_INLINE Facet(const PxU32 _i0, const PxU32 _i1, const PxU32 _i2) + : m_obsolete(false), m_inHeap(false) + { + m_indices[0]= Ps::toI8(_i0); + m_indices[1]= Ps::toI8(_i1); + m_indices[2]= Ps::toI8(_i2); + + m_adjFacets[0] = m_adjFacets[1] = m_adjFacets[2] = NULL; + m_adjEdges[0] = m_adjEdges[1] = m_adjEdges[2] = -1; + } + + + PX_FORCE_INLINE void invalidate() + { + m_adjFacets[0] = m_adjFacets[1] = m_adjFacets[2] = NULL; + m_adjEdges[0] = m_adjEdges[1] = m_adjEdges[2] = -1; + } + + PX_FORCE_INLINE bool Valid() + { + return (m_adjFacets[0] != NULL) & (m_adjFacets[1] != NULL) & (m_adjFacets[2] != NULL); + } + + PX_FORCE_INLINE PxU32 operator[](const PxU32 i) const + { + return PxU32(m_indices[i]); + } + + //create ajacency information + bool link(const PxU32 edge0, Facet* PX_RESTRICT facet, const PxU32 edge1); + + PX_FORCE_INLINE bool isObsolete() const { return m_obsolete; } + + //calculate the signed distance from a point to a plane + PX_FORCE_INLINE Ps::aos::FloatV getPlaneDist(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf) const; + + //check to see whether the triangle is a valid triangle, calculate plane normal and plane distance at the same time + PX_EPA_FORCE_INLINE Ps::aos::BoolV isValid2(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, + const Ps::aos::FloatVArg lower, const Ps::aos::FloatVArg upper); + + //return the absolute value for the plane distance from origin + PX_FORCE_INLINE Ps::aos::FloatV getPlaneDist() const + { + return Ps::aos::FLoad(m_planeDist); + } + + //return the plane normal + PX_FORCE_INLINE Ps::aos::Vec3V getPlaneNormal()const + { + return m_planeNormal; + } + + //calculate the closest points for a shape pair + void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB) const; + + //performs a flood fill over the boundary of the current polytope. + void silhouette(const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager); + + + //m_planeDist is positive + bool operator <(const Facet& b) const + { + return m_planeDist < b.m_planeDist; + } + + //store all the boundary facets for the new polytope in the edgeBuffer and free indices when an old facet isn't part of the boundary anymore + PX_FORCE_INLINE void silhouette(const PxU32 index, const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, + EPAFacetManager& manager); + + Ps::aos::Vec3V m_planeNormal; //16 + PxF32 m_planeDist; //20 + + Facet* PX_RESTRICT m_adjFacets[3]; //the triangle adjacent to edge i in this triangle //32 + PxI8 m_adjEdges[3]; //the edge connected with the corresponding triangle //35 + PxI8 m_indices[3]; //the index of vertices of the triangle //38 + bool m_obsolete; //a flag to denote whether the triangle are still part of the bundeary of the new polytope //39 + bool m_inHeap; //a flag to indicate whether the triangle is in the heap //40 + PxU8 m_FacetId; //41 //73 + + }; + + + class Edge + { + public: + PX_FORCE_INLINE Edge() {} + PX_FORCE_INLINE Edge(Facet * PX_RESTRICT facet, const PxU32 index) : m_facet(facet), m_index(index) {} + PX_FORCE_INLINE Edge(const Edge& other) : m_facet(other.m_facet), m_index(other.m_index){} + + PX_FORCE_INLINE Edge& operator = (const Edge& other) + { + m_facet = other.m_facet; + m_index = other.m_index; + return *this; + } + + PX_FORCE_INLINE Facet *getFacet() const { return m_facet; } + PX_FORCE_INLINE PxU32 getIndex() const { return m_index; } + + //get out the associated start vertex index in this edge from the facet + PX_FORCE_INLINE PxU32 getSource() const + { + PX_ASSERT(m_index < 3); + return (*m_facet)[m_index]; + } + + //get out the associated end vertex index in this edge from the facet + PX_FORCE_INLINE PxU32 getTarget() const + { + PX_ASSERT(m_index < 3); + return (*m_facet)[incMod3(m_index)]; + } + + Facet* PX_RESTRICT m_facet; + PxU32 m_index; + }; + + + class EdgeBuffer + { + public: + EdgeBuffer() : m_Size(0) + { + } + + Edge* Insert(const Edge& edge) + { + PX_ASSERT(m_Size < MaxEdges); + Edge* PX_RESTRICT pEdge = &m_pEdges[m_Size++]; + *pEdge = edge; + return pEdge; + } + + Edge* Insert(Facet* PX_RESTRICT facet, const PxU32 index) + { + PX_ASSERT(m_Size < MaxEdges); + Edge* pEdge = &m_pEdges[m_Size++]; + pEdge->m_facet=facet; + pEdge->m_index=index; + return pEdge; + } + + Edge* Get(const PxU32 index) + { + PX_ASSERT(index < m_Size); + return &m_pEdges[index]; + } + + PxU32 Size() + { + return m_Size; + } + + bool IsEmpty() + { + return m_Size == 0; + } + + void MakeEmpty() + { + m_Size = 0; + } + + Edge m_pEdges[MaxEdges]; + PxU32 m_Size; + }; + + //ML: calculate MTD points for a shape pair + PX_FORCE_INLINE void Facet::getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB) const + { + using namespace Ps::aos; + + const Vec3V pa0(aBuf[m_indices[0]]); + const Vec3V pa1(aBuf[m_indices[1]]); + const Vec3V pa2(aBuf[m_indices[2]]); + + const Vec3V pb0(bBuf[m_indices[0]]); + const Vec3V pb1(bBuf[m_indices[1]]); + const Vec3V pb2(bBuf[m_indices[2]]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + + const Vec3V v1 = V3Sub(p1, p0); + const Vec3V v2 = V3Sub(p2, p0); + + const FloatV v1dv1 = V3Dot(v1, v1); + const FloatV v1dv2 = V3Dot(v1, v2); + const FloatV v2dv2 = V3Dot(v2, v2); + + const FloatV p0dv1 = V3Dot(p0, v1); //V3Dot(V3Sub(p0, origin), v1); + const FloatV p0dv2 = V3Dot(p0, v2); + + const FloatV det = FNegScaleSub(v1dv2, v1dv2, FMul(v1dv1, v2dv2));//FSub( FMul(v1dv1, v2dv2), FMul(v1dv2, v1dv2) ); // non-negative + const FloatV recip = FRecip(det); + + const FloatV lambda1 = FMul(FNegScaleSub(p0dv1, v2dv2, FMul(p0dv2, v1dv2)), recip); + const FloatV lambda2 = FMul(FNegScaleSub(p0dv2, v1dv1, FMul(p0dv1, v1dv2)), recip); + + const Vec3V a0 = V3Scale(V3Sub(pa1, pa0), lambda1); + const Vec3V a1 = V3Scale(V3Sub(pa2, pa0), lambda2); + const Vec3V b0 = V3Scale(V3Sub(pb1, pb0), lambda1); + const Vec3V b1 = V3Scale(V3Sub(pb2, pb0), lambda2); + closestA = V3Add(V3Add(a0, a1), pa0); + closestB = V3Add(V3Add(b0, b1), pb0); + } + + //ML: create adjacency informations for both facets + PX_FORCE_INLINE bool Facet::link(const PxU32 edge0, Facet * PX_RESTRICT facet, const PxU32 edge1) + { + m_adjFacets[edge0] = facet; + m_adjEdges[edge0] = Ps::toI8(edge1); + facet->m_adjFacets[edge1] = this; + facet->m_adjEdges[edge1] = Ps::toI8(edge0); + + return (m_indices[edge0] == facet->m_indices[incMod3(edge1)]) && (m_indices[incMod3(edge0)] == facet->m_indices[edge1]); + } + +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJK.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJK.h new file mode 100644 index 00000000..d0f1254e --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJK.h @@ -0,0 +1,219 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_H +#define GU_GJK_H + + +#include "GuGJKType.h" +#include "GuGJKUtil.h" +#include "GuConvexSupportTable.h" +#include "GuGJKSimplex.h" +#include "PsFPU.h" + +#define GJK_SEPERATING_AXIS_VALIDATE 0 + +namespace physx +{ +namespace Gu +{ + + class ConvexV; + +#if GJK_SEPERATING_AXIS_VALIDATE + template<typename ConvexA, typename ConvexB> + static void validateSeparatingAxis(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg separatingAxis) + { + using namespace Ps::aos; + const Vec3V minV0 = a.ConvexA::support(V3Neg(separatingAxis)); + const Vec3V maxV0 = a.ConvexA::support(separatingAxis); + + const Vec3V minV1 = b.ConvexB::support(V3Neg(separatingAxis)); + const Vec3V maxV1 = b.ConvexB::support(separatingAxis); + + const FloatV min0 = V3Dot(minV0, separatingAxis); + const FloatV max0 = V3Dot(maxV0, separatingAxis); + + const FloatV min1 = V3Dot(minV1, separatingAxis); + const FloatV max1 = V3Dot(maxV1, separatingAxis); + + PX_ASSERT(FAllGrtr(min1, max0) || FAllGrtr(min0, max1)); + } +#endif + + + /* + + initialSearchDir :initial search direction in the mincowsky sum, it should be in the local space of ConvexB + closestA :it is the closest point in ConvexA in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + closestB :it is the closest point in ConvexB in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + normal :normal pointing from ConvexA to ConvexB in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + distance :the distance of the closest points between ConvexA and ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + contactDist :the distance which we will generate contact information if ConvexA and ConvexB are both separated within contactDist + */ + + //*Each convex has + //* a support function + //* a margin - the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + //* a minMargin - some percentage of margin, which is used to determine the termination condition for gjk + + //*We'll report: + //* GJK_NON_INTERSECT if the sign distance between the shapes is greater than the sum of the margins and the the contactDistance + //* GJK_CLOSE if the minimum distance between the shapes is less than the sum of the margins and the the contactDistance + //* GJK_CONTACT if the two shapes are overlapped with each other + template<typename ConvexA, typename ConvexB> + GjkStatus gjk(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3V& initialSearchDir, const Ps::aos::FloatV& contactDist, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& distance) + { + using namespace Ps::aos; + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + + const FloatV zero = FZero(); + PxU32 size=0; + + //const Vec3V _initialSearchDir = aToB.p; + Vec3V closest = V3Sel(FIsGrtr(V3Dot(initialSearchDir, initialSearchDir), zero), initialSearchDir, V3UnitX()); + Vec3V v = V3Normalize(closest); + + // ML: eps2 is the square value of an epsilon value which applied in the termination condition for two shapes overlap. + // GJK will terminate based on sq(v) < eps and indicate that two shapes are overlapping. + // we calculate the eps based on 10% of the minimum margin of two shapes + const FloatV tenPerc = FLoad(0.1f); + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + const FloatV eps = FMul(minMargin, tenPerc); + + // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin. + // If |v|- v/|v|.dot(w) < epsRel*|v|, + // two shapes are clearly separated, GJK terminate and return non intersect. + // This adjusts the termination condition based on the length of v + // which avoids ill-conditioned terminations. + const FloatV epsRel = FLoad(0.000225f);//1.5%. + + FloatV dist = FMax(); + FloatV prevDist; + Vec3V prevClos, prevDir; + + const BoolV bTrue = BTTTT(); + BoolV bNotTerminated = bTrue; + BoolV bNotDegenerated = bTrue; + + //ML: we treate sphere as a point and capsule as segment in the support function, so that we need to add on radius + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + + const FloatV sumMargin = FAdd(FSel(aQuadratic, a.getMargin(), zero), FSel(bQuadratic, b.getMargin(), zero)); + const FloatV separatingDist = FAdd(sumMargin, contactDist); + const FloatV relDif = FSub(FOne(), epsRel); + + do + { + prevDist = dist; + prevClos = closest; + prevDir = v; + + //de-virtualize, we don't need to use a normalize direction to get the support point + //this will allow the cpu better pipeline the normalize calculation while it does the + //support map + const Vec3V supportA=a.ConvexA::support(V3Neg(closest)); + const Vec3V supportB=b.ConvexB::support(closest); + + //calculate the support point + const Vec3V support = V3Sub(supportA, supportB); + + const FloatV signDist = V3Dot(v, support); + + if(FAllGrtr(signDist, separatingDist)) + { + //ML:gjk found a separating axis for these two objects and the distance is large than the seperating distance, gjk might not converage so that + //we won't generate contact information +#if GJK_SEPERATING_AXIS_VALIDATE + validateSeparatingAxis(a, b, v); +#endif + return GJK_NON_INTERSECT; + } + + const BoolV con = BAnd(FIsGrtr(signDist, sumMargin), FIsGrtr(signDist, FMul(relDif, dist))); + + if(BAllEqTTTT(con)) + { + //ML:: gjk converage and we get the closest point information + Vec3V closA, closB; + //normal point from A to B + const Vec3V n = V3Neg(v); + getClosestPoint(Q, A, B, closest, closA, closB, size); + closestA = V3Sel(aQuadratic, V3ScaleAdd(n, a.getMargin(), closA), closA); + closestB = V3Sel(bQuadratic, V3NegScaleSub(n, b.getMargin(), closB), closB); + distance = FMax(zero, FSub(dist, sumMargin)); + normal = n;//V3Normalize(V3Neg(closest)); + return GJK_CLOSE; + } + + PX_ASSERT(size < 4); + A[size]=supportA; + B[size]=supportB; + Q[size++]=support; + + //calculate the closest point between two convex hull + closest = GJKCPairDoSimplex(Q, A, B, support, size); + + dist = V3Length(closest); + v = V3ScaleInv(closest, dist); + bNotDegenerated = FIsGrtr(prevDist, dist); + bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated); + }while(BAllEqTTTT(bNotTerminated)); + + if(BAllEqTTTT(bNotDegenerated)) + { + //GJK_CONTACT + distance = zero; + return GJK_CONTACT; + } + + //GJK degenerated, use the previous closest point + const FloatV acceptancePerc = FLoad(0.2f); + const FloatV acceptanceMargin = FMul(acceptancePerc, FMin(a.getMargin(), b.getMargin())); + const FloatV acceptanceDist = FSel(FIsGrtr(sumMargin, zero), sumMargin, acceptanceMargin); + Vec3V closA, closB; + const Vec3V n = V3Neg(prevDir);//V3Normalize(V3Neg(prevClos)); + getClosestPoint(Q, A, B, prevClos, closA, closB, size); + closestA = V3Sel(aQuadratic, V3ScaleAdd(n, a.getMargin(), closA), closA); + closestB = V3Sel(bQuadratic, V3NegScaleSub(n, b.getMargin(), closB), closB); + normal = n; + dist = FMax(zero, FSub(prevDist, sumMargin)); + distance = dist; + + return FAllGrtr(dist, acceptanceDist) ? GJK_CLOSE: GJK_CONTACT; + } +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKPenetration.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKPenetration.h new file mode 100644 index 00000000..17d26e62 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKPenetration.h @@ -0,0 +1,336 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_PENETRATION_H +#define GU_GJK_PENETRATION_H + + +#include "GuConvexSupportTable.h" +#include "GuGJKSimplex.h" +#include "GuVecShrunkConvexHullNoScale.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGJKUtil.h" +#include "PsUtilities.h" +#include "GuGJKType.h" + +#define GJK_VALIDATE 0 + + +namespace physx +{ +namespace Gu +{ + + class ConvexV; + + + PX_FORCE_INLINE void assignWarmStartValue(PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& size_, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32 size ) + { + if(aIndices) + { + PX_ASSERT(bIndices); + size_ = Ps::to8(size); + for(PxU32 i=0; i<size; ++i) + { + aIndices[i] = Ps::to8(aInd[i]); + bIndices[i] = Ps::to8(bInd[i]); + } + } + } + + + PX_FORCE_INLINE void validateDuplicateVertex(const Ps::aos::Vec3V* Q, const Ps::aos::Vec3VArg support, const PxU32 size) + { + using namespace Ps::aos; + + const FloatV eps = FEps(); + //Get rid of the duplicate point + BoolV match = BFFFF(); + for(PxU32 na = 0; na < size; ++na) + { + Vec3V dif = V3Sub(Q[na], support); + match = BOr(match, FIsGrtr(eps, V3Dot(dif, dif))); + } + + //we have duplicate code + if(BAllEqTTTT(match)) + { + PX_ASSERT(0); + } + } + + + //*Each convex has + //* a support function + //* a margin - the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + //* a minMargin - some percentage of margin, which is used to determine the termination condition for gjk + + //*We'll report: + //* GJK_NON_INTERSECT if the minimum distance between the shapes is greater than the sum of the margins and the the contactDistance + //* EPA_CONTACT if shrunk shapes overlap. We treat sphere/capsule as a point/a segment and we shrunk other shapes by 10% of margin + //* GJK_CONTACT if the algorithm converges, and the distance between the shapes is less than the sum of the margins plus the contactDistance. In this case we return the closest points found + //* GJK_DEGENERATE if the algorithm doesn't converge, we return this flag to indicate the normal and closest point we return might not be accurated + template<typename ConvexA, typename ConvexB > + PX_NOINLINE GjkStatus gjkPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg originalContactDist, Ps::aos::Vec3V& contactA, + Ps::aos::Vec3V& contactB, Ps::aos::Vec3V& normal, Ps::aos::FloatV& penetrationDepth, PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& warmStartSize, const bool takeCoreShape) + { + using namespace Ps::aos; + + const FloatV marginA = a.getMargin(); + const FloatV marginB = b.getMargin(); + + //ML: eps is the threshold that uses to determine whether two (shrunk) shapes overlap. We calculate eps2 based on 10% of the minimum margin of two shapes + const FloatV minMargin = FMin(a.ConvexA::getMinMargin(), b.ConvexB::getMinMargin()); + const FloatV eps = FMul(minMargin, FLoad(0.1f)); + + //const FloatV eps2 = FMul(_eps2, _eps2); + //ML: epsRel2 is the square of 0.01. This is used to scale the square distance of a closest point to origin to determine whether two shrunk shapes overlap in the margin, but + //they don't overlap. + //const FloatV epsRel2 = FMax(FLoad(0.0001), eps2); + + // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin. + // If |v|- v/|v|.dot(w) < epsRel*|v|=>(|v|*(1-epsRel) < v/|v|.dot(w)), + // two shapes are clearly separated, GJK terminate and return non intersect. + // This adjusts the termination condition based on the length of v + // which avoids ill-conditioned terminations. + const FloatV epsRel = FLoad(0.000225f);//1.5%. + const FloatV relDif = FSub(FOne(), epsRel); + + const FloatV sumOriginalMargin = FAdd(marginA, marginB); + FloatV contactDist = originalContactDist; + + FloatV dist = FMax(); + FloatV prevDist = dist; + const Vec3V zeroV = V3Zero(); + Vec3V prevClos = zeroV; + + const BoolV bTrue = BTTTT(); + BoolV bNotTerminated = bTrue; + BoolV bNotDegenerated = bTrue; + Vec3V closest; + + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + PxI32 aInd[4]; + PxI32 bInd[4]; + Vec3V supportA = zeroV, supportB = zeroV, support=zeroV; + Vec3V v; + + PxU32 size = 0;//_size; + const FloatV zero = FZero(); + FloatV marginDifA = zero; + FloatV marginDifB = zero; + FloatV maxMarginDif = zero; + + //ML: if _size!=0, which means we pass in the previous frame simplex so that we can warm-start the simplex. + //In this case, GJK will normally terminate in one iteration + if(warmStartSize != 0) + { + for(PxU32 i=0; i<warmStartSize; ++i) + { + aInd[i] = aIndices[i]; + bInd[i] = bIndices[i]; + + //de-virtualize + supportA = a.ConvexA::supportPoint(aIndices[i], &marginDifA); + supportB = b.ConvexB::supportPoint(bIndices[i], &marginDifB); + maxMarginDif = FAdd(marginDifA, marginDifB); + contactDist = FMax(contactDist, FAdd(originalContactDist, maxMarginDif)); + + support = V3Sub(supportA, supportB); + +#if GJK_VALIDATE + //ML: this is used to varify whether we will have duplicate vertices in the warm-start value. If this function get triggered, + //this means something isn't right and we need to investigate + validateDuplicateVertex(Q, support, size); +#endif + A[size] = supportA; + B[size] = supportB; + Q[size++] = support; + } + + //run simplex solver to determine whether the point is closest enough so that gjk can terminate + closest = GJKCPairDoSimplex(Q, A, B, aInd, bInd, support, size); + dist = V3Length(closest); + //sDist = V3Dot(closest, closest); + v = V3ScaleInv(closest, dist); + prevDist = dist; + prevClos = closest; + + bNotTerminated = FIsGrtr(dist, eps); + } + else + { + //const Vec3V _initialSearchDir = V3Sub(a.getCenter(), b.getCenter()); + closest = V3Sel(FIsGrtr(V3Dot(initialSearchDir, initialSearchDir), zero), initialSearchDir, V3UnitX()); + v = V3Normalize(closest); + } + + + // ML : termination condition + //(1)two (shrunk)shapes overlap. GJK will terminate based on sq(v) < eps2 and indicate that two shapes are overlapping. + //(2)two (shrunk + margin)shapes are separated. If sq(vw) > sqMargin * sq(v), which means the original objects do not intesect, GJK terminate with GJK_NON_INTERSECT. + //(3)two (shrunk) shapes don't overlap. However, they interect within margin distance. if sq(v)- vw < epsRel2*sq(v), this means the shrunk shapes interect in the margin, + // GJK terminate with GJK_CONTACT. + while(BAllEqTTTT(bNotTerminated)) + { + //prevDist, prevClos are used to store the previous iteration's closest point and the square distance from the closest point + //to origin in Mincowski space + prevDist = dist; + prevClos = closest; + + //de-virtualize + supportA = a.ConvexA::support(V3Neg(closest), aInd[size], &marginDifA); + supportB = b.ConvexB::support(closest, bInd[size], &marginDifB); + + //calculate the support point + support = V3Sub(supportA, supportB); + + //ML: because we shrink the shapes by plane shifting(box and convexhull), the distance from the "shrunk" vertices to the original vertices may be larger than contact distance. + //therefore, we need to take the largest of these 2 values into account so that we don't incorrectly declare shapes to be disjoint. If we don't do this, there is + //an inherent inconsistency between fallback SAT tests and GJK tests that may result in popping due to SAT discovering deep penetrations that were not detected by + //GJK operating on a shrunk shape. + //const FloatV maxMarginDif = FMax(a.getMarginDif(), b.getMarginDif()); + //contactDist = FMax(contactDist, maxMarginDif); + maxMarginDif = FAdd(marginDifA, marginDifB); + contactDist = FMax(contactDist, FAdd(originalContactDist, maxMarginDif)); + + const FloatV sumMargin = FAdd(sumOriginalMargin, contactDist); + + const FloatV vw = V3Dot(v, support); + if(FAllGrtr(vw, sumMargin)) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + return GJK_NON_INTERSECT; + } + + //if(FAllGrtr(FMul(epsRel, dist), FSub(dist, vw))) + if(FAllGrtr(vw, FMul(dist, relDif))) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + PX_ASSERT(FAllGrtr(dist, FEps())); + //const Vec3V n = V3ScaleInv(closest, dist);//normalise + normal = v; + Vec3V closA, closB; + getClosestPoint(Q, A, B, closest, closA, closB, size); + //ML: if one of the shape is sphere/capsule and the takeCoreShape flag is true, the contact point for sphere/capsule will be the sphere center or a point in the + //capsule segment. This will increase the stability for the manifold recycling code. Otherwise, we will return a contact point on the surface for sphere/capsule + //while the takeCoreShape flag is set to be false + if(takeCoreShape) + { + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + const FloatV shrunkFactorA = FSel(aQuadratic, zero, marginA); + const FloatV shrunkFactorB = FSel(bQuadratic, zero, marginB); + const FloatV sumShrunkFactor = FAdd(shrunkFactorA, shrunkFactorB); + contactA = V3NegScaleSub(v, shrunkFactorA, closA); + contactB = V3ScaleAdd(v, shrunkFactorB, closB); + penetrationDepth = FSub(dist, sumShrunkFactor); + + } + else + { + contactA = V3NegScaleSub(v, marginA, closA); + contactB = V3ScaleAdd(v, marginB, closB); + penetrationDepth = FSub(dist, sumOriginalMargin); + } + + return GJK_CONTACT; + } + + A[size] = supportA; + B[size] = supportB; + Q[size++]=support; + PX_ASSERT(size <= 4); + + //calculate the closest point between two convex hull + closest = GJKCPairDoSimplex(Q, A, B, aInd, bInd, support, size); + + dist = V3Length(closest); + v = V3ScaleInv(closest, dist); + + bNotDegenerated = FIsGrtr(prevDist, dist); + bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated); + } + + if(BAllEqFFFF(bNotDegenerated)) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size-1); + + //Reset back to older closest point + dist = prevDist; + closest = prevClos;//V3Sub(closA, closB); + Vec3V closA, closB; + getClosestPoint(Q, A, B, closest, closA, closB, size); + + PX_ASSERT(FAllGrtr(dist, FEps())); + const Vec3V n = V3ScaleInv(closest, dist);//normalise + normal = n; + + if(takeCoreShape) + { + const FloatV sumExpandedMargin = FAdd(sumOriginalMargin, contactDist); + //const FloatV sqExpandedMargin = FMul(sumExpandedMargin, sumExpandedMargin); + const BoolV aQuadratic = a.ConvexA::isMarginEqRadius(); + const BoolV bQuadratic = b.ConvexB::isMarginEqRadius(); + const FloatV shrunkFactorA = FSel(aQuadratic, zero, marginA); + const FloatV shrunkFactorB = FSel(bQuadratic, zero, marginB); + const FloatV sumShrunkFactor = FAdd(shrunkFactorA, shrunkFactorB); + contactA = V3NegScaleSub(n, shrunkFactorA, closA); + contactB = V3ScaleAdd(n, shrunkFactorB, closB); + penetrationDepth = FSub(dist, sumShrunkFactor); + if(FAllGrtrOrEq(sumExpandedMargin, dist)) + return GJK_CONTACT; + } + else + { + contactA = V3NegScaleSub(n, marginA, closA); + contactB = V3ScaleAdd(n, marginB, closB); + penetrationDepth = FSub(dist, sumOriginalMargin); + } + + + return GJK_DEGENERATE; + + } + else + { + //this two shapes are deeply intersected with each other, we need to use EPA algorithm to calculate MTD + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + return EPA_CONTACT; + + } + } + +}//Gu + +}//physx + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKRaycast.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKRaycast.h new file mode 100644 index 00000000..32b12790 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKRaycast.h @@ -0,0 +1,291 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKRAYCAST_H +#define GU_GJKRAYCAST_H + +#include "GuGJKType.h" +#include "GuGJKSimplex.h" +#include "GuConvexSupportTable.h" +#include "GuGJKPenetration.h" +#include "GuEPA.h" + + +namespace physx +{ + + +namespace Gu +{ + + /* + ConvexA is in the local space of ConvexB + lambda : the time of impact(TOI) + initialLambda : the start time of impact value (disable) + s : the sweep ray origin + r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space + normal : the contact normal + inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, + the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 + the TOI will return the time at which both shapes are touching. + + */ + template<class ConvexA, class ConvexB> + bool gjkRaycast(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation) + { + PX_UNUSED(initialLambda); + + using namespace Ps::aos; + + const FloatV inflation = FLoad(_inflation); + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + const BoolV bTrue = BTTTT(); + + const FloatV maxDist = FLoad(PX_MAX_REAL); + + FloatV _lambda = zero;//initialLambda; + Vec3V x = V3ScaleAdd(r, _lambda, s); + PxU32 size=1; + + //const Vec3V dir = V3Sub(a.getCenter(), b.getCenter()); + const Vec3V _initialSearchDir = V3Sel(FIsGrtr(V3Dot(initialDir, initialDir), FEps()), initialDir, V3UnitX()); + const Vec3V initialSearchDir = V3Normalize(_initialSearchDir); + + const Vec3V initialSupportA(a.ConvexA::support(V3Neg(initialSearchDir))); + const Vec3V initialSupportB( b.ConvexB::support(initialSearchDir)); + + Vec3V Q[4] = {V3Sub(initialSupportA, initialSupportB), zeroV, zeroV, zeroV}; //simplex set + Vec3V A[4] = {initialSupportA, zeroV, zeroV, zeroV}; //ConvexHull a simplex set + Vec3V B[4] = {initialSupportB, zeroV, zeroV, zeroV}; //ConvexHull b simplex set + + + Vec3V v = V3Neg(Q[0]); + Vec3V supportA = initialSupportA; + Vec3V supportB = initialSupportB; + Vec3V support = Q[0]; + + const FloatV minMargin = FMin(a.ConvexA::getSweepMargin(), b.ConvexB::getSweepMargin()); + const FloatV eps1 = FMul(minMargin, FLoad(0.1f)); + const FloatV inflationPlusEps(FAdd(eps1, inflation)); + const FloatV eps2 = FMul(eps1, eps1); + + const FloatV inflation2 = FMul(inflationPlusEps, inflationPlusEps); + + Vec3V clos(Q[0]); + Vec3V preClos = clos; + //Vec3V closA(initialSupportA); + FloatV sDist = V3Dot(v, v); + FloatV minDist = sDist; + //Vec3V closAA = initialSupportA; + //Vec3V closBB = initialSupportB; + + BoolV bNotTerminated = FIsGrtr(sDist, eps2); + BoolV bNotDegenerated = bTrue; + + Vec3V nor = v; + + while(BAllEqTTTT(bNotTerminated)) + { + + minDist = sDist; + preClos = clos; + + const Vec3V vNorm = V3Normalize(v); + const Vec3V nvNorm = V3Neg(vNorm); + + supportA=a.ConvexA::support(vNorm); + supportB=V3Add(x, b.ConvexB::support(nvNorm)); + + //calculate the support point + support = V3Sub(supportA, supportB); + const Vec3V w = V3Neg(support); + const FloatV vw = FSub(V3Dot(vNorm, w), inflationPlusEps); + const FloatV vr = V3Dot(vNorm, r); + if(FAllGrtr(vw, zero)) + { + + if(FAllGrtrOrEq(vr, zero)) + { + return false; + } + else + { + const FloatV _oldLambda = _lambda; + _lambda = FSub(_lambda, FDiv(vw, vr)); + if(FAllGrtr(_lambda, _oldLambda)) + { + if(FAllGrtr(_lambda, one)) + { + return false; + } + const Vec3V bPreCenter = x; + x = V3ScaleAdd(r, _lambda, s); + + const Vec3V offSet =V3Sub(x, bPreCenter); + const Vec3V b0 = V3Add(B[0], offSet); + const Vec3V b1 = V3Add(B[1], offSet); + const Vec3V b2 = V3Add(B[2], offSet); + + B[0] = b0; + B[1] = b1; + B[2] = b2; + + Q[0]=V3Sub(A[0], b0); + Q[1]=V3Sub(A[1], b1); + Q[2]=V3Sub(A[2], b2); + + supportB = V3Add(x, b.ConvexB::support(nvNorm)); + support = V3Sub(supportA, supportB); + minDist = maxDist; + nor = v; + //size=0; + } + } + } + + PX_ASSERT(size < 4); + A[size]=supportA; + B[size]=supportB; + Q[size++]=support; + + //calculate the closest point between two convex hull + clos = GJKCPairDoSimplex(Q, A, B, support, size); + v = V3Neg(clos); + sDist = V3Dot(clos, clos); + + bNotDegenerated = FIsGrtr(minDist, sDist); + bNotTerminated = BAnd(FIsGrtr(sDist, inflation2), bNotDegenerated); + } + + + const BoolV aQuadratic = a.isMarginEqRadius(); + //ML:if the Minkowski sum of two objects are too close to the original(eps2 > sDist), we can't take v because we will lose lots of precision. Therefore, we will take + //previous configuration's normal which should give us a reasonable approximation. This effectively means that, when we do a sweep with inflation, we always keep v because + //the shapes converge separated. If we do a sweep without inflation, we will usually use the previous configuration's normal. + nor = V3Sel(BAnd(FIsGrtr(sDist, eps2), bNotDegenerated), v, nor); + nor = V3Neg(V3NormalizeSafe(nor, V3Zero())); + normal = nor; + lambda = _lambda; + + const Vec3V closestP = V3Sel(bNotDegenerated, clos, preClos); + Vec3V closA = zeroV, closB = zeroV; + getClosestPoint(Q, A, B, closestP, closA, closB, size); + closestA = V3Sel(aQuadratic, V3NegScaleSub(nor, a.getMargin(), closA), closA); + + return true; + } + + + + /* + ConvexA is in the local space of ConvexB + lambda : the time of impact(TOI) + initialLambda : the start time of impact value (disable) + s : the sweep ray origin in ConvexB's space + r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space + normal : the contact normal in ConvexB's space + closestA : the tounching contact in ConvexB's space + inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, + the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 + the TOI will return the time at which both shapes are touching. + + */ + template<class ConvexA, class ConvexB> + bool gjkRaycastPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, + Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap) + { + using namespace Ps::aos; + Vec3V closA; + Vec3V norm; + FloatV _lambda; + if(gjkRaycast(a, b, initialDir, initialLambda, s, r, _lambda, norm, closA, _inflation)) + { + const FloatV zero = FZero(); + lambda = _lambda; + if(FAllEq(_lambda, zero) && initialOverlap) + { + + //time of impact is zero, the sweep shape is intesect, use epa to get the normal and contact point + const FloatV contactDist = getSweepContactEps(a.getMargin(), b.getMargin()); + + Vec3V closAA; + Vec3V closBB; + FloatV sDist; + PxU8 aIndices[4]; + PxU8 bIndices[4]; + PxU8 size=0; + + //PX_COMPILE_TIME_ASSERT(typename Shrink<ConvexB>::Type != Gu::BoxV); + + typename ConvexA::ShrunkConvex convexA = a.getShrunkConvex(); + typename ConvexB::ShrunkConvex convexB = b.getShrunkConvex(); + + + GjkStatus status = gjkPenetration<typename ConvexA::ShrunkConvex, typename ConvexB::ShrunkConvex>(convexA, convexB, + initialDir, contactDist, closAA, closBB, norm, sDist, aIndices, bIndices, size, false); + //norm = V3Neg(norm); + if(status == GJK_CONTACT) + { + closA = closAA;//V3Sel(aQuadratic, V3Add(closAA, V3Scale(norm, a.getMargin())),closAA); + } + else if(status == EPA_CONTACT) + { + status = epaPenetration(a, b, aIndices, bIndices, size, closAA, closBB, norm, sDist); + + if (status == EPA_CONTACT || status == EPA_DEGENERATE) + { + closA = closAA; + } + else + { + //ML: if EPA fail, we will use the ray direction as the normal and set pentration to be zero + norm = V3Normalize(V3Neg(r)); + closA = V3Zero(); + sDist = zero;//FNeg(V3Length(V3Sub(closBB, closAA))); + } + } + else + { + //ML:: this will be gjk degenerate case, we will take the normal and sDist from the gjkRelativePenetration + closA = closAA; + } + lambda = FMin(zero, sDist); + } + closestA = closA; + normal = norm; + return true; + } + return false; + } +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKSimplex.cpp b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKSimplex.cpp new file mode 100644 index 00000000..e74e0d03 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKSimplex.cpp @@ -0,0 +1,216 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKSimplex.h" + +namespace physx +{ +namespace Gu +{ + + using namespace Ps::aos; + + static Vec3V getClosestPtPointTriangle(Vec3V* PX_RESTRICT Q, const BoolVArg bIsOutside4, PxU32* indices, PxU32& size) + { + FloatV bestSqDist = FMax(); + + PxU32 _indices[3] = {0, 1, 2}; + + Vec3V closestPt = V3Zero(); + + if(BAllEqTTTT(BGetX(bIsOutside4))) + { + //use the original indices, size, v and w + bestSqDist = closestPtPointTriangleBaryCentric(Q[0], Q[1], Q[2], indices, size, closestPt); + } + + if(BAllEqTTTT(BGetY(bIsOutside4))) + { + + PxU32 _size = 3; + _indices[0] = 0; _indices[1] = 2; _indices[2] = 3; + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[0], Q[2], Q[3], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + } + + if(BAllEqTTTT(BGetZ(bIsOutside4))) + { + PxU32 _size = 3; + + _indices[0] = 0; _indices[1] = 3; _indices[2] = 1; + + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[0], Q[3], Q[1], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + + } + + if(BAllEqTTTT(BGetW(bIsOutside4))) + { + + + PxU32 _size = 3; + _indices[0] = 1; _indices[1] = 3; _indices[2] = 2; + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[1], Q[3], Q[2], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + } + + return closestPt; + } + + PX_NOALIAS Vec3V closestPtPointTetrahedron(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxU32& size) + { + + const FloatV eps = FLoad(1e-4f); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + + //degenerated + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V n = V3Normalize(V3Cross(ab, ac)); + const FloatV signDist = V3Dot(n, V3Sub(d, a)); + if(FAllGrtr(eps, FAbs(signDist))) + { + size = 3; + return closestPtPointTriangle(Q, A, B, size); + } + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if(BAllEqFFFF(bIsOutside4)) + { + //All inside + return V3Zero(); + } + + PxU32 indices[3] = {0, 1, 2}; + + const Vec3V closest = getClosestPtPointTriangle(Q, bIsOutside4, indices, size); + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; const Vec3V q2 = Q[indices[2]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; const Vec3V a2 = A[indices[2]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; const Vec3V b2 = B[indices[2]]; + Q[0] = q0; Q[1] = q1; Q[2] = q2; + A[0] = a0; A[1] = a1; A[2] = a2; + B[0] = b0; B[1] = b1; B[2] = b2; + + return closest; + } + + PX_NOALIAS Vec3V closestPtPointTetrahedron(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32& size) + { + + const FloatV eps = FLoad(1e-4f); + const Vec3V zeroV = V3Zero(); + + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + + //degenerated + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V n = V3Normalize(V3Cross(ab, ac)); + const FloatV signDist = V3Dot(n, V3Sub(d, a)); + if(FAllGrtr(eps, FAbs(signDist))) + { + size = 3; + return closestPtPointTriangle(Q, A, B, aInd, bInd, size); + } + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if(BAllEqFFFF(bIsOutside4)) + { + //All inside + return zeroV; + } + + PxU32 indices[3] = {0, 1, 2}; + const Vec3V closest = getClosestPtPointTriangle(Q, bIsOutside4, indices, size); + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; const Vec3V q2 = Q[indices[2]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; const Vec3V a2 = A[indices[2]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; const Vec3V b2 = B[indices[2]]; + const PxI32 _aInd0 = aInd[indices[0]]; const PxI32 _aInd1 = aInd[indices[1]]; const PxI32 _aInd2 = aInd[indices[2]]; + const PxI32 _bInd0 = bInd[indices[0]]; const PxI32 _bInd1 = bInd[indices[1]]; const PxI32 _bInd2 = bInd[indices[2]]; + Q[0] = q0; Q[1] = q1; Q[2] = q2; + A[0] = a0; A[1] = a1; A[2] = a2; + B[0] = b0; B[1] = b1; B[2] = b2; + aInd[0] = _aInd0; aInd[1] = _aInd1; aInd[2] = _aInd2; + bInd[0] = _bInd0; bInd[1] = _bInd1; bInd[2] = _bInd2; + + return closest; + } +} + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKSimplex.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKSimplex.h new file mode 100644 index 00000000..05ae2c33 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKSimplex.h @@ -0,0 +1,473 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKSIMPLEX_H +#define GU_GJKSIMPLEX_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "GuBarycentricCoordinates.h" + +#if (defined __GNUC__ && defined _DEBUG) +#define PX_GJK_INLINE PX_INLINE +#define PX_GJK_FORCE_INLINE PX_INLINE +#else +#define PX_GJK_INLINE PX_INLINE +#define PX_GJK_FORCE_INLINE PX_FORCE_INLINE +#endif + + +namespace physx +{ +namespace Gu +{ + + + PX_NOALIAS Ps::aos::Vec3V closestPtPointTetrahedron(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxU32& size); + + PX_NOALIAS Ps::aos::Vec3V closestPtPointTetrahedron(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + PxU32& size); + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::BoolV PointOutsideOfPlane4(const Ps::aos::Vec3VArg _a, const Ps::aos::Vec3VArg _b, const Ps::aos::Vec3VArg _c, const Ps::aos::Vec3VArg _d) + { + using namespace Ps::aos; + + // this is not 0 because of the following scenario: + // All the points lie on the same plane and the plane goes through the origin (0,0,0). + // On the Wii U, the math below has the problem that when point A gets projected on the + // plane cumputed by A, B, C, the distance to the plane might not be 0 for the mentioned + // scenario but a small positive or negative value. This can lead to the wrong boolean + // results. Using a small negative value as threshold is more conservative but safer. + const Vec4V zero = V4Load(-1e-6); + + const Vec3V ab = V3Sub(_b, _a); + const Vec3V ac = V3Sub(_c, _a); + const Vec3V ad = V3Sub(_d, _a); + const Vec3V bd = V3Sub(_d, _b); + const Vec3V bc = V3Sub(_c, _b); + + const Vec3V v0 = V3Cross(ab, ac); + const Vec3V v1 = V3Cross(ac, ad); + const Vec3V v2 = V3Cross(ad, ab); + const Vec3V v3 = V3Cross(bd, bc); + + const FloatV signa0 = V3Dot(v0, _a); + const FloatV signa1 = V3Dot(v1, _a); + const FloatV signa2 = V3Dot(v2, _a); + const FloatV signd3 = V3Dot(v3, _a); + + const FloatV signd0 = V3Dot(v0, _d); + const FloatV signd1 = V3Dot(v1, _b); + const FloatV signd2 = V3Dot(v2, _c); + const FloatV signa3 = V3Dot(v3, _b); + + const Vec4V signa = V4Merge(signa0, signa1, signa2, signa3); + const Vec4V signd = V4Merge(signd0, signd1, signd2, signd3); + return V4IsGrtrOrEq(V4Mul(signa, signd), zero);//same side, outside of the plane + + + } + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V closestPtPointSegment(Ps::aos::Vec3V* PX_RESTRICT Q, PxU32& size) + { + using namespace Ps::aos; + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + + //const Vec3V origin = V3Zero(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsGrtrOrEq(FEps(), denom);//FIsEq(denom, zero); + //TODO - can we get rid of this branch? The problem is size, which isn't a vector! + if(BAllEqTTTT(con)) + { + size = 1; + return Q[0]; + } + + /* const PxU32 count = BAllEq(con, bTrue); + size = 2 - count;*/ + + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + return V3ScaleAdd(ab, tValue, a); + } + + + PX_FORCE_INLINE void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT Q, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const Ps::aos::Vec3VArg closest, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, const PxU32 size) + { + using namespace Ps::aos; + + switch(size) + { + case 1: + { + closestA = A[0]; + closestB = B[0]; + break; + } + case 2: + { + FloatV v; + barycentricCoordinates(closest, Q[0], Q[1], v); + const Vec3V av = V3Sub(A[1], A[0]); + const Vec3V bv = V3Sub(B[1], B[0]); + closestA = V3ScaleAdd(av, v, A[0]); + closestB = V3ScaleAdd(bv, v, B[0]); + + break; + } + case 3: + { + //calculate the Barycentric of closest point p in the mincowsky sum + FloatV v, w; + barycentricCoordinates(closest, Q[0], Q[1], Q[2], v, w); + + const Vec3V av0 = V3Sub(A[1], A[0]); + const Vec3V av1 = V3Sub(A[2], A[0]); + const Vec3V bv0 = V3Sub(B[1], B[0]); + const Vec3V bv1 = V3Sub(B[2], B[0]); + + closestA = V3Add(A[0], V3Add(V3Scale(av0, v), V3Scale(av1, w))); + closestB = V3Add(B[0], V3Add(V3Scale(bv0, v), V3Scale(bv1, w))); + } + }; + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::FloatV closestPtPointTriangleBaryCentric(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + PxU32* PX_RESTRICT indices, PxU32& size, Ps::aos::Vec3V& closestPt) + { + using namespace Ps::aos; + + size = 3; + const FloatV zero = FZero(); + const FloatV eps = FEps(); + + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + + const Vec3V n = V3Cross(ab, ac); + //ML: if the shape is oblong, the degeneracy test sometime can't catch the degeneracy in the tetraheron. Therefore, we need to make sure we still can ternimate with the previous + //triangle by returning the maxinum distance. + const FloatV nn = V3Dot(n, n); + if (FAllEq(nn, zero)) + return FMax(); + + //const FloatV va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + //const FloatV vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + //const FloatV vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + //const FloatV va = V3Dot(n, V3Cross(b, c));//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + //const FloatV vb = V3Dot(n, V3Cross(c, a));//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + //const FloatV vc = V3Dot(n, V3Cross(a, b));//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + const VecCrossV crossA = V3PrepareCross(a); + const VecCrossV crossB = V3PrepareCross(b); + const VecCrossV crossC = V3PrepareCross(c); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + const BoolV isFacePoints = BAnd(FIsGrtrOrEq(va, zero), BAnd(FIsGrtrOrEq(vb, zero), FIsGrtrOrEq(vc, zero))); + + + //face region + if(BAllEqTTTT(isFacePoints)) + { + const FloatV t = FDiv(V3Dot(n, a), nn); + const Vec3V q = V3Scale(n, t); + closestPt = q; + return V3Dot(q, q); + } + + const Vec3V ap = V3Neg(a); + const Vec3V bp = V3Neg(b); + const Vec3V cp = V3Neg(c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + + + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + size = 2; + //check if p in edge region of AB + const BoolV con30 = FIsGrtrOrEq(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtrOrEq(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32));//edge AB region + if(BAllEqTTTT(con3)) + { + const FloatV toRecipAB = FSub(d1, d3); + const FloatV recipAB = FSel(FIsGrtr(FAbs(toRecipAB), eps), FRecip(toRecipAB), zero); + const FloatV t = FMul(d1, recipAB); + const Vec3V q = V3ScaleAdd(ab, t, a); + closestPt = q; + return V3Dot(q, q); + } + + //check if p in edge region of BC + const BoolV con40 = FIsGrtrOrEq(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); //edge BC region + if(BAllEqTTTT(con4)) + { + const Vec3V bc = V3Sub(c, b); + const FloatV toRecipBC = FAdd(unom, udenom); + const FloatV recipBC = FSel(FIsGrtr(FAbs(toRecipBC), eps), FRecip(toRecipBC), zero); + const FloatV t = FMul(unom, recipBC); + indices[0] = indices[1]; + indices[1] = indices[2]; + const Vec3V q = V3ScaleAdd(bc, t, b); + closestPt = q; + return V3Dot(q, q); + } + + //check if p in edge region of AC + const BoolV con50 = FIsGrtrOrEq(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtrOrEq(zero, d6); + + const BoolV con5 = BAnd(con50, BAnd(con51, con52));//edge AC region + if(BAllEqTTTT(con5)) + { + const FloatV toRecipAC = FSub(d2, d6); + const FloatV recipAC = FSel(FIsGrtr(FAbs(toRecipAC), eps), FRecip(toRecipAC), zero); + const FloatV t = FMul(d2, recipAC); + indices[1]=indices[2]; + const Vec3V q = V3ScaleAdd(ac, t, a); + closestPt = q; + return V3Dot(q, q); + } + + size = 1; + //check if p in vertex region outside a + const BoolV con00 = FIsGrtrOrEq(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtrOrEq(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + if(BAllEqTTTT(con0)) + { + closestPt = a; + return V3Dot(a, a); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + indices[0] = indices[1]; + closestPt = b; + return V3Dot(b, b); + } + + //p is in vertex region outside c + indices[0] = indices[2]; + closestPt = c; + return V3Dot(c, c); + + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::Vec3V closestPtPointTriangle(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* A, Ps::aos::Vec3V* B, PxU32& size) + { + + using namespace Ps::aos; + + size = 3; + + const FloatV eps = FEps(); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V signArea = V3Cross(ab, ac);//0.5*(abXac) + const FloatV area = V3Dot(signArea, signArea); + if(FAllGrtrOrEq(eps, area)) + { + //degenerate + size = 2; + return closestPtPointSegment(Q, size); + } + + PxU32 _size; + PxU32 indices[3]={0, 1, 2}; + Vec3V closestPt; + closestPtPointTriangleBaryCentric(a, b, c, indices, _size, closestPt); + + if(_size != 3) + { + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; + + Q[0] = q0; Q[1] = q1; + A[0] = a0; A[1] = a1; + B[0] = b0; B[1] = b1; + + size = _size; + } + + return closestPt; + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::Vec3V closestPtPointTriangle(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* A, Ps::aos::Vec3V* B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + PxU32& size) + { + + using namespace Ps::aos; + + size = 3; + + const FloatV eps = FEps(); + + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V signArea = V3Cross(ab, ac);//0.5*(abXac) + const FloatV area = V3Dot(signArea, signArea); + if(FAllGrtrOrEq(eps, area)) + { + //degenerate + size = 2; + return closestPtPointSegment(Q, size); + } + + PxU32 _size; + PxU32 indices[3]={0, 1, 2}; + Vec3V closestPt; + closestPtPointTriangleBaryCentric(a, b, c, indices, _size, closestPt); + + if(_size != 3) + { + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; + const PxI32 aInd0 = aInd[indices[0]]; const PxI32 aInd1 = aInd[indices[1]]; + const PxI32 bInd0 = bInd[indices[0]]; const PxI32 bInd1 = bInd[indices[1]]; + + Q[0] = q0; Q[1] = q1; + A[0] = a0; A[1] = a1; + B[0] = b0; B[1] = b1; + aInd[0] = aInd0; aInd[1] = aInd1; + bInd[0] = bInd0; bInd[1] = bInd1; + + size = _size; + } + + return closestPt; + } + + + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V GJKCPairDoSimplex(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, const Ps::aos::Vec3VArg support, + PxU32& size) + { + using namespace Ps::aos; + + //const PxU32 tempSize = size; + //calculate a closest from origin to the simplex + switch(size) + { + case 1: + { + return support; + } + case 2: + { + return closestPtPointSegment(Q, size); + } + case 3: + { + return closestPtPointTriangle(Q, A, B, size); + } + case 4: + return closestPtPointTetrahedron(Q, A, B, size); + default: + PX_ASSERT(0); + } + return support; + } + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V GJKCPairDoSimplex(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + const Ps::aos::Vec3VArg support, PxU32& size) + { + using namespace Ps::aos; + + //const PxU32 tempSize = size; + //calculate a closest from origin to the simplex + switch(size) + { + case 1: + { + return support; + } + case 2: + { + return closestPtPointSegment(Q, size); + } + case 3: + { + return closestPtPointTriangle(Q, A, B, aInd, bInd, size); + } + case 4: + return closestPtPointTetrahedron(Q, A, B, aInd, bInd, size); + default: + PX_ASSERT(0); + } + return support; + } +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKTest.cpp b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKTest.cpp new file mode 100644 index 00000000..530634c2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKTest.cpp @@ -0,0 +1,92 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJK.h" +#include "GuGJKRaycast.h" +#include "GuGJKPenetration.h" +#include "GuGJKTest.h" + +namespace physx +{ +namespace Gu +{ + +using namespace Ps::aos; + +GjkStatus testGjk(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& dist) +{ + return gjk<GjkConvex, GjkConvex>(a, b, initialSearchDir, contactDist, closestA, closestB, normal, dist); +} + + +bool testGjkRaycast(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, + Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal inflation) +{ + return gjkRaycast(a, b, initialSearchDir, initialLambda, s, r, lambda, normal, closestA, inflation); +} + +GjkStatus testGjkPenetration (GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& sqDist) +{ + const bool takeCoreShape = a.getMarginIsRadius() || b.getMarginIsRadius(); + PxU8 aIndices[4]; + PxU8 bIndices[4]; + PxU8 size=0; + return gjkPenetration<GjkConvex, GjkConvex>(a, b, initialSearchDir, contactDist, closestA, closestB, normal, + sqDist, aIndices, bIndices, size, takeCoreShape); +} + +GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& sqDist, + PxU8* aIndices, PxU8* bIndices, PxU8 size) +{ + const bool takeCoreShape = a.getMarginIsRadius() || b.getMarginIsRadius(); + return gjkPenetration<GjkConvex, GjkConvex>(a, b, initialSearchDir, contactDist, closestA, closestB, normal, + sqDist, aIndices, bIndices, size, takeCoreShape); +} + +GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, Ps::aos::Vec3V& contactA, Ps::aos::Vec3V& contactB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& penetrationDepth) +{ + const bool takeCoreShape = a.getMarginIsRadius() || b.getMarginIsRadius(); + PxU8 aIndices[4]; + PxU8 bIndices[4]; + PxU8 size=0; + return epaPenetration(a, b, aIndices, bIndices, size, contactA, contactB, normal, penetrationDepth, takeCoreShape); +} + +GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, Ps::aos::Vec3V& contactA, Ps::aos::Vec3V& contactB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& penetrationDepth, PxU8* aIndices, PxU8* bIndices, PxU8 size) +{ + const bool takeCoreShape = a.getMarginIsRadius() || b.getMarginIsRadius(); + return epaPenetration(a, b, aIndices, bIndices, size, contactA, contactB, normal, penetrationDepth, takeCoreShape); +} + +} +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKTest.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKTest.h new file mode 100644 index 00000000..f32690c7 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKTest.h @@ -0,0 +1,64 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_TEST_H +#define GU_GJK_TEST_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "GuGJKUtil.h" + +namespace physx +{ +namespace Gu +{ + struct GjkConvex; + + + PX_PHYSX_COMMON_API GjkStatus testGjk(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, + Ps::aos::Vec3V& normal, Ps::aos::FloatV& dist); + + PX_PHYSX_COMMON_API bool testGjkRaycast(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, + Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap); + + PX_PHYSX_COMMON_API GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& closestA, + Ps::aos::Vec3V& closestB, Ps::aos::Vec3V& normal, Ps::aos::FloatV& sqDist); + + PX_PHYSX_COMMON_API GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& closestA, + Ps::aos::Vec3V& closestB, Ps::aos::Vec3V& normal, Ps::aos::FloatV& sqDist, PxU8* aIndices, PxU8* bIndices, PxU8 size); + + PX_PHYSX_COMMON_API GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, Ps::aos::Vec3V& contactA, Ps::aos::Vec3V& contactB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& penetrationDepth); + + PX_PHYSX_COMMON_API GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, Ps::aos::Vec3V& contactA, Ps::aos::Vec3V& contactB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& penetrationDepth, PxU8* aIndices, PxU8* bIndices, PxU8 size); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKType.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKType.h new file mode 100644 index 00000000..dc823e0a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKType.h @@ -0,0 +1,175 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKTYPE_H +#define GU_GJKTYPE_H + +#include "GuVecConvex.h" +#include "PsVecTransform.h" + +namespace physx +{ +namespace Gu +{ + class ShrunkBoxV; + class ShrunkConvexHullV; + class ShrunkConvexHullNoScaleV; + class ConvexHullV; + class ConvexHullNoScaleV; + class BoxV; + + template <typename Convex> struct Shrink { typedef Convex Type; }; + template <> struct Shrink<ConvexHullV> { typedef ShrunkConvexHullV Type; }; + template <> struct Shrink<ConvexHullNoScaleV> { typedef ShrunkConvexHullNoScaleV Type; }; + template <> struct Shrink<BoxV> { typedef ShrunkBoxV Type; }; + + struct GjkConvexBase + { + + GjkConvexBase(const ConvexV& convex) : mConvex(convex){} + PX_FORCE_INLINE Ps::aos::FloatV getMinMargin() const { return mConvex.getMinMargin(); } + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius() const { return mConvex.isMarginEqRadius(); } + PX_FORCE_INLINE bool getMarginIsRadius() const { return mConvex.getMarginIsRadius(); } + PX_FORCE_INLINE Ps::aos::FloatV getMargin() const { return mConvex.getMargin(); } + PX_FORCE_INLINE Ps::aos::Vec3V getCenter() const { return mConvex.getCenter(); } + + template <typename Convex> + PX_FORCE_INLINE const Convex& getConvex() const { return static_cast<const Convex&>(mConvex); } + + virtual Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif) const = 0; + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const = 0; + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const = 0; + virtual Ps::aos::FloatV getSweepMargin() const = 0; + virtual ~GjkConvexBase(){} + + + private: + GjkConvexBase& operator = (const GjkConvexBase&); + protected: + const ConvexV& mConvex; + }; + + struct GjkConvex : public GjkConvexBase + { + GjkConvex(const ConvexV& convex) : GjkConvexBase(convex) { } + + virtual Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif) const { return doVirtualSupportPoint(index, marginDif); } + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return doVirtualSupport(v); } + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const{ return doVirtualSupport(dir, index, marginDif); } + + virtual Ps::aos::FloatV getSweepMargin() const { return doVirtualGetSweepMargin(); } + + private: + Ps::aos::Vec3V doVirtualSupportPoint(const PxI32 index,Ps::aos::FloatV* marginDif) const + { + return supportPoint(index, marginDif); //Call the v-table + } + Ps::aos::Vec3V doVirtualSupport(const Ps::aos::Vec3VArg v) const + { + return support(v); //Call the v-table + } + Ps::aos::Vec3V doVirtualSupport(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const + { + return support(dir, index, marginDif); //Call the v-table + } + + Ps::aos::FloatV doVirtualGetSweepMargin() const { return getSweepMargin(); } + + GjkConvex& operator = (const GjkConvex&); + }; + + template <typename Convex> + struct LocalConvex : public GjkConvex + { + LocalConvex(const Convex& convex) : GjkConvex(convex){} + + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif) const { return getConvex<Convex>().supportPoint(index, marginDif); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return getConvex<Convex>().supportLocal(v); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const + { + return getConvex<Convex>().supportLocal(dir, index, marginDif); + } + + //ML: we can't force inline function, otherwise win modern will throw compiler error + PX_INLINE LocalConvex<typename Shrink<Convex>::Type > getShrunkConvex() const + { + return LocalConvex<typename Shrink<Convex>::Type >(static_cast<const typename Shrink<Convex>::Type&>(GjkConvex::mConvex)); + } + + PX_INLINE Ps::aos::FloatV getSweepMargin() const { return getConvex<Convex>().getSweepMargin(); } + + typedef LocalConvex<typename Shrink<Convex>::Type > ShrunkConvex; + + typedef Convex Type; + + + private: + LocalConvex<Convex>& operator = (const LocalConvex<Convex>&); + }; + + template <typename Convex> + struct RelativeConvex : public GjkConvex + { + RelativeConvex(const Convex& convex, const Ps::aos::PsMatTransformV& aToB) : GjkConvex(convex), mAToB(aToB), mAToBTransposed(aToB) + { + shdfnd::aos::V3Transpose(mAToBTransposed.rot.col0, mAToBTransposed.rot.col1, mAToBTransposed.rot.col2); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif) const { return mAToB.transform(getConvex<Convex>().supportPoint(index, marginDif)); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return getConvex<Convex>().supportRelative(v, mAToB, mAToBTransposed); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const + { + return getConvex<Convex>().supportRelative(dir, mAToB, mAToBTransposed, index, marginDif); + } + + PX_FORCE_INLINE Ps::aos::PsMatTransformV& getRelativeTransform(){ return mAToB; } + + //ML: we can't force inline function, otherwise win modern will throw compiler error + PX_INLINE RelativeConvex<typename Shrink<Convex>::Type > getShrunkConvex() const + { + return RelativeConvex<typename Shrink<Convex>::Type >(static_cast<const typename Shrink<Convex>::Type&>(GjkConvex::mConvex), mAToB); + } + + PX_INLINE Ps::aos::FloatV getSweepMargin() const { return getConvex<Convex>().getSweepMargin(); } + + typedef RelativeConvex<typename Shrink<Convex>::Type > ShrunkConvex; + + typedef Convex Type; + + private: + RelativeConvex<Convex>& operator = (const RelativeConvex<Convex>&); + const Ps::aos::PsMatTransformV& mAToB; + Ps::aos::PsMatTransformV mAToBTransposed; // PT: precomputed mAToB transpose (because 'rotate' is faster than 'rotateInv') + }; + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKUtil.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKUtil.h new file mode 100644 index 00000000..aa463e78 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuGJKUtil.h @@ -0,0 +1,57 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKUTIL_H +#define GU_GJKUTIL_H + +/* + This file is used to avoid the inner loop cross DLL calls +*/ +namespace physx +{ +namespace Gu +{ + +enum GjkStatus +{ + GJK_NON_INTERSECT, // two shapes doesn't intersect + GJK_CLOSE, // two shapes doesn't intersect and gjk algorithm will return closest point information + GJK_CONTACT, // two shapes overlap within margin + GJK_UNDEFINED, // undefined status + GJK_DEGENERATE, // gjk can't converage + + EPA_CONTACT, // two shapes intersect + EPA_DEGENERATE, // epa can't converage + EPA_FAIL // epa fail to construct an initial polygon to work with +}; + +}//Gu +}//physx + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecBox.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecBox.h new file mode 100644 index 00000000..15f3b28d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecBox.h @@ -0,0 +1,217 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_BOX_H +#define GU_VEC_BOX_H + +/** \addtogroup geomutils +@{ +*/ +#include "foundation/PxTransform.h" +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "GuConvexSupportTable.h" +#include "PxBoxGeometry.h" + +namespace physx +{ +PX_PHYSX_COMMON_API extern const Ps::aos::BoolV boxVertexTable[8]; + +namespace Gu +{ + +#define BOX_MARGIN_RATIO 0.15f +#define BOX_MIN_MARGIN_RATIO 0.05f +#define BOX_SWEEP_MARGIN_RATIO 0.05f + +#define BOX_MARGIN_CCD_RATIO 0.01f +#define BOX_MIN_MARGIN_CCD_RATIO 0.005f + + + class CapsuleV; + + + PX_FORCE_INLINE void CalculateBoxMargin(const Ps::aos::Vec3VArg extent, PxReal& margin, PxReal& minMargin, PxReal& sweepMargin, + const PxReal marginR = BOX_MARGIN_RATIO, const PxReal minMarginR = BOX_MIN_MARGIN_RATIO) + { + using namespace Ps::aos; + + const FloatV min = V3ExtractMin(extent); + + const FloatV margin_ = FMul(min, FLoad(marginR)); + const FloatV minMargin_ = FMul(min, FLoad(minMarginR)); + const FloatV sweepMargin_ = FMul(min, FLoad(BOX_SWEEP_MARGIN_RATIO)); + + FStore(margin_, &margin); + FStore(minMargin_, &minMargin); + FStore(sweepMargin_, &sweepMargin); + } + + PX_FORCE_INLINE Ps::aos::FloatV CalculateBoxTolerance(const Ps::aos::Vec3VArg extent) + { + using namespace Ps::aos; + + const FloatV r0 = FLoad(0.01f); + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + return FMul(min, r0); + } + + //This method is called in the PCM contact gen for the refreshing contacts + PX_FORCE_INLINE Ps::aos::FloatV CalculatePCMBoxMargin(const Ps::aos::Vec3VArg extent) + { + using namespace Ps::aos; + + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + return FMul(min, FLoad(BOX_MARGIN_RATIO)); + } + + class BoxV : public ConvexV + { + public: + + /** + \brief Constructor + */ + PX_INLINE BoxV() : ConvexV(ConvexType::eBOX) + { + } + + PX_FORCE_INLINE BoxV(const Ps::aos::Vec3VArg origin, const Ps::aos::Vec3VArg extent) : + ConvexV(ConvexType::eBOX, origin), extents(extent) + { + CalculateBoxMargin(extent, margin, minMargin, sweepMargin); + marginDif = Ps::aos::FZero(); + } + + PX_FORCE_INLINE BoxV(const PxGeometry& geom) : ConvexV(ConvexType::eBOX, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + const Vec3V extent = Ps::aos::V3LoadU(boxGeom.halfExtents); + extents = extent; + CalculateBoxMargin(extent, margin, minMargin, sweepMargin, BOX_MARGIN_CCD_RATIO, BOX_MIN_MARGIN_CCD_RATIO); + marginDif = Ps::aos::FZero(); + } + + /** + \brief Destructor + */ + PX_INLINE ~BoxV() + { + } + + //! Assignment operator + PX_INLINE const BoxV& operator=(const BoxV& other) + { + center = other.center; + extents = other.extents; + margin = other.margin; + minMargin = other.minMargin; + marginDif = other.marginDif; + sweepMargin = other.sweepMargin; + return *this; + } + + PX_FORCE_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i<numInds; ++i) + verts[i] = V3LoadU_SafeReadW(originalVerts[inds[i]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'populateVerts' is always called with polyData.mVerts) + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* /*marginDif*/)const + { + using namespace Ps::aos; + const BoolV con = boxVertexTable[index]; + return V3Sel(con, extents, V3Neg(extents)); + } + + PX_FORCE_INLINE void getIndex(const Ps::aos::BoolV con, PxI32& index)const + { + using namespace Ps::aos; + index = PxI32(BGetBitMask(con) & 0x7); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + return V3Sel(V3IsGrtr(dir, V3Zero()), extents, V3Neg(extents)); + } + + //this is used in the sat test for the full contact gen + PX_SUPPORT_INLINE void supportLocal(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + const Vec3V point = V3Sel(V3IsGrtr(dir, V3Zero()), extents, V3Neg(extents)); + max = V3Dot(dir, point); + min = FNeg(max); + } + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, const Ps::aos::PsMatTransformV& aTobT) const + { + //a is the current object, b is the other object, dir is in the local space of b + using namespace Ps::aos; +// const Vec3V _dir = aTob.rotateInv(dir);//relTra.rotateInv(dir);//from b to a + const Vec3V _dir = aTobT.rotate(dir);//relTra.rotateInv(dir);//from b to a + const Vec3V p = supportLocal(_dir); + //transfer p into the b space + return aTob.transform(p);//relTra.transform(p); + } + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* /*marginDif_*/)const + { + using namespace Ps::aos; + const BoolV comp = V3IsGrtr(dir, V3Zero()); + getIndex(comp, index); + return V3Sel(comp, extents, V3Neg(extents)); + } + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif_)const + { + //a is the current object, b is the other object, dir is in the local space of b + using namespace Ps::aos; +// const Vec3V _dir = aTob.rotateInv(dir);//relTra.rotateInv(dir);//from b to a + const Vec3V _dir = aTobT.rotate(dir);//relTra.rotateInv(dir);//from b to a + const Vec3V p = supportLocal(_dir, index, marginDif_); + //transfer p into the b space + return aTob.transform(p);//relTra.transform(p); + } + + Ps::aos::Vec3V extents; + Ps::aos::FloatV marginDif; + }; +} //PX_COMPILE_TIME_ASSERT(sizeof(Gu::BoxV) == 96); + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecCapsule.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecCapsule.h new file mode 100644 index 00000000..e6c7d53c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecCapsule.h @@ -0,0 +1,256 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CAPSULE_H +#define GU_VEC_CAPSULE_H + +/** \addtogroup geomutils +@{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "PxCapsuleGeometry.h" + + +namespace physx +{ +namespace Gu +{ + + PX_FORCE_INLINE Ps::aos::FloatV CalculateCapsuleMinMargin(const Ps::aos::FloatVArg radius) + { + using namespace Ps::aos; + const FloatV ratio = Ps::aos::FLoad(0.05f); + return FMul(radius, ratio); + } + + class CapsuleV : public ConvexV + { + public: + /** + \brief Constructor + */ + + PX_INLINE CapsuleV():ConvexV(ConvexType::eCAPSULE) + { + bMarginIsRadius = true; + } + + //constructor for sphere + PX_INLINE CapsuleV(const Ps::aos::Vec3VArg p, const Ps::aos::FloatVArg radius_) : ConvexV(ConvexType::eCAPSULE) + { + using namespace Ps::aos; + center = p; + radius = radius_; + p0 = p; + p1 = p; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + PX_INLINE CapsuleV(const Ps::aos::Vec3VArg center_, const Ps::aos::Vec3VArg v_, const Ps::aos::FloatVArg radius_) : + ConvexV(ConvexType::eCAPSULE, center_) + { + using namespace Ps::aos; + radius = radius_; + p0 = V3Add(center_, v_); + p1 = V3Sub(center_, v_); + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + PX_INLINE CapsuleV(const PxGeometry& geom) : ConvexV(ConvexType::eCAPSULE, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + const Vec3V axis = V3Scale(V3UnitX(), FLoad(capsuleGeom.halfHeight)); + const FloatV r = FLoad(capsuleGeom.radius); + p0 = axis; + p1 = V3Neg(axis); + radius = r; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + /** + \brief Constructor + + \param _radius Radius of the capsule. + */ + + /** + \brief Destructor + */ + PX_INLINE ~CapsuleV() + { + } + + PX_FORCE_INLINE void initialize(const Ps::aos::Vec3VArg _p0, const Ps::aos::Vec3VArg _p1, const Ps::aos::FloatVArg _radius) + { + using namespace Ps::aos; + radius = _radius; + p0 = _p0; + p1 = _p1; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + center = V3Scale(V3Add(_p0, _p1), FHalf()); + } + + PX_INLINE Ps::aos::Vec3V computeDirection() const + { + return Ps::aos::V3Sub(p1, p0); + } + + PX_FORCE_INLINE Ps::aos::FloatV getRadius() const + { + return radius; + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* /*marginDif*/)const + { + return (&p0)[1-index]; + } + + PX_FORCE_INLINE void getIndex(const Ps::aos::BoolV con, PxI32& index)const + { + using namespace Ps::aos; + const VecI32V v = VecI32V_From_BoolV(con); + const VecI32V t = VecI32V_And(v, VecI32V_One()); + PxI32_From_VecI32V(t, &index); + } + + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + using namespace Ps::aos; + Vec3V offset = V3Sub(_center, center); + center = _center; + + p0 = V3Add(p0, offset); + p1 = V3Add(p1, offset); + } + + //dir, p0 and p1 are in the local space of dir + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + //const Vec3V _dir = V3Normalize(dir); + const FloatV dist0 = V3Dot(p0, dir); + const FloatV dist1 = V3Dot(p1, dir); + return V3Sel(FIsGrtr(dist0, dist1), p0, p1); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + //transform dir into the local space of a +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + const Vec3V p = supportLocal(_dir); + //transform p back to the local space of b + return aToB.transform(p); + } + + //dir, p0 and p1 are in the local space of dir + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* /*marginDif*/)const + { + using namespace Ps::aos; + + const FloatV dist0 = V3Dot(p0, dir); + const FloatV dist1 = V3Dot(p1, dir); + const BoolV comp = FIsGrtr(dist0, dist1); + getIndex(comp, index); + return V3Sel(comp, p0, p1); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif)const + { + using namespace Ps::aos; + //transform dir into the local space of a +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + + const Vec3V p = supportLocal(_dir, index, marginDif); + //transform p back to the local space of b + return aToB.transform(p); + } + + //PX_FORCE_INLINE Ps::aos::BoolV supportLocalIndex(const Ps::aos::Vec3VArg dir, PxI32& index)const + //{ + // using namespace Ps::aos; + // //scale dir and put it in the vertex space + // const FloatV dist0 = V3Dot(p0, dir); + // const FloatV dist1 = V3Dot(p1, dir); + // const BoolV comp = FIsGrtr(dist0, dist1); + // getIndex(comp, index); + // return comp; + //} + + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(Ps::aos::Vec3V& support, const PxI32& index, const Ps::aos::BoolV comp)const + { + PX_UNUSED(index); + + using namespace Ps::aos; + const Vec3V p = V3Sel(comp, p0, p1); + support = p; + return p; + } + + //PX_FORCE_INLINE Ps::aos::BoolV supportRelativeIndex(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, PxI32& index)const + //{ + // using namespace Ps::aos; + // //scale dir and put it in the vertex space + // const Vec3V _dir =aTob.rotateInv(dir);//relTra.rotateInv(dir); + // return supportLocalIndex(_dir, index); + //} + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FZero(); + } + + //don't change the order of p0 and p1, the getPoint function depend on the order + Ps::aos::Vec3V p0; //!< Start of segment + Ps::aos::Vec3V p1; //!< End of segment + Ps::aos::FloatV radius; + }; +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvex.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvex.h new file mode 100644 index 00000000..2ba97446 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvex.h @@ -0,0 +1,173 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEX_H +#define GU_VEC_CONVEX_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +#define PX_SUPPORT_INLINE PX_FORCE_INLINE +#define PX_SUPPORT_FORCE_INLINE PX_FORCE_INLINE + +namespace physx +{ +namespace Gu +{ + + struct ConvexType + { + enum Type + { + eCONVEXHULL = 0, + eCONVEXHULLNOSCALE = 1, + eSPHERE = 2, + eBOX = 3, + eCAPSULE = 4, + eTRIANGLE = 5 + }; + }; + + class ConvexV + { + public: + + PX_FORCE_INLINE ConvexV(const ConvexType::Type type_) : type(type_), bMarginIsRadius(false) + { + margin = 0.f; + minMargin = 0.f; + sweepMargin = 0.f; + center = Ps::aos::V3Zero(); + } + + PX_FORCE_INLINE ConvexV(const ConvexType::Type type_, const Ps::aos::Vec3VArg center_) : type(type_), bMarginIsRadius(false) + { + using namespace Ps::aos; + center = center_; + margin = 0.f; + minMargin = 0.f; + sweepMargin = 0.f; + } + + + //everytime when someone transform the object, they need to up + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + center = _center; + } + + PX_FORCE_INLINE void setMargin(const Ps::aos::FloatVArg margin_) + { + Ps::aos::FStore(margin_, &margin); + } + + PX_FORCE_INLINE void setMargin(const PxReal margin_) + { + margin = margin_; + } + + PX_FORCE_INLINE void setMinMargin(const Ps::aos::FloatVArg minMargin_) + { + Ps::aos::FStore(minMargin_, & minMargin); + } + + PX_FORCE_INLINE void setSweepMargin(const Ps::aos::FloatVArg sweepMargin_) + { + Ps::aos::FStore(sweepMargin_, &sweepMargin); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getCenter()const + { + return center; + } + + PX_FORCE_INLINE Ps::aos::FloatV getMargin() const + { + return Ps::aos::FLoad(margin); + } + + PX_FORCE_INLINE Ps::aos::FloatV getMinMargin() const + { + return Ps::aos::FLoad(minMargin); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FLoad(sweepMargin); + } + + PX_FORCE_INLINE ConvexType::Type getType() const + { + return type; + } + + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius()const + { + return Ps::aos::BLoad(bMarginIsRadius); + } + + PX_FORCE_INLINE bool getMarginIsRadius() const + { + return bMarginIsRadius; + } + + protected: + ~ConvexV(){} + Ps::aos::Vec3V center; + PxReal margin; //margin is the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + PxReal minMargin; //minMargin is some percentage of marginBase, which is used to determine the termination condition for gjk + PxReal sweepMargin;// sweepMargin minMargin is some percentage of marginBase, which is used to determine the termination condition for gjkRaycast + ConvexType::Type type; + bool bMarginIsRadius; + }; + + PX_FORCE_INLINE Ps::aos::FloatV getContactEps(const Ps::aos::FloatV& _marginA, const Ps::aos::FloatV& _marginB) + { + using namespace Ps::aos; + + const FloatV ratio = FLoad(0.25f); + const FloatV minMargin = FMin(_marginA, _marginB); + + return FMul(minMargin, ratio); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepContactEps(const Ps::aos::FloatV& _marginA, const Ps::aos::FloatV& _marginB) + { + using namespace Ps::aos; + + const FloatV ratio = FLoad(100.f); + const FloatV minMargin = FAdd(_marginA, _marginB); + + return FMul(minMargin, ratio); + } +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvexHull.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvexHull.h new file mode 100644 index 00000000..114944e6 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvexHull.h @@ -0,0 +1,475 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEXHULL_H +#define GU_VEC_CONVEXHULL_H + +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData.h" +#include "GuConvexSupportTable.h" +#include "GuCubeIndex.h" +#include "PsFPU.h" +#include "GuGeometryUnion.h" +#include "PsVecQuat.h" +#include "PxMeshScale.h" + +namespace physx +{ +namespace Gu +{ +#define CONVEX_MARGIN_RATIO 0.1f +#define CONVEX_MIN_MARGIN_RATIO 0.05f +#define CONVEX_SWEEP_MARGIN_RATIO 0.025f + + //This margin is used in Persistent contact manifold + PX_SUPPORT_FORCE_INLINE Ps::aos::FloatV CalculatePCMConvexMargin(const Gu::ConvexHullData* hullData, const Ps::aos::Vec3VArg scale) + { + using namespace Ps::aos; + const Vec3V extents= V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min = V3ExtractMin(extents); + //ML: 25% of the minimum extents of the internal AABB as this convex hull's margin + return FMul(min, FLoad(0.25f)); + } + + + //This margin is used in PCM contact gen + PX_SUPPORT_FORCE_INLINE void CalculateConvexMargin(const Gu::ConvexHullData* hullData, PxReal& margin, PxReal& minMargin, PxReal& sweepMargin, const Ps::aos::Vec3VArg scale) + { + using namespace Ps::aos; + const Vec3V extents = V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min = V3ExtractMin(extents); + + //Margin is used in the plane shifting for the shrunk convex hull + const FloatV margin_ = FMul(min, FLoad(CONVEX_MARGIN_RATIO)); + //minMargin is used in the GJK termination condition + const FloatV minMargin_ = FMul(min, FLoad(CONVEX_MIN_MARGIN_RATIO)); + + const FloatV sweepMargin_ = FMul(min, FLoad(CONVEX_SWEEP_MARGIN_RATIO)); + + FStore(margin_, &margin); + FStore(minMargin_, &minMargin); + FStore(sweepMargin_, &sweepMargin); + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::Mat33V ConstructSkewMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation) + { + using namespace Ps::aos; + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + Mat33V trans = M33Trnsps(rot); + trans.col0 = V3Scale(trans.col0, V3GetX(scale)); + trans.col1 = V3Scale(trans.col1, V3GetY(scale)); + trans.col2 = V3Scale(trans.col2, V3GetZ(scale)); + return M33MulM33(trans, rot); + } + + PX_SUPPORT_FORCE_INLINE void ConstructSkewMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation, Ps::aos::Mat33V& vertex2Shape, Ps::aos::Mat33V& shape2Vertex, const bool idtScale) + { + using namespace Ps::aos; + + PX_ASSERT(!V3AllEq(scale, V3Zero())); + + if(idtScale) + { + //create identity buffer + const Mat33V identity = M33Identity(); + vertex2Shape = identity; + shape2Vertex = identity; + } + else + { + const FloatV scaleX = V3GetX(scale); + const Vec3V invScale = V3Recip(scale); + + //this is uniform scale + if(V3AllEq(V3Splat(scaleX), scale)) + { + vertex2Shape = M33Diagonal(scale); + shape2Vertex = M33Diagonal(invScale); + } + else + { + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + const Mat33V trans = M33Trnsps(rot); + /* + vertex2shape + skewMat = Inv(R)*Diagonal(scale)*R; + */ + + const Mat33V temp(V3Scale(trans.col0, scaleX), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + vertex2Shape = M33MulM33(temp, rot); + + //don't need it in the support function + /* + shape2Vertex + invSkewMat =(invSkewMat)= Inv(R)*Diagonal(1/scale)*R; + */ + + shape2Vertex.col0 = V3Scale(trans.col0, V3GetX(invScale)); + shape2Vertex.col1 = V3Scale(trans.col1, V3GetY(invScale)); + shape2Vertex.col2 = V3Scale(trans.col2, V3GetZ(invScale)); + shape2Vertex = M33MulM33(shape2Vertex, rot); + + //shape2Vertex = M33Inverse(vertex2Shape); + } + } + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::Mat33V ConstructVertex2ShapeMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation) + { + using namespace Ps::aos; + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + const Mat33V trans = M33Trnsps(rot); + /* + vertex2shape + skewMat = Inv(R)*Diagonal(scale)*R; + */ + + const Mat33V temp(V3Scale(trans.col0, V3GetX(scale)), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + return M33MulM33(temp, rot); + } + + + class ConvexHullV : public ConvexV + { + + class TinyBitMap + { + public: + PxU32 m[8]; + PX_FORCE_INLINE TinyBitMap() { m[0] = m[1] = m[2] = m[3] = m[4] = m[5] = m[6] = m[7] = 0; } + PX_FORCE_INLINE void set(PxU8 v) { m[v>>5] |= 1<<(v&31); } + PX_FORCE_INLINE bool get(PxU8 v) const { return (m[v>>5] & 1<<(v&31)) != 0; } + }; + + + public: + /** + \brief Constructor + */ + PX_SUPPORT_INLINE ConvexHullV(): ConvexV(ConvexType::eCONVEXHULL) + { + } + + PX_SUPPORT_INLINE ConvexHullV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot, const bool idtScale): + ConvexV(ConvexType::eCONVEXHULL, _center) + { + using namespace Ps::aos; + + hullData = _hullData; + const PxVec3* PX_RESTRICT tempVerts = _hullData->getHullVertices(); + //const PxU8* PX_RESTRICT polyInds = _hullData->getFacesByVertices8(); + //const HullPolygonData* PX_RESTRICT polygons = _hullData->mPolygons; + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + CalculateConvexMargin( _hullData, margin, minMargin, sweepMargin, scale); + ConstructSkewMatrix(scale, scaleRot, vertex2Shape, shape2Vertex, idtScale); + /*skewScale = Mat33V temp(V3Scale(trans.col0, V3GetX(scale)), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + skewRot = QuatGetMat33V(scaleRot);*/ + + data = _hullData->mBigConvexRawData; + } + + PX_SUPPORT_INLINE ConvexHullV(const PxGeometry& geom) : ConvexV(ConvexType::eCONVEXHULL, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxConvexMeshGeometryLL& convexGeom = static_cast<const PxConvexMeshGeometryLL&>(geom); + const Gu::ConvexHullData* hData = convexGeom.hullData; + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vRot = QuatVLoadU(&convexGeom.scale.rotation.x); + const bool idtScale = convexGeom.scale.isIdentity(); + + hullData = hData; + const PxVec3* PX_RESTRICT tempVerts = hData->getHullVertices(); + verts = tempVerts; + numVerts = hData->mNbHullVertices; + CalculateConvexMargin( hData, margin, minMargin, sweepMargin, vScale); + ConstructSkewMatrix(vScale, vRot, vertex2Shape, shape2Vertex, idtScale); + + data = hData->mBigConvexRawData; + } + + PX_SUPPORT_INLINE void initialize(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot, const bool idtScale) + { + using namespace Ps::aos; + + const PxVec3* tempVerts = _hullData->getHullVertices(); + CalculateConvexMargin(_hullData, margin, minMargin, sweepMargin, scale); + ConstructSkewMatrix(scale, scaleRot, vertex2Shape, shape2Vertex, idtScale); + + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + //rot = _rot; + + center = _center; + + // searchIndex = 0; + data = _hullData->mBigConvexRawData; + + hullData = _hullData; + if(_hullData->mBigConvexRawData) + { + Ps::prefetchLine(hullData->mBigConvexRawData->mValencies); + Ps::prefetchLine(hullData->mBigConvexRawData->mValencies,128); + Ps::prefetchLine(hullData->mBigConvexRawData->mAdjacentVerts); + } + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* /*marginDif*/)const + { + using namespace Ps::aos; + + return M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[index])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + PX_NOINLINE PxU32 hillClimbing(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + + const Gu::Valency* valency = data->mValencies; + const PxU8* adjacentVerts = data->mAdjacentVerts; + + //NotSoTinyBitMap visited; + PxU32 smallBitMap[8] = {0,0,0,0,0,0,0,0}; + + // PxU32 index = searchIndex; + PxU32 index = 0; + + { + PxVec3 vertexSpaceDirection; + V3StoreU(_dir, vertexSpaceDirection); + const PxU32 offset = ComputeCubemapNearestOffset(vertexSpaceDirection, data->mSubdiv); + //const PxU32 offset = ComputeCubemapOffset(vertexSpaceDirection, data->mSubdiv); + index = data->mSamples[offset]; + } + + Vec3V maxPoint = V3LoadU_SafeReadW(verts[index]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + FloatV max = V3Dot(maxPoint, _dir); + + PxU32 initialIndex = index; + + do + { + initialIndex = index; + const PxU32 numNeighbours = valency[index].mCount; + const PxU32 offset = valency[index].mOffset; + + for(PxU32 a = 0; a < numNeighbours; ++a) + { + const PxU32 neighbourIndex = adjacentVerts[offset + a]; + + const Vec3V vertex = V3LoadU_SafeReadW(verts[neighbourIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const FloatV dist = V3Dot(vertex, _dir); + if(FAllGrtr(dist, max)) + { + const PxU32 ind = neighbourIndex>>5; + const PxU32 mask = PxU32(1 << (neighbourIndex & 31)); + if((smallBitMap[ind] & mask) == 0) + { + smallBitMap[ind] |= mask; + max = dist; + index = neighbourIndex; + } + } + } + + }while(index != initialIndex); + + return index; + } + + PX_SUPPORT_INLINE PxU32 bruteForceSearch(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + //brute force + //get the support point from the orignal margin + FloatV max = V3Dot(V3LoadU_SafeReadW(verts[0]), _dir); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + PxU32 maxIndex=0; + + // PT: TODO: not sure SIMD is useful here... + + for(PxU32 i = 1; i < numVerts; ++i) + { +// Ps::prefetchLine(&verts[i], 128); // PT: TODO: 128 doesn't help when your cache line is 64. HW prefetchers do a better job here. + const Vec3V vertex = V3LoadU_SafeReadW(verts[i]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const FloatV dist = V3Dot(vertex, _dir); + if(FAllGrtr(dist, max)) + { + max = dist; + maxIndex = i; + } + } + return maxIndex; + } + + //points are in vertex space, _dir in vertex space + PX_NOINLINE PxU32 supportVertexIndex(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + if(data) + return hillClimbing(_dir); + else + return bruteForceSearch(_dir); + } + + //dir is in the vertex space + PX_SUPPORT_INLINE void bruteForceSearchMinMax(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + //brute force + //get the support point from the orignal margin + FloatV _max = V3Dot(V3LoadU_SafeReadW(verts[0]), dir); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + FloatV _min = _max; + + for(PxU32 i = 1; i < numVerts; ++i) + { +// Ps::prefetchLine(&verts[i], 128); // PT: TODO: 128 doesn't help when your cache line is 64. HW prefetchers do a better job here. + const FloatV dist = V3Dot(V3LoadU_SafeReadW(verts[i]), dir); + _max = FMax(dist, _max); + _min = FMin(dist, _min); + } + + + min = _min; + max = _max; + } + + //This function is used in the full contact manifold generation code, points are in vertex space. + //This function support scaling, _dir is in the shape space + PX_SUPPORT_INLINE void supportVertexMinMax(const Ps::aos::Vec3VArg _dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + + //dir is in the vertex space + const Vec3V dir = M33TrnspsMulV3(vertex2Shape, _dir); + + if(data) + { + const PxU32 maxIndex= hillClimbing(dir); + const PxU32 minIndex= hillClimbing(V3Neg(dir)); + const Vec3V maxPoint= M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[maxIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const Vec3V minPoint= M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[minIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + min = V3Dot(_dir, minPoint); + max = V3Dot(_dir, maxPoint); + } + else + { + //dir is in the vertex space + bruteForceSearchMinMax(dir, min, max); + } + } + + //This function is used in the full contact manifold generation code + PX_SUPPORT_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* _verts)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i<numInds; ++i) + _verts[i] = M33MulV3(vertex2Shape, V3LoadU_SafeReadW(originalVerts[inds[i]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'populateVerts' is always called with polyData.mVerts) + } + + //This function is used in epa + //dir is in the shape space + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + //scale dir and put it in the vertex space + const Vec3V _dir = M33TrnspsMulV3(vertex2Shape, dir); + const PxU32 maxIndex = supportVertexIndex(_dir); + return M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[maxIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + //this is used in the sat test for the full contact gen + PX_SUPPORT_INLINE void supportLocal(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + //dir is in the shape space + supportVertexMinMax(dir, min, max); + } + + //This function is used in epa + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + + //transform dir into the shape space +// const Vec3V dir_ = aTob.rotateInv(dir);//relTra.rotateInv(dir); + const Vec3V dir_ = aTobT.rotate(dir);//relTra.rotateInv(dir); + const Vec3V maxPoint =supportLocal(dir_); + //translate maxPoint from shape space of a back to the b space + return aTob.transform(maxPoint);//relTra.transform(maxPoint); + } + + //dir in the shape space, this function is used in gjk + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* /*marginDif*/)const + { + using namespace Ps::aos; + //scale dir and put it in the vertex space, for non-uniform scale, we don't want the scale in the dir, therefore, we are using + //the transpose of the inverse of shape2Vertex(which is vertex2shape). This will allow us igore the scale and keep the rotation + const Vec3V dir_ = M33TrnspsMulV3(vertex2Shape, dir); + //get the extreme point index + const PxU32 maxIndex = supportVertexIndex(dir_); + index = PxI32(maxIndex); + //p is in the shape space + return M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[index])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + //this function is used in gjk + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif)const + { + using namespace Ps::aos; + + //transform dir from b space to the shape space of a space +// const Vec3V dir_ = aTob.rotateInv(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V dir_ = aTobT.rotate(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V p = supportLocal(dir_, index, marginDif); + //transfrom from a to b space + return aTob.transform(p); + } + + Ps::aos::Mat33V vertex2Shape;//inv(R)*S*R + Ps::aos::Mat33V shape2Vertex;//inv(vertex2Shape) + + const Gu::ConvexHullData* hullData; + const BigConvexRawData* data; + const PxVec3* verts; + PxU8 numVerts; + }; + +} + +} + +#endif // diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvexHullNoScale.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvexHullNoScale.h new file mode 100644 index 00000000..a50532dc --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecConvexHullNoScale.h @@ -0,0 +1,250 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEXHULL_NOSCALE_H +#define GU_VEC_CONVEXHULL_NOSCALE_H + +#include "foundation/PxUnionCast.h" +#include "PxPhysXCommonConfig.h" +#include "GuVecConvexHull.h" + + +namespace physx +{ +namespace Gu +{ + + class ConvexHullNoScaleV : public ConvexHullV + { + + + public: + /** + \brief Constructor + */ + PX_SUPPORT_INLINE ConvexHullNoScaleV(): ConvexHullV() + { + } + + PX_SUPPORT_INLINE ConvexHullNoScaleV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot) + { + PX_UNUSED(scaleRot); + PX_UNUSED(_center); + + using namespace Ps::aos; + + hullData = _hullData; + const PxVec3* PX_RESTRICT tempVerts = _hullData->getHullVertices(); + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + CalculateConvexMargin( _hullData, margin, minMargin, sweepMargin, scale); + data = _hullData->mBigConvexRawData; + + PxU8* startAddress = reinterpret_cast<PxU8*>(_hullData->mPolygons); + PxI32 totalPrefetchBytes = PxI32((_hullData->getFacesByVertices8() + _hullData->mNbHullVertices * 3) - startAddress); + + //Prefetch core data + + while(totalPrefetchBytes > 0) + { + totalPrefetchBytes -= 128; + Ps::prefetchLine(startAddress); + startAddress += 128; + } + + if(data) + { + PxI32 totalSize = PxI32(data->mNbSamples + data->mNbVerts * sizeof(Gu::Valency) + data->mNbAdjVerts); + startAddress = data->mSamples; + while(totalSize > 0) + { + totalSize -= 128; + Ps::prefetchLine(startAddress); + startAddress += 128; + } + } + + } + + + /* PX_SUPPORT_INLINE ConvexHullNoScaleV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::FloatVArg _margin, const Ps::aos::FloatVArg _minMargin, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot) + { + PX_UNUSED(scaleRot); + PX_UNUSED(_center); + PX_UNUSED(scale); + + using namespace Ps::aos; + + hullData = _hullData; + margin = _margin; + minMargin = _minMargin; + + const PxVec3* tempVerts = _hullData->getHullVertices(); + const PxU8* PX_RESTRICT polyInds = _hullData->getFacesByVertices8(); + const HullPolygonData* PX_RESTRICT polygons = _hullData->mPolygons; + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + + Ps::prefetchLine(tempVerts); + Ps::prefetchLine(tempVerts,128); + Ps::prefetchLine(tempVerts,256); + + Ps::prefetchLine(polyInds); + Ps::prefetchLine(polyInds,128); + + Ps::prefetchLine(polygons); + Ps::prefetchLine(polygons, 128); + Ps::prefetchLine(polygons, 256); + }*/ + + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* /*marginDif*/)const + { + using namespace Ps::aos; + return V3LoadU_SafeReadW(verts[index]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + + // PT: TODO: is there a difference between 'originalVerts' and the 'verts' class member? Also why is this a member function at all? + PX_SUPPORT_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts_)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i<numInds; ++i) + verts_[i] = V3LoadU_SafeReadW(originalVerts[inds[i]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'populateVerts' is always called with polyData.mVerts) + } + + + //This function is used in epa + //dir is in the shape space + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + const PxU32 maxIndex = supportVertexIndex(dir); + return V3LoadU_SafeReadW(verts[maxIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + //this is used in the sat test for the full contact gen + PX_SUPPORT_INLINE void supportLocal(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + supportVertexMinMax(dir, min, max); + } + + + //This function is used in epa + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + + //transform dir into the shape space +// const Vec3V _dir = aTob.rotateInv(dir);//relTra.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir);//relTra.rotateInv(dir); + const Vec3V maxPoint = supportLocal(_dir); + //translate maxPoint from shape space of a back to the b space + return aTob.transform(maxPoint);//relTra.transform(maxPoint); + } + + //dir in the shape space, this function is used in gjk + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* /*marginDif*/)const + { + using namespace Ps::aos; + //scale dir and put it in the vertex space, for non-uniform scale, we don't want the scale in the dir, therefore, we are using + //the transpose of the inverse of shape2Vertex(which is vertex2shape). This will allow us igore the scale and keep the rotation + //get the extreme point index + const PxU32 maxIndex = supportVertexIndex(dir); + index = PxI32(maxIndex); + return V3LoadU_SafeReadW(verts[index]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + //this function is used in gjk + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif)const + { + using namespace Ps::aos; + + //transform dir from b space to the shape space of a space +// const Vec3V _dir = aTob.rotateInv(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V _dir = aTobT.rotate(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V p = supportLocal(_dir, index, marginDif); + //transfrom from a to b space + return aTob.transform(p); + } + + + PX_SUPPORT_INLINE void bruteForceSearchMinMax(const Ps::aos::Vec3VArg _dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + //brute force + //get the support point from the orignal margin + FloatV _max = V3Dot(V3LoadU_SafeReadW(verts[0]), _dir); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + FloatV _min = _max; + + for(PxU32 i = 1; i < numVerts; ++i) + { + Ps::prefetchLine(&verts[i], 128); + const Vec3V vertex = V3LoadU_SafeReadW(verts[i]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const FloatV dist = V3Dot(vertex, _dir); + + _max = FMax(dist, _max); + _min = FMin(dist, _min); + } + + min = _min; + max = _max; + } + + //This function support no scaling, dir is in the shape space(the same as vertex space) + PX_SUPPORT_INLINE void supportVertexMinMax(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + + if(data) + { + const PxU32 maxIndex= hillClimbing(dir); + const PxU32 minIndex= hillClimbing(V3Neg(dir)); + const Vec3V maxPoint= V3LoadU_SafeReadW(verts[maxIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const Vec3V minPoint= V3LoadU_SafeReadW(verts[minIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + min = V3Dot(dir, minPoint); + max = V3Dot(dir, maxPoint); + } + else + { + bruteForceSearchMinMax(dir, min, max); + } + } + + + }; + + #define PX_CONVEX_TO_NOSCALECONVEX(x) (static_cast<ConvexHullNoScaleV*>(x)) +} + +} + +#endif // diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecPlane.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecPlane.h new file mode 100644 index 00000000..ad41cd3e --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecPlane.h @@ -0,0 +1,224 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_PLANE_H +#define GU_VEC_PLANE_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxPlane.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +/** +\brief Representation of a plane. + +Plane equation used: a*x + b*y + c*z + d = 0 +*/ +namespace physx +{ +namespace Gu +{ + + class PlaneV + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE PlaneV() + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::FloatVArg nx, const Ps::aos::FloatVArg ny, const Ps::aos::FloatVArg nz, const Ps::aos::FloatVArg _d) + { + set(nx, ny, nz, _d); + } + + PX_FORCE_INLINE PlaneV(const PxPlane& plane) + { + using namespace Ps::aos; + const Vec3V _n = V3LoadU(plane.n); + const FloatV _d = FLoad(plane.d); + nd = V4SetW(Vec4V_From_Vec3V(_n), _d); + } + + + /** + \brief Constructor from three points + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2) + { + set(p0, p1, p2); + } + + /** + \brief Constructor from a normal and a distance + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::Vec3VArg _n, const Ps::aos::FloatVArg _d) + { + nd = Ps::aos::V4SetW(Ps::aos::Vec4V_From_Vec3V(_n), _d); + } + + /** + \brief Copy constructor + */ + PX_FORCE_INLINE PlaneV(const PlaneV& plane) : nd(plane.nd) + { + } + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~PlaneV() + { + } + + /** + \brief Sets plane to zero. + */ + PX_FORCE_INLINE PlaneV& setZero() + { + nd = Ps::aos::V4Zero(); + return *this; + } + + PX_FORCE_INLINE PlaneV& set(const Ps::aos::FloatVArg nx, const Ps::aos::FloatVArg ny, const Ps::aos::FloatVArg nz, const Ps::aos::FloatVArg _d) + { + + using namespace Ps::aos; + const Vec3V n= V3Merge(nx, ny, nz); + nd = V4SetW(Vec4V_From_Vec3V(n), _d); + return *this; + } + + PX_FORCE_INLINE PlaneV& set(const Ps::aos::Vec3VArg _normal, Ps::aos::FloatVArg _d) + { + nd = Ps::aos::V4SetW(Ps::aos::Vec4V_From_Vec3V(_normal), _d); + return *this; + } + + /** + \brief Computes the plane equation from 3 points. + */ + PlaneV& set(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2) + { + using namespace Ps::aos; + const Vec3V edge0 = V3Sub(p1, p0); + const Vec3V edge1 = V3Sub(p2, p0); + + const Vec3V n = V3Normalize(V3Cross(edge0, edge1)); + // See comments in set() for computation of d + const FloatV d = FNeg(V3Dot(p0, n)); + nd = V4SetW(Vec4V_From_Vec3V(n), d); + return *this; + } + + /*** + \brief Computes distance, assuming plane is normalized + \sa normalize + */ + PX_FORCE_INLINE Ps::aos::FloatV distance(const Ps::aos::Vec3VArg p) const + { + // Valid for plane equation a*x + b*y + c*z + d = 0 + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return FAdd(V3Dot(p, n), V4GetW(nd)); + } + + PX_FORCE_INLINE Ps::aos::BoolV belongs(const Ps::aos::Vec3VArg p) const + { + using namespace Ps::aos; + const FloatV eps = FLoad(1.0e-7f); + return FIsGrtr(eps, FAbs(distance(p))); + } + + /** + \brief projects p into the plane + */ + PX_FORCE_INLINE Ps::aos::Vec3V project(const Ps::aos::Vec3VArg p) const + { + // Pretend p is on positive side of plane, i.e. plane.distance(p)>0. + // To project the point we have to go in a direction opposed to plane's normal, i.e.: + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return V3Sub(p, V3Scale(n, V4GetW(nd))); + + } + + PX_FORCE_INLINE Ps::aos::FloatV signedDistanceHessianNormalForm(const Ps::aos::Vec3VArg point) const + { + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return FAdd(V3Dot(n, point), V4GetW(nd)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getNormal() const + { + return Ps::aos::Vec3V_From_Vec4V(nd); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSignDist() const + { + return Ps::aos::V4GetW(nd); + } + + /** + \brief find an arbitrary point in the plane + */ + PX_FORCE_INLINE Ps::aos::Vec3V pointInPlane() const + { + // Project origin (0,0,0) to plane: + // (0) - normal * distance(0) = - normal * ((p|(0)) + d) = -normal*d + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return V3Neg(V3Scale(n, V4GetW(nd))); + } + + PX_FORCE_INLINE void normalize() + { + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + const FloatV denom = FRecip(V3Length(n)); + V4Scale(nd, denom); + } + + Ps::aos::Vec4V nd; //!< The normal to the plan , w store the distance from the origin + }; +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkBox.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkBox.h new file mode 100644 index 00000000..8fbaa111 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkBox.h @@ -0,0 +1,191 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_SHRUNK_BOX_H +#define GU_VEC_SHRUNK_BOX_H + +/** \addtogroup geomutils +@{ +*/ +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "GuConvexSupportTable.h" +#include "GuVecBox.h" + + + +namespace physx +{ + +namespace Gu +{ + + /** + \brief Represents an oriented bounding box. + + As a center point, extents(radii) and a rotation. i.e. the center of the box is at the center point, + the box is rotated around this point with the rotation and it is 2*extents in width, height and depth. + */ + + /** + Box geometry + + The rot member describes the world space orientation of the box. + The center member gives the world space position of the box. + The extents give the local space coordinates of the box corner in the positive octant. + Dimensions of the box are: 2*extent. + Transformation to world space is: worldPoint = rot * localPoint + center + Transformation to local space is: localPoint = T(rot) * (worldPoint - center) + Where T(M) denotes the transpose of M. + */ + + class ShrunkBoxV : public BoxV + { + public: + + /** + \brief Constructor + */ + PX_INLINE ShrunkBoxV() : BoxV() + { + } + + PX_INLINE ShrunkBoxV(const PxGeometry& geom) : BoxV(geom) + { + initialiseMarginDif(); + } + + /** + \brief Constructor + + \param origin Center of the OBB + \param extent Extents/radii of the obb. + */ + + PX_FORCE_INLINE ShrunkBoxV(const Ps::aos::Vec3VArg origin, const Ps::aos::Vec3VArg extent) : + BoxV(origin, extent) + { + initialiseMarginDif(); + } + + /** + \brief Destructor + */ + PX_INLINE ~ShrunkBoxV() + { + } + + //! Assignment operator + PX_INLINE const ShrunkBoxV& operator=(const ShrunkBoxV& other) + { + center = other.center; + extents = other.extents; + margin = other.margin; + minMargin = other.minMargin; + return *this; + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif_)const + { + using namespace Ps::aos; + const Vec3V extents_ = V3Sub(extents, V3Splat(getMargin())); + const BoolV con = boxVertexTable[index]; + (*marginDif_) = marginDif; + return V3Sel(con, extents_, V3Neg(extents_)); + } + + //local space point + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + + const Vec3V zero = V3Zero(); + const Vec3V extents_ = V3Sub(extents, V3Splat(getMargin())); + const BoolV comp = V3IsGrtr(dir, zero); + return V3Sel(comp, extents_, V3Neg(extents_)); + } + + //local space point + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif_)const + { + using namespace Ps::aos; + + const Vec3V zero = V3Zero(); + const Vec3V extents_ = V3Sub(extents, V3Splat(getMargin())); + const BoolV comp = V3IsGrtr(dir, zero); + getIndex(comp, index); + (*marginDif_) = marginDif; + return V3Sel(comp, extents_, V3Neg(extents_)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + + //transfer dir into the local space of the box +// const Vec3V dir_ =aTob.rotateInv(dir);//relTra.rotateInv(dir); + const Vec3V dir_ = aTobT.rotate(dir);//relTra.rotateInv(dir); + const Vec3V p = supportLocal(dir_); + return aTob.transform(p);//relTra.transform(p);//V3Add(center, M33MulV3(rot, p)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif_)const + { + using namespace Ps::aos; + + //transfer dir into the local space of the box +// const Vec3V dir_ =aTob.rotateInv(dir);//relTra.rotateInv(dir); + const Vec3V dir_ = aTobT.rotate(dir);//relTra.rotateInv(dir); + const Vec3V p = supportLocal(dir_, index, marginDif_); + return aTob.transform(p);//relTra.transform(p);//V3Add(center, M33MulV3(rot, p)); + } + + private: + + PX_FORCE_INLINE void initialiseMarginDif() + { + using namespace Ps::aos; + //calculate the marginDif + const PxReal sqMargin = margin * margin; + const PxReal tempMarginDif = sqrtf(sqMargin * 3.f); + const PxReal marginDif_ = tempMarginDif - margin; + marginDif = FLoad(marginDif_); + /* const FloatV sqMargin = FMul(margin, margin); + const FloatV tempMarginDif = FSqrt(FAdd(sqMargin, FAdd(sqMargin, sqMargin))); + marginDif = FSub(tempMarginDif, margin);*/ + } + }; +} + +} + +/** @} */ +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkConvexHull.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkConvexHull.h new file mode 100644 index 00000000..fe72bc1d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkConvexHull.h @@ -0,0 +1,183 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_SHRUNK_CONVEX_HULL_H +#define GU_VEC_SHRUNK_CONVEX_HULL_H + +#include "PxPhysXCommonConfig.h" +#include "GuVecConvexHull.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData.h" +#include "GuConvexSupportTable.h" +#include "GuCubeIndex.h" +#include "PsFPU.h" + + +namespace physx +{ +namespace Gu +{ + + PX_SUPPORT_FORCE_INLINE Ps::aos::Vec3V intersectPlanes(const Ps::aos::Vec3VArg n1, const Ps::aos::FloatVArg d1, + const Ps::aos::Vec3VArg n2, const Ps::aos::FloatVArg d2, + const Ps::aos::Vec3VArg n3, const Ps::aos::FloatVArg d3) + { + using namespace Ps::aos; + const Vec3V u = V3Cross(n2, n3); + const FloatV denom = V3Dot(n1, u); + const Vec3V temp = V3NegScaleSub(n2, d3, V3Scale(n3, d2)); + const Vec3V p = V3NegScaleSub(u, d1, V3Cross(n1, temp)); + return V3ScaleInv(p, denom); + } + + + + /* + ML: + ShrinkedConvexHull is used in GJK code but not EPA code + */ + class ShrunkConvexHullV : public ConvexHullV + { + + + public: + /** + \brief Constructor + */ + PX_SUPPORT_INLINE ShrunkConvexHullV(): ConvexHullV() + { + } + + PX_SUPPORT_INLINE ShrunkConvexHullV(const PxGeometry& geom) : ConvexHullV(geom) + { + } + + PX_SUPPORT_INLINE ShrunkConvexHullV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot, const bool idtScale): + ConvexHullV(_hullData, _center, scale, scaleRot, idtScale) + { + } + + + + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif) const + { + + return planeShift(PxU32(index), getMargin(), marginDif); + } + + //get the support point in vertex space + PX_SUPPORT_INLINE Ps::aos::Vec3V planeShift(const PxU32 index, const Ps::aos::FloatVArg margin_, Ps::aos::FloatV* marginDif) const + { + using namespace Ps::aos; + + //calculate the support point for the core(shrunk) shape + const PxU8* PX_RESTRICT polyInds = hullData->getFacesByVertices8(); + + //transfrom the vertex from vertex space to shape space + const Vec3V p = M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[index])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + + const PxU32 ind = index*3; + + const PxPlane& data1 = hullData->mPolygons[polyInds[ind]].mPlane; + const PxPlane& data2 = hullData->mPolygons[polyInds[ind+1]].mPlane; + const PxPlane& data3 = hullData->mPolygons[polyInds[ind+2]].mPlane; + + //transform the normal from vertex space to shape space + //This is only required if the scale is not uniform + const Vec3V n1 = V3Normalize(M33TrnspsMulV3(shape2Vertex, V3LoadU_SafeReadW(data1.n))); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V n2 = V3Normalize(M33TrnspsMulV3(shape2Vertex, V3LoadU_SafeReadW(data2.n))); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V n3 = V3Normalize(M33TrnspsMulV3(shape2Vertex, V3LoadU_SafeReadW(data3.n))); // PT: safe because 'd' follows 'n' in the plane class + + //This is only required if the scale is not 1 + const FloatV d1 = FSub(margin_, V3Dot(p, n1)); + const FloatV d2 = FSub(margin_, V3Dot(p, n2)); + const FloatV d3 = FSub(margin_, V3Dot(p, n3)); + + //This is unavoidable unless we pre-calc the core shape. The intersect point is in shape space + const Vec3V intersectPoints = intersectPlanes(n1, d1, n2, d2, n3, d3); + const Vec3V v =V3Sub(p, intersectPoints); + (*marginDif) = FSub(V3Length(v), margin_); + return intersectPoints; + } + + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir) const + { + PxI32 index; + Ps::aos::FloatV marginDif; + return supportLocal(dir, index, &marginDif); + } + + + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, const Ps::aos::PsMatTransformV& aTobT) const + { + PxI32 index; + Ps::aos::FloatV marginDif; + return supportRelative(dir, aTob, aTobT, index, &marginDif); + } + + //This function is used in gjk + //dir in the shape space + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const + { + using namespace Ps::aos; + //scale dir and put it in the vertex space, for non-uniform scale, we don't want the scale in the dir, therefore, we are using + //the transpose of the inverse of shape2Vertex(which is vertex2shape). This will allow us igore the scale and keep the rotation + const Vec3V _dir = M33TrnspsMulV3(vertex2Shape, dir); + //get the extreme point index + const PxU32 maxIndex = supportVertexIndex(_dir); + index = PxI32(maxIndex); + //p is in the shape space + return planeShift(maxIndex, getMargin(), marginDif); + } + + + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif) const + { + using namespace Ps::aos; + + //transform dir from b space to the shape space of a space +// const Vec3V _dir = aTob.rotateInv(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V _dir = aTobT.rotate(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V p = supportLocal(_dir, index, marginDif); + //transfrom from a to b space + return aTob.transform(p); + } + + }; + +} + +} + +#endif // diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkConvexHullNoScale.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkConvexHullNoScale.h new file mode 100644 index 00000000..9e5d8c0c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecShrunkConvexHullNoScale.h @@ -0,0 +1,155 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_SHRUNK_CONVEX_HULL_NO_SCALE_H +#define GU_VEC_SHRUNK_CONVEX_HULL_NO_SCALE_H + +#include "foundation/PxUnionCast.h" +#include "GuVecShrunkConvexHull.h" + + +namespace physx +{ +namespace Gu +{ + + + /* + ML: + ShrinkedConvexHull is used in GJK code but not EPA code + */ + class ShrunkConvexHullNoScaleV : public ShrunkConvexHullV + { + + + public: + /** + \brief Constructor + */ + PX_SUPPORT_INLINE ShrunkConvexHullNoScaleV(): ShrunkConvexHullV() + { + } + + PX_SUPPORT_INLINE ShrunkConvexHullNoScaleV(const PxGeometry& geom) : ShrunkConvexHullV(geom) + { + } + + PX_SUPPORT_INLINE ShrunkConvexHullNoScaleV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot): + ShrunkConvexHullV(_hullData, _center, scale, scaleRot, true) + { + } + + + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* marginDif) const + { + return planeShift(PxU32(index), getMargin(), marginDif); + } + + //get the support point in vertex space + PX_SUPPORT_INLINE Ps::aos::Vec3V planeShift(const PxU32 index, const Ps::aos::FloatVArg margin_, Ps::aos::FloatV* marginDif) const + { + using namespace Ps::aos; + + //calculate the support point for the core(shrunk) shape + const PxU8* PX_RESTRICT polyInds = hullData->getFacesByVertices8(); + + const Vec3V p = V3LoadU_SafeReadW(verts[index]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + + const PxU32 ind = index*3; + + const PxPlane& data1 = hullData->mPolygons[polyInds[ind]].mPlane; + const PxPlane& data2 = hullData->mPolygons[polyInds[ind+1]].mPlane; + const PxPlane& data3 = hullData->mPolygons[polyInds[ind+2]].mPlane; + + //ML: because we don't have scale in this type of convex hull so that normal in vertex space will be the same as shape space normal + //This is only required if the scale is not uniform + const Vec3V n1 = V3LoadU_SafeReadW(data1.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V n2 = V3LoadU_SafeReadW(data2.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V n3 = V3LoadU_SafeReadW(data3.n); // PT: safe because 'd' follows 'n' in the plane class + + //This is only required if the scale is not 1 + const FloatV d1 = FSub(margin_, V3Dot(p, n1)); + const FloatV d2 = FSub(margin_, V3Dot(p, n2)); + const FloatV d3 = FSub(margin_, V3Dot(p, n3)); + + //This is unavoidable unless we pre-calc the core shape + const Vec3V intersectPoints = intersectPlanes(n1, d1, n2, d2, n3, d3); + const Vec3V v =V3Sub(p, intersectPoints); + (*marginDif) = V3Length(v); + return intersectPoints; + } + + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir) const + { + PxI32 index; + Ps::aos::FloatV marginDif; + return supportLocal(dir, index, &marginDif); + } + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, const Ps::aos::PsMatTransformV& aTobT) const + { + PxI32 index; + Ps::aos::FloatV marginDif; + return supportRelative(dir, aTob, aTobT, index, &marginDif); + } + + //This function is used in gjk penetration + //dir in the shape space + PX_SUPPORT_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* marginDif) const + { + using namespace Ps::aos; + //get the extreme point index + const PxU32 maxIndex = supportVertexIndex(dir); + index = PxI32(maxIndex); + //p is in the shape space + return planeShift(maxIndex, getMargin(), marginDif); + } + + PX_SUPPORT_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aTob, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif) const + { + using namespace Ps::aos; + + //transform dir from b space to the shape space of a space +// const Vec3V dir_ = aTob.rotateInv(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V dir_ = aTobT.rotate(dir);//relTra.rotateInv(dir);//M33MulV3(skewInvRot, dir); + const Vec3V p = supportLocal(dir_, index, marginDif); + //transfrom from a to b space + return aTob.transform(p); + } + }; + + #define PX_SCONVEX_TO_NOSCALECONVEX(x) (static_cast<ShrunkConvexHullNoScaleV*>(x)) +} + +} + +#endif // diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecSphere.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecSphere.h new file mode 100644 index 00000000..fdb850ff --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecSphere.h @@ -0,0 +1,243 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_SPHERE_H +#define GU_VEC_SPHERE_H +/** \addtogroup geomutils +@{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "PxSphereGeometry.h" + +/** +\brief Represents a sphere defined by its center point and radius. +*/ +namespace physx +{ +namespace Gu +{ + class SphereV : public ConvexV + { + public: + /** + \brief Constructor + */ + PX_INLINE SphereV(): ConvexV(ConvexType::eSPHERE) + { + radius = Ps::aos::FZero(); + bMarginIsRadius = true; + } + + PX_INLINE SphereV(const Ps::aos::Vec3VArg _center, const Ps::aos::FloatV _radius) : ConvexV(ConvexType::eSPHERE, _center) + { + using namespace Ps::aos; + radius = _radius; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + + /** + \brief Copy constructor + */ + PX_INLINE SphereV(const SphereV& sphere) : ConvexV(ConvexType::eSPHERE), radius(sphere.radius) + { + + margin = sphere.margin; + minMargin = sphere.minMargin; + sweepMargin = sphere.sweepMargin; + bMarginIsRadius = true; + } + + PX_INLINE SphereV(const PxGeometry& geom) : ConvexV(ConvexType::eSPHERE, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + const FloatV r = FLoad(sphereGeom.radius); + radius = r; + margin = sphereGeom.radius; + minMargin = sphereGeom.radius; + sweepMargin = sphereGeom.radius; + bMarginIsRadius = true; + } + + /** + \brief Destructor + */ + PX_INLINE ~SphereV() + { + } + + PX_INLINE void setV(const Ps::aos::Vec3VArg _center, const Ps::aos::FloatVArg _radius) + { + center = _center; + radius = _radius; + } + + /** + \brief Checks the sphere is valid. + + \return true if the sphere is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + using namespace Ps::aos; + return BAllEqTTTT(FIsGrtrOrEq(radius, FZero())) != 0; + } + + /** + \brief Tests if a point is contained within the sphere. + + \param[in] p the point to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const Ps::aos::Vec3VArg p) const + { + using namespace Ps::aos; + const FloatV rr = FMul(radius, radius); + const FloatV cc = V3LengthSq(V3Sub(center, p)); + return FAllGrtrOrEq(rr, cc) != 0; + } + + /** + \brief Tests if a sphere is contained within the sphere. + + \param sphere [in] the sphere to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const SphereV& sphere) const + { + using namespace Ps::aos; + + const Vec3V centerDif= V3Sub(center, sphere.center); + const FloatV radiusDif = FSub(radius, sphere.radius); + const FloatV cc = V3Dot(centerDif, centerDif); + const FloatV rr = FMul(radiusDif, radiusDif); + + const BoolV con0 = FIsGrtrOrEq(radiusDif, FZero());//might contain + const BoolV con1 = FIsGrtr(rr, cc);//return true + return BAllEqTTTT(BAnd(con0, con1))==1; + } + + /** + \brief Tests if a box is contained within the sphere. + + \param minimum [in] minimum value of the box + \param maximum [in] maximum value of the box + \return true if inside the sphere + */ + PX_INLINE bool contains(const Ps::aos::Vec3VArg minimum, const Ps::aos::Vec3VArg maximum) const + { + + //compute the sphere which wrap around the box + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV half = FHalf(); + + const Vec3V boxSphereCenter = V3Scale(V3Add(maximum, minimum), half); + const Vec3V v = V3Scale(V3Sub(maximum, minimum), half); + const FloatV boxSphereR = V3Length(v); + + const Vec3V w = V3Sub(center, boxSphereCenter); + const FloatV wLength = V3Length(w); + const FloatV dif = FSub(FSub(radius, wLength), boxSphereR); + + return FAllGrtrOrEq(dif, zero) != 0; + } + + /** + \brief Tests if the sphere intersects another sphere + + \param sphere [in] the other sphere + \return true if spheres overlap + */ + PX_INLINE bool intersect(const SphereV& sphere) const + { + using namespace Ps::aos; + const Vec3V centerDif = V3Sub(center, sphere.center); + const FloatV cc = V3Dot(centerDif, centerDif); + const FloatV r = FAdd(radius, sphere.radius); + const FloatV rr = FMul(r, r); + return FAllGrtrOrEq(rr, cc) != 0; + } + + //return point in local space + PX_FORCE_INLINE Ps::aos::Vec3V getPoint(const PxU8) + { + return Ps::aos::V3Zero(); + } + // + //sweep code need to have full version + PX_FORCE_INLINE Ps::aos::Vec3V supportSweep(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + const Vec3V _dir = V3Normalize(dir); + return V3ScaleAdd(_dir, radius, center); + } + + //make the support function the same as support margin + PX_FORCE_INLINE Ps::aos::Vec3V support(const Ps::aos::Vec3VArg)const + { + return center;//_margin is the same as radius + } + + + PX_FORCE_INLINE Ps::aos::Vec3V supportMargin(const Ps::aos::Vec3VArg dir, const Ps::aos::FloatVArg _margin, Ps::aos::Vec3V& support)const + { + PX_UNUSED(_margin); + PX_UNUSED(dir); + + support = center; + return center;//_margin is the same as radius + } + + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius()const + { + return Ps::aos::BTTTT(); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FZero(); + } + + + Ps::aos::FloatV radius; //!< Sphere's center, w component is radius + + }; +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecTriangle.h b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecTriangle.h new file mode 100644 index 00000000..004cc560 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/gjk/GuVecTriangle.h @@ -0,0 +1,268 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_TRIANGLE_H +#define GU_VEC_TRIANGLE_H +/** \addtogroup geomutils + @{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "GuDistancePointTriangleSIMD.h" + +namespace physx +{ +namespace Gu +{ + + + class TriangleV : public ConvexV + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE TriangleV() : ConvexV(ConvexType::eTRIANGLE) + { + margin = 0.02f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + /** + \brief Constructor + + \param[in] p0 Point 0 + \param[in] p1 Point 1 + \param[in] p2 Point 2 + */ + + PX_FORCE_INLINE TriangleV(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2): ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + //const FloatV zero = FZero(); + const FloatV num = FLoad(0.333333f); + center = V3Scale(V3Add(V3Add(p0, p1), p2), num); + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + + PX_FORCE_INLINE TriangleV(const PxVec3* pts) : ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + const Vec3V p0 = V3LoadU(pts[0]); + const Vec3V p1 = V3LoadU(pts[1]); + const Vec3V p2 = V3LoadU(pts[2]); + const FloatV num = FLoad(0.333333f); + center = V3Scale(V3Add(V3Add(p0, p1), p2), num); + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + + /** + \brief Copy constructor + + \param[in] triangle Tri to copy + */ + PX_FORCE_INLINE TriangleV(const Gu::TriangleV& triangle) : ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + + center = triangle.center; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + /** + \brief Destructor + */ + PX_FORCE_INLINE ~TriangleV() + { + } + + PX_FORCE_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* vertexs)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i<numInds; ++i) + { + vertexs[i] = V3LoadU(originalVerts[inds[i]]); + } + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FMax(); + } + + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + using namespace Ps::aos; + Vec3V offset = V3Sub(_center, center); + center = _center; + verts[0] = V3Add(verts[0], offset); + verts[1] = V3Add(verts[1], offset); + verts[2] = V3Add(verts[2], offset); + } + + /** + \brief Compute the normal of the Triangle. + + \return Triangle normal. + */ + PX_FORCE_INLINE Ps::aos::Vec3V normal() const + { + using namespace Ps::aos; + const Vec3V ab = V3Sub(verts[1], verts[0]); + const Vec3V ac = V3Sub(verts[2], verts[0]); + const Vec3V n = V3Cross(ab, ac); + return V3Normalize(n); + } + + /** + \brief Compute the unnormalized normal of the Triangle. + + \param[out] _normal Triangle normal (not normalized). + */ + PX_FORCE_INLINE void denormalizedNormal(Ps::aos::Vec3V& _normal) const + { + using namespace Ps::aos; + const Vec3V ab = V3Sub(verts[1], verts[0]); + const Vec3V ac = V3Sub(verts[2], verts[0]); + _normal = V3Cross(ab, ac); + } + + PX_FORCE_INLINE Ps::aos::FloatV area() const + { + using namespace Ps::aos; + const FloatV half = FLoad(0.5f); + const Vec3V ba = V3Sub(verts[0], verts[1]); + const Vec3V ca = V3Sub(verts[0], verts[2]); + const Vec3V v = V3Cross(ba, ca); + return FMul(V3Length(v), half); + } + + //dir is in local space, verts in the local space + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir) const + { + using namespace Ps::aos; + const Vec3V v0 = verts[0]; + const Vec3V v1 = verts[1]; + const Vec3V v2 = verts[2]; + const FloatV d0 = V3Dot(v0, dir); + const FloatV d1 = V3Dot(v1, dir); + const FloatV d2 = V3Dot(v2, dir); + + const BoolV con0 = BAnd(FIsGrtr(d0, d1), FIsGrtr(d0, d2)); + const BoolV con1 = FIsGrtr(d1, d2); + return V3Sel(con0, v0, V3Sel(con1, v1, v2)); + } + + PX_FORCE_INLINE void supportLocal(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const + { + using namespace Ps::aos; + const Vec3V v0 = verts[0]; + const Vec3V v1 = verts[1]; + const Vec3V v2 = verts[2]; + FloatV d0 = V3Dot(v0, dir); + FloatV d1 = V3Dot(v1, dir); + FloatV d2 = V3Dot(v2, dir); + + max = FMax(d0, FMax(d1, d2)); + min = FMin(d0, FMin(d1, d2)); + } + + //dir is in b space + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + //verts are in local space +// const Vec3V _dir = aToB.rotateInv(dir); //transform dir back to a space + const Vec3V _dir = aTobT.rotate(dir); //transform dir back to a space + const Vec3V maxPoint = supportLocal(_dir); + return aToB.transform(maxPoint);//transform maxPoint to the b space + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index, Ps::aos::FloatV* /*marginDif*/) const + { + + using namespace Ps::aos; + const VecI32V vZero = VecI32V_Zero(); + const VecI32V vOne = VecI32V_One(); + const VecI32V vTwo = VecI32V_Two(); + const Vec3V v0 = verts[0]; + const Vec3V v1 = verts[1]; + const Vec3V v2 = verts[2]; + const FloatV d0 = V3Dot(v0, dir); + const FloatV d1 = V3Dot(v1, dir); + const FloatV d2 = V3Dot(v2, dir); + const BoolV con0 = BAnd(FIsGrtr(d0, d1), FIsGrtr(d0, d2)); + const BoolV con1 = FIsGrtr(d1, d2); + const VecI32V vIndex = VecI32V_Sel(con0, vZero, VecI32V_Sel(con1, vOne, vTwo)); + PxI32_From_VecI32V(vIndex, &index); + return V3Sel(con0, v0, V3Sel(con1, v1, v2)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index, Ps::aos::FloatV* marginDif)const + { + //don't put margin in the triangle + using namespace Ps::aos; + //transfer dir into the local space of triangle +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + return aToB.transform(supportLocal(_dir, index, marginDif));//transform the support poin to b space + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index, Ps::aos::FloatV* /*marginDif*/)const + { + return verts[index]; + } + + /** + \brief Array of Vertices. + */ + Ps::aos::Vec3V verts[3]; + }; +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h new file mode 100644 index 00000000..5a423f3b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuEntityReport.h @@ -0,0 +1,56 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_ENTITY_REPORT_H +#define GU_ENTITY_REPORT_H + +#include "Ps.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + +template<class T> +class EntityReport +{ + public: + + virtual ~EntityReport() {} + + virtual PxAgain onEvent(PxU32 nbEntities, T* entities) = 0; +}; + + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp new file mode 100644 index 00000000..d376715c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.cpp @@ -0,0 +1,814 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxMemory.h" +#include "PsIntrinsics.h" +#include "GuHeightField.h" +#include "PsAllocator.h" +#include "PsUtilities.h" +#include "GuMeshFactory.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "CmBitMap.h" +#include "PsFoundation.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::HeightField(GuMeshFactory* meshFactory) +: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mSampleStride (0) +, mNbSamples (0) +, mMinHeight (0.0f) +, mMaxHeight (0.0f) +, mModifyCount (0) +, mMeshFactory (meshFactory) +{ + mData.format = PxHeightFieldFormat::eS16_TM; + mData.rows = 0; + mData.columns = 0; + mData.convexEdgeThreshold = 0; + mData.flags = PxHeightFieldFlags(); + mData.samples = NULL; + mData.thickness = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data) +: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mSampleStride (0) +, mNbSamples (0) +, mMinHeight (0.0f) +, mMaxHeight (0.0f) +, mModifyCount (0) +, mMeshFactory (&factory) +{ + mData = data; + data.samples = NULL; // set to null so that we don't release the memory +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::~HeightField() +{ + releaseMemory(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PX_SERIALIZATION +void Gu::HeightField::onRefCountZero() +{ + PX_ASSERT(mMeshFactory); + if(mMeshFactory->removeHeightField(*this)) + { + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, PxConcreteType::eHEIGHTFIELD); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::HeightField::onRefCountZero: double deletion detected!"); +} + +void Gu::HeightField::exportExtraData(PxSerializationContext& stream) +{ + // PT: warning, order matters for the converter. Needs to export the base stuff first + const PxU32 size = mData.rows * mData.columns * sizeof(PxHeightFieldSample); + stream.alignData(PX_SERIAL_ALIGN); // PT: generic align within the generic allocator + stream.writeData(mData.samples, size); +} + +void Gu::HeightField::importExtraData(PxDeserializationContext& context) +{ + mData.samples = context.readExtraData<PxHeightFieldSample, PX_SERIAL_ALIGN>(mData.rows * mData.columns); +} + +Gu::HeightField* Gu::HeightField::createObject(PxU8*& address, PxDeserializationContext& context) +{ + HeightField* obj = new (address) HeightField(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(HeightField); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +//~PX_SERIALIZATION + +void Gu::HeightField::release() +{ + decRefCount(); +} + +void Gu::HeightField::acquireReference() +{ + incRefCount(); +} + +PxU32 Gu::HeightField::getReferenceCount() const +{ + return getRefCount(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Gu::HeightField::modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& desc, bool shrinkBounds) +{ + const PxU32 nbCols = getNbColumns(); + const PxU32 nbRows = getNbRows(); + PX_CHECK_AND_RETURN_NULL(desc.format == mData.format, "Gu::HeightField::modifySamples: desc.format mismatch"); + //PX_CHECK_AND_RETURN_NULL(startCol + desc.nbColumns <= nbCols, + // "Gu::HeightField::modifySamples: startCol + nbColumns out of range"); + //PX_CHECK_AND_RETURN_NULL(startRow + desc.nbRows <= nbRows, + // "Gu::HeightField::modifySamples: startRow + nbRows out of range"); + //PX_CHECK_AND_RETURN_NULL(desc.samples.stride == mSampleStride, "Gu::HeightField::modifySamples: desc.samples.stride mismatch"); + + // by default bounds don't shrink since the whole point of this function is to avoid modifying the whole HF + // unless shrinkBounds is specified. then the bounds will be fully recomputed later + PxReal minHeight = mMinHeight; + PxReal maxHeight = mMaxHeight; + PxU32 hiRow = PxMin(PxU32(PxMax(0, startRow + PxI32(desc.nbRows))), nbRows); + PxU32 hiCol = PxMin(PxU32(PxMax(0, startCol + PxI32(desc.nbColumns))), nbCols); + for (PxU32 row = PxU32(PxMax(startRow, 0)); row < hiRow; row++) + { + for (PxU32 col = PxU32(PxMax(startCol, 0)); col < hiCol; col++) + { + const PxU32 vertexIndex = col + row*nbCols; + PxHeightFieldSample* targetSample = &mData.samples[vertexIndex]; + + // update target sample from source sample + const PxHeightFieldSample& sourceSample = + (reinterpret_cast<const PxHeightFieldSample*>(desc.samples.data))[col - startCol + (row - startRow) * desc.nbColumns]; + *targetSample = sourceSample; + + if(isCollisionVertexPreca(vertexIndex, row, col, PxHeightFieldMaterial::eHOLE)) + targetSample->materialIndex1.setBit(); + else + targetSample->materialIndex1.clearBit(); + + // grow (but not shrink) the height extents + const PxReal h = getHeight(vertexIndex); + minHeight = physx::intrinsics::selectMin(h, minHeight); + maxHeight = physx::intrinsics::selectMax(h, maxHeight); + } + } + + if (shrinkBounds) + { + // do a full recompute on vertical bounds to allow shrinking + minHeight = PX_MAX_REAL; + maxHeight = -PX_MAX_REAL; + // have to recompute the min&max from scratch... + for (PxU32 vertexIndex = 0; vertexIndex < nbRows * nbCols; vertexIndex ++) + { + // update height extents + const PxReal h = getHeight(vertexIndex); + minHeight = physx::intrinsics::selectMin(h, minHeight); + maxHeight = physx::intrinsics::selectMax(h, maxHeight); + } + } + mMinHeight = minHeight; + mMaxHeight = maxHeight; + + // update local space aabb + CenterExtents& bounds = mData.mAABB; + bounds.mCenter.y = (maxHeight + minHeight)*0.5f; + bounds.mExtents.y = (maxHeight - minHeight)*0.5f; + + mModifyCount++; + + return true; +} + +bool Gu::HeightField::load(PxInputStream& stream) +{ + // release old memory + releaseMemory(); + + // Import header + PxU32 version; + bool endian; + if(!readHeader('H', 'F', 'H', 'F', version, endian, stream)) + return false; + + // load mData + mData.rows = readDword(endian, stream); + mData.columns = readDword(endian, stream); + mData.rowLimit = readFloat(endian, stream); + mData.colLimit = readFloat(endian, stream); + mData.nbColumns = readFloat(endian, stream); + mData.thickness = readFloat(endian, stream); + mData.convexEdgeThreshold = readFloat(endian, stream); + + PxU16 flags = readWord(endian, stream); + mData.flags = PxHeightFieldFlags(flags); + + PxU32 format = readDword(endian, stream); + mData.format = PxHeightFieldFormat::Enum(format); + + PxBounds3 minMaxBounds; + minMaxBounds.minimum.x = readFloat(endian, stream); + minMaxBounds.minimum.y = readFloat(endian, stream); + minMaxBounds.minimum.z = readFloat(endian, stream); + minMaxBounds.maximum.x = readFloat(endian, stream); + minMaxBounds.maximum.y = readFloat(endian, stream); + minMaxBounds.maximum.z = readFloat(endian, stream); + mData.mAABB = CenterExtents(minMaxBounds); + + mSampleStride = readDword(endian, stream); + mNbSamples = readDword(endian, stream); + mMinHeight = readFloat(endian, stream); + mMaxHeight = readFloat(endian, stream); + + // allocate height samples + mData.samples = NULL; + const PxU32 nbVerts = mData.rows * mData.columns; + if (nbVerts > 0) + { + mData.samples = reinterpret_cast<PxHeightFieldSample*>(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample")); + if (mData.samples == NULL) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!"); + return false; + } + stream.read(mData.samples, mNbSamples*sizeof(PxHeightFieldSample)); + if (endian) + for(PxU32 i = 0; i < mNbSamples; i++) + { + PxHeightFieldSample& s = mData.samples[i]; + PX_ASSERT(sizeof(PxU16) == sizeof(s.height)); + flip(s.height); + } + } + + return true; +} + +bool Gu::HeightField::loadFromDesc(const PxHeightFieldDesc& desc) +{ + // verify descriptor + PX_CHECK_AND_RETURN_NULL(desc.isValid(), "Gu::HeightField::loadFromDesc: desc.isValid() failed!"); + + // release old memory + releaseMemory(); + + // copy trivial data + mData.format = desc.format; + mData.rows = desc.nbRows; + mData.columns = desc.nbColumns; + mData.thickness = desc.thickness; + mData.convexEdgeThreshold = desc.convexEdgeThreshold; + mData.flags = desc.flags; + mSampleStride = desc.samples.stride; + + // PT: precompute some data - mainly for Xbox + mData.rowLimit = float(mData.rows - 2); + mData.colLimit = float(mData.columns - 2); + mData.nbColumns = float(desc.nbColumns); + + // allocate and copy height samples + // compute extents too + mData.samples = NULL; + const PxU32 nbVerts = desc.nbRows * desc.nbColumns; + mMinHeight = PX_MAX_REAL; + mMaxHeight = -PX_MAX_REAL; + + if (nbVerts > 0) + { + mData.samples = reinterpret_cast<PxHeightFieldSample*>(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample")); + if (mData.samples == NULL) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!"); + return false; + } + const PxU8* PX_RESTRICT src = reinterpret_cast<const PxU8*>(desc.samples.data); + PxHeightFieldSample* PX_RESTRICT dst = mData.samples; + PxI16 minHeight = PX_MAX_I16; + PxI16 maxHeight = PX_MIN_I16; + for(PxU32 i=0;i<nbVerts;i++) + { + const PxHeightFieldSample& sample = *reinterpret_cast<const PxHeightFieldSample*>(src); + *dst++ = sample; + const PxI16 height = sample.height; + minHeight = height < minHeight ? height : minHeight; + maxHeight = height > maxHeight ? height : maxHeight; + src += desc.samples.stride; + } + mMinHeight = PxReal(minHeight); + mMaxHeight = PxReal(maxHeight); + } + + PX_ASSERT(mMaxHeight >= mMinHeight); + + parseTrianglesForCollisionVertices(PxHeightFieldMaterial::eHOLE); + +// PT: "mNbSamples" only used by binary converter + mNbSamples = mData.rows * mData.columns; + + //Compute local space aabb. + PxBounds3 bounds; + bounds.minimum.y = getMinHeight(); + bounds.maximum.y = getMaxHeight(); + + bounds.minimum.x = 0; + bounds.maximum.x = PxReal(getNbRowsFast() - 1); + bounds.minimum.z = 0; + bounds.maximum.z = PxReal(getNbColumnsFast() - 1); + mData.mAABB=bounds; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 Gu::HeightField::saveCells(void* destBuffer, PxU32 destBufferSize) const +{ + PxU32 n = mData.columns * mData.rows * sizeof(PxHeightFieldSample); + if (n > destBufferSize) n = destBufferSize; + PxMemCopy(destBuffer, mData.samples, n); + + return n; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PX_PHYSX_COMMON_API void Gu::HeightField::releaseMemory() +{ +// PX_SERIALIZATION + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) +//~PX_SERIALIZATION + { + PX_FREE(mData.samples); + mData.samples = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: use those faster functions everywhere +namespace physx +{ + +PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]) +{ + const PxU32 nbColumns = heightfield.getData().columns; + const PxU32 nbRows = heightfield.getData().rows; + PX_ASSERT((vertexIndex / nbColumns)==row); + PX_ASSERT((vertexIndex % nbColumns)==column); + + PxU32 count = 0; + + if (row > 0) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 2; + const PxU32 cell = vertexIndex - nbColumns; + edgeIndices[count].edgeIndex = 3 * cell + 2; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column; + count++; + } + + if (column < nbColumns-1) + { + if (row > 0) + { + if (!heightfield.isZerothVertexShared(vertexIndex - nbColumns)) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 1; + const PxU32 cell = vertexIndex - nbColumns; + edgeIndices[count].edgeIndex = 3 * cell + 1; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column; + count++; + } + } +// edgeIndices[count++] = 3 * vertexIndex; + edgeIndices[count].edgeIndex = 3 * vertexIndex; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + + if (row < nbRows - 1) + { + if (heightfield.isZerothVertexShared(vertexIndex)) + { +// edgeIndices[count++] = 3 * vertexIndex + 1; + edgeIndices[count].edgeIndex = 3 * vertexIndex + 1; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + } + } + } + + if (row < nbRows - 1) + { +// edgeIndices[count++] = 3 * vertexIndex + 2; + edgeIndices[count].edgeIndex = 3 * vertexIndex + 2; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + } + + if (column > 0) + { + if (row < nbRows - 1) + { + if (!heightfield.isZerothVertexShared(vertexIndex - 1)) + { +// edgeIndices[count++] = 3 * (vertexIndex - 1) + 1; + const PxU32 cell = vertexIndex - 1; + edgeIndices[count].edgeIndex = 3 * cell + 1; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row; + edgeIndices[count].column = column-1; + count++; + } + } +// edgeIndices[count++] = 3 * (vertexIndex - 1); + const PxU32 cell = vertexIndex - 1; + edgeIndices[count].edgeIndex = 3 * cell; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row; + edgeIndices[count].column = column-1; + count++; + if (row > 0) + { + if (heightfield.isZerothVertexShared(vertexIndex - nbColumns - 1)) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns - 1) + 1; + const PxU32 cell1 = vertexIndex - nbColumns - 1; + edgeIndices[count].edgeIndex = 3 * cell1 + 1; + edgeIndices[count].cell = cell1; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column-1; + count++; + } + } + } + return count; +} + +PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices) +{ + const PxU32 nbColumns = heightfield.getData().columns; + const PxU32 nbRows = heightfield.getData().rows; + + const PxU32 edgeIndex = edgeData.edgeIndex; + const PxU32 cell = edgeData.cell; + const PxU32 row = edgeData.row; + const PxU32 column = edgeData.column; + PX_ASSERT(cell==edgeIndex / 3); + PX_ASSERT(row==cell / nbColumns); + PX_ASSERT(column==cell % nbColumns); + PxU32 count = 0; + switch (edgeIndex - cell*3) + { + case 0: + if (column < nbColumns - 1) + { + if (row > 0) + { + if (heightfield.isZerothVertexShared(cell - nbColumns)) + triangleIndices[count++] = ((cell - nbColumns) << 1); + else + triangleIndices[count++] = ((cell - nbColumns) << 1) + 1; + } + if (row < nbRows - 1) + { + if (heightfield.isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1; + } + } + break; + case 1: + if ((row < nbRows - 1) && (column < nbColumns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < nbRows - 1) + { + if (column > 0) + { + triangleIndices[count++] = ((cell - 1) << 1) + 1; + } + if (column < nbColumns - 1) + { + triangleIndices[count++] = cell << 1; + } + } + break; + } + + return count; +} + +} + +PX_FORCE_INLINE PxU32 anyHole(PxU32 doubleMatIndex, PxU16 holeMaterialIndex) +{ + return PxU32((doubleMatIndex & 0xFFFF) == holeMaterialIndex) | (PxU32(doubleMatIndex >> 16) == holeMaterialIndex); +} + +void Gu::HeightField::parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex) +{ + const PxU32 nbColumns = getNbColumnsFast(); + const PxU32 nbRows = getNbRowsFast(); + + Cm::BitMap rowHoles[2]; + rowHoles[0].resizeAndClear(nbColumns + 1); + rowHoles[1].resizeAndClear(nbColumns + 1); + + for (PxU32 iCol = 0; iCol < nbColumns; iCol++) + { + if (anyHole(getMaterialIndex01(iCol), holeMaterialIndex)) + { + rowHoles[0].set(iCol); + rowHoles[0].set(iCol + 1); + } + PxU32 vertIndex = iCol; + if(isCollisionVertexPreca(vertIndex, 0, iCol, holeMaterialIndex)) + mData.samples[vertIndex].materialIndex1.setBit(); + else + mData.samples[vertIndex].materialIndex1.clearBit(); + } + + PxU32 nextRow = 1, currentRow = 0; + for (PxU32 iRow = 1; iRow < nbRows; iRow++) + { + PxU32 rowOffset = iRow*nbColumns; + for (PxU32 iCol = 0; iCol < nbColumns; iCol++) + { + const PxU32 vertIndex = rowOffset + iCol; // column index plus current row offset (vertex/cell index) + if(anyHole(getMaterialIndex01(vertIndex), holeMaterialIndex)) + { + rowHoles[currentRow].set(iCol); + rowHoles[currentRow].set(iCol + 1); + rowHoles[nextRow].set(iCol); + rowHoles[nextRow].set(iCol + 1); + } + + if ((iCol == 0) || (iCol == nbColumns - 1) || (iRow == nbRows - 1) || rowHoles[currentRow].test(iCol)) + { + if(isCollisionVertexPreca(vertIndex, iRow, iCol, holeMaterialIndex)) + mData.samples[vertIndex].materialIndex1.setBit(); + else + mData.samples[vertIndex].materialIndex1.clearBit(); + } else + { + if (isConvexVertex(vertIndex, iRow, iCol)) + mData.samples[vertIndex].materialIndex1.setBit(); + } + } + + rowHoles[currentRow].clear(); + + // swap prevRow and prevPrevRow + nextRow ^= 1; currentRow ^= 1; + } +} + +bool Gu::HeightField::isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex, bool& nbSolid) const +{ + // check if solid and boundary + // retrieve edge indices for current vertexIndex + EdgeData edgeIndices[8]; + const PxU32 edgeCount = ::getVertexEdgeIndices(*this, vertexIndex, row, column, edgeIndices); + + PxU32 faceCounts[8]; + PxU32 faceIndices[2 * 8]; + PxU32* dst = faceIndices; + for (PxU32 i = 0; i < edgeCount; i++) + { + faceCounts[i] = ::getEdgeTriangleIndices(*this, edgeIndices[i], dst); + dst += 2; + } + + nbSolid = false; + const PxU32* currentfaceIndices = faceIndices; // parallel array of pairs of face indices per edge index + for (PxU32 i = 0; i < edgeCount; i++) + { + if (faceCounts[i] > 1) + { + const PxU16& material0 = getTriangleMaterial(currentfaceIndices[0]); + const PxU16& material1 = getTriangleMaterial(currentfaceIndices[1]); + // ptchernev TODO: this is a bit arbitrary + if (material0 != holeMaterialIndex) + { + nbSolid = true; + if (material1 == holeMaterialIndex) + return true; // edge between solid and hole => return true + } + if (material1 != holeMaterialIndex) + { + nbSolid = true; + if (material0 == holeMaterialIndex) + return true; // edge between hole and solid => return true + } + } + else + { + if (getTriangleMaterial(currentfaceIndices[0]) != holeMaterialIndex) + return true; + } + currentfaceIndices += 2; // 2 face indices per edge + } + return false; +} + +bool Gu::HeightField::isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(isValidVertex(vertexIndex)); +#endif + PX_ASSERT((vertexIndex / getNbColumnsFast()) == row); + PX_ASSERT((vertexIndex % getNbColumnsFast()) == column); + + // check boundary conditions - boundary edges shouldn't produce collision with eNO_BOUNDARY_EDGES flag + if(mData.flags & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) + if ((row == 0) || (column == 0) || (row >= mData.rows-1) || (column >= mData.columns-1)) + return false; + + bool nbSolid; + if(isSolidVertex(vertexIndex, row, column, holeMaterialIndex, nbSolid)) + return true; + + // return true if it is boundary or solid and convex + return (nbSolid && isConvexVertex(vertexIndex, row, column)); +} + + +/*struct int64 +{ + int a,b; +};*/ + +#ifdef REMOVED +// PT: special version computing vertex index directly +PxU32 Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, PxU32 nbColumns, PxReal& fracX, PxReal& fracZ) const +{ + x = physx::intrinsics::selectMax(x, 0.0f); + z = physx::intrinsics::selectMax(z, 0.0f); + + PxU32 row = (PxU32)x; + PxU32 column = (PxU32)z; + +/*int64 tmp_x, tmp_z; +_asm lwz r11, x +_asm lfs fr0, 0(r11) +_asm fctiwz fr13, fr0 +_asm stfd fr13, tmp_x + +_asm lwz r11, z +_asm lfs fr0, 0(r11) +_asm fctiwz fr13, fr0 +_asm stfd fr13, tmp_z + +PxU32 row = tmp_x.b; +PX_ASSERT(row==PxU32(x));*/ + if (row > mData.rows - 2) + { + row = mData.rows - 2; + fracX = PxReal(1); + } + else + { + fracX = x - PxReal(row); + } + +//PxU32 column = tmp_z.b; +//PX_ASSERT(column==PxU32(z)); + + if (column > mData.columns - 2) + { + column = mData.columns - 2; + fracZ = PxReal(1); + } + else + { + fracZ = z - PxReal(column); + } + const PxU32 vertexIndex = row * nbColumns + column; + + return vertexIndex; +} +#endif + +// AP: this naming is confusing and inconsistent with return value. the function appears to compute vertex coord rather than cell coords +// it would most likely be better to stay in cell coords instead, since fractional vertex coords just do not make any sense +PxU32 Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, + PxReal& fracX, PxReal& fracZ) const +{ + namespace i = physx::intrinsics; + + x = i::selectMax(x, 0.0f); + z = i::selectMax(z, 0.0f); +#if 0 // validation code for scaled clamping epsilon computation + for (PxReal ii = 1.0f; ii < 100000.0f; ii+=1.0f) + { + PX_UNUSED(ii); + PX_ASSERT(PxFloor(ii+(1-1e-7f*ii)) == ii); + } +#endif + PxF32 epsx = 1.0f - PxAbs(x+1.0f) * 1e-6f; // epsilon needs to scale with values of x,z... + PxF32 epsz = 1.0f - PxAbs(z+1.0f) * 1e-6f; + PxF32 x1 = i::selectMin(x, mData.rowLimit+epsx); + PxF32 z1 = i::selectMin(z, mData.colLimit+epsz); + x = PxFloor(x1); + fracX = x1 - x; + z = PxFloor(z1); + fracZ = z1 - z; + PX_ASSERT(x >= 0.0f && x < PxF32(mData.rows)); + PX_ASSERT(z >= 0.0f && z < PxF32(mData.columns)); + + const PxU32 vertexIndex = PxU32(x * (mData.nbColumns) + z); + PX_ASSERT(vertexIndex < (mData.rows)*(mData.columns)); + + return vertexIndex; +} + +PxReal Gu::HeightField::computeExtreme(PxU32 minRow, PxU32 maxRow, PxU32 minColumn, PxU32 maxColumn) const +{ + const bool thicknessNegOrNull = (getThicknessFast() <= 0.0f); + +// PxReal hfExtreme = thicknessNegOrNull ? -PX_MAX_REAL : PX_MAX_REAL; + PxI32 hfExtreme = thicknessNegOrNull ? PX_MIN_I32 : PX_MAX_I32; + +/* for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxReal h = getHeight(row * getNbColumnsFast() + column); + hfExtreme = thicknessNegOrNull ? PxMax(hfExtreme, h) : PxMin(hfExtreme, h); + } + }*/ + + if(thicknessNegOrNull) + { + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { +// const PxReal h = getHeight(row * getNbColumnsFast() + column); + const PxI32 h = getSample(row * getNbColumnsFast() + column).height; + hfExtreme = PxMax(hfExtreme, h); + } + } + } + else + { + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { +// const PxReal h = getHeight(row * getNbColumnsFast() + column); + const PxI32 h = getSample(row * getNbColumnsFast() + column).height; + hfExtreme = PxMin(hfExtreme, h); + } + } + } + +// return hfExtreme; + return PxReal(hfExtreme); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h new file mode 100644 index 00000000..4cc9c6ba --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightField.h @@ -0,0 +1,1520 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_H +#define GU_HEIGHTFIELD_H + +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "PsMathUtils.h" +#include "GuSphere.h" +#include "PxHeightFieldSample.h" +#include "PxHeightFieldDesc.h" +#include "GuHeightFieldData.h" +#include "PxHeightField.h" + +//#define PX_HEIGHTFIELD_VERSION 0 +#define PX_HEIGHTFIELD_VERSION 1 // tiled version that was needed for PS3 only has been removed + +namespace physx +{ + +class GuMeshFactory; +class PxHeightFieldDesc; + +} + +namespace physx +{ +namespace Gu +{ +class HeightField : public PxHeightField, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + HeightField(PxBaseFlags baseFlags) : PxHeightField(baseFlags), Cm::RefCountable(PxEmpty), mData(PxEmpty), mModifyCount(0) {} + + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext&); + PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context); + PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; } + PX_PHYSX_COMMON_API static HeightField* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + void resolveReferences(PxDeserializationContext&) {} + virtual void requires(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + PX_PHYSX_COMMON_API HeightField(GuMeshFactory* meshFactory); + PX_PHYSX_COMMON_API HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data); + + // PxHeightField + PX_PHYSX_COMMON_API virtual void release(); + PX_PHYSX_COMMON_API virtual PxU32 saveCells(void* destBuffer, PxU32 destBufferSize) const; + PX_PHYSX_COMMON_API virtual bool modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& subfieldDesc, bool shrinkBounds); + PX_PHYSX_COMMON_API virtual PxU32 getNbRows() const { return mData.rows; } + PX_PHYSX_COMMON_API virtual PxU32 getNbColumns() const { return mData.columns; } + PX_PHYSX_COMMON_API virtual PxHeightFieldFormat::Enum getFormat() const { return mData.format; } + PX_PHYSX_COMMON_API virtual PxU32 getSampleStride() const { return sizeof(PxHeightFieldSample); } + PX_PHYSX_COMMON_API virtual PxReal getThickness() const { return mData.thickness; } + PX_PHYSX_COMMON_API virtual PxReal getConvexEdgeThreshold() const { return mData.convexEdgeThreshold; } + PX_PHYSX_COMMON_API virtual PxHeightFieldFlags getFlags() const { return mData.flags; } + PX_PHYSX_COMMON_API virtual PxReal getHeight(PxReal x, PxReal z) const { return getHeightInternal(x, z); } + + PX_PHYSX_COMMON_API virtual void acquireReference(); + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const; + //~PxHeightField + + // RefCountable + PX_PHYSX_COMMON_API virtual void onRefCountZero(); + //~RefCountable + PX_PHYSX_COMMON_API virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const + { + return getTriangleMaterial(triangleIndex); + } + PX_PHYSX_COMMON_API virtual PxVec3 getTriangleNormal(PxTriangleID triangleIndex) const + { + return getTriangleNormalInternal(triangleIndex); + } + + /** + \brief Returns the number of times the heightfield data has been modified + + Each time the heightfield is changed via 'modifySamples' this increments a counter. This method will return + the number of times the heightfield has been modified so that rendering code can know whether or not it needs to + rebuild the graphics representation of the mesh. + + \return the number of times the heightfield sample data has been modified. + */ + PX_PHYSX_COMMON_API virtual PxU32 getTimestamp() const + { + return mModifyCount; + } + + PX_PHYSX_COMMON_API bool loadFromDesc(const PxHeightFieldDesc&); + PX_PHYSX_COMMON_API bool load(PxInputStream&); + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbRowsFast() const { return mData.rows; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbColumnsFast() const { return mData.columns; } + PX_FORCE_INLINE PxHeightFieldFormat::Enum getFormatFast() const { return mData.format; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getThicknessFast() const { return mData.thickness; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return mData.flags; } + + PX_FORCE_INLINE bool isDeltaHeightInsideExtent(PxReal dy, PxReal eps = 0.0f) const + { + return (mData.thickness <= 0.0f && dy <= eps && dy >= mData.thickness) || + (mData.thickness > 0.0f && dy > -eps && dy < mData.thickness); + } + + PX_FORCE_INLINE bool isDeltaHeightOppositeExtent(PxReal dy) const + { + return (mData.thickness <= 0.0f && dy > 0.0f) || (mData.thickness > 0.0f && dy < 0.0f); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZerothVertexShared(PxU32 vertexIndex) const + { +// return (getSample(vertexIndex).tessFlag & PxHeightFieldTessFlag::e0TH_VERTEX_SHARED); + return getSample(vertexIndex).tessFlag() != 0; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex0(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex0; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex1(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex1; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaterialIndex01(PxU32 vertexIndex) const + { + const PxHeightFieldSample& sample = getSample(vertexIndex); + return PxU32(sample.materialIndex0 | (sample.materialIndex1 << 16)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getHeight(PxU32 vertexIndex) const + { + return PxReal(getSample(vertexIndex).height); + } + + PX_INLINE PxReal getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const; + PX_FORCE_INLINE PxReal getHeightInternal(PxReal x, PxReal z) const + { + PxReal fracX, fracZ; + const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ); + + return getHeightInternal2(vertexIndex, fracX, fracZ); + } + + PX_FORCE_INLINE bool isValidVertex(PxU32 vertexIndex) const { return vertexIndex < mData.rows*mData.columns; } + + PX_INLINE PxVec3 getVertex(PxU32 vertexIndex) const; + PX_INLINE bool isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + + PX_INLINE bool isValidEdge(PxU32 edgeIndex) const; + PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const; + PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const; + PX_INLINE void getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const; +// PX_INLINE bool isConvexEdge(PxU32 edgeIndex) const; + PX_INLINE bool isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const; + PX_FORCE_INLINE bool isConvexEdge(PxU32 edgeIndex) const + { + const PxU32 cell = edgeIndex / 3; + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; + return isConvexEdge(edgeIndex, cell, row, column); + } + +// PX_INLINE void computeCellCoordinates(PxReal x, PxReal z, PxU32& row, PxU32& column, PxReal& fracX, PxReal& fracZ) const; +// PX_INLINE PxU32 computeCellCoordinates(PxReal x, PxReal z, PxU32 nbColumns, PxReal& fracX, PxReal& fracZ) const; + PX_PHYSX_COMMON_API PxU32 computeCellCoordinates(PxReal x, PxReal z, PxReal& fracX, PxReal& fracZ) const; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinRow(PxReal x) const { return PxU32(PxClamp(PxI32(Ps::floor(x)), PxI32(0), PxI32(mData.rows-2))); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxRow(PxReal x) const { return PxU32(PxClamp(PxI32(Ps::ceil(x)), PxI32(0), PxI32(mData.rows-1))); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinColumn(PxReal z) const { return PxU32(PxClamp(PxI32(Ps::floor(z)), PxI32(0), PxI32(mData.columns-2))); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxColumn(PxReal z) const { return PxU32(PxClamp(PxI32(Ps::ceil(z)), PxI32(0), PxI32(mData.columns-1))); } + + PX_CUDA_CALLABLE PX_INLINE bool isValidTriangle(PxU32 triangleIndex) const; + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFirstTriangle(PxU32 triangleIndex) const { return ((triangleIndex & 0x1) == 0); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getTriangleMaterial(PxU32 triangleIndex) const + { + return isFirstTriangle(triangleIndex) ? getMaterialIndex0(triangleIndex >> 1) : getMaterialIndex1(triangleIndex >> 1); + } + + PX_CUDA_CALLABLE PX_INLINE void getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const; + PX_CUDA_CALLABLE PX_INLINE PxVec3 getTriangleNormalInternal(PxU32 triangleIndex) const; + PX_INLINE void getTriangleAdjacencyIndices(PxU32 triangleIndex,PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const; + + PX_INLINE PxVec3 getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const; + PX_FORCE_INLINE PxVec3 getNormal_(PxReal x, PxReal z, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const + { + PxReal fracX, fracZ; + const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ); + + return getNormal_2(vertexIndex, fracX, fracZ, xcoeff, ycoeff, zcoeff); + } + + PX_INLINE PxU32 getTriangleIndex(PxReal x, PxReal z) const; + PX_INLINE PxU32 getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const; + PX_FORCE_INLINE PxU16 getMaterial(PxReal x, PxReal z) const + { + return getTriangleMaterial(getTriangleIndex(x, z)); + } + + PX_FORCE_INLINE PxReal getMinHeight() const { return mMinHeight; } + PX_FORCE_INLINE PxReal getMaxHeight() const { return mMaxHeight; } + + PX_FORCE_INLINE const Gu::HeightFieldData& getData() const { return mData; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const; + + // checks if current vertex is solid or not + bool isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 coloumn, PxU16 holeMaterialIndex, + bool& nbSolid) const; + + // if precomputed bitmap define is used, the collision vertex information + // is precomputed during create height field and stored as a bit in materialIndex1 + PX_PHYSX_COMMON_API bool isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const; + PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32, PxU32, PxU16) const + { + return getSample(vertexIndex).materialIndex1.isBitSet()!=0; + } + void parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex); + + PX_PHYSX_COMMON_API PxReal computeExtreme(PxU32 minRow, PxU32 maxRow, PxU32 minColumn, PxU32 maxColumn) const; + + PX_FORCE_INLINE + PX_CUDA_CALLABLE const PxHeightFieldSample& getSample(PxU32 vertexIndex) const + { + PX_ASSERT(isValidVertex(vertexIndex)); + return mData.samples[vertexIndex]; + } + +#ifdef __CUDACC__ + PX_CUDA_CALLABLE void setSamplePtr(PxHeightFieldSample* s) { mData.samples = s; } +#endif + + Gu::HeightFieldData mData; + PxU32 mSampleStride; + PxU32 mNbSamples; // PT: added for platform conversion. Try to remove later. + PxReal mMinHeight; + PxReal mMaxHeight; + PxU32 mModifyCount; + // methods + PX_PHYSX_COMMON_API void releaseMemory(); + + PX_PHYSX_COMMON_API virtual ~HeightField(); + +private: + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization +}; + +} // namespace Gu + + + +PX_INLINE PxVec3 Gu::HeightField::getVertex(PxU32 vertexIndex) const +{ + const PxU32 row = vertexIndex / mData.columns; + const PxU32 column = vertexIndex % mData.columns; +// return PxVec3(PxReal(row), getHeight(row * mData.columns + column), PxReal(column)); + return PxVec3(PxReal(row), getHeight(vertexIndex), PxReal(column)); +} + +// PT: only called from "isCollisionVertex", should move +PX_INLINE bool Gu::HeightField::isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(isValidVertex(vertexIndex)); +#endif + PX_ASSERT((vertexIndex / mData.columns)==row); + PX_ASSERT((vertexIndex % mData.columns)==column); + +// PxReal h0 = PxReal(2) * getHeight(vertexIndex); + PxI32 h0 = getSample(vertexIndex).height; + h0 += h0; + + bool definedInX, definedInZ; + PxI32 convexityX, convexityZ; + + if ((row > 0) && (row < mData.rows - 1)) + { +// convexityX = h0 - getHeight(vertexIndex + mData.columns) - getHeight(vertexIndex - mData.columns); + convexityX = h0 - getSample(vertexIndex + mData.columns).height - getSample(vertexIndex - mData.columns).height; + definedInX = true; + } + else + { + convexityX = 0; + definedInX = false; + } + + if ((column > 0) && (column < mData.columns - 1)) + { +// convexityZ = h0 - getHeight(vertexIndex + 1) - getHeight(vertexIndex - 1); + convexityZ = h0 - getSample(vertexIndex + 1).height - getSample(vertexIndex - 1).height; + definedInZ = true; + } + else + { + convexityZ = 0; + definedInZ = false; + } + + if(definedInX || definedInZ) + { + // PT: use XOR here + // saddle points +/* if ((convexityX > 0) && (convexityZ < 0)) + return false; + if ((convexityX < 0) && (convexityZ > 0)) + return false;*/ + if(((convexityX ^ convexityZ) & 0x80000000)==0) + return false; + + // inequality depends on thickness and offset by threshold. + const PxReal value = PxReal(convexityX + convexityZ); + // PT: thickness is always the same for a given heightfield so the comparison shouldn't be here + if (mData.thickness <= 0) + return value > mData.convexEdgeThreshold; + else + return value < -mData.convexEdgeThreshold; + } + + // this has to be one of the two corner vertices + return true; +} + + +PX_INLINE bool Gu::HeightField::isValidEdge(PxU32 edgeIndex) const +{ + const PxU32 cell = (edgeIndex / 3); + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (row > mData.rows - 1) return false; + if (column >= mData.columns - 1) return false; + break; + case 1: + if (row >= mData.rows - 1) return false; + if (column >= mData.columns - 1) return false; + break; + case 2: + if (row >= mData.rows - 1) return false; + if (column > mData.columns - 1) return false; + break; + } + return true; +} + + +PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const +{ + const PxU32 cell = edgeIndex / 3; + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; + PxU32 count = 0; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (column < mData.columns - 1) + { + if (row > 0) + { +/* if (isZerothVertexShared(cell - mData.columns)) + triangleIndices[count++] = ((cell - mData.columns) << 1); + else + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/ + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns); + } + if (row < mData.rows - 1) + { +/* if (isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1;*/ + triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell); + } + } + break; + case 1: + if ((row < mData.rows - 1) && (column < mData.columns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < mData.rows - 1) + { + if (column > 0) + triangleIndices[count++] = ((cell - 1) << 1) + 1; + if (column < mData.columns - 1) + triangleIndices[count++] = cell << 1; + } + break; + } + return count; +} + +PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; +// const PxU32 row = cell / mData.columns; +// const PxU32 column = cell % mData.columns; + PxU32 count = 0; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (column < mData.columns - 1) + { + if (row > 0) + { +/* if (isZerothVertexShared(cell - mData.columns)) + triangleIndices[count++] = ((cell - mData.columns) << 1); + else + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/ + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns); + } + if (row < mData.rows - 1) + { +/* if (isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1;*/ + triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell); + } + } + break; + case 1: + if ((row < mData.rows - 1) && (column < mData.columns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < mData.rows - 1) + { + if (column > 0) + triangleIndices[count++] = ((cell - 1) << 1) + 1; + if (column < mData.columns - 1) + triangleIndices[count++] = cell << 1; + } + break; + } + return count; +} + + +PX_INLINE void Gu::HeightField::getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const +{ + const PxU32 cell = edgeIndex / 3; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + vertexIndex0 = cell; + vertexIndex1 = cell + 1; + break; + case 1: + { +/* if (isZerothVertexShared(cell)) + { + vertexIndex0 = cell; + vertexIndex1 = cell + mData.columns + 1; + } + else + { + vertexIndex0 = cell + 1; + vertexIndex1 = cell + mData.columns; + }*/ + const bool b = isZerothVertexShared(cell); + vertexIndex0 = cell + 1 - b; + vertexIndex1 = cell + mData.columns + b; + } + break; + case 2: + vertexIndex0 = cell; + vertexIndex1 = cell + mData.columns; + break; + } +} + +#ifdef REMOVED +PX_INLINE bool Gu::HeightField::isConvexEdge(PxU32 edgeIndex) const +{ + const PxU32 cell = edgeIndex / 3; + + const PxU32 row = cell / mData.columns; + if (row > mData.rows-2) return false; + + const PxU32 column = cell % mData.columns; + if (column > mData.columns-2) return false; + +// PxReal h0 = 0, h1 = 0, h2 = 0, h3 = 0; +// PxReal convexity = 0; + PxI32 h0 = 0, h1 = 0, h2 = 0, h3 = 0; + PxI32 convexity = 0; + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (row < 1) return false; +/* if(isZerothVertexShared(cell - mData.columns)) + { + // <------ COL + // +----+ 0 R + // | / /# O + // | / / # W + // | / / # | + // |/ / # | + // + +====1 | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns); +// h1 = getHeight(cell); + h0 = getSample(cell - mData.columns).height; + h1 = getSample(cell).height; + } + else + { + // <------ COL + // 0 +----+ R + // #\ \ | O + // # \ \ | W + // # \ \ | | + // # \ \| | + // 1====+ + | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns + 1); +// h1 = getHeight(cell + 1); + h0 = getSample(cell - mData.columns + 1).height; + h1 = getSample(cell + 1).height; + }*/ + const bool b0 = !isZerothVertexShared(cell - mData.columns); + h0 = getSample(cell - mData.columns + b0).height; + h1 = getSample(cell + b0).height; + +/* if(isZerothVertexShared(cell)) + { + // <------ COL + // R + // O + // W + // | + // | + // | + // 2====+ 0 | + // # / /| | + // # / / | | + // # / / | | + // #/ / | | + // 3 +----+ | + // V + // +// h2 = getHeight(cell + 1); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + 1).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <------ COL + // R + // O + // W + // | + // | + // | + // + +====2 | + // |\ \ # | + // | \ \ # | + // | \ \ # | + // | \ \# | + // +----+ 3 | + // V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + mData.columns); + h2 = getSample(cell).height; + h3 = getSample(cell + mData.columns).height; + }*/ + const bool b1 = isZerothVertexShared(cell); + h2 = getSample(cell + b1).height; + h3 = getSample(cell + mData.columns + b1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + break; + case 1: +// h0 = getHeight(cell); +// h1 = getHeight(cell + 1); +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h0 = getSample(cell).height; + h1 = getSample(cell + 1).height; + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + if (isZerothVertexShared(cell)) + //convex = (h0 + h3) > (h1 + h2); + convexity = (h0 + h3) - (h1 + h2); + else + //convex = (h2 + h1) > (h0 + h3); + convexity = (h2 + h1) - (h0 + h3); + break; + case 2: + if (column < 1) return false; +/* if(isZerothVertexShared(cell-1)) + { + // <-------------- COL + // 1====0 + R + // + / /| O + // + / / | W + // + / / | | + // +/ / | | + // + +----+ V + // +// h0 = getHeight(cell - 1); +// h1 = getHeight(cell); + h0 = getSample(cell - 1).height; + h1 = getSample(cell).height; + } + else + { + // <-------------- COL + // + +----+ R + // +\ \ | O + // + \ \ | W + // + \ \ | | + // + \ \| | + // 1====0 + V + // +// h0 = getHeight(cell - 1 + mData.columns); +// h1 = getHeight(cell + mData.columns); + h0 = getSample(cell - 1 + mData.columns).height; + h1 = getSample(cell + mData.columns).height; + }*/ + const PxU32 offset0 = isZerothVertexShared(cell-1) ? 0 : mData.columns; + h0 = getSample(cell - 1 + offset0).height; + h1 = getSample(cell + offset0).height; + +/* if(isZerothVertexShared(cell)) + { + // <-------------- COL + // +----+ + R + // | / /+ O + // | / / + W + // | / / + | + // |/ / + | + // + 3====2 V + // +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <-------------- COL + // + 3====2 R + // |\ \ + O + // | \ \ + W + // | \ \ + | + // | \ \+ | + // +----+ + V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + 1); + h2 = getSample(cell).height; + h3 = getSample(cell + 1).height; + }*/ + const PxU32 offset1 = isZerothVertexShared(cell) ? mData.columns : 0; + h2 = getSample(cell + offset1).height; + h3 = getSample(cell + offset1 + 1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + break; + } + + const PxI32 threshold = PxI32(mData.convexEdgeThreshold); + if (mData.thickness <= 0) + { +// return convexity > mData.convexEdgeThreshold; + return convexity > threshold; + } + else + { +// return convexity < -mData.convexEdgeThreshold; + return convexity < -threshold; + } +} +#endif + +PX_INLINE bool Gu::HeightField::isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); + +// const PxU32 row = cell / mData.columns; + PX_ASSERT(row == cell / mData.columns); + if (row > mData.rows-2) return false; + +// const PxU32 column = cell % mData.columns; + PX_ASSERT(column == cell % mData.columns); + if (column > mData.columns-2) return false; + +// PxReal h0 = 0, h1 = 0, h2 = 0, h3 = 0; +// PxReal convexity = 0; + PxI32 h0 = 0, h1 = 0, h2 = 0, h3 = 0; + PxI32 convexity = 0; + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + if (row < 1) return false; +/* if(isZerothVertexShared(cell - mData.columns)) + { + // <------ COL + // +----+ 0 R + // | / /# O + // | / / # W + // | / / # | + // |/ / # | + // + +====1 | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns); +// h1 = getHeight(cell); + h0 = getSample(cell - mData.columns).height; + h1 = getSample(cell).height; + } + else + { + // <------ COL + // 0 +----+ R + // #\ \ | O + // # \ \ | W + // # \ \ | | + // # \ \| | + // 1====+ + | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns + 1); +// h1 = getHeight(cell + 1); + h0 = getSample(cell - mData.columns + 1).height; + h1 = getSample(cell + 1).height; + }*/ + const bool b0 = !isZerothVertexShared(cell - mData.columns); + h0 = getSample(cell - mData.columns + b0).height; + h1 = getSample(cell + b0).height; + +/* if(isZerothVertexShared(cell)) + { + // <------ COL + // R + // O + // W + // | + // | + // | + // 2====+ 0 | + // # / /| | + // # / / | | + // # / / | | + // #/ / | | + // 3 +----+ | + // V + // +// h2 = getHeight(cell + 1); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + 1).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <------ COL + // R + // O + // W + // | + // | + // | + // + +====2 | + // |\ \ # | + // | \ \ # | + // | \ \ # | + // | \ \# | + // +----+ 3 | + // V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + mData.columns); + h2 = getSample(cell).height; + h3 = getSample(cell + mData.columns).height; + }*/ + const bool b1 = isZerothVertexShared(cell); + h2 = getSample(cell + b1).height; + h3 = getSample(cell + mData.columns + b1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + } + break; + case 1: +// h0 = getHeight(cell); +// h1 = getHeight(cell + 1); +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h0 = getSample(cell).height; + h1 = getSample(cell + 1).height; + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + if (isZerothVertexShared(cell)) + //convex = (h0 + h3) > (h1 + h2); + convexity = (h0 + h3) - (h1 + h2); + else + //convex = (h2 + h1) > (h0 + h3); + convexity = (h2 + h1) - (h0 + h3); + break; + case 2: + { + if (column < 1) return false; +/* if(isZerothVertexShared(cell-1)) + { + // <-------------- COL + // 1====0 + R + // + / /| O + // + / / | W + // + / / | | + // +/ / | | + // + +----+ V + // +// h0 = getHeight(cell - 1); +// h1 = getHeight(cell); + h0 = getSample(cell - 1).height; + h1 = getSample(cell).height; + } + else + { + // <-------------- COL + // + +----+ R + // +\ \ | O + // + \ \ | W + // + \ \ | | + // + \ \| | + // 1====0 + V + // +// h0 = getHeight(cell - 1 + mData.columns); +// h1 = getHeight(cell + mData.columns); + h0 = getSample(cell - 1 + mData.columns).height; + h1 = getSample(cell + mData.columns).height; + }*/ + const PxU32 offset0 = isZerothVertexShared(cell-1) ? 0 : mData.columns; + h0 = getSample(cell - 1 + offset0).height; + h1 = getSample(cell + offset0).height; + +/* if(isZerothVertexShared(cell)) + { + // <-------------- COL + // +----+ + R + // | / /+ O + // | / / + W + // | / / + | + // |/ / + | + // + 3====2 V + // +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <-------------- COL + // + 3====2 R + // |\ \ + O + // | \ \ + W + // | \ \ + | + // | \ \+ | + // +----+ + V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + 1); + h2 = getSample(cell).height; + h3 = getSample(cell + 1).height; + }*/ + const PxU32 offset1 = isZerothVertexShared(cell) ? mData.columns : 0; + h2 = getSample(cell + offset1).height; + h3 = getSample(cell + offset1 + 1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + } + break; + } + + const PxI32 threshold = PxI32(mData.convexEdgeThreshold); + if (mData.thickness <= 0) + { +// return convexity > mData.convexEdgeThreshold; + return convexity > threshold; + } + else + { +// return convexity < -mData.convexEdgeThreshold; + return convexity < -threshold; + } +} +#ifdef REMOVED +PX_INLINE void Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, PxU32& _row, PxU32& _column, PxReal& _fracX, PxReal& _fracZ) const +{ + x = physx::intrinsics::selectMax(x, 0.0f); + z = physx::intrinsics::selectMax(z, 0.0f); + + PxU32 row = (PxU32)x; + PxReal fracX = x - PxReal(row); + if (row > mData.rows - 2) + { + row = mData.rows - 2; + fracX = PxReal(1); + } + + PxU32 column = (PxU32)z; + PxReal fracZ = z - PxReal(column); + if (column > mData.columns - 2) + { + column = mData.columns - 2; + fracZ = PxReal(1); + } + + _row = row; + _column = column; + _fracX = fracX; + _fracZ = fracZ; +} +#endif + +PX_INLINE bool Gu::HeightField::isValidTriangle(PxU32 triangleIndex) const +{ + const PxU32 cell = triangleIndex >> 1; + const PxU32 row = cell / mData.columns; + if (row >= (mData.rows - 1)) return false; + const PxU32 column = cell % mData.columns; + if (column >= (mData.columns - 1)) return false; + return true; +} + + + + +PX_INLINE void Gu::HeightField::getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const +{ + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { + vertexIndex0 = cell + mData.columns; + vertexIndex1 = cell; + vertexIndex2 = cell + mData.columns + 1; + } + else + { + vertexIndex0 = cell + 1; + vertexIndex1 = cell + mData.columns + 1; + vertexIndex2 = cell; + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { + vertexIndex0 = cell; + vertexIndex1 = cell + 1; + vertexIndex2 = cell + mData.columns; + } + else + { + vertexIndex0 = cell + mData.columns + 1; + vertexIndex1 = cell + mData.columns; + vertexIndex2 = cell + 1; + } + } +} + +PX_INLINE void Gu::HeightField::getTriangleAdjacencyIndices(PxU32 triangleIndex, PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const +{ + PX_UNUSED(vertexIndex0); + PX_UNUSED(vertexIndex1); + PX_UNUSED(vertexIndex2); + + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex + 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if((cell % (mData.columns) != 0)) + { + adjacencyIndex0 = triangleIndex - 1; + } + + if((cell / mData.columns != mData.rows - 2)) + { + adjacencyIndex2 = ((cell + mData.columns) * 2) + 1; + } + } + else + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex - 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if(cell % (mData.columns) < (mData.columns - 2)) + { + adjacencyIndex0 = triangleIndex + 1; + } + + if(cell >= mData.columns - 1) + { + adjacencyIndex2 = (cell - mData.columns) * 2; + } + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex + 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if(cell >= mData.columns - 1) + { + adjacencyIndex0 = ((cell - (mData.columns)) * 2) + 1; + } + + if((cell % (mData.columns) != 0)) + { + adjacencyIndex2 = triangleIndex - 1; + } + } + else + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex - 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if((cell / mData.columns != mData.rows - 2)) + { + adjacencyIndex0 = (cell + (mData.columns)) * 2; + } + + if(cell % (mData.columns) < (mData.columns - 2)) + { + adjacencyIndex2 = triangleIndex + 1; + } + } + } +} + +PX_INLINE PxVec3 Gu::HeightField::getTriangleNormalInternal(PxU32 triangleIndex) const +{ + PxU32 v0, v1, v2; + getTriangleVertexIndices(triangleIndex, v0, v1, v2); + +// const PxReal h0 = getHeight(v0); +// const PxReal h1 = getHeight(v1); +// const PxReal h2 = getHeight(v2); + const PxI32 h0 = getSample(v0).height; + const PxI32 h1 = getSample(v1).height; + const PxI32 h2 = getSample(v2).height; + + // Fix for NvBug 685420 +// if(mThickness>0.0f) +// n = -n; + const PxReal coeff = physx::intrinsics::fsel(mData.thickness, -1.0f, 1.0f); + +// PxVec3 n(0,1,0); + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { +// n.x = -(h0-h1); +// n.z = -(h2-h0); + return PxVec3(coeff*PxReal(h1-h0), coeff, coeff*PxReal(h0-h2)); + } + else + { +// n.x = -(h1-h0); +// n.z = -(h0-h2); + return PxVec3(coeff*PxReal(h0-h1), coeff, coeff*PxReal(h2-h0)); + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { +// n.x = -(h2-h0); +// n.z = -(h1-h0); + return PxVec3(coeff*PxReal(h0-h2), coeff, coeff*PxReal(h0-h1)); + } + else + { +// n.x = -(h0-h2); +// n.z = -(h0-h1); + return PxVec3(coeff*PxReal(h2-h0), coeff, coeff*PxReal(h1-h0)); + } + } +// return n; +} + +PX_INLINE PxReal Gu::HeightField::getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const +{ + if (isZerothVertexShared(vertexIndex)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V + const PxReal h0 = getHeight(vertexIndex); + const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); + if (fracZ > fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V + const PxReal h1 = getHeight(vertexIndex + 1); + return h0 + fracZ*(h1-h0) + fracX*(h2-h1); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V + const PxReal h1 = getHeight(vertexIndex + mData.columns); + return h0 + fracX*(h1-h0) + fracZ*(h2-h1); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + const PxReal h2 = getHeight(vertexIndex + mData.columns); + const PxReal h1 = getHeight(vertexIndex + 1); + if (fracX + fracZ < 1.0f) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V + const PxReal h0 = getHeight(vertexIndex); + return h0 + fracZ*(h1-h0) + fracX*(h2-h0); + } + else + { + // <----Z---+ + // 1 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----2 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin + const PxReal h0 = getHeight(vertexIndex + mData.columns + 1); + return h0 + (1.0f - fracZ)*(h2-h0) + (1.0f - fracX)*(h1-h0); + } + } +} + +PX_INLINE PxVec3 Gu::HeightField::getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const +{ + PxVec3 normal; + if (isZerothVertexShared(vertexIndex)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); + const PxI32 ih0 = getSample(vertexIndex).height; + const PxI32 ih2 = getSample(vertexIndex + mData.columns + 1).height; + if (fracZ >= fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + 1); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h2-h1), 1.0f, -(h1-h0)); + const PxI32 ih1 = getSample(vertexIndex + 1).height; + normal = PxVec3(PxReal(ih1 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + mData.columns); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h1-h0), 1.0f, -(h2-h1)); + const PxI32 ih1 = getSample(vertexIndex + mData.columns).height; + normal = PxVec3(PxReal(ih0 - ih1)*xcoeff, ycoeff, PxReal(ih1 - ih2)*zcoeff); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + const PxI32 ih1 = getSample(vertexIndex + 1).height; + const PxI32 ih2 = getSample(vertexIndex + mData.columns).height; + if (fracX + fracZ <= PxReal(1)) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + 1); +// const PxReal h2 = getHeight(vertexIndex + mData.columns); +// normal.set(-(h2-h0), 1.0f, -(h1-h0)); + const PxI32 ih0 = getSample(vertexIndex).height; +// const PxI32 ih1 = getSample(vertexIndex + 1).height; +// const PxI32 ih2 = getSample(vertexIndex + mData.columns).height; + normal = PxVec3(PxReal(ih0 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff); + } + else + { + // <----Z---+ + // 2 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----1 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin +// const PxReal h2 = getHeight(vertexIndex + 1); +// const PxReal h1 = getHeight(vertexIndex + mData.columns); +// const PxReal h0 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h0-h2), 1.0f, -(h0-h1)); +// const PxI32 ih2 = getSample(vertexIndex + 1).height; +// const PxI32 ih1 = getSample(vertexIndex + mData.columns).height; + const PxI32 ih0 = getSample(vertexIndex + mData.columns + 1).height; +// normal.set(PxReal(ih2 - ih0), 1.0f, PxReal(ih1b - ih0)); + normal = PxVec3(PxReal(ih1 - ih0)*xcoeff, ycoeff, PxReal(ih2 - ih0)*zcoeff); + } + } + return (mData.thickness <= 0.0f) ? normal : -normal; +} + +PX_INLINE PxU32 Gu::HeightField::getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const +{ + if (isZerothVertexShared(cell)) + return (fracZ > fracX) ? (cell << 1) + 1 : (cell << 1); + else + return (fracX + fracZ > 1) ? (cell << 1) + 1 : (cell << 1); +} + +PX_INLINE PxU32 Gu::HeightField::getTriangleIndex(PxReal x, PxReal z) const +{ + PxReal fracX, fracZ; + const PxU32 cell = computeCellCoordinates(x, z, fracX, fracZ); + + return getTriangleIndex2(cell, fracX, fracZ); +} + +/** +Although inefficient, this is used for particles (PxcHeightFieldAabbTest.h). +*/ +PX_FORCE_INLINE void Gu::HeightField::getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const +{ + PxU32 cell = triangleIndex >> 1; + PX_ASSERT(row * getNbColumnsFast() + column == cell); + + PxReal h0 = getHeight(cell); + PxReal h1 = getHeight(cell + 1); + PxReal h2 = getHeight(cell + getNbColumnsFast()); + PxReal h3 = getHeight(cell + getNbColumnsFast() + 1); + + if (isFirstTriangle(triangleIndex)) + { + if (isZerothVertexShared(cell)) + { + // <---- COL + // 1 R + // /| O + // / | W + // / | | + // / 0 | | + // 2----0 V + // + v0 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + v1 = PxVec3(PxReal(row ), h0, PxReal(column )); + v2 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + } + else + { + // <---- COL + // 1----0 R + // \ 0 | O + // \ | W + // \ | | + // \| | + // 2 V + // + v0 = PxVec3(PxReal(row ), h0, PxReal(column )); + v1 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + v2 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + } + } + else + { + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 R + // | 1 / O + // | / W + // | / | + // |/ | + // 1 V + // + v0 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + v1 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + v2 = PxVec3(PxReal(row ), h0, PxReal(column )); + } + else + { + // <---- COL + // 2 R + // |\ O + // | \ W + // | \ | + // | 1 \ | + // 0----1 V + // + v0 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + v1 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + v2 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + } + } +} + +struct EdgeData +{ + PxU32 edgeIndex; + PxU32 cell; + PxU32 row; + PxU32 column; +}; +PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]); +PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices); + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h new file mode 100644 index 00000000..74c746dc --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldData.h @@ -0,0 +1,88 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_DATA_H +#define GU_HEIGHTFIELD_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "PxHeightFieldFlag.h" +#include "PxHeightFieldSample.h" +#include "GuCenterExtents.h" + +namespace physx +{ + +namespace Gu +{ + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif +struct PX_PHYSX_COMMON_API HeightFieldData +{ +// PX_SERIALIZATION + PX_FORCE_INLINE HeightFieldData() {} + PX_FORCE_INLINE HeightFieldData(const PxEMPTY) : flags(PxEmpty) {} +//~PX_SERIALIZATION + + //properties + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU32 rows; // PT: WARNING: don't change this member's name (used in ConvX) + PxU32 columns; // PT: WARNING: don't change this member's name (used in ConvX) + PxReal rowLimit; // PT: to avoid runtime int-to-float conversions on Xbox + PxReal colLimit; // PT: to avoid runtime int-to-float conversions on Xbox + PxReal nbColumns; // PT: to avoid runtime int-to-float conversions on Xbox + PxHeightFieldSample* samples; // PT: WARNING: don't change this member's name (used in ConvX) + PxReal thickness; + PxReal convexEdgeThreshold; + + PxHeightFieldFlags flags; + + PxHeightFieldFormat::Enum format; + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert below + return static_cast<const CenterExtentsPadded&>(mAABB); + } +}; +#if PX_VC + #pragma warning(pop) +#endif + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::HeightFieldData, rows)>=PX_OFFSET_OF(Gu::HeightFieldData, mAABB)+4); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp new file mode 100644 index 00000000..a5ffaccf --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.cpp @@ -0,0 +1,1055 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "GuHeightFieldUtil.h" +#include "GuSweepSharedTests.h" + +#include "PsFoundation.h" +#include "GuHeightField.h" +#include "GuEntityReport.h" +#include "PxMeshScale.h" + +using namespace physx; + +void Gu::HeightFieldUtil::computeLocalBounds(PxBounds3& bounds) const +{ + const PxMeshScale scale(PxVec3(mHfGeom->rowScale, mHfGeom->heightScale, mHfGeom->columnScale), PxQuat(PxIdentity)); + const PxMat33 mat33 = scale.toMat33(); + + bounds.minimum = mat33.transform(mHeightField->getData().mAABB.getMin()); + bounds.maximum = mat33.transform(mHeightField->getData().mAABB.getMax()); + + if(mHeightField->getData().thickness < 0) + bounds.minimum.y += mHeightField->getData().thickness; + else + bounds.maximum.y += mHeightField->getData().thickness; +} + +PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePoint(PxReal x, PxReal z) const +{ + if (isShapePointOnHeightField(x, z)) + { + const PxU32 triangleIndex = mHeightField->getTriangleIndex(x * mOneOverRowScale, z * mOneOverColumnScale); + return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getFaceIndexAtTriangleIndex(PxU32 triangleIndex) const +{ + return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePointNoTest(PxReal x, PxReal z) const +{ + PX_ASSERT(isShapePointOnHeightField(x, z)); + + const PxU32 triangleIndex = mHeightField->getTriangleIndex(x * mOneOverRowScale, z * mOneOverColumnScale); + return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePointNoTest2(PxU32 cell, PxReal fracX, PxReal fracZ) const +{ + const PxU32 triangleIndex = mHeightField->getTriangleIndex2(cell, fracX, fracZ); + return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff; +} + + +PxVec3 Gu::HeightFieldUtil::getSmoothNormalAtShapePoint(PxReal x, PxReal z) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(getFaceIndexAtShapePoint(x, z) != 0xffffffff); +#endif + x *= mOneOverRowScale; + z *= mOneOverColumnScale; + + PxReal fracX, fracZ; + const PxU32 cell = mHeightField->computeCellCoordinates(x, z, fracX, fracZ); + + if (mHeightField->isZerothVertexShared(cell)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V + if (fracZ > fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V + const PxVec3 n0 = getVertexNormal(cell); + const PxVec3 n1 = getVertexNormal(cell + 1); + const PxVec3 n2 = getVertexNormal(cell + mHeightField->getNbColumnsFast() + 1); + return n0 + fracZ*(n1-n0) + fracX*(n2-n1); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V + const PxVec3 n0 = getVertexNormal(cell); + const PxVec3 n1 = getVertexNormal(cell + mHeightField->getNbColumnsFast()); + const PxVec3 n2 = getVertexNormal(cell + mHeightField->getNbColumnsFast() + 1); + return n0 + fracX*(n1-n0) + fracZ*(n2-n1); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + if (fracX + fracZ < 1) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V + const PxVec3 n0 = getVertexNormal(cell); + const PxVec3 n1 = getVertexNormal(cell + 1); + const PxVec3 n2 = getVertexNormal(cell + mHeightField->getNbColumnsFast()); + return n0 + fracZ*(n1-n0) + fracX*(n2-n0); + } + else + { + // <----Z---+ + // 2 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----1 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin + const PxVec3 n0 = getVertexNormal(cell + mHeightField->getNbColumnsFast() + 1); + const PxVec3 n1 = getVertexNormal(cell + mHeightField->getNbColumnsFast()); + const PxVec3 n2 = getVertexNormal(cell + 1); + return n0 + (1-fracZ)*(n1-n0) + (1-fracX)*(n2 - n0); + } + } +} + +PxVec3 Gu::HeightFieldUtil::getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidVertex(vertexIndex)); +#endif + // PxU32 edges[8]; + // const PxU32 edgeCount = mHeightField.getVertexEdgeIndices(vertexIndex, edges); + + //const PxU32 nbColumns = mHeightField.getData().columns; + //const PxU32 row = vertexIndex / nbColumns; + //const PxU32 column = vertexIndex % nbColumns; + PX_ASSERT(row == vertexIndex / mHeightField->getData().columns); + PX_ASSERT(column == vertexIndex % mHeightField->getData().columns); + EdgeData edgeIndices[8]; + const PxU32 edgeCount = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices); + + + PxVec3 n(0.0f); + PxVec3 tn; + for (PxU32 i=0; i<edgeCount; i++) + { + PxU32 faces[2]; + // const PxU32 faceCount = mHeightField.getEdgeTriangleIndices(edges[i], faces); + const PxU32 faceCount = ::getEdgeTriangleIndices(*mHeightField, edgeIndices[i], faces); + + for(PxU32 j=0; j<faceCount; j++) + { + if (mHeightField->getTriangleMaterial(faces[j]) != PxHeightFieldMaterial::eHOLE) + { + tn = hf2shapen(mHeightField->getTriangleNormalInternal(faces[j])).getNormalized(); + n+=tn; + } + } + } + + return n.getNormalized(); +} + +PxU32 Gu::HeightFieldUtil::findClosestPointsOnCell( + PxU32 row, PxU32 column, PxVec3 point, + PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes, + bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const +{ + PxU32 count = 0; + + const PxU32 offset = row * mHeightField->getNbColumnsFast() + column; + const PxU32 firstEdgeIndex = 3 * offset; + + // ptchernev TODO: + // move the material assignments to an else in the ifs on triangle material + // instead of doing it all the time + + PX_ASSERT(row < (mHeightField->getNbRowsFast() - 1)); + PX_ASSERT(column < (mHeightField->getNbColumnsFast() - 1)); + const bool lastRow = (row == (mHeightField->getNbRowsFast() - 2)); + const bool lastColumn = (column == (mHeightField->getNbColumnsFast() - 2)); + + bool testVertex0 = testEdges; + bool testColumnEdge0 = testEdges; + bool testRowEdge0 = testEdges; + bool testDiagonal = testEdges; + bool testVertex1 = lastColumn && testEdges; + bool testVertex2 = lastRow && testEdges; + + bool testRowEdge1 = lastColumn && testEdges; + bool testColumnEdge1 = lastRow && testEdges; + bool testVertex3 = lastRow && lastColumn && testEdges; + + const PxU32 triangleIndex0 = offset << 1; + const PxMaterialTableIndex materialIndex0 = mHeightField->getTriangleMaterial(triangleIndex0); + const PxU32 triangleIndex1 = triangleIndex0 + 1; + const PxMaterialTableIndex materialIndex1 = mHeightField->getTriangleMaterial(triangleIndex1); + + if (testFaces) + { + if (materialIndex0 != PxHeightFieldMaterial::eHOLE) + { + // face 0 + PxVec3 closestPoint; + if (findProjectionOnTriangle(triangleIndex0, row, column, point, closestPoint)) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex0, eFACE); + count++; + testRowEdge0 = false; + testVertex0 = false; + testVertex2 = false; + testDiagonal = false; + } + } + + if (materialIndex1 != PxHeightFieldMaterial::eHOLE) + { + // face 1 + PxVec3 closestPoint; + if (findProjectionOnTriangle(triangleIndex1, row, column, point, closestPoint)) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex1, eFACE); + count++; + testRowEdge1 = false; + testVertex1 = false; + testVertex3 = false; + testDiagonal = false; + } + } + if (!testEdges) + return count; + } + + // if there were any face contacts and we asked to skip edges if face contacts, return current count here + if (count && skipEdgesIfFaceHits) + return count; + + const PxU32 nbColumns = mHeightField->getNbColumnsFast(); + if (testVertex0 || testColumnEdge0 || testVertex1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex, offset, row, column, point, closestPoint); + if (t <= 0) + { + if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX); + count++; + } + testVertex0 = false; + } + else if (t < 1) + { + if (testColumnEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex, eEDGE); + count++; + } + } + else + { + if (testVertex1 && 0xffffffff != (getVertexFaceIndex(offset + 1, row, column + 1))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column+1, eVERTEX); + count++; + } + } + } + + if (testVertex0 || testRowEdge0 || testVertex2) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 2, offset, row, column, point, closestPoint); + if (t <= 0) + { + if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX); + count++; + } + } + else if(t < 1) + { + if (testRowEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 2))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+2, eEDGE); + count++; + } + } + else + { + if (testVertex2 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns, row + 1, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column, eVERTEX); + count++; + } + } + } + + if (testColumnEdge1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 3 * nbColumns, offset + nbColumns, row + 1, column, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + const PxU32 edgeIndex3 = firstEdgeIndex + 3 * nbColumns; + if (0xffffffff != (getEdgeFaceIndex(edgeIndex3))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(edgeIndex3, eEDGE); + count++; + } + } + } + + if (testRowEdge1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 5, offset + 1, row, column + 1, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + if (0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 5))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+5, eEDGE); + count++; + } + } + } + + if (testVertex3 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns + 1, row + 1, column + 1))) + { + closestPoints[count] = PxVec3((row + 1) * mHfGeom->rowScale, mHfGeom->heightScale * mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1), (column + 1) * mHfGeom->columnScale); + if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column+1, eVERTEX); + count++; + } + + if (testDiagonal && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 1))) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 1, offset, row, column, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+1, eEDGE); + count++; + } + } + + return count; +} + +//PxReal Gu::HeightFieldUtil::findClosestPointOnEdge(PxU32 edgeIndex, const PxVec3& point, PxVec3& closestPoint) const +PxReal Gu::HeightFieldUtil::findClosestPointOnEdge( + PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField->getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); +// const PxU32 column = cell % mHeightField->getNbColumnsFast(); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + + PxVec3 origin, direction; + PxReal lengthSquared; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y1 - y0; + direction = PxVec3(0, dy, mHfGeom->columnScale); + lengthSquared = mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + break; + case 1: + if (mHeightField->isZerothVertexShared(cell)) + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y3 - y0; + direction = PxVec3(mHfGeom->rowScale, dy, mHfGeom->columnScale); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + else + { + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + const PxReal dy = y2 - y1; + direction = PxVec3(mHfGeom->rowScale, dy, -mHfGeom->columnScale); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + break; + case 2: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y2 - y0; + direction = PxVec3(mHfGeom->rowScale, dy, 0); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + dy * dy; + } + break; + default: + origin = direction = PxVec3(PxReal(0)); + lengthSquared = 0.0f; + PX_ALWAYS_ASSERT_MESSAGE("Invalid edge index in findClosestPointOnEdge"); + } // switch (edgeIndex % 3) + + const PxVec3 relative = point - origin; + const PxReal t = relative.dot(direction) / lengthSquared; + if (t < 0) + closestPoint = origin; + else if (t > 1) + closestPoint = origin + direction; + else + closestPoint = origin + direction * t; + + return t; +} + +PxU32 Gu::HeightFieldUtil::getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidVertex(vertexIndex)); +#endif + +// PxU32 edgeIndices[8]; +// const PxU32 count = mHeightField->getVertexEdgeIndices(vertexIndex, edgeIndices); + +//const PxU32 nbColumns = mHeightField->getData().columns; +//const PxU32 row = vertexIndex / nbColumns; +//const PxU32 column = vertexIndex % nbColumns; +PX_ASSERT(row == vertexIndex / mHeightField->getData().columns); +PX_ASSERT(column == vertexIndex % mHeightField->getData().columns); +EdgeData edgeIndices[8]; +const PxU32 count = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices); + + for (PxU32 i = 0; i<count; i+= 2) + { + const PxU32 index = getEdgeFaceIndex(edgeIndices[i].edgeIndex, edgeIndices[i].cell, edgeIndices[i].row, edgeIndices[i].column); + if (index != 0xffffffff) return index; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices, cell, row, column); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const +{ + PX_UNUSED(edgeIndex); + +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif +// PxU32 faceIndices[2]; +// const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +bool Gu::HeightFieldUtil::findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const +{ + const PxU32 cell = (triangleIndex >> 1); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + PxVec3 origin; + PxReal h0, h1, h2, uInvScale, vInvScale; + + // specify a triangle according to current triangle index as origin, 3 heights and (uInvScale, vInvScale) vector for (z,x) + // set uInvScale in h1-h0; vScale in h2-h0 direction + if (mHeightField->isZerothVertexShared(cell)) + { + // COLUMN --> + // + // R 0---1 + // O |\ 1| + // W | \ | + // | |0 \| + // | 2---3 + // V + if ((triangleIndex & 1) == 0) + { + // case 0 + // face 0 + origin = PxVec3((row + 1) * mHfGeom->rowScale, y2, column * mHfGeom->columnScale); + // origin -> 2 + // verts -> 2,3,0 + h0 = y2; + h1 = y3; + h2 = y0; + uInvScale = mOneOverColumnScale; + vInvScale = -mOneOverRowScale; + } + else // if (testFace1) + { + // case 1 + // face 1 + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + // origin -> 1 + // verts -> 1,0,3 + h0 = y1; + h1 = y0; + h2 = y3; + uInvScale = -mOneOverColumnScale; + vInvScale = mOneOverRowScale; + } + } + else + { + // COLUMN --> + // + // R 0---1 + // O |0 /| + // W | / | + // | |/ 1| + // | 2---3 + // V + if ((triangleIndex & 1) == 0) + { + // case 2 + // face 0 + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + // origin -> 0 + // verts -> 0,1,2 + h0 = y0; + h1 = y1; + h2 = y2; + uInvScale = mOneOverColumnScale; + vInvScale = mOneOverRowScale; + } + else + { + // case 3 + // face 1 + origin = PxVec3((row + 1) * mHfGeom->rowScale, y3, (column + 1) * mHfGeom->columnScale); + // origin -> 3 + // verts -> 3,2,1 + h0 = y3; + h1 = y2; + h2 = y1; + uInvScale = -mOneOverColumnScale; + vInvScale = -mOneOverRowScale; + } + } + + // vector from triangle origin to point we want to project + const PxVec3 relative = point - origin; + + // Looking at the triangle diagram for case 2 + // The normal computation should be + // n = (p1-p0) x (p2-p0) + // For a right handed cross product that's pointing into the screen (negative h), so -n is in the direction of increasing h + // cs = column scale, rs = row scale, h10 = h1-h0; u=column, v=row + // (i j k); + // p1-p0 = (cs, h10, 0); this is column, u and z + // p2-p0 = (0, h20, rs); this is row, v and x + // n = (h10*rs, -cs*rs, +cs*h20) + // n/(cs*rs) = (h10/cs, -1, h20/rs) + // -n = (-h10/cs, 1, -h20/rs) + PxReal h10 = h1-h0, h20 = h2-h0; + PxReal nu = -h10 * uInvScale; + PxReal nv = -h20 * vInvScale; + + PxVec3 n(nv, 1.0f, nu); // for whatever reason.. x is v, z is u. + //n *= 1.0f / PxSqrt(nu*nu + nv*nv + 1.0f); // technically we need to do this but since later + + // project relative onto the n plane, it gives us unclipped projection onto the triangle + // the computation without sqrt shortcut is relPrj = relative - n.dot(relative)*n, but because we divide by sqrt^2 we skip sqrt + PxVec3 relPrj = relative - n.dot(relative)* (1.0f/(nu*nu+nv*nv+1.0f)) * n; + + // project relPrj onto 2d UV plane with h = 0 oriented according to vInvScale, uInvScale (for x and z) + // to convert to HF cell coords we'd multiply by inv scale, after that the coords should be >0 and the sum within 1 to be + // inside of a 2d triangle + PxReal scaledX = relPrj.x * vInvScale, scaledZ = relPrj.z * uInvScale; + //PxVec3 testProjection = relPrj + origin; + //PxVec3 testN = (point - testProjection).getNormalized(); + if (scaledX > 0.0f && scaledZ > 0.0f && scaledX + scaledZ < 1.0f) + { + projection = relPrj + origin; + return true; + } + + return false; +} + +bool Gu::HeightFieldUtil::isCollisionEdge(PxU32 edgeIndex) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif + + // This code was simple, readable but slow + // return isBoundaryEdge(edgeIndex) || (mHeightField->isConvexEdge(edgeIndex) && isSolidEdge(edgeIndex)); + + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]); + PxMaterialTableIndex mat1 = mHeightField->getTriangleMaterial(faceIndices[1]); + if (mat0 == PxHeightFieldMaterial::eHOLE) return (mat1 != PxHeightFieldMaterial::eHOLE); + if (mat1 == PxHeightFieldMaterial::eHOLE) return (mat0 != PxHeightFieldMaterial::eHOLE); + } + else + { + if (mHeightField->getFlagsFast() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) return false; + PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]); + return (mat0 != PxHeightFieldMaterial::eHOLE); + } + + return mHeightField->isConvexEdge(edgeIndex); +} + +bool Gu::HeightFieldUtil::isCollisionEdge(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices, PxU32 cell, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif + + // This code was simple, readable but slow + // return isBoundaryEdge(edgeIndex) || (mHeightField->isConvexEdge(edgeIndex) && isSolidEdge(edgeIndex)); + +// PxU32 faceIndices[2]; +// const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]); + PxMaterialTableIndex mat1 = mHeightField->getTriangleMaterial(faceIndices[1]); + if (mat0 == PxHeightFieldMaterial::eHOLE) return (mat1 != PxHeightFieldMaterial::eHOLE); + if (mat1 == PxHeightFieldMaterial::eHOLE) return (mat0 != PxHeightFieldMaterial::eHOLE); + } + else + { + if (mHeightField->getFlagsFast() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) return false; + PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]); + return (mat0 != PxHeightFieldMaterial::eHOLE); + } + +// return mHeightField->isConvexEdge(edgeIndex); + return mHeightField->isConvexEdge(edgeIndex, cell, row, column); +} + +void Gu::HeightFieldUtil::getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField->getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); +// const PxU32 column = cell % mHeightField->getNbColumnsFast(); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(0, y1 - y0, mHfGeom->columnScale); + } + break; + case 1: + if (mHeightField->isZerothVertexShared(cell)) + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y3 - y0, mHfGeom->columnScale); + } + else + { + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y2 - y1, -mHfGeom->columnScale); + } + break; + case 2: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y2 - y0, 0); + } + break; + } +} + +bool Gu::HeightFieldUtil::overlapAABBTriangles( + const PxTransform& pose, const PxBounds3& bounds, PxU32 flags, EntityReport<PxU32>* callback) const +{ + PxBounds3 localBounds = bounds; + + if(flags & GuHfQueryFlags::eWORLD_SPACE) + { + PX_ASSERT(!localBounds.isEmpty()); + localBounds = PxBounds3::transformFast(pose.getInverse(), localBounds); + } + + localBounds.minimum.x *= mOneOverRowScale; + localBounds.minimum.y *= mOneOverHeightScale; + localBounds.minimum.z *= mOneOverColumnScale; + + localBounds.maximum.x *= mOneOverRowScale; + localBounds.maximum.y *= mOneOverHeightScale; + localBounds.maximum.z *= mOneOverColumnScale; + + if (mHfGeom->rowScale < 0) + { + PxReal swap = localBounds.minimum.x; + localBounds.minimum.x = localBounds.maximum.x; + localBounds.maximum.x = swap; + } + + if (mHfGeom->columnScale < 0) + { + PxReal swap = localBounds.minimum.z; + localBounds.minimum.z = localBounds.maximum.z; + localBounds.maximum.z = swap; + } + + // early exit for aabb does not overlap in XZ plane + // DO NOT MOVE: since rowScale / columnScale may be negative this has to be done after scaling localBounds + if (localBounds.minimum.x > mHeightField->getNbRowsFast() - 1) + return false; + if (localBounds.minimum.z > mHeightField->getNbColumnsFast() - 1) + return false; + if (localBounds.maximum.x < 0) + return false; + if (localBounds.maximum.z < 0) + return false; + + PxU32 minRow = mHeightField->getMinRow(localBounds.minimum.x); + PxU32 maxRow = mHeightField->getMaxRow(localBounds.maximum.x); + PxU32 minColumn = mHeightField->getMinColumn(localBounds.minimum.z); + PxU32 maxColumn = mHeightField->getMaxColumn(localBounds.maximum.z); + + PxU32 maxNbTriangles = 2 * (maxColumn - minColumn) * (maxRow - minRow); + + if (maxNbTriangles == 0) + return false; + + if (flags & GuHfQueryFlags::eFIRST_CONTACT) maxNbTriangles = 1; + + static const PxU32 bufferSize = HF_SWEEP_REPORT_BUFFER_SIZE; + PxU32 indexBuffer[bufferSize]; + PxU32 indexBufferUsed = 0; + PxU32 nb = 0; + + PxU32 offset = minRow * mHeightField->getNbColumnsFast() + minColumn; + + const PxReal& miny = localBounds.minimum.y; + const PxReal& maxy = localBounds.maximum.y; + + for (PxU32 row = minRow; row < maxRow; row++) + { + for (PxU32 column = minColumn; column < maxColumn; column++) + { + PxReal h0 = mHeightField->getHeight(offset); + PxReal h1 = mHeightField->getHeight(offset + 1); + PxReal h2 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast()); + PxReal h3 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1); + if (!((maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3) || (miny > h0 && miny > h1 && miny > h2 && miny > h3))) + { + PxU32 material0 = mHeightField->getMaterialIndex0(offset); + if (material0 != PxHeightFieldMaterial::eHOLE) + { + if(indexBufferUsed >= bufferSize) + { + callback->onEvent(indexBufferUsed, indexBuffer); + indexBufferUsed = 0; + } + + indexBuffer[indexBufferUsed++] = offset << 1; + nb++; + + if (flags & GuHfQueryFlags::eFIRST_CONTACT) goto search_done; + } + + PxU32 material1 = mHeightField->getMaterialIndex1(offset); + if (material1 != PxHeightFieldMaterial::eHOLE) + { + if(indexBufferUsed >= bufferSize) + { + callback->onEvent(indexBufferUsed, indexBuffer); + indexBufferUsed = 0; + } + + indexBuffer[indexBufferUsed++] = (offset << 1) + 1; + nb++; + + if (flags & GuHfQueryFlags::eFIRST_CONTACT) goto search_done; + } + } + offset++; + } + offset += (mHeightField->getNbColumnsFast() - (maxColumn - minColumn)); + } + +search_done: + + if(indexBufferUsed > 0) + callback->onEvent(indexBufferUsed, indexBuffer); + + return nb > 0; +} + +PxU32 Gu::HeightFieldUtil::getTriangle(const PxTransform& pose, PxTriangle& worldTri, + PxU32* _vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation, bool worldSpaceRotation) const +{ +#if PX_CHECKED + if (!mHeightField->isValidTriangle(triangleIndex)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Invalid triangle index!"); + return 0; + } +#endif + + PxVec3 handedness(1.0f); // Vector to invert normal coordinates according to the heightfield scales + bool wrongHanded = false; + if (mHfGeom->columnScale < 0) + { + wrongHanded = !wrongHanded; + handedness.z = -1.0f; + } + if (mHfGeom->rowScale < 0) + { + wrongHanded = !wrongHanded; + handedness.x = -1.0f; + } + if (mHeightField->getThicknessFast() > 0) + { + wrongHanded = !wrongHanded; + handedness.y = -1.0f; + } + +/* if (0) // ptchernev: Iterating over triangles becomes a pain. + { + if (mHeightField.getTriangleMaterial(triangleIndex) == mHfGeom.holeMaterialIndex) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Non-existing triangle (triangle has hole material)!"); + return 0; + } + }*/ + + PxU32 vertexIndices[3]; + mHeightField->getTriangleVertexIndices(triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded]); + + if(adjacencyIndices) + { + mHeightField->getTriangleAdjacencyIndices( triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded], + adjacencyIndices[wrongHanded ? 2 : 0], adjacencyIndices[1], adjacencyIndices[wrongHanded ? 0 : 2]); + } + + if(_vertexIndices) + { + _vertexIndices[0] = vertexIndices[0]; + _vertexIndices[1] = vertexIndices[1]; + _vertexIndices[2] = vertexIndices[2]; + } + + if (worldSpaceRotation) + { + if (worldSpaceTranslation) + { + for (PxU32 vi = 0; vi < 3; vi++) + worldTri.verts[vi] = hf2worldp(pose, mHeightField->getVertex(vertexIndices[vi])); + } + else + { + for (PxU32 vi = 0; vi < 3; vi++) + { + // TTP 2390 + // local space here is rotated (but not translated) world space + worldTri.verts[vi] = pose.q.rotate(hf2shapep(mHeightField->getVertex(vertexIndices[vi]))); + } + } + } + else + { + const PxVec3 offset = worldSpaceTranslation ? pose.p : PxVec3(0.0f); + for (PxU32 vi = 0; vi < 3; vi++) + worldTri.verts[vi] = hf2shapep(mHeightField->getVertex(vertexIndices[vi])) + offset; + } + return PxU32(mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE); +} + + +bool Gu::HeightFieldUtil::isBoundaryEdge(PxU32 edgeIndex) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField.isValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + const PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]); + const PxMaterialTableIndex mat1 = mHeightField->getTriangleMaterial(faceIndices[1]); + if (mat0 == PxHeightFieldMaterial::eHOLE) return (mat1 != PxHeightFieldMaterial::eHOLE); + if (mat1 == PxHeightFieldMaterial::eHOLE) return (mat0 != PxHeightFieldMaterial::eHOLE); + } + else + { + const PxMaterialTableIndex mat0 = mHeightField->getTriangleMaterial(faceIndices[0]); + return (mat0 != PxHeightFieldMaterial::eHOLE); + } + return false; +} + + +/*PxReal Gu::HeightFieldUtil::getHeightAtShapePoint(PxReal x, PxReal z) const +{ + return mHfGeom.heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale); +}*/ + + +/* +PxVec3 Gu::HeightFieldUtil::getNormalAtShapePoint(PxReal x, PxReal z) const +{ + return hf2shapen(mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale)); +} +*/ diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h new file mode 100644 index 00000000..08db5883 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuHeightFieldUtil.h @@ -0,0 +1,1532 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_UTIL_H +#define GU_HEIGHTFIELD_UTIL_H + +#include "PxHeightFieldGeometry.h" +#include "GuHeightField.h" +#include "PxTriangle.h" +#include "../intersection/GuIntersectionRayTriangle.h" +#include "../intersection/GuIntersectionRayBox.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +#define HF_SWEEP_REPORT_BUFFER_SIZE 64 + +/** +\brief Used to control contact queries. +*/ +struct GuHfQueryFlags +{ + enum Enum + { + eWORLD_SPACE = (1<<0), //!< world-space parameter, else object space + eFIRST_CONTACT = (1<<1) //!< returns first contact only, else returns all contacts + }; +}; + +namespace Gu +{ + template<class T> class EntityReport; + + class PX_PHYSX_COMMON_API HeightFieldUtil + { + private: + + PX_CUDA_CALLABLE PX_FORCE_INLINE void initialize() + { + const PxReal absRowScale = PxAbs(mHfGeom->rowScale); + const PxReal absColScale = PxAbs(mHfGeom->columnScale); + //warning #1931-D on WIIU: sizeof is not a type, variable, or dereferenced pointer expression + PX_ASSERT(sizeof(reinterpret_cast<PxHeightFieldSample*>(0)->height) == 2); + //PxReal minHeightPerSample = PX_MIN_HEIGHTFIELD_Y_SCALE; + PX_ASSERT(mHfGeom->heightScale >= PX_MIN_HEIGHTFIELD_Y_SCALE); + PX_ASSERT(absRowScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE); + PX_ASSERT(absColScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE); + PX_UNUSED(absRowScale); + PX_UNUSED(absColScale); + //using physx::intrinsics::fsel; + //mOneOverHeightScale = fsel(mHfGeom->heightScale - minHeightPerSample, 1.0f / mHfGeom->heightScale, 1.0f / minHeightPerSample); + mOneOverHeightScale = 1.0f / mHfGeom->heightScale; + mOneOverRowScale = 1.0f / mHfGeom->rowScale; + mOneOverColumnScale = 1.0f / mHfGeom->columnScale; + } + + PxReal mOneOverRowScale; + PxReal mOneOverHeightScale; + PxReal mOneOverColumnScale; + const Gu::HeightField* mHeightField; + const PxHeightFieldGeometry* mHfGeom; + + public: + + //sschirm: added empty ctor for gpu shared mem allocation + PX_FORCE_INLINE HeightFieldUtil() {} + + PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom, const Gu::HeightField& hf) : mHeightField(&hf), mHfGeom(&hfGeom) + { + initialize(); + } + + PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom) : mHeightField(static_cast<const Gu::HeightField*>(hfGeom.heightField)), mHfGeom(&hfGeom) + { + initialize(); + } + + //sschirm: initialize with PxHeightFieldGeometry and Gu::HeightField for gpu shared mem allocation + PX_CUDA_CALLABLE PX_FORCE_INLINE void initialize(const PxHeightFieldGeometry& hfGeom, const Gu::HeightField& hf) + { + mHeightField = &hf; + mHfGeom = &hfGeom; + initialize(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Gu::HeightField& getHeightField() const { return *mHeightField; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxHeightFieldGeometry& getHeightFieldGeometry() const { return *mHfGeom; } + + PX_FORCE_INLINE PxReal getOneOverRowScale() const { return mOneOverRowScale; } + PX_FORCE_INLINE PxReal getOneOverHeightScale() const { return mOneOverHeightScale; } + PX_FORCE_INLINE PxReal getOneOverColumnScale() const { return mOneOverColumnScale; } + + void computeLocalBounds(PxBounds3& bounds) const; + PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE); + } + PX_CUDA_CALLABLE bool isCollisionEdge(PxU32 edgeIndex) const; + bool isCollisionEdge(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices, PxU32 cell, PxU32 row, PxU32 column) const; + bool isBoundaryEdge(PxU32 edgeIndex) const; +// PxReal getHeightAtShapePoint(PxReal x, PxReal z) const; + PX_FORCE_INLINE PxReal getHeightAtShapePoint(PxReal x, PxReal z) const + { + return mHfGeom->heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale); + } + PX_FORCE_INLINE PxReal getHeightAtShapePoint2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const + { + return mHfGeom->heightScale * mHeightField->getHeightInternal2(vertexIndex, fracX, fracZ); + } + +// PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const; + PX_FORCE_INLINE PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const + { + return mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale); + } + PX_FORCE_INLINE PxVec3 getNormalAtShapePoint2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const + { + return mHeightField->getNormal_2(vertexIndex, fracX, fracZ, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale); + } + + PxU32 getFaceIndexAtShapePoint(PxReal x, PxReal z) const; + PxU32 getFaceIndexAtShapePointNoTest(PxReal x, PxReal z) const; + PxU32 getFaceIndexAtShapePointNoTest2(PxU32 cell, PxReal fracX, PxReal fracZ) const; + PxU32 getFaceIndexAtTriangleIndex(PxU32 triangleIndex) const; + + PxVec3 getSmoothNormalAtShapePoint(PxReal x, PxReal z) const; + + PxVec3 getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const; +// PxVec3 getVertexNormal(PxU32 vertexIndex) const; + PX_FORCE_INLINE PxVec3 getVertexNormal(PxU32 vertexIndex) const + { + const PxU32 nbColumns = mHeightField->getData().columns; + const PxU32 row = vertexIndex / nbColumns; + const PxU32 column = vertexIndex % nbColumns; + return getVertexNormal(vertexIndex, row, column); + } + + PxU32 getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + void getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const; + + PxU32 getEdgeFaceIndex(PxU32 edgeIndex) const; + PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const; + PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const; + + enum Feature { eFACE, eEDGE, eVERTEX }; + static PX_INLINE Feature getFeatureType(PxU32 featureCode) { return Feature(featureCode >> 30); } + static PX_INLINE PxU32 getFeatureIndex(PxU32 featureCode) { return (featureCode & ~0xC0000000); } + static PX_INLINE PxU32 makeFeatureCode(PxU32 index, Feature type) { return (index | (PxU32(type) << 30)); } + + // possible improvement: face index and edge index can be folded into one incremental feature index (along with vert index) + // such as, 0: vert index for cell, 1,2: edge indices, 3,4: face indices, then wrap around in multiples of 5 or 8 + // all return arrays must have a size of 11 to accomodate the possible 11 contacts + // the feature index in featureIndices array is encoded as follows: + // the high 2 bits are 00 - face, 01 - edge, 10 - vertex, 11 - unused/hole + PxU32 findClosestPointsOnCell( + PxU32 row, PxU32 column, PxVec3 point, + PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes, + bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const; + + bool findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const; + +// PxReal findClosestPointOnEdge(PxU32 edgeIndex, const PxVec3& point, PxVec3& closestPoint) const; + PxReal findClosestPointOnEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const; + + PxU32 getTriangle(const PxTransform&, PxTriangle& worldTri, + PxU32* vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation=true, bool worldSpaceRotation=true) const; + bool overlapAABBTriangles(const PxTransform&, const PxBounds3& bounds, PxU32 flags, EntityReport<PxU32>* callback) const; + + // check's if vertex can be used for scene query - note it is not the same as collision vertex + // the difference here is because of the eNO_BOUNDARY_EDGES flag, which should not ignore boundary edges. + // We don t have precomputed data for this case, we need to manually check these vertices if they are inside + // a hole or not - check if the vertex isSolid + PX_FORCE_INLINE bool isQueryVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + // if noBoundaryEdges flag is on, we need to check the solid vertices manually for + // vertices which are on the boundaries, as those data are not precomputed + if((mHeightField->getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) && + (row == 0 || column == 0 || (row >= mHeightField->getNbRowsFast() - 1) || (column >= mHeightField->getNbColumnsFast() - 1))) + { + // early exit if the material0 for the vertex is not a hole + if(mHeightField->getMaterialIndex0(vertexIndex) != PxHeightFieldMaterial::eHOLE) + return true; + bool nbSolid; + return mHeightField->isSolidVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE, nbSolid); + } + else + { + return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE); + } + } + + PX_FORCE_INLINE PxVec3 computePointNormal(PxU32 meshFlags, const PxVec3& d, const PxTransform& transform, PxReal l_sqr, PxReal x, PxReal z, PxReal epsilon, PxReal& l) const + { + PX_UNUSED(meshFlags); + + PxVec3 n; + if(l_sqr > epsilon) + { + n = transform.rotate(d); + l = n.normalize(); + } + else // l == 0 + { + n = transform.rotate(getNormalAtShapePoint(x, z)); + n = n.getNormalized(); + l = PxSqrt(l_sqr); + } + return n; + } + + PX_INLINE bool isShapePointOnHeightField(PxReal x, PxReal z) const + { + x *= mOneOverRowScale; + z *= mOneOverColumnScale; +/* return ((!(x < 0)) + && (!(z < 0)) + && (x < (mHeightField.getNbRowsFast()-1)) + && (z < (mHeightField.getNbColumnsFast()-1)));*/ + return ((x >= 0.0f) + && (z >= 0.0f) + && (x < (mHeightField->getData().rowLimit+1.0f)) + && (z < (mHeightField->getData().colLimit+1.0f))); + } + + // floor and ceil don't clamp down exact integers but we want that + static PX_FORCE_INLINE PxF32 floorDown(PxF32 x) { PxF32 f = PxFloor(x); return (f == x) ? f-1 : f; } + static PX_FORCE_INLINE PxF32 ceilUp (PxF32 x) { PxF32 f = PxCeil (x); return (f == x) ? f+1 : f; } + + // helper class for testing triangle height and reporting the overlapped triangles + template<class T> + class OverlapTraceSegment + { + public: + // helper rectangle struct + struct OverlapRectangle + { + PxI32 mMinu; + PxI32 mMaxu; + PxI32 mMinv; + PxI32 mMaxv; + + void invalidate() + { + mMinu = 1; + mMaxu = -1; + mMinv = 1; + mMaxv = -1; + } + }; + + // helper line struct + struct OverlapLine + { + bool mColumn; + PxI32 mLine; + PxI32 mMin; + PxI32 mMax; + + void invalidate() + { + mMin = 1; + mMax = -1; + } + }; + + public: + void operator = (OverlapTraceSegment&) {} + + OverlapTraceSegment(const HeightFieldUtil& hfUtil,const Gu::HeightField& hf) + : mInitialized(false), mHfUtil(hfUtil), mHf(hf), mNbIndices(0) {} + + PX_FORCE_INLINE bool initialized() const { return mInitialized; } + + // prepare for iterations, set the expand u|v + PX_INLINE void prepare(const PxVec3& aP0, const PxVec3& aP1, const PxVec3& overlapObjectExtent, PxF32& expandu, PxF32& expandv) + { + // height test bounds + mMinY = (PxMin(aP1.y,aP0.y) - overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale(); + mMaxY = (PxMax(aP1.y,aP0.y) + overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale(); + + // sets the clipping variables + mMinRow = PxI32(mHf.getMinRow((PxMin(aP1.x,aP0.x) - overlapObjectExtent.x)* mHfUtil.getOneOverRowScale())); + mMaxRow = PxI32(mHf.getMaxRow((PxMax(aP1.x,aP0.x) + overlapObjectExtent.x)* mHfUtil.getOneOverRowScale())); + mMinColumn = PxI32(mHf.getMinColumn((PxMin(aP1.z,aP0.z) - overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale())); + mMaxColumn = PxI32(mHf.getMaxColumn((PxMax(aP1.z,aP0.z) + overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale())); + + // sets the expanded u|v coordinates + expandu = PxCeil(overlapObjectExtent.x*mHfUtil.getOneOverRowScale()); + expandv = PxCeil(overlapObjectExtent.z*mHfUtil.getOneOverColumnScale()); + + // sets the offset that will be overlapped in each axis + mOffsetU = PxI32(expandu) + 1; + mOffsetV = PxI32(expandv) + 1; + } + + // sets all necessary variables and makes initial rectangle setup and overlap + PX_INLINE bool init(const PxI32 ui, const PxI32 vi, const PxI32 nbVi, const PxI32 step_ui, const PxI32 step_vi, T* aCallback) + { + mInitialized = true; + mCallback = aCallback; + mNumColumns = nbVi; + mStep_ui = step_ui > 0 ? 0 : -1; + mStep_vi = step_vi > 0 ? 0 : -1; + + // sets the rectangles + mCurrentRectangle.invalidate(); + mPreviousRectangle.mMinu = ui - mOffsetU; + mPreviousRectangle.mMaxu = ui + mOffsetU; + mPreviousRectangle.mMinv = vi - mOffsetV; + mPreviousRectangle.mMaxv = vi + mOffsetV; + + // visits all cells in given initial rectangle + if(!visitCells(mPreviousRectangle)) + return false; + + // reports all overlaps + if(!reportOverlaps()) + return false; + + return true; + } + + // u|v changed, check for new rectangle - compare with previous one and parse + // the added line, which is a result from the rectangle compare + PX_INLINE bool step(const PxI32 ui, const PxI32 vi) + { + mCurrentRectangle.mMinu = ui - mOffsetU; + mCurrentRectangle.mMaxu = ui + mOffsetU; + mCurrentRectangle.mMinv = vi - mOffsetV; + mCurrentRectangle.mMaxv = vi + mOffsetV; + OverlapLine line; + computeRectangleDifference(mCurrentRectangle,mPreviousRectangle,line); + + if(!visitCells(line)) + return false; + if(!reportOverlaps()) + return false; + + mPreviousRectangle = mCurrentRectangle; + return true; + } + + PX_INLINE void computeRectangleDifference(const OverlapRectangle& currentRectangle, const OverlapRectangle& previousRectangle, OverlapLine& line) + { + // check if u changes - add the row for visit + if(currentRectangle.mMinu != previousRectangle.mMinu) + { + line.mColumn = false; + line.mLine = currentRectangle.mMinu < previousRectangle.mMinu ? currentRectangle.mMinu : currentRectangle.mMaxu; + line.mMin = currentRectangle.mMinv; + line.mMax = currentRectangle.mMaxv; + return; + } + + // check if v changes - add the column for visit + if(currentRectangle.mMinv != previousRectangle.mMinv) + { + line.mColumn = true; + line.mLine = currentRectangle.mMinv < previousRectangle.mMinv ? currentRectangle.mMinv : currentRectangle.mMaxv; + line.mMin = currentRectangle.mMinu; + line.mMax = currentRectangle.mMaxu; + } + } + + // visits all cells in given rectangle + PX_INLINE bool visitCells(const OverlapRectangle& rectangle) + { + for(PxI32 ui = rectangle.mMinu + mStep_ui; ui <= rectangle.mMaxu + mStep_ui; ui++) + { + if(ui < mMinRow) + continue; + if(ui >= mMaxRow) + break; + for(PxI32 vi = rectangle.mMinv + mStep_vi; vi <= rectangle.mMaxv + mStep_vi; vi++) + { + if(vi < mMinColumn) + continue; + if(vi >= mMaxColumn) + break; + const PxI32 vertexIndex = ui*mNumColumns + vi; + if(!testVertexIndex(PxU32(vertexIndex))) + return false; + } + } + return true; + } + + // visits all cells in given line - can be row or column + PX_INLINE bool visitCells(const OverlapLine& line) + { + if(line.mMin > line.mMax) + return true; + + if(line.mColumn) + { + const PxI32 vi = line.mLine + mStep_vi; + // early exit if column is out of hf clip area + if(vi < mMinColumn) + return true; + if(vi >= mMaxColumn) + return true; + + for(PxI32 ui = line.mMin + mStep_ui; ui <= line.mMax + mStep_ui; ui++) + { + // early exit or continue if row is out of hf clip area + if(ui >= mMaxRow) + break; + // continue if we did not reach the valid area, we can still get there + if(ui < mMinRow) + continue; + // if the cell has not been tested test and report + if(!testVertexIndex(PxU32(mNumColumns * ui + vi))) + return false; + } + } + else + { + const PxI32 ui = line.mLine + mStep_ui; + // early exit if row is out of hf clip area + if(ui < mMinRow) + return true; + if(ui >= mMaxRow) + return true; + + for(PxI32 vi = line.mMin + mStep_vi; vi <= line.mMax + mStep_vi; vi++) + { + // early exit or continue if column is out of hf clip area + if(vi >= mMaxColumn) + break; + // continue if we did not reach the valid area, we can still get there + if(vi < mMinColumn) + continue; + // if the cell has not been tested test and report + if(!testVertexIndex(PxU32(mNumColumns * ui + vi))) + return false; + } + } + return true; + } + + // does height check and if succeeded adds to report + PX_INLINE bool testVertexIndex(const PxU32 vertexIndex) + { +#define ISHOLE0 (mHf.getMaterialIndex0(vertexIndex) == PxHeightFieldMaterial::eHOLE) +#define ISHOLE1 (mHf.getMaterialIndex1(vertexIndex) == PxHeightFieldMaterial::eHOLE) + const PxReal h0 = mHf.getHeight(vertexIndex); + const PxReal h1 = mHf.getHeight(vertexIndex + 1); + const PxReal h2 = mHf.getHeight(vertexIndex + mNumColumns); + const PxReal h3 = mHf.getHeight(vertexIndex + mNumColumns + 1); + // actual height test, if some height pass we accept the cell + if(!((mMaxY < h0 && mMaxY < h1 && mMaxY < h2 && mMaxY < h3) || (mMinY > h0 && mMinY > h1 && mMinY > h2 && mMinY > h3))) + { + // check if the triangle is not a hole + if(!ISHOLE0) + { + if(!addIndex(vertexIndex*2)) + return false; + } + if(!ISHOLE1) + { + if(!addIndex(vertexIndex*2 + 1)) + return false; + } + } +#undef ISHOLE0 +#undef ISHOLE1 + return true; + } + + // add triangle index, if we get out of buffer size, report them + bool addIndex(PxU32 triangleIndex) + { + if(mNbIndices == HF_SWEEP_REPORT_BUFFER_SIZE) + { + if(!reportOverlaps()) + return false; + } + + mIndexBuffer[mNbIndices++] = triangleIndex; + return true; + } + + PX_FORCE_INLINE bool reportOverlaps() + { + if(mNbIndices) + { + if(!mCallback->onEvent(mNbIndices, mIndexBuffer)) + return false; + mNbIndices = 0; + } + return true; + } + + private: + bool mInitialized; + const HeightFieldUtil& mHfUtil; + const Gu::HeightField& mHf; + T* mCallback; + PxI32 mOffsetU; + PxI32 mOffsetV; + float mMinY; + float mMaxY; + PxI32 mMinRow; + PxI32 mMaxRow; + PxI32 mMinColumn; + PxI32 mMaxColumn; + PxI32 mNumColumns; + PxI32 mStep_ui; + PxI32 mStep_vi; + OverlapRectangle mPreviousRectangle; + OverlapRectangle mCurrentRectangle; + PxU32 mIndexBuffer[HF_SWEEP_REPORT_BUFFER_SIZE]; + PxU32 mNbIndices; + }; + + // If useUnderFaceCalblack is false, traceSegment will report segment/triangle hits via + // faceHit(const Gu::HeightFieldUtil& hf, const PxVec3& point, PxU32 triangleIndex) + // Otherwise traceSegment will report all triangles the segment passes under via + // underFaceHit(const Gu::HeightFieldUtil& hf, const PxVec3& triNormal, const PxVec3& crossedEdge, + // PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex) + // where x,z is the point of previous intercept in hf coords, rayHeight is at that same point + // crossedEdge is the edge vector crossed from last call to underFaceHit, undefined for first call + // Note that underFaceHit can be called when a line is above a triangle if it's within AABB for that hf cell + // Note that backfaceCull is ignored if useUnderFaceCallback is true + // overlapObjectExtent (localSpace) and overlap are used for triangle collecting using an inflated tracesegment + // Note that hfLocalBounds are passed as a parameter instead of being computed inside the traceSegment. + // The localBounds can be obtained: PxBounds3 hfLocalBounds; hfUtil.computeLocalBounds(hfLocalBounds); and passed as + // a parameter. + template<class T, bool useUnderFaceCallback, bool overlap> + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDir, const float rayLength , T* aCallback, const PxBounds3& hfLocalBounds, bool backfaceCull, + const PxVec3* overlapObjectExtent = NULL) const + { + PxF32 tnear, tfar; + if(!Gu::intersectRayAABB2(hfLocalBounds.minimum, hfLocalBounds.maximum, aP0, rayDir, rayLength, tnear, tfar)) + return; + + const PxVec3 p0 = aP0 + rayDir * tnear; + const PxVec3 p1 = aP0 + rayDir * tfar; + + // helper class used for overlap tests + OverlapTraceSegment<T> overlapTraceSegment(*this, *mHeightField); + + // values which expand the HF area + PxF32 expandu = 0.0f, expandv = 0.0f; + + if (overlap) + { + // setup overlap variables + overlapTraceSegment.prepare(aP0,aP0 + rayDir*rayLength,*overlapObjectExtent,expandu,expandv); + } + + // row = x|u, column = z|v + const PxF32 rowScale = mHfGeom->rowScale, columnScale = mHfGeom->columnScale, heightScale = mHfGeom->heightScale; + const PxI32 nbVi = PxI32(mHeightField->getNbColumnsFast()), nbUi = PxI32(mHeightField->getNbRowsFast()); + PX_ASSERT(nbVi > 0 && nbUi > 0); + + // clampEps is chosen so that we get a reasonable clamp value for 65536*0.9999999f = 65535.992187500000 + const PxF32 clampEps = 1e-7f; // shrink u,v to within 1e-7 away from the world bounds + + // we now clamp uvs to [1e-7, rowLimit-1e-7] to avoid out of range uvs and eliminate related checks in the loop + const PxF32 nbUcells = PxF32(nbUi-1)*(1.0f-clampEps), nbVcells = PxF32(nbVi-1)*(1.0f-clampEps); + + // if u0,v0 is near an integer, shift up or down in direction opposite to du,dv by PxMax(|u,v|*1e-7, 1e-7) + // (same direction as du,dv for u1,v1) + // we do this to ensure that we get at least one intersection with u or v when near the cell edge to eliminate special cases in the loop + // we need to extend the field for the inflated radius, we will now operate even with negative u|v + + // map p0 from (x, z, y) to (u0, v0, h0) + PxF32 u0 = PxMin(PxMax(p0.x * mOneOverRowScale, 1e-7f - expandu), nbUcells + expandu); // multiplication rescales the u,v grid steps to 1 + PxF32 v0 = PxMin(PxMax(p0.z * mOneOverColumnScale, 1e-7f - expandv), nbVcells + expandv); + const PxReal h0 = p0.y; // we don't scale y + + // map p1 from (x, z, y) to (u1, v1, h1) + PxF32 u1 = PxMin(PxMax(p1.x * mOneOverRowScale, 1e-7f - expandu), nbUcells + expandu); + PxF32 v1 = PxMin(PxMax(p1.z * mOneOverColumnScale, 1e-7f - expandv), nbVcells + expandv); + const PxReal h1 = p1.y; // we don't scale y + + PxF32 du = u1 - u0, dv = v1 - v0; // recompute du, dv from adjusted uvs + const PxReal dh = h1 - h0; + + // grid u&v step is always either 1 or -1, we precompute as both integers and floats to avoid conversions + // so step_uif is +/-1.0f, step_ui is +/-1 + const PxF32 step_uif = PxSign(du), step_vif = PxSign(dv); + const PxI32 step_ui = PxI32(step_uif), step_vi = PxI32(step_vif); + + // clamp magnitude of du, dv to at least clampEpsilon to avoid special cases when dividing + const PxF32 divEpsilon = 1e-10f; + if(PxAbs(du) < divEpsilon) + du = step_uif * divEpsilon; + if(PxAbs(dv) < divEpsilon) + dv = step_vif * divEpsilon; + + const PxVec3 auhP0(aP0.x*mOneOverRowScale, aP0.y, aP0.z*mOneOverColumnScale); + const PxVec3 duhv(rayDir.x*rayLength*mOneOverRowScale, rayDir.y*rayLength, rayDir.z*rayLength*mOneOverColumnScale); + const PxReal duhvLength = duhv.magnitude(); + PxVec3 duhvNormalized = duhv; + if(duhvLength > PX_NORMALIZATION_EPSILON) + duhvNormalized *= 1.0f/duhvLength; + + // Math derivation: + // points on 2d segment are parametrized as: [u0,v0] + t [du, dv]. We solve for t_u[n], t for nth u-intercept + // u0 + t_un du = un + // t_un = (un-u0) / du + // t_un1 = (un+1-u0) / du ; we use +1 since we rescaled the grid step to 1 + // therefore step_tu = t_un - t_un1 = 1/du + + // seed the initial integer cell coordinates with u0, v0 rounded up or down with standard PxFloor/Ceil behavior + // to ensure we have the correct first cell between (ui,vi) and (ui+step_ui,vi+step_vi) + PxI32 ui = (du > 0.0f) ? PxI32(PxFloor(u0)) : PxI32(PxCeil(u0)); + PxI32 vi = (dv > 0.0f) ? PxI32(PxFloor(v0)) : PxI32(PxCeil(v0)); + + // find the nearest integer u, v in ray traversal direction and corresponding tu and tv + const PxReal uhit0 = du > 0.0f ? ceilUp(u0) : floorDown(u0); + const PxReal vhit0 = dv > 0.0f ? ceilUp(v0) : floorDown(v0); + + // tu, tv can be > 1 but since the loop is structured as do {} while(tMin < tEnd) we still visit the first cell + PxF32 last_tu = 0.0f, last_tv = 0.0f; + PxReal tu = (uhit0-u0) / du; + PxReal tv = (vhit0-v0) / dv; + PX_ASSERT(tu >= 0.0f && tv >= 0.0f); + + // compute step_tu and step_tv; t steps per grid cell in u and v direction + const PxReal step_tu = 1.0f / PxAbs(du), step_tv = 1.0f / PxAbs(dv); + + // t advances at the same rate for u, v and h therefore we can compute h at u,v grid intercepts + #define COMPUTE_H_FROM_T(t) (h0 + (t) * dh) + + const PxF32 hEpsilon = 1e-4f; + PxF32 uif = PxF32(ui), vif = PxF32(vi); + + // these are used to remap h values to correspond to u,v increasing order + PxI32 uflip = 1-step_ui; /*0 or 2*/ + PxI32 vflip = (1-step_vi)/2; /*0 or 1*/ + + // this epsilon is needed to ensure that we include the last [t, t+1] range in the do {} while(t<tEnd) loop + // A.B. in case of overlap we do miss actually a line with this epsilon, should it not be +? + PxF32 tEnd = 1.0f - 1e-4f; + if(overlap) + tEnd = 1.0f + 1e-4f; + PxF32 tMinUV; + + const Gu::HeightField& hf = *mHeightField; + + // seed hLinePrev as h(0) + PxReal hLinePrev = COMPUTE_H_FROM_T(0); + + do + { + tMinUV = PxMin(tu, tv); // determine where next closest u or v-intercept point is + PxF32 hLineNext = COMPUTE_H_FROM_T(tMinUV); // compute the corresponding h + + // the operating u|v space has been extended by expandu|expandv if inflation is used + PX_ASSERT(ui >= 0 - expandu && ui < nbUi + expandu && vi >= 0 - expandv && vi < nbVi + expandv); + PX_ASSERT(ui+step_ui >= 0 - expandu && ui+step_ui < nbUi + expandu && vi+step_vi >= 0 - expandv && vi+step_vi < nbVi + expandv); + + // handle overlap in overlapCallback + if(overlap) + { + if(!overlapTraceSegment.initialized()) + { + // initial overlap and setup + if(!overlapTraceSegment.init(ui,vi,nbVi,step_ui,step_vi,aCallback)) + return; + } + else + { + // overlap step + if(!overlapTraceSegment.step(ui,vi)) + return; + } + } + else + { + const PxU32 colIndex0 = PxU32(nbVi * ui + vi); + const PxU32 colIndex1 = PxU32(nbVi * (ui + step_ui) + vi); + const PxReal h[4] = { // h[0]=h00, h[1]=h01, h[2]=h10, h[3]=h11 - oriented relative to step_uv + hf.getHeight(colIndex0) * heightScale, hf.getHeight(colIndex0 + step_vi) * heightScale, + hf.getHeight(colIndex1) * heightScale, hf.getHeight(colIndex1 + step_vi) * heightScale }; + + PxF32 minH = PxMin(PxMin(h[0], h[1]), PxMin(h[2], h[3])); + PxF32 maxH = PxMax(PxMax(h[0], h[1]), PxMax(h[2], h[3])); + + // how much space in h have we covered from previous to current u or v intercept + PxF32 hLineCellRangeMin = PxMin(hLinePrev, hLineNext); + PxF32 hLineCellRangeMax = PxMax(hLinePrev, hLineNext); + + // do a quick overlap test in h, this should be rejecting the vast majority of tests + if(!(hLineCellRangeMin-hEpsilon > maxH || hLineCellRangeMax+hEpsilon < minH) || + (useUnderFaceCallback && hLineCellRangeMax < maxH)) + { + // arrange h so that h00 corresponds to min(uif, uif+step_uif) h10 to max et c. + // this is only needed for backface culling to work so we know the proper winding order without branches + // uflip is 0 or 2, vflip is 0 or 1 (corresponding to positive and negative ui_step and vi_step) + PxF32 h00 = h[0+uflip+vflip]; + PxF32 h01 = h[1+uflip-vflip]; + PxF32 h10 = h[2-uflip+vflip]; + PxF32 h11 = h[3-uflip-vflip]; + + PxF32 minuif = PxMin(uif, uif+step_uif); + PxF32 maxuif = PxMax(uif, uif+step_uif); + PxF32 minvif = PxMin(vif, vif+step_vif); + PxF32 maxvif = PxMax(vif, vif+step_vif); + PxVec3 p00(minuif, h00, minvif); + PxVec3 p01(minuif, h01, maxvif); + PxVec3 p10(maxuif, h10, minvif); + PxVec3 p11(maxuif, h11, maxvif); + + const PxF32 enlargeEpsilon = 0.0001f; + const PxVec3* p00a = &p00, *p01a = &p01, *p10a = &p10, *p11a = &p11; + PxU32 minui = PxU32(PxMin(ui+step_ui, ui)), minvi = PxU32(PxMin(vi+step_vi, vi)); + + // row = x|u, column = z|v + const PxU32 vertIndex = nbVi * minui + minvi; + const PxU32 cellIndex = vertIndex; // this adds a dummy unused cell in the end of each row; was -minui + bool isZVS = hf.isZerothVertexShared(vertIndex); + if(!isZVS) + { + // rotate the pointers for flipped edge cells + p10a = &p00; + p00a = &p01; + p01a = &p11; + p11a = &p10; + } + + // For triangle index computation, see illustration in Gu::HeightField::getTriangleNormal() + // Since row = u, column = v + // for zeroth vert shared the 10 index is the corner of the 0-index triangle, and 01 is 1-index + // if zeroth vertex is not shared, the 00 index is the corner of 0-index triangle + if(!useUnderFaceCallback) + { + if(mHeightField->getThicknessFast() > 0.0f) // new in 3.4: flip triangle winding if thickness is positive + Ps::swap<const PxVec3*>(p00a, p11a); + + #define ISHOLE0 (hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE) + #define ISHOLE1 (hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE) + PxReal triT0 = PX_MAX_REAL, triT1 = PX_MAX_REAL; + bool hit0 = false, hit1 = false; + PxF32 triU0, triV0, triU1, triV1; + if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p10a, *p00a, *p11a, triT0, triU0, triV0, backfaceCull, enlargeEpsilon) && + triT0 >= 0.0f && triT0 <= duhvLength && !ISHOLE0) + { + hit0 = true; + } else + triT0 = PX_MAX_REAL; + if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p01a, *p11a, *p00a, triT1, triU1, triV1, backfaceCull, enlargeEpsilon) + && triT1 >= 0.0f && triT1 <= duhvLength && !ISHOLE1) + { + hit1 = true; + } else + triT1 = PX_MAX_REAL; + + if(hit0 && triT0 <= triT1) + { + const PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale); + if(!aCallback->faceHit(*this, hitPoint, cellIndex*2, triU0, triV0)) + return; + if(hit1) // possible to hit both triangles in a cell with eMESH_MULTIPLE + { + PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale); + if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2 + 1, triU1, triV1)) + return; + } + } + else if(hit1 && triT1 <= triT0) + { + PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale); + if(!aCallback->faceHit(*this, hitPoint, cellIndex*2 + 1, triU1, triV1)) + return; + if(hit0) // possible to hit both triangles in a cell with eMESH_MULTIPLE + { + PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale); + if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2, triU0, triV0)) + return; + } + } + #undef ISHOLE0 + #undef ISHOLE1 + } + else + { + // TODO: quite a few optimizations are possible here. edges can be shared, intersectRayTriangle inlined etc + // Go to shape space. Height is already in shape space so we only scale x and z + PxVec3 p00s(p00a->x * rowScale, p00a->y, p00a->z * columnScale); + PxVec3 p01s(p01a->x * rowScale, p01a->y, p01a->z * columnScale); + PxVec3 p10s(p10a->x * rowScale, p10a->y, p10a->z * columnScale); + PxVec3 p11s(p11a->x * rowScale, p11a->y, p11a->z * columnScale); + + PxVec3 triNormals[2] = { (p00s - p10s).cross(p11s - p10s), (p11s - p01s).cross(p00s-p01s) }; + triNormals[0] *= PxRecipSqrt(triNormals[0].magnitudeSquared()); + triNormals[1] *= PxRecipSqrt(triNormals[1].magnitudeSquared()); + // since the heightfield can be mirrored with negative rowScale or columnScale, this assert doesn't hold + //PX_ASSERT(triNormals[0].y >= 0.0f && triNormals[1].y >= 0.0f); + + // at this point we need to compute the edge direction that we crossed + // also since we don't DDA the w we need to find u,v for w-intercept (w refers to diagonal adjusted with isZVS) + PxF32 wnu = isZVS ? -1.0f : 1.0f, wnv = 1.0f; // uv-normal to triangle edge that splits the cell + PxF32 wpu = uif + 0.5f * step_uif, wpv = vif + 0.5f * step_vif; // a point on triangle edge that splits the cell + // note that (wpu, wpv) is on both edges (for isZVS and non-ZVS cases) which is nice + + // we clamp tNext to 1 because we still want to issue callbacks even if we stay in one cell + // note that tNext can potentially be arbitrarily large for a segment contained within a cell + PxF32 tNext = PxMin(PxMin(tu, tv), 1.0f), tPrev = PxMax(last_tu, last_tv); + + // compute uvs corresponding to tPrev, tNext + PxF32 unext = u0 + tNext*du, vnext = v0 + tNext*dv; + PxF32 uprev = u0 + tPrev*du, vprev = v0 + tPrev*dv; + + const PxReal& h00_ = h[0], &h01_ = h[1], &h10_ = h[2]/*, h11_ = h[3]*/; // aliases for step-oriented h + + // (wpu, wpv) is a point on the diagonal + // we compute a dot of ((unext, vnext) - (wpu, wpv), wn) to see on which side of triangle edge we are + // if the dot is positive we need to add 1 to triangle index + PxU32 dotPrevGtz = PxU32(((uprev - wpu) * wnu + (vprev - wpv) * wnv) > 0); + PxU32 dotNextGtz = PxU32(((unext - wpu) * wnu + (vnext - wpv) * wnv) > 0); + PxU32 triIndex0 = cellIndex*2 + dotPrevGtz; + PxU32 triIndex1 = cellIndex*2 + dotNextGtz; + PxU32 isHole0 = PxU32(hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE); + PxU32 isHole1 = PxU32(hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE); + if(triIndex0 > triIndex1) + shdfnd::swap<PxU32>(isHole0, isHole1); + + // TODO: compute height at u,v inside here, change callback param to PxVec3 + PxVec3 crossedEdge; + if(last_tu > last_tv) // previous intercept was at u, so we use u=const edge + crossedEdge = PxVec3(0.0f, h01_-h00_, step_vif * columnScale); + else // previous intercept at v, use v=const edge + crossedEdge = PxVec3(step_uif * rowScale, h10_-h00_, 0.0f); + + if(!isHole0 && !aCallback->underFaceHit(*this, triNormals[dotPrevGtz], crossedEdge, + uprev * rowScale, vprev * columnScale, COMPUTE_H_FROM_T(tPrev), triIndex0)) + return; + + if(triIndex1 != triIndex0 && !isHole1) // if triIndex0 != triIndex1 that means we cross the triangle edge + { + // Need to compute tw, the t for ray intersecting the diagonal within the current cell + // dot((wnu, wnv), (u0+tw*du, v0+tw*dv)-(wpu, wpv)) = 0 + // wnu*(u0+tw*du-wpu) + wnv*(v0+tw*dv-wpv) = 0 + // wnu*u0+wnv*v0-wnu*wpu-wnv*wpv + tw*(du*wnu + dv*wnv) = 0 + const PxF32 denom = du*wnu + dv*wnv; + if(PxAbs(denom) > 1e-6f) + { + const PxF32 tw = (wnu*(wpu-u0)+wnv*(wpv-v0)) / denom; + if(!aCallback->underFaceHit(*this, triNormals[dotNextGtz], p10s-p01s, + (u0+tw*du) * rowScale, (v0+tw*dv) * columnScale, COMPUTE_H_FROM_T(tw), triIndex1)) + return; + } + } + } + } + } + + if(tu < tv) + { + last_tu = tu; + ui += step_ui; + // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565) + if(ui+step_ui< (0 - expandu) || ui+step_ui>=(nbUi + expandu)) // should hold true for ui without step from previous iteration + break; + uif += step_uif; + tu += step_tu; + } + else + { + last_tv = tv; + vi += step_vi; + // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565) + if(vi+step_vi< (0 - expandv) || vi+step_vi>=(nbVi + expandv)) // should hold true for vi without step from previous iteration + break; + vif += step_vif; + tv += step_tv; + } + hLinePrev = hLineNext; + } + // since min(tu,tv) is the END of the active interval we need to check if PREVIOUS min(tu,tv) was past interval end + // since we update tMinUV in the beginning of the loop, at this point it stores the min(last tu,last tv) + while (tMinUV < tEnd); + #undef COMPUTE_H_FROM_T + } + + PX_FORCE_INLINE PxVec3 hf2shapen(const PxVec3& v) const + { + return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 shape2hfp(const PxVec3& v) const + { + return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 hf2shapep(const PxVec3& v) const + { + return PxVec3(v.x * mHfGeom->rowScale, v.y * mHfGeom->heightScale, v.z * mHfGeom->columnScale); + } + + PX_INLINE PxVec3 hf2worldp(const PxTransform& pose, const PxVec3& v) const + { + const PxVec3 s = hf2shapep(v); + return pose.transform(s); + } + + PX_INLINE PxVec3 hf2worldn(const PxTransform& pose, const PxVec3& v) const + { + const PxVec3 s = hf2shapen(v); + return pose.q.rotate(s); + } + +#ifdef REMOVED +bool clipShapeNormalToEdgeVoronoi(PxVec3& normal, PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField.getNbColumnsFast(); +// const PxU32 column = cell % mHeightField.getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField.getNbColumnsFast()); + PX_ASSERT(column == cell % mHeightField.getNbColumnsFast()); + + //PxcHeightFieldFormat format = getFormatFast(); +// PxHeightFieldFormat::Enum format = mHeightField.getFormatFast(); + + bool result = false; + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (row > 0) + { + //const PxcHeightFieldSample& sample = getSample(cell - getNbColumnsFast()); + //if(isZerothVertexShared(cell - getNbColumnsFast())) + if(mHeightField.isZerothVertexShared(cell - mHeightField.getNbColumnsFast())) + { + //if (getMaterialIndex0(cell - getNbColumnsFast()) != getHoleMaterial()) + if (mHeightField.getMaterialIndex0(cell - mHeightField.getNbColumnsFast()) != PxHeightFieldMaterial::eHOLE) + { + // <------ COL + // +----+ 0 R + // |1 / /^ O + // | / / # W + // | / / # | + // |/ / 0 # | + // + 2<===1 | + // | + // | + // | + // | + // | + // | + // V + // + //PxReal h0 = getHeightScale() * getHeight(cell - getNbColumnsFast()); + //PxReal h1 = getHeightScale() * getHeight(cell); + //PxReal h2 = getHeightScale() * getHeight(cell + 1); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - mHeightField.getNbColumnsFast()); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1); + //PxVec3 eC; + //eC.set(0, h2-h1, getColumnScale()); + const PxVec3 eC(0, h2-h1, mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(-getRowScale(), h0-h1, 0); + const PxVec3 eR(-mHfGeom.rowScale, h0-h1, 0); + const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + else + { + //if (getMaterialIndex1(cell - getNbColumnsFast()) != getHoleMaterial()) + if (mHeightField.getMaterialIndex1(cell - mHeightField.getNbColumnsFast()) != PxHeightFieldMaterial::eHOLE) + { + // <------ COL + // 0 +----+ R + // ^\ \ 0 | O + // # \ \ | W + // # \ \ | | + // # 1 \ \| | + // 1===>2 + | + // | + // | + // | + // | + // | + // | + // V + // + //PxReal h0 = getHeightScale() * getHeight(cell - getNbColumnsFast() + 1); + //PxReal h1 = getHeightScale() * getHeight(cell + 1); + //PxReal h2 = getHeightScale() * getHeight(cell); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - mHeightField.getNbColumnsFast() + 1); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell); + //PxVec3 eC; + //eC.set(0, h2-h1, -getColumnScale()); + const PxVec3 eC(0, h2-h1, -mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(-getRowScale(), h0-h1, 0); + const PxVec3 eR(-mHfGeom.rowScale, h0-h1, 0); + const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + } + //if (row < getNbRowsFast() - 1) + if (row < mHeightField.getNbRowsFast() - 1) + { + //const PxcHeightFieldSample& sample = getSample(cell); + //if(isZerothVertexShared(cell)) + if(mHeightField.isZerothVertexShared(cell)) + { + //if (getMaterialIndex1(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex1(cell) != PxHeightFieldMaterial::eHOLE) + { + // <------ COL + // R + // O + // W + // | + // | + // | + // 0===>2 0 | + // # 1 / /| | + // # / / | | + // # / / | | + // V/ / 0 | | + // 1 +----+ | + // V + // + //PxReal h0 = getHeightScale() * getHeight(cell + 1); + //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast() + 1); + //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + //PxVec3 eC; + //eC.set(0, h2-h0, -getColumnScale()); + const PxVec3 eC(0, h2-h0, -mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(getRowScale(), h1-h0, 0); + const PxVec3 eR(mHfGeom.rowScale, h1-h0, 0); + const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + else + { + //if (getMaterialIndex0(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE) + { + // <------ COL + // R + // O + // W + // | + // | + // | + // + 2<===0 | + // |\ \ 0 # | + // | \ \ # | + // | \ \ # | + // |1 \ \V | + // +----+ 1 | + // V + // + //PxReal h0 = getHeightScale() * getHeight(cell); + //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + //PxReal h2 = getHeightScale() * getHeight(cell + 1); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1); + //PxVec3 eC; + //eC.set(0, h2-h0, getColumnScale()); + const PxVec3 eC(0, h2-h0, mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(getRowScale(), h1-h0, 0); + const PxVec3 eR(mHfGeom.rowScale, h1-h0, 0); + const PxVec3 e = eR - eC * eC.dot(eR) / eC.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } } + } + } + break; + case 1: + //if ((row < getNbRowsFast() - 1) && (column < getNbColumnsFast() - 1)) + if ((row < mHeightField.getNbRowsFast() - 1) && (column < mHeightField.getNbColumnsFast() - 1)) + { + //const PxcHeightFieldSample& sample = getSample(cell); + + //PxReal h0 = getHeightScale() * getHeight(cell); + //PxReal h1 = getHeightScale() * getHeight(cell + 1); + //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + //PxReal h3 = getHeightScale() * getHeight(cell + getNbColumnsFast() + 1); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + const PxReal h3 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1); + + //if (isZerothVertexShared(cell)) + if (mHeightField.isZerothVertexShared(cell)) + { + // <------ COL + // 1<---0 R + // |1 /| O + // | / | W + // | / | | + // |V 0 V | + // 3----2 | + // V + // + //PxVec3 eD; + //eD.set(getRowScale(), h3-h0, getColumnScale()); + const PxVec3 eD(mHfGeom.rowScale, h3-h0, mHfGeom.columnScale); + const PxReal DD = eD.magnitudeSquared(); + + //if (getMaterialIndex0(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE) + { + //PxVec3 eR; + //eR.set(getRowScale(), h2-h0, 0); + const PxVec3 eR(mHfGeom.rowScale, h2-h0, 0); + const PxVec3 e = eR - eD * eD.dot(eR) / DD; + const PxReal proj = e.dot(normal); + if (proj > 0) { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + + //if (getMaterialIndex1(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex1(cell) != PxHeightFieldMaterial::eHOLE) + { + //PxVec3 eC; + //eC.set(0, h1-h0, getColumnScale()); + const PxVec3 eC(0, h1-h0, mHfGeom.columnScale); + const PxVec3 e = eC - eD * eD.dot(eC) / DD; + const PxReal proj = e.dot(normal); + if (proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + } + else + { + // <------ COL + // 1--->0 R + // |\ 0 | O + // | \ | W + // | \ | | + // V 1 V| | + // 3----2 | + // V + // + //PxVec3 eD; + //eD.set(getRowScale(), h2-h1, -getColumnScale()); + const PxVec3 eD(mHfGeom.rowScale, h2-h1, -mHfGeom.columnScale); + const PxReal DD = eD.magnitudeSquared(); + + //if (getMaterialIndex0(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE) + { + //PxVec3 eC; + //eC.set(0, h0-h1, -getColumnScale()); + const PxVec3 eC(0, h0-h1, -mHfGeom.columnScale); + const PxVec3 e = eC - eD * eD.dot(eC) / DD; + const PxReal proj = e.dot(normal); + if (proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + + //if (getMaterialIndex1(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex1(cell) != PxHeightFieldMaterial::eHOLE) + { + //PxVec3 eR; + //eR.set(getRowScale(), h3-h1, 0); + const PxVec3 eR(mHfGeom.rowScale, h3-h1, 0); + const PxVec3 e = eR - eD * eD.dot(eR) / DD; + const PxReal proj = e.dot(normal); + if (proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + } + } + break; + case 2: + if (column > 0) + { + //const PxcHeightFieldSample& sample = getSample(cell - 1); + + //if(isZerothVertexShared(cell - 1)) + if(mHeightField.isZerothVertexShared(cell - 1)) + { + //if (getMaterialIndex1(cell - 1) != getHoleMaterial()) + if (mHeightField.getMaterialIndex1(cell - 1) != PxHeightFieldMaterial::eHOLE) + { + // <-------------- COL + // 1===>0 + R + // + 1 / /| O + // + / / | W + // + / / | | + // V/ / 0 | | + // 2 +----+ V + // + //PxReal h0 = getHeightScale() * getHeight(cell - 1); + //PxReal h1 = getHeightScale() * getHeight(cell); + //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - 1); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + //PxVec3 eC; + //eC.set(0,h0-h1,-getColumnScale()); + const PxVec3 eC(0,h0-h1,-mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(getRowScale(),h2-h1,0); + const PxVec3 eR(mHfGeom.rowScale,h2-h1,0); + const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + else + { + //if (getMaterialIndex1(cell - 1) != getHoleMaterial()) + if (mHeightField.getMaterialIndex1(cell - 1) != PxHeightFieldMaterial::eHOLE) + { + // <-------------- COL + // 2 +----+ R + // ^\ \ 0 | O + // + \ \ | W + // + \ \ | | + // + 1 \ \| | + // 1===>0 + V + // + //PxReal h0 = getHeightScale() * getHeight(cell - 1 + getNbColumnsFast()); + //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + //PxReal h2 = getHeightScale() * getHeight(cell); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell - 1 + mHeightField.getNbColumnsFast()); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell); + //PxVec3 eC; + //eC.set(0,h0-h1,-getColumnScale()); + const PxVec3 eC(0,h0-h1,-mHfGeom.columnScale); + //PxVec3 eR; + //eC.set(-getRowScale(),h2-h1,0); + //eC.set(-mHfGeom.rowScale,h2-h1,0); + const PxVec3 eR(-mHfGeom.rowScale,h2-h1,0); // PT: I assume this was eR, not eC !!!!! + const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + } + //if (column < getNbColumnsFast() - 1) + if (column < mHeightField.getNbColumnsFast() - 1) + { + //const PxcHeightFieldSample& sample = getSample(cell); + + //if (isZerothVertexShared(cell)) + if (mHeightField.isZerothVertexShared(cell)) + { + //if (getMaterialIndex0(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE) + { + // <-------------- COL + // +----+ 2 R + // | 1 / /^ O + // | / / + W + // | / / + | + // |/ / 0 + | + // + 1<===0 V + // + //PxReal h0 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + //PxReal h1 = getHeightScale() * getHeight(cell + getNbColumnsFast() + 1); + //PxReal h2 = getHeightScale() * getHeight(cell); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell); + //PxVec3 eC; + //eC.set(0,h1-h0,getColumnScale()); + const PxVec3 eC(0,h1-h0,mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(-getRowScale(),h2-h0,0); + const PxVec3 eR(-mHfGeom.rowScale,h2-h0,0); + const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) + { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + else + { + //if (getMaterialIndex0(cell) != getHoleMaterial()) + if (mHeightField.getMaterialIndex0(cell) != PxHeightFieldMaterial::eHOLE) + { + // <-------------- COL + // + 1<===0 R + // |\ \ 0 + O + // | \ \ + W + // | \ \ + | + // | 1 \ \V | + // +----+ 2 V + // + //PxReal h0 = getHeightScale() * getHeight(cell); + //PxReal h1 = getHeightScale() * getHeight(cell + 1); + //PxReal h2 = getHeightScale() * getHeight(cell + getNbColumnsFast()); + const PxReal h0 = mHfGeom.heightScale * mHeightField.getHeight(cell); + const PxReal h1 = mHfGeom.heightScale * mHeightField.getHeight(cell + 1); + const PxReal h2 = mHfGeom.heightScale * mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); + //PxVec3 eC; + //eC.set(0,h1-h0,getColumnScale()); + const PxVec3 eC(0,h1-h0,mHfGeom.columnScale); + //PxVec3 eR; + //eR.set(getRowScale(),h2-h0,0); + const PxVec3 eR(mHfGeom.rowScale,h2-h0,0); + const PxVec3 e = eC - eR * eR.dot(eC) / eR.magnitudeSquared(); + const PxReal s = normal.dot(e); + if (s > 0) { + normal -= e * s / e.magnitudeSquared(); + result = true; + } + } + } + } + break; + } + return result; +} +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ptchernev TODO: this is wrong it only clips in x and z + bool clipShapeNormalToVertexVoronoi(PxVec3& normal, PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + //PxU32 row = vertexIndex / getNbColumnsFast(); + //PxU32 column = vertexIndex % getNbColumnsFast(); +// const PxU32 row = vertexIndex / mHeightField.getNbColumnsFast(); +// const PxU32 column = vertexIndex % mHeightField.getNbColumnsFast(); + PX_ASSERT(row == vertexIndex / mHeightField->getNbColumnsFast()); + PX_ASSERT(column == vertexIndex % mHeightField->getNbColumnsFast()); + + //PxReal h0 = getHeight(vertexIndex); + const PxReal h0 = mHeightField->getHeight(vertexIndex); + + bool result = false; + + if(row > 0) + { + // - row + //PxVec3 e; + //e.set(-getRowScale(), getHeightScale() * (getHeight(vertexIndex - getNbColumnsFast()) - h0), 0); + const PxVec3 e(-mHfGeom->rowScale, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex - mHeightField->getNbColumnsFast()) - h0), 0); + const PxReal proj = e.dot(normal); + if(proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + + //if(row < getNbRowsFast() - 1) + if(row < mHeightField->getNbRowsFast() - 1) + { + // + row + //PxVec3 e; + //e.set(getRowScale(), getHeightScale() * (getHeight(vertexIndex + getNbColumnsFast()) - h0), 0); + const PxVec3 e(mHfGeom->rowScale, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex + mHeightField->getNbColumnsFast()) - h0), 0); + const PxReal proj = e.dot(normal); + if(proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + + if(column > 0) + { + // - column + //PxVec3 e; + //e.set(0, getHeightScale() * (getHeight(vertexIndex - 1) - h0), -getColumnScale()); + const PxVec3 e(0, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex - 1) - h0), -mHfGeom->columnScale); + const PxReal proj = e.dot(normal); + if(proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + + //if(column < getNbColumnsFast() - 1) + if(column < mHeightField->getNbColumnsFast() - 1) + { + // + column + //PxVec3 e; + //e.set(0, getHeightScale() * (getHeight(vertexIndex + 1) - h0), getColumnScale()); + const PxVec3 e(0, mHfGeom->heightScale * (mHeightField->getHeight(vertexIndex + 1) - h0), mHfGeom->columnScale); + const PxReal proj = e.dot(normal); + if(proj > 0) + { + normal -= e * proj / e.magnitudeSquared(); + result = true; + } + } + + return result; + } + +PxVec3 getEdgeDirection(PxU32 edgeIndex, PxU32 cell) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { +// const PxReal y0 = mHeightField.getHeight(cell); +// const PxReal y1 = mHeightField.getHeight(cell + 1); +// return PxVec3(0.0f, mHfGeom.heightScale * (y1 - y0), mHfGeom.columnScale); + const PxI32 y0 = mHeightField->getSample(cell).height; + const PxI32 y1 = mHeightField->getSample(cell + 1).height; + return PxVec3(0.0f, mHfGeom->heightScale * PxReal(y1 - y0), mHfGeom->columnScale); + } + case 1: + if(mHeightField->isZerothVertexShared(cell)) + { +// const PxReal y0 = mHeightField.getHeight(cell); +// const PxReal y3 = mHeightField.getHeight(cell + mHeightField.getNbColumnsFast() + 1); +// return PxVec3(mHfGeom.rowScale, mHfGeom.heightScale * (y3 - y0), mHfGeom.columnScale); + const PxI32 y0 = mHeightField->getSample(cell).height; + const PxI32 y3 = mHeightField->getSample(cell + mHeightField->getNbColumnsFast() + 1).height; + return PxVec3(mHfGeom->rowScale, mHfGeom->heightScale * PxReal(y3 - y0), mHfGeom->columnScale); + } + else + { +// const PxReal y1 = mHeightField.getHeight(cell + 1); +// const PxReal y2 = mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); +// return PxVec3(mHfGeom.rowScale, mHfGeom.heightScale * (y2 - y1), -mHfGeom.columnScale); + const PxI32 y1 = mHeightField->getSample(cell + 1).height; + const PxI32 y2 = mHeightField->getSample(cell + mHeightField->getNbColumnsFast()).height; + return PxVec3(mHfGeom->rowScale, mHfGeom->heightScale * PxReal(y2 - y1), -mHfGeom->columnScale); + } + case 2: + { +// const PxReal y0 = mHeightField.getHeight(cell); +// const PxReal y2 = mHeightField.getHeight(cell + mHeightField.getNbColumnsFast()); +// return PxVec3(mHfGeom.rowScale, mHfGeom.heightScale * (y2 - y0), 0.0f); + const PxI32 y0 = mHeightField->getSample(cell).height; + const PxI32 y2 = mHeightField->getSample(cell + mHeightField->getNbColumnsFast()).height; + return PxVec3(mHfGeom->rowScale, mHfGeom->heightScale * PxReal(y2 - y0), 0.0f); + } + } + return PxVec3(0); +} + +/*PX_FORCE_INLINE PxVec3 getEdgeDirection(PxU32 edgeIndex) const +{ + const PxU32 cell = edgeIndex / 3; + return getEdgeDirection(edgeIndex, cell); +}*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + }; + + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp new file mode 100644 index 00000000..0fe55f18 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuOverlapTestsHF.cpp @@ -0,0 +1,768 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "PsAllocator.h" +#include "GuOverlapTests.h" + +#include "PsUtilities.h" +#include "PsVecMath.h" + +#include "GuHeightFieldUtil.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionTriangleBox.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +#include "PxSphereGeometry.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" + +#include "GuCapsule.h" +#include "GuEdgeCache.h" +#include "GuBoxConversion.h" + +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" + +#include "GuVecTriangle.h" +#include "GuVecSphere.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace Ps::aos; + +static bool intersectHeightFieldSphere(const HeightFieldUtil& hfUtil, const Sphere& sphereInHfShape) +{ + const HeightField& hf = hfUtil.getHeightField(); + + // sample the sphere center in the heightfield to find out + // if we have penetration with more than the sphere radius + if(hfUtil.isShapePointOnHeightField(sphereInHfShape.center.x, sphereInHfShape.center.z)) + { + // The sphere origin projects within the bounds of the heightfield in the X-Z plane + PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z); + PxReal deltaHeight = sphereInHfShape.center.y - sampleHeight; + if(hf.isDeltaHeightInsideExtent(deltaHeight)) + { + // The sphere origin is 'below' the heightfield surface + PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z); + if(faceIndex != 0xffffffff) + { + return true; + } + return false; + } + } + + const PxReal radiusSquared = sphereInHfShape.radius * sphereInHfShape.radius; + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape.center); + + const PxReal radiusOverRowScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverRowScale()); + const PxReal radiusOverColumnScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverColumnScale()); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + for(PxU32 r = minRow; r < maxRow; r++) + { + for(PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 pcp[11]; + PxU32 npcp = 0; + npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape.center, pcp, NULL, true, true, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + PxVec3 d = sphereInHfShape.center - pcp[pi]; + + PxReal ll = d.magnitudeSquared(); + + if(ll > radiusSquared) + // Too far + continue; + + return true; + } + } + } + return false; +} + +static bool intersectHeightFieldCapsule(const HeightFieldUtil& hfUtil, const PxCapsuleGeometry& capsuleGeom, const PxTransform& capsulePose) +{ + const HeightField& hf = hfUtil.getHeightField(); + + PxVec3 verticesInHfShape[2]; + PxVec3 capsuleOrigin, capsuleExtent; + { + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(capsulePose, capsuleGeom); + + capsuleOrigin = capsulePose.p + capsuleHalfHeightVector; + capsuleExtent = -capsuleHalfHeightVector*2.0f; + + verticesInHfShape[0] = capsuleOrigin; + verticesInHfShape[1] = capsulePose.p - capsuleHalfHeightVector; + } + + const PxReal radius = capsuleGeom.radius; + const PxReal radiusOverRowScale = radius * PxAbs(hfUtil.getOneOverRowScale()); + const PxReal radiusOverColumnScale = radius * PxAbs(hfUtil.getOneOverColumnScale()); + + PxU32 absMinRow = 0xffffffff; + PxU32 absMaxRow = 0; + PxU32 absMinColumn = 0xffffffff; + PxU32 absMaxColumn = 0; + + PxReal radiusSquared = radius * radius; + + // test both of capsule's corner vertices+radius for HF overlap + for(PxU32 i = 0; i<2; i++) + { + const PxVec3& sphereInHfShape = verticesInHfShape[i]; + + // we have to do this first to update the absMin / absMax correctly even if + // we decide to continue from inside the deep penetration code. + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + if(minRow < absMinRow) absMinRow = minRow; + if(minColumn < absMinColumn) absMinColumn = minColumn; + if(maxRow > absMaxRow) absMaxRow = maxRow; + if(maxColumn > absMaxColumn) absMaxColumn = maxColumn; + + if(hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z)) + { + // The sphere origin projects within the bounds of the heightfield in the X-Z plane + const PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + const PxReal deltaHeight = sphereInHfShape.y - sampleHeight; + if(hf.isDeltaHeightInsideExtent(deltaHeight)) + { + // The sphere origin is 'below' the heightfield surface + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + if(faceIndex != 0xffffffff) + { + return true; + } + continue; + } + } + + for(PxU32 r = minRow; r < maxRow; r++) + { + for(PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 pcp[11]; + PxU32 npcp = 0; + npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape, pcp, NULL, true, true, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + const PxVec3 d = sphereInHfShape - pcp[pi]; + + if(hf.isDeltaHeightOppositeExtent(d.y)) + { + // We are 'above' the heightfield + + const PxReal ll = d.magnitudeSquared(); + if(ll > radiusSquared) + // Too far above + continue; + + return true; + } + } + } + } + } + + const Vec3V p1 = V3LoadU(capsuleOrigin); + const Vec3V d1 = V3LoadU(capsuleExtent); + + // now test capsule's inflated segment for overlap with HF edges + PxU32 row, column; + for(row = absMinRow; row <= absMaxRow; row++) + { + for(column = absMinColumn; column <= absMaxColumn; column++) + { + const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + const PxU32 firstEdge = 3 * vertexIndex; + // omg I am sorry about this code but I can't find a simpler way: + // last column will only test edge 2 + // last row will only test edge 0 + // and most importantly last row and column will not go inside the for + const PxU32 minEi = (column == absMaxColumn) ? 2u : 0; + const PxU32 maxEi = (row == absMaxRow ) ? 1u : 3u; + for(PxU32 ei = minEi; ei < maxEi; ei++) + { + const PxU32 edgeIndex = firstEdge + ei; + + const PxU32 cell = vertexIndex; + PX_ASSERT(cell == edgeIndex / 3); + const PxU32 row_ = row; + PX_ASSERT(row_ == cell / hf.getNbColumnsFast()); + const PxU32 column_ = column; + PX_ASSERT(column_ == cell % hf.getNbColumnsFast()); + + const PxU32 faceIndex = hfUtil.getEdgeFaceIndex(edgeIndex, cell, row_, column_); + if(faceIndex != 0xffffffff) + { + PxVec3 origin; + PxVec3 direction; + hfUtil.getEdge(edgeIndex, cell, row_, column_, origin, direction); + + const Vec3V p2 = V3LoadU(origin); + const Vec3V d2 = V3LoadU(direction); + FloatV s, t; + const FloatV llV = Gu::distanceSegmentSegmentSquared(p1, d1, p2, d2, s, t); + + PxReal ll; + FStore(llV, &ll); + + if(ll < radiusSquared) + return true; + } + } + } + } + return false; +} + +namespace physx +{ +namespace Gu +{ + const PxReal signs[24] = + { + -1,-1,-1, + -1,-1, 1, + -1, 1,-1, + -1, 1, 1, + 1,-1,-1, + 1,-1, 1, + 1, 1,-1, + 1, 1, 1, + }; + + const char edges[24] = + { + 0,1, + 1,3, + 3,2, + 2,0, + 4,5, + 5,7, + 7,6, + 6,4, + 0,4, + 1,5, + 2,6, + 3,7, + }; + + struct TriggerTraceSegmentCallback + { + bool intersection; + + PX_INLINE TriggerTraceSegmentCallback() : intersection(false) + { + } + + PX_INLINE bool underFaceHit( + const HeightFieldUtil&, const PxVec3&, + const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; + } + + PX_INLINE bool faceHit(const HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) + { + intersection = true; + return false; + } + bool onEvent(PxU32 , PxU32* ) + { + return true; + } + }; + + + class OverlapHeightfieldTraceSegmentHelper + { + PX_NOCOPY(OverlapHeightfieldTraceSegmentHelper) + public: + OverlapHeightfieldTraceSegmentHelper(const HeightFieldUtil& hfUtil) + : mHfUtil(hfUtil) + { + mHfUtil.computeLocalBounds(mLocalBounds); + } + + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& aP1, TriggerTraceSegmentCallback* aCallback) const + { + mHfUtil.traceSegment<TriggerTraceSegmentCallback, false, false>(aP0, aP1 - aP0, 1.0f, aCallback, mLocalBounds, false, NULL); + } + + private: + const HeightFieldUtil& mHfUtil; + PxBounds3 mLocalBounds; + }; + +} // namespace +} + +static bool intersectHeightFieldBox(const HeightFieldUtil& hfUtil, const Box& boxInHfShape) +{ + const HeightField& hf = hfUtil.getHeightField(); + + // Get box vertices + PxVec3 boxVertices[8]; + for(PxU32 i=0; i<8; i++) + boxVertices[i] = PxVec3(boxInHfShape.extents.x*signs[3*i], boxInHfShape.extents.y*signs[3*i+1], boxInHfShape.extents.z*signs[3*i+2]); + + // Transform box vertices to HeightFieldShape space + PxVec3 boxVerticesInHfShape[8]; + for(PxU32 i=0; i<8; i++) + boxVerticesInHfShape[i] = boxInHfShape.transform(boxVertices[i]); + + // Test box vertices. + { + for(PxU32 i=0; i<8; i++) + { + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; + if(hfUtil.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z)) + { + const PxReal y = hfUtil.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + const PxReal dy = boxVertexInHfShape.y - y; + if(hf.isDeltaHeightInsideExtent(dy)) + { + PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + if(faceIndex != 0xffffffff) + { + return true; + } + } + } + } + } + + // Test box edges. + { + OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + for(PxU32 i=0; i<12; i++) + { + const PxVec3 v0 = boxVerticesInHfShape[PxU8(edges[2*i])]; + const PxVec3 v1 = boxVerticesInHfShape[PxU8(edges[2*i+1])]; + TriggerTraceSegmentCallback cb; + traceSegmentHelper.traceSegment(v0, v1, &cb); + if(cb.intersection) + return true; + } + } + + // Test HeightField vertices. + { + PsTransformV _hfShape2BoxShape; + const PxQuat bq(boxInHfShape.rot); + const QuatV q1 = QuatVLoadU(&bq.x); + const Vec3V p1 = V3LoadU(&boxInHfShape.center.x); + const PsTransformV _boxPose(p1, q1); + _hfShape2BoxShape = _boxPose.getInverse(); + + PxReal minx(PX_MAX_REAL); + PxReal minz(PX_MAX_REAL); + PxReal maxx(-PX_MAX_REAL); + PxReal maxz(-PX_MAX_REAL); + + for(PxU32 i=0; i<8; i++) + { + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; + +/* if(boxVertexInHfShape.x < minx) minx = boxVertexInHfShape.x; + if(boxVertexInHfShape.z < minz) minz = boxVertexInHfShape.z; + if(boxVertexInHfShape.x > maxx) maxx = boxVertexInHfShape.x; + if(boxVertexInHfShape.z > maxz) maxz = boxVertexInHfShape.z;*/ + minx = physx::intrinsics::selectMin(boxVertexInHfShape.x, minx); + minz = physx::intrinsics::selectMin(boxVertexInHfShape.z, minz); + maxx = physx::intrinsics::selectMax(boxVertexInHfShape.x, maxx); + maxz = physx::intrinsics::selectMax(boxVertexInHfShape.z, maxz); + } + + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxU32 minRow = hf.getMinRow(minx * oneOverRowScale); + const PxU32 maxRow = hf.getMaxRow(maxx * oneOverRowScale); + const PxU32 minColumn = hf.getMinColumn(minz * oneOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(maxz * oneOverColumnScale); + + const Vec4V extentV = V4LoadXYZW(boxInHfShape.extents.x, boxInHfShape.extents.y, boxInHfShape.extents.z, PX_MAX_REAL); + const PxHeightFieldGeometry& geom = hfUtil.getHeightFieldGeometry(); + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + if(hfUtil.isQueryVertex(vertexIndex, row, column)) + { + // check if hf vertex is inside the box + const Vec4V hfVertex = V4LoadXYZW(geom.rowScale * row, geom.heightScale * hf.getHeight(vertexIndex), geom.columnScale * column, 0.0f); + const Vec4V hfVertexInBoxShape = Vec4V_From_Vec3V(_hfShape2BoxShape.transform(Vec3V_From_Vec4V(hfVertex))); + const Vec4V hfVertexInBoxShapeAbs = V4Abs(hfVertexInBoxShape); + + if(V4AllGrtr(extentV, hfVertexInBoxShapeAbs)) + { + return true; + } + } + } + } + } + return false; +} + +static Matrix34 multiplyInverseRTLeft(const Matrix34& left, const Matrix34& right) +{ +// t = left.M % (right.t - left.t); + PxVec3 t = left.rotateTranspose(right.p - left.p); + +// M.setMultiplyTransposeLeft(left.M, right.M); + const PxMat33& left33 = left.m; + const PxMat33& right33 = right.m; + PxMat33 multiplyTransposeLeft33 = (left33.getTranspose()) * right33; + + return Matrix34(multiplyTransposeLeft33, t); +} + +static bool intersectHeightFieldConvex( + const HeightFieldUtil& hfUtil, const PxTransform& _hfAbsPose, const ConvexMesh& convexMesh, + const PxTransform& _convexAbsPose, const PxMeshScale& convexMeshScaling) +{ + const Matrix34 hfAbsPose34(_hfAbsPose); + const Matrix34 convexAbsPose34(_convexAbsPose); + const Matrix34 vertexToShapeSkew34(convexMeshScaling.toMat33()); + const Matrix34 temp34 = convexAbsPose34 * vertexToShapeSkew34; + const Matrix34 convexShape2HfShapeSkew34 = multiplyInverseRTLeft(hfAbsPose34, temp34); + + const ConvexHullData* hull = &convexMesh.getHull(); + + // Allocate space for transformed vertices. + PxVec3* convexVerticesInHfShape = reinterpret_cast<PxVec3*>(PxAlloca(hull->mNbHullVertices*sizeof(PxVec3))); + + // Transform vertices to height field shape + const PxVec3* hullVerts = hull->getHullVertices(); + for(PxU32 i=0; i<hull->mNbHullVertices; i++) + convexVerticesInHfShape[i] = convexShape2HfShapeSkew34.transform(hullVerts[i]); + + // Compute bounds of convex in hf space + PxBounds3 convexBoundsInHfShape; + computeBoundsAroundVertices(convexBoundsInHfShape, hull->mNbHullVertices, convexVerticesInHfShape); + + // Compute the height field extreme over the bounds area. + const HeightField& hf = hfUtil.getHeightField(); + PxReal hfExtreme = (hf.getThicknessFast() <= 0) ? -PX_MAX_REAL : PX_MAX_REAL; + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxReal rowScale = (1.0f / hfUtil.getOneOverRowScale()); + const PxReal columnScale = (1.0f / hfUtil.getOneOverColumnScale()); + const PxReal heightScale = (1.0f / hfUtil.getOneOverHeightScale()); + + // negative scale support + PxU32 minRow; + PxU32 maxRow; + if(oneOverRowScale > 0.0f) + { + minRow = hf.getMinRow(convexBoundsInHfShape.minimum.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShape.maximum.x * oneOverRowScale); + } + else + { + minRow = hf.getMinRow(convexBoundsInHfShape.maximum.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShape.minimum.x * oneOverRowScale); + } + + PxU32 minColumn; + PxU32 maxColumn; + if(oneOverColumnScale > 0.0f) + { + minColumn = hf.getMinColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale); + } + else + { + minColumn = hf.getMinColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale); + } + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxReal h = hf.getHeight(row * hf.getNbColumnsFast() + column); + hfExtreme = (hf.getThicknessFast() <= 0) ? PxMax(hfExtreme, h) : PxMin(hfExtreme, h); + } + } + hfExtreme *= heightScale; + + + // Return if convex is on the wrong side of the extreme. + if(hf.getThicknessFast() <= 0) + { + if(convexBoundsInHfShape.minimum.y > hfExtreme) + return false; + } + else + { + if(convexBoundsInHfShape.maximum.y < hfExtreme) + return false; + } + + + // Test convex vertices + { + for(PxU32 i=0; i<hull->mNbHullVertices; i++) + { + const PxVec3& convexVertexInHfShape = convexVerticesInHfShape[i]; + bool insideExtreme = (hf.getThicknessFast() <= 0) ? (convexVertexInHfShape.y < hfExtreme) : (convexVertexInHfShape.y > hfExtreme); + if(insideExtreme && hfUtil.isShapePointOnHeightField(convexVertexInHfShape.x, convexVertexInHfShape.z)) + { + const PxReal y = hfUtil.getHeightAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z); + const PxReal dy = convexVertexInHfShape.y - y; + if(hf.isDeltaHeightInsideExtent(dy)) + { + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z); + if(faceIndex != 0xffffffff) + return true; + } + } + } + } + + // Test convex edges. + { + EdgeCache edgeCache; + PxU32 numPolygons = hull->mNbPolygons; + const HullPolygonData* polygons = hull->mPolygons; + const PxU8* const vertexData = hull->getVertexData8(); + + OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + while(numPolygons--) + { + const HullPolygonData& polygon = *polygons++; + + const PxU8* verts = vertexData + polygon.mVRef8; + + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = verts[a]; + PxU8 vi1 = verts[b]; + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + if(edgeCache.isInCache(vi0, vi1)) //avoid processing edges 2x if possible (this will typically have cache misses about 5% of the time leading to 5% redundant work. + continue; + + const PxVec3& sv0 = convexVerticesInHfShape[vi0]; + const PxVec3& sv1 = convexVerticesInHfShape[vi1]; + a = b; + b++; + + + if(hf.getThicknessFast() <= 0) + { + if((sv0.y > hfExtreme) && (sv1.y > hfExtreme)) + continue; + } + else + { + if((sv0.y < hfExtreme) && (sv1.y < hfExtreme)) + continue; + } + const PxVec3 v0 = sv0; + const PxVec3 v1 = sv1; + TriggerTraceSegmentCallback cb; + traceSegmentHelper.traceSegment(v0, v1, &cb); + if(cb.intersection) + return true; + } + } + } + + // Test HeightField vertices + { + const Matrix34 tmp34 = multiplyInverseRTLeft(convexAbsPose34, hfAbsPose34); + const Matrix34 hfShape2ConvexShapeSkew34 = vertexToShapeSkew34 * tmp34; + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxU32 hfVertexIndex = row * hf.getNbColumnsFast() + column; + if(hfUtil.isQueryVertex(hfVertexIndex, row, column)) + { + // Check if hf vertex is inside the convex + const PxVec3 hfVertex(rowScale * row, heightScale * hf.getHeight(hfVertexIndex), columnScale * column); + const PxVec3 hfVertexInConvexShape = hfShape2ConvexShapeSkew34.transform(hfVertex); + + bool inside = true; + for(PxU32 poly = 0; poly < hull->mNbPolygons; poly++) + { + PxReal d = hull->mPolygons[poly].mPlane.distance(hfVertexInConvexShape); + if(d >= 0) + { + inside = false; + break; + } + } + if(inside) + return true; + } + } + } + } + return false; +} + +bool Gu::checkOverlapAABB_heightFieldGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); + + const Matrix34 invAbsPose(pose.getInverse()); + + const Box boxInHfShape( + invAbsPose.transform(box.getCenter()), + box.getExtents(), + invAbsPose.m); + + HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldBox(hfUtil, boxInHfShape); +} + +bool GeomOverlapCallback_SphereHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + const Sphere sphereInHf(pose1.transformInv(pose0.p), sphereGeom.radius); + + HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldSphere(hfUtil, sphereInHf); +} + +bool GeomOverlapCallback_CapsuleHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + const PxTransform capsuleShapeToHfShape = pose1.transformInv(pose0); + + const HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldCapsule(hfUtil, capsuleGeom, capsuleShapeToHfShape); +} + +bool GeomOverlapCallback_BoxHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + const PxTransform boxShape2HfShape = pose1.transformInv(pose0); + + Box box; + buildFrom(box, boxShape2HfShape.p, boxGeom.halfExtents, boxShape2HfShape.q); + + HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldBox(hfUtil, box); +} + +/////////////////////////////////////////////////////////////////////////////// +bool GeomOverlapCallback_ConvexHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom1); + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldConvex(hfUtil, pose1, *cm, pose0, convexGeom.scale); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp b/PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp new file mode 100644 index 00000000..323a7cb6 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/hf/GuSweepsHF.cpp @@ -0,0 +1,604 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "GuVecCapsule.h" +#include "GuSweepMTD.h" +#include "GuSweepTriangleUtils.h" +#include "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "CmScaling.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuInternal.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +#include "GuSweepConvexTri.h" + +#define AbortTraversal false +#define ContinueTraversal true + +/////////////////////////////////////////////////////////////////////////////// + +class HeightFieldTraceSegmentSweepHelper +{ + PX_NOCOPY(HeightFieldTraceSegmentSweepHelper) +public: + HeightFieldTraceSegmentSweepHelper(const HeightFieldUtil& hfUtil, const PxVec3& aabbExtentHfLocalSpace) + : mHfUtil(hfUtil), mOverlapObjectExtent(aabbExtentHfLocalSpace) + { + mHfUtil.computeLocalBounds(mLocalBounds); + // extend the bounds + mLocalBounds.minimum = mLocalBounds.minimum - aabbExtentHfLocalSpace; + mLocalBounds.maximum = mLocalBounds.maximum + aabbExtentHfLocalSpace; + } + + template<class T> + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDirNorm, const float rayLength, T* aCallback) const + { + mHfUtil.traceSegment<T, false, true>(aP0, rayDirNorm, rayLength, aCallback, mLocalBounds, false, &mOverlapObjectExtent); + } + +private: + const HeightFieldUtil& mHfUtil; + const PxVec3& mOverlapObjectExtent; + PxBounds3 mLocalBounds; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class HeightFieldTraceSegmentReport : public EntityReport<PxU32> +{ + PX_NOCOPY(HeightFieldTraceSegmentReport) +public: + + HeightFieldTraceSegmentReport(const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags) : + mHfUtil (hfUtil), + mHitFlags (hitFlags), + mStatus (false), + mInitialOverlap (false), + mIsDoubleSided ((hfUtil.getHeightFieldGeometry().heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES)), + mIsAnyHit (hitFlags & PxHitFlag::eMESH_ANY) + { + } + + bool underFaceHit(const Gu::HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; + } + + bool faceHit(const Gu::HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) + { + return true; + } + + protected: + const HeightFieldUtil& mHfUtil; + const PxHitFlags mHitFlags; + bool mStatus; + bool mInitialOverlap; + const bool mIsDoubleSided; + const bool mIsAnyHit; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class CapsuleTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(CapsuleTraceSegmentReport) +public: + CapsuleTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, + const Capsule& inflatedCapsule, + const PxVec3& unitDir, PxSweepHit& sweepHit, const PxTransform& pose, PxReal distance) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mInflatedCapsule (inflatedCapsule), + mUnitDir (unitDir), + mSweepHit (sweepHit), + mPose (pose), + mDistance (distance) + { + } + + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + PX_ALIGN_PREFIX(16) PxU8 tribuf[HF_SWEEP_REPORT_BUFFER_SIZE*sizeof(PxTriangle)] PX_ALIGN_SUFFIX(16); + PxTriangle* tmpT = reinterpret_cast<PxTriangle*>(tribuf); + PX_ASSERT(nb <= HF_SWEEP_REPORT_BUFFER_SIZE); + for(PxU32 i=0; i<nb; i++) + { + const PxU32 triangleIndex = indices[i]; + mHfUtil.getTriangle(mPose, tmpT[i], NULL, NULL, triangleIndex, true); + } + + PxSweepHit h; // PT: TODO: ctor! + // PT: this one is safe because cullbox is NULL (no need to allocate one more triangle) + // PT: TODO: is it ok to pass the initial distance here? + PxVec3 bestNormal; + const bool status = sweepCapsuleTriangles_Precise(nb, tmpT, mInflatedCapsule, mUnitDir, mDistance, NULL, h, bestNormal, mHitFlags, mIsDoubleSided); + if(status && (h.distance <= mSweepHit.distance)) + { + mSweepHit.faceIndex = indices[h.faceIndex]; + mSweepHit.normal = h.normal; + mSweepHit.position = h.position; + mSweepHit.distance = h.distance; + + mStatus = true; + if(h.distance == 0.0f) + { + mInitialOverlap = true; + return AbortTraversal; + } + + if(mIsAnyHit) + return AbortTraversal; + } + return ContinueTraversal; + } + + bool finalizeHit(PxSweepHit& sweepHit, const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, const Capsule& lss, const Capsule& inflatedCapsule, const PxVec3& unitDir) + { + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + + if(mHitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(lss.p0); + const Vec3V p1 = V3LoadU(lss.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //calculate MTD + const bool hasContacts = computeCapsule_HeightFieldMTD(hfGeom, pose, capsuleV, inflatedCapsule.radius, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit); + + //ML: the center of mass is below the surface, we won't have MTD contact generate + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL| PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX; + } + return true; + } + + private: + const Capsule& mInflatedCapsule; + const PxVec3& mUnitDir; + PxSweepHit& mSweepHit; + const PxTransform& mPose; + const PxReal mDistance; +}; + +bool sweepCapsule_HeightFieldGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + // Compute swept box + Box capsuleBox; + computeBoxAroundCapsule(inflatedCapsule, capsuleBox); + + const PxVec3 capsuleAABBExtents = capsuleBox.computeAABBExtent(); + + const HeightFieldUtil hfUtil(hfGeom); + CapsuleTraceSegmentReport myReport(hfUtil, hitFlags, inflatedCapsule, unitDir, sweepHit, pose, distance); + + sweepHit.distance = PX_MAX_F32; + + // need hf local space stuff + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(capsuleBox.center); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 capsuleAABBBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), capsuleAABBExtents).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, capsuleAABBBExtentHfLocalSpace); + traceSegmentHelper.traceSegment<CapsuleTraceSegmentReport>(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); + + return myReport.finalizeHit(sweepHit, hfGeom, pose, lss, inflatedCapsule, unitDir); +} + +/////////////////////////////////////////////////////////////////////////////// + +class ConvexTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(ConvexTraceSegmentReport) +public: + ConvexTraceSegmentReport( const HeightFieldUtil& hfUtil, const ConvexHullData& hull, const PxMeshScale& convexScale, + const PxTransform& convexPose, const PxTransform& heightFieldPose, + const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, PxReal inflation) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mUnitDir (unitDir), + mInflation (inflation) + { + using namespace Ps::aos; + mSweepHit.faceIndex = 0xFFFFffff; + mSweepHit.distance = distance; + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const QuatV q0 = QuatVLoadU(&heightFieldPose.q.x); + const Vec3V p0 = V3LoadU(&heightFieldPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV meshTransf(p0, q0); + const PsTransformV convexTransf(p1, q1); + + mMeshToConvex = convexTransf.transformInv(meshTransf); + mConvexPoseV = convexTransf; + mConvexSpaceDir = convexTransf.rotateInv(V3Neg(V3Scale(worldDir, dist))); + mDistance = dist; + + const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); + + mMeshSpaceUnitDir = heightFieldPose.rotateInv(unitDir); + mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); + } + + virtual PxAgain onEvent(PxU32 nbEntities, PxU32* entities) + { + const PxTransform idt(PxIdentity); + for(PxU32 i=0; i<nbEntities; i++) + { + PxTriangle tri; + mHfUtil.getTriangle(idt, tri, NULL, NULL, entities[i], false, false); // First parameter not needed if local space triangle is enough + + // use mSweepHit.distance as max sweep distance so far, mSweepHit.distance will be clipped by this function + if(sweepConvexVsTriangle( tri.verts[0], tri.verts[1], tri.verts[2], mConvexHull, mMeshToConvex, mConvexPoseV, + mConvexSpaceDir, mUnitDir, mMeshSpaceUnitDir, mDistance, mSweepHit.distance, mSweepHit, mIsDoubleSided, + mInflation, mInitialOverlap, entities[i])) + { + mStatus = true; + if(mIsAnyHit || mSweepHit.distance == 0.0f) + return AbortTraversal; + } + } + return ContinueTraversal; + } + + bool finalizeHit(PxSweepHit& sweepHit, + const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation) + { + if(!mStatus) + return false; + + if(mInitialOverlap) + { + if(mHitFlags & PxHitFlag::eMTD) + { + const bool hasContacts = computeConvex_HeightFieldMTD(hfGeom, pose, convexGeom, convexPose, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit); + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + setInitialOverlapResults(sweepHit, unitDir, mSweepHit.faceIndex); // hit index must be set to closest for IO + } + } + else + { + sweepHit = mSweepHit; + sweepHit.normal = -sweepHit.normal; + sweepHit.normal.normalize(); + } + return true; + } + + private: + PsMatTransformV mMeshToConvex; + PsTransformV mConvexPoseV; + ConvexHullV mConvexHull; + PxSweepHit mSweepHit; + Vec3V mConvexSpaceDir; + FloatV mDistance; + const PxVec3 mUnitDir; + PxVec3 mMeshSpaceUnitDir; + const PxReal mInflation; +}; + +bool sweepConvex_HeightFieldGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); + + const Matrix34 convexTM(convexPose); + const Matrix34 meshTM(pose); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABBLocalSpace = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + const HeightFieldUtil hfUtil(hfGeom); + ConvexTraceSegmentReport entityReport( + hfUtil, convexMesh->getHull(), convexGeom.scale, convexPose, pose, -unitDir, distance, hitFlags, inflation); + + // need hf local space stuff + const PxBounds3 hullAABB = PxBounds3::transformFast(convexPose, hullAABBLocalSpace); + const PxVec3 aabbExtents = hullAABB.getExtents() + PxVec3(inflation); + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(hullAABB.getCenter()); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 convexAABBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), aabbExtents).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, convexAABBExtentHfLocalSpace); + traceSegmentHelper.traceSegment<ConvexTraceSegmentReport>(centerLocalSpace, sweepDirLocalSpace, distance, &entityReport); + + return entityReport.finalizeHit(sweepHit, hfGeom, pose, convexGeom, convexPose, unitDir, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(BoxTraceSegmentReport) +public: + BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, + const PsTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion, + PxSweepHit& sweepHit, PxReal inflation) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mWorldToBoxV (worldToBoxV), + mPose (pose), + mBox (box), + mLocalMotion (localMotion), + mSweepHit (sweepHit), + mInflation (inflation) + { + mMinToi = FMax(); + } + + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V dir = V3LoadU(mLocalMotion); + //FloatV minToi = FMax(); + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + + for(PxU32 i=0; i<nb; i++) + { + const PxU32 triangleIndex = indices[i]; + + PxTriangle currentTriangle; // in world space + mHfUtil.getTriangle(mPose, currentTriangle, NULL, NULL, triangleIndex, true, true); + + const Vec3V localV0 = V3LoadU(currentTriangle.verts[0]); + const Vec3V localV1 = V3LoadU(currentTriangle.verts[1]); + const Vec3V localV2 = V3LoadU(currentTriangle.verts[2]); + + const Vec3V triV0 = mWorldToBoxV.transform(localV0); + const Vec3V triV1 = mWorldToBoxV.transform(localV1); + const Vec3V triV2 = mWorldToBoxV.transform(localV2); + + if(!mIsDoubleSided) + { + const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1)); + if(FAllGrtrOrEq(V3Dot(triNormal, dir), zero)) + continue; + } + + const TriangleV triangle(triV0, triV1, triV2); + + ////move triangle to box space + //const Vec3V localV0 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[0])); + //const Vec3V localV1 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[1])); + //const Vec3V localV2 = Vec3V_From_PxVec3(WorldToBox.transform(currentTriangle.verts[2])); + + //TriangleV triangle(localV0, localV1, localV2); + const LocalConvex<TriangleV> convexA(triangle); + const LocalConvex<BoxV> convexB(mBox); + const Vec3V initialSearchDir = V3Sub(triangle.getCenter(), mBox.getCenter()); + + if(gjkRaycastPenetration< LocalConvex<TriangleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, mInflation, false)) + { + mStatus = true; + if(FAllGrtr(toi, zero)) + { + if(FAllGrtr(mMinToi, toi)) + { + mMinToi = toi; + FStore(toi, &mSweepHit.distance); + V3StoreU(normal, mSweepHit.normal); + V3StoreU(closestA, mSweepHit.position); + mSweepHit.faceIndex = triangleIndex; + + if(mIsAnyHit) + return AbortTraversal; + } + } + else + { + mSweepHit.distance = 0.0f; + mSweepHit.faceIndex = triangleIndex; + mInitialOverlap = true; + return AbortTraversal; + } + } + } + return ContinueTraversal; + } + + bool finalizeHit(PxSweepHit& sweepHit, + const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, + const PxTransform& boxPose_, const Box& box, + const PxVec3& unitDir, PxReal distance, PxReal inflation) + { + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + + if(mHitFlags & PxHitFlag::eMTD) + { + const bool hasContacts = computeBox_HeightFieldMTD(hfGeom, pose, box, boxPose_, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit); + + //ML: the center of mass is below the surface, we won't have MTD contact generate + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + PxVec3 n = sweepHit.normal.getNormalized(); + if((n.dot(mLocalMotion))>0.0f) + n = -n; + + sweepHit.distance *= distance; // stored as toi [0,1] during computation -> scale + sweepHit.normal = boxPose_.rotate(n); + sweepHit.position = boxPose_.transform(sweepHit.position); + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + } + return true; + } + + private: + const PsTransformV& mWorldToBoxV; + const PxTransform& mPose; + const BoxV& mBox; + FloatV mMinToi; + const PxVec3 mLocalMotion; + PxSweepHit& mSweepHit; + const PxReal mInflation; +}; + +#if PX_VC + #pragma warning(pop) +#endif + +bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(boxGeom_); + PX_UNUSED(hitFlags); + + const PxHeightFieldGeometry& hfGeom = static_cast<const PxHeightFieldGeometry&>(geom); + + const PxVec3 boxAABBExtent = box.computeAABBExtent() + PxVec3(inflation); + + // Move to AABB space + PX_ALIGN_PREFIX(16) PxTransform WorldToBox PX_ALIGN_SUFFIX(16); + WorldToBox = boxPose_.getInverse(); + + const QuatV q1 = QuatVLoadA(&WorldToBox.q.x); + const Vec3V p1 = V3LoadA(&WorldToBox.p.x); + const PsTransformV WorldToBoxV(p1, q1); + + const PxVec3 motion = unitDir * distance; + const PxVec3 localMotion = WorldToBox.rotate(motion); + + const BoxV boxV(V3Zero(), V3LoadU(box.extents)); + + sweepHit.distance = PX_MAX_F32; + + const HeightFieldUtil hfUtil(hfGeom); + BoxTraceSegmentReport myReport(hfUtil, hitFlags, WorldToBoxV, pose, boxV, localMotion, sweepHit, inflation); + + // need hf local space stuff + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(box.center); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 boxAABBExtentInHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), boxAABBExtent).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, boxAABBExtentInHfLocalSpace); + traceSegmentHelper.traceSegment<BoxTraceSegmentReport>(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); + + return myReport.finalizeHit(sweepHit, hfGeom, pose, boxPose_, box, unitDir, distance, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionBoxBox.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionBoxBox.cpp new file mode 100644 index 00000000..19c8c1d0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionBoxBox.cpp @@ -0,0 +1,139 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionBoxBox.h" + +using namespace physx; + +bool Gu::intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test) +{ + // Translation, in parent frame + const PxVec3 v = c1 - c0; + // Translation, in A's frame + const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2])); + + // B's basis with respect to A's local frame + PxReal R[3][3]; + PxReal FR[3][3]; + PxReal ra, rb, t; + + // Calculate rotation matrix + for(PxU32 i=0;i<3;i++) + { + for(PxU32 k=0;k<3;k++) + { + R[i][k] = r0[i].dot(r1[k]); + FR[i][k] = 1e-6f + PxAbs(R[i][k]); // Precompute fabs matrix + } + } + + // A's basis vectors + for(PxU32 i=0;i<3;i++) + { + ra = e0[i]; + + rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2]; + + t = PxAbs(T[i]); + + if(t > ra + rb) return false; + } + + // B's basis vectors + for(PxU32 k=0;k<3;k++) + { + ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k]; + + rb = e1[k]; + + t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]); + + if( t > ra + rb ) return false; + } + + if(full_test) + { + //9 cross products + + //L = A0 x B0 + ra = e0[1]*FR[2][0] + e0[2]*FR[1][0]; + rb = e1[1]*FR[0][2] + e1[2]*FR[0][1]; + t = PxAbs(T[2]*R[1][0] - T[1]*R[2][0]); + if(t > ra + rb) return false; + + //L = A0 x B1 + ra = e0[1]*FR[2][1] + e0[2]*FR[1][1]; + rb = e1[0]*FR[0][2] + e1[2]*FR[0][0]; + t = PxAbs(T[2]*R[1][1] - T[1]*R[2][1]); + if(t > ra + rb) return false; + + //L = A0 x B2 + ra = e0[1]*FR[2][2] + e0[2]*FR[1][2]; + rb = e1[0]*FR[0][1] + e1[1]*FR[0][0]; + t = PxAbs(T[2]*R[1][2] - T[1]*R[2][2]); + if(t > ra + rb) return false; + + //L = A1 x B0 + ra = e0[0]*FR[2][0] + e0[2]*FR[0][0]; + rb = e1[1]*FR[1][2] + e1[2]*FR[1][1]; + t = PxAbs(T[0]*R[2][0] - T[2]*R[0][0]); + if(t > ra + rb) return false; + + //L = A1 x B1 + ra = e0[0]*FR[2][1] + e0[2]*FR[0][1]; + rb = e1[0]*FR[1][2] + e1[2]*FR[1][0]; + t = PxAbs(T[0]*R[2][1] - T[2]*R[0][1]); + if(t > ra + rb) return false; + + //L = A1 x B2 + ra = e0[0]*FR[2][2] + e0[2]*FR[0][2]; + rb = e1[0]*FR[1][1] + e1[1]*FR[1][0]; + t = PxAbs(T[0]*R[2][2] - T[2]*R[0][2]); + if(t > ra + rb) return false; + + //L = A2 x B0 + ra = e0[0]*FR[1][0] + e0[1]*FR[0][0]; + rb = e1[1]*FR[2][2] + e1[2]*FR[2][1]; + t = PxAbs(T[1]*R[0][0] - T[0]*R[1][0]); + if(t > ra + rb) return false; + + //L = A2 x B1 + ra = e0[0]*FR[1][1] + e0[1]*FR[0][1]; + rb = e1[0] *FR[2][2] + e1[2]*FR[2][0]; + t = PxAbs(T[1]*R[0][1] - T[0]*R[1][1]); + if(t > ra + rb) return false; + + //L = A2 x B2 + ra = e0[0]*FR[1][2] + e0[1]*FR[0][2]; + rb = e1[0]*FR[2][1] + e1[1]*FR[2][0]; + t = PxAbs(T[1]*R[0][2] - T[0]*R[1][2]); + if(t > ra + rb) return false; + } + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionCapsuleTriangle.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionCapsuleTriangle.cpp new file mode 100644 index 00000000..6451405c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionCapsuleTriangle.cpp @@ -0,0 +1,61 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionCapsuleTriangle.h" +#include "GuDistancePointSegment.h" + +using namespace physx; +using namespace Gu; + +bool Gu::intersectCapsuleTriangle(const PxVec3& N, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Gu::Capsule& capsule, const CapsuleTriangleOverlapData& params) +{ + PX_ASSERT(capsule.p0!=capsule.p1); + + { + const PxReal d2 = distancePointSegmentSquaredInternal(capsule.p0, params.mCapsuleDir, p0); + if(d2<=capsule.radius*capsule.radius) + return true; + } + +// const PxVec3 N = (p0 - p1).cross(p0 - p2); + + if(!testAxis(p0, p1, p2, capsule, N)) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p0, p1 - p0, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p1, p2 - p1, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p2, p0 - p2, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionCapsuleTriangle.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionCapsuleTriangle.h new file mode 100644 index 00000000..1e327360 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionCapsuleTriangle.h @@ -0,0 +1,137 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_CAPSULE_TRIANGLE_H +#define GU_INTERSECTION_CAPSULE_TRIANGLE_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "PsUtilities.h" + +namespace physx +{ +namespace Gu +{ + // PT: precomputed data for capsule-triangle test. Useful when testing the same capsule vs several triangles. + struct CapsuleTriangleOverlapData + { + PxVec3 mCapsuleDir; + float mBDotB; + float mOneOverBDotB; + + void init(const Capsule& capsule) + { + const PxVec3 dir = capsule.p1 - capsule.p0; + const float BDotB = dir.dot(dir); + mCapsuleDir = dir; + mBDotB = BDotB; + mOneOverBDotB = BDotB!=0.0f ? 1.0f/BDotB : 0.0f; + } + }; + + // PT: tests if projections of capsule & triangle overlap on given axis + PX_FORCE_INLINE PxU32 testAxis(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Capsule& capsule, const PxVec3& axis) + { + // Project capsule + float min0 = capsule.p0.dot(axis); + float max0 = capsule.p1.dot(axis); + if(min0>max0) + Ps::swap(min0, max0); + const float MR = axis.magnitude()*capsule.radius; + min0 -= MR; + max0 += MR; + + // Project triangle + float min1, max1; + { + min1 = max1 = p0.dot(axis); + float dp = p1.dot(axis); + if(dp<min1) min1 = dp; + if(dp>max1) max1 = dp; + dp = p2.dot(axis); + if(dp<min1) min1 = dp; + if(dp>max1) max1 = dp; + } + + // Test projections + if(max0<min1 || max1<min0) + return 0; + + return 1; + } + + // PT: computes shortest vector going from capsule axis to triangle edge + PX_FORCE_INLINE PxVec3 computeEdgeAxis( const PxVec3& p, const PxVec3& a, + const PxVec3& q, const PxVec3& b, + float BDotB, float oneOverBDotB) + { + const PxVec3 T = q - p; + const float ADotA = a.dot(a); + const float ADotB = a.dot(b); + const float ADotT = a.dot(T); + const float BDotT = b.dot(T); + + const float denom = ADotA*BDotB - ADotB*ADotB; + + float t = denom!=0.0f ? (ADotT*BDotB - BDotT*ADotB) / denom : 0.0f; + t = PxClamp(t, 0.0f, 1.0f); + + float u = (t*ADotB - BDotT) * oneOverBDotB; + + if(u<0.0f) + { + u = 0.0f; + t = ADotT / ADotA; + t = PxClamp(t, 0.0f, 1.0f); + } + else if(u>1.0f) + { + u = 1.0f; + t = (ADotB + ADotT) / ADotA; + t = PxClamp(t, 0.0f, 1.0f); + } + return T + b*u - a*t; + } + + /** + * Checks if a capsule intersects a triangle. + * + * \param normal [in] triangle normal (orientation does not matter) + * \param p0 [in] triangle's first point + * \param p1 [in] triangle's second point + * \param p2 [in] triangle's third point + * \param capsule [in] capsule + * \param params [in] precomputed capsule params + * \return true if capsule overlaps triangle + */ + bool intersectCapsuleTriangle(const PxVec3& normal, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Gu::Capsule& capsule, const CapsuleTriangleOverlapData& params); +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionEdgeEdge.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionEdgeEdge.cpp new file mode 100644 index 00000000..3f828ba5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionEdgeEdge.cpp @@ -0,0 +1,84 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionEdgeEdge.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +bool Gu::intersectEdgeEdge(const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) +{ + const PxVec3 v1 = p2 - p1; + + // Build plane P based on edge (p1, p2) and direction (dir) + PxPlane plane; + plane.n = v1.cross(dir); + plane.d = -(plane.n.dot(p1)); + + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // find largest 2D plane projection + PxU32 i,j; + Ps::closestAxis(plane.n, i, j); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))/(v1[i]*dir[j]-v1[j]*dir[i]); + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<1e-3f) return true; // collision found + + return false; // no collision +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionEdgeEdge.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionEdgeEdge.h new file mode 100644 index 00000000..ccff0f35 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionEdgeEdge.h @@ -0,0 +1,52 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_EDGE_EDGE_H +#define GU_INTERSECTION_EDGE_EDGE_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + // collide edge (p1,p2) moving in direction (dir) colliding + // width edge (p3,p4). Return true on a collision with + // collision distance (dist) and intersection point (ip) + // note: dist and ip are invalid if function returns false. + // note: ip is on (p1,p2), not (p1+dist*dir,p2+dist*dir) + PX_PHYSX_COMMON_API bool intersectEdgeEdge(const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRay.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRay.h new file mode 100644 index 00000000..ce52150e --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRay.h @@ -0,0 +1,38 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_H +#define GU_INTERSECTION_RAY_H + +// PT: small distance between a ray origin and a potentially hit surface. Should be small enough to +// limit accuracy issues coming from large distance values, but not too close to the surface to make +// sure we don't start inside the shape. +#define GU_RAY_SURFACE_OFFSET 10.0f + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBox.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBox.cpp new file mode 100644 index 00000000..44d583f3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBox.cpp @@ -0,0 +1,449 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxVec3.h" +#include "foundation/PxIntrinsics.h" +#include "PsFPU.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionRayBoxSIMD.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: [email protected] +* +* \param aabb [in] the axis-aligned bounding box +* \param origin [in] ray origin +* \param dir [in] ray direction +* \param coord [out] impact coordinates +* \return true if ray intersects AABB +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define RAYAABB_EPSILON 0.00001f +bool Gu::rayAABBIntersect(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord) +{ + Ps::IntBool Inside = Ps::IntTrue; + PxVec3 MaxT(-1.0f, -1.0f, -1.0f); + const PxReal* dir = &_dir.x; + const PxU32* idir = reinterpret_cast<const PxU32*>(dir); + // Find candidate planes. + for(PxU32 i=0;i<3;i++) + { + if(origin[i] < minimum[i]) + { + coord[i] = minimum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (minimum[i] - origin[i]) / dir[i]; + } + else if(origin[i] > maximum[i]) + { + coord[i] = maximum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (maximum[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + return true; + } + + // Get largest of the maxT's for final choice of intersection + PxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + const PxU32* tmp = reinterpret_cast<const PxU32*>(&MaxT[WhichPlane]); + if((*tmp)&PX_SIGN_BITMASK) +// if(PX_IR(MaxT[WhichPlane])&PX_SIGN_BITMASK) + return false; + + for(PxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < minimum[i] - RAYAABB_EPSILON || coord[i] > maximum[i] + RAYAABB_EPSILON) +#else + if(coord[i] < minimum[i] || coord[i] > maximum[i]) +#endif + return false; + } + } + return true; // ray hits box +} + + + +/** +* Computes a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* Return of intersected face code and parameter by Adam! Also modified behavior for ray starts inside AABB. 2004 :-p +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: [email protected] +* +* \param minimum [in] the smaller corner of the bounding box +* \param maximum [in] the larger corner of the bounding box +* \param origin [in] ray origin +* \param _dir [in] ray direction +* \param coord [out] impact coordinates +* \param t [out] t such that coord = origin + dir * t +* \return false if ray does not intersect AABB, or ray origin is inside AABB. Else: + 1 + coordinate index of box axis that was hit + + Note: sign bit that determines if the minimum (0) or maximum (1) of the axis was hit is equal to sign(coord[returnVal-1]). +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 Gu::rayAABBIntersect2(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord, PxReal & t) +{ + Ps::IntBool Inside = Ps::IntTrue; + PxVec3 MaxT(-1.0f, -1.0f, -1.0f); + const PxReal* dir = &_dir.x; + const PxU32* idir = reinterpret_cast<const PxU32*>(dir); + // Find candidate planes. + for(PxU32 i=0;i<3;i++) + { + if(origin[i] < minimum[i]) + { + coord[i] = minimum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (minimum[i] - origin[i]) / dir[i]; + } + else if(origin[i] > maximum[i]) + { + coord[i] = maximum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (maximum[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + t = 0; + return 1; + } + + // Get largest of the maxT's for final choice of intersection + PxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + const PxU32* tmp = reinterpret_cast<const PxU32*>(&MaxT[WhichPlane]); + if((*tmp)&PX_SIGN_BITMASK) +// if(PX_IR(MaxT[WhichPlane])&PX_SIGN_BITMASK) + return 0; + + for(PxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < minimum[i] - RAYAABB_EPSILON || coord[i] > maximum[i] + RAYAABB_EPSILON) return 0; +#else + if(coord[i] < minimum[i] || coord[i] > maximum[i]) return 0; +#endif + } + } + t = MaxT[WhichPlane]; + return 1 + WhichPlane; // ray hits box +} + +// Collide ray defined by ray origin (ro) and ray direction (rd) +// with the bounding box. Returns -1 on no collision and the face index +// for first intersection if a collision is found together with +// the distance to the collision points (tnear and tfar) + +// ptchernev: +// Should we use an enum, or should we keep the anonymous ints? +// Should we increment the return code by one (return 0 for non intersection)? + +int Gu::intersectRayAABB(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& ro, const PxVec3& rd, float& tnear, float& tfar) +{ + // Refactor + int ret=-1; + + tnear = -PX_MAX_F32; + tfar = PX_MAX_F32; + // PT: why did we change the initial epsilon value? + #define LOCAL_EPSILON PX_EPS_F32 + //#define LOCAL_EPSILON 0.0001f + + for(unsigned int a=0;a<3;a++) + { + if(rd[a]>-LOCAL_EPSILON && rd[a]<LOCAL_EPSILON) + { + if(ro[a]<minimum[a] || ro[a]>maximum[a]) + return -1; + } + else + { + const PxReal OneOverDir = 1.0f / rd[a]; + PxReal t1 = (minimum[a]-ro[a]) * OneOverDir; + PxReal t2 = (maximum[a]-ro[a]) * OneOverDir; + + unsigned int b = a; + if(t1>t2) + { + PxReal t=t1; + t1=t2; + t2=t; + b += 3; + } + + if(t1>tnear) + { + tnear = t1; + ret = int(b); + } + if(t2<tfar) + tfar=t2; + if(tnear>tfar || tfar<LOCAL_EPSILON) + return -1; + } + } + + if(tnear>tfar || tfar<LOCAL_EPSILON) + return -1; + + return ret; +} + +// PT: specialized version where oneOverDir is available +int Gu::intersectRayAABB(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& ro, const PxVec3& rd, const PxVec3& oneOverDir, float& tnear, float& tfar) +{ + // PT: why did we change the initial epsilon value? + #define LOCAL_EPSILON PX_EPS_F32 + //#define LOCAL_EPSILON 0.0001f + + if(physx::intrinsics::abs(rd.x)<LOCAL_EPSILON) +// if(rd.x>-LOCAL_EPSILON && rd.x<LOCAL_EPSILON) + if(ro.x<minimum.x || ro.x>maximum.x) + return -1; + if(physx::intrinsics::abs(rd.y)<LOCAL_EPSILON) +// if(rd.y>-LOCAL_EPSILON && rd.y<LOCAL_EPSILON) + if(ro.y<minimum.y || ro.y>maximum.y) + return -1; + if(physx::intrinsics::abs(rd.z)<LOCAL_EPSILON) +// if(rd.z>-LOCAL_EPSILON && rd.z<LOCAL_EPSILON) + if(ro.z<minimum.z || ro.z>maximum.z) + return -1; + + PxReal t1x = (minimum.x - ro.x) * oneOverDir.x; + PxReal t2x = (maximum.x - ro.x) * oneOverDir.x; + PxReal t1y = (minimum.y - ro.y) * oneOverDir.y; + PxReal t2y = (maximum.y - ro.y) * oneOverDir.y; + PxReal t1z = (minimum.z - ro.z) * oneOverDir.z; + PxReal t2z = (maximum.z - ro.z) * oneOverDir.z; + + int bx; + int by; + int bz; + + if(t1x>t2x) + { + PxReal t=t1x; t1x=t2x; t2x=t; + bx = 3; + } + else + { + bx = 0; + } + + if(t1y>t2y) + { + PxReal t=t1y; t1y=t2y; t2y=t; + by = 4; + } + else + { + by = 1; + } + + if(t1z>t2z) + { + PxReal t=t1z; t1z=t2z; t2z=t; + bz = 5; + } + else + { + bz = 2; + } + + int ret; +// if(t1x>tnear) // PT: no need to test for the first value + { + tnear = t1x; + ret = bx; + } +// tfar = PxMin(tfar, t2x); + tfar = t2x; // PT: no need to test for the first value + + if(t1y>tnear) + { + tnear = t1y; + ret = by; + } + tfar = PxMin(tfar, t2y); + + if(t1z>tnear) + { + tnear = t1z; + ret = bz; + } + tfar = PxMin(tfar, t2z); + + if(tnear>tfar || tfar<LOCAL_EPSILON) + return -1; + + return ret; +} + +bool Gu::intersectRayAABB2( + const PxVec3& minimum, const PxVec3& maximum, const PxVec3& ro, const PxVec3& rd, float maxDist, float& tnear, float& tfar) +{ + PX_ASSERT(maximum.x-minimum.x >= GU_MIN_AABB_EXTENT*0.5f); + PX_ASSERT(maximum.y-minimum.y >= GU_MIN_AABB_EXTENT*0.5f); + PX_ASSERT(maximum.z-minimum.z >= GU_MIN_AABB_EXTENT*0.5f); + // not using vector math due to vector to integer pipeline penalties. TODO: verify that it's indeed faster + namespace i = physx::intrinsics; + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + const PxF32 dEpsilon = 1e-9f; + // using recipFast fails height field unit tests case where a ray cast from y=10000 to 0 gets clipped to 0.27 in y + PxF32 invDx = i::recip(i::selectMax(i::abs(rd.x), dEpsilon) * i::sign(rd.x)); +#ifdef RAYAABB_EPSILON + PxF32 tx0 = (minimum.x - RAYAABB_EPSILON - ro.x) * invDx; + PxF32 tx1 = (maximum.x + RAYAABB_EPSILON - ro.x) * invDx; +#else + PxF32 tx0 = (minimum.x - ro.x) * invDx; + PxF32 tx1 = (maximum.x - ro.x) * invDx; +#endif + PxF32 txMin = i::selectMin(tx0, tx1); + PxF32 txMax = i::selectMax(tx0, tx1); + + PxF32 invDy = i::recip(i::selectMax(i::abs(rd.y), dEpsilon) * i::sign(rd.y)); +#ifdef RAYAABB_EPSILON + PxF32 ty0 = (minimum.y - RAYAABB_EPSILON - ro.y) * invDy; + PxF32 ty1 = (maximum.y + RAYAABB_EPSILON - ro.y) * invDy; +#else + PxF32 ty0 = (minimum.y - ro.y) * invDy; + PxF32 ty1 = (maximum.y - ro.y) * invDy; +#endif + PxF32 tyMin = i::selectMin(ty0, ty1); + PxF32 tyMax = i::selectMax(ty0, ty1); + + PxF32 invDz = i::recip(i::selectMax(i::abs(rd.z), dEpsilon) * i::sign(rd.z)); +#ifdef RAYAABB_EPSILON + PxF32 tz0 = (minimum.z - RAYAABB_EPSILON - ro.z) * invDz; + PxF32 tz1 = (maximum.z + RAYAABB_EPSILON - ro.z) * invDz; +#else + PxF32 tz0 = (minimum.z - ro.z) * invDz; + PxF32 tz1 = (maximum.z - ro.z) * invDz; +#endif + PxF32 tzMin = i::selectMin(tz0, tz1); + PxF32 tzMax = i::selectMax(tz0, tz1); + + PxF32 maxOfNears = i::selectMax(i::selectMax(txMin, tyMin), tzMin); + PxF32 minOfFars = i::selectMin(i::selectMin(txMax, tyMax), tzMax); + + tnear = i::selectMax(maxOfNears, 0.0f); + tfar = i::selectMin(minOfFars, maxDist); + + return (tnear<tfar); +} + +bool Gu::intersectRayAABB2(const Ps::aos::Vec3VArg minimum, const Ps::aos::Vec3VArg maximum, + const Ps::aos::Vec3VArg ro, const Ps::aos::Vec3VArg rd, const Ps::aos::FloatVArg maxDist, + Ps::aos::FloatV& tnear, Ps::aos::FloatV& tfar) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V eps = V3Load(1e-9f); + const Vec3V absRD = V3Max(V3Abs(rd), eps); + const Vec3V signRD = V3Sign(rd); + const Vec3V rdV = V3Mul(absRD, signRD); + const Vec3V rdVRecip = V3Recip(rdV); + const Vec3V _min = V3Mul(V3Sub(minimum, ro), rdVRecip); + const Vec3V _max = V3Mul(V3Sub(maximum, ro), rdVRecip); + const Vec3V min = V3Min(_max, _min); + const Vec3V max = V3Max(_max, _min); + const FloatV maxOfNears = FMax(V3GetX(min), FMax(V3GetY(min), V3GetZ(min))); + const FloatV minOfFars = FMin(V3GetX(max), FMin(V3GetY(max), V3GetZ(max))); + + tnear = FMax(maxOfNears, zero); + tfar = FMin(minOfFars, maxDist); + //tfar = FAdd(FMin(minOfFars, maxDist), V3GetX(eps)); // AP: + epsilon because a test vs empty box should return true + + return FAllGrtr(tfar, tnear) != 0; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBox.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBox.h new file mode 100644 index 00000000..edfece5c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBox.h @@ -0,0 +1,91 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_BOX_H +#define GU_INTERSECTION_RAY_BOX_H + +#include "foundation/PxIntrinsics.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + bool rayAABBIntersect(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord); + PxU32 rayAABBIntersect2(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord, PxReal & t); + + // Collide ray defined by ray origin (rayOrigin) and ray direction (rayDirection) + // with the bounding box. Returns -1 on no collision and the face index + // for first intersection if a collision is found together with + // the distance to the collision points (tnear and tfar) + // + // ptchernev: + // Even though the above is the original comment by Pierre I am quite confident + // that the tnear and tfar parameters are parameters along rayDirection of the + // intersection points: + // + // ip0 = rayOrigin + (rayDirection * tnear) + // ip1 = rayOrigin + (rayDirection * tfar) + // + // The return code is: + // -1 no intersection + // 0 the ray first hits the plane at aabbMin.x + // 1 the ray first hits the plane at aabbMin.y + // 2 the ray first hits the plane at aabbMin.z + // 3 the ray first hits the plane at aabbMax.x + // 4 the ray first hits the plane at aabbMax.y + // 5 the ray first hits the plane at aabbMax.z + // + // The return code will be -1 if the RAY does not intersect the AABB. + // The tnear and tfar values will give the parameters of the intersection + // points between the INFINITE LINE and the AABB. + int PX_PHYSX_COMMON_API intersectRayAABB( const PxVec3& minimum, const PxVec3& maximum, + const PxVec3& rayOrigin, const PxVec3& rayDirection, + float& tnear, float& tfar); + + // Faster version when one-over-dir is available + int intersectRayAABB( const PxVec3& minimum, const PxVec3& maximum, + const PxVec3& rayOrigin, const PxVec3& rayDirection, const PxVec3& invDirection, + float& tnear, float& tfar); + + // minimum extent length required for intersectRayAABB2 to return true for a zero-extent box + // this can happen when inflating the raycast by a 2-d square + #define GU_MIN_AABB_EXTENT 1e-3f + + // a much faster version that doesn't return face codes + bool PX_PHYSX_COMMON_API intersectRayAABB2( + const PxVec3& minimum, const PxVec3& maximum, const PxVec3& ro, const PxVec3& rd, float maxDist, float& tnear, float& tfar); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBoxSIMD.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBoxSIMD.h new file mode 100644 index 00000000..51f01dd7 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayBoxSIMD.h @@ -0,0 +1,50 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_BOX_SIMD_H +#define GU_INTERSECTION_RAY_BOX_SIMD_H + +#include "foundation/PxIntrinsics.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + bool PX_PHYSX_COMMON_API intersectRayAABB2( const Ps::aos::Vec3VArg minimum, const Ps::aos::Vec3VArg maximum, + const Ps::aos::Vec3VArg ro, const Ps::aos::Vec3VArg rd, const Ps::aos::FloatVArg maxDist, + Ps::aos::FloatV& tnear, Ps::aos::FloatV& tfar); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayCapsule.cpp new file mode 100644 index 00000000..bde848bd --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayCapsule.cpp @@ -0,0 +1,245 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionRayCapsule.h" +#include "GuIntersectionRaySphere.h" + +using namespace physx; + +// PT: ray-capsule intersection code, originally from the old Magic Software library. +PxU32 Gu::intersectRayCapsuleInternal(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal s[2]) +{ + // set up quadratic Q(t) = a*t^2 + 2*b*t + c + PxVec3 kW = p1 - p0; + const float fWLength = kW.magnitude(); + if(fWLength!=0.0f) + kW /= fWLength; + + // PT: if the capsule is in fact a sphere, switch back to dedicated sphere code. + // This is not just an optimization, the rest of the code fails otherwise. + if(fWLength<=1e-6f) + { + const float d0 = (origin - p0).magnitudeSquared(); + const float d1 = (origin - p1).magnitudeSquared(); + const float approxLength = (PxMax(d0, d1) + radius)*2.0f; + return PxU32(Gu::intersectRaySphere(origin, dir, approxLength, p0, radius, s[0])); + } + + // generate orthonormal basis + PxVec3 kU(0.0f); + + if (fWLength > 0.0f) + { + PxReal fInvLength; + if ( PxAbs(kW.x) >= PxAbs(kW.y) ) + { + // W.x or W.z is the largest magnitude component, swap them + fInvLength = PxRecipSqrt(kW.x*kW.x + kW.z*kW.z); + kU.x = -kW.z*fInvLength; + kU.y = 0.0f; + kU.z = kW.x*fInvLength; + } + else + { + // W.y or W.z is the largest magnitude component, swap them + fInvLength = PxRecipSqrt(kW.y*kW.y + kW.z*kW.z); + kU.x = 0.0f; + kU.y = kW.z*fInvLength; + kU.z = -kW.y*fInvLength; + } + } + + PxVec3 kV = kW.cross(kU); + kV.normalize(); // PT: fixed november, 24, 2004. This is a bug in Magic. + + // compute intersection + + PxVec3 kD(kU.dot(dir), kV.dot(dir), kW.dot(dir)); + const float fDLength = kD.magnitude(); + const float fInvDLength = fDLength!=0.0f ? 1.0f/fDLength : 0.0f; + kD *= fInvDLength; + + const PxVec3 kDiff = origin - p0; + const PxVec3 kP(kU.dot(kDiff), kV.dot(kDiff), kW.dot(kDiff)); + const PxReal fRadiusSqr = radius*radius; + + // Is the velocity parallel to the capsule direction? (or zero) + if ( PxAbs(kD.z) >= 1.0f - PX_EPS_REAL || fDLength < PX_EPS_REAL ) + { + const float fAxisDir = dir.dot(kW); + + const PxReal fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y; + if ( fAxisDir < 0 && fDiscr >= 0.0f ) + { + // Velocity anti-parallel to the capsule direction + const PxReal fRoot = PxSqrt(fDiscr); + s[0] = (kP.z + fRoot)*fInvDLength; + s[1] = -(fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } + else if ( fAxisDir > 0 && fDiscr >= 0.0f ) + { + // Velocity parallel to the capsule direction + const PxReal fRoot = PxSqrt(fDiscr); + s[0] = -(kP.z + fRoot)*fInvDLength; + s[1] = (fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } + else + { + // sphere heading wrong direction, or no velocity at all + return 0; + } + } + + // test intersection with infinite cylinder + PxReal fA = kD.x*kD.x + kD.y*kD.y; + PxReal fB = kP.x*kD.x + kP.y*kD.y; + PxReal fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr; + PxReal fDiscr = fB*fB - fA*fC; + if ( fDiscr < 0.0f ) + { + // line does not intersect infinite cylinder + return 0; + } + + PxU32 iQuantity = 0; + + if ( fDiscr > 0.0f ) + { + // line intersects infinite cylinder in two places + const PxReal fRoot = PxSqrt(fDiscr); + const PxReal fInv = 1.0f/fA; + PxReal fT = (-fB - fRoot)*fInv; + PxReal fTmp = kP.z + fT*kD.z; + const float epsilon = 1e-3f; // PT: see TA35174 + if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) + s[iQuantity++] = fT*fInvDLength; + + fT = (-fB + fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) + s[iQuantity++] = fT*fInvDLength; + + if ( iQuantity == 2 ) + { + // line intersects capsule wall in two places + return 2; + } + } + else + { + // line is tangent to infinite cylinder + const PxReal fT = -fB/fA; + const PxReal fTmp = kP.z + fT*kD.z; + if ( 0.0f <= fTmp && fTmp <= fWLength ) + { + s[0] = fT*fInvDLength; + return 1; + } + } + + // test intersection with bottom hemisphere + // fA = 1 + fB += kP.z*kD.z; + fC += kP.z*kP.z; + fDiscr = fB*fB - fC; + if ( fDiscr > 0.0f ) + { + const PxReal fRoot = PxSqrt(fDiscr); + PxReal fT = -fB - fRoot; + PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + else if ( fDiscr == 0.0f ) + { + const PxReal fT = -fB; + const PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + + // test intersection with top hemisphere + // fA = 1 + fB -= kD.z*fWLength; + fC += fWLength*(fWLength - 2.0f*kP.z); + + fDiscr = fB*fB - fC; + if ( fDiscr > 0.0f ) + { + const PxReal fRoot = PxSqrt(fDiscr); + PxReal fT = -fB - fRoot; + PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + else if ( fDiscr == 0.0f ) + { + const PxReal fT = -fB; + const PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + return iQuantity; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayCapsule.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayCapsule.h new file mode 100644 index 00000000..93df2702 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayCapsule.h @@ -0,0 +1,93 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_CAPSULE_H +#define GU_INTERSECTION_RAY_CAPSULE_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionRay.h" + +namespace physx +{ +namespace Gu +{ + PxU32 intersectRayCapsuleInternal(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal s[2]); + + PX_FORCE_INLINE bool intersectRayCapsule(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal& t) + { + // PT: move ray origin close to capsule, to solve accuracy issues. + // We compute the distance D between the ray origin and the capsule's segment. + // Then E = D - radius = distance between the ray origin and the capsule. + // We can move the origin freely along 'dir' up to E units before touching the capsule. + PxReal l = distancePointSegmentSquaredInternal(p0, p1 - p0, origin); + l = PxSqrt(l) - radius; + + // PT: if this becomes negative or null, the ray starts inside the capsule and we can early exit + if(l<=0.0f) + { + t = 0.0f; + return true; + } + + // PT: we remove an arbitrary GU_RAY_SURFACE_OFFSET units to E, to make sure we don't go close to the surface. + // If we're moving in the direction of the capsule, the origin is now about GU_RAY_SURFACE_OFFSET units from it. + // If we're moving away from the capsule, the ray won't hit the capsule anyway. + // If l is smaller than GU_RAY_SURFACE_OFFSET we're close enough, accuracy is good, there is nothing to do. + if(l>GU_RAY_SURFACE_OFFSET) + l -= GU_RAY_SURFACE_OFFSET; + else + l = 0.0f; + + // PT: move origin closer to capsule and do the raycast + PxReal s[2]; + const PxU32 nbHits = Gu::intersectRayCapsuleInternal(origin + l*dir, dir, p0, p1, radius, s); + if(!nbHits) + return false; + + // PT: keep closest hit only + if(nbHits == 1) + t = s[0]; + else + t = (s[0] < s[1]) ? s[0] : s[1]; + + // PT: fix distance (smaller than expected after moving ray close to capsule) + t += l; + return true; + } + + PX_FORCE_INLINE bool intersectRayCapsule(const PxVec3& origin, const PxVec3& dir, const Gu::Capsule& capsule, PxReal& t) + { + return Gu::intersectRayCapsule(origin, dir, capsule.p0, capsule.p1, capsule.radius, t); + } +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayPlane.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayPlane.h new file mode 100644 index 00000000..b28c0658 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayPlane.h @@ -0,0 +1,58 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_PLANE_H +#define GU_INTERSECTION_RAY_PLANE_H + +#include "foundation/PxPlane.h" + +namespace physx +{ +namespace Gu +{ + // Returns true if line and plane are not parallel + PX_INLINE bool intersectRayPlane(const PxVec3& orig, const PxVec3& dir, const PxPlane& plane, float& distanceAlongLine, PxVec3* pointOnPlane = NULL) + { + const float dn = dir.dot(plane.n); + if(-1E-7f < dn && dn < 1E-7f) + return false; // parallel + + distanceAlongLine = -plane.distance(orig)/dn; + + if(pointOnPlane) + *pointOnPlane = orig + distanceAlongLine * dir; + + return true; + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRaySphere.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRaySphere.cpp new file mode 100644 index 00000000..66b1f08f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRaySphere.cpp @@ -0,0 +1,105 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxVec3.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRay.h" + +using namespace physx; + +// Based on GD Mag code, but now works correctly when origin is inside the sphere. +// This version has limited accuracy. +bool Gu::intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos) +{ + // get the offset vector + const PxVec3 offset = center - origin; + + // get the distance along the ray to the center point of the sphere + const PxReal ray_dist = dir.dot(offset); + + // get the squared distances + const PxReal off2 = offset.dot(offset); + const PxReal rad_2 = radius * radius; + if(off2 <= rad_2) + { + // we're in the sphere + if(hit_pos) + *hit_pos = origin; + dist = 0.0f; + return true; + } + + if(ray_dist <= 0 || (ray_dist - length) > radius) + { + // moving away from object or too far away + return false; + } + + // find hit distance squared + const PxReal d = rad_2 - (off2 - ray_dist * ray_dist); + if(d<0.0f) + { + // ray passes by sphere without hitting + return false; + } + + // get the distance along the ray + dist = ray_dist - PxSqrt(d); + if(dist > length) + { + // hit point beyond length + return false; + } + + // sort out the details + if(hit_pos) + *hit_pos = origin + dir * dist; + return true; +} + +// PT: modified version calls the previous function, but moves the ray origin closer to the sphere. The test accuracy is +// greatly improved as a result. This is an idea proposed on the GD-Algorithms list by Eddie Edwards. +// See: http://www.codercorner.com/blog/?p=321 +bool Gu::intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos) +{ + const PxVec3 x = origin - center; + PxReal l = PxSqrt(x.dot(x)) - radius - GU_RAY_SURFACE_OFFSET; + +// if(l<0.0f) +// l=0.0f; + l = physx::intrinsics::selectMax(l, 0.0f); + + bool status = intersectRaySphereBasic(origin + l*dir, dir, length - l, center, radius, dist, hit_pos); + if(status) + { +// dist += l/length; + dist += l; + } + return status; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRaySphere.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRaySphere.h new file mode 100644 index 00000000..6c4244f3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRaySphere.h @@ -0,0 +1,49 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_SPHERE_H +#define GU_INTERSECTION_RAY_SPHERE_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + // PT: basic version, limited accuracy, might fail for long rays vs small spheres + bool intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos = NULL); + + // PT: version with improved accuracy + bool intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos = NULL); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayTriangle.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayTriangle.h new file mode 100644 index 00000000..3a8d22b6 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionRayTriangle.h @@ -0,0 +1,179 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_TRIANGLE_H +#define GU_INTERSECTION_RAY_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +namespace Gu +{ + // PT: this is used for backface culling. It existed in Moller's original code already. Basically this is only to avoid dividing by zero. + // This should not depend on what units are used, and neither should it depend on the size of triangles. A large triangle with the same + // orientation as a small triangle should be backface culled the same way. A triangle whose orientation does not change should not suddenly + // become culled or visible when we scale it. + // + // An absolute epsilon is fine here. The computation will work fine for small triangles, and large triangles will simply make 'det' larger, + // more and more inaccurate, but it won't suddenly make it negative. + // + // Using FLT_EPSILON^2 ensures that triangles whose edges are smaller than FLT_EPSILON long are rejected. This epsilon makes the code work + // for very small triangles, while still preventing divisions by too small values. + #define GU_CULLING_EPSILON_RAY_TRIANGLE FLT_EPSILON*FLT_EPSILON + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes a ray-triangle intersection test. + * From Tomas Moeller's "Fast Minimum Storage Ray-Triangle Intersection" + * Could be optimized and cut into 2 methods (culled or not). Should make a batch one too to avoid the call overhead, or make it inline. + * + * \param orig [in] ray origin + * \param dir [in] ray direction + * \param vert0 [in] triangle vertex + * \param vert1 [in] triangle vertex + * \param vert2 [in] triangle vertex + * \param at [out] distance + * \param au [out] impact barycentric coordinate + * \param av [out] impact barycentric coordinate + * \param cull [in] true to use backface culling + * \param enlarge [in] enlarge triangle by specified epsilon in UV space to avoid false near-edge rejections + * \return true on overlap + * \note u, v and t will remain unchanged if false is returned. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE bool intersectRayTriangle( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& at, PxReal& au, PxReal& av, + bool cull, float enlarge=0.0f) + { + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); // error ~ |v2-v0| + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); // error ~ |v2-v0|*|v1-v0| + + if(cull) + { + if(det<GU_CULLING_EPSILON_RAY_TRIANGLE) + return false; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + const PxReal u = tvec.dot(pvec); + + const PxReal enlargeCoeff = enlarge*det; + const PxReal uvlimit = -enlargeCoeff; + const PxReal uvlimit2 = det + enlargeCoeff; + + if(u<uvlimit || u>uvlimit2) + return false; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const PxReal v = dir.dot(qvec); + if(v<uvlimit || (u+v)>uvlimit2) + return false; + + // Calculate t, scale parameters, ray intersects triangle + const PxReal t = edge2.dot(qvec); + + const PxReal inv_det = 1.0f / det; + at = t*inv_det; + au = u*inv_det; + av = v*inv_det; + } + else + { + // the non-culling branch + if(PxAbs(det)<GU_CULLING_EPSILON_RAY_TRIANGLE) + return false; + + const PxReal inv_det = 1.0f / det; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; // error ~ |orig-v0| + + // Calculate U parameter and test bounds + const PxReal u = tvec.dot(pvec) * inv_det; + if(u<-enlarge || u>1.0f+enlarge) + return false; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const PxReal v = dir.dot(qvec) * inv_det; + if(v<-enlarge || (u+v)>1.0f+enlarge) + return false; + + // Calculate t, ray intersects triangle + const PxReal t = edge2.dot(qvec) * inv_det; + + at = t; + au = u; + av = v; + } + return true; + } + + /* \note u, v and t will remain unchanged if false is returned. */ + PX_FORCE_INLINE bool intersectRayTriangleCulling( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& t, PxReal& u, PxReal& v, + float enlarge=0.0f) + { + return intersectRayTriangle(orig, dir, vert0, vert1, vert2, t, u, v, true, enlarge); + } + + /* \note u, v and t will remain unchanged if false is returned. */ + PX_FORCE_INLINE bool intersectRayTriangleNoCulling( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& t, PxReal& u, PxReal& v, + float enlarge=0.0f) + { + return intersectRayTriangle(orig, dir, vert0, vert1, vert2, t, u, v, false, enlarge); + } + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionSphereBox.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionSphereBox.cpp new file mode 100644 index 00000000..bb5e5769 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionSphereBox.cpp @@ -0,0 +1,88 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionSphereBox.h" +#include "GuSphere.h" +#include "GuBox.h" + +using namespace physx; + +bool Gu::intersectSphereBox(const Sphere& sphere, const Box& box) +{ + const PxVec3 delta = sphere.center - box.center; + PxVec3 dRot = box.rot.transformTranspose(delta); //transform delta into OBB body coords. (use method call!) + + //check if delta is outside AABB - and clip the vector to the AABB. + bool outside = false; + + if(dRot.x < -box.extents.x) + { + outside = true; + dRot.x = -box.extents.x; + } + else if(dRot.x > box.extents.x) + { + outside = true; + dRot.x = box.extents.x; + } + + if(dRot.y < -box.extents.y) + { + outside = true; + dRot.y = -box.extents.y; + } + else if(dRot.y > box.extents.y) + { + outside = true; + dRot.y = box.extents.y; + } + + if(dRot.z < -box.extents.z) + { + outside = true; + dRot.z = -box.extents.z; + } + else if(dRot.z > box.extents.z) + { + outside = true; + dRot.z = box.extents.z; + } + + if(outside) //if clipping was done, sphere center is outside of box. + { + const PxVec3 clippedDelta = box.rot.transform(dRot); //get clipped delta back in world coords. + + const PxVec3 clippedVec = delta - clippedDelta; //what we clipped away. + const PxReal lenSquared = clippedVec.magnitudeSquared(); + const PxReal radius = sphere.radius; + if(lenSquared > radius * radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return false; //disjoint + } + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionSphereBox.h b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionSphereBox.h new file mode 100644 index 00000000..b8864473 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionSphereBox.h @@ -0,0 +1,54 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_SPHERE_BOX_H +#define GU_INTERSECTION_SPHERE_BOX_H + +namespace physx +{ +namespace Gu +{ + class Sphere; + class Box; + + /** + Checks if a sphere intersects a box. Based on: Jim Arvo, A Simple Method for Box-Sphere Intersection Testing, Graphics Gems, pp. 247-250. + + \param sphere [in] sphere + \param box [in] box + + \return true if sphere overlaps box (or exactly touches it) + */ + bool intersectSphereBox(const Gu::Sphere& sphere, const Gu::Box& box); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionTriangleBox.cpp b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionTriangleBox.cpp new file mode 100644 index 00000000..9d3fbed3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/intersection/GuIntersectionTriangleBox.cpp @@ -0,0 +1,395 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuIntersectionTriangleBox.h" +#include "CmMatrix34.h" +#include "PsVecMath.h" +#include "GuBox.h" +#include "GuSIMDHelpers.h" + +using namespace physx; + +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-M?r */ +/* Function: int triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ + +#define CROSS(dest,v1,v2) \ + dest.x=v1.y*v2.z-v1.z*v2.y; \ + dest.y=v1.z*v2.x-v1.x*v2.z; \ + dest.z=v1.x*v2.y-v1.y*v2.x; + +#define DOT(v1,v2) (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z) + +#define FINDMINMAX(x0, x1, x2, minimum, maximum) \ + minimum = physx::intrinsics::selectMin(x0, x1); \ + maximum = physx::intrinsics::selectMax(x0, x1); \ + minimum = physx::intrinsics::selectMin(minimum, x2); \ + maximum = physx::intrinsics::selectMax(maximum, x2); + +static PX_FORCE_INLINE Ps::IntBool planeBoxOverlap(const PxVec3& normal, PxReal d, const PxVec3& maxbox) +{ + PxVec3 vmin,vmax; + + if (normal.x>0.0f) + { + vmin.x = -maxbox.x; + vmax.x = maxbox.x; + } + else + { + vmin.x = maxbox.x; + vmax.x = -maxbox.x; + } + + if (normal.y>0.0f) + { + vmin.y = -maxbox.y; + vmax.y = maxbox.y; + } + else + { + vmin.y = maxbox.y; + vmax.y = -maxbox.y; + } + + if (normal.z>0.0f) + { + vmin.z = -maxbox.z; + vmax.z = maxbox.z; + } + else + { + vmin.z = maxbox.z; + vmax.z = -maxbox.z; + } + + if( normal.dot(vmin) + d > 0.0f) return Ps::IntFalse; + if( normal.dot(vmax) + d >= 0.0f) return Ps::IntTrue; + return Ps::IntFalse; +} + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + minimum = physx::intrinsics::selectMin(p0, p2); \ + maximum = physx::intrinsics::selectMax(p0, p2); \ + rad = fa * extents.y + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.y + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + minimum = physx::intrinsics::selectMin(p0, p2); \ + maximum = physx::intrinsics::selectMax(p0, p2); \ + rad = fa * extents.x + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.x + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +/*======================== Z-tests ========================*/ +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + minimum = physx::intrinsics::selectMin(p1, p2); \ + maximum = physx::intrinsics::selectMax(p1, p2); \ + rad = fa * extents.x + fb * extents.y; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.x + fb * extents.y; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +Ps::IntBool Gu::intersectTriangleBox_ReferenceCode(const PxVec3& boxcenter, const PxVec3& extents, const PxVec3& tp0, const PxVec3& tp1, const PxVec3& tp2) +{ + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + + // This is the fastest branch on Sun - move everything so that the boxcenter is in (0,0,0) + const PxVec3 v0 = tp0 - boxcenter; + const PxVec3 v1 = tp1 - boxcenter; + const PxVec3 v2 = tp2 - boxcenter; + + // compute triangle edges + const PxVec3 e0 = v1 - v0; // tri edge 0 + const PxVec3 e1 = v2 - v1; // tri edge 1 + const PxVec3 e2 = v0 - v2; // tri edge 2 + + float minimum,maximum,rad,p0,p1,p2; + + // Bullet 3: test the 9 tests first (this was faster) + float fex = PxAbs(e0.x); + float fey = PxAbs(e0.y); + float fez = PxAbs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = PxAbs(e1.x); + fey = PxAbs(e1.y); + fez = PxAbs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = PxAbs(e2.x); + fey = PxAbs(e2.y); + fez = PxAbs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + // Bullet 1: + // first test overlap in the {x,y,z}-directions + // find minimum, maximum of the triangle each direction, and test for overlap in + // that direction -- this is equivalent to testing a minimal AABB around + // the triangle against the AABB + + // test in X-direction + FINDMINMAX(v0.x, v1.x, v2.x, minimum, maximum); + if(minimum>extents.x || maximum<-extents.x) return Ps::IntFalse; + + // test in Y-direction + FINDMINMAX(v0.y, v1.y, v2.y, minimum, maximum); + if(minimum>extents.y || maximum<-extents.y) return Ps::IntFalse; + + // test in Z-direction + FINDMINMAX(v0.z, v1.z, v2.z, minimum, maximum); + if(minimum>extents.z || maximum<-extents.z) return Ps::IntFalse; + + // Bullet 2: + // test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + PxVec3 normal; + CROSS(normal,e0,e1); + const float d=-DOT(normal,v0); // plane eq: normal.x+d=0 + if(!planeBoxOverlap(normal, d, extents)) return Ps::IntFalse; + + return Ps::IntTrue; // box and triangle overlaps +} + +#undef CROSS +#undef DOT +#undef FINDMINMAX +#undef AXISTEST_X01 +#undef AXISTEST_X2 +#undef AXISTEST_Y02 +#undef AXISTEST_Y1 +#undef AXISTEST_Z12 +#undef AXISTEST_Z0 + + + +using namespace Ps::aos; + +static PX_FORCE_INLINE int testClassIIIAxes(const Vec4V& e0V, const Vec4V v0V, const Vec4V v1V, const Vec4V v2V, const PxVec3& extents) +{ + const Vec4V e0XZY_V = V4PermYZXW(e0V); + + const Vec4V v0XZY_V = V4PermYZXW(v0V); + const Vec4V p0V = V4NegMulSub(v0XZY_V, e0V, V4Mul(v0V, e0XZY_V)); + + const Vec4V v1XZY_V = V4PermYZXW(v1V); + const Vec4V p1V = V4NegMulSub(v1XZY_V, e0V, V4Mul(v1V, e0XZY_V)); + + const Vec4V v2XZY_V = V4PermYZXW(v2V); + const Vec4V p2V = V4NegMulSub(v2XZY_V, e0V, V4Mul(v2V, e0XZY_V)); + + Vec4V minV = V4Min(p0V, p1V); + minV = V4Min(minV, p2V); + + const Vec4V extentsV = V4LoadU(&extents.x); + const Vec4V fe0ZYX_V = V4Abs(e0V); + + const Vec4V fe0XZY_V = V4PermYZXW(fe0ZYX_V); + const Vec4V extentsXZY_V = V4PermYZXW(extentsV); + Vec4V radV = V4MulAdd(extentsV, fe0XZY_V, V4Mul(extentsXZY_V, fe0ZYX_V)); + + if(V4AnyGrtr3(minV, radV)) + return 0; + + Vec4V maxV = V4Max(p0V, p1V); + maxV = V4Max(maxV, p2V); + + radV = V4Sub(V4Zero(), radV); + + if(V4AnyGrtr3(radV, maxV)) + return 0; + return 1; +} + +static const VecU32V signV = U4LoadXYZW(0x80000000, 0x80000000, 0x80000000, 0x80000000); + +static PX_FORCE_INLINE Ps::IntBool intersectTriangleBoxInternal(const Vec4V v0V, const Vec4V v1V, const Vec4V v2V, const PxVec3& extents) +{ + // Test box axes + { + Vec4V extentsV = V4LoadU(&extents.x); + + { + const Vec4V cV = V4Abs(v0V); + if(V4AllGrtrOrEq3(extentsV, cV)) + return 1; + } + + Vec4V minV = V4Min(v0V, v1V); + minV = V4Min(minV, v2V); + + if(V4AnyGrtr3(minV, extentsV)) + return 0; + + Vec4V maxV = V4Max(v0V, v1V); + maxV = V4Max(maxV, v2V); + extentsV = V4Sub(V4Zero(), extentsV); + + if(V4AnyGrtr3(extentsV, maxV)) + return 0; + } + + // Test if the box intersects the plane of the triangle + const Vec4V e0V = V4Sub(v1V, v0V); + const Vec4V e1V = V4Sub(v2V, v1V); + { + const Vec4V normalV = V4Cross(e0V, e1V); + const Vec4V dV = Vec4V_From_FloatV(V4Dot3(normalV, v0V)); + + const Vec4V extentsV = V4LoadU(&extents.x); + VecU32V normalSignsV = V4U32and(VecU32V_ReinterpretFrom_Vec4V(normalV), signV); + const Vec4V maxV = Vec4V_ReinterpretFrom_VecU32V(V4U32or(VecU32V_ReinterpretFrom_Vec4V(extentsV), normalSignsV)); + + Vec4V tmpV = Vec4V_From_FloatV(V4Dot3(normalV, maxV)); + if(V4AnyGrtr3(dV, tmpV)) + return 0; + + normalSignsV = V4U32xor(normalSignsV, signV); + const Vec4V minV = Vec4V_ReinterpretFrom_VecU32V(V4U32or(VecU32V_ReinterpretFrom_Vec4V(extentsV), normalSignsV)); + + tmpV = Vec4V_From_FloatV(V4Dot3(normalV, minV)); + if(V4AnyGrtr3(tmpV, dV)) + return 0; + } + + // Edge-edge tests + { + if(!testClassIIIAxes(e0V, v0V, v1V, v2V, extents)) + return 0; + if(!testClassIIIAxes(e1V, v0V, v1V, v2V, extents)) + return 0; + const Vec4V e2V = V4Sub(v0V, v2V); + if(!testClassIIIAxes(e2V, v0V, v1V, v2V, extents)) + return 0; + } + return 1; +} + +// PT: a SIMD version of Tomas Moller's triangle-box SAT code +Ps::IntBool Gu::intersectTriangleBox_Unsafe(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + // Move everything so that the boxcenter is in (0,0,0) + const Vec4V BoxCenterV = V4LoadU(¢er.x); + const Vec4V v0V = V4Sub(V4LoadU(&p0.x), BoxCenterV); + const Vec4V v1V = V4Sub(V4LoadU(&p1.x), BoxCenterV); + const Vec4V v2V = V4Sub(V4LoadU(&p2.x), BoxCenterV); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, extents); +} + +Ps::IntBool Gu::intersectTriangleBox(const BoxPadded& box, const PxVec3& p0_, const PxVec3& p1_, const PxVec3& p2_) +{ + // PT: TODO: SIMDify this part + + // Vec3p ensures we can safely V4LoadU the data + const Vec3p p0 = box.rotateInv(p0_ - box.center); + const Vec3p p1 = box.rotateInv(p1_ - box.center); + const Vec3p p2 = box.rotateInv(p2_ - box.center); + + const Vec4V v0V = V4LoadU(&p0.x); + const Vec4V v1V = V4LoadU(&p1.x); + const Vec4V v2V = V4LoadU(&p2.x); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, box.extents); +} + +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadU(&mat.column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat.column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat.column2.x), zzzV)); + return ResV; +} + +// PT: warning: all params must be safe to V4LoadU +Ps::IntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, + const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents) +{ + const Vec4V transModelToBoxV = V4LoadU(&transModelToBox.x); + const Vec4V v0V = V4Add(multiply3x3V(V4LoadU(&p0.x), rotModelToBox), transModelToBoxV); + const Vec4V v1V = V4Add(multiply3x3V(V4LoadU(&p1.x), rotModelToBox), transModelToBoxV); + const Vec4V v2V = V4Add(multiply3x3V(V4LoadU(&p2.x), rotModelToBox), transModelToBoxV); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, extents); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32.cpp new file mode 100644 index 00000000..1ee2a683 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32.cpp @@ -0,0 +1,277 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxMemory.h" +#include "GuBV32.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + + +BV32Tree::BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + reset(); + init(meshInterface, localBounds); +} + +BV32Tree::BV32Tree() +{ + reset(); +} + +void BV32Tree::release() +{ + if (!mUserAllocated) + { + DELETEARRAY(mNodes); + PX_FREE_AND_RESET(mPackedNodes); + } + mNodes = NULL; + mNbNodes = 0; +} + +BV32Tree::~BV32Tree() +{ + release(); +} + +void BV32Tree::reset() +{ + mMeshInterface = NULL; + mNbNodes = 0; + mNodes = NULL; + mNbPackedNodes = 0; + mPackedNodes = NULL; + mInitData = 0; + mUserAllocated = false; +} + +void BV32Tree::operator=(BV32Tree& v) +{ + mMeshInterface = v.mMeshInterface; + mLocalBounds = v.mLocalBounds; + mNbNodes = v.mNbNodes; + mNodes = v.mNodes; + mInitData = v.mInitData; + mUserAllocated = v.mUserAllocated; + v.reset(); +} + +bool BV32Tree::init(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + mMeshInterface = meshInterface; + mLocalBounds.init(localBounds); + return true; +} + +// PX_SERIALIZATION +BV32Tree::BV32Tree(const PxEMPTY) +{ + mUserAllocated = true; +} + +void BV32Tree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(16); + stream.writeData(mNodes, mNbNodes*sizeof(BVDataPacked)); +} + +void BV32Tree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(16); + mNodes = context.readExtraData<BV32Data>(mNbNodes); +} +//~PX_SERIALIZATION + +bool BV32Tree::load(PxInputStream& stream, PxU32 meshVersion) +{ + PX_ASSERT(!mUserAllocated); + PX_UNUSED(meshVersion); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if (a != 'B' || b != 'V' || c != '3' || d != '2') + return false; + + const PxU32 version = 1; + const bool mismatch = (shdfnd::littleEndian() == 1); + if (readDword(mismatch, stream) != version) + return false; + + mLocalBounds.mCenter.x = readFloat(mismatch, stream); + mLocalBounds.mCenter.y = readFloat(mismatch, stream); + mLocalBounds.mCenter.z = readFloat(mismatch, stream); + mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream); + + mInitData = readDword(mismatch, stream); + + /*const PxU32 nbNodes = readDword(mismatch, stream); + mNbNodes = nbNodes; + + if (nbNodes) + { + BV32Data* nodes = PX_NEW(BV32Data)[nbNodes]; + + mNodes = nodes; + Cm::markSerializedMem(nodes, sizeof(BV32Data)*nbNodes); + + for (PxU32 i = 0; i<nbNodes; i++) + { + BV32Data& node = nodes[i]; + + readFloatBuffer(&node.mCenter.x, 3, mismatch, stream); + node.mData = readDword(mismatch, stream); + readFloatBuffer(&node.mExtents.x, 3, mismatch, stream); + } + }*/ + + + //read SOA format node data + const PxU32 nbPackedNodes = readDword(mismatch, stream); + mNbPackedNodes = nbPackedNodes; + + if (nbPackedNodes) + { + mPackedNodes = reinterpret_cast<BV32DataPacked*>(PX_ALLOC(sizeof(BV32DataPacked)*nbPackedNodes, "BV32DataPacked")); + + Cm::markSerializedMem(mPackedNodes, sizeof(BV32DataPacked)*nbPackedNodes); + + for (PxU32 i = 0; i < nbPackedNodes; ++i) + { + BV32DataPacked& node = mPackedNodes[i]; + node.mNbNodes = readDword(mismatch, stream); + PX_ASSERT(node.mNbNodes > 0); + ReadDwordBuffer(node.mData, node.mNbNodes, mismatch, stream); + const PxU32 nbElements = 4 * node.mNbNodes; + readFloatBuffer(&node.mCenter[0].x, nbElements, mismatch, stream); + readFloatBuffer(&node.mExtents[0].x, nbElements, mismatch, stream); + + } + } + + return true; +} + + +void BV32Tree::calculateLeafNode(BV32Data& node) +{ + if (!node.isLeaf()) + { + const PxU32 nbChildren = node.getNbChildren(); + const PxU32 offset = node.getChildOffset(); + //calcualte how many children nodes are leaf nodes + PxU32 nbLeafNodes = 0; + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + + if (child.isLeaf()) + { + nbLeafNodes++; + } + } + + node.mNbLeafNodes = nbLeafNodes; + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + calculateLeafNode(child); + } + + } +} + + + +void BV32Tree::createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes) +{ + + //found the next 32 nodes and fill it in SOA format + + const PxU32 nbChildren = node.getNbChildren(); + const PxU32 offset = node.getChildOffset(); + + + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + + packedData.mCenter[i] = PxVec4(child.mCenter, 0.f); + packedData.mExtents[i] = PxVec4(child.mExtents, 0.f); + packedData.mData[i] = PxU32(child.mData); + } + + packedData.mNbNodes = nbChildren; + + PxU32 NbToGo = 0; + PxU32 NextIDs[32]; + memset(NextIDs, PX_INVALID_U32, sizeof(PxU32) * 32); + const BV32Data* ChildNodes[32]; + memset(ChildNodes, 0, sizeof(BV32Data*) * 32); + + + for (PxU32 i = 0; i< nbChildren; i++) + { + BV32Data& child = mNodes[offset + i]; + + if (!child.isLeaf()) + { + const PxU32 NextID = currentIndex; + + const PxU32 ChildSize = child.getNbChildren() - child.mNbLeafNodes; + currentIndex += ChildSize; + + //packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT); + packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | ((childOffset + NbToGo) << GU_BV4_CHILD_OFFSET_SHIFT_COUNT); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = &child; + NbToGo++; + } + } + + nbPackedNodes += NbToGo; + for (PxU32 i = 0; i < NbToGo; ++i) + { + const BV32Data& child = *ChildNodes[i]; + + BV32DataPacked& childData = mPackedNodes[childOffset+i]; + + createSOAformatNode(childData, child, NextIDs[i], currentIndex, nbPackedNodes); + + } + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32.h new file mode 100644 index 00000000..4caf67d5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32.h @@ -0,0 +1,146 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV32_H +#define GU_BV32_H + +#include "foundation/PxBounds3.h" +#include "PxSerialFramework.h" +#include "PsUserAllocated.h" +#include "GuBV4.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "foundation/PxVec4.h" + +namespace physx +{ + namespace Gu + { + struct BV32Data : public physx::shdfnd::UserAllocated + { + PxVec3 mCenter; + PxU32 mNbLeafNodes; + PxVec3 mExtents; + size_t mData; + + + PX_FORCE_INLINE BV32Data() : mNbLeafNodes(0), mData(PX_INVALID_U32) + { + setEmpty(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf() const { return mData & 1; } + + //if the node is leaf, + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedTriangles() const { PX_ASSERT(isLeaf()); return PxU32((mData >>1)&63); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTriangleStartIndex() const { PX_ASSERT(isLeaf()); return PxU32(mData >> 7); } + + //PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getPrimitive() const { return mData >> 1; } + //if the node isn't leaf, we will get the childOffset + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset() const { PX_ASSERT(!isLeaf()); return PxU32(mData >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren() const { PX_ASSERT(!isLeaf()); return ((mData) & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1))>>1; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void getMinMax(PxVec3& min, PxVec3& max) const + { + min = mCenter - mExtents; + max = mCenter + mExtents; + } + + PX_FORCE_INLINE void setEmpty() + { + mCenter = PxVec3(0.0f, 0.0f, 0.0f); + mExtents = PxVec3(-1.0f, -1.0f, -1.0f); + } + + }; + + PX_ALIGN_PREFIX(16) + struct BV32DataPacked + { + PxVec4 mCenter[32]; + PxVec4 mExtents[32]; + PxU32 mData[32]; + PxU32 mNbNodes; + PxU32 pad[3]; + + PX_CUDA_CALLABLE PX_FORCE_INLINE BV32DataPacked() : mNbNodes(0) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf(const PxU32 index) const { return mData[index] & 1; } + //if the node is leaf, + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedTriangles(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 1) & 63; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTriangleStartIndex(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 7); } + //if the node isn't leaf, we will get the childOffset + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return mData[index] >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return ((mData[index])& ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) >> 1; } + } + PX_ALIGN_SUFFIX(16); + + class BV32Tree : public physx::shdfnd::UserAllocated + { + public: + // PX_SERIALIZATION + BV32Tree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_PHYSX_COMMON_API BV32Tree(); + PX_PHYSX_COMMON_API BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds); + PX_PHYSX_COMMON_API ~BV32Tree(); + + bool load(PxInputStream& stream, PxU32 meshVersion); // converts to proper endian at load time + + void calculateLeafNode(BV32Data& node); + void createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes); + + void reset(); + void operator = (BV32Tree& v); + + bool init(SourceMesh* meshInterface, const PxBounds3& localBounds); + void release(); + + SourceMesh* mMeshInterface; + LocalBounds mLocalBounds; + + PxU32 mNbNodes; + BV32Data* mNodes; + BV32DataPacked* mPackedNodes; + PxU32 mNbPackedNodes; + PxU32 mInitData; + bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading + bool mPadding[3]; + }; + + } // namespace Gu +} + +#endif // GU_BV32_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32Build.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32Build.cpp new file mode 100644 index 00000000..da62280f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32Build.cpp @@ -0,0 +1,530 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxVec4.h" +#include "GuBV32Build.h" +#include "GuBV32.h" +#include "PxTriangle.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "GuCenterExtents.h" +#include "GuBV4Build.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +struct BV32Node : public physx::shdfnd::UserAllocated +{ + BV32Node() : mNbChildBVNodes(0) + {} + + BV32Data mBVData[32]; + PxU32 mNbChildBVNodes; + + PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData & 1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData >> 1); } + PX_FORCE_INLINE const BV32Node* getChild(PxU32 i) const { return reinterpret_cast<BV32Node*>(mBVData[i].mData); } + + + PxU32 getSize() const + { + return sizeof(BV32Data)*mNbChildBVNodes; + } +}; + + +static void fillInNodes(const AABBTreeNode* current_node, const PxU32 startIndex, const PxU32 endIndex, const AABBTreeNode** NODES, PxU32& stat) +{ + + if (startIndex + 1 == endIndex) + { + //fill in nodes + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + NODES[startIndex] = P; + NODES[endIndex] = N; + stat += 2; + } + else + { + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + const PxU32 midIndex = startIndex + ((endIndex - startIndex) / 2); + if (!P->isLeaf()) + fillInNodes(P, startIndex, midIndex, NODES, stat); + else + { + NODES[startIndex] = P; + stat++; + } + + if (!N->isLeaf()) + fillInNodes(N, midIndex + 1, endIndex, NODES, stat); + else + { + NODES[midIndex + 1] = N; + stat++; + } + } +} + + + +static void setPrimitive(const AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + const PxU32 nbPrims = node->getNbPrimitives(); + PX_ASSERT(nbPrims<=32); + const PxU32* indexBase = source.getIndices(); + const PxU32* prims = node->getPrimitives(); + const PxU32 offset = PxU32(prims - indexBase); + +#if BV32_VALIDATE + for (PxU32 j = 0; j<nbPrims; j++) + { + PX_ASSERT(prims[j] == offset + j); + } +#endif + const PxU32 primitiveIndex = (offset << 6) | (nbPrims & 63); + + node32->mBVData[i].mCenter = node->getAABB().getCenter(); + node32->mBVData[i].mExtents = node->getAABB().getExtents(); + if (epsilon != 0.0f) + node32->mBVData[i].mExtents += PxVec3(epsilon, epsilon, epsilon); + node32->mBVData[i].mData = (primitiveIndex << 1) | 1; +} + +static BV32Node* setNode(const AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + BV32Node* child = NULL; + + if (node) + { + if (node->isLeaf()) + { + setPrimitive(source, node32, i, node, epsilon); + } + else + { + node32->mBVData[i].mCenter = node->getAABB().getCenter(); + node32->mBVData[i].mExtents = node->getAABB().getExtents(); + if (epsilon != 0.0f) + node32->mBVData[i].mExtents += PxVec3(epsilon, epsilon, epsilon); + + child = PX_NEW(BV32Node); + node32->mBVData[i].mData = size_t(child); + } + } + + return child; +} + + +static void _BuildBV32(const AABBTree& source, BV32Node* tmp, const AABBTreeNode* current_node, float epsilon, PxU32& nbNodes) +{ + PX_ASSERT(!current_node->isLeaf()); + + const AABBTreeNode* NODES[32]; + memset(NODES, 0, sizeof(AABBTreeNode*) * 32); + + fillInNodes(current_node, 0, 31, NODES, tmp->mNbChildBVNodes); + + PxU32 left = 0; + PxU32 right = 31; + + while (left < right) + { + + //sweep from the front + while (left<right) + { + //found a hole + if (NODES[left] == NULL) + break; + left++; + } + + //sweep from the back + while (left < right) + { + //found a node + if (NODES[right]) + break; + right--; + } + + if (left != right) + { + //swap left and right + const AABBTreeNode* node = NODES[right]; + NODES[right] = NODES[left]; + NODES[left] = node; + } + + } + + nbNodes += tmp->mNbChildBVNodes; + + for (PxU32 i = 0; i < tmp->mNbChildBVNodes; ++i) + { + const AABBTreeNode* tempNode = NODES[i]; + BV32Node* Child = setNode(source, tmp, i, tempNode, epsilon); + if (Child) + { + _BuildBV32(source, Child, tempNode, epsilon, nbNodes); + } + } + +} + +// +//static void validateTree(const AABBTree& Source, const AABBTreeNode* currentNode) +//{ +// if (currentNode->isLeaf()) +// { +// const PxU32* indexBase = Source.getIndices(); +// const PxU32* prims = currentNode->getPrimitives(); +// const PxU32 offset = PxU32(prims - indexBase); +// const PxU32 nbPrims = currentNode->getNbPrimitives(); +// for (PxU32 j = 0; j<nbPrims; j++) +// { +// PX_ASSERT(prims[j] == offset + j); +// } +// } +// else +// { +// const AABBTreeNode* pos = currentNode->getPos(); +// validateTree(Source, pos); +// const AABBTreeNode* neg = currentNode->getNeg(); +// validateTree(Source, neg); +// } +//} + +#if BV32_VALIDATE +static void validateNodeBound(const BV32Node* currentNode, SourceMesh* mesh) +{ + const PxU32 nbNodes = currentNode->mNbChildBVNodes; + for (PxU32 i = 0; i < nbNodes; ++i) + { + const BV32Node* node = currentNode->getChild(i); + if (currentNode->isLeaf(i)) + { + BV32Data data = currentNode->mBVData[i]; + PxU32 nbTriangles = data.getNbReferencedTriangles(); + PxU32 startIndex = data.getTriangleStartIndex(); + const IndTri32* triIndices = mesh->getTris32(); + const PxVec3* verts = mesh->getVerts(); + PxVec3 min(PX_MAX_F32, PX_MAX_F32, PX_MAX_F32); + PxVec3 max(-PX_MAX_F32, -PX_MAX_F32, -PX_MAX_F32); + for (PxU32 j = 0; j < nbTriangles; ++j) + { + IndTri32 index = triIndices[startIndex + j]; + + for (PxU32 k = 0; k < 3; ++k) + { + const PxVec3& v = verts[index.mRef[k]]; + + min.x = (min.x > v.x) ? v.x : min.x; + min.y = (min.y > v.y) ? v.y : min.y; + min.z = (min.z > v.z) ? v.z : min.z; + + max.x = (max.x < v.x) ? v.x : max.x; + max.y = (max.y > v.y) ? v.y : max.y; + max.z = (max.z > v.z) ? v.z : max.z; + } + } + + PxVec3 dMin, dMax; + data.getMinMax(dMin, dMax); + PX_ASSERT(dMin.x <= min.x && dMin.y <= min.y && dMin.z <= min.z); + PX_ASSERT(dMax.x >= max.x && dMax.y >= max.y && dMax.z >= min.z); + + } + else + { + validateNodeBound(node, mesh); + } + } +} +#endif + +static bool BuildBV32Internal(BV32Tree& bv32Tree, const AABBTree& Source, SourceMesh* mesh, float epsilon) +{ + if (mesh->getNbTriangles() <= 32) + { + bv32Tree.mNbPackedNodes = 1; + bv32Tree.mPackedNodes = reinterpret_cast<BV32DataPacked*>(PX_ALLOC(sizeof(BV32DataPacked), "BV32DataPacked")); + BV32DataPacked& packedData = bv32Tree.mPackedNodes[0]; + packedData.mNbNodes = 1; + packedData.mCenter[0] = PxVec4(Source.getBV().getCenter(), 0.f); + packedData.mExtents[0] = PxVec4(Source.getBV().getExtents(), 0.f); + packedData.mData[0] = (mesh->getNbTriangles() << 1) | 1; + return bv32Tree.init(mesh, Source.getBV()); + } + + { + struct Local + { + static void _CheckMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd) + { + cd++; + md = PxMax(md, cd); + + if (current_node->getPos()) { _CheckMD(current_node->getPos(), md, cd); cd--; } + if (current_node->getNeg()) { _CheckMD(current_node->getNeg(), md, cd); cd--; } + } + + static void _Check(AABBTreeNode* current_node) + { + if (current_node->isLeaf()) + return; + + AABBTreeNode* P = const_cast<AABBTreeNode*>(current_node->getPos()); + AABBTreeNode* N = const_cast<AABBTreeNode*>(current_node->getNeg()); + { + PxU32 MDP = 0; PxU32 CDP = 0; _CheckMD(P, MDP, CDP); + PxU32 MDN = 0; PxU32 CDN = 0; _CheckMD(N, MDN, CDN); + + if (MDP>MDN) + // if(MDP<MDN) + { + Ps::swap(*P, *N); + Ps::swap(P, N); + } + } + _Check(P); + _Check(N); + } + }; + Local::_Check(const_cast<AABBTreeNode*>(Source.getNodes())); + } + + + PxU32 nbNodes = 1; + BV32Node* Root32 = PX_NEW(BV32Node); + + + _BuildBV32(Source, Root32, Source.getNodes(), epsilon, nbNodes); + +#if BV32_VALIDATE + validateNodeBound(Root32, mesh); +#endif + + if (!bv32Tree.init(mesh, Source.getBV())) + return false; + BV32Tree* T = &bv32Tree; + + // Version with variable-sized nodes in single stream + { + struct Local + { + static void _Flatten(BV32Data* const dest, const PxU32 box_id, PxU32& current_id, const BV32Node* current, PxU32& max_depth, PxU32& current_depth, const PxU32 nb_nodes) + { + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if (current_depth>max_depth) + max_depth = current_depth; + + for (PxU32 i = 0; i<current->mNbChildBVNodes; i++) + { + dest[box_id + i].mCenter = current->mBVData[i].mCenter; + dest[box_id + i].mExtents = current->mBVData[i].mExtents; + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); + + PX_ASSERT(box_id + i < nb_nodes); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[32]; + memset(NextIDs, PX_INVALID_U32, sizeof(PxU32)*32); + const BV32Node* ChildNodes[32]; + memset(ChildNodes, 0, sizeof(BV32Node*)*32); + + BV32Data* data = dest + box_id; + for (PxU32 i = 0; i<current->mNbChildBVNodes; i++) + { + PX_ASSERT(current->mBVData[i].mData != PX_INVALID_U32); + + if (!current->isLeaf(i)) + { + + const BV32Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; + + const PxU32 ChildSize = ChildNode->mNbChildBVNodes; + current_id += ChildSize; + + const PxU32 ChildType = ChildNode->mNbChildBVNodes << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + PX_ASSERT(box_id + i < nb_nodes); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + } + } + + + + for (PxU32 i = 0; i<NbToGo; i++) + { + _Flatten(dest, NextIDs[i], current_id, ChildNodes[i], max_depth, current_depth, nb_nodes); + current_depth--; + } + + DELETESINGLE(current); + } + }; + + + PxU32 CurID = Root32->mNbChildBVNodes+1; + + BV32Data* Nodes = PX_NEW(BV32Data)[nbNodes]; + Nodes[0].mCenter = Source.getBV().getCenter(); + Nodes[0].mExtents = Source.getBV().getExtents(); + + const PxU32 ChildType = Root32->mNbChildBVNodes << 1; + Nodes[0].mData = size_t(ChildType + (1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + + const PxU32 nbChilden = Nodes[0].getNbChildren(); + + PX_UNUSED(nbChilden); + + + T->mInitData = CurID; + PxU32 MaxDepth = 0; + PxU32 CurrentDepth = 0; + + Local::_Flatten(Nodes, 1, CurID, Root32, MaxDepth, CurrentDepth, nbNodes); + + PX_ASSERT(CurID == nbNodes); + + T->mNbNodes = nbNodes; + + T->mNodes = Nodes; + } + + + bv32Tree.calculateLeafNode(bv32Tree.mNodes[0]); + + bv32Tree.mPackedNodes = reinterpret_cast<BV32DataPacked*>(PX_ALLOC(sizeof(BV32DataPacked)*nbNodes, "BV32DataPacked")); + bv32Tree.mNbPackedNodes = nbNodes; + + PxU32 nbPackedNodes = 1; + PxU32 currentIndex = bv32Tree.mNodes[0].getNbChildren() - bv32Tree.mNodes[0].mNbLeafNodes + 1; + BV32DataPacked& packedData = bv32Tree.mPackedNodes[0]; + bv32Tree.createSOAformatNode(packedData, bv32Tree.mNodes[0], 1, currentIndex, nbPackedNodes); + + bv32Tree.mNbPackedNodes = nbPackedNodes; + + PX_ASSERT(nbPackedNodes == currentIndex); + PX_ASSERT(nbPackedNodes > 0); + + return true; +} + +///// + +struct ReorderData32 +{ + const SourceMesh* mMesh; + PxU32* mOrder; + PxU32 mNbTrisPerLeaf; + PxU32 mIndex; + PxU32 mNbTris; + PxU32 mStats[32]; +}; + +static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData) +{ + ReorderData32* Data = reinterpret_cast<ReorderData32*>(userData); + if (current->isLeaf()) + { + const PxU32 n = current->getNbPrimitives(); + PX_ASSERT(n > 0); + PX_ASSERT(n <= Data->mNbTrisPerLeaf); + Data->mStats[n-1]++; + PxU32* Prims = const_cast<PxU32*>(current->getPrimitives()); + + for (PxU32 i = 0; i<n; i++) + { + PX_ASSERT(Prims[i]<Data->mNbTris); + Data->mOrder[Data->mIndex] = Prims[i]; + PX_ASSERT(Data->mIndex<Data->mNbTris); + Prims[i] = Data->mIndex; + Data->mIndex++; + } + } + return true; +} + + +bool physx::Gu::BuildBV32Ex(BV32Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf) +{ + const PxU32 nbTris = mesh.mNbTris; + + AABBTree Source; + if (!Source.buildFromMesh(mesh, nbTrisPerLeaf)) + return false; + + + { + PxU32* order = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32)*nbTris, "BV32")); + ReorderData32 RD; + RD.mMesh = &mesh; + RD.mOrder = order; + RD.mNbTrisPerLeaf = nbTrisPerLeaf; + RD.mIndex = 0; + RD.mNbTris = nbTris; + for (PxU32 i = 0; i<32; i++) + RD.mStats[i] = 0; + Source.walk(gReorderCallback, &RD); + PX_ASSERT(RD.mIndex == nbTris); + mesh.remapTopology(order); + PX_FREE(order); + // for(PxU32 i=0;i<16;i++) + // printf("%d: %d\n", i, RD.mStats[i]); + } + + + //if (mesh.getNbTriangles() <= nbTrisPerLeaf) + // return tree.init(&mesh, Source.getBV()); + + return BuildBV32Internal(tree, Source, &mesh, epsilon); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32Build.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32Build.h new file mode 100644 index 00000000..68b8ebaf --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV32Build.h @@ -0,0 +1,50 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV32_BUILD_H +#define GU_BV32_BUILD_H + +#include "foundation/PxSimpleTypes.h" +#include "common/PxPhysXCommonConfig.h" + +#define BV32_VALIDATE 0 + +namespace physx +{ + namespace Gu + { + class BV32Tree; + class SourceMesh; + + PX_PHYSX_COMMON_API bool BuildBV32Ex(BV32Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf); + + } // namespace Gu +} + +#endif // GU_BV32_BUILD_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4.cpp new file mode 100644 index 00000000..b7e0f4d0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4.cpp @@ -0,0 +1,261 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxMemory.h" +#include "GuBV4.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +SourceMesh::SourceMesh() +{ + reset(); +} + +SourceMesh::~SourceMesh() +{ + PX_FREE_AND_RESET(mRemap); +} + +void SourceMesh::reset() +{ + mNbVerts = 0; + mVerts = NULL; + mNbTris = 0; + mTriangles32 = NULL; + mTriangles16 = NULL; + mRemap = NULL; +} + +void SourceMesh::operator=(SourceMesh& v) +{ + mNbVerts = v.mNbVerts; + mVerts = v.mVerts; + mNbTris = v.mNbTris; + mTriangles32 = v.mTriangles32; + mTriangles16 = v.mTriangles16; + mRemap = v.mRemap; + v.reset(); +} + +void SourceMesh::remapTopology(const PxU32* order) +{ + if(!mNbTris) + return; + + if(mTriangles32) + { + IndTri32* newTopo = PX_NEW(IndTri32)[mNbTris]; + for(PxU32 i=0;i<mNbTris;i++) + newTopo[i] = mTriangles32[order[i]]; + + PxMemCopy(mTriangles32, newTopo, sizeof(IndTri32)*mNbTris); + DELETEARRAY(newTopo); + } + else + { + PX_ASSERT(mTriangles16); + IndTri16* newTopo = PX_NEW(IndTri16)[mNbTris]; + for(PxU32 i=0;i<mNbTris;i++) + newTopo[i] = mTriangles16[order[i]]; + + PxMemCopy(mTriangles16, newTopo, sizeof(IndTri16)*mNbTris); + DELETEARRAY(newTopo); + } + + { + PxU32* newMap = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32)*mNbTris, "OPC2")); + for(PxU32 i=0;i<mNbTris;i++) + newMap[i] = mRemap ? mRemap[order[i]] : order[i]; + + PX_FREE_AND_RESET(mRemap); + mRemap = newMap; + } +} + +bool SourceMesh::isValid() const +{ + if(!mNbTris || !mNbVerts) return false; + if(!mVerts) return false; + if(!mTriangles32 && !mTriangles16) return false; + return true; +} + +///// + +BV4Tree::BV4Tree(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + reset(); + init(meshInterface, localBounds); +} + +BV4Tree::BV4Tree() +{ + reset(); +} + +void BV4Tree::release() +{ + if(!mUserAllocated) + { +#ifdef GU_BV4_USE_SLABS + PX_DELETE_AND_RESET(mNodes); +#else + DELETEARRAY(mNodes); +#endif + } + + mNodes = NULL; + mNbNodes = 0; +} + +BV4Tree::~BV4Tree() +{ + release(); +} + +void BV4Tree::reset() +{ + mMeshInterface = NULL; + mNbNodes = 0; + mNodes = NULL; + mInitData = 0; +#ifdef GU_BV4_QUANTIZED_TREE + mCenterOrMinCoeff = PxVec3(0.0f); + mExtentsOrMaxCoeff = PxVec3(0.0f); +#endif + mUserAllocated = false; +} + +void BV4Tree::operator=(BV4Tree& v) +{ + mMeshInterface = v.mMeshInterface; + mLocalBounds = v.mLocalBounds; + mNbNodes = v.mNbNodes; + mNodes = v.mNodes; + mInitData = v.mInitData; +#ifdef GU_BV4_QUANTIZED_TREE + mCenterOrMinCoeff = v.mCenterOrMinCoeff; + mExtentsOrMaxCoeff = v.mExtentsOrMaxCoeff; +#endif + mUserAllocated = v.mUserAllocated; + v.reset(); +} + +bool BV4Tree::init(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + mMeshInterface = meshInterface; + mLocalBounds.init(localBounds); + return true; +} + +// PX_SERIALIZATION +BV4Tree::BV4Tree(const PxEMPTY) +{ + mUserAllocated = true; +} + +void BV4Tree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(16); + stream.writeData(mNodes, mNbNodes*sizeof(BVDataPacked)); +} + +void BV4Tree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(16); + mNodes = context.readExtraData<BVDataPacked>(mNbNodes); +} +//~PX_SERIALIZATION + +bool BV4Tree::load(PxInputStream& stream, PxU32 meshVersion) +{ + PX_ASSERT(!mUserAllocated); + PX_UNUSED(meshVersion); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='B' || b!='V' || c!='4' || d!=' ') + return false; + + const PxU32 version = 1; + const bool mismatch = (shdfnd::littleEndian() == 1); + if(readDword(mismatch, stream) != version) + return false; + + mLocalBounds.mCenter.x = readFloat(mismatch, stream); + mLocalBounds.mCenter.y = readFloat(mismatch, stream); + mLocalBounds.mCenter.z = readFloat(mismatch, stream); + mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream); + + mInitData = readDword(mismatch, stream); + +#ifdef GU_BV4_QUANTIZED_TREE + mCenterOrMinCoeff.x = readFloat(mismatch, stream); + mCenterOrMinCoeff.y = readFloat(mismatch, stream); + mCenterOrMinCoeff.z = readFloat(mismatch, stream); + mExtentsOrMaxCoeff.x = readFloat(mismatch, stream); + mExtentsOrMaxCoeff.y = readFloat(mismatch, stream); + mExtentsOrMaxCoeff.z = readFloat(mismatch, stream); +#endif + const PxU32 nbNodes = readDword(mismatch, stream); + mNbNodes = nbNodes; + + if(nbNodes) + { +#ifdef GU_BV4_USE_SLABS + BVDataPacked* nodes = reinterpret_cast<BVDataPacked*>(PX_ALLOC(sizeof(BVDataPacked)*nbNodes, "BV4 nodes")); // PT: PX_NEW breaks alignment here +#else + BVDataPacked* nodes = PX_NEW(BVDataPacked)[nbNodes]; +#endif + mNodes = nodes; + Cm::markSerializedMem(nodes, sizeof(BVDataPacked)*nbNodes); + + for(PxU32 i=0;i<nbNodes;i++) + { + BVDataPacked& node = nodes[i]; +#ifdef GU_BV4_QUANTIZED_TREE + readWordBuffer(&node.mAABB.mData[0].mExtents, 6, mismatch, stream); +#else + readFloatBuffer(&node.mAABB.mCenter.x, 6, mismatch, stream); +#endif + node.mData = readDword(mismatch, stream); + } + } + else mNodes = NULL; + + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4.h new file mode 100644 index 00000000..8746ef08 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4.h @@ -0,0 +1,254 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_H +#define GU_BV4_H + +#include "foundation/PxBounds3.h" +#include "PxSerialFramework.h" +#include "PsUserAllocated.h" +#include "GuBV4Settings.h" + +#define V4LoadU_Safe V4LoadU +#define V4LoadA_Safe V4LoadA +#define V4StoreA_Safe V4StoreA +#define V4StoreU_Safe V4StoreU + +namespace physx +{ +namespace Gu +{ + + struct VertexPointers + { + const PxVec3* Vertex[3]; + }; + + class IndTri32 : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE IndTri32() {} + PX_FORCE_INLINE IndTri32(PxU32 r0, PxU32 r1, PxU32 r2) { mRef[0]=r0; mRef[1]=r1; mRef[2]=r2; } + PX_FORCE_INLINE IndTri32(const IndTri32& triangle) + { + mRef[0] = triangle.mRef[0]; + mRef[1] = triangle.mRef[1]; + mRef[2] = triangle.mRef[2]; + } + PX_FORCE_INLINE ~IndTri32() {} + PxU32 mRef[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(IndTri32)==12); + + class IndTri16 : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE IndTri16() {} + PX_FORCE_INLINE IndTri16(PxU16 r0, PxU16 r1, PxU16 r2) { mRef[0]=r0; mRef[1]=r1; mRef[2]=r2; } + PX_FORCE_INLINE IndTri16(const IndTri16& triangle) + { + mRef[0] = triangle.mRef[0]; + mRef[1] = triangle.mRef[1]; + mRef[2] = triangle.mRef[2]; + } + PX_FORCE_INLINE ~IndTri16() {} + PxU16 mRef[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(IndTri16)==6); + + PX_FORCE_INLINE void getVertexReferences(PxU32& vref0, PxU32& vref1, PxU32& vref2, PxU32 index, const IndTri32* T32, const IndTri16* T16) + { + if(T32) + { + const IndTri32* PX_RESTRICT tri = T32 + index; + vref0 = tri->mRef[0]; + vref1 = tri->mRef[1]; + vref2 = tri->mRef[2]; + } + else + { + const IndTri16* PX_RESTRICT tri = T16 + index; + vref0 = tri->mRef[0]; + vref1 = tri->mRef[1]; + vref2 = tri->mRef[2]; + } + } + + class SourceMesh : public physx::shdfnd::UserAllocated + { + public: + PX_PHYSX_COMMON_API SourceMesh(); + PX_PHYSX_COMMON_API ~SourceMesh(); + // PX_SERIALIZATION + SourceMesh(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + void reset(); + void operator = (SourceMesh& v); + + PxU32 mNbVerts; + const PxVec3* mVerts; + PxU32 mNbTris; + IndTri32* mTriangles32; + IndTri16* mTriangles16; + + PX_FORCE_INLINE PxU32 getNbTriangles() const { return mNbTris; } + PX_FORCE_INLINE PxU32 getNbVertices() const { return mNbVerts; } + PX_FORCE_INLINE const IndTri32* getTris32() const { return mTriangles32; } + PX_FORCE_INLINE const IndTri16* getTris16() const { return mTriangles16; } + PX_FORCE_INLINE const PxVec3* getVerts() const { return mVerts; } + + PX_FORCE_INLINE void setNbTriangles(PxU32 nb) { mNbTris = nb; } + PX_FORCE_INLINE void setNbVertices(PxU32 nb) { mNbVerts = nb; } + + PX_FORCE_INLINE void setPointers(IndTri32* tris32, IndTri16* tris16, const PxVec3* verts) + { + mTriangles32 = tris32; + mTriangles16 = tris16; + mVerts = verts; + } + + PX_FORCE_INLINE void initRemap() { mRemap = NULL; } + PX_FORCE_INLINE const PxU32* getRemap() const { return mRemap; } + PX_FORCE_INLINE void releaseRemap() { PX_FREE_AND_RESET(mRemap); } + void remapTopology(const PxU32* order); + + bool isValid() const; + + PX_FORCE_INLINE void getTriangle(VertexPointers& vp, PxU32 index) const + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, index, mTriangles32, mTriangles16); + vp.Vertex[0] = mVerts + VRef0; + vp.Vertex[1] = mVerts + VRef1; + vp.Vertex[2] = mVerts + VRef2; + } + private: + PxU32* mRemap; + }; + + struct LocalBounds + { + LocalBounds() : mCenter(PxVec3(0.0f)), mExtentsMagnitude(0.0f) {} + + PxVec3 mCenter; + float mExtentsMagnitude; + + PX_FORCE_INLINE void init(const PxBounds3& bounds) + { + mCenter = bounds.getCenter(); + // PT: TODO: compute mag first, then multiplies by 0.5f (TA34704) + mExtentsMagnitude = bounds.getExtents().magnitude(); + } + }; + +#ifdef GU_BV4_QUANTIZED_TREE + class QuantizedAABB + { + public: + + struct Data + { + PxU16 mExtents; //!< Quantized extents + PxI16 mCenter; //!< Quantized center + }; + Data mData[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(QuantizedAABB)==12); +#endif + + ///// + + #define GU_BV4_CHILD_OFFSET_SHIFT_COUNT 11 + + struct BVDataPacked : public physx::shdfnd::UserAllocated + { +#ifdef GU_BV4_QUANTIZED_TREE + QuantizedAABB mAABB; +#else + CenterExtents mAABB; +#endif + PxU32 mData; + + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + PX_FORCE_INLINE PxU32 getPrimitive() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getChildOffset() const { return mData>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT;} + PX_FORCE_INLINE PxU32 getChildType() const { return (mData>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData() const { return mData; } + + PX_FORCE_INLINE void encodePNS(PxU32 code) + { + PX_ASSERT(code<256); + mData |= code<<3; + } + PX_FORCE_INLINE PxU32 decodePNSNoShift() const { return mData; } + }; + + // PT: TODO: align class to 16? (TA34704) + class BV4Tree : public physx::shdfnd::UserAllocated + { + public: + // PX_SERIALIZATION + BV4Tree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_PHYSX_COMMON_API BV4Tree(); + PX_PHYSX_COMMON_API BV4Tree(SourceMesh* meshInterface, const PxBounds3& localBounds); + PX_PHYSX_COMMON_API ~BV4Tree(); + + bool load(PxInputStream& stream, PxU32 meshVersion); // converts to proper endian at load time + + void reset(); + void operator = (BV4Tree& v); + + bool init(SourceMesh* meshInterface, const PxBounds3& localBounds); + void release(); + + SourceMesh* mMeshInterface; + LocalBounds mLocalBounds; + + PxU32 mNbNodes; + BVDataPacked* mNodes; + PxU32 mInitData; +#ifdef GU_BV4_QUANTIZED_TREE + PxVec3 mCenterOrMinCoeff; // PT: dequantization coeff, either for Center or Min (depending on AABB format) + PxVec3 mExtentsOrMaxCoeff; // PT: dequantization coeff, either for Extents or Max (depending on AABB format) +#endif + bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading + bool mPadding[3]; + }; + +} // namespace Gu +} + +#endif // GU_BV4_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Build.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Build.cpp new file mode 100644 index 00000000..fbe97042 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Build.cpp @@ -0,0 +1,1294 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxVec4.h" +#include "GuBV4Build.h" +#include "GuBV4.h" +#include "PxTriangle.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "GuCenterExtents.h" + +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#define GU_BV4_USE_NODE_POOLS + +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +static PX_FORCE_INLINE PxU32 largestAxis(const PxVec4& v) +{ + const float* Vals = &v.x; + PxU32 m = 0; + if(Vals[1] > Vals[m]) m = 1; + if(Vals[2] > Vals[m]) m = 2; + return m; +} + +AABBTree::AABBTree() : mIndices(NULL), mPool(NULL), mTotalNbNodes(0) +{ +} + +AABBTree::~AABBTree() +{ + release(); +} + +void AABBTree::release() +{ + DELETEARRAY(mPool); + PX_FREE_AND_RESET(mIndices); +} + +static PxU32 local_Split(const AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT /*Boxes*/, const PxVec3* PX_RESTRICT centers, PxU32 axis) +{ + const PxU32 nb = node->mNbPrimitives; + PxU32* PX_RESTRICT prims = node->mNodePrimitives; + + // Get node split value + const float splitValue = node->mBV.getCenter(axis); + + PxU32 nbPos = 0; + // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1]. + // Those indices map the global list in the tree builder. + const size_t ptrValue = size_t(centers) + axis*sizeof(float); + const PxVec3* PX_RESTRICT centersX = reinterpret_cast<const PxVec3*>(ptrValue); + + for(PxU32 i=0;i<nb;i++) + { + // Get index in global list + const PxU32 index = prims[i]; + + // Test against the splitting value. The primitive value is tested against the enclosing-box center. + // [We only need an approximate partition of the enclosing box here.] + const float primitiveValue = centersX[index].x; + + // Reorganize the list of indices in this order: positive - negative. + if(primitiveValue > splitValue) + { + // Swap entries + prims[i] = prims[nbPos]; + prims[nbPos] = index; + // Count primitives assigned to positive space + nbPos++; + } + } + return nbPos; +} + +static bool local_Subdivide(AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT boxes, const PxVec3* PX_RESTRICT centers, BuildStats& stats, const AABBTreeNode* const PX_RESTRICT node_base, PxU32 limit) +{ + const PxU32* PX_RESTRICT prims = node->mNodePrimitives; + const PxU32 nb = node->mNbPrimitives; + + // Compute bv & means at the same time + Vec4V meansV; + { + Vec4V minV = V4LoadU(&boxes[prims[0]].minimum.x); + Vec4V maxV = V4LoadU(&boxes[prims[0]].maximum.x); + meansV = V4LoadU(¢ers[prims[0]].x); + + for(PxU32 i=1;i<nb;i++) + { + const PxU32 index = prims[i]; + minV = V4Min(minV, V4LoadU(&boxes[index].minimum.x)); + maxV = V4Max(maxV, V4LoadU(&boxes[index].maximum.x)); + meansV = V4Add(meansV, V4LoadU(¢ers[index].x)); + } + const float coeffNb = 1.0f/float(nb); + meansV = V4Scale(meansV, FLoad(coeffNb)); + +// BV4_ALIGN16(PxVec4 mergedMin); +// BV4_ALIGN16(PxVec4 mergedMax); + PX_ALIGN_PREFIX(16) PxVec4 mergedMin PX_ALIGN_SUFFIX(16); + PX_ALIGN_PREFIX(16) PxVec4 mergedMax PX_ALIGN_SUFFIX(16); + + V4StoreA_Safe(minV, &mergedMin.x); + V4StoreA_Safe(maxV, &mergedMax.x); + node->mBV.minimum = PxVec3(mergedMin.x, mergedMin.y, mergedMin.z); + node->mBV.maximum = PxVec3(mergedMax.x, mergedMax.y, mergedMax.z); + } + +// // Stop subdividing if we reach a leaf node. This is always performed here, +// // else we could end in trouble if user overrides this. +// if(nb==1) +// return false; + if(nb<=limit) + return false; + + bool validSplit = true; + PxU32 nbPos; + { + // Compute variances + Vec4V varsV = V4Zero(); + for(PxU32 i=0;i<nb;i++) + { + const PxU32 index = prims[i]; + Vec4V centerV = V4LoadU(¢ers[index].x); + centerV = V4Sub(centerV, meansV); + centerV = V4Mul(centerV, centerV); + varsV = V4Add(varsV, centerV); + } + const float coeffNb1 = 1.0f/float(nb-1); + varsV = V4Scale(varsV, FLoad(coeffNb1)); + +// BV4_ALIGN16(PxVec4 vars); + PX_ALIGN_PREFIX(16) PxVec4 vars PX_ALIGN_SUFFIX(16); + V4StoreA_Safe(varsV, &vars.x); + + // Choose axis with greatest variance + const PxU32 axis = largestAxis(vars); + + // Split along the axis + nbPos = local_Split(node, boxes, centers, axis); + + // Check split validity + if(!nbPos || nbPos==nb) + validSplit = false; + } + + // Check the subdivision has been successful + if(!validSplit) + { + // Here, all boxes lie in the same sub-space. Two strategies: + // - if the tree *must* be complete, make an arbitrary 50-50 split + // - else stop subdividing +// if(nb>limit) + { + nbPos = node->mNbPrimitives>>1; + + if(1) + { + // Test 3 axis, take the best + float results[3]; + nbPos = local_Split(node, boxes, centers, 0); results[0] = float(nbPos)/float(node->mNbPrimitives); + nbPos = local_Split(node, boxes, centers, 1); results[1] = float(nbPos)/float(node->mNbPrimitives); + nbPos = local_Split(node, boxes, centers, 2); results[2] = float(nbPos)/float(node->mNbPrimitives); + results[0]-=0.5f; results[0]*=results[0]; + results[1]-=0.5f; results[1]*=results[1]; + results[2]-=0.5f; results[2]*=results[2]; + PxU32 Min=0; + if(results[1]<results[Min]) Min = 1; + if(results[2]<results[Min]) Min = 2; + + // Split along the axis + nbPos = local_Split(node, boxes, centers, Min); + + // Check split validity + if(!nbPos || nbPos==node->mNbPrimitives) + nbPos = node->mNbPrimitives>>1; + } + } + //else return + } + + // Now create children and assign their pointers. + // We use a pre-allocated linear pool for complete trees [Opcode 1.3] + const PxU32 count = stats.getCount(); + node->mPos = size_t(node_base + count); + + // Update stats + stats.increaseCount(2); + + // Assign children + AABBTreeNode* pos = const_cast<AABBTreeNode*>(node->getPos()); + AABBTreeNode* neg = const_cast<AABBTreeNode*>(node->getNeg()); + pos->mNodePrimitives = node->mNodePrimitives; + pos->mNbPrimitives = nbPos; + neg->mNodePrimitives = node->mNodePrimitives + nbPos; + neg->mNbPrimitives = node->mNbPrimitives - nbPos; + return true; +} + +static void local_BuildHierarchy(AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT Boxes, const PxVec3* PX_RESTRICT centers, BuildStats& stats, const AABBTreeNode* const PX_RESTRICT node_base, PxU32 limit) +{ + if(local_Subdivide(node, Boxes, centers, stats, node_base, limit)) + { + AABBTreeNode* pos = const_cast<AABBTreeNode*>(node->getPos()); + AABBTreeNode* neg = const_cast<AABBTreeNode*>(node->getNeg()); + local_BuildHierarchy(pos, Boxes, centers, stats, node_base, limit); + local_BuildHierarchy(neg, Boxes, centers, stats, node_base, limit); + } +} + +bool AABBTree::buildFromMesh(SourceMesh& mesh, PxU32 limit) +{ + const PxU32 nbBoxes = mesh.getNbTriangles(); + if(!nbBoxes) + return false; + PxBounds3* boxes = reinterpret_cast<PxBounds3*>(PX_ALLOC(sizeof(PxBounds3)*(nbBoxes+1), "BV4")); // PT: +1 to safely V4Load/V4Store the last element + PxVec3* centers = reinterpret_cast<PxVec3*>(PX_ALLOC(sizeof(PxVec3)*(nbBoxes+1), "BV4")); // PT: +1 to safely V4Load/V4Store the last element + const FloatV halfV = FLoad(0.5f); + for(PxU32 i=0;i<nbBoxes;i++) + { + VertexPointers VP; + mesh.getTriangle(VP, i); + + const Vec4V v0V = V4LoadU(&VP.Vertex[0]->x); + const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x); + const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x); + Vec4V minV = V4Min(v0V, v1V); + minV = V4Min(minV, v2V); + Vec4V maxV = V4Max(v0V, v1V); + maxV = V4Max(maxV, v2V); + V4StoreU_Safe(minV, &boxes[i].minimum.x); // PT: safe because 'maximum' follows 'minimum' + V4StoreU_Safe(maxV, &boxes[i].maximum.x); // PT: safe because we allocated one more box + + const Vec4V centerV = V4Scale(V4Add(maxV, minV), halfV); + V4StoreU_Safe(centerV, ¢ers[i].x); // PT: safe because we allocated one more PxVec3 + } + + { + // Release previous tree + release(); + + // Init stats + BuildStats Stats; + Stats.setCount(1); + + // Initialize indices. This list will be modified during build. + mIndices = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32)*nbBoxes, "BV4 indices")); + // Identity permutation + for(PxU32 i=0;i<nbBoxes;i++) + mIndices[i] = i; + + // Use a linear array for complete trees (since we can predict the final number of nodes) [Opcode 1.3] + // Allocate a pool of nodes + // PT: TODO: optimize memory here (TA34704) + mPool = PX_NEW(AABBTreeNode)[nbBoxes*2 - 1]; + + // Setup initial node. Here we have a complete permutation of the app's primitives. + mPool->mNodePrimitives = mIndices; + mPool->mNbPrimitives = nbBoxes; + + // Build the hierarchy + local_BuildHierarchy(mPool, boxes, centers, Stats, mPool, limit); + + // Get back total number of nodes + mTotalNbNodes = Stats.getCount(); + } + + PX_FREE(centers); + PX_FREE(boxes); + return true; +} + +PxU32 AABBTree::walk(WalkingCallback cb, void* userData) const +{ + // Call it without callback to compute max depth + PxU32 maxDepth = 0; + PxU32 currentDepth = 0; + + struct Local + { + static void _Walk(const AABBTreeNode* current_node, PxU32& max_depth, PxU32& current_depth, WalkingCallback callback, void* userData_) + { + // Checkings + if(!current_node) + return; + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + + // Callback + if(callback && !(callback)(current_node, current_depth, userData_)) + return; + + // Recurse + if(current_node->getPos()) { _Walk(current_node->getPos(), max_depth, current_depth, callback, userData_); current_depth--; } + if(current_node->getNeg()) { _Walk(current_node->getNeg(), max_depth, current_depth, callback, userData_); current_depth--; } + } + }; + Local::_Walk(mPool, maxDepth, currentDepth, cb, userData); + return maxDepth; +} + + + +#include "GuBV4_Internal.h" + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT +// PT: see http://www.codercorner.com/blog/?p=734 +static PxU32 precomputeNodeSorting(const PxBounds3& box0, const PxBounds3& box1) +{ + const PxVec3 C0 = box0.getCenter(); + const PxVec3 C1 = box1.getCenter(); + + PxVec3 dirPPP(1.0f, 1.0f, 1.0f); dirPPP.normalize(); + PxVec3 dirPPN(1.0f, 1.0f, -1.0f); dirPPN.normalize(); + PxVec3 dirPNP(1.0f, -1.0f, 1.0f); dirPNP.normalize(); + PxVec3 dirPNN(1.0f, -1.0f, -1.0f); dirPNN.normalize(); + PxVec3 dirNPP(-1.0f, 1.0f, 1.0f); dirNPP.normalize(); + PxVec3 dirNPN(-1.0f, 1.0f, -1.0f); dirNPN.normalize(); + PxVec3 dirNNP(-1.0f, -1.0f, 1.0f); dirNNP.normalize(); + PxVec3 dirNNN(-1.0f, -1.0f, -1.0f); dirNNN.normalize(); + + const PxVec3 deltaC = C0 - C1; + const bool bPPP = deltaC.dot(dirPPP)<0.0f; + const bool bPPN = deltaC.dot(dirPPN)<0.0f; + const bool bPNP = deltaC.dot(dirPNP)<0.0f; + const bool bPNN = deltaC.dot(dirPNN)<0.0f; + const bool bNPP = deltaC.dot(dirNPP)<0.0f; + const bool bNPN = deltaC.dot(dirNPN)<0.0f; + const bool bNNP = deltaC.dot(dirNNP)<0.0f; + const bool bNNN = deltaC.dot(dirNNN)<0.0f; + + PxU32 code = 0; + if(!bPPP) + code |= (1<<7); // Bit 0: PPP + if(!bPPN) + code |= (1<<6); // Bit 1: PPN + if(!bPNP) + code |= (1<<5); // Bit 2: PNP + if(!bPNN) + code |= (1<<4); // Bit 3: PNN + if(!bNPP) + code |= (1<<3); // Bit 4: NPP + if(!bNPN) + code |= (1<<2); // Bit 5: NPN + if(!bNNP) + code |= (1<<1); // Bit 6: NNP + if(!bNNN) + code |= (1<<0); // Bit 7: NNN + return code; +} +#endif + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Common.h" +#endif + +static void setEmpty(CenterExtents& box) +{ + box.mCenter = PxVec3(0.0f, 0.0f, 0.0f); + box.mExtents = PxVec3(-1.0f, -1.0f, -1.0f); +} + +// Data: +// 1 bit for leaf/no leaf +// 2 bits for child-node type +// 8 bits for PNS +// => 32 - 1 - 2 - 8 = 21 bits left for encoding triangle index or node *offset* +// => limited to 2.097.152 triangles +// => and 2Mb-large trees (this one may not work out well in practice) +// ==> lines marked with //* have been changed to address this. Now we don't store offsets in bytes directly +// but in BVData indices. There's more work at runtime calculating addresses, but now the format can support +// 2 million single nodes. +// +// That being said we only need 3*8 = 24 bits in total, so that could be only 6 bits in each BVData. +// For type0: we have 2 nodes, we need 8 bits => 6 bits/node = 12 bits available, ok +// For type1: we have 3 nodes, we need 8*2 = 16 bits => 6 bits/node = 18 bits available, ok +// For type2: we have 4 nodes, we need 8*3 = 24 bits => 6 bits/node = 24 bits available, ok +//#pragma pack(1) +struct BVData : public physx::shdfnd::UserAllocated +{ + BVData(); + CenterExtents mAABB; + size_t mData; +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + PxU32 mTempPNS; +#endif +}; +//#pragma pack() + +BVData::BVData() : mData(PX_INVALID_U32) +{ + setEmpty(mAABB); +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + mTempPNS = 0; +#endif +} + +struct BV4Node : public physx::shdfnd::UserAllocated +{ + PX_FORCE_INLINE BV4Node() {} + PX_FORCE_INLINE ~BV4Node() {} + + BVData mBVData[4]; + + PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData>>1); } + PX_FORCE_INLINE const BV4Node* getChild(PxU32 i) const { return reinterpret_cast<BV4Node*>(mBVData[i].mData); } + + PxU32 getType() const + { + PxU32 Nb=0; + for(PxU32 i=0;i<4;i++) + { + if(mBVData[i].mData!=PX_INVALID_U32) + Nb++; + } + return Nb; + } + + PxU32 getSize() const + { + const PxU32 type = getType(); + return sizeof(BVData)*type; + } +}; + +#define NB_NODES_PER_SLAB 256 +struct BV4BuildParams +{ + PX_FORCE_INLINE BV4BuildParams(float epsilon) : mEpsilon(epsilon) +#ifdef GU_BV4_USE_NODE_POOLS + ,mTop(NULL) +#endif + {} + ~BV4BuildParams(); + + // Stats + PxU32 mNbNodes; + PxU32 mStats[4]; + + // + float mEpsilon; + +#ifdef GU_BV4_USE_NODE_POOLS + // + struct Slab : public physx::shdfnd::UserAllocated + { + BV4Node mNodes[NB_NODES_PER_SLAB]; + PxU32 mNbUsedNodes; + Slab* mNext; + }; + Slab* mTop; + + BV4Node* allocateNode(); + void releaseNodes(); +#endif +}; + +BV4BuildParams::~BV4BuildParams() +{ +#ifdef GU_BV4_USE_NODE_POOLS + releaseNodes(); +#endif +} + +#ifdef GU_BV4_USE_NODE_POOLS +BV4Node* BV4BuildParams::allocateNode() +{ + if(!mTop || mTop->mNbUsedNodes==NB_NODES_PER_SLAB) + { + Slab* newSlab = PX_NEW(Slab); + newSlab->mNbUsedNodes = 0; + newSlab->mNext = mTop; + mTop = newSlab; + } + return &mTop->mNodes[mTop->mNbUsedNodes++]; +} + +void BV4BuildParams::releaseNodes() +{ + Slab* current = mTop; + while(current) + { + Slab* next = current->mNext; + PX_DELETE(current); + current = next; + } + mTop = NULL; +} +#endif + +static void setPrimitive(const AABBTree& source, BV4Node* node4, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + const PxU32 nbPrims = node->getNbPrimitives(); + PX_ASSERT(nbPrims<16); + const PxU32* indexBase = source.getIndices(); + const PxU32* prims = node->getPrimitives(); + const PxU32 offset = PxU32(prims - indexBase); + for(PxU32 j=0;j<nbPrims;j++) + { + PX_ASSERT(prims[j] == offset+j); + } + const PxU32 primitiveIndex = (offset<<4)|(nbPrims&15); + + node4->mBVData[i].mAABB = node->getAABB(); + if(epsilon!=0.0f) + node4->mBVData[i].mAABB.mExtents += PxVec3(epsilon, epsilon, epsilon); + node4->mBVData[i].mData = (primitiveIndex<<1)|1; +} + +static BV4Node* setNode(const AABBTree& source, BV4Node* node4, PxU32 i, const AABBTreeNode* node, BV4BuildParams& params) +{ + BV4Node* child = NULL; + if(node->isLeaf()) + { + setPrimitive(source, node4, i, node, params.mEpsilon); + } + else + { + node4->mBVData[i].mAABB = node->getAABB(); + if(params.mEpsilon!=0.0f) + node4->mBVData[i].mAABB.mExtents += PxVec3(params.mEpsilon); + + params.mNbNodes++; +#ifdef GU_BV4_USE_NODE_POOLS + child = params.allocateNode(); +#else + child = PX_NEW(BV4Node); +#endif + node4->mBVData[i].mData = size_t(child); + } + return child; +} + +static void _BuildBV4(const AABBTree& source, BV4Node* tmp, const AABBTreeNode* current_node, BV4BuildParams& params) +{ + PX_ASSERT(!current_node->isLeaf()); + + // In the regular tree we have current node A, and: + // ____A____ + // P N + // __|__ __|__ + // PP PN NP NN + // + // For PNS we have: + // bit0 to sort P|N + // bit1 to sort PP|PN + // bit2 to sort NP|NN + // + // As much as possible we need to preserve the original order in BV4, if we want to reuse the same PNS bits. + // + // bit0|bit1|bit2 Order 8bits code + // 0 0 0 PP PN NP NN 0 1 2 3 + // 0 0 1 PP PN NN NP 0 1 3 2 + // 0 1 0 PN PP NP NN 1 0 2 3 + // 0 1 1 PN PP NN NP 1 0 3 2 + // 1 0 0 NP NN PP PN 2 3 0 1 + // 1 0 1 NN NP PP PN 3 2 0 1 + // 1 1 0 NP NN PN PP 2 3 1 0 + // 1 1 1 NN NP PN PP 3 2 1 0 + // + // So we can fetch/compute the sequence from the bits, combine it with limitations from the node type, and process the nodes in order. In theory. + // 8*8bits => the whole thing fits in a single 64bit register, so we could potentially use a "register LUT" here. + + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + + const bool PLeaf = P->isLeaf(); + const bool NLeaf = N->isLeaf(); + + if(PLeaf) + { + if(NLeaf) + { + // Case 1: P and N are both leaves: + // ____A____ + // P N + // => store as (P,N) and keep bit0 + params.mStats[0]++; + // PN leaves => store 2 triangle pointers, lose 50% of node space + setPrimitive(source, tmp, 0, P, params.mEpsilon); + setPrimitive(source, tmp, 1, N, params.mEpsilon); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); +#endif + } + else + { + // Case 2: P leaf, N no leaf + // ____A____ + // P N + // __|__ + // NP NN + // => store as (P,NP,NN), keep bit0 and bit2 + params.mStats[1]++; + // P leaf => store 1 triangle pointers and 2 node pointers + // => 3 slots used, 25% wasted + setPrimitive(source, tmp, 0, P, params.mEpsilon); + + // + + const AABBTreeNode* NP = N->getPos(); + const AABBTreeNode* NN = N->getNeg(); + +//#define NODE_FUSION +#ifdef NODE_FUSION + PxU32 c=0; + BV4Node* ChildNP; + if(!NP->isLeaf() && NP->getPos()->isLeaf() && NP->getNeg()->isLeaf()) + { + // Drag the terminal leaves directly into this BV4 node, drop internal node NP + setPrimitive(source, tmp, 1, NP->getPos(), params.mEpsilon); + setPrimitive(source, tmp, 2, NP->getNeg(), params.mEpsilon); + ChildNP = NULL; + params.mStats[1]--; + params.mStats[3]++; + c=1; + } + else + { + ChildNP = setNode(source, tmp, 1, NP, params); + } + + BV4Node* ChildNN; + if(c==0 && !NN->isLeaf() && NN->getPos()->isLeaf() && NN->getNeg()->isLeaf()) + { + // Drag the terminal leaves directly into this BV4 node, drop internal node NN + setPrimitive(source, tmp, 2, NN->getPos(), params.mEpsilon); + setPrimitive(source, tmp, 3, NN->getNeg(), params.mEpsilon); + ChildNN = NULL; + params.mStats[1]--; + params.mStats[3]++; + } + else + { + ChildNN = setNode(source, tmp, 2+c, NN, params); + } + + //BV4Node* ChildNN = setNode(tmp, 2+c, NN, epsilon, params); +#else + BV4Node* ChildNP = setNode(source, tmp, 1, NP, params); + BV4Node* ChildNN = setNode(source, tmp, 2, NN, params); +#endif + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[2].mTempPNS = precomputeNodeSorting(NP->mBV, NN->mBV); +#endif + if(ChildNP) + _BuildBV4(source, ChildNP, NP, params); + if(ChildNN) + _BuildBV4(source, ChildNN, NN, params); + } + } + else + { + if(NLeaf) + { + // Case 3: P no leaf, N leaf + // ____A____ + // P N + // __|__ + // PP PN + // => store as (PP,PN,N), keep bit0 and bit1 + params.mStats[2]++; + + // N leaf => store 1 triangle pointers and 2 node pointers + // => 3 slots used, 25% wasted + setPrimitive(source, tmp, 2, N, params.mEpsilon); + + // + + const AABBTreeNode* PP = P->getPos(); + const AABBTreeNode* PN = P->getNeg(); + + BV4Node* ChildPP = setNode(source, tmp, 0, PP, params); + BV4Node* ChildPN = setNode(source, tmp, 1, PN, params); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[1].mTempPNS = precomputeNodeSorting(PP->mBV, PN->mBV); +#endif + if(ChildPP) + _BuildBV4(source, ChildPP, PP, params); + if(ChildPN) + _BuildBV4(source, ChildPN, PN, params); + } + else + { + // Case 4: P and N are no leaves: + // => store as (PP,PN,NP,NN), keep bit0/bit1/bit2 + params.mStats[3]++; + + // No leaves => store 4 node pointers + const AABBTreeNode* PP = P->getPos(); + const AABBTreeNode* PN = P->getNeg(); + const AABBTreeNode* NP = N->getPos(); + const AABBTreeNode* NN = N->getNeg(); + + BV4Node* ChildPP = setNode(source, tmp, 0, PP, params); + BV4Node* ChildPN = setNode(source, tmp, 1, PN, params); + BV4Node* ChildNP = setNode(source, tmp, 2, NP, params); + BV4Node* ChildNN = setNode(source, tmp, 3, NN, params); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[1].mTempPNS = precomputeNodeSorting(PP->mBV, PN->mBV); + tmp->mBVData[2].mTempPNS = precomputeNodeSorting(NP->mBV, NN->mBV); +#endif + if(ChildPP) + _BuildBV4(source, ChildPP, PP, params); + if(ChildPN) + _BuildBV4(source, ChildPN, PN, params); + if(ChildNP) + _BuildBV4(source, ChildNP, NP, params); + if(ChildNN) + _BuildBV4(source, ChildNN, NN, params); + } + } +} + +static bool BuildBV4Internal(BV4Tree& tree, const AABBTree& Source, SourceMesh* mesh, float epsilon) +{ + if(mesh->getNbTriangles()<=4) + return tree.init(mesh, Source.getBV()); + + { + struct Local + { + static void _CheckMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd) + { + cd++; + md = PxMax(md, cd); + + if(current_node->getPos()) { _CheckMD(current_node->getPos(), md, cd); cd--; } + if(current_node->getNeg()) { _CheckMD(current_node->getNeg(), md, cd); cd--; } + } + + static void _Check(AABBTreeNode* current_node) + { + if(current_node->isLeaf()) + return; + + AABBTreeNode* P = const_cast<AABBTreeNode*>(current_node->getPos()); + AABBTreeNode* N = const_cast<AABBTreeNode*>(current_node->getNeg()); + { + PxU32 MDP = 0; PxU32 CDP = 0; _CheckMD(P, MDP, CDP); + PxU32 MDN = 0; PxU32 CDN = 0; _CheckMD(N, MDN, CDN); + + if(MDP>MDN) +// if(MDP<MDN) + { + Ps::swap(*P, *N); + Ps::swap(P, N); + } + } + _Check(P); + _Check(N); + } + }; + Local::_Check(const_cast<AABBTreeNode*>(Source.getNodes())); + } + + BV4BuildParams Params(epsilon); + Params.mNbNodes=1; // Root node + Params.mStats[0]=0; + Params.mStats[1]=0; + Params.mStats[2]=0; + Params.mStats[3]=0; + +#ifdef GU_BV4_USE_NODE_POOLS + BV4Node* Root = Params.allocateNode(); +#else + BV4Node* Root = PX_NEW(BV4Node); +#endif + _BuildBV4(Source, Root, Source.getNodes(), Params); + + if(!tree.init(mesh, Source.getBV())) + return false; + BV4Tree* T = &tree; + + // Version with variable-sized nodes in single stream + { + struct Local + { +#ifdef GU_BV4_QUANTIZED_TREE + #ifdef GU_BV4_USE_SLABS + static void _ComputeMaxValues(const BV4Node* current, PxVec3& MinMax, PxVec3& MaxMax) + { + for(PxU32 i=0;i<4;i++) + { + if(current->mBVData[i].mData!=PX_INVALID_U32) + { + const CenterExtents& Box = current->mBVData[i].mAABB; + const PxVec3 Min = Box.mCenter - Box.mExtents; + const PxVec3 Max = Box.mCenter + Box.mExtents; + if(fabsf(Min.x)>MinMax.x) MinMax.x = fabsf(Min.x); + if(fabsf(Min.y)>MinMax.y) MinMax.y = fabsf(Min.y); + if(fabsf(Min.z)>MinMax.z) MinMax.z = fabsf(Min.z); + if(fabsf(Max.x)>MaxMax.x) MaxMax.x = fabsf(Max.x); + if(fabsf(Max.y)>MaxMax.y) MaxMax.y = fabsf(Max.y); + if(fabsf(Max.z)>MaxMax.z) MaxMax.z = fabsf(Max.z); + if(!current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + _ComputeMaxValues(ChildNode, MinMax, MaxMax); + } + } + } + } + #else + static void _ComputeMaxValues(const BV4Node* current, PxVec3& CMax, PxVec3& EMax) + { + for(PxU32 i=0;i<4;i++) + { + if(current->mBVData[i].mData!=PX_INVALID_U32) + { + const CenterExtents& Box = current->mBVData[i].mAABB; + if(fabsf(Box.mCenter.x)>CMax.x) CMax.x = fabsf(Box.mCenter.x); + if(fabsf(Box.mCenter.y)>CMax.y) CMax.y = fabsf(Box.mCenter.y); + if(fabsf(Box.mCenter.z)>CMax.z) CMax.z = fabsf(Box.mCenter.z); + if(fabsf(Box.mExtents.x)>EMax.x) EMax.x = fabsf(Box.mExtents.x); + if(fabsf(Box.mExtents.y)>EMax.y) EMax.y = fabsf(Box.mExtents.y); + if(fabsf(Box.mExtents.z)>EMax.z) EMax.z = fabsf(Box.mExtents.z); + + if(!current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + _ComputeMaxValues(ChildNode, CMax, EMax); + } + } + } + } + #endif +#endif + + static void _Flatten(BVDataPacked* const dest, const PxU32 box_id, PxU32& current_id, const BV4Node* current, PxU32& max_depth, PxU32& current_depth +#ifdef GU_BV4_QUANTIZED_TREE + , const PxVec3& CQuantCoeff, const PxVec3& EQuantCoeff, + const PxVec3& mCenterCoeff, const PxVec3& mExtentsCoeff +#endif + ) + { + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + +// dest[box_id] = *current; + const PxU32 CurrentType = current->getType(); + for(PxU32 i=0;i<CurrentType;i++) + { +#ifdef GU_BV4_QUANTIZED_TREE + const CenterExtents& Box = current->mBVData[i].mAABB; + #ifdef GU_BV4_USE_SLABS + const PxVec3 m = Box.mCenter - Box.mExtents; + const PxVec3 M = Box.mCenter + Box.mExtents; + + dest[box_id+i].mAABB.mData[0].mCenter = PxI16(m.x * CQuantCoeff.x); + dest[box_id+i].mAABB.mData[1].mCenter = PxI16(m.y * CQuantCoeff.y); + dest[box_id+i].mAABB.mData[2].mCenter = PxI16(m.z * CQuantCoeff.z); + dest[box_id+i].mAABB.mData[0].mExtents = PxU16(PxI16(M.x * EQuantCoeff.x)); + dest[box_id+i].mAABB.mData[1].mExtents = PxU16(PxI16(M.y * EQuantCoeff.y)); + dest[box_id+i].mAABB.mData[2].mExtents = PxU16(PxI16(M.z * EQuantCoeff.z)); + + if(1) + { + for(PxU32 j=0;j<3;j++) + { + // Dequantize the min/max +// const float qmin = float(dest[box_id+i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; +// const float qmax = float(PxI16(dest[box_id+i].mAABB.mData[j].mExtents)) * mExtentsCoeff[j]; + // Compare real & dequantized values +/* if(qmax<M[j] || qmin>m[j]) + { + int stop=1; + }*/ + bool CanLeave; + do + { + CanLeave=true; + const float qmin = float(dest[box_id+i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + const float qmax = float(PxI16(dest[box_id+i].mAABB.mData[j].mExtents)) * mExtentsCoeff[j]; + + if(qmax<M[j]) + { +// if(dest[box_id+i].mAABB.mData[j].mExtents!=0xffff) + if(dest[box_id+i].mAABB.mData[j].mExtents!=0x7fff) + { + dest[box_id+i].mAABB.mData[j].mExtents++; + CanLeave = false; + } + } + if(qmin>m[j]) + { + if(dest[box_id+i].mAABB.mData[j].mCenter) + { + dest[box_id+i].mAABB.mData[j].mCenter--; + CanLeave = false; + } + } + }while(!CanLeave); + } + } + #else + dest[box_id+i].mAABB.mData[0].mCenter = PxI16(Box.mCenter.x * CQuantCoeff.x); + dest[box_id+i].mAABB.mData[1].mCenter = PxI16(Box.mCenter.y * CQuantCoeff.y); + dest[box_id+i].mAABB.mData[2].mCenter = PxI16(Box.mCenter.z * CQuantCoeff.z); + dest[box_id+i].mAABB.mData[0].mExtents = PxU16(Box.mExtents.x * EQuantCoeff.x); + dest[box_id+i].mAABB.mData[1].mExtents = PxU16(Box.mExtents.y * EQuantCoeff.y); + dest[box_id+i].mAABB.mData[2].mExtents = PxU16(Box.mExtents.z * EQuantCoeff.z); + + // Fix quantized boxes + if(1) + { + // Make sure the quantized box is still valid + const PxVec3 Max = Box.mCenter + Box.mExtents; + const PxVec3 Min = Box.mCenter - Box.mExtents; + // For each axis + for(PxU32 j=0;j<3;j++) + { // Dequantize the box center + const float qc = float(dest[box_id+i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + bool FixMe=true; + do + { // Dequantize the box extent + const float qe = float(dest[box_id+i].mAABB.mData[j].mExtents) * mExtentsCoeff[j]; + // Compare real & dequantized values + if(qc+qe<Max[j] || qc-qe>Min[j]) dest[box_id+i].mAABB.mData[j].mExtents++; + else FixMe=false; + // Prevent wrapping + if(!dest[box_id+i].mAABB.mData[j].mExtents) + { + dest[box_id+i].mAABB.mData[j].mExtents=0xffff; + FixMe=false; + } + }while(FixMe); + } + } + #endif +#else + #ifdef GU_BV4_USE_SLABS + // Compute min & max right here. Store temp as Center/Extents = Min/Max + const CenterExtents& Box = current->mBVData[i].mAABB; + dest[box_id+i].mAABB.mCenter = Box.mCenter - Box.mExtents; + dest[box_id+i].mAABB.mExtents = Box.mCenter + Box.mExtents; + #else + dest[box_id+i].mAABB = current->mBVData[i].mAABB; + #endif +#endif + dest[box_id+i].mData = PxU32(current->mBVData[i].mData); +// dest[box_id+i].encodePNS(current->mBVData[i].mTempPNS); + } + + PxU32 NbToGo=0; + PxU32 NextIDs[4] = {PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32}; + const BV4Node* ChildNodes[4] = {NULL,NULL,NULL,NULL}; + + BVDataPacked* data = dest+box_id; + for(PxU32 i=0;i<4;i++) + { + if(current->mBVData[i].mData!=PX_INVALID_U32 && !current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; +#ifdef GU_BV4_USE_SLABS + current_id += 4; +#else + const PxU32 ChildSize = ChildNode->getType(); + current_id += ChildSize; +#endif + const PxU32 ChildType = (ChildNode->getType()-2)<<1; + data[i].mData = size_t(ChildType+(NextID<<GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + data[i].encodePNS(current->mBVData[i].mTempPNS); +#endif +//#define DEPTH_FIRST +#ifdef DEPTH_FIRST + _Flatten(dest, NextID, current_id, ChildNode, max_depth, current_depth + #ifdef GU_BV4_QUANTIZED_TREE + , CQuantCoeff, EQuantCoeff, mCenterCoeff, mExtentsCoeff + #endif + ); + current_depth--; +#endif + } +#ifdef GU_BV4_USE_SLABS + if(current->mBVData[i].mData==PX_INVALID_U32) + { + #ifdef GU_BV4_QUANTIZED_TREE + data[i].mAABB.mData[0].mExtents = 0; + data[i].mAABB.mData[1].mExtents = 0; + data[i].mAABB.mData[2].mExtents = 0; + data[i].mAABB.mData[0].mCenter = 0; + data[i].mAABB.mData[1].mCenter = 0; + data[i].mAABB.mData[2].mCenter = 0; + #else + data[i].mAABB.mCenter = PxVec3(0.0f); + data[i].mAABB.mExtents = PxVec3(0.0f); + #endif + data[i].mData = PX_INVALID_U32; + } +#endif + } + +#ifndef DEPTH_FIRST + for(PxU32 i=0;i<NbToGo;i++) + { + _Flatten(dest, NextIDs[i], current_id, ChildNodes[i], max_depth, current_depth + #ifdef GU_BV4_QUANTIZED_TREE + , CQuantCoeff, EQuantCoeff, mCenterCoeff, mExtentsCoeff + #endif + ); + current_depth--; + } +#endif +#ifndef GU_BV4_USE_NODE_POOLS + DELETESINGLE(current); +#endif + } + }; + + const PxU32 NbSingleNodes = Params.mStats[0]*2+(Params.mStats[1]+Params.mStats[2])*3+Params.mStats[3]*4; + + PxU32 CurID = Root->getType(); + PxU32 InitData = PX_INVALID_U32; +#ifdef GU_BV4_USE_SLABS + PX_UNUSED(NbSingleNodes); + const PxU32 NbNeeded = (Params.mStats[0]+Params.mStats[1]+Params.mStats[2]+Params.mStats[3])*4; + BVDataPacked* Nodes = reinterpret_cast<BVDataPacked*>(PX_ALLOC(sizeof(BVDataPacked)*NbNeeded, "BV4 nodes")); // PT: PX_NEW breaks alignment here +// BVDataPacked* Nodes = PX_NEW(BVDataPacked)[NbNeeded]; + + if(CurID==2) + { + InitData = 0; + } + else if(CurID==3) + { + InitData = 2; + } + else if(CurID==4) + { + InitData = 4; + } + + CurID = 4; +// PxU32 CurID = 4; +// PxU32 InitData = 4; +#else + BVDataPacked* Nodes = PX_NEW(BVDataPacked)[NbSingleNodes]; + + if(CurID==2) + { + InitData = 0; + } + else if(CurID==3) + { + InitData = 2; + } + else if(CurID==4) + { + InitData = 4; + } +#endif + + T->mInitData = InitData; + PxU32 MaxDepth = 0; + PxU32 CurrentDepth = 0; +#ifdef GU_BV4_QUANTIZED_TREE + #ifdef GU_BV4_USE_SLABS + PxVec3 MinQuantCoeff, MaxQuantCoeff; + { + // Get max values + PxVec3 MinMax(-FLT_MAX); + PxVec3 MaxMax(-FLT_MAX); + Local::_ComputeMaxValues(Root, MinMax, MaxMax); + + const PxU32 nbm=15; + + // Compute quantization coeffs + const float MinCoeff = float((1<<nbm)-1); + const float MaxCoeff = float((1<<nbm)-1); + MinQuantCoeff.x = MinMax.x!=0.0f ? MinCoeff/MinMax.x : 0.0f; + MinQuantCoeff.y = MinMax.y!=0.0f ? MinCoeff/MinMax.y : 0.0f; + MinQuantCoeff.z = MinMax.z!=0.0f ? MinCoeff/MinMax.z : 0.0f; + MaxQuantCoeff.x = MaxMax.x!=0.0f ? MaxCoeff/MaxMax.x : 0.0f; + MaxQuantCoeff.y = MaxMax.y!=0.0f ? MaxCoeff/MaxMax.y : 0.0f; + MaxQuantCoeff.z = MaxMax.z!=0.0f ? MaxCoeff/MaxMax.z : 0.0f; + // Compute and save dequantization coeffs + T->mCenterOrMinCoeff.x = MinMax.x/MinCoeff; + T->mCenterOrMinCoeff.y = MinMax.y/MinCoeff; + T->mCenterOrMinCoeff.z = MinMax.z/MinCoeff; + T->mExtentsOrMaxCoeff.x = MaxMax.x/MaxCoeff; + T->mExtentsOrMaxCoeff.y = MaxMax.y/MaxCoeff; + T->mExtentsOrMaxCoeff.z = MaxMax.z/MaxCoeff; + } + Local::_Flatten(Nodes, 0, CurID, Root, MaxDepth, CurrentDepth, MinQuantCoeff, MaxQuantCoeff, T->mCenterOrMinCoeff, T->mExtentsOrMaxCoeff); + #else + PxVec3 CQuantCoeff, EQuantCoeff; + { + // Get max values + PxVec3 CMax(-FLT_MAX); + PxVec3 EMax(-FLT_MAX); + Local::_ComputeMaxValues(Root, CMax, EMax); + + const PxU32 nbc=15; + const PxU32 nbe=16; +// const PxU32 nbc=7; +// const PxU32 nbe=8; + + const float UnitQuantError = 2.0f/65535.0f; + EMax.x += CMax.x*UnitQuantError; + EMax.y += CMax.y*UnitQuantError; + EMax.z += CMax.z*UnitQuantError; + + // Compute quantization coeffs + const float CCoeff = float((1<<nbc)-1); + CQuantCoeff.x = CMax.x!=0.0f ? CCoeff/CMax.x : 0.0f; + CQuantCoeff.y = CMax.y!=0.0f ? CCoeff/CMax.y : 0.0f; + CQuantCoeff.z = CMax.z!=0.0f ? CCoeff/CMax.z : 0.0f; + const float ECoeff = float((1<<nbe)-32); + EQuantCoeff.x = EMax.x!=0.0f ? ECoeff/EMax.x : 0.0f; + EQuantCoeff.y = EMax.y!=0.0f ? ECoeff/EMax.y : 0.0f; + EQuantCoeff.z = EMax.z!=0.0f ? ECoeff/EMax.z : 0.0f; + // Compute and save dequantization coeffs + T->mCenterOrMinCoeff.x = CMax.x/CCoeff; + T->mCenterOrMinCoeff.y = CMax.y/CCoeff; + T->mCenterOrMinCoeff.z = CMax.z/CCoeff; + T->mExtentsOrMaxCoeff.x = EMax.x/ECoeff; + T->mExtentsOrMaxCoeff.y = EMax.y/ECoeff; + T->mExtentsOrMaxCoeff.z = EMax.z/ECoeff; + } + Local::_Flatten(Nodes, 0, CurID, Root, MaxDepth, CurrentDepth, CQuantCoeff, EQuantCoeff, T->mCenterOrMinCoeff, T->mExtentsOrMaxCoeff); + #endif +#else + Local::_Flatten(Nodes, 0, CurID, Root, MaxDepth, CurrentDepth); +#endif + +#ifdef GU_BV4_USE_NODE_POOLS + Params.releaseNodes(); +#endif + +#ifdef GU_BV4_USE_SLABS + { + PX_ASSERT(sizeof(BVDataSwizzled)==sizeof(BVDataPacked)*4); + BVDataPacked* Copy = PX_NEW(BVDataPacked)[NbNeeded]; + memcpy(Copy, Nodes, sizeof(BVDataPacked)*NbNeeded); + for(PxU32 i=0;i<NbNeeded/4;i++) + { + const BVDataPacked* Src = Copy + i*4; + BVDataSwizzled* Dst = reinterpret_cast<BVDataSwizzled*>(Nodes + i*4); + for(PxU32 j=0;j<4;j++) + { + // We previously stored m/M within c/e so we just need to swizzle now + #ifdef GU_BV4_QUANTIZED_TREE + const QuantizedAABB& Box = Src[j].mAABB; + Dst->mX[j].mMin = Box.mData[0].mCenter; + Dst->mY[j].mMin = Box.mData[1].mCenter; + Dst->mZ[j].mMin = Box.mData[2].mCenter; + Dst->mX[j].mMax = PxI16(Box.mData[0].mExtents); + Dst->mY[j].mMax = PxI16(Box.mData[1].mExtents); + Dst->mZ[j].mMax = PxI16(Box.mData[2].mExtents); + #else + const CenterExtents& Box = Src[j].mAABB; + Dst->mMinX[j] = Box.mCenter.x; + Dst->mMinY[j] = Box.mCenter.y; + Dst->mMinZ[j] = Box.mCenter.z; + Dst->mMaxX[j] = Box.mExtents.x; + Dst->mMaxY[j] = Box.mExtents.y; + Dst->mMaxZ[j] = Box.mExtents.z; + #endif + Dst->mData[j] = Src[j].mData; + } + } + DELETEARRAY(Copy); + } + T->mNbNodes = NbNeeded; +#else + PX_ASSERT(CurID==NbSingleNodes); + T->mNbNodes = NbSingleNodes; +#endif + T->mNodes = Nodes; + } + return true; +} + +///// + +struct ReorderData +{ + const SourceMesh* mMesh; + PxU32* mOrder; + PxU32 mNbTrisPerLeaf; + PxU32 mIndex; + PxU32 mNbTris; + PxU32 mStats[16]; +}; +static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData) +{ + ReorderData* Data = reinterpret_cast<ReorderData*>(userData); + if(current->isLeaf()) + { + const PxU32 n = current->getNbPrimitives(); + PX_ASSERT(n<=Data->mNbTrisPerLeaf); + Data->mStats[n]++; + PxU32* Prims = const_cast<PxU32*>(current->getPrimitives()); + + for(PxU32 i=0;i<n;i++) + { + PX_ASSERT(Prims[i]<Data->mNbTris); + Data->mOrder[Data->mIndex] = Prims[i]; + PX_ASSERT(Data->mIndex<Data->mNbTris); + Prims[i] = Data->mIndex; + Data->mIndex++; + } + } + return true; +} + +bool physx::Gu::BuildBV4Ex(BV4Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf) +{ + const PxU32 nbTris = mesh.mNbTris; + + AABBTree Source; + if(!Source.buildFromMesh(mesh, nbTrisPerLeaf)) + return false; + + { + PxU32* order = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32)*nbTris, "BV4")); + ReorderData RD; + RD.mMesh = &mesh; + RD.mOrder = order; + RD.mNbTrisPerLeaf = nbTrisPerLeaf; + RD.mIndex = 0; + RD.mNbTris = nbTris; + for(PxU32 i=0;i<16;i++) + RD.mStats[i] = 0; + Source.walk(gReorderCallback, &RD); + PX_ASSERT(RD.mIndex==nbTris); + mesh.remapTopology(order); + PX_FREE(order); +// for(PxU32 i=0;i<16;i++) +// printf("%d: %d\n", i, RD.mStats[i]); + } + + if(mesh.getNbTriangles()<=nbTrisPerLeaf) + return tree.init(&mesh, Source.getBV()); + + return BuildBV4Internal(tree, Source, &mesh, epsilon); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Build.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Build.h new file mode 100644 index 00000000..eb2d9e99 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Build.h @@ -0,0 +1,125 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BUILD_H +#define GU_BV4_BUILD_H + +#include "foundation/PxSimpleTypes.h" +#include "GuBV4.h" + +namespace physx +{ +namespace Gu +{ + class BV4Tree; + class SourceMesh; + + //! Contains AABB-tree build statistics + // PT: TODO: this is a duplicate of the SQ structure (TA34704) + struct BuildStats + { + BuildStats() : mCount(0), mTotalPrims(0) {} + + PxU32 mCount; //!< Number of nodes created + PxU32 mTotalPrims; //!< Total accumulated number of primitives. Should be much higher than the source + //!< number of prims, since it accumulates all prims covered by each node (i.e. internal + //!< nodes too, not just leaf ones) + + PX_FORCE_INLINE void reset() { mCount = mTotalPrims = 0; } + + PX_FORCE_INLINE void setCount(PxU32 nb) { mCount=nb; } + PX_FORCE_INLINE void increaseCount(PxU32 nb) { mCount+=nb; } + PX_FORCE_INLINE PxU32 getCount() const { return mCount; } + }; + + // PT: TODO: refactor with SQ version (TA34704) + class AABBTreeNode : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE AABBTreeNode() : mPos(0), mNodePrimitives(NULL), mNbPrimitives(0) + { + } + PX_FORCE_INLINE ~AABBTreeNode() + { + mPos = 0; + mNodePrimitives = NULL; // This was just a shortcut to the global list => no release + mNbPrimitives = 0; + } + // Data access + PX_FORCE_INLINE const PxBounds3& getAABB() const { return mBV; } + + PX_FORCE_INLINE const AABBTreeNode* getPos() const { return reinterpret_cast<const AABBTreeNode*>(mPos); } + PX_FORCE_INLINE const AABBTreeNode* getNeg() const { const AABBTreeNode* P = getPos(); return P ? P+1 : NULL; } + + PX_FORCE_INLINE bool isLeaf() const { return !getPos(); } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + size_t mPos; // "Positive" & "Negative" children + + // Data access + PX_FORCE_INLINE const PxU32* getPrimitives() const { return mNodePrimitives; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mNbPrimitives; } + + PxU32* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) + PxU32 mNbPrimitives; //!< Number of primitives for this node + }; + + typedef bool (*WalkingCallback) (const AABBTreeNode* current, PxU32 depth, void* userData); + + // PT: TODO: refactor with SQ version (TA34704) + class AABBTree : public physx::shdfnd::UserAllocated + { + public: + AABBTree(); + ~AABBTree(); + + bool buildFromMesh(SourceMesh& mesh, PxU32 limit); + void release(); + + PX_FORCE_INLINE const PxU32* getIndices() const { return mIndices; } //!< Catch the indices + PX_FORCE_INLINE PxU32 getNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + + PX_FORCE_INLINE const PxU32* getPrimitives() const { return mPool->mNodePrimitives; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mPool->mNbPrimitives; } + PX_FORCE_INLINE const AABBTreeNode* getNodes() const { return mPool; } + PX_FORCE_INLINE const PxBounds3& getBV() const { return mPool->mBV; } + + PxU32 walk(WalkingCallback callback, void* userData) const; + private: + PxU32* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3] + PxU32 mTotalNbNodes; //!< Number of nodes in the tree. + }; + + PX_PHYSX_COMMON_API bool BuildBV4Ex(BV4Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf); + +} // namespace Gu +} + +#endif // GU_BV4_BUILD_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Settings.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Settings.h new file mode 100644 index 00000000..9807e526 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4Settings.h @@ -0,0 +1,39 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SETTINGS_H +#define GU_BV4_SETTINGS_H + + // PT: "BV4" ported from "Opcode 2.0". Available compile-time options are: + #define GU_BV4_STACK_SIZE 256 // Default size of local stacks for non-recursive traversals. + #define GU_BV4_PRECOMPUTED_NODE_SORT // Use node sorting or not. This should probably always be enabled. + #define GU_BV4_QUANTIZED_TREE // Use AABB quantization/compression or not. + #define GU_BV4_USE_SLABS // Use swizzled data format or not. Swizzled = faster raycasts, but slower overlaps & larger trees. + +#endif // GU_BV4_SETTINGS_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_AABBAABBSweepTest.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_AABBAABBSweepTest.h new file mode 100644 index 00000000..1131edad --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_AABBAABBSweepTest.h @@ -0,0 +1,114 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_AABB_AABB_SWEEP_TEST_H +#define GU_BV4_AABB_AABB_SWEEP_TEST_H + +#ifndef GU_BV4_USE_SLABS +#if PX_INTEL_FAMILY + PX_FORCE_INLINE Ps::IntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const PxVec3& extents2, const RayParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4Add(V4LoadU(&extents.x), V4LoadU(&extents2.x)); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), V4LoadU(¢er.x)); + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } + } + +#ifdef GU_BV4_QUANTIZED_TREE + template<class T> + PX_FORCE_INLINE Ps::IntBool BV4_SegmentAABBOverlap(const T* PX_RESTRICT node, const PxVec3& extents2, const RayParams* PX_RESTRICT params) + { + const __m128i testV = _mm_load_si128((__m128i*)node->mAABB.mData); + const __m128i qextentsV = _mm_and_si128(testV, _mm_set1_epi32(0x0000ffff)); + const __m128i qcenterV = _mm_srai_epi32(testV, 16); + const Vec4V centerV0 = V4Mul(_mm_cvtepi32_ps(qcenterV), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + const Vec4V extentsV0 = V4Mul(_mm_cvtepi32_ps(qextentsV), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + + const PxU32 maskI = 0x7fffffff; + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4Add(extentsV0, V4LoadU(&extents2.x)); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), centerV0); + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } + } +#endif +#endif +#endif + +#endif // GU_BV4_AABB_AABB_SWEEP_TEST_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_AABBSweep.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_AABBSweep.cpp new file mode 100644 index 00000000..2f377521 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_AABBSweep.cpp @@ -0,0 +1,39 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY +#define SWEEP_AABB_IMPL +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; +#include "GuBV4_BoxSweep_Internal.h" +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxBoxOverlapTest.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxBoxOverlapTest.h new file mode 100644 index 00000000..ff696a38 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxBoxOverlapTest.h @@ -0,0 +1,201 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BOX_BOX_OVERLAP_TEST_H +#define GU_BV4_BOX_BOX_OVERLAP_TEST_H + +#if PX_INTEL_FAMILY +#ifndef GU_BV4_USE_SLABS + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const PxVec3& extents, const PxVec3& center, const OBBTestParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + + const Vec4V extentsV = V4LoadU(&extents.x); + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), V4LoadU(¢er.x)); + { + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps((float*)&maskI)); + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = (PxU32)_mm_movemask_ps(absTV); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps((float*)&maskI)); + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = (PxU32)_mm_movemask_ps(abstV); + if(test&7) + return 0; + } + return 1; + } + +#ifdef GU_BV4_QUANTIZED_TREE + template<class T> + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const T* PX_RESTRICT node, const OBBTestParams* PX_RESTRICT params) + { +#define NEW_VERSION +#ifdef NEW_VERSION + SSE_CONST4(maskV, 0x7fffffff); + SSE_CONST4(maskQV, 0x0000ffff); +#else + const PxU32 maskI = 0x7fffffff; +#endif + + Vec4V centerV = V4LoadA((float*)node->mAABB.mData); +#ifdef NEW_VERSION + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV))); +#else + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), _mm_set1_epi32(0x0000ffff))); +#endif + extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16)); + centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), centerV); + { +#ifdef NEW_VERSION + __m128 absTV = _mm_and_ps(TV, SSE_CONSTF(maskV)); +#else + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps((float*)&maskI)); +#endif + + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = (PxU32)_mm_movemask_ps(absTV); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { +#ifdef NEW_VERSION + __m128 abstV = _mm_and_ps(tV, SSE_CONSTF(maskV)); +#else + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps((float*)&maskI)); +#endif + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = (PxU32)_mm_movemask_ps(abstV); + if(test&7) + return 0; + } + return 1; + } +#endif // GU_BV4_QUANTIZED_TREE +#endif // GU_BV4_USE_SLABS + +#ifdef GU_BV4_USE_SLABS + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const __m128 boxCenter, const __m128 extentsV, const OBBTestParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), boxCenter); + { + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps(reinterpret_cast<const float*>(&maskI))); + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = PxU32(_mm_movemask_ps(absTV)); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps(reinterpret_cast<const float*>(&maskI))); + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = PxU32(_mm_movemask_ps(abstV)); + if(test&7) + return 0; + } + return 1; + } +#endif // GU_BV4_USE_SLABS +#endif // PX_INTEL_FAMILY + +#endif // GU_BV4_BOX_BOX_OVERLAP_TEST_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxOverlap.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxOverlap.cpp new file mode 100644 index 00000000..febf7261 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxOverlap.cpp @@ -0,0 +1,473 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuInternal.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionTriangleBox.h" + +#include "GuBV4_BoxOverlap_Internal.h" +#include "GuBV4_BoxBoxOverlapTest.h" + +// Box overlap any + +struct OBBParams : OBBTestParams +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space + Vec3p mTModelToBox_Padded; //!< Translation from model space to obb space +}; + +// PT: TODO: this used to be inlined so we lost some perf by moving to PhysX's version. Revisit. (TA34704) +Ps::IntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, + const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents); +namespace +{ +class LeafFunction_BoxOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template<class ParamsT> +static PX_FORCE_INLINE void setupBoxParams(ParamsT* PX_RESTRICT params, const Box& localBox, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh) +{ + invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, localBox); + params->mTBoxToModel_PaddedAligned = localBox.center; + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + params->precomputeBoxData(localBox.extents, &localBox.rot); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "GuBV4_Internal.h" +#include "GuBV4_BoxBoxOverlapTest.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" +#endif + +Ps::IntBool BV4_OverlapBoxAny(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + Box localBox; + computeLocalBox(localBox, box, worldm_Aligned); + + OBBParams Params; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + { + return processStreamNoOrder<LeafFunction_BoxOverlapAny>(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_BoxOverlapAny::doLeafTest(&Params, nbTris); + } +} + + +// Box overlap all + +struct OBBParamsAll : OBBParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_BoxOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + { + OBBParamsAll* ParamsAll = static_cast<OBBParamsAll*>(params); + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +PxU32 BV4_OverlapBoxAll(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + Box localBox; + computeLocalBox(localBox, box, worldm_Aligned); + + OBBParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + { + overflow = processStreamNoOrder<LeafFunction_BoxOverlapAll>(tree.mNodes, tree.mInitData, &Params)!=0; + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_BoxOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + +// Box overlap - callback version + +struct OBBParamsCB : OBBParams +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_BoxOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +void BV4_OverlapBoxCB(const Box& localBox, const BV4Tree& tree, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + OBBParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + { + processStreamNoOrder<LeafFunction_BoxOverlapCB>(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_BoxOverlapCB::doLeafTest(&Params, nbTris); + } +} + +// Capsule overlap any + +struct CapsuleParamsAny : OBBParams +{ + Capsule mLocalCapsule; // Capsule in mesh space + CapsuleTriangleOverlapData mData; +}; + +// PT: TODO: try to refactor this one with the PhysX version (TA34704) +static bool CapsuleVsTriangle_SAT(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const CapsuleParamsAny* PX_RESTRICT params) +{ +// PX_ASSERT(capsule.p0!=capsule.p1); + + { + const PxReal d2 = distancePointSegmentSquaredInternal(params->mLocalCapsule.p0, params->mData.mCapsuleDir, p0); + if(d2<=params->mLocalCapsule.radius*params->mLocalCapsule.radius) + return 1; + } + + const PxVec3 N = (p0 - p1).cross(p0 - p2); + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, N)) + return 0; + + const float BDotB = params->mData.mBDotB; + const float oneOverBDotB = params->mData.mOneOverBDotB; + const PxVec3& capP0 = params->mLocalCapsule.p0; + const PxVec3& capDir = params->mData.mCapsuleDir; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p0, p1 - p0, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p1, p2 - p1, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p2, p0 - p2, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + return 1; +} + +static Ps::IntBool PX_FORCE_INLINE __CapsuleTriangle(const CapsuleParamsAny* PX_RESTRICT params, PxU32 primIndex) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + return CapsuleVsTriangle_SAT(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params); +} + +namespace +{ +class LeafFunction_CapsuleOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__CapsuleTriangle(static_cast<const CapsuleParamsAny*>(params), primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template<class ParamsT> +static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh) +{ + computeLocalCapsule(params->mLocalCapsule, capsule, worldm_Aligned); + + params->mData.init(params->mLocalCapsule); + + Box localBox; + computeBoxAroundCapsule(params->mLocalCapsule, localBox); + + setupBoxParams(params, localBox, tree, mesh); +} + +Ps::IntBool BV4_OverlapCapsuleAny(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsAny Params; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + { + return processStreamNoOrder<LeafFunction_CapsuleOverlapAny>(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_CapsuleOverlapAny::doLeafTest(&Params, nbTris); + } +} + + +// Capsule overlap all + +struct CapsuleParamsAll : CapsuleParamsAny +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_CapsuleOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + CapsuleParamsAll* ParamsAll = static_cast<CapsuleParamsAll*>(params); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__CapsuleTriangle(ParamsAll, primIndex)) + { + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +PxU32 BV4_OverlapCapsuleAll(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + { + overflow = processStreamNoOrder<LeafFunction_CapsuleOverlapAll>(tree.mNodes, tree.mInitData, &Params)!=0; + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_CapsuleOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + +// Capsule overlap - callback version + +struct CapsuleParamsCB : CapsuleParamsAny +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_CapsuleOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const CapsuleParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + 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]; + + if(CapsuleVsTriangle_SAT(p0, p1, p2, params)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this one is currently not used +void BV4_OverlapCapsuleCB(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + { + processStreamNoOrder<LeafFunction_CapsuleOverlapCB>(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_CapsuleOverlapCB::doLeafTest(&Params, nbTris); + } +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxOverlap_Internal.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxOverlap_Internal.h new file mode 100644 index 00000000..410af5b8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxOverlap_Internal.h @@ -0,0 +1,105 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BOX_OVERLAP_INTERNAL_H +#define GU_BV4_BOX_OVERLAP_INTERNAL_H + +#include "GuBV4_Common.h" + + template<class ParamsT> + PX_FORCE_INLINE void precomputeData(ParamsT* PX_RESTRICT dst, PxMat33* PX_RESTRICT absRot, const PxMat33* PX_RESTRICT boxToModelR) + { + // Precompute absolute box-to-model rotation matrix + dst->mPreca0_PaddedAligned.x = boxToModelR->column0.x; + dst->mPreca0_PaddedAligned.y = boxToModelR->column1.y; + dst->mPreca0_PaddedAligned.z = boxToModelR->column2.z; + + dst->mPreca1_PaddedAligned.x = boxToModelR->column0.y; + dst->mPreca1_PaddedAligned.y = boxToModelR->column1.z; + dst->mPreca1_PaddedAligned.z = boxToModelR->column2.x; + + dst->mPreca2_PaddedAligned.x = boxToModelR->column0.z; + dst->mPreca2_PaddedAligned.y = boxToModelR->column1.x; + dst->mPreca2_PaddedAligned.z = boxToModelR->column2.y; + + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + const PxReal epsilon = 1e-6f; + absRot->column0.x = dst->mPreca0b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.x); + absRot->column0.y = dst->mPreca1b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.y); + absRot->column0.z = dst->mPreca2b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.z); + + absRot->column1.x = dst->mPreca2b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.x); + absRot->column1.y = dst->mPreca0b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.y); + absRot->column1.z = dst->mPreca1b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.z); + + absRot->column2.x = dst->mPreca1b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.x); + absRot->column2.y = dst->mPreca2b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.y); + absRot->column2.z = dst->mPreca0b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.z); + } + + template<class ParamsT> + PX_FORCE_INLINE void setupBoxData(ParamsT* PX_RESTRICT dst, const PxVec3& extents, const PxMat33* PX_RESTRICT mAR) + { + dst->mBoxExtents_PaddedAligned = extents; + + const float Ex = extents.x; + const float Ey = extents.y; + const float Ez = extents.z; + dst->mBB_PaddedAligned.x = Ex*mAR->column0.x + Ey*mAR->column1.x + Ez*mAR->column2.x; + dst->mBB_PaddedAligned.y = Ex*mAR->column0.y + Ey*mAR->column1.y + Ez*mAR->column2.y; + dst->mBB_PaddedAligned.z = Ex*mAR->column0.z + Ey*mAR->column1.z + Ez*mAR->column2.z; + } + + struct OBBTestParams // Data needed to perform the OBB-OBB overlap test + { +#ifdef GU_BV4_QUANTIZED_TREE + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +#endif + BV4_ALIGN16(Vec3p mTBoxToModel_PaddedAligned); //!< Translation from obb space to model space + BV4_ALIGN16(Vec3p mBB_PaddedAligned); + BV4_ALIGN16(Vec3p mBoxExtents_PaddedAligned); + + BV4_ALIGN16(Vec3p mPreca0_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca1_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca2_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca0b_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca1b_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca2b_PaddedAligned); + + PX_FORCE_INLINE void precomputeBoxData(const PxVec3& extents, const PxMat33* PX_RESTRICT box_to_model) + { + PxMat33 absRot; //!< Absolute rotation matrix + precomputeData(this, &absRot, box_to_model); + + setupBoxData(this, extents, &absRot); + } + }; + +#endif // GU_BV4_BOX_OVERLAP_INTERNAL_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxSweep_Internal.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxSweep_Internal.h new file mode 100644 index 00000000..ed595e39 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxSweep_Internal.h @@ -0,0 +1,512 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 \\sw\physx\PhysXSDK\3.4\trunk\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 + { + #ifdef GU_BV4_QUANTIZED_TREE + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + #endif + #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<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 +} + +#include "GuBV4_Internal.h" +#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 +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.mNodes, tree.mInitData, &Params); + else + processStreamRayOrdered(1, LeafFunction_BoxSweepClosest)(tree.mNodes, tree.mInitData, &Params); +#else + if(Params.mEarlyExit) + processStreamNoOrder<LeafFunction_BoxSweepAny>(tree.mNodes, tree.mInitData, &Params); + else + processStreamOrdered<LeafFunction_BoxSweepClosest>(tree.mNodes, tree.mInitData, &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.mNodes, tree.mInitData, &Params); +#else + processStreamOrdered<LeafFunction_BoxSweepCB>(tree.mNodes, tree.mInitData, &Params); +#endif + } + else + { +#ifdef SWEEP_AABB_IMPL + processStreamRayNoOrder(1, LeafFunction_BoxSweepCB)(tree.mNodes, tree.mInitData, &Params); +#else + processStreamNoOrder<LeafFunction_BoxSweepCB>(tree.mNodes, tree.mInitData, &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.mNodes, tree.mInitData, &Params); + else + processStreamRayOrdered(1, LeafFunction_BoxSweepClosestCB)(tree.mNodes, tree.mInitData, &Params); +#else + if(Params.mEarlyExit) + processStreamNoOrder<LeafFunction_BoxSweepAnyCB>(tree.mNodes, tree.mInitData, &Params); + else + processStreamOrdered<LeafFunction_BoxSweepClosestCB>(tree.mNodes, tree.mInitData, &Params); +#endif + } + else + doBruteForceTests<LeafFunction_BoxSweepAnyCB, LeafFunction_BoxSweepClosestCB>(mesh->getNbTriangles(), &Params); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxSweep_Params.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxSweep_Params.h new file mode 100644 index 00000000..6869783f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_BoxSweep_Params.h @@ -0,0 +1,211 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This is used by the box-sweep & capsule-sweep code + +#if PX_VC + #pragma warning(disable: 4505) // unreferenced local function has been removed +#endif + +#include "PsBasicTemplates.h" + +namespace +{ +#ifdef SWEEP_AABB_IMPL +struct BoxSweepParams : RayParams +#else +struct BoxSweepParams : OBBTestParams +#endif +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + +#ifndef SWEEP_AABB_IMPL + Box mLocalBox; +#endif + PxVec3 mLocalDir_Padded; + RaycastHitInternal mStabbedFace; + + PxU32 mBackfaceCulling; + PxU32 mEarlyExit; + + PxVec3 mP0, mP1, mP2; + PxVec3 mBestTriNormal; + + float mOffset; + PxVec3 mProj; + PxVec3 mDP; + +#ifndef SWEEP_AABB_IMPL + PxMat33 mAR; //!< Absolute rotation matrix +#endif + + PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space + PxVec3 mTModelToBox_Padded; //!< Translation from model space to obb space + PxVec3 mOriginalExtents_Padded; + PxVec3 mOriginalDir_Padded; + PxVec3 mOneOverDir_Padded; + PxVec3 mOneOverOriginalDir; + +#ifndef SWEEP_AABB_IMPL + PX_FORCE_INLINE void ShrinkOBB(float d) + { + const PxVec3 BoxExtents = mDP + d * mProj; + mTBoxToModel_PaddedAligned = mLocalBox.center + mLocalDir_Padded*d*0.5f; + + setupBoxData(this, BoxExtents, &mAR); + } +#endif +}; +} + +// PT: TODO: check asm again in PhysX version, compare to original (TA34704) +static void prepareSweepData(const Box& box, const PxVec3& dir, float maxDist, BoxSweepParams* PX_RESTRICT params) +{ + invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, box); + + params->mOriginalExtents_Padded = box.extents; + + const PxVec3 OriginalDir = params->mRModelToBox_Padded.transform(dir); + params->mOriginalDir_Padded = OriginalDir; + + const PxVec3 OneOverOriginalDir(OriginalDir.x!=0.0f ? 1.0f/OriginalDir.x : 0.0f, + OriginalDir.y!=0.0f ? 1.0f/OriginalDir.y : 0.0f, + OriginalDir.z!=0.0f ? 1.0f/OriginalDir.z : 0.0f); + + params->mOneOverOriginalDir = OneOverOriginalDir; + params->mOneOverDir_Padded = OneOverOriginalDir / maxDist; + + { + const Box& LocalBox = box; + const PxVec3& LocalDir = dir; + + params->mLocalDir_Padded = LocalDir; + params->mStabbedFace.mDistance = maxDist; +#ifndef SWEEP_AABB_IMPL + params->mLocalBox = LocalBox; // PT: TODO: check asm for operator= +#endif + + PxMat33 boxToModelR; + + // Original code: + // OBB::CreateOBB(LocalBox, LocalDir, 0.5f) + { + PxVec3 R1, R2; + { + float dd[3]; + dd[0] = fabsf(LocalBox.rot.column0.dot(LocalDir)); + dd[1] = fabsf(LocalBox.rot.column1.dot(LocalDir)); + dd[2] = fabsf(LocalBox.rot.column2.dot(LocalDir)); + float dmax = dd[0]; + PxU32 ax0=1; + PxU32 ax1=2; + if(dd[1]>dmax) + { + dmax=dd[1]; + ax0=0; + ax1=2; + } + if(dd[2]>dmax) + { + dmax=dd[2]; + ax0=0; + ax1=1; + } + if(dd[ax1]<dd[ax0]) + Ps::swap(ax0, ax1); + + R1 = LocalBox.rot[ax0]; + R1 -= R1.dot(LocalDir)*LocalDir; // Project to plane whose normal is dir + R1.normalize(); + R2 = LocalDir.cross(R1); + } + // Original code: + // mRot = params->mRBoxToModel + boxToModelR.column0 = LocalDir; + boxToModelR.column1 = R1; + boxToModelR.column2 = R2; + + // Original code: + // float Offset[3]; + // 0.5f comes from the Offset[r]*0.5f, doesn't mean 'd' is 0.5f + params->mProj.x = 0.5f; + params->mProj.y = LocalDir.dot(R1)*0.5f; + params->mProj.z = LocalDir.dot(R2)*0.5f; + + // Original code: + //mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z; + // => we store the first part of the computation, minus 'Offset[r]*0.5f' + for(PxU32 r=0;r<3;r++) + { + const PxVec3& R = boxToModelR[r]; + params->mDP[r] = fabsf(LocalBox.rot.column0.dot(R)*LocalBox.extents.x) + + fabsf(LocalBox.rot.column1.dot(R)*LocalBox.extents.y) + + fabsf(LocalBox.rot.column2.dot(R)*LocalBox.extents.z); + } + // In the original code, both mCenter & mExtents depend on 'd', and thus we will need to recompute these two members. + // + // For mExtents we have: + // + // float Offset[3]; + // Offset[0] = d; + // Offset[1] = d*(dir|R1); + // Offset[2] = d*(dir|R2); + // + // mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z; + // <=> mExtents[r] = Offset[r]*0.5f + Params.mDP[r]; We precompute the second part that doesn't depend on d, stored in mDP + // <=> mExtents[r] = Params.mProj[r]*d + Params.mDP[r]; We extract d from the first part, store what is left in mProj + // + // Thus in ShrinkOBB the code needed to update the extents is just: + // mBoxExtents = mDP + d * mProj; + // + // For mCenter we have: + // + // mCenter = box.mCenter + dir*d*0.5f; + // + // So we simply use this formula directly, with the new d. Result is stored in 'mTBoxToModel' +/* + PX_FORCE_INLINE void ShrinkOBB(float d) + { + mBoxExtents = mDP + d * mProj; + mTBoxToModel = mLocalBox.mCenter + mLocalDir*d*0.5f; +*/ + } + + // This one is for culling tris, unrelated to CreateOBB + params->mOffset = params->mDP.x + LocalBox.center.dot(LocalDir); + +#ifndef SWEEP_AABB_IMPL + precomputeData(params, ¶ms->mAR, &boxToModelR); + + params->ShrinkOBB(maxDist); +#endif + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweep.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweep.cpp new file mode 100644 index 00000000..c8d8a5c2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweep.cpp @@ -0,0 +1,173 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +#include "GuSweepSphereTriangle.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuSIMDHelpers.h" +#include "GuInternal.h" + +#include "GuBV4_BoxOverlap_Internal.h" +#include "GuBV4_BoxSweep_Params.h" + +namespace +{ + struct CapsuleSweepParams : BoxSweepParams + { + Capsule mLocalCapsule; + PxVec3 mCapsuleCenter; + PxVec3 mExtrusionDir; + PxU32 mEarlyExit; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_CapsuleSweep_Internal.h" +#include "GuBV4_Internal.h" + +#include "GuBV4_BoxBoxOverlapTest.h" + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#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 + +Ps::IntBool BV4_CapsuleSweepSingle(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParams Params; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamNoOrder<LeafFunction_CapsuleSweepAny>(tree.mNodes, tree.mInitData, &Params); + else + processStreamOrdered<LeafFunction_CapsuleSweepClosest>(tree.mNodes, tree.mInitData, &Params); + } + else + doBruteForceTests<LeafFunction_CapsuleSweepAny, LeafFunction_CapsuleSweepClosest>(mesh->getNbTriangles(), &Params); + + return computeImpactDataT<ImpactFunctionCapsule>(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +// PT: capsule sweep callback version - currently not used + +namespace +{ + struct CapsuleSweepParamsCB : CapsuleSweepParams + { + // PT: these new members are only here to call computeImpactDataT during traversal :( + // PT: TODO: most of them may not be needed + // PT: TODO: for example mCapsuleCB probably dup of mLocalCapsule + Capsule mCapsuleCB; // Capsule 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_CapsuleSweepCB +{ +public: + + static PX_FORCE_INLINE Ps::IntBool doLeafTest(CapsuleSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triCapsuleSweep(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 = computeImpactDataT<ImpactFunctionCapsule>(params->mCapsuleCB, params->mDirCB, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + PX_UNUSED(b); + + reportUnlimitedCallbackHit(params, hit); + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_CapsuleSweepCB(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParamsCB Params; + Params.mCapsuleCB = capsule; + Params.mDirCB = dir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + processStreamOrdered<LeafFunction_CapsuleSweepCB>(tree.mNodes, tree.mInitData, &Params); + else + processStreamNoOrder<LeafFunction_CapsuleSweepCB>(tree.mNodes, tree.mInitData, &Params); + } + else + doBruteForceTests<LeafFunction_CapsuleSweepCB, LeafFunction_CapsuleSweepCB>(mesh->getNbTriangles(), &Params); +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweepAA.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweepAA.cpp new file mode 100644 index 00000000..1fd6aa05 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweepAA.cpp @@ -0,0 +1,111 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +#include "GuSweepSphereTriangle.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" +#include "GuInternal.h" + +#define SWEEP_AABB_IMPL + + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + #ifdef GU_BV4_QUANTIZED_TREE + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + #endif + #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_BoxSweep_Params.h" + +namespace +{ + struct CapsuleSweepParams : BoxSweepParams + { + Capsule mLocalCapsule; + PxVec3 mCapsuleCenter; + PxVec3 mExtrusionDir; + PxU32 mEarlyExit; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_CapsuleSweep_Internal.h" +#include "GuBV4_Internal.h" + +#include "GuBV4_AABBAABBSweepTest.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#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 + +Ps::IntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParams Params; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder(1, LeafFunction_CapsuleSweepAny)(tree.mNodes, tree.mInitData, &Params); + else + processStreamRayOrdered(1, LeafFunction_CapsuleSweepClosest)(tree.mNodes, tree.mInitData, &Params); + } + else + doBruteForceTests<LeafFunction_CapsuleSweepAny, LeafFunction_CapsuleSweepClosest>(mesh->getNbTriangles(), &Params); + + return computeImpactDataT<ImpactFunctionCapsule>(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweep_Internal.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweep_Internal.h new file mode 100644 index 00000000..260ba0af --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_CapsuleSweep_Internal.h @@ -0,0 +1,434 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_CAPSULE_SWEEP_INTERNAL_H +#define GU_BV4_CAPSULE_SWEEP_INTERNAL_H + +// PT: for capsule-sweeps please refer to \\sw\physx\PhysXSDK\3.4\trunk\InternalDocumentation\GU\Sweep strategies.ppt. +// We use: +// - method 3 if the capsule is axis-aligned (SWEEP_AABB_IMPL is defined) +// - method 2 otherwise (SWEEP_AABB_IMPL is undefined) + +// PT: TODO: get rid of that one +static PX_FORCE_INLINE bool sweepSphereVSTriangle( const PxVec3& center, const float radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal, + const PxVec3& unitDir, + float& curT, bool& directHit) +{ + float currentDistance; + if(!sweepSphereVSTri(triVerts, triUnitNormal, center, radius, unitDir, currentDistance, directHit, true)) + return false; + + // PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed. + if(currentDistance > curT) + return false; + curT = currentDistance; + return true; +} + +static PX_FORCE_INLINE bool sweepSphereVSQuad( const PxVec3& center, const float radius, + const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal, + const PxVec3& unitDir, + float& curT) +{ + float currentDistance; + if(!sweepSphereVSQuad(quadVerts, quadUnitNormal, center, radius, unitDir, currentDistance)) + return false; + + // PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed. + if(currentDistance > curT) + return false; + curT = currentDistance; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ testTri( const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& N, + const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status) +{ + // PT: TODO: check the assembly here (TA34704) + PxVec3 currentTri[3]; + // PT: TODO: optimize this copy (TA34704) + currentTri[0] = p0; + currentTri[1] = p1; + currentTri[2] = p2; + + // PT: beware, culling is only ok on the sphere I think + if(rejectTriangle(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentTri, dpc0)) + return false; + + float magnitude = N.magnitude(); + if(magnitude==0.0f) + return false; + + PxVec3 triNormal = N / magnitude; + + bool DirectHit; + if(sweepSphereVSTriangle(params->mCapsuleCenter, capsuleRadius, currentTri, triNormal, unitDir, curT, DirectHit)) + { + status = true; + } + return DirectHit; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static void /*__fastcall*/ testQuad(const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, const PxVec3& N, + const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status) +{ + // PT: TODO: optimize this copy (TA34704) + PxVec3 currentQuad[4]; + currentQuad[0] = p0; + currentQuad[1] = p1; + currentQuad[2] = p2; + currentQuad[3] = p3; + + // PT: beware, culling is only ok on the sphere I think + if(rejectQuad(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentQuad, dpc0)) + return; + + float magnitude = N.magnitude(); + if(magnitude==0.0f) + return; + + PxVec3 triNormal = N / magnitude; + + if(sweepSphereVSQuad(params->mCapsuleCenter, capsuleRadius, currentQuad, triNormal, unitDir, curT)) + { + status = true; + } +} + +static PX_FORCE_INLINE float Set2(const PxVec3& p0, const PxVec3& n, const PxVec3& p) +{ + return (p-p0).dot(n); +} + +static PX_FORCE_INLINE bool sweepCapsuleVsTriangle(const CapsuleSweepParams* PX_RESTRICT params, const PxTriangle& triangle, float& t, bool isDoubleSided, PxVec3& normal) +{ + const PxVec3& unitDir = params->mLocalDir_Padded; + + // Create triangle normal + PxVec3 denormalizedNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]); + + normal = denormalizedNormal; + + // Backface culling + const bool culled = denormalizedNormal.dot(unitDir) > 0.0f; + if(culled) + { + if(!isDoubleSided) + return false; + + denormalizedNormal = -denormalizedNormal; + } + + const float capsuleRadius = params->mLocalCapsule.radius; + float curT = params->mStabbedFace.mDistance; + const float dpc0 = params->mCapsuleCenter.dot(unitDir); + + bool status = false; + + // Extrude mesh on the fly + const PxVec3 p0 = triangle.verts[0] - params->mExtrusionDir; + const PxVec3 p1 = triangle.verts[1+culled] - params->mExtrusionDir; + const PxVec3 p2 = triangle.verts[2-culled] - params->mExtrusionDir; + + const PxVec3 p0b = triangle.verts[0] + params->mExtrusionDir; + const PxVec3 p1b = triangle.verts[1+culled] + params->mExtrusionDir; + const PxVec3 p2b = triangle.verts[2-culled] + params->mExtrusionDir; + + const float extrusionSign = denormalizedNormal.dot(params->mExtrusionDir); + + const PxVec3 p2b_p1b = p2b - p1b; + const PxVec3 p0b_p1b = p0b - p1b; + const PxVec3 p2b_p2 = 2.0f * params->mExtrusionDir; + const PxVec3 p1_p1b = -p2b_p2; + + const PxVec3 N1 = p2b_p1b.cross(p0b_p1b); + const float dp0 = Set2(p0b, N1, params->mCapsuleCenter); + + const PxVec3 N2 = (p2 - p1).cross(p0 - p1); + const float dp1 = -Set2(p0, N2, params->mCapsuleCenter); + + bool directHit; + if(extrusionSign >= 0.0f) + directHit = testTri(params, p0b, p1b, p2b, N1, unitDir, capsuleRadius, dpc0, curT, status); + else + directHit = testTri(params, p0, p1, p2, N2, unitDir, capsuleRadius, dpc0, curT, status); + + const PxVec3 N3 = p2b_p1b.cross(p1_p1b); + const float dp2 = -Set2(p1, N3, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N3.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p1, p1b, p2, p2b, N3, unitDir, capsuleRadius, dpc0, curT, status); + } + + const PxVec3 N5 = p2b_p2.cross(p0 - p2); + const float dp3 = -Set2(p0, N5, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N5.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p2, p2b, p0, p0b, N5, unitDir, capsuleRadius, dpc0, curT, status); + } + + const PxVec3 N7 = p1_p1b.cross(p0b_p1b); + const float dp4 = -Set2(p0b, N7, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N7.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p0, p0b, p1, p1b, N7, unitDir, capsuleRadius, dpc0, curT, status); + } + + if(1) + { + bool originInside = true; + if(extrusionSign<0.0f) + { + if(dp0<0.0f || dp1<0.0f || dp2<0.0f || dp3<0.0f || dp4<0.0f) + originInside = false; + } + else + { + if(dp0>0.0f || dp1>0.0f || dp2>0.0f || dp3>0.0f || dp4>0.0f) + originInside = false; + } + if(originInside) + { + t = 0.0f; + return true; + } + } + + if(!status) + return false; // We didn't touch any triangle + + t = curT; + + return true; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triCapsuleSweep(CapsuleSweepParams* 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]; + + const PxTriangle Tri(p0, p1, p2); // PT: TODO: check calls to empty ctor/dtor here (TA34704) + + const bool isDoubleSided = params->mBackfaceCulling==0; + + float Dist; + PxVec3 denormalizedNormal; + if(sweepCapsuleVsTriangle(params, Tri, Dist, isDoubleSided, denormalizedNormal)) + { + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal alignmentValue = computeAlignmentValue(denormalizedNormal, params->mLocalDir_Padded); + + if(keepTriangle(Dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist, distEpsilon)) + { + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = primIndex; + + params->mP0 = p0; + params->mP1 = p1; + params->mP2 = p2; + + params->mBestDistance = PxMin(params->mBestDistance, Dist); // exact lower bound + params->mBestAlignmentValue = alignmentValue; + params->mBestTriNormal = denormalizedNormal; + + 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; +} + +#include "GuDistanceSegmentTriangleSIMD.h" + +namespace +{ +class LeafFunction_CapsuleSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triCapsuleSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_CapsuleSweepAny +{ +public: + + static PX_FORCE_INLINE Ps::IntBool doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triCapsuleSweep(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class ImpactFunctionCapsule +{ +public: + static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Capsule& capsule, const PxVec3& dir, const PxReal t, const TrianglePadded& triangle) + { + const PxVec3 delta = dir * t; + const Vec3p P0 = capsule.p0 + delta; + const Vec3p P1 = capsule.p1 + delta; + Vec3V pointOnSeg, pointOnTri; + distanceSegmentTriangleSquared( + // PT: we use Vec3p so it is safe to V4LoadU P0 and P1 + V3LoadU_SafeReadW(P0), V3LoadU_SafeReadW(P1), + // PT: we use TrianglePadded so it is safe to V4LoadU the triangle vertices + V3LoadU_SafeReadW(triangle.verts[0]), V3LoadU_SafeReadW(triangle.verts[1]), V3LoadU_SafeReadW(triangle.verts[2]), + pointOnSeg, pointOnTri); + + PxVec3 localImpactPos, tmp; + V3StoreU(pointOnTri, localImpactPos); + V3StoreU(pointOnSeg, tmp); + + // PT: TODO: refactor with computeSphereTriImpactData (TA34704) + PxVec3 localImpactNormal = tmp - localImpactPos; + const float M = localImpactNormal.magnitude(); + if(M<1e-3f) + { + localImpactNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]); + localImpactNormal.normalize(); + } + else + localImpactNormal /= M; + + impactPos = localImpactPos; + impactNormal = localImpactNormal; + } +}; +} + +static void computeBoxAroundCapsule(const Capsule& capsule, Box& box, PxVec3& extrusionDir) +{ + // Box center = center of the two capsule's endpoints + box.center = capsule.computeCenter(); + + extrusionDir = (capsule.p0 - capsule.p1)*0.5f; + const PxF32 d = extrusionDir.magnitude(); + + // Box extents + box.extents.x = capsule.radius + d; + box.extents.y = capsule.radius; + box.extents.z = capsule.radius; + + // Box orientation + if(d==0.0f) + { + box.rot = PxMat33(PxIdentity); + } + else + { + PxVec3 dir, right, up; + Ps::computeBasis(capsule.p0, capsule.p1, dir, right, up); + box.setAxes(dir, right, up); + } +} + +template<class ParamsT> +static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + params->mBestAlignmentValue = 2.0f; + params->mBestDistance = maxDist + GU_EPSILON_SAME_DISTANCE; + params->mMaxDist = maxDist; + + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + params->mLocalCapsule = capsule; + + Box localBox; + computeBoxAroundCapsule(capsule, localBox, params->mExtrusionDir); + + params->mCapsuleCenter = localBox.center; + + const PxVec3& localDir = dir; + +#ifdef SWEEP_AABB_IMPL + const PxVec3& localP0 = params->mLocalCapsule.p0; + const PxVec3& localP1 = params->mLocalCapsule.p1; + const PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + const PxVec3 sweepExtents = PxVec3(params->mLocalCapsule.radius) + (localP0-localP1).abs()*0.5f; + + #ifndef GU_BV4_USE_SLABS + params->mLocalDir_PaddedAligned = localDir; + #endif + params->mOrigin_Padded = sweepOrigin; + + const Box aabb(sweepOrigin, sweepExtents, PxMat33(PxIdentity)); + prepareSweepData(aabb, localDir, maxDist, params); // PT: TODO: optimize this call for idt rotation (TA34704) + + #ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, sweepOrigin, localDir); + #endif +#else + prepareSweepData(localBox, localDir, maxDist, params); +#endif +} + +#endif // GU_BV4_CAPSULE_SWEEP_INTERNAL_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Common.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Common.h new file mode 100644 index 00000000..2596785f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Common.h @@ -0,0 +1,437 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 Guard +#ifndef GU_BV4_COMMON_H +#define GU_BV4_COMMON_H + +#include "foundation/PxMat44.h" +#include "GuBox.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuSIMDHelpers.h" + +#define BV4_ALIGN16(x) PX_ALIGN_PREFIX(16) x PX_ALIGN_SUFFIX(16) + +namespace physx +{ +namespace Gu +{ + enum QueryModifierFlag + { + QUERY_MODIFIER_ANY_HIT = (1<<0), + QUERY_MODIFIER_DOUBLE_SIDED = (1<<1), + QUERY_MODIFIER_MESH_BOTH_SIDES = (1<<2) + }; + + template<class ParamsT> + PX_FORCE_INLINE void setupParamsFlags(ParamsT* PX_RESTRICT params, PxU32 flags) + { + params->mBackfaceCulling = (flags & (QUERY_MODIFIER_DOUBLE_SIDED|QUERY_MODIFIER_MESH_BOTH_SIDES)) ? 0 : 1u; + params->mEarlyExit = flags & QUERY_MODIFIER_ANY_HIT; + } + + enum HitCode + { + HIT_NONE = 0, //!< No hit + HIT_CONTINUE = 1, //!< Hit found, but keep looking for closer one + HIT_EXIT = 2 //!< Hit found, you can early-exit (raycast any) + }; + + class RaycastHitInternal : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE RaycastHitInternal() {} + PX_FORCE_INLINE ~RaycastHitInternal() {} + + float mDistance; + PxU32 mTriangleID; + }; + + class SweepHit : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE SweepHit() {} + PX_FORCE_INLINE ~SweepHit() {} + + PxU32 mTriangleID; //!< Index of touched face + float mDistance; //!< Impact distance + + PxVec3 mPos; + PxVec3 mNormal; + }; + + typedef HitCode (*MeshRayCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, float dist, float u, float v); + typedef bool (*MeshOverlapCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices); + typedef bool (*MeshSweepCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist); + typedef bool (*SweepUnlimitedCallback) (void* userData, const SweepHit& hit); + + template<class ParamsT> + PX_FORCE_INLINE void reportUnlimitedCallbackHit(ParamsT* PX_RESTRICT params, const SweepHit& hit) + { + // PT: we can't reuse the MeshSweepCallback here since it's designed for doing the sweep test inside the callback + // (in the user's code) rather than inside the traversal code. So we use the SweepUnlimitedCallback instead to + // report the already fully computed hit to users. + // PT: TODO: this may not be very efficient, since computing the full hit is expensive. If we use this codepath + // to implement the Epic Tweak, the resulting code will not be optimal. + (params->mCallback)(params->mUserData, hit); + + // PT: the existing traversal code already shrunk the ray. For real "sweep all" calls we must undo that by reseting the max dist. + // (params->mStabbedFace.mDistance is used in computeImpactDataX code, so we need it before that point - we can't simply avoid + // modifying this value before this point). + if(!params->mNodeSorting) + params->mStabbedFace.mDistance = params->mMaxDist; + } + + PX_FORCE_INLINE void invertPRMatrix(PxMat44* PX_RESTRICT dest, const PxMat44* PX_RESTRICT src) + { + const float m30 = src->column3.x; + const float m31 = src->column3.y; + const float m32 = src->column3.z; + + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + dest->column0.x = m00; + dest->column1.x = m01; + dest->column2.x = m02; + dest->column3.x = -(m30*m00 + m31*m01 + m32*m02); + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + dest->column0.y = m10; + dest->column1.y = m11; + dest->column2.y = m12; + dest->column3.y = -(m30*m10 + m31*m11 + m32*m12); + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + dest->column0.z = m20; + dest->column1.z = m21; + dest->column2.z = m22; + dest->column3.z = -(m30*m20 + m31*m21 + m32*m22); + + dest->column0.w = 0.0f; + dest->column1.w = 0.0f; + dest->column2.w = 0.0f; + dest->column3.w = 1.0f; + } + + PX_FORCE_INLINE void invertBoxMatrix(PxMat33& m, PxVec3& t, const Gu::Box& box) + { + const float m30 = box.center.x; + const float m31 = box.center.y; + const float m32 = box.center.z; + + const float m00 = box.rot.column0.x; + const float m01 = box.rot.column0.y; + const float m02 = box.rot.column0.z; + + m.column0.x = m00; + m.column1.x = m01; + m.column2.x = m02; + t.x = -(m30*m00 + m31*m01 + m32*m02); + + const float m10 = box.rot.column1.x; + const float m11 = box.rot.column1.y; + const float m12 = box.rot.column1.z; + + m.column0.y = m10; + m.column1.y = m11; + m.column2.y = m12; + t.y = -(m30*m10 + m31*m11 + m32*m12); + + const float m20 = box.rot.column2.x; + const float m21 = box.rot.column2.y; + const float m22 = box.rot.column2.z; + + m.column0.z = m20; + m.column1.z = m21; + m.column2.z = m22; + t.z = -(m30*m20 + m31*m21 + m32*m22); + } + +#ifdef GU_BV4_USE_SLABS + // PT: this class moved here to make things compile with pedantic compilers. + struct BVDataSwizzled : public physx::shdfnd::UserAllocated + { + #ifdef GU_BV4_QUANTIZED_TREE + struct Data + { + PxI16 mMin; //!< Quantized min + PxI16 mMax; //!< Quantized max + }; + + Data mX[4]; + Data mY[4]; + Data mZ[4]; + #else + float mMinX[4]; + float mMinY[4]; + float mMinZ[4]; + float mMaxX[4]; + float mMaxY[4]; + float mMaxZ[4]; + #endif + PxU32 mData[4]; + + PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; } + PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; } + PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; } + }; +#else + #define SSE_CONST4(name, val) static const __declspec(align(16)) PxU32 name[4] = { (val), (val), (val), (val) } + #define SSE_CONST(name) *(const __m128i *)&name + #define SSE_CONSTF(name) *(const __m128 *)&name +#endif + + PX_FORCE_INLINE PxU32 getNbPrimitives(PxU32& primIndex) + { + PxU32 NbToGo = (primIndex & 15)-1; + primIndex>>=4; + return NbToGo; + } + + template<class ParamsT> + PX_FORCE_INLINE void setupMeshPointersAndQuantizedCoeffs(ParamsT* PX_RESTRICT params, const SourceMesh* PX_RESTRICT mesh, const BV4Tree* PX_RESTRICT tree) + { + params->mTris32 = mesh->getTris32(); + params->mTris16 = mesh->getTris16(); + params->mVerts = mesh->getVerts(); + +#ifdef GU_BV4_QUANTIZED_TREE + V4StoreA_Safe(V4LoadU_Safe(&tree->mCenterOrMinCoeff.x), ¶ms->mCenterOrMinCoeff_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(&tree->mExtentsOrMaxCoeff.x), ¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); +#else + PX_UNUSED(tree); +#endif + } + + PX_FORCE_INLINE void rotateBox(Gu::Box& dst, const PxMat44& m, const Gu::Box& src) + { + // The extents remain constant + dst.extents = src.extents; + // The center gets x-formed + dst.center = m.transform(src.center); + // Combine rotations + // PT: TODO: revisit.. this is awkward... grab 3x3 part of 4x4 matrix (TA34704) + const PxMat33 tmp( PxVec3(m.column0.x, m.column0.y, m.column0.z), + PxVec3(m.column1.x, m.column1.y, m.column1.z), + PxVec3(m.column2.x, m.column2.y, m.column2.z)); + dst.rot = tmp * src.rot; + } + + PX_FORCE_INLINE PxVec3 inverseRotate(const PxMat44* PX_RESTRICT src, const PxVec3& p) + { + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + return PxVec3( m00*p.x + m01*p.y + m02*p.z, + m10*p.x + m11*p.y + m12*p.z, + m20*p.x + m21*p.y + m22*p.z); + } + + PX_FORCE_INLINE PxVec3 inverseTransform(const PxMat44* PX_RESTRICT src, const PxVec3& p) + { + const float m30 = src->column3.x; + const float m31 = src->column3.y; + const float m32 = src->column3.z; + + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + return PxVec3( m00*p.x + m01*p.y + m02*p.z -(m30*m00 + m31*m01 + m32*m02), + m10*p.x + m11*p.y + m12*p.z -(m30*m10 + m31*m11 + m32*m12), + m20*p.x + m21*p.y + m22*p.z -(m30*m20 + m31*m21 + m32*m22)); + } + + PX_FORCE_INLINE void computeLocalRay(PxVec3& localDir, PxVec3& localOrigin, const PxVec3& dir, const PxVec3& origin, const PxMat44* PX_RESTRICT worldm_Aligned) + { + if(worldm_Aligned) + { + localDir = inverseRotate(worldm_Aligned, dir); + localOrigin = inverseTransform(worldm_Aligned, origin); + } + else + { + localDir = dir; + localOrigin = origin; + } + } + + PX_FORCE_INLINE void computeLocalSphere(float& radius2, PxVec3& local_center, const Sphere& sphere, const PxMat44* PX_RESTRICT worldm_Aligned) + { + radius2 = sphere.radius * sphere.radius; + if(worldm_Aligned) + { + local_center = inverseTransform(worldm_Aligned, sphere.center); + } + else + { + local_center = sphere.center; + } + } + + PX_FORCE_INLINE void computeLocalCapsule(Capsule& localCapsule, const Capsule& capsule, const PxMat44* PX_RESTRICT worldm_Aligned) + { + localCapsule.radius = capsule.radius; + if(worldm_Aligned) + { + localCapsule.p0 = inverseTransform(worldm_Aligned, capsule.p0); + localCapsule.p1 = inverseTransform(worldm_Aligned, capsule.p1); + } + else + { + localCapsule.p0 = capsule.p0; + localCapsule.p1 = capsule.p1; + } + } + + PX_FORCE_INLINE void computeLocalBox(Gu::Box& dst, const Gu::Box& src, const PxMat44* PX_RESTRICT worldm_Aligned) + { + if(worldm_Aligned) + { + PxMat44 invWorldM; + invertPRMatrix(&invWorldM, worldm_Aligned); + + rotateBox(dst, invWorldM, src); + } + else + { + dst = src; // PT: TODO: check asm for operator= (TA34704) + } + } + + template<class ImpactFunctionT, class ShapeT, class ParamsT> + static PX_FORCE_INLINE bool computeImpactDataT(const ShapeT& shape, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const ParamsT* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm, 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: we shouldn't compute impact in world space, and in fact moving this to local space is necessary if we want to reuse this for box-sweeps (TA34704) + TrianglePadded WP; + if(worldm) + { + WP.verts[0] = worldm->transform(params->mP0); + WP.verts[1] = worldm->transform(params->mP1); + WP.verts[2] = worldm->transform(params->mP2); + } + else + { + WP.verts[0] = params->mP0; + WP.verts[1] = params->mP1; + WP.verts[2] = params->mP2; + } + + PxVec3 impactNormal; + ImpactFunctionT::computeImpact(hit->mPos, impactNormal, shape, dir, t, WP); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(impactNormal, meshBothSides, isDoubleSided, params->mBestTriNormal, dir)) + impactNormal = -impactNormal; + + hit->mNormal = impactNormal; + } + } + return true; + } + + // PT: we don't create a structure for small meshes with just a few triangles. We use brute-force tests on these. + template<class LeafFunction_AnyT, class LeafFunction_ClosestT, class ParamsT> + static void doBruteForceTests(PxU32 nbTris, ParamsT* PX_RESTRICT params) + { + PX_ASSERT(nbTris<16); + if(params->mEarlyExit) + LeafFunction_AnyT::doLeafTest(params, nbTris); + else + LeafFunction_ClosestT::doLeafTest(params, nbTris); + } + +#if PX_INTEL_FAMILY +#ifndef GU_BV4_USE_SLABS + template<class ParamsT> + PX_FORCE_INLINE void setupRayData(ParamsT* PX_RESTRICT params, float max_dist, const PxVec3& origin, const PxVec3& dir) + { + const float Half = 0.5f*max_dist; + const FloatV HalfV = FLoad(Half); + const Vec4V DataV = V4Scale(V4LoadU(&dir.x), HalfV); + const Vec4V Data2V = V4Add(V4LoadU(&origin.x), DataV); + const PxU32 MaskI = 0x7fffffff; + const Vec4V FDirV = _mm_and_ps(_mm_load1_ps((float*)&MaskI), DataV); + V4StoreA_Safe(DataV, ¶ms->mData_PaddedAligned.x); + V4StoreA_Safe(Data2V, ¶ms->mData2_PaddedAligned.x); + V4StoreA_Safe(FDirV, ¶ms->mFDir_PaddedAligned.x); + } +#endif +#endif + +} +} + +#endif // GU_BV4_COMMON_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Internal.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Internal.h new file mode 100644 index 00000000..07df2109 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Internal.h @@ -0,0 +1,265 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_INTERNAL_H +#define GU_BV4_INTERNAL_H + +#include "CmPhysXCommon.h" +#include "PsFPU.h" + + static PX_FORCE_INLINE PxU32 getChildOffset(PxU32 data) { return data>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + static PX_FORCE_INLINE PxU32 getChildType(PxU32 data) { return (data>>1)&3; } + + // PT: the general structure is that there is a root "process stream" function which is the entry point for the query. + // It then calls "process node" functions for each traversed node, except for the Slabs-based raycast versions that deal + // with 4 nodes at a time within the "process stream" function itself. When a leaf is found, "doLeafTest" functors + // passed to the "process stream" entry point are called. +#ifdef GU_BV4_USE_SLABS + #define processStreamNoOrder BV4_ProcessStreamSwizzledNoOrder + #define processStreamOrdered BV4_ProcessStreamSwizzledOrdered + #define processStreamRayNoOrder(a, b) BV4_ProcessStreamKajiyaNoOrder<a, b> + #define processStreamRayOrdered(a, b) BV4_ProcessStreamKajiyaOrdered<a, b> +#else + #define processStreamNoOrder BV4_ProcessStreamNoOrder + #define processStreamOrdered BV4_ProcessStreamOrdered2 + #define processStreamRayNoOrder(a, b) BV4_ProcessStreamNoOrder<b> + #define processStreamRayOrdered(a, b) BV4_ProcessStreamOrdered2<b> +#endif + +#ifndef GU_BV4_USE_SLABS +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + // PT: see http://www.codercorner.com/blog/?p=734 + + // PT: TODO: refactor with dup in bucket pruner (TA34704) + PX_FORCE_INLINE PxU32 computeDirMask(const PxVec3& dir) + { + // XYZ + // --- + // --+ + // -+- + // -++ + // +-- + // +-+ + // ++- + // +++ + + const PxU32 X = PX_IR(dir.x)>>31; + const PxU32 Y = PX_IR(dir.y)>>31; + const PxU32 Z = PX_IR(dir.z)>>31; + const PxU32 bitIndex = Z|(Y<<1)|(X<<2); + return 1u<<bitIndex; + } + + // 0 0 0 PP PN NP NN 0 1 2 3 + // 0 0 1 PP PN NN NP 0 1 3 2 + // 0 1 0 PN PP NP NN 1 0 2 3 + // 0 1 1 PN PP NN NP 1 0 3 2 + // 1 0 0 NP NN PP PN 2 3 0 1 + // 1 0 1 NN NP PP PN 3 2 0 1 + // 1 1 0 NP NN PN PP 2 3 1 0 + // 1 1 1 NN NP PN PP 3 2 1 0 + static const PxU8 order[] = { + 0,1,2,3, + 0,1,3,2, + 1,0,2,3, + 1,0,3,2, + 2,3,0,1, + 3,2,0,1, + 2,3,1,0, + 3,2,1,0, + }; + + PX_FORCE_INLINE PxU32 decodePNS(const BVDataPacked* PX_RESTRICT node, const PxU32 dirMask) + { + const PxU32 bit0 = (node[0].decodePNSNoShift() & dirMask) ? 1u : 0; + const PxU32 bit1 = (node[1].decodePNSNoShift() & dirMask) ? 1u : 0; + const PxU32 bit2 = (node[2].decodePNSNoShift() & dirMask) ? 1u : 0; //### potentially reads past the end of the stream here! + return bit2|(bit1<<1)|(bit0<<2); + } +#endif // GU_BV4_PRECOMPUTED_NODE_SORT + + #define PNS_BLOCK(i, a, b, c, d) \ + case i: \ + { \ + if(code & (1<<a)) { stack[nb++] = node[a].getChildData(); } \ + if(code & (1<<b)) { stack[nb++] = node[b].getChildData(); } \ + if(code & (1<<c)) { stack[nb++] = node[c].getChildData(); } \ + if(code & (1<<d)) { stack[nb++] = node[d].getChildData(); } \ + }break; + + #define PNS_BLOCK1(i, a, b, c, d) \ + case i: \ + { \ + stack[nb] = node[a].getChildData(); nb += (code & (1<<a))?1:0; \ + stack[nb] = node[b].getChildData(); nb += (code & (1<<b))?1:0; \ + stack[nb] = node[c].getChildData(); nb += (code & (1<<c))?1:0; \ + stack[nb] = node[d].getChildData(); nb += (code & (1<<d))?1:0; \ + }break; + + #define PNS_BLOCK2(a, b, c, d) { \ + if(code & (1<<a)) { stack[nb++] = node[a].getChildData(); } \ + if(code & (1<<b)) { stack[nb++] = node[b].getChildData(); } \ + if(code & (1<<c)) { stack[nb++] = node[c].getChildData(); } \ + if(code & (1<<d)) { stack[nb++] = node[d].getChildData(); } } \ + +#if PX_INTEL_FAMILY + template<class LeafTestT, class ParamsT> + static Ps::IntBool BV4_ProcessStreamNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder<LeafTestT, 3>(stack, nb, node, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder<LeafTestT, 2>(stack, nb, node, params)) + return 1; + if(BV4_ProcessNodeNoOrder<LeafTestT, 1>(stack, nb, node, params)) + return 1; + if(BV4_ProcessNodeNoOrder<LeafTestT, 0>(stack, nb, node, params)) + return 1; + + }while(nb); + + return 0; + } + + template<class LeafTestT, class ParamsT> + static void BV4_ProcessStreamOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32 dirMask = computeDirMask(params->mLocalDir)<<3; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const PxU8* PX_RESTRICT ord = order + decodePNS(node, dirMask)*4; + const PxU32 limit = 2 + getChildType(childData); + + BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[0], limit); + BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[1], limit); + BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[2], limit); + BV4_ProcessNodeOrdered<LeafTestT>(stack, nb, node, params, ord[3], limit); + }while(Nb); + } + + // Alternative, experimental version using PNS + template<class LeafTestT, class ParamsT> + static void BV4_ProcessStreamOrdered2(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; + const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; + const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<<bitIndex; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + const PxU32 nodeType = getChildType(childData); + + PxU32 code = 0; + BV4_ProcessNodeOrdered2<LeafTestT, 0>(code, node, params); + BV4_ProcessNodeOrdered2<LeafTestT, 1>(code, node, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2<LeafTestT, 2>(code, node, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2<LeafTestT, 3>(code, node, params); + + if(code) + { + // PT: TODO: check which implementation is best on each platform (TA34704) +#define FOURTH_TEST // Version avoids computing the PNS index, and also avoids all non-constant shifts. Full of branches though. Fastest on Win32. +#ifdef FOURTH_TEST + { + if(node[0].decodePNSNoShift() & dirMask) // Bit2 + { + if(node[1].decodePNSNoShift() & dirMask) // Bit1 + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(3,2,1,0) // 7 + else + PNS_BLOCK2(2,3,1,0) // 6 + } + else + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(3,2,0,1) // 5 + else + PNS_BLOCK2(2,3,0,1) // 4 + } + } + else + { + if(node[1].decodePNSNoShift() & dirMask) // Bit1 + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(1,0,3,2) // 3 + else + PNS_BLOCK2(1,0,2,3) // 2 + } + else + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(0,1,3,2) // 1 + else + PNS_BLOCK2(0,1,2,3) // 0 + } + } + } +#endif + } + }while(nb); + } +#endif // PX_INTEL_FAMILY +#endif // GU_BV4_USE_SLABS + +#endif // GU_BV4_INTERNAL_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_OBBSweep.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_OBBSweep.cpp new file mode 100644 index 00000000..c578b359 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_OBBSweep.cpp @@ -0,0 +1,170 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; +#include "GuBV4_BoxSweep_Internal.h" + +Ps::IntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags); +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); + +// PT: TODO: optimize this (TA34704) +static PX_FORCE_INLINE void computeLocalData(Box& localBox, PxVec3& localDir, const Box& box, const PxVec3& dir, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + if(worldm_Aligned) + { + PxMat44 IWM; + invertPRMatrix(&IWM, worldm_Aligned); + + localDir = IWM.rotate(dir); + + rotateBox(localBox, IWM, box); + } + else + { + localDir = dir; + localBox = box; // PT: TODO: check asm for operator= (TA34704) + } +} + +static PX_FORCE_INLINE bool isAxisAligned(const PxVec3& axis) +{ + const PxReal minLimit = 1e-3f; + const PxReal maxLimit = 1.0f - 1e-3f; + + const PxReal absX = PxAbs(axis.x); + if(absX>minLimit && absX<maxLimit) + return false; + + const PxReal absY = PxAbs(axis.y); + if(absY>minLimit && absY<maxLimit) + return false; + + const PxReal absZ = PxAbs(axis.z); + if(absZ>minLimit && absZ<maxLimit) + return false; + + return true; +} + +static PX_FORCE_INLINE bool isAABB(const Box& box) +{ + if(!isAxisAligned(box.rot.column0)) + return false; + if(!isAxisAligned(box.rot.column1)) + return false; + if(!isAxisAligned(box.rot.column2)) + return false; + return true; +} + +Ps::IntBool BV4_BoxSweepSingle(const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + Box localBox; + PxVec3 localDir; + computeLocalData(localBox, localDir, box, dir, worldm_Aligned); + + Ps::IntBool Status; + if(isAABB(localBox)) + Status = Sweep_AABB_BV4(localBox, localDir, maxDist, tree, hit, flags); + else + Status = Sweep_OBB_BV4(localBox, localDir, maxDist, tree, hit, flags); + if(Status && worldm_Aligned) + { + // Move to world space + // PT: TODO: optimize (TA34704) + hit->mPos = worldm_Aligned->transform(hit->mPos); + hit->mNormal = worldm_Aligned->rotate(hit->mNormal); + } + return Status; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_BoxSweepCB(const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + Box localBox; + PxVec3 localDir; + computeLocalData(localBox, localDir, box, dir, worldm_Aligned); + + if(isAABB(localBox)) + Sweep_AABB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting); + else + Sweep_OBB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting); +} + + +// PT: this generic sweep uses an OBB because this is the most versatile volume, but it does not mean this function is +// a "box sweep function" per-se. In fact it could be used all alone to implement all sweeps in the SDK (but that would +// have an impact on performance). +// +// So the idea here is simply to provide and use a generic function for everything that the BV4 code does not support directly. +// In particular this should be used: +// - for convex sweeps (where the OBB is the box around the swept convex) +// - for non-trivial sphere/capsule/box sweeps where mesh scaling or inflation +// +// By design we don't do leaf tests inside the BV4 traversal code here (because we don't support them, e.g. convex +// sweeps. If we could do them inside the BV4 traversal code, like we do for regular sweeps, then this would not be a generic +// sweep function, but instead a built-in, natively supported query). So the leaf tests are performed outside of BV4, in the +// client code, through MeshSweepCallback. This has a direct impact on the design & parameters of MeshSweepCallback. +// +// On the other hand this is used for "regular sweeps with shapes we don't natively support", i.e. SweepSingle kind of queries. +// This means that we need to support an early-exit codepath (without node-sorting) and a regular sweep single codepath (with +// node sorting) for this generic function. The leaf tests are external, but everything traversal-related should be exactly the +// same as the regular box-sweep function otherwise. +// +// As a consequence, this function is not well-suited to implement "unlimited results" kind of queries, a.k.a. "sweep all": +// +// - for regular sphere/capsule/box "sweep all" queries, the leaf tests should be internal (same as sweep single queries). This +// means the existing MeshSweepCallback can't be reused. +// +// - there is no need to support "sweep any" (it is already supported by the other sweep functions). +// +// - there may be no need for ordered traversal/node sorting/ray shrinking, since we want to return all results anyway. But this +// may not be true if the "sweep all" function is used to emulate the Epic Tweak. In that case we still want to shrink the ray +// and use node sorting. Since both versions are useful, we should probably have a bool param to enable/disable node sorting. +// +// - we are interested in all hits so we can't delay the computation of impact data (computing it only once in the end, for the +// closest hit). We actually need to compute the data for all hits, possibly within the traversal code. +void BV4_GenericSweepCB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit) +{ + const PxU32 flags = anyHit ? PxU32(QUERY_MODIFIER_ANY_HIT) : 0; + + if(isAABB(localBox)) + GenericSweep_AABB_CB(localBox, localDir, maxDist, tree, callback, userData, flags); + else + GenericSweep_OBB_CB(localBox, localDir, maxDist, tree, callback, userData, flags); +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h new file mode 100644 index 00000000..9c55cd66 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h @@ -0,0 +1,73 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H + +#ifdef GU_BV4_USE_SLABS + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } +#else + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_BoxBoxOverlap(node+i, params)) + #else + if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h new file mode 100644 index 00000000..86ea5f97 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h @@ -0,0 +1,55 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H + +#ifndef GU_BV4_USE_SLABS + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h new file mode 100644 index 00000000..7bf3285c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h @@ -0,0 +1,55 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H + +#ifndef GU_BV4_USE_SLABS + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h new file mode 100644 index 00000000..52d1dce5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h @@ -0,0 +1,74 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H + +#ifdef GU_BV4_USE_SLABS + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { +// OPC_SLABS_GET_CE(i) + OPC_SLABS_GET_CE2(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } +#else + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SphereAABBOverlap(node+i, params)) + #else + if(BV4_SphereAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h new file mode 100644 index 00000000..24dd9757 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h @@ -0,0 +1,81 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H +#define GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H + +#ifdef GU_BV4_USE_SLABS + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_Swizzled(PxU32& code, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1<<i; + } + } +#else + template<class LeafTestT, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i<limit && BV4_BoxBoxOverlap(node+i, params)) + #else + if(i<limit && BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + Stack[Nb++] = node[i].getChildData(); + } + } + + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_BoxBoxOverlap(node+i, params)) + #else + if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1<<i; + } + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h new file mode 100644 index 00000000..b3b3e90a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h @@ -0,0 +1,67 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_H +#define GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_H + +#ifndef GU_BV4_USE_SLABS + template<class LeafTestT, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i<limit && BV4_SegmentAABBOverlap(node+i, params)) + #else + if(i<limit && BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + Stack[Nb++] = node[i].getChildData(); + } + } + + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1<<i; + } + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h new file mode 100644 index 00000000..9e4f8ed1 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h @@ -0,0 +1,67 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_INFLATED_H +#define GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_INFLATED_H + +#ifndef GU_BV4_USE_SLABS + template<class LeafTestT, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i<limit && BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents, params)) + #else + if(i<limit && BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + Stack[Nb++] = node[i].getChildData(); + } + } + + template<class LeafTestT, int i, class ParamsT> + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1<<i; + } + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_ORDERED_SEGMENT_AABB_INFLATED_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Raycast.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Raycast.cpp new file mode 100644 index 00000000..83becacc --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Raycast.cpp @@ -0,0 +1,625 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY + +#include "PxQueryReport.h" +#include "GuInternal.h" + +#include "GuIntersectionRayTriangle.h" + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" + +class RaycastHitInternalUV : public RaycastHitInternal +{ + public: + PX_FORCE_INLINE RaycastHitInternalUV() {} + PX_FORCE_INLINE ~RaycastHitInternalUV() {} + + float mU, mV; +}; + +template<class T> +PX_FORCE_INLINE Ps::IntBool RayTriOverlapT(PxRaycastHit& mStabbedFace, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, const T* PX_RESTRICT params) +{ + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = params->mLocalDir_Padded.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const float det = edge1.dot(pvec); + + if(params->mBackfaceCulling) + { + if(det<GU_CULLING_EPSILON_RAY_TRIANGLE) + return 0; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = params->mOrigin_Padded - vert0; + + // Calculate U parameter and test bounds + const float u = tvec.dot(pvec); + + const PxReal enlargeCoeff = params->mGeomEpsilon*det; + const PxReal uvlimit = -enlargeCoeff; + const PxReal uvlimit2 = det + enlargeCoeff; + + if(u < uvlimit || u > uvlimit2) + return 0; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const float v = params->mLocalDir_Padded.dot(qvec); + if(v < uvlimit || (u + v) > uvlimit2) + return 0; + + // Calculate t, scale parameters, ray intersects triangle + const float d = edge2.dot(qvec); + // Det > 0 so we can early exit here + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(d<0.0f) + return 0; + + // Else go on + const float OneOverDet = 1.0f / det; + mStabbedFace.distance = d * OneOverDet; + mStabbedFace.u = u * OneOverDet; + mStabbedFace.v = v * OneOverDet; + } + else + { + if(PxAbs(det)<GU_CULLING_EPSILON_RAY_TRIANGLE) + return 0; + + const float OneOverDet = 1.0f / det; + + const PxVec3 tvec = params->mOrigin_Padded - vert0; + + const float u = tvec.dot(pvec) * OneOverDet; + if(u<-params->mGeomEpsilon || u>1.0f+params->mGeomEpsilon) + return 0; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const float v = params->mLocalDir_Padded.dot(qvec) * OneOverDet; + if(v < -params->mGeomEpsilon || (u + v) > 1.0f + params->mGeomEpsilon) + return 0; + + // Calculate t, ray intersects triangle + const float d = edge2.dot(qvec) * OneOverDet; + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(d<0.0f) + return 0; + mStabbedFace.distance = d; + mStabbedFace.u = u; + mStabbedFace.v = v; + } + return 1; +} + +#if PX_VC +#pragma warning ( disable : 4324 ) +#endif + +struct RayParams +{ +#ifdef GU_BV4_QUANTIZED_TREE + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +#endif +// Organized in the order they are accessed +#ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); +#endif + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + PxVec3 mLocalDir_Padded; + PxVec3 mOrigin_Padded; + + float mGeomEpsilon; + PxU32 mBackfaceCulling; + + RaycastHitInternalUV mStabbedFace; + PxU32 mEarlyExit; + + PxVec3 mOriginalExtents_Padded; // Added to please the slabs code + + BV4_ALIGN16(Vec3p mP0_PaddedAligned); + BV4_ALIGN16(Vec3p mP1_PaddedAligned); + BV4_ALIGN16(Vec3p mP2_PaddedAligned); +}; + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE void updateParamsAfterImpact(RayParams* PX_RESTRICT params, PxU32 primIndex, PxU32 VRef0, PxU32 VRef1, PxU32 VRef2, const PxRaycastHit& StabbedFace) +{ + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef0].x), ¶ms->mP0_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef1].x), ¶ms->mP1_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef2].x), ¶ms->mP2_PaddedAligned.x); + + params->mStabbedFace.mTriangleID = primIndex; + params->mStabbedFace.mDistance = StabbedFace.distance; + params->mStabbedFace.mU = StabbedFace.u; + params->mStabbedFace.mV = StabbedFace.v; +} + +namespace +{ +class LeafFunction_RaycastClosest +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT params, PxU32 primIndex) + { + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast<PxRaycastHit&>(buffer); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(RayTriOverlapT<RayParams>(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + if(StabbedFace.distance<params->mStabbedFace.mDistance) //### just for a corner case UT in PhysX :( + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, StabbedFace.distance, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class LeafFunction_RaycastAny +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast<PxRaycastHit&>(buffer); + if(RayTriOverlapT<RayParams>(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + if(StabbedFace.distance<params->mStabbedFace.mDistance) //### just for a corner case UT in PhysX :( + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + return 1; + } + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +static PX_FORCE_INLINE Vec4V multiply3x3V_Aligned(const Vec4V p, const PxMat44* PX_RESTRICT mat) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadA(&mat->column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column2.x), zzzV)); + return ResV; +} + +static PX_FORCE_INLINE Ps::IntBool computeImpactData(PxRaycastHit* PX_RESTRICT hit, const RayParams* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm_Aligned, PxHitFlags /*hitFlags*/) +{ + if(params->mStabbedFace.mTriangleID!=PX_INVALID_U32 /*&& !params->mEarlyExit*/) //### PhysX needs the raycast data even for "any hit" :( + { + const float u = params->mStabbedFace.mU; + const float v = params->mStabbedFace.mV; + const float d = params->mStabbedFace.mDistance; + const PxU32 id = params->mStabbedFace.mTriangleID; + hit->u = u; + hit->v = v; + hit->distance = d; + hit->faceIndex = id; + + { + const Vec4V P0V = V4LoadA_Safe(¶ms->mP0_PaddedAligned.x); + const Vec4V P1V = V4LoadA_Safe(¶ms->mP1_PaddedAligned.x); + const Vec4V P2V = V4LoadA_Safe(¶ms->mP2_PaddedAligned.x); + + const FloatV uV = FLoad(params->mStabbedFace.mU); + const FloatV vV = FLoad(params->mStabbedFace.mV); + const float w = 1.0f - params->mStabbedFace.mU - params->mStabbedFace.mV; + const FloatV wV = FLoad(w); + //pt = (1.0f - u - v)*p0 + u*p1 + v*p2; + Vec4V LocalPtV = V4Scale(P1V, uV); + LocalPtV = V4Add(LocalPtV, V4Scale(P2V, vV)); + LocalPtV = V4Add(LocalPtV, V4Scale(P0V, wV)); + + const Vec4V LocalNormalV = V4Cross(V4Sub(P0V, P1V), V4Sub(P0V, P2V)); + + BV4_ALIGN16(Vec3p tmp_PaddedAligned); + if(worldm_Aligned) + { + const Vec4V TransV = V4LoadA(&worldm_Aligned->column3.x); + V4StoreU_Safe(V4Add(multiply3x3V_Aligned(LocalPtV, worldm_Aligned), TransV), &hit->position.x); + V4StoreA_Safe(multiply3x3V_Aligned(LocalNormalV, worldm_Aligned), &tmp_PaddedAligned.x); + } + else + { + V4StoreU_Safe(LocalPtV, &hit->position.x); + V4StoreA_Safe(LocalNormalV, &tmp_PaddedAligned.x); + } + tmp_PaddedAligned.normalize(); + hit->normal = tmp_PaddedAligned; // PT: TODO: check asm here (TA34704) + } + } + return params->mStabbedFace.mTriangleID!=PX_INVALID_U32; +} + +static PX_FORCE_INLINE float clipRay(const PxVec3& ray_orig, const PxVec3& ray_dir, const LocalBounds& local_bounds) +{ + const float dpc = local_bounds.mCenter.dot(ray_dir); + const float dpMin = dpc - local_bounds.mExtentsMagnitude; + const float dpMax = dpc + local_bounds.mExtentsMagnitude; + const float dpO = ray_orig.dot(ray_dir); + const float boxLength = local_bounds.mExtentsMagnitude * 2.0f; + const float distToBox = PxMin(fabsf(dpMin - dpO), fabsf(dpMax - dpO)); + return distToBox + boxLength * 2.0f; +} + +template<class ParamsT> +static PX_FORCE_INLINE void setupRayParams(ParamsT* PX_RESTRICT params, const PxVec3& origin, const PxVec3& dir, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT world, const SourceMesh* PX_RESTRICT mesh, float maxDist, float geomEpsilon, PxU32 flags) +{ + params->mGeomEpsilon = geomEpsilon; + setupParamsFlags(params, flags); + + computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, origin, world); + + // PT: TODO: clipRay may not be needed with GU_BV4_USE_SLABS (TA34704) + const float MaxDist = clipRay(params->mOrigin_Padded, params->mLocalDir_Padded, tree->mLocalBounds); + maxDist = PxMin(maxDist, MaxDist); + params->mStabbedFace.mDistance = maxDist; + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif +} + +#include "GuBV4_Internal.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#ifndef GU_BV4_USE_SLABS +#ifdef GU_BV4_QUANTIZED_TREE + +#define NEW_VERSION + +static PX_FORCE_INLINE /*PX_NOINLINE*/ Ps::IntBool BV4_SegmentAABBOverlap(const BVDataPacked* PX_RESTRICT node, const RayParams* PX_RESTRICT params) +{ +#ifdef NEW_VERSION + SSE_CONST4(maskV, 0x7fffffff); + SSE_CONST4(maskQV, 0x0000ffff); +#else + const PxU32 maskI = 0x7fffffff; +#endif + + Vec4V centerV = V4LoadA((float*)node->mAABB.mData); +#ifdef NEW_VERSION + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV))); +#else + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), _mm_set1_epi32(0x0000ffff))); +#endif + extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16)); + centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), centerV); + +#ifdef NEW_VERSION + __m128 absDV = _mm_and_ps(DV, SSE_CONSTF(maskV)); +#else + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); +#endif + + absDV = V4Sub(V4Add(extentsV, fdirV), absDV); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + +#ifdef NEW_VERSION + __m128 absfV = _mm_and_ps(fV, SSE_CONSTF(maskV)); +#else + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); +#endif + absfV = V4Sub(fg, absfV); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + + if(test2&7) + return 0; + return 1; + } +} +#else +static PX_FORCE_INLINE /*PX_NOINLINE*/ Ps::IntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const RayParams* PX_RESTRICT params) +{ + const PxU32 maskI = 0x7fffffff; + + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4LoadU(&extents.x); + + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), V4LoadU(¢er.x)); //###center should be aligned + + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } +} +#endif +#endif + +Ps::IntBool BV4_RaycastSingle(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + RayParams Params; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder(0, LeafFunction_RaycastAny)(tree.mNodes, tree.mInitData, &Params); + else + processStreamRayOrdered(0, LeafFunction_RaycastClosest)(tree.mNodes, tree.mInitData, &Params); + } + else + doBruteForceTests<LeafFunction_RaycastAny, LeafFunction_RaycastClosest>(mesh->getNbTriangles(), &Params); + + return computeImpactData(hit, &Params, worldm_Aligned, hitFlags); +} + + + +// Callback-based version + +namespace +{ + +struct RayParamsCB : RayParams +{ + MeshRayCallback mCallback; + void* mUserData; +}; + +class LeafFunction_RaycastCB +{ +public: + static Ps::IntBool doLeafTest(RayParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + 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]; + + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast<PxRaycastHit&>(buffer); + if(RayTriOverlapT<RayParams>(StabbedFace, p0, p1, p2, params)) + { + HitCode Code = (params->mCallback)(params->mUserData, p0, p1, p2, primIndex, StabbedFace.distance, StabbedFace.u, StabbedFace.v); + if(Code==HIT_EXIT) + return 1; + + // PT: TODO: no shrinking here? (TA34704) + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB.h" + +void BV4_RaycastCB(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + //### beware, some parameters in the struct aren't used + RayParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + { + processStreamRayNoOrder(0, LeafFunction_RaycastCB)(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); +// if(Params.mEarlyExit) +// LeafFunction_BoxSweepAnyCB::doLeafTest(&Params, nbTris); +// else + LeafFunction_RaycastCB::doLeafTest(&Params, nbTris); + } +} + +// Raycast all + +namespace +{ +struct RayParamsAll : RayParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxRaycastHit* mHits; + const PxMat44* mWorld_Aligned; + PxHitFlags mHitFlags; +}; + +class LeafFunction_RaycastAll +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT p, PxU32 primIndex) + { + RayParamsAll* params = static_cast<RayParamsAll*>(p); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + PxRaycastHit& StabbedFace = params->mHits[params->mNbHits]; + if(RayTriOverlapT<RayParams>(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + + computeImpactData(&StabbedFace, params, params->mWorld_Aligned, params->mHitFlags); + + params->mNbHits++; + if(params->mNbHits==params->mMaxNbHits) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this function is not used yet, but eventually it should be +PxU32 BV4_RaycastAll(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + RayParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = maxNbHits; + Params.mHits = hits; + Params.mWorld_Aligned = worldm_Aligned; + Params.mHitFlags = hitFlags; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + { + processStreamRayNoOrder(0, LeafFunction_RaycastAll)(tree.mNodes, tree.mInitData, &Params); + } + else + { + PX_ASSERT(0); + } + return Params.mNbHits; +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs.h new file mode 100644 index 00000000..a371ea93 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs.h @@ -0,0 +1,206 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_H +#define GU_BV4_SLABS_H + +#include "PsFPU.h" +#include "GuBV4_Common.h" + +#ifdef GU_BV4_USE_SLABS + + // PT: contains code for tree-traversal using the swizzled format. + // PT: ray traversal based on Kay & Kajiya's slab intersection code, but using SIMD to do 4 ray-vs-AABB tests at a time. + // PT: other (ordered or unordered) traversals just process one node at a time, similar to the non-swizzled format. + + #define BV4_SLABS_FIX + #define BV4_SLABS_SORT + + #define PNS_BLOCK3(a, b, c, d) { \ + if(code2 & (1<<a)) { stack[nb++] = tn->getChildData(a); } \ + if(code2 & (1<<b)) { stack[nb++] = tn->getChildData(b); } \ + if(code2 & (1<<c)) { stack[nb++] = tn->getChildData(c); } \ + if(code2 & (1<<d)) { stack[nb++] = tn->getChildData(d); } } \ + + #ifdef GU_BV4_QUANTIZED_TREE + #define OPC_SLABS_GET_MIN_MAX(i) \ + const __m128i minVi = _mm_set_epi32(0, node->mZ[i].mMin, node->mY[i].mMin, node->mX[i].mMin); \ + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); \ + Vec4V minV = V4Mul(_mm_cvtepi32_ps(minVi), minCoeffV); \ + const __m128i maxVi = _mm_set_epi32(0, node->mZ[i].mMax, node->mY[i].mMax, node->mX[i].mMax); \ + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); \ + Vec4V maxV = V4Mul(_mm_cvtepi32_ps(maxVi), maxCoeffV); \ + + #define OPC_SLABS_GET_CE(i) \ + OPC_SLABS_GET_MIN_MAX(i) \ + const FloatV HalfV = FLoad(0.5f); \ + const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \ + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV); + + #define OPC_SLABS_GET_CE2(i) \ + OPC_SLABS_GET_MIN_MAX(i) \ + const Vec4V centerV = V4Add(maxV, minV); \ + const Vec4V extentsV = V4Sub(maxV, minV); + #else + #define OPC_SLABS_GET_CE(i) \ + const FloatV HalfV = FLoad(0.5f); \ + const Vec4V minV = _mm_set_ps(0.0f, node->mMinZ[i], node->mMinY[i], node->mMinX[i]); \ + const Vec4V maxV = _mm_set_ps(0.0f, node->mMaxZ[i], node->mMaxY[i], node->mMaxX[i]); \ + const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \ + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV); + + #define OPC_SLABS_GET_CE2(i) \ + const Vec4V minV = _mm_set_ps(0.0f, node->mMinZ[i], node->mMinY[i], node->mMinX[i]); \ + const Vec4V maxV = _mm_set_ps(0.0f, node->mMaxZ[i], node->mMaxY[i], node->mMaxX[i]); \ + const Vec4V centerV = V4Add(maxV, minV); \ + const Vec4V extentsV = V4Sub(maxV, minV); + #endif // GU_BV4_QUANTIZED_TREE + +#if PX_PS4 + // PT: TODO: for some reason using the intrinsics directly produces a compile error on PS4. TODO: find a better fix. + PX_FORCE_INLINE __m128i my_mm_srai_epi32(__m128i a, int count) + { + return _mm_srai_epi32(a, count); + } + + PX_FORCE_INLINE __m128i my_mm_slli_epi32(__m128i a, int count) + { + return _mm_slli_epi32(a, count); + } +#else + #define my_mm_srai_epi32 _mm_srai_epi32 + #define my_mm_slli_epi32 _mm_slli_epi32 +#endif + +#define OPC_DEQ4(part2xV, part1xV, mMember, minCoeff, maxCoeff) \ +{ \ + part2xV = V4LoadA(reinterpret_cast<const float*>(tn->mMember)); \ + part1xV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(part2xV), _mm_set1_epi32(0x0000ffff))); \ + part1xV = _mm_castsi128_ps(my_mm_srai_epi32(my_mm_slli_epi32(_mm_castps_si128(part1xV), 16), 16)); \ + part1xV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(part1xV)), minCoeff); \ + part2xV = _mm_castsi128_ps(my_mm_srai_epi32(_mm_castps_si128(part2xV), 16)); \ + part2xV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(part2xV)), maxCoeff); \ +} + +#define SLABS_INIT\ + Vec4V maxT4 = V4Load(params->mStabbedFace.mDistance);\ + const Vec4V rayP = V4LoadU_Safe(¶ms->mOrigin_Padded.x);\ + Vec4V rayD = V4LoadU_Safe(¶ms->mLocalDir_Padded.x);\ + const VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask);\ + const Vec4V rayDAbs = V4Abs(rayD);\ + Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4))));\ + rayD = rayInvD;\ + rayInvD = V4RecipFast(rayInvD);\ + rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos));\ + const Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes);\ + const Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD);\ + const Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD);\ + const Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD);\ + const Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD);\ + const Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD);\ + const Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD); + +#define SLABS_TEST\ + const Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX);\ + const Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY);\ + const Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ);\ + const Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX);\ + const Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY);\ + const Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ);\ + const Vec4V tminxa = V4Min(tminxa0, tmaxxa0);\ + const Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0);\ + const Vec4V tminya = V4Min(tminya0, tmaxya0);\ + const Vec4V tmaxya = V4Max(tminya0, tmaxya0);\ + const Vec4V tminza = V4Min(tminza0, tmaxza0);\ + const Vec4V tmaxza = V4Max(tminza0, tmaxza0);\ + const Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza);\ + const Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza);\ + + #define SLABS_TEST2\ + __m128 ignore4a = _mm_cmpgt_ps(epsFloat4, minOfFarsa); /* if tfar is negative, ignore since its a ray, not a line */\ + ignore4a = _mm_or_ps(ignore4a, _mm_cmpgt_ps(maxOfNeasa, maxT4)); /* if tnear is over maxT, ignore this result */\ + __m128 resa4 = _mm_cmpgt_ps(maxOfNeasa, minOfFarsa); /* if 1 => fail */\ + resa4 = _mm_or_ps(resa4, ignore4a);\ + const int code = _mm_movemask_ps(resa4);\ + if(code==15)\ + continue; + +#define SLABS_PNS \ + if(code2) \ + { \ + if(tn->decodePNSNoShift(0) & dirMask) \ + { \ + if(tn->decodePNSNoShift(1) & dirMask) \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(3,2,1,0) \ + else \ + PNS_BLOCK3(2,3,1,0) \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(3,2,0,1) \ + else \ + PNS_BLOCK3(2,3,0,1) \ + } \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(1) & dirMask) \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(1,0,3,2) \ + else \ + PNS_BLOCK3(1,0,2,3) \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(0,1,3,2) \ + else \ + PNS_BLOCK3(0,1,2,3) \ + } \ + } \ + } + +#if PX_INTEL_FAMILY +namespace +{ + const VecU32V signMask = U4LoadXYZW((PxU32(1)<<31), (PxU32(1)<<31), (PxU32(1)<<31), (PxU32(1)<<31)); + const Vec4V epsFloat4 = V4Load(1e-9f); + const Vec4V zeroes = V4Zero(); + const Vec4V twos = V4Load(2.0f); + const Vec4V epsInflateFloat4 = V4Load(1e-7f); +} +#endif // PX_INTEL_FAMILY + +#endif // GU_BV4_USE_SLABS + +#endif // GU_BV4_SLABS_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h new file mode 100644 index 00000000..45f4e4a9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h @@ -0,0 +1,136 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_KAJIYA_NO_ORDER_H +#define GU_BV4_SLABS_KAJIYA_NO_ORDER_H + + // Kajiya, no sort + template<int inflateT, class LeafTestT, class ParamsT> + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + +#ifdef GU_BV4_QUANTIZED_TREE + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); +#endif + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node); + +#ifdef GU_BV4_QUANTIZED_TREE + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) +#else + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); +#endif + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + +#define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \ + return 1; \ + } \ + else \ + stack[nb++] = tn->getChildData(x);} + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } +#undef DO_LEAF_TEST + +#endif // GU_BV4_SLABS_KAJIYA_NO_ORDER_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h new file mode 100644 index 00000000..4bdcee3a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h @@ -0,0 +1,240 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_KAJIYA_ORDERED_H +#define GU_BV4_SLABS_KAJIYA_ORDERED_H + + // Kajiya + PNS + template<const int inflateT, class LeafTestT, class ParamsT> + static void BV4_ProcessStreamKajiyaOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast<const PxU32*>(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<<bitIndex; +#endif + +#ifdef BV4_SLABS_FIX + BV4_ALIGN16(float distances4[4]); +#endif + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + +#ifdef GU_BV4_QUANTIZED_TREE + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); +#endif + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node); + +#ifdef GU_BV4_QUANTIZED_TREE + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) +#else + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); +#endif + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + #ifdef BV4_SLABS_FIX + // PT: for some unknown reason the PS4/Linux/OSX compilers fail to understand this version +/* #define DO_LEAF_TEST(x) \ + { \ + if(!inflateT) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<<x; \ + } \ + } \ + else \ + { \ + if(distances4[x]<params->mStabbedFace.mDistance) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<<x; \ + } \ + } \ + } \ + }*/ + + // PT: TODO: check that this version compiles to the same code as above. Redo benchmarks. + #define DO_LEAF_TEST(x) \ + { \ + if(!inflateT || distances4[x]<params->mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<<x; \ + } \ + } \ + } + + #else + #define DO_LEAF_TEST(x) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<<x; \ + } \ + } + #endif + PxU32 code2 = 0; + const PxU32 nodeType = getChildType(childData); + + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + #define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + stack[nb++] = tn->getChildData(x); \ + }} + + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } +#undef DO_LEAF_TEST + +#endif // GU_BV4_SLABS_KAJIYA_ORDERED_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h new file mode 100644 index 00000000..a7717d7c --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h @@ -0,0 +1,66 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_SWIZZLED_NO_ORDER_H +#define GU_BV4_SLABS_SWIZZLED_NO_ORDER_H + + // Generic, no sort + template<class LeafTestT, class ParamsT> + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 3>(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 2>(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 1>(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_Swizzled<LeafTestT, 0>(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + } + +#endif // GU_BV4_SLABS_SWIZZLED_NO_ORDER_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h new file mode 100644 index 00000000..4be851e1 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h @@ -0,0 +1,74 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_SWIZZLED_ORDERED_H +#define GU_BV4_SLABS_SWIZZLED_ORDERED_H + + // Generic + PNS + template<class LeafTestT, class ParamsT> + static void BV4_ProcessStreamSwizzledOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast<const PxU32*>(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<<bitIndex; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + const PxU32 nodeType = getChildType(childData); + + const BVDataSwizzled* tn = reinterpret_cast<const BVDataSwizzled*>(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 0>(code2, tn, params); + BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 1>(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 2>(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_Swizzled<LeafTestT, 3>(code2, tn, params); + + SLABS_PNS + + }while(nb); + } + +#endif // GU_BV4_SLABS_SWIZZLED_ORDERED_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_SphereOverlap.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_SphereOverlap.cpp new file mode 100644 index 00000000..d709e273 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_SphereOverlap.cpp @@ -0,0 +1,330 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" +#include "GuSphere.h" +#include "GuDistancePointTriangle.h" +#include "PsVecMath.h" + +using namespace physx::shdfnd::aos; + +#if PX_VC +#pragma warning ( disable : 4324 ) +#endif + +// Sphere overlap any + +struct SphereParams +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + +#ifdef GU_BV4_QUANTIZED_TREE + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +#endif + + BV4_ALIGN16(PxVec3 mCenter_PaddedAligned); float mRadius2; +#ifdef GU_BV4_USE_SLABS + BV4_ALIGN16(PxVec3 mCenter_PaddedAligned2); float mRadius22; +#endif +}; + +#ifndef GU_BV4_QUANTIZED_TREE +// PT: TODO: refactor with bucket pruner code (TA34704) +static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const PxVec3& center, const PxVec3& extents, const SphereParams* PX_RESTRICT params) +{ + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned.x); + const FloatV mRadius2 = FLoad(params->mRadius2); + + const Vec4V boxCenter = V4LoadU(¢er.x); + const Vec4V boxExtents = V4LoadU(&extents.x); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d))); + return (test & 0x7) == 0x7; +} +#endif + +static PX_FORCE_INLINE Ps::IntBool __SphereTriangle(const SphereParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + { + const float sqrDist = (p0 - params->mCenter_PaddedAligned).magnitudeSquared(); + if(sqrDist <= params->mRadius2) + return 1; + } + + const PxVec3 edge10 = p1 - p0; + const PxVec3 edge20 = p2 - p0; + const PxVec3 cp = closestPtPointTriangle2(params->mCenter_PaddedAligned, p0, p1, p2, edge10, edge20); + const float sqrDist = (cp - params->mCenter_PaddedAligned).magnitudeSquared(); + return sqrDist <= params->mRadius2; +} + +// PT: TODO: evaluate if SIMD distance function would be faster here (TA34704) +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static /*PX_FORCE_INLINE*/ Ps::IntBool /*__fastcall*/ __SphereTriangle(const SphereParams* PX_RESTRICT params, PxU32 primIndex) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + return __SphereTriangle(params, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2]); +} + +namespace +{ +class LeafFunction_SphereOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const SphereParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__SphereTriangle(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template<class ParamsT> +static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh) +{ + computeLocalSphere(params->mRadius2, params->mCenter_PaddedAligned, sphere, worldm_Aligned); + +#ifdef GU_BV4_USE_SLABS + params->mCenter_PaddedAligned2 = params->mCenter_PaddedAligned*2.0f; + params->mRadius22 = params->mRadius2*4.0f; +#endif + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); +} + +#include "GuBV4_Internal.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" + + static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const Vec4V boxCenter, const Vec4V boxExtents, const SphereParams* PX_RESTRICT params) + { + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned2.x); + const FloatV mRadius2 = FLoad(params->mRadius22); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = PxU32(_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d)))); + return (test & 0x7) == 0x7; + } +#else + #ifdef GU_BV4_QUANTIZED_TREE + static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const BVDataPacked* PX_RESTRICT node, const SphereParams* PX_RESTRICT params) + { + const __m128i testV = _mm_load_si128((__m128i*)node->mAABB.mData); + const __m128i qextentsV = _mm_and_si128(testV, _mm_set1_epi32(0x0000ffff)); + const __m128i qcenterV = _mm_srai_epi32(testV, 16); + const Vec4V boxCenter = V4Mul(_mm_cvtepi32_ps(qcenterV), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + const Vec4V boxExtents = V4Mul(_mm_cvtepi32_ps(qextentsV), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned.x); + const FloatV mRadius2 = FLoad(params->mRadius2); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d))); + return (test & 0x7) == 0x7; + } + #endif +#endif + +#include "GuBV4_ProcessStreamNoOrder_SphereAABB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" +#endif + +Ps::IntBool BV4_OverlapSphereAny(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParams Params; + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + { + return processStreamNoOrder<LeafFunction_SphereOverlapAny>(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_SphereOverlapAny::doLeafTest(&Params, nbTris); + } +} + +// Sphere overlap all + +struct SphereParamsAll : SphereParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_SphereOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__SphereTriangle(params, primIndex)) + { + SphereParamsAll* ParamsAll = static_cast<SphereParamsAll*>(params); + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +PxU32 BV4_OverlapSphereAll(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + { + overflow = processStreamNoOrder<LeafFunction_SphereOverlapAll>(tree.mNodes, tree.mInitData, &Params)!=0; + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_SphereOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + + +// Sphere overlap - callback version + +struct SphereParamsCB : SphereParams +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_SphereOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const SphereParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + 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]; + + if(__SphereTriangle(params, p0, p1, p2)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this one is currently not used +void BV4_OverlapSphereCB(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + { + processStreamNoOrder<LeafFunction_SphereOverlapCB>(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_SphereOverlapCB::doLeafTest(&Params, nbTris); + } +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_SphereSweep.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_SphereSweep.cpp new file mode 100644 index 00000000..c955c7f5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuBV4_SphereSweep.cpp @@ -0,0 +1,388 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxSimpleTypes.h" +#include "foundation/PxMat44.h" +#include "GuBV4.h" +#include "GuBox.h" +#include "GuSphere.h" +#include "GuSIMDHelpers.h" +#include "GuSweepSphereTriangle.h" + +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" + +// PT: for sphere-sweeps we use method 3 in \\sw\physx\PhysXSDK\3.4\trunk\InternalDocumentation\GU\Sweep strategies.ppt + +namespace +{ + // PT: TODO: refactor structure (TA34704) + struct RayParams + { +#ifdef GU_BV4_QUANTIZED_TREE + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +#endif +#ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); +#endif + BV4_ALIGN16(Vec3p mLocalDir_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + + struct SphereSweepParams : RayParams + { + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + PxVec3 mOriginalExtents_Padded; + + RaycastHitInternal mStabbedFace; + PxU32 mBackfaceCulling; + PxU32 mEarlyExit; + + PxVec3 mP0, mP1, mP2; + PxVec3 mBestTriNormal; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_AABBAABBSweepTest.h" + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triSphereSweep(SphereSweepParams* 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]; + + PxVec3 normal = (p1 - p0).cross(p2 - p0); + + // Backface culling + const bool culled = params->mBackfaceCulling && normal.dot(params->mLocalDir_Padded) > 0.0f; + if(culled) + return false; + + const PxTriangle T(p0, p1, p2); // PT: TODO: check potential bad ctor/dtor here (TA34704) <= or avoid creating the tri, not needed anymore + + normal.normalize(); + + // PT: TODO: we lost some perf when switching to PhysX version. Revisit/investigate. (TA34704) + float dist; + bool directHit; + if(!sweepSphereVSTri(T.verts, normal, params->mOrigin_Padded, params->mOriginalExtents_Padded.x, params->mLocalDir_Padded, dist, directHit, true)) + return false; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal alignmentValue = computeAlignmentValue(normal, params->mLocalDir_Padded); + if(keepTriangle(dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist, distEpsilon)) + { + params->mStabbedFace.mDistance = dist; + params->mStabbedFace.mTriangleID = primIndex; + params->mP0 = p0; + params->mP1 = p1; + params->mP2 = p2; + params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound + params->mBestAlignmentValue = alignmentValue; + params->mBestTriNormal = normal; + if(nodeSorting) + { +#ifndef GU_BV4_USE_SLABS + setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + return true; + } + return false; +} + +namespace +{ +class LeafFunction_SphereSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triSphereSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_SphereSweepAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triSphereSweep(params, primIndex)) + return 1; + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class ImpactFunctionSphere +{ +public: + static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Sphere& sphere, const PxVec3& dir, const PxReal t, const TrianglePadded& triangle) + { + computeSphereTriImpactData(impactPos, impactNormal, sphere.center, dir, t, triangle); + } +}; +} + +template<class ParamsT> +static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mOriginalExtents_Padded = PxVec3(sphere.radius); + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + params->mStabbedFace.mDistance = maxDist; + params->mBestDistance = PX_MAX_REAL; + params->mBestAlignmentValue = 2.0f; + params->mMaxDist = maxDist; + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, sphere.center, worldm_Aligned); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif +} + +#include "GuBV4_Internal.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#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 + +Ps::IntBool BV4_SphereSweepSingle(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereSweepParams Params; + setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder(1, LeafFunction_SphereSweepAny)(tree.mNodes, tree.mInitData, &Params); + else + processStreamRayOrdered(1, LeafFunction_SphereSweepClosest)(tree.mNodes, tree.mInitData, &Params); + } + else + doBruteForceTests<LeafFunction_SphereSweepAny, LeafFunction_SphereSweepClosest>(mesh->getNbTriangles(), &Params); + + return computeImpactDataT<ImpactFunctionSphere>(sphere, dir, hit, &Params, worldm_Aligned, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +// PT: sphere sweep callback version - currently not used + +namespace +{ + struct SphereSweepParamsCB : SphereSweepParams + { + // PT: these new members are only here to call computeImpactDataT during traversal :( + // PT: TODO: most of them may not be needed if we just move sphere to local space before traversal + Sphere mSphere; // Sphere in original space (maybe not local/mesh space) + PxVec3 mDir; // 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_SphereSweepCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triSphereSweep(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 = computeImpactDataT<ImpactFunctionSphere>(params->mSphere, params->mDir, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + PX_UNUSED(b); + + reportUnlimitedCallbackHit(params, hit); + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_SphereSweepCB(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereSweepParamsCB Params; + Params.mSphere = sphere; + Params.mDir = dir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + processStreamRayOrdered(1, LeafFunction_SphereSweepCB)(tree.mNodes, tree.mInitData, &Params); + else + processStreamRayNoOrder(1, LeafFunction_SphereSweepCB)(tree.mNodes, tree.mInitData, &Params); + } + else + doBruteForceTests<LeafFunction_SphereSweepCB, LeafFunction_SphereSweepCB>(mesh->getNbTriangles(), &Params); +} + + +// Old box sweep callback version, using sphere code + +namespace +{ +struct BoxSweepParamsCB : SphereSweepParams +{ + MeshSweepCallback mCallback; + void* mUserData; +}; + +class ExLeafTestSweepCB +{ +public: + static PX_FORCE_INLINE void doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, /*vrefs,*/ dist)) + return; + + if(dist<params->mStabbedFace.mDistance) + { + params->mStabbedFace.mDistance = dist; +#ifndef GU_BV4_USE_SLABS + setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + } + + primIndex++; + }while(nbToGo--); + } +}; +} + +void BV4_GenericSweepCB_Old(const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData) +{ + BoxSweepParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + Params.mOriginalExtents_Padded = extents; + + Params.mStabbedFace.mTriangleID = PX_INVALID_U32; + Params.mStabbedFace.mDistance = maxDist; + + computeLocalRay(Params.mLocalDir_Padded, Params.mOrigin_Padded, dir, origin, worldm_Aligned); + +#ifndef GU_BV4_USE_SLABS + setupRayData(&Params, maxDist, Params.mOrigin_Padded, Params.mLocalDir_Padded); +#endif + + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + setupMeshPointersAndQuantizedCoeffs(&Params, mesh, &tree); + + if(tree.mNodes) + { + processStreamRayOrdered(1, ExLeafTestSweepCB)(tree.mNodes, tree.mInitData, &Params); + } + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + ExLeafTestSweepCB::doLeafTest(&Params, nbTris); + } +} + +#endif + diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuMeshData.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMeshData.h new file mode 100644 index 00000000..37cdbcfc --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMeshData.h @@ -0,0 +1,298 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MESH_DATA_H +#define GU_MESH_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec4.h" +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PxTriangleMesh.h" +#include "GuRTree.h" +#include "GuBV4.h" +#include "GuBV32.h" + +namespace physx +{ + +#define RTREE_COOK_VERSION 1 + +namespace Gu { + +// 1: support stackless collision trees for non-recursive collision queries +// 2: height field functionality not supported anymore +// 3: mass struct removed +// 4: bounding sphere removed +// 5: RTree added, opcode tree still in the binary image, physx 3.0 +// 6: opcode tree removed from binary image +// 7: convex decomposition is out +// 8: adjacency information added +// 9: removed leaf triangles and most of opcode data, changed rtree layout +// 10: float rtrees +// 11: new build, isLeaf added to page +// 12: isLeaf is now the lowest bit in ptrs +// 13: TA30159 removed deprecated convexEdgeThreshold and bumped version +// 14: added midphase ID + +#define PX_MESH_VERSION 14 + +// these flags are used to indicate/validate the contents of a cooked mesh file +enum InternalMeshSerialFlag +{ + IMSF_MATERIALS = (1<<0), //!< if set, the cooked mesh file contains per-triangle material indices + IMSF_FACE_REMAP = (1<<1), //!< if set, the cooked mesh file contains a remap table + IMSF_8BIT_INDICES = (1<<2), //!< if set, the cooked mesh file contains 8bit indices (topology) + IMSF_16BIT_INDICES = (1<<3), //!< if set, the cooked mesh file contains 16bit indices (topology) + IMSF_ADJACENCIES = (1<<4), //!< if set, the cooked mesh file contains adjacency structures + IMSF_GRB_DATA = (1<<5) //!< if set, the cooked mesh file contains GRB data structures +}; + + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + class TriangleMeshData : public Ps::UserAllocated + { + public: + PxMeshMidPhase::Enum mType; + + PxU32 mNbVertices; + PxU32 mNbTriangles; + PxVec3* mVertices; + void* mTriangles; + + PxBounds3 mAABB; + PxU8* mExtraTrigData; + PxReal mGeomEpsilon; + + PxU8 mFlags; + PxU16* mMaterialIndices; + PxU32* mFaceRemap; + PxU32* mAdjacencies; + + // GRB data ------------------------- + void * mGRB_triIndices; //!< GRB: GPU-friendly tri indices(uint3) + + // TODO avoroshilov: adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325 + void * mGRB_triAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) [uin4] + PxU32 * mGRB_vertValency; //!< GRB: number of adjacent vertices to a vertex + PxU32 * mGRB_adjVertStart; //!< GRB: offset for each vertex in the adjacency list + PxU32 * mGRB_adjVertices; //!< GRB: list of adjacent vertices + + PxU32 mGRB_meshAdjVerticiesTotal; //!< GRB: total number of indices in the 'mGRB_adjVertices' + PxU32* mGRB_faceRemap; //!< GRB: this remap the GPU triangle indices to CPU triangle indices + + void* mGRB_BV32Tree; + // End of GRB data ------------------ + + TriangleMeshData() : + mNbVertices (0), + mNbTriangles (0), + mVertices (NULL), + mTriangles (NULL), + mAABB (PxBounds3::empty()), + mExtraTrigData (NULL), + mGeomEpsilon (0.0f), + mFlags (0), + mMaterialIndices (NULL), + mFaceRemap (NULL), + mAdjacencies (NULL), + + mGRB_triIndices (NULL), + mGRB_triAdjacencies (NULL), + mGRB_vertValency (NULL), + mGRB_adjVertStart (NULL), + mGRB_adjVertices (NULL), + + mGRB_meshAdjVerticiesTotal (0), + mGRB_faceRemap (NULL), + mGRB_BV32Tree (NULL) + + { + } + + virtual ~TriangleMeshData() + { + if(mVertices) + PX_FREE(mVertices); + if(mTriangles) + PX_FREE(mTriangles); + if(mMaterialIndices) + PX_DELETE_POD(mMaterialIndices); + if(mFaceRemap) + PX_DELETE_POD(mFaceRemap); + if(mAdjacencies) + PX_DELETE_POD(mAdjacencies); + if(mExtraTrigData) + PX_DELETE_POD(mExtraTrigData); + + + if (mGRB_triIndices) + PX_FREE(mGRB_triIndices); + if (mGRB_triAdjacencies) + PX_DELETE_POD(mGRB_triAdjacencies); + if (mGRB_vertValency) + PX_DELETE_POD(mGRB_vertValency); + if (mGRB_adjVertStart) + PX_DELETE_POD(mGRB_adjVertStart); + if (mGRB_adjVertices) + PX_DELETE_POD(mGRB_adjVertices); + + if (mGRB_faceRemap) + PX_DELETE_POD(mGRB_faceRemap); + + if (mGRB_BV32Tree) + { + Gu::BV32Tree* bv32Tree = reinterpret_cast<BV32Tree*>(mGRB_BV32Tree); + PX_DELETE(bv32Tree); + mGRB_BV32Tree = NULL; + } + + + } + + + PxVec3* allocateVertices(PxU32 nbVertices) + { + PX_ASSERT(!mVertices); + // PT: we allocate one more vertex to make sure it's safe to V4Load the last one + const PxU32 nbAllocatedVerts = nbVertices + 1; + mVertices = reinterpret_cast<PxVec3*>(PX_ALLOC(nbAllocatedVerts * sizeof(PxVec3), "PxVec3")); + mNbVertices = nbVertices; + return mVertices; + } + + void* allocateTriangles(PxU32 nbTriangles, bool force32Bit, PxU32 allocateGPUData = 0) + { + PX_ASSERT(mNbVertices); + PX_ASSERT(!mTriangles); + + bool index16 = mNbVertices <= 0xffff && !force32Bit; + if(index16) + mFlags |= PxTriangleMeshFlag::e16_BIT_INDICES; + + mTriangles = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mTriangles"); + if (allocateGPUData) + mGRB_triIndices = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mGRB_triIndices"); + mNbTriangles = nbTriangles; + return mTriangles; + } + + PxU16* allocateMaterials() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mMaterialIndices); + mMaterialIndices = PX_NEW(PxU16)[mNbTriangles]; + return mMaterialIndices; + } + + PxU32* allocateAdjacencies() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mAdjacencies); + mAdjacencies = PX_NEW(PxU32)[mNbTriangles*3]; + mFlags |= PxTriangleMeshFlag::eADJACENCY_INFO; + return mAdjacencies; + } + + PxU32* allocateFaceRemap() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mFaceRemap); + mFaceRemap = PX_NEW(PxU32)[mNbTriangles]; + return mFaceRemap; + } + + PxU8* allocateExtraTrigData() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mExtraTrigData); + mExtraTrigData = PX_NEW(PxU8)[mNbTriangles]; + return mExtraTrigData; + } + + PX_FORCE_INLINE void setTriangleAdjacency(PxU32 triangleIndex, PxU32 adjacency, PxU32 offset) + { + PX_ASSERT(mAdjacencies); + mAdjacencies[triangleIndex*3 + offset] = adjacency; + } + + PX_FORCE_INLINE bool has16BitIndices() const + { + return (mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? true : false; + } + }; + + class RTreeTriangleData : public TriangleMeshData + { + public: + RTreeTriangleData() { mType = PxMeshMidPhase::eBVH33; } + virtual ~RTreeTriangleData() {} + + Gu::RTree mRTree; + }; + + class BV4TriangleData : public TriangleMeshData + { + public: + BV4TriangleData() { mType = PxMeshMidPhase::eBVH34; } + virtual ~BV4TriangleData() {} + + Gu::SourceMesh mMeshInterface; + Gu::BV4Tree mBV4Tree; + }; + + + class BV32TriangleData : public TriangleMeshData + { + public: + //using the same type as BV4 + BV32TriangleData() { mType = PxMeshMidPhase::eBVH34; } + virtual ~BV32TriangleData() {} + + Gu::SourceMesh mMeshInterface; + Gu::BV32Tree mBV32Tree; + }; + +#if PX_VC +#pragma warning(pop) +#endif + + +} // namespace Gu + +} + +#endif // #ifdef GU_MESH_DATA_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuMeshQuery.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMeshQuery.cpp new file mode 100644 index 00000000..74b352d5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMeshQuery.cpp @@ -0,0 +1,312 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxProfiler.h" +#include "PxMeshQuery.h" +#include "GuInternal.h" +#include "PxSphereGeometry.h" +#include "PxGeometryQuery.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBoxConversion.h" +#include "GuIntersectionTriangleBox.h" +#include "CmScaling.h" +#include "GuSweepTests.h" +#include "GuSIMDHelpers.h" +#include "GuMidphaseInterface.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +namespace { + + class HfTrianglesEntityReport2 : public EntityReport<PxU32>, public LimitedResults + { + public: + HfTrianglesEntityReport2( + PxU32* results, PxU32 maxResults, PxU32 startIndex, + HeightFieldUtil& hfUtil, + const PxVec3& boxCenter, const PxVec3& boxExtents, const PxQuat& boxRot, + bool aabbOverlap) : + LimitedResults (results, maxResults, startIndex), + mHfUtil (hfUtil), + mAABBOverlap (aabbOverlap) + { + buildFrom(mBox2Hf, boxCenter, boxExtents, boxRot); + } + + virtual bool onEvent(PxU32 nbEntities, PxU32* entities) + { + if(mAABBOverlap) + { + while(nbEntities--) + if(!add(*entities++)) + return false; + } + else + { + const PxTransform idt(PxIdentity); + for(PxU32 i=0; i<nbEntities; i++) + { + TrianglePadded tri; + mHfUtil.getTriangle(idt, tri, NULL, NULL, entities[i], false, false); // First parameter not needed if local space triangle is enough + + // PT: this one is safe because triangle class is padded + if(intersectTriangleBox(mBox2Hf, tri.verts[0], tri.verts[1], tri.verts[2])) + { + if(!add(entities[i])) + return false; + } + } + } + return true; + } + + HeightFieldUtil& mHfUtil; + BoxPadded mBox2Hf; + bool mAABBOverlap; + + private: + HfTrianglesEntityReport2& operator=(const HfTrianglesEntityReport2&); + }; + + +} // namespace + +void physx::PxMeshQuery::getTriangle(const PxTriangleMeshGeometry& triGeom, const PxTransform& globalPose, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices, PxU32* adjacencyIndices) +{ + TriangleMesh* tm = static_cast<TriangleMesh*>(triGeom.triangleMesh); + + PX_CHECK_AND_RETURN(triangleIndex<tm->getNbTriangles(), "PxMeshQuery::getTriangle: triangle index is out of bounds"); + + if(adjacencyIndices && !tm->getAdjacencies()) + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacency information not created. Set buildTriangleAdjacencies on Cooking params."); + + const Cm::Matrix34 vertex2worldSkew = globalPose * triGeom.scale; + tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, triGeom.scale.hasNegativeDeterminant(), vertexIndices, adjacencyIndices); +} + +/////////////////////////////////////////////////////////////////////////////// + +void physx::PxMeshQuery::getTriangle(const PxHeightFieldGeometry& hfGeom, const PxTransform& globalPose, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices, PxU32* adjacencyIndices) +{ + HeightFieldUtil hfUtil(hfGeom); + + hfUtil.getTriangle(globalPose, triangle, vertexIndices, adjacencyIndices, triangleIndex, true, true); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::PxMeshQuery::findOverlapTriangleMesh( + const PxGeometry& geom, const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow) +{ + PX_SIMD_GUARD; + + LimitedResults limitedResults(results, maxResults, startIndex); + + TriangleMesh* tm = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + switch(geom.getType()) + { + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + Box box; + buildFrom(box, geomPose.p, boxGeom.halfExtents, geomPose.q); + + Midphase::intersectBoxVsMesh(box, *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsGeom = static_cast<const PxCapsuleGeometry&>(geom); + + Capsule capsule; + getCapsule(capsule, capsGeom, geomPose); + + Midphase::intersectCapsuleVsMesh(capsule, *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + Midphase::intersectSphereVsMesh(Sphere(geomPose.p, sphereGeom.radius), *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_CHECK_MSG(false, "findOverlapTriangleMesh: Only box, capsule and sphere geometries are supported."); + } + } + + overflow = limitedResults.mOverflow; + return limitedResults.mNbResults; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::PxMeshQuery::findOverlapHeightField( const PxGeometry& geom, const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow) +{ + PX_SIMD_GUARD; + const PxTransform localPose0 = hfPose.transformInv(geomPose); + PxBoxGeometry boxGeom; + + switch(geom.getType()) + { + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& cap = static_cast<const PxCapsuleGeometry&>(geom); + boxGeom.halfExtents = PxVec3(cap.halfHeight+cap.radius, cap.radius, cap.radius); + } + break; + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sph = static_cast<const PxSphereGeometry&>(geom); + boxGeom.halfExtents = PxVec3(sph.radius, sph.radius, sph.radius); + } + break; + case PxGeometryType::eBOX: + boxGeom = static_cast<const PxBoxGeometry&>(geom); + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + overflow = false; + PX_CHECK_AND_RETURN_VAL(false, "findOverlapHeightField: Only box, sphere and capsule queries are supported.", false); + } + } + + const bool isAABB = ((localPose0.q.x == 0.0f) && (localPose0.q.y == 0.0f) && (localPose0.q.z == 0.0f)); + + PxBounds3 bounds; + if (isAABB) + bounds = PxBounds3::centerExtents(localPose0.p, boxGeom.halfExtents); + else + bounds = PxBounds3::poseExtent(localPose0, boxGeom.halfExtents); // box.halfExtents is really extent + + HeightFieldUtil hfUtil(hfGeom); + HfTrianglesEntityReport2 entityReport(results, maxResults, startIndex, hfUtil, localPose0.p, boxGeom.halfExtents, localPose0.q, isAABB); + + hfUtil.overlapAABBTriangles(hfPose, bounds, 0, &entityReport); + overflow = entityReport.mOverflow; + return entityReport.mNbResults; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool physx::PxMeshQuery::sweep( const PxVec3& unitDir, const PxReal maxDistance, + const PxGeometry& geom, const PxTransform& pose, + PxU32 triangleCount, const PxTriangle* triangles, + PxSweepHit& sweepHit, PxHitFlags hitFlags, + const PxU32* cachedIndex, const PxReal inflation, bool doubleSided) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxMeshQuery::sweep(): pose is not valid.", false); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxMeshQuery::sweep(): unitDir is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDistance), "PxMeshQuery::sweep(): distance is not valid.", false); + PX_CHECK_AND_RETURN_VAL(maxDistance > 0, "PxMeshQuery::sweep(): sweep distance must be greater than 0.", false); + + PX_PROFILE_ZONE("MeshQuery.sweep", 0); + + const PxReal distance = PxMin(maxDistance, PX_MAX_SWEEP_DISTANCE); + + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); + + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + + return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, + sweepHit, cachedIndex, inflation, hitFlags); + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); + + return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, + sweepHit, cachedIndex, inflation, hitFlags); + } + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); + + if(hitFlags & PxHitFlag::ePRECISE_SWEEP) + { + return sweepBoxTriangles_Precise( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex, + inflation, hitFlags); + } + else + { + return sweepBoxTriangles( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex, + inflation, hitFlags); + } + } + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxMeshQuery::sweep(): geometry object parameter must be sphere, capsule or box geometry."); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +// Exposing wrapper for Midphase::intersectOBB just for particles in order to avoid DelayLoad performance problem. This should be removed with particles in PhysX 3.5 (US16993) +void physx::Gu::intersectOBB_Particles(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + Midphase::intersectOBB(mesh, obb, callback, bothTriangleSidesCollide, checkObbIsAligned); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseBV4.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseBV4.cpp new file mode 100644 index 00000000..1cab487f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseBV4.cpp @@ -0,0 +1,999 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuBV4.h" +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuSweepMesh.h" +#include "GuBV4Build.h" +#include "GuBV4_Common.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionRayBox.h" +#include "PxTriangleMeshGeometry.h" +#include "CmScaling.h" +#include "GuTriangleMeshBV4.h" + +// This file contains code specific to the BV4 midphase. + +// PT: TODO: revisit/inline static sweep functions (TA34704) + +using namespace physx; +using namespace Gu; +using namespace Cm; + +#if PX_INTEL_FAMILY +Ps::IntBool BV4_RaycastSingle (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags); +PxU32 BV4_RaycastAll (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags); +void BV4_RaycastCB (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData); + +Ps::IntBool BV4_OverlapSphereAny (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapSphereAll (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapSphereCB (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_OverlapBoxAny (const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapBoxAll (const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapBoxCB (const Box& box, const BV4Tree& tree, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_OverlapCapsuleAny (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapCapsuleAll (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapCapsuleCB (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_SphereSweepSingle (const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_SphereSweepCB (const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +Ps::IntBool BV4_BoxSweepSingle (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_BoxSweepCB (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +Ps::IntBool BV4_CapsuleSweepSingle (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +Ps::IntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_CapsuleSweepCB (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags); +void BV4_CapsuleSweepAACB (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags); + +void BV4_GenericSweepCB_Old (const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData); +void BV4_GenericSweepCB (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit); + +static PX_FORCE_INLINE void setIdentity(PxMat44& m) +{ + m.column0 = PxVec4(1.0f, 0.0f, 0.0f, 0.0f); + m.column1 = PxVec4(0.0f, 1.0f, 0.0f, 0.0f); + m.column2 = PxVec4(0.0f, 0.0f, 1.0f, 0.0f); + m.column3 = PxVec4(0.0f, 0.0f, 0.0f, 1.0f); +} + +static PX_FORCE_INLINE void setRotation(PxMat44& m, const PxQuat& q) +{ + const PxReal x = q.x; + const PxReal y = q.y; + const PxReal z = q.z; + const PxReal w = q.w; + + const PxReal x2 = x + x; + const PxReal y2 = y + y; + const PxReal z2 = z + z; + + const PxReal xx = x2*x; + const PxReal yy = y2*y; + const PxReal zz = z2*z; + + const PxReal xy = x2*y; + const PxReal xz = x2*z; + const PxReal xw = x2*w; + + const PxReal yz = y2*z; + const PxReal yw = y2*w; + const PxReal zw = z2*w; + + m.column0 = PxVec4(1.0f - yy - zz, xy + zw, xz - yw, 0.0f); + m.column1 = PxVec4(xy - zw, 1.0f - xx - zz, yz + xw, 0.0f); + m.column2 = PxVec4(xz + yw, yz - xw, 1.0f - xx - yy, 0.0f); +} + +#define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 +static PX_FORCE_INLINE const PxMat44* setupWorldMatrix(PxMat44& world, const float* meshPos, const float* meshRot) +{ +// world = PxMat44(PxIdentity); + setIdentity(world); + + bool isIdt = true; + if(meshRot) + { + const PxU32* Bin = reinterpret_cast<const PxU32*>(meshRot); + if(Bin[0]!=0 || Bin[1]!=0 || Bin[2]!=0 || Bin[3]!=IEEE_1_0) + { +// const PxQuat Q(meshRot[0], meshRot[1], meshRot[2], meshRot[3]); +// world = PxMat44(Q); + setRotation(world, PxQuat(meshRot[0], meshRot[1], meshRot[2], meshRot[3])); + isIdt = false; + } + } + + if(meshPos) + { + const PxU32* Bin = reinterpret_cast<const PxU32*>(meshPos); + if(Bin[0]!=0 || Bin[1]!=0 || Bin[2]!=0) + { +// world.setPosition(PxVec3(meshPos[0], meshPos[1], meshPos[2])); + world.column3.x = meshPos[0]; + world.column3.y = meshPos[1]; + world.column3.z = meshPos[2]; + isIdt = false; + } + } + return isIdt ? NULL : &world; +} + +static PX_FORCE_INLINE PxU32 setupFlags(bool anyHit, bool doubleSided, bool meshBothSides) +{ + PxU32 flags = 0; + if(anyHit) + flags |= QUERY_MODIFIER_ANY_HIT; + if(doubleSided) + flags |= QUERY_MODIFIER_DOUBLE_SIDED; + if(meshBothSides) + flags |= QUERY_MODIFIER_MESH_BOTH_SIDES; + return flags; +} + +static Ps::IntBool boxSweepVsMesh(SweepHit& h, const BV4Tree& tree, const float* meshPos, const float* meshRot, const Box& box, const PxVec3& dir, float maxDist, bool anyHit, bool doubleSided, bool meshBothSides) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const PxU32 flags = setupFlags(anyHit, doubleSided, meshBothSides); + return BV4_BoxSweepSingle(box, dir, maxDist, tree, TM, &h, flags); +} + +static Ps::IntBool sphereSweepVsMesh(SweepHit& h, const BV4Tree& tree, const PxVec3& center, float radius, const PxVec3& dir, float maxDist, const PxMat44* TM, const PxU32 flags) +{ + // PT: TODO: avoid this copy (TA34704) + const Sphere tmp(center, radius); + + return BV4_SphereSweepSingle(tmp, dir, maxDist, tree, TM, &h, flags); +} + +static bool capsuleSweepVsMesh(SweepHit& h, const BV4Tree& tree, const Capsule& capsule, const PxVec3& dir, float maxDist, const PxMat44* TM, const PxU32 flags) +{ + Capsule localCapsule; + computeLocalCapsule(localCapsule, capsule, TM); + + // PT: TODO: optimize + PxVec3 localDir, unused; + computeLocalRay(localDir, unused, dir, dir, TM); + + const PxVec3 capsuleDir = localCapsule.p1 - localCapsule.p0; + PxU32 nbNullComponents = 0; + const float epsilon = 1e-3f; + if(PxAbs(capsuleDir.x)<epsilon) + nbNullComponents++; + if(PxAbs(capsuleDir.y)<epsilon) + nbNullComponents++; + if(PxAbs(capsuleDir.z)<epsilon) + nbNullComponents++; + + // PT: TODO: consider passing TM to BV4_CapsuleSweepSingleXX just to do the final transforms there instead + // of below. It would make the parameters slightly inconsistent (local input + world TM) but it might make + // the code better overall, more aligned with the "unlimited results" version. + Ps::IntBool status; + if(nbNullComponents==2) + { + status = BV4_CapsuleSweepSingleAA(localCapsule, localDir, maxDist, tree, &h, flags); + } + else + { + status = BV4_CapsuleSweepSingle(localCapsule, localDir, maxDist, tree, &h, flags); + } + if(status && TM) + { + h.mPos = TM->transform(h.mPos); + h.mNormal = TM->rotate(h.mNormal); + } + return status!=0; +} + +static PX_FORCE_INLINE void boxSweepVsMeshCBOld(const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& center, const PxVec3& extents, const PxVec3& dir, float maxDist, MeshSweepCallback callback, void* userData) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + BV4_GenericSweepCB_Old(center, extents, dir, maxDist, tree, TM, callback, userData); +} + +// + +static PX_FORCE_INLINE bool raycastVsMesh(PxRaycastHit& hitData, const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, PxHitFlags hitFlags) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const PxU32 flags = setupFlags(anyHit, doubleSided, false); + + if(!BV4_RaycastSingle(orig, dir, tree, TM, &hitData, maxDist, geomEpsilon, flags, hitFlags)) + return false; + + return true; +} + +/*static PX_FORCE_INLINE PxU32 raycastVsMeshAll(PxRaycastHit* hits, PxU32 maxNbHits, const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, PxHitFlags hitFlags) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const PxU32 flags = setupFlags(anyHit, doubleSided, false); + + return BV4_RaycastAll(orig, dir, tree, TM, hits, maxNbHits, maxDist, geomEpsilon, flags, hitFlags); +}*/ + +static PX_FORCE_INLINE void raycastVsMeshCB(const BV4Tree& tree, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, MeshRayCallback callback, void* userData) +{ + const PxU32 flags = setupFlags(false, doubleSided, false); + BV4_RaycastCB(orig, dir, tree, NULL, maxDist, geomEpsilon, flags, callback, userData); +} + +struct BV4RaycastCBParams +{ + PX_FORCE_INLINE BV4RaycastCBParams( PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose, + const Cm::Matrix34* world2vertexSkew, PxU32 hitFlags, + const PxVec3& rayDir, bool isDoubleSided, float distCoeff) : + mDstBase (hits), + mHitNum (0), + mMaxHits (maxHits), + mScale (scale), + mPose (pose), + mWorld2vertexSkew (world2vertexSkew), + mHitFlags (hitFlags), + mRayDir (rayDir), + mIsDoubleSided (isDoubleSided), + mDistCoeff (distCoeff) + { + } + + PxRaycastHit* mDstBase; + PxU32 mHitNum; + PxU32 mMaxHits; + const PxMeshScale* mScale; + const PxTransform* mPose; + const Cm::Matrix34* mWorld2vertexSkew; + PxU32 mHitFlags; + const PxVec3& mRayDir; + bool mIsDoubleSided; + float mDistCoeff; + +private: + BV4RaycastCBParams& operator=(const BV4RaycastCBParams&); +}; + +static PX_FORCE_INLINE PxVec3 processLocalNormal(const Cm::Matrix34* PX_RESTRICT world2vertexSkew, const PxTransform* PX_RESTRICT pose, const PxVec3& localNormal, const PxVec3& rayDir, const bool isDoubleSided) +{ + PxVec3 normal; + if(world2vertexSkew) + normal = world2vertexSkew->rotateTranspose(localNormal); + else + normal = pose->rotate(localNormal); + normal.normalize(); + + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(isDoubleSided && normal.dot(rayDir) > 0.0f) + normal = -normal; + return normal; +} + +static HitCode gRayCallback(void* userData, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxU32 triangleIndex, float dist, float u, float v) +{ + BV4RaycastCBParams* params = reinterpret_cast<BV4RaycastCBParams*>(userData); + +//const bool last = params->mHitNum == params->mMaxHits; + + //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose); + // PT: TODO: revisit this for N hits + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast<PxRaycastHit&>(buffer); +//PxRaycastHit& hit = last ? (PxRaycastHit&)buffer : params->mDstBase[params->mHitNum]; + + hit.distance = dist * params->mDistCoeff; + hit.u = u; + hit.v = v; + hit.faceIndex = triangleIndex; + + PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2; + if(params->mWorld2vertexSkew) + { + localImpact = params->mScale->transform(localImpact); + if(params->mScale->hasNegativeDeterminant()) + Ps::swap<PxReal>(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space + } + + hit.position = params->mPose->transform(localImpact); + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + PxVec3 normal(0.0f); + // Compute additional information if needed + if(params->mHitFlags & PxHitFlag::eNORMAL) + { + const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0); + normal = processLocalNormal(params->mWorld2vertexSkew, params->mPose, localNormal, params->mRayDir, params->mIsDoubleSided); + hit.flags |= PxHitFlag::eNORMAL; + } + hit.normal = normal; + + // PT: no callback => store results in provided buffer + if(params->mHitNum == params->mMaxHits) +// if(last) + return HIT_EXIT; + + params->mDstBase[params->mHitNum++] = hit; +// params->mHitNum++; + + return HIT_NONE; +} + +PxU32 physx::Gu::raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast<const BV4TriangleMesh*>(mesh); + + const bool multipleHits = (maxHits > 1); + const bool idtScale = meshGeom.scale.isIdentity(); + + const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + const BV4Tree& tree = static_cast<const BV4TriangleMesh*>(meshData)->getBV4Tree(); + if(idtScale && !multipleHits) + { + bool b = raycastVsMesh(*hits, tree, &pose.p.x, &pose.q.x, rayOrigin, rayDir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + if(b) + { + PxHitFlags dstFlags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + // PT: TODO: pass flags to BV4 code (TA34704) + if(hitFlags & PxHitFlag::eNORMAL) + { + dstFlags |= PxHitFlag::eNORMAL; + if(isDoubleSided) + { + PxVec3 normal = hits->normal; + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(normal.dot(rayDir) > 0.0f) + normal = -normal; + hits->normal = normal; + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = dstFlags; + } + return PxU32(b); + } + +/* + if(idtScale && multipleHits) + { + PxU32 nbHits = raycastVsMeshAll(hits, maxHits, tree, &pose.p.x, &pose.q.x, rayOrigin, rayDir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + + return nbHits; + } +*/ + + //scaling: transform the ray to vertex space + PxVec3 orig, dir; + Cm::Matrix34 world2vertexSkew; + Cm::Matrix34* world2vertexSkewP = NULL; + PxReal distCoeff = 1.0f; + if(idtScale) + { + orig = pose.transformInv(rayOrigin); + dir = pose.rotateInv(rayDir); + } + else + { + world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse(); + world2vertexSkewP = &world2vertexSkew; + orig = world2vertexSkew.transform(rayOrigin); + dir = world2vertexSkew.rotate(rayDir); + { + distCoeff = dir.normalize(); + maxDist *= distCoeff; + maxDist += 1e-3f; + distCoeff = 1.0f/distCoeff; + } + } + + if(!multipleHits) + { + bool b = raycastVsMesh(*hits, tree, NULL, NULL, orig, dir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + if(b) + { + hits->distance *= distCoeff; + hits->position = pose.transform(meshGeom.scale.transform(hits->position)); + PxHitFlags dstFlags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + if(meshGeom.scale.hasNegativeDeterminant()) + Ps::swap<PxReal>(hits->u, hits->v); // have to swap the UVs though since they were computed in mesh local space + + // PT: TODO: pass flags to BV4 code (TA34704) + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + dstFlags |= PxHitFlag::eNORMAL; + hits->normal = processLocalNormal(world2vertexSkewP, &pose, hits->normal, rayDir, isDoubleSided); + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = dstFlags; + } + return PxU32(b); + } + + BV4RaycastCBParams callback(hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff); + + raycastVsMeshCB( static_cast<const BV4TriangleMesh*>(meshData)->getBV4Tree(), + orig, dir, + maxDist, meshData->getGeomEpsilon(), bothSides, + gRayCallback, &callback); + return callback.mHitNum; +} + +namespace +{ +struct IntersectShapeVsMeshCallback +{ + IntersectShapeVsMeshCallback(LimitedResults* results, bool flipNormal) : mResults(results), mAnyHits(false), mFlipNormal(flipNormal) {} + + LimitedResults* mResults; + bool mAnyHits; + bool mFlipNormal; + + PX_FORCE_INLINE bool recordHit(PxU32 faceIndex, Ps::IntBool hit) + { + if(hit) + { + mAnyHits = true; + if(mResults) + mResults->add(faceIndex); + else + return false; // abort traversal if we are only interested in firstContact (mResults is NULL) + } + return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectSphereVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Sphere& sphere, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + mVertexToShapeSkew = meshScale.toMat33(); + mLocalCenter = meshTransform.transformInv(sphere.center); // sphereCenterInMeshSpace + mSphereRadius2 = sphere.radius*sphere.radius; + } + + PxMat33 mVertexToShapeSkew; + PxVec3 mLocalCenter; // PT: sphere center in local/mesh space + PxF32 mSphereRadius2; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const Vec3V v0 = V3LoadU(mVertexToShapeSkew * av0); + const Vec3V v1 = V3LoadU(mVertexToShapeSkew * (mFlipNormal ? av2 : av1)); + const Vec3V v2 = V3LoadU(mVertexToShapeSkew * (mFlipNormal ? av1 : av2)); + + FloatV dummy1, dummy2; + Vec3V closestP; + PxReal dist2; + FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2); + return recordHit(faceIndex, dist2 <= mSphereRadius2); + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectCapsuleVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Capsule& capsule, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + mVertexToShapeSkew = meshScale.toMat33(); + + // transform world capsule to mesh shape space + mLocalCapsule.p0 = meshTransform.transformInv(capsule.p0); + mLocalCapsule.p1 = meshTransform.transformInv(capsule.p1); + mLocalCapsule.radius = capsule.radius; + mParams.init(mLocalCapsule); + } + + PxMat33 mVertexToShapeSkew; + Capsule mLocalCapsule; // PT: capsule in mesh/local space + CapsuleTriangleOverlapData mParams; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const PxVec3 v0 = mVertexToShapeSkew * av0; + const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2); + const PxVec3 normal = (v0 - v1).cross(v0 - v2); + bool hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams); + return recordHit(faceIndex, hit); + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectBoxVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Box& box, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + const PxMat33 vertexToShapeSkew = meshScale.toMat33(); + + // mesh scale needs to be included - inverse transform and optimize the box + const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * vertexToShapeSkew; + const PxVec3& vertexToWorldSkew_Trans = meshTransform.p; + + Matrix34 tmp; + buildMatrixFromBox(tmp, box); + const Matrix34 inv = tmp.getInverseRT(); + const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans); + + mVertexToBox = inv * _vertexToWorldSkew; + mBoxCenter = PxVec3(0.0f); + mBoxExtents = box.extents; // extents do not change + } + + Matrix34 mVertexToBox; + Vec3p mBoxExtents, mBoxCenter; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const Vec3p v0 = mVertexToBox.transform(av0); + const Vec3p v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1); + const Vec3p v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2); + + // PT: this one is safe because we're using Vec3p for all parameters + const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2); + return recordHit(faceIndex, hit); + } +}; +} + +static bool gSphereVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectSphereVsMeshCallback* callback = reinterpret_cast<IntersectSphereVsMeshCallback*>(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +static bool gCapsuleVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectCapsuleVsMeshCallback* callback = reinterpret_cast<IntersectCapsuleVsMeshCallback*>(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +static bool gBoxVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectBoxVsMeshCallback* callback = reinterpret_cast<IntersectBoxVsMeshCallback*>(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +bool physx::Gu::intersectSphereVsMesh_BV4(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast<const BV4TriangleMesh&>(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapSphereAll(sphere, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapSphereAny(sphere, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectSphereVsMeshCallback callback(meshScale, meshTransform, sphere, results, meshScale.hasNegativeDeterminant()); + + const Box worldOBB_(sphere.center, PxVec3(sphere.radius), PxMat33(PxIdentity)); + Box vertexOBB; + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gSphereVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +bool physx::Gu::intersectBoxVsMesh_BV4(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast<const BV4TriangleMesh&>(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapBoxAll(box, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapBoxAny(box, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectBoxVsMeshCallback callback(meshScale, meshTransform, box, results, meshScale.hasNegativeDeterminant()); + + Box vertexOBB; // query box in vertex space + computeVertexSpaceOBB(vertexOBB, box, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gBoxVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +bool physx::Gu::intersectCapsuleVsMesh_BV4(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast<const BV4TriangleMesh&>(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapCapsuleAll(capsule, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapCapsuleAny(capsule, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectCapsuleVsMeshCallback callback(meshScale, meshTransform, capsule, results, meshScale.hasNegativeDeterminant()); + + // make vertex space OBB + Box vertexOBB; + Box worldOBB_; + worldOBB_.create(capsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule) + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gCapsuleVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +// PT: TODO: get rid of this (TA34704) +static bool gVolumeCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices) +{ + MeshHitCallback<PxRaycastHit>* callback = reinterpret_cast<MeshHitCallback<PxRaycastHit>*>(userData); + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast<PxRaycastHit&>(buffer); + hit.faceIndex = triangleIndex; + PxReal dummy; + return !callback->processHit(hit, p0, p1, p2, dummy, vertexIndices); +} + +void physx::Gu::intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + PX_UNUSED(checkObbIsAligned); + PX_UNUSED(bothTriangleSidesCollide); + BV4_OverlapBoxCB(obb, static_cast<const BV4TriangleMesh*>(mesh)->getBV4Tree(), gVolumeCallback, &callback); +} + + + + +#include "GuVecCapsule.h" +#include "GuSweepMTD.h" + +static bool gCapsuleMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepCapsuleMeshHitCallback* callback = reinterpret_cast<SweepCapsuleMeshHitCallback*>(userData); + PxRaycastHit meshHit; + meshHit.faceIndex = triangleIndex; + return !callback->SweepCapsuleMeshHitCallback::processHit(meshHit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +// PT: TODO: refactor/share bits of this (TA34704) +bool physx::Gu::sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast<const BV4TriangleMesh*>(mesh); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED); + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + + if(isIdentity) + { + const BV4Tree& tree = meshData->getBV4Tree(); + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &pose.p.x, &pose.q.x); + + const PxU32 flags = setupFlags(anyHit, isDoubleSided, meshBothSides!=0); + + SweepHit hitData; + if(lss.p0==lss.p1) + { + if(!sphereSweepVsMesh(hitData, tree, inflatedCapsule.p0, inflatedCapsule.radius, unitDir, distance, TM, flags)) + return false; + } + else + { + if(!capsuleSweepVsMesh(hitData, tree, inflatedCapsule, unitDir, distance, TM, flags)) + return false; + } + + sweepHit.distance = hitData.mDistance; + sweepHit.position = hitData.mPos; + sweepHit.normal = hitData.mNormal; + sweepHit.faceIndex = hitData.mTriangleID; + + if(hitData.mDistance==0.0f) + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + if(meshBothSides) + isDoubleSided = true; + + // PT: TODO: consider using 'setInitialOverlapResults' here + bool hasContacts = false; + if(hitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(inflatedCapsule.p0); + const Vec3V p1 = V3LoadU(inflatedCapsule.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //we need to calculate the MTD + hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, inflatedCapsule.radius, isDoubleSided, sweepHit); + } + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + } + else + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + return true; + } + + // compute sweptAABB + const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0); + const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1); + PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + PxVec3 sweepDir = pose.rotateInv(unitDir); + PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f; + PxReal distance1 = distance; + PxReal distCoef = 1.0f; + Matrix34 poseWithScale; + if(!isIdentity) + { + poseWithScale = pose * triMeshGeom.scale; + distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance); + distCoef = distance1 / distance; + } else + poseWithScale = Matrix34(pose); + + SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoef); + + boxSweepVsMeshCBOld(meshData->getBV4Tree(), NULL, NULL, sweepOrigin, sweepExtents, sweepDir, distance1, gCapsuleMeshSweepCallback, &callback); + + if(meshBothSides) + isDoubleSided = true; + + return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided); +} + +#include "GuSweepSharedTests.h" +static bool gBoxMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepBoxMeshHitCallback* callback = reinterpret_cast<SweepBoxMeshHitCallback*>(userData); + PxRaycastHit meshHit; + meshHit.faceIndex = triangleIndex; + return !callback->SweepBoxMeshHitCallback::processHit(meshHit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +// PT: TODO: refactor/share bits of this (TA34704) +bool physx::Gu::sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast<const BV4TriangleMesh*>(mesh); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + + if(isIdentity && inflation==0.0f) + { + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + + // PT: TODO: this is wrong, we shouldn't actually sweep the inflated version +// const PxVec3 inflated = (box.extents + PxVec3(inflation)) * 1.01f; + // PT: TODO: avoid this copy +// const Box tmp(box.center, inflated, box.rot); + + SweepHit hitData; +// if(!boxSweepVsMesh(hitData, meshData->getBV4Tree(), &pose.p.x, &pose.q.x, tmp, unitDir, distance, anyHit, isDoubleSided, meshBothSides)) + if(!boxSweepVsMesh(hitData, meshData->getBV4Tree(), &pose.p.x, &pose.q.x, box, unitDir, distance, anyHit, isDoubleSided, meshBothSides)) + return false; + + sweepHit.distance = hitData.mDistance; + sweepHit.position = hitData.mPos; + sweepHit.normal = hitData.mNormal; + sweepHit.faceIndex = hitData.mTriangleID; + + if(hitData.mDistance==0.0f) + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + const PxTransform boxTransform = box.getTransform(); + + bool hasContacts = false; + if(hitFlags & PxHitFlag::eMTD) + hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, box, boxTransform, inflation, bothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + } + else + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + } + return true; + } + + // PT: TODO: revisit this codepath, we don't need to sweep an AABB all the time (TA34704) + + Matrix34 meshToWorldSkew; + PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir; + + // Input sweep params: geom, pose, box, unitDir, distance + // We convert the origin from world space to mesh local space + // and convert the box+pose to mesh space AABB + if(isIdentity) + { + meshToWorldSkew = Matrix34(pose); + PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q + meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p); + meshSpaceDir = worldToMeshRot.transform(unitDir) * distance; + PxMat33 boxToMeshRot = worldToMeshRot * box.rot; + sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x + + boxToMeshRot.column1.abs() * box.extents.y + + boxToMeshRot.column2.abs() * box.extents.z; + } + else + { + meshToWorldSkew = pose * triMeshGeom.scale; + const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33(); + const PxVec3& meshToWorldSkew_Trans = pose.p; + + PxMat33 worldToVertexSkew_Rot; + PxVec3 worldToVertexSkew_Trans; + getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans); + + //make vertex space OBB + Box vertexSpaceBox1; + const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans); + vertexSpaceBox1 = transform(worldToVertexSkew, box); + // compute swept aabb + sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent(); + + meshSpaceOrigin = worldToVertexSkew.transform(box.center); + meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length + } + + sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation + sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies + + PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f); + PxReal distCoeff = 1.0f; + if (!isIdentity) + distCoeff = dirLen / distance; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + + const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew; + const PxTransform boxTransform = box.getTransform(); // PT: TODO: this is not needed when there's no hit (TA34704) + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localDirDist = localDir*distance; + SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT + CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + const PxVec3 dir = meshSpaceDir/dirLen; + boxSweepVsMeshCBOld(meshData->getBV4Tree(), NULL, NULL, meshSpaceOrigin, sweptAABBMeshSpaceExtents, dir, dirLen, gBoxMeshSweepCallback, &callback); + + return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided); +} + +static bool gConvexVsMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepConvexMeshHitCallback* callback = reinterpret_cast<SweepConvexMeshHitCallback*>(userData); + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast<PxRaycastHit&>(buffer); + hit.faceIndex = triangleIndex; + return !callback->SweepConvexMeshHitCallback::processHit(hit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +void physx::Gu::sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast<const BV4TriangleMesh*>(mesh); + BV4_GenericSweepCB(hullBox, localDir, distance, meshData->getBV4Tree(), gConvexVsMeshSweepCallback, &callback, anyHit); +} + +#endif + diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseInterface.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseInterface.h new file mode 100644 index 00000000..b7cab6ef --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseInterface.h @@ -0,0 +1,420 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MIDPHASE_INTERFACE_H +#define GU_MIDPHASE_INTERFACE_H + +#include "GuOverlapTests.h" +#include "GuRaycastTests.h" +#include "GuTriangleMesh.h" +#include "PsVecMath.h" + +// PT: this file contains the common interface for all midphase implementations. Specifically the Midphase namespace contains the +// midphase-related entry points, dispatching calls to the proper implementations depending on the triangle mesh's type. The rest of it +// is simply classes & structs shared by all implementations. + +namespace physx +{ + class PxMeshScale; + class PxTriangleMeshGeometry; +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ + struct ConvexHullData; + + struct CallbackMode { enum Enum { eANY, eCLOSEST, eMULTIPLE }; }; + + template<typename HitType> + struct MeshHitCallback + { + CallbackMode::Enum mode; + + MeshHitCallback(CallbackMode::Enum aMode) : mode(aMode) {} + + PX_FORCE_INLINE bool inAnyMode() const { return mode == CallbackMode::eANY; } + PX_FORCE_INLINE bool inClosestMode() const { return mode == CallbackMode::eCLOSEST; } + PX_FORCE_INLINE bool inMultipleMode() const { return mode == CallbackMode::eMULTIPLE; } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const HitType& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32* vIndices) = 0; + + virtual ~MeshHitCallback() {} + }; + + struct SweepConvexMeshHitCallback; + + struct LimitedResults + { + PxU32* mResults; + PxU32 mNbResults; + PxU32 mMaxResults; + PxU32 mStartIndex; + PxU32 mNbSkipped; + bool mOverflow; + + PX_FORCE_INLINE LimitedResults(PxU32* results, PxU32 maxResults, PxU32 startIndex) + : mResults(results), mMaxResults(maxResults), mStartIndex(startIndex) + { + reset(); + } + + PX_FORCE_INLINE void reset() + { + mNbResults = 0; + mNbSkipped = 0; + mOverflow = false; + } + + PX_FORCE_INLINE bool add(PxU32 index) + { + if(mNbResults>=mMaxResults) + { + mOverflow = true; + return false; + } + + if(mNbSkipped>=mStartIndex) + mResults[mNbResults++] = index; + else + mNbSkipped++; + + return true; + } + }; + + // Exposing wrapper for Midphase::intersectOBB just for particles in order to avoid DelayLoad performance problem. This should be removed with particles in PhysX 3.5 (US16993) + PX_PHYSX_COMMON_API void intersectOBB_Particles(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned = true); + + // RTree forward declarations + PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + PX_PHYSX_COMMON_API bool intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectBoxVsMesh_RTREE (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API void intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); + +#if PX_INTEL_FAMILY + // BV4 forward declarations + PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + PX_PHYSX_COMMON_API bool intersectSphereVsMesh_BV4 (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectBoxVsMesh_BV4 (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_BV4 (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API void intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); +#endif + + typedef PxU32 (*MidphaseRaycastFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + + typedef bool (*MidphaseSphereOverlapFunction) (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef bool (*MidphaseBoxOverlapFunction) (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef bool (*MidphaseCapsuleOverlapFunction) (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef void (*MidphaseBoxCBOverlapFunction) (const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + + typedef bool (*MidphaseCapsuleSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + typedef bool (*MidphaseBoxSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + typedef void (*MidphaseConvexSweepFunction)( const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); + +namespace Midphase +{ + PX_FORCE_INLINE bool outputError() + { + static bool reportOnlyOnce = false; + if(!reportOnlyOnce) + { + reportOnlyOnce = true; + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "BV4 midphase only supported on Intel platforms."); + } + return false; + } +} + + #if PX_INTEL_FAMILY + #else + static PxU32 unsupportedMidphase( const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, + const PxVec3&, const PxVec3&, PxReal, + PxHitFlags, PxU32, PxRaycastHit* PX_RESTRICT) + { + return PxU32(Midphase::outputError()); + } + static bool unsupportedSphereOverlapMidphase(const Sphere&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static bool unsupportedBoxOverlapMidphase(const Box&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static bool unsupportedCapsuleOverlapMidphase(const Capsule&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static void unsupportedBoxCBOverlapMidphase(const TriangleMesh*, const Box&, MeshHitCallback<PxRaycastHit>&, bool, bool) + { + Midphase::outputError(); + } + static bool unsupportedBoxSweepMidphase(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const Gu::Box&, const PxVec3&, const PxReal, PxSweepHit&, PxHitFlags, const PxReal) + { + return Midphase::outputError(); + } + static bool unsupportedCapsuleSweepMidphase(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const Gu::Capsule&, const PxVec3&, const PxReal, PxSweepHit&, PxHitFlags, const PxReal) + { + return Midphase::outputError(); + } + static void unsupportedConvexSweepMidphase(const TriangleMesh*, const Gu::Box&, const PxVec3&, const PxReal, SweepConvexMeshHitCallback&, bool) + { + Midphase::outputError(); + } + #endif + + static const MidphaseRaycastFunction gMidphaseRaycastTable[PxMeshMidPhase::eLAST] = + { + raycast_triangleMesh_RTREE, + #if PX_INTEL_FAMILY + raycast_triangleMesh_BV4, + #else + unsupportedMidphase, + #endif + }; + + static const MidphaseSphereOverlapFunction gMidphaseSphereOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectSphereVsMesh_RTREE, + #if PX_INTEL_FAMILY + intersectSphereVsMesh_BV4, + #else + unsupportedSphereOverlapMidphase, + #endif + }; + + static const MidphaseBoxOverlapFunction gMidphaseBoxOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectBoxVsMesh_RTREE, + #if PX_INTEL_FAMILY + intersectBoxVsMesh_BV4, + #else + unsupportedBoxOverlapMidphase, + #endif + }; + + static const MidphaseCapsuleOverlapFunction gMidphaseCapsuleOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectCapsuleVsMesh_RTREE, + #if PX_INTEL_FAMILY + intersectCapsuleVsMesh_BV4, + #else + unsupportedCapsuleOverlapMidphase, + #endif + }; + + static const MidphaseBoxCBOverlapFunction gMidphaseBoxCBOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectOBB_RTREE, + #if PX_INTEL_FAMILY + intersectOBB_BV4, + #else + unsupportedBoxCBOverlapMidphase, + #endif + }; + + static const MidphaseBoxSweepFunction gMidphaseBoxSweepTable[PxMeshMidPhase::eLAST] = + { + sweepBox_MeshGeom_RTREE, + #if PX_INTEL_FAMILY + sweepBox_MeshGeom_BV4, + #else + unsupportedBoxSweepMidphase, + #endif + }; + + static const MidphaseCapsuleSweepFunction gMidphaseCapsuleSweepTable[PxMeshMidPhase::eLAST] = + { + sweepCapsule_MeshGeom_RTREE, + #if PX_INTEL_FAMILY + sweepCapsule_MeshGeom_BV4, + #else + unsupportedCapsuleSweepMidphase, + #endif + }; + + static const MidphaseConvexSweepFunction gMidphaseConvexSweepTable[PxMeshMidPhase::eLAST] = + { + sweepConvex_MeshGeom_RTREE, + #if PX_INTEL_FAMILY + sweepConvex_MeshGeom_BV4, + #else + unsupportedConvexSweepMidphase, + #endif + }; + +namespace Midphase +{ + // \param[in] mesh triangle mesh to raycast against + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] rayOrigin ray's origin + // \param[in] rayDir ray's unit dir + // \param[in] maxDist ray's length/max distance + // \param[in] hitFlags query behavior flags + // \param[in] maxHits max number of hits = size of 'hits' buffer + // \param[out] hits result buffer where to write raycast hits + // \return number of hits written to 'hits' result buffer + // \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits. + PX_FORCE_INLINE PxU32 raycastTriangleMesh( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseRaycastTable[index](mesh, meshGeom, meshTransform, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits); + } + + // \param[in] sphere sphere + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectSphereVsMesh(const Sphere& sphere, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseSphereOverlapTable[index](sphere, mesh, meshTransform, meshScale, results); + } + + // \param[in] box box + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectBoxVsMesh(const Box& box, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseBoxOverlapTable[index](box, mesh, meshTransform, meshScale, results); + } + + // \param[in] capsule capsule + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectCapsuleVsMesh(const Capsule& capsule, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseCapsuleOverlapTable[index](capsule, mesh, meshTransform, meshScale, results); + } + + // \param[in] mesh triangle mesh + // \param[in] box box + // \param[in] callback callback object, called each time a hit is found + // \param[in] bothTriangleSidesCollide true for double-sided meshes + // \param[in] checkObbIsAligned true to use a dedicated codepath for axis-aligned boxes + PX_FORCE_INLINE void intersectOBB(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned = true) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + gMidphaseBoxCBOverlapTable[index](mesh, obb, callback, bothTriangleSidesCollide, checkObbIsAligned); + } + + // \param[in] mesh triangle mesh + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] capsule swept capsule + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + // \return true if a hit was found, false otherwise + PX_FORCE_INLINE bool sweepCapsuleVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const Gu::Capsule& capsule, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseCapsuleSweepTable[index](mesh, meshGeom, meshTransform, capsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + // \param[in] mesh triangle mesh + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] box swept box + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + // \return true if a hit was found, false otherwise + PX_FORCE_INLINE bool sweepBoxVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseBoxSweepTable[index](mesh, meshGeom, meshTransform, box, unitDir, distance, sweepHit, hitFlags, inflation); + } + + // \param[in] mesh triangle mesh + // \param[in] hullBox hull's bounding box + // \param[in] localDir sweep's unit dir, in local/mesh space + // \param[in] distance sweep's length/max distance + // \param[in] callback callback object, called each time a hit is found + // \param[in] anyHit true for PxHitFlag::eMESH_ANY queries + PX_FORCE_INLINE void sweepConvexVsMesh(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + gMidphaseConvexSweepTable[index](mesh, hullBox, localDir, distance, callback, anyHit); + } +} +} +} +#endif // GU_MIDPHASE_INTERFACE_H diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp new file mode 100644 index 00000000..6133f0b8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuMidphaseRTree.cpp @@ -0,0 +1,886 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepMesh.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionRayBoxSIMD.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuSIMDHelpers.h" +#include "GuTriangleVertexPointers.h" +#include "GuTriangleMeshRTree.h" +#include "GuInternal.h" + +// This file contains code specific to the RTree midphase. + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace physx::shdfnd::aos; + +struct MeshRayCollider +{ + template <int tInflate, int tRayTest> + PX_PHYSX_COMMON_API static void collide( + const PxVec3& orig, const PxVec3& dir, // dir is not normalized (full length), both in mesh space (unless meshWorld is non-zero) + PxReal maxT, // maxT is from [0,1], if maxT is 0.0f, AABB traversal will be used + bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback<PxRaycastHit>& callback, + const PxVec3* inflate = NULL); + + PX_PHYSX_COMMON_API static void collideOBB( + const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback<PxRaycastHit>& callback, + bool checkObbIsAligned = true); // perf hint, pass false if obb is rarely axis aligned +}; + +class SimpleRayTriOverlap +{ +public: + PX_FORCE_INLINE SimpleRayTriOverlap(const PxVec3& origin, const PxVec3& dir, bool bothSides, PxReal geomEpsilon) + : mOrigin(origin), mDir(dir), mBothSides(bothSides), mGeomEpsilon(geomEpsilon) + { + } + + PX_FORCE_INLINE Ps::IntBool overlap(const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxRaycastHit& hit) const + { + if(!intersectRayTriangle(mOrigin, mDir, vert0, vert1, vert2, hit.distance, hit.u, hit.v, !mBothSides, mGeomEpsilon)) + return false; + + if(hit.distance<-mGeomEpsilon) // test if the ray intersection t is really negative + return false; + + return true; + } + + PxVec3 mOrigin; + PxVec3 mDir; + bool mBothSides; + PxReal mGeomEpsilon; +}; + +using Gu::RTree; + +// This callback comes from RTree and decodes LeafTriangle indices stored in rtree into actual triangles +// This callback is needed because RTree doesn't know that it stores triangles since it's a general purpose spatial index + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +template <int tInflate, bool tRayTest> +struct RayRTreeCallback : RTree::CallbackRaycast, RTree::Callback +{ + MeshHitCallback<PxRaycastHit>& outerCallback; + PxI32 has16BitIndices; + const void* mTris; + const PxVec3* mVerts; + const PxVec3* mInflate; + const SimpleRayTriOverlap rayCollider; + PxReal maxT; + PxRaycastHit closestHit; // recorded closest hit over the whole traversal (only for callback mode eCLOSEST) + PxVec3 cv0, cv1, cv2; // PT: make sure these aren't last in the class, to safely V4Load them + PxU32 cis[3]; + bool hadClosestHit; + const bool closestMode; + Vec3V inflateV, rayOriginV, rayDirV; + + RayRTreeCallback( + PxReal geomEpsilon, MeshHitCallback<PxRaycastHit>& callback, + PxI32 has16BitIndices_, const void* tris, const PxVec3* verts, + const PxVec3& origin, const PxVec3& dir, PxReal maxT_, bool bothSides, const PxVec3* inflate) + : outerCallback(callback), has16BitIndices(has16BitIndices_), + mTris(tris), mVerts(verts), mInflate(inflate), rayCollider(origin, dir, bothSides, geomEpsilon), + maxT(maxT_), closestMode(callback.inClosestMode()) + { + PX_ASSERT(closestHit.distance == PX_MAX_REAL); + hadClosestHit = false; + if (tInflate) + inflateV = V3LoadU(*mInflate); + rayOriginV = V3LoadU(rayCollider.mOrigin); + rayDirV = V3LoadU(rayCollider.mDir); + } + + PX_FORCE_INLINE void getVertIndices(PxU32 triIndex, PxU32& i0, PxU32 &i1, PxU32 &i2) + { + if(has16BitIndices) + { + const PxU16* p = reinterpret_cast<const PxU16*>(mTris) + triIndex*3; + i0 = p[0]; i1 = p[1]; i2 = p[2]; + } + else + { + const PxU32* p = reinterpret_cast<const PxU32*>(mTris) + triIndex*3; + i0 = p[0]; i1 = p[1]; i2 = p[2]; + } + } + + virtual PX_FORCE_INLINE bool processResults(PxU32 NumTouched, PxU32* Touched, PxF32& newMaxT) + { + PX_ASSERT(NumTouched > 0); + // Loop through touched leaves + PxRaycastHit tempHit; + for(PxU32 leaf = 0; leaf<NumTouched; leaf++) + { + // Each leaf box has a set of triangles + LeafTriangles currentLeaf; + currentLeaf.Data = Touched[leaf]; + PxU32 nbLeafTris = currentLeaf.GetNbTriangles(); + PxU32 baseLeafTriIndex = currentLeaf.GetTriangleIndex(); + + for(PxU32 i = 0; i < nbLeafTris; i++) + { + PxU32 i0, i1, i2; + const PxU32 triangleIndex = baseLeafTriIndex+i; + getVertIndices(triangleIndex, i0, i1, i2); + + const PxVec3& v0 = mVerts[i0], &v1 = mVerts[i1], &v2 = mVerts[i2]; + const PxU32 vinds[3] = { i0, i1, i2 }; + + if (tRayTest) + { + Ps::IntBool overlap; + if (tInflate) + { + // AP: mesh skew is already included here (ray is pre-transformed) + Vec3V v0v = V3LoadU(v0), v1v = V3LoadU(v1), v2v = V3LoadU(v2); + Vec3V minB = V3Min(V3Min(v0v, v1v), v2v), maxB = V3Max(V3Max(v0v, v1v), v2v); + + // PT: we add an epsilon to max distance, to make sure we don't reject triangles that are just at the same + // distance as best triangle so far. We need to keep all of these to make sure we return the one with the + // best normal. + const float relativeEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, maxT); + FloatV tNear, tFar; + overlap = intersectRayAABB2( + V3Sub(minB, inflateV), V3Add(maxB, inflateV), rayOriginV, rayDirV, FLoad(maxT+relativeEpsilon), tNear, tFar); + if (overlap) + { + // can't clip to tFar here because hitting the AABB doesn't guarantee that we can clip + // (since we can still miss the actual tri) + tempHit.distance = maxT; + tempHit.faceIndex = triangleIndex; + tempHit.u = tempHit.v = 0.0f; + } + } else + overlap = rayCollider.overlap(v0, v1, v2, tempHit) && tempHit.distance <= maxT; + if(!overlap) + continue; + } + tempHit.faceIndex = triangleIndex; + tempHit.flags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE; + // Intersection point is valid if dist < segment's length + // We know dist>0 so we can use integers + if (closestMode) + { + if(tempHit.distance < closestHit.distance) + { + closestHit = tempHit; + newMaxT = PxMin(tempHit.distance, newMaxT); + cv0 = v0; cv1 = v1; cv2 = v2; + cis[0] = vinds[0]; cis[1] = vinds[1]; cis[2] = vinds[2]; + hadClosestHit = true; + } + } else + { + PxReal shrunkMaxT = newMaxT; + PxAgain again = outerCallback.processHit(tempHit, v0, v1, v2, shrunkMaxT, vinds); + if (!again) + return false; + if (shrunkMaxT < newMaxT) + { + newMaxT = shrunkMaxT; + maxT = shrunkMaxT; + } + } + + if (outerCallback.inAnyMode()) // early out if in ANY mode + return false; + } + + } // for(PxU32 leaf = 0; leaf<NumTouched; leaf++) + + return true; + } + + virtual bool processResults(PxU32 numTouched, PxU32* touched) + { + PxF32 dummy; + return RayRTreeCallback::processResults(numTouched, touched, dummy); + } + + + virtual ~RayRTreeCallback() + { + if (hadClosestHit) + { + PX_ASSERT(outerCallback.inClosestMode()); + outerCallback.processHit(closestHit, cv0, cv1, cv2, maxT, cis); + } + } + +private: + RayRTreeCallback& operator=(const RayRTreeCallback&); +}; + +#if PX_VC + #pragma warning(pop) +#endif + +void MeshRayCollider::collideOBB( + const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mi, MeshHitCallback<PxRaycastHit>& callback, + bool checkObbIsAligned) +{ + const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out + PxU32 buf[maxResults]; + RayRTreeCallback<false, false> rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + PxVec3(0), PxVec3(0), 0.0f, bothTriangleSidesCollide, NULL); + if (checkObbIsAligned && PxAbs(PxQuat(obb.rot).w) > 0.9999f) + { + PxVec3 aabbExtents = obb.computeAABBExtent(); + mi->getRTree().traverseAABB(obb.center - aabbExtents, obb.center + aabbExtents, maxResults, buf, &rTreeCallback); + } else + mi->getRTree().traverseOBB(obb, maxResults, buf, &rTreeCallback); +} + +template <int tInflate, int tRayTest> +void MeshRayCollider::collide( + const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, + const RTreeTriangleMesh* mi, MeshHitCallback<PxRaycastHit>& callback, + const PxVec3* inflate) +{ + const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out + PxU32 buf[maxResults]; + if (maxT == 0.0f) // AABB traversal path + { + RayRTreeCallback<tInflate, false> rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + orig, dir, maxT, bothSides, inflate); + PxVec3 inflate1 = tInflate ? *inflate : PxVec3(0); // both maxT and inflate can be zero, so need to check tInflate + mi->getRTree().traverseAABB(orig-inflate1, orig+inflate1, maxResults, buf, &rTreeCallback); + } + else // ray traversal path + { + RayRTreeCallback<tInflate, tRayTest> rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + orig, dir, maxT, bothSides, inflate); + mi->getRTree().traverseRay<tInflate>(orig, dir, maxResults, buf, &rTreeCallback, inflate, maxT); + } +} + + +#define TINST(a,b) \ +template void MeshRayCollider::collide<a,b>( \ + const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, const RTreeTriangleMesh* mesh, \ + MeshHitCallback<PxRaycastHit>& callback, const PxVec3* inflate); + +TINST(0,0) +TINST(1,0) +TINST(0,1) +TINST(1,1) + +#undef TINST + +#include "GuRaycastTests.h" +#include "PxTriangleMeshGeometry.h" +#include "GuTriangleMesh.h" +#include "CmScaling.h" + +struct RayMeshColliderCallback : public MeshHitCallback<PxRaycastHit> +{ + PxRaycastHit* mDstBase; + PxU32 mHitNum; + PxU32 mMaxHits; + const PxMeshScale* mScale; + const PxTransform* mPose; + const Matrix34* mWorld2vertexSkew; + PxU32 mHitFlags; + const PxVec3& mRayDir; + bool mIsDoubleSided; + float mDistCoeff; + + RayMeshColliderCallback( + CallbackMode::Enum mode_, PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose, + const Matrix34* world2vertexSkew, PxU32 hitFlags, const PxVec3& rayDir, bool isDoubleSided, float distCoeff) : + MeshHitCallback<PxRaycastHit> (mode_), + mDstBase (hits), + mHitNum (0), + mMaxHits (maxHits), + mScale (scale), + mPose (pose), + mWorld2vertexSkew (world2vertexSkew), + mHitFlags (hitFlags), + mRayDir (rayDir), + mIsDoubleSided (isDoubleSided), + mDistCoeff (distCoeff) + { + } + + // return false for early out + virtual bool processHit( + const PxRaycastHit& lHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal&, const PxU32*) + { + const PxReal u = lHit.u, v = lHit.v; + const PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2; + + //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose); + // PT: TODO: revisit this for N hits + PxRaycastHit hit = lHit; + hit.position = mPose->transform(mScale->transform(localImpact)); + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eDISTANCE|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + hit.normal = PxVec3(0.0f); + hit.distance *= mDistCoeff; + + // Compute additional information if needed + if(mHitFlags & PxHitFlag::eNORMAL) + { + // User requested impact normal + const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0); + + if(mWorld2vertexSkew) + { + hit.normal = mWorld2vertexSkew->rotateTranspose(localNormal); + if (mScale->hasNegativeDeterminant()) + Ps::swap<PxReal>(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space + } + else + hit.normal = hit.normal = mPose->rotate(localNormal); + hit.normal.normalize(); + + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(mIsDoubleSided && hit.normal.dot(mRayDir) > 0.0f) + hit.normal = -hit.normal; + + hit.flags |= PxHitFlag::eNORMAL; + } + + // PT: no callback => store results in provided buffer + if(mHitNum == mMaxHits) + return false; + + mDstBase[mHitNum++] = hit; + return true; + } + +private: + RayMeshColliderCallback& operator=(const RayMeshColliderCallback&); +}; + +PxU32 physx::Gu::raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + + const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh); + + //scaling: transform the ray to vertex space + + PxVec3 orig, dir; + Matrix34 world2vertexSkew; + Matrix34* world2vertexSkewP = NULL; + PxReal distCoeff = 1.0f; + if(meshGeom.scale.isIdentity()) + { + orig = pose.transformInv(rayOrigin); + dir = pose.rotateInv(rayDir); + } + else + { + world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse(); + world2vertexSkewP = &world2vertexSkew; + orig = world2vertexSkew.transform(rayOrigin); + dir = world2vertexSkew.rotate(rayDir); + { + distCoeff = dir.normalize(); + maxDist *= distCoeff; + maxDist += 1e-3f; + distCoeff = 1.0f / distCoeff; + } + } + + const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + RayMeshColliderCallback callback( + (maxHits > 1) ? CallbackMode::eMULTIPLE : (hitFlags & PxHitFlag::eMESH_ANY ? CallbackMode::eANY : CallbackMode::eCLOSEST), + hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff); + + MeshRayCollider::collide<0, 1>(orig, dir, maxDist, bothSides, static_cast<const RTreeTriangleMesh*>(meshData), callback, NULL); + return callback.mHitNum; +} + + +static PX_INLINE void computeSweptAABBAroundOBB( + const Box& obb, PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, PxReal& sweepLen) +{ + PxU32 other1, other2; + // largest axis of the OBB is the sweep direction, sum of abs of two other is the swept AABB extents + PxU32 lai = Ps::largestAxis(obb.extents, other1, other2); + PxVec3 longestAxis = obb.rot[lai]*obb.extents[lai]; + PxVec3 absOther1 = obb.rot[other1].abs()*obb.extents[other1]; + PxVec3 absOther2 = obb.rot[other2].abs()*obb.extents[other2]; + sweepOrigin = obb.center - longestAxis; + sweepExtents = absOther1 + absOther2 + PxVec3(GU_MIN_AABB_EXTENT); // see comments for GU_MIN_AABB_EXTENT + sweepLen = 2.0f; // length is already included in longestAxis + sweepDir = longestAxis; +} + +enum { eSPHERE, eCAPSULE, eBOX }; // values for tSCB + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. + #pragma warning( disable : 4512 ) // assignment operator could not be generated +#endif + +namespace +{ +struct IntersectShapeVsMeshCallback : MeshHitCallback<PxRaycastHit> +{ + PX_NOCOPY(IntersectShapeVsMeshCallback) +public: + IntersectShapeVsMeshCallback(const PxMat33& vertexToShapeSkew, LimitedResults* results, bool flipNormal) + : MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE), + mVertexToShapeSkew (vertexToShapeSkew), + mResults (results), + mAnyHits (false), + mFlipNormal (flipNormal) + { + } + virtual ~IntersectShapeVsMeshCallback(){} + + const PxMat33& mVertexToShapeSkew; // vertex to box without translation for boxes + LimitedResults* mResults; + bool mAnyHits; + bool mFlipNormal; + + PX_FORCE_INLINE bool recordHit(const PxRaycastHit& aHit, Ps::IntBool hit) + { + if(hit) + { + mAnyHits = true; + if(mResults) + mResults->add(aHit.faceIndex); + else + return false; // abort traversal if we are only interested in firstContact (mResults is NULL) + } + return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal + } +}; + +template<bool tScaleIsIdentity> +struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectSphereVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectSphereVsMeshCallback(){} + PxF32 mMinDist2; + PxVec3 mLocalCenter; // PT: sphere center in local/mesh space + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + const Vec3V v0 = V3LoadU(tScaleIsIdentity ? av0 : mVertexToShapeSkew * av0); + const Vec3V v1 = V3LoadU(tScaleIsIdentity ? av1 : mVertexToShapeSkew * (mFlipNormal ? av2 : av1)); + const Vec3V v2 = V3LoadU(tScaleIsIdentity ? av2 : mVertexToShapeSkew * (mFlipNormal ? av1 : av2)); + + FloatV dummy1, dummy2; + Vec3V closestP; + PxReal dist2; + FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2); + return recordHit(aHit, dist2 <= mMinDist2); + } +}; + +template<bool tScaleIsIdentity> +struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectCapsuleVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectCapsuleVsMeshCallback(){} + + Capsule mLocalCapsule; // PT: capsule in mesh/local space + CapsuleTriangleOverlapData mParams; + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + bool hit; + if(tScaleIsIdentity) + { + const PxVec3 normal = (av0 - av1).cross(av0 - av2); + hit = intersectCapsuleTriangle(normal, av0, av1, av2, mLocalCapsule, mParams); + } + else + { + const PxVec3 v0 = mVertexToShapeSkew * av0; + const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2); + const PxVec3 normal = (v0 - v1).cross(v0 - v2); + hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams); + } + return recordHit(aHit, hit); + } +}; + +template<bool tScaleIsIdentity> +struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectBoxVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectBoxVsMeshCallback(){} + + Matrix34 mVertexToBox; + Vec3p mBoxExtents, mBoxCenter; + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + Vec3p v0, v1, v2; + if(tScaleIsIdentity) + { + v0 = mVertexToShapeSkew * av0; // transform from skewed mesh vertex to box space, + v1 = mVertexToShapeSkew * av1; // this includes inverse skew, inverse mesh shape transform and inverse box basis + v2 = mVertexToShapeSkew * av2; + } + else + { + v0 = mVertexToBox.transform(av0); + v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1); + v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2); + } + + // PT: this one is safe because we're using Vec3p for all parameters + const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2); + return recordHit(aHit, hit); + } +}; +} + +#if PX_VC + #pragma warning(pop) +#endif + +template<int tSCB, bool idtMeshScale> +static bool intersectAnyVsMeshT( + const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB, + const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, + LimitedResults* results) +{ + const bool flipNormal = meshScale.hasNegativeDeterminant(); + PxMat33 shapeToVertexSkew, vertexToShapeSkew; + if (!idtMeshScale && tSCB != eBOX) + { + vertexToShapeSkew = meshScale.toMat33(); + shapeToVertexSkew = vertexToShapeSkew.getInverse(); + } + + if (tSCB == eSPHERE) + { + IntersectSphereVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal); + // transform sphere center from world to mesh shape space + const PxVec3 center = meshTransform.transformInv(worldSphere->center); + + // callback will transform verts + callback.mLocalCenter = center; + callback.mMinDist2 = worldSphere->radius*worldSphere->radius; + + PxVec3 sweepOrigin, sweepDir, sweepExtents; + PxReal sweepLen; + if (!idtMeshScale) + { + // AP: compute a swept AABB around an OBB around a skewed sphere + // TODO: we could do better than an AABB around OBB actually because we can slice off the corners.. + const Box worldOBB_(worldSphere->center, PxVec3(worldSphere->radius), PxMat33(PxIdentity)); + Box vertexOBB; + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + computeSweptAABBAroundOBB(vertexOBB, sweepOrigin, sweepExtents, sweepDir, sweepLen); + } else + { + sweepOrigin = center; + sweepDir = PxVec3(1.0f,0,0); + sweepLen = 0.0f; + sweepExtents = PxVec3(PxMax(worldSphere->radius, GU_MIN_AABB_EXTENT)); + } + + MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, sweepLen, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback, &sweepExtents); + + return callback.mAnyHits; + } + else if (tSCB == eCAPSULE) + { + IntersectCapsuleVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal); + const PxF32 radius = worldCapsule->radius; + + // transform world capsule to mesh shape space + callback.mLocalCapsule.p0 = meshTransform.transformInv(worldCapsule->p0); + callback.mLocalCapsule.p1 = meshTransform.transformInv(worldCapsule->p1); + callback.mLocalCapsule.radius = radius; + callback.mParams.init(callback.mLocalCapsule); + + if (idtMeshScale) + { + // traverse a sweptAABB around the capsule + const PxVec3 radius3(radius); + MeshRayCollider::collide<1, 0>(callback.mLocalCapsule.p0, callback.mLocalCapsule.p1-callback.mLocalCapsule.p0, 1.0f, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback, &radius3); + } + else + { + // make vertex space OBB + Box vertexOBB; + Box worldOBB_; + worldOBB_.create(*worldCapsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule) + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback); + } + return callback.mAnyHits; + } + else if (tSCB == eBOX) + { + Box vertexOBB; // query box in vertex space + if (idtMeshScale) + { + // mesh scale is identity - just inverse transform the box without optimization + vertexOBB = transformBoxOrthonormal(*worldOBB, meshTransform.getInverse()); + // mesh vertices will be transformed from skewed vertex space directly to box AABB space + // box inverse rotation is baked into the vertexToShapeSkew transform + // if meshScale is not identity, vertexOBB already effectively includes meshScale transform + PxVec3 boxCenter; + getInverse(vertexToShapeSkew, boxCenter, vertexOBB.rot, vertexOBB.center); + IntersectBoxVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal); + + callback.mBoxCenter = -boxCenter; + callback.mBoxExtents = worldOBB->extents; // extents do not change + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback); + + return callback.mAnyHits; + } else + { + computeVertexSpaceOBB(vertexOBB, *worldOBB, meshTransform, meshScale); + + // mesh scale needs to be included - inverse transform and optimize the box + const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * meshScale.toMat33(); + const PxVec3& vertexToWorldSkew_Trans = meshTransform.p; + + Matrix34 tmp; + buildMatrixFromBox(tmp, *worldOBB); + const Matrix34 inv = tmp.getInverseRT(); + const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans); + + IntersectBoxVsMeshCallback<idtMeshScale> callback(vertexToShapeSkew, results, flipNormal); + callback.mVertexToBox = inv * _vertexToWorldSkew; + callback.mBoxCenter = PxVec3(0.0f); + callback.mBoxExtents = worldOBB->extents; // extents do not change + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast<const RTreeTriangleMesh*>(&triMesh), callback); + + return callback.mAnyHits; + } + } + else + { + PX_ASSERT(0); + return false; + } +} + +template<int tSCB> +static bool intersectAnyVsMesh( + const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB, + const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, + LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + if (meshScale.isIdentity()) + return intersectAnyVsMeshT<tSCB, true>(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results); + else + return intersectAnyVsMeshT<tSCB, false>(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh<eSPHERE>(&sphere, NULL, NULL, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectBoxVsMesh_RTREE(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh<eBOX>(NULL, NULL, &box, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh<eCAPSULE>(NULL, &capsule, NULL, triMesh, meshTransform, meshScale, results); +} + +void physx::Gu::intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback<PxRaycastHit>& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + MeshRayCollider::collideOBB(obb, bothTriangleSidesCollide, static_cast<const RTreeTriangleMesh*>(mesh), callback, checkObbIsAligned); +} + +// PT: TODO: refactor/share bits of this +bool physx::Gu::sweepCapsule_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED); + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + + // compute sweptAABB + const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0); + const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1); + PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + PxVec3 sweepDir = pose.rotateInv(unitDir); + PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f; + PxReal distance1 = distance; + PxReal distCoeff = 1.0f; + Matrix34 poseWithScale; + if(!isIdentity) + { + poseWithScale = pose * triMeshGeom.scale; + distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance); + distCoeff = distance1 / distance; + } else + poseWithScale = Matrix34(pose); + + SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, distance1, true, meshData, callback, &sweepExtents); + + if(meshBothSides) + isDoubleSided = true; + + return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided); +} + +#include "GuSweepSharedTests.h" + +// PT: TODO: refactor/share bits of this +bool physx::Gu::sweepBox_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + + Matrix34 meshToWorldSkew; + PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir; + + // Input sweep params: geom, pose, box, unitDir, distance + // We convert the origin from world space to mesh local space + // and convert the box+pose to mesh space AABB + if(isIdentity) + { + meshToWorldSkew = Matrix34(pose); + PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q + meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p); + meshSpaceDir = worldToMeshRot.transform(unitDir) * distance; + PxMat33 boxToMeshRot = worldToMeshRot * box.rot; + sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x + + boxToMeshRot.column1.abs() * box.extents.y + + boxToMeshRot.column2.abs() * box.extents.z; + } + else + { + meshToWorldSkew = pose * triMeshGeom.scale; + const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33(); + const PxVec3& meshToWorldSkew_Trans = pose.p; + + PxMat33 worldToVertexSkew_Rot; + PxVec3 worldToVertexSkew_Trans; + getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans); + + //make vertex space OBB + Box vertexSpaceBox1; + const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans); + vertexSpaceBox1 = transform(worldToVertexSkew, box); + // compute swept aabb + sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent(); + + meshSpaceOrigin = worldToVertexSkew.transform(box.center); + meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length + } + + sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation + sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies + + PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f); + PxReal distCoeff = 1.0f; + if (!isIdentity) + distCoeff = dirLen / distance; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + + const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew; + const PxTransform boxTransform = box.getTransform(); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localDirDist = localDir*distance; + SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT + CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + MeshRayCollider::collide<1, 1>(meshSpaceOrigin, meshSpaceDir/dirLen, dirLen, bothTriangleSidesCollide, meshData, callback, &sweptAABBMeshSpaceExtents); + + return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided); +} + +#include "GuInternal.h" +void physx::Gu::sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast<const RTreeTriangleMesh*>(mesh); + + // create temporal bounds + Box querySweptBox; + computeSweptBox(querySweptBox, hullBox.extents, hullBox.center, hullBox.rot, localDir, distance); + + MeshRayCollider::collideOBB(querySweptBox, true, meshData, callback); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuOverlapTestsMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuOverlapTestsMesh.cpp new file mode 100644 index 00000000..a44343e8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuOverlapTestsMesh.cpp @@ -0,0 +1,241 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuMidphaseInterface.h" +#include "CmScaling.h" +#include "GuSphere.h" +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" +#include "GuGJK.h" +#include "GuSweepSharedTests.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace physx::shdfnd::aos; + +// PT: TODO: remove this function, replace with Midphase:: call at calling sites (TA34704) +bool Gu::checkOverlapAABB_triangleGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom); + + // PT: TODO: pass AABB directly to interface + const Box obb(box.getCenter(), box.getExtents(), PxMat33(PxIdentity)); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + return Midphase::intersectBoxVsMesh(obb, *meshData, pose, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_SphereMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + const Sphere worldSphere(pose0.p, sphereGeom.radius); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + return Midphase::intersectSphereVsMesh(worldSphere, *meshData, pose1, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_CapsuleMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose0); + return Midphase::intersectCapsuleVsMesh(capsule, *meshData, pose1, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_BoxMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + Box box; + buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q); + return Midphase::intersectBoxVsMesh(box, *meshData, pose1, meshGeom.scale, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +struct ConvexVsMeshOverlapCallback : MeshHitCallback<PxRaycastHit> +{ + PsMatTransformV MeshToBoxV; + Vec3V boxExtents; + + ConvexVsMeshOverlapCallback( + const ConvexMesh& cm, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale, + const PxTransform& tr0, const PxTransform& tr1, bool identityScale, const Box& meshSpaceOBB) + : + MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE), + mAnyHit (false), + mIdentityScale (identityScale) + { + if (!mIdentityScale) // not done in initializer list for performance + mMeshScale = Ps::aos::Mat33V( + V3LoadU(meshScale.getVertex2ShapeSkew().column0), + V3LoadU(meshScale.getVertex2ShapeSkew().column1), + V3LoadU(meshScale.getVertex2ShapeSkew().column2) ); + using namespace Ps::aos; + + const ConvexHullData* hullData = &cm.getHull(); + + const Vec3V vScale0 = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexScale.rotation.x); + + mConvex = ConvexHullV(hullData, V3Zero(), vScale0, vQuat0, convexScale.isIdentity()); + aToB = PsMatTransformV(tr0.transformInv(tr1)); + + mIdentityScale = identityScale; + + { + // Move to AABB space + Matrix34 MeshToBox; + computeWorldToBoxMatrix(MeshToBox, meshSpaceOBB); + + const Vec3V base0 = V3LoadU(MeshToBox.m.column0); + const Vec3V base1 = V3LoadU(MeshToBox.m.column1); + const Vec3V base2 = V3LoadU(MeshToBox.m.column2); + const Mat33V matV(base0, base1, base2); + const Vec3V p = V3LoadU(MeshToBox.p); + MeshToBoxV = PsMatTransformV(p, matV); + boxExtents = V3LoadU(meshSpaceOBB.extents+PxVec3(0.001f)); + } + } + virtual ~ConvexVsMeshOverlapCallback() {} + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit&, const PxVec3& v0a, const PxVec3& v1a, const PxVec3& v2a, PxReal&, const PxU32*) + { + using namespace Ps::aos; + Vec3V v0 = V3LoadU(v0a), v1 = V3LoadU(v1a), v2 = V3LoadU(v2a); + + // test triangle AABB in box space vs box AABB in box local space + const Vec3V triV0 = MeshToBoxV.transform(v0); // AP: MeshToBoxV already includes mesh scale so we have to use unscaled verts here + const Vec3V triV1 = MeshToBoxV.transform(v1); + const Vec3V triV2 = MeshToBoxV.transform(v2); + Vec3V triMn = V3Min(V3Min(triV0, triV1), triV2); + Vec3V triMx = V3Max(V3Max(triV0, triV1), triV2); + Vec3V negExtents = V3Neg(boxExtents); + BoolV minSeparated = V3IsGrtr(triMn, boxExtents), maxSeparated = V3IsGrtr(negExtents, triMx); + BoolV bSeparated = BAnyTrue3(BOr(minSeparated, maxSeparated)); + if (BAllEqTTTT(bSeparated)) + return true; // continue traversal + + if (!mIdentityScale) + { + v0 = M33MulV3(mMeshScale, v0); + v1 = M33MulV3(mMeshScale, v1); + v2 = M33MulV3(mMeshScale, v2); + } + + TriangleV triangle(v0, v1, v2); + Vec3V contactA, contactB, normal; + FloatV dist; + GjkStatus status; + RelativeConvex<TriangleV> convexA(triangle, aToB); + LocalConvex<ConvexHullV> convexB(mConvex); + status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + if (status == GJK_CONTACT)// || FAllGrtrOrEq(mSqTolerance, sqDist)) + { + mAnyHit = true; + return false; // abort traversal + } + return true; // continue traversal + } + + ConvexHullV mConvex; + PsMatTransformV aToB; + Ps::aos::Mat33V mMeshScale; + bool mAnyHit; + bool mIdentityScale; + +private: + ConvexVsMeshOverlapCallback& operator=(const ConvexVsMeshOverlapCallback&); +}; + +// PT: TODO: refactor bits of this with convex-vs-mesh code +bool GeomOverlapCallback_ConvexMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom1); + + ConvexMesh* cm = static_cast<ConvexMesh*>(convexGeom.convexMesh); + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + const bool idtScaleMesh = meshGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if (!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + FastVertex2ShapeScaling meshScaling; + if (!idtScaleMesh) + meshScaling.init(meshGeom.scale); + + const Matrix34 world0(pose0); + const Matrix34 world1(pose1); + + PX_ASSERT(!cm->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABB = cm->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + Box hullOBB; + computeHullOBB(hullOBB, hullAABB, 0.0f, world0, world1, meshScaling, idtScaleMesh); + + ConvexVsMeshOverlapCallback cb(*cm, convexGeom.scale, meshScaling, pose0, pose1, idtScaleMesh, hullOBB); + Midphase::intersectOBB(meshData, hullOBB, cb, true, false); + + return cb.mAnyHit; +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTree.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTree.cpp new file mode 100644 index 00000000..7556f4e0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTree.cpp @@ -0,0 +1,466 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxPreprocessor.h" + +#define RTREE_TEXT_DUMP_ENABLE 0 +#if PX_P64_FAMILY +#define RTREE_PAGES_PER_POOL_SLAB 16384 // preallocate all pages in first batch to make sure we stay within 32 bits for relative pointers.. this is 2 megs +#else +#define RTREE_PAGES_PER_POOL_SLAB 128 +#endif + +#define INSERT_SCAN_LOOKAHEAD 1 // enable one level lookahead scan for determining which child page is best to insert a node into + +#define RTREE_INFLATION_EPSILON 5e-4f + +#include "GuRTree.h" +#include "PsSort.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +#if PX_ENABLE_DYNAMIC_MESH_RTREE +using namespace shdfnd::aos; +#endif +using Ps::Array; +using Ps::sort; +using namespace Gu; + +namespace physx +{ +namespace Gu { + +///////////////////////////////////////////////////////////////////////// +PxU32 RTree::mVersion = 1; + +bool RTree::save(PxOutputStream& stream) const +{ + // save the RTree root structure followed immediately by RTreePage pages to an output stream + bool mismatch = (Ps::littleEndian() == 1); + writeChunk('R', 'T', 'R', 'E', stream); + writeDword(mVersion, mismatch, stream); + writeFloatBuffer(&mBoundsMin.x, 4, mismatch, stream); + writeFloatBuffer(&mBoundsMax.x, 4, mismatch, stream); + writeFloatBuffer(&mInvDiagonal.x, 4, mismatch, stream); + writeFloatBuffer(&mDiagonalScaler.x, 4, mismatch, stream); + writeDword(mPageSize, mismatch, stream); + writeDword(mNumRootPages, mismatch, stream); + writeDword(mNumLevels, mismatch, stream); + writeDword(mTotalNodes, mismatch, stream); + writeDword(mTotalPages, mismatch, stream); + PxU32 unused = 0; // backwards compatibility + writeDword(unused, mismatch, stream); + for (PxU32 j = 0; j < mTotalPages; j++) + { + writeFloatBuffer(mPages[j].minx, RTREE_N, mismatch, stream); + writeFloatBuffer(mPages[j].miny, RTREE_N, mismatch, stream); + writeFloatBuffer(mPages[j].minz, RTREE_N, mismatch, stream); + writeFloatBuffer(mPages[j].maxx, RTREE_N, mismatch, stream); + writeFloatBuffer(mPages[j].maxy, RTREE_N, mismatch, stream); + writeFloatBuffer(mPages[j].maxz, RTREE_N, mismatch, stream); + WriteDwordBuffer(mPages[j].ptrs, RTREE_N, mismatch, stream); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////// +bool RTree::load(PxInputStream& stream, PxU32 meshVersion) +{ + PX_UNUSED(meshVersion); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='R' || b!='T' || c!='R' || d!='E') + return false; + + bool mismatch = (Ps::littleEndian() == 1); + if(readDword(mismatch, stream) != mVersion) + return false; + + readFloatBuffer(&mBoundsMin.x, 4, mismatch, stream); + readFloatBuffer(&mBoundsMax.x, 4, mismatch, stream); + readFloatBuffer(&mInvDiagonal.x, 4, mismatch, stream); + readFloatBuffer(&mDiagonalScaler.x, 4, mismatch, stream); + mPageSize = readDword(mismatch, stream); + mNumRootPages = readDword(mismatch, stream); + mNumLevels = readDword(mismatch, stream); + mTotalNodes = readDword(mismatch, stream); + mTotalPages = readDword(mismatch, stream); + PxU32 unused = readDword(mismatch, stream); PX_UNUSED(unused); // backwards compatibility + mPages = static_cast<RTreePage*>( + Ps::AlignedAllocator<128>().allocate(sizeof(RTreePage)*mTotalPages, __FILE__, __LINE__)); + Cm::markSerializedMem(mPages, sizeof(RTreePage)*mTotalPages); + for (PxU32 j = 0; j < mTotalPages; j++) + { + readFloatBuffer(mPages[j].minx, RTREE_N, mismatch, stream); + readFloatBuffer(mPages[j].miny, RTREE_N, mismatch, stream); + readFloatBuffer(mPages[j].minz, RTREE_N, mismatch, stream); + readFloatBuffer(mPages[j].maxx, RTREE_N, mismatch, stream); + readFloatBuffer(mPages[j].maxy, RTREE_N, mismatch, stream); + readFloatBuffer(mPages[j].maxz, RTREE_N, mismatch, stream); + ReadDwordBuffer(mPages[j].ptrs, RTREE_N, mismatch, stream); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////// +PxU32 RTree::computeBottomLevelCount(PxU32 multiplier) const +{ + PxU32 topCount = 0, curCount = mNumRootPages; + const RTreePage* rightMostPage = &mPages[mNumRootPages-1]; + PX_ASSERT(rightMostPage); + for (PxU32 level = 0; level < mNumLevels-1; level++) + { + topCount += curCount; + PxU32 nc = rightMostPage->nodeCount(); + PX_ASSERT(nc > 0 && nc <= RTREE_N); + // old version pointer, up to PX_MESH_VERSION 8 + PxU32 ptr = (rightMostPage->ptrs[nc-1]) * multiplier; + PX_ASSERT(ptr % sizeof(RTreePage) == 0); + const RTreePage* rightMostPageNext = mPages + (ptr / sizeof(RTreePage)); + curCount = PxU32(rightMostPageNext - rightMostPage); + rightMostPage = rightMostPageNext; + } + + return mTotalPages - topCount; +} + +///////////////////////////////////////////////////////////////////////// +RTree::RTree(const PxEMPTY) +{ + mFlags |= USER_ALLOCATED; +} + + +// PX_SERIALIZATION +///////////////////////////////////////////////////////////////////////// +void RTree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(128); + stream.writeData(mPages, mTotalPages*sizeof(RTreePage)); +} + +///////////////////////////////////////////////////////////////////////// +void RTree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(128); + mPages = context.readExtraData<RTreePage>(mTotalPages); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE PxU32 RTreePage::nodeCount() const +{ + for (int j = 0; j < RTREE_N; j ++) + if (minx[j] == MX) + return PxU32(j); + + return RTREE_N; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::clearNode(PxU32 nodeIndex) +{ + PX_ASSERT(nodeIndex < RTREE_N); + minx[nodeIndex] = miny[nodeIndex] = minz[nodeIndex] = MX; // initialize empty node with sentinels + maxx[nodeIndex] = maxy[nodeIndex] = maxz[nodeIndex] = MN; + ptrs[nodeIndex] = 0; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::getNode(const PxU32 nodeIndex, RTreeNodeQ& r) const +{ + PX_ASSERT(nodeIndex < RTREE_N); + r.minx = minx[nodeIndex]; + r.miny = miny[nodeIndex]; + r.minz = minz[nodeIndex]; + r.maxx = maxx[nodeIndex]; + r.maxy = maxy[nodeIndex]; + r.maxz = maxz[nodeIndex]; + r.ptr = ptrs[nodeIndex]; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::setEmpty(PxU32 startIndex) +{ + PX_ASSERT(startIndex < RTREE_N); + for (PxU32 j = startIndex; j < RTREE_N; j ++) + clearNode(j); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::computeBounds(RTreeNodeQ& newBounds) +{ + RTreeValue _minx = MX, _miny = MX, _minz = MX, _maxx = MN, _maxy = MN, _maxz = MN; + for (PxU32 j = 0; j < RTREE_N; j++) + { + if (isEmpty(j)) + continue; + _minx = PxMin(_minx, minx[j]); + _miny = PxMin(_miny, miny[j]); + _minz = PxMin(_minz, minz[j]); + _maxx = PxMax(_maxx, maxx[j]); + _maxy = PxMax(_maxy, maxy[j]); + _maxz = PxMax(_maxz, maxz[j]); + } + newBounds.minx = _minx; + newBounds.miny = _miny; + newBounds.minz = _minz; + newBounds.maxx = _maxx; + newBounds.maxy = _maxy; + newBounds.maxz = _maxz; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::adjustChildBounds(PxU32 index, const RTreeNodeQ& adjChild) +{ + PX_ASSERT(index < RTREE_N); + minx[index] = adjChild.minx; + miny[index] = adjChild.miny; + minz[index] = adjChild.minz; + maxx[index] = adjChild.maxx; + maxy[index] = adjChild.maxy; + maxz[index] = adjChild.maxz; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::growChildBounds(PxU32 index, const RTreeNodeQ& child) +{ + PX_ASSERT(index < RTREE_N); + minx[index] = PxMin(minx[index], child.minx); + miny[index] = PxMin(miny[index], child.miny); + minz[index] = PxMin(minz[index], child.minz); + maxx[index] = PxMax(maxx[index], child.maxx); + maxy[index] = PxMax(maxy[index], child.maxy); + maxz[index] = PxMax(maxz[index], child.maxz); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex) +{ + PX_ASSERT(targetIndex < RTREE_N); + PX_ASSERT(sourceIndex < RTREE_N); + minx[targetIndex] = sourcePage.minx[sourceIndex]; + miny[targetIndex] = sourcePage.miny[sourceIndex]; + minz[targetIndex] = sourcePage.minz[sourceIndex]; + maxx[targetIndex] = sourcePage.maxx[sourceIndex]; + maxy[targetIndex] = sourcePage.maxy[sourceIndex]; + maxz[targetIndex] = sourcePage.maxz[sourceIndex]; + ptrs[targetIndex] = sourcePage.ptrs[sourceIndex]; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::setNode(PxU32 targetIndex, const RTreeNodeQ& sourceNode) +{ + PX_ASSERT(targetIndex < RTREE_N); + minx[targetIndex] = sourceNode.minx; + miny[targetIndex] = sourceNode.miny; + minz[targetIndex] = sourceNode.minz; + maxx[targetIndex] = sourceNode.maxx; + maxy[targetIndex] = sourceNode.maxy; + maxz[targetIndex] = sourceNode.maxz; + ptrs[targetIndex] = sourceNode.ptr; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreePage& page, int nodeIndex) +{ + PX_ASSERT(nodeIndex < RTREE_N); + minx = PxMin(minx, page.minx[nodeIndex]); + miny = PxMin(miny, page.miny[nodeIndex]); + minz = PxMin(minz, page.minz[nodeIndex]); + maxx = PxMax(maxx, page.maxx[nodeIndex]); + maxy = PxMax(maxy, page.maxy[nodeIndex]); + maxz = PxMax(maxz, page.maxz[nodeIndex]); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreeNodeQ& node) +{ + minx = PxMin(minx, node.minx); miny = PxMin(miny, node.miny); minz = PxMin(minz, node.minz); + maxx = PxMax(maxx, node.maxx); maxy = PxMax(maxy, node.maxy); maxz = PxMax(maxz, node.maxz); +} + +///////////////////////////////////////////////////////////////////////// +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cbLeaf) +#else +void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page) +#endif +{ + PX_UNUSED(parentBounds); + + static PxU32 validateCounter = 0; // this is to suppress a warning that recursive call has no side effects + validateCounter++; + + RTreeNodeQ n; + PxU32 pageNodeCount = page->nodeCount(); + for (PxU32 j = 0; j < pageNodeCount; j++) + { + page->getNode(j, n); + if (page->isEmpty(j)) + continue; + PX_ASSERT(n.minx >= parentBounds.minx); PX_ASSERT(n.miny >= parentBounds.miny); PX_ASSERT(n.minz >= parentBounds.minz); + PX_ASSERT(n.maxx <= parentBounds.maxx); PX_ASSERT(n.maxy <= parentBounds.maxy); PX_ASSERT(n.maxz <= parentBounds.maxz); + if (!n.isLeaf()) + { + PX_ASSERT((n.ptr&1) == 0); + RTreePage* childPage = reinterpret_cast<RTreePage*>(size_t(mPages) + n.ptr); +#if PX_ENABLE_DYNAMIC_MESH_RTREE + validateRecursive(level+1, n, childPage, cbLeaf); + } else if (cbLeaf) + { + Vec3V mnv, mxv; + cbLeaf->recomputeBounds(page->ptrs[j] & ~1, mnv, mxv); + PxVec3 mn3, mx3; V3StoreU(mnv, mn3); V3StoreU(mxv, mx3); + const PxBounds3 lb(mn3, mx3); + const PxVec3& mn = lb.minimum; const PxVec3& mx = lb.maximum; PX_UNUSED(mn); PX_UNUSED(mx); + PX_ASSERT(mn.x >= n.minx); PX_ASSERT(mn.y >= n.miny); PX_ASSERT(mn.z >= n.minz); + PX_ASSERT(mx.x <= n.maxx); PX_ASSERT(mx.y <= n.maxy); PX_ASSERT(mx.z <= n.maxz); + } +#else + validateRecursive(level+1, n, childPage); + } +#endif + } + RTreeNodeQ recomputedBounds; + page->computeBounds(recomputedBounds); + PX_ASSERT((recomputedBounds.minx - parentBounds.minx)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.miny - parentBounds.miny)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.minz - parentBounds.minz)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxx - parentBounds.maxx)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxy - parentBounds.maxy)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxz - parentBounds.maxz)<=RTREE_INFLATION_EPSILON); +} + +///////////////////////////////////////////////////////////////////////// +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::validate(CallbackRefit* cbLeaf) +#else +void RTree::validate() +#endif +{ + for (PxU32 j = 0; j < mNumRootPages; j++) + { + RTreeNodeQ rootBounds; + mPages[j].computeBounds(rootBounds); +#if PX_ENABLE_DYNAMIC_MESH_RTREE + validateRecursive(0, rootBounds, mPages+j, cbLeaf); +#else + validateRecursive(0, rootBounds, mPages+j); +#endif + } +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::refitAllStaticTree(CallbackRefit& cb, PxBounds3* retBounds) +{ + PxU8* treeNodes8 = reinterpret_cast<PxU8*>(mPages); + + // since pages are ordered we can scan back to front and the hierarchy will be updated + for (PxI32 iPage = PxI32(mTotalPages)-1; iPage>=0; iPage--) + { + RTreePage& page = mPages[iPage]; + for (PxU32 j = 0; j < RTREE_N; j++) + { + if (page.isEmpty(j)) + continue; + if (page.isLeaf(j)) + { + Vec3V childMn, childMx; + cb.recomputeBounds(page.ptrs[j]-1, childMn, childMx); // compute the bound around triangles + PxVec3 mn3, mx3; + V3StoreU(childMn, mn3); + V3StoreU(childMx, mx3); + page.minx[j] = mn3.x; page.miny[j] = mn3.y; page.minz[j] = mn3.z; + page.maxx[j] = mx3.x; page.maxy[j] = mx3.y; page.maxz[j] = mx3.z; + } else + { + const RTreePage* child = reinterpret_cast<const RTreePage*>(treeNodes8 + page.ptrs[j]); + PX_COMPILE_TIME_ASSERT(RTREE_N == 4); + bool first = true; + for (PxU32 k = 0; k < RTREE_N; k++) + { + if (child->isEmpty(k)) + continue; + if (first) + { + page.minx[j] = child->minx[k]; page.miny[j] = child->miny[k]; page.minz[j] = child->minz[k]; + page.maxx[j] = child->maxx[k]; page.maxy[j] = child->maxy[k]; page.maxz[j] = child->maxz[k]; + first = false; + } else + { + page.minx[j] = PxMin(page.minx[j], child->minx[k]); + page.miny[j] = PxMin(page.miny[j], child->miny[k]); + page.minz[j] = PxMin(page.minz[j], child->minz[k]); + page.maxx[j] = PxMax(page.maxx[j], child->maxx[k]); + page.maxy[j] = PxMax(page.maxy[j], child->maxy[k]); + page.maxz[j] = PxMax(page.maxz[j], child->maxz[k]); + } + } + } + } + } + + if (retBounds) + { + RTreeNodeQ bound1; + for (PxU32 ii = 0; ii<mNumRootPages; ii++) + { + mPages[ii].computeBounds(bound1); + if (ii == 0) + { + retBounds->minimum = PxVec3(bound1.minx, bound1.miny, bound1.minz); + retBounds->maximum = PxVec3(bound1.maxx, bound1.maxy, bound1.maxz); + } else + { + retBounds->minimum = retBounds->minimum.minimum(PxVec3(bound1.minx, bound1.miny, bound1.minz)); + retBounds->maximum = retBounds->maximum.maximum(PxVec3(bound1.maxx, bound1.maxy, bound1.maxz)); + } + } + } + +#if PX_CHECKED + validate(&cb); +#endif +} +#endif // PX_ENABLE_DYNAMIC_MESH_RTREE + +//~PX_SERIALIZATION +const RTreeValue RTreePage::MN = -PX_MAX_F32; +const RTreeValue RTreePage::MX = PX_MAX_F32; + +} // namespace Gu + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTree.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTree.h new file mode 100644 index 00000000..48c54fc5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTree.h @@ -0,0 +1,304 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_RTREE_H +#define GU_RTREE_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec4.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxAssert.h" +#include "PsUserAllocated.h" // for PxSerializationContext +#include "PxSerialFramework.h" +#include "PxTriangleMesh.h" +#include "PsAlignedMalloc.h" + + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +#include "PsVecMath.h" +#endif + +#define RTREE_N 4 // changing this number will affect the mesh format +PX_COMPILE_TIME_ASSERT(RTREE_N == 4 || RTREE_N == 8); // using the low 5 bits for storage of index(childPtr) for dynamic rtree + +namespace physx +{ + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +namespace Gu { + + class Box; + struct RTreePage; + + typedef PxF32 RTreeValue; + + ///////////////////////////////////////////////////////////////////////// + // quantized untransposed RTree node - used for offline build and dynamic insertion + struct RTreeNodeQ + { + RTreeValue minx, miny, minz, maxx, maxy, maxz; + PxU32 ptr; // lowest bit is leaf flag + + PX_FORCE_INLINE void setLeaf(bool set) { if (set) ptr |= 1; else ptr &= ~1; } + PX_FORCE_INLINE PxU32 isLeaf() const { return ptr & 1; } + PX_FORCE_INLINE void setEmpty(); + PX_FORCE_INLINE void grow(const RTreePage& page, int nodeIndex); + PX_FORCE_INLINE void grow(const RTreeNodeQ& node); + }; + + ///////////////////////////////////////////////////////////////////////// + // RTreePage data structure, holds RTREE_N transposed nodes + + // RTreePage data structure, holds 8 transposed nodes + PX_ALIGN_PREFIX(16) + struct RTreePage + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + static const RTreeValue MN, MX; + + RTreeValue minx[RTREE_N]; // [min=MX, max=MN] is used as a sentinel range for empty bounds + RTreeValue miny[RTREE_N]; + RTreeValue minz[RTREE_N]; + RTreeValue maxx[RTREE_N]; + RTreeValue maxy[RTREE_N]; + RTreeValue maxz[RTREE_N]; + PxU32 ptrs[RTREE_N]; // for static rtree this is an offset relative to the first page divided by 16, for dynamics it's an absolute pointer divided by 16 + + PX_FORCE_INLINE PxU32 nodeCount() const; // returns the number of occupied nodes in this page + PX_FORCE_INLINE void setEmpty(PxU32 startIndex = 0); + PX_FORCE_INLINE bool isEmpty(PxU32 index) const { return minx[index] > maxx[index]; } + PX_FORCE_INLINE void copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex); + PX_FORCE_INLINE void setNode(PxU32 targetIndex, const RTreeNodeQ& node); + PX_FORCE_INLINE void clearNode(PxU32 nodeIndex); + PX_FORCE_INLINE void getNode(PxU32 nodeIndex, RTreeNodeQ& result) const; + PX_FORCE_INLINE void computeBounds(RTreeNodeQ& bounds); + PX_FORCE_INLINE void adjustChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds); + PX_FORCE_INLINE void growChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds); + PX_FORCE_INLINE PxU32 getNodeHandle(PxU32 index) const; + PX_FORCE_INLINE PxU32 isLeaf(PxU32 index) const { return ptrs[index] & 1; } + } PX_ALIGN_SUFFIX(16); + + ///////////////////////////////////////////////////////////////////////// + // RTree root data structure + PX_ALIGN_PREFIX(16) + struct RTree + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + // PX_SERIALIZATION + RTree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_INLINE RTree(); // offline static rtree constructor used with cooking + + ~RTree() { release(); } + + PX_INLINE void release(); + bool save(PxOutputStream& stream) const; // always saves as big endian + bool load(PxInputStream& stream, PxU32 meshVersion); // converts to proper endian at load time + + //////////////////////////////////////////////////////////////////////////// + // QUERIES + struct Callback + { + // result buffer should have room for at least RTREE_N items + // should return true to continue traversal. If false is returned, traversal is aborted + virtual bool processResults(PxU32 count, PxU32* buf) = 0; + virtual void profile() {} + virtual ~Callback() {} + }; + + struct CallbackRaycast + { + // result buffer should have room for at least RTREE_N items + // should return true to continue traversal. If false is returned, traversal is aborted + // newMaxT serves as both input and output, as input it's the maxT so far + // set it to a new value (which should be smaller) and it will become the new far clip t + virtual bool processResults(PxU32 count, PxU32* buf, PxF32& newMaxT) = 0; + virtual ~CallbackRaycast() {} + }; + + // callback will be issued as soon as the buffer overflows maxResultsPerBlock-RTreePage:SIZE entries + // use maxResults = RTreePage:SIZE and return false from callback for "first hit" early out + void traverseAABB( + const PxVec3& boxMin, const PxVec3& boxMax, + const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const; + void traverseOBB( + const Gu::Box& obb, + const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const; + template <int inflate> + //PX_PHYSX_COMMON_API + void traverseRay( + const PxVec3& rayOrigin, const PxVec3& rayDir, // dir doesn't have to be normalized and is B-A for raySegment + const PxU32 maxResults, PxU32* resultsPtr, + Gu::RTree::CallbackRaycast* callback, + const PxVec3* inflateAABBs, // inflate tree's AABBs by this amount. This function turns into AABB sweep. + PxF32 maxT = PX_MAX_F32 // maximum ray t parameter, p(t)=origin+t*dir; use 1.0f for ray segment + ) const; + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + struct CallbackRefit + { + // In this callback index is the number stored in the RTree, which is a LeafTriangles object for current PhysX mesh + virtual void recomputeBounds(PxU32 index, shdfnd::aos::Vec3V& mn, shdfnd::aos::Vec3V& mx) = 0; + virtual ~CallbackRefit() {} + }; + void refitAllStaticTree(CallbackRefit& cb, PxBounds3* resultMeshBounds); // faster version of refit for static RTree only +#endif + + + //////////////////////////////////////////////////////////////////////////// + // DEBUG HELPER FUNCTIONS +#if PX_ENABLE_DYNAMIC_MESH_RTREE + PX_PHYSX_COMMON_API void validate(CallbackRefit* cb = NULL); // verify that all children are indeed included in parent bounds +#else + PX_PHYSX_COMMON_API void validate(); // verify that all children are indeed included in parent bounds +#endif + void openTextDump(); + void closeTextDump(); + void textDump(const char* prefix); + void maxscriptExport(); + PxU32 computeBottomLevelCount(PxU32 storedToMemMultiplier) const; + + //////////////////////////////////////////////////////////////////////////// + // DATA + // remember to update save() and load() when adding or removing data + PxVec4 mBoundsMin, mBoundsMax, mInvDiagonal, mDiagonalScaler; // 16 + PxU32 mPageSize; + PxU32 mNumRootPages; + PxU32 mNumLevels; + PxU32 mTotalNodes; // 16 + PxU32 mTotalPages; + PxU32 mFlags; enum { USER_ALLOCATED = 0x1, IS_EDGE_SET = 0x2 }; + RTreePage* mPages; + + static PxU32 mVersion; + + protected: + typedef PxU32 NodeHandle; +#if PX_ENABLE_DYNAMIC_MESH_RTREE + void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cb = NULL); +#else + void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page); +#endif + + friend struct RTreePage; + } PX_ALIGN_SUFFIX(16); + +#if PX_VC +#pragma warning(pop) +#endif + + ///////////////////////////////////////////////////////////////////////// + PX_INLINE RTree::RTree() + { + mFlags = 0; + mPages = NULL; + mTotalNodes = 0; + mNumLevels = 0; + mPageSize = RTREE_N; + } + + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void RTree::release() + { + if ((mFlags & USER_ALLOCATED) == 0 && mPages) + { + physx::shdfnd::AlignedAllocator<128>().deallocate(mPages); + mPages = NULL; + } + } + + // explicit instantiations for traverseRay + // XXX: dima: g++ 4.4 won't compile this => skipping by PX_UNIX_FAMILY +#if PX_X86 && !PX_UNIX_FAMILY + template + //PX_PHYSX_COMMON_API + void RTree::traverseRay<0>( + const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32 maxT) const; + template + //PX_PHYSX_COMMON_API + void RTree::traverseRay<1>( + const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32 maxT) const; +#endif + + ///////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE void RTreeNodeQ::setEmpty() + { + minx = miny = minz = RTreePage::MX; + maxx = maxy = maxz = RTreePage::MN; + } + + + // bit 1 is always expected to be set to differentiate between leaf and non-leaf node + PX_FORCE_INLINE PxU32 LeafGetNbTriangles(PxU32 Data) { return ((Data>>1) & 15)+1; } + PX_FORCE_INLINE PxU32 LeafGetTriangleIndex(PxU32 Data) { return Data>>5; } + PX_FORCE_INLINE PxU32 LeafSetData(PxU32 nb, PxU32 index) + { + PX_ASSERT(nb>0 && nb<=16); PX_ASSERT(index < (1<<27)); + return (index<<5)|(((nb-1)&15)<<1) | 1; + } + + struct LeafTriangles + { + PxU32 Data; + + // Gets number of triangles in the leaf, returns the number of triangles N, with 0 < N <= 16 + PX_FORCE_INLINE PxU32 GetNbTriangles() const { return LeafGetNbTriangles(Data); } + + // Gets triangle index for this leaf. Indexed model's array of indices retrieved with RTreeMidphase::GetIndices() + PX_FORCE_INLINE PxU32 GetTriangleIndex() const { return LeafGetTriangleIndex(Data); } + PX_FORCE_INLINE void SetData(PxU32 nb, PxU32 index) { Data = LeafSetData(nb, index); } + }; + + PX_COMPILE_TIME_ASSERT(sizeof(LeafTriangles)==4); // RTree has space for 4 bytes + +} // namespace Gu + +} + +#endif // #ifdef PX_COLLISION_RTREE diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTreeQueries.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTreeQueries.cpp new file mode 100644 index 00000000..9d7bd57a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuRTreeQueries.cpp @@ -0,0 +1,581 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/* +General notes: + + rtree depth-first traversal looks like this: + push top level page onto stack + + pop page from stack + for each node in page + if node overlaps with testrect + push node's subpage + + we want to efficiently keep track of current stack level to know if the current page is a leaf or not + (since we don't store a flag with the page due to no space, we can't determine it just by looking at current page) + since we traverse depth first, the levels for nodes on the stack look like this: + l0 l0 l1 l2 l2 l3 l3 l3 l4 + + we can encode this as an array of 4 bits per level count into a 32-bit integer + to simplify the code->level computation we also keep track of current level by incrementing the level whenever any subpages + from current test page are pushed onto the stack + when we pop a page off the stack we use this encoding to determine if we should decrement the stack level +*/ + +#include "foundation/PxBounds3.h" +#include "GuRTree.h" +#include "PsIntrinsics.h" +#include "GuBox.h" +#include "PsVecMath.h" +#include "PxQueryReport.h" // for PxAgain +#include "PsBitUtils.h" + +//#define VERIFY_RTREE +#ifdef VERIFY_RTREE +#include "GuIntersectionRayBox.h" +#include "GuIntersectionBoxBox.h" +#include "stdio.h" +#endif + +using namespace physx; +using namespace physx::shdfnd; +using namespace Ps::aos; + +namespace physx +{ +namespace Gu { + +using namespace Ps::aos; + +#define v_absm(a) V4Andc(a, signMask) +#define V4FromF32A(x) V4LoadA(x) +#define PxF32FV(x) FStore(x) +#define CAST_U8(a) reinterpret_cast<PxU8*>(a) + +///////////////////////////////////////////////////////////////////////// +void RTree::traverseAABB(const PxVec3& boxMin, const PxVec3& boxMax, const PxU32 maxResults, PxU32* resultsPtr, Callback* callback) const +{ + PX_UNUSED(resultsPtr); + + PX_ASSERT(callback); + PX_ASSERT(maxResults >= mPageSize); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack1[maxStack]; + PxU32* stack = stack1+1; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + // conservatively quantize the input box + Vec4V nqMin = Vec4V_From_PxVec3_WUndefined(boxMin); + Vec4V nqMax = Vec4V_From_PxVec3_WUndefined(boxMax); + + Vec4V nqMinx4 = V4SplatElement<0>(nqMin); + Vec4V nqMiny4 = V4SplatElement<1>(nqMin); + Vec4V nqMinz4 = V4SplatElement<2>(nqMin); + Vec4V nqMaxx4 = V4SplatElement<0>(nqMax); + Vec4V nqMaxy4 = V4SplatElement<1>(nqMax); + Vec4V nqMaxz4 = V4SplatElement<2>(nqMax); + + // on 64-bit platforms the dynamic rtree pointer is also relative to mPages + PxU8* treeNodes8 = CAST_U8(mPages); + PxU32* stackPtr = stack; + + // AP potential perf optimization - fetch the top level right away + PX_ASSERT(RTREE_N == 4 || RTREE_N == 8); + PX_ASSERT(Ps::isPowerOfTwo(mPageSize)); + + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + *stackPtr++ = j*sizeof(RTreePage); + + PxU32 cacheTopValid = true; + PxU32 cacheTop = 0; + + do { + stackPtr--; + PxU32 top; + if (cacheTopValid) // branch is faster than lhs + top = cacheTop; + else + top = stackPtr[0]; + PX_ASSERT(!cacheTopValid || stackPtr[0] == cacheTop); + RTreePage* PX_RESTRICT tn = reinterpret_cast<RTreePage*>(treeNodes8 + top); + const PxU32* ptrs = (reinterpret_cast<RTreePage*>(tn))->ptrs; + + Vec4V minx4 = V4LoadA(tn->minx); + Vec4V miny4 = V4LoadA(tn->miny); + Vec4V minz4 = V4LoadA(tn->minz); + Vec4V maxx4 = V4LoadA(tn->maxx); + Vec4V maxy4 = V4LoadA(tn->maxy); + Vec4V maxz4 = V4LoadA(tn->maxz); + + // AABB/AABB overlap test + BoolV res0 = V4IsGrtr(nqMinx4, maxx4); BoolV res1 = V4IsGrtr(nqMiny4, maxy4); BoolV res2 = V4IsGrtr(nqMinz4, maxz4); + BoolV res3 = V4IsGrtr(minx4, nqMaxx4); BoolV res4 = V4IsGrtr(miny4, nqMaxy4); BoolV res5 = V4IsGrtr(minz4, nqMaxz4); + BoolV resx = BOr(BOr(BOr(res0, res1), BOr(res2, res3)), BOr(res4, res5)); + PX_ALIGN_PREFIX(16) PxU32 resa[RTREE_N] PX_ALIGN_SUFFIX(16); + + VecU32V res4x = VecU32V_From_BoolV(resx); + U4StoreA(res4x, resa); + + cacheTopValid = false; + for (PxU32 i = 0; i < RTREE_N; i++) + { + PxU32 ptr = ptrs[i] & ~1; // clear the isLeaf bit + if (resa[i]) + continue; + if (tn->isLeaf(i)) + { + if (!callback->processResults(1, &ptr)) + return; + } + else + { + *(stackPtr++) = ptr; + cacheTop = ptr; + cacheTopValid = true; + } + } + } while (stackPtr > stack); +} + +namespace +{ + const VecU32V signMask = U4LoadXYZW((PxU32(1)<<31), (PxU32(1)<<31), (PxU32(1)<<31), (PxU32(1)<<31)); + const Vec4V epsFloat4 = V4Load(1e-9f); + const Vec4V zeroes = V4Zero(); + const Vec4V twos = V4Load(2.0f); + const Vec4V epsInflateFloat4 = V4Load(1e-7f); +} + +///////////////////////////////////////////////////////////////////////// +template <int inflate> +void RTree::traverseRay( + const PxVec3& rayOrigin, const PxVec3& rayDir, + const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::CallbackRaycast* callback, + const PxVec3* fattenAABBs, PxF32 maxT) const +{ + // implements Kay-Kajiya 4-way SIMD test + PX_UNUSED(resultsPtr); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack1[maxStack]; + PxU32* stack = stack1+1; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + PxU8* treeNodes8 = CAST_U8(mPages); + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + PX_UNUSED(fattenAABBsX); PX_UNUSED(fattenAABBsY); PX_UNUSED(fattenAABBsZ); + if (inflate) + { + Vec4V fattenAABBs4 = Vec4V_From_PxVec3_WUndefined(*fattenAABBs); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + Vec4V maxT4; + maxT4 = V4Load(maxT); + Vec4V rayP = Vec4V_From_PxVec3_WUndefined(rayOrigin); + Vec4V rayD = Vec4V_From_PxVec3_WUndefined(rayDir); + VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask); + Vec4V rayDAbs = V4Abs(rayD); // abs value of rayD + Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4)))); // clamp near-zero components up to epsilon + rayD = rayInvD; + + //rayInvD = V4Recip(rayInvD); + // Newton-Raphson iteration for reciprocal (see wikipedia): + // X[n+1] = X[n]*(2-original*X[n]), X[0] = V4RecipFast estimate + //rayInvD = rayInvD*(twos-rayD*rayInvD); + rayInvD = V4RecipFast(rayInvD); // initial estimate, not accurate enough + rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos)); + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes); + Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD); + Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD); + Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD); + Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD); + Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD); + Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD); + + PX_ASSERT(RTREE_N == 4 || RTREE_N == 8); + PX_ASSERT(mNumRootPages > 0); + + PxU32 stackPtr = 0; + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + stack[stackPtr++] = j*sizeof(RTreePage); + + PX_ALIGN_PREFIX(16) PxU32 resa[4] PX_ALIGN_SUFFIX(16); + + while (stackPtr) + { + PxU32 top = stack[--stackPtr]; + if (top&1) // isLeaf test + { + top--; + PxF32 newMaxT = maxT; + if (!callback->processResults(1, &top, newMaxT)) + return; + /* shrink the ray if newMaxT is reduced compared to the original maxT */ + if (maxT != newMaxT) + { + PX_ASSERT(newMaxT < maxT); + maxT = newMaxT; + maxT4 = V4Load(newMaxT); + } + continue; + } + + RTreePage* PX_RESTRICT tn = reinterpret_cast<RTreePage*>(treeNodes8 + top); + + // 6i load + Vec4V minx4a = V4LoadA(tn->minx), miny4a = V4LoadA(tn->miny), minz4a = V4LoadA(tn->minz); + Vec4V maxx4a = V4LoadA(tn->maxx), maxy4a = V4LoadA(tn->maxy), maxz4a = V4LoadA(tn->maxz); + + // 1i disabled test + // AP scaffold - optimization opportunity - can save 2 instructions here + VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot in the page) + + if (inflate) + { + // 6i + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + // 6i + Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX); + Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY); + Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ); + Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX); + Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY); + Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ); + + // test half-spaces + // P+tD=dN + // t = (d(N,D)-(P,D))/(D,D) , (D,D)=1 + + // compute 4x dot products (N,D) and (P,N) for each AABB in the page + + // 6i + // now compute tnear and tfar for each pair of planes for each box + Vec4V tminxa = V4Min(tminxa0, tmaxxa0); Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0); + Vec4V tminya = V4Min(tminya0, tmaxya0); Vec4V tmaxya = V4Max(tminya0, tmaxya0); + Vec4V tminza = V4Min(tminza0, tmaxza0); Vec4V tmaxza = V4Max(tminza0, tmaxza0); + + // 8i + Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza); + Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza); + ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(epsFloat4, minOfFarsa)); // if tfar is negative, ignore since its a ray, not a line + // AP scaffold: update the build to eliminate 3 more instructions for ignore4a above + //VecU32V ignore4a = V4IsGrtrV32u(epsFloat4, minOfFarsa); // if tfar is negative, ignore since its a ray, not a line + ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(maxOfNeasa, maxT4)); // if tnear is over maxT, ignore this result + + // 2i + VecU32V resa4 = V4IsGrtrV32u(maxOfNeasa, minOfFarsa); // if 1 => fail + resa4 = V4U32or(resa4, ignore4a); + + // 1i + V4U32StoreAligned(resa4, reinterpret_cast<VecU32V*>(resa)); + + PxU32* ptrs = (reinterpret_cast<RTreePage*>(tn))->ptrs; + + stack[stackPtr] = ptrs[0]; stackPtr += (1+resa[0]); // AP scaffold TODO: use VecU32add + stack[stackPtr] = ptrs[1]; stackPtr += (1+resa[1]); + stack[stackPtr] = ptrs[2]; stackPtr += (1+resa[2]); + stack[stackPtr] = ptrs[3]; stackPtr += (1+resa[3]); + } +} + +template void RTree::traverseRay<0>( + const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32 maxT) const; +template void RTree::traverseRay<1>( + const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32 maxT) const; + +///////////////////////////////////////////////////////////////////////// +void RTree::traverseOBB( + const Gu::Box& obb, const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::Callback* callback) const +{ + PX_UNUSED(resultsPtr); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack[maxStack]; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + PxU8* treeNodes8 = CAST_U8(mPages); + PxU32* stackPtr = stack; + + Vec4V ones, halves, eps; + ones = V4Load(1.0f); + halves = V4Load(0.5f); + eps = V4Load(1e-6f); + + PX_UNUSED(ones); + + Vec4V obbO = Vec4V_From_PxVec3_WUndefined(obb.center); + Vec4V obbE = Vec4V_From_PxVec3_WUndefined(obb.extents); + // Gu::Box::rot matrix columns are the OBB axes + Vec4V obbX = Vec4V_From_PxVec3_WUndefined(obb.rot.column0); + Vec4V obbY = Vec4V_From_PxVec3_WUndefined(obb.rot.column1); + Vec4V obbZ = Vec4V_From_PxVec3_WUndefined(obb.rot.column2); + +#if PX_WINDOWS || PX_XBOXONE + // Visual Studio compiler hangs with #defines + // On VMX platforms we use #defines in the other branch of this #ifdef to avoid register spills (LHS) + Vec4V obbESplatX = V4SplatElement<0>(obbE); + Vec4V obbESplatY = V4SplatElement<1>(obbE); + Vec4V obbESplatZ = V4SplatElement<2>(obbE); + Vec4V obbESplatNegX = V4Sub(zeroes, obbESplatX); + Vec4V obbESplatNegY = V4Sub(zeroes, obbESplatY); + Vec4V obbESplatNegZ = V4Sub(zeroes, obbESplatZ); + Vec4V obbXE = V4MulAdd(obbX, obbESplatX, zeroes); // scale axii by E + Vec4V obbYE = V4MulAdd(obbY, obbESplatY, zeroes); // scale axii by E + Vec4V obbZE = V4MulAdd(obbZ, obbESplatZ, zeroes); // scale axii by E + Vec4V obbOSplatX = V4SplatElement<0>(obbO); + Vec4V obbOSplatY = V4SplatElement<1>(obbO); + Vec4V obbOSplatZ = V4SplatElement<2>(obbO); + Vec4V obbXSplatX = V4SplatElement<0>(obbX); + Vec4V obbXSplatY = V4SplatElement<1>(obbX); + Vec4V obbXSplatZ = V4SplatElement<2>(obbX); + Vec4V obbYSplatX = V4SplatElement<0>(obbY); + Vec4V obbYSplatY = V4SplatElement<1>(obbY); + Vec4V obbYSplatZ = V4SplatElement<2>(obbY); + Vec4V obbZSplatX = V4SplatElement<0>(obbZ); + Vec4V obbZSplatY = V4SplatElement<1>(obbZ); + Vec4V obbZSplatZ = V4SplatElement<2>(obbZ); + Vec4V obbXESplatX = V4SplatElement<0>(obbXE); + Vec4V obbXESplatY = V4SplatElement<1>(obbXE); + Vec4V obbXESplatZ = V4SplatElement<2>(obbXE); + Vec4V obbYESplatX = V4SplatElement<0>(obbYE); + Vec4V obbYESplatY = V4SplatElement<1>(obbYE); + Vec4V obbYESplatZ = V4SplatElement<2>(obbYE); + Vec4V obbZESplatX = V4SplatElement<0>(obbZE); + Vec4V obbZESplatY = V4SplatElement<1>(obbZE); + Vec4V obbZESplatZ = V4SplatElement<2>(obbZE); +#else + #define obbESplatX V4SplatElement<0>(obbE) + #define obbESplatY V4SplatElement<1>(obbE) + #define obbESplatZ V4SplatElement<2>(obbE) + #define obbESplatNegX V4Sub(zeroes, obbESplatX) + #define obbESplatNegY V4Sub(zeroes, obbESplatY) + #define obbESplatNegZ V4Sub(zeroes, obbESplatZ) + #define obbXE V4MulAdd(obbX, obbESplatX, zeroes) + #define obbYE V4MulAdd(obbY, obbESplatY, zeroes) + #define obbZE V4MulAdd(obbZ, obbESplatZ, zeroes) + #define obbOSplatX V4SplatElement<0>(obbO) + #define obbOSplatY V4SplatElement<1>(obbO) + #define obbOSplatZ V4SplatElement<2>(obbO) + #define obbXSplatX V4SplatElement<0>(obbX) + #define obbXSplatY V4SplatElement<1>(obbX) + #define obbXSplatZ V4SplatElement<2>(obbX) + #define obbYSplatX V4SplatElement<0>(obbY) + #define obbYSplatY V4SplatElement<1>(obbY) + #define obbYSplatZ V4SplatElement<2>(obbY) + #define obbZSplatX V4SplatElement<0>(obbZ) + #define obbZSplatY V4SplatElement<1>(obbZ) + #define obbZSplatZ V4SplatElement<2>(obbZ) + #define obbXESplatX V4SplatElement<0>(obbXE) + #define obbXESplatY V4SplatElement<1>(obbXE) + #define obbXESplatZ V4SplatElement<2>(obbXE) + #define obbYESplatX V4SplatElement<0>(obbYE) + #define obbYESplatY V4SplatElement<1>(obbYE) + #define obbYESplatZ V4SplatElement<2>(obbYE) + #define obbZESplatX V4SplatElement<0>(obbZE) + #define obbZESplatY V4SplatElement<1>(obbZE) + #define obbZESplatZ V4SplatElement<2>(obbZE) +#endif + + PX_ASSERT(mPageSize == 4 || mPageSize == 8); + PX_ASSERT(mNumRootPages > 0); + + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + *stackPtr++ = j*sizeof(RTreePage); + PxU32 cacheTopValid = true; + PxU32 cacheTop = 0; + + PX_ALIGN_PREFIX(16) PxU32 resa_[4] PX_ALIGN_SUFFIX(16); + + do { + stackPtr--; + + PxU32 top; + if (cacheTopValid) // branch is faster than lhs + top = cacheTop; + else + top = stackPtr[0]; + PX_ASSERT(!cacheTopValid || top == cacheTop); + RTreePage* PX_RESTRICT tn = reinterpret_cast<RTreePage*>(treeNodes8 + top); + + const PxU32 offs = 0; + PxU32* ptrs = (reinterpret_cast<RTreePage*>(tn))->ptrs; + + // 6i + Vec4V minx4a = V4LoadA(tn->minx+offs); + Vec4V miny4a = V4LoadA(tn->miny+offs); + Vec4V minz4a = V4LoadA(tn->minz+offs); + Vec4V maxx4a = V4LoadA(tn->maxx+offs); + Vec4V maxy4a = V4LoadA(tn->maxy+offs); + Vec4V maxz4a = V4LoadA(tn->maxz+offs); + + VecU32V noOverlapa; + VecU32V resa4u; + { + // PRECOMPUTE FOR A BLOCK + // 109 instr per 4 OBB/AABB + // ABB iteration 1, start with OBB origin as other point -- 6 + Vec4V p1ABBxa = V4Max(minx4a, V4Min(maxx4a, obbOSplatX)); + Vec4V p1ABBya = V4Max(miny4a, V4Min(maxy4a, obbOSplatY)); + Vec4V p1ABBza = V4Max(minz4a, V4Min(maxz4a, obbOSplatZ)); + + // OBB iteration 1, move to OBB space first -- 12 + Vec4V p1ABBOxa = V4Sub(p1ABBxa, obbOSplatX); + Vec4V p1ABBOya = V4Sub(p1ABBya, obbOSplatY); + Vec4V p1ABBOza = V4Sub(p1ABBza, obbOSplatZ); + Vec4V obbPrjXa = V4MulAdd(p1ABBOxa, obbXSplatX, V4MulAdd(p1ABBOya, obbXSplatY, V4MulAdd(p1ABBOza, obbXSplatZ, zeroes))); + Vec4V obbPrjYa = V4MulAdd(p1ABBOxa, obbYSplatX, V4MulAdd(p1ABBOya, obbYSplatY, V4MulAdd(p1ABBOza, obbYSplatZ, zeroes))); + Vec4V obbPrjZa = V4MulAdd(p1ABBOxa, obbZSplatX, V4MulAdd(p1ABBOya, obbZSplatY, V4MulAdd(p1ABBOza, obbZSplatZ, zeroes))); + // clamp AABB point in OBB space to OBB extents. Since we scaled the axii, the extents are [-1,1] -- 6 + Vec4V pOBBxa = V4Max(obbESplatNegX, V4Min(obbPrjXa, obbESplatX)); + Vec4V pOBBya = V4Max(obbESplatNegY, V4Min(obbPrjYa, obbESplatY)); + Vec4V pOBBza = V4Max(obbESplatNegZ, V4Min(obbPrjZa, obbESplatZ)); + // go back to AABB space. we have x,y,z in obb space, need to multiply by axii -- 9 + Vec4V p1OBBxa = V4MulAdd(pOBBxa, obbXSplatX, V4MulAdd(pOBBya, obbYSplatX, V4MulAdd(pOBBza, obbZSplatX, obbOSplatX))); + Vec4V p1OBBya = V4MulAdd(pOBBxa, obbXSplatY, V4MulAdd(pOBBya, obbYSplatY, V4MulAdd(pOBBza, obbZSplatY, obbOSplatY))); + Vec4V p1OBBza = V4MulAdd(pOBBxa, obbXSplatZ, V4MulAdd(pOBBya, obbYSplatZ, V4MulAdd(pOBBza, obbZSplatZ, obbOSplatZ))); + + // ABB iteration 2 -- 6 instructions + Vec4V p2ABBxa = V4Max(minx4a, V4Min(maxx4a, p1OBBxa)); + Vec4V p2ABBya = V4Max(miny4a, V4Min(maxy4a, p1OBBya)); + Vec4V p2ABBza = V4Max(minz4a, V4Min(maxz4a, p1OBBza)); + // above blocks add up to 12+12+15=39 instr + // END PRECOMPUTE FOR A BLOCK + + // for AABBs precompute extents and center -- 9i + Vec4V abbCxa = V4MulAdd(V4Add(maxx4a, minx4a), halves, zeroes); + Vec4V abbCya = V4MulAdd(V4Add(maxy4a, miny4a), halves, zeroes); + Vec4V abbCza = V4MulAdd(V4Add(maxz4a, minz4a), halves, zeroes); + Vec4V abbExa = V4Sub(maxx4a, abbCxa); + Vec4V abbEya = V4Sub(maxy4a, abbCya); + Vec4V abbEza = V4Sub(maxz4a, abbCza); + + // now test separating axes D1 = p1OBB-p1ABB and D2 = p1OBB-p2ABB -- 37 instructions per axis + // D1 first -- 3 instructions + Vec4V d1xa = V4Sub(p1OBBxa, p1ABBxa), d1ya = V4Sub(p1OBBya, p1ABBya), d1za = V4Sub(p1OBBza, p1ABBza); + + // for AABB compute projections of extents and center -- 6 + Vec4V abbExd1Prja = V4MulAdd(d1xa, abbExa, zeroes); + Vec4V abbEyd1Prja = V4MulAdd(d1ya, abbEya, zeroes); + Vec4V abbEzd1Prja = V4MulAdd(d1za, abbEza, zeroes); + Vec4V abbCd1Prja = V4MulAdd(d1xa, abbCxa, V4MulAdd(d1ya, abbCya, V4MulAdd(d1za, abbCza, zeroes))); + + // for obb project each halfaxis and origin and add abs values of half-axis projections -- 12 instructions + Vec4V obbXEd1Prja = V4MulAdd(d1xa, obbXESplatX, V4MulAdd(d1ya, obbXESplatY, V4MulAdd(d1za, obbXESplatZ, zeroes))); + Vec4V obbYEd1Prja = V4MulAdd(d1xa, obbYESplatX, V4MulAdd(d1ya, obbYESplatY, V4MulAdd(d1za, obbYESplatZ, zeroes))); + Vec4V obbZEd1Prja = V4MulAdd(d1xa, obbZESplatX, V4MulAdd(d1ya, obbZESplatY, V4MulAdd(d1za, obbZESplatZ, zeroes))); + Vec4V obbOd1Prja = V4MulAdd(d1xa, obbOSplatX, V4MulAdd(d1ya, obbOSplatY, V4MulAdd(d1za, obbOSplatZ, zeroes))); + + // compare lengths between projected centers with sum of projected radii -- 16i + Vec4V originDiffd1a = v_absm(V4Sub(abbCd1Prja, obbOd1Prja)); + Vec4V absABBRd1a = V4Add(V4Add(v_absm(abbExd1Prja), v_absm(abbEyd1Prja)), v_absm(abbEzd1Prja)); + Vec4V absOBBRd1a = V4Add(V4Add(v_absm(obbXEd1Prja), v_absm(obbYEd1Prja)), v_absm(obbZEd1Prja)); + VecU32V noOverlapd1a = V4IsGrtrV32u(V4Sub(originDiffd1a, eps), V4Add(absABBRd1a, absOBBRd1a)); + VecU32V epsNoOverlapd1a = V4IsGrtrV32u(originDiffd1a, eps); + + // D2 next (35 instr) + // 3i + Vec4V d2xa = V4Sub(p1OBBxa, p2ABBxa), d2ya = V4Sub(p1OBBya, p2ABBya), d2za = V4Sub(p1OBBza, p2ABBza); + // for AABB compute projections of extents and center -- 6 + Vec4V abbExd2Prja = V4MulAdd(d2xa, abbExa, zeroes); + Vec4V abbEyd2Prja = V4MulAdd(d2ya, abbEya, zeroes); + Vec4V abbEzd2Prja = V4MulAdd(d2za, abbEza, zeroes); + Vec4V abbCd2Prja = V4MulAdd(d2xa, abbCxa, V4MulAdd(d2ya, abbCya, V4MulAdd(d2za, abbCza, zeroes))); + // for obb project each halfaxis and origin and add abs values of half-axis projections -- 12i + Vec4V obbXEd2Prja = V4MulAdd(d2xa, obbXESplatX, V4MulAdd(d2ya, obbXESplatY, V4MulAdd(d2za, obbXESplatZ, zeroes))); + Vec4V obbYEd2Prja = V4MulAdd(d2xa, obbYESplatX, V4MulAdd(d2ya, obbYESplatY, V4MulAdd(d2za, obbYESplatZ, zeroes))); + Vec4V obbZEd2Prja = V4MulAdd(d2xa, obbZESplatX, V4MulAdd(d2ya, obbZESplatY, V4MulAdd(d2za, obbZESplatZ, zeroes))); + Vec4V obbOd2Prja = V4MulAdd(d2xa, obbOSplatX, V4MulAdd(d2ya, obbOSplatY, V4MulAdd(d2za, obbOSplatZ, zeroes))); + // compare lengths between projected centers with sum of projected radii -- 16i + Vec4V originDiffd2a = v_absm(V4Sub(abbCd2Prja, obbOd2Prja)); + Vec4V absABBRd2a = V4Add(V4Add(v_absm(abbExd2Prja), v_absm(abbEyd2Prja)), v_absm(abbEzd2Prja)); + Vec4V absOBBRd2a = V4Add(V4Add(v_absm(obbXEd2Prja), v_absm(obbYEd2Prja)), v_absm(obbZEd2Prja)); + VecU32V noOverlapd2a = V4IsGrtrV32u(V4Sub(originDiffd2a, eps), V4Add(absABBRd2a, absOBBRd2a)); + VecU32V epsNoOverlapd2a = V4IsGrtrV32u(originDiffd2a, eps); + + // 8i + noOverlapa = V4U32or(V4U32and(noOverlapd1a, epsNoOverlapd1a), V4U32and(noOverlapd2a, epsNoOverlapd2a)); + VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot) + noOverlapa = V4U32or(noOverlapa, ignore4a); + resa4u = V4U32Andc(U4Load(1), noOverlapa); // 1 & ~noOverlap + V4U32StoreAligned(resa4u, reinterpret_cast<VecU32V*>(resa_)); + ///// 8+16+12+6+3+16+12+6+3+9+6+9+6+12+6+6=136i from load to result + } + + cacheTopValid = false; + for (PxU32 i = 0; i < 4; i++) + { + PxU32 ptr = ptrs[i+offs] & ~1; // clear the isLeaf bit + if (resa_[i]) + { + if (tn->isLeaf(i)) + { + if (!callback->processResults(1, &ptr)) + return; + } + else + { + *(stackPtr++) = ptr; + cacheTop = ptr; + cacheTopValid = true; + } + } + } + } while (stackPtr > stack); +} + +} // namespace Gu + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepConvexTri.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepConvexTri.h new file mode 100644 index 00000000..15263717 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepConvexTri.h @@ -0,0 +1,103 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CONVEX_TRI +#define GU_SWEEP_CONVEX_TRI + +#include "GuVecTriangle.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" +#include "PxConvexMeshGeometry.h" +#include "GuGJKRaycast.h" + +// return true if hit, false if no hit +static PX_FORCE_INLINE bool sweepConvexVsTriangle( + const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, + ConvexHullV& convexHull, const Ps::aos::PsMatTransformV& meshToConvex, const Ps::aos::PsTransformV& convexTransfV, + const Ps::aos::Vec3VArg convexSpaceDir, const PxVec3& unitDir, const PxVec3& meshSpaceUnitDir, + const Ps::aos::FloatVArg fullDistance, PxReal shrunkDistance, + PxSweepHit& hit, bool isDoubleSided, const PxReal inflation, bool& initialOverlap, PxU32 faceIndex) +{ + using namespace Ps::aos; + // Create triangle normal + const PxVec3 denormalizedNormal = (v1 - v0).cross(v2 - v1); + + // Backface culling + // PT: WARNING, the test is reversed compared to usual because we pass -unitDir to this function + const bool culled = !isDoubleSided && (denormalizedNormal.dot(meshSpaceUnitDir) <= 0.0f); + if(culled) + return false; + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const Vec3V p0 = V3LoadU(v0); // in mesh local space + const Vec3V p1 = V3LoadU(v1); + const Vec3V p2 = V3LoadU(v2); + + // transform triangle verts from mesh local to convex local space + TriangleV triangleV(meshToConvex.transform(p0), meshToConvex.transform(p1), meshToConvex.transform(p2)); + + FloatV toi; + Vec3V closestA,normal; + + LocalConvex<TriangleV> convexA(triangleV); + LocalConvex<ConvexHullV> convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), convexHull.getCenter()); + // run GJK raycast + // sweep triangle in convex local space vs convex, closestA will be the impact point in convex local space + const bool gjkHit = gjkRaycastPenetration<LocalConvex<TriangleV>, LocalConvex<ConvexHullV> >( + convexA, convexB, initialSearchDir, zero, zeroV, convexSpaceDir, toi, normal, closestA, inflation, false); + if(!gjkHit) + return false; + + const FloatV minDist = FLoad(shrunkDistance); + const Vec3V destWorldPointA = convexTransfV.transform(closestA); + const Vec3V destNormal = V3Normalize(convexTransfV.rotate(normal)); + + if(FAllGrtrOrEq(zero, toi)) + { + initialOverlap = true; // PT: TODO: redundant with hit distance, consider removing + return setInitialOverlapResults(hit, unitDir, faceIndex); + } + + const FloatV dist = FMul(toi, fullDistance); // scale the toi to original full sweep distance + if(FAllGrtr(minDist, dist)) // is current dist < minDist? + { + hit.faceIndex = faceIndex; + hit.flags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + V3StoreU(destWorldPointA, hit.position); + V3StoreU(destNormal, hit.normal); + FStore(dist, &hit.distance); + return true; // report a hit + } + return false; // report no hit +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepMesh.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepMesh.h new file mode 100644 index 00000000..c1f8521e --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepMesh.h @@ -0,0 +1,169 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_MESH_H +#define GU_SWEEP_MESH_H + +#include "GuMidphaseInterface.h" +#include "GuVecConvexHull.h" + +namespace physx +{ + +namespace Gu +{ + // PT: class to make sure we can safely V4Load Matrix34's last column + class Matrix34Padded : public Cm::Matrix34 + { + public: + PX_FORCE_INLINE Matrix34Padded(const Matrix34& src) : Matrix34(src) {} + PX_FORCE_INLINE Matrix34Padded() {} + PX_FORCE_INLINE ~Matrix34Padded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(0==(sizeof(Matrix34Padded)==16)); + + // PT: intermediate class containing shared bits of code & members + struct SweepShapeMeshHitCallback : MeshHitCallback<PxRaycastHit> + { + SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef); + + const PxHitFlags mHitFlags; + bool mStatus; // Default is false, set to true if a valid hit is found. Stays true once true. + bool mInitialOverlap; // Default is false, set to true if an initial overlap hit is found. Reset for each hit. + bool mFlipNormal; // If negative scale is used we need to flip normal + PxReal mDistCoeff; // dist coeff from unscaled to scaled distance + + void operator=(const SweepShapeMeshHitCallback&) {} + }; + + struct SweepCapsuleMeshHitCallback : SweepShapeMeshHitCallback + { + PxSweepHit& mSweepHit; + const Cm::Matrix34& mVertexToWorldSkew; + const PxReal mTrueSweepDistance; // max sweep distance that can be used + PxReal mBestAlignmentValue; // best alignment value for triangle normal + PxReal mBestDist; // best distance, not the same as sweepHit.distance, can be shorter by epsilon + const Capsule& mCapsule; + const PxVec3& mUnitDir; + const bool mMeshDoubleSided; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED + const bool mIsSphere; + + SweepCapsuleMeshHitCallback(PxSweepHit& sweepHit, const Cm::Matrix34& worldMatrix, PxReal distance, bool meshDoubleSided, + const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef); + + virtual PxAgain processHit(const PxRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*); + + // PT: TODO: unify these operators + void operator=(const SweepCapsuleMeshHitCallback&) {} + + bool finalizeHit( PxSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom, + const PxTransform& pose, bool isDoubleSided) const; + }; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + struct SweepBoxMeshHitCallback : SweepShapeMeshHitCallback + { + const Matrix34Padded& mMeshToBox; + PxReal mDist, mDist0; + physx::shdfnd::aos::FloatV mDistV; + const Box& mBox; + const PxVec3& mLocalDir; + const PxVec3& mWorldUnitDir; + PxReal mInflation; + PxTriangle mHitTriangle; + physx::shdfnd::aos::Vec3V mMinClosestA; + physx::shdfnd::aos::Vec3V mMinNormal; + physx::shdfnd::aos::Vec3V mLocalMotionV; + PxU32 mMinTriangleIndex; + PxVec3 mOneOverDir; + const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES + + SweepBoxMeshHitCallback(CallbackMode::Enum mode_, const Matrix34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide, + const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir, + const PxHitFlags& hitFlags, const PxReal inflation, bool flipNormal, float distCoef); + + virtual ~SweepBoxMeshHitCallback() {} + + virtual PxAgain processHit(const PxRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*); + + bool finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const PxTransform& boxTransform, const PxVec3& localDir, + bool meshBothSides, bool isDoubleSided) const; + + private: + SweepBoxMeshHitCallback& operator=(const SweepBoxMeshHitCallback&); + }; + + struct SweepConvexMeshHitCallback : SweepShapeMeshHitCallback + { + PxTriangle mHitTriangle; + ConvexHullV mConvexHull; + physx::shdfnd::aos::PsMatTransformV mMeshToConvex; + physx::shdfnd::aos::PsTransformV mConvexPoseV; + const Cm::FastVertex2ShapeScaling& mMeshScale; + PxSweepHit mSweepHit; // stores either the closest or any hit depending on value of mAnyHit + physx::shdfnd::aos::FloatV mInitialDistance; + physx::shdfnd::aos::Vec3V mConvexSpaceDir; // convexPose.rotateInv(-unit*distance) + PxVec3 mUnitDir; + PxVec3 mMeshSpaceUnitDir; + PxReal mInflation; + const bool mAnyHit; + const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES + + SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const Cm::FastVertex2ShapeScaling& meshScale, + const PxTransform& convexPose, const PxTransform& meshPose, + const PxVec3& unitDir, const PxReal distance, PxHitFlags hitFlags, const bool bothTriangleSidesCollide, const PxReal inflation, + const bool anyHit, float distCoef); + + virtual ~SweepConvexMeshHitCallback() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*); + + bool finalizeHit(PxSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation, + bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide); + + private: + SweepConvexMeshHitCallback& operator=(const SweepConvexMeshHitCallback&); + }; + +#if PX_VC + #pragma warning(pop) +#endif + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepsMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepsMesh.cpp new file mode 100644 index 00000000..6efb85db --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuSweepsMesh.cpp @@ -0,0 +1,602 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepTests.h" +#include "GuSweepMesh.h" +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" +#include "CmScaling.h" +#include "GuVecShrunkBox.h" +#include "GuSweepMTD.h" +#include "GuVecCapsule.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuSweepSphereTriangle.h" +#include "GuDistancePointTriangle.h" +#include "GuCapsule.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +#include "GuSweepConvexTri.h" + +/////////////////////////////////////////////////////////////////////////////// + +static bool sweepSphereTriangle(const PxTriangle& tri, + const PxVec3& center, PxReal radius, + const PxVec3& unitDir, const PxReal distance, + PxSweepHit& hit, PxVec3& triNormalOut, + PxHitFlags hitFlags, bool isDoubleSided) +{ + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + // PT: test if shapes initially overlap + // PT: add culling here for now, but could be made more efficiently... + + // Create triangle normal + PxVec3 denormalizedNormal; + tri.denormalizedNormal(denormalizedNormal); + + // Backface culling + if(doBackfaceCulling && (denormalizedNormal.dot(unitDir) > 0.0f)) + return false; + + float s_unused, t_unused; + const PxVec3 cp = closestPtPointTriangle(center, tri.verts[0], tri.verts[1], tri.verts[2], s_unused, t_unused); + const PxReal dist2 = (cp - center).magnitudeSquared(); + if(dist2<=radius*radius) + { + triNormalOut = denormalizedNormal.getNormalized(); + return setInitialOverlapResults(hit, unitDir, 0); + } + } + + return sweepSphereTriangles(1, &tri, + center, radius, + unitDir, distance, + NULL, + hit, triNormalOut, + isDoubleSided, meshBothSides, false, false); +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepShapeMeshHitCallback::SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) : + MeshHitCallback<PxRaycastHit> (mode), + mHitFlags (hitFlags), + mStatus (false), + mInitialOverlap (false), + mFlipNormal (flipNormal), + mDistCoeff (distCoef) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepCapsuleMeshHitCallback::SweepCapsuleMeshHitCallback( + PxSweepHit& sweepHit, const Matrix34& worldMatrix, PxReal distance, bool meshDoubleSided, + const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) : + SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, flipNormal, distCoef), + mSweepHit (sweepHit), + mVertexToWorldSkew (worldMatrix), + mTrueSweepDistance (distance), + mBestAlignmentValue (2.0f), + mBestDist (distance + GU_EPSILON_SAME_DISTANCE), + mCapsule (capsule), + mUnitDir (unitDir), + mMeshDoubleSided (meshDoubleSided), + mIsSphere (capsule.p0 == capsule.p1) +{ + mSweepHit.distance = mTrueSweepDistance; +} + +PxAgain SweepCapsuleMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*) +{ + const PxTriangle tmpt( mVertexToWorldSkew.transform(v0), + mVertexToWorldSkew.transform(mFlipNormal ? v2 : v1), + mVertexToWorldSkew.transform(mFlipNormal ? v1 : v2)); + + PxSweepHit localHit; // PT: TODO: ctor! + PxVec3 triNormal; + // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + // make it a relative epsilon to make sure it still works with large distances + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, mSweepHit.distance); + const float minD = mSweepHit.distance + distEpsilon; + if(mIsSphere) + { + if(!::sweepSphereTriangle( tmpt, + mCapsule.p0, mCapsule.radius, + mUnitDir, minD, + localHit, triNormal, + mHitFlags, mMeshDoubleSided)) + return true; + } + else + { + // PT: this one is safe because cullbox is NULL (no need to allocate one more triangle) + if(!sweepCapsuleTriangles_Precise( 1, &tmpt, + mCapsule, + mUnitDir, minD, + NULL, + localHit, triNormal, + mHitFlags, mMeshDoubleSided, + NULL)) + return true; + } + + const PxReal alignmentValue = computeAlignmentValue(triNormal, mUnitDir); + if(keepTriangle(localHit.distance, alignmentValue, mBestDist, mBestAlignmentValue, mTrueSweepDistance, distEpsilon)) + { + mBestAlignmentValue = alignmentValue; + + // AP: need to shrink the sweep distance passed into sweepCapsuleTriangles for correctness so that next sweep is closer + shrunkMaxT = localHit.distance * mDistCoeff; // shrunkMaxT is scaled + + mBestDist = PxMin(mBestDist, localHit.distance); // exact lower bound + mSweepHit.flags = localHit.flags; + mSweepHit.distance = localHit.distance; + mSweepHit.normal = localHit.normal; + mSweepHit.position = localHit.position; + mSweepHit.faceIndex = aHit.faceIndex; + + mStatus = true; + //ML:this is the initial overlap condition + if(localHit.distance == 0.0f) + { + mInitialOverlap = true; + return false; + } + if(mHitFlags & PxHitFlag::eMESH_ANY) + return false; // abort traversal + } + return true; +} + +bool SweepCapsuleMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom, + const PxTransform& pose, bool isDoubleSided) const +{ + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + bool hasContacts = false; + if(mHitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(mCapsule.p0); + const Vec3V p1 = V3LoadU(mCapsule.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //we need to calculate the MTD + hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, mCapsule.radius, isDoubleSided, sweepHit); + } + setupSweepHitForMTD(sweepHit, hasContacts, mUnitDir); + } + else + { + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL | PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepCapsule_MeshGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + return Midphase::sweepCapsuleVsMesh(meshData, meshGeom, pose, lss, unitDir, distance, sweepHit, hitFlags, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + + // same as 'mat.transform(p)' but using SIMD + static PX_FORCE_INLINE Vec4V transformV(const Vec4V p, const Matrix34Padded& mat) + { + Vec4V ResV = V4Scale(V4LoadU(&mat.m.column0.x), V4GetX(p)); + ResV = V4ScaleAdd(V4LoadU(&mat.m.column1.x), V4GetY(p), ResV); + ResV = V4ScaleAdd(V4LoadU(&mat.m.column2.x), V4GetZ(p), ResV); + ResV = V4Add(ResV, V4LoadU(&mat.p.x)); // PT: this load is safe thanks to padding + return ResV; + } + +/////////////////////////////////////////////////////////////////////////////// + +SweepBoxMeshHitCallback::SweepBoxMeshHitCallback( CallbackMode::Enum mode_, const Matrix34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide, + const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir, + const PxHitFlags& hitFlags, const PxReal inflation, bool flipNormal, float distCoef) : + SweepShapeMeshHitCallback (mode_, hitFlags, flipNormal,distCoef), + mMeshToBox (meshToBox), + mDist (distance), + mBox (box), + mLocalDir (localDir), + mWorldUnitDir (unitDir), + mInflation (inflation), + mBothTriangleSidesCollide (bothTriangleSidesCollide) +{ + mLocalMotionV = V3LoadU(localMotion); + mDistV = FLoad(distance); + mDist0 = distance; + mOneOverDir = PxVec3( + mLocalDir.x!=0.0f ? 1.0f/mLocalDir.x : 0.0f, + mLocalDir.y!=0.0f ? 1.0f/mLocalDir.y : 0.0f, + mLocalDir.z!=0.0f ? 1.0f/mLocalDir.z : 0.0f); +} + +PxAgain SweepBoxMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*) +{ + if(mHitFlags & PxHitFlag::ePRECISE_SWEEP) + { + const PxTriangle currentTriangle( + mMeshToBox.transform(lp0), + mMeshToBox.transform(mFlipNormal ? lp2 : lp1), + mMeshToBox.transform(mFlipNormal ? lp1 : lp2)); + + PxF32 t = PX_MAX_REAL; // PT: could be better! + if(!triBoxSweepTestBoxSpace(currentTriangle, mBox.extents, mLocalDir, mOneOverDir, mDist, t, !mBothTriangleSidesCollide)) + return true; + + if(t <= mDist) + { + // PT: test if shapes initially overlap + mDist = t; + shrinkMaxT = t * mDistCoeff; // shrunkMaxT is scaled + mMinClosestA = V3LoadU(currentTriangle.verts[0]); // PT: this is arbitrary + mMinNormal = V3LoadU(-mWorldUnitDir); + mStatus = true; + mMinTriangleIndex = meshHit.faceIndex; + mHitTriangle = currentTriangle; + if(t == 0.0f) + { + mInitialOverlap = true; + return false; // abort traversal + } + } + } + else + { + const FloatV zero = FZero(); + + // PT: SIMD code similar to: + // const Vec3V triV0 = V3LoadU(mMeshToBox.transform(lp0)); + // const Vec3V triV1 = V3LoadU(mMeshToBox.transform(lp1)); + // const Vec3V triV2 = V3LoadU(mMeshToBox.transform(lp2)); + // + // SIMD version works but we need to ensure all loads are safe. + // For incoming vertices they should either come from the vertex array or from a binary deserialized file. + // For the vertex array we can just allocate one more vertex. For the binary file it should be ok as soon + // as vertices aren't the last thing serialized in the file. + // For the matrix only the last column is a problem, and we can easily solve that with some padding in the local class. + const Vec3V triV0 = Vec3V_From_Vec4V(transformV(V4LoadU(&lp0.x), mMeshToBox)); + const Vec3V triV1 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp2.x : &lp1.x), mMeshToBox)); + const Vec3V triV2 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp1.x : &lp2.x), mMeshToBox)); + + if(!mBothTriangleSidesCollide) + { + const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1)); + if(FAllGrtrOrEq(V3Dot(triNormal, mLocalMotionV), zero)) + return true; + } + + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(mBox.extents); + const BoxV boxV(zeroV, boxExtents); + + const TriangleV triangleV(triV0, triV1, triV2); + + FloatV lambda; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex<TriangleV> convexA(triangleV); + LocalConvex<BoxV> convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + if(!gjkRaycastPenetration< LocalConvex<TriangleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, zero, zeroV, mLocalMotionV, lambda, normal, closestA, mInflation, false)) + return true; + + mStatus = true; + mMinClosestA = closestA; + mMinTriangleIndex = meshHit.faceIndex; + if(FAllGrtrOrEq(zero, lambda)) // lambda < 0? => initial overlap + { + mInitialOverlap = true; + shrinkMaxT = 0.0f; + mDistV = zero; + mDist = 0.0f; + mMinNormal = V3LoadU(-mWorldUnitDir); + return false; + } + + PxF32 f; + FStore(lambda, &f); + mDist = f*mDist; // shrink dist + mLocalMotionV = V3Scale(mLocalMotionV, lambda); // shrink localMotion + mDistV = FMul(mDistV, lambda); // shrink distV + mMinNormal = normal; + if(mDist * mDistCoeff < shrinkMaxT) // shrink shrinkMaxT + shrinkMaxT = mDist * mDistCoeff; // shrunkMaxT is scaled + + //mHitTriangle = currentTriangle; + V3StoreU(triV0, mHitTriangle.verts[0]); + V3StoreU(triV1, mHitTriangle.verts[1]); + V3StoreU(triV2, mHitTriangle.verts[2]); + } + return true; +} + +bool SweepBoxMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const PxTransform& boxTransform, const PxVec3& localDir, + bool meshBothSides, bool isDoubleSided) const +{ + if(!mStatus) + return false; + + Vec3V minClosestA = mMinClosestA; + Vec3V minNormal = mMinNormal; + sweepHit.faceIndex = mMinTriangleIndex; + + if(mInitialOverlap) + { + bool hasContacts = false; + if(mHitFlags & PxHitFlag::eMTD) + hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, mBox, boxTransform, mInflation, mBothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, mWorldUnitDir); + } + else + { + sweepHit.distance = mDist; + sweepHit.flags = PxHitFlag::eDISTANCE | PxHitFlag::eFACE_INDEX; + + // PT: we need the "best triangle" normal in order to call 'shouldFlipNormal'. We stored the best + // triangle in both GJK & precise codepaths (in box space). We use a dedicated 'shouldFlipNormal' + // function that delays computing the triangle normal. + // TODO: would still be more efficient to store the best normal directly, it's already computed at least + // in the GJK codepath. + + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const PsTransformV boxPos(p0, q0); + + if(mHitFlags & PxHitFlag::ePRECISE_SWEEP) + { + computeBoxLocalImpact(sweepHit.position, sweepHit.normal, sweepHit.flags, mBox, localDir, mHitTriangle, mHitFlags, isDoubleSided, meshBothSides, mDist); + } + else + { + sweepHit.flags |= PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + + // PT: now for the GJK path, we must first always negate the returned normal. Similar to what happens in the precise path, + // we can't delay this anymore: our normal must be properly oriented in order to call 'shouldFlipNormal'. + minNormal = V3Neg(minNormal); + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + PxVec3 tmp; + V3StoreU(minNormal, tmp); + + if(shouldFlipNormal(tmp, meshBothSides, isDoubleSided, mHitTriangle, localDir, NULL)) + minNormal = V3Neg(minNormal); + + // PT: finally, this moves everything back to world space + V3StoreU(boxPos.rotate(minNormal), sweepHit.normal); + V3StoreU(boxPos.transform(minClosestA), sweepHit.position); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_MeshGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom); + + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + return Midphase::sweepBoxVsMesh(meshData, meshGeom, pose, box, unitDir, distance, sweepHit, hitFlags, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepConvexMeshHitCallback::SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale, + const PxTransform& convexPose, const PxTransform& meshPose, + const PxVec3& unitDir, const PxReal distance, PxHitFlags hitFlags, const bool bothTriangleSidesCollide, const PxReal inflation, + const bool anyHit, float distCoef) : + SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, meshScale.flipsNormal(), distCoef), + mMeshScale (meshScale), + mUnitDir (unitDir), + mInflation (inflation), + mAnyHit (anyHit), + mBothTriangleSidesCollide (bothTriangleSidesCollide) +{ + mSweepHit.distance = distance; // this will be shrinking progressively as we sweep and clip the sweep length + mSweepHit.faceIndex = 0xFFFFFFFF; + + mMeshSpaceUnitDir = meshPose.rotateInv(unitDir); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const QuatV q0 = QuatVLoadU(&meshPose.q.x); + const Vec3V p0 = V3LoadU(&meshPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV meshPoseV(p0, q0); + const PsTransformV convexPoseV(p1, q1); + + mMeshToConvex = convexPoseV.transformInv(meshPoseV); + mConvexPoseV = convexPoseV; + mConvexSpaceDir = convexPoseV.rotateInv(V3Neg(V3Scale(worldDir, dist))); + mInitialDistance = dist; + + const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); + mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); +} + +PxAgain SweepConvexMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*) +{ + const PxVec3 v0 = mMeshScale * av0; + const PxVec3 v1 = mMeshScale * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mMeshScale * (mFlipNormal ? av1 : av2); + + // mSweepHit will be updated if sweep distance is < input mSweepHit.distance + const PxReal oldDist = mSweepHit.distance; + if(sweepConvexVsTriangle( + v0, v1, v2, mConvexHull, mMeshToConvex, mConvexPoseV, mConvexSpaceDir, + mUnitDir, mMeshSpaceUnitDir, mInitialDistance, oldDist, mSweepHit, mBothTriangleSidesCollide, + mInflation, mInitialOverlap, hit.faceIndex)) + { + mStatus = true; + shrunkMaxT = mSweepHit.distance * mDistCoeff; // shrunkMaxT is scaled + + // PT: added for 'shouldFlipNormal' + mHitTriangle.verts[0] = v0; + mHitTriangle.verts[1] = v1; + mHitTriangle.verts[2] = v2; + + if(mAnyHit) + return false; // abort traversal + + if(mSweepHit.distance == 0.0f) + return false; + } + return true; // continue traversal +} + +bool SweepConvexMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation, + bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide) +{ + if(!mStatus) + return false; + + if(mInitialOverlap) + { + bool hasContacts = false; + if(isMtd) + hasContacts = computeConvex_TriangleMeshMTD(meshGeom, pose, convexGeom, convexPose, inflation, bothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + + sweepHit.faceIndex = mSweepHit.faceIndex; + } + else + { + sweepHit = mSweepHit; + //sweepHit.position += unitDir * sweepHit.distance; + sweepHit.normal = -sweepHit.normal; + sweepHit.normal.normalize(); + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + // PT: beware, the best triangle is in mesh-space, but the impact data is in world-space already + if(shouldFlipNormal(sweepHit.normal, meshBothSides, isDoubleSided, mHitTriangle, unitDir, &pose)) + sweepHit.normal = -sweepHit.normal; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepConvex_MeshGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom); + + ConvexMesh* convexMesh = static_cast<ConvexMesh*>(convexGeom.convexMesh); + TriangleMesh* meshData = static_cast<TriangleMesh*>(meshGeom.triangleMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + const bool idtScaleMesh = meshGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(meshGeom.scale); + + PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABB = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + Box hullOBB; + computeHullOBB(hullOBB, hullAABB, 0.0f, Matrix34(convexPose), Matrix34(pose), meshScaling, idtScaleMesh); + + hullOBB.extents.x += inflation; + hullOBB.extents.y += inflation; + hullOBB.extents.z += inflation; + + const PxVec3 localDir = pose.rotateInv(unitDir); + + // inverse transform the sweep direction and distance to mesh space + PxVec3 meshSpaceSweepVector = meshScaling.getShape2VertexSkew().transform(localDir*distance); + const PxReal meshSpaceSweepDist = meshSpaceSweepVector.normalize(); + + PxReal distCoeff = 1.0f; + if (!idtScaleMesh) + distCoeff = meshSpaceSweepDist / distance; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = meshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + SweepConvexMeshHitCallback callback( + convexMesh->getHull(), convexGeom.scale, meshScaling, convexPose, pose, -unitDir, distance, hitFlags, + bothTriangleSidesCollide, inflation, anyHit, distCoeff); + + Midphase::sweepConvexVsMesh(meshData, hullOBB, meshSpaceSweepVector, meshSpaceSweepDist, callback, anyHit); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + return callback.finalizeHit(sweepHit, meshGeom, pose, convexGeom, convexPose, unitDir, inflation, isMtd, meshBothSides, isDoubleSided, bothTriangleSidesCollide); +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangle32.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangle32.h new file mode 100644 index 00000000..7607e730 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangle32.h @@ -0,0 +1,132 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE32_H +#define GU_TRIANGLE32_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" + +namespace physx +{ +namespace Gu +{ + /** + \brief Structure used to store indices for a triangles points. T is either PxU32 or PxU16 + + */ + + template <class T> + struct TriangleT// : public Ps::UserAllocated + { + PX_INLINE TriangleT() {} + PX_INLINE TriangleT(T a, T b, T c) { v[0] = a; v[1] = b; v[2] = c; } + template <class TX> + PX_INLINE TriangleT(const TriangleT<TX>& other) { v[0] = other[0]; v[1] = other[1]; v[2] = other[2]; } + PX_INLINE T& operator[](T i) { return v[i]; } + template<class TX>//any type of TriangleT<>, possibly with different T + PX_INLINE TriangleT<T>& operator=(const TriangleT<TX>& i) { v[0]=i[0]; v[1]=i[1]; v[2]=i[2]; return *this; } + PX_INLINE const T& operator[](T i) const { return v[i]; } + + void flip() + { + Ps::swap(v[1], v[2]); + } + + PX_INLINE void center(const PxVec3* verts, PxVec3& center) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + center = (p0+p1+p2)*0.33333333333333333333f; + } + + float area(const PxVec3* verts) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + return ((p0-p1).cross(p0-p2)).magnitude() * 0.5f; + } + + PxU8 findEdge(T vref0, T vref1) const + { + if(v[0]==vref0 && v[1]==vref1) return 0; + else if(v[0]==vref1 && v[1]==vref0) return 0; + else if(v[0]==vref0 && v[2]==vref1) return 1; + else if(v[0]==vref1 && v[2]==vref0) return 1; + else if(v[1]==vref0 && v[2]==vref1) return 2; + else if(v[1]==vref1 && v[2]==vref0) return 2; + return 0xff; + } + + // counter clock wise order + PxU8 findEdgeCCW(T vref0, T vref1) const + { + if(v[0]==vref0 && v[1]==vref1) return 0; + else if(v[0]==vref1 && v[1]==vref0) return 0; + else if(v[0]==vref0 && v[2]==vref1) return 2; + else if(v[0]==vref1 && v[2]==vref0) return 2; + else if(v[1]==vref0 && v[2]==vref1) return 1; + else if(v[1]==vref1 && v[2]==vref0) return 1; + return 0xff; + } + + bool replaceVertex(T oldref, T newref) + { + if(v[0]==oldref) { v[0] = newref; return true; } + else if(v[1]==oldref) { v[1] = newref; return true; } + else if(v[2]==oldref) { v[2] = newref; return true; } + return false; + } + + bool isDegenerate() const + { + if(v[0]==v[1]) return true; + if(v[1]==v[2]) return true; + if(v[2]==v[0]) return true; + return false; + } + + PX_INLINE void denormalizedNormal(const PxVec3* verts, PxVec3& normal) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + normal = ((p2 - p1).cross(p0 - p1)); + } + + T v[3]; //vertex indices + }; +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleCache.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleCache.h new file mode 100644 index 00000000..9dc2a453 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleCache.h @@ -0,0 +1,207 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE_CACHE_H +#define GU_TRIANGLE_CACHE_H +#include "PsHash.h" +#include "PsUtilities.h" + +namespace physx +{ + namespace Gu + { + struct CachedEdge + { + protected: + PxU32 mId0, mId1; + public: + CachedEdge(PxU32 i0, PxU32 i1) + { + mId0 = PxMin(i0, i1); + mId1 = PxMax(i0, i1); + } + + CachedEdge() + { + } + + PxU32 getId0() const { return mId0; } + PxU32 getId1() const { return mId1; } + + bool operator == (const CachedEdge& other) const + { + return mId0 == other.mId0 && mId1 == other.mId1; + } + + PxU32 getHashCode() const + { + return Ps::hash(mId0 << 16 | mId1); + } + }; + + struct CachedVertex + { + private: + PxU32 mId; + public: + CachedVertex(PxU32 id) + { + mId = id; + } + + CachedVertex() + { + } + + PxU32 getId() const { return mId; } + + PxU32 getHashCode() const + { + return mId; + } + + bool operator == (const CachedVertex& other) const + { + return mId == other.mId; + } + }; + + template <typename Elem, PxU32 MaxCount> + struct CacheMap + { + PX_COMPILE_TIME_ASSERT(MaxCount < 0xFF); + Elem mCache[MaxCount]; + PxU8 mNextInd[MaxCount]; + PxU8 mIndex[MaxCount]; + PxU32 mSize; + + CacheMap() : mSize(0) + { + for(PxU32 a = 0; a < MaxCount; ++a) + { + mIndex[a] = 0xFF; + } + } + + bool addData(const Elem& data) + { + if(mSize == MaxCount) + return false; + + const PxU8 hash = PxU8(data.getHashCode() % MaxCount); + + PxU8 index = hash; + PxU8 nextInd = mIndex[hash]; + while(nextInd != 0xFF) + { + index = nextInd; + if(mCache[index] == data) + return false; + nextInd = mNextInd[nextInd]; + } + + if(mIndex[hash] == 0xFF) + { + mIndex[hash] = Ps::to8(mSize); + } + else + { + mNextInd[index] = Ps::to8(mSize); + } + mNextInd[mSize] = 0xFF; + mCache[mSize++] = data; + return true; + } + + bool contains(const Elem& data) const + { + PxU32 hash = (data.getHashCode() % MaxCount); + PxU8 index = mIndex[hash]; + + while(index != 0xFF) + { + if(mCache[index] == data) + return true; + index = mNextInd[index]; + } + return false; + } + + const Elem* get(const Elem& data) const + { + PxU32 hash = (data.getHashCode() % MaxCount); + PxU8 index = mIndex[hash]; + + while(index != 0xFF) + { + if(mCache[index] == data) + return &mCache[index]; + index = mNextInd[index]; + } + return NULL; + } + }; + + template <PxU32 MaxTriangles> + struct TriangleCache + { + PxVec3 mVertices[3*MaxTriangles]; + PxU32 mIndices[3*MaxTriangles]; + PxU32 mTriangleIndex[MaxTriangles]; + PxU8 mEdgeFlags[MaxTriangles]; + PxU32 mNumTriangles; + + TriangleCache() : mNumTriangles(0) + { + } + + PX_FORCE_INLINE bool isEmpty() const { return mNumTriangles == 0; } + PX_FORCE_INLINE bool isFull() const { return mNumTriangles == MaxTriangles; } + PX_FORCE_INLINE void reset() { mNumTriangles = 0; } + + void addTriangle(const PxVec3* verts, const PxU32* indices, PxU32 triangleIndex, PxU8 edgeFlag) + { + PX_ASSERT(mNumTriangles < MaxTriangles); + PxU32 triInd = mNumTriangles++; + PxU32 triIndMul3 = triInd*3; + mVertices[triIndMul3] = verts[0]; + mVertices[triIndMul3+1] = verts[1]; + mVertices[triIndMul3+2] = verts[2]; + mIndices[triIndMul3] = indices[0]; + mIndices[triIndMul3+1] = indices[1]; + mIndices[triIndMul3+2] = indices[2]; + mTriangleIndex[triInd] = triangleIndex; + mEdgeFlags[triInd] = edgeFlag; + } + }; + } +} + +#endif + diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMesh.cpp new file mode 100644 index 00000000..be47d3e1 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMesh.cpp @@ -0,0 +1,457 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsIntrinsics.h" +#include "GuMidphaseInterface.h" +#include "GuSerialize.h" +#include "GuMeshFactory.h" +#include "CmRenderOutput.h" +#include "PxVisualizationParameter.h" +#include "GuConvexEdgeFlags.h" +#include "GuBox.h" +#include "PxMeshScale.h" +#include "CmUtils.h" + +using namespace physx; + +namespace physx +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PxConcreteType::Enum gTable[] = { PxConcreteType::eTRIANGLE_MESH_BVH33, + PxConcreteType::eTRIANGLE_MESH_BVH34 + }; + +Gu::TriangleMesh::TriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: PxTriangleMesh(PxType(gTable[d.mType]), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNbVertices (d.mNbVertices) +, mNbTriangles (d.mNbTriangles) +, mVertices (d.mVertices) +, mTriangles (d.mTriangles) +, mAABB (d.mAABB) +, mExtraTrigData (d.mExtraTrigData) +, mGeomEpsilon (d.mGeomEpsilon) +, mFlags (d.mFlags) +, mMaterialIndices (d.mMaterialIndices) +, mFaceRemap (d.mFaceRemap) +, mAdjacencies (d.mAdjacencies) + +, mMeshFactory (&factory) + +, mGRB_triIndices (d.mGRB_triIndices) + +, mGRB_triAdjacencies (d.mGRB_triAdjacencies) +, mGRB_vertValency (d.mGRB_vertValency) +, mGRB_adjVertStart (d.mGRB_adjVertStart) +, mGRB_adjVertices (d.mGRB_adjVertices) + +, mGRB_meshAdjVerticiesTotal (d.mGRB_meshAdjVerticiesTotal) +, mGRB_faceRemap (d.mGRB_faceRemap) +, mGRB_BV32Tree (d.mGRB_BV32Tree) +{ + // this constructor takes ownership of memory from the data object + d.mVertices = 0; + d.mTriangles = 0; + d.mExtraTrigData = 0; + d.mFaceRemap = 0; + d.mAdjacencies = 0; + d.mMaterialIndices = 0; + + d.mGRB_triIndices = 0; + + d.mGRB_triAdjacencies = 0; + d.mGRB_vertValency = 0; + d.mGRB_adjVertStart = 0; + d.mGRB_adjVertices = 0; + d.mGRB_faceRemap = 0; + d.mGRB_BV32Tree = 0; + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::TriangleMesh, mExtraTrigData)>=PX_OFFSET_OF(Gu::TriangleMesh, mAABB)+4); + +} + +Gu::TriangleMesh::~TriangleMesh() +{ + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + PX_FREE_AND_RESET(mExtraTrigData); + PX_FREE_AND_RESET(mFaceRemap); + PX_FREE_AND_RESET(mAdjacencies); + PX_FREE_AND_RESET(mMaterialIndices); + PX_FREE_AND_RESET(mTriangles); + PX_FREE_AND_RESET(mVertices); + + PX_FREE_AND_RESET(mGRB_triIndices); + + PX_FREE_AND_RESET(mGRB_triAdjacencies); + PX_FREE_AND_RESET(mGRB_vertValency); + PX_FREE_AND_RESET(mGRB_adjVertStart); + PX_FREE_AND_RESET(mGRB_adjVertices); + PX_FREE_AND_RESET(mGRB_faceRemap); + + BV32Tree* bv32Tree = reinterpret_cast<BV32Tree*>(mGRB_BV32Tree); + PX_DELETE_AND_RESET(bv32Tree); + + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: used to be automatic but making it manual saves bytes in the internal mesh + +void Gu::TriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + //PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mVertices, PxField::eVEC3, mNbVertices, Ps::PxFieldFlag::eSERIALIZE), + if(mVertices) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mVertices, mNbVertices * sizeof(PxVec3)); + } + + if(mTriangles) + { + const PxU32 triangleSize = mFlags & PxTriangleMeshFlag::e16_BIT_INDICES ? sizeof(PxU16) : sizeof(PxU32); + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mTriangles, mNbTriangles * 3 * triangleSize); + } + + //PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mExtraTrigData, PxField::eBYTE, mNbTriangles, Ps::PxFieldFlag::eSERIALIZE), + if(mExtraTrigData) + { + // PT: it might not be needed to 16-byte align this array of PxU8.... + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mExtraTrigData, mNbTriangles * sizeof(PxU8)); + } + + if(mMaterialIndices) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mMaterialIndices, mNbTriangles * sizeof(PxU16)); + } + + if(mFaceRemap) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mFaceRemap, mNbTriangles * sizeof(PxU32)); + } + + if(mAdjacencies) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAdjacencies, mNbTriangles * sizeof(PxU32) * 3); + } +} + +void Gu::TriangleMesh::importExtraData(PxDeserializationContext& context) +{ + // PT: vertices are followed by indices, so it will be safe to V4Load vertices from a deserialized binary file + if(mVertices) + mVertices = context.readExtraData<PxVec3, PX_SERIAL_ALIGN>(mNbVertices); + + if(mTriangles) + { + if(mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + mTriangles = context.readExtraData<PxU16, PX_SERIAL_ALIGN>(3*mNbTriangles); + else + mTriangles = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(3*mNbTriangles); + } + + if(mExtraTrigData) + mExtraTrigData = context.readExtraData<PxU8, PX_SERIAL_ALIGN>(mNbTriangles); + + if(mMaterialIndices) + mMaterialIndices = context.readExtraData<PxU16, PX_SERIAL_ALIGN>(mNbTriangles); + + if(mFaceRemap) + mFaceRemap = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(mNbTriangles); + + if(mAdjacencies) + mAdjacencies = context.readExtraData<PxU32, PX_SERIAL_ALIGN>(3*mNbTriangles); +} + +void Gu::TriangleMesh::onRefCountZero() +{ + if(mMeshFactory->removeTriangleMesh(*this)) + { + const PxType type = getConcreteType(); + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, type); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::TriangleMesh::release: double deletion detected!"); +} +//~PX_SERIALIZATION + +void Gu::TriangleMesh::release() +{ + decRefCount(); +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +PxVec3 * Gu::TriangleMesh::getVerticesForModification() +{ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxTriangleMesh::getVerticesForModification() is only supported for meshes with PxMeshMidPhase::eBVHDynamic."); + + return NULL; +} + +PxBounds3 Gu::TriangleMesh::refitBVH() +{ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxTriangleMesh::refitBVH() is only supported for meshes with PxMeshMidPhase::eBVHDynamic."); + + return PxBounds3(mAABB.getMin(), mAABB.getMax()); +} +#endif + +#if PX_ENABLE_DEBUG_VISUALIZATION + +static void getTriangle(const Gu::TriangleMesh&, PxU32 i, PxVec3* wp, const PxVec3* vertices, const void* indices, bool has16BitIndices) +{ + PxU32 ref0, ref1, ref2; + + if(!has16BitIndices) + { + const PxU32* dtriangles = reinterpret_cast<const PxU32*>(indices); + ref0 = dtriangles[i*3+0]; + ref1 = dtriangles[i*3+1]; + ref2 = dtriangles[i*3+2]; + } + else + { + const PxU16* wtriangles = reinterpret_cast<const PxU16*>(indices); + ref0 = wtriangles[i*3+0]; + ref1 = wtriangles[i*3+1]; + ref2 = wtriangles[i*3+2]; + } + + wp[0] = vertices[ref0]; + wp[1] = vertices[ref1]; + wp[2] = vertices[ref2]; +} + +static void getTriangle(const Gu::TriangleMesh& mesh, PxU32 i, PxVec3* wp, const PxVec3* vertices, const void* indices, const Cm::Matrix34& absPose, bool has16BitIndices) +{ + PxVec3 localVerts[3]; + getTriangle(mesh, i, localVerts, vertices, indices, has16BitIndices); + + wp[0] = absPose.transform(localVerts[0]); + wp[1] = absPose.transform(localVerts[1]); + wp[2] = absPose.transform(localVerts[2]); +} + +static void visualizeActiveEdges(Cm::RenderOutput& out, const Gu::TriangleMesh& mesh, PxU32 nbTriangles, const PxU32* results, const Cm::Matrix34& absPose, const PxMat44& midt) +{ + const PxU8* extraTrigData = mesh.getExtraTrigData(); + PX_ASSERT(extraTrigData); + + const PxVec3* vertices = mesh.getVerticesFast(); + const void* indices = mesh.getTrianglesFast(); + + const PxU32 ecolor = PxU32(PxDebugColor::eARGB_YELLOW); + const bool has16Bit = mesh.has16BitIndices(); + for(PxU32 i=0; i<nbTriangles; i++) + { + const PxU32 index = results ? results[i] : i; + + PxVec3 wp[3]; + getTriangle(mesh, index, wp, vertices, indices, absPose, has16Bit); + + const PxU32 flags = extraTrigData[index]; + + if(flags & Gu::ETD_CONVEX_EDGE_01) + { + out << midt << ecolor << Cm::RenderOutput::LINES << wp[0] << wp[1]; + } + if(flags & Gu::ETD_CONVEX_EDGE_12) + { + out << midt << ecolor << Cm::RenderOutput::LINES << wp[1] << wp[2]; + } + if(flags & Gu::ETD_CONVEX_EDGE_20) + { + out << midt << ecolor << Cm::RenderOutput::LINES << wp[0] << wp[2]; + } + } +} + +void Gu::TriangleMesh::debugVisualize( + Cm::RenderOutput& out, const PxTransform& pose, const PxMeshScale& scaling, const PxBounds3& cullbox, + const PxU64 mask, const PxReal fscale, const PxU32 numMaterials) const +{ + PX_UNUSED(numMaterials); + + //bool cscale = !!(mask & ((PxU64)1 << PxVisualizationParameter::eCULL_BOX)); + const PxU64 cullBoxMask = PxU64(1) << PxVisualizationParameter::eCULL_BOX; + bool cscale = ((mask & cullBoxMask) == cullBoxMask); + + const PxMat44 midt(PxIdentity); + const Cm::Matrix34 absPose(PxMat33(pose.q) * scaling.toMat33(), pose.p); + + PxU32 nbTriangles = getNbTrianglesFast(); + const PxU32 nbVertices = getNbVerticesFast(); + const PxVec3* vertices = getVerticesFast(); + const void* indices = getTrianglesFast(); + + const PxDebugColor::Enum colors[] = + { + PxDebugColor::eARGB_BLACK, + PxDebugColor::eARGB_RED, + PxDebugColor::eARGB_GREEN, + PxDebugColor::eARGB_BLUE, + PxDebugColor::eARGB_YELLOW, + PxDebugColor::eARGB_MAGENTA, + PxDebugColor::eARGB_CYAN, + PxDebugColor::eARGB_WHITE, + PxDebugColor::eARGB_GREY, + PxDebugColor::eARGB_DARKRED, + PxDebugColor::eARGB_DARKGREEN, + PxDebugColor::eARGB_DARKBLUE, + }; + + const PxU32 colorCount = sizeof(colors)/sizeof(PxDebugColor::Enum); + + if(cscale) + { + const Gu::Box worldBox( + (cullbox.maximum + cullbox.minimum)*0.5f, + (cullbox.maximum - cullbox.minimum)*0.5f, + PxMat33(PxIdentity)); + + // PT: TODO: use the callback version here to avoid allocating this huge array + PxU32* results = reinterpret_cast<PxU32*>(PX_ALLOC_TEMP(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); + LimitedResults limitedResults(results, nbTriangles, 0); + Midphase::intersectBoxVsMesh(worldBox, *this, pose, scaling, &limitedResults); + nbTriangles = limitedResults.mNbResults; + + if (fscale) + { + const PxU32 fcolor = PxU32(PxDebugColor::eARGB_DARKRED); + + for (PxU32 i=0; i<nbTriangles; i++) + { + const PxU32 index = results[i]; + PxVec3 wp[3]; + getTriangle(*this, index, wp, vertices, indices, absPose, has16BitIndices()); + + const PxVec3 center = (wp[0] + wp[1] + wp[2]) / 3.0f; + PxVec3 normal = (wp[0] - wp[1]).cross(wp[0] - wp[2]); + PX_ASSERT(!normal.isZero()); + normal = normal.getNormalized(); + + out << midt << fcolor << + Cm::DebugArrow(center, normal * fscale); + } + } + + if (mask & (PxU64(1) << PxVisualizationParameter::eCOLLISION_SHAPES)) + { + const PxU32 scolor = PxU32(PxDebugColor::eARGB_MAGENTA); + + out << midt << scolor; // PT: no need to output this for each segment! + + PxDebugLine* segments = out.reserveSegments(nbTriangles*3); + for(PxU32 i=0; i<nbTriangles; i++) + { + const PxU32 index = results[i]; + PxVec3 wp[3]; + getTriangle(*this, index, wp, vertices, indices, absPose, has16BitIndices()); + segments[0] = PxDebugLine(wp[0], wp[1], scolor); + segments[1] = PxDebugLine(wp[1], wp[2], scolor); + segments[2] = PxDebugLine(wp[2], wp[0], scolor); + segments+=3; + } + } + + if ((mask & (PxU64(1) << PxVisualizationParameter::eCOLLISION_EDGES)) && mExtraTrigData) + visualizeActiveEdges(out, *this, nbTriangles, results, absPose, midt); + + PX_FREE(results); + } + else + { + if (fscale) + { + const PxU32 fcolor = PxU32(PxDebugColor::eARGB_DARKRED); + + for (PxU32 i=0; i<nbTriangles; i++) + { + PxVec3 wp[3]; + getTriangle(*this, i, wp, vertices, indices, absPose, has16BitIndices()); + + const PxVec3 center = (wp[0] + wp[1] + wp[2]) / 3.0f; + PxVec3 normal = (wp[0] - wp[1]).cross(wp[0] - wp[2]); + PX_ASSERT(!normal.isZero()); + normal = normal.getNormalized(); + + out << midt << fcolor << + Cm::DebugArrow(center, normal * fscale); + } + } + + if (mask & (PxU64(1) << PxVisualizationParameter::eCOLLISION_SHAPES)) + { + PxU32 scolor = PxU32(PxDebugColor::eARGB_MAGENTA); + + out << midt << scolor; // PT: no need to output this for each segment! + + PxVec3* transformed = reinterpret_cast<PxVec3*>(PX_ALLOC(sizeof(PxVec3)*nbVertices, "PxVec3")); + for(PxU32 i=0;i<nbVertices;i++) + transformed[i] = absPose.transform(vertices[i]); + + PxDebugLine* segments = out.reserveSegments(nbTriangles*3); + for (PxU32 i=0; i<nbTriangles; i++) + { + PxVec3 wp[3]; + getTriangle(*this, i, wp, transformed, indices, has16BitIndices()); + const PxU32 localMaterialIndex = getTriangleMaterialIndex(i); + scolor = colors[localMaterialIndex % colorCount]; + + segments[0] = PxDebugLine(wp[0], wp[1], scolor); + segments[1] = PxDebugLine(wp[1], wp[2], scolor); + segments[2] = PxDebugLine(wp[2], wp[0], scolor); + segments+=3; + } + + PX_FREE(transformed); + } + + if ((mask & (PxU64(1) << PxVisualizationParameter::eCOLLISION_EDGES)) && mExtraTrigData) + visualizeActiveEdges(out, *this, nbTriangles, NULL, absPose, midt); + } +} + +#endif // #if PX_ENABLE_DEBUG_VISUALIZATION + +} // namespace physx diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMesh.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMesh.h new file mode 100644 index 00000000..854f43b5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMesh.h @@ -0,0 +1,302 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_H +#define GU_TRIANGLEMESH_H + +#include "foundation/PxIO.h" +#include "PxSimpleTriangleMesh.h" +#include "PxTriangleMeshGeometry.h" +#include "CmScaling.h" +#include "GuTriangleMesh.h" +#include "GuTriangle32.h" +#include "CmRefCountable.h" +#include "PxTriangle.h" +#include "PxTriangleMesh.h" +#include "CmRenderOutput.h" +#include "GuMeshData.h" +#include "GuCenterExtents.h" + +namespace physx +{ + +class GuMeshFactory; +class PxMeshScale; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +// Possible optimization: align the whole struct to cache line +class TriangleMesh : public PxTriangleMesh, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + +// PX_SERIALIZATION + TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags), Cm::RefCountable(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + virtual void release(); + + void resolveReferences(PxDeserializationContext& ) {} + virtual void requires(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + +// Cm::RefCountable + virtual void onRefCountZero(); +//~Cm::RefCountable + + TriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~TriangleMesh(); + +// PxTriangleMesh + virtual PxU32 getNbVertices() const { return mNbVertices; } + virtual const PxVec3* getVertices() const { return mVertices; } + virtual const PxU32* getTrianglesRemap() const { return mFaceRemap; } + virtual PxU32 getNbTriangles() const { return mNbTriangles; } + virtual const void* getTriangles() const { return mTriangles; } + virtual PxTriangleMeshFlags getTriangleMeshFlags() const { return PxTriangleMeshFlags(mFlags); } + virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const { + return hasPerTriangleMaterials() ? getMaterials()[triangleIndex] : PxMaterialTableIndex(0xffff); } + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + virtual PxVec3* getVerticesForModification(); + virtual PxBounds3 refitBVH(); +#endif + + virtual PxBounds3 getLocalBounds() const + { + PX_ASSERT(mAABB.isValid()); + return PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents); + } + + virtual void acquireReference() { incRefCount(); } + virtual PxU32 getReferenceCount() const { return getRefCount(); } +//~PxTriangleMesh + // PT: this one is just to prevent instancing Gu::TriangleMesh. + // But you should use PxBase::getConcreteType() instead to avoid the virtual call. + virtual PxMeshMidPhase::Enum getMidphaseID() const = 0; + + PX_FORCE_INLINE const PxU32* getFaceRemap() const { return mFaceRemap; } + PX_FORCE_INLINE bool has16BitIndices() const { return (mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false; } + PX_FORCE_INLINE bool hasPerTriangleMaterials() const { return mMaterialIndices != NULL; } + PX_FORCE_INLINE PxU32 getNbVerticesFast() const { return mNbVertices; } + PX_FORCE_INLINE PxU32 getNbTrianglesFast() const { return mNbTriangles; } + PX_FORCE_INLINE const void* getTrianglesFast() const { return mTriangles; } + PX_FORCE_INLINE const PxVec3* getVerticesFast() const { return mVertices; } + PX_FORCE_INLINE const PxU32* getAdjacencies() const { return mAdjacencies; } + PX_FORCE_INLINE PxReal getGeomEpsilon() const { return mGeomEpsilon; } + PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mAABB; } + PX_FORCE_INLINE const PxU16* getMaterials() const { return mMaterialIndices; } + PX_FORCE_INLINE const PxU8* getExtraTrigData() const { return mExtraTrigData; } + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert in cpp + return static_cast<const CenterExtentsPadded&>(mAABB); + } + + PX_FORCE_INLINE void computeWorldTriangle( + PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, bool flipNormal = false, + PxU32* PX_RESTRICT vertexIndices=NULL, PxU32* PX_RESTRICT adjacencyIndices=NULL) const; + PX_FORCE_INLINE void getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal = false) const; + + void setMeshFactory(GuMeshFactory* factory) { mMeshFactory = factory; } + +protected: + PxU32 mNbVertices; + PxU32 mNbTriangles; + PxVec3* mVertices; + void* mTriangles; //!< 16 (<= 0xffff #vertices) or 32 bit trig indices (mNbTriangles * 3) + // 16 bytes block + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU8* mExtraTrigData; //one per trig + PxReal mGeomEpsilon; //!< see comments in cooking code referencing this variable + // 16 bytes block + /* + low 3 bits (mask: 7) are the edge flags: + b001 = 1 = ignore edge 0 = edge v0-->v1 + b010 = 2 = ignore edge 1 = edge v0-->v2 + b100 = 4 = ignore edge 2 = edge v1-->v2 + */ + PxU8 mFlags; //!< Flag whether indices are 16 or 32 bits wide + //!< Flag whether triangle adajacencies are build + PxU16* mMaterialIndices; //!< the size of the array is numTriangles. + PxU32* mFaceRemap; //!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new] + PxU32* mAdjacencies; //!< Adjacency information for each face - 3 adjacent faces + //!< Set to 0xFFFFffff if no adjacent face + + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + /** + \brief Perform triangle mesh geometry debug visualization + + \param out Debug renderer. + \param pose World position. + */ + void debugVisualize( Cm::RenderOutput& out, const PxTransform& pose, const PxMeshScale& scaling, const PxBounds3& cullbox, + const PxU64 mask, const PxReal fscale, const PxU32 numMaterials) const; +#endif + +public: + + // GRB data ------------------------- + void * mGRB_triIndices; //!< GRB: GPU-friendly tri indices [uint4] + + // TODO avoroshilov: cooking - adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325 + void * mGRB_triAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) + PxU32 * mGRB_vertValency; //!< GRB: number of adjacent vertices to a vertex + PxU32 * mGRB_adjVertStart; //!< GRB: offset for each vertex in the adjacency list + PxU32 * mGRB_adjVertices; //!< GRB: list of adjacent vertices + + PxU32 mGRB_meshAdjVerticiesTotal; //!< GRB: total number of indices in the 'mGRB_adjVertices' + PxU32* mGRB_faceRemap; //!< GRB : gpu to cpu triangle indice remap + void* mGRB_BV32Tree; //!< GRB: BV32 tree + // End of GRB data ------------------ + +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +PX_FORCE_INLINE void Gu::TriangleMesh::computeWorldTriangle(PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, bool flipNormal, + PxU32* PX_RESTRICT vertexIndices, PxU32* PX_RESTRICT adjacencyIndices) const +{ + PxU32 vref0, vref1, vref2; + if(has16BitIndices()) + { + const Gu::TriangleT<PxU16>& T = (reinterpret_cast<const Gu::TriangleT<PxU16>*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + else + { + const Gu::TriangleT<PxU32>& T = (reinterpret_cast<const Gu::TriangleT<PxU32>*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + if (flipNormal) + Ps::swap<PxU32>(vref1, vref2); + const PxVec3* PX_RESTRICT vertices = getVerticesFast(); + worldTri.verts[0] = worldMatrix.transform(vertices[vref0]); + worldTri.verts[1] = worldMatrix.transform(vertices[vref1]); + worldTri.verts[2] = worldMatrix.transform(vertices[vref2]); + + if(vertexIndices) + { + vertexIndices[0] = vref0; + vertexIndices[1] = vref1; + vertexIndices[2] = vref2; + } + + if(adjacencyIndices) + { + if(getAdjacencies()) + { + adjacencyIndices[0] = flipNormal ? getAdjacencies()[triangleIndex*3 + 2] : getAdjacencies()[triangleIndex*3 + 0]; + adjacencyIndices[1] = getAdjacencies()[triangleIndex*3 + 1]; + adjacencyIndices[2] = flipNormal ? getAdjacencies()[triangleIndex*3 + 0] : getAdjacencies()[triangleIndex*3 + 2]; + } + else + { + adjacencyIndices[0] = 0xffffffff; + adjacencyIndices[1] = 0xffffffff; + adjacencyIndices[2] = 0xffffffff; + } + } +} + +PX_FORCE_INLINE void Gu::TriangleMesh::getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal) const +{ + PxU32 vref0, vref1, vref2; + if(has16BitIndices()) + { + const Gu::TriangleT<PxU16>& T = (reinterpret_cast<const Gu::TriangleT<PxU16>*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + else + { + const Gu::TriangleT<PxU32>& T = (reinterpret_cast<const Gu::TriangleT<PxU32>*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + if (flipNormal) + Ps::swap<PxU32>(vref1, vref2); + const PxVec3* PX_RESTRICT vertices = getVerticesFast(); + localTri.verts[0] = vertices[vref0]; + localTri.verts[1] = vertices[vref1]; + localTri.verts[2] = vertices[vref2]; +} + +PX_INLINE float computeSweepData(const PxTriangleMeshGeometry& triMeshGeom, /*const Cm::FastVertex2ShapeScaling& scaling,*/ PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, float distance) +{ + PX_ASSERT(!Cm::isEmpty(sweepOrigin, sweepExtents)); + + const PxVec3 endPt = sweepOrigin + sweepDir*distance; + PX_ASSERT(!Cm::isEmpty(endPt, sweepExtents)); + + const Cm::FastVertex2ShapeScaling meshScaling(triMeshGeom.scale.getInverse()); // shape to vertex transform + + const PxMat33& vertex2ShapeSkew = meshScaling.getVertex2ShapeSkew(); + + const PxVec3 originBoundsCenter = vertex2ShapeSkew * sweepOrigin; + const PxVec3 originBoundsExtents = Cm::basisExtent(vertex2ShapeSkew.column0, vertex2ShapeSkew.column1, vertex2ShapeSkew.column2, sweepExtents); + + sweepOrigin = originBoundsCenter; + sweepExtents = originBoundsExtents; + sweepDir = (vertex2ShapeSkew * endPt) - originBoundsCenter; + return sweepDir.normalizeSafe(); +} + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshBV4.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshBV4.cpp new file mode 100644 index 00000000..f10409e2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshBV4.cpp @@ -0,0 +1,76 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" + +using namespace physx; + +namespace physx +{ + +Gu::BV4TriangleMesh::BV4TriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: TriangleMesh(factory, d) +{ + PX_ASSERT(d.mType==PxMeshMidPhase::eBVH34); + + BV4TriangleData& bv4Data = static_cast<BV4TriangleData&>(d); + mMeshInterface = bv4Data.mMeshInterface; + mBV4Tree = bv4Data.mBV4Tree; + mBV4Tree.mMeshInterface = &mMeshInterface; +} + +Gu::TriangleMesh* Gu::BV4TriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + BV4TriangleMesh* obj = new (address) BV4TriangleMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(BV4TriangleMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void Gu::BV4TriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + mBV4Tree.exportExtraData(stream); + TriangleMesh::exportExtraData(stream); +} + +void Gu::BV4TriangleMesh::importExtraData(PxDeserializationContext& context) +{ + mBV4Tree.importExtraData(context); + TriangleMesh::importExtraData(context); + + if(has16BitIndices()) + mMeshInterface.setPointers(NULL, const_cast<IndTri16*>(reinterpret_cast<const IndTri16*>(getTrianglesFast())), getVerticesFast()); + else + mMeshInterface.setPointers(const_cast<IndTri32*>(reinterpret_cast<const IndTri32*>(getTrianglesFast())), NULL, getVerticesFast()); + mBV4Tree.mMeshInterface = &mMeshInterface; +} + +} // namespace physx diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshBV4.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshBV4.h new file mode 100644 index 00000000..608f5d2d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshBV4.h @@ -0,0 +1,76 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_BV4_H +#define GU_TRIANGLEMESH_BV4_H + +#include "GuTriangleMesh.h" + +namespace physx +{ +class GuMeshFactory; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class BV4TriangleMesh : public TriangleMesh +{ + public: + virtual const char* getConcreteTypeName() const { return "PxBVH34TriangleMesh"; } +// PX_SERIALIZATION + BV4TriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mMeshInterface(PxEmpty), mBV4Tree(PxEmpty) {} + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + BV4TriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~BV4TriangleMesh(){} + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; } + PX_FORCE_INLINE const Gu::BV4Tree& getBV4Tree() const { return mBV4Tree; } + private: + Gu::SourceMesh mMeshInterface; + Gu::BV4Tree mBV4Tree; +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshRTree.cpp b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshRTree.cpp new file mode 100644 index 00000000..ec5a1931 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshRTree.cpp @@ -0,0 +1,148 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuTriangleMesh.h" +#include "GuTriangleMeshRTree.h" +#if PX_ENABLE_DYNAMIC_MESH_RTREE +#include "GuConvexEdgeFlags.h" +#endif + +using namespace physx; + +namespace physx +{ + +Gu::RTreeTriangleMesh::RTreeTriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: TriangleMesh(factory, d) +{ + PX_ASSERT(d.mType==PxMeshMidPhase::eBVH33); + + RTreeTriangleData& rtreeData = static_cast<RTreeTriangleData&>(d); + mRTree = rtreeData.mRTree; + rtreeData.mRTree.mPages = NULL; +} + +Gu::TriangleMesh* Gu::RTreeTriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + RTreeTriangleMesh* obj = new (address) RTreeTriangleMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(RTreeTriangleMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void Gu::RTreeTriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + mRTree.exportExtraData(stream); + TriangleMesh::exportExtraData(stream); +} + +void Gu::RTreeTriangleMesh::importExtraData(PxDeserializationContext& context) +{ + mRTree.importExtraData(context); + TriangleMesh::importExtraData(context); +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +PxVec3 * Gu::RTreeTriangleMesh::getVerticesForModification() +{ + return const_cast<PxVec3*>(getVertices()); +} + +template<typename IndexType> +struct RefitCallback : Gu::RTree::CallbackRefit +{ + const PxVec3* newPositions; + const IndexType* indices; + + RefitCallback(const PxVec3* aNewPositions, const IndexType* aIndices) : newPositions(aNewPositions), indices(aIndices) {} + PX_FORCE_INLINE ~RefitCallback() {} + + virtual void recomputeBounds(PxU32 index, shdfnd::aos::Vec3V& aMn, shdfnd::aos::Vec3V& aMx) + { + using namespace shdfnd::aos; + + // Each leaf box has a set of triangles + Gu::LeafTriangles currentLeaf; currentLeaf.Data = index; + PxU32 nbTris = currentLeaf.GetNbTriangles(); + PxU32 baseTri = currentLeaf.GetTriangleIndex(); + PX_ASSERT(nbTris > 0); + const IndexType* vInds = indices + 3 * baseTri; + Vec3V vPos = V3LoadU(newPositions[vInds[0]]); + Vec3V mn = vPos, mx = vPos; + //PxBounds3 result(newPositions[vInds[0]], newPositions[vInds[0]]); + vPos = V3LoadU(newPositions[vInds[1]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds[2]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + for (PxU32 i = 1; i < nbTris; i++) + { + const IndexType* vInds1 = indices + 3 * (baseTri + i); + vPos = V3LoadU(newPositions[vInds1[0]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds1[1]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds1[2]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + } + + aMn = mn; + aMx = mx; + } +}; + +PxBounds3 Gu::RTreeTriangleMesh::refitBVH() +{ + PxBounds3 meshBounds; + if (has16BitIndices()) + { + RefitCallback<PxU16> cb(mVertices, static_cast<const PxU16*>(mTriangles)); + mRTree.refitAllStaticTree(cb, &meshBounds); + } + else + { + RefitCallback<PxU32> cb(mVertices, static_cast<const PxU32*>(mTriangles)); + mRTree.refitAllStaticTree(cb, &meshBounds); + } + + // reset edge flags and remember we did that using a mesh flag (optimization) + if ((mRTree.mFlags & RTree::IS_EDGE_SET) == 0) + { + mRTree.mFlags |= RTree::IS_EDGE_SET; + const PxU32 nbTris = getNbTriangles(); + for (PxU32 i = 0; i < nbTris; i++) + mExtraTrigData[i] |= (Gu::ETD_CONVEX_EDGE_01 | Gu::ETD_CONVEX_EDGE_12 | Gu::ETD_CONVEX_EDGE_20); + } + + mAABB = meshBounds; + return meshBounds; +} +#endif + +} // namespace physx diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshRTree.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshRTree.h new file mode 100644 index 00000000..7c861663 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleMeshRTree.h @@ -0,0 +1,81 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_RTREE_H +#define GU_TRIANGLEMESH_RTREE_H + +#include "GuTriangleMesh.h" + +namespace physx +{ +class GuMeshFactory; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class RTreeTriangleMesh : public TriangleMesh +{ + public: + virtual const char* getConcreteTypeName() const { return "PxBVH33TriangleMesh"; } +// PX_SERIALIZATION + RTreeTriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mRTree(PxEmpty) {} + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + RTreeTriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~RTreeTriangleMesh(){} + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH33; } + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + virtual PxVec3* getVerticesForModification(); + virtual PxBounds3 refitBVH(); +#endif + + PX_FORCE_INLINE const Gu::RTree& getRTree() const { return mRTree; } + private: + Gu::RTree mRTree; +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleVertexPointers.h b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleVertexPointers.h new file mode 100644 index 00000000..952f6998 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/mesh/GuTriangleVertexPointers.h @@ -0,0 +1,65 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE_VERTEX_POINTERS_H +#define GU_TRIANGLE_VERTEX_POINTERS_H + +#include "PxTriangleMesh.h" +#include "GuTriangleMesh.h" + +namespace physx { + namespace Gu { + + // PT: TODO: replace with Gu::TriangleMesh::getLocalTriangle(...) + struct TriangleVertexPointers + { + static void PX_FORCE_INLINE getTriangleVerts(const TriangleMesh* mesh, PxU32 triangleIndex, PxVec3& v0, PxVec3& v1, PxVec3& v2) + { + const PxVec3* verts = mesh->getVerticesFast(); + if(mesh->has16BitIndices()) + { + const PxU16* tris = reinterpret_cast<const PxU16*>(mesh->getTrianglesFast()); + const PxU16* inds = tris+triangleIndex*3; + v0 = verts[inds[0]]; + v1 = verts[inds[1]]; + v2 = verts[inds[2]]; + } + else + { + const PxU32* tris = reinterpret_cast<const PxU32*>(mesh->getTrianglesFast()); + const PxU32* inds = tris+triangleIndex*3; + v0 = verts[inds[0]]; + v1 = verts[inds[1]]; + v2 = verts[inds[2]]; + } + } + }; +} } // physx, Gu + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactBoxBox.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactBoxBox.cpp new file mode 100644 index 00000000..28417e66 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactBoxBox.cpp @@ -0,0 +1,1029 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "GuGeometryUnion.h" + +#include "GuConvexHelper.h" +#include "GuPCMShapeConvex.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGenUtil.h" + + +namespace physx +{ + +namespace Gu +{ + + static void getIncidentPolygon(Ps::aos::Vec3V* pts, Ps::aos::Vec3V& faceNormal, const Ps::aos::Vec3VArg axis, const Ps::aos::PsMatTransformV& transf1To0, + const Ps::aos::Vec3VArg extents) + { + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + FloatV ex = V3GetX(extents); + FloatV ey = V3GetY(extents); + FloatV ez = V3GetZ(extents); + + const Vec3V u0 = transf1To0.getCol0(); + const Vec3V u1 = transf1To0.getCol1(); + const Vec3V u2 = transf1To0.getCol2(); + + //calcaulte the insident face for b + const FloatV d0 = V3Dot(u0, axis); + const FloatV d1 = V3Dot(u1, axis); + const FloatV d2 = V3Dot(u2, axis); + + const FloatV absd0 = FAbs(d0); + const FloatV absd1 = FAbs(d1); + const FloatV absd2 = FAbs(d2); + + Vec3V r0, r1, r2; + + + if(FAllGrtrOrEq(absd0, absd1) && FAllGrtrOrEq(absd0, absd2)) + { + //the incident face is on u0 + const BoolV con = FIsGrtr(d0, zero); + faceNormal = V3Sel(con, V3Neg(u0), u0); + ex = FSel(con, FNeg(ex), ex); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r0); + const Vec3V temp1 = V3Add(r1, r2); + const Vec3V temp2 = V3Sub(r1, r2); + + + pts[0] = V3Add(temp0, temp1); // (-x/x, y, z) + pts[1] = V3Add(temp0, temp2); // (-x/x, y, -z) + pts[2] = V3Sub(temp0, temp1); // (-x/x, -y, -z) + pts[3] = V3Sub(temp0, temp2); // (-x/x, -y, z) + + } + else if(FAllGrtrOrEq(absd1, absd2)) + { + //the incident face is on u1 + const BoolV con = FIsGrtr(d1, zero); + faceNormal = V3Sel(con, V3Neg(u1), u1); + ey = FSel(con, FNeg(ey), ey); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r1); + const Vec3V temp1 = V3Add(r0, r2); + const Vec3V temp2 = V3Sub(r0, r2); + + pts[0] = V3Add(temp0, temp1); // (x, -y/y, z) + pts[1] = V3Add(temp0, temp2); // (x, -y/y, -z) + pts[2] = V3Sub(temp0, temp1); // (-x, -y/y, -z) + pts[3] = V3Sub(temp0, temp2); // (-x, -y/y, z) + + } + else + { + //the incident face is on u2 + const BoolV con = FIsGrtr(d2, zero); + faceNormal = V3Sel(con, V3Neg(u2), u2); + ez = FSel(con, FNeg(ez), ez); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r2); + const Vec3V temp1 = V3Add(r0, r1); + const Vec3V temp2 = V3Sub(r0, r1); + + pts[0] = V3Add(temp0, temp1); // ( x, y, z) + pts[1] = V3Add(temp0, temp2); // ( x, -y, z) + pts[2] = V3Sub(temp0, temp1); // (-x, -y, z) + pts[3] = V3Sub(temp0, temp2); // (-x, y, z) + } + } + + + //p0 and p1 is in the local space of AABB + static bool intersectSegmentAABB(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg d, const Ps::aos::Vec3VArg max, const Ps::aos::Vec3VArg min, Ps::aos::FloatV& tmin, Ps::aos::FloatV& tmax) + { + using namespace Ps::aos; + + const Vec3V eps = V3Load(1e-6); + const Vec3V absV = V3Abs(d); + const FloatV one = FOne(); + const Vec3V zero = V3Zero(); + const Vec3V fMax = Vec3V_From_FloatV(FMax()); + + FloatV tminf = FZero(); + FloatV tmaxf = one; + const BoolV isParallel = V3IsGrtr(eps, absV); + const BoolV isOutsideOfRange = BOr(V3IsGrtr(p0, max), V3IsGrtr(min, p0)); + //const BoolV isParallelAndOutOfRange = BAnd(isParallel, isOutsideOfRange); + + if(!BAllEqFFFF(BAnd(isParallel, isOutsideOfRange))) + { + return false; + } + + const Vec3V odd = V3RecipFast(d); + const Vec3V t1 = V3Sel(isParallel, zero, V3Mul(V3Sub(min, p0), odd)); + const Vec3V t2 = V3Sel(isParallel, fMax, V3Mul(V3Sub(max, p0), odd)); + + const Vec3V tt1 = V3Min(t1, t2); + const Vec3V tt2 = V3Max(t1, t2); + + const FloatV ft1 = V3ExtractMax(tt1); + const FloatV ft2 = V3ExtractMin(tt2); + + tminf = FMax(ft1, tminf); + tmaxf = FMin(tmaxf, ft2); + + tmin = tminf; + tmax = tmaxf; + + const BoolV con0 = FIsGrtr(tminf, tmaxf); + const BoolV con1 = FIsGrtr(tminf, one); + const BoolV isNotIntersect = BOr(con0, con1); + return BAllEqFFFF(isNotIntersect) == 1; + } + + + //pts, faceNormal and contact normal are in the local space of new space + static void calculateContacts( const Ps::aos::FloatVArg extentX, const Ps::aos::FloatVArg extentY, Ps::aos::Vec3V* pts, const Ps::aos::Vec3VArg incidentFaceNormalInNew, const Ps::aos::Vec3VArg localNormal, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist) + { + using namespace Ps::aos; + + const FloatV zero = FZero(); + const FloatV max = FMax(); + + const FloatV nExtentX = FNeg(extentX); + const FloatV nExtentY = FNeg(extentY); + + bool pPenetration[4]; + bool pArea[4]; + + Vec3V bmin = V3Splat(max); + Vec3V bmax = V3Neg(bmin); + + const Vec3V bound = V3Merge(extentX, extentY, max); + + + //get the projection point of pts + for(PxU32 i=0; i< 4; ++i) + { + bmin = V3Min(bmin, pts[i]); + bmax = V3Max(bmax, pts[i]); + const FloatV z = FNeg(V3GetZ(pts[i])); + if(FAllGrtr(contactDist, z)) + { + + pPenetration[i] = true; + + const Vec3V absPt= V3Abs(pts[i]); + const BoolV con = V3IsGrtrOrEq(bound, absPt); + if(BAllEqTTTT(con)) + { + pArea[i] = true; + + //Add the point to the manifold + manifoldContacts[numContacts].mLocalPointA = V3SetZ(pts[i], zero); //transformNewTo0.transform(localPointA); + manifoldContacts[numContacts].mLocalPointB = pts[i];//transform1ToNew.transformInv(pts[i]); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), z); + } + else + { + pArea[i] = false; + } + } + else + { + pPenetration[i] = false; + pArea[i] = false; + } + + } + + if(numContacts == 4) + return; + + //if(pPenetration[0] && pPenetration[1] && pPenetration[2] && pPenetration[3]) + { + //if(!pArea[0] || !pArea[1] || !pArea[2] || !pArea[3]) + { + const FloatV denom = V3GetZ(incidentFaceNormalInNew); + { + const Vec3V q0 = V3Merge(extentX, extentY, zero); + + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge(extentX, nExtentY, zero); + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge( nExtentX, extentY, zero); + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge(nExtentX, nExtentY, zero); + + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + } + } + + + + const Vec3V ext = V3Merge(extentX, extentY, max); + const Vec3V negExt = V3Merge(nExtentX, nExtentY, FNeg(FAdd(contactDist, FEps()))); + + for (PxU32 rStart = 0, rEnd = 3; rStart < 4; rEnd = rStart++) + { + const Vec3V p0 = pts[rStart]; + const Vec3V p1 = pts[rEnd]; + + if(!pPenetration[rStart] && !pPenetration[rEnd]) + continue; + + const bool con0 = pPenetration[rStart] && pArea[rStart]; + const bool con1 = pPenetration[rEnd] && pArea[rEnd]; + if(con0 && con1) + continue; + + //intersect t value with x plane + const Vec3V p0p1 = V3Sub(p1, p0); + + FloatV tmin, tmax; + if(Gu::intersectSegmentAABB(p0, p0p1, ext, negExt, tmin, tmax)) + { + if(!con0) + { + const Vec3V intersectP = V3ScaleAdd(p0p1, tmin, p0); + manifoldContacts[numContacts].mLocalPointA = V3SetZ(intersectP, zero); + manifoldContacts[numContacts].mLocalPointB = intersectP; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), FNeg(V3GetZ(intersectP))); + } + if(!con1) + { + const Vec3V intersectP = V3ScaleAdd(p0p1, tmax, p0); + manifoldContacts[numContacts].mLocalPointA = V3SetZ(intersectP, zero); + manifoldContacts[numContacts].mLocalPointB = intersectP; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), FNeg(V3GetZ(intersectP))); + } + } + } +} + + +static PxU32 doBoxBoxGenerateContacts(const Ps::aos::Vec3VArg box0Extent, const Ps::aos::Vec3VArg box1Extent, const Ps::aos::PsMatTransformV& transform0, const Ps::aos::PsMatTransformV& transform1, const Ps::aos::FloatVArg contactDist, Gu::PersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + const FloatV ea0 = V3GetX(box0Extent); + const FloatV ea1 = V3GetY(box0Extent); + const FloatV ea2 = V3GetZ(box0Extent); + + const FloatV eb0 = V3GetX(box1Extent); + const FloatV eb1 = V3GetY(box1Extent); + const FloatV eb2 = V3GetZ(box1Extent); + + + const PsMatTransformV transform1To0 = transform0.transformInv(transform1); + const Mat33V rot0To1 =M33Trnsps(transform1To0.rot); + + const Vec3V uEps = V3Load(1e-6f); + + const FloatV zero = FZero(); + + const FloatV tx = V3GetX(transform1To0.p); + const FloatV ty = V3GetY(transform1To0.p); + const FloatV tz = V3GetZ(transform1To0.p); + const Vec3V col0 = transform1To0.getCol0(); + const Vec3V col1 = transform1To0.getCol1(); + const Vec3V col2 = transform1To0.getCol2(); + + const Vec3V abs1To0Col0 = V3Add(V3Abs(col0), uEps); + const Vec3V abs1To0Col1 = V3Add(V3Abs(col1), uEps); + const Vec3V abs1To0Col2 = V3Add(V3Abs(col2), uEps); + + const Vec3V abs0To1Col0 = V3Add(V3Abs(rot0To1.col0), uEps); + const Vec3V abs0To1Col1 = V3Add(V3Abs(rot0To1.col1), uEps); + const Vec3V abs0To1Col2 = V3Add(V3Abs(rot0To1.col2), uEps); + + + FloatV sign[6]; + FloatV overlap[6]; + + FloatV ra, rb, radiusSum; + //ua0 + { + + sign[0] = tx; + + const Vec3V vtemp3 = V3Mul(abs0To1Col0, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea0, rb); + overlap[0] = FAdd(FSub(radiusSum, FAbs(sign[0])), contactDist); + if(FAllGrtr(zero, overlap[0])) + return false; + } + + //ua1 + { + sign[1] = ty; + + const Vec3V vtemp3 = V3Mul(abs0To1Col1, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea1, rb); + overlap[1] = FAdd(FSub(radiusSum, FAbs(sign[1])), contactDist); + if(FAllGrtr(zero, overlap[1])) + return false; + + } + + + //ua2 + { + sign[2] = tz; + ra = ea2; + + const Vec3V vtemp3 = V3Mul(abs0To1Col2, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea2, rb); + overlap[2] = FAdd(FSub(radiusSum, FAbs(sign[2])), contactDist); + if(FAllGrtr(zero, overlap[2])) + return false; + + } + + //ub0 + { + sign[3] = V3Dot(transform1To0.p, col0); + + const Vec3V vtemp3 = V3Mul(abs1To0Col0, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb0); + overlap[3] = FAdd(FSub(radiusSum, FAbs(sign[3])), contactDist); + if(FAllGrtr(zero, overlap[3])) + return false; + + } + + //ub1 + { + sign[4] = V3Dot(transform1To0.p, col1); + + const Vec3V vtemp3 = V3Mul(abs1To0Col1, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb1); + overlap[4] = FAdd(FSub(radiusSum, FAbs(sign[4])), contactDist); + if(FAllGrtr(zero, overlap[4])) + return false; + } + + //ub2 + { + sign[5] = V3Dot(transform1To0.p, col2); + + const Vec3V vtemp3 = V3Mul(abs1To0Col2, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb2); + overlap[5] = FAdd(FSub(radiusSum, FAbs(sign[5])), contactDist); + if(FAllGrtr(zero, overlap[5])) + return false; + } + + + //ua0 X ub0 + { + //B into A's space, ua0Xub0[0,-b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col0), tz), FMul(V3GetZ(col0), ty))); + + //B into A's space, ua0Xub0[0,-b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col0), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col0), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub0[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col0), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col0), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum)) return false; + } + + //ua0 X ub1 + { + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col1), tz), FMul(V3GetZ(col1), ty))); + + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col1), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col1), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col0), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col0), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + + if(FAllGrtr(absSign, radiusSum)) return false; + + } + + //ua0 X ub2 + { + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col2), tz), FMul(V3GetZ(col2), ty))); + + + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col2), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col2), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col0), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col0), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum)) return false; + + } + + //ua1 X ub0 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign = FAbs(FSub(FMul(V3GetZ(col0), tx), FMul(V3GetX(col0), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col0), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col0), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col1), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col1), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + + } + + //ua1 X ub1 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign = FAbs(FSub(FMul(V3GetZ(col1), tx), FMul(V3GetX(col1), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col1), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col1), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, -a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col1), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col1), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua1 X ub2 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign=FAbs(FSub(FMul(V3GetZ(col2), tx), FMul(V3GetX(col2), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col2), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col2), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col1), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col1), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub0 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign = FAbs(FSub(FMul(V3GetX(col0), ty), FMul(V3GetY(col0), tx))); + + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col0), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col0), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col2), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col2), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub1 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign = FAbs(FSub(FMul(V3GetX(col1), ty), FMul(V3GetY(col1), tx))); + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col1), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col1), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col2), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col2), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub2 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign=FAbs(FSub(FMul(V3GetX(col2), ty), FMul(V3GetY(col2), tx))); + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col2), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col2), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col2), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col2), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + Vec3V mtd; + + PxU32 feature = 0; + FloatV minOverlap = overlap[0]; + + for(PxU32 i=1; i<6; ++i) + { + if(FAllGrtr(minOverlap, overlap[i])) + { + minOverlap = overlap[i]; + feature = i; + } + } + + + PsMatTransformV newTransformV; + const Vec3V axis00 = transform0.getCol0(); + const Vec3V axis01 = transform0.getCol1(); + const Vec3V axis02 = transform0.getCol2(); + const Vec3V axis10 = transform1.getCol0(); + const Vec3V axis11 = transform1.getCol1(); + const Vec3V axis12 = transform1.getCol2(); + + Vec3V incidentFaceNormalInNew; + Vec3V pts[4]; + bool flip = false; + switch(feature) + { + case 0: //ua0 + { + + + if(FAllGrtrOrEq(zero, sign[0])) + { + mtd = axis00; + newTransformV.rot.col0 = V3Neg(axis02); + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = axis00; + newTransformV.p = V3NegScaleSub(axis00, ea0, transform0.p); + } + else + { + + const Vec3V nAxis00 = V3Neg(axis00); + mtd = nAxis00; + newTransformV.rot.col0 = axis02; + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = nAxis00; + newTransformV.p = V3ScaleAdd(axis00, ea0, transform0.p); + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea2, ea1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + + break; + }; + case 1: //ua1 + { + + if(FAllGrtrOrEq(zero, sign[1])) + { + mtd = axis01; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = V3Neg(axis02); + newTransformV.rot.col2 = axis01; + newTransformV.p = V3NegScaleSub(axis01, ea1, transform0.p); + + } + else + { + + + const Vec3V nAxis01 = V3Neg(axis01); + mtd = nAxis01; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = axis02; + newTransformV.rot.col2 = nAxis01; + newTransformV.p = V3ScaleAdd(axis01, ea1, transform0.p); + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea0, ea2, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + break; + + }; + case 2: //ua2 + { + + if(FAllGrtrOrEq(zero, sign[2])) + { + mtd = axis02; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = axis02; + + newTransformV.p = V3NegScaleSub(axis02, ea2, transform0.p); + } + else + { + const Vec3V nAxis02 = V3Neg(axis02); + mtd = nAxis02; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = V3Neg(axis01); + newTransformV.rot.col2 = nAxis02; + newTransformV.p = V3ScaleAdd(axis02, ea2, transform0.p); + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea0, ea1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + + break; + }; + case 3: //ub0 + { + + flip = true; + if(FAllGrtrOrEq(zero, sign[3])) + { + mtd = axis10; + newTransformV.rot.col0 = axis12; + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = V3Neg(axis10); + newTransformV.p = V3ScaleAdd(axis10, eb0, transform1.p); //transform0.p - extents0.x*axis00; + } + else + { + mtd = V3Neg(axis10); + newTransformV.rot.col0 = V3Neg(axis12); + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = axis10; + newTransformV.p =V3NegScaleSub(axis10, eb0, transform1.p);//transform0.p + extents0.x*axis00; + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + + calculateContacts(eb2, eb1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + break; + }; + case 4: //ub1; + { + flip = true; + if(FAllGrtrOrEq(zero, sign[4])) + { + mtd = axis11; + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = axis12; + newTransformV.rot.col2 = V3Neg(axis11); + + newTransformV.p = V3ScaleAdd(axis11, eb1, transform1.p); + + } + else + { + mtd = V3Neg(axis11); + + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = V3Neg(axis12); + newTransformV.rot.col2 = axis11; + newTransformV.p = V3NegScaleSub(axis11, eb1, transform1.p); //transform0.p + extents0.x*axis00; + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + calculateContacts(eb0, eb2, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + break; + } + case 5: //ub2; + { + + flip = true; + if(FAllGrtrOrEq(zero, sign[5])) + { + mtd = axis12; + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = V3Neg(axis11); + newTransformV.rot.col2 = V3Neg(axis12); + newTransformV.p = V3ScaleAdd(axis12, eb2, transform1.p); + } + else + { + mtd = V3Neg(axis12); + + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = axis12; + newTransformV.p = V3NegScaleSub(axis12, eb2, transform1.p); + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + + calculateContacts(eb0, eb1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + break; + }; + default: + return false; + } + + if(flip) + { + for(PxU32 i=0; i<numContacts; ++i) + { + const Vec3V localB = manifoldContacts[i].mLocalPointB; + manifoldContacts[i].mLocalPointB = manifoldContacts[i].mLocalPointA; + manifoldContacts[i].mLocalPointA = localB; + } + } + const Ps::aos::PsMatTransformV transformNewTo1 = transform1.transformInv(newTransformV); + const Ps::aos::PsMatTransformV transformNewTo0 = transform0.transformInv(newTransformV); + //transform points to the local space of transform0 and transform1 + const Ps::aos::Vec3V localNormalInB = transformNewTo1.rotate(Vec3V_From_Vec4V(manifoldContacts[0].mLocalNormalPen)); + + for(PxU32 i=0; i<numContacts; ++i) + { + manifoldContacts[i].mLocalPointA = transformNewTo0.transform(manifoldContacts[i].mLocalPointA); + manifoldContacts[i].mLocalPointB = transformNewTo1.transform(manifoldContacts[i].mLocalPointB); + manifoldContacts[i].mLocalNormalPen = V4SetW(localNormalInB, V4GetW(manifoldContacts[i].mLocalNormalPen)); + } + + return true; +} + + + +bool pcmContactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + // Get actual shape data + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + const PxBoxGeometry& shapeBox0 = shape0.get<const PxBoxGeometry>(); + const PxBoxGeometry& shapeBox1 = shape1.get<const PxBoxGeometry>(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + + const FloatV contactDist = FLoad(params.mContactDistance); + const Vec3V boxExtents0 = V3LoadU(shapeBox0.halfExtents); + const Vec3V boxExtents1 = V3LoadU(shapeBox1.halfExtents); + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const FloatV boxMargin0 = Gu::CalculatePCMBoxMargin(boxExtents0); + const FloatV boxMargin1 = Gu::CalculatePCMBoxMargin(boxExtents1); + const FloatV minMargin = FMin(boxMargin0, boxMargin1); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + + const bool bLostContacts = (newContacts != initialContacts); + + PX_UNUSED(bLostContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + + manifold.setRelativeTransform(curRTrans); + + const PsMatTransformV transfV0(transf0); + const PsMatTransformV transfV1(transf1); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + if(doBoxBoxGenerateContacts(boxExtents0, boxExtents1, transfV0, transfV1, contactDist, manifoldContacts, numContacts)) + { + if(numContacts > 0) + { + + manifold.addBatchManifoldContacts(manifoldContacts, numContacts); + const Vec3V worldNormal = transfV1.rotate(Vec3V_From_Vec4V(manifold.mContactPoints[0].mLocalNormalPen)); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transfV1); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + else + { + const Vec3V zeroV = V3Zero(); + ShrunkBoxV box0(zeroV, boxExtents0); + ShrunkBoxV box1(zeroV, boxExtents1); + Vec3V closestA(zeroV), closestB(zeroV), normal(zeroV); // these will be in the local space of B + FloatV penDep = FZero(); + manifold.mNumWarmStartPoints = 0; + RelativeConvex<ShrunkBoxV> convexA(box0, aToB); + LocalConvex<ShrunkBoxV> convexB(box1); + GjkStatus status = gjkPenetration<RelativeConvex<ShrunkBoxV>, LocalConvex<ShrunkBoxV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, false); + + if(status == EPA_CONTACT) + { + + RelativeConvex<BoxV> convexA1(box0, aToB); + LocalConvex<BoxV> convexB1(box1); + + status= epaPenetration(convexA1, convexB1, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep); + } + + if(status == GJK_CONTACT || status == EPA_CONTACT) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec3V localPointB = closestB; + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + numContacts += manifold.addManifoldPoint(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + + //transform the normal back to world space + normal = transf1.rotate(normal); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf1, contactDist); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + return true; + } + + } + } + } + else if(manifold.getNumContacts() > 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; +} + +} +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactBoxConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactBoxConvex.cpp new file mode 100644 index 00000000..8378e2ce --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactBoxConvex.cpp @@ -0,0 +1,281 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "GuVecShrunkConvexHull.h" +#include "GuVecShrunkConvexHullNoScale.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "GuContactBuffer.h" + +namespace physx +{ + +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationBoxConvex(const PxVec3& halfExtents, const BoxV& box, ConvexHullV& convexHull, const PsTransformV& transf0, const PsTransformV& transf1, + PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, Gu::PersistentContactManifold& manifold, Vec3VArg normal, + const Vec3VArg closestA, const Vec3VArg closestB, const FloatVArg contactDist, const bool idtScale, const bool doOverlapTest, Cm::RenderOutput* renderOutput, + const FloatVArg toleranceScale) +{ + Gu::PolygonalData polyData0; + PCMPolygonalBox polyBox0(halfExtents); + polyBox0.getPolygonalData(&polyData0); + polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; + + Gu::PolygonalData polyData1; + getPCMConvexData(convexHull, idtScale, polyData1); + + Mat33V identity = M33Identity(); + SupportLocalImpl<BoxV> map0(box, transf0, identity, identity, true); + + PxU8 buff1[sizeof(SupportLocalImpl<ConvexHullV>)]; + + SupportLocal* map1 = (idtScale ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff1, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<ConvexHullNoScaleV&>(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff1, SupportLocalImpl<ConvexHullV>)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if(generateFullContactManifold(polyData0, polyData1, &map0, map1, manifoldContacts, numContacts, contactDist, normal, closestA, closestB, box.getMargin(), convexHull.getMargin(), + doOverlapTest, renderOutput, toleranceScale)) + { + if (numContacts > 0) + { + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + else + { + //if doOverlapTest is true, which means GJK/EPA degenerate so we won't have any contact in the manifoldContacts array + if (!doOverlapTest) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + } + + return true; + } + + return false; + +} + +static bool addGJKEPAContacts(Gu::ShrunkConvexHullV& convexHull, Gu::ShrunkBoxV& box, const PsMatTransformV& aToB, GjkStatus status, + Gu::PersistentContact* manifoldContacts, const FloatV replaceBreakingThreshold, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& penDep, + Gu::PersistentContactManifold& manifold) +{ + bool doOverlapTest = false; + if (status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint(aToB.transformInv(closestA), closestB, localNormalPen, replaceBreakingThreshold); + + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + RelativeConvex<BoxV> epaConvexA(box, aToB); + LocalConvex<ConvexHullV> epaConvexB(convexHull); + + status = epaPenetration(epaConvexA, epaConvexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep); + if (status == EPA_CONTACT) + { + + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint(localPointA, closestB, localNormalPen, replaceBreakingThreshold); + } + else + { + doOverlapTest = true; + } + } + + return doOverlapTest; +} + +bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>(); + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(shapeConvex.hullData); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V zeroV = V3Zero(); + + + const FloatV contactDist = FLoad(params.mContactDistance); + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale); + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents); + + const FloatV minMargin = FMin(convexMargin, boxMargin);//FMin(boxMargin, convexMargin); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + //After the refresh contact points, the numcontacts in the manifold will be changed + + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + PX_UNUSED(bLostContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + Vec3V closestA(zeroV), closestB(zeroV), normal(zeroV); // from a to b + FloatV penDep = FZero(); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + const bool idtScale = shapeConvex.scale.isIdentity(); + Gu::ShrunkConvexHullV convexHull(hullData, zeroV, vScale, vQuat, idtScale); + Gu::ShrunkBoxV box(zeroV, boxExtents); + + const RelativeConvex<ShrunkBoxV> convexA(box, aToB); + if(idtScale) + { + const LocalConvex<ShrunkConvexHullNoScaleV> convexB(*PX_SCONVEX_TO_NOSCALECONVEX(&convexHull)); + status = gjkPenetration<RelativeConvex<ShrunkBoxV>, LocalConvex<ShrunkConvexHullNoScaleV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, false); + } + else + { + const LocalConvex<ShrunkConvexHullV> convexB(convexHull); + status = gjkPenetration<RelativeConvex<ShrunkBoxV>, LocalConvex<ShrunkConvexHullV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, false); + + } + + manifold.setRelativeTransform(curRTrans); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + if(status == GJK_DEGENERATE) + { + return fullContactsGenerationBoxConvex(shapeBox.halfExtents, box, convexHull, transf0, transf1, manifoldContacts, contactBuffer, + manifold, normal, closestA, closestB, contactDist, idtScale, true, renderOutput, FLoad(params.mToleranceLength)); + } + else if(status == GJK_NON_INTERSECT) + { + return false; + } + else + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + //addGJKEPAContacts will increase the number of contacts in manifold. If status == EPA_CONTACT, we need to run epa algorithm and generate closest points, normal and + //pentration. If epa doesn't degenerate, we will store the contacts information in the manifold. Otherwise, we will return true to do the fallback test + const bool doOverlapTest = addGJKEPAContacts(convexHull, box, aToB, status, manifoldContacts, replaceBreakingThreshold, closestA, closestB, normal, penDep, manifold); + + if ((initialContacts == 0) || (manifold.mNumContacts < initialContacts) || doOverlapTest) + { + return fullContactsGenerationBoxConvex(shapeBox.halfExtents, box, convexHull, transf0, transf1, manifoldContacts, contactBuffer, + manifold, normal, closestA, closestB, contactDist, idtScale, doOverlapTest, renderOutput, FLoad(params.mToleranceLength)); + } + else + { + const Vec3V worldNormal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + return true; + } + } + } + else if(manifold.getNumContacts()>0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; + +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleBox.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleBox.cpp new file mode 100644 index 00000000..c0c4c968 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleBox.cpp @@ -0,0 +1,230 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "GuGJKPenetration.h" +#include "GuEPA.h" + + +namespace physx +{ + using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationCapsuleBox(const CapsuleV& capsule, const BoxV& box, const PxVec3 halfExtents, const PsMatTransformV& aToB, const PsTransformV& transf0, const PsTransformV& transf1, + PersistentContact* manifoldContacts, PxU32& numContacts, ContactBuffer& contactBuffer, PersistentContactManifold& manifold, Vec3V& normal, const Vec3VArg closest, + const FloatVArg boxMargin, const FloatVArg contactDist, const bool doOverlapTest, const FloatVArg toleranceScale) +{ + + PolygonalData polyData; + PCMPolygonalBox polyBox(halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + SupportLocalImpl<BoxV> map(box, transf1, identity, identity); + + PxU32 origContacts = numContacts; + if (generateCapsuleBoxFullContactManifold(capsule, polyData, &map, aToB, manifoldContacts, numContacts, contactDist, normal, closest, boxMargin, doOverlapTest, toleranceScale)) + { + //EPA has contacts and we have new contacts, we discard the EPA contacts + if(origContacts != 0 && numContacts != origContacts) + { + numContacts--; + manifoldContacts++; + } + + manifold.addBatchManifoldContacts2(manifoldContacts, numContacts); + + normal = transf1.rotate(normal); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf0, capsule.radius, contactDist); + + return true; + + } + + return false; + +} + + +bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + + PX_UNUSED(renderOutput); + + PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + + const PsTransformV curRTrans = transf1.transformInv(transf0); + const PsMatTransformV aToB_(curRTrans); + + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + + const PxU32 initialContacts = manifold.mNumContacts; + + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents); + + const FloatV minMargin = FMin(boxMargin, capsuleRadius); + + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + const FloatV refreshDist = FAdd(contactDist, capsuleRadius); + //refreshContactPoints remove invalid contacts from the manifold and update the number correspondingly + manifold.refreshContactPoints(aToB_, projectBreakingThreshold, refreshDist); + + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + PX_UNUSED(bLostContacts); + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + Vec3V closestA(zeroV), closestB(zeroV); + Vec3V normal(zeroV); // from a to b + const FloatV zero = FZero(); + FloatV penDep = zero; + + manifold.setRelativeTransform(curRTrans); + const PsMatTransformV aToB(curRTrans); + + BoxV box(transf1.p, boxExtents); + box.setMargin(zero); + + //transform capsule into the local space of box + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + LocalConvex<CapsuleV> convexA(capsule); + LocalConvex<BoxV> convexB(box); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); + status = gjkPenetration<LocalConvex<CapsuleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, true); + + PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + bool doOverlapTest = false; + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_DEGENERATE) + { + return fullContactsGenerationCapsuleBox(capsule, box, shapeBox.halfExtents, aToB, transf0, transf1, manifoldContacts, numContacts, contactBuffer, + manifold, normal, closestB, box.getMargin(), contactDist, true, FLoad(params.mToleranceLength)); + } + else + { + if(status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + //Add contact to contact stream + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = closestB; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + status= epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep, true); + if(status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + //Add contact to contact stream + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = closestB; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + + } + else + { + doOverlapTest = true; + } + + } + + + if(initialContacts == 0 || bLostContacts || doOverlapTest) + { + return fullContactsGenerationCapsuleBox(capsule, box, shapeBox.halfExtents, aToB, transf0, transf1, manifoldContacts, numContacts, contactBuffer, manifold, normal, + closestB, box.getMargin(), contactDist, doOverlapTest, FLoad(params.mToleranceLength)); + } + else + { + + //The contacts is either come from GJK or EPA + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.1f)); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + manifold.addManifoldPoint2(curRTrans.transformInv(closestA), closestB, localNormalPen, replaceBreakingThreshold); + + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf0, capsuleRadius, contactDist); + + return true; + } + } + } + else if(manifold.getNumContacts() > 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf0, capsuleRadius, contactDist); + return true; + } + + return false; + +} +} +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleCapsule.cpp new file mode 100644 index 00000000..4b99ae76 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleCapsule.cpp @@ -0,0 +1,297 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecCapsule.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +using namespace physx; +using namespace Gu; +using namespace Ps; +using namespace aos; + +static Vec4V pcmDistancePointSegmentTValue22( const Vec3VArg a0, const Vec3VArg b0, + const Vec3VArg a1, const Vec3VArg b1, + const Vec3VArg p0, const Vec3VArg p1, + const Vec3VArg p2, const Vec3VArg p3) +{ + const Vec4V zero = V4Zero(); + const Vec3V ap00 = V3Sub(p0, a0); + const Vec3V ap10 = V3Sub(p1, a0); + const Vec3V ap01 = V3Sub(p2, a1); + const Vec3V ap11 = V3Sub(p3, a1); + + const Vec3V ab0 = V3Sub(b0, a0); + const Vec3V ab1 = V3Sub(b1, a1); + +/* const FloatV nom00 = V3Dot(ap00, ab0); + const FloatV nom10 = V3Dot(ap10, ab0); + const FloatV nom01 = V3Dot(ap01, ab1); + const FloatV nom11 = V3Dot(ap11, ab1);*/ + + const Vec4V combinedDot = V3Dot4(ap00, ab0, ap10, ab0, ap01, ab1, ap11, ab1); + const FloatV nom00 = V4GetX(combinedDot); + const FloatV nom10 = V4GetY(combinedDot); + const FloatV nom01 = V4GetZ(combinedDot); + const FloatV nom11 = V4GetW(combinedDot); + + const FloatV denom0 = V3Dot(ab0, ab0); + const FloatV denom1 = V3Dot(ab1, ab1); + + const Vec4V nom = V4Merge(nom00, nom10, nom01, nom11); + const Vec4V denom = V4Merge(denom0, denom0, denom1, denom1); + + const Vec4V tValue = V4Div(nom, denom); + return V4Sel(V4IsEq(denom, zero), zero, tValue); +} + +namespace physx +{ +namespace Gu +{ + + static void storeContact(const Vec3VArg contact, const Vec3VArg normal, const FloatVArg separation, Gu::ContactBuffer& buffer) + { + Gu::ContactPoint& point = buffer.contacts[buffer.count++]; + + const Vec4V normalSep = Ps::aos::V4SetW(Vec4V_From_Vec3V(normal), separation); + + V4StoreA(normalSep, &point.normal.x); + V3StoreA(contact, point.point); + point.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + } + +bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule0 = shape0.get<const PxCapsuleGeometry>(); + const PxCapsuleGeometry& shapeCapsule1 = shape1.get<const PxCapsuleGeometry>(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V _p0 = V3LoadA(&transform0.p.x); + const QuatV q0 = QuatVLoadA(&transform0.q.x); + + const Vec3V _p1 = V3LoadA(&transform1.p.x); + const QuatV q1 = QuatVLoadA(&transform1.q.x); + + /*PsTransformV transf0(p0, q0); + PsTransformV transf1(p1, q1);*/ + + + const FloatV r0 = FLoad(shapeCapsule0.radius); + const FloatV halfHeight0 = FLoad(shapeCapsule0.halfHeight); + + const FloatV r1 = FLoad(shapeCapsule1.radius); + const FloatV halfHeight1 = FLoad(shapeCapsule1.halfHeight); + + const FloatV cDist = FLoad(params.mContactDistance); + + const Vec3V positionOffset = V3Scale(V3Add(_p0, _p1), FHalf()); + const Vec3V p0 = V3Sub(_p0, positionOffset); + const Vec3V p1 = V3Sub(_p1, positionOffset); + + const FloatV zero = FZero(); + //const FloatV one = FOne(); + const Vec3V zeroV = V3Zero(); + + + /*const Vec3V positionOffset = V3Scale(V3Add(transf0.p, transf1.p), FloatV_From_F32(0.5f)); + transf0.p = V3Sub(transf0.p, positionOffset); + transf1.p = V3Sub(transf1.p, positionOffset);*/ + + const Vec3V basisVector0 = QuatGetBasisVector0(q0); + const Vec3V tmp0 = V3Scale(basisVector0, halfHeight0); + const Vec3V s0 = V3Add(p0, tmp0); + const Vec3V e0 = V3Sub(p0, tmp0); + const Vec3V d0 = V3Sub(e0, s0); + + const Vec3V basisVector1 = QuatGetBasisVector0(q1); + const Vec3V tmp1 = V3Scale(basisVector1, halfHeight1); + const Vec3V s1 = V3Add(p1, tmp1); + const Vec3V e1 = V3Sub(p1, tmp1); + + const Vec3V d1 = V3Sub(e1, s1); + + const FloatV sumRadius = FAdd(r0, r1); + const FloatV inflatedSum = FAdd(sumRadius, cDist); + const FloatV inflatedSumSquared = FMul(inflatedSum, inflatedSum); + const FloatV a = V3Dot(d0, d0);//squared length of segment1 + const FloatV e = V3Dot(d1, d1);//squared length of segment2 + const FloatV eps = FLoad(1e-6);//FEps(); + + FloatV t0, t1; + const FloatV sqDist0 = distanceSegmentSegmentSquared(s0, d0, s1, d1, t0, t1); + + if(FAllGrtrOrEq(inflatedSumSquared, sqDist0)) + { + const Vec4V zeroV4 = V4Zero(); + const Vec4V oneV4 = V4One(); + //check to see whether these two capsule are paralle + const FloatV parallelTolerance = FLoad(0.9998f); + + + const BoolV con0 = FIsGrtr(eps, a); + const BoolV con1 = FIsGrtr(eps, e); + const Vec3V dir0 = V3Sel(con0, zeroV, V3ScaleInv(d0, FSqrt(a))); + const Vec3V dir1 = V3Sel(con1, zeroV, V3ScaleInv(d1, FSqrt(e))); + + const FloatV cos = FAbs(V3Dot(dir0, dir1)); + if(FAllGrtr(cos, parallelTolerance))//paralle + { + //project s, e into s1e1 + const Vec4V t= pcmDistancePointSegmentTValue22(s0, e0, s1, e1, + s1, e1, s0, e0); + + const BoolV con = BAnd(V4IsGrtrOrEq(t, zeroV4), V4IsGrtrOrEq(oneV4, t)); + const BoolV con00 = BGetX(con); + const BoolV con01 = BGetY(con); + const BoolV con10 = BGetZ(con); + const BoolV con11 = BGetW(con); + + /* PX_ALIGN(16, PxU32 conditions[4]); + F32Array_Aligned_From_Vec4V(con, (PxF32*)conditions);*/ + + + PxU32 numContact=0; + + if(BAllEqTTTT(con00)) + { + const Vec3V projS1 = V3ScaleAdd(d0, V4GetX(t), s0); + const Vec3V v = V3Sub(projS1, s1); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, projS1); + const Vec3V p = V3Add(_p, positionOffset); + + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + if(BAllEqTTTT(con01)) + { + const Vec3V projE1 = V3ScaleAdd(d0, V4GetY(t), s0); + const Vec3V v = V3Sub(projE1, e1); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, projE1); + const Vec3V p = V3Add(_p, positionOffset); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(BAllEqTTTT(con10)) + { + const Vec3V projS0 = V3ScaleAdd(d1, V4GetZ(t), s1); + const Vec3V v = V3Sub(s0, projS0); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, s0); + const Vec3V p = V3Add(_p, positionOffset); + //const Vec3V p = V3ScaleAdd(normal, r0, s0); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(BAllEqTTTT(con11)) + { + const Vec3V projE0 = V3ScaleAdd(d1, V4GetW(t), s1); + const Vec3V v = V3Sub(e0, projE0); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, e0); + const Vec3V p = V3Add(_p, positionOffset); + //const Vec3V p = V3ScaleAdd(normal, r0, e0); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(numContact) + return true; + + } + + const Vec3V closestA = V3ScaleAdd(d0, t0, s0); + const Vec3V closestB = V3ScaleAdd(d1, t1, s1); + + const BoolV con = FIsGrtr(eps, sqDist0); + //const Vec3V normal = V3Sel(FIsEq(dist, zero), V3Sel(FIsGrtr(a, eps), V3Normalise(d0), V3Scale(V3Sub(closestA, closestB), FRecip(dist))); + const Vec3V _normal = V3Sel(con, V3Sel(FIsGrtr(a, eps), d0, V3UnitX()), V3Sub(closestA, closestB)); + const Vec3V normal = V3Normalize(_normal); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _point = V3NegScaleSub(normal, r0, closestA); + const Vec3V p = V3Add(_point, positionOffset); + const FloatV dist = FSel(con, zero, FSqrt(sqDist0)); + const FloatV pen = FSub(dist, sumRadius); + //PX_ASSERT(FAllGrtrOrEq(zero, pen)); + storeContact(p, normal, pen, contactBuffer); + return true; + } + return false; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleConvex.cpp new file mode 100644 index 00000000..e80e39bd --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleConvex.cpp @@ -0,0 +1,272 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" + +namespace physx +{ + +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationCapsuleConvex(const CapsuleV& capsule, const ConvexHullV& convexHull, const PsMatTransformV& aToB, const PsTransformV& transf0,const PsTransformV& transf1, + PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, const bool idtScale, PersistentContactManifold& manifold, Vec3VArg normal, + const Vec3VArg closest, const FloatVArg tolerance, const FloatVArg contactDist, const bool doOverlapTest, Cm::RenderOutput* renderOutput, const FloatVArg toleranceScale) +{ + + PX_UNUSED(renderOutput); + Gu::PolygonalData polyData; + getPCMConvexData(convexHull,idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl<ConvexHullV>)]; + SupportLocal* map = (idtScale ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<const ConvexHullNoScaleV&>(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullV>)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if (generateFullContactManifold(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, normal, closest, tolerance, doOverlapTest, toleranceScale)) + { + + if (numContacts > 0) + { + manifold.addBatchManifoldContacts2(manifoldContacts, numContacts); + //transform normal into the world space + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf0, capsule.radius, contactDist); + } + else + { + if (!doOverlapTest) + { + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf0, capsule.radius, contactDist); + } + } + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + + } + return false; + +} + +bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>(); + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + + PersistentContactManifold& manifold = cache.getManifold(); + + Ps::prefetchLine(shapeConvex.hullData); + + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V zeroV = V3Zero(); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const FloatV contactDist = FLoad(params.mContactDistance); + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const ConvexHullData* hullData =shapeConvex.hullData; + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale); + const FloatV capsuleMinMargin = Gu::CalculateCapsuleMinMargin(capsuleRadius); + const FloatV minMargin = FMin(convexMargin, capsuleMinMargin); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(1.25f)); + const FloatV refreshDist = FAdd(contactDist, capsuleRadius); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDist); + + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + Vec3V closestA(zeroV), closestB(zeroV), normal(zeroV); // from a to b + const FloatV zero = FZero(); + FloatV penDep = zero; + + PX_UNUSED(bLostContacts); + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + const bool idtScale = shapeConvex.scale.isIdentity(); + + manifold.setRelativeTransform(curRTrans); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, idtScale); + convexHull.setMargin(zero); + + //transform capsule(a) into the local space of convexHull(b) + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + LocalConvex<CapsuleV> convexA(capsule); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(idtScale) + { + LocalConvex<ConvexHullNoScaleV> convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status = gjkPenetration<LocalConvex<CapsuleV>, LocalConvex<ConvexHullNoScaleV> >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, true); + } + else + { + LocalConvex<ConvexHullV> convexB(convexHull); + status = gjkPenetration<LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, true); + + } + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + bool doOverlapTest = false; + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_DEGENERATE) + { + return fullContactsGenerationCapsuleConvex(capsule, convexHull, aToB, transf0, transf1, manifoldContacts, contactBuffer, idtScale, manifold, normal, + closestB, convexHull.getMargin(), contactDist, true, renderOutput, FLoad(params.mToleranceLength)); + } + else + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + if(status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint2(localPointA, closestB, localNormalPen, replaceBreakingThreshold); + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + if(idtScale) + { + LocalConvex<ConvexHullNoScaleV> convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep, true); + } + else + { + LocalConvex<ConvexHullV> convexB(convexHull); + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep, true); + } + + + if(status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint2(localPointA, closestB, localNormalPen, replaceBreakingThreshold); + + + } + else + { + doOverlapTest = true; + } + } + + + if(initialContacts == 0 || bLostContacts || doOverlapTest) + { + return fullContactsGenerationCapsuleConvex(capsule, convexHull, aToB, transf0, transf1, manifoldContacts, contactBuffer, idtScale, manifold, normal, + closestB, convexHull.getMargin(), contactDist, doOverlapTest, renderOutput, FLoad(params.mToleranceLength)); + } + else + { + //This contact is either come from GJK or EPA + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf0, capsuleRadius, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + } + } + else if (manifold.getNumContacts() > 0) + { + normal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, transf0, capsuleRadius, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + return false; +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleHeightField.cpp new file mode 100644 index 00000000..8f187307 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleHeightField.cpp @@ -0,0 +1,158 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "PsVecMath.h" +#include "PsVecTransform.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSegment.h" +#include "GuInternal.h" +#include "GuPCMContactMeshCallback.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +namespace physx +{ + +struct PCMCapsuleVsHeightfieldContactGenerationCallback : PCMHeightfieldContactGenerationCallback<PCMCapsuleVsHeightfieldContactGenerationCallback> +{ + PCMCapsuleVsHeightfieldContactGenerationCallback& operator=(const PCMCapsuleVsHeightfieldContactGenerationCallback&); + +public: + PCMCapsuleVsMeshContactGeneration mGeneration; + + PCMCapsuleVsHeightfieldContactGenerationCallback( + const Gu::CapsuleV& capsule, + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + + const PsTransformV& capsuleTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Gu::HeightFieldUtil& hfUtil + + + ) : + PCMHeightfieldContactGenerationCallback<PCMCapsuleVsHeightfieldContactGenerationCallback>(hfUtil, heightfieldTransform1), + mGeneration(capsule, contactDistance, replaceBreakingThreshold, capsuleTransform, heightfieldTransform, multiManifold, contactBuffer) + { + } + + template<PxU32 CacheSize> + void processTriangleCache(Gu::TriangleCache<CacheSize>& cache) + { + mGeneration.processTriangleCache<CacheSize, PCMCapsuleVsMeshContactGeneration>(cache); + } + +}; + +bool Gu::pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get<const PxCapsuleGeometry>(); + const PxHeightFieldGeometryLL& shapeHeight = shape1.get<const PxHeightFieldGeometryLL>(); + + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV capsuleTransform = loadTransformA(transform0);//capsule transform + const PsTransformV heightfieldTransform = loadTransformA(transform1);//height feild + + const PsTransformV curTransform = heightfieldTransform.transformInv(capsuleTransform); + + const FloatV replaceBreakingThreshold = FMul(capsuleRadius, FLoad(0.001f)); + + if(multiManifold.invalidate(curTransform, capsuleRadius, FLoad(0.02f))) + { + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(shapeHeight.heightField); + + Gu::HeightFieldUtil hfUtil(shapeHeight, hf); + + const PxVec3 tmp = getCapsuleHalfHeightVector(transform0, shapeCapsule); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + const PxVec3 capsuleCenterInMesh = transform1.transformInv(transform0.p); + const PxVec3 capsuleDirInMesh = transform1.rotateInv(tmp); + const Gu::CapsuleV capsule(V3LoadU(capsuleCenterInMesh), V3LoadU(capsuleDirInMesh), capsuleRadius); + + + PCMCapsuleVsHeightfieldContactGenerationCallback callback( + capsule, + contactDist, + replaceBreakingThreshold, + capsuleTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + hfUtil + ); + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + callback.mGeneration.processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + // We must be in local space to use the cache + const FloatV projectBreakingThreshold = FMul(capsuleRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(capsuleRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + + } + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, capsuleTransform, heightfieldTransform, capsuleRadius); +} + + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleMesh.cpp new file mode 100644 index 00000000..d2a085fb --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactCapsuleMesh.cpp @@ -0,0 +1,182 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecTriangle.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" +#include "PsVecMath.h" +#include "PsVecTransform.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSegment.h" +#include "GuVecCapsule.h" +#include "GuInternal.h" +#include "GuPCMContactMeshCallback.h" +#include "GuConvexEdgeFlags.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMCapsuleVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback< PCMCapsuleVsMeshContactGenerationCallback > +{ + PCMCapsuleVsMeshContactGenerationCallback& operator=(const PCMCapsuleVsMeshContactGenerationCallback&); +public: + PCMCapsuleVsMeshContactGeneration mGeneration; + + PCMCapsuleVsMeshContactGenerationCallback( + const CapsuleV& capsule, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& sphereTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PxU8* extraTriData, + const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtMeshScale, + Cm::RenderOutput* renderOutput = NULL + ) : + PCMMeshContactGenerationCallback<PCMCapsuleVsMeshContactGenerationCallback>(meshScaling, extraTriData, idtMeshScale), + mGeneration(capsule, contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, renderOutput) + { + } + + PX_FORCE_INLINE bool doTest(const PxVec3&, const PxVec3&, const PxVec3&) { return true; } + + template<PxU32 CacheSize> + void processTriangleCache(TriangleCache<CacheSize>& cache) + { + mGeneration.processTriangleCache<CacheSize, PCMCapsuleVsMeshContactGeneration>(cache); + } + +}; + +bool Gu::pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + const PxCapsuleGeometry& shapeCapsule= shape0.get<const PxCapsuleGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + //gRenderOutPut = cache.mRenderOutput; + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV capsuleTransform = loadTransformA(transform0);//capsule transform + const PsTransformV meshTransform = loadTransformA(transform1);//triangleMesh + + const PsTransformV curTransform = meshTransform.transformInv(capsuleTransform); + + // We must be in local space to use the cache + if(multiManifold.invalidate(curTransform, capsuleRadius, FLoad(0.02f))) + { + const FloatV replaceBreakingThreshold = FMul(capsuleRadius, FLoad(0.001f)); + //const FloatV capsuleHalfHeight = FloatV_From_F32(shapeCapsule.halfHeight); + Cm::FastVertex2ShapeScaling meshScaling; + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + // Capsule data + const PxVec3 tmp = getCapsuleHalfHeightVector(transform0, shapeCapsule); + Segment worldCapsule; + worldCapsule.p0 = transform0.p + tmp; + worldCapsule.p1 = transform0.p - tmp; + + + const Segment meshCapsule( // Capsule in mesh space + transform1.transformInv(worldCapsule.p0), + transform1.transformInv(worldCapsule.p1)); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + const PxVec3 capsuleCenterInMesh = transform1.transformInv(transform0.p); + const PxVec3 capsuleDirInMesh = transform1.rotateInv(tmp); + const CapsuleV capsule(V3LoadU(capsuleCenterInMesh), V3LoadU(capsuleDirInMesh), capsuleRadius); + + // We must be in local space to use the cache + const Capsule queryCapsule(meshCapsule, inflatedRadius); + + const TriangleMesh* meshData = shapeMesh.meshData; + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + // mesh scale is not baked into cached verts + PCMCapsuleVsMeshContactGenerationCallback callback( + capsule, + contactDist, + replaceBreakingThreshold, + capsuleTransform, + meshTransform, + multiManifold, + contactBuffer, + extraData, + meshScaling, + idtMeshScale, + renderOutput); + + //bound the capsule in shape space by an OBB: + Box queryBox; + queryBox.create(queryCapsule); + + //apply the skew transform to the box: + if(!idtMeshScale) + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + + callback.flushCache(); + + callback.mGeneration.processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(capsuleRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(capsuleRadius, contactDist); + //multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + } + + //multiManifold.drawManifold(*gRenderOutPut, capsuleTransform, meshTransform); + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, capsuleTransform, meshTransform, capsuleRadius); +} + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexCommon.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexCommon.cpp new file mode 100644 index 00000000..1934caa5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexCommon.cpp @@ -0,0 +1,1266 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecTriangle.h" +#include "GuPCMContactConvexCommon.h" +#include "GuConvexEdgeFlags.h" +#include "GuBarycentricCoordinates.h" + +namespace physx +{ + +namespace Gu +{ +/* + This function adds the newly created manifold contacts to a new patch or existing patches +*/ +void PCMConvexVsMeshContactGeneration::addContactsToPatch(const Ps::aos::Vec3VArg patchNormal, const PxU32 previousNumContacts) +{ + using namespace Ps::aos; + + const Vec3V patchNormalInTriangle = mMeshToConvex.rotateInv(patchNormal); + + + const PxU32 newContacts = mNumContacts - previousNumContacts; + + if(newContacts > GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE) + { + //if the current created manifold contacts are more than GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points, we will reduce the total numContacts + //to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE. However, after we add these points into a patch, the patch contacts will be variable. Then we will + //do contact reduction for that patch in the processContacts. After the contact reduction, there will be no more than GU_SINGLE_MANIFOLD_CACHE_SIZE(6) + //contacts inside a signlePersistentContactManifold + Gu::SinglePersistentContactManifold::reduceContacts(&mManifoldContacts[previousNumContacts], newContacts); + mNumContacts = previousNumContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + + //get rid of duplicate manifold contacts for the newly created contacts + for(PxU32 i = previousNumContacts; i<mNumContacts; ++i) + { + for(PxU32 j=i+1; j<mNumContacts; ++j) + { + Vec3V dif = V3Sub(mManifoldContacts[j].mLocalPointB, mManifoldContacts[i].mLocalPointB); + FloatV d = V3Dot(dif, dif); + if(FAllGrtr(mSqReplaceBreakingThreshold, d)) + { + mManifoldContacts[j] = mManifoldContacts[mNumContacts-1]; + mNumContacts--; + j--; + } + } + } + + //calculate the maxPen and transform the patch normal and localPointB into mesh's local space + FloatV maxPen = FMax(); + for(PxU32 i = previousNumContacts; i<mNumContacts; ++i) + { + const FloatV pen = V4GetW(mManifoldContacts[i].mLocalNormalPen); + mManifoldContacts[i].mLocalNormalPen = V4SetW(patchNormalInTriangle, pen); + mManifoldContacts[i].mLocalPointB = mMeshToConvex.transformInv(mManifoldContacts[i].mLocalPointB); + maxPen = FMin(maxPen, pen); + } + + //Based on the patch normal and add the newly avaiable manifold points to the corresponding patch + addManifoldPointToPatch(patchNormalInTriangle, maxPen, previousNumContacts); + + PX_ASSERT(mNumContactPatch <PCM_MAX_CONTACTPATCH_SIZE); + if(mNumContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + PX_ASSERT(mNumContacts <= ContactBuffer::MAX_CONTACTS); + processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE); + } +} + + +void PCMConvexVsMeshContactGeneration::generateLastContacts() +{ + using namespace Ps::aos; + // Process delayed contacts + PxU32 nbEntries = mDeferredContacts.size(); + + if(nbEntries) + { + nbEntries /= sizeof(PCMDeferredPolyData)/sizeof(PxU32); + + const PCMDeferredPolyData* PX_RESTRICT cd = reinterpret_cast<const PCMDeferredPolyData*>(mDeferredContacts.begin()); + for(PxU32 i=0;i<nbEntries;i++) + { + const PCMDeferredPolyData& currentContact = cd[i]; + + const PxU32 ref0 = currentContact.mInds[0]; + const PxU32 ref1 = currentContact.mInds[1]; + const PxU32 ref2 = currentContact.mInds[2]; + + PxU8 triFlags = currentContact.triFlags; + + + bool needsProcessing = (((triFlags & ETD_CONVEX_EDGE_01) != 0 || mEdgeCache.get(CachedEdge(ref0, ref1)) == NULL)) && + (((triFlags & ETD_CONVEX_EDGE_12) != 0 || mEdgeCache.get(CachedEdge(ref1, ref2)) == NULL)) && + (((triFlags & ETD_CONVEX_EDGE_20) != 0 || mEdgeCache.get(CachedEdge(ref2, ref0)) == NULL)); + + + if(needsProcessing) + { + + Gu::TriangleV localTriangle(currentContact.mVerts); + Vec3V patchNormal; + const PxU32 previousNumContacts = mNumContacts; + //the localTriangle is in the convex space + //Generate contacts - we didn't generate contacts with any neighbours + generatePolyDataContactManifold(localTriangle, currentContact.mFeatureIndex, currentContact.mTriangleIndex, triFlags, mManifoldContacts, mNumContacts, mContactDist, patchNormal); + + FloatV v, w; + const FloatV upperBound = FLoad(0.97f); + const FloatV lowerBound = FSub(FOne(), upperBound); + PxU32 currentContacts = mNumContacts; + for(PxU32 j=currentContacts; j>previousNumContacts; --j) + { + PxU32 ind = j-1; + //calculate the barycentric coordinate of the contacts in localTriangle, p = a + v(b-a) + w(c-a)., p=ua+vb+wc + barycentricCoordinates(mManifoldContacts[ind].mLocalPointB, localTriangle.verts[0], localTriangle.verts[1], localTriangle.verts[2], v, w); + //const FloatV u = FSub(one, FAdd(v, w)); + + bool keepContact = true; + if(FAllGrtr(v, upperBound))//v > upperBound + { + //vertex1 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref1)); + } + else if(FAllGrtr(w, upperBound))// w > upperBound + { + //vertex2 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref2)); + } + else if(FAllGrtrOrEq(lowerBound, FAdd(v, w))) // u(1-(v+w)) > upperBound + { + //vertex0 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref0)); + } + + if(!keepContact) + { + //ML: if feature code is any of the vertex in this triangle and we have generated contacts with any other triangles which contains this vertex, we should drop it + currentContacts--; + + for(PxU32 k = ind; k < currentContacts; ++k) + { + mManifoldContacts[k] = mManifoldContacts[k+1]; + } + } + } + + mNumContacts = currentContacts; + + if(currentContacts > previousNumContacts) + { + addContactsToPatch(patchNormal, previousNumContacts); + } + + } + } + } + +} + +bool PCMConvexVsMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + using namespace Ps::aos; + + + const Mat33V identity = M33Identity(); + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + + + const Vec3V v10 = V3Sub(v1, v0); + const Vec3V v20 = V3Sub(v2, v0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(v0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(mHullCenterMesh, n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist)) + return false; + + + //tranform verts into the box local space + const Vec3V locV0 = mMeshToConvex.transform(v0); + const Vec3V locV1 = mMeshToConvex.transform(v1); + const Vec3V locV2 = mMeshToConvex.transform(v2); + + Gu::TriangleV localTriangle(locV0, locV1, locV2); + + { + + SupportLocalImpl<Gu::TriangleV> localTriMap(localTriangle, mConvexTransform, identity, identity, true); + + const PxU32 previousNumContacts = mNumContacts; + Vec3V patchNormal; + + generateTriangleFullContactManifold(localTriangle, triangleIndex, vertInds, triFlags, mPolyData, &localTriMap, mPolyMap, mManifoldContacts, mNumContacts, mContactDist, patchNormal); + + if(mNumContacts > previousNumContacts) + { +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawTriangle(*mRenderOutput, mMeshTransform.transform(v0), mMeshTransform.transform(v1), mMeshTransform.transform(v2), 0x00ff00); +#endif + + bool inActiveEdge0 = (triFlags & ETD_CONVEX_EDGE_01) == 0; + bool inActiveEdge1 = (triFlags & ETD_CONVEX_EDGE_12) == 0; + bool inActiveEdge2 = (triFlags & ETD_CONVEX_EDGE_20) == 0; + + if(inActiveEdge0) + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1])); + if(inActiveEdge1) + mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2])); + if(inActiveEdge2) + mEdgeCache.addData(CachedEdge(vertInds[2], vertInds[0])); + + mVertexCache.addData(CachedVertex(vertInds[0])); + mVertexCache.addData(CachedVertex(vertInds[1])); + mVertexCache.addData(CachedVertex(vertInds[2])); + + addContactsToPatch(patchNormal, previousNumContacts); + } + } + + + + return true; +} + + +bool PCMConvexVsMeshContactGeneration::processTriangle(const Gu::PolygonalData& polyData, SupportLocal* polyMap, const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags,const Ps::aos::FloatVArg inflation, const bool isDoubleSided, + const Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + + const Mat33V identity = M33Identity(); + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + //tranform verts into the box local space + const Vec3V locV0 = meshToConvex.transform(v0); + const Vec3V locV1 = meshToConvex.transform(v1); + const Vec3V locV2 = meshToConvex.transform(v2); + + const Vec3V v10 = V3Sub(locV1, locV0); + const Vec3V v20 = V3Sub(locV2, locV0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(locV0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(polyMap->shapeSpaceCenterOfMass, n), d);//p.dot(n) + d; + + // Backface culling + const bool culled = !isDoubleSided && (FAllGrtr(zero, dist)); + if(culled) + return false; + + + Gu::TriangleV localTriangle(locV0, locV1, locV2); + + SupportLocalImpl<Gu::TriangleV> localTriMap(localTriangle, convexTransform, identity, identity, true); + + Vec3V patchNormal; + + generateTriangleFullContactManifold(localTriangle, triangleIndex, triFlags, polyData, &localTriMap, polyMap, manifoldContacts, numContacts, inflation, patchNormal); + + return true; +} + +PX_FORCE_INLINE Ps::aos::Vec4V pcmDistanceSegmentSegmentSquared4( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg d0, + const Ps::aos::Vec3VArg p02, const Ps::aos::Vec3VArg d02, + const Ps::aos::Vec3VArg p12, const Ps::aos::Vec3VArg d12, + const Ps::aos::Vec3VArg p22, const Ps::aos::Vec3VArg d22, + const Ps::aos::Vec3VArg p32, const Ps::aos::Vec3VArg d32, + Ps::aos::Vec4V& s, Ps::aos::Vec4V& t) +{ + using namespace Ps::aos; + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + const Vec4V eps = V4Eps(); + const Vec4V half = V4Splat(FHalf()); + + const Vec4V d0X = V4Splat(V3GetX(d0)); + const Vec4V d0Y = V4Splat(V3GetY(d0)); + const Vec4V d0Z = V4Splat(V3GetZ(d0)); + const Vec4V pX = V4Splat(V3GetX(p)); + const Vec4V pY = V4Splat(V3GetY(p)); + const Vec4V pZ = V4Splat(V3GetZ(p)); + + Vec4V d024 = Vec4V_From_Vec3V(d02); + Vec4V d124 = Vec4V_From_Vec3V(d12); + Vec4V d224 = Vec4V_From_Vec3V(d22); + Vec4V d324 = Vec4V_From_Vec3V(d32); + + Vec4V p024 = Vec4V_From_Vec3V(p02); + Vec4V p124 = Vec4V_From_Vec3V(p12); + Vec4V p224 = Vec4V_From_Vec3V(p22); + Vec4V p324 = Vec4V_From_Vec3V(p32); + + Vec4V d0123X, d0123Y, d0123Z; + Vec4V p0123X, p0123Y, p0123Z; + + PX_TRANSPOSE_44_34(d024, d124, d224, d324, d0123X, d0123Y, d0123Z); + PX_TRANSPOSE_44_34(p024, p124, p224, p324, p0123X, p0123Y, p0123Z); + + const Vec4V rX = V4Sub(pX, p0123X); + const Vec4V rY = V4Sub(pY, p0123Y); + const Vec4V rZ = V4Sub(pZ, p0123Z); + + //TODO - store this in a transposed state and avoid so many dot products? + + const FloatV dd = V3Dot(d0, d0); + + const Vec4V e = V4MulAdd(d0123Z, d0123Z, V4MulAdd(d0123X, d0123X, V4Mul(d0123Y, d0123Y))); + const Vec4V b = V4MulAdd(d0Z, d0123Z, V4MulAdd(d0X, d0123X, V4Mul(d0Y, d0123Y))); + const Vec4V c = V4MulAdd(d0Z, rZ, V4MulAdd(d0X, rX, V4Mul(d0Y, rY))); + const Vec4V f = V4MulAdd(d0123Z, rZ, V4MulAdd(d0123X, rX, V4Mul(d0123Y, rY))); + + const Vec4V a(V4Splat(dd)); + + const Vec4V aRecip(V4Recip(a)); + const Vec4V eRecip(V4Recip(e)); + + //if segments not parallell, compute closest point on two segments and clamp to segment1 + const Vec4V denom = V4Sub(V4Mul(a, e), V4Mul(b, b)); + const Vec4V temp = V4Sub(V4Mul(b, f), V4Mul(c, e)); + //const Vec4V s0 = V4Clamp(V4Div(temp, denom), zero, one); + //In PS3, 0(temp)/0(denom) will produce QNaN and V4Clamp can't clamp the value to zero and one. In PC, 0/0 will produce inf and V4Clamp clamp the value to be one. + //Therefore, we need to add the select code to protect against this case + const Vec4V value = V4Sel(V4IsEq(denom, zero), one, V4Div(temp, denom)); + const Vec4V s0 = V4Clamp(value, zero, one); + + //test whether segments are parallel + const BoolV con2 = V4IsGrtrOrEq(eps, denom); + const Vec4V sTmp = V4Sel(con2, half, s0); + + //compute point on segment2 closest to segment1 + //const Vec4V tTmp = V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip); + const Vec4V tTmp = V4Sel(V4IsEq(e, zero), one, V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip)); + + //if t is in [zero, one], done. otherwise clamp t + const Vec4V t2 = V4Clamp(tTmp, zero, one); + + //recompute s for the new value + //const Vec4V comp = V4Mul(V4Sub(V4Mul(b,t2), c), aRecip); + const Vec4V comp = V4Sel(V4IsEq(a, zero), one, V4Mul(V4Sub(V4Mul(b,t2), c), aRecip)); + const Vec4V s2 = V4Clamp(comp, zero, one); + + s = s2; + t = t2; + + const Vec4V closest1X = V4MulAdd(d0X, s2, pX); + const Vec4V closest1Y = V4MulAdd(d0Y, s2, pY); + const Vec4V closest1Z = V4MulAdd(d0Z, s2, pZ); + + const Vec4V closest2X = V4MulAdd(d0123X, t2, p0123X); + const Vec4V closest2Y = V4MulAdd(d0123Y, t2, p0123Y); + const Vec4V closest2Z = V4MulAdd(d0123Z, t2, p0123Z); + + const Vec4V vvX = V4Sub(closest1X, closest2X); + const Vec4V vvY = V4Sub(closest1Y, closest2Y); + const Vec4V vvZ = V4Sub(closest1Z, closest2Z); + + const Vec4V vd = V4MulAdd(vvX, vvX, V4MulAdd(vvY, vvY, V4Mul(vvZ, vvZ))); + + return vd; +} + +static Ps::aos::FloatV pcmDistancePointTriangleSquared( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + const PxU8 triFlags, + Ps::aos::Vec3V& closestP, + bool& generateContact) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + const Vec3V n = V3Cross(ab, ac); + const VecCrossV crossA = V3PrepareCross(ap); + const VecCrossV crossB = V3PrepareCross(bp); + const VecCrossV crossC = V3PrepareCross(cp); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + //const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + //const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + //const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + //Vertex 0 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) || (triFlags & Gu::ETD_CONVEX_EDGE_20); + closestP = a; + return V3Dot(ap, ap); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + //Vertex 1 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) || (triFlags & Gu::ETD_CONVEX_EDGE_12); + closestP = b; + return V3Dot(bp, bp); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + //Vertex 2 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_12) || (triFlags & Gu::ETD_CONVEX_EDGE_20); + closestP = c; + return V3Dot(cp, cp); + } + + //check if p in edge region of AB + //const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + // Edge 01 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) != 0; + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3ScaleAdd(ab, sScale, a);//V3Add(a, V3Scale(ab, sScale)); + const Vec3V vv = V3Sub(p, closest3); + closestP = closest3; + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + //const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + // Edge 12 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_12) != 0; + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3ScaleAdd(bc, uScale, b);//V3Add(b, V3Scale(bc, uScale)); + const Vec3V vv = V3Sub(p, closest4); + closestP = closest4; + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + //const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + //Edge 20 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_20) != 0; + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3ScaleAdd(ac, tScale, a);//V3Add(a, V3Scale(ac, tScale)); + const Vec3V vv = V3Sub(p, closest5); + closestP = closest5; + return V3Dot(vv, vv); + } + + generateContact = true; + + //P must project inside face region. Compute Q using Barycentric coordinates + const FloatV nn = V3Dot(n, n); + const FloatV t = FDiv(V3Dot(n, V3Sub(a, p)), nn); + const Vec3V vv = V3Scale(n, t); + closestP = V3Add(p, vv); + return V3Dot(vv, vv); +} + +bool Gu::PCMSphereVsMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + PX_UNUSED(triangleIndex); + PX_UNUSED(vertInds); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + const Vec3V v10 = V3Sub(v1, v0); + const Vec3V v20 = V3Sub(v2, v0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(v0, n);//d = -p0.dot(n); + + const FloatV dist0 = FSub(V3Dot(mSphereCenter, n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist0)) + return false; + + + const FloatV tolerance = FLoad(0.996);//around 5 degree + //FloatV u, v; + Vec3V closestP; + //mShereCenter will be in the local space of the triangle mesh + //const FloatV sqDist = Gu::distancePointTriangleSquared(mSphereCenter, v0, v1, v2, u, v, closestP); + bool generateContact = false; + const FloatV sqDist = pcmDistancePointTriangleSquared(mSphereCenter, v0, v1, v2, triFlags, closestP, generateContact); + + //sphere center is on the triangle surface, we take triangle normal as the patchNormal. Otherwise, we need to calculate the patchNormal + Vec3V patchNormal = n; + if(FAllGrtr(sqDist, FEps() )) + patchNormal = V3Normalize(V3Sub(mSphereCenter, closestP)); + + const FloatV cosTheta = V3Dot(patchNormal, n); + + //sphere overlap with triangles + if(FAllGrtr(mSqInflatedSphereRadius, sqDist) && (generateContact || FAllGrtr(cosTheta, tolerance))) + { + const FloatV dist = FSqrt(sqDist); + + PX_ASSERT(mNumContactPatch <PCM_MAX_CONTACTPATCH_SIZE); + + bool foundPatch = false; + if(mNumContactPatch > 0) + { + if(FAllGrtr(V3Dot(mContactPatch[mNumContactPatch-1].mPatchNormal, patchNormal), mAcceptanceEpsilon)) + { + PCMContactPatch& patch = mContactPatch[mNumContactPatch-1]; + + PX_ASSERT((patch.mEndIndex - patch.mStartIndex) == 1); + + if(FAllGrtr(patch.mPatchMaxPen, dist)) + { + //overwrite the old contact + mManifoldContacts[patch.mStartIndex].mLocalPointA = zeroV;//in sphere's space + mManifoldContacts[patch.mStartIndex].mLocalPointB = closestP; + mManifoldContacts[patch.mStartIndex].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(patchNormal), dist); + mManifoldContacts[patch.mStartIndex].mFaceIndex = triangleIndex; + patch.mPatchMaxPen = dist; + } + + foundPatch = true; + } + } + if(!foundPatch) + { + mManifoldContacts[mNumContacts].mLocalPointA = zeroV;//in sphere's space + mManifoldContacts[mNumContacts].mLocalPointB = closestP; + mManifoldContacts[mNumContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(patchNormal), dist); + mManifoldContacts[mNumContacts++].mFaceIndex = triangleIndex; + + mContactPatch[mNumContactPatch].mStartIndex = mNumContacts - 1; + mContactPatch[mNumContactPatch].mEndIndex = mNumContacts; + mContactPatch[mNumContactPatch].mPatchMaxPen = dist; + mContactPatch[mNumContactPatch++].mPatchNormal = patchNormal; + } + + PX_ASSERT(mNumContactPatch <PCM_MAX_CONTACTPATCH_SIZE); + + if(mNumContacts >= 16) + { + PX_ASSERT(mNumContacts <= 64); + processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE); + } + + } + return true; +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEE(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + const FloatV one = FOne(); + //calculate the intersect point in the segment pq + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //calculate a normal perpendicular to ray localPointA + normal, 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + //const FloatV tValue = FClamp(FDiv(nom, denom), zero, FOne()); + const FloatV tValue = FDiv(nom, denom); + const BoolV con = BAnd(FIsGrtrOrEq(one, tValue), FIsGrtrOrEq(tValue, zero)); + if(BAllEqFFFF(con)) + return; + + //const Vec3V localPointB = V3ScaleAdd(ab, tValue, a); v = V3Sub(localPointA, localPointB); v = V3NegScaleSub(ab, tValue, tap) + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV sqDist = V3Dot(v, v); + + if(FAllGrtr(sqInflatedRadius, sqDist)) + { + + const Vec3V localPointB = V3Sub(localPointA, v); + const FloatV signedDist = V3Dot(v, normal); + + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } +} + + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, a, b, manifoldContacts, numContacts); + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, b, c, manifoldContacts, numContacts); + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, a, c, manifoldContacts, numContacts); + +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEMTD(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + //calculate the intersect point in the segment pq + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //calculate a normal perpendicular to ray localPointA + normal, 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + const FloatV tValue = FClamp(FDiv(nom, denom), zero, FOne()); + + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV signedDist = V3Dot(v, normal); + + if(FAllGrtr(inflatedRadius, signedDist)) + { + + const Vec3V localPointB = V3Sub(localPointA, v); + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEContactsMTD(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, a, b, manifoldContacts, numContacts); + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, b, c, manifoldContacts, numContacts); + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, a, c, manifoldContacts, numContacts); + +} + +bool Gu::PCMCapsuleVsMeshContactGeneration::generateContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg planeNormal, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::FloatVArg inflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + //This is used to calculate the barycentric coordinate + const FloatV d00 = V3Dot(ab, ab); + const FloatV d01 = V3Dot(ab, ac); + const FloatV d11 = V3Dot(ac, ac); + const FloatV bdenom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + + //compute the intersect point of p and triangle plane abc + const FloatV inomp = V3Dot(planeNormal, V3Neg(ap)); + const FloatV ideom = V3Dot(planeNormal, normal); + + const FloatV ipt = FSel(FIsGrtr(ideom, FZero()), FDiv(inomp, ideom), FZero()); + //compute the distance from triangle plane abc + const FloatV dist3 = V3Dot(ap, planeNormal); + + const Vec3V closestP31 = V3ScaleAdd(normal, ipt, p); + const Vec3V closestP30 = p; + + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const FloatV pD20 = V3Dot(pV20, ab); + const FloatV pD21 = V3Dot(pV20, ac); + const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); + + //check closestP3 is inside the triangle + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + + + const BoolV tempCon0 = BAnd(con0, FIsGrtr(inflatedRadius, dist3)); + if(BAllEqTTTT(tempCon0)) + { + manifoldContacts[numContacts].mLocalPointA = closestP30;//transform to B space, it will get convert to A space later + manifoldContacts[numContacts].mLocalPointB = closestP31; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), FNeg(ipt)); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } + + const FloatV inomq = V3Dot(planeNormal, V3Neg(aq)); + + //compute the distance of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, planeNormal); + + const FloatV iqt = FSel(FIsGrtr(ideom, FZero()), FDiv(inomq, ideom), FZero()); + + const Vec3V closestP41 = V3ScaleAdd(normal, iqt, q); + const Vec3V closestP40 = q; + + //Compute the barycentric coordinate for project point of q + const Vec3V qV20 = V3Sub(closestP41, a); + const FloatV qD20 = V3Dot(qV20, ab); + const FloatV qD21 = V3Dot(qV20, ac); + const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); + const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); + + + const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); + + const BoolV tempCon1 = BAnd(con1, FIsGrtr(inflatedRadius, dist4)); + if(BAllEqTTTT(tempCon1)) + { + manifoldContacts[numContacts].mLocalPointA = closestP40; + manifoldContacts[numContacts].mLocalPointB = closestP41; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal),FNeg(iqt)); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + + } + + return false; +} + + +/* + t is the barycenteric coordinate of a segment + u is the barycenteric coordinate of a triangle + v is the barycenteric coordinate of a triangle +*/ +Ps::aos::FloatV pcmDistanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& t, Ps::aos::FloatV& u, Ps::aos::FloatV& v) +{ + using namespace Ps::aos; + + const FloatV one = FOne(); + const FloatV zero = FZero(); + + const Vec3V pq = V3Sub(q, p); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector + + const Vec4V combinedDot = V3Dot4(ab, ab, ab, ac, ac, ac, ap, n); + const FloatV d00 = V4GetX(combinedDot); + const FloatV d01 = V4GetY(combinedDot); + const FloatV d11 = V4GetZ(combinedDot); + const FloatV dist3 = V4GetW(combinedDot); + + const FloatV bdenom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + + + const FloatV sqDist3 = FMul(dist3, dist3); + + + //compute the closest point of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, n); + const FloatV sqDist4 = FMul(dist4, dist4); + const FloatV dMul = FMul(dist3, dist4); + const BoolV con = FIsGrtr(zero, dMul); + + if(BAllEqTTTT(con)) + { + //compute the intersect point + const FloatV nom = FNeg(V3Dot(n, ap)); + const FloatV denom = FRecip(V3Dot(n, pq)); + const FloatV t0 = FMul(nom, denom); + const Vec3V ip = V3ScaleAdd(pq, t0, p);//V3Add(p, V3Scale(pq, t)); + const Vec3V v2 = V3Sub(ip, a); + const FloatV d20 = V3Dot(v2, ab); + const FloatV d21 = V3Dot(v2, ac); + + const FloatV v0 = FMul(FNegScaleSub(d01, d21, FMul(d11, d20)), bdenom); + const FloatV w0 = FMul(FNegScaleSub(d01, d20, FMul(d00, d21)), bdenom); + + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + if(BAllEqTTTT(con0)) + { + t = t0; + u = v0; + v = w0; + return zero; + } + } + + const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); + const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const Vec3V qV20 = V3Sub(closestP41, a); + + const Vec4V pD2 = V3Dot4(pV20, ab, pV20, ac, qV20, ab, qV20, ac); + + const Vec4V pD2Swizzle = V4PermYXWZ(pD2); + + const Vec4V d11d00 = V4UnpackXY(V4Splat(d11), V4Splat(d00)); + + const Vec4V v0w0v1w1 = V4Scale(V4NegMulSub(V4Splat(d01), pD2Swizzle, V4Mul(d11d00, pD2)), bdenom); + + const FloatV v0 = V4GetX(v0w0v1w1); + const FloatV w0 = V4GetY(v0w0v1w1); + const FloatV v1 = V4GetZ(v0w0v1w1); + const FloatV w1 = V4GetW(v0w0v1w1); + + const BoolV _con = isValidTriangleBarycentricCoord2(v0w0v1w1); + + const BoolV con0 = BGetX(_con); + const BoolV con1 = BGetY(_con); + + + const BoolV cond2 = BAnd(con0, con1); + + if(BAllEqTTTT(cond2)) + { + /* + both p and q project points are interior point + */ + const BoolV d2 = FIsGrtr(sqDist4, sqDist3); + t = FSel(d2, zero, one); + u = FSel(d2, v0, v1); + v = FSel(d2, w0, w1); + return FSel(d2, sqDist3, sqDist4); + } + else + { + Vec4V t40, t41; + const Vec4V sqDist44 = pcmDistanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); + + const FloatV t00 = V4GetX(t40); + const FloatV t10 = V4GetY(t40); + const FloatV t20 = V4GetZ(t40); + + const FloatV t01 = V4GetX(t41); + const FloatV t11 = V4GetY(t41); + const FloatV t21 = V4GetZ(t41); + + //edge ab + const FloatV u01 = t01; + const FloatV v01 = zero; + + //edge bc + const FloatV u11 = FSub(one, t11); + const FloatV v11 = t11; + + //edge ac + const FloatV u21 = zero; + const FloatV v21 = t21; + + const FloatV sqDist0(V4GetX(sqDist44)); + const FloatV sqDist1(V4GetY(sqDist44)); + const FloatV sqDist2(V4GetZ(sqDist44)); + + const BoolV con2 = BAnd(FIsGrtr(sqDist1, sqDist0), FIsGrtr(sqDist2, sqDist0)); + const BoolV con3 = FIsGrtr(sqDist2, sqDist1); + const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); + const FloatV uEdge = FSel(con2, u01, FSel(con3, u11, u21)); + const FloatV vEdge = FSel(con2, v01, FSel(con3, v11, v21)); + const FloatV tSeg = FSel(con2, t00, FSel(con3, t10, t20)); + + + if(BAllEqTTTT(con0)) + { + //p's project point is an interior point + const BoolV d2 = FIsGrtr(sqDistPE, sqDist3); + t = FSel(d2, zero, tSeg); + u = FSel(d2, v0, uEdge); + v = FSel(d2, w0, vEdge); + return FSel(d2, sqDist3, sqDistPE); + } + else if(BAllEqTTTT(con1)) + { + //q's project point is an interior point + const BoolV d2 = FIsGrtr(sqDistPE, sqDist4); + t = FSel(d2, one, tSeg); + u = FSel(d2, v1, uEdge); + v = FSel(d2, w1, vEdge); + return FSel(d2, sqDist4, sqDistPE); + } + else + { + t = tSeg; + u = uEdge; + v = vEdge; + return sqDistPE; + } + + } + +} + + +static bool selectNormal(const Ps::aos::FloatVArg u, Ps::aos::FloatVArg v, PxU8 data) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + // Analysis + if(FAllEq(u, zero)) + { + if(FAllEq(v,zero)) + { + // Vertex 0 + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20))) + return true; + } + else if(FAllEq(v,one)) + { + // Vertex 2 + if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20))) + return true; + } + else + { + // Edge 0-2 + if(!(data & Gu::ETD_CONVEX_EDGE_20)) + return true; + } + } + else if(FAllEq(u,one)) + { + if(FAllEq(v,zero)) + { + // Vertex 1 + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12))) + return true; + + } + } + else + { + if(FAllEq(v,zero)) + { + // Edge 0-1 + if(!(data & Gu::ETD_CONVEX_EDGE_01)) + return true; + } + else + { + const FloatV threshold = FLoad(0.9999f); + const FloatV temp = FAdd(u, v); + if(FAllGrtrOrEq(temp, threshold)) + { + // Edge 1-2 + if(!(data & Gu::ETD_CONVEX_EDGE_12)) + return true; + } + else + { + // Face + return true; + } + } + } + return false; +} + + +bool Gu::PCMCapsuleVsMeshContactGeneration::processTriangle(const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + PX_UNUSED(triangleIndex); + PX_UNUSED(vertInds); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V p0 = V3LoadU(verts[0]); + const Vec3V p1 = V3LoadU(verts[1]); + const Vec3V p2 = V3LoadU(verts[2]); + + const Vec3V p10 = V3Sub(p1, p0); + const Vec3V p20 = V3Sub(p2, p0); + + const Vec3V n = V3Normalize(V3Cross(p10, p20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(p0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(mCapsule.getCenter(), n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist)) + return false; + + + FloatV t, u, v; + const FloatV sqDist = pcmDistanceSegmentTriangleSquared(mCapsule.p0, mCapsule.p1, p0, p1, p2, t, u, v); + + if(FAllGrtr(mSqInflatedRadius, sqDist)) + { + + Vec3V patchNormalInTriangle; + if(selectNormal(u, v, triFlags)) + { + patchNormalInTriangle = n; + } + else + { + if(FAllEq(sqDist, zero)) + { + //segment intersect with the triangle + patchNormalInTriangle = n; + } + else + { + const Vec3V pq = V3Sub(mCapsule.p1, mCapsule.p0); + const Vec3V pointOnSegment = V3ScaleAdd(pq, t, mCapsule.p0); + const FloatV w = FSub(FOne(), FAdd(u, v)); + const Vec3V pointOnTriangle = V3ScaleAdd(p0, w, V3ScaleAdd(p1, u, V3Scale(p2, v))); + patchNormalInTriangle = V3Normalize(V3Sub(pointOnSegment, pointOnTriangle)); + + } + } + + const PxU32 previousNumContacts = mNumContacts; + + generateContacts(p0, p1, p2, n, patchNormalInTriangle, triangleIndex, mCapsule.p0, mCapsule.p1, mInflatedRadius, mManifoldContacts, mNumContacts); + //ML: this need to use the sqInflatedRadius to avoid some bad contacts + generateEEContacts(p0, p1, p2,patchNormalInTriangle, triangleIndex, mCapsule.p0, mCapsule.p1, mSqInflatedRadius, mManifoldContacts, mNumContacts); + + + PxU32 numContacts = mNumContacts - previousNumContacts; + + if(numContacts > 0) + { + FloatV maxPen = FMax(); + for(PxU32 i = previousNumContacts; i<mNumContacts; ++i) + { + const FloatV pen = V4GetW(mManifoldContacts[i].mLocalNormalPen); + mManifoldContacts[i].mLocalPointA = mMeshToConvex.transform(mManifoldContacts[i].mLocalPointA); + maxPen = FMin(maxPen, pen); + } + + for(PxU32 i = previousNumContacts; i<mNumContacts; ++i) + { + Vec3V contact0 = mManifoldContacts[i].mLocalPointB; + for(PxU32 j=i+1; j<mNumContacts; ++j) + { + Vec3V contact1 = mManifoldContacts[j].mLocalPointB; + Vec3V dif = V3Sub(contact1, contact0); + FloatV d1 = V3Dot(dif, dif); + if(FAllGrtr(mSqReplaceBreakingThreshold, d1)) + { + mManifoldContacts[j] = mManifoldContacts[mNumContacts-1]; + mNumContacts--; + j--; + } + } + } + + PX_ASSERT(mNumContactPatch <PCM_MAX_CONTACTPATCH_SIZE); + + addManifoldPointToPatch(patchNormalInTriangle, maxPen, previousNumContacts); + + PX_ASSERT(mNumContactPatch <PCM_MAX_CONTACTPATCH_SIZE); + if(mNumContacts >= 16) + { + PX_ASSERT(mNumContacts <= 64); + processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE); + } + } + } + + return true; +} + +bool Gu::PCMCapsuleVsMeshContactGeneration::processTriangle(const TriangleV& triangleV, const PxU32 triangleIndex, const CapsuleV& capsule, const Ps::aos::FloatVArg inflatedRadius, const PxU8 trigFlag, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V p0 = triangleV.verts[0]; + const Vec3V p1 = triangleV.verts[1]; + const Vec3V p2 = triangleV.verts[2]; + + const Vec3V n = triangleV.normal(); + + const FloatV sqInflatedRadius = FMul(inflatedRadius, inflatedRadius); + + + FloatV t, u, v; + const FloatV sqDist = pcmDistanceSegmentTriangleSquared(capsule.p0, capsule.p1, p0, p1, p2, t, u, v); + + if(FAllGrtr(sqInflatedRadius, sqDist)) + { + + Vec3V patchNormalInTriangle; + if(selectNormal(u, v, trigFlag)) + { + patchNormalInTriangle = n; + } + else + { + if(FAllEq(sqDist, zero)) + { + //segment intersect with the triangle + patchNormalInTriangle = n; + } + else + { + const Vec3V pq = V3Sub(capsule.p1, capsule.p0); + const Vec3V pointOnSegment = V3ScaleAdd(pq, t, capsule.p0); + const FloatV w = FSub(FOne(), FAdd(u, v)); + const Vec3V pointOnTriangle = V3ScaleAdd(p0, w, V3ScaleAdd(p1, u, V3Scale(p2, v))); + patchNormalInTriangle = V3Normalize(V3Sub(pointOnSegment, pointOnTriangle)); + } + } + + generateContacts(p0, p1, p2, n, patchNormalInTriangle, triangleIndex, capsule.p0, capsule.p1, inflatedRadius, manifoldContacts, numContacts); + + generateEEContactsMTD(p0, p1, p2, patchNormalInTriangle, triangleIndex, capsule.p0, capsule.p1, inflatedRadius, manifoldContacts, numContacts); + + + } + + return true; + +} + +} +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexCommon.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexCommon.h new file mode 100644 index 00000000..c2af8d6f --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexCommon.h @@ -0,0 +1,419 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_CONVEX_COMMON_H +#define GU_PCM_CONTACT_CONVEX_COMMON_H + +#define PCM_MAX_CONTACTPATCH_SIZE 32 + +#include "GuContactBuffer.h" +#include "GuVecCapsule.h" +#include "GuPCMTriangleContactGen.h" +#include "PsInlineArray.h" +#include "GuTriangleCache.h" + +namespace physx +{ + +namespace Gu +{ + +class PCMMeshContactGeneration +{ + PX_NOCOPY(PCMMeshContactGeneration) +public: + PCMContactPatch mContactPatch[PCM_MAX_CONTACTPATCH_SIZE]; + PCMContactPatch* mContactPatchPtr[PCM_MAX_CONTACTPATCH_SIZE]; + const Ps::aos::FloatV mContactDist; + const Ps::aos::FloatV mReplaceBreakingThreshold; + const Ps::aos::PsTransformV& mConvexTransform; + const Ps::aos::PsTransformV& mMeshTransform; + Gu::MultiplePersistentContactManifold& mMultiManifold; + Gu::ContactBuffer& mContactBuffer; + + Ps::aos::FloatV mAcceptanceEpsilon; + Ps::aos::FloatV mSqReplaceBreakingThreshold; + Ps::aos::PsMatTransformV mMeshToConvex; + Gu::MeshPersistentContact* mManifoldContacts; + PxU32 mNumContacts; + PxU32 mNumContactPatch; + PxU32 mNumCalls; + Cm::RenderOutput* mRenderOutput; + + PCMMeshContactGeneration( + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& convexTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Cm::RenderOutput* renderOutput + + ) : + mContactDist(contactDist), + mReplaceBreakingThreshold(replaceBreakingThreshold), + mConvexTransform(convexTransform), + mMeshTransform(meshTransform), + mMultiManifold(multiManifold), + mContactBuffer(contactBuffer), + mRenderOutput(renderOutput) + + { + using namespace Ps::aos; + mNumContactPatch = 0; + mNumContacts = 0; + mNumCalls = 0; + + mMeshToConvex = mConvexTransform.transformInv(mMeshTransform); + + //Assign the PCMContactPatch to the PCMContactPathPtr + for(PxU32 i=0; i<PCM_MAX_CONTACTPATCH_SIZE; ++i) + { + mContactPatchPtr[i] = &mContactPatch[i]; + } + mManifoldContacts = PX_CP_TO_MPCP(contactBuffer.contacts); + + mSqReplaceBreakingThreshold = FMul(replaceBreakingThreshold, replaceBreakingThreshold); + + mAcceptanceEpsilon = FLoad(0.996);//5 degree + //mAcceptanceEpsilon = FloatV_From_F32(0.9999);//5 degree + } + + template <PxU32 TriangleCount, typename Derived> + bool processTriangleCache(Gu::TriangleCache<TriangleCount>& cache) + { + PxU32 count = cache.mNumTriangles; + PxVec3* verts = cache.mVertices; + PxU32* vertInds = cache.mIndices; + PxU32* triInds = cache.mTriangleIndex; + PxU8* edgeFlags = cache.mEdgeFlags; + while(count--) + { + (static_cast<Derived*>(this))->processTriangle(verts, *triInds, *edgeFlags, vertInds); + verts += 3; + vertInds += 3; + triInds++; + edgeFlags++; + } + return true; + } + void prioritizeContactPatches(); + void addManifoldPointToPatch(const Ps::aos::Vec3VArg currentPatchNormal, const Ps::aos::FloatVArg maxPen, const PxU32 previousNumContacts); + void processContacts(const PxU8 maxContactPerManifold, const bool isNotLastPatch = true); +}; + +/* + This function is based on the current patch normal to either create a new patch or merge the manifold contacts in this patch with the manifold contacts in the last existing + patch. This means there might be more than GU_SINGLE_MANIFOLD_CACHE_SIZE in a SinglePersistentContactManifold. +*/ +PX_FORCE_INLINE void PCMMeshContactGeneration::addManifoldPointToPatch(const Ps::aos::Vec3VArg currentPatchNormal, const Ps::aos::FloatVArg maxPen, const PxU32 previousNumContacts) +{ + using namespace Ps::aos; + + bool foundPatch = false; + //we have existing patch + if(mNumContactPatch > 0) + { + //if the direction between the last existing patch normal and the current patch normal are within acceptance epsilon, which means we will be + //able to merge the last patch's contacts with the current patch's contacts. This is just to avoid to create an extra patch. We have some logic + //later to refine the patch again + if(FAllGrtr(V3Dot(mContactPatch[mNumContactPatch-1].mPatchNormal, currentPatchNormal), mAcceptanceEpsilon)) + { + //get the last patch + PCMContactPatch& patch = mContactPatch[mNumContactPatch-1]; + + //remove duplicate contacts + for(PxU32 i = patch.mStartIndex; i<patch.mEndIndex; ++i) + { + for(PxU32 j = previousNumContacts; j<mNumContacts; ++j) + { + Vec3V dif = V3Sub(mManifoldContacts[j].mLocalPointB, mManifoldContacts[i].mLocalPointB); + FloatV d = V3Dot(dif, dif); + if(FAllGrtr(mSqReplaceBreakingThreshold, d)) + { + if(FAllGrtr(V4GetW(mManifoldContacts[i].mLocalNormalPen), V4GetW(mManifoldContacts[j].mLocalNormalPen))) + { + //The new contact is deeper than the old contact so we keep the deeper contact + mManifoldContacts[i] = mManifoldContacts[j]; + } + mManifoldContacts[j] = mManifoldContacts[mNumContacts-1]; + mNumContacts--; + j--; + } + } + } + patch.mEndIndex = mNumContacts; + patch.mPatchMaxPen = FMin(patch.mPatchMaxPen, maxPen); + foundPatch = true; + } + } + + //If there are no existing patch which match the currentPatchNormal, we will create a new patch + if(!foundPatch) + { + mContactPatch[mNumContactPatch].mStartIndex = previousNumContacts; + mContactPatch[mNumContactPatch].mEndIndex = mNumContacts; + mContactPatch[mNumContactPatch].mPatchMaxPen = maxPen; + mContactPatch[mNumContactPatch++].mPatchNormal = currentPatchNormal; + } +} + +/* + This function sort the contact patch based on the max penetration so that deepest penetration contact patch will be in front of the less penetration contact + patch +*/ +PX_FORCE_INLINE void PCMMeshContactGeneration::prioritizeContactPatches() +{ + //we are using insertion sort to prioritize contact patchs + using namespace Ps::aos; + //sort the contact patch based on the max penetration + for(PxU32 i=1; i<mNumContactPatch; ++i) + { + const PxU32 indexi = i-1; + if(FAllGrtr(mContactPatchPtr[indexi]->mPatchMaxPen, mContactPatchPtr[i]->mPatchMaxPen)) + { + //swap + PCMContactPatch* tmp = mContactPatchPtr[indexi]; + mContactPatchPtr[indexi] = mContactPatchPtr[i]; + mContactPatchPtr[i] = tmp; + + for(PxI32 j=PxI32(i-2); j>=0; j--) + { + const PxU32 indexj = PxU32(j+1); + if(FAllGrtrOrEq(mContactPatchPtr[indexj]->mPatchMaxPen, mContactPatchPtr[j]->mPatchMaxPen)) + break; + //swap + PCMContactPatch* temp = mContactPatchPtr[indexj]; + mContactPatchPtr[indexj] = mContactPatchPtr[j]; + mContactPatchPtr[j] = temp; + } + } + } +} + + +PX_FORCE_INLINE void PCMMeshContactGeneration::processContacts(const PxU8 maxContactPerManifold, bool isNotLastPatch) +{ + using namespace Ps::aos; + + if(mNumContacts != 0) + { + //reorder the contact patches based on the max penetration + prioritizeContactPatches(); + //connect the patches which's angle between patch normals are within 5 degree + mMultiManifold.refineContactPatchConnective(mContactPatchPtr, mNumContactPatch, mManifoldContacts, mAcceptanceEpsilon); + //get rid of duplicate manifold contacts in connected contact patches + mMultiManifold.reduceManifoldContactsInDifferentPatches(mContactPatchPtr, mNumContactPatch, mManifoldContacts, mNumContacts, mSqReplaceBreakingThreshold); + //add the manifold contact to the corresponding manifold + mMultiManifold.addManifoldContactPoints(mManifoldContacts, mNumContacts, mContactPatchPtr, mNumContactPatch, mSqReplaceBreakingThreshold, mAcceptanceEpsilon, maxContactPerManifold); + + mNumContacts = 0; + mNumContactPatch = 0; + + if(isNotLastPatch) + { + //remap the contact patch pointer to contact patch + for(PxU32 i=0; i<PCM_MAX_CONTACTPATCH_SIZE; ++i) + { + mContactPatchPtr[i] = &mContactPatch[i]; + } + } + } +} + +struct PCMDeferredPolyData +{ +public: + PxVec3 mVerts[3]; //36 + PxU32 mInds[3]; //48 + PxU32 mTriangleIndex; //52 + PxU32 mFeatureIndex; //56 + PxU8 triFlags; //57 +}; + +#define MAX_CACHE_SIZE 128 + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class PCMConvexVsMeshContactGeneration : public PCMMeshContactGeneration +{ + PCMConvexVsMeshContactGeneration &operator=(PCMConvexVsMeshContactGeneration &); + +public: + + Gu::CacheMap<Gu::CachedEdge, MAX_CACHE_SIZE> mEdgeCache; + Gu::CacheMap<Gu::CachedVertex, MAX_CACHE_SIZE> mVertexCache; + Ps::aos::Vec3V mHullCenterMesh; + + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& mDeferredContacts; + const Gu::PolygonalData& mPolyData; + SupportLocal* mPolyMap; + const Cm::FastVertex2ShapeScaling& mConvexScaling; + bool mIdtConvexScale; + Cm::RenderOutput* mRenderOutput; + + + PCMConvexVsMeshContactGeneration( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& convexTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + + const Gu::PolygonalData& polyData, + SupportLocal* polyMap, + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + Cm::RenderOutput* renderOutput + + ) : PCMMeshContactGeneration(contactDistance, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, renderOutput), + mDeferredContacts(delayedContacts), + mPolyData(polyData), + mPolyMap(polyMap), + mConvexScaling(convexScaling), + mIdtConvexScale(idtConvexScale), + mRenderOutput(renderOutput) + { + using namespace Ps::aos; + + // Hull center in local space + const Vec3V hullCenterLocal = V3LoadU(mPolyData.mCenter); + // Hull center in mesh space + mHullCenterMesh = mMeshToConvex.transformInv(hullCenterLocal); + + } + + bool generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU32* triIndices, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl<Gu::TriangleV>* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal); + + bool generatePolyDataContactManifold(Gu::TriangleV& localTriangle, const PxU32 featureIndex, const PxU32 triangleIndex, const PxU8 triFlags, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal); + void generateLastContacts(); + void addContactsToPatch(const Ps::aos::Vec3VArg patchNormal, const PxU32 previousNumContacts); + + bool processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + + static bool generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl<Gu::TriangleV>* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal, Cm::RenderOutput* renderOutput = NULL); + + static bool processTriangle(const Gu::PolygonalData& polyData, SupportLocal* polyMap, const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const Ps::aos::FloatVArg inflation, const bool isDoubleSided, + const Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, Gu::MeshPersistentContact* manifoldContact, PxU32& numContacts); +}; + +#if PX_VC + #pragma warning(pop) +#endif + +class PCMSphereVsMeshContactGeneration : public PCMMeshContactGeneration +{ +public: + Ps::aos::Vec3V mSphereCenter; + Ps::aos::FloatV mSphereRadius; + Ps::aos::FloatV mSqInflatedSphereRadius; + + + PCMSphereVsMeshContactGeneration( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& sphereTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Cm::RenderOutput* renderOutput = NULL + + ) : PCMMeshContactGeneration(contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, renderOutput), + mSphereCenter(sphereCenter), + mSphereRadius(sphereRadius) + { + using namespace Ps::aos; + const FloatV inflatedSphereRadius = FAdd(sphereRadius, contactDist); + mSqInflatedSphereRadius = FMul(inflatedSphereRadius, inflatedSphereRadius); + } + + + bool processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); +}; + +class PCMCapsuleVsMeshContactGeneration : public PCMMeshContactGeneration +{ + PCMCapsuleVsMeshContactGeneration &operator=(PCMCapsuleVsMeshContactGeneration &); +public: + Ps::aos::FloatV mInflatedRadius; + Ps::aos::FloatV mSqInflatedRadius; + const CapsuleV& mCapsule; + + + PCMCapsuleVsMeshContactGeneration( + const CapsuleV& capsule, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& sphereTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Cm::RenderOutput* renderOutput = NULL + + ) : PCMMeshContactGeneration(contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, renderOutput), + mCapsule(capsule) + { + using namespace Ps::aos; + mInflatedRadius = FAdd(capsule.radius, contactDist); + mSqInflatedRadius = FMul(mInflatedRadius, mInflatedRadius); + } + + void generateEEContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + void generateEE(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static bool generateContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg planeNormal, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static void generateEEContactsMTD(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static void generateEEMTD(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, const Ps::aos::Vec3VArg normal, const PxU32 trianlgeIndex, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + bool processTriangle(const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + + static bool processTriangle(const TriangleV& triangle, const PxU32 triangleIndex, const CapsuleV& capsule, const Ps::aos::FloatVArg inflatedRadius, const PxU8 triFlag, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); +}; + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexConvex.cpp new file mode 100644 index 00000000..4d0fd351 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexConvex.cpp @@ -0,0 +1,310 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecConvexHull.h" +#include "GuVecShrunkConvexHull.h" +#include "GuVecShrunkConvexHullNoScale.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "GuPCMContactGen.h" +#include "GuContactBuffer.h" + +namespace physx +{ + + using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationConvexConvex(const ConvexHullV& convexHull0, Gu::ConvexHullV& convexHull1, const PsTransformV& transf0, const PsTransformV& transf1, + const bool idtScale0, const bool idtScale1, PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, + PersistentContactManifold& manifold, Vec3VArg normal, const Vec3VArg closestA, const Vec3VArg closestB, + const FloatVArg contactDist, const bool doOverlapTest, Cm::RenderOutput* renderOutput, const FloatVArg toleranceScale) +{ + Gu::PolygonalData polyData0, polyData1; + getPCMConvexData(convexHull0, idtScale0, polyData0); + getPCMConvexData(convexHull1, idtScale1, polyData1); + + PxU8 buff0[sizeof(SupportLocalImpl<ConvexHullV>)]; + PxU8 buff1[sizeof(SupportLocalImpl<ConvexHullV>)]; + + SupportLocal* map0 = (idtScale0 ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff0, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<const ConvexHullNoScaleV&>(convexHull0), transf0, convexHull0.vertex2Shape, convexHull0.shape2Vertex, idtScale0)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff0, SupportLocalImpl<ConvexHullV>)(convexHull0, transf0, convexHull0.vertex2Shape, convexHull0.shape2Vertex, idtScale0))); + + SupportLocal* map1 = (idtScale1 ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff1, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<const ConvexHullNoScaleV&>(convexHull1), transf1, convexHull1.vertex2Shape, convexHull1.shape2Vertex, idtScale1)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff1, SupportLocalImpl<ConvexHullV>)(convexHull1, transf1, convexHull1.vertex2Shape, convexHull1.shape2Vertex, idtScale1))); + + + PxU32 numContacts = 0; + + if(generateFullContactManifold(polyData0, polyData1, map0, map1, manifoldContacts, numContacts, contactDist, normal, closestA, closestB, convexHull0.getMargin(), + convexHull1.getMargin(), doOverlapTest, renderOutput, toleranceScale)) + { + + if (numContacts > 0) + { + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts); + + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + //add the manifold contacts; + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + } + else + { + //if doOverlapTest is true, which means GJK/EPA degenerate so we won't have any contact in the manifoldContacts array + if (!doOverlapTest) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + + } + return true; + + } + + return false; + +} + +static GjkStatus convexHullNoScale0(const ShrunkConvexHullV& convexHull0, const ShrunkConvexHullV& convexHull1, const bool idtScale1, const PsMatTransformV& aToB, const FloatVArg contactDist, + Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& penDep, PersistentContactManifold& manifold) +{ + const RelativeConvex<ShrunkConvexHullNoScaleV> convexA(static_cast<const ShrunkConvexHullNoScaleV&>(convexHull0), aToB); + if(idtScale1) + { + const LocalConvex<ShrunkConvexHullNoScaleV> convexB(static_cast<const ShrunkConvexHullNoScaleV&>(convexHull1)); + return gjkPenetration<RelativeConvex<ShrunkConvexHullNoScaleV>, LocalConvex<ShrunkConvexHullNoScaleV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, false); + } + else + { + const LocalConvex<ShrunkConvexHullV> convexB(convexHull1); + return gjkPenetration<RelativeConvex<ShrunkConvexHullNoScaleV>, LocalConvex<ShrunkConvexHullV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints,false); + } +} + +static GjkStatus convexHullHasScale0(ShrunkConvexHullV& convexHull0, ShrunkConvexHullV& convexHull1, const bool idtScale1, const PsMatTransformV& aToB, + const FloatVArg contactDist, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& penDep, PersistentContactManifold& manifold) +{ + RelativeConvex<ShrunkConvexHullV> convexA(convexHull0, aToB); + if(idtScale1) + { + LocalConvex<ShrunkConvexHullNoScaleV> convexB(static_cast<ShrunkConvexHullNoScaleV&>(convexHull1)); + return gjkPenetration< RelativeConvex<ShrunkConvexHullV>, LocalConvex<ShrunkConvexHullNoScaleV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints,false); + } + else + { + LocalConvex<ShrunkConvexHullV> convexB(convexHull1); + return gjkPenetration<RelativeConvex<ShrunkConvexHullV>, LocalConvex<ShrunkConvexHullV> >(convexA, convexB, aToB.p, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, false); + } +} + + +static bool addGJKEPAContacts(Gu::ShrunkConvexHullV& convexHull0, Gu::ShrunkConvexHullV& convexHull1, const PsMatTransformV& aToB, GjkStatus status, + Gu::PersistentContact* manifoldContacts, const FloatV replaceBreakingThreshold, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& penDep, + Gu::PersistentContactManifold& manifold) +{ + bool doOverlapTest = false; + if (status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint(localPointA, closestB, localNormalPen, replaceBreakingThreshold); + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + RelativeConvex<ConvexHullV> convexA1(convexHull0, aToB); + LocalConvex<ConvexHullV> convexB1(convexHull1); + + status = epaPenetration(convexA1, convexB1, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep); + + if (status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint(localPointA, closestB, localNormalPen, replaceBreakingThreshold); + } + else + { + doOverlapTest = true; + } + } + + return doOverlapTest; +} + +bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + const PxConvexMeshGeometryLL& shapeConvex0 = shape0.get<const PxConvexMeshGeometryLL>(); + const PxConvexMeshGeometryLL& shapeConvex1 = shape1.get<const PxConvexMeshGeometryLL>(); + PersistentContactManifold& manifold = cache.getManifold(); + + Ps::prefetchLine(shapeConvex0.hullData); + Ps::prefetchLine(shapeConvex1.hullData); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V vScale0 = V3LoadU_SafeReadW(shapeConvex0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const Vec3V vScale1 = V3LoadU_SafeReadW(shapeConvex1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV contactDist = FLoad(params.mContactDistance); + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const Gu::ConvexHullData* hullData0 = shapeConvex0.hullData; + const Gu::ConvexHullData* hullData1 = shapeConvex1.hullData; + + const FloatV convexMargin0 = Gu::CalculatePCMConvexMargin(hullData0, vScale0); + const FloatV convexMargin1 = Gu::CalculatePCMConvexMargin(hullData1, vScale1); + + const PxU32 initialContacts = manifold.mNumContacts; + + const FloatV minMargin = FMin(convexMargin0, convexMargin1); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + PX_UNUSED(bLostContacts); + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + const bool idtScale0 = shapeConvex0.scale.isIdentity(); + const bool idtScale1 = shapeConvex1.scale.isIdentity(); + const QuatV vQuat0 = QuatVLoadU(&shapeConvex0.scale.rotation.x); + const QuatV vQuat1 = QuatVLoadU(&shapeConvex1.scale.rotation.x); + const Vec3V zeroV = V3Zero(); + Gu::ShrunkConvexHullV convexHull0(hullData0, zeroV, vScale0, vQuat0, idtScale0); + Gu::ShrunkConvexHullV convexHull1(hullData1, zeroV, vScale1, vQuat1, idtScale1); + + Vec3V closestA(zeroV), closestB(zeroV), normal(zeroV); // from a to b + FloatV penDep = FZero(); + + if(idtScale0) + { + status = convexHullNoScale0(convexHull0, convexHull1, idtScale1, aToB, contactDist, closestA, closestB, normal, penDep, manifold); + } + else + { + status = convexHullHasScale0(convexHull0, convexHull1, idtScale1, aToB, contactDist, closestA, closestB, normal, penDep, manifold); + } + + manifold.setRelativeTransform(curRTrans); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + if(status == GJK_DEGENERATE) + { + return fullContactsGenerationConvexConvex(convexHull0, convexHull1, transf0, transf1, idtScale0, idtScale1, manifoldContacts, contactBuffer, + manifold, normal, closestA, closestB, contactDist, true, renderOutput, FLoad(params.mToleranceLength)); + } + else if(status == GJK_NON_INTERSECT) + { + return false; + } + else + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + const bool doOverlapTest = addGJKEPAContacts(convexHull0, convexHull1, aToB, status, manifoldContacts, replaceBreakingThreshold, closestA, closestB, normal, penDep, manifold); + + //ML: after we refresh the contacts(newContacts) and generate a GJK/EPA contacts(we will store that in the manifold), if the number of contacts is still less than the original contacts, + //which means we lose too mang contacts and we should regenerate all the contacts in the current configuration + if (initialContacts == 0 || (manifold.mNumContacts < initialContacts) || doOverlapTest) + { + return fullContactsGenerationConvexConvex(convexHull0, convexHull1, transf0, transf1, idtScale0, idtScale1, manifoldContacts, contactBuffer, + manifold, normal, closestA, closestB, contactDist, doOverlapTest, renderOutput, FLoad(params.mToleranceLength)); + } + else + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + return true; + } + + } + } + else if(manifold.getNumContacts()> 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; + +} + +} +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexHeightField.cpp new file mode 100644 index 00000000..029a60d9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexHeightField.cpp @@ -0,0 +1,274 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecShrunkConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuHeightFieldUtil.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" + +#include "PsVecMath.h" + + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMConvexVsHeightfieldContactGenerationCallback + : PCMHeightfieldContactGenerationCallback< PCMConvexVsHeightfieldContactGenerationCallback > +{ + PCMConvexVsHeightfieldContactGenerationCallback& operator=(const PCMConvexVsHeightfieldContactGenerationCallback&); +public: + PCMConvexVsMeshContactGeneration mGeneration; + + PCMConvexVsHeightfieldContactGenerationCallback( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Gu::PolygonalData& polyData, + SupportLocal* polyMap, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + const PsTransformV& convexTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Gu::HeightFieldUtil& hfUtil, + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + Cm::RenderOutput* renderOutput = NULL + + ) : + PCMHeightfieldContactGenerationCallback< PCMConvexVsHeightfieldContactGenerationCallback >(hfUtil, heightfieldTransform1), + mGeneration(contactDistance, replaceBreakingThreshold, convexTransform, heightfieldTransform, multiManifold, + contactBuffer, polyData, polyMap, delayedContacts, convexScaling, idtConvexScale, renderOutput) + { + } + + template<PxU32 CacheSize> + void processTriangleCache(Gu::TriangleCache<CacheSize>& cache) + { + mGeneration.processTriangleCache<CacheSize, PCMConvexVsMeshContactGeneration>(cache); + } + +}; + +bool Gu::PCMContactConvexHeightfield( + const Gu::PolygonalData& polyData, Gu::SupportLocal* polyMap, const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, const PxHeightFieldGeometry& shapeHeightfield, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale, + Gu::MultiplePersistentContactManifold& multiManifold, Cm::RenderOutput* renderOutput) + +{ + + using namespace Ps::aos; + using namespace Gu; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV contactDist = FLoad(contactDistance); + //Transfer A into the local space of B + const PsTransformV convexTransform(p0, q0);//box + const PsTransformV heightfieldTransform(p1, q1);//heightfield + const PsTransformV curTransform = heightfieldTransform.transformInv(convexTransform); + + + if(multiManifold.invalidate(curTransform, minMargin)) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + //////////////////// + + const PxTransform t0to1 = transform1.transformInv(transform0); + + const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(shapeHeightfield.heightField); + Gu::HeightFieldUtil hfUtil(shapeHeightfield, hf); + + //Gu::HeightFieldUtil hfUtil(shapeHeightfield); + + //////////////////// + + /*const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + const PxU8* PX_RESTRICT extraData = meshData->mExtraTrigData;*/ + + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE> delayedContacts; + + PCMConvexVsHeightfieldContactGenerationCallback blockCallback( + contactDist, + replaceBreakingThreshold, + polyData, + polyMap, + convexScaling, + idtConvexScale, + convexTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + hfUtil, + delayedContacts, + renderOutput + ); + + hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); + + PX_ASSERT(multiManifold.mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.6f)); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + } + +#if PCM_LOW_LEVEL_DEBUG + multiManifold.drawManifold(*renderOutput, convexTransform, heightfieldTransform); +#endif + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, heightfieldTransform); + +} + + +bool Gu::pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + const PxConvexMeshGeometryLL& shapeConvex = shape0.get<const PxConvexMeshGeometryLL>(); + const physx::PxHeightFieldGeometryLL& shapHeightField = shape1.get<const PxHeightFieldGeometryLL>(); + + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const PsTransformV convexTransform(p0, q0); + + //const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + //Cm::FastVertex2ShapeScaling meshScaling; + //if(!idtScaleMesh) + // meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData; + const bool idtScaleConvex = getPCMConvexData(shape0, convexScaling, hullAABB, polyData); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale); + const FloatV epsilon = FLoad(GU_PCM_MESH_MANIFOLD_EPSILON); + const FloatV toleranceLength = FLoad(params.mToleranceLength); + const FloatV toleranceMargin = FMul(epsilon, toleranceLength); + const FloatV minMargin = FMin(convexMargin, toleranceMargin); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + Gu::ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, shapeConvex.scale.isIdentity()); + + if(idtScaleConvex) + { + SupportLocalShrunkImpl<Gu::ConvexHullNoScaleV, Gu::ShrunkConvexHullNoScaleV> convexMap(static_cast<ConvexHullNoScaleV&>(convexHull), convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex); + return Gu::PCMContactConvexHeightfield(polyData, &convexMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, convexScaling, + idtScaleConvex, multiManifold, renderOutput); + } + else + { + SupportLocalShrunkImpl<Gu::ConvexHullV, Gu::ShrunkConvexHullV> convexMap(convexHull, convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex); + return Gu::PCMContactConvexHeightfield(polyData, &convexMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, convexScaling, + idtScaleConvex, multiManifold, renderOutput); + } +} + +bool Gu::pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + const physx::PxHeightFieldGeometryLL& shapHeightField = shape1.get<const PxHeightFieldGeometryLL>(); + + const PxVec3 ext = shapeBox.halfExtents + PxVec3(params.mContactDistance); + const PxBounds3 hullAABB(-ext, ext); + + Cm::FastVertex2ShapeScaling idtScaling; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents); + + const FloatV epsilon = FLoad(GU_PCM_MESH_MANIFOLD_EPSILON); + const FloatV toleranceLength = FLoad(params.mToleranceLength); + const FloatV toleranceMargin = FMul(epsilon, toleranceLength); + const FloatV minMargin = FMin(boxMargin, toleranceMargin); + + Gu::BoxV boxV(V3Zero(), boxExtents); + + const PsTransformV boxTransform(p0, q0);//box + + Gu::PolygonalData polyData; + Gu::PCMPolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + //SupportLocalImpl<Gu::BoxV> boxMap(boxV, boxTransform, identity, identity); + SupportLocalShrunkImpl<Gu::BoxV, Gu::ShrunkBoxV> boxMap(boxV, boxTransform, identity, identity, true); + + return Gu::PCMContactConvexHeightfield(polyData, &boxMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, + idtScaling, true, multiManifold, renderOutput); +} +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexMesh.cpp new file mode 100644 index 00000000..d739b6dc --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactConvexMesh.cpp @@ -0,0 +1,264 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "GuConvexUtilsInternal.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" +#include "GuIntersectionTriangleBox.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMConvexVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback<PCMConvexVsMeshContactGenerationCallback> +{ + PCMConvexVsMeshContactGenerationCallback& operator=(const PCMConvexVsMeshContactGenerationCallback&); +public: + PCMConvexVsMeshContactGeneration mGeneration; + const BoxPadded& mBox; + + PCMConvexVsMeshContactGenerationCallback( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& convexTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PolygonalData& polyData, + SupportLocal* polyMap, + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE>& delayedContacts, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + const Cm::FastVertex2ShapeScaling& meshScaling, + const PxU8* extraTriData, + bool idtMeshScale, + const BoxPadded& box, + Cm::RenderOutput* renderOutput = NULL + + ) : + PCMMeshContactGenerationCallback<PCMConvexVsMeshContactGenerationCallback>(meshScaling, extraTriData, idtMeshScale), + mGeneration(contactDistance, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, polyData, polyMap, delayedContacts, convexScaling, idtConvexScale, renderOutput), + mBox(box) + { + } + + PX_FORCE_INLINE Ps::IntBool doTest(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2) + { + // PT: this one is safe because midphase vertices are directly passed to the function + return intersectTriangleBox(mBox, v0, v1, v2); + } + + template<PxU32 CacheSize> + void processTriangleCache(TriangleCache<CacheSize>& cache) + { + mGeneration.processTriangleCache<CacheSize, PCMConvexVsMeshContactGeneration>(cache); + } + +}; + + +bool Gu::PCMContactConvexMesh(const PolygonalData& polyData, SupportLocal* polyMap, const Ps::aos::FloatVArg minMargin, const PxBounds3& hullAABB, const PxTriangleMeshGeometryLL& shapeMesh, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput) + +{ + using namespace Ps::aos; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV contactDist = FLoad(contactDistance); + //Transfer A into the local space of B + const PsTransformV convexTransform(p0, q0);//box + const PsTransformV meshTransform(p1, q1);//triangleMesh + const PsTransformV curTransform = meshTransform.transformInv(convexTransform); + + + if(multiManifold.invalidate(curTransform, minMargin)) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + + //////////////////// + const TriangleMesh* PX_RESTRICT meshData = shapeMesh.meshData; + + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + BoxPadded hullOBB; + computeHullOBB(hullOBB, hullAABB, contactDistance, world0, world1, meshScaling, idtMeshScale); + + // Setup the collider + + Ps::InlineArray<PxU32,LOCAL_CONTACTS_SIZE> delayedContacts; + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + PCMConvexVsMeshContactGenerationCallback blockCallback( + contactDist, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, + polyData, polyMap, delayedContacts, convexScaling, idtConvexScale, meshScaling, extraData, idtMeshScale, + hullOBB, renderOutput); + + Midphase::intersectOBB(meshData, hullOBB, blockCallback, true); + + PX_ASSERT(multiManifold.mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + + blockCallback.flushCache(); + //This is very important + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE, false); + +#if PCM_LOW_LEVEL_DEBUG + multiManifold.drawManifold(*renderOutput, transform0, transform1); +#endif + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + } + + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, meshTransform); +} + +bool Gu::pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + + const PxConvexMeshGeometryLL& shapeConvex = shape0.get<const PxConvexMeshGeometryLL>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + const ConvexHullData* hullData = shapeConvex.hullData; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PsTransformV convexTransform = loadTransformA(transform0); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData; + const bool idtScaleConvex = getPCMConvexData(shape0, convexScaling, hullAABB, polyData); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV convexMargin = CalculatePCMConvexMargin(hullData, vScale); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const FloatV epsilon = FLoad(GU_PCM_MESH_MANIFOLD_EPSILON); + const FloatV toleranceLength = FLoad(params.mToleranceLength); + const FloatV toleranceMargin = FMul(epsilon, toleranceLength); + const FloatV minMargin = FMin(convexMargin, toleranceMargin); + + ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, idtScaleConvex); + + + if(idtScaleConvex) + { + SupportLocalImpl<Gu::ConvexHullNoScaleV> convexMap(static_cast<ConvexHullNoScaleV&>(convexHull), convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, true); + return Gu::PCMContactConvexMesh(polyData, &convexMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, convexScaling, + meshScaling, idtScaleConvex, idtScaleMesh, multiManifold, renderOutput); + } + else + { + SupportLocalImpl<Gu::ConvexHullV> convexMap(convexHull, convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, false); + return Gu::PCMContactConvexMesh(polyData, &convexMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, convexScaling, + meshScaling, idtScaleConvex, idtScaleMesh, multiManifold, renderOutput); + } +} + +bool Gu::pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PxBoxGeometry& shapeBox = shape0.get<const PxBoxGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); + + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling idtScaling; + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents); + const FloatV epsilon = FLoad(GU_PCM_MESH_MANIFOLD_EPSILON); + const FloatV toleranceLength = FLoad(params.mToleranceLength); + const FloatV toleranceMargin = FMul(epsilon, toleranceLength); + const FloatV minMargin = FMin(boxMargin, toleranceMargin); + + BoxV boxV(V3Zero(), boxExtents); + + const PsTransformV boxTransform = loadTransformA(transform0);//box + + PolygonalData polyData; + PCMPolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + SupportLocalImpl<BoxV> boxMap(boxV, boxTransform, identity, identity, true); + + return Gu::PCMContactConvexMesh(polyData, &boxMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, idtScaling, meshScaling, + true, idtMeshScale, multiManifold, renderOutput); +} + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGen.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGen.h new file mode 100644 index 00000000..6747b49b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGen.h @@ -0,0 +1,74 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_GEN_H +#define GU_PCM_CONTACT_GEN_H + + +#include "GuConvexSupportTable.h" +#include "GuPersistentContactManifold.h" +#include "GuShapeConvex.h" +#include "GuSeparatingAxes.h" + +namespace physx +{ + +namespace Gu +{ + + + //full contact gen code for box/convexhull vs convexhull + bool generateFullContactManifold(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, Gu::SupportLocal* map0, Gu::SupportLocal* map1, Gu::PersistentContact* manifoldContacts, + PxU32& numContacts, const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg closestA, const Ps::aos::Vec3VArg closestB, + const Ps::aos::FloatVArg toleranceA, const Ps::aos::FloatVArg toleranceB, bool doOverlapTest, Cm::RenderOutput* renderOutput, const Ps::aos::FloatVArg toleranceScale); + + //full contact gen code for capsule vs convexhulll + bool generateFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, const Ps::aos::PsMatTransformV& aToB, Gu::PersistentContact* manifoldContacts, + PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const Ps::aos::Vec3VArg closest, const Ps::aos::FloatVArg tolerance, bool doOverlapTest, const Ps::aos::FloatVArg toleranceScale); + + //full contact gen code for capsule vs box + bool generateCapsuleBoxFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, const Ps::aos::PsMatTransformV& aToB, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const Ps::aos::Vec3VArg closest, const Ps::aos::FloatVArg boxMargin, const bool doOverlapTest, const Ps::aos::FloatVArg toeranceScale); + + //MTD code for box/convexhull vs box/convexhull + bool computeMTD(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal); + + //MTD code for capsule vs box/convexhull + bool computeMTD(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal); + + void buildPartialHull(const Gu::PolygonalData& polyData, SupportLocal* map, Gu::SeparatingAxes& validAxes, const Ps::aos::Vec3VArg v, const Ps::aos::Vec3VArg _dir); + + //full contact gen code for sphere vs convexhull + bool generateSphereFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const bool doOverlapTest); + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenBoxConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenBoxConvex.cpp new file mode 100644 index 00000000..68354425 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenBoxConvex.cpp @@ -0,0 +1,725 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "CmRenderOutput.h" +#include "GuPCMContactGenUtil.h" +#include "PsVecMath.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" + +#define PCM_USE_INTERNAL_OBJECT 1 + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + + + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + +namespace physx +{ + +namespace Gu +{ + + static bool testFaceNormal(const PolygonalData& polyData0, const PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, const PsMatTransformV& transform1To0, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(polyData1); + + FloatV _minOverlap = FMax();//minOverlap; + PxU32 _feature = 0; + Vec3V _faceNormal = faceNormal; + FloatV min0, max0; + FloatV min1, max1; + + const Vec3V center1To0 = transform1To0.p; + +#if PCM_USE_INTERNAL_OBJECT + const Vec3V zeroV = V3Zero(); + const Vec3V shapeSpaceCenter1 = V3LoadU(polyData1.mCenter); + const Vec3V internalCenter1In0 = transform1To0.transform(shapeSpaceCenter1); + const FloatV internalRadius1 = FLoad(polyData1.mInternal.mRadius); + const Vec3V internalExtents1 = V3LoadU(polyData1.mInternal.mExtents); + const Vec3V negInternalExtents1 = V3Neg(internalExtents1); + +#endif + + //in the local space of polyData0 + for(PxU32 i=0; i<polyData0.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData0.mPolygons[i]; + + const Vec3V minVert = V3LoadU(polyData0.mVerts[polygon.mMinIndex]); + const FloatV planeDist = FLoad(polygon.mPlane.d); + const Vec3V vertexSpacePlaneNormal = V3LoadU(polygon.mPlane.n); + + //transform plane n to shape space + const Vec3V shapeSpacePlaneNormal = M33TrnspsMulV3(map0->shape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRecip(V3Length(shapeSpacePlaneNormal)); + //ML::use this to avoid LHS + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + //normalize shape space normal + const Vec3V n0 = V3Scale(shapeSpacePlaneNormal, magnitude); + + //calculate polyData1 projection + //rotate polygon's normal into the local space of polyData1 + const Vec3V n1 = transform0To1.rotate(n0); + + +#if PCM_USE_INTERNAL_OBJECT + + //test internal object + //ML: we don't need to transform the normal into the vertex space. If polyData1 don't have scale, + //the vertex2Shape matrix will be identity, shape space normal will be the same as vertex space's normal. + //If polyData0 have scale, internalExtens1 will be 0. + const Vec3V proj = V3Sel(V3IsGrtr(n1, zeroV), internalExtents1, negInternalExtents1); + const FloatV radius = FMax(V3Dot(n1, proj), internalRadius1); + const FloatV internalTrans = V3Dot(internalCenter1In0, n0); + + const FloatV _min1 = FSub(internalTrans, radius); + const FloatV _max1 = FAdd(internalTrans, radius); + + const FloatV _min = FMax(min0, _min1); + const FloatV _max = FMin(max0, _max1); + + const FloatV _tempOverlap = FSub(_max, _min); + //const FloatV _tempOverlap = FSub(max0, _min1); + + //Internal object overlaps more than current min, so can skip it + //because (a) it isn't a separating axis and (b) it isn't the smallest axis + if(FAllGrtr(_tempOverlap, _minOverlap)) + { + continue; + } + +#endif + + const FloatV translate = V3Dot(center1To0, n0); + map1->doSupport(n1, min1, max1); + + min1 = FAdd(translate, min1); + max1 = FAdd(translate, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = n0; + } + } + + if(FAllGrtr(minOverlap, _minOverlap)) + { + faceNormal = _faceNormal; + minOverlap = _minOverlap; + status = faceStatus; + } + + + + feature = _feature; + + return true; + + } + + + //plane is in the shape space of polyData + void buildPartialHull(const PolygonalData& polyData, SupportLocal* map, SeparatingAxes& validAxes, const Vec3VArg planeP, const Vec3VArg planeDir) + { + const FloatV zero = FZero(); + const Vec3V dir = V3Normalize(planeDir); + for(PxU32 i=0; i<polyData.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + const PxU8* inds = polyData.mPolygonVertexRefs + polygon.mVRef8; + + Vec3V v0 = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[0]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + FloatV dist0 = V3Dot(dir, V3Sub(v0, planeP)); + + for (PxU32 iStart = 0, iEnd = PxU32(polygon.mNbVerts - 1); iStart < polygon.mNbVerts; iEnd = iStart++) + { + const Vec3V v1 = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[iEnd]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const FloatV dist1 = V3Dot(dir, V3Sub(v1, planeP)); + + const BoolV con = BOr(FIsGrtr(dist0, zero), FIsGrtr(dist1, zero)); + + //cull edge if either of the vertex will on the positive size of the plane + if(BAllEqTTTT(con)) + { + const Vec3V tempV = V3Sub(v0, v1); + PxVec3 temp; + V3StoreU(tempV, temp); + validAxes.addAxis(temp.getNormalized()); + } + + v0 = v1; + dist0 = dist1; + } + } + } + + + static bool testEdgeNormal(const PolygonalData& polyData0, const PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, const PsMatTransformV& transform1To0, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& edgeNormalIn0, const FeatureStatus edgeStatus, FeatureStatus& status) + { + + FloatV overlap = minOverlap; + FloatV min0, max0; + FloatV min1, max1; + const FloatV eps = FEps(); + + const Vec3V shapeSpaceCenter0 = V3LoadU(polyData0.mCenter); + const Vec3V shapeSpaceCenter1 = V3LoadU(polyData1.mCenter); + +#if PCM_USE_INTERNAL_OBJECT + const Vec3V zeroV = V3Zero(); + const Vec3V internalCenter1In0 = V3Sub(transform1To0.transform(shapeSpaceCenter1), shapeSpaceCenter0); + const FloatV internalRadius1 = FLoad(polyData1.mInternal.mRadius); + const Vec3V internalExtents1 = V3LoadU(polyData1.mInternal.mExtents); + const Vec3V negInternalExtents1 = V3Neg(internalExtents1); + + const FloatV internalRadius0 = FLoad(polyData0.mInternal.mRadius); + const Vec3V internalExtents0 = V3LoadU(polyData0.mInternal.mExtents); + const Vec3V negInternalExtents0 = V3Neg(internalExtents0); +#endif + + const Vec3V center1To0 = transform1To0.p; + + //in polyData0 shape space + const Vec3V dir0 = V3Sub(transform1To0.transform(shapeSpaceCenter1), shapeSpaceCenter0); + const Vec3V support0 = map0->doSupport(dir0); + + //in polyData1 shape space + const Vec3V dir1 = transform0To1.rotate(V3Neg(dir0)); + const Vec3V support1 = map1->doSupport(dir1); + + const Vec3V support0In1 = transform0To1.transform(support0); + const Vec3V support1In0 = transform1To0.transform(support1); + + SeparatingAxes mSA0; + SeparatingAxes mSA1; + mSA0.reset(); + mSA1.reset(); + + buildPartialHull(polyData0, map0, mSA0, support1In0, dir0); + buildPartialHull(polyData1, map1, mSA1, support0In1, dir1); + + const PxVec3* axe0 = mSA0.getAxes(); + const PxVec3* axe1 = mSA1.getAxes(); + const PxU32 numAxe0 = mSA0.getNumAxes(); + const PxU32 numAxe1 = mSA1.getNumAxes(); + for(PxU32 i=0; i < numAxe0; ++i) + { + //axe0[i] is in the shape space of polyData0 + const Vec3V v0 = V3LoadU(axe0[i]); + + for(PxU32 j=0; j< numAxe1; ++j) + { + //axe1[j] is in the shape space of polyData1 + const Vec3V v1 = V3LoadU(axe1[j]); + + const Vec3V dir = V3Cross(v0, transform1To0.rotate(v1)); + const FloatV lenSq = V3Dot(dir, dir); + if(FAllGrtr(eps, lenSq)) + continue; + + //n0 is in polyData0's local space + const Vec3V n0 = V3Scale(dir, FRsqrt(lenSq)); + + //n1 is in polyData1's local space + const Vec3V n1 = transform0To1.rotate(n0); + +#if PCM_USE_INTERNAL_OBJECT + + //ML: we don't need to transform the normal into the vertex space. If polyData1 don't have scale, + //the vertex2Shape matrix will be identity, shape space normal will be the same as vertex space's normal. + //If polyData0 have scale, internalExtens1 will be 0. + //vertex space n1 + const Vec3V proj = V3Sel(V3IsGrtr(n1, zeroV), internalExtents1, negInternalExtents1); + const FloatV radius = FMax(V3Dot(n1, proj), internalRadius1); + + const FloatV internalTrans = V3Dot(internalCenter1In0, n0); + + const FloatV _min1 = FSub(internalTrans, radius); + const FloatV _max1 = FAdd(internalTrans, radius); + + const Vec3V proj0 = V3Sel(V3IsGrtr(n0, zeroV), internalExtents0, negInternalExtents0); + const FloatV radius0 = FMax(V3Dot(n0, proj0), internalRadius0); + + const FloatV _max0 = radius0; + const FloatV _min0 = FNeg(radius0); + + PX_ASSERT(FAllGrtrOrEq(_max0, _min0)); + PX_ASSERT(FAllGrtrOrEq(_max1, _min1)); + + + const FloatV _min = FMax(_min0, _min1); + const FloatV _max = FMin(_max0, _max1); + + const FloatV _tempOverlap = FSub(_max, _min); + + //Internal object overlaps more than current min, so can skip it + //because (a) it isn't a separating axis and (b) it isn't the smallest axis + if(FAllGrtr(_tempOverlap, overlap)) + { + continue; + } + +#endif + //get polyData0's projection + map0->doSupport(n0, min0, max0); + + const FloatV translate = V3Dot(center1To0, n0); + + //get polyData1's projection + map1->doSupport(n1, min1, max1); + + min1 = FAdd(translate, min1); + max1 = FAdd(translate, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + +#if PCM_USE_INTERNAL_OBJECT + PX_ASSERT(FAllGrtrOrEq(tempOverlap, _tempOverlap)); +#endif + + if(FAllGrtr(overlap, tempOverlap)) + { + overlap = tempOverlap; + edgeNormalIn0 = n0; + status = edgeStatus; + } + } + } + + minOverlap = overlap; + + return true; + + } + + + //contactNormal is in the space of polyData0 + void generatedContacts(PolygonalData& polyData0, PolygonalData& polyData1, const HullPolygonData& referencePolygon, const HullPolygonData& incidentPolygon, + SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, PersistentContact* manifoldContacts, + PxU32& numContacts, const FloatVArg contactDist, Cm::RenderOutput* renderOutput) + { + + PX_UNUSED(renderOutput); + const FloatV zero = FZero(); + + const PxU8* inds0 = polyData0.mPolygonVertexRefs + referencePolygon.mVRef8; + + //transform the plane normal to shape space + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(map0->shape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + + //this is the matrix transform all points to the 2d plane + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + const PxU8* inds1 = polyData1.mPolygonVertexRefs + incidentPolygon.mVRef8; + + + Vec3V* points0In0 = reinterpret_cast<Vec3V*>(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + Vec3V* points1In0 = reinterpret_cast<Vec3V*>(PxAllocaAligned(sizeof(Vec3V)*incidentPolygon.mNbVerts, 16)); + bool* points1In0Penetration = reinterpret_cast<bool*>(PxAlloca(sizeof(bool)*incidentPolygon.mNbVerts)); + FloatV* points1In0TValue = reinterpret_cast<FloatV*>(PxAllocaAligned(sizeof(FloatV)*incidentPolygon.mNbVerts, 16)); + + //Transform all the verts from vertex space to shape space + map0->populateVerts(inds0, referencePolygon.mNbVerts, polyData0.mVerts, points0In0); + map1->populateVerts(inds1, incidentPolygon.mNbVerts, polyData1.mVerts, points1In0); + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map0->transform, points0In0, (PxU32)referencePolygon.mNbVerts, 0x00ff0000); + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map1->transform, points1In0, (PxU32)incidentPolygon.mNbVerts, 0x0000ff00); +#endif + + //This is used to calculate the project point when the 2D reference face points is inside the 2D incident face point + const Vec3V sPoint = points1In0[0]; + + PX_ASSERT(incidentPolygon.mNbVerts <= 64); + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i<referencePolygon.mNbVerts; ++i) + { + points0In0[i] = M33MulV3(rot, points0In0[i]); + rPolygonMin = V3Min(rPolygonMin, points0In0[i]); + rPolygonMax = V3Max(rPolygonMax, points0In0[i]); + } + + rPolygonMin = V3Sub(rPolygonMin, eps); + rPolygonMax = V3Add(rPolygonMax, eps); + + const FloatV d = V3GetZ(points0In0[0]); + const FloatV rd = FAdd(d, contactDist); + + Vec3V iPolygonMin= max; + Vec3V iPolygonMax = nmax; + + + PxU32 inside = 0; + for(PxU32 i=0; i<incidentPolygon.mNbVerts; ++i) + { + const Vec3V vert1 =points1In0[i]; //this still in polyData1's local space + const Vec3V a = transform0To1.transformInv(vert1); + points1In0[i] = M33MulV3(rot, a); + const FloatV z = V3GetZ(points1In0[i]); + points1In0TValue[i] = FSub(z, d); + points1In0[i] = V3SetZ(points1In0[i], d); + iPolygonMin = V3Min(iPolygonMin, points1In0[i]); + iPolygonMax = V3Max(iPolygonMax, points1In0[i]); + if(FAllGrtr(rd, z)) + { + points1In0Penetration[i] = true; + + if(contains(points0In0, referencePolygon.mNbVerts, points1In0[i], rPolygonMin, rPolygonMax)) + { + inside++; + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), points1In0TValue[i]); + manifoldContacts[numContacts].mLocalPointA = vert1; + manifoldContacts[numContacts].mLocalPointB = M33TrnspsMulV3(rot, points1In0[i]); + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + + } + } + else + { + points1In0Penetration[i] = false; + } + + } + + + if(inside == incidentPolygon.mNbVerts) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = V3Normalize(M33TrnspsMulV3(map1->shape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + + const Vec3V contactNormalIn1 = transform0To1.rotate(contactNormal); + + for(PxU32 i=0; i<referencePolygon.mNbVerts; ++i) + { + if(contains(points1In0, incidentPolygon.mNbVerts, points0In0[i], iPolygonMin, iPolygonMax)) + { + //const Vec3V vert0=Vec3V_From_PxVec3(polyData0.mVerts[inds0[i]]); + const Vec3V vert0 = M33TrnspsMulV3(rot, points0In0[i]); + const Vec3V a = transform0To1.transform(vert0); + + const FloatV nom = V3Dot(incidentNormal, V3Sub(sPoint, a)); + const FloatV denom = V3Dot(incidentNormal, contactNormalIn1); + PX_ASSERT(FAllEq(denom, zero)==0); + const FloatV t = FDiv(nom, denom); + + if(FAllGrtr(t, contactDist)) + continue; + + + inside++; + + const Vec3V projPoint = V3ScaleAdd(contactNormalIn1, t, a); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), t); + + manifoldContacts[numContacts].mLocalPointA = projPoint; + manifoldContacts[numContacts].mLocalPointB = vert0; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + + } + } + + if(inside == referencePolygon.mNbVerts) + return; + + + //(2) segment intesection + for (PxU32 iStart = 0, iEnd = PxU32(incidentPolygon.mNbVerts - 1); iStart < incidentPolygon.mNbVerts; iEnd = iStart++) + { + if((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd] ) )//|| (points1In0[i].status == POINT_OUTSIDE && points1In0[incidentIndex].status == POINT_OUTSIDE)) + continue; + + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + const BoolV tempCon =BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + continue; + + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + + if(FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if(FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect + + const FloatV t = FDiv(a1, FSub(a2, a1)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = transform0To1.transform(M33TrnspsMulV3(rot, pBB)); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + if(FAllGrtr(pen, contactDist)) + continue; + + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), pen); + manifoldContacts[numContacts].mLocalPointA = pB; + manifoldContacts[numContacts].mLocalPointB = pA; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + } + } + } + + } + } + + + bool generateFullContactManifold(PolygonalData& polyData0, PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, const Vec3VArg normal, const Vec3VArg closestA, const Vec3VArg closestB, const FloatVArg marginA, const FloatVArg marginB, const bool doOverlapTest, + Cm::RenderOutput* renderOutput, const Ps::aos::FloatVArg toleranceScale) + { + + const PsMatTransformV transform1To0V = map0->transform.transformInv(map1->transform); + const PsMatTransformV transform0To1V = map1->transform.transformInv(map0->transform); + + + if(doOverlapTest) + { + //if gjk fail, SAT based yes/no test + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + Vec3V minNormal = V3Zero(); + + PxU32 feature0; + //in the local space of polyData0, minNormal is in polyData0 space + if(!testFaceNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + //in the local space of polyData1, if minOverlap is overwrite inside this function, minNormal will be in polyData1 space + if(!testFaceNormal(polyData1, polyData0, map1, map0, transform1To0V, transform0To1V, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + + bool doEdgeTest = false; + +EdgeTest: + if(doEdgeTest) + { + + if(!testEdgeNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + if(status != EDGE) + return true; + } + + + if(status == POLYDATA0) + { + //minNormal is in the local space of polydata0 + + const HullPolygonData& referencePolygon = polyData0.mPolygons[feature0]; + const Vec3V n = transform0To1V.rotate(minNormal); + const HullPolygonData& incidentPolygon = polyData1.mPolygons[getPolygonIndex(polyData1, map1, n)]; + + generatedContacts(polyData0, polyData1, referencePolygon, incidentPolygon, map0, map1, transform0To1V, manifoldContacts, numContacts, contactDist, renderOutput); + + if (numContacts > 0) + { + const Vec3V nn = V3Neg(n); + //flip the contacts + for(PxU32 i=0; i<numContacts; ++i) + { + const Vec3V localPointB = manifoldContacts[i].mLocalPointB; + manifoldContacts[i].mLocalPointB = manifoldContacts[i].mLocalPointA; + manifoldContacts[i].mLocalPointA = localPointB; + manifoldContacts[i].mLocalNormalPen = V4SetW(nn, V4GetW(manifoldContacts[i].mLocalNormalPen)); + } + } + } + else if(status == POLYDATA1) + { + //minNormal is in the local space of polydata1 + const HullPolygonData& referencePolygon = polyData1.mPolygons[feature1]; + const HullPolygonData& incidentPolygon = polyData0.mPolygons[getPolygonIndex(polyData0, map0, transform1To0V.rotate(minNormal))]; + + //reference face is polyData1 + generatedContacts(polyData1, polyData0, referencePolygon, incidentPolygon, map1, map0, transform1To0V, manifoldContacts, numContacts, contactDist, renderOutput); + + } + else //if(status == EDGE0) + { + //minNormal is in the local space of polydata0 + + const HullPolygonData& incidentPolygon = polyData0.mPolygons[getPolygonIndex(polyData0, map0, V3Neg(minNormal))]; + const HullPolygonData& referencePolygon = polyData1.mPolygons[getPolygonIndex(polyData1, map1, transform0To1V.rotate(minNormal))]; + generatedContacts(polyData1, polyData0, referencePolygon, incidentPolygon, map1, map0, transform1To0V, manifoldContacts, numContacts, contactDist, renderOutput); + } + + if(numContacts == 0 && !doEdgeTest) + { + doEdgeTest = true; + goto EdgeTest; + } + + } + else + { + const FloatV eps = FLoad(PCM_WITNESS_POINT_EPS); + const FloatV lowerEps = FMul(toleranceScale, FLoad(PCM_WITNESS_POINT_ABSOLUTE_EPS)); + const FloatV toleranceA = FMax(FMul(marginA, eps), lowerEps); + + const FloatV toleranceB = FMax(FMul(marginB, eps), lowerEps); + + //use gjk normal to get the faceIndex(status == GJK_CONTACT) + const PxU32 faceIndex1 = getWitnessPolygonIndex(polyData1, map1, V3Neg(normal), closestB, toleranceA); + const PxU32 faceIndex0 = getWitnessPolygonIndex(polyData0, map0, transform0To1V.rotateInv(normal), transform0To1V.transformInv(closestA), toleranceB); + + const HullPolygonData& referencePolygon = polyData1.mPolygons[faceIndex1]; + const HullPolygonData& incidentPolygon = polyData0.mPolygons[faceIndex0]; + generatedContacts(polyData1, polyData0, referencePolygon, incidentPolygon, map1, map0, transform1To0V, manifoldContacts, numContacts, contactDist, renderOutput); + + } + + return true; + } + + + bool computeMTD(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal) + { + + using namespace Ps::aos; + + const PsMatTransformV transform1To0V = map0->transform.transformInv(map1->transform); + const PsMatTransformV transform0To1V = map1->transform.transformInv(map0->transform); + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + Vec3V minNormal = V3Zero(); + const FloatV contactDist = FZero(); + + PxU32 feature0; + //in the local space of polyData0, minNormal is in polyData0 space + if(!testFaceNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + //in the local space of polyData1, if minOverlap is overwrite inside this function, minNormal will be in polyData1 space + if(!testFaceNormal(polyData1, polyData0, map1, map0, transform1To0V, transform0To1V, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + if(!testEdgeNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + penDepth = minOverlap; + if(status == POLYDATA1) + { + //minNormal is in the local space of polydata1 + normal = map1->transform.rotate(minNormal); + } + else + { + PX_ASSERT(status == POLYDATA0 || status == EDGE); + //ML: status == POLYDATA0 or status == EDGE, minNormal is in the local space of polydata0 + normal = V3Neg(map0->transform.rotate(minNormal)); + } + + return true; + } + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenSphereCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenSphereCapsule.cpp new file mode 100644 index 00000000..2ed5aef0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenSphereCapsule.cpp @@ -0,0 +1,441 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "CmRenderOutput.h" +#include "GuPCMContactGenUtil.h" +#include "PsVecMath.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + + + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + +namespace physx +{ + +namespace Gu +{ + + static bool testSATCapsulePoly(const CapsuleV& capsule, const PolygonalData& polyData, SupportLocal* map, const FloatVArg contactDist, FloatV& minOverlap, Vec3V& separatingAxis) + { + using namespace Ps::aos; + FloatV _minOverlap = FMax();//minOverlap; + FloatV min0, max0; + FloatV min1, max1; + Vec3V tempAxis = V3UnitY(); + const FloatV eps = FEps(); + + //ML: project capsule to polydata axis + if(!testPolyDataAxis(capsule, polyData, map, contactDist, _minOverlap, tempAxis)) + return false; + + const Vec3V capsuleAxis = V3Sub(capsule.p1, capsule.p0); + + for(PxU32 i=0;i<polyData.mNbPolygons;i++) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + const PxU8* inds1 = polyData.mPolygonVertexRefs + polygon.mVRef8; + + for(PxU32 lStart = 0, lEnd =PxU32(polygon.mNbVerts-1); lStart<polygon.mNbVerts; lEnd = lStart++) + { + + //in the vertex space + const Vec3V p10 = V3LoadU_SafeReadW(polyData.mVerts[inds1[lStart]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + const Vec3V p11 = V3LoadU_SafeReadW(polyData.mVerts[inds1[lEnd]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const Vec3V vertexSpaceV = V3Sub(p11, p10); + + //transform v to shape space + const Vec3V shapeSpaceV = M33TrnspsMulV3(map->shape2Vertex, vertexSpaceV); + + const Vec3V dir = V3Cross(capsuleAxis, shapeSpaceV); + const FloatV lenSq = V3Dot(dir, dir); + if(FAllGrtr(eps, lenSq)) + continue; + const Vec3V normal = V3ScaleInv(dir, FSqrt(lenSq)); + + map->doSupport(normal, min0, max0); + + const FloatV tempMin = V3Dot(capsule.p0, normal); + const FloatV tempMax = V3Dot(capsule.p1, normal); + min1 = FMin(tempMin, tempMax); + max1 = FMax(tempMin, tempMax); + + min1 = FSub(min1, capsule.radius); + max1 = FAdd(max1, capsule.radius); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + tempAxis = normal; + } + } + } + + + separatingAxis = tempAxis; + minOverlap = _minOverlap; + + return true; + + } + + + void generatedCapsuleBoxFaceContacts(const CapsuleV& capsule, PolygonalData& polyData, const HullPolygonData& referencePolygon, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, const Vec3VArg normal) + { + + const FloatV radius = FAdd(capsule.radius, contactDist); + + //calculate the intersect point of ray to plane + const Vec3V planeNormal = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + const PxU8* inds = polyData.mPolygonVertexRefs + referencePolygon.mVRef8; + const Vec3V a = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[0]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const FloatV denom0 = V3Dot(planeNormal, V3Sub(capsule.p0, a)); + const FloatV denom1 = V3Dot(planeNormal, V3Sub(capsule.p1, a)); + const FloatV projPlaneN = V3Dot(planeNormal, normal); + const FloatV numer = FSel(FIsGrtr(projPlaneN, FZero()), FRecip(projPlaneN), FZero()); + const FloatV t0 = FMul(denom0, numer); + const FloatV t1 = FMul(denom1, numer); + + const BoolV con0 = FIsGrtrOrEq(radius, t0); + const BoolV con1 = FIsGrtrOrEq(radius, t1); + if(BAllEqTTTT(BOr(con0, con1))) + { + + const Mat33V rot = findRotationMatrixFromZAxis(planeNormal); + Vec3V* points0In0 = reinterpret_cast<Vec3V*>(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + map->populateVerts(inds, referencePolygon.mNbVerts, polyData.mVerts, points0In0); + + Vec3V rPolygonMin = V3Splat(FMax()); + Vec3V rPolygonMax = V3Neg(rPolygonMin); + for(PxU32 i=0; i<referencePolygon.mNbVerts; ++i) + { + points0In0[i] = M33MulV3(rot, points0In0[i]); + rPolygonMin = V3Min(rPolygonMin, points0In0[i]); + rPolygonMax = V3Max(rPolygonMax, points0In0[i]); + } + + if(BAllEqTTTT(con0)) + { + //add contact + const Vec3V proj = V3NegScaleSub(normal, t0, capsule.p0);//V3ScaleAdd(t0, normal, capsule.p0); + //transform proj0 to 2D + const Vec3V point = M33MulV3(rot, proj); + + if(contains(points0In0, referencePolygon.mNbVerts, point, rPolygonMin, rPolygonMax)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p0); + manifoldContacts[numContacts].mLocalPointB = proj; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), t0); + } + + } + + if(BAllEqTTTT(con1)) + { + const Vec3V proj = V3NegScaleSub(normal, t1, capsule.p1);//V3ScaleAdd(t1, normal, capsule.p1); + //transform proj0 to 2D + const Vec3V point = M33MulV3(rot, proj); + + if(contains(points0In0, referencePolygon.mNbVerts, point, rPolygonMin, rPolygonMax)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p1); + manifoldContacts[numContacts].mLocalPointB = proj; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal),t1); + } + } + } + + } + + + static void generatedFaceContacts(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, const Vec3VArg normal) + { + using namespace Ps::aos; + + const FloatV zero = FZero(); + FloatV tEnter=zero, tExit=zero; + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + const Vec3V dir = V3Neg(normal); + const Vec3V vertexSpaceDir = M33MulV3(map->shape2Vertex, dir); + const Vec3V p0 = M33MulV3(map->shape2Vertex, capsule.p0); + + if(intersectSegmentPolyhedron(p0, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p0); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p0); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + const Vec3V p1 = M33MulV3(map->shape2Vertex, capsule.p1); + if(intersectSegmentPolyhedron(p1, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p1); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p1); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + } + + + static void generateEE(const Vec3VArg p, const Vec3VArg q, const Vec3VArg normal, const Vec3VArg a, const Vec3VArg b, const PsMatTransformV& aToB, + PersistentContact* manifoldContacts, PxU32& numContacts, const FloatVArg inflatedRadius) + { + + const FloatV zero = FZero(); + const FloatV expandedRatio = FLoad(0.005f); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + + //calculate the intersect point in the segment pq with plane n(x - a). + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //ML: ab, localPointA and normal is in the same plane, so that we can do 2D segment segment intersection + //calculate a normal perpendicular to ray localPointA + normal*t, then do 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + const FloatV tValue = FDiv(nom, denom); + + const FloatV max = FAdd(FOne(), expandedRatio); + const FloatV min = FSub(zero, expandedRatio); + if(FAllGrtr(tValue, max) || FAllGrtr(min, tValue)) + return; + + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV signedDist = V3Dot(v, normal); + if(FAllGrtrOrEq(inflatedRadius, signedDist)) + { + const Vec3V localPointB = V3Sub(localPointA, v); + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(localPointA); + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + + } + } + + + void generatedContactsEEContacts(const CapsuleV& capsule, PolygonalData& polyData, const HullPolygonData& referencePolygon, SupportLocal* map, const PsMatTransformV& aToB, + PersistentContact* manifoldContacts, PxU32& numContacts, const FloatVArg contactDist, const Vec3VArg contactNormal) + { + + const PxU8* inds = polyData.mPolygonVertexRefs + referencePolygon.mVRef8; + + Vec3V* points0In0 = reinterpret_cast<Vec3V*>(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + + + //Transform all the verts from vertex space to shape space + map->populateVerts(inds, referencePolygon.mNbVerts, polyData.mVerts, points0In0); + + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + generateEE(capsule.p0, capsule.p1, contactNormal,points0In0[rStart], points0In0[rEnd], aToB, manifoldContacts, numContacts, inflatedRadius); + + } + } + + + bool generateCapsuleBoxFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const Vec3VArg closest, const FloatVArg margin, const bool doOverlapTest, const FloatVArg toleranceScale) + { + + const PxU32 originalContacts = numContacts; + + const Gu::HullPolygonData* referencePolygon = NULL; + + if(doOverlapTest) + { + Ps::aos::FloatV minOverlap; + //overwrite the normal + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, normal)) + return false; + + referencePolygon = &polyData.mPolygons[getPolygonIndex(polyData, map, V3Neg(normal))]; + } + else + { + const FloatV eps = FLoad(PCM_WITNESS_POINT_EPS); + const FloatV lowerEps = FMul(toleranceScale, FLoad(PCM_WITNESS_POINT_ABSOLUTE_EPS)); + const FloatV tolerance = FMax(FMul(margin, eps), lowerEps); + + referencePolygon = &polyData.mPolygons[getWitnessPolygonIndex(polyData, map, V3Neg(normal), closest, tolerance)]; + + } + + generatedCapsuleBoxFaceContacts(capsule, polyData, *referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, normal); + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + generatedContactsEEContacts(capsule, polyData, *referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, normal); + } + + return true; + } + + //capsule is in the local space of polyData + bool generateFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const Vec3VArg closest, const FloatVArg margin, const bool doOverlapTest, const Ps::aos::FloatVArg toleranceScale) + { + const PxU32 originalContacts = numContacts; + + Vec3V tNormal = normal; + + if(doOverlapTest) + { + FloatV minOverlap; + + //overwrite the normal with the sperating axis + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, tNormal)) + return false; + + generatedFaceContacts(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getPolygonIndex(polyData, map, V3Neg(tNormal))]; + generatedContactsEEContacts(capsule, polyData,referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + } + } + else + { + generatedFaceContacts(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + const FloatV eps = FLoad(PCM_WITNESS_POINT_EPS); + const FloatV lowerEps = FMul(toleranceScale, FLoad(PCM_WITNESS_POINT_ABSOLUTE_EPS)); + const FloatV toleranceA = FMax(FMul(margin, eps), lowerEps); + + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getWitnessPolygonIndex(polyData, map, V3Neg(tNormal), closest, toleranceA)]; + generatedContactsEEContacts(capsule, polyData,referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + } + } + + normal = tNormal; + + return true; + } + + //sphere is in the local space of polyData, we treate sphere as capsule + bool generateSphereFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const bool doOverlapTest) + { + + if(doOverlapTest) + { + FloatV minOverlap; + //overwrite the normal with the sperating axis + if(!testPolyDataAxis(capsule, polyData, map, contactDist, minOverlap, normal)) + return false; + } + + const FloatV zero = FZero(); + FloatV tEnter=zero, tExit=zero; + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + const Vec3V dir = V3Neg(normal); + const Vec3V vertexSpaceDir = M33MulV3(map->shape2Vertex, dir); + const Vec3V p0 = M33MulV3(map->shape2Vertex, capsule.p0); + if(intersectSegmentPolyhedron(p0, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + //ML: for sphere, the contact point A is always zeroV in its local space + manifoldContacts[numContacts].mLocalPointA = V3Zero(); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p0); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + return true; + + } + + + //capsule is in the shape space of polyData + bool computeMTD(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, FloatV& penDepth, Vec3V& normal) + { + const FloatV contactDist = FZero(); + Vec3V separatingAxis; + FloatV minOverlap; + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, separatingAxis)) + return false; + + //transform normal in to the worl space + normal = map->transform.rotate(separatingAxis); + penDepth = minOverlap; + + return true; + } + + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenUtil.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenUtil.h new file mode 100644 index 00000000..4ce69a58 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactGenUtil.h @@ -0,0 +1,428 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_GEN_UTIL_H +#define GU_PCM_CONTACT_GEN_UTIL_H + +#include "PsVecMath.h" +#include "CmPhysXCommon.h" +#include "GuShapeConvex.h" +#include "GuVecCapsule.h" +#include "GuConvexSupportTable.h" + +#define PCM_WITNESS_POINT_EPS 1e-1f +#define PCM_WITNESS_POINT_ABSOLUTE_EPS 1e-5f + +namespace physx +{ + +namespace Gu +{ + + enum FeatureStatus + { + POLYDATA0, + POLYDATA1, + EDGE + }; + + + PX_FORCE_INLINE bool contains(Ps::aos::Vec3V* verts, const PxU32 numVerts, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg min, const Ps::aos::Vec3VArg max) + { + using namespace Ps::aos; + + const BoolV tempCon = BOr(V3IsGrtr(min, p), V3IsGrtr(p, max)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if (BAllEqTTTT(con)) + return false; + + const FloatV tx = V3GetX(p); + const FloatV ty = V3GetY(p); + + const FloatV eps = FEps(); + const FloatV zero = FZero(); + PxU32 intersectionPoints = 0; + + PxU32 i = 0, j = numVerts - 1; + + for (; i < numVerts; j = i++) + { + const FloatV jy = V3GetY(verts[j]); + const FloatV iy = V3GetY(verts[i]); + + const FloatV jx = V3GetX(verts[j]); + const FloatV ix = V3GetX(verts[i]); + + //if p is one of the end point, we will return intersect + const BoolV con0 = BAnd(FIsEq(tx, jx), FIsEq(ty, jy)); + const BoolV con1 = BAnd(FIsEq(tx, ix), FIsEq(ty, iy)); + + if (BAllEqTTTT(BOr(con0, con1))) + { + return true; + } + + //(verts[i].y > test.y) != (points[j].y > test.y) + const PxU32 yflag0 = FAllGrtr(jy, ty); + const PxU32 yflag1 = FAllGrtr(iy, ty); + + //ML: the only case the ray will intersect this segment is when the p's y is in between two segments y + if (yflag0 != yflag1) + { + + //ML: choose ray, which start at p and every points in the ray will have the same y component + //t1 = (yp - yj)/(yi - yj) + //qx = xj + t1*(xi-xj) + //t = qx - xp > 0 for the ray and segment intersection happen + const FloatV jix = FSub(ix, jx); + const FloatV jiy = FSub(iy, jy); + //const FloatV jtx = FSub(tx, jy); + const FloatV jty = FSub(ty, jy); + const FloatV part1 = FMul(jty, jix); + //const FloatV part2 = FMul(jx, jiy); + //const FloatV part3 = FMul(V3Sub(tx, eps), jiy); + const FloatV part2 = FMul(FAdd(jx, eps), jiy); + const FloatV part3 = FMul(tx, jiy); + + const BoolV comp = FIsGrtr(jiy, zero); + const FloatV tmp = FAdd(part1, part2); + const FloatV comp1 = FSel(comp, tmp, part3); + const FloatV comp2 = FSel(comp, part3, tmp); + + + if (FAllGrtrOrEq(comp1, comp2)) + { + if (intersectionPoints == 1) + { + return false; + } + intersectionPoints++; + } + } + } + return intersectionPoints> 0; + } + + + PX_FORCE_INLINE bool boxContainsInXY(const Ps::aos::FloatVArg x, const Ps::aos::FloatVArg y, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* verts, const Ps::aos::Vec3VArg min, const Ps::aos::Vec3VArg max) + { + using namespace Ps::aos; + + const BoolV tempCon = BOr(V3IsGrtr(min, p), V3IsGrtr(p, max)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + return false; + + const FloatV zero = FZero(); + FloatV PreviousX = V3GetX(verts[3]); + FloatV PreviousY = V3GetY(verts[3]); + + + // Loop through quad vertices + for(PxI32 i=0; i<4; i++) + { + const FloatV CurrentX = V3GetX(verts[i]); + const FloatV CurrentY = V3GetY(verts[i]); + + // |CurrentX - PreviousX x - PreviousX| + // |CurrentY - PreviousY y - PreviousY| + // => similar to backface culling, check each one of the 4 triangles are consistent, in which case + // the point is within the parallelogram. + const FloatV v00 = FSub(CurrentX, PreviousX); + const FloatV v01 = FSub(y, PreviousY); + const FloatV v10 = FSub(CurrentY, PreviousY); + const FloatV v11 = FSub(x, PreviousX); + const FloatV temp0 = FMul(v00, v01); + const FloatV temp1 = FMul(v10, v11); + if(FAllGrtrOrEq(FSub(temp0, temp1), zero)) + return false; + + PreviousX = CurrentX; + PreviousY = CurrentY; + } + + return true; + + } + + + PX_FORCE_INLINE Ps::aos::FloatV signed2DTriArea(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c) + { + using namespace Ps::aos; + const Vec3V ca = V3Sub(a, c); + const Vec3V cb = V3Sub(b, c); + + const FloatV t0 = FMul(V3GetX(ca), V3GetY(cb)); + const FloatV t1 = FMul(V3GetY(ca), V3GetX(cb)); + + return FSub(t0, t1); + } + + + PX_FORCE_INLINE PxI32 getPolygonIndex(const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::Vec3VArg normal) + { + using namespace Ps::aos; + + //normal is in shape space, need to transform the vertex space + const Vec3V n = M33TrnspsMulV3(map->vertex2Shape, normal); + const Vec3V nnormal = V3Neg(n); + const Vec3V planeN_ = V3LoadU_SafeReadW(polyData.mPolygons[0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + FloatV minProj = V3Dot(n, planeN_); + + const FloatV zero = FZero(); + PxI32 closestFaceIndex = 0; + + for(PxU32 i=1; i< polyData.mNbPolygons; ++i) + { + const Vec3V planeN = V3LoadU_SafeReadW(polyData.mPolygons[i].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const FloatV proj = V3Dot(n, planeN); + if(FAllGrtr(minProj, proj)) + { + minProj = proj; + closestFaceIndex = PxI32(i); + } + } + + const PxU32 numEdges = polyData.mNbEdges; + const PxU8* const edgeToFace = polyData.mFacesByEdges; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + FloatV maxDpSq = FMul(minProj, minProj); + + for(PxU32 i=0; i < numEdges; ++i)//, inc = VecI32V_Add(inc, vOne)) + { + const PxU32 index = i*2; + const PxU8 f0 = edgeToFace[index]; + const PxU8 f1 = edgeToFace[index+1]; + + const Vec3V planeNormal0 = V3LoadU_SafeReadW(polyData.mPolygons[f0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V planeNormal1 = V3LoadU_SafeReadW(polyData.mPolygons[f1].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + // unnormalized edge normal + const Vec3V edgeNormal = V3Add(planeNormal0, planeNormal1);//polys[f0].mPlane.n + polys[f1].mPlane.n; + const FloatV enMagSq = V3Dot(edgeNormal, edgeNormal);//edgeNormal.magnitudeSquared(); + //Test normal of current edge - squared test is valid if dp and maxDp both >= 0 + const FloatV dp = V3Dot(edgeNormal, nnormal);//edgeNormal.dot(normal); + const FloatV sqDp = FMul(dp, dp); + + const BoolV con0 = FIsGrtrOrEq(dp, zero); + const BoolV con1 = FIsGrtr(sqDp, FMul(maxDpSq, enMagSq)); + const BoolV con = BAnd(con0, con1); + if(BAllEqTTTT(con)) + { + maxDpSq = FDiv(sqDp, enMagSq); + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const PxU8* FBE = edgeToFace; + + const PxU32 index = closestEdge*2; + const PxU32 f0 = FBE[index]; + const PxU32 f1 = FBE[index+1]; + + const Vec3V planeNormal0 = V3LoadU_SafeReadW(polyData.mPolygons[f0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V planeNormal1 = V3LoadU_SafeReadW(polyData.mPolygons[f1].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + const FloatV dp0 = V3Dot(planeNormal0, nnormal); + const FloatV dp1 = V3Dot(planeNormal1, nnormal); + if(FAllGrtr(dp0, dp1)) + { + closestFaceIndex = PxI32(f0); + } + else + { + closestFaceIndex = PxI32(f1); + } + } + + return closestFaceIndex; + + } + + PX_FORCE_INLINE PxU32 getWitnessPolygonIndex(const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::Vec3VArg normal, + const Ps::aos::Vec3VArg closest, const Ps::aos::FloatVArg tolerance) + { + using namespace Ps::aos; + + //transform the closest p to vertex space + const Vec3V p = M33MulV3(map->shape2Vertex, closest); + PxU32 closestFaceIndex = 0; + Vec4V plane = V4LoadU(&polyData.mPolygons[0].mPlane.n.x); + Vec3V n = Vec3V_From_Vec4V(plane); + Vec3V bestN = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, n)); + FloatV bestProj = V3Dot(bestN, normal); + FloatV d = V4GetW(plane); + FloatV pd = FAbs(FAdd(d, V3Dot(n, p))); + FloatV minDist = pd; + + for(PxU32 i=1; i< polyData.mNbPolygons; ++i) + { + plane = V4LoadU(&polyData.mPolygons[i].mPlane.n.x); + n = Vec3V_From_Vec4V(plane); + d = V4GetW(plane); + pd = FAbs(FAdd(d, V3Dot(n, p))); + + //if the difference between the minimum distance and the distance of p to plane i is within tolerance, we use the normal to chose the best plane + if (FAllGrtrOrEq(tolerance, FSub(pd, minDist))) + { + //rotate the plane normal to shape space + n = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, n)); + FloatV proj = V3Dot(n, normal); + + if(FAllGrtr(bestProj, proj)) + { + minDist= pd; + closestFaceIndex = i; + bestProj = proj; + } + } + } + + return closestFaceIndex; + } + + + //ML: this function is shared by the sphere/capsule vs convex hulls full contact gen, capsule in the local space of polyData + PX_FORCE_INLINE bool testPolyDataAxis(const Gu::CapsuleV& capsule, const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::FloatVArg contactDist, Ps::aos::FloatV& minOverlap, Ps::aos::Vec3V& separatingAxis) + { + using namespace Ps::aos; + FloatV _minOverlap = FMax();//minOverlap; + FloatV min0, max0; + FloatV min1, max1; + Vec3V tempAxis = V3UnitY(); + + //capsule in the local space of polyData + for(PxU32 i=0; i<polyData.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + + const Vec3V minVert = V3LoadU_SafeReadW(polyData.mVerts[polygon.mMinIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + const FloatV planeDist = FLoad(polygon.mPlane.d); + const Vec3V vertexSpacePlaneNormal = V3LoadU_SafeReadW(polygon.mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + //transform plane n to shape space + const Vec3V shapeSpacePlaneNormal = M33TrnspsMulV3(map->shape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRecip(V3Length(shapeSpacePlaneNormal)); + //normalize shape space normal + const Vec3V planeN = V3Scale(shapeSpacePlaneNormal, magnitude); + //ML::use this to avoid LHS + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + + const FloatV tempMin = V3Dot(capsule.p0, planeN); + const FloatV tempMax = V3Dot(capsule.p1, planeN); + min1 = FMin(tempMin, tempMax); + max1 = FMax(tempMin, tempMax); + + min1 = FSub(min1, capsule.radius); + max1 = FAdd(max1, capsule.radius); + + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + tempAxis = planeN; + } + } + + separatingAxis = tempAxis; + minOverlap = _minOverlap; + + return true; + + } + + //ML: this function is shared by sphere/capsule. Point a and direction d need to be in the local space of polyData + PX_FORCE_INLINE bool intersectSegmentPolyhedron(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg dir, const PolygonalData& polyData, Ps::aos::FloatV& tEnter, Ps::aos::FloatV& tExit) + { + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + const FloatV eps = FLoad(1e-7f); + FloatV tFirst = zero; + FloatV tLast= one; + + for(PxU32 k=0; k<polyData.mNbPolygons; ++k) + { + const Gu::HullPolygonData& data = polyData.mPolygons[k]; + const Vec3V n = V3LoadU_SafeReadW(data.mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + FloatV d = FLoad(data.mPlane.d); + + const FloatV denominator = V3Dot(n, dir); + const FloatV distToPlane = FAdd(V3Dot(n, a), d); + + if(FAllGrtr(eps, FAbs(denominator))) + { + if(FAllGrtr(distToPlane, zero)) + return false; + } + else + { + FloatV tTemp = FNeg(FDiv(distToPlane, denominator)); + + //ML: denominator < 0 means the ray is entering halfspace; denominator > 0 means the ray is exiting halfspace + const BoolV con = FIsGrtr(zero, denominator); + const BoolV con0= FIsGrtr(tTemp, tFirst); + const BoolV con1 = FIsGrtr(tLast, tTemp); + + tFirst = FSel(BAnd(con, con0), tTemp, tFirst); + tLast = FSel(BAndNot(con1, con), tTemp, tLast); + } + + if(FAllGrtr(tFirst, tLast)) + return false; + } + + //calculate the intersect p in the local space + tEnter = tFirst; + tExit = tLast; + + return true; + } + +}//Gu +}//physx + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactMeshCallback.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactMeshCallback.h new file mode 100644 index 00000000..9917b429 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactMeshCallback.h @@ -0,0 +1,203 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_MESH_CALLBACK_H +#define GU_PCM_CONTACT_MESH_CALLBACK_H + +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuTriangleCache.h" +#include "GuConvexEdgeFlags.h" + +namespace physx +{ + +namespace Gu +{ + +template <typename Derived> +struct PCMMeshContactGenerationCallback : MeshHitCallback<PxRaycastHit> +{ +public: + const Cm::FastVertex2ShapeScaling& mMeshScaling; + const PxU8* PX_RESTRICT mExtraTrigData; + bool mIdtMeshScale; + static const PxU32 CacheSize = 16; + Gu::TriangleCache<CacheSize> mCache; + + PCMMeshContactGenerationCallback(const Cm::FastVertex2ShapeScaling& meshScaling, const PxU8* extraTrigData, bool idtMeshScale) + : MeshHitCallback<PxRaycastHit>(CallbackMode::eMULTIPLE), + mMeshScaling(meshScaling), mExtraTrigData(extraTrigData), mIdtMeshScale(idtMeshScale) + { + } + + void flushCache() + { + if (!mCache.isEmpty()) + { + (static_cast<Derived*>(this))->template processTriangleCache< CacheSize >(mCache); + mCache.reset(); + } + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if (!(static_cast<Derived*>(this))->doTest(v0, v1, v2)) + return true; + + PxVec3 v[3]; + if(mIdtMeshScale) + { + v[0] = v0; + v[1] = v1; + v[2] = v2; + } + else + { + const PxI32 winding = mMeshScaling.flipsNormal() ? 1 : 0; + v[0] = mMeshScaling * v0; + v[1 + winding] = mMeshScaling * v1; + v[2 - winding] = mMeshScaling * v2; + } + + const PxU32 triangleIndex = hit.faceIndex; + const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); + + if (mCache.isFull()) + { + (static_cast<Derived*>(this))->template processTriangleCache< CacheSize >(mCache); + mCache.reset(); + } + mCache.addTriangle(v, vinds, triangleIndex, extraData); + + return true; + } + +protected: + PCMMeshContactGenerationCallback& operator=(const PCMMeshContactGenerationCallback&); +}; + +template <typename Derived> +struct PCMHeightfieldContactGenerationCallback : Gu::EntityReport<PxU32> +{ +public: + const Gu::HeightFieldUtil& mHfUtil; + const PxTransform& mHeightfieldTransform; + + PCMHeightfieldContactGenerationCallback(const Gu::HeightFieldUtil& hfUtil, const PxTransform& heightfieldTransform) : + mHfUtil(hfUtil), mHeightfieldTransform(heightfieldTransform) + { + + } + + // PT: TODO: refactor/unify with similar code in other places + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + const PxU32 CacheSize = 16; + Gu::TriangleCache<CacheSize> cache; + + const PxU32 nbPasses = (nb+(CacheSize-1))/CacheSize; + PxU32 nbTrigs = nb; + PxU32* inds0 = indices; + + const PxU8 nextInd[] = {2,0,1}; + + for(PxU32 i = 0; i < nbPasses; ++i) + { + cache.mNumTriangles = 0; + PxU32 trigCount = PxMin(nbTrigs, CacheSize); + nbTrigs -= trigCount; + while(trigCount--) + { + PxU32 triangleIndex = *(inds0++); + PxU32 vertIndices[3]; + + PxTriangle currentTriangle; // in world space + + PxU32 adjInds[3]; + mHfUtil.getTriangle(mHeightfieldTransform, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + PxU32 inds[3]; + mHfUtil.getTriangle(mHeightfieldTransform, adjTri, inds, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PX_ASSERT(inds[0] == vertIndices[a] || inds[1] == vertIndices[a] || inds[2] == vertIndices[a]); + PX_ASSERT(inds[0] == vertIndices[(a+1)%3] || inds[1] == vertIndices[(a+1)%3] || inds[2] == vertIndices[(a+1)%3]); + + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.997f) + { + triFlags |= (1 << (a+3)); + } + } + } + else + triFlags |= (1 << (a+3)); + } + + cache.addTriangle(currentTriangle.verts, vertIndices, triangleIndex, triFlags); + } + PX_ASSERT(cache.mNumTriangles <= 16); + + (static_cast<Derived*>(this))->template processTriangleCache< CacheSize >(cache); + } + return true; + } +protected: + PCMHeightfieldContactGenerationCallback& operator=(const PCMHeightfieldContactGenerationCallback&); +}; + +}//Gu +}//physx + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneBox.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneBox.cpp new file mode 100644 index 00000000..68b30f56 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneBox.cpp @@ -0,0 +1,220 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecBox.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + +#include "GuPersistentContactManifold.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>(); + + const PsTransformV transf0 = loadTransformA(transform1);//box transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + + //box to plane + const PsTransformV curTransf(transf1.transformInv(transf0)); + + //in world space + const Vec3V negPlaneNormal = V3Normalize(V3Neg(QuatGetBasisVector0(transf1.q))); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const FloatV boxMargin = CalculatePCMBoxMargin(boxExtents); + const FloatV projectBreakingThreshold = FMul(boxMargin, FLoad(0.2f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(curTransf, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + //PX_UNUSED(bLostContacts); + //if(bLostContacts || manifold.invalidate_BoxConvex(curTransf, boxMargin)) + if(bLostContacts || manifold.invalidate_PrimitivesPlane(curTransf, boxMargin, FLoad(0.2f))) + { + //ML:localNormal is the local space of plane normal, however, because shape1 is box and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + manifold.mNumContacts = 0; + manifold.setRelativeTransform(curTransf); + + const PsMatTransformV aToB(curTransf); + const FloatV bx = V3GetX(boxExtents); + const FloatV by = V3GetY(boxExtents); + const FloatV bz = V3GetZ(boxExtents); + + const FloatV nbx = FNeg(bx); + const FloatV nby = FNeg(by); + const FloatV nbz = FNeg(bz); + + const Vec3V temp0 = V3Scale(aToB.getCol0(), bx); + const Vec3V temp1 = V3Scale(aToB.getCol1(), by); + const Vec3V temp2 = V3Scale(aToB.getCol2(), bz); + + const Vec3V ntemp2 = V3Neg(temp2); + + const FloatV px = V3GetX(aToB.p); + + //box's points in the local space of plane + const Vec3V temp01 = V3Add(temp0, temp1);//(x, y) + const Vec3V temp02 = V3Sub(temp0, temp1);//(x, -y) + + const FloatV s0 = V3GetX(V3Add(temp2, temp01));//(x, y, z) + const FloatV s1 = V3GetX(V3Add(ntemp2, temp01));//(x, y, -z) + const FloatV s2 = V3GetX(V3Add(temp2, temp02));//(x, -y, z) + const FloatV s3 = V3GetX(V3Add(ntemp2, temp02));//(x, -y, -z) + const FloatV s4 = V3GetX(V3Sub(temp2, temp02));//(-x, y, z) + const FloatV s5 = V3GetX(V3Sub(ntemp2, temp02));//(-x, y, -z) + const FloatV s6 = V3GetX(V3Sub(temp2, temp01));//(-x, -y, z) + const FloatV s7 = V3GetX(V3Sub(ntemp2, temp01));//(-x, -y, -z) + + const FloatV acceptanceDist = FSub(contactDist, px); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + if(FAllGrtr(acceptanceDist, s0)) + { + const FloatV pen = FAdd(s0, px); + //(x, y, z) + manifoldContacts[numContacts].mLocalPointA = boxExtents;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(boxExtents)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s1)) + { + const FloatV pen = FAdd(s1, px); + //(x, y, -z) + const Vec3V p = V3Merge(bx, by, nbz); + //add to contact stream + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s2)) + { + const FloatV pen = FAdd(s2, px); + //(x, -y, z) + const Vec3V p = V3Merge(bx, nby, bz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s3)) + { + const FloatV pen = FAdd(s3, px); + //(x, -y, -z) + const Vec3V p = V3Merge(bx, nby, nbz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s4)) + { + const FloatV pen = FAdd(s4, px); + //(-x, y, z) + const Vec3V p =V3Merge(nbx, by, bz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s5)) + { + const FloatV pen = FAdd(s5, px); + //(-x, y, -z) + const Vec3V p = V3Merge(nbx, by, nbz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + + if(FAllGrtr(acceptanceDist, s6)) + { + const FloatV pen = FAdd(s6, px); + //(-x, -y, z) + const Vec3V p = V3Merge(nbx, nby, bz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s7)) + { + const FloatV pen = FAdd(s7, px); + //(-x, -y, -z) + const Vec3V p = V3Merge(nbx, nby, nbz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + //reduce contacts + manifold.addBatchManifoldContactsCluster(manifoldContacts, numContacts); + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); + + //manifold.drawManifold(*gRenderOutPut, transf0, transf1); + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneCapsule.cpp new file mode 100644 index 00000000..0a8bd8e8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneCapsule.cpp @@ -0,0 +1,128 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPersistentContactManifold.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule = shape1.get<const PxCapsuleGeometry>(); + + const PsTransformV transf0 = loadTransformA(transform1);//capsule transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + //capsule to plane + const PsTransformV aToB(transf1.transformInv(transf0)); + + //in world space + const Vec3V negPlaneNormal = V3Normalize(V3Neg(QuatGetBasisVector0(transf1.q))); + //ML:localNormal is the local space of plane normal, however, because shape1 is capulse and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const FloatV radius = FLoad(shapeCapsule.radius); + const FloatV halfHeight = FLoad(shapeCapsule.halfHeight); + + //capsule is in the local space of plane(n = (1.f, 0.f, 0.f), d=0.f) + const Vec3V basisVector = QuatGetBasisVector0(aToB.q); + const Vec3V tmp = V3Scale(basisVector, halfHeight); + const Vec3V s = V3Add(aToB.p, tmp); + const Vec3V e = V3Sub(aToB.p, tmp); + + const FloatV inflatedRadius = FAdd(radius, contactDist); + const FloatV replaceBreakingThreshold = FMul(radius, FLoad(0.001f)); + const FloatV projectBreakingThreshold = FMul(radius, FLoad(0.05f)); + const PxU32 initialContacts = manifold.mNumContacts; + + //manifold.refreshContactPoints(curRTrans, projectBreakingThreshold, contactDist); + const FloatV refreshDist = FAdd(contactDist, radius); + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + //PX_UNUSED(bLostContacts); + if(bLostContacts || manifold.invalidate_PrimitivesPlane(aToB, radius, FLoad(0.02f))) + { + manifold.mNumContacts = 0; + manifold.setRelativeTransform(aToB); + //calculate the distance from s to the plane + const FloatV signDist0 = V3GetX(s);//V3Dot(localNormal, s); + if(FAllGrtr(inflatedRadius, signDist0)) + { + const Vec3V localPointA = aToB.transformInv(s); + const Vec3V localPointB = V3NegScaleSub(localNormal, signDist0, s); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist0); + //add to manifold + manifold.addManifoldPoint2(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + } + + const FloatV signDist1 = V3GetX(e);//V3Dot(localNormal, e); + if(FAllGrtr(inflatedRadius, signDist1)) + { + const Vec3V localPointA = aToB.transformInv(e); + const Vec3V localPointB = V3NegScaleSub(localNormal, signDist1, e); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist1); + //add to manifold + manifold.addManifoldPoint2(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + } + + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf0, radius, contactDist); + + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf0, radius, contactDist); + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneConvex.cpp new file mode 100644 index 00000000..611dcfd5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactPlaneConvex.cpp @@ -0,0 +1,159 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecConvexHull.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPersistentContactManifold.h" + + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>(); + + const PsTransformV transf0 = loadTransformA(transform1);//convex transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + //convex to plane + const PsTransformV curTransf(transf1.transformInv(transf0)); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale); + + + //in world space + const Vec3V planeNormal = V3Normalize(QuatGetBasisVector0(transf1.q)); + const Vec3V negPlaneNormal = V3Neg(planeNormal); + + + const FloatV contactDist = FLoad(params.mContactDistance); + + //const FloatV replaceBreakingThreshold = FMul(convexMargin, FLoad(0.001f)); + const FloatV projectBreakingThreshold = FMul(convexMargin, FLoad(0.2f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(curTransf, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + + PX_UNUSED(bLostContacts); + //if(bLostContacts || manifold.invalidate_BoxConvex(curTransf, convexMargin)) + if(bLostContacts || manifold.invalidate_PrimitivesPlane(curTransf, convexMargin, FLoad(0.2f))) + { + const PsMatTransformV aToB(curTransf); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const Mat33V vertex2Shape = ConstructVertex2ShapeMatrix(vScale, vQuat); + + //ML:localNormal is the local space of plane normal, however, because shape1 is box and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + manifold.mNumContacts = 0; + manifold.setRelativeTransform(curTransf); + const PxVec3* PX_RESTRICT verts = hullData->getHullVertices(); + const PxU8 numVerts = hullData->mNbHullVertices; + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + const PsMatTransformV aToBVertexSpace(aToB.p, M33MulM33(aToB.rot, vertex2Shape)); + //brute force each points + for(PxU8 i=0; i<numVerts; ++i) + { + //in the vertex space of convex + const Vec3V pInVertexSpace = V3LoadU(verts[i]); + + //transform p into plane space + const Vec3V pInPlaneSpace = aToBVertexSpace.transform(pInVertexSpace);//V3Add(aToB.p, M33MulV3(temp1, pInVertexSpace)); + + const FloatV signDist = V3GetX(pInPlaneSpace); + + if(FAllGrtr(contactDist, signDist)) + { + //transform p into shape space + const Vec3V pInShapeSpace = M33MulV3(vertex2Shape, pInVertexSpace); + //add to manifold + + manifoldContacts[numContacts].mLocalPointA = pInShapeSpace; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, signDist, pInPlaneSpace); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist); + + if(numContacts >= Gu::ContactBuffer::MAX_CONTACTS) + { + //ML: number of contacts are more than MAX_CONTACTS, we need to force contact reduction + manifold.reduceBatchContacts(manifoldContacts, numContacts); + numContacts = GU_MANIFOLD_CACHE_SIZE; + for(PxU32 j=0; j<GU_MANIFOLD_CACHE_SIZE; ++j) + { + manifoldContacts[j] = manifold.mContactPoints[j]; + } + } + } + } + + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts); + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereBox.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereBox.cpp new file mode 100644 index 00000000..2c0378e6 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereBox.cpp @@ -0,0 +1,157 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKWrapper.h" +#include "GuVecBox.h" +#include "GuVecSphere.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + + + +namespace physx +{ + +namespace Gu +{ +bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + using namespace Ps::aos; + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxBoxGeometry& shapeBox = shape1.get<const PxBoxGeometry>(); + // + + //const PsTransformV transf0(transform0); + const Vec3V sphereOrigin = V3LoadA(&transform0.p.x); + //const PsTransformV transf1(transform1); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV radius = FLoad(shapeSphere.radius); + + const PsTransformV transf1(p1, q1); + + const FloatV cDist = FLoad(params.mContactDistance); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + //translate sphere center into the box space + const Vec3V sphereCenter = transf1.transformInv(sphereOrigin); + + const Vec3V nBoxExtents = V3Neg(boxExtents); + + //const FloatV radSq = FMul(radius, radius); + + const FloatV inflatedSum = FAdd(radius, cDist); + const FloatV sqInflatedSum = FMul(inflatedSum, inflatedSum); + + const Vec3V p = V3Clamp(sphereCenter, nBoxExtents, boxExtents); + const Vec3V v = V3Sub(sphereCenter, p); + const FloatV lengthSq = V3Dot(v, v); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + + if(FAllGrtr(sqInflatedSum, lengthSq))//intersect + { + //check whether the spherCenter is inside the box + const BoolV bInsideBox = V3IsGrtrOrEq(boxExtents, V3Abs(sphereCenter)); + // PT: TODO: ??? revisit this, why do we have both BAllEqTTTT and BAllTrue3? + if(BAllEqTTTT(BAllTrue3(bInsideBox)))//sphere center inside the box + { + //Pick directions and sign + const Vec3V absP = V3Abs(p); + const Vec3V distToSurface = V3Sub(boxExtents, absP);//dist from embedded center to box surface along 3 dimensions. + + const FloatV x = V3GetX(distToSurface); + const FloatV y = V3GetY(distToSurface); + const FloatV z = V3GetZ(distToSurface); + + const Vec3V xV = V3Splat(x); + const Vec3V zV = V3Splat(z); + + //find smallest element of distToSurface + const BoolV con0 = BAllTrue3(V3IsGrtrOrEq(distToSurface, zV)); + const BoolV con1 = BAllTrue3(V3IsGrtrOrEq(distToSurface, xV)); + const Vec3V sign = V3Sign(p); + + const Vec3V tmpX = V3Mul(V3UnitX(), sign); + const Vec3V tmpY = V3Mul(V3UnitY(), sign); + const Vec3V tmpZ = V3Mul(V3UnitZ(), sign); + + const Vec3V locNorm= V3Sel(con0, tmpZ, V3Sel(con1, tmpX, tmpY));////local coords contact normal + const FloatV dist = FNeg(FSel(con0, z, FSel(con1, x, y))); + + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + const Vec3V point = sphereOrigin; + const Vec3V normal = transf1.rotate(locNorm); + const FloatV penetration = FSub(dist, radius); + + + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(penetration, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + //context.mContactBuffer.contact(point, normal, penetration); + } + else + { + //get the closest point from the center to the box surface + const FloatV recipLength = FRsqrt(lengthSq); + const FloatV length = FRecip(recipLength); + const Vec3V locNorm = V3Scale(v, recipLength); + const FloatV penetration = FSub(length, radius); + const Vec3V normal = transf1.rotate(locNorm); + const Vec3V point = transf1.transform(p); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(penetration, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + //context.mContactBuffer.contact(point, normal, penetration); + } + return true; + } + return false; +} + +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereCapsule.cpp new file mode 100644 index 00000000..090c3152 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereCapsule.cpp @@ -0,0 +1,121 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKWrapper.h" +#include "GuVecSphere.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + + +namespace physx +{ + +namespace Gu +{ +PX_FORCE_INLINE Ps::aos::FloatV PxcDistancePointSegmentSquared(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg p, Ps::aos::FloatV& param) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + + const Vec3V ap = V3Sub(p, a); + const Vec3V ab = V3Sub(b, a); + const FloatV nom = V3Dot(ap, ab); + + const FloatV denom = V3Dot(ab, ab); + const FloatV tValue = FClamp(FMul(nom, FDiv(one, denom)), zero, one); + + const FloatV t = FSel(FIsEq(denom, zero), zero, tValue); + const Vec3V v = V3NegScaleSub(ab, t, ap); + param = t; + return V3Dot(v, v); +} + +bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxCapsuleGeometry& shapeCapsule = shape1.get<const PxCapsuleGeometry>(); + + //Sphere in world space + const Vec3V sphereCenter = V3LoadA(&transform0.p.x); + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV cDist = FLoad(params.mContactDistance); + + //const FloatV r0 = FloatV_From_F32(shapeCapsule.radius); + const FloatV halfHeight0 = FLoad(shapeCapsule.halfHeight); + const Vec3V basisVector0 = QuatGetBasisVector0(q1); + const Vec3V tmp0 = V3Scale(basisVector0, halfHeight0); + const Vec3V s = V3Add(p1, tmp0); + const Vec3V e = V3Sub(p1, tmp0); + + + const FloatV radiusSum = FAdd(sphereRadius, capsuleRadius); + const FloatV inflatedSum = FAdd(radiusSum, cDist); + + // Collision detection + FloatV t; + const FloatV squareDist = PxcDistancePointSegmentSquared(s, e, sphereCenter, t); + const FloatV sqInflatedSum = FMul(inflatedSum, inflatedSum); + + if(FAllGrtr(sqInflatedSum, squareDist))//BAllEq(con, bTrue)) + { + const Vec3V p = V3ScaleAdd(V3Sub(e, s), t, s); + const Vec3V dir = V3Sub(sphereCenter, p); + const Vec3V normal = V3NormalizeSafe(dir, V3UnitX()); + const Vec3V point = V3NegScaleSub(normal, sphereRadius, sphereCenter);//transform back to the world space + + const FloatV dist = FSub(FSqrt(squareDist), radiusSum); + //context.mContactBuffer.contact(point, normal, FSub(FSqrt(squareDist), radiusSum)); + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereConvex.cpp new file mode 100644 index 00000000..379b90b1 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereConvex.cpp @@ -0,0 +1,294 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecShrunkConvexHull.h" +#include "GuVecShrunkConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" + +namespace physx +{ +namespace Gu +{ + +static void addToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg worldNormal, const Ps::aos::Vec3VArg worldPoint, const Ps::aos::FloatVArg penDep) +{ + using namespace Ps::aos; + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(worldNormal), reinterpret_cast<PxF32*>(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldPoint), reinterpret_cast<PxF32*>(&contact.point.x)); + FStore(penDep, &contact.separation); + + PX_ASSERT(contact.point.isFinite()); + PX_ASSERT(contact.normal.isFinite()); + PX_ASSERT(PxIsFinite(contact.separation)); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + +} + +static bool fullContactsGenerationSphereConvex(const Gu::CapsuleV& capsule, const Gu::ConvexHullV& convexHull, const Ps::aos::PsTransformV& transf0,const Ps::aos::PsTransformV& transf1, + Gu::PersistentContact* manifoldContacts, Gu::ContactBuffer& contactBuffer, const bool idtScale, Gu::PersistentContactManifold& manifold, + Ps::aos::Vec3VArg normal, const Ps::aos::FloatVArg contactDist, bool doOverlapTest, Cm::RenderOutput* renderOutput) +{ + PX_UNUSED(renderOutput); + using namespace Ps::aos; + Gu::PolygonalData polyData; + getPCMConvexData(convexHull,idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl<ConvexHullV>)]; + SupportLocal* map = (idtScale ? static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullNoScaleV>)(static_cast<const ConvexHullNoScaleV&>(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast<SupportLocal*>(PX_PLACEMENT_NEW(buff, SupportLocalImpl<ConvexHullV>)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if(generateSphereFullContactManifold(capsule, polyData, map, manifoldContacts, numContacts, contactDist, normal, doOverlapTest)) + { + + if(numContacts > 0) + { + + Gu::PersistentContact& p = manifold.getContactPoint(0); + + p.mLocalPointA = manifoldContacts[0].mLocalPointA; + p.mLocalPointB = manifoldContacts[0].mLocalPointB; + p.mLocalNormalPen = manifoldContacts[0].mLocalNormalPen; + manifold.mNumContacts =1; + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, capsule.radius, transf0.p); + const FloatV penDep = FSub(V4GetW(manifoldContacts[0].mLocalNormalPen), capsule.radius); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + + return true; + } + + } + + return false; +} + +bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get<const PxConvexMeshGeometryLL>(); + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + + const Vec3V zeroV = V3Zero(); + + Ps::prefetchLine(shapeConvex.hullData); + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV minMargin = FMin(convexMargin, sphereRadius); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + const FloatV refreshDistance = FAdd(sphereRadius, contactDist); + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDistance); + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + + Vec3V closestA(zeroV), closestB(zeroV); + Vec3V normal(zeroV); // from a to b + const FloatV zero = FZero(); + FloatV penDep = zero; + + PX_UNUSED(bLostContacts); + + + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + + manifold.setRelativeTransform(curRTrans); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const bool idtScale = shapeConvex.scale.isIdentity(); + //use the original shape + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, idtScale); + convexHull.setMargin(zero); + //transform capsule into the local space of convexHull + CapsuleV capsule(aToB.p, sphereRadius); + + LocalConvex<CapsuleV> convexA(capsule); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(idtScale) + { + LocalConvex<ConvexHullNoScaleV> convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + status = gjkPenetration<LocalConvex<CapsuleV>, LocalConvex<ConvexHullNoScaleV> >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, true); + } + else + { + LocalConvex<ConvexHullV> convexB(convexHull); + status = gjkPenetration<LocalConvex<CapsuleV>, LocalConvex<ConvexHullV> >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normal, penDep, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, true); + + } + + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_CONTACT) + { + Gu::PersistentContact& p = manifold.getContactPoint(0); + p.mLocalPointA = zeroV;//sphere center + p.mLocalPointB = closestB; + p.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + manifold.mNumContacts =1; + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + penDep = FSub(penDep, sphereRadius); + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + + } + else if(status == GJK_DEGENERATE) + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + return fullContactsGenerationSphereConvex(capsule, convexHull, transf0, transf1, manifoldContacts, contactBuffer, idtScale, + manifold, normal, contactDist, true, renderOutput); + } + else if(status == EPA_CONTACT) + { + + if(idtScale) + { + LocalConvex<ConvexHullNoScaleV> convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep, true); + + } + else + { + LocalConvex<ConvexHullV> convexB(convexHull); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + closestA, closestB, normal, penDep, true); + + } + + if(status == EPA_CONTACT) + { + Gu::PersistentContact& p = manifold.getContactPoint(0); + p.mLocalPointA = zeroV;//sphere center + p.mLocalPointB = closestB; + p.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), penDep); + manifold.mNumContacts =1; + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + penDep = FSub(penDep, sphereRadius); + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + return fullContactsGenerationSphereConvex(capsule, convexHull, transf0, transf1, manifoldContacts, contactBuffer, idtScale, + manifold, normal, contactDist, true, renderOutput); + + } + + } + } + else if(manifold.mNumContacts > 0) + { + //ML:: the manifold originally has contacts + Gu::PersistentContact& p = manifold.getContactPoint(0); + const Vec3V worldNormal = transf1.rotate(Vec3V_From_Vec4V(p.mLocalNormalPen)); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + penDep = FSub(V4GetW(p.mLocalNormalPen), sphereRadius); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, sphereRadius); +#endif + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + } + + return false; + +} +}//Gu +}//phyxs + diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereHeightField.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereHeightField.cpp new file mode 100644 index 00000000..cebb8142 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereHeightField.cpp @@ -0,0 +1,155 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecBox.h" +#include "GuVecShrunkBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMSphereVsHeightfieldContactGenerationCallback : PCMHeightfieldContactGenerationCallback<PCMSphereVsHeightfieldContactGenerationCallback> +{ + +public: + PCMSphereVsMeshContactGeneration mGeneration; + + PCMSphereVsHeightfieldContactGenerationCallback( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + + const PsTransformV& sphereTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Gu::HeightFieldUtil& hfUtil + + + ) : + PCMHeightfieldContactGenerationCallback<PCMSphereVsHeightfieldContactGenerationCallback>(hfUtil, heightfieldTransform1), + mGeneration(sphereCenter, sphereRadius, contactDistance, replaceBreakingThreshold, sphereTransform, heightfieldTransform, multiManifold, contactBuffer) + { + } + + template<PxU32 CacheSize> + void processTriangleCache(Gu::TriangleCache<CacheSize>& cache) + { + mGeneration.processTriangleCache<CacheSize, PCMSphereVsMeshContactGeneration>(cache); + } + +}; + + +bool Gu::pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxHeightFieldGeometryLL& shapeHeight = shape1.get<const PxHeightFieldGeometryLL>(); + + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV sphereTransform(p0, q0);//sphere transform + const PsTransformV heightfieldTransform(p1, q1);//height feild + const PsTransformV curTransform = heightfieldTransform.transformInv(sphereTransform); + + + // We must be in local space to use the cache + + if(multiManifold.invalidate(curTransform, sphereRadius, FLoad(0.02f))) + { + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const FloatV replaceBreakingThreshold = FMul(sphereRadius, FLoad(0.001f)); + const Gu::HeightField& hf = *static_cast<Gu::HeightField*>(shapeHeight.heightField); + Gu::HeightFieldUtil hfUtil(shapeHeight, hf); + const PxVec3 sphereCenterShape1Space = transform1.transformInv(transform0.p); + const Vec3V sphereCenter = V3LoadU(sphereCenterShape1Space); + PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + PxVec3 inflatedRadiusV(inflatedRadius); + + PxBounds3 bounds(sphereCenterShape1Space - inflatedRadiusV, sphereCenterShape1Space + inflatedRadiusV); + + PCMSphereVsHeightfieldContactGenerationCallback blockCallback( + sphereCenter, + sphereRadius, + contactDist, + replaceBreakingThreshold, + sphereTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + hfUtil); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(sphereRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(sphereRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + + } + + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, sphereTransform, heightfieldTransform, sphereRadius); +} + + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereMesh.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereMesh.cpp new file mode 100644 index 00000000..430b7baa --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereMesh.cpp @@ -0,0 +1,171 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuConvexUtilsInternal.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuTriangleCache.h" +#include "GuPCMContactConvexCommon.h" +#include "GuHeightFieldUtil.h" +#include "GuPCMContactMeshCallback.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMSphereVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback< PCMSphereVsMeshContactGenerationCallback > +{ + +public: + PCMSphereVsMeshContactGeneration mGeneration; + + + PCMSphereVsMeshContactGenerationCallback( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& sphereTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PxU8* extraTriData, + const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtMeshScale, + Cm::RenderOutput* renderOutput = NULL + ) : + PCMMeshContactGenerationCallback<PCMSphereVsMeshContactGenerationCallback>(meshScaling, extraTriData, idtMeshScale), + mGeneration(sphereCenter, sphereRadius, contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, renderOutput) + { + } + + PX_FORCE_INLINE bool doTest(const PxVec3&, const PxVec3&, const PxVec3&) { return true; } + + template<PxU32 CacheSize> + void processTriangleCache(TriangleCache<CacheSize>& cache) + { + mGeneration.processTriangleCache<CacheSize, PCMSphereVsMeshContactGeneration>(cache); + } + +}; + + +bool Gu::pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get<const PxTriangleMeshGeometryLL>(); + + //gRenderOutPut = cache.mRenderOutput; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV sphereTransform(p0, q0);//sphere transform + const PsTransformV meshTransform(p1, q1);//triangleMesh + const PsTransformV curTransform = meshTransform.transformInv(sphereTransform); + + // We must be in local space to use the cache + if(multiManifold.invalidate(curTransform, sphereRadius, FLoad(0.02f))) + { + const FloatV replaceBreakingThreshold = FMul(sphereRadius, FLoad(0.001f)); + const PxVec3 sphereCenterShape1Space = transform1.transformInv(transform0.p); + PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + + const Vec3V sphereCenter = V3LoadU(sphereCenterShape1Space); + + const TriangleMesh* meshData = shapeMesh.meshData; + + Cm::FastVertex2ShapeScaling meshScaling; // PT: TODO: get rid of default ctor :( + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + // mesh scale is not baked into cached verts + PCMSphereVsMeshContactGenerationCallback callback( + sphereCenter, + sphereRadius, + contactDist, + replaceBreakingThreshold, + sphereTransform, + meshTransform, + multiManifold, + contactBuffer, + extraData, + meshScaling, + idtMeshScale); + + PxVec3 obbCenter = sphereCenterShape1Space; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + if(!idtMeshScale) + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + + callback.flushCache(); + + callback.mGeneration.processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(sphereRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(sphereRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + } + + //multiManifold.drawManifold(*gRenderOutPut, sphereTransform, meshTransform); + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, sphereTransform, meshTransform, sphereRadius); +} + +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSpherePlane.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSpherePlane.cpp new file mode 100644 index 00000000..c9e6909d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSpherePlane.cpp @@ -0,0 +1,86 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuContactBuffer.h" +#include "PsVecTransform.h" + +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" + +namespace physx +{ + +namespace Gu +{ +bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape1); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get<const PxSphereGeometry>(); + + //sphere transform + const Vec3V p0 = V3LoadU_SafeReadW(transform0.p); // PT: safe because 'mRefCount' follows 'mTransform' in PxsTransform + + //plane transform + const Vec3V p1 = V3LoadU_SafeReadW(transform1.p); // PT: safe because 'mRefCount' follows 'mTransform' in PxsTransform + const QuatV q1 = QuatVLoadU(&transform1.q.x); + + const FloatV radius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV transf1(p1, q1); + //Sphere in plane space + const Vec3V sphereCenterInPlaneSpace = transf1.transformInv(p0); + + + //Separation + const FloatV separation = FSub(V3GetX(sphereCenterInPlaneSpace), radius); + + if(FAllGrtrOrEq(contactDist, separation)) + { + //get the plane normal + const Vec3V worldNormal = QuatGetBasisVector0(q1); + const Vec3V worldPoint = V3NegScaleSub(worldNormal, radius, p0); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(worldNormal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(worldPoint), &contact.point.x); + FStore(separation, &contact.separation); + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereSphere.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereSphere.cpp new file mode 100644 index 00000000..021fd9b3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMContactSphereSphere.cpp @@ -0,0 +1,86 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "PsVecTransform.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + const PxSphereGeometry& shapeSphere0 = shape0.get<const PxSphereGeometry>(); + const PxSphereGeometry& shapeSphere1 = shape1.get<const PxSphereGeometry>(); + + const FloatV cDist = FLoad(params.mContactDistance); + const Vec3V p0 = V3LoadA(&transform0.p.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV r0 = FLoad(shapeSphere0.radius); + const FloatV r1 = FLoad(shapeSphere1.radius); + + + const Vec3V _delta = V3Sub(p0, p1); + const FloatV distanceSq = V3Dot(_delta, _delta); + const FloatV radiusSum = FAdd(r0, r1); + const FloatV inflatedSum = FAdd(radiusSum, cDist); + + if(FAllGrtr(FMul(inflatedSum, inflatedSum), distanceSq)) + { + const FloatV eps = FLoad(0.00001f); + const FloatV nhalf = FLoad(-0.5f); + const FloatV magn = FSqrt(distanceSq); + const BoolV bCon = FIsGrtrOrEq(eps, magn); + const Vec3V normal = V3Sel(bCon, V3UnitX(), V3ScaleInv(_delta, magn)); + const FloatV scale = FMul(FSub(FAdd(r0, magn), r1), nhalf); + const Vec3V point = V3ScaleAdd(normal, scale, p0); + const FloatV dist = FSub(magn, radiusSum); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMShapeConvex.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMShapeConvex.cpp new file mode 100644 index 00000000..a95c46fa --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMShapeConvex.cpp @@ -0,0 +1,210 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" +#include "GuPCMShapeConvex.h" +#include "GuVecConvexHull.h" +#include "GuPCMContactGen.h" +#include "CmRenderOutput.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ +namespace Gu +{ + const PxU8 gPCMBoxPolygonData[24] = + { + 0, 3, 2, 1, + 1, 2, 6, 5, + 5, 6, 7, 4, + 4, 7, 3, 0, + 3, 7, 6, 2, + 4, 0, 1, 5, + }; + + + Gu::PCMPolygonalBox::PCMPolygonalBox(const PxVec3& halfSide) : mHalfSide(halfSide) + { + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + PxVec3 minimum = -mHalfSide; + PxVec3 maximum = mHalfSide; + // Generate 8 corners of the bbox + mVertices[0] = PxVec3(minimum.x, minimum.y, minimum.z); + mVertices[1] = PxVec3(maximum.x, minimum.y, minimum.z); + mVertices[2] = PxVec3(maximum.x, maximum.y, minimum.z); + mVertices[3] = PxVec3(minimum.x, maximum.y, minimum.z); + mVertices[4] = PxVec3(minimum.x, minimum.y, maximum.z); + mVertices[5] = PxVec3(maximum.x, minimum.y, maximum.z); + mVertices[6] = PxVec3(maximum.x, maximum.y, maximum.z); + mVertices[7] = PxVec3(minimum.x, maximum.y, maximum.z); + + //Setup the polygons + for(PxU8 i=0; i < 6; i++) + { + mPolygons[i].mNbVerts = 4; + mPolygons[i].mVRef8 = PxU16(i*4); + } + + // ### planes needs *very* careful checks + // X axis + mPolygons[1].mPlane.n = PxVec3(1.0f, 0.0f, 0.0f); + mPolygons[1].mPlane.d = -mHalfSide.x; + mPolygons[3].mPlane.n = PxVec3(-1.0f, 0.0f, 0.0f); + mPolygons[3].mPlane.d = -mHalfSide.x; + + mPolygons[1].mMinIndex = 0; + mPolygons[3].mMinIndex = 1; + + PX_ASSERT(mPolygons[1].getMin(mVertices) == -mHalfSide.x); + PX_ASSERT(mPolygons[3].getMin(mVertices) == -mHalfSide.x); + + + // Y axis + mPolygons[4].mPlane.n = PxVec3(0.f, 1.0f, 0.0f); + mPolygons[4].mPlane.d = -mHalfSide.y; + mPolygons[5].mPlane.n = PxVec3(0.0f, -1.0f, 0.0f); + mPolygons[5].mPlane.d = -mHalfSide.y; + + mPolygons[4].mMinIndex = 0; + mPolygons[5].mMinIndex = 2; + + + PX_ASSERT(mPolygons[4].getMin(mVertices) == -mHalfSide.y); + PX_ASSERT(mPolygons[5].getMin(mVertices) == -mHalfSide.y); + + // Z axis + mPolygons[2].mPlane.n = PxVec3(0.f, 0.0f, 1.0f); + mPolygons[2].mPlane.d = -mHalfSide.z; + mPolygons[0].mPlane.n = PxVec3(0.0f, 0.0f, -1.0f); + mPolygons[0].mPlane.d = -mHalfSide.z; + + mPolygons[2].mMinIndex = 0; + mPolygons[0].mMinIndex = 4; + PX_ASSERT(mPolygons[2].getMin(mVertices) == -mHalfSide.z); + PX_ASSERT(mPolygons[0].getMin(mVertices) == -mHalfSide.z); + } + + void Gu::PCMPolygonalBox::getPolygonalData(Gu::PolygonalData* PX_RESTRICT dst) const + { + dst->mCenter = PxVec3(0.0f, 0.0f, 0.0f); + dst->mNbVerts = 8; + dst->mNbPolygons = 6; + dst->mPolygons = mPolygons; + dst->mNbEdges = 0; + dst->mVerts = mVertices; + dst->mPolygonVertexRefs = gPCMBoxPolygonData; + dst->mFacesByEdges = NULL; + dst->mVerticesByEdges = NULL; + dst->mInternal.mRadius = 0.0f; + dst->mInternal.mExtents[0] = 0.0f; + dst->mInternal.mExtents[1] = 0.0f; + dst->mInternal.mExtents[2] = 0.0f; + } + + static void getPCMPolygonalData_Convex(Gu::PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Ps::aos::Mat33V& vertexToShape) + { + using namespace Ps::aos; + const Vec3V vertexSpaceCenterOfMass = V3LoadU(src->mCenterOfMass); + const Vec3V shapeSpaceCenterOfMass = M33MulV3(vertexToShape, vertexSpaceCenterOfMass); + V3StoreU(shapeSpaceCenterOfMass, dst->mCenter); + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + dst->mVerticesByEdges = src->getVerticesByEdges16(); + + dst->mBigData = src->mBigConvexRawData; + + dst->mInternal = src->mInternal; + } + + + static void getPCMPolygonalData_Convex(Gu::PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling) + { + dst->mCenter = scaling * src->mCenterOfMass; + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + dst->mVerticesByEdges = src->getVerticesByEdges16(); + + dst->mBigData = src->mBigConvexRawData; + dst->mInternal = src->mInternal; + } + + + void getPCMConvexData(const Gu::ConvexHullV& convexHull, const bool idtScale, PolygonalData& polyData) + { + + PX_ASSERT(!convexHull.hullData->mAABB.isEmpty()); + + //this is used to calculate the convex hull's center of mass + getPCMPolygonalData_Convex(&polyData, convexHull.hullData, convexHull.vertex2Shape); + + if(!idtScale) + polyData.mInternal.reset(); + + } + + bool getPCMConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData) + { + const PxConvexMeshGeometryLL& shapeConvex = shape.get<const PxConvexMeshGeometryLL>(); + + const bool idtScale = shapeConvex.scale.isIdentity(); + if(!idtScale) + scaling.init(shapeConvex.scale); + + PX_ASSERT(!shapeConvex.hullData->mAABB.isEmpty()); + bounds = shapeConvex.hullData->mAABB.transformFast(scaling.getVertex2ShapeSkew()); + + getPCMPolygonalData_Convex(&polyData, shapeConvex.hullData, scaling); + + return idtScale; + } + +} +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMShapeConvex.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMShapeConvex.h new file mode 100644 index 00000000..f69c2501 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMShapeConvex.h @@ -0,0 +1,68 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_SHAPE_CONVEX_H +#define GU_PCM_SHAPE_CONVEX_H + +#include "GuConvexSupportTable.h" +#include "GuPersistentContactManifold.h" +#include "GuShapeConvex.h" + + +namespace physx +{ + +namespace Gu +{ + class GeometryUnion; + + extern const PxU8 gPCMBoxPolygonData[24]; + + class PCMPolygonalBox + { + public: + PCMPolygonalBox(const PxVec3& halfSide); + + void getPolygonalData(Gu::PolygonalData* PX_RESTRICT dst) const; + + const PxVec3& mHalfSide; + PxVec3 mVertices[8]; + Gu::HullPolygonData mPolygons[6]; + private: + PCMPolygonalBox& operator=(const PCMPolygonalBox&); + }; + + + void getPCMConvexData(const Gu::ConvexHullV& convexHull, const bool idtScale, Gu::PolygonalData& polyData); + bool getPCMConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, Gu::PolygonalData& polyData); + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMTriangleContactGen.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMTriangleContactGen.cpp new file mode 100644 index 00000000..ea400b94 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMTriangleContactGen.cpp @@ -0,0 +1,1250 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuGeometryUnion.h" +#include "GuPCMTriangleContactGen.h" +#include "GuPCMContactConvexCommon.h" +#include "GuVecTriangle.h" +#include "GuBarycentricCoordinates.h" + +#if PCM_LOW_LEVEL_DEBUG +#include "PxRenderBuffer.h" +#endif + +#define EDGE_EDGE_GAUSS_MAP 0 +#define BRUTE_FORCE_EDGE_EDGE 0 + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +namespace physx +{ + static bool testPolyFaceNormal(const Gu::TriangleV& triangle, const PolygonalData& polyData, SupportLocalImpl<TriangleV>* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(triangle); + + FloatV _minOverlap = FMax(); + PxU32 _feature = 0; + Vec3V _faceNormal = faceNormal; + FloatV min0, max0; + FloatV min1, max1; + const FloatV eps = FEps(); + + if(polyMap->isIdentityScale) + { + //in the local space of polyData0 + for(PxU32 i=0; i<polyData.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + + const Vec3V minVert = V3LoadU_SafeReadW(polyData.mVerts[polygon.mMinIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + const FloatV planeDist = FLoad(polygon.mPlane.d); + //shapeSpace and vertexSpace are the same + const Vec3V planeNormal = V3LoadU_SafeReadW(polygon.mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + //ML::avoid lHS, don't use the exiting function + min0 = V3Dot(planeNormal, minVert); + max0 = FNeg(planeDist); + + triMap->doSupport(planeNormal, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = planeNormal; + } + } + } + else + { + + //in the local space of polyData0 + for(PxU32 i=0; i<polyData.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + + const Vec3V minVert = V3LoadU_SafeReadW(polyData.mVerts[polygon.mMinIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + const FloatV planeDist = FLoad(polygon.mPlane.d); + const Vec3V vertexSpacePlaneNormal = V3LoadU_SafeReadW(polygon.mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + //transform plane n to shape space + const Vec3V shapeSpacePlaneNormal = M33TrnspsMulV3(polyMap->shape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRsqrtFast(V3LengthSq(shapeSpacePlaneNormal)); //FRecip(V3Length(shapeSpacePlaneNormal)); + + //ML::avoid lHS, don't use the exiting function + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + //normalize the shapeSpacePlaneNormal + const Vec3V planeN = V3Scale(shapeSpacePlaneNormal, magnitude); + + triMap->doSupport(planeN, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = planeN; + } + } + } + + if(FAllGrtr(minOverlap, FAdd(_minOverlap, eps))) + { + faceNormal = _faceNormal; + minOverlap = _minOverlap; + status = faceStatus; + } + + feature = _feature; + + return true; + + } + + + + //triangle is in the local space of polyData + static bool testTriangleFaceNormal(const TriangleV& triangle, const PolygonalData& polyData, SupportLocalImpl<TriangleV>* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(triMap); + PX_UNUSED(polyData); + + FloatV min1, max1; + const FloatV eps = FEps(); + + const Vec3V triangleLocNormal = triangle.normal(); + + const FloatV min0 = V3Dot(triangleLocNormal, triangle.verts[0]); + const FloatV max0 = min0; + + //triangle normal is in the vertex space + polyMap->doSupport(triangleLocNormal, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + minOverlap = FSub(FSub(max0, min1), eps); + status = faceStatus; + feature = 0; + faceNormal=triangleLocNormal; + + return true; + + } + + static bool testPolyEdgeNormal(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl<TriangleV>* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status) + { + PX_UNUSED(triFlags); + FloatV overlap = minOverlap; + FloatV min0, max0; + FloatV min1, max1; + const FloatV zero = FZero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + const Vec3V v0 = M33MulV3(polyMap->shape2Vertex, triangle.verts[0]); + const Vec3V v1 = M33MulV3(polyMap->shape2Vertex, triangle.verts[1]); + const Vec3V v2 = M33MulV3(polyMap->shape2Vertex, triangle.verts[2]); + + TriangleV vertexSpaceTriangle(v0, v1, v2); + + PxU32 nbTriangleAxes = 0; + Vec3V triangleAxes[3]; + for(PxI8 kStart = 0, kEnd =2; kStart<3; kEnd = kStart++) + { + bool active = (triFlags & (1 << (kEnd+3))) != 0; + + if(active) + { + const Vec3V p00 = vertexSpaceTriangle.verts[kStart]; + const Vec3V p01 = vertexSpaceTriangle.verts[kEnd]; + triangleAxes[nbTriangleAxes++] = V3Sub(p01, p00); + } + } + + if(nbTriangleAxes == 0) + return true; + + //create localTriPlane in the vertex space + const Vec3V vertexSpaceTriangleNormal = vertexSpaceTriangle.normal(); + + for(PxU32 i =0; i<polyData.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + const PxU8* inds = polyData.mPolygonVertexRefs + polygon.mVRef8; + const Vec3V vertexSpacePlaneNormal = V3LoadU(polygon.mPlane.n); + + //fast culling. + if(FAllGrtr(V3Dot(vertexSpacePlaneNormal, vertexSpaceTriangleNormal), zero)) + continue; + + // Loop through polygon vertices == polygon edges; + for(PxU32 lStart = 0, lEnd =PxU32(polygon.mNbVerts-1); lStart<polygon.mNbVerts; lEnd = PxU32(lStart++)) + { + //in the vertex space + const Vec3V p10 = V3LoadU_SafeReadW(polyData.mVerts[inds[lStart]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + const Vec3V p11 = V3LoadU_SafeReadW(polyData.mVerts[inds[lEnd]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const Vec3V convexEdge = V3Sub(p11, p10); + + for (PxU32 j = 0; j < nbTriangleAxes; ++j) + { + + const Vec3V currentPolyEdge = triangleAxes[j]; + const Vec3V v = V3Cross(convexEdge, currentPolyEdge); + + //two edges aren't parallel + if ((!V3AllGrtr(eps2, V3Abs(v))) && (FAllGrtr(V3Dot(v, vertexSpaceTriangleNormal), zero))) + { + //transform the v back to the shape space + const Vec3V shapeSpaceV = M33TrnspsMulV3(polyMap->shape2Vertex, v); + const Vec3V n0 = V3Normalize(shapeSpaceV); + triMap->doSupport(n0, min0, max0); + polyMap->doSupport(n0, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if (FAllGrtr(overlap, tempOverlap)) + { + overlap = tempOverlap; + minNormal = n0; + status = edgeStatus; + } + + } + } + + } + } + minOverlap = overlap; + + return true; + + } + +#if BRUTE_FORCE_EDGE_EDGE + + + bool testPolyEdgeNormalBruteForce(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl<TriangleV>* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status) + { + PX_UNUSED(triFlags); + FloatV min0, max0; + FloatV min1, max1; + + FloatV bestDist = FLoad(PX_MAX_F32); + Vec3V bestAxis = V3Zero(); + + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + PxU32 bestPolyIndex = 0; + PxU32 bestStart = 0; + PxU32 bestEnd = 0; + PxI8 bestTriStart = 0; + PxI8 bestTriEnd = 0; + + for(PxU32 i =0; i<polyData.mNbPolygons; ++i) + { + const Gu::HullPolygonData& polygon = polyData.mPolygons[i]; + const PxU8* inds = polyData.mPolygonVertexRefs + polygon.mVRef8; + + // Loop through polygon vertices == polygon edges; + for(PxU32 lStart = 0, lEnd =PxU32(polygon.mNbVerts-1); lStart<polygon.mNbVerts; lEnd = PxU32(lStart++)) + { + //in the vertex space + const Vec3V p10 = V3LoadU_SafeReadW(polyData.mVerts[inds[lStart]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + const Vec3V p11 = V3LoadU_SafeReadW(polyData.mVerts[inds[lEnd]]); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, p10); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, p11); + + const Vec3V convexEdge = V3Sub(vertex11, vertex10); + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + + const Vec3V triangleEdge = V3Sub(triVert1, triVert0); + const Vec3V v = V3Cross(convexEdge, triangleEdge); + + if (!V3AllGrtr(eps2, V3Abs(v))) + { + //transform the v back to the shape space + const Vec3V n0 = V3Normalize(v); + triMap->doSupport(n0, min0, max0); + polyMap->doSupport(n0, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if (FAllGrtr(bestDist, tempOverlap)) + { + bestDist = tempOverlap; + bestAxis = n0; + bestPolyIndex = i; + bestStart = lStart; + bestEnd = lEnd; + bestTriStart = kStart; + bestTriEnd = kEnd; + } + + } + } + } + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + } + + return true; + + } + + bool testPolyEdgeNormalBruteForceVertsByEdges(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl<TriangleV>* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status, PxU32& bestEdgeIndex, PxI8& bestTriStart, PxI8& bestTriEnd) + { + + PX_UNUSED(triFlags); + + const PxU32 numConvexEdges = polyData.mNbEdges; + const PxU16* verticesByEdges16 = polyData.mVerticesByEdges; + const PxVec3* vertices = polyData.mVerts; + + FloatV bestDist = FLoad(PX_MAX_F32); + Vec3V bestAxis = V3Zero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + for (PxU32 convexEdgeIdx = 0; convexEdgeIdx < numConvexEdges; ++convexEdgeIdx) + { + const PxU16 v0idx1 = verticesByEdges16[convexEdgeIdx * 2]; + const PxU32 v1idx1 = verticesByEdges16[convexEdgeIdx * 2 + 1]; + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v0idx1])); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v1idx1])); + + Vec3V convexEdge = V3Sub(vertex11, vertex10); + + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + const Vec3V triEdge = V3Sub(triVert1, triVert0); + + // compute the separation along this axis in vertex space + Vec3V axis = V3Cross(convexEdge, triEdge); + + if (!V3AllGrtr(eps2, V3Abs(axis))) + { + axis = V3Normalize(axis); + + FloatV min0, max0; + FloatV min1, max1; + triMap->doSupport(axis, min0, max0); + polyMap->doSupport(axis, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + const FloatV dist0 = FSub(max0, min1); + const FloatV dist1 = FSub(max1, min0); + + if (FAllGrtr(dist0, dist1)) + axis = V3Neg(axis); + + const FloatV dist = FMin(dist0, dist1); + + if (FAllGrtr(bestDist, dist)) + { + bestDist = dist; + bestAxis = axis; + + bestEdgeIndex = convexEdgeIdx; + bestTriStart = kStart; + bestTriEnd = kEnd; + } + } + } + + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + + } + return true; + + } + + +#endif + +#if EDGE_EDGE_GAUSS_MAP + + + + bool isMinkowskiFace(const Vec3V& A, const Vec3V& B, const Vec3V& B_x_A, const Vec3V& C, const Vec3V& D, const Vec3V& D_x_C) + { + + const FloatV zero = FZero(); + // Two edges build a face on the Minkowski sum if the associated arcs AB and CD intersect on the Gauss map. + // The associated arcs are defined by the adjacent face normals of each edge. + const FloatV CBA = V3Dot(C, B_x_A); + const FloatV DBA = V3Dot(D, B_x_A); + const FloatV ADC = V3Dot(A, D_x_C); + const FloatV BDC = V3Dot(B, D_x_C); + + const BoolV con0 = FIsGrtrOrEq(zero, FMul(CBA, DBA)); + const BoolV con1 = FIsGrtrOrEq(zero, FMul(ADC, BDC)); + const BoolV con2 = FIsGrtr(FMul(CBA, BDC), zero); + + const BoolV con = BAnd(con2, BAnd(con0, con1)); + + return (BAllEqTTTT(con) != 0); + + //return CBA * DBA < 0.0f && ADC * BDC < 0.0f && CBA * BDC > 0.0f; + } + +#define SAT_VARIFY 0 + + bool testPolyEdgeNormalGaussMap(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl<TriangleV>* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status, PxU32& gaussMapBestEdgeIndex, PxI8& gaussMapBestTriStart, PxI8& gaussMapBestTriEnd) + { + + PX_UNUSED(triFlags); + const FloatV zero = FZero(); + + const Vec3V triNormal = triangle.normal(); + + const PxU32 numConvexEdges = polyData.mNbEdges; + const PxU16* verticesByEdges16 = polyData.mVerticesByEdges; + const PxU8* facesByEdges8 = polyData.mFacesByEdges; + const PxVec3* vertices = polyData.mVerts; + + FloatV bestDist = FLoad(-PX_MAX_F32); + Vec3V axis = V3Zero(); + Vec3V bestAxis = V3Zero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + + //Center is in shape space + const Vec3V shapeSpaceCOM =V3LoadU(polyData.mCenter); + const Vec3V vertexSpaceCOM = M33MulV3(polyMap->shape2Vertex, shapeSpaceCOM); + + PxVec3 vertexCOM; + V3StoreU(vertexSpaceCOM, vertexCOM); + + for (PxU32 convexEdgeIdx = 0; convexEdgeIdx < numConvexEdges; ++convexEdgeIdx) + { + + const PxU16 v0idx1 = verticesByEdges16[convexEdgeIdx*2]; + const PxU32 v1idx1 = verticesByEdges16[convexEdgeIdx * 2 + 1]; + + const PxU8 f10 = facesByEdges8[convexEdgeIdx * 2]; + const PxU8 f11 = facesByEdges8[convexEdgeIdx * 2 + 1]; + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v0idx1])); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v1idx1])); + + Vec3V convexEdge = V3Sub(vertex11, vertex10); + + const Gu::HullPolygonData& face0 = polyData.mPolygons[f10]; + const Gu::HullPolygonData& face1 = polyData.mPolygons[f11]; + + const Vec3V convexNormal0 = M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(face0.mPlane.n)); + const Vec3V convexNormal1 = M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(face1.mPlane.n)); + + float signDist0 = face0.mPlane.distance(vertexCOM); + float signDist1 = face1.mPlane.distance(vertexCOM); + + PX_ASSERT(signDist0 < 0.f); + PX_ASSERT(signDist1 < 0.f); + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + const Vec3V triEdge = V3Sub(triVert1, triVert0); + + //if (isMinkowskiFace(convexNormal0, convexNormal1, V3Neg(convexEdge), V3Neg(triNormal), triNormal, V3Neg(triEdge))) + if (isMinkowskiFace(convexNormal0, convexNormal1, convexEdge, V3Neg(triNormal), triNormal, triEdge)) + { + + // compute the separation along this axis in vertex space + axis = V3Cross(convexEdge, triEdge); + + if (!V3AllGrtr(eps2, V3Abs(axis))) + { + axis = V3Normalize(axis); + + const Vec3V v = V3Sub(vertex10, shapeSpaceCOM); + + // ensure the axis is outward pointing on the edge on the minkowski sum. Assure normal points from convex hull to triangle + const FloatV temp = V3Dot(axis, v); + if (FAllGrtr(zero, temp)) + axis = V3Neg(axis); + + //compute the distance from any of the verts in the triangle to plane(axis, vertex10)(n.dot(p-a)) + const Vec3V ap = V3Sub(triVert0, vertex10); + + const FloatV dist = V3Dot(axis, ap); + + if (FAllGrtr(dist, contactDist)) + return false; + +#if SAT_VARIFY + FloatV min0, max0; + FloatV min1, max1; + triMap->doSupport(V3Neg(axis), min0, max0); + polyMap->doSupport(V3Neg(axis), min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + /*const FloatV dist = FSub(max0, min1); + + if (FAllGrtr(bestDist, dist)) + { + bestDist = dist; + bestAxis = axis; + }*/ + const FloatV tempDist = FSub(max0, min1); + + const FloatV dif = FAbs(FAdd(tempDist, dist)); + PX_UNUSED(dif); + PX_ASSERT(FAllGrtr(FLoad(1e-4f), dif)); + +#endif + + if (FAllGrtr(dist, bestDist)) + { + bestDist = dist; + bestAxis = axis; + + gaussMapBestEdgeIndex = convexEdgeIdx; + gaussMapBestTriStart = kStart; + gaussMapBestTriEnd = kEnd; + } + } + } + } + + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + } + return true; + + } + +#endif + + static PX_FORCE_INLINE PxU32 addMeshContacts(MeshPersistentContact* manifoldContacts, const Vec3V& pA, const Vec3V& pB, const Vec4V& normalPen, const PxU32 triangleIndex, const PxU32 numContacts) + { + manifoldContacts[numContacts].mLocalPointA = pA; + manifoldContacts[numContacts].mLocalPointB = pB; + manifoldContacts[numContacts].mLocalNormalPen = normalPen; + manifoldContacts[numContacts].mFaceIndex = triangleIndex; + return numContacts+1; + } + + + static void generatedTriangleContacts(const Gu::TriangleV& triangle, const PxU32 triangleIndex, const PxU8/* _triFlags*/, const Gu::PolygonalData& polyData1, const Gu::HullPolygonData& incidentPolygon, Gu::SupportLocal* map1, Gu::MeshPersistentContact* manifoldContacts, PxU32& numManifoldContacts, + const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg contactNormal, Cm::RenderOutput* renderOutput) + { + + PX_UNUSED(renderOutput); + using namespace Ps::aos; + + //PxU8 triFlags = _triFlags; + const PxU32 previousContacts = numManifoldContacts; + + const FloatV zero = FZero(); + + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + const PxU8* inds1 = polyData1.mPolygonVertexRefs + incidentPolygon.mVRef8; + + Vec3V points0In0[3]; + Vec3V* points1In0 = reinterpret_cast<Vec3V*>(PxAllocaAligned(sizeof(Vec3V)*incidentPolygon.mNbVerts, 16)); + FloatV* points1In0TValue = reinterpret_cast<FloatV*>(PxAllocaAligned(sizeof(FloatV)*incidentPolygon.mNbVerts, 16)); + bool* points1In0Penetration = reinterpret_cast<bool*>(PxAlloca(sizeof(bool)*incidentPolygon.mNbVerts)); + + + points0In0[0] = triangle.verts[0]; + points0In0[1] = triangle.verts[1]; + points0In0[2] = triangle.verts[2]; + + + + //Transform all the verts from vertex space to shape space + map1->populateVerts(inds1, incidentPolygon.mNbVerts, polyData1.mVerts, points1In0); + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map1->transform, points1In0, incidentPolygon.mNbVerts, (PxU32)PxDebugColor::eARGB_RED); + //Gu::PersistentContactManifold::drawTriangle(*renderOutput, map1->transform.transform(points1In0[0]), map1->transform.transform(points0In0[1]), map1->transform.transform(points0In0[2]), (PxU32)PxDebugColor::eARGB_BLUE); +#endif + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i<3; ++i) + { + points0In0[i] = M33MulV3(rot, points0In0[i]); + rPolygonMin = V3Min(rPolygonMin, points0In0[i]); + rPolygonMax = V3Max(rPolygonMax, points0In0[i]); + } + + + rPolygonMin = V3Sub(rPolygonMin, eps); + rPolygonMax = V3Add(rPolygonMax, eps); + + const FloatV d = V3GetZ(points0In0[0]); + const FloatV rd = FAdd(d, contactDist); + + Vec3V iPolygonMin= max; + Vec3V iPolygonMax = nmax; + + + PxU32 inside = 0; + for(PxU32 i=0; i<incidentPolygon.mNbVerts; ++i) + { + const Vec3V vert1 =points1In0[i]; //this still in polyData1's local space + points1In0[i] = M33MulV3(rot, vert1); + const FloatV z = V3GetZ(points1In0[i]); + points1In0TValue[i] = FSub(z, d); + points1In0[i] = V3SetZ(points1In0[i], d); + iPolygonMin = V3Min(iPolygonMin, points1In0[i]); + iPolygonMax = V3Max(iPolygonMax, points1In0[i]); + if(FAllGrtr(rd, z)) + { + points1In0Penetration[i] = true; + + if(contains(points0In0, 3, points1In0[i], rPolygonMin, rPolygonMax)) + { + inside++; + + //add this contact to the buffer + const FloatV t = V3Dot(contactNormal, V3Sub(triangle.verts[0], vert1)); + const Vec3V projectPoint = V3ScaleAdd(contactNormal, t, vert1); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), FNeg(t)); + numManifoldContacts = addMeshContacts(manifoldContacts, vert1, projectPoint, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + + } + + + + if(inside == incidentPolygon.mNbVerts) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = V3Normalize(M33TrnspsMulV3(map1->shape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + const FloatV iPlaneD = V3Dot(incidentNormal, M33MulV3(map1->vertex2Shape, V3LoadU(polyData1.mVerts[inds1[0]]))); + + for(PxU32 i=0; i<3; ++i) + { + if(contains(points1In0, incidentPolygon.mNbVerts, points0In0[i], iPolygonMin, iPolygonMax)) + { + + inside++; + + const Vec3V vert0 = M33TrnspsMulV3(rot, points0In0[i]); + const FloatV t = FSub(V3Dot(incidentNormal, vert0), iPlaneD); + + if(FAllGrtr(t, contactDist)) + continue; + + + const Vec3V projPoint = V3NegScaleSub(incidentNormal, t, vert0); + + const Vec3V v = V3Sub(projPoint, vert0); + const FloatV t3 = V3Dot(v, contactNormal); + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), t3); + numManifoldContacts = addMeshContacts(manifoldContacts, projPoint, vert0, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + + } + + + if(inside == 3) + return; + + //(2) segment intesection + for (PxU32 rStart = 0, rEnd = 2; rStart < 3; rEnd = rStart++) + { + + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + for (PxU32 iStart = 0, iEnd = PxU32(incidentPolygon.mNbVerts - 1); iStart < incidentPolygon.mNbVerts; iEnd = iStart++) + { + if ((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd]))//|| (points1In0[i].status == POINT_OUTSIDE && points1In0[incidentIndex].status == POINT_OUTSIDE)) + continue; + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + const BoolV tempCon = BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if (BAllEqTTTT(con)) + continue; + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + if (FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if (FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect in 2d + const FloatV t = FMul(a1, FRecip(FSub(a2, a1))); + + const Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + const Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = M33TrnspsMulV3(rot, pBB); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + + if (FAllGrtr(pen, contactDist)) + continue; + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), pen); + numManifoldContacts = addMeshContacts(manifoldContacts, pB, pA, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if (numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + } + + } + + } + + + static void generatedPolyContacts(const Gu::PolygonalData& polyData0, const Gu::HullPolygonData& referencePolygon, const Gu::TriangleV& triangle, const PxU32 triangleIndex, const PxU8 triFlags, + Gu::SupportLocal* map0, Gu::MeshPersistentContact* manifoldContacts, PxU32& numManifoldContacts, const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg contactNormal, + Cm::RenderOutput* renderOutput) + { + PX_UNUSED(triFlags); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const PxU32 previousContacts = numManifoldContacts; + + const PxU8* inds0 = polyData0.mPolygonVertexRefs + referencePolygon.mVRef8; + + const Vec3V nContactNormal = V3Neg(contactNormal); + + //this is the matrix transform all points to the 2d plane + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + Vec3V* points0In0=reinterpret_cast<Vec3V*>(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + Vec3V points1In0[3]; + FloatV points1In0TValue[3]; + + bool points1In0Penetration[3]; + + //Transform all the verts from vertex space to shape space + map0->populateVerts(inds0, referencePolygon.mNbVerts, polyData0.mVerts, points0In0); + + points1In0[0] = triangle.verts[0]; + points1In0[1] = triangle.verts[1]; + points1In0[2] = triangle.verts[2]; + + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map0->transform, points0In0, referencePolygon.mNbVerts, (PxU32)PxDebugColor::eARGB_GREEN); + //Gu::PersistentContactManifold::drawTriangle(*gRenderOutPut, map0->transform.transform(points1In0[0]), map0->transform.transform(points1In0[1]), map0->transform.transform(points1In0[2]), (PxU32)PxDebugColor::eARGB_BLUE); +#endif + + //the first point in the reference plane + const Vec3V referencePoint = points0In0[0]; + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i<referencePolygon.mNbVerts; ++i) + { + //points0In0[i].vertext = M33TrnspsMulV3(rot, Vec3V_From_PxVec3(polyData0.mVerts[inds0[i]])); + points0In0[i] = M33MulV3(rot, points0In0[i]); + rPolygonMin = V3Min(rPolygonMin, points0In0[i]); + rPolygonMax = V3Max(rPolygonMax, points0In0[i]); + } + + rPolygonMin = V3Sub(rPolygonMin, eps); + rPolygonMax = V3Add(rPolygonMax, eps); + + + + const FloatV d = V3GetZ(points0In0[0]); + + const FloatV rd = FAdd(d, contactDist); + + Vec3V iPolygonMin= max; + Vec3V iPolygonMax = nmax; + + PxU32 inside = 0; + for(PxU32 i=0; i<3; ++i) + { + const Vec3V vert1 =points1In0[i]; //this still in polyData1's local space + points1In0[i] = M33MulV3(rot, vert1); + const FloatV z = V3GetZ(points1In0[i]); + points1In0TValue[i] = FSub(z, d); + points1In0[i] = V3SetZ(points1In0[i], d); + iPolygonMin = V3Min(iPolygonMin, points1In0[i]); + iPolygonMax = V3Max(iPolygonMax, points1In0[i]); + if(FAllGrtr(rd, z)) + { + points1In0Penetration[i] = true; + + //ML : check to see whether all the points of triangles in 2D space are within reference polygon's range + if(contains(points0In0, referencePolygon.mNbVerts, points1In0[i], rPolygonMin, rPolygonMax)) + { + inside++; + + //calculate projection point + const FloatV t = V3Dot(contactNormal, V3Sub(vert1, referencePoint)); + const Vec3V projectPoint = V3NegScaleSub(contactNormal, t, vert1); + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(nContactNormal), t); + numManifoldContacts = addMeshContacts(manifoldContacts, projectPoint, vert1, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + + } + + + if(inside == 3) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = triangle.normal(); + const FloatV iPlaneD = V3Dot(incidentNormal, triangle.verts[0]); + const FloatV one = FOne(); + for(PxU32 i=0; i<referencePolygon.mNbVerts; ++i) + { + if(contains(points1In0, 3, points0In0[i], iPolygonMin, iPolygonMax)) + { + + const Vec3V vert0 = M33TrnspsMulV3(rot, points0In0[i]); + + const FloatV t =FSub(V3Dot(incidentNormal, vert0), iPlaneD); + + if(FAllGrtr(t, contactDist)) + continue; + + const Vec3V projPoint = V3NegScaleSub(incidentNormal, t, vert0); + + FloatV u, w; + barycentricCoordinates(projPoint, triangle.verts[0], triangle.verts[1], triangle.verts[2], u, w); + const BoolV con = BAnd(FIsGrtrOrEq(u, zero), BAnd(FIsGrtrOrEq(w, zero), FIsGrtrOrEq(one, FAdd(u, w)))); + + if(BAllEqTTTT(con)) + { + inside++; + + const Vec3V v = V3Sub(projPoint, vert0); + const FloatV t3 = V3Dot(v, contactNormal); + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(nContactNormal), t3); + numManifoldContacts = addMeshContacts(manifoldContacts, vert0, projPoint, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + + } + + } + + if(inside == referencePolygon.mNbVerts) + return; + + + + //Always generate segment contacts + //(2) segment intesection + for (PxU32 iStart = 0, iEnd = 2; iStart < 3; iEnd = iStart++) + { + if((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd] ) ) + continue; + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + const BoolV tempCon =BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + continue; + + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + + if(FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if(FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect + const FloatV t = FMul(a1, FRecip(FSub(a2, a1))); + + const Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + const Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = M33TrnspsMulV3(rot, pBB); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + + if(FAllGrtr(pen, contactDist)) + continue; + + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(nContactNormal), pen); + numManifoldContacts = addMeshContacts(manifoldContacts, pA, pB, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + } + } + + } + + + bool Gu::PCMConvexVsMeshContactGeneration::generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU32* triIndices, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl<Gu::TriangleV>* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal) + { + + using namespace Ps::aos; + + const FloatV threshold = FLoad(0.7071f);//about 45 degree0 + PX_UNUSED(threshold); + { + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + //minNormal will be in the local space of polyData + Vec3V minNormal = V3Zero(); + + + PxU32 feature0; + if(!testTriangleFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + if(!testPolyFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + + if (!testPolyEdgeNormal(localTriangle, triFlags, polyData, localTriMap, polyMap, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + + const Vec3V triNormal = localTriangle.normal(); + + if(status == POLYDATA0) + { + //minNormal is the triangle normal and it is in the local space of polydata0 + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getPolygonIndex(polyData, polyMap, minNormal)]; + + patchNormal = triNormal; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, mRenderOutput); + + } + else + { + + if(status == POLYDATA1) + { + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[feature1]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + const FloatV cosTheta = V3Dot(nContactNormal, triNormal); + + if(FAllGrtr(cosTheta, threshold)) + { + patchNormal = triNormal; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, *referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, mRenderOutput); + } + else + { + //ML : defer the contacts generation + const PxU32 nb = sizeof(PCMDeferredPolyData)/sizeof(PxU32); + PxU32 newSize = nb + mDeferredContacts.size(); + mDeferredContacts.reserve(newSize); + PCMDeferredPolyData* PX_RESTRICT data = reinterpret_cast<PCMDeferredPolyData*>(mDeferredContacts.end()); + mDeferredContacts.forceSize_Unsafe(newSize); + + data->mTriangleIndex = triangleIndex; + data->mFeatureIndex = feature1; + data->triFlags = triFlags; + data->mInds[0] = triIndices[0]; + data->mInds[1] = triIndices[1]; + data->mInds[2] = triIndices[2]; + V3StoreU(localTriangle.verts[0], data->mVerts[0]); + V3StoreU(localTriangle.verts[1], data->mVerts[1]); + V3StoreU(localTriangle.verts[2], data->mVerts[2]); + return true; + + } + } + else + { + feature1 = PxU32(getPolygonIndex(polyData, polyMap, minNormal)); + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[feature1]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + + //if the minimum sperating axis is edge case, we don't defer it because it is an activeEdge + patchNormal = nContactNormal; + generatedPolyContacts(polyData, *referencePolygon, localTriangle, triangleIndex, triFlags, polyMap, manifoldContacts, numContacts, contactDist, contactNormal, mRenderOutput); + + } + + + } + + } + + return true; + } + + + bool Gu::PCMConvexVsMeshContactGeneration::generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl<Gu::TriangleV>* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal, Cm::RenderOutput* renderOutput) + { + + using namespace Ps::aos; + + const FloatV threshold = FLoad(0.7071f);//about 45 degree + PX_UNUSED(threshold); + { + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + //minNormal will be in the local space of polyData + Vec3V minNormal = V3Zero(); + + PxU32 feature0; + if(!testTriangleFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + if(!testPolyFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + if(!testPolyEdgeNormal(localTriangle, triFlags, polyData, localTriMap, polyMap, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + const Vec3V triNormal = localTriangle.normal(); + patchNormal = triNormal; + + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[getPolygonIndex(polyData, polyMap, triNormal)]; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, *referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, renderOutput); + + } + + return true; + } + + bool Gu::PCMConvexVsMeshContactGeneration::generatePolyDataContactManifold(Gu::TriangleV& localTriangle, const PxU32 featureIndex, const PxU32 triangleIndex, const PxU8 triFlags, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal) + { + + using namespace Ps::aos; + + const Gu::HullPolygonData* referencePolygon = &mPolyData.mPolygons[featureIndex]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(mPolyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + + patchNormal = nContactNormal; + generatedPolyContacts(mPolyData, *referencePolygon, localTriangle, triangleIndex, triFlags, mPolyMap, manifoldContacts, numContacts, contactDist, contactNormal, mRenderOutput); + + return true; + } + + +}//physx diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMTriangleContactGen.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMTriangleContactGen.h new file mode 100644 index 00000000..ae115bc2 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPCMTriangleContactGen.h @@ -0,0 +1,69 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_TRIANGLE_CONTACT_GEN_H +#define GU_PCM_TRIANGLE_CONTACT_GEN_H + +#include "GuPCMContactGenUtil.h" +#include "GuPersistentContactManifold.h" + +namespace physx +{ + struct PxTriangleMeshGeometryLL; + class PxHeightFieldGeometry; + +namespace Gu +{ + + bool PCMContactConvexMesh(const Gu::PolygonalData& polyData0, + Gu::SupportLocal* polyMap, + const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, + const PxTriangleMeshGeometryLL& shapeMesh, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput); + + bool PCMContactConvexHeightfield(const Gu::PolygonalData& polyData0, + Gu::SupportLocal* polyMap, + const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, + const PxHeightFieldGeometry& shapeHeightfield, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput); + + +} +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPersistentContactManifold.cpp b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPersistentContactManifold.cpp new file mode 100644 index 00000000..6e70c27b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPersistentContactManifold.cpp @@ -0,0 +1,2281 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "GuPersistentContactManifold.h" +#include "GuContactBuffer.h" +#include "PsAllocator.h" +#include "PsVecTransform.h" +#include "PsUtilities.h" + +using namespace physx; + +namespace physx +{ +namespace Gu +{ + +/* + This local function is to avoid DLL call +*/ +static Ps::aos::FloatV distancePointSegmentSquaredLocal(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg p) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + + const Vec3V ap = V3Sub(p, a); + const Vec3V ab = V3Sub(b, a); + const FloatV nom = V3Dot(ap, ab); + + const FloatV denom = V3Dot(ab, ab); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + + const FloatV t = FSel(FIsEq(denom, zero), zero, tValue); + const Vec3V v = V3NegScaleSub(ab, t, ap); + return V3Dot(v, v); +} + +/* + This local function is to avoid DLL call +*/ +static Ps::aos::FloatV distancePointTriangleSquaredLocal( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + //const Vec3V zero = V3Zero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + const Vec3V vv = V3Sub(p, a); + return V3Dot(vv, vv); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + const Vec3V vv = V3Sub(p, b); + return V3Dot(vv, vv); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + const Vec3V vv = V3Sub(p, c); + return V3Dot(vv, vv); + } + + //check if p in edge region of AB + const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3ScaleAdd(ab, sScale, a);//V3Add(a, V3Scale(ab, sScale)); + const Vec3V vv = V3Sub(p, closest3); + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3ScaleAdd(bc, uScale, b);//V3Add(b, V3Scale(bc, uScale)); + const Vec3V vv = V3Sub(p, closest4); + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3ScaleAdd(ac, tScale, a);//V3Add(a, V3Scale(ac, tScale)); + const Vec3V vv = V3Sub(p, closest5); + return V3Dot(vv, vv); + } + + //P must project inside face region. Compute Q using Barycentric coordinates + const Vec3V n = V3Cross(ab, ac); + const FloatV nn = V3Dot(n,n); + const FloatV t = FSel(FIsGrtr(nn, zero),FDiv(V3Dot(n, V3Sub(a, p)), nn), zero); + const Vec3V closest6 = V3Add(p, V3Scale(n, t)); + + const Vec3V vv = V3Sub(p, closest6); + + return V3Dot(vv, vv); +} + + + + +//This is the translational threshold used in invalidate_BoxConvexHull. 0.5 is 50% of the object margin. we use different threshold between +//0 and 4 points. This threashold is a scale that is multiplied by the objects' margins. +const PxF32 invalidateThresholds[5] = { 0.5f, 0.125f, 0.25f, 0.375f, 0.375f }; + +//This is the translational threshold used in invalidate_SphereCapsule. 0.5 is 50% of the object margin, we use different threshold between +//0 and 2 points. This threshold is a scale that is multiplied by the objects' margin + +const PxF32 invalidateThresholds2[3] = { 0.5f, 0.1f, 0.75f }; + +//This is the rotational threshold used in invalidate_BoxConvexHull. 0.9998 is a threshold for quat difference +//between previous and current frame +const PxF32 invalidateQuatThresholds[5] = { 0.9998f, 0.9999f, 0.9999f, 0.9999f, 0.9999f }; + + +//This is the rotational threshold used in invalidate_SphereCapsule. 0.9995f is a threshold for quat difference +//between previous and current frame +const PxF32 invalidateQuatThresholds2[3] = { 0.9995f, 0.9999f, 0.9997f }; + +} +} + + +#if VISUALIZE_PERSISTENT_CONTACT +#include "CmRenderOutput.h" + +static void drawManifoldPoint(const Gu::PersistentContact& manifold, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, Cm::RenderOutput& out, PxU32 color=0xffffff) +{ + PX_UNUSED(color); + + using namespace Ps::aos; + const Vec3V worldA = trA.transform(manifold.mLocalPointA); + const Vec3V worldB = trB.transform(manifold.mLocalPointB); + const Vec3V localNormal = Vec3V_From_Vec4V(manifold.mLocalNormalPen); + const FloatV pen = V4GetW(manifold.mLocalNormalPen); + + const Vec3V worldNormal = trB.rotate(localNormal); + PxVec3 a, b, v; + V3StoreU(worldA, a); + V3StoreU(worldB, b); + V3StoreU(worldNormal, v); + PxF32 dist; + FStore(pen, &dist); + PxVec3 e = a - v*dist; + + PxF32 size = 0.05f; + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxF32 size2 = 0.1f; + const PxVec3 up2(0.f, size2, 0.f); + const PxVec3 right2(size2, 0.f, 0.f); + const PxVec3 forwards2(0.f, 0.f, size2); + + PxMat44 m = PxMat44(PxIdentity); + + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << e; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + up << a - up; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + right << a - right; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + up2 << b - up2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + right2 << b - right2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + forwards2 << b - forwards2; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << a << b; + +} + +static void drawManifoldPoint(const Gu::PersistentContact& manifold, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius, Cm::RenderOutput& out, PxU32 color=0xffffff) +{ + PX_UNUSED(color); + + using namespace Ps::aos; + + const Vec3V localNormal = Vec3V_From_Vec4V(manifold.mLocalNormalPen); + const Vec3V worldNormal = trB.rotate(localNormal); + const Vec3V worldA = V3NegScaleSub(worldNormal, radius, trA.transform(manifold.mLocalPointA)); + const Vec3V worldB = trB.transform(manifold.mLocalPointB); + const FloatV pen = FSub(V4GetW(manifold.mLocalNormalPen), radius); + + PxVec3 a, b, v; + V3StoreU(worldA, a); + V3StoreU(worldB, b); + V3StoreU(worldNormal, v); + PxF32 dist; + FStore(pen, &dist); + PxVec3 e = a - v*dist; + + PxF32 size = 0.05f; + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxF32 size2 = 0.1f; + const PxVec3 up2(0.f, size2, 0.f); + const PxVec3 right2(size2, 0.f, 0.f); + const PxVec3 forwards2(0.f, 0.f, size2); + + PxMat44 m = PxMat44(PxIdentity); + + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << e; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + up << a - up; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + right << a - right; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + up2 << b - up2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + right2 << b - right2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + forwards2 << b - forwards2; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << a << b; + +} + + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; + +#endif + +/* + SIMD version +*/ +Ps::aos::Mat33V Gu::findRotationMatrixFromZAxis(const Ps::aos::Vec3VArg to) +{ + using namespace Ps::aos; + + const FloatV one = FOne(); + const FloatV threshold = FLoad(0.9999f); + + const FloatV e = V3GetZ(to); + const FloatV f = FAbs(e); + + if(FAllGrtr(threshold, f)) + { + const FloatV vx = FNeg(V3GetY(to)); + const FloatV vy = V3GetX(to); + const FloatV h = FRecip(FAdd(one, e)); + const FloatV hvx = FMul(h,vx); + const FloatV hvxy = FMul(hvx, vy); + + const Vec3V col0 = V3Merge(FScaleAdd(hvx, vx, e), hvxy, vy); + const Vec3V col1 = V3Merge(hvxy, FScaleAdd(h, FMul(vy, vy), e), FNeg(vx)); + const Vec3V col2 = V3Merge(FNeg(vy), vx, e); + + return Mat33V(col0, col1, col2); + + } + else + { + + const FloatV two = FLoad(2.f); + const Vec3V from = V3UnitZ(); + const Vec3V absFrom = V3UnitY(); + + const Vec3V u = V3Sub(absFrom, from); + const Vec3V v = V3Sub(absFrom, to); + + const FloatV dotU = V3Dot(u, u); + const FloatV dotV = V3Dot(v, v); + const FloatV dotUV = V3Dot(u, v); + + const FloatV c1 = FNeg(FDiv(two, dotU)); + const FloatV c2 = FNeg(FDiv(two, dotV)); + const FloatV c3 = FMul(c1, FMul(c2, dotUV)); + + const Vec3V c1u = V3Scale(u, c1); + const Vec3V c2v = V3Scale(v, c2); + const Vec3V c3v = V3Scale(v, c3); + + + FloatV temp0 = V3GetX(c1u); + FloatV temp1 = V3GetX(c2v); + FloatV temp2 = V3GetX(c3v); + + Vec3V col0 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col0 = V3SetX(col0, FAdd(V3GetX(col0), one)); + + temp0 = V3GetY(c1u); + temp1 = V3GetY(c2v); + temp2 = V3GetY(c3v); + + Vec3V col1 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col1 = V3SetY(col1, FAdd(V3GetY(col1), one)); + + temp0 = V3GetZ(c1u); + temp1 = V3GetZ(c2v); + temp2 = V3GetZ(c3v); + + Vec3V col2 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col2 = V3SetZ(col2, FAdd(V3GetZ(col2), one)); + + return Mat33V(col0, col1, col2); + + } +} + + +void Gu::PersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::PersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); +#endif +} + +void Gu::PersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::PersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, radius, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); + PX_UNUSED(radius); +#endif +} + +void Gu::PersistentContactManifold::drawManifold(const Gu::PersistentContact& m, Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ +#if VISUALIZE_PERSISTENT_CONTACT + drawManifoldPoint(m, trA, trB, out, gColors[0]); +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); + PX_UNUSED(m); +#endif +} + +void Gu::PersistentContactManifold::drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxVec3 a; + V3StoreU(p, a); + + PxMat44 m = PxMat44(PxIdentity); + + out << color << m << Cm::RenderOutput::LINES << a + up << a - up; + out << color << m << Cm::RenderOutput::LINES << a + right << a - right; + out << color << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; +#else + PX_UNUSED(out); + PX_UNUSED(p); + PX_UNUSED(size); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b; + V3StoreU(p0, a); + V3StoreU(p1, b); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << a << b; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawTriangle(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b, c; + V3StoreU(p0, a); + V3StoreU(p1, b); + V3StoreU(p2, c); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(p2); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; i<numVerts; ++i) + { + Vec3V tempV0 = points[i == 0 ? numVerts-1 : i-1]; + Vec3V tempV1 = points[i]; + + drawLine(out, transform.transform(tempV0), transform.transform(tempV1), color); + } +#else + PX_UNUSED(out); + PX_UNUSED(transform); + PX_UNUSED(points); + PX_UNUSED(numVerts); + PX_UNUSED(color); +#endif + +} + +void Gu::PersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsMatTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; i<numVerts; ++i) + { + Vec3V tempV0 = points[i == 0 ? numVerts-1 : i-1]; + Vec3V tempV1 = points[i]; + + drawLine(out, transform.transform(tempV0), transform.transform(tempV1), color); + } +#else + PX_UNUSED(out); + PX_UNUSED(transform); + PX_UNUSED(points); + PX_UNUSED(numVerts); + PX_UNUSED(color); +#endif + +} + +/* + If a new point and the exisitng point's distance are within some replace breaking threshold, we will replace the existing point with the new point. This is used for + incremental manifold strategy. +*/ +bool Gu::PersistentContactManifold::replaceManifoldPoint(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen + , const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + using namespace Ps::aos; + + const FloatV shortestDist = FMul(replaceBreakingThreshold, replaceBreakingThreshold); + + for( PxU32 i = 0; i < mNumContacts; ++i ) + { + const PersistentContact& mp = mContactPoints[i]; + + const Vec3V diffB = V3Sub(mp.mLocalPointB, localPointB); + const FloatV sqDifB = V3Dot(diffB, diffB); + const Vec3V diffA = V3Sub(mp.mLocalPointA, localPointA); + const FloatV sqDifA = V3Dot(diffA, diffA); + const FloatV minSqDif = FMin(sqDifB, sqDifA); + + if (FAllGrtr(shortestDist, minSqDif)) + { + mContactPoints[i].mLocalPointA = localPointA; + mContactPoints[i].mLocalPointB = localPointB; + mContactPoints[i].mLocalNormalPen = localNormalPen; + return true; + } + + } + + return false; +} + + + +PxU32 Gu::PersistentContactManifold::reduceContactSegment(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen) +{ + using namespace Ps::aos; + const Vec3V p = localPointB; + const Vec3V p0 = mContactPoints[0].mLocalPointB; + const Vec3V p1 = mContactPoints[1].mLocalPointB; + const Vec3V v0 = V3Sub(p0, p); + const Vec3V v1 = V3Sub(p1, p); + const FloatV dist0 = V3Dot(v0, v0); + const FloatV dist1 = V3Dot(v1, v1); + if(FAllGrtr(dist0, dist1)) + { + mContactPoints[1].mLocalPointA = localPointA; + mContactPoints[1].mLocalPointB = localPointB; + mContactPoints[1].mLocalNormalPen = localNormalPen; + } + else + { + mContactPoints[0].mLocalPointA = localPointA; + mContactPoints[0].mLocalPointB = localPointB; + mContactPoints[0].mLocalNormalPen = localNormalPen; + } + + return 0; +} + + + +PxU32 Gu::PersistentContactManifold::reduceContactsForPCM(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen) +{ + using namespace Ps::aos; + + + bool chosen[5]; + physx::PxMemZero(chosen, sizeof(bool)*5); + const FloatV negMax = FNeg(FMax()); + PersistentContact tempContacts[5]; + + for(PxU32 i=0; i<4; ++i) + { + tempContacts[i] = mContactPoints[i]; + } + tempContacts[4].mLocalPointA = localPointA; + tempContacts[4].mLocalPointB = localPointB; + tempContacts[4].mLocalNormalPen = localNormalPen; + + //ML: we set the start point to be the 4th point + FloatV maxDist =V4GetW(localNormalPen); + PxI32 index = 4; + //Choose deepest point + for(PxI32 i=0; i<4; ++i) + { + const FloatV pen = V4GetW(tempContacts[i].mLocalNormalPen); + if(FAllGrtr(maxDist, pen)) + { + maxDist = pen; + index = i; + } + } + + chosen[index] = true; + mContactPoints[0] = tempContacts[index]; + + //ML: we set the start point to be the 0th point + Vec3V dir= V3Sub(tempContacts[0].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDist = V3Dot(dir, dir); + index = 0; + + for(PxI32 i=1; i<5; ++i) + { + if(!chosen[i]) + { + dir = V3Sub(tempContacts[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(dir, dir); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index = i; + } + } + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[1] = tempContacts[index]; + + maxDist = negMax; + for(PxI32 i=0; i<5; ++i) + { + if(!chosen[i]) + { + const FloatV sqDif = distancePointSegmentSquaredLocal(mContactPoints[0].mLocalPointB, mContactPoints[1].mLocalPointB, tempContacts[i].mLocalPointB); + if(FAllGrtr(sqDif, maxDist)) + { + maxDist = sqDif; + index = i; + } + } + } + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[2]=tempContacts[index]; + + //Find point farthest away from segment tempContactPoints[0] - tempContactPoints[1] + maxDist = negMax; + for(PxI32 i=0; i<5; ++i) + { + if(!chosen[i]) + { + const FloatV sqDif = distancePointTriangleSquaredLocal( tempContacts[i].mLocalPointB, mContactPoints[0].mLocalPointB, mContactPoints[1].mLocalPointB, mContactPoints[2].mLocalPointB); + if(FAllGrtr(sqDif, maxDist)) + { + maxDist= sqDif; + index = i; + } + } + } + + //PX_ASSERT(chosen[index] == false); + if(chosen[index] == true) + { + //if we don't have any new contacts, which means the leftover contacts are inside the triangles + mNumContacts = 3; + return 0; + } + else + { + chosen[index] = true; + mContactPoints[3]=tempContacts[index]; + } + + //Final pass, we work out the index that we didn't choose and bind it to its closest point. We then consider whether we want to swap the point if the + //point we were about to discard is deeper... + + PxU32 notChosenIndex = 0; + for(PxU32 a = 0; a < 5; ++a) + { + if(!chosen[a]) + { + notChosenIndex = a; + break; + } + } + + FloatV closest = FMax(); + index = 0; + for(PxI32 a = 0; a < 4; ++a) + { + Vec3V dif = V3Sub(mContactPoints[a].mLocalPointA, tempContacts[notChosenIndex].mLocalPointA); + const FloatV d2 = V3Dot(dif, dif); + if(FAllGrtr(closest, d2)) + { + closest = d2; + index = a; + } + } + + if(FAllGrtr(V4GetW(mContactPoints[index].mLocalNormalPen), V4GetW(tempContacts[notChosenIndex].mLocalNormalPen))) + { + //Swap + mContactPoints[index] = tempContacts[notChosenIndex]; + } + + + return 0; +} + + +/* + This function is for box/convexHull vs box/convexHull. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsTransformV& transf1, const Ps::aos::FloatVArg contactOffset) +{ + using namespace Ps::aos; + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + PersistentContact& p = getContactPoint(i); + + const FloatV dist = V4GetW(p.mLocalNormalPen); + + //Either the newly created points or the cache points might have dist/penetration larger than contactOffset + if(FAllGrtrOrEq(contactOffset, dist)) + { + const Vec3V worldP =transf1.transform(p.mLocalPointB); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast<PxF32*>(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast<PxF32*>(&contact.point.x)); + FStore(dist, &contact.separation); + + PX_ASSERT(contact.point.isFinite()); + PX_ASSERT(contact.normal.isFinite()); + PX_ASSERT(PxIsFinite(contact.separation)); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + + } + + contactBuffer.count = contactCount; +} + +/* + This function is for direct implementation for box vs box. We don't need to discard the contacts based on whether the contact offset is larger than penetration/dist because + the direct implementation will guarantee the penetration/dist won't be larger than the contact offset. Also, we won't have points from the cache if the direct implementation + of box vs box is called. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsMatTransformV& transf1) +{ + using namespace Ps::aos; + + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + + PersistentContact& p = getContactPoint(i); + + const Vec3V worldP =transf1.transform(p.mLocalPointB); + const FloatV dist = V4GetW(p.mLocalNormalPen); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast<PxF32*>(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast<PxF32*>(&contact.point.x)); + FStore(dist, &contact.separation); + PX_ASSERT(PxIsFinite(contact.point.x)); + PX_ASSERT(PxIsFinite(contact.point.y)); + PX_ASSERT(PxIsFinite(contact.point.z)); + + //PX_ASSERT(contact.separation > -0.2f); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + + contactBuffer.count = contactCount; +} + +/* + This function is for sphere/capsule vs other primitives. We treat sphere as a point and capsule as a segment in the contact gen and store the sphere center or a point in the segment for capsule + in the manifold. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsTransformV& transf0, const Ps::aos::FloatVArg radius, const Ps::aos::FloatVArg contactOffset) +{ + using namespace Ps::aos; + + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + + PersistentContact& p = getContactPoint(i); + const FloatV dist = FSub(V4GetW(p.mLocalNormalPen), radius); + + //The newly created points should have a dist < contactOffset. However, points might come from the PCM contact cache so we might still have points which are + //larger than contactOffset. The reason why we don't want to discard the contacts in the contact cache whose dist > contactOffset is because the contacts may + //only temporarily become separated. The points still project onto roughly the same spot but so, if the bodies move together again, the contacts may be valid again. + //This is important when simulating at large time-steps because GJK can only generate 1 point of contact and missing contacts for just a single frame with large time-steps + //may introduce noticeable instability. + if(FAllGrtrOrEq(contactOffset, dist)) + { + const Vec3V worldP =V3NegScaleSub(normal, radius, transf0.transform(p.mLocalPointA)); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast<PxF32*>(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast<PxF32*>(&contact.point.x)); + FStore(dist, &contact.separation); + //PX_ASSERT(PxAbs(contact.separation) < 2.f); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + } + + contactBuffer.count = contactCount; +} + + +/* + This function is used in the box/convexhull full manifold contact genenation. We will pass in a list of manifold contacts. If the number of contacts are more than + GU_MANIFOLD_CACHE_SIZE, we will need to do contact reduction while we are storing the chosen manifold contacts from the manifold contact list to the manifold contact + buffer. +*/ +void Gu::PersistentContactManifold::addBatchManifoldContacts(const PersistentContact* manifoldContacts, const PxU32 numPoints) +{ + + if(numPoints <= GU_MANIFOLD_CACHE_SIZE) + { + for(PxU32 i=0; i<numPoints; ++i) + { + mContactPoints[i].mLocalPointA = manifoldContacts[i].mLocalPointA; + mContactPoints[i].mLocalPointB = manifoldContacts[i].mLocalPointB; + mContactPoints[i].mLocalNormalPen = manifoldContacts[i].mLocalNormalPen; + } + mNumContacts = Ps::to8(numPoints); + } + else + { + reduceBatchContacts(manifoldContacts, numPoints); + mNumContacts = GU_MANIFOLD_CACHE_SIZE; + } +} + +/* + This function is for the plane and box contact gen. If the number of points passed in is more than 4, we need to do contact reduction +*/ +void Gu::PersistentContactManifold::addBatchManifoldContactsCluster(const PersistentContact* manifoldContacts, const PxU32 numPoints) +{ + + if(numPoints <= GU_MANIFOLD_CACHE_SIZE) + { + for(PxU32 i=0; i<numPoints; ++i) + { + mContactPoints[i].mLocalPointA = manifoldContacts[i].mLocalPointA; + mContactPoints[i].mLocalPointB = manifoldContacts[i].mLocalPointB; + mContactPoints[i].mLocalNormalPen = manifoldContacts[i].mLocalNormalPen; + } + mNumContacts = Ps::to8(numPoints); + } + else + { + reduceBatchContactsCluster(manifoldContacts, numPoints); + mNumContacts = GU_MANIFOLD_CACHE_SIZE; + } +} + + +/* + This function is called by addBatchManifoldContactCluster. The logic in this funtion is: + (1)get the furthest away point from origin and store in mContactPoints[0] + (2)get the furthest away point from mContactPoints[0] and store in mContactPoints[1] + (3)calculate the min and max distance point away the segment (mContactPoints[0] and mContactPoints[1]) and store the max distance point to mContactPoints[2] + (4)if the min and max distance on the same side of the segment, we need to chose the min distance point again and store this point to mContactPoints[3]; + (5)cluster around that 4 points and chose the deepest points + (6)reassign contact points +*/ +void Gu::PersistentContactManifold::reduceBatchContactsCluster(const PersistentContact* manifoldPoints, const PxU32 numPoints) +{ + using namespace Ps::aos; + //get the deepest points + + bool chosen[64]; + physx::PxMemZero(chosen, sizeof(bool)*numPoints); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxDist = nmax; + PxU32 index = 0; + + PxU32 indices[4]; + + //get the furthest away point from itself + for(PxU32 i=0; i<numPoints; ++i) + { + const FloatV dist = V3Dot(manifoldPoints[i].mLocalPointB, manifoldPoints[i].mLocalPointB); + if(FAllGrtr(dist, maxDist)) + { + maxDist = dist; + index = i; + } + } + + //keep the furthest points in the first position + mContactPoints[0] = manifoldPoints[index]; + chosen[index] = true; + indices[0] = index; + + + //calculate the furthest away points from mContactPoints[0] + Vec3V v = V3Sub(manifoldPoints[0].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDist = V3Dot(v, v); + index = 0; + + for(PxU32 i=1; i<numPoints; ++i) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index = i; + } + } + + //PX_ASSERT(chosen[index] == false); + mContactPoints[1] = manifoldPoints[index]; + chosen[index] = true; + indices[1] = index; + + + maxDist = nmax; + index = PxU32(-1); + v = V3Sub(mContactPoints[1].mLocalPointB, mContactPoints[0].mLocalPointB); + Vec3V norm = V3Normalize(V3Cross(v, Vec3V_From_Vec4V(mContactPoints[0].mLocalNormalPen))); + + FloatV minDist = max; + PxU32 index1 = PxU32(-1); + + + //calculate the min and max point away from the segment + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index = i; + } + + if(FAllGrtr(minDist, d)) + { + minDist = d; + index1 = i; + } + } + } + + //PX_ASSERT(chosen[index] == false && chosen[index1] == false); + mContactPoints[2] = manifoldPoints[index]; + chosen[index] = true; + indices[2] = index; + + //if min and max in the same side, chose again + const FloatV temp = FMul(minDist, maxDist); + if(FAllGrtr(temp, FZero())) + { + //chose again + maxDist = nmax; + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index1 = i; + } + } + } + } + + mContactPoints[3] = manifoldPoints[index1]; + chosen[index1] = true; + indices[3] = index1; + + //cluster around that 4 points and chose the deepest points + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + maxDist = max; + const FloatV pen = V4GetW(manifoldPoints[i].mLocalNormalPen); + index = 0; + for(PxU32 j=0; j<4; ++j) + { + const Vec3V v1 = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[j].mLocalPointB); + const FloatV dist = V3Dot(v1, v1); + if(FAllGrtr(maxDist, dist)) + { + maxDist = dist; + index = j; + } + } + + //check to see whether the penetration is deeper than the point in mContactPoints + const FloatV tempPen = V4GetW(manifoldPoints[indices[index]].mLocalNormalPen); + if(FAllGrtr(tempPen, pen)) + { + //swap the indices + indices[index] = i; + } + } + } + + mContactPoints[0] = manifoldPoints[indices[0]]; + mContactPoints[1] = manifoldPoints[indices[1]]; + mContactPoints[2] = manifoldPoints[indices[2]]; + mContactPoints[3] = manifoldPoints[indices[3]]; + + +} + +/* + This function is for box/convexhull full contact generation. If the numPoints > 4, we will reduce the contact points to 4 +*/ +void Gu::PersistentContactManifold::reduceBatchContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints) +{ + using namespace Ps::aos; + + bool chosen[64]; + physx::PxMemZero(chosen, sizeof(bool)*numPoints); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxDist = V4GetW(manifoldPoints[0].mLocalNormalPen); + PxI32 index = 0; + //keep the deepest point + for(PxU32 i=1; i<numPoints; ++i) + { + const FloatV pen = V4GetW(manifoldPoints[i].mLocalNormalPen); + if(FAllGrtr(maxDist, pen)) + { + maxDist = pen; + index = PxI32(i); + } + } + //keep the deepest points in the first position + mContactPoints[0] = manifoldPoints[index]; + chosen[index] = true; + + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldPoints[0].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDist = V3Dot(v, v); + index = 0; + + for(PxU32 i=1; i<numPoints; ++i) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index = PxI32(i); + } + } + + //PX_ASSERT(chosen[index] == false); + mContactPoints[1] = manifoldPoints[index]; + chosen[index] = true; + + + maxDist = nmax; + index = -1; + + v = V3Sub(mContactPoints[1].mLocalPointB, mContactPoints[0].mLocalPointB); + const Vec3V vCross = V3Cross(v, Vec3V_From_Vec4V(mContactPoints[0].mLocalNormalPen)); + const FloatV sqLen = V3Dot(vCross, vCross); + const Vec3V norm = V3Sel(FIsGrtr(sqLen, FZero()), V3ScaleInv(vCross, FSqrt(sqLen)), V3Zero()); + + FloatV minDist = max; + PxI32 index1 = -1; + + + //calculate the min and max point away from the segment + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index = PxI32(i); + } + + if(FAllGrtr(minDist, d)) + { + minDist = d; + index1 = PxI32(i); + } + } + } + + mContactPoints[2] = manifoldPoints[index]; + chosen[index] = true; + + //if min and max in the same side, chose again + const FloatV temp = FMul(minDist, maxDist); + if(FAllGrtr(temp, FZero())) + { + //chose again + maxDist = nmax; + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDist)) + { + maxDist = d; + index1 = PxI32(i); + } + } + } + } + + mContactPoints[3] = manifoldPoints[index1]; +} + +/* + This function is for capsule full contact generation. If the numPoints > 2, we will reduce the contact points to 2 +*/ +void Gu::PersistentContactManifold::reduceBatchContacts2(const PersistentContact* manifoldPoints, const PxU32 numPoints) +{ + using namespace Ps::aos; + + PX_ASSERT(numPoints < 64); + bool chosen[64]; + physx::PxMemZero(chosen, sizeof(bool)*numPoints); + FloatV maxDis = V4GetW(manifoldPoints[0].mLocalNormalPen); + PxI32 index = 0; + //keep the deepest point + for(PxU32 i=1; i<numPoints; ++i) + { + const FloatV pen = V4GetW(manifoldPoints[i].mLocalNormalPen); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + //keep the deepest points in the first position + mContactPoints[0] = manifoldPoints[index]; + chosen[index] = true; + + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldPoints[0].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDis = V3Dot(v, v); + index = 0; + + for(PxU32 i=1; i<numPoints; ++i) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = PxI32(i); + } + } + + //PX_ASSERT(chosen[index] == false); + mContactPoints[1] = manifoldPoints[index]; + chosen[index] = true; + + PxI32 secondIndex = index; + FloatV maxDepth = V4GetW(manifoldPoints[index].mLocalNormalPen); + for(PxU32 i = 0; i < numPoints; ++i) + { + if(!chosen[i]) + { + Vec3V d0 = V3Sub(mContactPoints[0].mLocalPointB, manifoldPoints[i].mLocalPointB); + Vec3V d1 = V3Sub(mContactPoints[1].mLocalPointB, manifoldPoints[i].mLocalPointB); + const FloatV dd0 = V3Dot(d0, d0); + const FloatV dd1 = V3Dot(d1, d1); + + if(FAllGrtr(dd0, dd1)) + { + //This clusters to point 1 + if(FAllGrtr(maxDepth, V4GetW(manifoldPoints[i].mLocalNormalPen))) + { + secondIndex = PxI32(i); + } + } + } + } + + if(secondIndex != index) + { + mContactPoints[1] = manifoldPoints[secondIndex]; + } +} + +PxU32 Gu::PersistentContactManifold::addManifoldPoint(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen + , const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + using namespace Ps::aos; + + if(replaceManifoldPoint(localPointA, localPointB, localNormalPen, replaceBreakingThreshold)) //replace the new point with the old one + return 0; + + switch(mNumContacts) + { + case 0: + case 1: + case 2: + case 3: + mContactPoints[mNumContacts].mLocalPointA = localPointA; + mContactPoints[mNumContacts].mLocalPointB = localPointB; + mContactPoints[mNumContacts++].mLocalNormalPen = localNormalPen; + + return 1; + default: + return reduceContactsForPCM(localPointA, localPointB, localNormalPen);//should be always return zero + }; + +} + + +/* + This function is for capsule vs other primitives. If the manifold originally has contacts and we can incrementally add a point at a time, we will + use this function to add a point to manifold. If the number of contacts inside the manifold is more than 2, we will reduce contacts to 2 points. +*/ +PxU32 Gu::PersistentContactManifold::addManifoldPoint2(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen + , const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + using namespace Ps::aos; + + if(replaceManifoldPoint(localPointA, localPointB, localNormalPen, replaceBreakingThreshold)) //replace the new point with the old one + return 0; + + switch(mNumContacts) + { + case 0: + case 1: + mContactPoints[mNumContacts].mLocalPointA = localPointA; + mContactPoints[mNumContacts].mLocalPointB = localPointB; + mContactPoints[mNumContacts++].mLocalNormalPen = localNormalPen; + return 1; + case 2: + return reduceContactSegment(localPointA, localPointB, localNormalPen); + default: + PX_ASSERT(0); + }; + return 0; + +} + +/* + This function is used in the capsule full manifold contact genenation. We will pass in a list of manifold contacts. If the number of contacts are more than + 2, we will need to do contact reduction while we are storing the chosen manifold contacts from the manifold contact list to the manifold contact + buffer. +*/ +void Gu::PersistentContactManifold::addBatchManifoldContacts2(const PersistentContact* manifoldContacts, const PxU32 numPoints) +{ + using namespace Ps::aos; + + if(numPoints <= 2) + { + for(PxU32 i=0; i<numPoints; ++i) + { + mContactPoints[i].mLocalPointA = manifoldContacts[i].mLocalPointA; + mContactPoints[i].mLocalPointB = manifoldContacts[i].mLocalPointB; + mContactPoints[i].mLocalNormalPen = manifoldContacts[i].mLocalNormalPen; + } + + mNumContacts = Ps::to8(numPoints); + } + else + { + reduceBatchContacts2(manifoldContacts, numPoints); + mNumContacts = 2; + } + +} + + +/* + If the patch total number of manifold contacts are less than or equal to GU_SINGLE_MANIFOLD_CACHE_SIZE, we will add the manifold contacts in the contact stream to the manifold contact buffer + which is associated to this single persistent contact manifold. Otherwise, we will reduce the manifold contacts to GU_SINGLE_MANIFOLD_CACHE_SIZE. +*/ +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsConvex(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + if(patch.mTotalSize <= GU_SINGLE_MANIFOLD_CACHE_SIZE) + { + PCMContactPatch* currentPatch = &patch; + + //this is because we already add the manifold contacts into manifoldContact array + PxU32 tempNumContacts = 0; + while(currentPatch) + { + for(PxU32 j=currentPatch->mStartIndex; j<currentPatch->mEndIndex; ++j) + { + mContactPoints[tempNumContacts++] = manifoldContact[j]; + } + currentPatch = currentPatch->mNextPatch; + } + mNumContacts = tempNumContacts; + return patch.mPatchMaxPen; + } + else + { + //contact reduction + const FloatV maxPen = reduceBatchContactsConvex(manifoldContact, numContactExt, patch); + mNumContacts = GU_SINGLE_MANIFOLD_CACHE_SIZE; + return maxPen; + } +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsSphere(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + const FloatV maxPen = reduceBatchContactsSphere(manifoldContact, numContactExt, patch); + mNumContacts = 1; + return maxPen; +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsCapsule(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + if(patch.mTotalSize <=GU_CAPSULE_MANIFOLD_CACHE_SIZE) + { + PCMContactPatch* currentPatch = &patch; + + //this is because we already add the manifold contacts into manifoldContact array + PxU32 tempNumContacts = 0; + while(currentPatch) + { + for(PxU32 j=currentPatch->mStartIndex; j<currentPatch->mEndIndex; ++j) + { + mContactPoints[tempNumContacts++] = manifoldContact[j]; + } + currentPatch = currentPatch->mNextPatch; + } + mNumContacts = tempNumContacts; + return patch.mPatchMaxPen; + } + else + { + + const FloatV maxPen = reduceBatchContactsCapsule(manifoldContact, numContactExt, patch); + mNumContacts = GU_CAPSULE_MANIFOLD_CACHE_SIZE; + return maxPen; + } + +} + + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsSphere(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + PX_UNUSED(numContacts); + + using namespace Ps::aos; + FloatV max = FMax(); + FloatV maxDist = max; + PxI32 index = -1; + + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + if(FAllGrtr(maxDist, pen)) + { + maxDist = pen; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + PX_ASSERT(index!=-1); + mContactPoints[0] = manifoldContactExt[index]; + + return maxDist; + +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsCapsule(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast<bool*>(PxAlloca(sizeof(bool)*numContacts)); + physx::PxMemZero(chosen, sizeof(bool)*numContacts); + const FloatV max = FMax(); + FloatV maxDis = max; + PxI32 index = -1; + + FloatV maxPen = max; + + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + chosen[index] = true; + mContactPoints[0] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldContactExt[patch.mStartIndex].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDis = V3Dot(v, v); + index = PxI32(patch.mStartIndex); + + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[1] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + //keep the second deepest point + maxDis = max; + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + if(!chosen[i]) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + //const FloatV v = V3Dot(manifoldContactExt[i].mLocalPointB, manifoldContactExt[i].mLocalPointB); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + } + currentPatch = currentPatch->mNextPatch; + } + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[2] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + return maxPen; +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsConvex(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast<bool*>(PxAlloca(sizeof(bool)*numContacts)); + physx::PxMemZero(chosen, sizeof(bool)*numContacts); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxDis = nmax; + PxI32 index = -1; + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + //const FloatV pen = V4GetW(manifoldContactExt[i].localNormalPen); + const FloatV v = V3Dot(manifoldContactExt[i].mLocalPointB, manifoldContactExt[i].mLocalPointB); + if(FAllGrtr(v, maxDis)) + { + maxDis = v; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + chosen[index] = true; + + const Vec3V contact0 = manifoldContactExt[index].mLocalPointB; + mContactPoints[0] = manifoldContactExt[index]; + + FloatV maxPen = V4GetW(manifoldContactExt[index].mLocalNormalPen); + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldContactExt[patch.mStartIndex].mLocalPointB, contact0); + maxDis = V3Dot(v, v); + index = PxI32(patch.mStartIndex); + + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + const Vec3V contact1 = manifoldContactExt[index].mLocalPointB; + mContactPoints[1] = manifoldContactExt[index]; + + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + maxDis = nmax; + index = -1; + v = V3Sub(contact1, contact0); + Vec3V norm = V3Normalize(V3Cross(v, Vec3V_From_Vec4V(mContactPoints[0].mLocalNormalPen))); + FloatV minDis = max; + PxI32 index1 = -1; + + + //calculate the point furthest way to the segment + currentPatch = &patch; + + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = PxI32(i); + } + + if(FAllGrtr(minDis, d)) + { + minDis = d; + index1 = PxI32(i); + } + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + + chosen[index] = true; + mContactPoints[2] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + //if min and max in the same side, choose again + const FloatV temp = FMul(minDis, maxDis); + if(FAllGrtr(temp, FZero())) + { + //choose again + maxDis = nmax; + //calculate the point furthest way to the segment + currentPatch = &patch; + + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; i<currentPatch->mEndIndex; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index1 = PxI32(i); + } + } + } + currentPatch = currentPatch->mNextPatch; + } + } + + //PX_ASSERT(chosen[index1] == false); + + chosen[index1] = true; + mContactPoints[3] = manifoldContactExt[index1]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index1].mLocalNormalPen)); + + const PxU32 NB_TO_ADD = GU_SINGLE_MANIFOLD_CACHE_SIZE - 4; + + FloatV pens[NB_TO_ADD]; + PxU32 inds[NB_TO_ADD]; + for (PxU32 a = 0; a < NB_TO_ADD; ++a) + { + pens[a] = FMax(); + inds[a] = 0; + } + + + + { + currentPatch = &patch; + + while (currentPatch) + { + for (PxU32 i = currentPatch->mStartIndex; i < currentPatch->mEndIndex; ++i) + { + if (!chosen[i]) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + for (PxU32 a = 0; a < NB_TO_ADD; ++a) + { + if (FAllGrtr(pens[a], pen)) + { + for (PxU32 b = a + 1; b < NB_TO_ADD; ++b) + { + pens[b] = pens[b - 1]; + inds[b] = inds[b - 1]; + } + pens[a] = pen; + inds[a] = i; + break; + } + } + } + } + currentPatch = currentPatch->mNextPatch; + } + for (PxU32 i = 0; i < NB_TO_ADD; ++i) + { + mContactPoints[i + 4] = manifoldContactExt[inds[i]]; + maxPen = FMin(maxPen, pens[i]); + } + } + + + + + return maxPen; +} + +PxU32 Gu::SinglePersistentContactManifold::reduceContacts(MeshPersistentContact* manifoldPoints, PxU32 numPoints) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast<bool*>(PxAlloca(sizeof(bool)*numPoints)); + physx::PxMemZero(chosen, sizeof(bool)*numPoints); + FloatV max = FMax(); + FloatV maxDis = max; + PxU32 index = 0xffffffff; + MeshPersistentContact newManifold[GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE]; + + //keep the deepest point + for(PxU32 i=0; i<numPoints; ++i) + { + const FloatV pen = V4GetW(manifoldPoints[i].mLocalNormalPen); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = i; + } + } + //keep the deepest points in the first position + newManifold[0] = manifoldPoints[index]; + chosen[index] = true; + + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldPoints[0].mLocalPointB, newManifold[0].mLocalPointB); + maxDis = V3Dot(v, v); + index = 0; + + for(PxU32 i=1; i<numPoints; ++i) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, newManifold[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = i; + } + } + + //PX_ASSERT(chosen[index] == false); + newManifold[1] = manifoldPoints[index]; + chosen[index] = true; + + + maxDis = FNeg(max); + index = 0xffffffff; + v = V3Sub(newManifold[1].mLocalPointB, newManifold[0].mLocalPointB); + Vec3V norm = V3Normalize(V3Cross(v, Vec3V_From_Vec4V(newManifold[0].mLocalNormalPen))); + FloatV minDis = max; + PxU32 index1 = 0xffffffff; + + + //calculate the point furthest way to the segment + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, newManifold[0].mLocalPointB); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = i; + } + + if(FAllGrtr(minDis, d)) + { + minDis = d; + index1 = i; + } + } + } + + //PX_ASSERT(chosen[index] == false && chosen[index1]== false); + + chosen[index] = true; + newManifold[2] = manifoldPoints[index]; + + const FloatV temp = FMul(minDis, maxDis); + if(FAllGrtr(temp, FZero())) + { + //chose the something further away from newManifold[2] + maxDis = FNeg(max); + for(PxU32 i=0; i<numPoints; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldPoints[i].mLocalPointB, newManifold[0].mLocalPointB); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index1 = i; + } + } + } + + } + + newManifold[3]= manifoldPoints[index1]; + chosen[index1] = true; + + maxDis = max; + index = 0xffffffff; + //choose the 5 point, second deepest in the left overlap point + for (PxU32 i = 0; i < numPoints; ++i) + { + if (!chosen[i]) + { + const FloatV pen = V4GetW(manifoldPoints[i].mLocalNormalPen); + if (FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = i; + } + } + } + + newManifold[4] = manifoldPoints[index]; + chosen[index] = true; + + //copy the new manifold back + for(PxU32 i=0; i<GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; ++i) + { + manifoldPoints[i] = newManifold[i]; + } + + return GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; +} + + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::refreshContactPoints(const Ps::aos::PsMatTransformV& aToB, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg /*contactOffset*/) +{ + using namespace Ps::aos; + const FloatV sqProjectBreakingThreshold = FMul(projectBreakingThreshold, projectBreakingThreshold); + + FloatV maxPen = FZero(); + // first refresh worldspace positions and distance + for (PxU32 i=mNumContacts; i > 0; --i) + { + MeshPersistentContact& manifoldPoint = mContactPoints[i-1]; + const Vec3V localAInB = aToB.transform( manifoldPoint.mLocalPointA ); // from a to b + const Vec3V localBInB = manifoldPoint.mLocalPointB; + const Vec3V v = V3Sub(localAInB, localBInB); + + const Vec3V localNormal = Vec3V_From_Vec4V(manifoldPoint.mLocalNormalPen); // normal in b space + const FloatV dist= V3Dot(v, localNormal); + + const Vec3V projectedPoint = V3NegScaleSub(localNormal, dist, localAInB);//manifoldPoint.worldPointA - manifoldPoint.worldPointB * manifoldPoint.m_distance1; + const Vec3V projectedDifference = V3Sub(localBInB, projectedPoint); + + const FloatV distance2d = V3Dot(projectedDifference, projectedDifference); + //const BoolV con = BOr(FIsGrtr(dist, contactOffset), FIsGrtr(distance2d, sqProjectBreakingThreshold)); + const BoolV con = FIsGrtr(distance2d, sqProjectBreakingThreshold); + if(BAllEqTTTT(con)) + { + removeContactPoint(i-1); + } + else + { + manifoldPoint.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), dist); + maxPen = FMin(maxPen, dist); + } + } + + return maxPen; +} + + +void Gu::SinglePersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::MeshPersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); +#endif +} + +void Gu::MultiplePersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + for(PxU32 i=0; i<mNumManifolds; ++i) + { + SinglePersistentContactManifold* manifold = getManifold(i); + manifold->drawManifold(out, trA, trB); + } +} + +void Gu::MultiplePersistentContactManifold::drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b; + V3StoreU(p0, a); + V3StoreU(p1, b); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << a << b; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); + +#endif +} + +void Gu::MultiplePersistentContactManifold::drawLine(Cm::RenderOutput& out, const PxVec3 p0, const PxVec3 p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << p0 << p1; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); +#endif +} + +void Gu::MultiplePersistentContactManifold::drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxVec3 a; + V3StoreU(p, a); + + PxMat44 m = PxMat44(PxIdentity); + + out << color << m << Cm::RenderOutput::LINES << a + up << a - up; + out << color << m << Cm::RenderOutput::LINES << a + right << a - right; + out << color << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; +#else + PX_UNUSED(out); + PX_UNUSED(p); + PX_UNUSED(size); + PX_UNUSED(color); +#endif +} + + +void Gu::MultiplePersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; i<numVerts; ++i) + { + Vec3V tempV0 = points[i == 0 ? numVerts-1 : i-1]; + Vec3V tempV1 = points[i]; + + drawLine(out, transform.transform(tempV0), transform.transform(tempV1), color); + } +#else + PX_UNUSED(out); + PX_UNUSED(transform); + PX_UNUSED(points); + PX_UNUSED(numVerts); + PX_UNUSED(color); +#endif + +} + +static Ps::aos::FloatV addBatchManifoldContactsToSingleManifold(Gu::SinglePersistentContactManifold* manifold, Gu::MeshPersistentContact* manifoldContact, PxU32 numManifoldContacts, Gu::PCMContactPatch* patch, const Ps::aos::FloatVArg sqReplaceBreakingThreshold, PxU8 maxContactsPerManifold) +{ + using namespace Ps::aos; + switch(maxContactsPerManifold) + { + case GU_SPHERE_MANIFOLD_CACHE_SIZE://sphere + return manifold->addBatchManifoldContactsSphere(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + case GU_CAPSULE_MANIFOLD_CACHE_SIZE://capsule, need to implement keep two deepest + return manifold->addBatchManifoldContactsCapsule(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + default://cache size GU_SINGLE_MANIFOLD_CACHE_SIZE + return manifold->addBatchManifoldContactsConvex(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + }; +} + +/* + This function adds the manifold contacts with different patches into the corresponding single persistent contact manifold +*/ +void Gu::MultiplePersistentContactManifold::addManifoldContactPoints(MeshPersistentContact* manifoldContact, PxU32 numManifoldContacts, PCMContactPatch** contactPatch, const PxU32 numPatch, const Ps::aos::FloatVArg sqReplaceBreakingThreshold, const Ps::aos::FloatVArg acceptanceEpsilon, PxU8 maxContactsPerManifold) +{ + using namespace Ps::aos; + + if(mNumManifolds == 0) + { + for(PxU32 i=0; i<numPatch; ++i) + { + PCMContactPatch* patch = contactPatch[i]; + //this mean the patch hasn't been add to the manifold + if(patch->mRoot == patch) + { + + SinglePersistentContactManifold* manifold = getEmptyManifold(); + if(manifold) + { + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[mNumManifolds]]); + mNumManifolds++; + } + else + { + //We already pre-sorted the patches so we know we can return here + return; + } + + } + } + + + } + else + { + //we do processContacts() when the number of contacts are more than 16, such that, we might call processContacts() multiple times when we have very detailed mesh + //or very large contact offset/objects. In this case, the mNumManifolds will be large than 0. + PCMContactPatch tempPatch; + for(PxU32 i=0; i<numPatch; ++i) + { + bool found = false; + PCMContactPatch* patch = contactPatch[i]; + //this mean the patch has't been add to the manifold + if(patch->mRoot == patch) + { + PX_ASSERT(mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + for(PxU32 j=0; j<mNumManifolds; ++j) + { + PX_ASSERT(mManifoldIndices[j] < GU_MAX_MANIFOLD_SIZE); + SinglePersistentContactManifold& manifold = *getManifold(j); + + const Vec3V pNor = manifold.getLocalNormal(); + const FloatV d = V3Dot(patch->mPatchNormal, pNor); + + if(FAllGrtrOrEq(d, acceptanceEpsilon)) + { + //appending the existing contacts to the manifold contact stream + for(PxU32 k=0; k< manifold.mNumContacts; ++k) + { + PxU32 index = k + numManifoldContacts; + //not duplicate point + PX_ASSERT(index < 64); + manifoldContact[index] = manifold.mContactPoints[k]; + } + + //create a new patch for the exiting manifold + tempPatch.mStartIndex = numManifoldContacts; + tempPatch.mEndIndex = numManifoldContacts + manifold.mNumContacts; + tempPatch.mPatchNormal = pNor;//manifold.getLocalNormal(); + tempPatch.mRoot = patch; + tempPatch.mNextPatch = NULL; + + patch->mEndPatch->mNextPatch = &tempPatch; + + //numManifoldContacts += manifold.numContacts; + patch->mTotalSize += manifold.mNumContacts; + patch->mPatchMaxPen = FMin(patch->mPatchMaxPen, FLoad(mMaxPen[mManifoldIndices[j]])); + + //manifold.numContacts = 0; + + PX_ASSERT((numManifoldContacts+manifold.mNumContacts) <= 64); + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(&manifold, manifoldContact, numManifoldContacts+manifold.mNumContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[j]]); + found = true; + break; + } + } + + if(!found)// && numManifolds < 4) + { + SinglePersistentContactManifold* manifold = getEmptyManifold(); + //we still have slot to create a new manifold + if(manifold) + { + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[mNumManifolds]]); + mNumManifolds++; + } + else + { + //we can't allocate a new manifold and no existing manifold has the same normal as this patch, we need to find the shallowest penetration manifold. If this manifold is shallower than + //the current patch, replace the manifold with the current patch + PxU32 index = 0; + for(PxU32 j=1; j<mNumManifolds; ++j) + { + //if(FAllGrtr(mMaxPen[mManifoldIndices[i]], mMaxPen[mManifoldIndices[index]])) + if(mMaxPen[mManifoldIndices[j]] > mMaxPen[mManifoldIndices[index]]) + { + index = j; + } + } + + if(FAllGrtr(FLoad(mMaxPen[mManifoldIndices[index]]), patch->mPatchMaxPen)) + { + manifold = getManifold(index); + manifold->mNumContacts = 0; + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[index]]); + + } + return; + } + } + } + } + } +} + +//This function adds the multi manifold contacts to the contact buffer for box/convexhull +bool Gu::MultiplePersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& meshTransform) +{ + using namespace Ps::aos; + PxU32 contactCount = 0;//contactBuffer.count; + PxU32 numContacts = 0; + mNumTotalContacts = 0; + //drawManifold(*gRenderOutPut, convexTransform, meshTransform); + for(PxU32 i=0; i < mNumManifolds; ++i) + { + Gu::SinglePersistentContactManifold& manifold = *getManifold(i); + numContacts = manifold.getNumContacts(); + PX_ASSERT(mNumTotalContacts + numContacts <= 0xFF); + mNumTotalContacts += Ps::to8(numContacts); + const Vec3V normal = manifold.getWorldNormal(meshTransform); + + for(PxU32 j=0; (j< numContacts) & (contactCount < ContactBuffer::MAX_CONTACTS); ++j) + { + Gu::MeshPersistentContact& p = manifold.getContactPoint(j); + + const Vec3V worldP =meshTransform.transform(p.mLocalPointB); + const FloatV dist = V4GetW(p.mLocalNormalPen); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast<PxF32*>(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast<PxF32*>(&contact.point.x)); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = p.mFaceIndex; + + } + } + + PX_ASSERT(contactCount <= 64); + contactBuffer.count = contactCount; + return contactCount > 0; +} + +//This function adds the multi manifold contacts to the contact buffer for sphere and capsule, radius is corresponding to the sphere radius/capsule radius +bool Gu::MultiplePersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius) +{ + using namespace Ps::aos; + PxU32 contactCount = 0; + PxU32 numContacts = 0; + mNumTotalContacts = 0; + + for(PxU32 i=0; i < mNumManifolds; ++i) + { + //get a single manifold + Gu::SinglePersistentContactManifold& manifold = *getManifold(i); + numContacts = manifold.getNumContacts(); + PX_ASSERT(mNumTotalContacts + numContacts <= 0xFF); + mNumTotalContacts += Ps::to8(numContacts); + const Vec3V normal = manifold.getWorldNormal(trB); + + //iterate all the contacts in this single manifold and add contacts to the contact buffer + //each manifold contact point store two points which are in each other's local space. In this + //case, mLocalPointA is stored as the center of sphere/a point in the segment for capsule + for(PxU32 j=0; (j< numContacts) & (contactCount < ContactBuffer::MAX_CONTACTS); ++j) + { + Gu::MeshPersistentContact& p = manifold.getContactPoint(j); + + const Vec3V worldP =V3NegScaleSub(normal, radius, trA.transform(p.mLocalPointA)); + const FloatV dist = FSub(V4GetW(p.mLocalNormalPen), radius); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast<PxF32*>(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast<PxF32*>(&contact.point.x)); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = p.mFaceIndex; + + } + } + + PX_ASSERT(contactCount <= 64); + contactBuffer.count = contactCount; + return contactCount > 0; +} + + +/* + This function copies the mesh persistent contact from compress buffer(NpCacheStreamPair in the PxcNpThreadContext) to the multiple manifold +*/ +// PT: function moved to cpp to go around a compiler bug on PS4 +void physx::Gu::MultiplePersistentContactManifold::fromBuffer(PxU8* PX_RESTRICT buffer) +{ + using namespace Ps::aos; + PxU32 numManifolds = 0; + if(buffer != NULL) + { + PX_ASSERT(((uintptr_t(buffer)) & 0xF) == 0); + PxU8* PX_RESTRICT buff = buffer; + MultiPersistentManifoldHeader* PX_RESTRICT header = reinterpret_cast<MultiPersistentManifoldHeader*>(buff); + buff += sizeof(MultiPersistentManifoldHeader); + + numManifolds = header->mNumManifolds; + + PX_ASSERT(numManifolds <= GU_MAX_MANIFOLD_SIZE); + mRelativeTransform = header->mRelativeTransform; + + for(PxU32 a = 0; a < numManifolds; ++a) + { + mManifoldIndices[a] = PxU8(a); + SingleManifoldHeader* PX_RESTRICT manHeader = reinterpret_cast<SingleManifoldHeader*>(buff); + buff += sizeof(SingleManifoldHeader); + PxU32 numContacts = manHeader->mNumContacts; + PX_ASSERT(numContacts <= GU_SINGLE_MANIFOLD_CACHE_SIZE); + SinglePersistentContactManifold& manifold = mManifolds[a]; + manifold.mNumContacts = numContacts; + PX_ASSERT((uintptr_t(buff) & 0xf) == 0); + CachedMeshPersistentContact* contacts = reinterpret_cast<CachedMeshPersistentContact*>(buff); + for(PxU32 b=0; b<manifold.mNumContacts; ++b) + { + manifold.mContactPoints[b].mLocalPointA = Vec3V_From_Vec4V(V4LoadA(&contacts[b].mLocalPointA.x)); + manifold.mContactPoints[b].mLocalPointB = Vec3V_From_Vec4V(V4LoadA(&contacts[b].mLocalPointB.x)); + manifold.mContactPoints[b].mLocalNormalPen = V4LoadA(&contacts[b].mLocalNormal.x); + manifold.mContactPoints[b].mFaceIndex = contacts[b].mFaceIndex; + } + buff += sizeof(Gu::CachedMeshPersistentContact) * numContacts; + } + } + else + { + mRelativeTransform.Invalidate(); + } + mNumManifolds = PxU8(numManifolds); + for(PxU32 a = numManifolds; a < GU_MAX_MANIFOLD_SIZE; ++a) + { + mManifoldIndices[a] = PxU8(a); + } +} diff --git a/PhysX_3.4/Source/GeomUtils/src/pcm/GuPersistentContactManifold.h b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPersistentContactManifold.h new file mode 100644 index 00000000..7ee6c484 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/pcm/GuPersistentContactManifold.h @@ -0,0 +1,846 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef Gu_PERSISTENTCONTACTMANIFOLD_H +#define Gu_PERSISTENTCONTACTMANIFOLD_H + +#include "PxPhysXCommonConfig.h" +#include "foundation/PxUnionCast.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsVecTransform.h" + +#define PCM_LOW_LEVEL_DEBUG 0 + +namespace physx +{ + +#define VISUALIZE_PERSISTENT_CONTACT 1 +//This is for pritimives vs primitives +#define GU_MANIFOLD_CACHE_SIZE 4 +//These are for pritimives vs mesh +#define GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE 5 +#define GU_SINGLE_MANIFOLD_CACHE_SIZE 6 +#define GU_SPHERE_MANIFOLD_CACHE_SIZE 1 +#define GU_CAPSULE_MANIFOLD_CACHE_SIZE 3 +#define GU_MAX_MANIFOLD_SIZE 6 +#define GU_MESH_CONTACT_REDUCTION_THRESHOLD 16 + + +//ML: this is used to compared with the shape's margin to decide the final tolerance used in the manifold to validate the existing contacts. +//In the case of big shape and relatively speaking small triangles in the mesh, we need to take a smaller margin. This helps because the PCM +//recycling thresholds are proportionate to margin so it makes it less likely to discard previous contacts due to separation +#define GU_PCM_MESH_MANIFOLD_EPSILON 0.05f + + +namespace Cm +{ + class RenderOutput; +} + + +namespace Gu +{ + struct ContactPoint; + class ContactBuffer; + +extern const PxF32 invalidateThresholds[5]; +extern const PxF32 invalidateQuatThresholds[5]; +extern const PxF32 invalidateThresholds2[3]; +extern const PxF32 invalidateQuatThresholds2[3]; + + +Ps::aos::Mat33V findRotationMatrixFromZAxis(const Ps::aos::Vec3VArg to); + +//This contact is used in the primitives vs primitives contact gen +class PersistentContact +{ +public: + PersistentContact() + { + } + + PersistentContact(Ps::aos::Vec3V _localPointA, Ps::aos::Vec3V _localPointB, Ps::aos::Vec4V _localNormalPen) : + mLocalPointA(_localPointA), mLocalPointB(_localPointB), mLocalNormalPen(_localNormalPen) + { + + } + + Ps::aos::Vec3V mLocalPointA; + Ps::aos::Vec3V mLocalPointB; + Ps::aos::Vec4V mLocalNormalPen; // the (x, y, z) is the local normal, and the w is the penetration depth +}; + +//This contact is used in the mesh contact gen to store an extra variable +class MeshPersistentContact : public PersistentContact +{ +public: + PxU32 mFaceIndex; +}; + +//This contact is used in the compress stream buffer(NpCacheStreamPair in the PxcNpThreadContext) to store the data we need +PX_ALIGN_PREFIX(16) +class CachedMeshPersistentContact +{ +public: + PxVec3 mLocalPointA; //16 byte aligned + PxU32 mFaceIndex; //face index + PxVec3 mLocalPointB; //16 byte aligned + PxU32 mPad; //pad + PxVec3 mLocalNormal; //16 byte aligned + PxReal mPen; //penetration +}PX_ALIGN_SUFFIX(16); + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +//This class is used to store the start and end index of the mesh persistent contacts in an array. +struct PCMContactPatch +{ +public: + PCMContactPatch() + { + mNextPatch = NULL; + mEndPatch = NULL; + mRoot = this; + mPatchMaxPen = Ps::aos::FMax(); + } + Ps::aos::Vec3V mPatchNormal; + PCMContactPatch* mNextPatch;//store the next patch pointer in the patch list + PCMContactPatch* mEndPatch;//store the last patch pointer in the patch list + PCMContactPatch* mRoot;//if this is the start of the patch list which has very similar patch normal, the root will be itself + Ps::aos::FloatV mPatchMaxPen;//store the deepest penetration of the whole patch + PxU32 mStartIndex;//store the start index of the manifold contacts in the manifold contacts stream + PxU32 mEndIndex;//store the end index of the manifold contacts in the manifold contacts stream + PxU32 mTotalSize;//if this is the root, the total size will store the total number of manifold contacts in the whole patch list +}; + +#if PX_VC + #pragma warning(pop) +#endif + +//ML: this is needed because it seems NEON doesn't force the alignment on SIMD type, which cause some of the PCM unit tests fail +PX_ALIGN_PREFIX(16) +class PersistentContactManifold +{ +public: + + PersistentContactManifold(PersistentContact* contactPointsBuff, PxU8 capacity): mNumContacts(0), mCapacity(capacity), mNumWarmStartPoints(0), mContactPoints(contactPointsBuff) + { + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE PxU32 getNumContacts() const { return mNumContacts;} + + PX_FORCE_INLINE bool isEmpty() { return mNumContacts==0; } + + PX_FORCE_INLINE PersistentContact& getContactPoint(const PxU32 index) + { + PX_ASSERT(index < GU_MANIFOLD_CACHE_SIZE); + return mContactPoints[index]; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformdelta(const Ps::aos::PsTransformV& curTransform) + { + using namespace Ps::aos; + const Vec4V p0 = Vec4V_From_Vec3V(mRelativeTransform.p); + const Vec4V q0 = mRelativeTransform.q; + const Vec4V p1 = Vec4V_From_Vec3V(curTransform.p); + const Vec4V q1 = curTransform.q; + + const Vec4V dp = V4Abs(V4Sub(p1, p0)); + const Vec4V dq = V4Abs(V4Sub(q1, q0)); + + const Vec4V max0 = V4Max(dp, dq); + + //need to work out max from a single vector... + return V4ExtractMax(max0); + } + + PX_FORCE_INLINE Ps::aos::Vec4V maxTransformdelta2(const Ps::aos::PsTransformV& curTransform) + { + using namespace Ps::aos; + const Vec4V p0 = Vec4V_From_Vec3V(mRelativeTransform.p); + const Vec4V q0 = mRelativeTransform.q; + const Vec4V p1 = Vec4V_From_Vec3V(curTransform.p); + const Vec4V q1 = curTransform.q; + + const Vec4V dp = V4Abs(V4Sub(p1, p0)); + const Vec4V dq = V4Abs(V4Sub(q1, q0)); + + const Vec4V max0 = V4Max(dp, dq); + + //need to work out max from a single vector... + return max0; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformPositionDelta(const Ps::aos::Vec3V& curP) + { + using namespace Ps::aos; + + const Vec3V deltaP = V3Sub(curP, mRelativeTransform.p); + const Vec4V delta = Vec4V_From_Vec3V(V3Abs(deltaP)); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformQuatDelta(const Ps::aos::QuatV& curQ) + { + using namespace Ps::aos; + + const Vec4V deltaQ = V4Sub(curQ, mRelativeTransform.q); + const Vec4V delta = V4Abs(deltaQ); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE void setRelativeTransform(const Ps::aos::PsTransformV& transform) + { + mRelativeTransform = transform; + } + + //This is used for the box/convexhull vs box/convexhull contact gen to decide whether the relative movement of a pair of objects are + //small enough. In this case, we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_BoxConvex(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + PX_ASSERT(mNumContacts <= GU_MANIFOLD_CACHE_SIZE); + const FloatV ratio = FLoad(invalidateThresholds[mNumContacts]); + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(invalidateQuatThresholds[mNumContacts]); + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + //This is used for the sphere/capsule vs other primitives contact gen to decide whether the relative movement of a pair of objects are + //small enough. In this case, we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_SphereCapsule(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + PX_ASSERT(mNumContacts <= 2); + const FloatV ratio = FLoad(invalidateThresholds2[mNumContacts]); + + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(invalidateQuatThresholds2[mNumContacts]); + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + //This is used for plane contact gen to decide whether the relative movement of a pair of objects are small enough. In this case, + //we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_PrimitivesPlane(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin, const Ps::aos::FloatVArg ratio) + { + using namespace Ps::aos; + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(0.9998f);//about 1 degree + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + PX_FORCE_INLINE void removeContactPoint (PxU32 index) + { + mNumContacts--; + mContactPoints[index] = mContactPoints[mNumContacts]; + } + + bool validContactDistance(const PersistentContact& pt, const Ps::aos::FloatVArg breakingThreshold) const + { + using namespace Ps::aos; + const FloatV dist = V4GetW(pt.mLocalNormalPen); + return FAllGrtr(breakingThreshold, dist) != 0; + } + + PX_FORCE_INLINE void clearManifold() + { + mNumWarmStartPoints = 0; + mNumContacts = 0; + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE void initialize() + { + clearManifold(); + } + + //This function is used to replace the existing contact with the newly created contact if their distance are within some threshold + bool replaceManifoldPoint(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen, const Ps::aos::FloatVArg replaceBreakingThreshold); + + //This function is to add a point(in box/convexhull contact gen) to the exising manifold. If the number of manifold is more than 4, we need to do contact reduction + PxU32 addManifoldPoint( const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalAPen, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function is to add a point(in capsule contact gen) to the exising manifold. If the number of manifold is more than 4, we need to do contact reduction + PxU32 addManifoldPoint2( const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalAPen, const Ps::aos::FloatVArg replaceBreakingThreshold);//max two points of contacts + //This function is used in box-plane contact gen to add the plane contacts to the manifold + void addBatchManifoldContactsCluster( const PersistentContact* manifoldPoints, const PxU32 numPoints); + + //This function is used in the capsule full manifold contact genenation(maximum 2 points). + void addBatchManifoldContacts2( const PersistentContact* manifoldPoints, const PxU32 numPoints);//max two points of contacts + + //This function is used in the box/convexhull full manifold contact generation(maximum 4 points). + void addBatchManifoldContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is using the cluster algorithm to reduce contacts + void reduceBatchContactsCluster(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is called by addBatchManifoldContacts2 to reduce the manifold contacts to 2 points; + void reduceBatchContacts2(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is called by addBatchManifoldContacts to reduce the manifold contacts to 4 points + void reduceBatchContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints); + + //This function is used for incremental manifold contact reduction for box/convexhull + PxU32 reduceContactsForPCM(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen); + //This function is used for incremental manifold contact reduction for capsule + PxU32 reduceContactSegment(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen); + + + /* + This function recalculate the contacts in the manifold based on the current relative transform between a pair of objects. If the recalculated contacts are within some threshold, + we will keep the contacts; Otherwise, we will remove the contacts. + */ + void refreshContactPoints(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactOffset); + //This function is just used in boxbox contact gen for fast transform + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsMatTransformV& transf1); + //This function is for adding box/convexhull manifold contacts to the contact buffer + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsTransformV& transf1, const Ps::aos::FloatVArg contactOffset); + //This function is for adding sphere/capsule manifold contacts to the contact buffer + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsTransformV& transf0, const Ps::aos::FloatVArg radius, const Ps::aos::FloatVArg contactOffset); + + //get the average normal in the manifold in world space + Ps::aos::Vec3V getWorldNormal(const Ps::aos::PsTransformV& trB); + //get the average normal in the manifold in local B object space + Ps::aos::Vec3V getLocalNormal(); + + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius); + void drawManifold(const PersistentContact& m, Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + static void drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color = 0x0000ff00); + static void drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color = 0xff00ffff); + static void drawTriangle(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const PxU32 color = 0xffff0000); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsMatTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxU8 mNumContacts; + PxU8 mCapacity; + PxU8 mNumWarmStartPoints; + PxU8 mAIndice[4]; + PxU8 mBIndice[4]; + PersistentContact* mContactPoints; +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class LargePersistentContactManifold : public PersistentContactManifold +{ +public: + LargePersistentContactManifold() : PersistentContactManifold(mContactPointsBuff, GU_MANIFOLD_CACHE_SIZE) + { + } + + PersistentContact mContactPointsBuff[GU_MANIFOLD_CACHE_SIZE]; +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class SpherePersistentContactManifold : public PersistentContactManifold +{ +public: + SpherePersistentContactManifold() : PersistentContactManifold(mContactPointsBuff, GU_SPHERE_MANIFOLD_CACHE_SIZE) + { + } + + PersistentContact mContactPointsBuff[GU_SPHERE_MANIFOLD_CACHE_SIZE]; +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class SinglePersistentContactManifold +{ +public: + + SinglePersistentContactManifold(): mNumContacts(0) + { + } + + PX_FORCE_INLINE PxU32 getNumContacts() const { return mNumContacts;} + + PX_FORCE_INLINE bool isEmpty() { return mNumContacts==0; } + + PX_FORCE_INLINE MeshPersistentContact& getContactPoint(const PxU32 index) + { + PX_ASSERT(index < GU_SINGLE_MANIFOLD_CACHE_SIZE); + return mContactPoints[index]; + } + + PX_FORCE_INLINE void removeContactPoint (PxU32 index) + { + mNumContacts--; + mContactPoints[index] = mContactPoints[mNumContacts]; + } + + PX_FORCE_INLINE void clearManifold() + { + mNumContacts = 0; + } + + PX_FORCE_INLINE void initialize() + { + clearManifold(); + } + + + PX_FORCE_INLINE Ps::aos::Vec3V getWorldNormal(const Ps::aos::PsTransformV& trB) + { + using namespace Ps::aos; + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + + const Vec3V n = Vec3V_From_Vec4V(nPen); + return V3Normalize(trB.rotate(n)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getLocalNormal() + { + using namespace Ps::aos; + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + return V3Normalize(Vec3V_From_Vec4V(nPen)); + } + + //This function reduces the manifold contact list in a patch for box/convexhull vs mesh + Ps::aos::FloatV reduceBatchContactsConvex(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + //This function reduces the manifold contact list in a patch for sphere vs mesh + Ps::aos::FloatV reduceBatchContactsSphere(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + //This function reduces the manifold contact list in a pathc for capsuel vs mesh + Ps::aos::FloatV reduceBatchContactsCapsule(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + + //This function adds the manifold contact list in a patch for box/convexhull vs mesh + Ps::aos::FloatV addBatchManifoldContactsConvex(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function adds the manifold contact list in a patch for sphere vs mesh + Ps::aos::FloatV addBatchManifoldContactsSphere(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function adds the manifold contact list in a patch for capsule vs mesh + Ps::aos::FloatV addBatchManifoldContactsCapsule(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + + //This is used for in the addContactsToPatch for convex mesh contact gen. + static PxU32 reduceContacts(MeshPersistentContact* manifoldContactExt, PxU32 numContacts); + + //This function is to recalculate the contacts based on the relative transform between a pair of objects + Ps::aos::FloatV refreshContactPoints(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactOffset); + + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + + MeshPersistentContact mContactPoints[GU_SINGLE_MANIFOLD_CACHE_SIZE];//384 bytes + PxU32 mNumContacts;//400 bytes + +} PX_ALIGN_SUFFIX(16); + +//This is a structure used to cache a multi-persistent-manifold in the cache stream +struct MultiPersistentManifoldHeader +{ + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxU32 mNumManifolds; + PxU32 pad[3]; +}; + +struct SingleManifoldHeader +{ + PxU32 mNumContacts; + PxU32 pad[3]; +}; + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + +PX_ALIGN_PREFIX(16) +class PX_PHYSX_COMMON_API MultiplePersistentContactManifold +{ +public: + MultiplePersistentContactManifold():mNumManifolds(0), mNumTotalContacts(0) + { + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE void setRelativeTransform(const Ps::aos::PsTransformV& transform) + { + mRelativeTransform = transform; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformPositionDelta(const Ps::aos::Vec3V& curP) + { + using namespace Ps::aos; + + const Vec3V deltaP = V3Sub(curP, mRelativeTransform.p); + const Vec4V delta = Vec4V_From_Vec3V(V3Abs(deltaP)); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformQuatDelta(const Ps::aos::QuatV& curQ) + { + using namespace Ps::aos; + + const Vec4V deltaQ = V4Sub(curQ, mRelativeTransform.q); + const Vec4V delta = V4Abs(deltaQ); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE PxU32 invalidate(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin, const Ps::aos::FloatVArg ratio) + { + using namespace Ps::aos; + + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + const FloatV thresholdQ = FLoad(0.9998f);//about 1 degree + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + PX_FORCE_INLINE PxU32 invalidate(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + return invalidate(curRTrans, minMargin, FLoad(0.2f)); + } + + /* + This function work out the contact patch connectivity. If two patches's normal are within 5 degree, we would link these two patches together and reset the total size. + */ + PX_FORCE_INLINE void refineContactPatchConnective(PCMContactPatch** contactPatch, PxU32 numContactPatch, MeshPersistentContact* manifoldContacts, const Ps::aos::FloatVArg acceptanceEpsilon) + { + PX_UNUSED(manifoldContacts); + + using namespace Ps::aos; + + //work out the contact patch connectivity, the patchNormal should be in the local space of mesh + for(PxU32 i=0; i<numContactPatch; ++i) + { + PCMContactPatch* patch = contactPatch[i]; + patch->mRoot = patch; + patch->mEndPatch = patch; + patch->mTotalSize = patch->mEndIndex - patch->mStartIndex; + patch->mNextPatch = NULL; + + for(PxU32 j=i; j>0; --j) + { + PCMContactPatch* other = contactPatch[j-1]; + const FloatV d = V3Dot(patch->mPatchNormal, other->mRoot->mPatchNormal); + if(FAllGrtrOrEq(d, acceptanceEpsilon))//less than 5 degree + { + + other->mNextPatch = patch; + other->mRoot->mEndPatch = patch; + patch->mRoot = other->mRoot; + other->mRoot->mTotalSize += patch->mEndIndex - patch->mStartIndex; + break; + } + } + } + } + + + /* + This function uses to reduce the manifold contacts which are in different connected patchs but are within replace breaking threshold + */ + PX_FORCE_INLINE PxU32 reduceManifoldContactsInDifferentPatches(PCMContactPatch** contactPatch, PxU32 numContactPatch, MeshPersistentContact* manifoldContacts, PxU32 numContacts, const Ps::aos::FloatVArg sqReplaceBreaking) + { + using namespace Ps::aos; + + for(PxU32 i=0; i<numContactPatch; ++i) + { + PCMContactPatch* currentPatch = contactPatch[i]; + //this make sure the patch is the root before we do the contact reduction, otherwise, we will do duplicate work + if(currentPatch->mRoot == currentPatch) + { + while(currentPatch) + { + PCMContactPatch* nextPatch = currentPatch->mNextPatch; + if(nextPatch) + { + for(PxU32 k = currentPatch->mStartIndex; k<currentPatch->mEndIndex; ++k) + { + for(PxU32 l = nextPatch->mStartIndex; l < nextPatch->mEndIndex; ++l) + { + Vec3V dif = V3Sub(manifoldContacts[l].mLocalPointB, manifoldContacts[k].mLocalPointB); + FloatV d = V3Dot(dif, dif); + if(FAllGrtr(sqReplaceBreaking, d)) + { + //if two manifold contacts are within threshold, we will get rid of the manifold contacts in the other contact patch + manifoldContacts[l] = manifoldContacts[nextPatch->mEndIndex-1]; + nextPatch->mEndIndex--; + numContacts--; + l--; + } + } + } + } + currentPatch = nextPatch; + } + } + } + + return numContacts; + } + + + /* + This function is for the multiple manifold loop through each individual single manifold to recalculate the contacts based on the relative transform between a pair of objects + */ + PX_FORCE_INLINE void refreshManifold(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactDist) + { + using namespace Ps::aos; + + //refresh manifold contacts + for(PxU32 i=0; i < mNumManifolds; ++i) + { + PxU8 ind = mManifoldIndices[i]; + PX_ASSERT(mManifoldIndices[i] < GU_MAX_MANIFOLD_SIZE); + PxU32 nextInd = PxMin(i, mNumManifolds-2u)+1; + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]]); + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]],128); + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]],256); + FloatV _maxPen = mManifolds[ind].refreshContactPoints(relTra, projectBreakingThreshold, contactDist); + if(mManifolds[ind].isEmpty()) + { + //swap the index with the next manifolds + PxU8 index = mManifoldIndices[--mNumManifolds]; + mManifoldIndices[mNumManifolds] = ind; + mManifoldIndices[i] = index; + i--; + } + else + { + FStore(_maxPen, &mMaxPen[ind]); + } + } + } + + + PX_FORCE_INLINE void initialize() + { + mNumManifolds = 0; + mNumTotalContacts = 0; + mRelativeTransform.Invalidate(); + for(PxU8 i=0; i<GU_MAX_MANIFOLD_SIZE; ++i) + { + mManifolds[i].initialize(); + mManifoldIndices[i] = i; + } + } + + PX_FORCE_INLINE void clearManifold() + { + for(PxU8 i=0; i<mNumManifolds; ++i) + { + mManifolds[i].clearManifold(); + } + mNumManifolds = 0; + mNumTotalContacts = 0; + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE SinglePersistentContactManifold* getManifold(const PxU32 index) + { + PX_ASSERT(index < GU_MAX_MANIFOLD_SIZE); + return &mManifolds[mManifoldIndices[index]]; + } + + PX_FORCE_INLINE SinglePersistentContactManifold* getEmptyManifold() + { + if(mNumManifolds < GU_MAX_MANIFOLD_SIZE) + return &mManifolds[mManifoldIndices[mNumManifolds]]; + return NULL; + } + + //This function adds the manifold contacts with different patches into the corresponding single persistent contact manifold + void addManifoldContactPoints(MeshPersistentContact* manifoldContact, PxU32 numManifoldContacts, PCMContactPatch** contactPatch, const PxU32 numPatch, + const Ps::aos::FloatVArg sqReplaceBreakingThreshold, const Ps::aos::FloatVArg acceptanceEpsilon, PxU8 maxContactsPerManifold); + //This function adds the box/convexhull manifold contacts to the contact buffer + bool addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& transf1); + //This function adds the sphere/capsule manifold contacts to the contact buffer + bool addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius); + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + + //Code to load from a buffer and store to a buffer. + void fromBuffer(PxU8* PX_RESTRICT buffer); + void toBuffer(PxU8* PX_RESTRICT buffer); + + static void drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color = 0xff00ffff); + static void drawLine(Cm::RenderOutput& out, const PxVec3 p0, const PxVec3 p1, const PxU32 color = 0xff00ffff); + static void drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color = 0x00ff0000); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxF32 mMaxPen[GU_MAX_MANIFOLD_SIZE]; + PxU8 mManifoldIndices[GU_MAX_MANIFOLD_SIZE]; + PxU8 mNumManifolds; + PxU8 mNumTotalContacts; + SinglePersistentContactManifold mManifolds[GU_MAX_MANIFOLD_SIZE]; + + +} PX_ALIGN_SUFFIX(16); + +#if PX_VC +#pragma warning(pop) +#endif + +/* + This function calculates the average normal in the manifold in world space +*/ +PX_FORCE_INLINE Ps::aos::Vec3V PersistentContactManifold::getWorldNormal(const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; + + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + + const Vec3V n = Vec3V_From_Vec4V(nPen); + const FloatV sqLength = V3Dot(n, n); + const Vec3V nn = V3Sel(FIsGrtr(sqLength, FEps()), n, Vec3V_From_Vec4V(mContactPoints[0].mLocalNormalPen)); + return V3Normalize(trB.rotate(nn)); +} + +/* + This function calculates the average normal in the manifold in local B space +*/ +PX_FORCE_INLINE Ps::aos::Vec3V PersistentContactManifold::getLocalNormal() +{ + using namespace Ps::aos; + + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + return V3Normalize(Vec3V_From_Vec4V(nPen)); +} + +/* + This function recalculates the contacts in the manifold based on the current relative transform between a pair of objects. If the recalculated contacts are within some threshold, + we will keep the contacts; Otherwise, we will remove the contacts. +*/ +PX_FORCE_INLINE void PersistentContactManifold::refreshContactPoints(const Ps::aos::PsMatTransformV& aToB, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg /*contactOffset*/) +{ + using namespace Ps::aos; + const FloatV sqProjectBreakingThreshold = FMul(projectBreakingThreshold, projectBreakingThreshold); + + // first refresh worldspace positions and distance + for (PxU32 i=mNumContacts; i > 0; --i) + { + PersistentContact& manifoldPoint = mContactPoints[i-1]; + const Vec3V localAInB = aToB.transform( manifoldPoint.mLocalPointA ); // from a to b + const Vec3V localBInB = manifoldPoint.mLocalPointB; + const Vec3V v = V3Sub(localAInB, localBInB); + + const Vec3V localNormal = Vec3V_From_Vec4V(manifoldPoint.mLocalNormalPen); // normal in b space + const FloatV dist= V3Dot(v, localNormal); + + const Vec3V projectedPoint = V3NegScaleSub(localNormal, dist, localAInB);//manifoldPoint.worldPointA - manifoldPoint.worldPointB * manifoldPoint.m_distance1; + const Vec3V projectedDifference = V3Sub(localBInB, projectedPoint); + + const FloatV distance2d = V3Dot(projectedDifference, projectedDifference); + //const BoolV con = BOr(FIsGrtr(dist, contactOffset), FIsGrtr(distance2d, sqProjectBreakingThreshold)); + const BoolV con = FIsGrtr(distance2d, sqProjectBreakingThreshold); + if(BAllEqTTTT(con)) + { + removeContactPoint(i-1); + } + else + { + manifoldPoint.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), dist); + } + } +} + +/* + This function copies the mesh persistent contact from the multiple manifold to compress buffer(NpCacheStreamPair in the PxcNpThreadContext) +*/ +PX_INLINE void MultiplePersistentContactManifold::toBuffer(PxU8* PX_RESTRICT buffer) +{ + using namespace Ps::aos; + PxU8* buff = buffer; + + PX_ASSERT(((uintptr_t(buff)) & 0xF) == 0); + MultiPersistentManifoldHeader* PX_RESTRICT header = reinterpret_cast<MultiPersistentManifoldHeader*>(buff); + buff += sizeof(MultiPersistentManifoldHeader); + + PX_ASSERT(mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + header->mNumManifolds = mNumManifolds; + header->mRelativeTransform = mRelativeTransform; + + for(PxU32 a = 0; a < mNumManifolds; ++a) + { + SingleManifoldHeader* manHeader = reinterpret_cast<SingleManifoldHeader*>(buff); + buff += sizeof(SingleManifoldHeader); + SinglePersistentContactManifold& manifold = *getManifold(a); + manHeader->mNumContacts = manifold.mNumContacts; + PX_ASSERT((uintptr_t(buff) & 0xf) == 0); + CachedMeshPersistentContact* contacts = reinterpret_cast<CachedMeshPersistentContact*>(buff); + //convert the mesh persistent contact to cached mesh persistent contact to save 16 byte memory per contact + for(PxU32 b = 0; b<manifold.mNumContacts; ++b) + { + V4StoreA(Vec4V_From_Vec3V(manifold.mContactPoints[b].mLocalPointA), &contacts[b].mLocalPointA.x); + V4StoreA(Vec4V_From_Vec3V(manifold.mContactPoints[b].mLocalPointB), &contacts[b].mLocalPointB.x); + V4StoreA(manifold.mContactPoints[b].mLocalNormalPen, &contacts[b].mLocalNormal.x); + //Note - this must be written last because we just wrote mLocalPointA to this memory so need to make sure + //that face index is written after that. + contacts[b].mFaceIndex = manifold.mContactPoints[b].mFaceIndex; + } + buff += sizeof(CachedMeshPersistentContact) * manifold.mNumContacts; + } +} + +#define PX_CP_TO_PCP(contactPoint) (reinterpret_cast<PersistentContact*>(contactPoint)) //this is used in the normal pcm contact gen +#define PX_CP_TO_MPCP(contactPoint) (reinterpret_cast<MeshPersistentContact*>(contactPoint))//this is used in the mesh pcm contact gen + +}//Gu +}//physx + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxBox.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxBox.cpp new file mode 100644 index 00000000..86b8100a --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxBox.cpp @@ -0,0 +1,272 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepBoxBox.h" +#include "GuBox.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionEdgeEdge.h" +#include "GuSweepSharedTests.h" +#include "CmMatrix34.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +namespace +{ +// PT: TODO: get rid of this copy +static const PxReal gFatBoxEdgeCoeff = 0.01f; + +// PT: TODO: get rid of this copy +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; + +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + +static PxVec3 EdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; + +// PT: TODO: get rid of this copy +static const PxVec3* getBoxLocalEdgeNormals() +{ + return EdgeNormals; +} + +/** +Returns world edge normal +\param edgeIndex [in] 0 <= edge index < 12 +\param worldNormal [out] edge normal in world space +*/ +static void computeBoxWorldEdgeNormal(const Box& box, PxU32 edgeIndex, PxVec3& worldNormal) +{ + PX_ASSERT(edgeIndex<12); + worldNormal = box.rotate(getBoxLocalEdgeNormals()[edgeIndex]); +} + +} + +// ### optimize! and refactor. And optimize for aabbs +bool Gu::sweepBoxBox(const Box& box0, const Box& box1, const PxVec3& dir, PxReal length, PxHitFlags hitFlags, PxSweepHit& sweepHit) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(intersectOBBOBB(box0.extents, box0.center, box0.rot, box1.extents, box1.center, box1.rot, true)) + { + sweepHit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL; + sweepHit.distance = 0.0f; + sweepHit.normal = -dir; + return true; + } + } + + PxVec3 boxVertices0[8]; box0.computeBoxPoints(boxVertices0); + PxVec3 boxVertices1[8]; box1.computeBoxPoints(boxVertices1); + + // float MinDist = PX_MAX_F32; + PxReal MinDist = length; + int col = -1; + + // In following VF tests: + // - the direction is FW/BK since we project one box onto the other *and vice versa* + // - the normal reaction is FW/BK for the same reason + + // Vertices1 against Box0 + { + // We need: + + // - Box0 in local space + const PxVec3 Min0 = -box0.extents; + const PxVec3 Max0 = box0.extents; + + // - Vertices1 in Box0 space + Matrix34 worldToBox0; + computeWorldToBoxMatrix(worldToBox0, box0); + + // - the dir in Box0 space + const PxVec3 localDir0 = worldToBox0.rotate(dir); + + const PxVec3* boxNormals0 = gNearPlaneNormal; + + for(PxU32 i=0; i<8; i++) + { + PxReal tnear, tfar; + const int plane = intersectRayAABB(Min0, Max0, worldToBox0.transform(boxVertices1[i]), -localDir0, tnear, tfar); + + if(plane==-1 || tnear<0.0f) + continue; + + if(tnear <= MinDist) + { + MinDist = tnear; + sweepHit.normal = box0.rotate(boxNormals0[plane]); + sweepHit.position = boxVertices1[i]; + col = 0; + } + } + } + + // Vertices0 against Box1 + { + // We need: + + // - Box1 in local space + const PxVec3 Min1 = -box1.extents; + const PxVec3 Max1 = box1.extents; + + // - Vertices0 in Box1 space + Matrix34 worldToBox1; + computeWorldToBoxMatrix(worldToBox1, box1); + + // - the dir in Box1 space + const PxVec3 localDir1 = worldToBox1.rotate(dir); + + const PxVec3* boxNormals1 = gNearPlaneNormal; + + for(PxU32 i=0; i<8; i++) + { + PxReal tnear, tfar; + const int plane = intersectRayAABB(Min1, Max1, worldToBox1.transform(boxVertices0[i]), localDir1, tnear, tfar); + + if(plane==-1 || tnear<0.0f) + continue; + + if(tnear <= MinDist) + { + MinDist = tnear; + sweepHit.normal = box1.rotate(-boxNormals1[plane]); + sweepHit.position = boxVertices0[i] + tnear * dir; + col = 1; + } + } + } + + PxVec3 p1s, p2s, p3s, p4s; + { + const PxU8* PX_RESTRICT edges0 = getBoxEdges(); + const PxU8* PX_RESTRICT edges1 = getBoxEdges(); + + PxVec3 edgeNormals0[12]; + PxVec3 edgeNormals1[12]; + for(PxU32 i=0; i<12; i++) + computeBoxWorldEdgeNormal(box0, i, edgeNormals0[i]); + for(PxU32 i=0; i<12; i++) + computeBoxWorldEdgeNormal(box1, i, edgeNormals1[i]); + + // Loop through box edges + for(PxU32 i=0; i<12; i++) // 12 edges + { + if(!(edgeNormals0[i].dot(dir) >= 0.0f)) + continue; + + // Catch current box edge // ### one vertex already known using line-strips + + // Make it fat ### + PxVec3 p1 = boxVertices0[edges0[i*2+0]]; + PxVec3 p2 = boxVertices0[edges0[i*2+1]]; + Ps::makeFatEdge(p1, p2, gFatBoxEdgeCoeff); + + // Loop through box edges + for(PxU32 j=0;j<12;j++) + { + if(edgeNormals1[j].dot(dir) >= 0.0f) + continue; + + // Orientation culling + // PT: this was commented for some reason, but it fixes the "stuck" bug reported by Ubi. + // So I put it back. We'll have to see whether it produces Bad Things in particular cases. + if(edgeNormals0[i].dot(edgeNormals1[j]) >= 0.0f) + continue; + + // Catch current box edge + + // Make it fat ### + PxVec3 p3 = boxVertices1[edges1[j*2+0]]; + PxVec3 p4 = boxVertices1[edges1[j*2+1]]; + Ps::makeFatEdge(p3, p4, gFatBoxEdgeCoeff); + + PxReal Dist; + PxVec3 ip; + if(intersectEdgeEdge(p1, p2, dir, p3, p4, Dist, ip)) + { + if(Dist<=MinDist) + { + p1s = p1; + p2s = p2; + p3s = p3; + p4s = p4; + + sweepHit.position = ip + Dist * dir; + + col = 2; + MinDist = Dist; + } + } + } + } + } + + if(col==-1) + return false; + + if(col==2) + { + computeEdgeEdgeNormal(sweepHit.normal, p1s, p2s-p1s, p3s, p4s-p3s, dir, MinDist); + sweepHit.normal.normalize(); + } + + sweepHit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + sweepHit.distance = MinDist; + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxBox.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxBox.h new file mode 100644 index 00000000..a5a36016 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxBox.h @@ -0,0 +1,49 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_BOX_H +#define GU_SWEEP_BOX_BOX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Box; + + bool sweepBoxBox(const Box& box0, const Box& box1, const PxVec3& dir, PxReal length, PxHitFlags hitFlags, PxSweepHit& sweepHit); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxSphere.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxSphere.cpp new file mode 100644 index 00000000..408dedad --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxSphere.cpp @@ -0,0 +1,158 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepBoxSphere.h" +#include "GuOverlapTests.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuCapsule.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionSphereBox.h" +#include "GuDistancePointSegment.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +namespace +{ +// PT: TODO: get rid of this copy +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; + +} + +bool Gu::sweepBoxSphere(const Box& box, PxReal sphereRadius, const PxVec3& spherePos, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(intersectSphereBox(Sphere(spherePos, sphereRadius), box)) + { + // Overlap + min_dist = 0.0f; + normal = -dir; + return true; + } + } + + PxVec3 boxPts[8]; + box.computeBoxPoints(boxPts); + const PxU8* PX_RESTRICT edges = getBoxEdges(); + PxReal MinDist = length; + bool Status = false; + for(PxU32 i=0; i<12; i++) + { + const PxU8 e0 = *edges++; + const PxU8 e1 = *edges++; + const Capsule capsule(boxPts[e0], boxPts[e1], sphereRadius); + + PxReal t; + if(intersectRayCapsule(spherePos, dir, capsule, t)) + { + if(t>=0.0f && t<=MinDist) + { + MinDist = t; + + const PxVec3 ip = spherePos + t*dir; + distancePointSegmentSquared(capsule, ip, &t); + + PxVec3 ip2; + capsule.computePoint(ip2, t); + + normal = (ip2 - ip); + normal.normalize(); + Status = true; + } + } + } + + PxVec3 localPt; + { + Matrix34 M2; + buildMatrixFromBox(M2, box); + + localPt = M2.rotateTranspose(spherePos - M2.p); + } + + const PxVec3* boxNormals = gNearPlaneNormal; + + const PxVec3 localDir = box.rotateInv(dir); + + // PT: when the box exactly touches the sphere, the test for initial overlap can fail on some platforms. + // In this case we reach the sweep code below, which may return a slightly negative time of impact (it should be 0.0 + // but it ends up a bit negative because of limited FPU accuracy). The epsilon ensures that we correctly detect a hit + // in this case. + const PxReal epsilon = -1e-5f; + + PxReal tnear, tfar; + + PxVec3 extents = box.extents; + extents.x += sphereRadius; + int plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + extents = box.extents; + extents.y += sphereRadius; + plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + extents = box.extents; + extents.z += sphereRadius; + plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + min_dist = MinDist; + + return Status; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxSphere.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxSphere.h new file mode 100644 index 00000000..1fd163b9 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxSphere.h @@ -0,0 +1,49 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_SPHERE_H +#define GU_SWEEP_BOX_SPHERE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Box; + + bool sweepBoxSphere(const Box& box, PxReal sphereRadius, const PxVec3& spherePos, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp new file mode 100644 index 00000000..3e13f785 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp @@ -0,0 +1,622 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxBounds3.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuIntersectionRayBox.h" +#include "PxTriangle.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; + +#define LOCAL_EPSILON 0.00001f // PT: this value makes the 'basicAngleTest' pass. Fails because of a ray almost parallel to a triangle + +namespace +{ +static const PxReal gFatTriangleCoeff = 0.02f; + +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; +} + +#define INVSQRT3 0.577350269189f //!< 1 / sqrt(3) + +/** +Returns vertex normals. +\return 24 floats (8 normals) +*/ +static const PxF32* getBoxVertexNormals() +{ + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + static PxF32 VertexNormals[] = + { + -INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, INVSQRT3, INVSQRT3, + -INVSQRT3, INVSQRT3, INVSQRT3 + }; + + return VertexNormals; +} + +static PxTriangle inflateTriangle(const PxTriangle& triangle, PxReal fat_coeff) +{ + PxTriangle fatTri = triangle; + + // Compute triangle center + const PxVec3& p0 = triangle.verts[0]; + const PxVec3& p1 = triangle.verts[1]; + const PxVec3& p2 = triangle.verts[2]; + const PxVec3 center = (p0 + p1 + p2)*0.333333333f; + + // Don't normalize? + // Normalize => add a constant border, regardless of triangle size + // Don't => add more to big triangles + for(PxU32 i=0;i<3;i++) + { + const PxVec3 v = fatTri.verts[i] - center; + fatTri.verts[i] += v * fat_coeff; + } + return fatTri; +} + +// PT: special version to fire N parallel rays against the same tri +static PX_FORCE_INLINE Ps::IntBool rayTriPrecaCull( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, const PxVec3& pvec, + PxReal det, PxReal oneOverDet, PxReal& t) +{ + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + PxReal u = tvec.dot(pvec); + if((u < 0.0f) || u>det) + return 0; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + PxReal v = dir.dot(qvec); + if((v < 0.0f) || u+v>det) + return 0; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2.dot(qvec); + t *= oneOverDet; + return 1; +} + +static PX_FORCE_INLINE Ps::IntBool rayTriPrecaNoCull( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, const PxVec3& pvec, + PxReal /*det*/, PxReal oneOverDet, PxReal& t) +{ + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + PxReal u = (tvec.dot(pvec)) * oneOverDet; + if((u < 0.0f) || u>1.0f) + return 0; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + PxReal v = (dir.dot(qvec)) * oneOverDet; + if((v < 0.0f) || u+v>1.0f) + return 0; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + return 1; +} + +// PT: specialized version where oneOverDir is available +// PT: why did we change the initial epsilon value? +#define LOCAL_EPSILON_RAY_BOX PX_EPS_F32 +//#define LOCAL_EPSILON_RAY_BOX 0.0001f +static PX_FORCE_INLINE int intersectRayAABB2(const PxVec3& minimum, const PxVec3& maximum, + const PxVec3& ro, const PxVec3& /*rd*/, const PxVec3& oneOverDir, + float& tnear, float& tfar, + bool fbx, bool fby, bool fbz) +{ + // PT: this unrolled loop is a lot faster on Xbox + + if(fbx) + if(ro.x<minimum.x || ro.x>maximum.x) + { +// tnear = FLT_MAX; + return -1; + } + if(fby) + if(ro.y<minimum.y || ro.y>maximum.y) + { +// tnear = FLT_MAX; + return -1; + } + if(fbz) + if(ro.z<minimum.z || ro.z>maximum.z) + { +// tnear = FLT_MAX; + return -1; + } + + PxReal t1x = (minimum.x - ro.x) * oneOverDir.x; + PxReal t2x = (maximum.x - ro.x) * oneOverDir.x; + PxReal t1y = (minimum.y - ro.y) * oneOverDir.y; + PxReal t2y = (maximum.y - ro.y) * oneOverDir.y; + PxReal t1z = (minimum.z - ro.z) * oneOverDir.z; + PxReal t2z = (maximum.z - ro.z) * oneOverDir.z; + + int bx; + int by; + int bz; + + if(t1x>t2x) + { + PxReal t=t1x; t1x=t2x; t2x=t; + bx = 3; + } + else + { + bx = 0; + } + + if(t1y>t2y) + { + PxReal t=t1y; t1y=t2y; t2y=t; + by = 4; + } + else + { + by = 1; + } + + if(t1z>t2z) + { + PxReal t=t1z; t1z=t2z; t2z=t; + bz = 5; + } + else + { + bz = 2; + } + + int ret; + if(!fbx) + { +// if(t1x>tnear) // PT: no need to test for the first value + { + tnear = t1x; + ret = bx; + } +// tfar = Px::intrinsics::selectMin(tfar, t2x); + tfar = t2x; // PT: no need to test for the first value + } + else + { + ret=-1; + tnear = -PX_MAX_F32; + tfar = PX_MAX_F32; + } + + if(!fby) + { + if(t1y>tnear) + { + tnear = t1y; + ret = by; + } + tfar = physx::intrinsics::selectMin(tfar, t2y); + } + + if(!fbz) + { + if(t1z>tnear) + { + tnear = t1z; + ret = bz; + } + tfar = physx::intrinsics::selectMin(tfar, t2z); + } + + if(tnear>tfar || tfar<LOCAL_EPSILON_RAY_BOX) + return -1; + + return ret; +} + +// PT: force-inlining this saved 500.000 cycles in the benchmark. Ok to inline, only used once anyway. +static PX_FORCE_INLINE bool intersectEdgeEdge3(const PxPlane& plane, const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& v1, + const PxVec3& p3, const PxVec3& p4, + PxReal& dist, PxVec3& ip, PxU32 i, PxU32 j, const PxReal coeff) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + + const PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = p4 - p3; + + const PxReal temp2 = plane.n.dot(v2); + if(temp2==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp2); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + const PxReal temp3 = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + return temp3<0.0f; +} + +namespace +{ +static const PxReal gFatBoxEdgeCoeff = 0.01f; +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + +static const PxVec3 EdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; + +static const PxVec3* getBoxLocalEdgeNormals() +{ + return EdgeNormals; +} +} + +static PX_FORCE_INLINE void closestAxis2(const PxVec3& v, PxU32& j, PxU32& k) +{ + // find largest 2D plane projection + const PxF32 absPx = physx::intrinsics::abs(v.x); + const PxF32 absPy = physx::intrinsics::abs(v.y); + const PxF32 absPz = physx::intrinsics::abs(v.z); + //PxU32 m = 0; //x biggest axis + j = 1; + k = 2; + if( absPy > absPx && absPy > absPz) + { + //y biggest + j = 2; + k = 0; + //m = 1; + } + else if(absPz > absPx) + { + //z biggest + j = 0; + k = 1; + //m = 2; + } +// return m; +} + +bool Gu::sweepBoxTriangle( const PxTriangle& tri, const PxBounds3& box, + const PxVec3& motion, const PxVec3& oneOverMotion, + PxVec3& hit, PxVec3& normal, PxReal& d, bool isDoubleSided) +{ + // Create triangle normal + PxVec3 denormalizedTriNormal; + tri.denormalizedNormal(denormalizedTriNormal); + + // Backface culling + const bool doBackfaceCulling = !isDoubleSided; + if(doBackfaceCulling && (denormalizedTriNormal.dot(motion)) >= 0.0f) // ">=" is important ! + return false; + + ///////////////////////// + + PxVec3 boxVertices[8]; + computeBoxPoints(box, boxVertices); + + ///////////////////////// + + // Make fat triangle + const PxTriangle fatTri = inflateTriangle(tri, gFatTriangleCoeff); + + PxReal minDist = d; // Initialize with current best distance + int col = -1; + + // Box vertices VS triangle + { + // ### cull using box-plane distance ? + const PxVec3 edge1 = fatTri.verts[1] - fatTri.verts[0]; + const PxVec3 edge2 = fatTri.verts[2] - fatTri.verts[0]; + const PxVec3 PVec = motion.cross(edge2); + const PxReal Det = edge1.dot(PVec); + + // We can't use stamps here since we can still find a better TOI for a given vertex, + // even if that vertex has already been tested successfully against another triangle. + const PxVec3* VN = reinterpret_cast<const PxVec3*>(getBoxVertexNormals()); + + const PxReal oneOverDet = Det!=0.0f ? 1.0f / Det : 0.0f; + + PxU32 hitIndex=0; + if(doBackfaceCulling) + { + if(Det>=LOCAL_EPSILON) + { + for(PxU32 i=0;i<8;i++) + { + // Orientation culling + if((VN[i].dot(denormalizedTriNormal) >= 0.0f)) // Can't rely on triangle normal for double-sided faces + continue; + + // ### test this + // ### ok, this causes the bug in level3's v-shaped desk. Not really a real "bug", it just happens + // that this VF test fixes this case, so it's a bad idea to cull it. Oh, well. + // If we use a penetration-depth code to fixup bad cases, we can enable this culling again. (also + // if we find a better way to handle that desk) + // Discard back vertices +// if(VN[i].dot(motion)<0.0f) +// continue; + + // Shoot a ray from vertex against triangle, in direction "motion" + PxReal t; + if(!rayTriPrecaCull(boxVertices[i], motion, fatTri.verts[0], edge1, edge2, PVec, Det, oneOverDet, t)) + continue; + + //if(t<=OffsetLength) t=0.0f; + // Only consider positive distances, closer than current best + // ### we could test that first on tri vertices & discard complete tri if it's further than current best (or equal!) + if(t < 0.0f || t > minDist) + continue; + + minDist = t; + col = 0; +// hit = boxVertices[i] + t * motion; + hitIndex = i; + } + } + } + else + { + if(Det<=-LOCAL_EPSILON || Det>=LOCAL_EPSILON) + { + for(PxU32 i=0;i<8;i++) + { + // ### test this + // ### ok, this causes the bug in level3's v-shaped desk. Not really a real "bug", it just happens + // that this VF test fixes this case, so it's a bad idea to cull it. Oh, well. + // If we use a penetration-depth code to fixup bad cases, we can enable this culling again. (also + // if we find a better way to handle that desk) + // Discard back vertices + // if(!VN[i].SameDirection(motion)) + // continue; + + // Shoot a ray from vertex against triangle, in direction "motion" + PxReal t; + if(!rayTriPrecaNoCull(boxVertices[i], motion, fatTri.verts[0], edge1, edge2, PVec, Det, oneOverDet, t)) + continue; + + //if(t<=OffsetLength) t=0.0f; + // Only consider positive distances, closer than current best + // ### we could test that first on tri vertices & discard complete tri if it's further than current best (or equal!) + if(t < 0.0f || t > minDist) + continue; + + minDist = t; + col = 0; +// hit = boxVertices[i] + t * motion; + hitIndex = i; + } + } + } + + // Only copy this once, if needed + if(col==0) + { + // PT: hit point on triangle + hit = boxVertices[hitIndex] + minDist * motion; + normal = denormalizedTriNormal; + } + } + + // Triangle vertices VS box + { + const PxVec3 negMotion = -motion; + const PxVec3 negInvMotion = -oneOverMotion; + + // PT: precompute fabs-test for ray-box + // - doing this outside of the ray-box function gets rid of 3 fabs/fcmp per call + // - doing this with integer code removes the 3 remaining fabs/fcmps totally + // - doing this outside reduces the LHS + const bool b0 = physx::intrinsics::abs(negMotion.x)<LOCAL_EPSILON_RAY_BOX; + const bool b1 = physx::intrinsics::abs(negMotion.y)<LOCAL_EPSILON_RAY_BOX; + const bool b2 = physx::intrinsics::abs(negMotion.z)<LOCAL_EPSILON_RAY_BOX; + + // ### have this as a param ? + const PxVec3& Min = box.minimum; + const PxVec3& Max = box.maximum; + + const PxVec3* boxNormals = gNearPlaneNormal; + + // ### use stamps not to shoot shared vertices multiple times + // ### discard non-convex verts + for(PxU32 i=0;i<3;i++) + { + PxReal tnear, tfar; + const int plane = ::intersectRayAABB2(Min, Max, tri.verts[i], negMotion, negInvMotion, tnear, tfar, b0, b1, b2); + PX_ASSERT(plane == intersectRayAABB(Min, Max, tri.verts[i], negMotion, tnear, tfar)); + + // The following works as well but we need to call "intersectRayAABB" to get a plane index compatible with BoxNormals. + // We could fix this by unifying the plane indices returned by the different ray-aabb functions... + //PxVec3 coord; + //PxReal t; + //PxU32 status = rayAABBIntersect2(Min, Max, tri.verts[i], -motion, coord, t); + + // ### don't test -1 ? + if(plane==-1 || tnear<0.0f) continue; +// if(tnear<0.0f) continue; + if(tnear <= minDist) + { + minDist = tnear; // ### warning, tnear => flips normals + normal = boxNormals[plane]; + col = 1; + + // PT: hit point on triangle + hit = tri.verts[i]; + } + } + } + + PxU32 saved_j = PX_INVALID_U32; + PxU32 saved_k = PX_INVALID_U32; + PxVec3 p1s; + PxVec3 v1s; + + // Edge-vs-edge + { + // Loop through box edges + const PxU8* PX_RESTRICT edges = getBoxEdges(); + const PxVec3* PX_RESTRICT edgeNormals = getBoxLocalEdgeNormals(); + for(PxU32 i=0;i<12;i++) // 12 edges + { + // PT: TODO: skip this if edge is culled + PxVec3 p1 = boxVertices[*edges++]; + PxVec3 p2 = boxVertices[*edges++]; + Ps::makeFatEdge(p1, p2, gFatBoxEdgeCoeff); + + if(edgeNormals[i].dot(motion) < 0.0f) + continue; + + // While we're at it, precompute some more data for EE tests + const PxVec3 v1 = p2 - p1; + + // Build plane P based on edge (p1, p2) and direction (dir) + const PxVec3 planeNormal = v1.cross(motion); + const PxPlane plane(planeNormal, -(planeNormal.dot(p1))); + + // find largest 2D plane projection + PxU32 closest_i, closest_j; + // Ps::closestAxis(plane.normal, ii, jj); + closestAxis2(planeNormal, closest_i, closest_j); + + const PxReal coeff = 1.0f / (v1[closest_i]*motion[closest_j] - v1[closest_j]*motion[closest_i]); + + // Loop through triangle edges + for(PxU32 j=0; j<3; j++) + { + // Catch current triangle edge + // j=0 => 0-1 + // j=1 => 1-2 + // j=2 => 2-0 + // => this is compatible with EdgeList + const PxU32 k = Ps::getNextIndex3(j); + + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge3(plane, p1, p2, motion, v1, tri.verts[j], tri.verts[k], dist, ip, closest_i, closest_j, coeff)) + { + if(dist<=minDist) + { + p1s = p1; + v1s = v1; + saved_j = j; + saved_k = k; + + col = 2; + minDist = dist; + + // PT: hit point on triangle + hit = ip + motion*dist; + } + } + } + } + } + + if(col==-1) + return false; + + if(col==2) + { + PX_ASSERT(saved_j != PX_INVALID_U32); + PX_ASSERT(saved_k != PX_INVALID_U32); + const PxVec3& p3 = tri.verts[saved_j]; + const PxVec3& p4 = tri.verts[saved_k]; + computeEdgeEdgeNormal(normal, p1s, v1s, p3, p4-p3, motion, minDist); + } + + d = minDist; + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_FeatureBased.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_FeatureBased.h new file mode 100644 index 00000000..3bd450e8 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_FeatureBased.h @@ -0,0 +1,67 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_TRIANGLE_FEATURE_BASED_H +#define GU_SWEEP_BOX_TRIANGLE_FEATURE_BASED_H + +#include "foundation/PxVec3.h" +#include "foundation/PxPlane.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + class PxTriangle; + + namespace Gu + { + /** + Sweeps a box against a triangle, using a 'feature-based' approach. + + This is currently only used for computing the box-sweep impact data, in a second pass, + after the best triangle has been identified using faster approaches (SAT/GJK). + + \warning Returned impact normal is not normalized + + \param tri [in] the triangle + \param box [in] the box + \param motion [in] (box) motion vector + \param oneOverMotion [in] precomputed inverse of motion vector + \param hit [out] impact point + \param normal [out] impact normal (warning: not normalized) + \param d [in/out] impact distance (please initialize with best current distance) + \param isDoubleSided [in] whether triangle is double-sided or not + \return true if an impact has been found + */ + bool sweepBoxTriangle( const PxTriangle& tri, const PxBounds3& box, + const PxVec3& motion, const PxVec3& oneOverMotion, + PxVec3& hit, PxVec3& normal, PxReal& d, bool isDoubleSided=false); + } // namespace Gu +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_SAT.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_SAT.cpp new file mode 100644 index 00000000..fd3b3ff7 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_SAT.cpp @@ -0,0 +1,39 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepBoxTriangle_SAT.h" + +using namespace physx; +using namespace Gu; + +// PT: SAT-based version, in box space +int Gu::triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling) +{ + return triBoxSweepTestBoxSpace_inlined(tri, extents, dir, oneOverDir, tmax, toi, PxU32(doBackfaceCulling)); +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_SAT.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_SAT.h new file mode 100644 index 00000000..c0c172f0 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepBoxTriangle_SAT.h @@ -0,0 +1,235 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_TRIANGLE_SAT_H +#define GU_SWEEP_BOX_TRIANGLE_SAT_H + +#include "GuSweepSharedTests.h" +#include "PxTriangle.h" + + #define RetType int + #define MTDType bool + +namespace physx +{ +namespace Gu +{ + +// We have separation if one of those conditions is true: +// -BoxExt > TriMax (box strictly to the right of the triangle) +// BoxExt < TriMin (box strictly to the left of the triangle +// <=> d0 = -BoxExt - TriMax > 0 +// d1 = BoxExt - TriMin < 0 +// Hence we have overlap if d0 <= 0 and d1 >= 0 +// overlap = (d0<=0.0f && d1>=0.0f) +#define TEST_OVERLAP \ + const float d0 = -BoxExt - TriMax; \ + const float d1 = BoxExt - TriMin; \ + const bool bIntersect = (d0<=0.0f && d1>=0.0f); \ + bValidMTD &= bIntersect; + + // PT: inlining this one is important. Returning floats looks bad but is faster on Xbox. + static PX_FORCE_INLINE RetType testAxis(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& axis, MTDType& bValidMTD, float& tfirst, float& tlast) + { + const float d0t = tri.verts[0].dot(axis); + const float d1t = tri.verts[1].dot(axis); + const float d2t = tri.verts[2].dot(axis); + + float TriMin = PxMin(d0t, d1t); + float TriMax = PxMax(d0t, d1t); + TriMin = PxMin(TriMin, d2t); + TriMax = PxMax(TriMax, d2t); + + //////// + + const float BoxExt = PxAbs(axis.x)*extents.x + PxAbs(axis.y)*extents.y + PxAbs(axis.z)*extents.z; + TEST_OVERLAP + + const float v = dir.dot(axis); + if(PxAbs(v) < 1.0E-6f) + return bIntersect; + const float oneOverV = -1.0f / v; + + // float t0 = d0 * oneOverV; + // float t1 = d1 * oneOverV; + // if(t0 > t1) TSwap(t0, t1); + const float t0_ = d0 * oneOverV; + const float t1_ = d1 * oneOverV; + float t0 = PxMin(t0_, t1_); + float t1 = PxMax(t0_, t1_); + + if(t0 > tlast) return false; + if(t1 < tfirst) return false; + + // if(t1 < tlast) tlast = t1; + tlast = PxMin(t1, tlast); + + // if(t0 > tfirst) tfirst = t0; + tfirst = PxMax(t0, tfirst); + + return true; + } + + template<const int XYZ> + static PX_FORCE_INLINE RetType testAxisXYZ(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, float oneOverDir, MTDType& bValidMTD, float& tfirst, float& tlast) + { + const float d0t = tri.verts[0][XYZ]; + const float d1t = tri.verts[1][XYZ]; + const float d2t = tri.verts[2][XYZ]; + + float TriMin = PxMin(d0t, d1t); + float TriMax = PxMax(d0t, d1t); + TriMin = PxMin(TriMin, d2t); + TriMax = PxMax(TriMax, d2t); + + //////// + + const float BoxExt = extents[XYZ]; + TEST_OVERLAP + + const float v = dir[XYZ]; + if(PxAbs(v) < 1.0E-6f) + return bIntersect; + + const float oneOverV = -oneOverDir; + + // float t0 = d0 * oneOverV; + // float t1 = d1 * oneOverV; + // if(t0 > t1) TSwap(t0, t1); + const float t0_ = d0 * oneOverV; + const float t1_ = d1 * oneOverV; + float t0 = PxMin(t0_, t1_); + float t1 = PxMax(t0_, t1_); + + if(t0 > tlast) return false; + if(t1 < tfirst) return false; + + // if(t1 < tlast) tlast = t1; + tlast = PxMin(t1, tlast); + + // if(t0 > tfirst) tfirst = t0; + tfirst = PxMax(t0, tfirst); + + return true; + } + + PX_FORCE_INLINE int testSeparationAxes( const PxTriangle& tri, const PxVec3& extents, + const PxVec3& normal, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& tcoll) + { + bool bValidMTD = true; + float tfirst = -FLT_MAX; + float tlast = FLT_MAX; + + // Triangle normal + if(!testAxis(tri, extents, dir, normal, bValidMTD, tfirst, tlast)) + return 0; + + // Box normals + if(!testAxisXYZ<0>(tri, extents, dir, oneOverDir.x, bValidMTD, tfirst, tlast)) + return 0; + if(!testAxisXYZ<1>(tri, extents, dir, oneOverDir.y, bValidMTD, tfirst, tlast)) + return 0; + if(!testAxisXYZ<2>(tri, extents, dir, oneOverDir.z, bValidMTD, tfirst, tlast)) + return 0; + + // Edges + for(PxU32 i=0; i<3; i++) + { + int ip1 = int(i+1); + if(i>=2) ip1 = 0; + const PxVec3 TriEdge = tri.verts[ip1] - tri.verts[i]; + + { + const PxVec3 Sep = Ps::cross100(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + { + const PxVec3 Sep = Ps::cross010(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + { + const PxVec3 Sep = Ps::cross001(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + } + + if(tfirst > tmax || tlast < 0.0f) + return 0; + + if(tfirst <= 0.0f) + { + if(!bValidMTD) + return 0; + tcoll = 0.0f; + } + else tcoll = tfirst; + + return 1; + } + + //! Inlined version of triBoxSweepTestBoxSpace. See that other function for comments. + PX_FORCE_INLINE int triBoxSweepTestBoxSpace_inlined(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, PxU32 doBackfaceCulling) + { + // Create triangle normal + PxVec3 triNormal; + tri.denormalizedNormal(triNormal); + + // Backface culling + if(doBackfaceCulling && (triNormal.dot(dir)) >= 0.0f) // ">=" is important ! + return 0; + + // The SAT test will properly detect initial overlaps, no need for extra tests + return testSeparationAxes(tri, extents, triNormal, dir, oneOverDir, tmax, toi); + } + + /** + Sweeps a box against a triangle, using a 'SAT' approach (Separating Axis Theorem). + + The test is performed in box-space, i.e. the box is axis-aligned and its center is (0,0,0). In other words it is + defined by its extents alone. The triangle must have been transformed to this "box-space" before calling the function. + + \param tri [in] triangle in box-space + \param extents [in] box extents + \param dir [in] sweep direction. Does not need to be normalized. + \param oneOverDir [in] precomputed inverse of sweep direction + \param tmax [in] sweep length + \param toi [out] time of impact/impact distance. Does not need to be initialized before calling the function. + \param doBackfaceCulling [in] true to enable backface culling, false for double-sided triangles + \return non-zero value if an impact has been found (in which case returned 'toi' value is valid) + */ + int triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleBox.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleBox.cpp new file mode 100644 index 00000000..87764670 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleBox.cpp @@ -0,0 +1,215 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxBounds3.h" +#include "foundation/PxTransform.h" +#include "GuSweepCapsuleBox.h" +#include "GuSweepSphereTriangle.h" +#include "GuCapsule.h" +#include "PxTriangle.h" +#include "GuDistanceSegmentBox.h" +#include "GuInternal.h" +#include "PsAlloca.h" +#include "GuSIMDHelpers.h" + +using namespace physx; +using namespace Gu; + +namespace +{ +/** +* Returns triangles. +* \return 36 indices (12 triangles) indexing the list returned by ComputePoints() +*/ +static const PxU8* getBoxTriangles() +{ + static PxU8 Indices[] = { + 0,2,1, 0,3,2, + 1,6,5, 1,2,6, + 5,7,4, 5,6,7, + 4,3,0, 4,7,3, + 3,6,2, 3,7,6, + 5,0,1, 5,4,0 + }; + return Indices; +} +} + +#define OUTPUT_TRI(t, p0, p1, p2){ \ +t->verts[0] = p0; \ +t->verts[1] = p1; \ +t->verts[2] = p2; \ +t++;} + +#define OUTPUT_TRI2(t, p0, p1, p2, d){ \ +t->verts[0] = p0; \ +t->verts[1] = p1; \ +t->verts[2] = p2; \ +t->denormalizedNormal(denormalizedNormal); \ +if((denormalizedNormal.dot(d))>0.0f) { \ +PxVec3 Tmp = t->verts[1]; \ +t->verts[1] = t->verts[2]; \ +t->verts[2] = Tmp; \ +} \ +t++; *ids++ = i; } + +static PxU32 extrudeMesh( PxU32 nbTris, const PxTriangle* triangles, + const PxVec3& extrusionDir, PxTriangle* tris, PxU32* ids, const PxVec3& dir) +{ + const PxU32* base = ids; + + for(PxU32 i=0; i<nbTris; i++) + { + const PxTriangle& currentTriangle = triangles[i]; + + // Create triangle normal + PxVec3 denormalizedNormal; + currentTriangle.denormalizedNormal(denormalizedNormal); + + // Backface culling + const bool culled = (denormalizedNormal.dot(dir)) > 0.0f; + if(culled) continue; + + PxVec3 p0 = currentTriangle.verts[0]; + PxVec3 p1 = currentTriangle.verts[1]; + PxVec3 p2 = currentTriangle.verts[2]; + + PxVec3 p0b = p0 + extrusionDir; + PxVec3 p1b = p1 + extrusionDir; + PxVec3 p2b = p2 + extrusionDir; + + p0 -= extrusionDir; + p1 -= extrusionDir; + p2 -= extrusionDir; + + if(denormalizedNormal.dot(extrusionDir) >= 0.0f) OUTPUT_TRI(tris, p0b, p1b, p2b) + else OUTPUT_TRI(tris, p0, p1, p2) + *ids++ = i; + + // ### it's probably useless to extrude all the shared edges !!!!! + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE12) + { + OUTPUT_TRI2(tris, p1, p1b, p2b, dir) + OUTPUT_TRI2(tris, p1, p2b, p2, dir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE20) + { + OUTPUT_TRI2(tris, p0, p2, p2b, dir) + OUTPUT_TRI2(tris, p0, p2b, p0b, dir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE01) + { + OUTPUT_TRI2(tris, p0b, p1b, p1, dir) + OUTPUT_TRI2(tris, p0b, p1, p0, dir) + } + } + return PxU32(ids-base); +} + +static PxU32 extrudeBox(const PxBounds3& localBox, const PxTransform* world, const PxVec3& extrusionDir, PxTriangle* tris, const PxVec3& dir) +{ + // Handle the box as a mesh + + PxTriangle boxTris[12]; + + PxVec3 p[8]; + computeBoxPoints(localBox, p); + + const PxU8* PX_RESTRICT indices = getBoxTriangles(); + + for(PxU32 i=0; i<12; i++) + { + const PxU8 VRef0 = indices[i*3+0]; + const PxU8 VRef1 = indices[i*3+1]; + const PxU8 VRef2 = indices[i*3+2]; + + PxVec3 p0 = p[VRef0]; + PxVec3 p1 = p[VRef1]; + PxVec3 p2 = p[VRef2]; + if(world) + { + p0 = world->transform(p0); + p1 = world->transform(p1); + p2 = world->transform(p2); + } + + boxTris[i].verts[0] = p0; + boxTris[i].verts[1] = p1; + boxTris[i].verts[2] = p2; + } + PxU32 fakeIDs[12*7]; + return extrudeMesh(12, boxTris, extrusionDir, tris, fakeIDs, dir); +} + +// +// The problem of testing a swept capsule against a box is transformed into sweeping a sphere (lying at the center +// of the capsule) against the extruded triangles of the box. The box triangles are extruded along the +// capsule segment axis. +// +bool Gu::sweepCapsuleBox(const Capsule& capsule, const PxTransform& boxWorldPose, const PxVec3& boxDim, const PxVec3& dir, PxReal length, PxVec3& hit, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(distanceSegmentBoxSquared(capsule.p0, capsule.p1, boxWorldPose.p, boxDim, PxMat33Padded(boxWorldPose.q)) < capsule.radius*capsule.radius) + { + min_dist = 0.0f; + normal = -dir; + return true; + } + } + + // Extrusion dir = capsule segment + const PxVec3 extrusionDir = (capsule.p1 - capsule.p0)*0.5f; + + // Extrude box + PxReal MinDist = length; + bool Status = false; + { + const PxBounds3 aabb(-boxDim, boxDim); + + PX_ALLOCA(triangles, PxTriangle, 12*7); + const PxU32 nbTris = extrudeBox(aabb, &boxWorldPose, extrusionDir, triangles, dir); + PX_ASSERT(nbTris<=12*7); + + // Sweep sphere vs extruded box + PxSweepHit h; // PT: TODO: ctor! + PxVec3 bestNormal; + if(sweepSphereTriangles(nbTris, triangles, capsule.computeCenter(), capsule.radius, dir, length, NULL, h, bestNormal, false, false, false, false)) + { + hit = h.position; + MinDist = h.distance; + normal = h.normal; + Status = true; + } + } + + min_dist = MinDist; + return Status; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleBox.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleBox.h new file mode 100644 index 00000000..698e78a3 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleBox.h @@ -0,0 +1,49 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_BOX_H +#define GU_SWEEP_CAPSULE_BOX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + bool sweepCapsuleBox(const Capsule& capsule, const PxTransform& boxWorldPose, const PxVec3& boxDim, const PxVec3& dir, PxReal length, PxVec3& hit, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleCapsule.cpp new file mode 100644 index 00000000..d1f0158b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleCapsule.cpp @@ -0,0 +1,344 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepCapsuleCapsule.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuIntersectionRayCapsule.h" +#include "PxQueryReport.h" +#include "PxTriangle.h" + +using namespace physx; +using namespace Gu; + +#define LOCAL_EPSILON 0.00001f // PT: this value makes the 'basicAngleTest' pass. Fails because of a ray almost parallel to a triangle + +static void edgeEdgeDist(PxVec3& x, PxVec3& y, // closest points + const PxVec3& p, const PxVec3& a, // seg 1 origin, vector + const PxVec3& q, const PxVec3& b) // seg 2 origin, vector +{ + const PxVec3 T = q - p; + const PxReal ADotA = a.dot(a); + const PxReal BDotB = b.dot(b); + const PxReal ADotB = a.dot(b); + const PxReal ADotT = a.dot(T); + const PxReal BDotT = b.dot(T); + + // t parameterizes ray (p, a) + // u parameterizes ray (q, b) + + // Compute t for the closest point on ray (p, a) to ray (q, b) + const PxReal Denom = ADotA*BDotB - ADotB*ADotB; + + PxReal t; + if(Denom!=0.0f) + { + t = (ADotT*BDotB - BDotT*ADotB) / Denom; + + // Clamp result so t is on the segment (p, a) + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + + // find u for point on ray (q, b) closest to point at t + PxReal u; + if(BDotB!=0.0f) + { + u = (t*ADotB - BDotT) / BDotB; + + // if u is on segment (q, b), t and u correspond to closest points, otherwise, clamp u, recompute and clamp t + if(u<0.0f) + { + u = 0.0f; + if(ADotA!=0.0f) + { + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + else if(u > 1.0f) + { + u = 1.0f; + if(ADotA!=0.0f) + { + t = (ADotB + ADotT) / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + } + else + { + u = 0.0f; + + if(ADotA!=0.0f) + { + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + + x = p + a * t; + y = q + b * u; +} + +static bool rayQuad(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxReal& t, PxReal& u, PxReal& v, bool cull) +{ + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); + + if(cull) + { + if(det<LOCAL_EPSILON) + return false; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + u = tvec.dot(pvec); + if(u<0.0f || u>det) + return false; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + v = dir.dot(qvec); + if(v<0.0f || v>det) + return false; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2.dot(qvec); + const PxReal oneOverDet = 1.0f / det; + t *= oneOverDet; + u *= oneOverDet; + v *= oneOverDet; + } + else + { + // the non-culling branch + if(det>-LOCAL_EPSILON && det<LOCAL_EPSILON) + return false; + const PxReal oneOverDet = 1.0f / det; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + u = (tvec.dot(pvec)) * oneOverDet; + if(u<0.0f || u>1.0f) + return false; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + v = (dir.dot(qvec)) * oneOverDet; + if(v<0.0f || v>1.0f) + return false; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + } + return true; +} + +bool Gu::sweepCapsuleCapsule(const Capsule& capsule0, const Capsule& capsule1, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& ip, PxVec3& normal, PxU32 inHitFlags, PxU16& outHitFlags) +{ + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + if(!(inHitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + + // PT: It would be better not to use the same code path for spheres and capsules. The segment-segment distance + // function doesn't work for degenerate capsules so we need to test all combinations here anyway. + bool initialOverlapStatus; + if(capsule0.p0==capsule0.p1) + initialOverlapStatus = distancePointSegmentSquared(capsule1, capsule0.p0)<radiusSum*radiusSum; + else if(capsule1.p0==capsule1.p1) + initialOverlapStatus = distancePointSegmentSquared(capsule0, capsule1.p0)<radiusSum*radiusSum; + else + initialOverlapStatus = distanceSegmentSegmentSquared(capsule0, capsule1)<radiusSum*radiusSum; + + if(initialOverlapStatus) + { + min_dist = 0.0f; + normal = -dir; + outHitFlags = PxHitFlag::eDISTANCE | PxHitFlag::eNORMAL; + return true; + } + } + + // 1. Extrude capsule0 by capsule1's length + // 2. Inflate extruded shape by capsule1's radius + // 3. Raycast against resulting shape + + const PxVec3 capsuleExtent1 = capsule1.p1 - capsule1.p0; + + // Extrusion dir = capsule segment + const PxVec3 D = capsuleExtent1*0.5f; + + const PxVec3 p0 = capsule0.p0 - D; + const PxVec3 p1 = capsule0.p1 - D; + const PxVec3 p0b = capsule0.p0 + D; + const PxVec3 p1b = capsule0.p1 + D; + + PxTriangle T(p0b, p1b, p1); + PxVec3 Normal; + T.normal(Normal); + + PxReal MinDist = length; + bool Status = false; + + PxVec3 pa,pb,pc; + if((Normal.dot(dir)) >= 0) // Same direction + { + Normal *= radiusSum; + pc = p0 - Normal; + pa = p1 - Normal; + pb = p1b - Normal; + } + else + { + Normal *= radiusSum; + pb = p0 + Normal; + pa = p1 + Normal; + pc = p1b + Normal; + } + PxReal t, u, v; + const PxVec3 center = capsule1.computeCenter(); + if(rayQuad(center, dir, pa, pb, pc, t, u, v, true) && t>=0.0f && t<MinDist) + { + MinDist = t; + Status = true; + } + + // PT: optimization: if we hit one of the quad we can't possibly get a better hit, so let's skip all + // the remaining tests! + if(!Status) + { + Capsule Caps[4]; + Caps[0] = Capsule(p0, p1, radiusSum); + Caps[1] = Capsule(p1, p1b, radiusSum); + Caps[2] = Capsule(p1b, p0b, radiusSum); + Caps[3] = Capsule(p0, p0b, radiusSum); + + // ### a lot of ray-sphere tests could be factored out of the ray-capsule tests... + for(PxU32 i=0;i<4;i++) + { + PxReal w; + if(intersectRayCapsule(center, dir, Caps[i], w)) + { + if(w>=0.0f && w<= MinDist) + { + MinDist = w; + Status = true; + } + } + } + } + + if(Status) + { + outHitFlags = PxHitFlag::eDISTANCE; + if(inHitFlags & PxU32(PxHitFlag::ePOSITION|PxHitFlag::eNORMAL)) + { + const PxVec3 p00 = capsule0.p0 - MinDist * dir; + const PxVec3 p01 = capsule0.p1 - MinDist * dir; +// const PxVec3 p10 = capsule1.p0;// - MinDist * dir; +// const PxVec3 p11 = capsule1.p1;// - MinDist * dir; + + const PxVec3 edge0 = p01 - p00; + const PxVec3 edge1 = capsuleExtent1; + + PxVec3 x, y; + edgeEdgeDist(x, y, p00, edge0, capsule1.p0, edge1); + + if(inHitFlags & PxHitFlag::eNORMAL) + { + normal = (x - y); + const float epsilon = 0.001f; + if(normal.normalize()<epsilon) + { + // PT: happens when radiuses are zero + normal = edge1.cross(edge0); + if(normal.normalize()<epsilon) + { + // PT: happens when edges are parallel + const PxVec3 capsuleExtent0 = capsule0.p1 - capsule0.p0; + edgeEdgeDist(x, y, capsule0.p0, capsuleExtent0, capsule1.p0, edge1); + normal = (x - y); + normal.normalize(); + } + } + + outHitFlags |= PxHitFlag::eNORMAL; + } + + if(inHitFlags & PxHitFlag::ePOSITION) + { + ip = (capsule1.radius*x + capsule0.radius*y)/(capsule0.radius+capsule1.radius); + outHitFlags |= PxHitFlag::ePOSITION; + } + } + min_dist = MinDist; + } + return Status; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleCapsule.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleCapsule.h new file mode 100644 index 00000000..b6d6d712 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleCapsule.h @@ -0,0 +1,48 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_CAPSULE_H +#define GU_SWEEP_CAPSULE_CAPSULE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + bool sweepCapsuleCapsule(const Capsule& capsule0, const Capsule& capsule1, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& ip, PxVec3& normal, PxU32 inHitFlags, PxU16& outHitFlags); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleTriangle.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleTriangle.cpp new file mode 100644 index 00000000..82c4d1dc --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleTriangle.cpp @@ -0,0 +1,328 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepCapsuleTriangle.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuDistanceSegmentTriangleSIMD.h" +#include "GuIntersectionTriangleBox.h" +#include "GuSweepSphereTriangle.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +#define COLINEARITY_EPSILON 0.00001f + +/////////////////////////////////////////////////////////////////////////////// + +#define OUTPUT_TRI(pp0, pp1, pp2){ \ + extrudedTris[nbExtrudedTris].verts[0] = pp0; \ + extrudedTris[nbExtrudedTris].verts[1] = pp1; \ + extrudedTris[nbExtrudedTris].verts[2] = pp2; \ + extrudedTris[nbExtrudedTris].denormalizedNormal(extrudedTrisNormals[nbExtrudedTris]); \ + nbExtrudedTris++;} + +#define OUTPUT_TRI2(p0, p1, p2, d){ \ + PxTriangle& tri = extrudedTris[nbExtrudedTris]; \ + tri.verts[0] = p0; \ + tri.verts[1] = p1; \ + tri.verts[2] = p2; \ + PxVec3 nrm; \ + tri.denormalizedNormal(nrm); \ + if(nrm.dot(d)>0.0f) { \ + PxVec3 tmp = tri.verts[1]; \ + tri.verts[1] = tri.verts[2]; \ + tri.verts[2] = tmp; \ + nrm = -nrm; \ + } \ + extrudedTrisNormals[nbExtrudedTris] = nrm; \ + nbExtrudedTris++; } + + +bool Gu::sweepCapsuleTriangles_Precise( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const Capsule& capsule, // Capsule data + const PxVec3& unitDir, const PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + PxHitFlags hitFlags, bool isDoubleSided, // Query modifiers + const BoxPadded* cullBox) // Cull data +{ + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const bool testInitialOverlap = !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP); + + // PT: we can fallback to sphere sweep: + // - if the capsule is degenerate (i.e. it's a sphere) + // - if the sweep direction is the same as the capsule axis, in which case we can just sweep the top or bottom sphere + + const PxVec3 extrusionDir = (capsule.p0 - capsule.p1)*0.5f; // Extrusion dir = capsule segment + const PxReal halfHeight = extrusionDir.magnitude(); + bool mustExtrude = halfHeight!=0.0f; + if(!mustExtrude) + { + // PT: capsule is a sphere. Switch to sphere path (intersectCapsuleTriangle doesn't work for degenerate capsules) + return sweepSphereTriangles(nbTris, triangles, capsule.p0, capsule.radius, unitDir, distance, cachedIndex, hit, triNormalOut, isDoubleSided, meshBothSides, anyHit, testInitialOverlap); + } + else + { + const PxVec3 capsuleAxis = extrusionDir/halfHeight; + const PxReal colinearity = PxAbs(capsuleAxis.dot(unitDir)); + mustExtrude = (colinearity < (1.0f - COLINEARITY_EPSILON)); + } + + const PxVec3 capsuleCenter = capsule.computeCenter(); + + if(!mustExtrude) + { + CapsuleTriangleOverlapData params; + params.init(capsule); + // PT: unfortunately we need to do IO test with the *capsule*, even though we're in the sphere codepath. So we + // can't directly reuse the sphere function. + const PxVec3 sphereCenter = capsuleCenter + unitDir * halfHeight; + // PT: this is a copy of 'sweepSphereTriangles' but with a capsule IO test. Saves double backface culling.... + { + PxU32 index = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + PxReal curT = distance; + const PxReal dpc0 = sphereCenter.dot(unitDir); + + PxReal bestAlignmentValue = 2.0f; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0; ii<nbTris; ii++) // We need i for returned triangle index + { + const PxU32 i = getTriangleIndex(ii, initIndex); + + const PxTriangle& currentTri = triangles[i]; + + if(rejectTriangle(sphereCenter, unitDir, curT, capsule.radius, currentTri.verts, dpc0)) + continue; + + PxVec3 triNormal; + currentTri.denormalizedNormal(triNormal); + + // Backface culling + if(doBackfaceCulling && (triNormal.dot(unitDir) > 0.0f)) + continue; + + if(testInitialOverlap && intersectCapsuleTriangle(triNormal, currentTri.verts[0], currentTri.verts[1], currentTri.verts[2], capsule, params)) + return setInitialOverlapResults(hit, unitDir, i); + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if (!sweepSphereVSTri(currentTri.verts, triNormal, sphereCenter, capsule.radius, unitDir, currentDistance, unused, false)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal hitDot = computeAlignmentValue(triNormal, unitDir); + if (!keepTriangle(currentDistance, hitDot, curT, bestAlignmentValue, distance, distEpsilon)) + continue; + + curT = currentDistance; + index = i; + bestAlignmentValue = hitDot; + bestTriNormal = triNormal; + if(anyHit) + break; + } + return computeSphereTriangleImpactData(hit, triNormalOut, index, curT, sphereCenter, unitDir, bestTriNormal, triangles, isDoubleSided, meshBothSides); + } + } + + // PT: extrude mesh on the fly. This is a modified copy of sweepSphereTriangles, unfortunately + PxTriangle extrudedTris[7]; + PxVec3 extrudedTrisNormals[7]; // Not normalized + + hit.faceIndex = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + const PxReal radius = capsule.radius; + PxReal curT = distance; + const PxReal dpc0 = capsuleCenter.dot(unitDir); + + // PT: we will copy the best triangle here. Using indices alone doesn't work + // since we extrude on-the-fly (and we don't want to re-extrude later) + PxTriangle bestTri; + PxVec3 bestTriNormal(0.0f); + PxReal mostOpposingHitDot = 2.0f; + + CapsuleTriangleOverlapData params; + params.init(capsule); + + for(PxU32 ii=0; ii<nbTris; ii++) // We need i for returned triangle index + { + const PxU32 i = getTriangleIndex(ii, initIndex); + + const PxTriangle& currentSrcTri = triangles[i]; // PT: src tri, i.e. non-extruded + +///////////// PT: this part comes from "ExtrudeMesh" + // Create triangle normal + PxVec3 denormalizedNormal; + currentSrcTri.denormalizedNormal(denormalizedNormal); + + // Backface culling + if(doBackfaceCulling && (denormalizedNormal.dot(unitDir) > 0.0f)) + continue; + + if(cullBox) + { + if(!intersectTriangleBox(*cullBox, currentSrcTri.verts[0], currentSrcTri.verts[1], currentSrcTri.verts[2])) + continue; + } + + if(testInitialOverlap && intersectCapsuleTriangle(denormalizedNormal, currentSrcTri.verts[0], currentSrcTri.verts[1], currentSrcTri.verts[2], capsule, params)) + return setInitialOverlapResults(hit, unitDir, i); + + // Extrude mesh on the fly + PxU32 nbExtrudedTris=0; + + const PxVec3 p0 = currentSrcTri.verts[0] - extrusionDir; + const PxVec3 p1 = currentSrcTri.verts[1] - extrusionDir; + const PxVec3 p2 = currentSrcTri.verts[2] - extrusionDir; + + const PxVec3 p0b = currentSrcTri.verts[0] + extrusionDir; + const PxVec3 p1b = currentSrcTri.verts[1] + extrusionDir; + const PxVec3 p2b = currentSrcTri.verts[2] + extrusionDir; + + if(denormalizedNormal.dot(extrusionDir) >= 0.0f) OUTPUT_TRI(p0b, p1b, p2b) + else OUTPUT_TRI(p0, p1, p2) + + // ### it's probably useless to extrude all the shared edges !!!!! + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE12) + { + OUTPUT_TRI2(p1, p1b, p2b, unitDir) + OUTPUT_TRI2(p1, p2b, p2, unitDir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE20) + { + OUTPUT_TRI2(p0, p2, p2b, unitDir) + OUTPUT_TRI2(p0, p2b, p0b, unitDir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE01) + { + OUTPUT_TRI2(p0b, p1b, p1, unitDir) + OUTPUT_TRI2(p0b, p1, p0, unitDir) + } +///////////// + + // PT: TODO: this one is new, to fix the tweak issue. However this wasn't + // here before so the perf hit should be analyzed. + denormalizedNormal.normalize(); + const PxReal hitDot1 = computeAlignmentValue(denormalizedNormal, unitDir); + + for(PxU32 j=0;j<nbExtrudedTris;j++) + { + const PxTriangle& currentTri = extrudedTris[j]; + + PxVec3& triNormal = extrudedTrisNormals[j]; + // Backface culling + if(doBackfaceCulling && (triNormal.dot(unitDir)) > 0.0f) + continue; + + // PT: beware, culling is only ok on the sphere I think + if(rejectTriangle(capsuleCenter, unitDir, curT, radius, currentTri.verts, dpc0)) + continue; + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if (!sweepSphereVSTri(currentTri.verts, triNormal, capsuleCenter, radius, unitDir, currentDistance, unused, false)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + if (!keepTriangle(currentDistance, hitDot1, curT, mostOpposingHitDot, distance, distEpsilon)) + continue; + + curT = currentDistance; + hit.faceIndex = i; + mostOpposingHitDot = hitDot1; // arbitrary bias. works for hitDot1=-1, prevHitDot=0 + bestTri = currentTri; + bestTriNormal = denormalizedNormal; + if(anyHit) + goto Exit; // PT: using goto to have one test per hit, not test per triangle ('break' doesn't work here) + } + } +Exit: + if(hit.faceIndex==PX_INVALID_U32) + return false; // We didn't touch any triangle + + hit.distance = curT; + + triNormalOut = bestTriNormal; + + // Compute impact data only once, using best triangle + computeSphereTriImpactData(hit.position, hit.normal, capsuleCenter, unitDir, hit.distance, bestTri); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(hit.normal, meshBothSides, isDoubleSided, bestTriNormal, unitDir)) + hit.normal = -hit.normal; + + // PT: revisit this + if(hit.faceIndex!=PX_INVALID_U32) + { + // PT: we need to recompute a hit here because the hit between the *capsule* and the source mesh can be very + // different from the hit between the *sphere* and the extruded mesh. + + // Touched tri + const PxVec3& p0 = triangles[hit.faceIndex].verts[0]; + const PxVec3& p1 = triangles[hit.faceIndex].verts[1]; + const PxVec3& p2 = triangles[hit.faceIndex].verts[2]; + + // AP: measured to be a bit faster than the scalar version + const PxVec3 delta = unitDir*hit.distance; + Vec3V pointOnSeg, pointOnTri; + distanceSegmentTriangleSquared( + V3LoadU(capsule.p0 + delta), V3LoadU(capsule.p1 + delta), + V3LoadU(p0), V3LoadU(p1), V3LoadU(p2), + pointOnSeg, pointOnTri); + V3StoreU(pointOnTri, hit.position); + + hit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + } + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleTriangle.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleTriangle.h new file mode 100644 index 00000000..e56d02ef --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepCapsuleTriangle.h @@ -0,0 +1,75 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_TRIANGLE_H +#define GU_SWEEP_CAPSULE_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ + class PxTriangle; + +namespace Gu +{ + class BoxPadded; + class Capsule; + + /** + Sweeps a capsule against a set of triangles. + + \param nbTris [in] number of triangles in input array + \param triangles [in] array of input triangles + \param capsule [in] the capsule + \param unitDir [in] sweep's unit direcion + \param distance [in] sweep's length + \param cachedIndex [in] cached triangle index, or NULL. Cached triangle will be tested first. + \param hit [out] results + \param triNormalOut [out] triangle normal + \param hitFlags [in] query modifiers + \param isDoubleSided [in] true if input triangles are double-sided + \param cullBox [in] additional/optional culling box. Triangles not intersecting the box are quickly discarded. + \warning if using a cullbox, make sure all triangles can be safely V4Loaded (i.e. allocate 4 more bytes after last triangle) + \return true if an impact has been found + */ + bool sweepCapsuleTriangles_Precise( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const Capsule& capsule, // Capsule data + const PxVec3& unitDir, const PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + PxHitFlags hitFlags, bool isDoubleSided, // Query modifiers + const BoxPadded* cullBox=NULL); // Cull data + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereCapsule.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereCapsule.cpp new file mode 100644 index 00000000..498ae293 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereCapsule.cpp @@ -0,0 +1,103 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepSphereCapsule.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuSweepSphereSphere.h" +#include "GuIntersectionRayCapsule.h" + +using namespace physx; +using namespace Gu; + +bool Gu::sweepSphereCapsule(const Sphere& sphere, const Capsule& lss, const PxVec3& dir, PxReal length, PxReal& d, PxVec3& ip, PxVec3& nrm, PxHitFlags hitFlags) +{ + const PxReal radiusSum = lss.radius + sphere.radius; + + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(distancePointSegmentSquared(lss.p0, lss.p1, sphere.center)<radiusSum*radiusSum) + { + d = 0.0f; + nrm = -dir; + return true; + } + } + + if(lss.p0 == lss.p1) + { + // Sphere vs. sphere + if(sweepSphereSphere(sphere.center, sphere.radius, lss.p0, lss.radius, -dir*length, d, nrm)) + { + d*=length; +// if(hitFlags & PxHitFlag::ePOSITION) // PT: TODO + ip = sphere.center + nrm * sphere.radius; + return true; + } + return false; + } + + // Create inflated capsule + Capsule Inflated(lss.p0, lss.p1, radiusSum); + + // Raycast against it + PxReal t; + if(intersectRayCapsule(sphere.center, dir, Inflated, t)) + { + if(t>=0.0f && t<=length) + { + d = t; + +// PT: TODO: +// const Ps::IntBool needsImpactPoint = hitFlags & PxHitFlag::ePOSITION; +// if(needsImpactPoint || hitFlags & PxHitFlag::eNORMAL) + { + // Move capsule against sphere + const PxVec3 tdir = t*dir; + Inflated.p0 -= tdir; + Inflated.p1 -= tdir; + + // Compute closest point between moved capsule & sphere + distancePointSegmentSquared(Inflated, sphere.center, &t); + Inflated.computePoint(ip, t); + + // Normal + nrm = (ip - sphere.center); + nrm.normalize(); + +// if(needsImpactPoint) // PT: TODO + ip -= nrm * lss.radius; + } + return true; + } + } + return false; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereCapsule.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereCapsule.h new file mode 100644 index 00000000..c8b76508 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereCapsule.h @@ -0,0 +1,50 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_CAPSULE_H +#define GU_SWEEP_SPHERE_CAPSULE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Sphere; + class Capsule; + + bool sweepSphereCapsule(const Sphere& sphere, const Capsule& lss, const PxVec3& dir, PxReal length, PxReal& d, PxVec3& ip, PxVec3& nrm, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereSphere.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereSphere.cpp new file mode 100644 index 00000000..7a84750d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereSphere.cpp @@ -0,0 +1,116 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepSphereSphere.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +// Adapted from Gamasutra (Gomez article) +// Return true if r1 and r2 are real +static PX_FORCE_INLINE bool quadraticFormula(const PxReal a, const PxReal b, const PxReal c, PxReal& r1, PxReal& r2) +{ + const PxReal q = b*b - 4*a*c; + if(q>=0.0f) + { + PX_ASSERT(a!=0.0f); + const PxReal sq = PxSqrt(q); + const PxReal d = 1.0f / (2.0f*a); + r1 = (-b + sq) * d; + r2 = (-b - sq) * d; + return true;//real roots + } + else + { + return false;//complex roots + } +} + +static bool sphereSphereSweep( const PxReal ra, //radius of sphere A + const PxVec3& A0, //previous position of sphere A + const PxVec3& A1, //current position of sphere A + const PxReal rb, //radius of sphere B + const PxVec3& B0, //previous position of sphere B + const PxVec3& B1, //current position of sphere B + PxReal& u0, //normalized time of first collision + PxReal& u1 //normalized time of second collision + ) +{ + const PxVec3 va = A1 - A0; + const PxVec3 vb = B1 - B0; + const PxVec3 AB = B0 - A0; + const PxVec3 vab = vb - va; // relative velocity (in normalized time) + const PxReal rab = ra + rb; + + const PxReal a = vab.dot(vab); //u*u coefficient + const PxReal b = 2.0f*(vab.dot(AB)); //u coefficient + + const PxReal c = (AB.dot(AB)) - rab*rab; //constant term + + //check if they're currently overlapping + if(c<=0.0f || a==0.0f) + { + u0 = 0.0f; + u1 = 0.0f; + return true; + } + + //check if they hit each other during the frame + if(quadraticFormula(a, b, c, u0, u1)) + { + if(u0>u1) + Ps::swap(u0, u1); + + // u0<u1 +// if(u0<0.0f || u1>1.0f) return false; + if(u1<0.0f || u0>1.0f) return false; + + return true; + } + return false; +} + +bool Gu::sweepSphereSphere(const PxVec3& center0, PxReal radius0, const PxVec3& center1, PxReal radius1, const PxVec3& motion, PxReal& d, PxVec3& nrm) +{ + const PxVec3 movedCenter = center1 + motion; + + PxReal tmp; + if(!sphereSphereSweep(radius0, center0, center0, radius1, center1, movedCenter, d, tmp)) + return false; + + // Compute normal + // PT: if spheres initially overlap, the convention is that returned normal = -sweep direction + if(d==0.0f) + nrm = -motion; + else + nrm = (center1 + d * motion) - center0; + nrm.normalize(); + return true; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereSphere.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereSphere.h new file mode 100644 index 00000000..1fd7cef5 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereSphere.h @@ -0,0 +1,46 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_SPHERE_H +#define GU_SWEEP_SPHERE_SPHERE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + bool sweepSphereSphere(const PxVec3& center0, PxReal radius0, const PxVec3& center1, PxReal radius1, const PxVec3& motion, PxReal& d, PxVec3& nrm); + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereTriangle.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereTriangle.cpp new file mode 100644 index 00000000..c54dac8b --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereTriangle.cpp @@ -0,0 +1,521 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "GuSweepSphereTriangle.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRayTriangle.h" +#include "GuCapsule.h" +#include "GuInternal.h" +#include "PsUtilities.h" +#include "GuDistancePointTriangle.h" + +using namespace physx; +using namespace Gu; + +// PT: using GU_CULLING_EPSILON_RAY_TRIANGLE fails here, in capsule-vs-mesh's triangle extrusion, when +// the sweep dir is almost the same as the capsule's dir (i.e. when we usually fallback to the sphere codepath). +// I suspect det becomes so small that we lose all accuracy when dividing by det and using the result in computing +// impact distance. +#define LOCAL_EPSILON 0.00001f + +// PT: special version computing (u,v) even when the ray misses the tri. Version working on precomputed edges. +static PX_FORCE_INLINE PxU32 rayTriSpecial(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, PxReal& t, PxReal& u, PxReal& v) +{ + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); + + // the non-culling branch +// if(det>-GU_CULLING_EPSILON_RAY_TRIANGLE && det<GU_CULLING_EPSILON_RAY_TRIANGLE) + if(det>-LOCAL_EPSILON && det<LOCAL_EPSILON) + return 0; + const PxReal oneOverDet = 1.0f / det; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter + u = (tvec.dot(pvec)) * oneOverDet; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter + v = (dir.dot(qvec)) * oneOverDet; + + if(u<0.0f || u>1.0f) + return 1; + if(v<0.0f || u+v>1.0f) + return 1; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + + return 2; +} + +// Returns true if sphere can be tested against triangle vertex, false if edge test should be performed +// +// Uses a conservative approach to work for "sliver triangles" (long & thin) as well. +static PX_FORCE_INLINE bool edgeOrVertexTest(const PxVec3& planeIntersectPoint, const PxVec3* PX_RESTRICT tri, PxU32 vertIntersectCandidate, PxU32 vert0, PxU32 vert1, PxU32& secondEdgeVert) +{ + { + const PxVec3 edge0 = tri[vertIntersectCandidate] - tri[vert0]; + const PxReal edge0LengthSqr = edge0.dot(edge0); + + const PxVec3 diff = planeIntersectPoint - tri[vert0]; + + if (edge0.dot(diff) < edge0LengthSqr) // If the squared edge length is used for comparison, the edge vector does not need to be normalized + { + secondEdgeVert = vert0; + return false; + } + } + + { + const PxVec3 edge1 = tri[vertIntersectCandidate] - tri[vert1]; + const PxReal edge1LengthSqr = edge1.dot(edge1); + + const PxVec3 diff = planeIntersectPoint - tri[vert1]; + + if (edge1.dot(diff) < edge1LengthSqr) + { + secondEdgeVert = vert1; + return false; + } + } + return true; +} + +static PX_FORCE_INLINE bool testRayVsSphereOrCapsule(PxReal& impactDistance, bool testSphere, const PxVec3& center, PxReal radius, const PxVec3& dir, const PxVec3* PX_RESTRICT verts, PxU32 e0, PxU32 e1) +{ + if(testSphere) + { + PxReal t; + if(intersectRaySphere(center, dir, PX_MAX_F32, verts[e0], radius, t)) + { + impactDistance = t; + return true; + } + } + else + { + PxReal t; + if(intersectRayCapsule(center, dir, verts[e0], verts[e1], radius, t)) + { + if(t>=0.0f/* && t<MinDist*/) + { + impactDistance = t; + return true; + } + } + } + return false; +} + +bool Gu::sweepSphereVSTri(const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, const PxVec3& center, PxReal radius, const PxVec3& dir, PxReal& impactDistance, bool& directHit, bool testInitialOverlap) +{ + // Ok, this new version is now faster than the original code. Needs more testing though. + + directHit = false; + const PxVec3 edge10 = triVerts[1] - triVerts[0]; + const PxVec3 edge20 = triVerts[2] - triVerts[0]; + + if(testInitialOverlap) // ### brute force version that always works, but we can probably do better + { + const PxVec3 cp = closestPtPointTriangle2(center, triVerts[0], triVerts[1], triVerts[2], edge10, edge20); + if((cp - center).magnitudeSquared() <= radius*radius) + { + impactDistance = 0.0f; + return true; + } + } + + #define INTERSECT_POINT (triVerts[1]*u) + (triVerts[2]*v) + (triVerts[0] * (1.0f-u-v)) + + PxReal u,v; + { + PxVec3 R = normal * radius; + if(dir.dot(R) >= 0.0f) + R = -R; + + // The first point of the sphere to hit the triangle plane is the point of the sphere nearest to + // the triangle plane. Hence, we use center - (normal*radius) below. + + // PT: casting against the extruded triangle in direction R is the same as casting from a ray moved by -R + PxReal t; + const PxU32 r = rayTriSpecial(center-R, dir, triVerts[0], edge10, edge20, t, u, v); + if(!r) + return false; + if(r==2) + { + if(t<0.0f) + return false; + impactDistance = t; + directHit = true; + return true; + } + } + + // + // Let's do some art! + // + // The triangle gets divided into the following areas (based on the barycentric coordinates (u,v)): + // + // \ A0 / + // \ / + // \ / + // \/ 0 + // A02 * A01 + // u / / \ \ v + // * / \ * + // / \ . + // 2 / \ 1 + // ------*--------------*------- + // / \ . + // A2 / A12 \ A1 + // + // + // Based on the area where the computed triangle plane intersection point lies in, a different sweep test will be applied. + // + // A) A01, A02, A12 : Test sphere against the corresponding edge + // B) A0, A1, A2 : Test sphere against the corresponding vertex + // + // Unfortunately, B) does not work for long, thin triangles. Hence there is some extra code which does a conservative check and + // switches to edge tests if necessary. + // + + bool TestSphere; + PxU32 e0,e1; + if(u<0.0f) + { + if(v<0.0f) + { + // 0 or 0-1 or 0-2 + e0 = 0; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 0, 1, 2, e1); + } + else if(u+v>1.0f) + { + // 2 or 2-0 or 2-1 + e0 = 2; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 2, 0, 1, e1); + } + else + { + // 0-2 + TestSphere = false; + e0 = 0; + e1 = 2; + } + } + else + { + if(v<0.0f) + { + if(u+v>1.0f) + { + // 1 or 1-0 or 1-2 + e0 = 1; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 1, 0, 2, e1); + } + else + { + // 0-1 + TestSphere = false; + e0 = 0; + e1 = 1; + } + } + else + { + PX_ASSERT(u+v>=1.0f); // Else hit triangle + // 1-2 + TestSphere = false; + e0 = 1; + e1 = 2; + } + } + return testRayVsSphereOrCapsule(impactDistance, TestSphere, center, radius, dir, triVerts, e0, e1); +} + +bool Gu::sweepSphereTriangles( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const PxVec3& center, const PxReal radius, // Sphere data + const PxVec3& unitDir, PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& h, PxVec3& triNormalOut, // Results + bool isDoubleSided, bool meshBothSides, bool anyHit, bool testInitialOverlap) // Query modifiers +{ + if(!nbTris) + return false; + + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + PxU32 index = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + PxReal curT = distance; + const PxReal dpc0 = center.dot(unitDir); + + PxReal bestAlignmentValue = 2.0f; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0; ii<nbTris; ii++) // We need i for returned triangle index + { + const PxU32 i = getTriangleIndex(ii, initIndex); + + const PxTriangle& currentTri = triangles[i]; + + if(rejectTriangle(center, unitDir, curT, radius, currentTri.verts, dpc0)) + continue; + + PxVec3 triNormal; + currentTri.denormalizedNormal(triNormal); + + // Backface culling + if(doBackfaceCulling && (triNormal.dot(unitDir) > 0.0f)) + continue; + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if (!sweepSphereVSTri(currentTri.verts, triNormal, center, radius, unitDir, currentDistance, unused, testInitialOverlap)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal hitDot = computeAlignmentValue(triNormal, unitDir); + if (!keepTriangle(currentDistance, hitDot, curT, bestAlignmentValue, distance, distEpsilon)) + continue; + + if(currentDistance==0.0f) + return setInitialOverlapResults(h, unitDir, i); + + curT = currentDistance; + index = i; + bestAlignmentValue = hitDot; + bestTriNormal = triNormal; + if(anyHit) + break; + } + return computeSphereTriangleImpactData(h, triNormalOut, index, curT, center, unitDir, bestTriNormal, triangles, isDoubleSided, meshBothSides); +} + +static PX_FORCE_INLINE PxU32 rayQuadSpecial2(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, float& t, float& u, float& v) +{ + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const float det = edge1.dot(pvec); + + // the non-culling branch + if(det>-LOCAL_EPSILON && det<LOCAL_EPSILON) + return 0; + const float OneOverDet = 1.0f / det; + + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter + u = tvec.dot(pvec) * OneOverDet; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter + v = dir.dot(qvec) * OneOverDet; + + if(u<0.0f || u>1.0f) + return 1; + if(v<0.0f || v>1.0f) + return 1; + + // Calculate t, ray intersects triangle + t = edge2.dot(qvec) * OneOverDet; + + return 2; +} + +bool Gu::sweepSphereVSQuad(const PxVec3* PX_RESTRICT quadVerts, const PxVec3& normal, const PxVec3& center, float radius, const PxVec3& dir, float& impactDistance) +{ + // Quad formed by 2 tris: + // p0 p1 p2 + // p2 p1 p3 = p3 p2 p1 + // + // p0___p2 + // | /| + // | / | + // | / | + // |/ | + // p1---p3 + // + // Edge10 = p1 - p0 + // Edge20 = p2 - p0 + // Impact point = Edge10*u + Edge20*v + p0 + // => u is along Y, between 0.0 (p0;p2) and 1.0 (p1;p3) + // => v is along X, between 0.0 (p0;p1) and 1.0 (p2;p3) + // + // For the second triangle, + // Edge10b = p2 - p3 = -Edge10 + // Edge20b = p1 - p3 = -Edge20 + + const PxVec3 Edge10 = quadVerts[1] - quadVerts[0]; + const PxVec3 Edge20 = quadVerts[2] - quadVerts[0]; + + if(1) // ### brute force version that always works, but we can probably do better + { + const float r2 = radius*radius; + { + const PxVec3 Cp = closestPtPointTriangle2(center, quadVerts[0], quadVerts[1], quadVerts[2], Edge10, Edge20); + if((Cp - center).magnitudeSquared() <= r2) + { + impactDistance = 0.0f; + return true; + } + } + { + const PxVec3 Cp = closestPtPointTriangle2(center, quadVerts[3], quadVerts[2], quadVerts[1], -Edge10, -Edge20); + if((Cp - center).magnitudeSquared() <= r2) + { + impactDistance = 0.0f; + return true; + } + } + } + + float u,v; + if(1) + { + PxVec3 R = normal * radius; + if(dir.dot(R) >= 0.0f) + R = -R; + + // The first point of the sphere to hit the quad plane is the point of the sphere nearest to + // the quad plane. Hence, we use center - (normal*radius) below. + + // PT: casting against the extruded quad in direction R is the same as casting from a ray moved by -R + float t; + PxU32 r = rayQuadSpecial2(center-R, dir, quadVerts[0], Edge10, Edge20, t, u, v); + if(!r) + return false; + if(r==2) + { + if(t<0.0f) + return false; + impactDistance = t; + return true; + } + } + + #define INTERSECT_POINT_Q (quadVerts[1]*u) + (quadVerts[2]*v) + (quadVerts[0] * (1.0f-u-v)) + + Ps::swap(u,v); + bool TestSphere; + PxU32 e0,e1; + if(u<0.0f) + { + if(v<0.0f) + { + // 0 or 0-1 or 0-2 + e0 = 0; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 0, 1, 2, e1); + } + else if(v>1.0f) + { + // 1 or 1-0 or 1-3 + e0 = 1; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 1, 0, 3, e1); + } + else + { + // 0-1 + TestSphere = false; + e0 = 0; + e1 = 1; + } + } + else if(u>1.0f) + { + if(v<0.0f) + { + // 2 or 2-0 or 2-3 + e0 = 2; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 2, 0, 3, e1); + } + else if(v>1.0f) + { + // 3 or 3-1 or 3-2 + e0 = 3; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 3, 1, 2, e1); + } + else + { + // 2-3 + TestSphere = false; + e0 = 2; + e1 = 3; + } + } + else + { + if(v<0.0f) + { + // 0-2 + TestSphere = false; + e0 = 0; + e1 = 2; + } + else + { + PX_ASSERT(v>=1.0f); // Else hit quad + // 1-3 + TestSphere = false; + e0 = 1; + e1 = 3; + } + } + return testRayVsSphereOrCapsule(impactDistance, TestSphere, center, radius, dir, quadVerts, e0, e1); +} + diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereTriangle.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereTriangle.h new file mode 100644 index 00000000..a3345e34 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepSphereTriangle.h @@ -0,0 +1,156 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_TRIANGLE_H +#define GU_SWEEP_SPHERE_TRIANGLE_H + +#include "GuSweepTriangleUtils.h" + +namespace physx +{ + +namespace Gu +{ + /** + Sweeps a sphere against a triangle. + + All input parameters (sphere, triangle, sweep direction) must be in the same space. Sweep length is assumed to be infinite. + + By default, 'testInitialOverlap' must be set to true to properly handle the case where the sphere already overlaps the triangle + at the start of the sweep. In such a case, returned impact distance is exactly 0.0f. If it is known ahead of time that the sphere + cannot overlap the triangle at t=0.0, then 'testInitialOverlap' can be set to false to skip the initial overlap test and make the + function run faster. + + If the ray defined by the sphere's center and the unit direction directly intersects the triangle-related part of the TSS (*) (i.e. + the prism from the Minkowski sum of the inflated triangle) then 'directHit' is set to true. Otherwise it is set to false. + + (*) For Triangle Swept Sphere, see http://gamma.cs.unc.edu/SSV/ssv.pdf for the origin of these names. + + \param triVerts [in] triangle vertices + \param triUnitNormal [in] triangle's normalized normal + \param sphereCenter [in] sphere's center + \param sphereRadius [in] sphere's radius + \param unitDir [in] normalized sweep direction. + \param impactDistance [out] impact distance, if a hit has been found. Does not need to be initialized before calling the function. + \param directHit [out] true if a direct hit has been found, see comments above. + \param testInitialOverlap [in] true if an initial sphere-vs-triangle overlap test must be performed, see comments above. + + \return true if an impact has been found (in which case returned result values are valid) + */ + bool sweepSphereVSTri( const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal,// Triangle data + const PxVec3& sphereCenter, PxReal sphereRadius, // Sphere data + const PxVec3& unitDir, // Ray data + PxReal& impactDistance, bool& directHit, // Results + bool testInitialOverlap); // Query modifier + + /** + Sweeps a sphere against a quad. + + All input parameters (sphere, quad, sweep direction) must be in the same space. Sweep length is assumed to be infinite. + + Quad must be formed by 2 tris like this: + + p0___p2 + | /| + | / | + | / | + |/ | + p1---p3 + + \param quadVerts [in] quad vertices + \param quadUnitNormal [in] quad's normalized normal + \param sphereCenter [in] sphere's center + \param sphereRadius [in] sphere's radius + \param unitDir [in] normalized sweep direction. + \param impactDistance [out] impact distance, if a hit has been found. Does not need to be initialized before calling the function. + + \return true if an impact has been found (in which case returned result values are valid) + */ + bool sweepSphereVSQuad( const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal, // Quad data + const PxVec3& sphereCenter, float sphereRadius, // Sphere data + const PxVec3& unitDir, // Ray data + float& impactDistance); // Results + + + // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found + PX_FORCE_INLINE bool computeSphereTriangleImpactData(PxSweepHit& h, PxVec3& triNormalOut, PxU32 index, PxReal curT, + const PxVec3& center, const PxVec3& unitDir, const PxVec3& bestTriNormal, + const PxTriangle* PX_RESTRICT triangles, + bool isDoubleSided, bool meshBothSides) + { + if(index==PX_INVALID_U32) + return false; // We didn't touch any triangle + + // Compute impact data only once, using best triangle + PxVec3 hitPos, normal; + computeSphereTriImpactData(hitPos, normal, center, unitDir, curT, triangles[index]); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(normal, meshBothSides, isDoubleSided, bestTriNormal, unitDir)) + normal = -normal; + + h.position = hitPos; + h.normal = normal; + h.distance = curT; + h.faceIndex = index; + h.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + triNormalOut = bestTriNormal; + return true; + } + + /** + Sweeps a sphere against a set of triangles. + + \param nbTris [in] number of triangles in input array + \param triangles [in] array of input triangles + \param center [in] sphere's center + \param radius [in] sphere's radius + \param unitDir [in] sweep's unit direcion + \param distance [in] sweep's length + \param cachedIndex [in] cached triangle index, or NULL. Cached triangle will be tested first. + \param hit [out] results + \param triNormalOut [out] triangle normal + \param isDoubleSided [in] true if input triangles are double-sided + \param meshBothSides [in] true if PxHitFlag::eMESH_BOTH_SIDES is used + \param anyHit [in] true if PxHitFlag::eMESH_ANY is used + \param testInitialOverlap [in] true if PxHitFlag::eASSUME_NO_INITIAL_OVERLAP is not used + \return true if an impact has been found + */ + bool sweepSphereTriangles( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const PxVec3& center, const PxReal radius, // Sphere data + const PxVec3& unitDir, PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + bool isDoubleSided, bool meshBothSides, bool anyHit, bool testInitialOverlap); // Query modifiers + +} // namespace Gu + +} + +#endif diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepTriangleUtils.cpp b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepTriangleUtils.cpp new file mode 100644 index 00000000..a69ae54d --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepTriangleUtils.cpp @@ -0,0 +1,223 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 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 "foundation/PxBounds3.h" +#include "GuSweepTriangleUtils.h" +#include "GuDistancePointTriangle.h" +#include "GuVecTriangle.h" +#include "GuVecBox.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuInternal.h" +#include "GuGJK.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +#define GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION 0.1f + +void Gu::computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri) +{ + const PxVec3 newSphereCenter = center + dir*t; + + // We need the impact point, not computed by the new code + PxReal u_unused, v_unused; + const PxVec3 localHit = closestPtPointTriangle(newSphereCenter, tri.verts[0], tri.verts[1], tri.verts[2], u_unused, v_unused); + PX_UNUSED(u_unused); + PX_UNUSED(v_unused); + + // This is responsible for the cap-vs-box stuck while jumping. However it's needed to slide on box corners! + // PT: this one is also dubious since the sphere/capsule center can be far away from the hit point when the radius is big! + PxVec3 localNormal = newSphereCenter - localHit; + const PxReal m = localNormal.normalize(); + if(m<1e-3f) + tri.normal(localNormal); + + hit = localHit; + normal = localNormal; +} + +// PT: not inlining this rarely-run function makes the benchmark ~500.000 cycles faster... +// PT: using this version all the time makes the benchmark ~300.000 cycles slower. So we just use it as a backup. +static bool runBackupProcedure(PxVec3& hit, PxVec3& normal, const PxVec3& localMotion, const PxVec3& boxExtents, const PxTriangle& triInBoxSpace) +{ + const Vec3V v0 = V3LoadU(triInBoxSpace.verts[0]); + const Vec3V v1 = V3LoadU(triInBoxSpace.verts[1]); + const Vec3V v2 = V3LoadU(triInBoxSpace.verts[2]); + + const TriangleV triangleV(v0, v1, v2); + + // PT: the box is in the triangle's space already + //BoxV boxV(V3LoadU(PxVec3(0.0f)), V3LoadU(boxExtents), + // V3LoadU(PxVec3(1.0f, 0.0f, 0.0f)), V3LoadU(PxVec3(0.0f, 1.0f, 0.0f)), V3LoadU(PxVec3(0.0f, 0.0f, 1.0f))); + + const BoxV boxV(V3Zero(), V3LoadU(boxExtents)); + + Vec3V closestA; + Vec3V closestB; + Vec3V normalV; + FloatV distV; + LocalConvex<TriangleV> convexA(triangleV); + LocalConvex<BoxV> convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + const FloatV contactDist = FMax(); + GjkStatus status_ = gjk<LocalConvex<TriangleV>, LocalConvex<BoxV> >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normalV, distV); + + if(status_==GJK_CONTACT) + return false; + + PxVec3 ml_closestB; + PxVec3 ml_normal; + V3StoreU(closestB, ml_closestB); + V3StoreU(normalV, ml_normal); + + hit = ml_closestB + localMotion; +// normal = -ml_normal; + if((ml_normal.dot(localMotion))>0.0f) + ml_normal = -ml_normal; + normal = ml_normal; + return true; +} + +void Gu::computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist) +{ + // PT: the triangle is in "box space", i.e. the box can be seen as an AABB centered around the origin. + + // PT: compute impact point/normal in a second pass. Here we simply re-sweep the box against the best triangle, + // using the feature-based code (which computes impact point and normal). This is not great because: + // - we know there's an impact so why do all tests again? + // - the SAT test & the feature-based tests could return different results because of FPU accuracy. + // The backup procedure makes sure we compute a proper answer even when the SAT and feature-based versions differ. + const PxBounds3 aabb(-boxExtents, boxExtents); + + const PxVec3 oneOverDir( + localDir.x!=0.0f ? 1.0f/localDir.x : 0.0f, + localDir.y!=0.0f ? 1.0f/localDir.y : 0.0f, + localDir.z!=0.0f ? 1.0f/localDir.z : 0.0f); + + // PT: TODO: this is the only place left using sweepBoxTriangle() + // Backface culling could be removed here since we know we want a hit no matter what. Plus, it's sometimes + // incorrectly culled and we hit the backup procedure for no reason. On Win32Modern for unknown reasons + // returned normal is sometimes (0,0,0). In these cases we also switch to the backup procedure. + float t = PX_MAX_F32; // PT: no need to initialize with best dist here since we want a hit no matter what + if(!sweepBoxTriangle(triInBoxSpace, aabb, localDir, oneOverDir, hit, normal, t) || normal.isZero()) + { + // PT: move triangle close to box + const PxVec3 localMotion = localDir*impactDist; + + const PxVec3 delta = localMotion - localDir*GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION; + const PxTriangle movedTriangle( + triInBoxSpace.verts[0] - delta, + triInBoxSpace.verts[1] - delta, + triInBoxSpace.verts[2] - delta); + + if(!runBackupProcedure(hit, normal, localMotion, boxExtents, movedTriangle)) + { + // PT: if the backup procedure fails, we give up + hit = PxVec3(0.0f); + normal = -localDir; + } + } +} + +// PT: copy where we know that input vectors are not zero +static PX_FORCE_INLINE void edgeEdgeDistNoZeroVector( PxVec3& x, PxVec3& y, // closest points + const PxVec3& p, const PxVec3& a, // seg 1 origin, vector + const PxVec3& q, const PxVec3& b) // seg 2 origin, vector +{ + const PxVec3 T = q - p; + const PxReal ADotA = a.dot(a); + const PxReal BDotB = b.dot(b); + PX_ASSERT(ADotA!=0.0f); + PX_ASSERT(BDotB!=0.0f); + const PxReal ADotB = a.dot(b); + const PxReal ADotT = a.dot(T); + const PxReal BDotT = b.dot(T); + + // t parameterizes ray (p, a) + // u parameterizes ray (q, b) + + // Compute t for the closest point on ray (p, a) to ray (q, b) + const PxReal Denom = ADotA*BDotB - ADotB*ADotB; + + PxReal t; + if(Denom!=0.0f) + { + t = (ADotT*BDotB - BDotT*ADotB) / Denom; + + // Clamp result so t is on the segment (p, a) + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + + // find u for point on ray (q, b) closest to point at t + PxReal u; + { + u = (t*ADotB - BDotT) / BDotB; + + // if u is on segment (q, b), t and u correspond to closest points, otherwise, clamp u, recompute and clamp t + if(u<0.0f) + { + u = 0.0f; + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else if(u > 1.0f) + { + u = 1.0f; + t = (ADotB + ADotT) / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + } + + x = p + a * t; + y = q + b * u; +} + +void Gu::computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d) +{ + // PT: cross-product doesn't produce nice normals so we use an edge-edge distance function itself + + // PT: move the edges "0.1" units from each other before the computation. If the edges are too far + // away, computed normal tend to align itself with the swept direction. If the edges are too close, + // closest points x and y become identical and we can't compute a proper normal. + const PxVec3 p1s = p1 + dir*(d-GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION); + + PxVec3 x, y; + edgeEdgeDistNoZeroVector(x, y, p1s, p2_p1, p3, p4_p3); + normal = x - y; +} diff --git a/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepTriangleUtils.h b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepTriangleUtils.h new file mode 100644 index 00000000..302cd595 --- /dev/null +++ b/PhysX_3.4/Source/GeomUtils/src/sweep/GuSweepTriangleUtils.h @@ -0,0 +1,338 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_TRIANGLE_UTILS_H +#define GU_SWEEP_TRIANGLE_UTILS_H + +#include "CmPhysXCommon.h" +#include "GuSweepSharedTests.h" +#include "GuInternal.h" +#include "PxTriangle.h" +#include "PxQueryReport.h" + +namespace physx +{ + +namespace Gu +{ + // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found. + void computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri); + + // PT: computes proper impact data for box-sweep-vs-tri, after the closest tri has been found. + void computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist); + + // PT: computes impact normal between two edges. Produces better normals than just the EE cross product. + // This version properly computes the closest points between two colliding edges and makes a normal from these. + void computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d); + + // PT: small function just to avoid duplicating the code. + // Returns index of first triangle we should process (when processing arrays of input triangles) + PX_FORCE_INLINE PxU32 getInitIndex(const PxU32* PX_RESTRICT cachedIndex, PxU32 nbTris) + { + PxU32 initIndex = 0; // PT: by default the first triangle to process is just the first one in the array + if(cachedIndex) // PT: but if we cached the last closest triangle from a previous call... + { + PX_ASSERT(*cachedIndex < nbTris); + PX_UNUSED(nbTris); + initIndex = *cachedIndex; // PT: ...then we should start with that one, to potentially shrink the ray as early as possible + } + return initIndex; + } + + // PT: quick triangle rejection for sphere-based sweeps. + // Please refer to \\sw\physx\PhysXSDK\3.4\trunk\InternalDocumentation\GU\cullTriangle.png for details & diagram. + PX_FORCE_INLINE bool cullTriangle(const PxVec3* PX_RESTRICT triVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0) + { + // PT: project triangle on axis + const PxReal dp0 = triVerts[0].dot(dir); + const PxReal dp1 = triVerts[1].dot(dir); + const PxReal dp2 = triVerts[2].dot(dir); + + // PT: keep min value = earliest possible impact distance + PxReal dp = dp0; + dp = physx::intrinsics::selectMin(dp, dp1); + dp = physx::intrinsics::selectMin(dp, dp2); + + // PT: make sure we keep triangles that are about as close as best current distance + radius += 0.001f + GU_EPSILON_SAME_DISTANCE; + + // PT: if earliest possible impact distance for this triangle is already larger than + // sphere's current best known impact distance, we can skip the triangle + if(dp>dpc0 + t + radius) + { + //PX_ASSERT(resx == 0.0f); + return false; + } + + // PT: if triangle is fully located before the sphere's initial position, skip it too + const PxReal dpc1 = dpc0 - radius; + if(dp0<dpc1 && dp1<dpc1 && dp2<dpc1) + { + //PX_ASSERT(resx == 0.0f); + return false; + } + + //PX_ASSERT(resx != 0.0f); + return true; + } + + // PT: quick quad rejection for sphere-based sweeps. Same as for triangle, adapted for one more vertex. + PX_FORCE_INLINE bool cullQuad(const PxVec3* PX_RESTRICT quadVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0) + { + // PT: project quad on axis + const PxReal dp0 = quadVerts[0].dot(dir); + const PxReal dp1 = quadVerts[1].dot(dir); + const PxReal dp2 = quadVerts[2].dot(dir); + const PxReal dp3 = quadVerts[3].dot(dir); + + // PT: keep min value = earliest possible impact distance + PxReal dp = dp0; + dp = physx::intrinsics::selectMin(dp, dp1); + dp = physx::intrinsics::selectMin(dp, dp2); + dp = physx::intrinsics::selectMin(dp, dp3); + + // PT: make sure we keep quads that are about as close as best current distance + radius += 0.001f; + + // PT: if earliest possible impact distance for this quad is already larger than + // sphere's current best known impact distance, we can skip the quad + if(dp>dpc0 + t + radius) + return false; + + // PT: if quad is fully located before the sphere's initial position, skip it too + const float dpc1 = dpc0 - radius; + if(dp0<dpc1 && dp1<dpc1 && dp2<dpc1 && dp3<dpc1) + return false; + + return true; + } + + // PT: computes distance between a point 'point' and a segment. The segment is defined as a starting point 'p0' + // and a direction vector 'dir' plus a length 't'. Segment's endpoint is p0 + dir * t. + // + // point + // o + // __/| + // __/ / | + // __/ / |(B) + // __/ (A)/ | + // __/ / | dir + // p0 o/---------o---------------o-- --> + // t (t<=fT) t (t>fT) + // return (A)^2 return (B)^2 + // + // |<-------------->| + // fT + // + PX_FORCE_INLINE PxReal squareDistance(const PxVec3& p0, const PxVec3& dir, PxReal t, const PxVec3& point) + { + PxVec3 diff = point - p0; + PxReal fT = diff.dot(dir); + fT = physx::intrinsics::selectMax(fT, 0.0f); + fT = physx::intrinsics::selectMin(fT, t); + diff -= fT*dir; + return diff.magnitudeSquared(); + } + + // PT: quick triangle culling for sphere-based sweeps + // Please refer to \\sw\physx\PhysXSDK\3.4\trunk\InternalDocumentation\GU\coarseCulling.png for details & diagram. + PX_FORCE_INLINE bool coarseCullingTri(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT triVerts) + { + // PT: compute center of triangle ### could be precomputed? + const PxVec3 triCenter = (triVerts[0] + triVerts[1] + triVerts[2]) * (1.0f/3.0f); + + // PT: distance between the triangle center and the swept path (an LSS) + // Same as: distancePointSegmentSquared(center, center+dir*t, TriCenter); + PxReal d = PxSqrt(squareDistance(center, dir, t, triCenter)) - radius - 0.0001f; + + if (d < 0.0f) // The triangle center lies inside the swept sphere + return true; + + d*=d; + + // PT: coarse capsule-vs-triangle overlap test ### distances could be precomputed? + if(1) + { + if(d <= (triCenter-triVerts[0]).magnitudeSquared()) + return true; + if(d <= (triCenter-triVerts[1]).magnitudeSquared()) + return true; + if(d <= (triCenter-triVerts[2]).magnitudeSquared()) + return true; + } + else + { + const float d0 = (triCenter-triVerts[0]).magnitudeSquared(); + const float d1 = (triCenter-triVerts[1]).magnitudeSquared(); + const float d2 = (triCenter-triVerts[2]).magnitudeSquared(); + float triRadius = physx::intrinsics::selectMax(d0, d1); + triRadius = physx::intrinsics::selectMax(triRadius, d2); + if(d <= triRadius) + return true; + } + return false; + } + + // PT: quick quad culling for sphere-based sweeps. Same as for triangle, adapted for one more vertex. + PX_FORCE_INLINE bool coarseCullingQuad(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT quadVerts) + { + // PT: compute center of quad ### could be precomputed? + const PxVec3 quadCenter = (quadVerts[0] + quadVerts[1] + quadVerts[2] + quadVerts[3]) * (1.0f/4.0f); + + // PT: distance between the quad center and the swept path (an LSS) + PxReal d = PxSqrt(squareDistance(center, dir, t, quadCenter)) - radius - 0.0001f; + + if (d < 0.0f) // The quad center lies inside the swept sphere + return true; + + d*=d; + + // PT: coarse capsule-vs-quad overlap test ### distances could be precomputed? + if(1) + { + if(d <= (quadCenter-quadVerts[0]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[1]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[2]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[3]).magnitudeSquared()) + return true; + } + return false; + } + + // PT: combined triangle culling for sphere-based sweeps + PX_FORCE_INLINE bool rejectTriangle(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxReal dpc0) + { + if(!coarseCullingTri(center, unitDir, curT, radius, triVerts)) + return true; + if(!cullTriangle(triVerts, unitDir, radius, curT, dpc0)) + return true; + return false; + } + + // PT: combined quad culling for sphere-based sweeps + PX_FORCE_INLINE bool rejectQuad(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT quadVerts, const PxReal dpc0) + { + if(!coarseCullingQuad(center, unitDir, curT, radius, quadVerts)) + return true; + if(!cullQuad(quadVerts, unitDir, radius, curT, dpc0)) + return true; + return false; + } + + PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxVec3& triangleNormal, const PxVec3& dir) + { + // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always + // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. + + if(!meshBothSides || isDoubleSided) + return false; + + PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied + PX_UNUSED(normal); + return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits + } + + PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxTriangle& triangle, const PxVec3& dir, const PxTransform* pose) + { + // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always + // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. + + if(!meshBothSides || isDoubleSided) + return false; + + PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied + PX_UNUSED(normal); + + PxVec3 triangleNormal; + triangle.denormalizedNormal(triangleNormal); + + if(pose) + triangleNormal = pose->rotate(triangleNormal); + + return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits + } + + // PT: implements the spec for IO sweeps in a single place (to ensure consistency) + PX_FORCE_INLINE bool setInitialOverlapResults(PxSweepHit& hit, const PxVec3& unitDir, PxU32 faceIndex) + { + // PT: please write these fields in the order they are listed in the struct. + hit.faceIndex = faceIndex; + hit.flags = PxHitFlag::eDISTANCE|PxHitFlag::eNORMAL|PxHitFlag::eFACE_INDEX; + hit.normal = -unitDir; + hit.distance = 0.0f; + return true; // PT: true indicates a hit, saves some lines in calling code + } + + PX_FORCE_INLINE void computeBoxLocalImpact( PxVec3& pos, PxVec3& normal, PxHitFlags& outFlags, + const Box& box, const PxVec3& localDir, const PxTriangle& triInBoxSpace, + const PxHitFlags inFlags, bool isDoubleSided, bool meshBothSides, PxReal impactDist) + { + if(inFlags & (PxHitFlag::eNORMAL|PxHitFlag::ePOSITION)) + { + PxVec3 localPos, localNormal; + computeBoxTriImpactData(localPos, localNormal, box.extents, localDir, triInBoxSpace, impactDist); + + if(inFlags & PxHitFlag::eNORMAL) + { + localNormal.normalize(); + + // PT: doing this after the 'rotate' minimizes errors when normal and dir are close to perpendicular + // ....but we must do it before the rotate now, because triangleNormal is in box space (and thus we + // need the normal with the proper orientation, in box space. We can't fix it after it's been rotated + // to box space. + // Technically this one is only here because of the EE cross product in the feature-based sweep. + // PT: TODO: revisit corresponding code in computeImpactData, get rid of ambiguity + // PT: TODO: this may not be needed anymore + if((localNormal.dot(localDir))>0.0f) + localNormal = -localNormal; + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + if(shouldFlipNormal(localNormal, meshBothSides, isDoubleSided, triInBoxSpace, localDir, NULL)) + localNormal = -localNormal; + + normal = box.rotate(localNormal); + outFlags |= PxHitFlag::eNORMAL; + } + + if(inFlags & PxHitFlag::ePOSITION) + { + pos = box.transform(localPos); + outFlags |= PxHitFlag::ePOSITION; + } + } + } + +} // namespace Gu + +} + +#endif |