feat(physics): wire physx sdk into build
This commit is contained in:
662
engine/third_party/physx/source/geomutils/src/common/GuAdjacencies.cpp
vendored
Normal file
662
engine/third_party/physx/source/geomutils/src/common/GuAdjacencies.cpp
vendored
Normal file
@@ -0,0 +1,662 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "GuEdgeList.h"
|
||||
#include "GuAdjacencies.h"
|
||||
#include "CmSerialize.h"
|
||||
#include "CmRadixSort.h"
|
||||
|
||||
// PT: code archeology: this initially came from ICE (IceAdjacencies.h/cpp). Consider putting it back the way it was initially.
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PX_IMPLEMENT_OUTPUT_ERROR
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Flips the winding.
|
||||
*/
|
||||
void AdjTriangle::Flip()
|
||||
{
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
// Call the Triangle method
|
||||
IndexedTriangle::Flip();
|
||||
#endif
|
||||
|
||||
// Flip links. We flipped vertex references 1 & 2, i.e. links 0 & 1.
|
||||
physx::PxSwap(mATri[0], mATri[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of boundary edges in a triangle.
|
||||
* \return the number of boundary edges. (0 => 3)
|
||||
*/
|
||||
PxU32 AdjTriangle::ComputeNbBoundaryEdges() const
|
||||
{
|
||||
// Look for boundary edges
|
||||
PxU32 Nb = 0;
|
||||
if(IS_BOUNDARY(mATri[0])) Nb++;
|
||||
if(IS_BOUNDARY(mATri[1])) Nb++;
|
||||
if(IS_BOUNDARY(mATri[2])) Nb++;
|
||||
return Nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of valid neighbors.
|
||||
* \return the number of neighbors. (0 => 3)
|
||||
*/
|
||||
PxU32 AdjTriangle::ComputeNbNeighbors() const
|
||||
{
|
||||
PxU32 Nb = 0;
|
||||
if(!IS_BOUNDARY(mATri[0])) Nb++;
|
||||
if(!IS_BOUNDARY(mATri[1])) Nb++;
|
||||
if(!IS_BOUNDARY(mATri[2])) Nb++;
|
||||
return Nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the triangle has a particular neighbor or not.
|
||||
* \param tref [in] the triangle reference to look for
|
||||
* \param index [out] the corresponding index in the triangle (NULL if not needed)
|
||||
* \return true if the triangle has the given neighbor
|
||||
*/
|
||||
bool AdjTriangle::HasNeighbor(PxU32 tref, PxU32* index) const
|
||||
{
|
||||
// ### could be optimized
|
||||
if(!IS_BOUNDARY(mATri[0]) && MAKE_ADJ_TRI(mATri[0])==tref) { if(index) *index = 0; return true; }
|
||||
if(!IS_BOUNDARY(mATri[1]) && MAKE_ADJ_TRI(mATri[1])==tref) { if(index) *index = 1; return true; }
|
||||
if(!IS_BOUNDARY(mATri[2]) && MAKE_ADJ_TRI(mATri[2])==tref) { if(index) *index = 2; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
Adjacencies::Adjacencies() : mNbFaces(0), mFaces(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Adjacencies::~Adjacencies()
|
||||
{
|
||||
PX_DELETE_ARRAY(mFaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the number of boundary edges.
|
||||
* \return the number of boundary edges.
|
||||
*/
|
||||
PxU32 Adjacencies::ComputeNbBoundaryEdges() const
|
||||
{
|
||||
if(!mFaces)
|
||||
return 0;
|
||||
|
||||
// Look for boundary edges
|
||||
PxU32 Nb = 0;
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
AdjTriangle* CurTri = &mFaces[i];
|
||||
Nb+=CurTri->ComputeNbBoundaryEdges();
|
||||
}
|
||||
return Nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the boundary vertices. A boundary vertex is defined as a vertex shared by at least one boundary edge.
|
||||
* \param nb_verts [in] the number of vertices
|
||||
* \param bound_status [out] a user-provided array of bool
|
||||
* \return true if success. The user-array is filled with true or false (boundary vertex / not boundary vertex)
|
||||
*/
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
bool Adjacencies::GetBoundaryVertices(PxU32 nb_verts, bool* bound_status) const
|
||||
#else
|
||||
bool Adjacencies::GetBoundaryVertices(PxU32 nb_verts, bool* bound_status, const IndexedTriangle32* faces) const
|
||||
#endif
|
||||
{
|
||||
// We need the adjacencies
|
||||
if(!mFaces || !bound_status || !nb_verts)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "Adjacencies::GetBoundaryVertices: NULL parameter!");
|
||||
|
||||
#ifndef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
if(!faces)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "Adjacencies::GetBoundaryVertices: NULL parameter!");
|
||||
#endif
|
||||
|
||||
// Init
|
||||
PxMemZero(bound_status, nb_verts*sizeof(bool));
|
||||
|
||||
// Loop through faces
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
AdjTriangle* CurTri = &mFaces[i];
|
||||
if(IS_BOUNDARY(CurTri->mATri[0]))
|
||||
{
|
||||
// Two boundary vertices: 0 - 1
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
PxU32 VRef0 = CurTri->v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true;
|
||||
PxU32 VRef1 = CurTri->v[1]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true;
|
||||
#else
|
||||
PxU32 VRef0 = faces[i].mRef[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true;
|
||||
PxU32 VRef1 = faces[i].mRef[1]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true;
|
||||
#endif
|
||||
}
|
||||
if(IS_BOUNDARY(CurTri->mATri[1]))
|
||||
{
|
||||
// Two boundary vertices: 0 - 2
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
PxU32 VRef0 = CurTri->v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true;
|
||||
PxU32 VRef1 = CurTri->v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true;
|
||||
#else
|
||||
PxU32 VRef0 = faces[i].mRef[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true;
|
||||
PxU32 VRef1 = faces[i].mRef[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true;
|
||||
#endif
|
||||
}
|
||||
if(IS_BOUNDARY(CurTri->mATri[2]))
|
||||
{
|
||||
// Two boundary vertices: 1 - 2
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
PxU32 VRef0 = CurTri->v[1]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true;
|
||||
PxU32 VRef1 = CurTri->v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true;
|
||||
#else
|
||||
PxU32 VRef0 = faces[i].mRef[1]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true;
|
||||
PxU32 VRef1 = faces[i].mRef[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a new edge code to the counterpart link of a given link.
|
||||
* \param link [in] the link to modify - shouldn't be a boundary link
|
||||
* \param edge_nb [in] the new edge number
|
||||
*/
|
||||
void Adjacencies::AssignNewEdgeCode(PxU32 link, PxU8 edge_nb)
|
||||
{
|
||||
if(!IS_BOUNDARY(link))
|
||||
{
|
||||
PxU32 Id = MAKE_ADJ_TRI(link); // Triangle ID
|
||||
PxU32 Edge = GET_EDGE_NB(link); // Counterpart edge ID
|
||||
AdjTriangle* Tri = &mFaces[Id]; // Adjacent triangle
|
||||
|
||||
// Get link whose edge code is invalid
|
||||
PxU32 AdjLink = Tri->mATri[Edge]; // Link to ourself (i.e. to 'link')
|
||||
SET_EDGE_NB(AdjLink, edge_nb); // Assign new edge code
|
||||
Tri->mATri[Edge] = AdjLink; // Put link back
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the existing database so that reference 'vref' of triangle 'curtri' becomes the last one.
|
||||
* Provided reference must already exist in provided triangle.
|
||||
* \param cur_tri [in] the triangle
|
||||
* \param vref [in] the reference
|
||||
* \return true if success.
|
||||
*/
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
bool Adjacencies::MakeLastRef(AdjTriangle& cur_tri, PxU32 vref)
|
||||
#else
|
||||
bool Adjacencies::MakeLastRef(AdjTriangle& cur_tri, PxU32 vref, IndexedTriangle32* cur_topo)
|
||||
#endif
|
||||
{
|
||||
#ifndef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
if(!cur_topo)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "Adjacencies::MakeLastRef: NULL parameter!");
|
||||
#endif
|
||||
// We want pattern (x y vref)
|
||||
// Edge 0-1 is (x y)
|
||||
// Edge 0-2 is (x vref)
|
||||
// Edge 1-2 is (y vref)
|
||||
|
||||
// First thing is to scroll the existing references in order for vref to become the last one. Scrolling assures winding order is conserved.
|
||||
|
||||
// Edge code need fixing as well:
|
||||
// The two MSB for each link encode the counterpart edge in adjacent triangle. We swap the link positions, but adjacent triangles remain the
|
||||
// same. In other words, edge codes are still valid for current triangle since counterpart edges have not been swapped. *BUT* edge codes of
|
||||
// the three possible adjacent triangles *are* now invalid. We need to fix edge codes, but for adjacent triangles...
|
||||
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
if(cur_tri.v[0]==vref)
|
||||
#else
|
||||
if(cur_topo->mRef[0]==vref)
|
||||
#endif
|
||||
{
|
||||
// Pattern is (vref x y)
|
||||
// Edge 0-1 is (vref x)
|
||||
// Edge 0-2 is (vref y)
|
||||
// Edge 1-2 is (x y)
|
||||
|
||||
// Catch original data
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
PxU32 Ref0 = cur_tri.v[0]; PxU32 Link01 = cur_tri.mATri[0];
|
||||
PxU32 Ref1 = cur_tri.v[1]; PxU32 Link02 = cur_tri.mATri[1];
|
||||
PxU32 Ref2 = cur_tri.v[2]; PxU32 Link12 = cur_tri.mATri[2];
|
||||
|
||||
// Swap
|
||||
cur_tri.v[0] = Ref1;
|
||||
cur_tri.v[1] = Ref2;
|
||||
cur_tri.v[2] = Ref0;
|
||||
#else
|
||||
PxU32 Ref0 = cur_topo->mRef[0]; PxU32 Link01 = cur_tri.mATri[0];
|
||||
PxU32 Ref1 = cur_topo->mRef[1]; PxU32 Link02 = cur_tri.mATri[1];
|
||||
PxU32 Ref2 = cur_topo->mRef[2]; PxU32 Link12 = cur_tri.mATri[2];
|
||||
|
||||
// Swap
|
||||
cur_topo->mRef[0] = Ref1;
|
||||
cur_topo->mRef[1] = Ref2;
|
||||
cur_topo->mRef[2] = Ref0;
|
||||
#endif
|
||||
cur_tri.mATri[0] = Link12; // Edge 0-1 now encodes Ref1-Ref2, i.e. previous Link12
|
||||
cur_tri.mATri[1] = Link01; // Edge 0-2 now encodes Ref1-Ref0, i.e. previous Link01
|
||||
cur_tri.mATri[2] = Link02; // Edge 1-2 now encodes Ref2-Ref0, i.e. previous Link02
|
||||
|
||||
// Fix edge codes
|
||||
AssignNewEdgeCode(Link01, 1);
|
||||
AssignNewEdgeCode(Link02, 2);
|
||||
AssignNewEdgeCode(Link12, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
else if(cur_tri.v[1]==vref)
|
||||
#else
|
||||
else if(cur_topo->mRef[1]==vref)
|
||||
#endif
|
||||
{
|
||||
// Pattern is (x vref y)
|
||||
// Edge 0-1 is (x vref)
|
||||
// Edge 0-2 is (x y)
|
||||
// Edge 1-2 is (vref y)
|
||||
|
||||
// Catch original data
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
PxU32 Ref0 = cur_tri.v[0]; PxU32 Link01 = cur_tri.mATri[0];
|
||||
PxU32 Ref1 = cur_tri.v[1]; PxU32 Link02 = cur_tri.mATri[1];
|
||||
PxU32 Ref2 = cur_tri.v[2]; PxU32 Link12 = cur_tri.mATri[2];
|
||||
|
||||
// Swap
|
||||
cur_tri.v[0] = Ref2;
|
||||
cur_tri.v[1] = Ref0;
|
||||
cur_tri.v[2] = Ref1;
|
||||
#else
|
||||
PxU32 Ref0 = cur_topo->mRef[0]; PxU32 Link01 = cur_tri.mATri[0];
|
||||
PxU32 Ref1 = cur_topo->mRef[1]; PxU32 Link02 = cur_tri.mATri[1];
|
||||
PxU32 Ref2 = cur_topo->mRef[2]; PxU32 Link12 = cur_tri.mATri[2];
|
||||
|
||||
// Swap
|
||||
cur_topo->mRef[0] = Ref2;
|
||||
cur_topo->mRef[1] = Ref0;
|
||||
cur_topo->mRef[2] = Ref1;
|
||||
#endif
|
||||
cur_tri.mATri[0] = Link02; // Edge 0-1 now encodes Ref2-Ref0, i.e. previous Link02
|
||||
cur_tri.mATri[1] = Link12; // Edge 0-2 now encodes Ref2-Ref1, i.e. previous Link12
|
||||
cur_tri.mATri[2] = Link01; // Edge 1-2 now encodes Ref0-Ref1, i.e. previous Link01
|
||||
|
||||
// Fix edge codes
|
||||
AssignNewEdgeCode(Link01, 2);
|
||||
AssignNewEdgeCode(Link02, 0);
|
||||
AssignNewEdgeCode(Link12, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
else if(cur_tri.v[2]==vref)
|
||||
#else
|
||||
else if(cur_topo->mRef[2]==vref)
|
||||
#endif
|
||||
{
|
||||
// Nothing to do, provided reference already is the last one
|
||||
return true;
|
||||
}
|
||||
|
||||
// Here the provided reference doesn't belong to the provided triangle.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Adjacencies::Load(PxInputStream& stream)
|
||||
{
|
||||
// Import header
|
||||
PxU32 Version;
|
||||
bool Mismatch;
|
||||
if(!ReadHeader('A', 'D', 'J', 'A', Version, Mismatch, stream))
|
||||
return false;
|
||||
|
||||
// Import adjacencies
|
||||
mNbFaces = readDword(Mismatch, stream);
|
||||
mFaces = PX_NEW(AdjTriangle)[mNbFaces];
|
||||
stream.read(mFaces, sizeof(AdjTriangle)*mNbFaces);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//#ifdef PX_COOKING
|
||||
|
||||
//! An edge class used to compute the adjacency structures.
|
||||
class AdjEdge : public EdgeData, public PxUserAllocated
|
||||
{
|
||||
public:
|
||||
PX_INLINE AdjEdge() {}
|
||||
PX_INLINE ~AdjEdge() {}
|
||||
|
||||
PxU32 mFaceNb; //!< Owner face
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a new edge to the database.
|
||||
* \param ref0 [in] vertex reference for the new edge
|
||||
* \param ref1 [in] vertex reference for the new edge
|
||||
* \param face [in] owner face
|
||||
*/
|
||||
static void AddEdge(PxU32 ref0, PxU32 ref1, PxU32 face, PxU32& nb_edges, AdjEdge* edges)
|
||||
{
|
||||
// Store edge data
|
||||
edges[nb_edges].Ref0 = ref0;
|
||||
edges[nb_edges].Ref1 = ref1;
|
||||
edges[nb_edges].mFaceNb = face;
|
||||
nb_edges++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new triangle to the database.
|
||||
* \param ref0 [in] vertex reference for the new triangle
|
||||
* \param ref1 [in] vertex reference for the new triangle
|
||||
* \param ref2 [in] vertex reference for the new triangle
|
||||
* \param id [in] triangle index
|
||||
*/
|
||||
static void AddTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2, PxU32 id, AdjTriangle* faces, PxU32& nb_edges, AdjEdge* edges)
|
||||
{
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
// Store vertex-references
|
||||
faces[id].v[0] = ref0;
|
||||
faces[id].v[1] = ref1;
|
||||
faces[id].v[2] = ref2;
|
||||
#endif
|
||||
// Reset links
|
||||
faces[id].mATri[0] = PX_INVALID_U32;
|
||||
faces[id].mATri[1] = PX_INVALID_U32;
|
||||
faces[id].mATri[2] = PX_INVALID_U32;
|
||||
|
||||
// Add edge 01 to database
|
||||
if(ref0<ref1) AddEdge(ref0, ref1, id, nb_edges, edges);
|
||||
else AddEdge(ref1, ref0, id, nb_edges, edges);
|
||||
// Add edge 02 to database
|
||||
if(ref0<ref2) AddEdge(ref0, ref2, id, nb_edges, edges);
|
||||
else AddEdge(ref2, ref0, id, nb_edges, edges);
|
||||
// Add edge 12 to database
|
||||
if(ref1<ref2) AddEdge(ref1, ref2, id, nb_edges, edges);
|
||||
else AddEdge(ref2, ref1, id, nb_edges, edges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the links in two adjacent triangles.
|
||||
* \param first_tri [in] index of the first triangle
|
||||
* \param second_tri [in] index of the second triangle
|
||||
* \param ref0 [in] the common edge's first vertex reference
|
||||
* \param ref1 [in] the common edge's second vertex reference
|
||||
* \return true if success.
|
||||
*/
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
static bool UpdateLink(PxU32 first_tri, PxU32 second_tri, PxU32 ref0, PxU32 ref1, AdjTriangle* faces)
|
||||
#else
|
||||
static bool UpdateLink(PxU32 first_tri, PxU32 second_tri, PxU32 ref0, PxU32 ref1, AdjTriangle* faces, const ADJACENCIESCREATE& create)
|
||||
#endif
|
||||
{
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
AdjTriangle& Tri0 = faces[first_tri]; // Catch the first triangle
|
||||
AdjTriangle& Tri1 = faces[second_tri]; // Catch the second triangle
|
||||
|
||||
// Get the edge IDs. 0xff means input references are wrong.
|
||||
PxU8 EdgeNb0 = Tri0.FindEdge(ref0, ref1); if(EdgeNb0==0xff) return SetIceError("Adjacencies::UpdateLink: invalid edge reference in first triangle");
|
||||
PxU8 EdgeNb1 = Tri1.FindEdge(ref0, ref1); if(EdgeNb1==0xff) return SetIceError("Adjacencies::UpdateLink: invalid edge reference in second triangle");
|
||||
|
||||
// Update links. The two most significant bits contain the counterpart edge's ID.
|
||||
Tri0.mATri[EdgeNb0] = second_tri |(PxU32(EdgeNb1)<<30);
|
||||
Tri1.mATri[EdgeNb1] = first_tri |(PxU32(EdgeNb0)<<30);
|
||||
#else
|
||||
IndexedTriangle32 FirstTri, SecondTri;
|
||||
if(create.DFaces)
|
||||
{
|
||||
FirstTri.mRef[0] = create.DFaces[first_tri*3+0];
|
||||
FirstTri.mRef[1] = create.DFaces[first_tri*3+1];
|
||||
FirstTri.mRef[2] = create.DFaces[first_tri*3+2];
|
||||
SecondTri.mRef[0] = create.DFaces[second_tri*3+0];
|
||||
SecondTri.mRef[1] = create.DFaces[second_tri*3+1];
|
||||
SecondTri.mRef[2] = create.DFaces[second_tri*3+2];
|
||||
}
|
||||
if(create.WFaces)
|
||||
{
|
||||
FirstTri.mRef[0] = create.WFaces[first_tri*3+0];
|
||||
FirstTri.mRef[1] = create.WFaces[first_tri*3+1];
|
||||
FirstTri.mRef[2] = create.WFaces[first_tri*3+2];
|
||||
SecondTri.mRef[0] = create.WFaces[second_tri*3+0];
|
||||
SecondTri.mRef[1] = create.WFaces[second_tri*3+1];
|
||||
SecondTri.mRef[2] = create.WFaces[second_tri*3+2];
|
||||
}
|
||||
|
||||
// Get the edge IDs. 0xff means input references are wrong.
|
||||
const PxU8 EdgeNb0 = FirstTri.findEdge(ref0, ref1);
|
||||
const PxU8 EdgeNb1 = SecondTri.findEdge(ref0, ref1);
|
||||
if(EdgeNb0==0xff || EdgeNb1==0xff)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "Adjacencies::UpdateLink: invalid edge reference");
|
||||
|
||||
// Update links. The two most significant bits contain the counterpart edge's ID.
|
||||
faces[first_tri].mATri[EdgeNb0] = second_tri |(PxU32(EdgeNb1)<<30);
|
||||
faces[second_tri].mATri[EdgeNb1] = first_tri |(PxU32(EdgeNb0)<<30);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the adjacency structures.
|
||||
* \return true if success.
|
||||
*/
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
static bool CreateDatabase(AdjTriangle* faces, PxU32 nb_edges, const AdjEdge* edges)
|
||||
#else
|
||||
static bool CreateDatabase(AdjTriangle* faces, PxU32 nb_edges, const AdjEdge* edges, const ADJACENCIESCREATE& create)
|
||||
#endif
|
||||
{
|
||||
RadixSortBuffered Core;
|
||||
{
|
||||
// Multiple sorts - this rewritten version uses less ram
|
||||
// PT: TTP 2994: the mesh has 343000+ edges, so yeah, sure, allocating more than 1mb on the stack causes overflow...
|
||||
PxU32* VRefs = PX_ALLOCATE(PxU32, nb_edges, "tmp");
|
||||
|
||||
// Sort according to mRef0, then mRef1
|
||||
PxU32 i;
|
||||
for(i=0;i<nb_edges;i++)
|
||||
VRefs[i] = edges[i].Ref0;
|
||||
Core.Sort(VRefs, nb_edges);
|
||||
for(i=0;i<nb_edges;i++)
|
||||
VRefs[i] = edges[i].Ref1;
|
||||
Core.Sort(VRefs, nb_edges);
|
||||
|
||||
PX_FREE(VRefs);
|
||||
}
|
||||
const PxU32* Sorted = Core.GetRanks();
|
||||
|
||||
// Read the list in sorted order, look for similar edges
|
||||
PxU32 LastRef0 = edges[Sorted[0]].Ref0;
|
||||
PxU32 LastRef1 = edges[Sorted[0]].Ref1;
|
||||
PxU32 Count = 0;
|
||||
PxU32 TmpBuffer[3];
|
||||
|
||||
while(nb_edges--)
|
||||
{
|
||||
PxU32 SortedIndex = *Sorted++;
|
||||
PxU32 Face = edges[SortedIndex].mFaceNb; // Owner face
|
||||
PxU32 Ref0 = edges[SortedIndex].Ref0; // Vertex ref #1
|
||||
PxU32 Ref1 = edges[SortedIndex].Ref1; // Vertex ref #2
|
||||
if(Ref0==LastRef0 && Ref1==LastRef1)
|
||||
{
|
||||
// Current edge is the same as last one
|
||||
TmpBuffer[Count++] = Face; // Store face number
|
||||
// Only works with manifold meshes (i.e. an edge is not shared by more than 2 triangles)
|
||||
if(Count==3)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "Adjacencies::CreateDatabase: can't work on non-manifold meshes.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we have a new edge (LastRef0, LastRef1) shared by Count triangles stored in TmpBuffer
|
||||
if(Count==2)
|
||||
{
|
||||
// if Count==1 => edge is a boundary edge: it belongs to a single triangle.
|
||||
// Hence there's no need to update a link to an adjacent triangle.
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
if(!UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces)) return false;
|
||||
#else
|
||||
if(!UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces, create)) return false;
|
||||
#endif
|
||||
}
|
||||
// Reset for next edge
|
||||
Count = 0;
|
||||
TmpBuffer[Count++] = Face;
|
||||
LastRef0 = Ref0;
|
||||
LastRef1 = Ref1;
|
||||
}
|
||||
}
|
||||
bool Status = true;
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
if(Count==2) Status = UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces);
|
||||
#else
|
||||
if(Count==2) Status = UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces, create);
|
||||
#endif
|
||||
return Status;
|
||||
}
|
||||
|
||||
AdjacenciesBuilder::AdjacenciesBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
AdjacenciesBuilder::~AdjacenciesBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the component.
|
||||
* \param create [in] the creation structure
|
||||
* \return true if success.
|
||||
*/
|
||||
bool AdjacenciesBuilder::Init(const ADJACENCIESCREATE& create)
|
||||
{
|
||||
if(!create.NbFaces)
|
||||
return false;
|
||||
|
||||
// Get some bytes
|
||||
mNbFaces = create.NbFaces;
|
||||
mFaces = PX_NEW(AdjTriangle)[mNbFaces];
|
||||
|
||||
AdjEdge* Edges = PX_NEW(AdjEdge)[mNbFaces*3];
|
||||
PxU32 NbEdges=0;
|
||||
|
||||
// Feed me with triangles.....
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
// Get correct vertex references
|
||||
const PxU32 Ref0 = create.DFaces ? create.DFaces[i*3+0] : create.WFaces ? create.WFaces[i*3+0] : 0;
|
||||
const PxU32 Ref1 = create.DFaces ? create.DFaces[i*3+1] : create.WFaces ? create.WFaces[i*3+1] : 1;
|
||||
const PxU32 Ref2 = create.DFaces ? create.DFaces[i*3+2] : create.WFaces ? create.WFaces[i*3+2] : 2;
|
||||
|
||||
// Add a triangle to the database
|
||||
AddTriangle(Ref0, Ref1, Ref2, i, mFaces, NbEdges, Edges);
|
||||
}
|
||||
|
||||
// At this point of the process we have mFaces & Edges filled with input data. That is:
|
||||
// - a list of triangles with 3 NULL links (i.e. PX_INVALID_U32)
|
||||
// - a list of mNbFaces*3 edges, each edge having 2 vertex references and an owner face.
|
||||
|
||||
// Here NbEdges should be equal to mNbFaces*3.
|
||||
PX_ASSERT(NbEdges==mNbFaces*3);
|
||||
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
bool Status = CreateDatabase(mFaces, NbEdges, Edges);
|
||||
#else
|
||||
bool Status = CreateDatabase(mFaces, NbEdges, Edges, create);
|
||||
#endif
|
||||
|
||||
// We don't need the edges anymore
|
||||
PX_DELETE_ARRAY(Edges);
|
||||
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS
|
||||
// Now create convex information. This creates coupling between adjacencies & edge-list but in this case it's actually the goal:
|
||||
// mixing the two structures to save memory.
|
||||
if(Status && create.Verts)
|
||||
{
|
||||
EDGELISTCREATE ELC;
|
||||
ELC.NbFaces = create.NbFaces;
|
||||
ELC.DFaces = create.DFaces; // That's where I like having a unified way to do things... We
|
||||
ELC.WFaces = create.WFaces; // can just directly copy the same pointers.
|
||||
ELC.FacesToEdges = true;
|
||||
ELC.Verts = create.Verts;
|
||||
ELC.Epsilon = create.Epsilon;
|
||||
|
||||
EdgeList EL;
|
||||
if(EL.init(ELC))
|
||||
{
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
const EdgeTriangleData& ET = EL.getEdgeTriangle(i);
|
||||
if(EdgeTriangleAC::HasActiveEdge01(ET)) mFaces[i].mATri[EDGE01] |= 0x20000000;
|
||||
else mFaces[i].mATri[EDGE01] &= ~0x20000000;
|
||||
if(EdgeTriangleAC::HasActiveEdge20(ET)) mFaces[i].mATri[EDGE02] |= 0x20000000;
|
||||
else mFaces[i].mATri[EDGE02] &= ~0x20000000;
|
||||
if(EdgeTriangleAC::HasActiveEdge12(ET)) mFaces[i].mATri[EDGE12] |= 0x20000000;
|
||||
else mFaces[i].mATri[EDGE12] &= ~0x20000000;
|
||||
|
||||
PX_ASSERT((EdgeTriangleAC::HasActiveEdge01(ET) && mFaces[i].HasActiveEdge01()) || (!EdgeTriangleAC::HasActiveEdge01(ET) && !mFaces[i].HasActiveEdge01()));
|
||||
PX_ASSERT((EdgeTriangleAC::HasActiveEdge20(ET) && mFaces[i].HasActiveEdge20()) || (!EdgeTriangleAC::HasActiveEdge20(ET) && !mFaces[i].HasActiveEdge20()));
|
||||
PX_ASSERT((EdgeTriangleAC::HasActiveEdge12(ET) && mFaces[i].HasActiveEdge12()) || (!EdgeTriangleAC::HasActiveEdge12(ET) && !mFaces[i].HasActiveEdge12()));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return Status;
|
||||
}
|
||||
/*
|
||||
bool AdjacenciesBuilder::Save(Stream& stream) const
|
||||
{
|
||||
bool PlatformMismatch = PxPlatformMismatch();
|
||||
|
||||
// Export header
|
||||
if(!WriteHeader('A', 'D', 'J', 'A', gVersion, PlatformMismatch, stream))
|
||||
return false;
|
||||
|
||||
// Export adjacencies
|
||||
// stream.StoreDword(mNbFaces);
|
||||
WriteDword(mNbFaces, PlatformMismatch, stream);
|
||||
|
||||
// stream.StoreBuffer(mFaces, sizeof(AdjTriangle)*mNbFaces);
|
||||
WriteDwordBuffer((const PxU32*)mFaces, mNbFaces*3, PlatformMismatch, stream);
|
||||
|
||||
return true;
|
||||
}*/
|
||||
//#endif
|
||||
229
engine/third_party/physx/source/geomutils/src/common/GuAdjacencies.h
vendored
Normal file
229
engine/third_party/physx/source/geomutils/src/common/GuAdjacencies.h
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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_ADJACENCIES_H
|
||||
#define GU_ADJACENCIES_H
|
||||
|
||||
#define MSH_ADJACENCIES_INCLUDE_CONVEX_BITS
|
||||
#include "GuTriangle.h"
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
#include "foundation/PxIO.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS
|
||||
#define ADJ_TRIREF_MASK 0x1fffffff //!< Masks 3 bits
|
||||
#define IS_CONVEX_EDGE(x) (x & 0x20000000) //!< Returns true for convex edges
|
||||
#else
|
||||
#define ADJ_TRIREF_MASK 0x3fffffff //!< Masks 2 bits
|
||||
#endif
|
||||
|
||||
#define MAKE_ADJ_TRI(x) (x & ADJ_TRIREF_MASK) //!< Transforms a link into a triangle reference.
|
||||
#define GET_EDGE_NB(x) (x>>30) //!< Transforms a link into a counterpart edge ID.
|
||||
// #define IS_BOUNDARY(x) (x==PX_INVALID_U32) //!< Returns true for boundary edges.
|
||||
#define IS_BOUNDARY(x) ((x & ADJ_TRIREF_MASK)==ADJ_TRIREF_MASK) //!< Returns true for boundary edges.
|
||||
|
||||
// Forward declarations
|
||||
class Adjacencies;
|
||||
|
||||
enum SharedEdgeIndex
|
||||
{
|
||||
EDGE01 = 0,
|
||||
EDGE02 = 1,
|
||||
EDGE12 = 2
|
||||
};
|
||||
|
||||
/* PX_INLINE void GetEdgeIndices(SharedEdgeIndex edge_index, PxU32& id0, PxU32& id1)
|
||||
{
|
||||
if(edge_index==0)
|
||||
{
|
||||
id0 = 0;
|
||||
id1 = 1;
|
||||
}
|
||||
else if(edge_index==1)
|
||||
{
|
||||
id0 = 0;
|
||||
id1 = 2;
|
||||
}
|
||||
else if(edge_index==2)
|
||||
{
|
||||
id0 = 1;
|
||||
id1 = 2;
|
||||
}
|
||||
}*/
|
||||
|
||||
//! Sets a new edge code
|
||||
#define SET_EDGE_NB(link, code) \
|
||||
link&=ADJ_TRIREF_MASK; \
|
||||
link|=code<<30; \
|
||||
|
||||
//! A triangle class used to compute the adjacency structures.
|
||||
class AdjTriangle
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
: public IndexedTriangle
|
||||
#else
|
||||
: public PxUserAllocated
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
PX_INLINE AdjTriangle() {}
|
||||
//! Destructor
|
||||
PX_INLINE ~AdjTriangle() {}
|
||||
|
||||
/**
|
||||
* Computes the number of boundary edges in a triangle.
|
||||
* \return the number of boundary edges. (0 => 3)
|
||||
*/
|
||||
PxU32 ComputeNbBoundaryEdges() const;
|
||||
|
||||
/**
|
||||
* Computes the number of valid neighbors.
|
||||
* \return the number of neighbors. (0 => 3)
|
||||
*/
|
||||
PxU32 ComputeNbNeighbors() const;
|
||||
|
||||
/**
|
||||
* Checks whether the triangle has a particular neighbor or not.
|
||||
* \param tref [in] the triangle reference to look for
|
||||
* \param index [out] the corresponding index in the triangle (NULL if not needed)
|
||||
* \return true if the triangle has the given neighbor
|
||||
*/
|
||||
bool HasNeighbor(PxU32 tref, PxU32* index=NULL) const;
|
||||
|
||||
/**
|
||||
* Flips the winding.
|
||||
*/
|
||||
void Flip();
|
||||
|
||||
// Data access
|
||||
PX_INLINE PxU32 GetLink(SharedEdgeIndex edge_index) const { return mATri[edge_index]; }
|
||||
PX_INLINE PxU32 GetAdjTri(SharedEdgeIndex edge_index) const { return MAKE_ADJ_TRI(mATri[edge_index]); }
|
||||
PX_INLINE PxU32 GetAdjEdge(SharedEdgeIndex edge_index) const { return GET_EDGE_NB(mATri[edge_index]); }
|
||||
PX_INLINE PxIntBool IsBoundaryEdge(SharedEdgeIndex edge_index) const { return IS_BOUNDARY(mATri[edge_index]); }
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS
|
||||
PX_INLINE PxIntBool HasActiveEdge01() const { return PxIntBool(IS_CONVEX_EDGE(mATri[EDGE01])); }
|
||||
PX_INLINE PxIntBool HasActiveEdge20() const { return PxIntBool(IS_CONVEX_EDGE(mATri[EDGE02])); }
|
||||
PX_INLINE PxIntBool HasActiveEdge12() const { return PxIntBool(IS_CONVEX_EDGE(mATri[EDGE12])); }
|
||||
PX_INLINE PxIntBool HasActiveEdge(PxU32 i) const { return PxIntBool(IS_CONVEX_EDGE(mATri[i])); }
|
||||
#endif
|
||||
// private:
|
||||
//! Links/References of adjacent triangles. The 2 most significant bits contains the counterpart edge in the adjacent triangle.
|
||||
//! mATri[0] refers to edge 0-1
|
||||
//! mATri[1] refers to edge 0-2
|
||||
//! mATri[2] refers to edge 1-2
|
||||
PxU32 mATri[3];
|
||||
};
|
||||
|
||||
//! The adjacencies creation structure.
|
||||
struct ADJACENCIESCREATE
|
||||
{
|
||||
//! Constructor
|
||||
ADJACENCIESCREATE() : NbFaces(0), DFaces(NULL), WFaces(NULL)
|
||||
{
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS
|
||||
Verts = NULL;
|
||||
Epsilon = 0.1f;
|
||||
// Epsilon = 0.001f;
|
||||
#endif
|
||||
}
|
||||
|
||||
PxU32 NbFaces; //!< Number of faces in source topo
|
||||
const PxU32* DFaces; //!< List of faces (dwords) or NULL
|
||||
const PxU16* WFaces; //!< List of faces (words) or NULL
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS
|
||||
const PxVec3* Verts;
|
||||
float Epsilon;
|
||||
#endif
|
||||
};
|
||||
|
||||
class Adjacencies : public PxUserAllocated
|
||||
{
|
||||
public:
|
||||
Adjacencies();
|
||||
~Adjacencies();
|
||||
|
||||
PxU32 mNbFaces; //!< Number of faces involved in the computation.
|
||||
AdjTriangle* mFaces; //!< A list of AdjTriangles (one/face)
|
||||
|
||||
bool Load(PxInputStream& stream);
|
||||
// Basic mesh walking
|
||||
PX_INLINE const AdjTriangle* GetAdjacentFace(const AdjTriangle& current_tri, SharedEdgeIndex edge_nb) const
|
||||
{
|
||||
// No checkings here, make sure mFaces has been created
|
||||
|
||||
// Catch the link
|
||||
PxU32 Link = current_tri.GetLink(edge_nb);
|
||||
|
||||
// Returns NULL for boundary edges
|
||||
if(IS_BOUNDARY(Link)) return NULL;
|
||||
|
||||
// Else transform into face index
|
||||
PxU32 Id = MAKE_ADJ_TRI(Link);
|
||||
|
||||
// Possible counterpart edge is:
|
||||
// PxU32 Edge = GET_EDGE_NB(Link);
|
||||
|
||||
// And returns adjacent triangle
|
||||
return &mFaces[Id];
|
||||
}
|
||||
// Helpers
|
||||
PxU32 ComputeNbBoundaryEdges() const;
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
bool GetBoundaryVertices(PxU32 nb_verts, bool* bound_status) const;
|
||||
#else
|
||||
bool GetBoundaryVertices(PxU32 nb_verts, bool* bound_status, const IndexedTriangle32* faces) const;
|
||||
#endif
|
||||
//
|
||||
#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY
|
||||
bool MakeLastRef(AdjTriangle& cur_tri, PxU32 vref);
|
||||
#else
|
||||
bool MakeLastRef(AdjTriangle& cur_tri, PxU32 vref, IndexedTriangle32* cur_topo);
|
||||
#endif
|
||||
private:
|
||||
// New edge codes assignment
|
||||
void AssignNewEdgeCode(PxU32 link, PxU8 edge_nb);
|
||||
};
|
||||
|
||||
//#ifdef PX_COOKING
|
||||
class AdjacenciesBuilder : public Adjacencies
|
||||
{
|
||||
public:
|
||||
AdjacenciesBuilder();
|
||||
~AdjacenciesBuilder();
|
||||
|
||||
bool Init(const ADJACENCIESCREATE& create);
|
||||
// bool Save(Stream& stream) const;
|
||||
};
|
||||
//#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
84
engine/third_party/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp
vendored
Normal file
84
engine/third_party/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 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);
|
||||
const FloatV zero = FZero();
|
||||
const FloatV denom = FSel(FIsGrtr(denominator, zero), FRecip(denominator), zero);
|
||||
v = FMul(numerator, denom);
|
||||
}
|
||||
|
||||
void Gu::barycentricCoordinates(const aos::Vec3VArg p, const aos::Vec3VArg a, const aos::Vec3VArg b, const aos::Vec3VArg c, aos::FloatV& v, 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);
|
||||
}
|
||||
|
||||
91
engine/third_party/physx/source/geomutils/src/common/GuBarycentricCoordinates.h
vendored
Normal file
91
engine/third_party/physx/source/geomutils/src/common/GuBarycentricCoordinates.h
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "common/PxPhysXCommonConfig.h"
|
||||
#include "foundation/PxVecMath.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
//calculate the barycentric coorinates for a point in a segment
|
||||
void barycentricCoordinates(const aos::Vec3VArg p,
|
||||
const aos::Vec3VArg a,
|
||||
const aos::Vec3VArg b,
|
||||
aos::FloatV& v);
|
||||
|
||||
//calculate the barycentric coorinates for a point in a triangle
|
||||
void barycentricCoordinates(const aos::Vec3VArg p,
|
||||
const aos::Vec3VArg a,
|
||||
const aos::Vec3VArg b,
|
||||
const aos::Vec3VArg c,
|
||||
aos::FloatV& v,
|
||||
aos::FloatV& w);
|
||||
|
||||
void barycentricCoordinates(const aos::Vec3VArg v0,
|
||||
const aos::Vec3VArg v1,
|
||||
const aos::Vec3VArg v2,
|
||||
aos::FloatV& v,
|
||||
aos::FloatV& w);
|
||||
|
||||
PX_INLINE aos::BoolV isValidTriangleBarycentricCoord(const aos::FloatVArg v, const aos::FloatVArg w)
|
||||
{
|
||||
using namespace 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 aos::BoolV isValidTriangleBarycentricCoord2(const aos::Vec4VArg vwvw)
|
||||
{
|
||||
using namespace 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
|
||||
120
engine/third_party/physx/source/geomutils/src/common/GuBoxConversion.h
vendored
Normal file
120
engine/third_party/physx/source/geomutils/src/common/GuBoxConversion.h
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "foundation/PxMathUtils.h"
|
||||
#include "foundation/PxMat34.h"
|
||||
#include "foundation/PxVecMath.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 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 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(PxMat34& 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 PxMat34& 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 = PxOptimizeBoundingBox(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 PxMat34& 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
|
||||
84
engine/third_party/physx/source/geomutils/src/common/GuEdgeCache.h
vendored
Normal file
84
engine/third_party/physx/source/geomutils/src/common/GuEdgeCache.h
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "foundation/PxAllocator.h"
|
||||
#include "foundation/PxHash.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) & PxComputeHash(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
|
||||
|
||||
719
engine/third_party/physx/source/geomutils/src/common/GuEdgeList.cpp
vendored
Normal file
719
engine/third_party/physx/source/geomutils/src/common/GuEdgeList.cpp
vendored
Normal file
@@ -0,0 +1,719 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "geometry/PxTriangle.h"
|
||||
#include "GuEdgeList.h"
|
||||
#include "foundation/PxMathUtils.h"
|
||||
#include "foundation/PxPlane.h"
|
||||
#include "CmRadixSort.h"
|
||||
#include "CmSerialize.h"
|
||||
|
||||
// PT: code archeology: this initially came from ICE (IceEdgeList.h/cpp). Consider putting it back the way it was initially.
|
||||
// It makes little sense that something like EdgeList is in GeomUtils but some equivalent class like Adjacencies in is Cooking.
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PX_IMPLEMENT_OUTPUT_ERROR
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EdgeList::EdgeList() :
|
||||
mNbEdges (0),
|
||||
mEdges (NULL),
|
||||
mNbFaces (0),
|
||||
mEdgeFaces (NULL),
|
||||
mEdgeToTriangles (NULL),
|
||||
mFacesByEdges (NULL)
|
||||
{
|
||||
}
|
||||
|
||||
EdgeList::~EdgeList()
|
||||
{
|
||||
PX_FREE(mFacesByEdges);
|
||||
PX_FREE(mEdgeToTriangles);
|
||||
PX_FREE(mEdges);
|
||||
PX_FREE(mEdgeFaces);
|
||||
}
|
||||
|
||||
bool EdgeList::load(PxInputStream& stream)
|
||||
{
|
||||
// Import header
|
||||
PxU32 Version;
|
||||
bool Mismatch;
|
||||
if(!ReadHeader('E', 'D', 'G', 'E', Version, Mismatch, stream))
|
||||
return false;
|
||||
|
||||
// Import edges
|
||||
mNbEdges = readDword(Mismatch, stream);
|
||||
mEdges = PX_ALLOCATE(EdgeData, mNbEdges, "EdgeData");
|
||||
stream.read(mEdges, sizeof(EdgeData)*mNbEdges);
|
||||
|
||||
mNbFaces = readDword(Mismatch, stream);
|
||||
mEdgeFaces = PX_ALLOCATE(EdgeTriangleData, mNbFaces, "EdgeTriangleData");
|
||||
stream.read(mEdgeFaces, sizeof(EdgeTriangleData)*mNbFaces);
|
||||
|
||||
mEdgeToTriangles = PX_ALLOCATE(EdgeDescData, mNbEdges, "EdgeDescData");
|
||||
stream.read(mEdgeToTriangles, sizeof(EdgeDescData)*mNbEdges);
|
||||
|
||||
PxU32 LastOffset = mEdgeToTriangles[mNbEdges-1].Offset + mEdgeToTriangles[mNbEdges-1].Count;
|
||||
mFacesByEdges = PX_ALLOCATE(PxU32, LastOffset, "EdgeList FacesByEdges");
|
||||
stream.read(mFacesByEdges, sizeof(PxU32)*LastOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the edge-list.
|
||||
* \param create [in] edge-list creation structure
|
||||
* \return true if success.
|
||||
*/
|
||||
bool EdgeList::init(const EDGELISTCREATE& create)
|
||||
{
|
||||
const bool FacesToEdges = create.Verts ? true : create.FacesToEdges;
|
||||
const bool EdgesToFaces = create.Verts ? true : create.EdgesToFaces;
|
||||
|
||||
// "FacesToEdges" maps each face to three edges.
|
||||
if(FacesToEdges && !createFacesToEdges(create.NbFaces, create.DFaces, create.WFaces))
|
||||
return false;
|
||||
|
||||
// "EdgesToFaces" maps each edge to the set of faces sharing this edge
|
||||
if(EdgesToFaces && !createEdgesToFaces(create.NbFaces, create.DFaces, create.WFaces))
|
||||
return false;
|
||||
|
||||
// Create active edges
|
||||
if(create.Verts && !computeActiveEdges(create.NbFaces, create.DFaces, create.WFaces, create.Verts, create.Epsilon))
|
||||
return false;
|
||||
|
||||
// Get rid of useless data
|
||||
if(!create.FacesToEdges)
|
||||
PX_FREE(mEdgeFaces);
|
||||
|
||||
if(!create.EdgesToFaces)
|
||||
{
|
||||
PX_FREE(mEdgeToTriangles);
|
||||
PX_FREE(mFacesByEdges);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes FacesToEdges.
|
||||
* After the call:
|
||||
* - mNbEdges is updated with the number of non-redundant edges
|
||||
* - mEdges is a list of mNbEdges edges (one edge is 2 vertex-references)
|
||||
* - mEdgesRef is a list of nbfaces structures with 3 indexes in mEdges for each face
|
||||
*
|
||||
* \param nb_faces [in] a number of triangles
|
||||
* \param dfaces [in] list of triangles with PxU32 vertex references (or NULL)
|
||||
* \param wfaces [in] list of triangles with PxU16 vertex references (or NULL)
|
||||
* \return true if success.
|
||||
*/
|
||||
bool EdgeList::createFacesToEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces)
|
||||
{
|
||||
if(!nb_faces || (!dfaces && !wfaces))
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "EdgeList::CreateFacesToEdges: NULL parameter!");
|
||||
|
||||
if(mEdgeFaces)
|
||||
return true; // Already computed!
|
||||
|
||||
// 1) Get some bytes: I need one EdgesRefs for each face, and some temp buffers
|
||||
mEdgeFaces = PX_ALLOCATE(EdgeTriangleData, nb_faces, "mEdgeFaces"); // Link faces to edges
|
||||
PxU32* VRefs0 = PX_ALLOCATE(PxU32, nb_faces*3, "Tmp"); // Temp storage
|
||||
PxU32* VRefs1 = PX_ALLOCATE(PxU32, nb_faces*3, "Tmp"); // Temp storage
|
||||
EdgeData* Buffer = PX_ALLOCATE(EdgeData, nb_faces*3, "Tmp"); // Temp storage
|
||||
|
||||
// 2) Create a full redundant list of 3 edges / face.
|
||||
for(PxU32 i=0;i<nb_faces;i++)
|
||||
{
|
||||
// Get right vertex-references
|
||||
const PxU32 Ref0 = dfaces ? dfaces[i*3+0] : wfaces ? wfaces[i*3+0] : 0;
|
||||
const PxU32 Ref1 = dfaces ? dfaces[i*3+1] : wfaces ? wfaces[i*3+1] : 1;
|
||||
const PxU32 Ref2 = dfaces ? dfaces[i*3+2] : wfaces ? wfaces[i*3+2] : 2;
|
||||
|
||||
// Pre-Sort vertex-references and put them in the lists
|
||||
if(Ref0<Ref1) { VRefs0[i*3+0] = Ref0; VRefs1[i*3+0] = Ref1; } // Edge 0-1 maps (i%3)
|
||||
else { VRefs0[i*3+0] = Ref1; VRefs1[i*3+0] = Ref0; } // Edge 0-1 maps (i%3)
|
||||
|
||||
if(Ref1<Ref2) { VRefs0[i*3+1] = Ref1; VRefs1[i*3+1] = Ref2; } // Edge 1-2 maps (i%3)+1
|
||||
else { VRefs0[i*3+1] = Ref2; VRefs1[i*3+1] = Ref1; } // Edge 1-2 maps (i%3)+1
|
||||
|
||||
if(Ref2<Ref0) { VRefs0[i*3+2] = Ref2; VRefs1[i*3+2] = Ref0; } // Edge 2-0 maps (i%3)+2
|
||||
else { VRefs0[i*3+2] = Ref0; VRefs1[i*3+2] = Ref2; } // Edge 2-0 maps (i%3)+2
|
||||
}
|
||||
|
||||
// 3) Sort the list according to both keys (VRefs0 and VRefs1)
|
||||
Cm::RadixSortBuffered Sorter;
|
||||
const PxU32* Sorted = Sorter.Sort(VRefs1, nb_faces*3).Sort(VRefs0, nb_faces*3).GetRanks();
|
||||
|
||||
// 4) Loop through all possible edges
|
||||
// - clean edges list by removing redundant edges
|
||||
// - create EdgesRef list
|
||||
mNbEdges = 0; // #non-redundant edges
|
||||
mNbFaces = nb_faces;
|
||||
PxU32 PreviousRef0 = PX_INVALID_U32;
|
||||
PxU32 PreviousRef1 = PX_INVALID_U32;
|
||||
for(PxU32 i=0;i<nb_faces*3;i++)
|
||||
{
|
||||
PxU32 Face = Sorted[i]; // Between 0 and nbfaces*3
|
||||
PxU32 ID = Face % 3; // Get edge ID back.
|
||||
PxU32 SortedRef0 = VRefs0[Face]; // (SortedRef0, SortedRef1) is the sorted edge
|
||||
PxU32 SortedRef1 = VRefs1[Face];
|
||||
|
||||
if(SortedRef0!=PreviousRef0 || SortedRef1!=PreviousRef1)
|
||||
{
|
||||
// New edge found! => stored in temp buffer
|
||||
Buffer[mNbEdges].Ref0 = SortedRef0;
|
||||
Buffer[mNbEdges].Ref1 = SortedRef1;
|
||||
mNbEdges++;
|
||||
}
|
||||
PreviousRef0 = SortedRef0;
|
||||
PreviousRef1 = SortedRef1;
|
||||
|
||||
// Create mEdgesRef on the fly
|
||||
mEdgeFaces[Face/3].mLink[ID] = mNbEdges-1;
|
||||
}
|
||||
|
||||
// 5) Here, mNbEdges==#non redundant edges
|
||||
mEdges = PX_ALLOCATE(EdgeData, mNbEdges, "EdgeData");
|
||||
|
||||
// Create real edges-list.
|
||||
PxMemCopy(mEdges, Buffer, mNbEdges*sizeof(EdgeData));
|
||||
|
||||
// 6) Free ram and exit
|
||||
PX_FREE(Buffer);
|
||||
PX_FREE(VRefs1);
|
||||
PX_FREE(VRefs0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes EdgesToFaces.
|
||||
* After the call:
|
||||
* - mEdgeToTriangles is created
|
||||
* - mFacesByEdges is created
|
||||
*
|
||||
* \param nb_faces [in] a number of triangles
|
||||
* \param dfaces [in] list of triangles with PxU32 vertex references (or NULL)
|
||||
* \param wfaces [in] list of triangles with PxU16 vertex references (or NULL)
|
||||
* \return true if success.
|
||||
*/
|
||||
bool EdgeList::createEdgesToFaces(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces)
|
||||
{
|
||||
// 1) I need FacesToEdges !
|
||||
if(!createFacesToEdges(nb_faces, dfaces, wfaces))
|
||||
return false;
|
||||
|
||||
// 2) Get some bytes: one Pair structure / edge
|
||||
mEdgeToTriangles = PX_ALLOCATE(EdgeDescData, mNbEdges, "EdgeDescData");
|
||||
PxMemZero(mEdgeToTriangles, sizeof(EdgeDescData)*mNbEdges);
|
||||
|
||||
// 3) Create Counters, ie compute the #faces sharing each edge
|
||||
for(PxU32 i=0;i<nb_faces;i++)
|
||||
{
|
||||
mEdgeToTriangles[mEdgeFaces[i].mLink[0]].Count++;
|
||||
mEdgeToTriangles[mEdgeFaces[i].mLink[1]].Count++;
|
||||
mEdgeToTriangles[mEdgeFaces[i].mLink[2]].Count++;
|
||||
}
|
||||
|
||||
// 3) Create Radix-like Offsets
|
||||
mEdgeToTriangles[0].Offset=0;
|
||||
for(PxU32 i=1;i<mNbEdges;i++)
|
||||
mEdgeToTriangles[i].Offset = mEdgeToTriangles[i-1].Offset + mEdgeToTriangles[i-1].Count;
|
||||
|
||||
const PxU32 LastOffset = mEdgeToTriangles[mNbEdges-1].Offset + mEdgeToTriangles[mNbEdges-1].Count;
|
||||
|
||||
// 4) Get some bytes for mFacesByEdges. LastOffset is the number of indices needed.
|
||||
mFacesByEdges = PX_ALLOCATE(PxU32, LastOffset, "EdgeList FacesByEdges");
|
||||
|
||||
// 5) Create mFacesByEdges
|
||||
for(PxU32 i=0;i<nb_faces;i++)
|
||||
{
|
||||
mFacesByEdges[mEdgeToTriangles[mEdgeFaces[i].mLink[0]].Offset++] = i;
|
||||
mFacesByEdges[mEdgeToTriangles[mEdgeFaces[i].mLink[1]].Offset++] = i;
|
||||
mFacesByEdges[mEdgeToTriangles[mEdgeFaces[i].mLink[2]].Offset++] = i;
|
||||
}
|
||||
|
||||
// 6) Recompute offsets wasted by 5)
|
||||
mEdgeToTriangles[0].Offset=0;
|
||||
for(PxU32 i=1;i<mNbEdges;i++)
|
||||
mEdgeToTriangles[i].Offset = mEdgeToTriangles[i-1].Offset + mEdgeToTriangles[i-1].Count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static PX_INLINE PxU32 OppositeVertex(PxU32 r0, PxU32 r1, PxU32 r2, PxU32 vref0, PxU32 vref1)
|
||||
{
|
||||
if(vref0==r0)
|
||||
{
|
||||
if (vref1==r1) return r2;
|
||||
else if(vref1==r2) return r1;
|
||||
}
|
||||
else if(vref0==r1)
|
||||
{
|
||||
if (vref1==r0) return r2;
|
||||
else if(vref1==r2) return r0;
|
||||
}
|
||||
else if(vref0==r2)
|
||||
{
|
||||
if (vref1==r1) return r0;
|
||||
else if(vref1==r0) return r1;
|
||||
}
|
||||
return PX_INVALID_U32;
|
||||
}
|
||||
|
||||
bool EdgeList::computeActiveEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces, const PxVec3* verts, float epsilon)
|
||||
{
|
||||
if(!verts || (!dfaces && !wfaces))
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "EdgeList::ComputeActiveEdges: NULL parameter!");
|
||||
|
||||
PxU32 NbEdges = getNbEdges();
|
||||
if(!NbEdges)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "ActiveEdges::ComputeConvexEdges: no edges in edge list!");
|
||||
|
||||
const EdgeData* Edges = getEdges();
|
||||
if(!Edges)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "ActiveEdges::ComputeConvexEdges: no edge data in edge list!");
|
||||
|
||||
const EdgeDescData* ED = getEdgeToTriangles();
|
||||
if(!ED)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "ActiveEdges::ComputeConvexEdges: no edge-to-triangle in edge list!");
|
||||
|
||||
const PxU32* FBE = getFacesByEdges();
|
||||
if(!FBE)
|
||||
return outputError<PxErrorCode::eINVALID_OPERATION>(__LINE__, "ActiveEdges::ComputeConvexEdges: no faces-by-edges in edge list!");
|
||||
|
||||
// We first create active edges in a temporaray buffer. We have one bool / edge.
|
||||
bool* ActiveEdges = PX_ALLOCATE(bool, NbEdges, "bool");
|
||||
|
||||
// Loop through edges and look for convex ones
|
||||
bool* CurrentMark = ActiveEdges;
|
||||
|
||||
while(NbEdges--)
|
||||
{
|
||||
// Get number of triangles sharing current edge
|
||||
const PxU32 Count = ED->Count;
|
||||
// Boundary edges are active => keep them (actually they're silhouette edges directly)
|
||||
// Internal edges can be active => test them
|
||||
// Singular edges ? => discard them
|
||||
bool Active = false;
|
||||
if(Count==1)
|
||||
{
|
||||
Active = true;
|
||||
}
|
||||
else if(Count==2)
|
||||
{
|
||||
const PxU32 FaceIndex0 = FBE[ED->Offset+0]*3;
|
||||
const PxU32 FaceIndex1 = FBE[ED->Offset+1]*3;
|
||||
|
||||
PxU32 VRef00, VRef01, VRef02;
|
||||
PxU32 VRef10, VRef11, VRef12;
|
||||
|
||||
if(dfaces)
|
||||
{
|
||||
VRef00 = dfaces[FaceIndex0+0];
|
||||
VRef01 = dfaces[FaceIndex0+1];
|
||||
VRef02 = dfaces[FaceIndex0+2];
|
||||
VRef10 = dfaces[FaceIndex1+0];
|
||||
VRef11 = dfaces[FaceIndex1+1];
|
||||
VRef12 = dfaces[FaceIndex1+2];
|
||||
}
|
||||
else //if(wfaces)
|
||||
{
|
||||
PX_ASSERT(wfaces);
|
||||
VRef00 = wfaces[FaceIndex0+0];
|
||||
VRef01 = wfaces[FaceIndex0+1];
|
||||
VRef02 = wfaces[FaceIndex0+2];
|
||||
VRef10 = wfaces[FaceIndex1+0];
|
||||
VRef11 = wfaces[FaceIndex1+1];
|
||||
VRef12 = wfaces[FaceIndex1+2];
|
||||
}
|
||||
|
||||
{
|
||||
// We first check the opposite vertex against the plane
|
||||
|
||||
const PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1);
|
||||
|
||||
const PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
|
||||
if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges
|
||||
{
|
||||
const PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]);
|
||||
const PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
|
||||
PxVec3 N0, N1;
|
||||
T0.normal(N0);
|
||||
T1.normal(N1);
|
||||
const float a = PxComputeAngle(N0, N1);
|
||||
|
||||
if(fabsf(a)>epsilon)
|
||||
Active = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]);
|
||||
const PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
PxVec3 N0, N1;
|
||||
T0.normal(N0);
|
||||
T1.normal(N1);
|
||||
|
||||
if(N0.dot(N1) < -0.999f)
|
||||
Active = true;
|
||||
}
|
||||
//Active = true;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Connected to more than 2
|
||||
//We need to loop through the triangles and count the number of unique triangles (considering back-face triangles as non-unique). If we end up with more than 2 unique triangles,
|
||||
//then by definition this is an inactive edge. However, if we end up with 2 unique triangles (say like a double-sided tesselated surface), then it depends on the same rules as above
|
||||
|
||||
const PxU32 FaceInd0 = FBE[ED->Offset]*3;
|
||||
PxU32 VRef00, VRef01, VRef02;
|
||||
PxU32 VRef10=0, VRef11=0, VRef12=0;
|
||||
if(dfaces)
|
||||
{
|
||||
VRef00 = dfaces[FaceInd0+0];
|
||||
VRef01 = dfaces[FaceInd0+1];
|
||||
VRef02 = dfaces[FaceInd0+2];
|
||||
}
|
||||
else //if(wfaces)
|
||||
{
|
||||
PX_ASSERT(wfaces);
|
||||
VRef00 = wfaces[FaceInd0+0];
|
||||
VRef01 = wfaces[FaceInd0+1];
|
||||
VRef02 = wfaces[FaceInd0+2];
|
||||
}
|
||||
|
||||
PxU32 numUniqueTriangles = 1;
|
||||
bool doubleSided0 = false;
|
||||
bool doubleSided1 = 0;
|
||||
|
||||
for(PxU32 a = 1; a < Count; ++a)
|
||||
{
|
||||
const PxU32 FaceInd = FBE[ED->Offset+a]*3;
|
||||
|
||||
PxU32 VRef0, VRef1, VRef2;
|
||||
if(dfaces)
|
||||
{
|
||||
VRef0 = dfaces[FaceInd+0];
|
||||
VRef1 = dfaces[FaceInd+1];
|
||||
VRef2 = dfaces[FaceInd+2];
|
||||
}
|
||||
else //if(wfaces)
|
||||
{
|
||||
PX_ASSERT(wfaces);
|
||||
VRef0 = wfaces[FaceInd+0];
|
||||
VRef1 = wfaces[FaceInd+1];
|
||||
VRef2 = wfaces[FaceInd+2];
|
||||
}
|
||||
|
||||
if(((VRef0 != VRef00) && (VRef0 != VRef01) && (VRef0 != VRef02)) ||
|
||||
((VRef1 != VRef00) && (VRef1 != VRef01) && (VRef1 != VRef02)) ||
|
||||
((VRef2 != VRef00) && (VRef2 != VRef01) && (VRef2 != VRef02)))
|
||||
{
|
||||
//Not the same as trig 0
|
||||
if(numUniqueTriangles == 2)
|
||||
{
|
||||
if(((VRef0 != VRef10) && (VRef0 != VRef11) && (VRef0 != VRef12)) ||
|
||||
((VRef1 != VRef10) && (VRef1 != VRef11) && (VRef1 != VRef12)) ||
|
||||
((VRef2 != VRef10) && (VRef2 != VRef11) && (VRef2 != VRef12)))
|
||||
{
|
||||
//Too many unique triangles - terminate and mark as inactive
|
||||
numUniqueTriangles++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const PxTriangle T0(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
const PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]);
|
||||
PxVec3 N0, N1;
|
||||
T0.normal(N0);
|
||||
T1.normal(N1);
|
||||
|
||||
if(N0.dot(N1) < -0.999f)
|
||||
doubleSided1 = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VRef10 = VRef0;
|
||||
VRef11 = VRef1;
|
||||
VRef12 = VRef2;
|
||||
numUniqueTriangles++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Check for double sided...
|
||||
const PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]);
|
||||
const PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]);
|
||||
PxVec3 N0, N1;
|
||||
T0.normal(N0);
|
||||
T1.normal(N1);
|
||||
|
||||
if(N0.dot(N1) < -0.999f)
|
||||
doubleSided0 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(numUniqueTriangles == 1)
|
||||
Active = true;
|
||||
if(numUniqueTriangles == 2)
|
||||
{
|
||||
//Potentially active. Let's check the angles between the surfaces...
|
||||
|
||||
if(doubleSided0 || doubleSided1)
|
||||
{
|
||||
|
||||
// Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts);
|
||||
const PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
|
||||
// if(PL1.Distance(verts[Op])<-epsilon) Active = true;
|
||||
//if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges
|
||||
//KS - can't test signed distance for concave edges. This is a double-sided poly
|
||||
{
|
||||
const PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]);
|
||||
const PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
|
||||
PxVec3 N0, N1;
|
||||
T0.normal(N0);
|
||||
T1.normal(N1);
|
||||
const float a = PxComputeAngle(N0, N1);
|
||||
|
||||
if(fabsf(a)>epsilon)
|
||||
Active = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//Not double sided...must have had a bunch of duplicate triangles!!!!
|
||||
//Treat as normal
|
||||
const PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1);
|
||||
|
||||
// Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts);
|
||||
const PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
|
||||
// if(PL1.Distance(verts[Op])<-epsilon) Active = true;
|
||||
if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges
|
||||
{
|
||||
const PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]);
|
||||
const PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]);
|
||||
|
||||
PxVec3 N0, N1;
|
||||
T0.normal(N0);
|
||||
T1.normal(N1);
|
||||
const float a = PxComputeAngle(N0, N1);
|
||||
|
||||
if(fabsf(a)>epsilon)
|
||||
Active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Lots of triangles all smooshed together. Just activate the edge in this case
|
||||
Active = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*CurrentMark++ = Active;
|
||||
ED++;
|
||||
Edges++;
|
||||
}
|
||||
|
||||
// Now copy bits back into already existing edge structures
|
||||
// - first in edge triangles
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
EdgeTriangleData& ET = mEdgeFaces[i];
|
||||
for(PxU32 j=0;j<3;j++)
|
||||
{
|
||||
const PxU32 Link = ET.mLink[j];
|
||||
if(!(Link & MSH_ACTIVE_EDGE_MASK)) // else already active
|
||||
{
|
||||
if(ActiveEdges[Link & MSH_EDGE_LINK_MASK])
|
||||
ET.mLink[j] |= MSH_ACTIVE_EDGE_MASK; // Mark as active
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - then in edge-to-faces
|
||||
for(PxU32 i=0;i<mNbEdges;i++)
|
||||
{
|
||||
if(ActiveEdges[i])
|
||||
mEdgeToTriangles[i].Flags |= PX_EDGE_ACTIVE;
|
||||
}
|
||||
|
||||
// Free & exit
|
||||
PX_FREE(ActiveEdges);
|
||||
|
||||
if(0) // PT: this is not needed anymore
|
||||
{
|
||||
//initially all vertices are flagged to ignore them. (we assume them to be flat)
|
||||
//for all NONFLAT edges, incl boundary
|
||||
//unflag 2 vertices in up to 2 trigs as perhaps interesting
|
||||
//for all CONCAVE edges
|
||||
//flag 2 vertices in up to 2 trigs to ignore them.
|
||||
|
||||
// Handle active vertices
|
||||
PxU32 MaxIndex = 0;
|
||||
for(PxU32 i=0;i<nb_faces;i++)
|
||||
{
|
||||
PxU32 VRef0, VRef1, VRef2;
|
||||
if(dfaces)
|
||||
{
|
||||
VRef0 = dfaces[i*3+0];
|
||||
VRef1 = dfaces[i*3+1];
|
||||
VRef2 = dfaces[i*3+2];
|
||||
}
|
||||
else //if(wfaces)
|
||||
{
|
||||
PX_ASSERT(wfaces);
|
||||
VRef0 = wfaces[i*3+0];
|
||||
VRef1 = wfaces[i*3+1];
|
||||
VRef2 = wfaces[i*3+2];
|
||||
}
|
||||
if(VRef0>MaxIndex) MaxIndex = VRef0;
|
||||
if(VRef1>MaxIndex) MaxIndex = VRef1;
|
||||
if(VRef2>MaxIndex) MaxIndex = VRef2;
|
||||
}
|
||||
|
||||
MaxIndex++;
|
||||
bool* ActiveVerts = PX_ALLOCATE(bool, MaxIndex, "bool");
|
||||
PxMemZero(ActiveVerts, MaxIndex*sizeof(bool));
|
||||
|
||||
PX_ASSERT(dfaces || wfaces);
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
PxU32 VRef[3];
|
||||
if(dfaces)
|
||||
{
|
||||
VRef[0] = dfaces[i*3+0];
|
||||
VRef[1] = dfaces[i*3+1];
|
||||
VRef[2] = dfaces[i*3+2];
|
||||
}
|
||||
else if(wfaces)
|
||||
{
|
||||
VRef[0] = wfaces[i*3+0];
|
||||
VRef[1] = wfaces[i*3+1];
|
||||
VRef[2] = wfaces[i*3+2];
|
||||
}
|
||||
|
||||
const EdgeTriangleData& ET = mEdgeFaces[i];
|
||||
for(PxU32 j=0;j<3;j++)
|
||||
{
|
||||
PxU32 Link = ET.mLink[j];
|
||||
if(Link & MSH_ACTIVE_EDGE_MASK)
|
||||
{
|
||||
// Active edge => mark edge vertices as active
|
||||
PxU32 r0, r1;
|
||||
if(j==0) { r0=0; r1=1; }
|
||||
else if(j==1) { r0=1; r1=2; }
|
||||
else /*if(j==2)*/ { PX_ASSERT(j==2); r0=0; r1=2; }
|
||||
ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
PxU32 VRef[3];
|
||||
if(dfaces)
|
||||
{
|
||||
VRef[0] = dfaces[i*3+0];
|
||||
VRef[1] = dfaces[i*3+1];
|
||||
VRef[2] = dfaces[i*3+2];
|
||||
}
|
||||
else if(wfaces)
|
||||
{
|
||||
VRef[0] = wfaces[i*3+0];
|
||||
VRef[1] = wfaces[i*3+1];
|
||||
VRef[2] = wfaces[i*3+2];
|
||||
}
|
||||
|
||||
const EdgeTriangle& ET = mEdgeFaces[i];
|
||||
for(PxU32 j=0;j<3;j++)
|
||||
{
|
||||
PxU32 Link = ET.mLink[j];
|
||||
if(!(Link & MSH_ACTIVE_EDGE_MASK))
|
||||
{
|
||||
// Inactive edge => mark edge vertices as inactive
|
||||
PxU32 r0, r1;
|
||||
if(j==0) { r0=0; r1=1; }
|
||||
if(j==1) { r0=1; r1=2; }
|
||||
if(j==2) { r0=0; r1=2; }
|
||||
ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Now stuff this into the structure
|
||||
for(PxU32 i=0;i<mNbFaces;i++)
|
||||
{
|
||||
PxU32 VRef[3];
|
||||
if(dfaces)
|
||||
{
|
||||
VRef[0] = dfaces[i*3+0];
|
||||
VRef[1] = dfaces[i*3+1];
|
||||
VRef[2] = dfaces[i*3+2];
|
||||
}
|
||||
else if(wfaces)
|
||||
{
|
||||
VRef[0] = wfaces[i*3+0];
|
||||
VRef[1] = wfaces[i*3+1];
|
||||
VRef[2] = wfaces[i*3+2];
|
||||
}
|
||||
|
||||
EdgeTriangleData& ET = mEdgeFaces[i];
|
||||
for(PxU32 j=0;j<3;j++)
|
||||
{
|
||||
const PxU32 Link = ET.mLink[j];
|
||||
if(!(Link & MSH_ACTIVE_VERTEX_MASK)) // else already active
|
||||
{
|
||||
if(ActiveVerts[VRef[j]])
|
||||
ET.mLink[j] |= MSH_ACTIVE_VERTEX_MASK; // Mark as active
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PX_FREE(ActiveVerts);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
177
engine/third_party/physx/source/geomutils/src/common/GuEdgeList.h
vendored
Normal file
177
engine/third_party/physx/source/geomutils/src/common/GuEdgeList.h
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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_H
|
||||
#define GU_EDGE_LIST_H
|
||||
|
||||
#include "foundation/PxSimpleTypes.h"
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
#include "foundation/PxIO.h"
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "foundation/PxUserAllocated.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
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)
|
||||
};
|
||||
|
||||
//! Basic edge-data
|
||||
struct EdgeData
|
||||
{
|
||||
PxU32 Ref0; //!< First vertex reference
|
||||
PxU32 Ref1; //!< Second vertex reference
|
||||
};
|
||||
PX_COMPILE_TIME_ASSERT(sizeof(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(Edge8Data) == 2);
|
||||
|
||||
//! A count/offset pair = an edge descriptor
|
||||
struct EdgeDescData
|
||||
{
|
||||
PxU16 Flags;
|
||||
PxU16 Count;
|
||||
PxU32 Offset;
|
||||
};
|
||||
PX_COMPILE_TIME_ASSERT(sizeof(EdgeDescData) == 8);
|
||||
|
||||
//! Edge<->triangle mapping
|
||||
struct EdgeTriangleData
|
||||
{
|
||||
PxU32 mLink[3];
|
||||
};
|
||||
PX_COMPILE_TIME_ASSERT(sizeof(EdgeTriangleData) == 12);
|
||||
|
||||
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 EdgeTriangleData& data) { return data.mLink[0] & MSH_EDGE_LINK_MASK; }
|
||||
PX_INLINE static PxU32 GetEdge12(const EdgeTriangleData& data) { return data.mLink[1] & MSH_EDGE_LINK_MASK; }
|
||||
PX_INLINE static PxU32 GetEdge20(const EdgeTriangleData& data) { return data.mLink[2] & MSH_EDGE_LINK_MASK; }
|
||||
PX_INLINE static PxU32 GetEdge(const EdgeTriangleData& data, PxU32 i) { return data.mLink[i] & MSH_EDGE_LINK_MASK; }
|
||||
|
||||
PX_INLINE static PxIntBool HasActiveEdge01(const EdgeTriangleData& data) { return PxIntBool(data.mLink[0] & MSH_ACTIVE_EDGE_MASK); }
|
||||
PX_INLINE static PxIntBool HasActiveEdge12(const EdgeTriangleData& data) { return PxIntBool(data.mLink[1] & MSH_ACTIVE_EDGE_MASK); }
|
||||
PX_INLINE static PxIntBool HasActiveEdge20(const EdgeTriangleData& data) { return PxIntBool(data.mLink[2] & MSH_ACTIVE_EDGE_MASK); }
|
||||
PX_INLINE static PxIntBool HasActiveEdge(const EdgeTriangleData& data, PxU32 i) { return PxIntBool(data.mLink[i] & MSH_ACTIVE_EDGE_MASK); }
|
||||
};
|
||||
|
||||
//! The edge-list creation structure.
|
||||
struct EDGELISTCREATE
|
||||
{
|
||||
EDGELISTCREATE() :
|
||||
NbFaces (0),
|
||||
DFaces (NULL),
|
||||
WFaces (NULL),
|
||||
FacesToEdges (false),
|
||||
EdgesToFaces (false),
|
||||
Verts (NULL),
|
||||
Epsilon (0.1f)
|
||||
{}
|
||||
|
||||
PxU32 NbFaces; //!< Number of faces in source topo
|
||||
const PxU32* DFaces; //!< List of faces (dwords) or NULL
|
||||
const PxU16* WFaces; //!< List of faces (words) or NULL
|
||||
|
||||
bool FacesToEdges;
|
||||
bool EdgesToFaces;
|
||||
const PxVec3* Verts;
|
||||
float Epsilon;
|
||||
};
|
||||
|
||||
class EdgeList : public PxUserAllocated
|
||||
{
|
||||
public:
|
||||
EdgeList();
|
||||
~EdgeList();
|
||||
|
||||
bool init(const EDGELISTCREATE& create);
|
||||
|
||||
bool load(PxInputStream& stream);
|
||||
|
||||
PX_FORCE_INLINE PxU32 getNbEdges() const { return mNbEdges; }
|
||||
PX_FORCE_INLINE const EdgeData* getEdges() const { return mEdges; }
|
||||
PX_FORCE_INLINE const EdgeData& getEdge(PxU32 edge_index) const { return mEdges[edge_index]; }
|
||||
|
||||
PX_FORCE_INLINE PxU32 getNbFaces() const { return mNbFaces; }
|
||||
PX_FORCE_INLINE const EdgeTriangleData* getEdgeTriangles() const { return mEdgeFaces; }
|
||||
PX_FORCE_INLINE const EdgeTriangleData& getEdgeTriangle(PxU32 face_index) const { return mEdgeFaces[face_index]; }
|
||||
|
||||
PX_FORCE_INLINE const EdgeDescData* getEdgeToTriangles() const { return mEdgeToTriangles; }
|
||||
PX_FORCE_INLINE const EdgeDescData& getEdgeToTriangles(PxU32 edge_index) const { return mEdgeToTriangles[edge_index]; }
|
||||
PX_FORCE_INLINE const PxU32* getFacesByEdges() const { return mFacesByEdges; }
|
||||
PX_FORCE_INLINE PxU32 getFacesByEdges(PxU32 face_index) const { return mFacesByEdges[face_index]; }
|
||||
|
||||
private:
|
||||
// The edge list
|
||||
PxU32 mNbEdges; //!< Number of edges in the list
|
||||
EdgeData* mEdges; //!< List of edges
|
||||
// Faces to edges
|
||||
PxU32 mNbFaces; //!< Number of faces for which we have data
|
||||
EdgeTriangleData* mEdgeFaces; //!< Array of edge-triangles referencing mEdges
|
||||
// Edges to faces
|
||||
EdgeDescData* mEdgeToTriangles; //!< An EdgeDesc structure for each edge
|
||||
PxU32* mFacesByEdges; //!< A pool of face indices
|
||||
|
||||
bool createFacesToEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces);
|
||||
bool createEdgesToFaces(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces);
|
||||
bool computeActiveEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces, const PxVec3* verts, float epsilon);
|
||||
};
|
||||
|
||||
} // namespace Gu
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
221
engine/third_party/physx/source/geomutils/src/common/GuMeshAnalysis.cpp
vendored
Normal file
221
engine/third_party/physx/source/geomutils/src/common/GuMeshAnalysis.cpp
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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/PxArray.h"
|
||||
#include "GuMeshAnalysis.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
|
||||
PX_FORCE_INLINE PxU64 key(PxI32 a, PxI32 b)
|
||||
{
|
||||
if (a < b)
|
||||
return ((PxU64(a)) << 32) | (PxU64(b));
|
||||
else
|
||||
return ((PxU64(b)) << 32) | (PxU64(a));
|
||||
}
|
||||
|
||||
#define INITIAL_VALUE -3
|
||||
|
||||
const static PxU32 neighborEdges[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } };
|
||||
//const static PxU32 triTip[3] = { 2, 1, 0 };
|
||||
bool MeshAnalyzer::buildTriangleAdjacency(const Triangle* tris, PxU32 numTriangles, PxArray<PxI32>& result, PxHashMap<PxU64, PxI32>& edges)
|
||||
{
|
||||
PxU32 l = 4 * numTriangles; //Still factor 4 - waste one entry per triangle to get a power of 2 which allows for bit shift usage instead of modulo
|
||||
result.clear();
|
||||
result.resize(l, -1);
|
||||
|
||||
for (PxU32 i = 3; i < l; i += 4)
|
||||
result[i] = INITIAL_VALUE; //Mark the fields that get never accessed because they are just not used, this is useful for debugging
|
||||
|
||||
edges.clear();
|
||||
for (PxU32 i = 0; i < numTriangles; ++i)
|
||||
{
|
||||
const Triangle& tri = tris[i];
|
||||
if (tri[0] < 0)
|
||||
continue;
|
||||
|
||||
for (PxU32 j = 0; j < 3; ++j)
|
||||
{
|
||||
PxU64 edge = key(tri[neighborEdges[j][0]], tri[neighborEdges[j][1]]);
|
||||
if (const PxPair<const PxU64, PxI32>* ptr = edges.find(edge))
|
||||
{
|
||||
if (ptr->second < 0)
|
||||
return false; //Edge shared by more than 2 triangles
|
||||
if (result[4 * i + j] == -4 || result[ptr->second] == -4)
|
||||
{
|
||||
result[4 * i + j] = -4; //Mark as non-manifold edge
|
||||
result[ptr->second] = -4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result[4 * i + j] != -1 || result[ptr->second] != -1)
|
||||
{
|
||||
result[4 * i + j] = -4; //Mark as non-manifold edge
|
||||
result[ptr->second] = -4;
|
||||
}
|
||||
|
||||
result[4 * i + j] = ptr->second;
|
||||
result[ptr->second] = 4 * i + j;
|
||||
}
|
||||
edges.erase(ptr->first);
|
||||
edges.insert(edge, -1); //Mark as processed
|
||||
}
|
||||
else
|
||||
edges.insert(edge, 4 * i + j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PxI32 indexOf(const Triangle& tri, PxI32 node)
|
||||
{
|
||||
if (tri[0] == node) return 0;
|
||||
if (tri[1] == node) return 1;
|
||||
if (tri[2] == node) return 2;
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool MeshAnalyzer::checkConsistentTriangleOrientation(const Triangle* tris, PxU32 numTriangles)
|
||||
{
|
||||
PxArray<bool> flip;
|
||||
PxHashMap<PxU64, PxI32> edges;
|
||||
PxArray<PxArray<PxU32>> connectedTriangleGroups;
|
||||
if (!buildConsistentTriangleOrientationMap(tris, numTriangles, flip, edges, connectedTriangleGroups))
|
||||
return false;
|
||||
|
||||
for (PxU32 i = 0; i < flip.size(); ++i)
|
||||
{
|
||||
if (flip[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MeshAnalyzer::buildConsistentTriangleOrientationMap(const Triangle* tris, PxU32 numTriangles, PxArray<bool>& flip,
|
||||
PxHashMap<PxU64, PxI32>& edges, PxArray<PxArray<PxU32>>& connectedTriangleGroups)
|
||||
{
|
||||
PxArray<PxI32> adj;
|
||||
if (!buildTriangleAdjacency(tris, numTriangles, adj, edges))
|
||||
return false;
|
||||
|
||||
PxU32 l = numTriangles;
|
||||
PxArray<bool> done;
|
||||
done.resize(l, false);
|
||||
flip.clear();
|
||||
flip.resize(l, false);
|
||||
|
||||
PxU32 seedIndex = 0;
|
||||
PxArray<PxI32> stack;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (stack.size() == 0)
|
||||
{
|
||||
while (seedIndex < done.size() && done[seedIndex])
|
||||
++seedIndex;
|
||||
|
||||
if (seedIndex == done.size())
|
||||
break;
|
||||
|
||||
done[seedIndex] = true;
|
||||
flip[seedIndex] = false;
|
||||
stack.pushBack(seedIndex);
|
||||
PxArray<PxU32> currentGroup;
|
||||
currentGroup.pushBack(seedIndex);
|
||||
connectedTriangleGroups.pushBack(currentGroup);
|
||||
}
|
||||
|
||||
PxI32 index = stack.popBack();
|
||||
bool f = flip[index];
|
||||
const Triangle& tri = tris[index];
|
||||
|
||||
for (PxU32 i = 0; i < 3; ++i)
|
||||
{
|
||||
if (adj[4 * index + i] >= 0 && !done[adj[4 * index + i] >> 2])
|
||||
{
|
||||
PxI32 neighborTriIndex = adj[4 * index + i] >> 2;
|
||||
|
||||
done[neighborTriIndex] = true;
|
||||
connectedTriangleGroups[connectedTriangleGroups.size() - 1].pushBack(neighborTriIndex);
|
||||
|
||||
const Triangle& neighborTri = tris[neighborTriIndex];
|
||||
PxI32 j = indexOf(neighborTri, tri[neighborEdges[i][0]]);
|
||||
flip[neighborTriIndex] = (neighborTri[(j + 1) % 3] == tri[neighborEdges[i][1]]) != f;
|
||||
|
||||
stack.pushBack(neighborTriIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MeshAnalyzer::makeTriOrientationConsistent(Triangle* tris, PxU32 numTriangles, bool invertOrientation)
|
||||
{
|
||||
PxHashMap<PxU64, PxI32> edges;
|
||||
PxArray<bool> flipTriangle;
|
||||
PxArray<PxArray<PxU32>> connectedTriangleGroups;
|
||||
if (!buildConsistentTriangleOrientationMap(tris, numTriangles, flipTriangle, edges, connectedTriangleGroups))
|
||||
return false;
|
||||
|
||||
for (PxU32 i = 0; i < flipTriangle.size(); ++i)
|
||||
{
|
||||
Triangle& t = tris[i];
|
||||
if (flipTriangle[i] != invertOrientation)
|
||||
PxSwap(t[0], t[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MeshAnalyzer::checkMeshWatertightness(const Triangle* tris, PxU32 numTriangles, bool treatInconsistentWindingAsNonWatertight)
|
||||
{
|
||||
PxArray<bool> flip;
|
||||
PxHashMap<PxU64, PxI32> edges;
|
||||
PxArray<PxArray<PxU32>> connectedTriangleGroups;
|
||||
if (!MeshAnalyzer::buildConsistentTriangleOrientationMap(tris, numTriangles, flip, edges, connectedTriangleGroups))
|
||||
return false;
|
||||
|
||||
if (treatInconsistentWindingAsNonWatertight)
|
||||
{
|
||||
for (PxU32 i = 0; i < flip.size(); ++i)
|
||||
{
|
||||
if (flip[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (PxHashMap<PxU64, PxI32>::Iterator iter = edges.getIterator(); !iter.done(); ++iter)
|
||||
{
|
||||
if (iter->second >= 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
166
engine/third_party/physx/source/geomutils/src/common/GuMeshAnalysis.h
vendored
Normal file
166
engine/third_party/physx/source/geomutils/src/common/GuMeshAnalysis.h
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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_ANALYSIS_H
|
||||
#define GU_MESH_ANALYSIS_H
|
||||
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
#include "GuTriangle.h"
|
||||
#include "foundation/PxHashMap.h"
|
||||
#include "foundation/PxSort.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
using Triangle = Gu::IndexedTriangleT<PxI32>;
|
||||
|
||||
|
||||
class MeshAnalyzer
|
||||
{
|
||||
struct Range
|
||||
{
|
||||
PxI32 start;
|
||||
PxI32 end; //Exclusive
|
||||
|
||||
Range(PxI32 start_, PxI32 end_)
|
||||
{
|
||||
start = start_;
|
||||
end = end_;
|
||||
}
|
||||
|
||||
PxI32 Length() const { return end - start; }
|
||||
};
|
||||
|
||||
template<typename T, typename S>
|
||||
static void splitRanges(PxArray<Range>& mergeRanges, const PxArray<PxI32>& indexer, const T* points, PxI32 dimIndex, S tol)
|
||||
{
|
||||
PxArray<Range> newMergeRanges;
|
||||
|
||||
for (PxU32 i = 0; i < mergeRanges.size(); ++i)
|
||||
{
|
||||
const Range& r = mergeRanges[i];
|
||||
PxI32 start = r.start;
|
||||
for (PxI32 j = r.start + 1; j < r.end; ++j)
|
||||
{
|
||||
//PxF64 delta = PxAbs(points[start][dimIndex] - points[j - 1][dimIndex]);
|
||||
S delta = PxAbs(points[indexer[j]][dimIndex] - points[indexer[j - 1]][dimIndex]);
|
||||
if (delta > tol)
|
||||
{
|
||||
if (j - start > 1)
|
||||
newMergeRanges.pushBack(Range(start, j));
|
||||
start = j;
|
||||
}
|
||||
}
|
||||
if (r.end - start > 1)
|
||||
newMergeRanges.pushBack(Range(start, r.end));
|
||||
}
|
||||
|
||||
mergeRanges.clear();
|
||||
for (PxU32 i = 0; i < newMergeRanges.size(); ++i)
|
||||
mergeRanges.pushBack(newMergeRanges[i]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Comparer
|
||||
{
|
||||
const T* points;
|
||||
PxU32 dimension;
|
||||
|
||||
Comparer(const T* points_, const PxU32 dimension_) : points(points_), dimension(dimension_) {}
|
||||
|
||||
bool operator()(const PxI32& a, const PxI32& b) const
|
||||
{
|
||||
return points[a][dimension] > points[b][dimension];
|
||||
}
|
||||
|
||||
private:
|
||||
PX_NOCOPY(Comparer)
|
||||
};
|
||||
|
||||
public:
|
||||
template<typename T, typename S>
|
||||
static void mapDuplicatePoints(const T* points, const PxU32 nbPoints, PxArray<PxI32>& result, S duplicateDistanceManhattanMetric = static_cast<S>(1e-6))
|
||||
{
|
||||
result.reserve(nbPoints);
|
||||
result.forceSize_Unsafe(nbPoints);
|
||||
|
||||
PxArray<PxI32> indexer;
|
||||
indexer.reserve(nbPoints);
|
||||
indexer.forceSize_Unsafe(nbPoints);
|
||||
for (PxU32 i = 0; i < nbPoints; ++i)
|
||||
{
|
||||
indexer[i] = i;
|
||||
result[i] = i;
|
||||
}
|
||||
|
||||
Comparer<T> comparer(points, 0);
|
||||
PxSort(indexer.begin(), indexer.size(), comparer);
|
||||
|
||||
PxArray<Range> mergeRanges;
|
||||
mergeRanges.pushBack(Range(0, nbPoints));
|
||||
splitRanges<T>(mergeRanges, indexer, points, 0, duplicateDistanceManhattanMetric);
|
||||
|
||||
comparer.dimension = 1;
|
||||
for (PxU32 i = 0; i < mergeRanges.size(); ++i)
|
||||
{
|
||||
const Range& r = mergeRanges[i];
|
||||
PxSort(indexer.begin() + r.start, r.Length(), comparer);
|
||||
}
|
||||
splitRanges<T>(mergeRanges, indexer, points, 1, duplicateDistanceManhattanMetric);
|
||||
|
||||
comparer.dimension = 2;
|
||||
for (PxU32 i = 0; i < mergeRanges.size(); ++i)
|
||||
{
|
||||
const Range& r = mergeRanges[i];
|
||||
PxSort(indexer.begin() + r.start, r.Length(), comparer);
|
||||
}
|
||||
splitRanges<T>(mergeRanges, indexer, points, 2, duplicateDistanceManhattanMetric);
|
||||
|
||||
//Merge the ranges
|
||||
for (PxU32 i = 0; i < mergeRanges.size(); ++i)
|
||||
{
|
||||
const Range& r = mergeRanges[i];
|
||||
PxSort(indexer.begin() + r.start, r.Length());
|
||||
for (PxI32 j = r.start + 1; j < r.end; ++j)
|
||||
result[indexer[j]] = result[indexer[r.start]];
|
||||
}
|
||||
}
|
||||
|
||||
PX_PHYSX_COMMON_API static bool buildTriangleAdjacency(const Triangle* tris, PxU32 numTriangles, PxArray<PxI32>& result, PxHashMap<PxU64, PxI32>& edges);
|
||||
PX_PHYSX_COMMON_API static bool checkConsistentTriangleOrientation(const Triangle* tris, PxU32 numTriangles);
|
||||
PX_PHYSX_COMMON_API static bool buildConsistentTriangleOrientationMap(const Triangle* tris, PxU32 numTriangles, PxArray<bool>& flipMap,
|
||||
PxHashMap<PxU64, PxI32>& edges, PxArray<PxArray<PxU32>>& connectedTriangleGroups);
|
||||
PX_PHYSX_COMMON_API static bool makeTriOrientationConsistent(Triangle* tris, PxU32 numTriangles, bool invertOrientation = false);
|
||||
PX_PHYSX_COMMON_API static bool checkMeshWatertightness(const Triangle* tris, PxU32 numTriangles, bool treatInconsistentWindingAsNonWatertight = true);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
237
engine/third_party/physx/source/geomutils/src/common/GuMeshCleaner.cpp
vendored
Normal file
237
engine/third_party/physx/source/geomutils/src/common/GuMeshCleaner.cpp
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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/PxMemory.h"
|
||||
#include "foundation/PxAllocator.h"
|
||||
#include "foundation/PxBitUtils.h"
|
||||
#include "GuMeshCleaner.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
|
||||
struct Indices
|
||||
{
|
||||
PxU32 mRef[3];
|
||||
|
||||
PX_FORCE_INLINE bool operator!=(const Indices&v) const { return mRef[0] != v.mRef[0] || mRef[1] != v.mRef[1] || mRef[2] != v.mRef[2]; }
|
||||
};
|
||||
|
||||
static PX_FORCE_INLINE PxU32 getHashValue(const PxVec3& v)
|
||||
{
|
||||
const PxU32* h = reinterpret_cast<const PxU32*>(&v.x);
|
||||
const PxU32 f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0
|
||||
return (f>>22)^(f>>12)^(f);
|
||||
}
|
||||
|
||||
static PX_FORCE_INLINE PxU32 getHashValue(const Indices& v)
|
||||
{
|
||||
// const PxU32* h = v.mRef;
|
||||
// const PxU32 f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0
|
||||
// return (f>>22)^(f>>12)^(f);
|
||||
|
||||
PxU32 a = v.mRef[0];
|
||||
PxU32 b = v.mRef[1];
|
||||
PxU32 c = v.mRef[2];
|
||||
a=a-b; a=a-c; a=a^(c >> 13);
|
||||
b=b-c; b=b-a; b=b^(a << 8);
|
||||
c=c-a; c=c-b; c=c^(b >> 13);
|
||||
a=a-b; a=a-c; a=a^(c >> 12);
|
||||
b=b-c; b=b-a; b=b^(a << 16);
|
||||
c=c-a; c=c-b; c=c^(b >> 5);
|
||||
a=a-b; a=a-c; a=a^(c >> 3);
|
||||
b=b-c; b=b-a; b=b^(a << 10);
|
||||
c=c-a; c=c-b; c=c^(b >> 15);
|
||||
return c;
|
||||
}
|
||||
|
||||
MeshCleaner::MeshCleaner(PxU32 nbVerts, const PxVec3* srcVerts, PxU32 nbTris, const PxU32* srcIndices, PxF32 meshWeldTolerance, PxF32 areaLimit)
|
||||
{
|
||||
PxVec3* cleanVerts = PX_ALLOCATE(PxVec3, nbVerts, "MeshCleaner");
|
||||
PX_ASSERT(cleanVerts);
|
||||
|
||||
PxU32* indices = PX_ALLOCATE(PxU32, (nbTris*3), "MeshCleaner");
|
||||
|
||||
PxU32* remapTriangles = PX_ALLOCATE(PxU32, nbTris, "MeshCleaner");
|
||||
|
||||
PxU32* vertexIndices = NULL;
|
||||
if(meshWeldTolerance!=0.0f)
|
||||
{
|
||||
vertexIndices = PX_ALLOCATE(PxU32, nbVerts, "MeshCleaner");
|
||||
const PxF32 weldTolerance = 1.0f / meshWeldTolerance;
|
||||
// snap to grid
|
||||
for(PxU32 i=0; i<nbVerts; i++)
|
||||
{
|
||||
vertexIndices[i] = i;
|
||||
cleanVerts[i] = PxVec3( PxFloor(srcVerts[i].x*weldTolerance + 0.5f),
|
||||
PxFloor(srcVerts[i].y*weldTolerance + 0.5f),
|
||||
PxFloor(srcVerts[i].z*weldTolerance + 0.5f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PxMemCopy(cleanVerts, srcVerts, nbVerts*sizeof(PxVec3));
|
||||
}
|
||||
|
||||
const PxU32 maxNbElems = PxMax(nbTris, nbVerts);
|
||||
const PxU32 hashSize = PxNextPowerOfTwo(maxNbElems);
|
||||
const PxU32 hashMask = hashSize-1;
|
||||
PxU32* hashTable = PX_ALLOCATE(PxU32, (hashSize + maxNbElems), "MeshCleaner");
|
||||
PX_ASSERT(hashTable);
|
||||
PxMemSet(hashTable, 0xff, hashSize * sizeof(PxU32));
|
||||
PxU32* const next = hashTable + hashSize;
|
||||
|
||||
PxU32* remapVerts = PX_ALLOCATE(PxU32, nbVerts, "MeshCleaner");
|
||||
PxMemSet(remapVerts, 0xff, nbVerts * sizeof(PxU32));
|
||||
|
||||
for(PxU32 i=0;i<nbTris*3;i++)
|
||||
{
|
||||
const PxU32 vref = srcIndices[i];
|
||||
if(vref<nbVerts)
|
||||
remapVerts[vref] = 0;
|
||||
}
|
||||
|
||||
PxU32 nbCleanedVerts = 0;
|
||||
for(PxU32 i=0;i<nbVerts;i++)
|
||||
{
|
||||
if(remapVerts[i]==0xffffffff)
|
||||
continue;
|
||||
|
||||
const PxVec3& v = cleanVerts[i];
|
||||
const PxU32 hashValue = getHashValue(v) & hashMask;
|
||||
PxU32 offset = hashTable[hashValue];
|
||||
|
||||
while(offset!=0xffffffff && cleanVerts[offset]!=v)
|
||||
offset = next[offset];
|
||||
|
||||
if(offset==0xffffffff)
|
||||
{
|
||||
remapVerts[i] = nbCleanedVerts;
|
||||
cleanVerts[nbCleanedVerts] = v;
|
||||
if(vertexIndices)
|
||||
vertexIndices[nbCleanedVerts] = i;
|
||||
next[nbCleanedVerts] = hashTable[hashValue];
|
||||
hashTable[hashValue] = nbCleanedVerts++;
|
||||
}
|
||||
else remapVerts[i] = offset;
|
||||
}
|
||||
|
||||
// PT: area = ((p0 - p1).cross(p0 - p2)).magnitude() * 0.5
|
||||
// area < areaLimit
|
||||
// <=> ((p0 - p1).cross(p0 - p2)).magnitude() < areaLimit * 2.0
|
||||
// <=> ((p0 - p1).cross(p0 - p2)).magnitudeSquared() < (areaLimit * 2.0)^2
|
||||
const PxF32 limit = areaLimit * areaLimit * 4.0f;
|
||||
|
||||
PxU32 nbCleanedTris = 0;
|
||||
for(PxU32 i=0;i<nbTris;i++)
|
||||
{
|
||||
PxU32 vref0 = *srcIndices++;
|
||||
PxU32 vref1 = *srcIndices++;
|
||||
PxU32 vref2 = *srcIndices++;
|
||||
if(vref0>=nbVerts || vref1>=nbVerts || vref2>=nbVerts)
|
||||
continue;
|
||||
|
||||
// PT: you can still get zero-area faces when the 3 vertices are perfectly aligned
|
||||
const PxVec3& p0 = srcVerts[vref0];
|
||||
const PxVec3& p1 = srcVerts[vref1];
|
||||
const PxVec3& p2 = srcVerts[vref2];
|
||||
|
||||
const float area2 = ((p0 - p1).cross(p0 - p2)).magnitudeSquared();
|
||||
if(area2<=limit)
|
||||
continue;
|
||||
|
||||
vref0 = remapVerts[vref0];
|
||||
vref1 = remapVerts[vref1];
|
||||
vref2 = remapVerts[vref2];
|
||||
if(vref0==vref1 || vref1==vref2 || vref2==vref0)
|
||||
continue;
|
||||
|
||||
indices[nbCleanedTris*3+0] = vref0;
|
||||
indices[nbCleanedTris*3+1] = vref1;
|
||||
indices[nbCleanedTris*3+2] = vref2;
|
||||
remapTriangles[nbCleanedTris] = i;
|
||||
nbCleanedTris++;
|
||||
}
|
||||
PX_FREE(remapVerts);
|
||||
|
||||
PxU32 nbToGo = nbCleanedTris;
|
||||
nbCleanedTris = 0;
|
||||
PxMemSet(hashTable, 0xff, hashSize * sizeof(PxU32));
|
||||
|
||||
Indices* const I = reinterpret_cast<Indices*>(indices);
|
||||
bool idtRemap = true;
|
||||
for(PxU32 i=0;i<nbToGo;i++)
|
||||
{
|
||||
const Indices& v = I[i];
|
||||
const PxU32 hashValue = getHashValue(v) & hashMask;
|
||||
PxU32 offset = hashTable[hashValue];
|
||||
|
||||
while(offset!=0xffffffff && I[offset]!=v)
|
||||
offset = next[offset];
|
||||
|
||||
if(offset==0xffffffff)
|
||||
{
|
||||
const PxU32 originalIndex = remapTriangles[i];
|
||||
PX_ASSERT(nbCleanedTris<=i);
|
||||
remapTriangles[nbCleanedTris] = originalIndex;
|
||||
if(originalIndex!=nbCleanedTris)
|
||||
idtRemap = false;
|
||||
I[nbCleanedTris] = v;
|
||||
next[nbCleanedTris] = hashTable[hashValue];
|
||||
hashTable[hashValue] = nbCleanedTris++;
|
||||
}
|
||||
}
|
||||
PX_FREE(hashTable);
|
||||
|
||||
if(vertexIndices)
|
||||
{
|
||||
for(PxU32 i=0;i<nbCleanedVerts;i++)
|
||||
cleanVerts[i] = srcVerts[vertexIndices[i]];
|
||||
PX_FREE(vertexIndices);
|
||||
}
|
||||
mNbVerts = nbCleanedVerts;
|
||||
mNbTris = nbCleanedTris;
|
||||
mVerts = cleanVerts;
|
||||
mIndices = indices;
|
||||
if(idtRemap)
|
||||
{
|
||||
PX_FREE(remapTriangles);
|
||||
mRemap = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
mRemap = remapTriangles;
|
||||
}
|
||||
}
|
||||
|
||||
MeshCleaner::~MeshCleaner()
|
||||
{
|
||||
PX_FREE(mRemap);
|
||||
PX_FREE(mIndices);
|
||||
PX_FREE(mVerts);
|
||||
}
|
||||
54
engine/third_party/physx/source/geomutils/src/common/GuMeshCleaner.h
vendored
Normal file
54
engine/third_party/physx/source/geomutils/src/common/GuMeshCleaner.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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_CLEANER_H
|
||||
#define GU_MESH_CLEANER_H
|
||||
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
class MeshCleaner
|
||||
{
|
||||
public:
|
||||
MeshCleaner(PxU32 nbVerts, const PxVec3* verts, PxU32 nbTris, const PxU32* indices, PxF32 meshWeldTolerance, PxF32 areaLimit);
|
||||
~MeshCleaner();
|
||||
|
||||
PxU32 mNbVerts;
|
||||
PxU32 mNbTris;
|
||||
PxVec3* mVerts;
|
||||
PxU32* mIndices;
|
||||
PxU32* mRemap;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
332
engine/third_party/physx/source/geomutils/src/common/GuQuantizer.cpp
vendored
Normal file
332
engine/third_party/physx/source/geomutils/src/common/GuQuantizer.cpp
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "GuQuantizer.h"
|
||||
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "foundation/PxBounds3.h"
|
||||
|
||||
#include "foundation/PxUserAllocated.h"
|
||||
#include "foundation/PxAllocator.h"
|
||||
#include "foundation/PxArray.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
|
||||
PxU32 kmeans_cluster3d(const PxVec3* input, // an array of input 3d data points.
|
||||
PxU32 inputSize, // the number of input data points.
|
||||
PxU32 clumpCount, // the number of clumps you wish to product.
|
||||
PxVec3* outputClusters, // The output array of clumps 3d vectors, should be at least 'clumpCount' in size.
|
||||
PxU32* outputIndices, // A set of indices which remaps the input vertices to clumps; should be at least 'inputSize'
|
||||
float errorThreshold=0.01f, // The error threshold to converge towards before giving up.
|
||||
float collapseDistance=0.01f); // distance so small it is not worth bothering to create a new clump.
|
||||
|
||||
template <class Vec,class Type >
|
||||
PxU32 kmeans_cluster(const Vec* input,
|
||||
PxU32 inputCount,
|
||||
PxU32 clumpCount,
|
||||
Vec* clusters,
|
||||
PxU32* outputIndices,
|
||||
Type threshold, // controls how long it works to converge towards a least errors solution.
|
||||
Type collapseDistance) // distance between clumps to consider them to be essentially equal.
|
||||
{
|
||||
PxU32 convergeCount = 64; // maximum number of iterations attempting to converge to a solution..
|
||||
PxU32* counts = PX_ALLOCATE(PxU32, clumpCount, "PxU32");
|
||||
Type error=0;
|
||||
if ( inputCount <= clumpCount ) // if the number of input points is less than our clumping size, just return the input points.
|
||||
{
|
||||
clumpCount = inputCount;
|
||||
for (PxU32 i=0; i<inputCount; i++)
|
||||
{
|
||||
if ( outputIndices )
|
||||
{
|
||||
outputIndices[i] = i;
|
||||
}
|
||||
clusters[i] = input[i];
|
||||
counts[i] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PxVec3* centroids = PX_ALLOCATE(PxVec3, clumpCount, "PxVec3");
|
||||
|
||||
// Take a sampling of the input points as initial centroid estimates.
|
||||
for (PxU32 i=0; i<clumpCount; i++)
|
||||
{
|
||||
PxU32 index = (i*inputCount)/clumpCount;
|
||||
PX_ASSERT( index < inputCount );
|
||||
clusters[i] = input[index];
|
||||
}
|
||||
|
||||
// Here is the main convergence loop
|
||||
Type old_error = FLT_MAX; // old and initial error estimates are max Type
|
||||
error = FLT_MAX;
|
||||
do
|
||||
{
|
||||
old_error = error; // preserve the old error
|
||||
// reset the counts and centroids to current cluster location
|
||||
for (PxU32 i=0; i<clumpCount; i++)
|
||||
{
|
||||
counts[i] = 0;
|
||||
centroids[i] = PxVec3(PxZero);
|
||||
}
|
||||
error = 0;
|
||||
// For each input data point, figure out which cluster it is closest too and add it to that cluster.
|
||||
for (PxU32 i=0; i<inputCount; i++)
|
||||
{
|
||||
Type min_distance = FLT_MAX;
|
||||
// find the nearest clump to this point.
|
||||
for (PxU32 j=0; j<clumpCount; j++)
|
||||
{
|
||||
const Type distance = (input[i] - clusters[j]).magnitudeSquared();
|
||||
if ( distance < min_distance )
|
||||
{
|
||||
min_distance = distance;
|
||||
outputIndices[i] = j; // save which clump this point indexes
|
||||
}
|
||||
}
|
||||
const PxU32 index = outputIndices[i]; // which clump was nearest to this point.
|
||||
centroids[index]+=input[i];
|
||||
counts[index]++; // increment the counter indicating how many points are in this clump.
|
||||
error+=min_distance; // save the error accumulation
|
||||
}
|
||||
// Now, for each clump, compute the mean and store the result.
|
||||
for (PxU32 i=0; i<clumpCount; i++)
|
||||
{
|
||||
if ( counts[i] ) // if this clump got any points added to it...
|
||||
{
|
||||
const Type recip = 1.0f / Type(counts[i]); // compute the average (center of those points)
|
||||
centroids[i]*=recip; // compute the average center of the points in this clump.
|
||||
clusters[i] = centroids[i]; // store it as the new cluster.
|
||||
}
|
||||
}
|
||||
// decrement the convergence counter and bail if it is taking too long to converge to a solution.
|
||||
convergeCount--;
|
||||
if (convergeCount == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ( error < threshold ) // early exit if our first guess is already good enough (if all input points are the same)
|
||||
break;
|
||||
} while ( PxAbs(error - old_error) > threshold ); // keep going until the error is reduced by this threshold amount.
|
||||
|
||||
PX_FREE(centroids);
|
||||
}
|
||||
|
||||
// ok..now we prune the clumps if necessary.
|
||||
// The rules are; first, if a clump has no 'counts' then we prune it as it's unused.
|
||||
// The second, is if the centroid of this clump is essentially the same (based on the distance tolerance)
|
||||
// as an existing clump, then it is pruned and all indices which used to point to it, now point to the one
|
||||
// it is closest too.
|
||||
PxU32 outCount = 0; // number of clumps output after pruning performed.
|
||||
Type d2 = collapseDistance*collapseDistance; // squared collapse distance.
|
||||
for (PxU32 i=0; i<clumpCount; i++)
|
||||
{
|
||||
if ( counts[i] == 0 ) // if no points ended up in this clump, eliminate it.
|
||||
continue;
|
||||
// see if this clump is too close to any already accepted clump.
|
||||
bool add = true;
|
||||
PxU32 remapIndex = outCount; // by default this clump will be remapped to its current index.
|
||||
for (PxU32 j=0; j<outCount; j++)
|
||||
{
|
||||
Type distance = (clusters[i] - clusters[j]).magnitudeSquared();
|
||||
if ( distance < d2 )
|
||||
{
|
||||
remapIndex = j;
|
||||
add = false; // we do not add this clump
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we have fewer output clumps than input clumps so far, then we need to remap the old indices to the new ones.
|
||||
if ( outputIndices )
|
||||
{
|
||||
if ( outCount != i || !add ) // we need to remap indices! everything that was index 'i' now needs to be remapped to 'outCount'
|
||||
{
|
||||
for (PxU32 j=0; j<inputCount; j++)
|
||||
{
|
||||
if ( outputIndices[j] == i )
|
||||
{
|
||||
outputIndices[j] = remapIndex; //
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( add )
|
||||
{
|
||||
clusters[outCount] = clusters[i];
|
||||
outCount++;
|
||||
}
|
||||
}
|
||||
PX_FREE(counts);
|
||||
clumpCount = outCount;
|
||||
return clumpCount;
|
||||
}
|
||||
|
||||
PxU32 kmeans_cluster3d( const PxVec3* input, // an array of input 3d data points.
|
||||
PxU32 inputSize, // the number of input data points.
|
||||
PxU32 clumpCount, // the number of clumps you wish to produce
|
||||
PxVec3* outputClusters, // The output array of clumps 3d vectors, should be at least 'clumpCount' in size.
|
||||
PxU32* outputIndices, // A set of indices which remaps the input vertices to clumps; should be at least 'inputSize'
|
||||
float errorThreshold, // The error threshold to converge towards before giving up.
|
||||
float collapseDistance) // distance so small it is not worth bothering to create a new clump.
|
||||
{
|
||||
return kmeans_cluster< PxVec3, float >(input, inputSize, clumpCount, outputClusters, outputIndices, errorThreshold, collapseDistance);
|
||||
}
|
||||
|
||||
class QuantizerImpl : public Quantizer, public PxUserAllocated
|
||||
{
|
||||
public:
|
||||
QuantizerImpl()
|
||||
{
|
||||
mScale = PxVec3(1.0f, 1.0f, 1.0f);
|
||||
mCenter = PxVec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// Use the k-means quantizer, similar results, but much slower.
|
||||
virtual const PxVec3* kmeansQuantize3D(PxU32 vcount,
|
||||
const PxVec3* vertices,
|
||||
PxU32 stride,
|
||||
bool denormalizeResults,
|
||||
PxU32 maxVertices,
|
||||
PxU32& outVertsCount)
|
||||
{
|
||||
const PxVec3* ret = NULL;
|
||||
outVertsCount = 0;
|
||||
mNormalizedInput.clear();
|
||||
mQuantizedOutput.clear();
|
||||
|
||||
if ( vcount > 0 )
|
||||
{
|
||||
normalizeInput(vcount,vertices, stride);
|
||||
|
||||
PxVec3* quantizedOutput = PX_ALLOCATE(PxVec3, vcount, "PxVec3");
|
||||
PxU32* quantizedIndices = PX_ALLOCATE(PxU32, vcount, "PxU32");
|
||||
outVertsCount = kmeans_cluster3d(&mNormalizedInput[0], vcount, maxVertices, quantizedOutput, quantizedIndices, 0.01f, 0.0001f );
|
||||
if ( outVertsCount > 0 )
|
||||
{
|
||||
if ( denormalizeResults )
|
||||
{
|
||||
for (PxU32 i=0; i<outVertsCount; i++)
|
||||
{
|
||||
PxVec3 v( quantizedOutput[i] );
|
||||
v = v.multiply(mScale) + mCenter;
|
||||
mQuantizedOutput.pushBack(v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (PxU32 i=0; i<outVertsCount; i++)
|
||||
{
|
||||
const PxVec3& v( quantizedOutput[i] );
|
||||
mQuantizedOutput.pushBack(v);
|
||||
}
|
||||
}
|
||||
ret = &mQuantizedOutput[0];
|
||||
}
|
||||
PX_FREE(quantizedOutput);
|
||||
PX_FREE(quantizedIndices);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void release()
|
||||
{
|
||||
PX_DELETE_THIS;
|
||||
}
|
||||
|
||||
virtual const PxVec3& getDenormalizeScale() const
|
||||
{
|
||||
return mScale;
|
||||
}
|
||||
|
||||
virtual const PxVec3& getDenormalizeCenter() const
|
||||
{
|
||||
return mCenter;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void normalizeInput(PxU32 vcount, const PxVec3* vertices, PxU32 stride)
|
||||
{
|
||||
const char* vtx = reinterpret_cast<const char *> (vertices);
|
||||
mNormalizedInput.clear();
|
||||
mQuantizedOutput.clear();
|
||||
PxBounds3 bounds;
|
||||
bounds.setEmpty();
|
||||
for (PxU32 i=0; i<vcount; i++)
|
||||
{
|
||||
const PxVec3& v = *reinterpret_cast<const PxVec3 *> (vtx);
|
||||
vtx += stride;
|
||||
|
||||
bounds.include(v);
|
||||
}
|
||||
|
||||
mCenter = bounds.getCenter();
|
||||
|
||||
PxVec3 dim = bounds.getDimensions();
|
||||
dim *= 1.001f;
|
||||
mScale = dim*0.5f;
|
||||
|
||||
for (PxU32 i = 0; i < 3; i++)
|
||||
{
|
||||
if(dim[i] == 0)
|
||||
mScale[i] = 1.0f;
|
||||
}
|
||||
|
||||
PxVec3 recip;
|
||||
recip.x = 1.0f / mScale.x;
|
||||
recip.y = 1.0f / mScale.y;
|
||||
recip.z = 1.0f / mScale.z;
|
||||
|
||||
vtx = reinterpret_cast<const char *> (vertices);
|
||||
for (PxU32 i=0; i<vcount; i++)
|
||||
{
|
||||
PxVec3 v = *reinterpret_cast<const PxVec3 *> (vtx);
|
||||
vtx += stride;
|
||||
|
||||
v = (v - mCenter).multiply(recip);
|
||||
|
||||
mNormalizedInput.pushBack(v);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~QuantizerImpl()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
PxVec3 mScale;
|
||||
PxVec3 mCenter;
|
||||
PxArray<PxVec3> mNormalizedInput;
|
||||
PxArray<PxVec3> mQuantizedOutput;
|
||||
};
|
||||
|
||||
Quantizer* physx::Gu::createQuantizer()
|
||||
{
|
||||
return PX_NEW(QuantizerImpl);
|
||||
}
|
||||
75
engine/third_party/physx/source/geomutils/src/common/GuQuantizer.h
vendored
Normal file
75
engine/third_party/physx/source/geomutils/src/common/GuQuantizer.h
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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_QUANTIZER_H
|
||||
#define GU_QUANTIZER_H
|
||||
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// K-means quantization class
|
||||
// see http://en.wikipedia.org/wiki/K-means_clustering
|
||||
// implementation from John Ratcliff http://codesuppository.blogspot.ch/2010/12/k-means-clustering-algorithm.html
|
||||
class Quantizer
|
||||
{
|
||||
public:
|
||||
// quantize the input vertices
|
||||
virtual const PxVec3* kmeansQuantize3D( PxU32 vcount,
|
||||
const PxVec3* vertices,
|
||||
PxU32 stride,
|
||||
bool denormalizeResults,
|
||||
PxU32 maxVertices,
|
||||
PxU32& outVertsCount) = 0;
|
||||
|
||||
// returns the denormalized scale
|
||||
virtual const PxVec3& getDenormalizeScale() const = 0;
|
||||
|
||||
// returns the denormalized center
|
||||
virtual const PxVec3& getDenormalizeCenter() const = 0;
|
||||
|
||||
// release internal data
|
||||
virtual void release() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~Quantizer()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// creates the quantizer class
|
||||
Quantizer * createQuantizer();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
57
engine/third_party/physx/source/geomutils/src/common/GuSeparatingAxes.cpp
vendored
Normal file
57
engine/third_party/physx/source/geomutils/src/common/GuSeparatingAxes.cpp
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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;
|
||||
|
||||
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
|
||||
}
|
||||
90
engine/third_party/physx/source/geomutils/src/common/GuSeparatingAxes.h
vendored
Normal file
90
engine/third_party/physx/source/geomutils/src/common/GuSeparatingAxes.h
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "common/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 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
|
||||
112
engine/third_party/physx/source/geomutils/src/common/GuVertexReducer.cpp
vendored
Normal file
112
engine/third_party/physx/source/geomutils/src/common/GuVertexReducer.cpp
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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 "GuVertexReducer.h"
|
||||
#include "foundation/PxAllocator.h"
|
||||
#include "CmRadixSort.h"
|
||||
|
||||
using namespace physx;
|
||||
using namespace Gu;
|
||||
using namespace Cm;
|
||||
|
||||
// PT: code archeology: this initially came from ICE (IceVertexCloud.h/cpp). Consider dropping it.
|
||||
|
||||
ReducedVertexCloud::ReducedVertexCloud(const PxVec3* verts, PxU32 nb_verts) : mNbRVerts(0), mRVerts(NULL), mXRef(NULL)
|
||||
{
|
||||
mVerts = verts;
|
||||
mNbVerts = nb_verts;
|
||||
}
|
||||
|
||||
ReducedVertexCloud::~ReducedVertexCloud()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
ReducedVertexCloud& ReducedVertexCloud::clean()
|
||||
{
|
||||
PX_FREE(mXRef);
|
||||
PX_FREE(mRVerts);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduction method. Use this to create a minimal vertex cloud.
|
||||
* \param rc [out] result structure
|
||||
* \return true if success
|
||||
* \warning This is not about welding nearby vertices, here we look for real redundant ones.
|
||||
*/
|
||||
bool ReducedVertexCloud::reduce(REDUCEDCLOUD* rc)
|
||||
{
|
||||
clean();
|
||||
|
||||
mXRef = PX_ALLOCATE(PxU32, mNbVerts, "mXRef");
|
||||
|
||||
float* f = PX_ALLOCATE(float, mNbVerts, "tmp");
|
||||
|
||||
for(PxU32 i=0;i<mNbVerts;i++)
|
||||
f[i] = mVerts[i].x;
|
||||
|
||||
RadixSortBuffered Radix;
|
||||
Radix.Sort(reinterpret_cast<const PxU32*>(f), mNbVerts, RADIX_UNSIGNED);
|
||||
|
||||
for(PxU32 i=0;i<mNbVerts;i++)
|
||||
f[i] = mVerts[i].y;
|
||||
Radix.Sort(reinterpret_cast<const PxU32*>(f), mNbVerts, RADIX_UNSIGNED);
|
||||
|
||||
for(PxU32 i=0;i<mNbVerts;i++)
|
||||
f[i] = mVerts[i].z;
|
||||
const PxU32* Sorted = Radix.Sort(reinterpret_cast<const PxU32*>(f), mNbVerts, RADIX_UNSIGNED).GetRanks();
|
||||
|
||||
PX_FREE(f);
|
||||
|
||||
mNbRVerts = 0;
|
||||
const PxU32 Junk[] = {PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32};
|
||||
const PxU32* Previous = Junk;
|
||||
mRVerts = PX_ALLOCATE(PxVec3, mNbVerts, "PxVec3");
|
||||
PxU32 Nb = mNbVerts;
|
||||
while(Nb--)
|
||||
{
|
||||
const PxU32 Vertex = *Sorted++; // Vertex number
|
||||
|
||||
const PxU32* current = reinterpret_cast<const PxU32*>(&mVerts[Vertex]);
|
||||
if(current[0]!=Previous[0] || current[1]!=Previous[1] || current[2]!=Previous[2])
|
||||
mRVerts[mNbRVerts++] = mVerts[Vertex];
|
||||
|
||||
Previous = current;
|
||||
|
||||
mXRef[Vertex] = mNbRVerts-1;
|
||||
}
|
||||
|
||||
if(rc)
|
||||
{
|
||||
rc->CrossRef = mXRef;
|
||||
rc->NbRVerts = mNbRVerts;
|
||||
rc->RVerts = mRVerts;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
77
engine/third_party/physx/source/geomutils/src/common/GuVertexReducer.h
vendored
Normal file
77
engine/third_party/physx/source/geomutils/src/common/GuVertexReducer.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Copyright (c) 2008-2025 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_VERTEX_REDUCER_H
|
||||
#define GU_VERTEX_REDUCER_H
|
||||
|
||||
#include "foundation/PxVec3.h"
|
||||
#include "common/PxPhysXCommonConfig.h"
|
||||
|
||||
namespace physx
|
||||
{
|
||||
namespace Gu
|
||||
{
|
||||
//! Vertex cloud reduction result structure
|
||||
struct REDUCEDCLOUD
|
||||
{
|
||||
// Out
|
||||
PxVec3* RVerts; //!< Reduced list
|
||||
PxU32 NbRVerts; //!< Reduced number of vertices
|
||||
PxU32* CrossRef; //!< nb_verts remapped indices
|
||||
};
|
||||
|
||||
class ReducedVertexCloud
|
||||
{
|
||||
public:
|
||||
ReducedVertexCloud(const PxVec3* verts, PxU32 nb_verts);
|
||||
~ReducedVertexCloud();
|
||||
|
||||
ReducedVertexCloud& clean();
|
||||
bool reduce(REDUCEDCLOUD* rc=NULL);
|
||||
|
||||
PX_FORCE_INLINE PxU32 getNbVerts() const { return mNbVerts; }
|
||||
PX_FORCE_INLINE PxU32 getNbReducedVerts() const { return mNbRVerts; }
|
||||
PX_FORCE_INLINE const PxVec3* getReducedVerts() const { return mRVerts; }
|
||||
PX_FORCE_INLINE const PxVec3& getReducedVertex(PxU32 i) const { return mRVerts[i]; }
|
||||
PX_FORCE_INLINE const PxU32* getCrossRefTable() const { return mXRef; }
|
||||
|
||||
private:
|
||||
// Original vertex cloud
|
||||
PxU32 mNbVerts; //!< Number of vertices
|
||||
const PxVec3* mVerts; //!< List of vertices (pointer copy)
|
||||
|
||||
// Reduced vertex cloud
|
||||
PxU32 mNbRVerts; //!< Reduced number of vertices
|
||||
PxVec3* mRVerts; //!< Reduced list of vertices
|
||||
PxU32* mXRef; //!< Cross-reference table (used to remap topologies)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user