BGE: Add option to return UV coordinates aofthe hit point to KX_GameObject::rayCast...
authorBenoit Bolsee <benoit.bolsee@online.be>
Fri, 4 Dec 2009 11:27:40 +0000 (11:27 +0000)
committerBenoit Bolsee <benoit.bolsee@online.be>
Fri, 4 Dec 2009 11:27:40 +0000 (11:27 +0000)
extern/bullet2/src/BulletSoftBody/btSoftBodyHelpers.cpp
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_RayCast.cpp
source/gameengine/Ketsji/KX_RayCast.h
source/gameengine/Physics/Bullet/CcdPhysicsController.cpp
source/gameengine/Physics/Bullet/CcdPhysicsController.h
source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
source/gameengine/Physics/common/PHY_DynamicTypes.h
source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h
source/gameengine/PyDoc/GameTypes.py

index 6ab93c1640279853e6fcd06916afbe2db2639af7..4f81b0953a2c6b21ef6bf091d602ff2a812257d6 100644 (file)
@@ -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);
 }
 
index d18e11d3ca5c402bd75d151b1b6dafcabda0a9a9..e64e0914b87cf000e14e4f269e065312f3c24fc2 100644 (file)
@@ -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<KX_GameObject> callback(this,spc,NULL,face);
+       KX_RayCast::Callback<KX_GameObject> 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);
+                                       }
                                }
                        }
                }
index 8c7612bf6630c8c7f8ff5dcf39a707cfedfe40d6..7f8b7da7289a701552c076de10c969d018c6103b 100644 (file)
@@ -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<PHY_IPhysicsController*>(ignoreController), faceNormal) 
+KX_RayCast::KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal, bool faceUV)
+       :PHY_IRayCastFilterCallback(dynamic_cast<PHY_IPhysicsController*>(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;
 }
index cdafc894f6c380e1529fba29cdd3e3ef5da170cb..696a8ca78c0ab07f3d32ae751dedc3c10af86017 100644 (file)
@@ -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 T> 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)
        {
index 98e67afdd44c492d26fdbd39e204c6bd2454d543..767854e572564a30e37403dc0c826a6d522cc46c 100644 (file)
@@ -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]; i<numtris; ++i, ++ft)
+               {
+                       // Hack!! use m_tag to store the face number, normally it is a pointer
+                       // add 1 to make sure it is never 0
+                       ft->m_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; p2<numpolys; p2++)
                {
                        MFace* mf = &mface[p2];
+                       MTFace* tf = (tface) ? &tface[p2] : NULL;
                        RAS_Polygon* poly= meshobj->GetPolygon(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];
                        }
index dcb0af62cf990abdd7102350f13e9bfd6f5e773e..637007e285717b17501fdf6888758a57473a8fc2 100644 (file)
@@ -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<int>                m_triFaceArray; // Contains an array of triplets of face indicies
                                                                                        // quads turn into 2 tris
 
+       std::vector<UVco>               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;
index 3d1fa6fc1801c62501a778cfb52453d04d43532a..477a2c35d4faac654b9cf68ab63e0e5dfc35da93 100644 (file)
@@ -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<btGImpactMeshShape*>(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<btSoftBody*>(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<btSoftBody*>(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<btSoftBody*>(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:
+                                       ;
                                }
                        }
                }
index 7ce40001af75dca5c756982e375581cafecb1d89..08d94a2850a1766ee3ad9a0ed73c68fa8ddbc854 100644 (file)
@@ -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];
index 291dac298dc733ef9729dd428c139a525c48fe75..86e72c70bb717c48e7991e50c3806fb127bd98d1 100644 (file)
@@ -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)
        {
        }
        
index 3c0292e9e8cb8c2172cc0f4313b0929cd11b412b..27b4685ca149b956c3f515c4d5fe59c537f2c0a4 100644 (file)
@@ -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):
                """