Files
XCEngine/engine/third_party/physx/source/geomutils/src/GuWindingNumberT.h

329 lines
14 KiB
C
Raw Normal View History

// 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_WINDING_NUMBER_T_H
#define GU_WINDING_NUMBER_T_H
#include "GuTriangle.h"
#include "foundation/PxArray.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxVec3.h"
#include "GuBVH.h"
#include "GuAABBTreeQuery.h"
#include "GuAABBTreeNode.h"
#include "GuWindingNumberCluster.h"
namespace physx
{
namespace Gu
{
using Triangle = Gu::IndexedTriangleT<PxI32>;
template<typename R, typename V3>
struct SecondOrderClusterApproximationT : public ClusterApproximationT<R, V3>
{
PxMat33 WeightedOuterProductSum;
PX_FORCE_INLINE SecondOrderClusterApproximationT() {}
PX_FORCE_INLINE SecondOrderClusterApproximationT(R radius, R areaSum, const V3& weightedCentroid, const V3& weightedNormalSum, const PxMat33& weightedOuterProductSum) :
ClusterApproximationT<R, V3>(radius, areaSum, weightedCentroid, weightedNormalSum), WeightedOuterProductSum(weightedOuterProductSum)
{ }
};
//Evaluates a first order winding number approximation for a given cluster (cluster = bunch of triangles)
template<typename R, typename V3>
PX_FORCE_INLINE R firstOrderClusterApproximation(const V3& weightedCentroid, const V3& weightedNormalSum,
const V3& evaluationPoint)
{
const V3 dir = weightedCentroid - evaluationPoint;
const R l = dir.magnitude();
return (R(0.25 / 3.141592653589793238462643383) / (l * l * l)) * weightedNormalSum.dot(dir);
}
template<typename R, typename V3>
PX_FORCE_INLINE R clusterApproximation(const ClusterApproximationT<R, V3>& c, const V3& evaluationPoint)
{
return firstOrderClusterApproximation(c.WeightedCentroid, c.WeightedNormalSum, evaluationPoint);
}
//Evaluates a second order winding number approximation for a given cluster (cluster = bunch of triangles)
template<typename R, typename V3>
PX_FORCE_INLINE R secondOrderClusterApproximation(const V3& weightedCentroid, const V3& weightedNormalSum,
const PxMat33& weightedOuterProductSum, const V3& evaluationPoint)
{
const V3 dir = weightedCentroid - evaluationPoint;
const R l = dir.magnitude();
const R l2 = l * l;
const R scaling = R(0.25 / 3.141592653589793238462643383) / (l2 * l);
const R firstOrder = scaling * weightedNormalSum.dot(dir);
const R scaling2 = -R(3.0) * scaling / l2;
const R m11 = scaling + scaling2 * dir.x * dir.x, m12 = scaling2 * dir.x * dir.y, m13 = scaling2 * dir.x * dir.z;
const R m21 = scaling2 * dir.y * dir.x, m22 = scaling + scaling2 * dir.y * dir.y, m23 = scaling2 * dir.y * dir.z;
const R m31 = scaling2 * dir.z * dir.x, m32 = scaling2 * dir.z * dir.y, m33 = scaling + scaling2 * dir.z * dir.z;
return firstOrder + (weightedOuterProductSum.column0.x * m11 + weightedOuterProductSum.column1.x * m12 + weightedOuterProductSum.column2.x * m13 +
weightedOuterProductSum.column0.y * m21 + weightedOuterProductSum.column1.y * m22 + weightedOuterProductSum.column2.y * m23 +
weightedOuterProductSum.column0.z * m31 + weightedOuterProductSum.column1.z * m32 + weightedOuterProductSum.column2.z * m33);
}
template<typename R, typename V3>
PX_FORCE_INLINE R clusterApproximation(const SecondOrderClusterApproximationT<R, V3>& c, const V3& evaluationPoint)
{
return secondOrderClusterApproximation(c.WeightedCentroid, c.WeightedNormalSum, c.WeightedOuterProductSum, evaluationPoint);
}
//Computes parameters to approximately represent a cluster (cluster = bunch of triangles) to be used to compute a winding number approximation
template<typename R, typename V3>
void approximateCluster(const PxArray<PxI32>& triangleSet, PxU32 start, PxU32 end, const PxU32* triangles, const V3* points,
const PxArray<R>& triangleAreas, const PxArray<V3>& triangleNormalsTimesTriangleArea, const PxArray<V3>& triangleCentroids, ClusterApproximationT<R, V3>& cluster)
{
V3 weightedCentroid(0., 0., 0.);
R areaSum = 0;
V3 weightedNormalSum(0., 0., 0.);
for (PxU32 i = start; i < end; ++i)
{
PxI32 triId = triangleSet[i];
areaSum += triangleAreas[triId];
weightedCentroid += triangleCentroids[triId] * triangleAreas[triId];
weightedNormalSum += triangleNormalsTimesTriangleArea[triId];
}
weightedCentroid = weightedCentroid / areaSum;
R radiusSquared = 0;
for (PxU32 i = start; i < end; ++i)
{
PxI32 triId = triangleSet[i];
const PxU32* tri = &triangles[3 * triId];
R d2 = (weightedCentroid - points[tri[0]]).magnitudeSquared();
if (d2 > radiusSquared) radiusSquared = d2;
d2 = (weightedCentroid - points[tri[1]]).magnitudeSquared();
if (d2 > radiusSquared) radiusSquared = d2;
d2 = (weightedCentroid - points[tri[2]]).magnitudeSquared();
if (d2 > radiusSquared) radiusSquared = d2;
}
cluster = ClusterApproximationT<R, V3>(PxSqrt(radiusSquared), areaSum, weightedCentroid, weightedNormalSum/*, weightedOuterProductSum*/);
}
//Computes parameters to approximately represent a cluster (cluster = bunch of triangles) to be used to compute a winding number approximation
template<typename R, typename V3>
void approximateCluster(const PxArray<PxI32>& triangleSet, PxU32 start, PxU32 end, const PxU32* triangles, const V3* points,
const PxArray<R>& triangleAreas, const PxArray<V3>& triangleNormalsTimesTriangleArea, const PxArray<V3>& triangleCentroids, SecondOrderClusterApproximationT<R, V3>& cluster)
{
V3 weightedCentroid(0., 0., 0.);
R areaSum = 0;
V3 weightedNormalSum(0., 0., 0.);
for (PxU32 i = start; i < end; ++i)
{
PxI32 triId = triangleSet[i];
areaSum += triangleAreas[triId];
weightedCentroid += triangleCentroids[triId] * triangleAreas[triId];
weightedNormalSum += triangleNormalsTimesTriangleArea[triId];
}
weightedCentroid = weightedCentroid / areaSum;
R radiusSquared = 0;
PxMat33 weightedOuterProductSum(PxZERO::PxZero);
for (PxU32 i = start; i < end; ++i)
{
PxI32 triId = triangleSet[i];
const PxU32* tri = &triangles[3 * triId];
R d2 = (weightedCentroid - points[tri[0]]).magnitudeSquared();
if (d2 > radiusSquared) radiusSquared = d2;
d2 = (weightedCentroid - points[tri[1]]).magnitudeSquared();
if (d2 > radiusSquared) radiusSquared = d2;
d2 = (weightedCentroid - points[tri[2]]).magnitudeSquared();
if (d2 > radiusSquared) radiusSquared = d2;
weightedOuterProductSum = weightedOuterProductSum + PxMat33::outer(triangleCentroids[triId] - weightedCentroid, triangleNormalsTimesTriangleArea[triId]);
}
cluster = SecondOrderClusterApproximationT<R, V3>(PxSqrt(radiusSquared), areaSum, weightedCentroid, weightedNormalSum, weightedOuterProductSum);
}
//Exact winding number evaluation, needs to be called for every triangle close to the winding number query point
template<typename R, typename V3>
PX_FORCE_INLINE R evaluateExact(V3 a, V3 b, V3 c, const V3& p)
{
const R twoOver4PI = R(0.5 / 3.141592653589793238462643383);
a -= p;
b -= p;
c -= p;
const R la = a.magnitude(),
lb = b.magnitude(),
lc = c.magnitude();
const R y = a.x * b.y * c.z - a.x * b.z * c.y - a.y * b.x * c.z + a.y * b.z * c.x + a.z * b.x * c.y - a.z * b.y * c.x;
const R x = (la * lb * lc + (a.x * b.x + a.y * b.y + a.z * b.z) * lc +
(b.x * c.x + b.y * c.y + b.z * c.z) * la + (c.x * a.x + c.y * a.y + c.z * a.z) * lb);
return twoOver4PI * PxAtan2(y, x);
}
struct Section
{
PxI32 start;
PxI32 end;
Section(PxI32 s, PxI32 e) : start(s), end(e)
{}
};
//Helper method that recursively traverses the given BVH tree and computes a cluster approximation for every node and links it to the node
template<typename R, typename V3>
void precomputeClusterInformation(PxI32 nodeId, const BVHNode* tree, const PxU32* triangles, const PxU32 numTriangles,
const V3* points, PxHashMap<PxU32, ClusterApproximationT<R, V3>>& infos, const PxArray<R> triangleAreas,
const PxArray<V3>& triangleNormalsTimesTriangleArea, const PxArray<V3>& triangleCentroids)
{
PxArray<PxI32> stack;
stack.pushBack(nodeId);
PxArray<Section> returnStack;
PxArray<PxI32> triIndices;
triIndices.reserve(numTriangles);
infos.reserve(PxU32(1.2f*numTriangles));
while (stack.size() > 0)
{
nodeId = stack.popBack();
if (nodeId >= 0)
{
const BVHNode& node = tree[nodeId];
if (node.isLeaf())
{
triIndices.pushBack(node.getPrimitiveIndex());
returnStack.pushBack(Section(triIndices.size() - 1, triIndices.size()));
continue;
}
stack.pushBack(-nodeId - 1); //Marker for return index
stack.pushBack(node.getPosIndex());
stack.pushBack(node.getPosIndex() + 1);
}
else
{
Section trianglesA = returnStack.popBack();
Section trianglesB = returnStack.popBack();
Section sum(trianglesB.start, trianglesA.end);
nodeId = -nodeId - 1;
ClusterApproximationT<R, V3> c;
approximateCluster<R, V3>(triIndices, sum.start, sum.end, triangles, points, triangleAreas, triangleNormalsTimesTriangleArea, triangleCentroids, c);
infos.insert(PxU32(nodeId), c);
returnStack.pushBack(sum);
}
}
}
//Precomputes a cluster approximation for every node in the BVH tree
template<typename R, typename V3>
void precomputeClusterInformation(const BVHNode* tree, const PxU32* triangles, const PxU32 numTriangles,
const V3* points, PxHashMap<PxU32, ClusterApproximationT<R, V3>>& result, PxI32 rootNodeIndex)
{
PxArray<R> triangleAreas;
triangleAreas.resize(numTriangles);
PxArray<V3> triangleNormalsTimesTriangleArea;
triangleNormalsTimesTriangleArea.resize(numTriangles);
PxArray<V3> triangleCentroids;
triangleCentroids.resize(numTriangles);
for (PxU32 i = 0; i < numTriangles; ++i)
{
const PxU32* tri = &triangles[3 * i];
const V3& a = points[tri[0]];
const V3& b = points[tri[1]];
const V3& c = points[tri[2]];
triangleNormalsTimesTriangleArea[i] = (b - a).cross(c - a) * R(0.5);
triangleAreas[i] = triangleNormalsTimesTriangleArea[i].magnitude();
triangleCentroids[i] = (a + b + c) * R(1.0 / 3.0);
}
result.clear();
precomputeClusterInformation(rootNodeIndex, tree, triangles, numTriangles, points, result, triangleAreas, triangleNormalsTimesTriangleArea, triangleCentroids);
}
template<typename R, typename V3>
class WindingNumberTraversalController
{
public:
R mWindingNumber = 0;
private:
const PxU32* mTriangles;
const V3* mPoints;
const PxHashMap<PxU32, ClusterApproximationT<R, V3>>& mClusters;
V3 mQueryPoint;
R mDistanceThresholdBeta;
public:
PX_FORCE_INLINE WindingNumberTraversalController(const PxU32* triangles, const V3* points,
const PxHashMap<PxU32, ClusterApproximationT<R, V3>>& clusters, const V3& queryPoint, R distanceThresholdBeta = 2)
: mTriangles(triangles), mPoints(points), mClusters(clusters), mQueryPoint(queryPoint), mDistanceThresholdBeta(distanceThresholdBeta)
{ }
PX_FORCE_INLINE Gu::TraversalControl::Enum analyze(const BVHNode& node, PxI32 nodeIndex)
{
if (node.isLeaf())
{
PX_ASSERT(node.getNbPrimitives() == 1);
const PxU32* tri = &mTriangles[3 * node.getPrimitiveIndex()];
mWindingNumber += evaluateExact<R, V3>(mPoints[tri[0]], mPoints[tri[1]], mPoints[tri[2]], mQueryPoint);
return Gu::TraversalControl::eDontGoDeeper;
}
const ClusterApproximationT<R, V3>& cluster = mClusters.find(nodeIndex)->second;
const R distSquared = (mQueryPoint - cluster.WeightedCentroid).magnitudeSquared();
const R threshold = mDistanceThresholdBeta * cluster.Radius;
if (distSquared > threshold * threshold)
{
//mWindingNumber += secondOrderClusterApproximation(cluster.WeightedCentroid, cluster.WeightedNormalSum, cluster.WeightedOuterProductSum, mQueryPoint);
mWindingNumber += firstOrderClusterApproximation<R, V3>(cluster.WeightedCentroid, cluster.WeightedNormalSum, mQueryPoint); // secondOrderClusterApproximation(cluster.WeightedCentroid, cluster.WeightedNormalSum, cluster.WeightedOuterProductSum, mQueryPoint);
return Gu::TraversalControl::eDontGoDeeper;
}
return Gu::TraversalControl::eGoDeeper;
}
private:
PX_NOCOPY(WindingNumberTraversalController)
};
template<typename R, typename V3>
R computeWindingNumber(const BVHNode* tree, const V3& q, R beta, const PxHashMap<PxU32, ClusterApproximationT<R, V3>>& clusters,
const PxU32* triangles, const V3* points)
{
WindingNumberTraversalController<R, V3> c(triangles, points, clusters, q, beta);
traverseBVH<WindingNumberTraversalController<R, V3>>(tree, c);
return c.mWindingNumber;
}
}
}
#endif