BGE patch: KX_GameObject::rayCast() improvements to have X-Ray option, return true...
authorBenoit Bolsee <benoit.bolsee@online.be>
Wed, 27 Aug 2008 19:34:19 +0000 (19:34 +0000)
committerBenoit Bolsee <benoit.bolsee@online.be>
Wed, 27 Aug 2008 19:34:19 +0000 (19:34 +0000)
rayCast(to,from,dist,prop,face,xray,poly):

The face paremeter determines the orientation of the normal:
  0 or omitted => hit normal is always oriented towards the ray origin (as if you casted the ray from outside)
  1 => hit normal is the real face normal (only for mesh object, otherwise face has no effect)
The ray has X-Ray capability if xray parameter is 1, otherwise the first object hit (other than self object) stops the ray.
The prop and xray parameters interact as follow:
    prop off, xray off: return closest hit or no hit if there is no object on the full extend of the ray.
    prop off, xray on : idem.
    prop on,  xray off: return closest hit if it matches prop, no hit otherwise.
    prop on,  xray on : return closest hit matching prop or no hit if there is no object matching prop on the full extend of the ray.
if poly is 0 or omitted, 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 KX_PolyProxy as 4th element.

The KX_PolyProxy object holds information on the polygon hit by the ray: the index of the vertex forming the poylgon, material, etc.

Attributes (read-only):
 matname: The name of polygon material, empty if no material.
 material: The material of the polygon
 texture: The texture name of the polygon.
 matid: The material index of the polygon, use this to retrieve vertex proxy from mesh proxy
 v1: vertex index of the first vertex of the polygon, use this to retrieve vertex proxy from mesh proxy
 v2: vertex index of the second vertex of the polygon, use this to retrieve vertex proxy from mesh proxy
 v3: vertex index of the third vertex of the polygon, use this to retrieve vertex proxy from mesh proxy
 v4: vertex index of the fourth vertex of the polygon, 0 if polygon has only 3 vertex
     use this to retrieve vertex proxy from mesh proxy
 visible: visible state of the polygon: 1=visible, 0=invisible
 collide: collide state of the polygon: 1=receives collision, 0=collision free.
Methods:
 getMaterialName(): Returns the polygon material name with MA prefix
 getMaterial(): Returns the polygon material
 getTextureName(): Returns the polygon texture name
 getMaterialIndex(): Returns the material bucket index of the polygon.
 getNumVertex(): Returns the number of vertex of the polygon.
 isVisible(): Returns whether the polygon is visible or not
 isCollider(): Returns whether the polygon is receives collision or not
 getVertexIndex(vertex): Returns the mesh vertex index of a polygon vertex
 getMesh(): Returns a mesh proxy

New methods of KX_MeshProxy have been implemented to retrieve KX_PolyProxy objects:
 getNumPolygons(): Returns the number of polygon in the mesh.
 getPolygon(index): Gets the specified polygon from the mesh.

More details in PyDoc.

36 files changed:
projectfiles_vc7/gameengine/ketsji/KX_ketsji.vcproj
source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp
source/gameengine/BlenderRoutines/KX_BlenderRenderTools.h
source/gameengine/Expressions/PyObjectPlus.h
source/gameengine/GamePlayer/common/GPC_RenderTools.cpp
source/gameengine/GamePlayer/common/GPC_RenderTools.h
source/gameengine/Ketsji/KX_ConstraintActuator.cpp
source/gameengine/Ketsji/KX_ConstraintActuator.h
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_GameObject.h
source/gameengine/Ketsji/KX_MeshProxy.cpp
source/gameengine/Ketsji/KX_MeshProxy.h
source/gameengine/Ketsji/KX_MouseFocusSensor.cpp
source/gameengine/Ketsji/KX_MouseFocusSensor.h
source/gameengine/Ketsji/KX_PolyProxy.cpp [new file with mode: 0644]
source/gameengine/Ketsji/KX_PolyProxy.h [new file with mode: 0644]
source/gameengine/Ketsji/KX_RayCast.cpp
source/gameengine/Ketsji/KX_RayCast.h
source/gameengine/Ketsji/KX_RaySensor.cpp
source/gameengine/Ketsji/KX_RaySensor.h
source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp
source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h
source/gameengine/Physics/Bullet/CcdPhysicsController.cpp
source/gameengine/Physics/Bullet/CcdPhysicsController.h
source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp
source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h
source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.cpp
source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.h
source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.cpp
source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.h
source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h
source/gameengine/PyDoc/KX_GameObject.py
source/gameengine/PyDoc/KX_MeshProxy.py
source/gameengine/PyDoc/KX_PolyProxy.py [new file with mode: 0644]
source/gameengine/Rasterizer/RAS_MeshObject.cpp
source/gameengine/Rasterizer/RAS_MeshObject.h

index c046d43..4e362fa 100644 (file)
                        <File
                                RelativePath="..\..\..\source\gameengine\Ketsji\KX_PolygonMaterial.cpp">
                        </File>
+                       <File
+                               RelativePath="..\..\..\source\gameengine\Ketsji\KX_PolyProxy.cpp">
+                       </File>
                        <File
                                RelativePath="..\..\..\source\gameengine\Ketsji\KX_PyConstraintBinding.cpp">
                        </File>
                        <File
                                RelativePath="..\..\..\source\gameengine\Ketsji\KX_PolygonMaterial.h">
                        </File>
+                       <File
+                               RelativePath="..\..\..\source\gameengine\Ketsji\KX_PolyProxy.h">
+                       </File>
                        <File
                                RelativePath="..\..\..\source\gameengine\Ketsji\KX_PyConstraintBinding.h">
                        </File>
index e4eff16..f4a1f3a 100644 (file)
@@ -131,11 +131,11 @@ void KX_BlenderRenderTools::SetClientObject(void* obj)
        }
 }
 
-bool KX_BlenderRenderTools::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
+bool KX_BlenderRenderTools::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
 {
        double* const oglmatrix = (double* const) data;
-       MT_Point3 resultpoint(hit_point);
-       MT_Vector3 resultnormal(hit_normal);
+       MT_Point3 resultpoint(result->m_hitPoint);
+       MT_Vector3 resultnormal(result->m_hitNormal);
        MT_Vector3 left(oglmatrix[0],oglmatrix[1],oglmatrix[2]);
        MT_Vector3 dir = -(left.cross(resultnormal)).safe_normalized();
        left = (dir.cross(resultnormal)).safe_normalized();
@@ -236,9 +236,7 @@ void KX_BlenderRenderTools::applyTransform(RAS_IRasterizer* rasty,double* oglmat
                        if (parent)
                                parent->Release();
                                
-                       MT_Point3 resultpoint;
-                       MT_Vector3 resultnormal;
-                       if (!KX_RayCast::RayTest(physics_controller, physics_environment, frompoint, topoint, resultpoint, resultnormal, KX_RayCast::Callback<KX_BlenderRenderTools>(this, oglmatrix)))
+                       if (!KX_RayCast::RayTest(physics_environment, frompoint, topoint, KX_RayCast::Callback<KX_BlenderRenderTools>(this, physics_controller, oglmatrix)))
                        {
                                // couldn't find something to cast the shadow on...
                                glMultMatrixd(oglmatrix);
index 8abce1b..8027136 100644 (file)
@@ -37,6 +37,7 @@
 #include "RAS_IRenderTools.h"
 
 struct KX_ClientObjectInfo;
+class KX_RayCast;
 
 /**
 BlenderRenderTools are a set of tools to apply 2D/3D graphics effects, which are not
@@ -97,7 +98,8 @@ public:
                                                                        void* clientobject,
                                                                        void* tface);
        
-       bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data);
+       bool RayHit(KX_ClientObjectInfo* client, class KX_RayCast* result, void * const data);
+       bool NeedRayCast(KX_ClientObjectInfo*) { return true; }
 
        virtual void MotionBlur(RAS_IRasterizer* rasterizer);
 
index deb4732..65cd4e8 100644 (file)
@@ -156,6 +156,9 @@ static inline void Py_Fatal(char *M) {
 #define KX_PYMETHODTABLE(class_name, method_name) \
        {#method_name , (PyCFunction) class_name::sPy##method_name, METH_VARARGS, class_name::method_name##_doc}
 
+#define KX_PYMETHODTABLE_NOARG(class_name, method_name) \
+       {#method_name , (PyCFunction) class_name::sPy##method_name, METH_NOARGS, class_name::method_name##_doc}
+
 /**
  * Function implementation macro
  */
@@ -163,6 +166,9 @@ static inline void Py_Fatal(char *M) {
 char class_name::method_name##_doc[] = doc_string; \
 PyObject* class_name::Py##method_name(PyObject*, PyObject* args, PyObject*)
 
+#define KX_PYMETHODDEF_DOC_NOARG(class_name, method_name, doc_string) \
+char class_name::method_name##_doc[] = doc_string; \
+PyObject* class_name::Py##method_name(PyObject*)
 
 /*------------------------------
  * PyObjectPlus
index 8b82839..246466f 100644 (file)
@@ -470,11 +470,11 @@ void GPC_RenderTools::SetClientObject(void* obj)
        }
 }
 
-bool GPC_RenderTools::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
+bool GPC_RenderTools::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
 {
        double* const oglmatrix = (double* const) data;
-       MT_Point3 resultpoint(hit_point);
-       MT_Vector3 resultnormal(hit_normal);
+       MT_Point3 resultpoint(result->m_hitPoint);
+       MT_Vector3 resultnormal(result->m_hitNormal);
        MT_Vector3 left(oglmatrix[0],oglmatrix[1],oglmatrix[2]);
        MT_Vector3 dir = -(left.cross(resultnormal)).safe_normalized();
        left = (dir.cross(resultnormal)).safe_normalized();
@@ -563,9 +563,7 @@ void GPC_RenderTools::applyTransform(RAS_IRasterizer* rasty,double* oglmatrix,in
                        if (parent)
                                parent->Release();
                                
-                       MT_Point3 resultpoint;
-                       MT_Vector3 resultnormal;
-                       if (!KX_RayCast::RayTest(physics_controller, physics_environment, frompoint, topoint, resultpoint, resultnormal, KX_RayCast::Callback<GPC_RenderTools>(this, oglmatrix)))
+                       if (!KX_RayCast::RayTest(physics_environment, frompoint, topoint, KX_RayCast::Callback<GPC_RenderTools>(this, physics_controller, oglmatrix)))
                        {
                                // couldn't find something to cast the shadow on...
                                glMultMatrixd(oglmatrix);
index 8fae3d2..9f70f67 100644 (file)
@@ -41,7 +41,7 @@
 #include "BMF_Api.h"
 
 struct KX_ClientObjectInfo;
-
+class KX_RayCast;
 
 class GPC_RenderTools : public RAS_IRenderTools
 {
@@ -138,7 +138,8 @@ public:
 
        int applyLights(int objectlayer);
 
-       bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data);
+       bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data);
+       bool NeedRayCast(KX_ClientObjectInfo* client) { return true; }
 
        virtual void MotionBlur(RAS_IRasterizer* rasterizer);
 
index e00ec68..4b57b0e 100644 (file)
@@ -109,16 +109,11 @@ KX_ConstraintActuator::~KX_ConstraintActuator()
        // there's nothing to be done here, really....
 } /* end of destructor */
 
-bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
+bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
 {
 
        KX_GameObject* hitKXObj = client->m_gameobject;
        
-       if (client->m_type > KX_ClientObjectInfo::ACTOR)
-       {
-               // false hit
-               return false;
-       }
        bool bFound = false;
 
        if (m_property[0] == 0)
@@ -139,8 +134,26 @@ bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_p
                        bFound = hitKXObj->GetProperty(m_property) != NULL;
                }
        }
+       // update the hit status
+       result->m_hitFound = bFound;
+       // stop looking
+       return true;
+}
 
-       return bFound;
+/* this function is used to pre-filter the object before casting the ray on them.
+   This is useful for "X-Ray" option when we want to see "through" unwanted object.
+ */
+bool KX_ConstraintActuator::NeedRayCast(KX_ClientObjectInfo* client)
+{
+       if (client->m_type > KX_ClientObjectInfo::ACTOR)
+       {
+               // Unknown type of object, skip it.
+               // Should not occur as the sensor objects are filtered in RayTest()
+               printf("Invalid client type %d found in ray casting\n", client->m_type);
+               return false;
+       }
+       // no X-Ray function yet
+       return true;
 }
 
 bool KX_ConstraintActuator::Update(double curtime, bool frame)
@@ -287,8 +300,6 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame)
                        direction.normalize();
                        {
                                MT_Point3 topoint = position + (m_maximumBound) * direction;
-                               MT_Point3 resultpoint;
-                               MT_Vector3 resultnormal;
                                PHY_IPhysicsEnvironment* pe = obj->GetPhysicsEnvironment();
                                KX_IPhysicsController *spc = obj->GetPhysicsController();
 
@@ -304,9 +315,10 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame)
                                                parent->Release();
                                        }
                                }
