allow Z as up-axis for raycast acceleration in btHeightfieldTerrainShape

This commit is contained in:
erwincoumans 2019-04-16 10:34:59 -07:00
parent 6912290080
commit a8d1c121fd
4 changed files with 81 additions and 44 deletions

View File

@ -678,8 +678,9 @@ public:
sum_ms = 0; sum_ms = 0;
} }
btRaycastBar3(btScalar ray_length, btScalar z, btScalar max_y, struct GUIHelperInterface* guiHelper) btRaycastBar3(btScalar ray_length, btScalar z, btScalar max_y, struct GUIHelperInterface* guiHelper, int upAxisIndex)
{ {
m_guiHelper = guiHelper; m_guiHelper = guiHelper;
frame_counter = 0; frame_counter = 0;
ms = 0; ms = 0;
@ -697,14 +698,24 @@ public:
{ {
btScalar alpha = dalpha * i; btScalar alpha = dalpha * i;
// rotate around by alpha degrees y // rotate around by alpha degrees y
btQuaternion q(btVector3(0.0, 1.0, 0.0), alpha); btVector3 upAxis(0, 0, 0);
upAxis[upAxisIndex] = 1;
btQuaternion q(upAxis, alpha);
direction[i] = btVector3(1.0, 0.0, 0.0); direction[i] = btVector3(1.0, 0.0, 0.0);
direction[i] = quatRotate(q, direction[i]); direction[i] = quatRotate(q, direction[i]);
direction[i] = direction[i] * ray_length; direction[i] = direction[i] * ray_length;
if (upAxisIndex == 1)
{
source[i] = btVector3(min_x, max_y, z); source[i] = btVector3(min_x, max_y, z);
}
else
{
source[i] = btVector3(min_x, z, max_y);
}
dest[i] = source[i] + direction[i]; dest[i] = source[i] + direction[i];
dest[i][1] = -1000; dest[i][upAxisIndex] = -1000;
normal[i] = btVector3(1.0, 0.0, 0.0); normal[i] = btVector3(1.0, 0.0, 0.0);
} }
} }
@ -922,9 +933,11 @@ void HeightfieldExample::initPhysics()
createEmptyDynamicsWorld(); createEmptyDynamicsWorld();
m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld); m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld);
raycastBar = btRaycastBar3(2500.0, 0, 2.0, m_guiHelper); m_upAxis = 2; // start with Y-axis as "up"
m_guiHelper->setUpAxis(m_upAxis);
raycastBar = btRaycastBar3(2500.0, 0, 2.0, m_guiHelper, m_upAxis);
// set up basic state // set up basic state
m_upAxis = 1; // start with Y-axis as "up"
m_type = PHY_FLOAT;// SHORT; m_type = PHY_FLOAT;// SHORT;
@ -971,6 +984,8 @@ void HeightfieldExample::resetPhysics(void)
m_upAxis, m_type, flipQuadEdges); m_upAxis, m_type, flipQuadEdges);
btAssert(m_heightfieldShape && "null heightfield"); btAssert(m_heightfieldShape && "null heightfield");
if (m_upAxis == 2)
m_heightfieldShape->setFlipTriangleWinding(true);
//buildAccelerator is optional, it may not support all features. //buildAccelerator is optional, it may not support all features.
m_heightfieldShape->buildAccelerator(); m_heightfieldShape->buildAccelerator();

View File

