329 lines
14 KiB
C
329 lines
14 KiB
C
|
|
// Redistribution and use in source and binary forms, with or without
|
||
|
|
// modification, are permitted provided that the following conditions
|
||
|
|
// are met:
|
||
|
|
// * Redistributions of source code must retain the above copyright
|
||
|
|
// notice, this list of conditions and the following disclaimer.
|
||
|
|
// * Redistributions in binary form must reproduce the above copyright
|
||
|
|
// notice, this list of conditions and the following disclaimer in the
|
||
|
|
// documentation and/or other materials provided with the distribution.
|
||
|
|
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
||
|
|
// contributors may be used to endorse or promote products derived
|
||
|
|
// from this software without specific prior written permission.
|
||
|
|
//
|
||
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
|
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||
|
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
|
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
|
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||
|
|
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
//
|
||
|
|
// Copyright (c) 2008-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
|