-                               result = KX_RayCast::RayTest(spc, pe, position, topoint, resultpoint, resultnormal, KX_RayCast::Callback<KX_ConstraintActuator>(this));
-
+                               KX_RayCast::Callback<KX_ConstraintActuator> callback(this,spc);
+                               result = KX_RayCast::RayTest(pe, position, topoint, callback);
                                if (result)     {
+                                       MT_Vector3 newnormal = callback.m_hitNormal;
                                        // compute new position & orientation
                                        if ((m_option & (KX_ACT_CONSTRAINT_NORMAL|KX_ACT_CONSTRAINT_DISTANCE)) == 0) {
                                                // if none option is set, the actuator does nothing but detect ray 
@@ -316,27 +328,27 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame)
                                        if (m_option & KX_ACT_CONSTRAINT_NORMAL) {
                                                // the new orientation must be so that the axis is parallel to normal
                                                if (sign)
-                                                       resultnormal = -resultnormal;
+                                                       newnormal = -newnormal;
                                                // apply damping on the direction
                                                if (m_rotDampTime) {
                                                        MT_Scalar rotFilter = 1.0/(1.0+m_rotDampTime);
-                                                       resultnormal = (-m_rotDampTime*rotFilter)*direction + rotFilter*resultnormal;
+                                                       newnormal = (-m_rotDampTime*rotFilter)*direction + rotFilter*newnormal;
                                                } else if (m_posDampTime) {
-                                                       resultnormal = -filter*direction + (1.0-filter)*resultnormal;
+                                                       newnormal = -filter*direction + (1.0-filter)*newnormal;
                                                }
-                                               obj->AlignAxisToVect(resultnormal, axis);
-                                               direction = -resultnormal;
+                                               obj->AlignAxisToVect(newnormal, axis);
+                                               direction = -newnormal;
                                        }
                                        if (m_option & KX_ACT_CONSTRAINT_DISTANCE) {
                                                if (m_posDampTime) {
-                                                       newdistance = filter*(position-resultpoint).length()+(1.0-filter)*m_minimumBound;
+                                                       newdistance = filter*(position-callback.m_hitPoint).length()+(1.0-filter)*m_minimumBound;
                                                } else {
                                                        newdistance = m_minimumBound;
                                                }
                                        } else {
-                                               newdistance = (position-resultpoint).length();
+                                               newdistance = (position-callback.m_hitPoint).length();
                                        }
-                                       newposition = resultpoint-newdistance*direction;
+                                       newposition = callback.m_hitPoint-newdistance*direction;
                                } else if (m_option & KX_ACT_CONSTRAINT_PERMANENT) {
                                        // no contact but still keep running
                                        result = true;
index d9f3912..6ec4de9 100644 (file)
@@ -37,6 +37,8 @@
 #include "MT_Vector3.h"
 #include "KX_ClientObjectInfo.h"
 
+class KX_RayCast;
+
 class KX_ConstraintActuator : public SCA_IActuator
 {
        Py_Header;
@@ -100,7 +102,8 @@ protected:
                KX_ACT_CONSTRAINT_DISTANCE = 512
        };
        bool IsValidMode(KX_CONSTRAINTTYPE m); 
-       bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data);
+       bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data);
+       bool NeedRayCast(KX_ClientObjectInfo*);
 
        KX_ConstraintActuator(SCA_IObject* gameobj,
                                                  int posDamptime,
index 010ee1e..51322f8 100644 (file)
@@ -51,6 +51,7 @@ typedef unsigned long uint_ptr;
 #include "KX_GameObject.h"
 #include "RAS_MeshObject.h"
 #include "KX_MeshProxy.h"
+#include "KX_PolyProxy.h"
 #include <stdio.h> // printf
 #include "SG_Controller.h"
 #include "KX_IPhysicsController.h"
@@ -84,6 +85,7 @@ KX_GameObject::KX_GameObject(
        m_bVisible(true),
        m_pPhysicsController1(NULL),
        m_pPhysicsEnvironment(NULL),
+       m_xray(false),
        m_pHitObject(NULL),
        m_isDeformable(false)
 {
@@ -1628,25 +1630,45 @@ KX_PYMETHODDEF_DOC(KX_GameObject, getVectTo,
        return returnValue;
 }
 
-bool KX_GameObject::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
+bool KX_GameObject::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
 {
+       KX_GameObject* hitKXObj = client->m_gameobject;
+       
+       // if X-ray option is selected, the unwnted objects were not tested, so get here only with true hit
+       // if not, all objects were tested and the front one may not be the correct one.
+       if (m_xray || m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL)
+       {
+               m_pHitObject = hitKXObj;
+               return true;
+       }
+       // return true to stop RayCast::RayTest from looping, the above test was decisive
+       // We would want to loop only if we want to get more than one hit point
+       return true;
+}
 
+/* this function is used to pre-filter the object before casting the ray on them.
+   This is useful for "X-Ray" option when we want to see "through" unwanted object.
+ */
+bool KX_GameObject::NeedRayCast(KX_ClientObjectInfo* client)
+{
        KX_GameObject* hitKXObj = client->m_gameobject;
        
        if (client->m_type > KX_ClientObjectInfo::ACTOR)
        {
-               // false hit
+               // Unknown type of object, skip it.
+               // Should not occur as the sensor objects are filtered in RayTest()
+               printf("Invalid client type %d found in ray casting\n", client->m_type);
                return false;
        }
-
-       if (m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL)
+       
+       // if X-Ray option is selected, skip object that don't match the criteria as we see through them
+       // if not, test all objects because we don't know yet which one will be on front
+       if (!m_xray || m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL)
        {
-               m_pHitObject = hitKXObj;
                return true;
        }
-
+       // skip the object
        return false;
-       
 }
 
 KX_PYMETHODDEF_DOC(KX_GameObject, rayCastTo,
@@ -1686,8 +1708,6 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCastTo,
                toPoint = fromPoint + (dist) * toDir;
        }
 
-       MT_Point3 resultPoint;
-       MT_Vector3 resultNormal;
        PHY_IPhysicsEnvironment* pe = GetPhysicsEnvironment();
        KX_IPhysicsController *spc = GetPhysicsController();
        KX_GameObject *parent = GetParent();
@@ -1701,7 +1721,7 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCastTo,
                m_testPropName = propName;
        else
                m_testPropName.SetLength(0);
-       KX_RayCast::RayTest(spc, pe, fromPoint, toPoint, resultPoint, resultNormal, KX_RayCast::Callback<KX_GameObject>(this));
+       KX_RayCast::RayTest(pe, fromPoint, toPoint, KX_RayCast::Callback<KX_GameObject>(this,spc));
 
     if (m_pHitObject)
        {
@@ -1712,13 +1732,24 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCastTo,
 }
 
 KX_PYMETHODDEF_DOC(KX_GameObject, rayCast,
-                                  "rayCast(to,from,dist,prop): cast a ray and return tuple (object,hit,normal) of contact point with object within dist that matches prop or (None,None,None) tuple if no hit\n"
-" prop = property name that object must have; can be omitted => detect any object\n"
-" dist = max distance to look (can be negative => look behind); 0 or omitted => detect up to to\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) of contact point with object within dist that matches prop.\n"
+                                  " If no hit, return (None,None,None) or (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"
-" to = 3-tuple or object reference for destination of ray (if object, use center of object)\n"
-"Note: the object on which you call this method matters: the ray will ignore it if it goes through it\n")
+" dist = max distance to look (can be negative => look behind); 0 or omitted => detect up to to\n"
+" prop = property name that object must have; can be omitted => detect any object\n"
+" face = normal option: 1=>return face normal; 0 or omitted => normal is oriented towards origin\n"
+" 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"
+"        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"
+"        prop off, xray off: return closest hit or no hit if there is no object on the full extend of the ray\n"
+"        prop off, xray on : idem\n"
+"        prop on,  xray off: return closest hit if it matches prop, no hit otherwise\n"
+"        prop on,  xray on : return closest hit matching prop or no hit if there is no object matching prop on the full extend of the ray\n")
 {
        MT_Point3 toPoint;
        MT_Point3 fromPoint;
@@ -1727,8 +1758,9 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast,
        float dist = 0.0f;
        char *propName = NULL;
        KX_GameObject *other;
+       int face=0, xray=0, poly=0;
 
-       if (!PyArg_ParseTuple(args,"O|Ofs", &pyto, &pyfrom, &dist, &propName)) {
+       if (!PyArg_ParseTuple(args,"O|Ofsiii", &pyto, &pyfrom, &dist, &propName, &face, &xray, &poly)) {
                return NULL; // Python sets a simple error
        }
 
@@ -1774,8 +1806,6 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast,
                return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
        }
        
-       MT_Point3 resultPoint;
-       MT_Vector3 resultNormal;
        PHY_IPhysicsEnvironment* pe = GetPhysicsEnvironment();
        KX_IPhysicsController *spc = GetPhysicsController();
        KX_GameObject *parent = GetParent();
@@ -1789,20 +1819,41 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast,
                m_testPropName = propName;
        else
                m_testPropName.SetLength(0);
-       KX_RayCast::RayTest(spc, pe, fromPoint, toPoint, resultPoint, resultNormal, KX_RayCast::Callback<KX_GameObject>(this));
+       m_xray = xray;
+       // to get the hit results
+       KX_RayCast::Callback<KX_GameObject> callback(this,spc,NULL,face);
+       KX_RayCast::RayTest(pe, fromPoint, toPoint, callback);
 
-    if (m_pHitObject)
+       if (m_pHitObject)
        {
-               PyObject* returnValue = PyTuple_New(3);
+               PyObject* returnValue = (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->AddRef());
-                       PyTuple_SET_ITEM(returnValue, 1, PyObjectFrom(resultPoint));
-                       PyTuple_SET_ITEM(returnValue, 2, PyObjectFrom(resultNormal));
+                       PyTuple_SET_ITEM(returnValue, 1, PyObjectFrom(callback.m_hitPoint));
+                       PyTuple_SET_ITEM(returnValue, 2, PyObjectFrom(callback.m_hitNormal));
+                       if (poly)
+                       {
+                               if (callback.m_hitMesh)
+                               {
+                                       // if this field is set, then we can trust that m_hitPolygon is a valid polygon
+                                       RAS_Polygon* poly = callback.m_hitMesh->GetPolygon(callback.m_hitPolygon);
+                                       KX_PolyProxy* polyproxy = new KX_PolyProxy(callback.m_hitMesh, poly);
+                                       PyTuple_SET_ITEM(returnValue, 3, polyproxy);
+                               }
+                               else
+                               {
+                                       Py_INCREF(Py_None);
+                                       PyTuple_SET_ITEM(returnValue, 3, Py_None);
+                               }
+                       }
                }
                return returnValue;
        }
-       return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
-       //Py_RETURN_NONE;
+       // no hit
+       if (poly)
+               return Py_BuildValue("OOOO", Py_None, Py_None, Py_None, Py_None);
+       else
+               return Py_BuildValue("OOO", Py_None, Py_None, Py_None);
 }
 
 /* --------------------------------------------------------------------- 
index 0617190..508bc7c 100644 (file)
@@ -54,6 +54,7 @@
 
 //Forward declarations.
 struct KX_ClientObjectInfo;
+class KX_RayCast;
 class RAS_MeshObject;
 class KX_IPhysicsController;
 class PHY_IPhysicsEnvironment;
@@ -88,6 +89,7 @@ protected:
        // used for ray casting
        PHY_IPhysicsEnvironment*                        m_pPhysicsEnvironment;
        STR_String                                                      m_testPropName;
+       bool                                                            m_xray;
        KX_GameObject*                                          m_pHitObject;
 
        SG_Node*                                                        m_pSGNode;
@@ -428,7 +430,8 @@ public:
                return (m_pSGNode && m_pSGNode->GetSGParent() && m_pSGNode->GetSGParent()->IsVertexParent());
        }
 
-       bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data);
+       bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data);
+       bool NeedRayCast(KX_ClientObjectInfo* client);
 
 
        /**
index a0ac9cf..29842af 100644 (file)
@@ -35,6 +35,7 @@
 #include "RAS_MeshObject.h"
 
 #include "KX_VertexProxy.h"
+#include "KX_PolyProxy.h"
 
 #include "KX_PolygonMaterial.h"
 #include "KX_BlenderMaterial.h"
@@ -71,10 +72,12 @@ PyParentObject KX_MeshProxy::Parents[] = {
 
 PyMethodDef KX_MeshProxy::Methods[] = {
 {"getNumMaterials", (PyCFunction)KX_MeshProxy::sPyGetNumMaterials,METH_VARARGS},
+{"getNumPolygons", (PyCFunction)KX_MeshProxy::sPyGetNumPolygons,METH_NOARGS},
 {"getMaterialName", (PyCFunction)KX_MeshProxy::sPyGetMaterialName,METH_VARARGS},
 {"getTextureName", (PyCFunction)KX_MeshProxy::sPyGetTextureName,METH_VARARGS},
 {"getVertexArrayLength", (PyCFunction)KX_MeshProxy::sPyGetVertexArrayLength,METH_VARARGS},
 {"getVertex", (PyCFunction)KX_MeshProxy::sPyGetVertex,METH_VARARGS},
+{"getPolygon", (PyCFunction)KX_MeshProxy::sPyGetPolygon,METH_VARARGS},
 KX_PYMETHODTABLE(KX_MeshProxy, reinstancePhysicsMesh),
 //{"getIndexArrayLength", (PyCFunction)KX_MeshProxy::sPyGetIndexArrayLength,METH_VARARGS},
 
@@ -146,6 +149,12 @@ PyObject* KX_MeshProxy::PyGetNumMaterials(PyObject* self,
        return PyInt_FromLong(num);
 }
 
+PyObject* KX_MeshProxy::PyGetNumPolygons(PyObject* self)
+{
+       int num = m_meshobj->NumPolygons();
+       return PyInt_FromLong(num);
+}
+
 PyObject* KX_MeshProxy::PyGetMaterialName(PyObject* self, 
                               PyObject* args, 
                               PyObject* kwds)
@@ -234,6 +243,28 @@ PyObject* KX_MeshProxy::PyGetVertex(PyObject* self,
                
 }
 
+PyObject* KX_MeshProxy::PyGetPolygon(PyObject* self,
+                              PyObject* args, 
+                              PyObject* kwds)
+{
+    int polyindex= 1;
+       PyObject* polyob = NULL;
+
+       if (!PyArg_ParseTuple(args,"i",&polyindex))
+               return NULL;
+
+       RAS_Polygon* polygon = m_meshobj->GetPolygon(polyindex);
+       if (polygon)
+       {
+               polyob = new KX_PolyProxy(m_meshobj, polygon);
+       }
+       else
+       {
+               PyErr_SetString(PyExc_AttributeError, "Invalid polygon index");
+       }
+       return polyob;
+}
+
 KX_PYMETHODDEF_DOC(KX_MeshProxy, reinstancePhysicsMesh,
 "Reinstance the physics mesh.")
 {
index 7c6202c..3335c34 100644 (file)
@@ -57,10 +57,12 @@ public:
        KX_PYMETHOD(KX_MeshProxy,GetNumMaterials);
        KX_PYMETHOD(KX_MeshProxy,GetMaterialName);
        KX_PYMETHOD(KX_MeshProxy,GetTextureName);
+       KX_PYMETHOD_NOARGS(KX_MeshProxy,GetNumPolygons);
        
        // both take materialid (int)
        KX_PYMETHOD(KX_MeshProxy,GetVertexArrayLength);
        KX_PYMETHOD(KX_MeshProxy,GetVertex);
+       KX_PYMETHOD(KX_MeshProxy,GetPolygon);
        KX_PYMETHOD_DOC(KX_MeshProxy, reinstancePhysicsMesh);
 };
 
index db0bef8..721bd4d 100644 (file)
@@ -122,16 +122,10 @@ bool KX_MouseFocusSensor::Evaluate(CValue* event)
        return result;
 }
 
-bool KX_MouseFocusSensor::RayHit(KX_ClientObjectInfo* client_info, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
+bool KX_MouseFocusSensor::RayHit(KX_ClientObjectInfo* client_info, KX_RayCast* result, void * const data)
 {
        KX_GameObject* hitKXObj = client_info->m_gameobject;
        
-       if (client_info->m_type > KX_ClientObjectInfo::ACTOR)
-       {
-               // false hit
-               return false;
-       }
-       
        /* Is this me? In the ray test, there are a lot of extra checks
        * for aliasing artefacts from self-hits. That doesn't happen
        * here, so a simple test suffices. Or does the camera also get
@@ -142,8 +136,8 @@ bool KX_MouseFocusSensor::RayHit(KX_ClientObjectInfo* client_info, MT_Point3& hi
        if ((m_focusmode == 2) || hitKXObj == thisObj)
        {
                m_hitObject = hitKXObj;
-               m_hitPosition = hit_point;
-               m_hitNormal = hit_normal;
+               m_hitPosition = result->m_hitPoint;
+               m_hitNormal = result->m_hitNormal;
                return true;
        }
        
@@ -158,8 +152,6 @@ bool KX_MouseFocusSensor::ParentObjectHasFocus(void)
        m_hitObject = 0;
        m_hitPosition = MT_Vector3(0,0,0);
        m_hitNormal =   MT_Vector3(1,0,0);
-       MT_Point3 resultpoint;
-       MT_Vector3 resultnormal;
 
        /* All screen handling in the gameengine is done by GL,
         * specifically the model/view and projection parts. The viewport
@@ -280,7 +272,7 @@ bool KX_MouseFocusSensor::ParentObjectHasFocus(void)
 
        bool result = false;
 
-       result = KX_RayCast::RayTest(physics_controller, physics_environment, frompoint3, topoint3, resultpoint, resultnormal, KX_RayCast::Callback<KX_MouseFocusSensor>(this));
+       KX_RayCast::RayTest(physics_environment, frompoint3, topoint3, KX_RayCast::Callback<KX_MouseFocusSensor>(this,physics_controller));
        
        result = (m_hitObject!=0);
 
index b011ebe..a6cc39d 100644 (file)
@@ -33,6 +33,8 @@
 
 #include "SCA_MouseSensor.h"
 
+class KX_RayCast;
+
 /**
  * The mouse focus sensor extends the basic SCA_MouseSensor. It has
  * been placed in KX because it needs access to the rasterizer and
@@ -76,7 +78,9 @@ class KX_MouseFocusSensor : public SCA_MouseSensor
                return result;
        };
 
-       bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data);
+       bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data);
+       bool NeedRayCast(KX_ClientObjectInfo* client) { return true; }
+       
 
        
        /* --------------------------------------------------------------------- */
diff --git a/source/gameengine/Ketsji/KX_PolyProxy.cpp b/source/gameengine/Ketsji/KX_PolyProxy.cpp
new file mode 100644 (file)
index 0000000..658c8a9
--- /dev/null
@@ -0,0 +1,265 @@
+/**
+ * $Id$
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "KX_PolyProxy.h"
+#include "KX_MeshProxy.h"
+#include "RAS_MeshObject.h"
+#include "KX_BlenderMaterial.h"
+#include "KX_PolygonMaterial.h"
+
+#include "KX_PyMath.h"
+
+PyTypeObject KX_PolyProxy::Type = {
+       PyObject_HEAD_INIT(&PyType_Type)
+       0,
+       "KX_PolyProxy",
+       sizeof(KX_PolyProxy),
+       0,
+       PyDestructor,
+       0,
+       __getattr,
+       __setattr,
+       0, //&MyPyCompare,
+       __repr,
+       0, //&cvalue_as_number,
+       0,
+       0,
+       0,
+       0
+};
+
+PyParentObject KX_PolyProxy::Parents[] = {
+       &KX_PolyProxy::Type,
+       &SCA_IObject::Type,
+       &CValue::Type,
+       NULL
+};
+
+PyMethodDef KX_PolyProxy::Methods[] = {
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,getMaterialIndex),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,getNumVertex),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,isVisible),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,isCollider),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,getMaterialName),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,getTextureName),
+       KX_PYMETHODTABLE(KX_PolyProxy,getVertexIndex),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,getMesh),
+       KX_PYMETHODTABLE_NOARG(KX_PolyProxy,getMaterial),
+       {NULL,NULL} //Sentinel
+};
+
+PyObject*
+KX_PolyProxy::_getattr(const STR_String& attr)
+{
+       if (attr == "matname")
+       {
+               return PyString_FromString(m_polygon->GetMaterial()->GetPolyMaterial()->GetMaterialName());
+       }
+       if (attr == "texture")
+       {
+               return PyString_FromString(m_polygon->GetMaterial()->GetPolyMaterial()->GetTextureName());
+       }
+       if (attr == "material")
+       {
+               RAS_IPolyMaterial *polymat = m_polygon->GetMaterial()->GetPolyMaterial();
+               if(polymat->GetFlag() & RAS_BLENDERMAT)
+               {
+                       KX_BlenderMaterial* mat = static_cast<KX_BlenderMaterial*>(polymat);
+                       Py_INCREF(mat);
+                       return mat;
+               }
+               else
+               {
+                       KX_PolygonMaterial* mat = static_cast<KX_PolygonMaterial*>(polymat);
+                       Py_INCREF(mat);
+                       return mat;
+               }
+       }
+       if (attr == "matid")
+       {
+               // we'll have to scan through the material bucket of the mes and compare with 
+               // the one of the polygon
+               RAS_MaterialBucket* polyBucket = m_polygon->GetMaterial();
+               unsigned int matid;
+               for (matid=0; matid<m_mesh->NumMaterials(); matid++)
+               {
+                       RAS_MaterialBucket* meshBucket = m_mesh->GetMaterialBucket(matid);
+                       if (meshBucket == polyBucket)
+                               // found it
+                               break;
+               }
+               return PyInt_FromLong(matid);
+       }
+       if (attr == "v1")
+       {
+               return PyInt_FromLong(m_polygon->GetVertexIndexBase().m_indexarray[0]);
+       }
+       if (attr == "v2")
+       {
+               return PyInt_FromLong(m_polygon->GetVertexIndexBase().m_indexarray[1]);
+       }
+       if (attr == "v3")
+       {
+               return PyInt_FromLong(m_polygon->GetVertexIndexBase().m_indexarray[2]);
+       }
+       if (attr == "v4")
+       {
+               return PyInt_FromLong(((m_polygon->VertexCount()>3)?m_polygon->GetVertexIndexBase().m_indexarray[3]:0));
+       }
+       if (attr == "visible")
+       {
+               return PyInt_FromLong(m_polygon->IsVisible());
+       }
+       if (attr == "collide")
+       {
+               return PyInt_FromLong(m_polygon->IsCollider());
+       }
+       _getattr_up(SCA_IObject);
+}
+
+KX_PolyProxy::KX_PolyProxy(const RAS_MeshObject*mesh, RAS_Polygon* polygon)
+:      m_mesh((RAS_MeshObject*)mesh),
+       m_polygon(polygon)
+{
+}
+
+KX_PolyProxy::~KX_PolyProxy()
+{
+}
+
+
+// stuff for cvalue related things
+CValue*                KX_PolyProxy::Calc(VALUE_OPERATOR, CValue *) { return NULL;}
+CValue*                KX_PolyProxy::CalcFinal(VALUE_DATA_TYPE, VALUE_OPERATOR, CValue *) { return NULL;}      
+STR_String     sPolyName="polygone";
+const STR_String &     KX_PolyProxy::GetText() {return sPolyName;};
+float          KX_PolyProxy::GetNumber() { return -1;}
+STR_String     KX_PolyProxy::GetName() { return sPolyName;}
+void           KX_PolyProxy::SetName(STR_String) { };
+CValue*                KX_PolyProxy::GetReplica() { return NULL;}
+void           KX_PolyProxy::ReplicaSetName(STR_String) {};
+
+
+// stuff for python integration
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, getMaterialIndex, 
+"getMaterialIndex() : return the material index of the polygon in the mesh\n")
+{
+       RAS_MaterialBucket* polyBucket = m_polygon->GetMaterial();
+       unsigned int matid;
+       for (matid=0; matid<m_mesh->NumMaterials(); matid++)
+       {
+               RAS_MaterialBucket* meshBucket = m_mesh->GetMaterialBucket(matid);
+               if (meshBucket == polyBucket)
+                       // found it
+                       break;
+       }
+       return PyInt_FromLong(matid);
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, getNumVertex,
+"getNumVertex() : returns the number of vertex of the polygon, 3 or 4\n")
+{
+       return PyInt_FromLong(m_polygon->VertexCount());
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, isVisible,
+"isVisible() : returns whether the polygon is visible or not\n")
+{
+       return PyInt_FromLong(m_polygon->IsVisible());
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, isCollider,
+"isCollider() : returns whether the polygon is receives collision or not\n")
+{
+       return PyInt_FromLong(m_polygon->IsCollider());
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, getMaterialName,
+"getMaterialName() : returns the polygon material name, \"NoMaterial\" if no material\n")
+{
+       return PyString_FromString(m_polygon->GetMaterial()->GetPolyMaterial()->GetMaterialName());
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, getTextureName,
+"getTexturelName() : returns the polygon texture name, \"NULL\" if no texture\n")
+{
+       return PyString_FromString(m_polygon->GetMaterial()->GetPolyMaterial()->GetTextureName());
+}
+
+KX_PYMETHODDEF_DOC(KX_PolyProxy, getVertexIndex,
+"getVertexIndex(vertex) : returns the mesh vertex index of a polygon vertex\n"
+"vertex: index of the vertex in the polygon: 0->3\n"
+"return value can be used to retrieve the vertex details through mesh proxy\n"
+"Note: getVertexIndex(3) on a triangle polygon returns 0\n")
+{
+       int index;
+       if (!PyArg_ParseTuple(args,"i",&index))
+       {
+               return NULL;
+       }
+       if (index < 0 || index > 3)
+       {
+               PyErr_SetString(PyExc_AttributeError, "Valid range for index is 0-3");
+               return NULL;
+       }
+       if (index < m_polygon->VertexCount())
+       {
+               return PyInt_FromLong(m_polygon->GetVertexIndexBase().m_indexarray[index]);
+       }
+       return PyInt_FromLong(0);
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, getMesh,
+"getMesh() : returns a mesh proxy\n")
+{
+       KX_MeshProxy* meshproxy = new KX_MeshProxy((RAS_MeshObject*)m_mesh);
+       return meshproxy;
+}
+
+KX_PYMETHODDEF_DOC_NOARG(KX_PolyProxy, getMaterial,
+"getMaterial() : returns a material\n")
+{
+       RAS_IPolyMaterial *polymat = m_polygon->GetMaterial()->GetPolyMaterial();
+       if(polymat->GetFlag() & RAS_BLENDERMAT)
+       {
+               KX_BlenderMaterial* mat = static_cast<KX_BlenderMaterial*>(polymat);
+               Py_INCREF(mat);
+               return mat;
+       }
+       else
+       {
+               KX_PolygonMaterial* mat = static_cast<KX_PolygonMaterial*>(polymat);
+               Py_INCREF(mat);
+               return mat;
+       }
+}
diff --git a/source/gameengine/Ketsji/KX_PolyProxy.h b/source/gameengine/Ketsji/KX_PolyProxy.h
new file mode 100644 (file)
index 0000000..506e2c2
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __KX_POLYROXY
+#define __KX_POLYPROXY
+
+#include "SCA_IObject.h"
+
+class KX_PolyProxy     : public SCA_IObject
+{
+       Py_Header;
+protected:
+       class RAS_Polygon*              m_polygon;
+       class RAS_MeshObject*   m_mesh;
+public:
+       KX_PolyProxy(const class RAS_MeshObject*mesh, class RAS_Polygon* polygon);
+       virtual ~KX_PolyProxy();
+
+       // stuff for cvalue related things
+       CValue*         Calc(VALUE_OPERATOR op, CValue *val) ;
+       CValue*         CalcFinal(VALUE_DATA_TYPE dtype, VALUE_OPERATOR op, CValue *val);
+       const STR_String &      GetText();
+       float           GetNumber();
+       STR_String      GetName();
+       void            SetName(STR_String name);                                                               // Set the name of the value
+       void            ReplicaSetName(STR_String name);
+       CValue*         GetReplica();
+
+
+// stuff for python integration
+       virtual PyObject* _getattr(const STR_String& attr);
+
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,getMaterialIndex)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,getNumVertex)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,isVisible)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,isCollider)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,getMaterialName)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,getTextureName)
+       KX_PYMETHOD_DOC(KX_PolyProxy,getVertexIndex)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,getMesh)
+       KX_PYMETHOD_DOC_NOARGS(KX_PolyProxy,getMaterial)
+
+};
+
+#endif //__KX_POLYPROXY
+
index 89e2d64..974d4b9 100644 (file)
 #include "PHY_IPhysicsEnvironment.h"
 #include "PHY_IPhysicsController.h"
 
-bool KX_RayCast::RayTest(KX_IPhysicsController* ignore_controller, PHY_IPhysicsEnvironment* physics_environment, const MT_Point3& _frompoint, const MT_Point3& topoint, MT_Point3& result_point, MT_Vector3& result_normal, const KX_RayCast& callback)
+KX_RayCast::KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal)
+       :PHY_IRayCastFilterCallback(dynamic_cast<PHY_IPhysicsController*>(ignoreController), faceNormal) 
+{
+}
+
+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_hitMesh = result->m_meshObject;
+       m_hitPolygon = result->m_polygon;
+}
+
+bool KX_RayCast::RayTest(PHY_IPhysicsEnvironment* physics_environment, const MT_Point3& _frompoint, const MT_Point3& topoint, KX_RayCast& callback)
 {
        // Loops over all physics objects between frompoint and topoint,
        // calling callback.RayHit for each one.
@@ -50,58 +64,51 @@ bool KX_RayCast::RayTest(KX_IPhysicsController* ignore_controller, PHY_IPhysicsE
        // returns true if an object was found, false if not.
        MT_Point3 frompoint(_frompoint);
        const MT_Vector3 todir( (topoint - frompoint).safe_normalized() );
+       MT_Point3 prevpoint(_frompoint+todir*(-1.f));
        
        PHY_IPhysicsController* hit_controller;
-       PHY__Vector3 phy_pos;
-       PHY__Vector3 phy_normal;
 
-       while((hit_controller = physics_environment->rayTest(dynamic_cast<PHY_IPhysicsController*>(ignore_controller),
+       while((hit_controller = physics_environment->rayTest(callback,
                        frompoint.x(),frompoint.y(),frompoint.z(),
-                       topoint.x(),topoint.y(),topoint.z(),
-                       phy_pos[0],phy_pos[1],phy_pos[2],
-                       phy_normal[0],phy_normal[1],phy_normal[2]))) 
+                       topoint.x(),topoint.y(),topoint.z())) != NULL) 
        {
-               result_point = MT_Point3(phy_pos);
-               result_normal = MT_Vector3(phy_normal);
                KX_ClientObjectInfo* info = static_cast<KX_ClientObjectInfo*>(hit_controller->getNewClientInfo());
                
                if (!info)
                {
                        printf("no info!\n");
                        MT_assert(info && "Physics controller with no client object info");
-                       return false;
+                       break;
                }
                
-               if (callback.RayHit(info, result_point, result_normal))
-                       return true;
-       
-               // There is a bug in the code below: the delta is computed with the wrong
-               // sign on the face opposite to the center, resulting in infinite looping.
-               // In Blender 2.45 this code was never executed because callback.RayHit() always 
-               // returned true, causing the ray sensor to stop on the first object.
-               // To avoid changing the behaviour will simply return false here.
-               // It should be discussed if we want the ray sensor to "see" through objects
-               // that don't have the required property/material (condition to get here)
-               return false;
-       
-               // skip past the object and keep tracing
-               /* We add 0.01 of fudge, so that if the margin && radius == 0., we don't endless loop. */
-               MT_Scalar marg = 0.01 + hit_controller->GetMargin();
-               marg += 2.f * hit_controller->GetMargin();
+               // The biggest danger to to endless loop, prevent this by checking that the
+               // hit point always progresses along the ray direction..
+               prevpoint -= callback.m_hitPoint;
+               if (prevpoint.length2() < MT_EPSILON)
+                       break;
+
+               if (callback.RayHit(info))
+                       // caller may decide to stop the loop and still cancel the hit
+                       return callback.m_hitFound;
+
+               // Skip past the object and keep tracing.
+               // Note that retrieving in a single shot multiple hit points would be possible 
+               // but it would require some change in Bullet.
+               prevpoint = callback.m_hitPoint;
+               /* We add 0.001 of fudge, so that if the margin && radius == 0., we don't endless loop. */
+               MT_Scalar marg = 0.001 + hit_controller->GetMargin();
+               marg *= 2.f;
                /* Calculate the other side of this object */
-               PHY__Vector3 hitpos;
-               hit_controller->getPosition(hitpos);
-               MT_Point3 hitObjPos(hitpos);
-               
-               MT_Vector3 hitvector = hitObjPos - result_point;
-               if (hitvector.dot(hitvector) > MT_EPSILON)
-               {
-                       hitvector.normalize();
-                       marg *= 2.*todir.dot(hitvector);
-               }
-               frompoint = result_point + marg * todir;
+               MT_Scalar h = MT_abs(todir.dot(callback.m_hitNormal));
+               if (h <= 0.01)
+                       // the normal is almost orthogonal to the ray direction, cannot compute the other side
+                       break;
+               marg /= h; 
+               frompoint = callback.m_hitPoint + marg * todir;
+               // verify that we are not passed the to point
+               if ((topoint - frompoint).dot(todir) < 0.f)
+                       break;
        }
