diff --git a/extern/bullet2/src/BulletSoftBody/btSoftBodyHelpers.cpp b/extern/bullet2/src/BulletSoftBody/btSoftBodyHelpers.cpp index 6ab93c16402..4f81b0953a2 100644 --- a/extern/bullet2/src/BulletSoftBody/btSoftBodyHelpers.cpp +++ b/extern/bullet2/src/BulletSoftBody/btSoftBodyHelpers.cpp @@ -828,7 +828,8 @@ btSoftBody* btSoftBodyHelpers::CreateFromTriMesh(btSoftBodyWorldInfo& worldInfo #undef IDX psb->appendFace(idx[0],idx[1],idx[2]); } - psb->randomizeConstraints(); + // don't randomize now, let's give a chance to the application to set face data + //psb->randomizeConstraints(); return(psb); } diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index d18e11d3ca5..e64e0914b87 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -2606,8 +2606,8 @@ static PyObject *none_tuple_4() } KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, - "rayCast(to,from,dist,prop,face,xray,poly): cast a ray and return 3-tuple (object,hit,normal) or 4-tuple (object,hit,normal,polygon) of contact point with object within dist that matches prop.\n" - " If no hit, return (None,None,None) or (None,None,None,None).\n" + "rayCast(to,from,dist,prop,face,xray,poly): cast a ray and return 3-tuple (object,hit,normal) or 4-tuple (object,hit,normal,polygon) or 4-tuple (object,hit,normal,polygon,hituv) of contact point with object within dist that matches prop.\n" + " If no hit, return (None,None,None) or (None,None,None,None) or (None,None,None,None,None).\n" " to = 3-tuple or object reference for destination of ray (if object, use center of object)\n" " from = 3-tuple or object reference for origin of ray (if object, use center of object)\n" " Can be None or omitted => start from self object center\n" @@ -2617,6 +2617,8 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, " xray = X-ray option: 1=>skip objects that don't match prop; 0 or omitted => stop on first object\n" " poly = polygon option: 1=>return value is a 4-tuple and the 4th element is a KX_PolyProxy object\n" " which can be None if hit object has no mesh or if there is no hit\n" +" 2=>return value is a 5-tuple, the 4th element is the KX_PolyProxy object\n" +" and the 5th element is the vector of UV coordinates at the hit point of the None if there is no UV mapping\n" " If 0 or omitted, return value is a 3-tuple\n" "Note: The object on which you call this method matters: the ray will ignore it.\n" " prop and xray option interact as follow:\n" @@ -2697,12 +2699,12 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, m_testPropName.SetLength(0); m_xray = xray; // to get the hit results - KX_RayCast::Callback callback(this,spc,NULL,face); + KX_RayCast::Callback callback(this,spc,NULL,face,(poly==2)); KX_RayCast::RayTest(pe, fromPoint, toPoint, callback); if (m_pHitObject) { - PyObject* returnValue = (poly) ? PyTuple_New(4) : PyTuple_New(3); + PyObject* returnValue = (poly==2) ? PyTuple_New(5) : (poly) ? PyTuple_New(4) : PyTuple_New(3); if (returnValue) { // unlikely this would ever fail, if it does python sets an error PyTuple_SET_ITEM(returnValue, 0, m_pHitObject->GetProxy()); PyTuple_SET_ITEM(returnValue, 1, PyObjectFrom(callback.m_hitPoint)); @@ -2715,11 +2717,25 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, RAS_Polygon* polygon = callback.m_hitMesh->GetPolygon(callback.m_hitPolygon); KX_PolyProxy* polyproxy = new KX_PolyProxy(callback.m_hitMesh, polygon); PyTuple_SET_ITEM(returnValue, 3, polyproxy->NewProxy(true)); + if (poly == 2) + { + if (callback.m_hitUVOK) + PyTuple_SET_ITEM(returnValue, 4, PyObjectFrom(callback.m_hitUV)); + else { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(returnValue, 4, Py_None); + } + } } else { Py_INCREF(Py_None); PyTuple_SET_ITEM(returnValue, 3, Py_None); + if (poly==2) + { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(returnValue, 4, Py_None); + } } } } diff --git a/source/gameengine/Ketsji/KX_RayCast.cpp b/source/gameengine/Ketsji/KX_RayCast.cpp index 8c7612bf663..7f8b7da7289 100644 --- a/source/gameengine/Ketsji/KX_RayCast.cpp +++ b/source/gameengine/Ketsji/KX_RayCast.cpp @@ -40,8 +40,8 @@ #include "PHY_IPhysicsEnvironment.h" #include "PHY_IPhysicsController.h" -KX_RayCast::KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal) - :PHY_IRayCastFilterCallback(dynamic_cast(ignoreController), faceNormal) +KX_RayCast::KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal, bool faceUV) + :PHY_IRayCastFilterCallback(dynamic_cast(ignoreController), faceNormal, faceUV) { } @@ -50,6 +50,8 @@ void KX_RayCast::reportHit(PHY_RayCastResult* result) m_hitFound = true; m_hitPoint.setValue((const float*)result->m_hitPoint); m_hitNormal.setValue((const float*)result->m_hitNormal); + m_hitUVOK = result->m_hitUVOK; + m_hitUV.setValue((const float*)result->m_hitUV); m_hitMesh = result->m_meshObject; m_hitPolygon = result->m_polygon; } diff --git a/source/gameengine/Ketsji/KX_RayCast.h b/source/gameengine/Ketsji/KX_RayCast.h index cdafc894f6c..696a8ca78c0 100644 --- a/source/gameengine/Ketsji/KX_RayCast.h +++ b/source/gameengine/Ketsji/KX_RayCast.h @@ -32,6 +32,7 @@ #include "PHY_IPhysicsEnvironment.h" #include "PHY_IPhysicsController.h" +#include "MT_Vector2.h" #include "MT_Point3.h" #include "MT_Vector3.h" @@ -59,8 +60,10 @@ public: MT_Vector3 m_hitNormal; const RAS_MeshObject* m_hitMesh; int m_hitPolygon; + int m_hitUVOK; // !=0 if UV coordinate in m_hitUV is valid + MT_Vector2 m_hitUV; - KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal); + KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal, bool faceUV); virtual ~KX_RayCast() {} /** @@ -102,8 +105,8 @@ template class KX_RayCast::Callback : public KX_RayCast T *self; void *data; public: - Callback(T *_self, KX_IPhysicsController* controller=NULL, void *_data = NULL, bool faceNormal=false) - : KX_RayCast(controller, faceNormal), + Callback(T *_self, KX_IPhysicsController* controller=NULL, void *_data = NULL, bool faceNormal=false, bool faceUV=false) + : KX_RayCast(controller, faceNormal, faceUV), self(_self), data(_data) { 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()) { - 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 - result.m_polygon = shapeInfo->m_polygonIndexArray.at(rayCallback.m_hitTriangleIndex); + // save original collision shape triangle for soft body + int hitTriangleIndex = rayCallback.m_hitTriangleIndex; + result.m_meshObject = shapeInfo->GetMesh(); + 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) { } diff --git a/source/gameengine/PyDoc/GameTypes.py b/source/gameengine/PyDoc/GameTypes.py index 3c0292e9e8c..27b4685ca14 100644 --- a/source/gameengine/PyDoc/GameTypes.py +++ b/source/gameengine/PyDoc/GameTypes.py @@ -1958,6 +1958,7 @@ class KX_GameObject(SCA_IObject): Look from a point/object to another point/object and find first object hit within dist that matches prop. if poly is 0, returns a 3-tuple with object reference, hit point and hit normal or (None,None,None) if no hit. if poly is 1, returns a 4-tuple with in addition a L{KX_PolyProxy} as 4th element. + if poly is 2, returns a 5-tuple with in addition a 2D vector with the UV mapping of the hit point as 5th element. Ex:: # shoot along the axis gun-gunAim (gunAim should be collision-free) @@ -1996,13 +1997,18 @@ class KX_GameObject(SCA_IObject): @type face: int @param xray: X-ray option: 1=>skip objects that don't match prop; 0 or omitted => stop on first object @type xray: int - @param poly: polygon option: 1=>return value is a 4-tuple and the 4th element is a L{KX_PolyProxy} + @param poly: polygon option: 0,1 or 2 to return a 3-, 4- or 5-tuple with information on the face hit + 0 or omitted=> return value is a 3-tuple (object, hitpoint, hitnormal) or (None,None,None) if no hit + 1=>return value is a 4-tuple and the 4th element is a L{KX_PolyProxy} or None if no hit or the object doesn't use a mesh collision shape. + 2=>return value is a 5-tuple and the 5th element is a 2-tuple (u,v) with the UV mapping of the hit point or None if no hit, or the object doesn't use a mesh collision shape, or doesn't have a UV mapping. @type poly: int @rtype: 3-tuple (L{KX_GameObject}, 3-tuple (x,y,z), 3-tuple (nx,ny,nz)) or 4-tuple (L{KX_GameObject}, 3-tuple (x,y,z), 3-tuple (nx,ny,nz), L{KX_PolyProxy}) - @return: (object,hitpoint,hitnormal) or (object,hitpoint,hitnormal,polygon) - If no hit, returns (None,None,None) or (None,None,None,None) - If the object hit is not a static mesh, polygon is None + or 5-tuple (L{KX_GameObject}, 3-tuple (x,y,z), 3-tuple (nx,ny,nz), L{KX_PolyProxy}, 2-tuple (u,v)) + @return: (object,hitpoint,hitnormal) or (object,hitpoint,hitnormal,polygon) or (object,hitpoint,hitnormal,polygon,hituv) + object, hitpoint and hitnormal are None if no hit. + polygon is valid only if the object is valid and is a static object, a dynamic object using mesh collision shape or a soft body object, otherwise it is None + hituv is valid only if polygon is valid and the object has a UV mapping, otherwise it is None """ def setCollisionMargin(margin): """