Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Bolsee <benoit.bolsee@online.be>2009-12-04 14:27:40 +0300
committerBenoit Bolsee <benoit.bolsee@online.be>2009-12-04 14:27:40 +0300
commit0b6873a776552bd47c305ee1bc4e629a76da0037 (patch)
treea64b73ec3bba5adfc5e6cf23722fd93fcdec8a4e
parent1dcca75e047a54630c9c005c994fa3b21d6863cd (diff)
BGE: Add option to return UV coordinates aofthe hit point to KX_GameObject::rayCast(). Details in PyDoc.
-rw-r--r--extern/bullet2/src/BulletSoftBody/btSoftBodyHelpers.cpp3
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp24
-rw-r--r--source/gameengine/Ketsji/KX_RayCast.cpp6
-rw-r--r--source/gameengine/Ketsji/KX_RayCast.h9
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsController.cpp55
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsController.h11
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp171
-rw-r--r--source/gameengine/Physics/common/PHY_DynamicTypes.h14
-rw-r--r--source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h8
-rw-r--r--source/gameengine/PyDoc/GameTypes.py14
10 files changed, 249 insertions, 66 deletions
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<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);
+ }
}
}
}
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<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;
}
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 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)
{
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]; 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];
}
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<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;
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<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:
+ ;
}
}
}
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):
"""