-       
-       return hit_controller;
+       return false;
 }
 
index 607dabd..c3084c9 100644 (file)
 #ifndef __KX_RAYCAST_H__
 #define __KX_RAYCAST_H__
 
-class MT_Point3;
-class MT_Vector3;
-class KX_IPhysicsController;
-class PHY_IPhysicsEnvironment;
+#include "PHY_IPhysicsEnvironment.h"
+#include "PHY_IPhysicsController.h"
+#include "MT_Point3.h"
+#include "MT_Vector3.h"
 
+class RAS_MeshObject; 
 struct KX_ClientObjectInfo;
+class KX_IPhysicsController;
 
 /**
  *  Defines a function for doing a ray cast.
@@ -49,17 +51,27 @@ struct KX_ClientObjectInfo;
  *
  *  Returns true if a client was accepted, false if nothing found.
  */
-class KX_RayCast
+class KX_RayCast : public PHY_IRayCastFilterCallback
 {
-protected:
-       KX_RayCast() {};
 public:
+       bool                                    m_hitFound;
+       MT_Point3                               m_hitPoint;
+       MT_Vector3                              m_hitNormal;
+       const RAS_MeshObject*   m_hitMesh;
+       int                                             m_hitPolygon;
+
+       KX_RayCast(KX_IPhysicsController* ignoreController, bool faceNormal);
        virtual ~KX_RayCast() {}
 
+       /**
+        * The physic environment returns the ray casting result through this function
+        */
+       virtual void reportHit(PHY_RayCastResult* result);
+
        /** ray test callback.
         *  either override this in your class, or use a callback wrapper.
         */
-       virtual bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal) const = 0;
+       virtual bool RayHit(KX_ClientObjectInfo* client) = 0;
 
        /** 
         *  Callback wrapper.
@@ -71,13 +83,11 @@ public:
        
        /// Public interface.
        /// Implement bool RayHit in your class to receive ray callbacks.
-       static bool RayTest(KX_IPhysicsController* physics_controller, 
+       static bool RayTest(
                PHY_IPhysicsEnvironment* physics_environment, 
-               const MT_Point3& _frompoint, 
+               const MT_Point3& frompoint, 
                const MT_Point3& topoint, 
-               MT_Point3& result_point, 
-               MT_Vector3& result_normal, 
-               const KX_RayCast& callback);
+               KX_RayCast& callback);
        
 };
 
@@ -86,18 +96,32 @@ template<class T> class KX_RayCast::Callback : public KX_RayCast
        T *self;
        void *data;
 public:
-       Callback(T *_self, void *_data = NULL)
-               : self(_self),
+       Callback(T *_self, KX_IPhysicsController* controller=NULL, void *_data = NULL, bool faceNormal=false)
+               : KX_RayCast(controller, faceNormal),
+               self(_self),
                data(_data)
        {
        }
        
        ~Callback() {}
-       
-       virtual bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal) const
+
+       virtual bool RayHit(KX_ClientObjectInfo* client)
        {
-               return self->RayHit(client, hit_point, hit_normal, data);
+               return self->RayHit(client, this, data);
        }
+
+       virtual bool needBroadphaseRayCast(PHY_IPhysicsController* controller)
+       {
+               KX_ClientObjectInfo* info = static_cast<KX_ClientObjectInfo*>(controller->getNewClientInfo());
+               
+               if (!info)
+               {
+                       MT_assert(info && "Physics controller with no client object info");
+                       return false;
+               }
+               return self->NeedRayCast(info);
+       }
+
 };
        
 
index a416c8c..f62df54 100644 (file)
@@ -104,16 +104,10 @@ bool KX_RaySensor::IsPositiveTrigger()
        return result;
 }
 
-bool KX_RaySensor::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
+bool KX_RaySensor::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
 {
 
        KX_GameObject* hitKXObj = client->m_gameobject;
-       
-       if (client->m_type > KX_ClientObjectInfo::ACTOR)
-       {
-               // false hit
-               return false;
-       }
        bool bFound = false;
 
        if (m_propertyname.Length() == 0)
@@ -139,16 +133,29 @@ bool KX_RaySensor::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_
        {
                m_rayHit = true;
                m_hitObject = hitKXObj;
-               m_hitPosition = hit_point;
-               m_hitNormal = hit_normal;
+               m_hitPosition = result->m_hitPoint;
+               m_hitNormal = result->m_hitNormal;
                        
        }
-
-       return bFound;
-       
+       // no multi-hit search yet
+       return true;
 }
 
-
+/* this function is used to pre-filter the object before casting the ray on them.
+   This is useful for "X-Ray" option when we want to see "through" unwanted object.
+ */
+bool KX_RaySensor::NeedRayCast(KX_ClientObjectInfo* client)
+{
+       if (client->m_type > KX_ClientObjectInfo::ACTOR)
+       {
+               // Unknown type of object, skip it.
+               // Should not occur as the sensor objects are filtered in RayTest()
+               printf("Invalid client type %d found ray casting\n", client->m_type);
+               return false;
+       }
+       // no X-Ray function yet
+       return true;
+}
 
 bool KX_RaySensor::Evaluate(CValue* event)
 {
@@ -215,8 +222,6 @@ bool KX_RaySensor::Evaluate(CValue* event)
        m_rayDirection = todir;
 
        MT_Point3 topoint = frompoint + (m_distance) * todir;
-       MT_Point3 resultpoint;
-       MT_Vector3 resultnormal;
        PHY_IPhysicsEnvironment* pe = m_scene->GetPhysicsEnvironment();
 
        if (!pe)
@@ -238,7 +243,7 @@ bool KX_RaySensor::Evaluate(CValue* event)
        PHY_IPhysicsEnvironment* physics_environment = this->m_scene->GetPhysicsEnvironment();
        
 
-       result = KX_RayCast::RayTest(spc, physics_environment, frompoint, topoint, resultpoint, resultnormal, KX_RayCast::Callback<KX_RaySensor>(this));
+       KX_RayCast::RayTest(physics_environment, frompoint, topoint, KX_RayCast::Callback<KX_RaySensor>(this, spc));
 
        /* now pass this result to some controller */
 
@@ -265,6 +270,10 @@ bool KX_RaySensor::Evaluate(CValue* event)
                        // notify logicsystem that ray JUST left the Object
                        result = true;
                }
