Skip to content

Commit fbbd184

Browse files
committed
UPBGE: Fix compound ray cast with triangle child shape.
Previously when a compound shape was hit by a ray cast the information about the children triangles was inaccessible because of a lack from the bullet API. To fix this issue we have to keep the triangle index but add a child index of the compound child shape hit. m_childIndex is dedicated for in LocalRayResult and its value is set in rayTestSingleInternal, -1 for non compound shapes. This value is catch from FilterClosestRayResultCallback and used in RayTest to get the actual collision shape. To still get the shape construction info of the children, each collision shape created has as user pointer the shape costruction info. This object is get in RayTest to find the mesh and polygon. Tested with: - mix of convex and concave shape in a compound shape. - non compound concave and convex shape.
1 parent 4a5055c commit fbbd184

File tree

7 files changed

+119
-127
lines changed

7 files changed

+119
-127
lines changed

extern/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,8 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con
340340
collisionObjectWrap->getCollisionObject(),
341341
0,
342342
castResult.m_normal,
343-
castResult.m_fraction
344-
);
343+
castResult.m_fraction,
344+
-1);
345345

346346
bool normalInWorldSpace = true;
347347
resultCallback.addSingleResult(localRayResult, normalInWorldSpace);
@@ -386,7 +386,8 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con
386386
(m_collisionObject,
387387
&shapeInfo,
388388
hitNormalWorld,
389-
hitFraction);
389+
hitFraction,
390+
-1);
390391

391392
bool normalInWorldSpace = true;
392393
return m_resultCallback->addSingleResult(rayResult,normalInWorldSpace);
@@ -468,7 +469,8 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con
468469
(m_collisionObject,
469470
&shapeInfo,
470471
hitNormalWorld,
471-
hitFraction);
472+
hitFraction,
473+
-1);
472474

473475
bool normalInWorldSpace = true;
474476
return m_resultCallback->addSingleResult(rayResult,normalInWorldSpace);
@@ -509,11 +511,7 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con
509511

510512
virtual btScalar addSingleResult (btCollisionWorld::LocalRayResult &r, bool b)
511513
{
512-
btCollisionWorld::LocalShapeInfo shapeInfo;
513-
shapeInfo.m_shapePart = -1;
514-
shapeInfo.m_triangleIndex = m_i;
515-
if (r.m_localShapeInfo == NULL)
516-
r.m_localShapeInfo = &shapeInfo;
514+
r.m_childIndex = m_i;
517515

518516
const btScalar result = m_userCallback->addSingleResult(r, b);
519517
m_closestHitFraction = m_userCallback->m_closestHitFraction;

extern/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,21 @@ class btCollisionWorld
188188
LocalRayResult(const btCollisionObject* collisionObject,
189189
LocalShapeInfo* localShapeInfo,
190190
const btVector3& hitNormalLocal,
191-
btScalar hitFraction)
191+
btScalar hitFraction,
192+
int childIndex)
192193
:m_collisionObject(collisionObject),
193194
m_localShapeInfo(localShapeInfo),
194195
m_hitNormalLocal(hitNormalLocal),
195-
m_hitFraction(hitFraction)
196+
m_hitFraction(hitFraction),
197+
m_childIndex(childIndex)
196198
{
197199
}
198200

199201
const btCollisionObject* m_collisionObject;
200202
LocalShapeInfo* m_localShapeInfo;
201203
btVector3 m_hitNormalLocal;
202204
btScalar m_hitFraction;
203-
205+
int m_childIndex;
204206
};
205207

206208
///RayResultCallback is used to report new raycast results

extern/bullet/src/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,8 @@ void btSoftMultiBodyDynamicsWorld::rayTestSingle(const btTransform& rayFromTrans
317317
(collisionObject,
318318
&shapeInfo,
319319
normal,
320-
softResult.fraction);
320+
softResult.fraction,
321+
-1);
321322
bool normalInWorldSpace = true;
322323
resultCallback.addSingleResult(rayResult,normalInWorldSpace);
323324
}

extern/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,8 @@ void btSoftRigidDynamicsWorld::rayTestSingle(const btTransform& rayFromTrans,con
317317
(collisionObject,
318318
&shapeInfo,
319319
normal,
320-
softResult.fraction);
320+
softResult.fraction,
321+
-1);
321322
bool normalInWorldSpace = true;
322323
resultCallback.addSingleResult(rayResult,normalInWorldSpace);
323324
}

