mirror of
https://github.com/bulletphysics/bullet3
synced 2024-12-14 22:00:05 +00:00
Merge pull request #2208 from erwincoumans/master
allow Z as up-axis for raycast acceleration in btHeightfieldTerrainShape
This commit is contained in:
commit
10633c7c11
@ -34,8 +34,6 @@ static const btScalar s_gridHeightScale = 0.02;
|
||||
// the singularity at the center of the radial model means we need a lot of
|
||||
// finely-spaced time steps to get the physics right.
|
||||
// These numbers are probably too aggressive for a real game!
|
||||
static const int s_requestedHz = 180;
|
||||
static const float s_engineTimeStep = 1.0 / s_requestedHz;
|
||||
|
||||
// delta phase: radians per second
|
||||
static const btScalar s_deltaPhase = 0.25 * 2.0 * SIMD_PI;
|
||||
@ -81,53 +79,6 @@ getTerrainTypeName
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
getDataTypeName
|
||||
(
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
switch (type) {
|
||||
case PHY_UCHAR:
|
||||
return "UnsignedChar";
|
||||
|
||||
case PHY_SHORT:
|
||||
return "Short";
|
||||
|
||||
case PHY_FLOAT:
|
||||
return "Float";
|
||||
|
||||
default:
|
||||
btAssert(!"bad heightfield data type");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
getUpAxisName
|
||||
(
|
||||
int axis
|
||||
)
|
||||
{
|
||||
switch (axis) {
|
||||
case 0:
|
||||
return "X";
|
||||
|
||||
case 1:
|
||||
return "Y";
|
||||
|
||||
case 2:
|
||||
return "Z";
|
||||
|
||||
default:
|
||||
btAssert(!"bad up axis");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -352,7 +303,7 @@ randomHeight
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
static void
|
||||
dumpGrid
|
||||
(
|
||||
@ -376,7 +327,7 @@ dumpGrid
|
||||
//std::cerr << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
@ -509,13 +460,9 @@ getRawHeightfieldData
|
||||
btAssert(!"bad model type");
|
||||
}
|
||||
|
||||
if (0) {
|
||||
// inside if(0) so it keeps compiling but isn't
|
||||
// exercised and doesn't cause warnings
|
||||
// std::cerr << "final grid:\n";
|
||||
dumpGrid(raw, bytesPerElement, type, s_gridSize - 1);
|
||||
}
|
||||
|
||||
// std::cerr << "final grid:\n";
|
||||
//dumpGrid(raw, bytesPerElement, type, s_gridSize - 1);
|
||||
|
||||
// find min/max
|
||||
for (int i = 0; i < s_gridSize; ++i) {
|
||||
for (int j = 0; j < s_gridSize; ++j) {
|
||||
@ -731,8 +678,9 @@ public:
|
||||
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;
|
||||
frame_counter = 0;
|
||||
ms = 0;
|
||||
@ -750,14 +698,24 @@ public:
|
||||
{
|
||||
btScalar alpha = dalpha * i;
|
||||
// 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] = quatRotate(q, direction[i]);
|
||||
direction[i] = direction[i] * ray_length;
|
||||
|
||||
source[i] = btVector3(min_x, max_y, z);
|
||||
if (upAxisIndex == 1)
|
||||
{
|
||||
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][1] = -1000;
|
||||
dest[i][upAxisIndex] = -1000;
|
||||
normal[i] = btVector3(1.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
@ -975,10 +933,12 @@ void HeightfieldExample::initPhysics()
|
||||
|
||||
createEmptyDynamicsWorld();
|
||||
m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld);
|
||||
raycastBar = btRaycastBar3(2500.0, 0, 2.0, m_guiHelper);
|
||||
// set up basic state
|
||||
m_upAxis = 1; // start with Y-axis as "up"
|
||||
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
|
||||
|
||||
|
||||
m_type = PHY_FLOAT;// SHORT;
|
||||
m_model = gHeightfieldType;
|
||||
@ -993,34 +953,6 @@ void HeightfieldExample::initPhysics()
|
||||
|
||||
|
||||
|
||||
|
||||
static PHY_ScalarType nextType (PHY_ScalarType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PHY_FLOAT:
|
||||
return PHY_SHORT;
|
||||
break;
|
||||
case PHY_SHORT:
|
||||
return PHY_UCHAR;
|
||||
break;
|
||||
case PHY_UCHAR:
|
||||
return PHY_FLOAT;
|
||||
break;
|
||||
}
|
||||
btAssert (0);
|
||||
return PHY_FLOAT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void doPrint(int x, int& y, int dy, const char * text)
|
||||
{
|
||||
//GLDebugDrawString(x, y, text);
|
||||
y += dy;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TerrainDemo -- private helper methods
|
||||
@ -1051,7 +983,9 @@ void HeightfieldExample::resetPhysics(void)
|
||||
m_minHeight, m_maxHeight,
|
||||
m_upAxis, m_type, flipQuadEdges);
|
||||
btAssert(m_heightfieldShape && "null heightfield");
|
||||
|
||||
|
||||
if (m_upAxis == 2)
|
||||
m_heightfieldShape->setFlipTriangleWinding(true);
|
||||
//buildAccelerator is optional, it may not support all features.
|
||||
m_heightfieldShape->buildAccelerator();
|
||||
|
||||
|
@ -112,7 +112,7 @@ struct b3PluginManagerInternalData
|
||||
b3BulletDefaultFileIO m_defaultFileIO;
|
||||
|
||||
b3PluginManagerInternalData()
|
||||
: m_rpcCommandProcessorInterface(0), m_activeNotificationsBufferIndex(0), m_activeRendererPluginUid(-1), m_activeCollisionPluginUid(-1), m_numNotificationPlugins(0), m_activeFileIOPluginUid(-1)
|
||||
: m_physicsDirect(0), m_rpcCommandProcessorInterface(0), m_activeNotificationsBufferIndex(0), m_activeRendererPluginUid(-1), m_activeCollisionPluginUid(-1), m_numNotificationPlugins(0), m_activeFileIOPluginUid(-1)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -388,35 +388,37 @@ struct WrapperFileIO : public CommonFileIOInterface
|
||||
int childHandle = childFileIO->fileOpen(fileName, mode);
|
||||
if (childHandle>=0)
|
||||
{
|
||||
int fileSize = childFileIO->getFileSize(childHandle);
|
||||
char* buffer = 0;
|
||||
if (fileSize)
|
||||
{
|
||||
buffer = m_cachedFiles.allocateBuffer(fileSize);
|
||||
if (buffer)
|
||||
{
|
||||
int readBytes = childFileIO->fileRead(childHandle, buffer, fileSize);
|
||||
if (readBytes!=fileSize)
|
||||
{
|
||||
if (readBytes<fileSize)
|
||||
{
|
||||
fileSize = readBytes;
|
||||
} else
|
||||
{
|
||||
printf("WrapperFileIO error: reading more bytes (%d) then reported file size (%d) of file %s.\n", readBytes, fileSize, fileName);
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
fileSize=0;
|
||||
}
|
||||
}
|
||||
|
||||
//potentially register a zero byte file, or files that only can be read partially
|
||||
if (m_enableFileCaching)
|
||||
{
|
||||
int fileSize = childFileIO->getFileSize(childHandle);
|
||||
char* buffer = 0;
|
||||
if (fileSize)
|
||||
{
|
||||
buffer = m_cachedFiles.allocateBuffer(fileSize);
|
||||
if (buffer)
|
||||
{
|
||||
int readBytes = childFileIO->fileRead(childHandle, buffer, fileSize);
|
||||
if (readBytes!=fileSize)
|
||||
{
|
||||
if (readBytes<fileSize)
|
||||
{
|
||||
fileSize = readBytes;
|
||||
} else
|
||||
{
|
||||
printf("WrapperFileIO error: reading more bytes (%d) then reported file size (%d) of file %s.\n", readBytes, fileSize, fileName);
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
fileSize=0;
|
||||
}
|
||||
}
|
||||
|
||||
//potentially register a zero byte file, or files that only can be read partially
|
||||
|
||||
m_cachedFiles.registerFile(fileName, buffer, fileSize);
|
||||
}
|
||||
|
||||
childFileIO->fileClose(childHandle);
|
||||
break;
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ joints=[]
|
||||
|
||||
mocapData = motion_capture_data.MotionCaptureData()
|
||||
|
||||
motionPath = pybullet_data.getDataPath()+"/data/motions/laikago_walk.json"
|
||||
motionPath = pybullet_data.getDataPath()+"/data/motions/laikago_walk.txt"
|
||||
|
||||
mocapData.Load(motionPath)
|
||||
print("mocapData.NumFrames=",mocapData.NumFrames())
|
||||
|
@ -415,8 +415,7 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans, co
|
||||
triangleMesh->performRaycast(&rcb, rayFromLocalScaled, rayToLocalScaled);
|
||||
}
|
||||
else if (((resultCallback.m_flags&btTriangleRaycastCallback::kF_DisableHeightfieldAccelerator)==0)
|
||||
&& collisionShape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE &&
|
||||
(((btHeightfieldTerrainShape*)collisionShape)->getUpAxis()==1)//accelerator only supports Y axis at the moment
|
||||
&& collisionShape->getShapeType() == TERRAIN_SHAPE_PROXYTYPE
|
||||
)
|
||||
{
|
||||
///optimized version for btHeightfieldTerrainShape
|
||||
|
@ -71,6 +71,7 @@ void btHeightfieldTerrainShape::initialize(
|
||||
m_flipQuadEdges = flipQuadEdges;
|
||||
m_useDiamondSubdivision = false;
|
||||
m_useZigzagSubdivision = false;
|
||||
m_flipTriangleWinding = false;
|
||||
m_upAxis = upAxis;
|
||||
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++)
|
||||
{
|
||||
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)))
|
||||
{
|
||||
//first triangle
|
||||
getVertex(x, j, vertices[0]);
|
||||
getVertex(x, j + 1, vertices[1]);
|
||||
getVertex(x + 1, j + 1, vertices[2]);
|
||||
getVertex(x, j, vertices[indices[0]]);
|
||||
getVertex(x, j + 1, vertices[indices[1]]);
|
||||
getVertex(x + 1, j + 1, vertices[indices[2]]);
|
||||
callback->processTriangle(vertices, x, j);
|
||||
//second triangle
|
||||
// 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, vertices[2]);
|
||||
getVertex(x + 1, j + 1, vertices[indices[1]]);
|
||||
getVertex(x + 1, j, vertices[indices[2]]);
|
||||
callback->processTriangle(vertices, x, j);
|
||||
}
|
||||
else
|
||||
{
|
||||
//first triangle
|
||||
getVertex(x, j, vertices[0]);
|
||||
getVertex(x, j + 1, vertices[1]);
|
||||
getVertex(x + 1, j, vertices[2]);
|
||||
getVertex(x, j, vertices[indices[0]]);
|
||||
getVertex(x, j + 1, vertices[indices[1]]);
|
||||
getVertex(x + 1, j, vertices[indices[2]]);
|
||||
callback->processTriangle(vertices, x, j);
|
||||
//second triangle
|
||||
getVertex(x + 1, j, vertices[0]);
|
||||
getVertex(x + 1, j, vertices[indices[0]]);
|
||||
//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);
|
||||
}
|
||||
}
|
||||
@ -401,7 +409,7 @@ namespace
|
||||
/// 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
|
||||
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;
|
||||
rs.maxDistance3d = beginPos.distance(endPos);
|
||||
@ -410,9 +418,10 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
|
||||
// Consider the ray is too small to hit anything
|
||||
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);
|
||||
|
||||
if (rs.maxDistanceFlat < 0.0001)
|
||||
@ -444,11 +453,11 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
|
||||
{
|
||||
if (xiStep == 1)
|
||||
{
|
||||
paramCrossX = (ceil(beginPos[0]) - beginPos[0]) * paramDeltaX;
|
||||
paramCrossX = (ceil(beginPos[indices[0]]) - beginPos[indices[0]]) * paramDeltaX;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramCrossX = (beginPos[0] - floor(beginPos[0])) * paramDeltaX;
|
||||
paramCrossX = (beginPos[indices[0]] - floor(beginPos[indices[0]])) * paramDeltaX;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -461,11 +470,11 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
|
||||
{
|
||||
if (ziStep == 1)
|
||||
{
|
||||
paramCrossZ = (ceil(beginPos[2]) - beginPos[2]) * paramDeltaZ;
|
||||
paramCrossZ = (ceil(beginPos[indices[2]]) - beginPos[indices[2]]) * paramDeltaZ;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramCrossZ = (beginPos[2] - floor(beginPos[2])) * paramDeltaZ;
|
||||
paramCrossZ = (beginPos[indices[2]] - floor(beginPos[indices[2]])) * paramDeltaZ;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -473,8 +482,8 @@ void gridRaycast(Action_T& quadAction, const btVector3& beginPos, const btVector
|
||||
paramCrossZ = infinite; // Will never cross on Z
|
||||
}
|
||||
|
||||
rs.x = static_cast<int>(floor(beginPos[0]));
|
||||
rs.z = static_cast<int>(floor(beginPos[2]));
|
||||
rs.x = static_cast<int>(floor(beginPos[indices[0]]));
|
||||
rs.z = static_cast<int>(floor(beginPos[indices[2]]));
|
||||
|
||||
// Workaround cases where the ray starts at an integer position
|
||||
if (paramCrossX == 0.0)
|
||||
@ -603,10 +612,12 @@ struct ProcessVBoundsAction
|
||||
btVector3 rayEnd;
|
||||
btVector3 rayDir;
|
||||
|
||||
int* m_indices;
|
||||
ProcessTrianglesAction processTriangles;
|
||||
|
||||
ProcessVBoundsAction(const btAlignedObjectArray<btHeightfieldTerrainShape::Range>& bnd)
|
||||
: vbounds(bnd)
|
||||
ProcessVBoundsAction(const btAlignedObjectArray<btHeightfieldTerrainShape::Range>& bnd, int* indices)
|
||||
: vbounds(bnd),
|
||||
m_indices(indices)
|
||||
{
|
||||
}
|
||||
void operator()(const GridRaycastState& rs) const
|
||||
@ -634,11 +645,11 @@ struct ProcessVBoundsAction
|
||||
|
||||
// We did enter the flat projection of the AABB,
|
||||
// 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;
|
||||
}
|
||||
if (enterPos[1] < chunk.min && exitPos[1] < chunk.min)
|
||||
if (enterPos[1] < chunk.min && exitPos[m_indices[1]] < chunk.min)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -651,7 +662,7 @@ struct ProcessVBoundsAction
|
||||
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,
|
||||
// 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;
|
||||
|
||||
// TODO Transform vectors to account for m_upAxis
|
||||
int iBeginX = static_cast<int>(floor(beginPos[0]));
|
||||
int iBeginZ = static_cast<int>(floor(beginPos[2]));
|
||||
int iEndX = static_cast<int>(floor(endPos[0]));
|
||||
int iEndZ = static_cast<int>(floor(endPos[2]));
|
||||
int indices[3] = { 0, 1, 2 };
|
||||
if (m_upAxis == 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)
|
||||
{
|
||||
@ -691,22 +708,24 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (m_vboundsGrid.size()==0)
|
||||
{
|
||||
// Process all quads intersecting the flat projection of the ray
|
||||
gridRaycast(processTriangles, beginPos, endPos);
|
||||
gridRaycast(processTriangles, beginPos, endPos, &indices[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
// 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.length = m_vboundsGridLength;
|
||||
processVBounds.rayBegin = beginPos;
|
||||
@ -715,7 +734,7 @@ void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, con
|
||||
processVBounds.processTriangles = processTriangles;
|
||||
processVBounds.chunkSize = m_vboundsChunkSize;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ protected:
|
||||
bool m_flipQuadEdges;
|
||||
bool m_useDiamondSubdivision;
|
||||
bool m_useZigzagSubdivision;
|
||||
|
||||
bool m_flipTriangleWinding;
|
||||
int m_upAxis;
|
||||
|
||||
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
|
||||
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 processAllTriangles(btTriangleCallback * callback, const btVector3& aabbMin, const btVector3& aabbMax) const;
|
||||
|
Loading…
Reference in New Issue
Block a user