+               else
+               {
+                       result = false;
+               }
        
       }
     if (reset)
index f4305b0..9e53f80 100644 (file)
@@ -36,6 +36,7 @@
 #include "MT_Point3.h"
 
 struct KX_ClientObjectInfo;
+class KX_RayCast;
 
 class KX_RaySensor : public SCA_ISensor
 {
@@ -68,7 +69,8 @@ public:
        virtual bool IsPositiveTrigger();
        virtual void Init();
 
-       bool RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data);
+       bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data);
+       bool NeedRayCast(KX_ClientObjectInfo* client);
        
        KX_PYMETHOD_DOC(KX_RaySensor,GetHitObject);
        KX_PYMETHOD_DOC(KX_RaySensor,GetHitPosition);
index a04560a..6d9b41e 100644 (file)
@@ -206,8 +206,7 @@ void                ODEPhysicsEnvironment::removeConstraint(int constraintid)
        }
 }
 
-PHY_IPhysicsController* ODEPhysicsEnvironment::rayTest(PHY_IPhysicsController* ignoreClient,float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                       float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ)
+PHY_IPhysicsController* ODEPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallback &filterCallback,float fromX,float fromY,float fromZ, float toX,float toY,float toZ)
 {
 
        //m_OdeWorld
index 7c61902..dcc87d6 100644 (file)
@@ -54,8 +54,7 @@ public:
                        float axisX,float axisY,float axisZ);
 
        virtual void            removeConstraint(void * constraintid);