extern/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorldMt.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ void btSoftRigidDynamicsWorldMt::rayTestSingle(const btTransform& rayFromTrans,c
318318
(collisionObject,
319319
&shapeInfo,
320320
normal,
321-
softResult.fraction);
321+
softResult.fraction,
322+
-1);
322323
bool normalInWorldSpace = true;
323324
resultCallback.addSingleResult(rayResult,normalInWorldSpace);
324325
}

source/gameengine/Physics/Bullet/CcdPhysicsController.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,6 +2142,11 @@ btCollisionShape *CcdShapeConstructionInfo::CreateBulletShape(btScalar margin, b
21422142
BLI_assert(false);
21432143
}
21442144
}
2145+
2146+
if (collisionShape) {
2147+
collisionShape->setUserPointer(this);
2148+
}
2149+
21452150
return collisionShape;
21462151
}
21472152

source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp

Lines changed: 96 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,13 +1101,13 @@ void CcdPhysicsEnvironment::RemoveConstraintById(int constraintId, bool free)
11011101

11021102
struct FilterClosestRayResultCallback : public btCollisionWorld::ClosestRayResultCallback {
11031103
PHY_IRayCastFilterCallback& m_phyRayFilter;
1104-
const btCollisionShape *m_hitTriangleShape;
1104+
int m_hitChildIndex;
11051105
int m_hitTriangleIndex;
11061106

11071107
FilterClosestRayResultCallback(PHY_IRayCastFilterCallback& phyRayFilter, const btVector3& rayFrom, const btVector3& rayTo)
11081108
:btCollisionWorld::ClosestRayResultCallback(rayFrom, rayTo),
11091109
m_phyRayFilter(phyRayFilter),
1110-
m_hitTriangleShape(nullptr),
1110+
m_hitChildIndex(-1),
11111111
m_hitTriangleIndex(0)
11121112
{
11131113
}
@@ -1134,21 +1134,18 @@ struct FilterClosestRayResultCallback : public btCollisionWorld::ClosestRayResu
11341134

11351135
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
11361136
{
1137-
//CcdPhysicsController* curHit = static_cast<CcdPhysicsController*>(rayResult.m_collisionObject->getUserPointer());
1138-
// save shape information as ClosestRayResultCallback::AddSingleResult() does not do it
1137+
m_hitChildIndex = rayResult.m_childIndex;
11391138
if (rayResult.m_localShapeInfo) {
1140-
m_hitTriangleShape = rayResult.m_collisionObject->getCollisionShape();
11411139
m_hitTriangleIndex = rayResult.m_localShapeInfo->m_triangleIndex;
11421140
}
11431141
else {
1144-
m_hitTriangleShape = nullptr;
11451142
m_hitTriangleIndex = 0;
11461143
}
11471144
return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
11481145
}
11491146
};
11501147

