From 0b6873a776552bd47c305ee1bc4e629a76da0037 Mon Sep 17 00:00:00 2001 From: Benoit Bolsee Date: Fri, 4 Dec 2009 11:27:40 +0000 Subject: BGE: Add option to return UV coordinates aofthe hit point to KX_GameObject::rayCast(). Details in PyDoc. --- .../Physics/Bullet/CcdPhysicsController.cpp | 55 ++++++- .../Physics/Bullet/CcdPhysicsController.h | 11 +- .../Physics/Bullet/CcdPhysicsEnvironment.cpp | 171 +++++++++++++++------ .../gameengine/Physics/common/PHY_DynamicTypes.h | 14 ++ .../Physics/common/PHY_IPhysicsEnvironment.h | 8 +- 5 files changed, 207 insertions(+), 52 deletions(-) (limited to 'source/gameengine/Physics') diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp index 98e67afdd44..767854e5725 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp @@ -214,6 +214,7 @@ bool CcdPhysicsController::CreateSoftbody() } } else { + int numtris = 0; if (m_cci.m_collisionShape->getShapeType() ==SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE) { btScaledBvhTriangleMeshShape* scaledtrimeshshape = (btScaledBvhTriangleMeshShape*) m_cci.m_collisionShape; @@ -228,7 +229,6 @@ bool CcdPhysicsController::CreateSoftbody() int vertexstride; unsigned char* indexbase; int indexstride; - int numtris; PHY_ScalarType indexType; trimeshshape->getMeshInterface()->getLockedVertexIndexBase(&vertexBase,numverts,vertexType,vertexstride,&indexbase,indexstride,numtris,indexType); @@ -246,14 +246,21 @@ bool CcdPhysicsController::CreateSoftbody() int vertexstride; unsigned char* indexbase; int indexstride; - int numtris; PHY_ScalarType indexType; trimeshshape->getMeshInterface()->getLockedVertexIndexBase(&vertexBase,numverts,vertexType,vertexstride,&indexbase,indexstride,numtris,indexType); psb = btSoftBodyHelpers::CreateFromTriMesh(worldInfo,(const btScalar*)vertexBase,(const int*)indexbase,numtris); } } - + // store face tag so that we can find our original face when doing ray casting + btSoftBody::Face* ft; + int i; + for (i=0, ft=&psb->m_faces[0]; im_tag = (void*)((uintptr_t)(i+1)); + } } if (m_cci.m_margin > 0.f) { @@ -1402,6 +1409,7 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, DerivedMesh* dm, m_vertexArray.clear(); m_polygonIndexArray.clear(); m_triFaceArray.clear(); + m_triFaceUVcoArray.clear(); return false; } @@ -1415,6 +1423,7 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, DerivedMesh* dm, numpolys = dm->getNumFaces(dm); numverts = dm->getNumVerts(dm); int* index = (int*)dm->getFaceDataArray(dm, CD_ORIGINDEX); + MTFace *tface = (MTFace *)dm->getFaceDataArray(dm, CD_MTFACE); m_shapeType = (polytope) ? PHY_SHAPE_POLYTOPE : PHY_SHAPE_MESH; @@ -1515,14 +1524,23 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, DerivedMesh* dm, m_vertexArray.resize(tot_bt_verts*3); m_polygonIndexArray.resize(tot_bt_tris); m_triFaceArray.resize(tot_bt_tris*3); - btScalar *bt= &m_vertexArray[0]; int *poly_index_pt= &m_polygonIndexArray[0]; int *tri_pt= &m_triFaceArray[0]; + UVco *uv_pt = NULL; + if (tface) + { + m_triFaceUVcoArray.resize(tot_bt_tris*3); + uv_pt = &m_triFaceUVcoArray[0]; + } + else + m_triFaceUVcoArray.clear(); + for (int p2=0; p2GetPolygon(index[p2]); // only add polygons that have the collisionflag set @@ -1537,6 +1555,16 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, DerivedMesh* dm, tri_pt[1]= vert_remap_array[mf->v2]; tri_pt[2]= vert_remap_array[mf->v3]; tri_pt= tri_pt+3; + if (tf) + { + uv_pt[0].uv[0] = tf->uv[0][0]; + uv_pt[0].uv[1] = tf->uv[0][1]; + uv_pt[1].uv[0] = tf->uv[1][0]; + uv_pt[1].uv[1] = tf->uv[1][1]; + uv_pt[2].uv[0] = tf->uv[2][0]; + uv_pt[2].uv[1] = tf->uv[2][1]; + uv_pt += 3; + } // m_polygonIndexArray *poly_index_pt= index[p2]; @@ -1570,6 +1598,16 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, DerivedMesh* dm, tri_pt[1]= vert_remap_array[mf->v3]; tri_pt[2]= vert_remap_array[mf->v4]; tri_pt= tri_pt+3; + if (tf) + { + uv_pt[0].uv[0] = tf->uv[0][0]; + uv_pt[0].uv[1] = tf->uv[0][1]; + uv_pt[1].uv[0] = tf->uv[2][0]; + uv_pt[1].uv[1] = tf->uv[2][1]; + uv_pt[2].uv[0] = tf->uv[3][0]; + uv_pt[2].uv[1] = tf->uv[3][1]; + uv_pt += 3; + } // m_polygonIndexArray *poly_index_pt= index[p2]; @@ -1728,10 +1766,12 @@ bool CcdShapeConstructionInfo::UpdateMesh(class KX_GameObject* gameobj, class RA m_triFaceArray.resize(tot_bt_tris*3); int *tri_pt= &m_triFaceArray[0]; + m_triFaceUVcoArray.resize(tot_bt_tris*3); + UVco *uv_pt= &m_triFaceUVcoArray[0]; + m_polygonIndexArray.resize(tot_bt_tris); int *poly_index_pt= &m_polygonIndexArray[0]; - for(mf= mface, tf= tface, i=0; i < numpolys; mf++, tf++, i++) { if(tf->mode & TF_DYNAMIC) @@ -1760,6 +1800,9 @@ bool CcdShapeConstructionInfo::UpdateMesh(class KX_GameObject* gameobj, class RA vert_tag_array[v_orig]= false; } *tri_pt++ = vert_remap_array[v_orig]; + uv_pt->uv[0] = tf->uv[*fv_pt][0]; + uv_pt->uv[1] = tf->uv[*fv_pt][1]; + uv_pt++; } } } @@ -1782,6 +1825,8 @@ bool CcdShapeConstructionInfo::UpdateMesh(class KX_GameObject* gameobj, class RA m_polygonIndexArray.resize(tot_bt_tris); int *poly_index_pt= &m_polygonIndexArray[0]; + m_triFaceUVcoArray.clear(); + for(mv= mvert, i=0; i < numverts; mv++, i++) { *bt++ = mv->co[0]; *bt++ = mv->co[1]; *bt++ = mv->co[2]; } diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.h b/source/gameengine/Physics/Bullet/CcdPhysicsController.h index dcb0af62cf9..637007e2857 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.h @@ -52,12 +52,16 @@ class btCollisionShape; #define CCD_BSB_COL_VF_SS 16 /* Vertex/Face based soft vs soft */ - // Shape contructor // It contains all the information needed to create a simple bullet shape at runtime class CcdShapeConstructionInfo { public: + struct UVco + { + float uv[2]; + }; + static CcdShapeConstructionInfo* FindMesh(class RAS_MeshObject* mesh, struct DerivedMesh* dm, bool polytope, bool gimpact); CcdShapeConstructionInfo() : @@ -103,7 +107,7 @@ public: btTriangleMeshShape* GetMeshShape(void) { - return m_unscaledShape; + return (m_unscaledShape); } CcdShapeConstructionInfo* GetChildShape(int i) { @@ -174,6 +178,9 @@ public: std::vector m_triFaceArray; // Contains an array of triplets of face indicies // quads turn into 2 tris + std::vector m_triFaceUVcoArray; // Contains an array of pair of UV coordinate for each vertex of faces + // quads turn into 2 tris + void setVertexWeldingThreshold1(float threshold) { m_weldingThreshold1 = threshold*threshold; diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index 3d1fa6fc180..477a2c35d4f 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -974,6 +974,7 @@ struct FilterClosestRayResultCallback : public btCollisionWorld::ClosestRayResul const btCollisionShape* m_hitTriangleShape; int m_hitTriangleIndex; + FilterClosestRayResultCallback (PHY_IRayCastFilterCallback& phyRayFilter,const btVector3& rayFrom,const btVector3& rayTo) : btCollisionWorld::ClosestRayResultCallback(rayFrom,rayTo), m_phyRayFilter(phyRayFilter), @@ -1017,6 +1018,56 @@ struct FilterClosestRayResultCallback : public btCollisionWorld::ClosestRayResul }; +static bool GetHitTriangle(btCollisionShape* shape, CcdShapeConstructionInfo* shapeInfo, int hitTriangleIndex, btVector3 triangle[]) +{ + // this code is copied from Bullet + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + btStridingMeshInterface* meshInterface = NULL; + btTriangleMeshShape* triangleShape = shapeInfo->GetMeshShape(); + + if (triangleShape) + meshInterface = triangleShape->getMeshInterface(); + else + { + // other possibility is gImpact + if (shape->getShapeType() == GIMPACT_SHAPE_PROXYTYPE) + meshInterface = (static_cast(shape))->getMeshInterface(); + } + if (!meshInterface) + return false; + + meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + 0); + + unsigned int* gfxbase = (unsigned int*)(indexbase+hitTriangleIndex*indexstride); + const btVector3& meshScaling = shape->getLocalScaling(); + for (int j=2;j>=0;j--) + { + int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; + + btScalar* graphicsbase = (btScalar*)(vertexbase+graphicsindex*stride); + + triangle[j] = btVector3(graphicsbase[0]*meshScaling.getX(),graphicsbase[1]*meshScaling.getY(),graphicsbase[2]*meshScaling.getZ()); + } + meshInterface->unLockReadOnlyVertexBase(0); + return true; +} + PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallback &filterCallback, float fromX,float fromY,float fromZ, float toX,float toY,float toZ) { btVector3 rayFrom(fromX,fromY,fromZ); @@ -1069,64 +1120,98 @@ PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallbac if (shape == rayCallback.m_hitTriangleShape && rayCallback.m_hitTriangleIndex < shapeInfo->m_polygonIndexArray.size()) { + // save original collision shape triangle for soft body + int hitTriangleIndex = rayCallback.m_hitTriangleIndex; + result.m_meshObject = shapeInfo->GetMesh(); - // note for softbody: this assumes that the softbody shape uses the same triangle numbering - // than the triangle mesh shape that was used to build it + if (shape->isSoftBody()) + { + // soft body using different face numbering because of randomization + // hopefully we have stored the original face number in m_tag + btSoftBody* softBody = static_cast(rayCallback.m_collisionObject); + if (softBody->m_faces[hitTriangleIndex].m_tag != 0) + { + rayCallback.m_hitTriangleIndex = (int)((uintptr_t)(softBody->m_faces[hitTriangleIndex].m_tag)-1); + } + } + // retrieve the original mesh polygon (in case of quad->tri conversion) result.m_polygon = shapeInfo->m_polygonIndexArray.at(rayCallback.m_hitTriangleIndex); - + // hit triangle in world coordinate, for face normal and UV coordinate + btVector3 triangle[3]; + bool triangleOK = false; + if (filterCallback.m_faceUV && (3*rayCallback.m_hitTriangleIndex) < shapeInfo->m_triFaceUVcoArray.size()) + { + // interpolate the UV coordinate of the hit point + CcdShapeConstructionInfo::UVco* uvCo = &shapeInfo->m_triFaceUVcoArray[3*rayCallback.m_hitTriangleIndex]; + // 1. get the 3 coordinate of the triangle in world space + btVector3 v1, v2, v3; + if (shape->isSoftBody()) + { + // soft body give points directly in world coordinate + btSoftBody* softBody = static_cast(rayCallback.m_collisionObject); + v1 = softBody->m_faces[hitTriangleIndex].m_n[0]->m_x; + v2 = softBody->m_faces[hitTriangleIndex].m_n[1]->m_x; + v3 = softBody->m_faces[hitTriangleIndex].m_n[2]->m_x; + } else + { + // for rigid body we must apply the world transform + triangleOK = GetHitTriangle(shape, shapeInfo, hitTriangleIndex, triangle); + if (!triangleOK) + // if we cannot get the triangle, no use to continue + goto SKIP_UV_NORMAL; + v1 = rayCallback.m_collisionObject->getWorldTransform()(triangle[0]); + v2 = rayCallback.m_collisionObject->getWorldTransform()(triangle[1]); + v3 = rayCallback.m_collisionObject->getWorldTransform()(triangle[2]); + } + // 2. compute barycentric coordinate of the hit point + btVector3 v = v2-v1; + btVector3 w = v3-v1; + btVector3 u = v.cross(w); + btScalar A = u.length(); + + v = v2-rayCallback.m_hitPointWorld; + w = v3-rayCallback.m_hitPointWorld; + u = v.cross(w); + btScalar A1 = u.length(); + + v = rayCallback.m_hitPointWorld-v1; + w = v3-v1; + u = v.cross(w); + btScalar A2 = u.length(); + + btVector3 baryCo; + baryCo.setX(A1/A); + baryCo.setY(A2/A); + baryCo.setZ(1.0f-baryCo.getX()-baryCo.getY()); + // 3. compute UV coordinate + result.m_hitUV[0] = baryCo.getX()*uvCo[0].uv[0] + baryCo.getY()*uvCo[1].uv[0] + baryCo.getZ()*uvCo[2].uv[0]; + result.m_hitUV[1] = baryCo.getX()*uvCo[0].uv[1] + baryCo.getY()*uvCo[1].uv[1] + baryCo.getZ()*uvCo[2].uv[1]; + result.m_hitUVOK = 1; + } + // Bullet returns the normal from "outside". // If the user requests the real normal, compute it now if (filterCallback.m_faceNormal) { - // mesh shapes are shared and stored in the shapeInfo - btTriangleMeshShape* triangleShape = shapeInfo->GetMeshShape(); - if (shape->isSoftBody()) { // we can get the real normal directly from the body btSoftBody* softBody = static_cast(rayCallback.m_collisionObject); - rayCallback.m_hitNormalWorld = softBody->m_faces[rayCallback.m_hitTriangleIndex].m_normal; - } else if (triangleShape) + rayCallback.m_hitNormalWorld = softBody->m_faces[hitTriangleIndex].m_normal; + } else { - // this code is copied from Bullet - btVector3 triangle[3]; - const unsigned char *vertexbase; - int numverts; - PHY_ScalarType type; - int stride; - const unsigned char *indexbase; - int indexstride; - int numfaces; - PHY_ScalarType indicestype; - btStridingMeshInterface* meshInterface = triangleShape->getMeshInterface(); - - meshInterface->getLockedReadOnlyVertexIndexBase( - &vertexbase, - numverts, - type, - stride, - &indexbase, - indexstride, - numfaces, - indicestype, - 0); - - unsigned int* gfxbase = (unsigned int*)(indexbase+rayCallback.m_hitTriangleIndex*indexstride); - const btVector3& meshScaling = shape->getLocalScaling(); - for (int j=2;j>=0;j--) + if (!triangleOK) + triangleOK = GetHitTriangle(shape, shapeInfo, hitTriangleIndex, triangle); + if (triangleOK) { - int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; - - btScalar* graphicsbase = (btScalar*)(vertexbase+graphicsindex*stride); - - triangle[j] = btVector3(graphicsbase[0]*meshScaling.getX(),graphicsbase[1]*meshScaling.getY(),graphicsbase[2]*meshScaling.getZ()); + btVector3 triangleNormal; + triangleNormal = (triangle[1]-triangle[0]).cross(triangle[2]-triangle[0]); + rayCallback.m_hitNormalWorld = rayCallback.m_collisionObject->getWorldTransform().getBasis()*triangleNormal; } - meshInterface->unLockReadOnlyVertexBase(0); - btVector3 triangleNormal; - triangleNormal = (triangle[1]-triangle[0]).cross(triangle[2]-triangle[0]); - rayCallback.m_hitNormalWorld = rayCallback.m_collisionObject->getWorldTransform().getBasis()*triangleNormal; } } + SKIP_UV_NORMAL: + ; } } } diff --git a/source/gameengine/Physics/common/PHY_DynamicTypes.h b/source/gameengine/Physics/common/PHY_DynamicTypes.h index 7ce40001af7..08d94a2850a 100644 --- a/source/gameengine/Physics/common/PHY_DynamicTypes.h +++ b/source/gameengine/Physics/common/PHY_DynamicTypes.h @@ -22,6 +22,20 @@ subject to the following restrictions: struct KX_ClientObjectInfo; class PHY_Shape; +struct PHY__Vector2 +{ + float m_vec[2]; + + operator const float* () const + { + return &m_vec[0]; + } + operator float* () + { + return &m_vec[0]; + } +}; + struct PHY__Vector3 { float m_vec[4]; diff --git a/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h b/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h index 291dac298dc..86e72c70bb7 100644 --- a/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h +++ b/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h @@ -52,6 +52,8 @@ struct PHY_RayCastResult const RAS_MeshObject* m_meshObject; // !=NULL for mesh object (only for Bullet controllers) int m_polygon; // index of the polygon hit by the ray, // only if m_meshObject != NULL + int m_hitUVOK; // !=0 if UV coordinate in m_hitUV is valid + PHY__Vector2 m_hitUV; // UV coordinates of hit point }; /** @@ -64,6 +66,7 @@ class PHY_IRayCastFilterCallback public: PHY_IPhysicsController* m_ignoreController; bool m_faceNormal; + bool m_faceUV; virtual ~PHY_IRayCastFilterCallback() { @@ -76,9 +79,10 @@ public: virtual void reportHit(PHY_RayCastResult* result) = 0; - PHY_IRayCastFilterCallback(PHY_IPhysicsController* ignoreController, bool faceNormal=false) + PHY_IRayCastFilterCallback(PHY_IPhysicsController* ignoreController, bool faceNormal=false, bool faceUV=false) :m_ignoreController(ignoreController), - m_faceNormal(faceNormal) + m_faceNormal(faceNormal), + m_faceUV(faceUV) { } -- cgit v1.2.3