-       virtual PHY_IPhysicsController* rayTest(PHY_IPhysicsController* ignoreClient,float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                       float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ);
+       virtual PHY_IPhysicsController* rayTest(PHY_IRayCastFilterCallback &filterCallback,float fromX,float fromY,float fromZ, float toX,float toY,float toZ);
 
 
        //gamelogic callbacks
index d6a32df..2ec96c7 100644 (file)
@@ -767,6 +767,8 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope)
        // assume no shape information
        m_shapeType = PHY_SHAPE_NONE;
        m_vertexArray.clear();
+       m_polygonIndexArray.clear();
+       m_meshObject = NULL;
 
        if (!meshobj)
                return false;
@@ -838,6 +840,7 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope)
                                        m_vertexArray.push_back(vertex0);
                                        m_vertexArray.push_back(vertex1);
                                        m_vertexArray.push_back(vertex2);
+                                       m_polygonIndexArray.push_back(p2);
                                        numvalidpolys++;
                                }
                                if (poly->VertexCount() == 4)
@@ -857,6 +860,7 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope)
                                        m_vertexArray.push_back(vertex0);
                                        m_vertexArray.push_back(vertex1);
                                        m_vertexArray.push_back(vertex2);
+                                       m_polygonIndexArray.push_back(p2);
                                        numvalidpolys++;
                                }
                        }               