@ -415,8 +415,7 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans, co
triangleMesh->performRaycast(&rcb, rayFromLocalScaled, rayToLocalScaled); triangleMesh->performRaycast(&rcb, rayFromLocalScaled, rayToLocalScaled);
} }
else if (((resultCallback.m_flags&btTriangleRaycastCallback::kF_DisableHeightfieldAccelerator)==0) else if (((resultCallback.m_flags&btTriangleRaycastCallback::kF_DisableHeightfieldAccelerator)==0)
&& collisionShape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE && && collisionShape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE
(((btHeightfieldTerrainShape*)collisionShape)->getUpAxis()==1)//accelerator only supports Y axis at the moment
) )
{ {
///optimized version for btHeightfieldTerrainShape ///optimized version for btHeightfieldTerrainShape

View File

@ -71,6 +71,7 @@ void btHeightfieldTerrainShape::initialize(
m_flipQuadEdges = flipQuadEdges; m_flipQuadEdges = flipQuadEdges;
m_useDiamondSubdivision = false; m_useDiamondSubdivision = false;
m_useZigzagSubdivision = false; m_useZigzagSubdivision = false;
m_flipTriangleWinding = false;
m_upAxis = upAxis; m_upAxis = upAxis;
m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.)); m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.));
@ -335,30 +336,37 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
for (int x = startX; x < endX; x++) for (int x = startX; x < endX; x++)
{ {
btVector3 vertices[3]; btVector3 vertices[3];
int indices[3] = { 0, 1, 2 };
if (m_flipTriangleWinding)
{
indices[0] = 2;
indices[2] = 0;
}
if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j + x) & 1)) || (m_useZigzagSubdivision && !(j & 1))) if (m_flipQuadEdges || (m_useDiamondSubdivision && !((j + x) & 1)) || (m_useZigzagSubdivision && !(j & 1)))
{ {
//first triangle //first triangle
getVertex(x, j, vertices[0]); getVertex(x, j, vertices[indices[0]]);
getVertex(x, j + 1, vertices[1]); getVertex(x, j + 1, vertices[indices[1]]);
getVertex(x + 1, j + 1, vertices[2]); getVertex(x + 1, j + 1, vertices[indices[2]]);
callback->processTriangle(vertices, x, j); callback->processTriangle(vertices, x, j);
//second triangle //second triangle
// getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman // getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman
getVertex(x + 1, j + 1, vertices[1]); getVertex(x + 1, j + 1, vertices[indices[1]]);
getVertex(x + 1, j, vertices[2]); getVertex(x + 1, j, vertices[indices[2]]);
callback->processTriangle(vertices, x, j); callback->processTriangle(vertices, x, j);
} }
else else
{ {
//first triangle //first triangle
getVertex(x, j, vertices[0]); getVertex(x, j, vertices[indices[0]]);
getVertex(x, j + 1, vertices[1]); getVertex(x, j + 1, vertices[indices[1]]);
getVertex(x + 1, j, vertices[2]); getVertex(x + 1, j, vertices[indices[2]]);
callback->processTriangle(vertices, x, j); callback->processTriangle(vertices, x, j);
//second triangle //second triangle
getVertex(x + 1, j, vertices[0]); getVertex(x + 1, j, vertices[indices[0]]);
//getVertex(x,j+1,vertices[1]); //getVertex(x,j+1,vertices[1]);
getVertex(x + 1, j + 1, vertices[2]); getVertex(x + 1, j + 1, vertices[indices[2]]);
callback->processTriangle(vertices, x, j); callback->processTriangle(vertices, x, j);
} }
} }
@ -401,7 +409,7 @@ namespace
/// and executes an action on each cell intersecting the given segment, ordered from begin to end. /// and executes an action on each cell intersecting the given segment, ordered from begin to end.
/// Initially inspired by http://www.cse.yorku.ca/~amana/research/grid.pdf /// Initially inspired by http://www.cse.yorku.ca/~amana/research/grid.pdf
template <typename Action_T> template <typename Action_T>
void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector3& endPos) void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector3& endPos, int indices[3])
{ {
GridRaycastState rs; GridRaycastState rs;
rs.maxDistance3d = beginPos.distance(endPos); rs.maxDistance3d = beginPos.distance(endPos);
@ -411,8 +419,9 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
return; return;
} }
btScalar rayDirectionFlatX = endPos[0] - beginPos[0];
btScalar rayDirectionFlatZ = endPos[2] - beginPos[2]; btScalar rayDirectionFlatX = endPos[indices[0]] - beginPos[indices[0]];
btScalar rayDirectionFlatZ = endPos[indices[2]] - beginPos[indices[2]];
rs.maxDistanceFlat = btSqrt(rayDirectionFlatX * rayDirectionFlatX + rayDirectionFlatZ * rayDirectionFlatZ); rs.maxDistanceFlat = btSqrt(rayDirectionFlatX * rayDirectionFlatX + rayDirectionFlatZ * rayDirectionFlatZ);
if (rs.maxDistanceFlat < 0.0001) if (rs.maxDistanceFlat < 0.0001)
@ -444,11 +453,11 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
{ {
if (xiStep == 1) if (xiStep == 1)
{ {
paramCrossX = (ceil(beginPos[0]) - beginPos[0]) * paramDeltaX; paramCrossX = (ceil(beginPos[indices[0]]) - beginPos[indices[0]]) * paramDeltaX;
} }
else else
{ {
paramCrossX = (beginPos[0] - floor(beginPos[0])) * paramDeltaX; paramCrossX = (beginPos[indices[0]] - floor(beginPos[indices[0]])) * paramDeltaX;
} }
} }
else else
@ -461,11 +470,11 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
{ {
if (ziStep == 1) if (ziStep == 1)
{ {
paramCrossZ = (ceil(beginPos[2]) - beginPos[2]) * paramDeltaZ; paramCrossZ = (ceil(beginPos[indices[2]]) - beginPos[indices[2]]) * paramDeltaZ;
} }
else else
{ {
paramCrossZ = (beginPos[2] - floor(beginPos[2])) * paramDeltaZ; paramCrossZ = (beginPos[indices[2]] - floor(beginPos[indices[2]])) * paramDeltaZ;
} }
} }
else else
@ -473,8 +482,8 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
paramCrossZ = infinite; // Will never cross on Z paramCrossZ = infinite; // Will never cross on Z
} }
rs.x = static_cast<int>(floor(beginPos[0])); rs.x = static_cast<int>(floor(beginPos[indices[0]]));
rs.z = static_cast<int>(floor(beginPos[2])); rs.z = static_cast<int>(floor(beginPos[indices[2]]));
// Workaround cases where the ray starts at an integer position // Workaround cases where the ray starts at an integer position
if (paramCrossX == 0.0) if (paramCrossX == 0.0)
@ -603,10 +612,12 @@ struct ProcessVBoundsAction
btVector3 rayEnd; btVector3 rayEnd;
btVector3 rayDir; btVector3 rayDir;
int* m_indices;
ProcessTrianglesAction processTriangles; ProcessTrianglesAction processTriangles;
ProcessVBoundsAction(const btAlignedObjectArray<btHeightfieldTerrainShape::Range>& bnd) ProcessVBoundsAction(const btAlignedObjectArray<btHeightfieldTerrainShape::Range>& bnd, int* indices)
: vbounds(bnd) : vbounds(bnd),
m_indices(indices)
{ {
} }
void operator()(const GridRaycastState& rs) const void operator()(const GridRaycastState& rs) const
@ -634,11 +645,11 @@ struct ProcessVBoundsAction
// We did enter the flat projection of the AABB, // We did enter the flat projection of the AABB,
// but we have to check if we intersect it on the vertical axis // but we have to check if we intersect it on the vertical axis
if (enterPos[1] > chunk.max && exitPos[1] > chunk.max) if (enterPos[1] > chunk.max && exitPos[m_indices[1]] > chunk.max)
{ {
return; return;
} }
if (enterPos[1] < chunk.min && exitPos[1] < chunk.min) if (enterPos[1] < chunk.min && exitPos[m_indices[1]] < chunk.min)
{ {
return; return;
} }
@ -651,7 +662,7 @@ struct ProcessVBoundsAction
exitPos = rayEnd; exitPos = rayEnd;
} }
gridRaycast(processTriangles, enterPos, exitPos); gridRaycast(processTriangles, enterPos, exitPos, m_indices);
// Note: it could be possible to have more than one grid at different levels, // Note: it could be possible to have more than one grid at different levels,
// to do this there would be a branch using a pointer to another ProcessVBoundsAction // to do this there would be a branch using a pointer to another ProcessVBoundsAction
} }
@ -677,10 +688,16 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
processTriangles.length = m_heightStickLength - 1; processTriangles.length = m_heightStickLength - 1;
// TODO Transform vectors to account for m_upAxis // TODO Transform vectors to account for m_upAxis
int iBeginX = static_cast<int>(floor(beginPos[0])); int indices[3] = { 0, 1, 2 };
int iBeginZ = static_cast<int>(floor(beginPos[2])); if (m_upAxis == 2)
int iEndX = static_cast<int>(floor(endPos[0])); {
int iEndZ = static_cast<int>(floor(endPos[2])); indices[1] = 2;
indices[2] = 1;
}
int iBeginX = static_cast<int>(floor(beginPos[indices[0]]));
int iBeginZ = static_cast<int>(floor(beginPos[indices[2]]));
int iEndX = static_cast<int>(floor(endPos[indices[0]]));
int iEndZ = static_cast<int>(floor(endPos[indices[2]]));
if (iBeginX == iEndX && iBeginZ == iEndZ) if (iBeginX == iEndX && iBeginZ == iEndZ)
{ {
@ -691,22 +708,24 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
return; return;
} }
if (m_vboundsGrid.size()==0) if (m_vboundsGrid.size()==0)
{ {
// Process all quads intersecting the flat projection of the ray // Process all quads intersecting the flat projection of the ray
gridRaycast(processTriangles, beginPos, endPos); gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
} }
else else
{ {
btVector3 rayDiff = endPos - beginPos; btVector3 rayDiff = endPos - beginPos;
btScalar flatDistance2 = rayDiff[0] * rayDiff[0] + rayDiff[2] * rayDiff[2]; btScalar flatDistance2 = rayDiff[indices[0]] * rayDiff[indices[0]] + rayDiff[indices[2]] * rayDiff[indices[2]];
if (flatDistance2 < m_vboundsChunkSize * m_vboundsChunkSize) if (flatDistance2 < m_vboundsChunkSize * m_vboundsChunkSize)
{ {
// Don't use chunks, the ray is too short in the plane // Don't use chunks, the ray is too short in the plane
gridRaycast(processTriangles, beginPos, endPos); gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
} }
ProcessVBoundsAction processVBounds(m_vboundsGrid); ProcessVBoundsAction processVBounds(m_vboundsGrid, &indices[0]);
processVBounds.width = m_vboundsGridWidth; processVBounds.width = m_vboundsGridWidth;
processVBounds.length = m_vboundsGridLength; processVBounds.length = m_vboundsGridLength;
processVBounds.rayBegin = beginPos; processVBounds.rayBegin = beginPos;
@ -715,7 +734,7 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
processVBounds.processTriangles = processTriangles; processVBounds.processTriangles = processTriangles;
processVBounds.chunkSize = m_vboundsChunkSize; processVBounds.chunkSize = m_vboundsChunkSize;
// The ray is long, run raycast on a higher-level grid // The ray is long, run raycast on a higher-level grid
gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize); gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize, indices);
} }
} }

