Files
XCEngine/engine/third_party/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp

230 lines
8.2 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.
#include "geometry/PxMeshScale.h"
#include "GuHeightFieldUtil.h"
#include "GuSweepSharedTests.h"
#include "GuHeightField.h"
#include "GuEntityReport.h"
#include "foundation/PxIntrinsics.h"
#include "CmScaling.h"
using namespace physx;
void Gu::HeightFieldUtil::computeLocalBounds(PxBounds3& bounds) const
{
const PxMeshScale scale(PxVec3(mHfGeom->rowScale, mHfGeom->heightScale, mHfGeom->columnScale), PxQuat(PxIdentity));
const PxMat33 mat33 = Cm::toMat33(scale);
bounds.minimum = mat33.transform(mHeightField->getData().mAABB.getMin());
bounds.maximum = mat33.transform(mHeightField->getData().mAABB.getMax());
// PT: HFs will assert in Gu::intersectRayAABB2() if we don't deal with that
const float deltaY = GU_MIN_AABB_EXTENT*0.5f - (bounds.maximum.y - bounds.minimum.y);
if(deltaY>0.0f)
{
bounds.maximum.y += deltaY*0.6f;
bounds.minimum.y -= deltaY*0.6f;
}
}
static PX_FORCE_INLINE bool reportTriangle(Gu::OverlapReport& callback, PxU32 material, PxU32* PX_RESTRICT indexBuffer, const PxU32 bufferSize, PxU32& indexBufferUsed, PxU32 triangleIndex)
{
if(material != PxHeightFieldMaterial::eHOLE)
{
indexBuffer[indexBufferUsed++] = triangleIndex;
if(indexBufferUsed >= bufferSize)
{
if(!callback.reportTouchedTris(indexBufferUsed, indexBuffer))
return false;
indexBufferUsed = 0;
}
}
return true;
}
void Gu::HeightFieldUtil::overlapAABBTriangles(const PxBounds3& bounds, OverlapReport& callback, PxU32 batchSize) const
{
PX_ASSERT(batchSize<=HF_OVERLAP_REPORT_BUFFER_SIZE);
PX_ASSERT(!bounds.isEmpty());
PxBounds3 localBounds = bounds;
localBounds.minimum.x *= mOneOverRowScale;
localBounds.minimum.y *= mOneOverHeightScale;
localBounds.minimum.z *= mOneOverColumnScale;
localBounds.maximum.x *= mOneOverRowScale;
localBounds.maximum.y *= mOneOverHeightScale;
localBounds.maximum.z *= mOneOverColumnScale;
if(mHfGeom->rowScale < 0.0f)
PxSwap(localBounds.minimum.x, localBounds.maximum.x);
if(mHfGeom->columnScale < 0.0f)
PxSwap(localBounds.minimum.z, localBounds.maximum.z);
// early exit for aabb does not overlap in XZ plane
// DO NOT MOVE: since rowScale / columnScale may be negative this has to be done after scaling localBounds
const PxU32 nbRows = mHeightField->getNbRowsFast();
const PxU32 nbColumns = mHeightField->getNbColumnsFast();
if(localBounds.minimum.x > float(nbRows - 1))
return;
if(localBounds.minimum.z > float(nbColumns - 1))
return;
if(localBounds.maximum.x < 0.0f)
return;
if(localBounds.maximum.z < 0.0f)
return;
const PxU32 minRow = mHeightField->getMinRow(localBounds.minimum.x);
const PxU32 maxRow = mHeightField->getMaxRow(localBounds.maximum.x);
const PxU32 minColumn = mHeightField->getMinColumn(localBounds.minimum.z);
const PxU32 maxColumn = mHeightField->getMaxColumn(localBounds.maximum.z);
const PxU32 deltaColumn = maxColumn - minColumn;
const PxU32 maxNbTriangles = 2 * deltaColumn * (maxRow - minRow);
if(!maxNbTriangles)
return;
const PxU32 bufferSize = batchSize<=HF_OVERLAP_REPORT_BUFFER_SIZE ? batchSize : HF_OVERLAP_REPORT_BUFFER_SIZE;
PxU32 indexBuffer[HF_OVERLAP_REPORT_BUFFER_SIZE];
PxU32 indexBufferUsed = 0;
PxU32 offset = minRow * nbColumns + minColumn;
const PxReal miny = localBounds.minimum.y;
const PxReal maxy = localBounds.maximum.y;
const PxU32 columnStride = nbColumns - deltaColumn;
for(PxU32 row=minRow; row<maxRow; row++)
{
for(PxU32 column=minColumn; column<maxColumn; column++)
{
const PxReal h0 = mHeightField->getHeight(offset);
const PxReal h1 = mHeightField->getHeight(offset + 1);
const PxReal h2 = mHeightField->getHeight(offset + nbColumns);
const PxReal h3 = mHeightField->getHeight(offset + nbColumns + 1);
const bool bmax = maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3;
const bool bmin = miny > h0 && miny > h1 && miny > h2 && miny > h3;
if(!(bmax || bmin))
{
if(!reportTriangle(callback, mHeightField->getMaterialIndex0(offset), indexBuffer, bufferSize, indexBufferUsed, offset << 1))
return;
if(!reportTriangle(callback, mHeightField->getMaterialIndex1(offset), indexBuffer, bufferSize, indexBufferUsed, (offset << 1) + 1))
return;
}
offset++;
}
offset += columnStride;
}
if(indexBufferUsed > 0)
callback.reportTouchedTris(indexBufferUsed, indexBuffer);
}
PxU32 Gu::HeightFieldUtil::getTriangle(const PxTransform& pose, PxTriangle& worldTri,
PxU32* _vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation, bool worldSpaceRotation) const
{
#if PX_CHECKED
if (!mHeightField->isValidTriangle(triangleIndex))
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "HeightFieldShape::getTriangle: Invalid triangle index!");
return 0;
}
#endif
PxVec3 handedness(1.0f); // Vector to invert normal coordinates according to the heightfield scales
bool wrongHanded = false;
if (mHfGeom->columnScale < 0)
{
wrongHanded = !wrongHanded;
handedness.z = -1.0f;
}
if (mHfGeom->rowScale < 0)
{
wrongHanded = !wrongHanded;
handedness.x = -1.0f;
}
/* if (0) // ptchernev: Iterating over triangles becomes a pain.
{
if (mHeightField.getTriangleMaterial(triangleIndex) == mHfGeom.holeMaterialIndex)
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "HeightFieldShape::getTriangle: Non-existing triangle (triangle has hole material)!");
return 0;
}
}*/
PxU32 vertexIndices[3];
mHeightField->getTriangleVertexIndices(triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded]);
if(adjacencyIndices)
{
mHeightField->getTriangleAdjacencyIndices( triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded],
adjacencyIndices[wrongHanded ? 2 : 0], adjacencyIndices[1], adjacencyIndices[wrongHanded ? 0 : 2]);
}
if(_vertexIndices)
{
_vertexIndices[0] = vertexIndices[0];
_vertexIndices[1] = vertexIndices[1];
_vertexIndices[2] = vertexIndices[2];
}
if (worldSpaceRotation)
{
if (worldSpaceTranslation)
{
for (PxU32 vi = 0; vi < 3; vi++)
worldTri.verts[vi] = hf2worldp(pose, mHeightField->getVertex(vertexIndices[vi]));
}
else
{
for (PxU32 vi = 0; vi < 3; vi++)
{
// TTP 2390
// local space here is rotated (but not translated) world space
worldTri.verts[vi] = pose.q.rotate(hf2shapep(mHeightField->getVertex(vertexIndices[vi])));
}
}
}
else
{
const PxVec3 offset = worldSpaceTranslation ? pose.p : PxVec3(0.0f);
for (PxU32 vi = 0; vi < 3; vi++)
worldTri.verts[vi] = hf2shapep(mHeightField->getVertex(vertexIndices[vi])) + offset;
}
return PxU32(mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE);
}