@@ -869,6 +873,7 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope)
                m_shapeType = PHY_SHAPE_NONE;
                return false;
        }
+       m_meshObject = meshobj;
        return true;
 }
 
index 1e1a38a..8a7f106 100644 (file)
@@ -75,6 +75,16 @@ public:
        {
                return m_nextShape;
        }
+       CcdShapeConstructionInfo* GetChildShape(int i)
+       {
+               CcdShapeConstructionInfo* shape = m_nextShape;
+               while (i > 0 && shape != NULL)
+               {
+                       shape = shape->m_nextShape;
+                       i--;
+               }
+               return shape;
+       }
 
        bool SetMesh(RAS_MeshObject* mesh, bool polytope);
 
@@ -89,6 +99,11 @@ public:
        std::vector<btPoint3>   m_vertexArray;  // Contains both vertex array for polytope shape and
                                                                                        // triangle array for concave mesh shape.
                                                                                        // In this case a triangle is made of 3 consecutive points
+       std::vector<int>                m_polygonIndexArray;    // Contains the array of polygon index in the 
+                                                                                                       // original mesh that correspond to shape triangles.
+                                                                                                       // only set for concave mesh shape.
+       const RAS_MeshObject*   m_meshObject;   // Keep a pointer to the original mesh 
+
 protected:
        CcdShapeConstructionInfo* m_nextShape;  // for compound shape
        int                                             m_refCount;             // this class is shared between replicas
