230 lines
8.2 KiB
C++
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);
|
|
}
|