1151-
static bool GetHitTriangle(btCollisionShape *shape, CcdShapeConstructionInfo *shapeInfo, int hitTriangleIndex, btVector3 triangle[])
1148+
static bool GetHitTriangle(const btCollisionShape *shape, CcdShapeConstructionInfo *shapeInfo, int hitTriangleIndex, btVector3 triangle[])
11521149
{
11531150
// this code is copied from Bullet
11541151
const unsigned char *vertexbase;
@@ -1198,132 +1195,119 @@ PHY_IPhysicsController *CcdPhysicsEnvironment::RayTest(PHY_IRayCastFilterCallbac
11981195

11991196
//Either Ray Cast with or without filtering
12001197

1201-
//btCollisionWorld::ClosestRayResultCallback rayCallback(rayFrom,rayTo);
12021198
FilterClosestRayResultCallback rayCallback(filterCallback, rayFrom, rayTo);
1203-
12041199
PHY_RayCastResult result;
12051200

12061201
// don't collision with sensor object
12071202
rayCallback.m_collisionFilterMask = CcdConstructionInfo::AllFilter ^ CcdConstructionInfo::SensorFilter;
12081203
// use faster (less accurate) ray callback, works better with 0 collision margins
12091204
rayCallback.m_flags |= btTriangleRaycastCallback::kF_UseSubSimplexConvexCastRaytest;
1210-
//, ,filterCallback.m_faceNormal);
12111205

12121206
m_dynamicsWorld->rayTest(rayFrom, rayTo, rayCallback);
12131207
if (rayCallback.hasHit()) {
1208+
const btCollisionObject *object = rayCallback.m_collisionObject;
1209+
const btCollisionShape *shape = object->getCollisionShape();
1210+
12141211
CcdPhysicsController *controller = static_cast<CcdPhysicsController *>(rayCallback.m_collisionObject->getUserPointer());
12151212
result.m_controller = controller;
1216-
result.m_hitPoint[0] = rayCallback.m_hitPointWorld.getX();
1217-
result.m_hitPoint[1] = rayCallback.m_hitPointWorld.getY();
1218-
result.m_hitPoint[2] = rayCallback.m_hitPointWorld.getZ();
1219-
1220-
if (rayCallback.m_hitTriangleShape != nullptr) {
1221-
// identify the mesh polygon
1222-
CcdShapeConstructionInfo *shapeInfo = controller->m_shapeInfo;
1223-
if (shapeInfo) {
1224-
btCollisionShape *shape = controller->GetCollisionObject()->getCollisionShape();
1225-
if (shape->isCompound()) {
1226-
btCompoundShape *compoundShape = (btCompoundShape *)shape;
1227-
CcdShapeConstructionInfo *compoundShapeInfo = shapeInfo;
1228-
// need to search which sub-shape has been hit
1229-
for (int i = 0; i < compoundShape->getNumChildShapes(); i++) {
1230-
shapeInfo = compoundShapeInfo->GetChildShape(i);
1231-
shape = compoundShape->getChildShape(i);
1232-
if (shape == rayCallback.m_hitTriangleShape) {
1233-
break;
1213+
result.m_hitPoint = ToMt(rayCallback.m_hitPointWorld);
1214+
1215+
if (shape) {
1216+
if (shape->isCompound()) {
1217+
const btCompoundShape *compoundShape = static_cast<const btCompoundShape *>(shape);
1218+
shape = compoundShape->getChildShape(rayCallback.m_hitChildIndex);
1219+
}
1220+
1221+
CcdShapeConstructionInfo *shapeInfo = static_cast<CcdShapeConstructionInfo *>(shape->getUserPointer());
1222+
if (shapeInfo && rayCallback.m_hitTriangleIndex < shapeInfo->m_polygonIndexArray.size()) {
1223+
// save original collision shape triangle for soft body
1224+
const int hitTriangleIndex = rayCallback.m_hitTriangleIndex;
1225+
1226+
result.m_meshObject = shapeInfo->GetMesh();
1227+
if (shape->isSoftBody()) {
1228+
// soft body using different face numbering because of randomization
1229+
// hopefully we have stored the original face number in m_tag
1230+
const btSoftBody *softBody = static_cast<const btSoftBody *>(object);
1231+
if (softBody->m_faces[hitTriangleIndex].m_tag != 0) {
1232+
rayCallback.m_hitTriangleIndex = (int)((uintptr_t)(softBody->m_faces[hitTriangleIndex].m_tag) - 1);
1233+
}
1234+
}
1235+
// retrieve the original mesh polygon (in case of quad->tri conversion)
1236+
result.m_polygon = shapeInfo->m_polygonIndexArray[rayCallback.m_hitTriangleIndex];
1237+
// hit triangle in world coordinate, for face normal and UV coordinate
1238+
btVector3 triangle[3];
1239+
bool triangleOK = false;
1240+
if (filterCallback.m_faceUV && (3 * rayCallback.m_hitTriangleIndex) < shapeInfo->m_triFaceUVcoArray.size()) {
1241+
// interpolate the UV coordinate of the hit point
1242+
CcdShapeConstructionInfo::UVco *uvCo = &shapeInfo->m_triFaceUVcoArray[3 * rayCallback.m_hitTriangleIndex];
1243+
// 1. get the 3 coordinate of the triangle in world space
1244+
btVector3 v1, v2, v3;
1245+
if (shape->isSoftBody()) {
1246+
// soft body give points directly in world coordinate
1247+
const btSoftBody *softBody = static_cast<const btSoftBody *>(object);
1248+
v1 = softBody->m_faces[hitTriangleIndex].m_n[0]->m_x;
1249+
v2 = softBody->m_faces[hitTriangleIndex].m_n[1]->m_x;
1250+
v3 = softBody->m_faces[hitTriangleIndex].m_n[2]->m_x;
1251+
}
1252+
else {
1253+
// for rigid body we must apply the world transform
1254+
triangleOK = GetHitTriangle(shape, shapeInfo, hitTriangleIndex, triangle);
1255+
if (!triangleOK) {
1256+
// if we cannot get the triangle, no use to continue
1257+
goto SKIP_UV_NORMAL;
12341258
}
1259+
const btTransform& trans = object->getWorldTransform();
1260+
v1 = trans(triangle[0]);
1261+
v2 = trans(triangle[1]);
1262+
v3 = trans(triangle[2]);
12351263
}
1264+
// 2. compute barycentric coordinate of the hit point
1265+
btVector3 v = v2 - v1;
1266+
btVector3 w = v3 - v1;
1267+
btVector3 u = v.cross(w);
1268+
btScalar A = u.length();
1269+
1270+
v = v2 - rayCallback.m_hitPointWorld;
1271+
w = v3 - rayCallback.m_hitPointWorld;
1272+
u = v.cross(w);
1273+
btScalar A1 = u.length();
1274+
1275+
v = rayCallback.m_hitPointWorld - v1;
1276+
w = v3 - v1;
1277+
u = v.cross(w);
1278+
btScalar A2 = u.length();
1279+
1280+
btVector3 baryCo;
1281+
baryCo.setX(A1 / A);
1282+
baryCo.setY(A2 / A);
1283+
baryCo.setZ(1.0f - baryCo.getX() - baryCo.getY());
1284+
// 3. compute UV coordinate
1285+
result.m_hitUV[0] = baryCo.getX() * uvCo[0].uv[0] + baryCo.getY() * uvCo[1].uv[0] + baryCo.getZ() * uvCo[2].uv[0];
1286+
result.m_hitUV[1] = baryCo.getX() * uvCo[0].uv[1] + baryCo.getY() * uvCo[1].uv[1] + baryCo.getZ() * uvCo[2].uv[1];
1287+
result.m_hitUVOK = 1;
12361288
}
1237-
if (shape == rayCallback.m_hitTriangleShape &&
1238-
rayCallback.m_hitTriangleIndex < shapeInfo->m_polygonIndexArray.size()) {
1239-
// save original collision shape triangle for soft body
1240-
int hitTriangleIndex = rayCallback.m_hitTriangleIndex;
12411289

1242-
result.m_meshObject = shapeInfo->GetMesh();
1290+
// Bullet returns the normal from "outside".
1291+
// If the user requests the real normal, compute it now
1292+
if (filterCallback.m_faceNormal) {
12431293
if (shape->isSoftBody()) {
1244-
// soft body using different face numbering because of randomization
1245-
// hopefully we have stored the original face number in m_tag
1294+
// we can get the real normal directly from the body
12461295
const btSoftBody *softBody = static_cast<const btSoftBody *>(rayCallback.m_collisionObject);
1247-
if (softBody->m_faces[hitTriangleIndex].m_tag != 0) {
1248-
rayCallback.m_hitTriangleIndex = (int)((uintptr_t)(softBody->m_faces[hitTriangleIndex].m_tag) - 1);
1249-
}
1296+
rayCallback.m_hitNormalWorld = softBody->m_faces[hitTriangleIndex].m_normal;
12501297
}
1251-
// retrieve the original mesh polygon (in case of quad->tri conversion)
1252-
result.m_polygon = shapeInfo->m_polygonIndexArray[rayCallback.m_hitTriangleIndex];
1253-
// hit triangle in world coordinate, for face normal and UV coordinate
1254-
btVector3 triangle[3];
1255-
bool triangleOK = false;
1256-
if (filterCallback.m_faceUV && (3 * rayCallback.m_hitTriangleIndex) < shapeInfo->m_triFaceUVcoArray.size()) {
1257-
// interpolate the UV coordinate of the hit point
1258-
CcdShapeConstructionInfo::UVco *uvCo = &shapeInfo->m_triFaceUVcoArray[3 * rayCallback.m_hitTriangleIndex];
1259-
// 1. get the 3 coordinate of the triangle in world space
1260-
btVector3 v1, v2, v3;
1261-
if (shape->isSoftBody()) {
1262-
// soft body give points directly in world coordinate
1263-
const btSoftBody *softBody = static_cast<const btSoftBody *>(rayCallback.m_collisionObject);
1264-
v1 = softBody->m_faces[hitTriangleIndex].m_n[0]->m_x;
1265-
v2 = softBody->m_faces[hitTriangleIndex].m_n[1]->m_x;
1266-
v3 = softBody->m_faces[hitTriangleIndex].m_n[2]->m_x;
1267-
}
1268-
else {
1269-
// for rigid body we must apply the world transform
1298+
else {
1299+
if (!triangleOK) {
12701300
triangleOK = GetHitTriangle(shape, shapeInfo, hitTriangleIndex, triangle);
1271-
if (!triangleOK) {
1272-
// if we cannot get the triangle, no use to continue
1273-
goto SKIP_UV_NORMAL;
1274-
}
1275-
v1 = rayCallback.m_collisionObject->getWorldTransform()(triangle[0]);
1276-
v2 = rayCallback.m_collisionObject->getWorldTransform()(triangle[1]);
1277-
v3 = rayCallback.m_collisionObject->getWorldTransform()(triangle[2]);
12781301
}
1279-
// 2. compute barycentric coordinate of the hit point
1280-
btVector3 v = v2 - v1;
1281-
btVector3 w = v3 - v1;
1282-
btVector3 u = v.cross(w);
1283-
btScalar A = u.length();
1284-
1285-
v = v2 - rayCallback.m_hitPointWorld;
1286-
w = v3 - rayCallback.m_hitPointWorld;
1287-
u = v.cross(w);
1288-
btScalar A1 = u.length();
1289-
1290-
v = rayCallback.m_hitPointWorld - v1;
1291-
w = v3 - v1;
1292-
u = v.cross(w);
1293-
btScalar A2 = u.length();
1294-
1295-
btVector3 baryCo;
1296-
baryCo.setX(A1 / A);
1297-
baryCo.setY(A2 / A);
1298-
baryCo.setZ(1.0f - baryCo.getX() - baryCo.getY());
1299-
// 3. compute UV coordinate
1300-
result.m_hitUV[0] = baryCo.getX() * uvCo[0].uv[0] + baryCo.getY() * uvCo[1].uv[0] + baryCo.getZ() * uvCo[2].uv[0];
1301-
result.m_hitUV[1] = baryCo.getX() * uvCo[0].uv[1] + baryCo.getY() * uvCo[1].uv[1] + baryCo.getZ() * uvCo[2].uv[1];
1302-
result.m_hitUVOK = 1;
1303-
}
1304-
1305-
// Bullet returns the normal from "outside".
1306-
// If the user requests the real normal, compute it now
1307-
if (filterCallback.m_faceNormal) {
1308-
if (shape->isSoftBody()) {
1309-
// we can get the real normal directly from the body
1310-
const btSoftBody *softBody = static_cast<const btSoftBody *>(rayCallback.m_collisionObject);
1311-
rayCallback.m_hitNormalWorld = softBody->m_faces[hitTriangleIndex].m_normal;
1312-
}
1313-
else {
1314-
if (!triangleOK) {
1315-
triangleOK = GetHitTriangle(shape, shapeInfo, hitTriangleIndex, triangle);
1316-
}
1317-
if (triangleOK) {
1318-
btVector3 triangleNormal;
1319-
triangleNormal = (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]);
1320-
rayCallback.m_hitNormalWorld = rayCallback.m_collisionObject->getWorldTransform().getBasis() * triangleNormal;
1321-
}
1302+
if (triangleOK) {
1303+
btVector3 triangleNormal;
1304+
triangleNormal = (triangle[1] - triangle[0]).cross(triangle[2] - triangle[0]);
1305+
rayCallback.m_hitNormalWorld = rayCallback.m_collisionObject->getWorldTransform().getBasis() * triangleNormal;
13221306
}
13231307
}
1324-
SKIP_UV_NORMAL:
1325-
;
13261308
}
1309+
SKIP_UV_NORMAL:
1310+
;
13271311
}
13281312
}
13291313
if (rayCallback.m_hitNormalWorld.length2() > (SIMD_EPSILON * SIMD_EPSILON)) {
@@ -1332,9 +1316,8 @@ PHY_IPhysicsController *CcdPhysicsEnvironment::RayTest(PHY_IRayCastFilterCallbac
13321316
else {
13331317
rayCallback.m_hitNormalWorld.setValue(1.0f, 0.0f, 0.0f);
13341318
}
1335-
result.m_hitNormal[0] = rayCallback.m_hitNormalWorld.getX();
1336-
result.m_hitNormal[1] = rayCallback.m_hitNormalWorld.getY();
1337-
result.m_hitNormal[2] = rayCallback.m_hitNormalWorld.getZ();
1319+
1320+
result.m_hitNormal = ToMt(rayCallback.m_hitNormalWorld);
13381321
filterCallback.reportHit(&result);
13391322
}
13401323

@@ -3004,6 +2987,7 @@ void CcdPhysicsEnvironment::ConvertObject(BL_SceneConverter& converter, KX_GameO
30042987
// create the compound shape manually as we already have the child shape
30052988
btCompoundShape *compoundShape = new btCompoundShape();
30062989
compoundShape->addChildShape(shapeInfo->m_childTrans, bm);
2990+
compoundShape->setUserPointer(compoundShapeInfo);
30072991
// now replace the shape
30082992
bm = compoundShape;
30092993
shapeInfo->Release();

0 commit comments

Comments
 (0)