index d8e05fa..a34c701 100644 (file)
@@ -709,35 +709,51 @@ void              CcdPhysicsEnvironment::removeConstraint(int     constraintId)
 
 struct FilterClosestRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
 {
-       PHY_IPhysicsController* m_ignoreClient;
+       PHY_IRayCastFilterCallback&     m_phyRayFilter;
+       const btCollisionShape*         m_hitTriangleShape;
+       int                                                     m_hitTriangleIndex;
 
-       FilterClosestRayResultCallback (PHY_IPhysicsController* ignoreClient,const btVector3& rayFrom,const btVector3& rayTo)
+       FilterClosestRayResultCallback (PHY_IRayCastFilterCallback& phyRayFilter,const btVector3& rayFrom,const btVector3& rayTo)
                : btCollisionWorld::ClosestRayResultCallback(rayFrom,rayTo),
-               m_ignoreClient(ignoreClient)
+               m_phyRayFilter(phyRayFilter),
+               m_hitTriangleShape(NULL),
+               m_hitTriangleIndex(0)
        {
-
        }
 
        virtual ~FilterClosestRayResultCallback()
        {
        }
 
+       virtual bool    NeedRayCast(btCollisionObject* object)
+       {
+               CcdPhysicsController* phyCtrl = static_cast<CcdPhysicsController*>(object->getUserPointer());
+               if (phyCtrl != m_phyRayFilter.m_ignoreController)
+               {
+                       return m_phyRayFilter.needBroadphaseRayCast(phyCtrl);
+               }
+               return false;
+       }
+
        virtual float   AddSingleResult( btCollisionWorld::LocalRayResult& rayResult)
        {
                CcdPhysicsController* curHit = static_cast<CcdPhysicsController*>(rayResult.m_collisionObject->getUserPointer());
-               //ignore client...
-               if (curHit != m_ignoreClient)
-               {               
-                       //if valid
-                       return ClosestRayResultCallback::AddSingleResult(rayResult);
+               // save shape information as ClosestRayResultCallback::AddSingleResult() does not do it
+               if (rayResult.m_localShapeInfo)
+               {
+                       m_hitTriangleShape = rayResult.m_localShapeInfo->m_triangleShape;
+                       m_hitTriangleIndex = rayResult.m_localShapeInfo->m_triangleIndex;
+               } else 
+               {
+                       m_hitTriangleShape = NULL;
+                       m_hitTriangleIndex = 0;
                }
-               return m_closestHitFraction;
+               return ClosestRayResultCallback::AddSingleResult(rayResult);
        }
 
 };
 
-PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IPhysicsController* ignoreClient, float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                                                          float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ)
+PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallback &filterCallback, float fromX,float fromY,float fromZ, float toX,float toY,float toZ)
 {
 
 
@@ -751,18 +767,21 @@ PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IPhysicsController* i
        //Either Ray Cast with or without filtering
 
        //btCollisionWorld::ClosestRayResultCallback rayCallback(rayFrom,rayTo);
-       FilterClosestRayResultCallback   rayCallback(ignoreClient,rayFrom,rayTo);
+       FilterClosestRayResultCallback   rayCallback(filterCallback,rayFrom,rayTo);
 
 
-       PHY_IPhysicsController* nearestHit = 0;
+       PHY_RayCastResult result;
+       memset(&result, 0, sizeof(result));
+
        // don't collision with sensor object
-       m_dynamicsWorld->rayTest(rayFrom,rayTo,rayCallback, CcdConstructionInfo::AllFilter ^ CcdConstructionInfo::SensorFilter);
+       m_dynamicsWorld->rayTest(rayFrom,rayTo,rayCallback, CcdConstructionInfo::AllFilter ^ CcdConstructionInfo::SensorFilter,filterCallback.m_faceNormal);
        if (rayCallback.HasHit())
        {
-               nearestHit = static_cast<CcdPhysicsController*>(rayCallback.m_collisionObject->getUserPointer());
-               hitX =  rayCallback.m_hitPointWorld.getX();
-               hitY =  rayCallback.m_hitPointWorld.getY();
-               hitZ =  rayCallback.m_hitPointWorld.getZ();
+               CcdPhysicsController* controller = static_cast<CcdPhysicsController*>(rayCallback.m_collisionObject->getUserPointer());
+               result.m_controller = controller;
+               result.m_hitPoint[0] = rayCallback.m_hitPointWorld.getX();
+               result.m_hitPoint[1] = rayCallback.m_hitPointWorld.getY();
+               result.m_hitPoint[2] = rayCallback.m_hitPointWorld.getZ();
 
                if (rayCallback.m_hitNormalWorld.length2() > (SIMD_EPSILON*SIMD_EPSILON))
                {
@@ -771,14 +790,42 @@ PHY_IPhysicsController* CcdPhysicsEnvironment::rayTest(PHY_IPhysicsController* i
                {
                        rayCallback.m_hitNormalWorld.setValue(1,0,0);
                }
-               normalX = rayCallback.m_hitNormalWorld.getX();
-               normalY = rayCallback.m_hitNormalWorld.getY();
-               normalZ = rayCallback.m_hitNormalWorld.getZ();
-
+               result.m_hitNormal[0] = rayCallback.m_hitNormalWorld.getX();
+               result.m_hitNormal[1] = rayCallback.m_hitNormalWorld.getY();
+               result.m_hitNormal[2] = rayCallback.m_hitNormalWorld.getZ();
+               if (rayCallback.m_hitTriangleShape != NULL)
+               {
+                       // identify the mesh polygon
+                       CcdShapeConstructionInfo* shapeInfo = controller->m_shapeInfo;
+                       if (shapeInfo)
+                       {
+                               btCollisionShape* shape = controller->GetRigidBody()->getCollisionShape();
+                               if (shape->isCompound())
+                               {
+                                       btCompoundShape* compoundShape = (btCompoundShape*)shape;
+                                       CcdShapeConstructionInfo* compoundShapeInfo = shapeInfo;
+                                       // need to search which sub-shape has been hit
+                                       for (int i=0; i<compoundShape->getNumChildShapes(); i++)
+                                       {
+                                               shapeInfo = compoundShapeInfo->GetChildShape(i);
+                                               shape=compoundShape->getChildShape(i);
+                                               if (shape == rayCallback.m_hitTriangleShape)
+                                                       break;
+                                       }
+                               }
+                               if (shape == rayCallback.m_hitTriangleShape && 
+                                       rayCallback.m_hitTriangleIndex < shapeInfo->m_polygonIndexArray.size())
+                               {
+                                       result.m_meshObject = shapeInfo->m_meshObject;
+                                       result.m_polygon = shapeInfo->m_polygonIndexArray.at(rayCallback.m_hitTriangleIndex);
+                               }
+                       }
+               }
+               filterCallback.reportHit(&result);
        }       
 
 
-       return nearestHit;
+       return result.m_controller;
 }
 
 
index 825a5e5..667e310 100644 (file)
@@ -157,8 +157,7 @@ protected:
 
                btTypedConstraint*      getConstraintById(int constraintId);
 
-               virtual PHY_IPhysicsController* rayTest(PHY_IPhysicsController* ignoreClient, float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                               float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ);
+               virtual PHY_IPhysicsController* rayTest(PHY_IRayCastFilterCallback &filterCallback, float fromX,float fromY,float fromZ, float toX,float toY,float toZ);
 
 
                //Methods for gamelogic collision/physics callbacks
index f512d44..d78958b 100644 (file)
@@ -109,8 +109,7 @@ void                DummyPhysicsEnvironment::removeConstraint(int   constraintid)
        }
 }
 
-PHY_IPhysicsController* DummyPhysicsEnvironment::rayTest(PHY_IPhysicsController* ignoreClient,float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                       float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ)
+PHY_IPhysicsController* DummyPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallback &filterCallback,float fromX,float fromY,float fromZ, float toX,float toY,float toZ)
 {
        //collision detection / raytesting
        return NULL;
index b5a61f7..975be84 100644 (file)
@@ -69,8 +69,7 @@ public:
                return 0;
        }
 
-       virtual PHY_IPhysicsController* rayTest(PHY_IPhysicsController* ignoreClient, float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                       float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ);
+       virtual PHY_IPhysicsController* rayTest(PHY_IRayCastFilterCallback &filterCallback, float fromX,float fromY,float fromZ, float toX,float toY,float toZ);
 
 
        //gamelogic callbacks
index 65018d2..80e4dc4 100644 (file)
@@ -26,6 +26,7 @@
  *
  * ***** END GPL LICENSE BLOCK *****
  */
+#include <string.h>            // memset
 #include "SumoPhysicsEnvironment.h"
 #include "PHY_IMotionState.h"
 #include "SumoPhysicsController.h"
@@ -125,37 +126,35 @@ void SumoPhysicsEnvironment::removeConstraint(int constraintid)
        }
 }
 
-PHY_IPhysicsController* SumoPhysicsEnvironment::rayTest(PHY_IPhysicsController* ignoreClientCtrl
+PHY_IPhysicsController* SumoPhysicsEnvironment::rayTest(PHY_IRayCastFilterCallback &filterCallback
        float fromX,float fromY,float fromZ, 
-       float toX,float toY,float toZ, 
-       float& hitX,float& hitY,float& hitZ,
-       float& normalX,float& normalY,float& normalZ)
+       float toX,float toY,float toZ)
 {
-       SumoPhysicsController* ignoreCtr = static_cast<SumoPhysicsController*> (ignoreClientCtrl);
+       SumoPhysicsController* ignoreCtr = static_cast<SumoPhysicsController*> (filterCallback.m_ignoreController);
 
        //collision detection / raytesting
        MT_Point3 hit, normal;
-       PHY_IPhysicsController *ret = 0;
+       PHY_RayCastResult result;
 
        SM_Object* sm_ignore = 0;
        if (ignoreCtr)
                sm_ignore = ignoreCtr->GetSumoObject();
 
+       memset(&result, 0, sizeof(result));
 
        SM_Object* smOb = m_sumoScene->rayTest(sm_ignore,MT_Point3(fromX, fromY, fromZ),MT_Point3(toX, toY, toZ), hit, normal);
        if (smOb)
        {
-               ret = (PHY_IPhysicsController *) smOb->getPhysicsClientObject();
+               result.m_controller = (PHY_IPhysicsController *) smOb->getPhysicsClientObject();
+               result.m_hitPoint[0] = hit[0];
+               result.m_hitPoint[1] = hit[1];
+               result.m_hitPoint[2] = hit[2];
+               result.m_hitNormal[0] = normal[0];
+               result.m_hitNormal[1] = normal[1];
+               result.m_hitNormal[2] = normal[2];
+               filterCallback.reportHit(&result);
        }
-       hitX = hit[0];
-       hitY = hit[1];
-       hitZ = hit[2];
-
-       normalX = normal[0];
-       normalY = normal[1];
-       normalZ = normal[2];
-       
-       return ret;
+       return result.m_controller;
 }
 //gamelogic callbacks
 void SumoPhysicsEnvironment::addSensor(PHY_IPhysicsController* ctrl)
index 8b9fb46..100adf9 100644 (file)
@@ -75,8 +75,7 @@ public:
                return 0;
        }
 
-       virtual PHY_IPhysicsController* rayTest(PHY_IPhysicsController* ignoreClient,float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                       float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ);
+       virtual PHY_IPhysicsController* rayTest(PHY_IRayCastFilterCallback &filterCallback,float fromX,float fromY,float fromZ, float toX,float toY,float toZ);
 
        
        //gamelogic callbacks