View File

@ -103,7 +103,7 @@ protected:
bool m_flipQuadEdges; bool m_flipQuadEdges;
bool m_useDiamondSubdivision; bool m_useDiamondSubdivision;
bool m_useZigzagSubdivision; bool m_useZigzagSubdivision;
bool m_flipTriangleWinding;
int m_upAxis; int m_upAxis;
btVector3 m_localScaling; btVector3 m_localScaling;
@ -158,6 +158,10 @@ public:
///could help compatibility with Ogre heightfields. See https://code.google.com/p/bullet/issues/detail?id=625 ///could help compatibility with Ogre heightfields. See https://code.google.com/p/bullet/issues/detail?id=625
void setUseZigzagSubdivision(bool useZigzagSubdivision = true) { m_useZigzagSubdivision = useZigzagSubdivision; } void setUseZigzagSubdivision(bool useZigzagSubdivision = true) { m_useZigzagSubdivision = useZigzagSubdivision; }
void setFlipTriangleWinding(bool flipTriangleWinding)
{
m_flipTriangleWinding = flipTriangleWinding;
}
virtual void getAabb(const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const; virtual void getAabb(const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const;
virtual void processAllTriangles(btTriangleCallback * callback, const btVector3& aabbMin, const btVector3& aabbMax) const; virtual void processAllTriangles(btTriangleCallback * callback, const btVector3& aabbMin, const btVector3& aabbMax) const;