index 5b27506..98496fb 100644 (file)
 #include <vector>
 #include "PHY_DynamicTypes.h"
 class PHY_IVehicle;
+class RAS_MeshObject;
+class PHY_IPhysicsController;
+
+/**
+ * pass back information from rayTest
+ */
+struct PHY_RayCastResult
+{
+       PHY_IPhysicsController* m_controller;   
+       PHY__Vector3                    m_hitPoint;
+       PHY__Vector3                    m_hitNormal;
+       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
+};
+
+/**
+ * This class replaces the ignoreController parameter of rayTest function. 
+ * It allows more sophisticated filtering on the physics controller before computing the ray intersection to save CPU. 
+ * It is only used to its full extend by the Ccd physics environement (Bullet).
+ */
+class PHY_IRayCastFilterCallback
+{
+public:
+       PHY_IPhysicsController* m_ignoreController;
+       bool                                    m_faceNormal;
+
+       virtual         ~PHY_IRayCastFilterCallback()
+       {
+       }
+
+       virtual bool needBroadphaseRayCast(PHY_IPhysicsController* controller)
+       {
+               return true;
+       }
+
+       virtual void reportHit(PHY_RayCastResult* result) = 0;
+
+       PHY_IRayCastFilterCallback(PHY_IPhysicsController* ignoreController, bool faceNormal=false) 
+               :m_ignoreController(ignoreController),
+               m_faceNormal(faceNormal)
+       {
+       }
+};
 
 /**
 *      Physics Environment takes care of stepping the simulation and is a container for physics entities (rigidbodies,constraints, materials etc.)
@@ -94,8 +138,7 @@ class PHY_IPhysicsEnvironment
                //complex constraint for vehicles
                virtual PHY_IVehicle*   getVehicleConstraint(int constraintId) =0;
 
-               virtual PHY_IPhysicsController* rayTest(PHY_IPhysicsController* ignoreClient, float fromX,float fromY,float fromZ, float toX,float toY,float toZ, 
-                                                                               float& hitX,float& hitY,float& hitZ,float& normalX,float& normalY,float& normalZ)=0;
+               virtual PHY_IPhysicsController* rayTest(PHY_IRayCastFilterCallback &filterCallback, float fromX,float fromY,float fromZ, float toX,float toY,float toZ)=0;
 
 
                //Methods for gamelogic collision/physics callbacks
index 033b286..2136ce5 100644 (file)
@@ -300,10 +300,11 @@ class KX_GameObject:
                @rtype: L{KX_GameObject}
                @return: the first object hit or None if no object or object does not match prop
                """
-       def rayCast(to,from,dist,prop):
+       def rayCast(to,from,dist,prop,face,xray,poly):
                """
                Look from a point/object to another point/object and find first object hit within dist that matches prop.
-               Returns a 3-tuple with object reference, hit point and hit normal or (None,None,None) if no hit.
+               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.
                Ex:
                        # shoot along the axis gun-gunAim (gunAim should be collision-free)
                        ob,point,normal = gun.rayCast(gunAim,None,50)
@@ -312,9 +313,18 @@ class KX_GameObject:
 
                Notes:                          
                The ray ignores the object on which the method is called.
-               If is casted from/to object center or explicit [x,y,z] points.
-               The ray does not have X-Ray capability: the first object hit (other than self object) stops the ray
-               If a property was specified and the first object hit does not have that property, there is no hit
+               It is casted from/to object center or explicit [x,y,z] points.
+               The face paremeter determines the orientation of the normal: 
+                 0 => hit normal is always oriented towards the ray origin (as if you casted the ray from outside)
+                 1 => hit normal is the real face normal (only for mesh object, otherwise face has no effect)
+               The ray has X-Ray capability if xray parameter is 1, otherwise the first object hit (other than self object) stops the ray.
+               The prop and xray parameters interact as follow:
+                   prop off, xray off: return closest hit or no hit if there is no object on the full extend of the ray.
+                   prop off, xray on : idem.
+                   prop on,  xray off: return closest hit if it matches prop, no hit otherwise.
+                   prop on,  xray on : return closest hit matching prop or no hit if there is no object matching prop on the full extend of the ray.
+               The L{KX_PolyProxy} 4th element of the return tuple when poly=1 allows to retrieve information on the polygon hit by the ray.
+               If there is no hit or the hit object is not a static mesh, None is returned as 4th element. 
                The     ray ignores collision-free objects and faces that dont have the collision flag enabled, you can however use ghost objects.
 
                @param to: [x,y,z] or object to which the ray is casted
@@ -325,8 +335,17 @@ class KX_GameObject:
                @type dist: float
                @param prop: property name that object must have; can be omitted => detect any object
                @type prop: string
-               @rtype: 3-tuple (L{KX_GameObject}, 3-tuple (x,y,z), 3-tuple (nx,ny,nz))
-               @return: (object,hitpoint,hitnormal) or (None,None,None)
+               @param face: normal option: 1=>return face normal; 0 or omitted => normal is oriented towards origin
+               @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}
+               @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
                """
 
 
index e43fa35..03bc36b 100644 (file)
@@ -95,6 +95,21 @@ class KX_MeshProxy:
                @rtype: L{KX_VertexProxy}
                @return: a vertex object.
                """
+       def getNumPolygons():
+               """
+               Returns the number of polygon in the mesh.
+               
+               @rtype: integer
+               """
+       def getPolygon(index):
+               """
+               Gets the specified polygon from the mesh.
+               
+               @type index: integer
+               @param index: polygon number
+               @rtype: L{KX_PolyProxy}
+               @return: a polygon object.
+               """
        def reinstancePhysicsMesh():
                """
                Updates the physics system with the changed mesh.
diff --git a/source/gameengine/PyDoc/KX_PolyProxy.py b/source/gameengine/PyDoc/KX_PolyProxy.py
new file mode 100644 (file)
index 0000000..45ae30a
--- /dev/null
@@ -0,0 +1,100 @@
+# $Id$
+# Documentation for the polygon proxy class
+
+class KX_PolyProxy:
+       """
+       A polygon holds the index of the vertex forming the poylgon.
+
+       Note: 
+       The polygon attributes are read-only, you need to retrieve the vertex proxy if you want
+       to change the vertex settings. 
+       
+       @ivar matname: The name of polygon material, empty if no material.
+       @type matname: string
+       @ivar material: The material of the polygon
+       @type material: L{KX_PolygonMaterial} or KX_BlenderMaterial
+       @ivar texture: The texture name of the polygon.
+       @type texture: string
+       @ivar matid: The material index of the polygon, use this to retrieve vertex proxy from mesh proxy
+       @type matid: integer
+       @ivar v1: vertex index of the first vertex of the polygon, use this to retrieve vertex proxy from mesh proxy
+       @type v1: integer       
+       @ivar v2: vertex index of the second vertex of the polygon, use this to retrieve vertex proxy from mesh proxy
+       @type v2: integer       
+       @ivar v3: vertex index of the third vertex of the polygon, use this to retrieve vertex proxy from mesh proxy
+       @type v3: integer       
+       @ivar v4: vertex index of the fourth vertex of the polygon, 0 if polygon has only 3 vertex
+                 use this to retrieve vertex proxy from mesh proxy
+       @type v4: integer       
+       @ivar visible: visible state of the polygon: 1=visible, 0=invisible
+       @type visible: integer
+       @ivar collide: collide state of the polygon: 1=receives collision, 0=collision free.
+       @type collide: integer
+       """
+
+       def getMaterialName(): 
+               """
+               Returns the polygon material name with MA prefix
+               
+               @rtype: string
+               @return: material name
+               """
+       def getMaterial(): 
+               """
+               Returns the polygon material
+               
+               @rtype: L{KX_PolygonMaterial} or KX_BlenderMaterial
+               """
+       def getTextureName():
+               """
+               Returns the polygon texture name
+               
+               @rtype: string
+               @return: texture name
+               """
+       def getMaterialIndex():
+               """
+               Returns the material bucket index of the polygon. 
+               This index and the ones returned by getVertexIndex() are needed to retrieve the vertex proxy from L{KX_MeshProxy}.
+               
+               @rtype: integer
+               @return: the material index in the mesh
+       
+       def getNumVertex(): 
+               """
+               Returns the number of vertex of the polygon.
+               
+               @rtype: integer
+               @return: number of vertex, 3 or 4.
+               """
+       def isVisible():
+               """
+               Returns whether the polygon is visible or not
+               
+               @rtype: integer
+               @return: 0=invisible, 1=visible
+               """
+       def isCollider():
+               """
+               Returns whether the polygon is receives collision or not
+               
+               @rtype: integer
+               @return: 0=collision free, 1=receives collision
+               """
+       def getVertexIndex(vertex):
+               """
+               Returns the mesh vertex index of a polygon vertex
+               This index and the one returned by getMaterialIndex() are needed to retrieve the vertex proxy from L{KX_MeshProxy}.
+               
+               @type vertex: integer
+               @param vertex: index of the vertex in the polygon: 0->3
+               @rtype: integer
+               @return: mesh vertex index
+               """
+       def getMesh():
+               """
+               Returns a mesh proxy
+               
+               @rtype: L{KX_MeshProxy}
+               @return: mesh proxy
+               """
index af5228e..a4f7f3f 100644 (file)
@@ -134,7 +134,7 @@ int RAS_MeshObject::NumPolygons()
 
 
 
-RAS_Polygon* RAS_MeshObject::GetPolygon(int num)
+RAS_Polygon* RAS_MeshObject::GetPolygon(int num) const
 {
        return m_Polygons[num];
 }
index 9980666..9a46d89 100644 (file)
@@ -178,7 +178,7 @@ public:
        void                            UpdateMaterialList();
        
        int                                     NumPolygons();
-       RAS_Polygon*            GetPolygon(int num);
+       RAS_Polygon*            GetPolygon(int num) const;
        
        virtual void            Bucketize(
                                                        double* oglmatrix,