From ce70b6bf3b1543ec575e513947d04a34ff1368b9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 28 Aug 2008 11:22:06 +0000 Subject: Apricot Branch: svn merge -r 16266:HEAD https://svn.blender.org/svnroot/bf-blender/trunk/blender --- .../CollisionDispatch/btCollisionWorld.cpp | 39 ++-- .../CollisionDispatch/btCollisionWorld.h | 18 +- .../NarrowPhaseCollision/btRaycastCallback.cpp | 6 +- .../NarrowPhaseCollision/btRaycastCallback.h | 3 +- .../gameengine/ketsji/KX_ketsji.vcproj | 6 + source/blender/include/BDR_gpencil.h | 3 + source/blender/include/BSE_drawipo.h | 3 + source/blender/src/buttons_logic.c | 2 +- source/blender/src/drawgpencil.c | 2 +- source/blender/src/drawipo.c | 2 +- source/blender/src/editobject.c | 2 +- source/blender/src/gpencil.c | 224 ++++++++++++++++++++- source/blender/src/space.c | 2 + .../BlenderRoutines/KX_BlenderRenderTools.cpp | 11 +- .../BlenderRoutines/KX_BlenderRenderTools.h | 4 +- .../gameengine/Converter/KX_ConvertActuators.cpp | 78 +++---- source/gameengine/Expressions/PyObjectPlus.h | 6 + .../GamePlayer/common/GPC_RenderTools.cpp | 12 +- .../gameengine/GamePlayer/common/GPC_RenderTools.h | 4 +- source/gameengine/Ketsji/KX_ConstraintActuator.cpp | 50 +++-- source/gameengine/Ketsji/KX_ConstraintActuator.h | 5 +- source/gameengine/Ketsji/KX_GameObject.cpp | 102 +++++++--- source/gameengine/Ketsji/KX_GameObject.h | 5 +- source/gameengine/Ketsji/KX_IPO_SGController.cpp | 2 +- source/gameengine/Ketsji/KX_MeshProxy.cpp | 31 +++ source/gameengine/Ketsji/KX_MeshProxy.h | 2 + source/gameengine/Ketsji/KX_MouseFocusSensor.cpp | 17 +- source/gameengine/Ketsji/KX_MouseFocusSensor.h | 6 +- source/gameengine/Ketsji/KX_PythonInit.cpp | 53 +++-- source/gameengine/Ketsji/KX_RayCast.cpp | 85 ++++---- source/gameengine/Ketsji/KX_RayCast.h | 60 ++++-- source/gameengine/Ketsji/KX_RaySensor.cpp | 42 ++-- source/gameengine/Ketsji/KX_RaySensor.h | 4 +- source/gameengine/Ketsji/KX_Scene.cpp | 2 + source/gameengine/Ketsji/KX_SoundActuator.cpp | 64 ++++-- .../Physics/BlOde/OdePhysicsEnvironment.cpp | 3 +- .../Physics/BlOde/OdePhysicsEnvironment.h | 3 +- .../Physics/Bullet/CcdPhysicsController.cpp | 5 + .../Physics/Bullet/CcdPhysicsController.h | 15 ++ .../Physics/Bullet/CcdPhysicsEnvironment.cpp | 95 ++++++--- .../Physics/Bullet/CcdPhysicsEnvironment.h | 3 +- .../Physics/Dummy/DummyPhysicsEnvironment.cpp | 3 +- .../Physics/Dummy/DummyPhysicsEnvironment.h | 3 +- .../Physics/Sumo/SumoPhysicsEnvironment.cpp | 31 ++- .../Physics/Sumo/SumoPhysicsEnvironment.h | 3 +- .../Physics/common/PHY_IPhysicsEnvironment.h | 47 ++++- source/gameengine/PyDoc/KX_GameObject.py | 33 ++- source/gameengine/PyDoc/KX_MeshProxy.py | 15 ++ source/gameengine/Rasterizer/RAS_MeshObject.cpp | 2 +- source/gameengine/Rasterizer/RAS_MeshObject.h | 2 +- 50 files changed, 895 insertions(+), 325 deletions(-) diff --git a/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp index b49036a5b50..7dc7d8d2f68 100644 --- a/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp +++ b/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp @@ -181,7 +181,9 @@ void btCollisionWorld::rayTestSingle(const btTransform& rayFromTrans,const btTra btCollisionObject* collisionObject, const btCollisionShape* collisionShape, const btTransform& colObjWorldTransform, - RayResultCallback& resultCallback,short int collisionFilterMask) + RayResultCallback& resultCallback, + short int collisionFilterMask, + bool faceNormal) { btSphereShape pointShape(btScalar(0.0)); @@ -191,14 +193,16 @@ void btCollisionWorld::rayTestSingle(const btTransform& rayFromTrans,const btTra collisionObject, collisionShape, colObjWorldTransform, - resultCallback,collisionFilterMask); + resultCallback,collisionFilterMask,faceNormal); } void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const btTransform& rayFromTrans,const btTransform& rayToTrans, btCollisionObject* collisionObject, const btCollisionShape* collisionShape, const btTransform& colObjWorldTransform, - RayResultCallback& resultCallback,short int collisionFilterMask) + RayResultCallback& resultCallback, + short int collisionFilterMask, + bool faceNormal) { @@ -257,9 +261,9 @@ void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const bt btCollisionObject* m_collisionObject; btTriangleMeshShape* m_triangleMesh; - BridgeTriangleRaycastCallback( const btVector3& from,const btVector3& to, - btCollisionWorld::RayResultCallback* resultCallback, btCollisionObject* collisionObject,btTriangleMeshShape* triangleMesh): - btTriangleRaycastCallback(from,to), + BridgeTriangleRaycastCallback( const btVector3& from,const btVector3& to,bool faceNormal, + btCollisionWorld::RayResultCallback* resultCallback, btCollisionObject* collisionObject,btTriangleMeshShape* triangleMesh): + btTriangleRaycastCallback(from,to,faceNormal), m_resultCallback(resultCallback), m_collisionObject(collisionObject), m_triangleMesh(triangleMesh) @@ -272,6 +276,7 @@ void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const bt btCollisionWorld::LocalShapeInfo shapeInfo; shapeInfo.m_shapePart = partId; shapeInfo.m_triangleIndex = triangleIndex; + shapeInfo.m_triangleShape = m_triangleMesh; btCollisionWorld::LocalRayResult rayResult (m_collisionObject, @@ -287,7 +292,7 @@ void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const bt }; - BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObject,triangleMesh); + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,faceNormal,&resultCallback,collisionObject,triangleMesh); rcb.m_hitFraction = resultCallback.m_closestHitFraction; btVector3 rayAabbMinLocal = rayFromLocal; @@ -313,7 +318,7 @@ void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const bt collisionObject, childCollisionShape, childWorldTrans, - resultCallback, collisionFilterMask); + resultCallback, collisionFilterMask, faceNormal); } @@ -323,7 +328,7 @@ void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const bt } } -void btCollisionWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback,short int collisionFilterMask) +void btCollisionWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback,short int collisionFilterMask, bool faceNormal) { @@ -350,11 +355,17 @@ void btCollisionWorld::rayTest(const btVector3& rayFromWorld, const btVector3& r btVector3 hitNormal; if (btRayAabb(rayFromWorld,rayToWorld,collisionObjectAabbMin,collisionObjectAabbMax,hitLambda,hitNormal)) { - rayTestSingle(rayFromTrans,rayToTrans, - collisionObject, - collisionObject->getCollisionShape(), - collisionObject->getWorldTransform(), - resultCallback); + // before testing this object, verify that it is not filtered out + if (resultCallback.NeedRayCast(collisionObject)) + { + rayTestSingle(rayFromTrans,rayToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + resultCallback, + collisionFilterMask, + faceNormal); + } } } } diff --git a/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.h b/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.h index b6d80233ab7..ed41232ece3 100644 --- a/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.h +++ b/extern/bullet2/src/BulletCollision/CollisionDispatch/btCollisionWorld.h @@ -125,8 +125,8 @@ public: { int m_shapePart; int m_triangleIndex; - - //const btCollisionShape* m_shapeTemp; + // needed in case of compound shape + const btCollisionShape* m_triangleShape; //const btTransform* m_shapeLocalTransform; }; @@ -166,6 +166,10 @@ public: :m_closestHitFraction(btScalar(1.)) { } + virtual bool NeedRayCast(btCollisionObject* object) + { + return true; + } virtual btScalar AddSingleResult(LocalRayResult& rayResult) = 0; }; @@ -209,7 +213,7 @@ public: /// rayTest performs a raycast on all objects in the btCollisionWorld, and calls the resultCallback /// This allows for several queries: first hit, all hits, any hit, dependent on the value returned by the callback. - void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback, short int collisionFilterMask=-1); + void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback, short int collisionFilterMask=-1, bool faceNormal=false); /// rayTestSingle performs a raycast call and calls the resultCallback. It is used internally by rayTest. /// In a future implementation, we consider moving the ray test as a virtual method in btCollisionShape. @@ -218,14 +222,18 @@ public: btCollisionObject* collisionObject, const btCollisionShape* collisionShape, const btTransform& colObjWorldTransform, - RayResultCallback& resultCallback, short int collisionFilterMask=-1); + RayResultCallback& resultCallback, + short int collisionFilterMask=-1, + bool faceNormal=false); /// objectQuerySingle performs a collision detection query and calls the resultCallback. It is used internally by rayTest. static void objectQuerySingle(const btConvexShape* castShape, const btTransform& rayFromTrans,const btTransform& rayToTrans, btCollisionObject* collisionObject, const btCollisionShape* collisionShape, const btTransform& colObjWorldTransform, - RayResultCallback& resultCallback, short int collisionFilterMask=-1); + RayResultCallback& resultCallback, + short int collisionFilterMask=-1, + bool faceNormal=false); void addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup=1,short int collisionFilterMask=1); diff --git a/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp b/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp index 31b91467777..68ac93ec3cc 100644 --- a/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp +++ b/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp @@ -16,10 +16,11 @@ subject to the following restrictions: #include "btRaycastCallback.h" -btTriangleRaycastCallback::btTriangleRaycastCallback(const btVector3& from,const btVector3& to) +btTriangleRaycastCallback::btTriangleRaycastCallback(const btVector3& from,const btVector3& to,bool faceNormal) : m_from(from), m_to(to), + m_faceNormal(faceNormal), m_hitFraction(btScalar(1.)) { @@ -84,8 +85,7 @@ void btTriangleRaycastCallback::processTriangle(btVector3* triangle,int partId, if ( (btScalar)(cp2.dot(triangleNormal)) >=edge_tolerance) { - - if ( dist_a > 0 ) + if (m_faceNormal || dist_a > 0) { m_hitFraction = reportHit(triangleNormal,distance,partId,triangleIndex); } diff --git a/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h b/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h index a0bbc9f8fe9..71ed9fead49 100644 --- a/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h +++ b/extern/bullet2/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h @@ -27,10 +27,11 @@ public: //input btVector3 m_from; btVector3 m_to; + bool m_faceNormal; btScalar m_hitFraction; - btTriangleRaycastCallback(const btVector3& from,const btVector3& to); + btTriangleRaycastCallback(const btVector3& from,const btVector3& to,bool faceNormal); virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); diff --git a/projectfiles_vc7/gameengine/ketsji/KX_ketsji.vcproj b/projectfiles_vc7/gameengine/ketsji/KX_ketsji.vcproj index c046d434cb3..4e362faed69 100644 --- a/projectfiles_vc7/gameengine/ketsji/KX_ketsji.vcproj +++ b/projectfiles_vc7/gameengine/ketsji/KX_ketsji.vcproj @@ -394,6 +394,9 @@ + + @@ -621,6 +624,9 @@ + + diff --git a/source/blender/include/BDR_gpencil.h b/source/blender/include/BDR_gpencil.h index 7340a2e44e0..9b9294b0343 100644 --- a/source/blender/include/BDR_gpencil.h +++ b/source/blender/include/BDR_gpencil.h @@ -76,6 +76,9 @@ void gpencil_delete_laststroke(struct bGPdata *gpd); void gpencil_delete_operation(short mode); void gpencil_delete_menu(void); +void gpencil_convert_operation(short mode); +void gpencil_convert_menu(void); + //short gpencil_paint(short mousebutton); short gpencil_do_paint(struct ScrArea *sa, short mousebutton); diff --git a/source/blender/include/BSE_drawipo.h b/source/blender/include/BSE_drawipo.h index 932f103a579..b8388b2172a 100644 --- a/source/blender/include/BSE_drawipo.h +++ b/source/blender/include/BSE_drawipo.h @@ -42,6 +42,7 @@ struct ScrArea; struct EditIpo; struct View2D; struct rctf; +struct SpaceLink; void calc_ipogrid(void); void draw_ipogrid(void); @@ -50,6 +51,8 @@ void areamouseco_to_ipoco (struct View2D *v2d, short *mval, float *x, float *y); void ipoco_to_areaco (struct View2D *v2d, float *vec, short *mval); void ipoco_to_areaco_noclip (struct View2D *v2d, float *vec, short *mval); +struct View2D *spacelink_get_view2d(struct SpaceLink *sl); + void view2d_do_locks (struct ScrArea *cursa, int flag); void view2d_zoom (struct View2D *v2d, float factor, int winx, int winy); void view2d_getscale (struct View2D *v2d, float *x, float *y); diff --git a/source/blender/src/buttons_logic.c b/source/blender/src/buttons_logic.c index 78bab43a3c1..b7ce16f50a6 100644 --- a/source/blender/src/buttons_logic.c +++ b/source/blender/src/buttons_logic.c @@ -1857,7 +1857,7 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh but = uiDefButBitS(block, TOG, ACT_IPOFORCE, ACT_IPOFORCE, "Force", xco+10+(width-20)/2, yco-24, (width-20)/4-10, 19, &ia->flag, 0, 0, 0, 0, - "Convert Ipo to force. Force is applied in global or local coordinate according to Local flag"); + "Apply Ipo as a global or local force depending on the local option (dynamic objects only)"); uiButSetFunc(but, change_ipo_actuator, but, ia); but = uiDefButBitS(block, TOG, ACT_IPOADD, ACT_IPOADD, diff --git a/source/blender/src/drawgpencil.c b/source/blender/src/drawgpencil.c index dc9a880086f..4b6b5ab26c8 100644 --- a/source/blender/src/drawgpencil.c +++ b/source/blender/src/drawgpencil.c @@ -177,7 +177,7 @@ static void gp_drawui_layer (uiBlock *block, bGPdata *gpd, bGPDlayer *gpl, short if (gpl->flag & (GP_LAYER_LOCKED|GP_LAYER_HIDE)) { char name[256]; /* gpl->info is 128, but we need space for 'locked/hidden' as well */ - height= 26; + height= 0; /* visibility button (only if hidden but not locked!) */ if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED)) diff --git a/source/blender/src/drawipo.c b/source/blender/src/drawipo.c index 1b5aacd3629..7e81385930c 100644 --- a/source/blender/src/drawipo.c +++ b/source/blender/src/drawipo.c @@ -442,7 +442,7 @@ int in_ipo_buttons(void) else return 1; } -static View2D *spacelink_get_view2d(SpaceLink *sl) +View2D *spacelink_get_view2d(SpaceLink *sl) { if(sl->spacetype==SPACE_IPO) return &((SpaceIpo *)sl)->v2d; diff --git a/source/blender/src/editobject.c b/source/blender/src/editobject.c index fee967bcd9a..1ba7bce6dab 100644 --- a/source/blender/src/editobject.c +++ b/source/blender/src/editobject.c @@ -172,6 +172,7 @@ #include "BDR_drawobject.h" #include "BDR_editcurve.h" #include "BDR_unwrapper.h" +#include "BDR_gpencil.h" #include #include "mydevice.h" @@ -2827,7 +2828,6 @@ void convertmenu(void) if(G.scene->id.lib) return; obact= OBACT; - if(obact==0) return; if(!obact->flag & SELECT) return; if(G.obedit) return; diff --git a/source/blender/src/gpencil.c b/source/blender/src/gpencil.c index 0b28e778f62..0148ace20d4 100644 --- a/source/blender/src/gpencil.c +++ b/source/blender/src/gpencil.c @@ -46,7 +46,9 @@ #include "BLI_blenlib.h" #include "DNA_listBase.h" +#include "DNA_curve_types.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -57,6 +59,7 @@ #include "BKE_global.h" #include "BKE_utildefines.h" #include "BKE_blender.h" +#include "BKE_curve.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -74,6 +77,8 @@ #include "BDR_gpencil.h" #include "BIF_drawgpencil.h" +#include "BDR_editobject.h" + #include "BSE_drawipo.h" #include "BSE_headerbuttons.h" #include "BSE_view.h" @@ -678,6 +683,208 @@ void gpencil_delete_menu (void) gpencil_delete_operation(mode); } +/* --------- Data Conversion ---------- */ + +/* convert the coordinates from the given stroke point into 3d-coordinates */ +static void gp_strokepoint_convertcoords (bGPDstroke *gps, bGPDspoint *pt, float p3d[3]) +{ + if (gps->flag & GP_STROKE_3DSPACE) { + /* directly use 3d-coordinates */ + // FIXME: maybe we need to counterotate this for object rotation? + VecCopyf(p3d, &pt->x); + } + else { + short mval[2], mx, my; + float *fp= give_cursor(); + float dvec[3]; + + /* get screen coordinate */ + if (gps->flag & GP_STROKE_2DSPACE) { + View2D *v2d= spacelink_get_view2d(curarea->spacedata.first); + ipoco_to_areaco_noclip(v2d, &pt->x, mval); + } + else { + mval[0]= (pt->x / 1000 * curarea->winx); + mval[1]= (pt->y / 1000 * curarea->winy); + } + mx= mval[0]; + my= mval[1]; + + /* convert screen coordinate to 3d coordinates + * - method taken from editview.c - mouse_cursor() + */ + project_short_noclip(fp, mval); + window_to_3d(dvec, mval[0]-mx, mval[1]-my); + VecSubf(p3d, fp, dvec); + } +} + +/* convert stroke to 3d path */ +static void gp_layer_to_path (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu) +{ + bGPDspoint *pt; + Nurb *nu; + BPoint *bp; + int i; + + /* create new 'nurb' within the curve */ + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); + + nu->pntsu= gps->totpoints; + nu->pntsv= 1; + nu->orderu= gps->totpoints; + nu->flagu= 2; /* endpoint */ + nu->resolu= 32; + + nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints"); + + /* add points */ + for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) { + float p3d[3]; + + /* get coordinates to add at */ + gp_strokepoint_convertcoords(gps, pt, p3d); + VecCopyf(bp->vec, p3d); + + /* set settings */ + bp->f1= SELECT; + bp->radius = bp->weight = pt->pressure * gpl->thickness; + } + + /* add nurb to curve */ + BLI_addtail(&cu->nurb, nu); +} + +/* convert stroke to 3d bezier */ +static void gp_layer_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu) +{ + bGPDspoint *pt; + Nurb *nu; + BezTriple *bezt; + int i; + + /* create new 'nurb' within the curve */ + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); + + nu->pntsu= gps->totpoints; + nu->resolu= 12; + nu->resolv= 12; + nu->type= CU_BEZIER; + nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts"); + + /* add points */ + for (i=0, pt=gps->points, bezt=nu->bezt; i < gps->totpoints; i++, pt++, bezt++) { + float p3d[3]; + + /* get coordinates to add at */ + gp_strokepoint_convertcoords(gps, pt, p3d); + + /* TODO: maybe in future the handles shouldn't be in same place */ + VecCopyf(bezt->vec[0], p3d); + VecCopyf(bezt->vec[1], p3d); + VecCopyf(bezt->vec[2], p3d); + + /* set settings */ + bezt->h1= bezt->h2= HD_FREE; + bezt->f1= bezt->f2= bezt->f3= SELECT; + bezt->radius = bezt->weight = pt->pressure * gpl->thickness; + } + + /* must calculate handles or else we crash */ + calchandlesNurb(nu); + + /* add nurb to curve */ + BLI_addtail(&cu->nurb, nu); +} + +/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ +static void gp_layer_to_curve (bGPdata *gpd, bGPDlayer *gpl, short mode) +{ + bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0); + bGPDstroke *gps; + Object *ob; + Curve *cu; + + /* error checking */ + if (ELEM3(NULL, gpd, gpl, gpf)) + return; + + /* only convert if there are any strokes on this layer's frame to convert */ + if (gpf->strokes.first == NULL) + return; + + /* initialise the curve */ + cu= add_curve(gpl->info, 1); + cu->flag |= CU_3D; + + /* init the curve object (remove rotation and assign curve data to it) */ + add_object_draw(OB_CURVE); + ob= OBACT; + ob->loc[0]= ob->loc[1]= ob->loc[2]= 0; + ob->rot[0]= ob->rot[1]= ob->rot[2]= 0; + ob->data= cu; + + /* add points to curve */ + for (gps= gpf->strokes.first; gps; gps= gps->next) { + switch (mode) { + case 1: + gp_layer_to_path(gpl, gps, cu); + break; + case 2: + gp_layer_to_bezier(gpl, gps, cu); + break; + } + } +} + +/* convert grease-pencil strokes to another representation + * mode: 1 - Active layer to path + * 2 - Active layer to bezier + */ +void gpencil_convert_operation (short mode) +{ + bGPdata *gpd; + float *fp= give_cursor(); + + /* get datablock to work on */ + gpd= gpencil_data_getactive(NULL); + if (gpd == NULL) return; + + /* initialise 3d-cursor correction globals */ + initgrabz(fp[0], fp[1], fp[2]); + + /* handle selection modes */ + switch (mode) { + case 1: /* active layer only (to path) */ + case 2: /* active layer only (to bezier) */ + { + bGPDlayer *gpl= gpencil_layer_getactive(gpd); + gp_layer_to_curve(gpd, gpl, mode); + } + break; + } + + /* redraw and undo-push */ + BIF_undo_push("GPencil Convert"); + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWOOPS, 0); +} + +/* display a menu for converting grease-pencil strokes */ +void gpencil_convert_menu (void) +{ + bGPdata *gpd= gpencil_data_getactive(NULL); + short mode; + + /* only show menu if it will be relevant */ + if (gpd == NULL) return; + + mode= pupmenu("Grease Pencil Convert %t|Active Layer To Path%x1|Active Layer to Bezier%x2"); + if (mode <= 0) return; + + gpencil_convert_operation(mode); +} + /* ************************************************** */ /* GREASE-PENCIL EDITING MODE - Painting */ @@ -1113,12 +1320,11 @@ static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i) /* eraser tool - check if part of stroke occurs within last segment drawn by eraser */ static short gp_stroke_eraser_strokeinside (short mval[], short mvalo[], short rad, short x0, short y0, short x1, short y1) { - /* step 1: check if within the radius for the new one */ - /* simple within-radius check */ + /* simple within-radius check for now */ if (edge_inside_circle(mval[0], mval[1], rad, x0, y0, x1, y1)) return 1; - /* step 2: check if within the quad formed between the two eraser coords */ + /* not inside */ return 0; } @@ -1203,7 +1409,6 @@ static void gp_stroke_eraser_dostroke (tGPsdata *p, short mval[], short mvalo[], /* check if point segment of stroke had anything to do with * eraser region (either within stroke painted, or on its lines) * - this assumes that linewidth is irrelevant - * - handled using the lasso-select checking code */ if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) { /* if function returns true, break this loop (as no more point to check) */ @@ -1288,6 +1493,12 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode) case SPACE_SEQ: { /* for now, this is not applicable here... */ + //p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE; + } + break; + case SPACE_IMAGE: + { + p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE; } break; } @@ -1298,10 +1509,7 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode) static void gp_paint_strokeend (tGPsdata *p) { /* check if doing eraser or not */ - if (p->gpd->sbuffer_sflag & GP_STROKE_ERASER) { - /* don't do anything */ - } - else { + if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) { /* transfer stroke to frame */ gp_stroke_newfrombuffer(p); } diff --git a/source/blender/src/space.c b/source/blender/src/space.c index 1483bddf1b3..066c1c750ee 100644 --- a/source/blender/src/space.c +++ b/source/blender/src/space.c @@ -1916,6 +1916,8 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) else copy_attr_menu(); } + else if(G.qual==(LR_ALTKEY|LR_SHIFTKEY)) + gpencil_convert_menu(); /* gpencil.c */ else if(G.qual==LR_ALTKEY) { if(ob && (ob->flag & OB_POSEMODE)) pose_clear_constraints(); /* poseobject.c */ diff --git a/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp b/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp index 07cbb2ecf8f..1797d6c1a0f 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp +++ b/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp @@ -145,11 +145,11 @@ void KX_BlenderRenderTools::SetClientObject(RAS_IRasterizer *rasty, 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(); @@ -250,9 +250,8 @@ 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(this, oglmatrix))) + KX_RayCast::Callback callback(this, physics_controller, oglmatrix); + if (!KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback)) { // couldn't find something to cast the shadow on... glMultMatrixd(oglmatrix); diff --git a/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.h b/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.h index 7d52c6905bf..a7618462c9b 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.h +++ b/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.h @@ -38,6 +38,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 part of the (polygon) Rasterizer. Effects like 2D text, 3D (polygon) @@ -83,7 +84,8 @@ public: void PushMatrix(); void PopMatrix(); - 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); diff --git a/source/gameengine/Converter/KX_ConvertActuators.cpp b/source/gameengine/Converter/KX_ConvertActuators.cpp index 7771d710e46..321fc325bb8 100644 --- a/source/gameengine/Converter/KX_ConvertActuators.cpp +++ b/source/gameengine/Converter/KX_ConvertActuators.cpp @@ -406,50 +406,51 @@ void BL_ConvertActuators(char* maggiename, /* Note, allowing actuators for sounds that are not there was added since 2.47 * This is because python may expect the actuator and raise an exception if it dosnt find it * better just to add a dummy sound actuator. */ - /*if (sampleisloaded)*/ - - /* setup the SND_SoundObject */ - SND_SoundObject* sndobj = new SND_SoundObject(); - sndobj->SetSampleName(samplename.Ptr()); - sndobj->SetObjectName(bact->name); - if (soundact->sound) { - sndobj->SetRollOffFactor(soundact->sound->attenuation); - sndobj->SetGain(soundact->sound->volume); - sndobj->SetPitch(exp((soundact->sound->pitch / 12.0) * log(2.0))); - // sndobj->SetLoopStart(soundact->sound->loopstart); - // sndobj->SetLoopStart(soundact->sound->loopend); - if (soundact->sound->flags & SOUND_FLAGS_LOOP) - { - if (soundact->sound->flags & SOUND_FLAGS_BIDIRECTIONAL_LOOP) - sndobj->SetLoopMode(SND_LOOP_BIDIRECTIONAL); + SND_SoundObject* sndobj = NULL; + if (sampleisloaded) + { + /* setup the SND_SoundObject */ + sndobj = new SND_SoundObject(); + sndobj->SetSampleName(samplename.Ptr()); + sndobj->SetObjectName(bact->name); + if (soundact->sound) { + sndobj->SetRollOffFactor(soundact->sound->attenuation); + sndobj->SetGain(soundact->sound->volume); + sndobj->SetPitch(exp((soundact->sound->pitch / 12.0) * log(2.0))); + // sndobj->SetLoopStart(soundact->sound->loopstart); + // sndobj->SetLoopStart(soundact->sound->loopend); + if (soundact->sound->flags & SOUND_FLAGS_LOOP) + { + if (soundact->sound->flags & SOUND_FLAGS_BIDIRECTIONAL_LOOP) + sndobj->SetLoopMode(SND_LOOP_BIDIRECTIONAL); + else + sndobj->SetLoopMode(SND_LOOP_NORMAL); + } + else { + sndobj->SetLoopMode(SND_LOOP_OFF); + } + + if (soundact->sound->flags & SOUND_FLAGS_PRIORITY) + sndobj->SetHighPriority(true); else - sndobj->SetLoopMode(SND_LOOP_NORMAL); + sndobj->SetHighPriority(false); + + if (soundact->sound->flags & SOUND_FLAGS_3D) + sndobj->Set3D(true); + else + sndobj->Set3D(false); } else { + /* dummy values for a NULL sound + * see editsound.c - defaults are unlikely to change soon */ + sndobj->SetRollOffFactor(1.0); + sndobj->SetGain(1.0); + sndobj->SetPitch(1.0); sndobj->SetLoopMode(SND_LOOP_OFF); - } - - if (soundact->sound->flags & SOUND_FLAGS_PRIORITY) - sndobj->SetHighPriority(true); - else sndobj->SetHighPriority(false); - - if (soundact->sound->flags & SOUND_FLAGS_3D) - sndobj->Set3D(true); - else sndobj->Set3D(false); + } } - else { - /* dummy values for a NULL sound - * see editsound.c - defaults are unlikely to change soon */ - sndobj->SetRollOffFactor(1.0); - sndobj->SetGain(1.0); - sndobj->SetPitch(1.0); - sndobj->SetLoopMode(SND_LOOP_OFF); - sndobj->SetHighPriority(false); - sndobj->Set3D(false); - } - KX_SoundActuator* tmpsoundact = new KX_SoundActuator(gameobj, sndobj, @@ -460,7 +461,8 @@ void BL_ConvertActuators(char* maggiename, tmpsoundact->SetName(bact->name); baseact = tmpsoundact; - soundscene->AddObject(sndobj); + if (sndobj) + soundscene->AddObject(sndobj); } break; } diff --git a/source/gameengine/Expressions/PyObjectPlus.h b/source/gameengine/Expressions/PyObjectPlus.h index deb47322d49..65cd4e890f7 100644 --- a/source/gameengine/Expressions/PyObjectPlus.h +++ b/source/gameengine/Expressions/PyObjectPlus.h @@ -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 diff --git a/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp b/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp index 986ed46455d..78d8eaf2aa3 100644 --- a/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp +++ b/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp @@ -150,12 +150,11 @@ void GPC_RenderTools::SetClientObject(RAS_IRasterizer *rasty, 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(); @@ -256,9 +255,8 @@ 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(this, oglmatrix))) + KX_RayCast::Callback callback(this, physics_controller, oglmatrix); + if (!KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback)) { // couldn't find something to cast the shadow on... glMultMatrixd(oglmatrix); diff --git a/source/gameengine/GamePlayer/common/GPC_RenderTools.h b/source/gameengine/GamePlayer/common/GPC_RenderTools.h index 18401ce6577..382956e73ea 100644 --- a/source/gameengine/GamePlayer/common/GPC_RenderTools.h +++ b/source/gameengine/GamePlayer/common/GPC_RenderTools.h @@ -41,6 +41,7 @@ #include "BMF_Api.h" struct KX_ClientObjectInfo; +class KX_RayCast; /* BlenderRenderTools are a set of tools to apply 2D/3D graphics effects, which * are not part of the (polygon) Rasterizer. Effects like 2D text, 3D (polygon) @@ -89,7 +90,8 @@ public: void PushMatrix(); void PopMatrix(); - 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); diff --git a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp index e00ec68ad33..4b57b0e8c54 100644 --- a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp +++ b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp @@ -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(this)); - + KX_RayCast::Callback 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; diff --git a/source/gameengine/Ketsji/KX_ConstraintActuator.h b/source/gameengine/Ketsji/KX_ConstraintActuator.h index d9f39124cac..6ec4de9aad9 100644 --- a/source/gameengine/Ketsji/KX_ConstraintActuator.h +++ b/source/gameengine/Ketsji/KX_ConstraintActuator.h @@ -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, diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index e49172236d6..71bb1fc90d8 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -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 // printf #include "SG_Controller.h" #include "KX_IPhysicsController.h" @@ -86,6 +87,7 @@ KX_GameObject::KX_GameObject( m_bCulled(true), m_pPhysicsController1(NULL), m_pPhysicsEnvironment(NULL), + m_xray(false), m_pHitObject(NULL), m_isDeformable(false) { @@ -1614,25 +1616,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, @@ -1672,8 +1694,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(); @@ -1687,7 +1707,8 @@ 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(this)); + KX_RayCast::Callback callback(this,spc); + KX_RayCast::RayTest(pe, fromPoint, toPoint, callback); if (m_pHitObject) { @@ -1698,13 +1719,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; @@ -1713,8 +1745,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 } @@ -1760,8 +1793,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(); @@ -1775,20 +1806,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(this)); + m_xray = xray; + // to get the hit results + KX_RayCast::Callback 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); } /* --------------------------------------------------------------------- diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 8581e359f47..9dcf6526448 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -54,6 +54,7 @@ //Forward declarations. struct KX_ClientObjectInfo; +class KX_RayCast; class RAS_MeshObject; class KX_IPhysicsController; class PHY_IPhysicsEnvironment; @@ -90,6 +91,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; @@ -440,7 +442,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); /** diff --git a/source/gameengine/Ketsji/KX_IPO_SGController.cpp b/source/gameengine/Ketsji/KX_IPO_SGController.cpp index d3aa924665e..6223643f75a 100644 --- a/source/gameengine/Ketsji/KX_IPO_SGController.cpp +++ b/source/gameengine/Ketsji/KX_IPO_SGController.cpp @@ -143,7 +143,7 @@ bool KX_IpoSGController::Update(double currentTime) { if (m_ipo_as_force == true) { - if (m_game_object && ob) + if (m_game_object && ob && m_game_object->GetPhysicsController()) { m_game_object->GetPhysicsController()->ApplyForce(m_ipo_local ? ob->GetWorldOrientation() * m_ipo_xform.GetPosition() : diff --git a/source/gameengine/Ketsji/KX_MeshProxy.cpp b/source/gameengine/Ketsji/KX_MeshProxy.cpp index 12d983eed21..5bfe8c70cba 100644 --- a/source/gameengine/Ketsji/KX_MeshProxy.cpp +++ b/source/gameengine/Ketsji/KX_MeshProxy.cpp @@ -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}, @@ -147,6 +150,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) @@ -235,6 +244,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.") { diff --git a/source/gameengine/Ketsji/KX_MeshProxy.h b/source/gameengine/Ketsji/KX_MeshProxy.h index 7c6202c15a4..3335c349673 100644 --- a/source/gameengine/Ketsji/KX_MeshProxy.h +++ b/source/gameengine/Ketsji/KX_MeshProxy.h @@ -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); }; diff --git a/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp b/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp index db0bef8b7e1..3156f543ed5 100644 --- a/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp +++ b/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp @@ -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,8 @@ bool KX_MouseFocusSensor::ParentObjectHasFocus(void) bool result = false; - result = KX_RayCast::RayTest(physics_controller, physics_environment, frompoint3, topoint3, resultpoint, resultnormal, KX_RayCast::Callback(this)); + KX_RayCast::Callback callback(this,physics_controller); + KX_RayCast::RayTest(physics_environment, frompoint3, topoint3, callback); result = (m_hitObject!=0); diff --git a/source/gameengine/Ketsji/KX_MouseFocusSensor.h b/source/gameengine/Ketsji/KX_MouseFocusSensor.h index b011ebe1288..a6cc39d66eb 100644 --- a/source/gameengine/Ketsji/KX_MouseFocusSensor.h +++ b/source/gameengine/Ketsji/KX_MouseFocusSensor.h @@ -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_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp index 2d8462ec622..cb215958cc4 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.cpp +++ b/source/gameengine/Ketsji/KX_PythonInit.cpp @@ -933,13 +933,30 @@ PyObject* initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack // override builtin functions import() and open() -PyObject *KXpy_open(PyObject *self, PyObject *args) -{ +PyObject *KXpy_open(PyObject *self, PyObject *args) { PyErr_SetString(PyExc_RuntimeError, "Sandbox: open() function disabled!\nGame Scripts should not use this function."); return NULL; } +PyObject *KXpy_reload(PyObject *self, PyObject *args) { + PyErr_SetString(PyExc_RuntimeError, "Sandbox: reload() function disabled!\nGame Scripts should not use this function."); + return NULL; +} + +PyObject *KXpy_file(PyObject *self, PyObject *args) { + PyErr_SetString(PyExc_RuntimeError, "Sandbox: file() function disabled!\nGame Scripts should not use this function."); + return NULL; +} +PyObject *KXpy_execfile(PyObject *self, PyObject *args) { + PyErr_SetString(PyExc_RuntimeError, "Sandbox: execfile() function disabled!\nGame Scripts should not use this function."); + return NULL; +} + +PyObject *KXpy_compile(PyObject *self, PyObject *args) { + PyErr_SetString(PyExc_RuntimeError, "Sandbox: compile() function disabled!\nGame Scripts should not use this function."); + return NULL; +} PyObject *KXpy_import(PyObject *self, PyObject *args) { @@ -976,19 +993,13 @@ PyObject *KXpy_import(PyObject *self, PyObject *args) } +static PyMethodDef meth_open[] = {{ "open", KXpy_open, METH_VARARGS, "(disabled)"}}; +static PyMethodDef meth_reload[] = {{ "reload", KXpy_reload, METH_VARARGS, "(disabled)"}}; +static PyMethodDef meth_file[] = {{ "file", KXpy_file, METH_VARARGS, "(disabled)"}}; +static PyMethodDef meth_execfile[] = {{ "execfile", KXpy_execfile, METH_VARARGS, "(disabled)"}}; +static PyMethodDef meth_compile[] = {{ "compile", KXpy_compile, METH_VARARGS, "(disabled)"}}; -static PyMethodDef meth_open[] = { - { "open", KXpy_open, METH_VARARGS, - "(disabled)"} -}; - - -static PyMethodDef meth_import[] = { - { "import", KXpy_import, METH_VARARGS, - "our own import"} -}; - - +static PyMethodDef meth_import[] = {{ "import", KXpy_import, METH_VARARGS, "our own import"}}; //static PyObject *g_oldopen = 0; //static PyObject *g_oldimport = 0; @@ -999,15 +1010,21 @@ void setSandbox(TPythonSecurityLevel level) { PyObject *m = PyImport_AddModule("__builtin__"); PyObject *d = PyModule_GetDict(m); - PyObject *meth = PyCFunction_New(meth_open, NULL); switch (level) { case psl_Highest: //if (!g_security) { //g_oldopen = PyDict_GetItemString(d, "open"); - PyDict_SetItemString(d, "open", meth); - meth = PyCFunction_New(meth_import, NULL); - PyDict_SetItemString(d, "__import__", meth); + + // functions we cant trust + PyDict_SetItemString(d, "open", PyCFunction_New(meth_open, NULL)); + PyDict_SetItemString(d, "reload", PyCFunction_New(meth_reload, NULL)); + PyDict_SetItemString(d, "file", PyCFunction_New(meth_file, NULL)); + PyDict_SetItemString(d, "execfile", PyCFunction_New(meth_execfile, NULL)); + PyDict_SetItemString(d, "compile", PyCFunction_New(meth_compile, NULL)); + + // our own import + PyDict_SetItemString(d, "__import__", PyCFunction_New(meth_import, NULL)); //g_security = level; //} break; diff --git a/source/gameengine/Ketsji/KX_RayCast.cpp b/source/gameengine/Ketsji/KX_RayCast.cpp index 89e2d645d54..974d4b992a6 100644 --- a/source/gameengine/Ketsji/KX_RayCast.cpp +++ b/source/gameengine/Ketsji/KX_RayCast.cpp @@ -40,7 +40,21 @@ #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(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(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(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; } diff --git a/source/gameengine/Ketsji/KX_RayCast.h b/source/gameengine/Ketsji/KX_RayCast.h index 607dabd8afc..c3084c997a1 100644 --- a/source/gameengine/Ketsji/KX_RayCast.h +++ b/source/gameengine/Ketsji/KX_RayCast.h @@ -30,12 +30,14 @@ #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 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(controller->getNewClientInfo()); + + if (!info) + { + MT_assert(info && "Physics controller with no client object info"); + return false; + } + return self->NeedRayCast(info); + } + }; diff --git a/source/gameengine/Ketsji/KX_RaySensor.cpp b/source/gameengine/Ketsji/KX_RaySensor.cpp index a416c8c9f89..57101b769ea 100644 --- a/source/gameengine/Ketsji/KX_RaySensor.cpp +++ b/source/gameengine/Ketsji/KX_RaySensor.cpp @@ -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,8 @@ 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(this)); + KX_RayCast::Callback callback(this, spc); + KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback); /* now pass this result to some controller */ @@ -265,6 +271,10 @@ bool KX_RaySensor::Evaluate(CValue* event) // notify logicsystem that ray JUST left the Object result = true; } + else + { + result = false; + } } if (reset) diff --git a/source/gameengine/Ketsji/KX_RaySensor.h b/source/gameengine/Ketsji/KX_RaySensor.h index f4305b053d1..9e53f80b69c 100644 --- a/source/gameengine/Ketsji/KX_RaySensor.h +++ b/source/gameengine/Ketsji/KX_RaySensor.h @@ -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); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 1b021011e85..2ad0412f1bf 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -951,6 +951,8 @@ int KX_Scene::NewRemoveObject(class CValue* gameobj) newobj->RemoveMeshes(); ret = 1; + if (m_lightlist->RemoveValue(newobj)) // TODO - use newobj->IsLight() test when its merged in from apricot. - Campbell + ret = newobj->Release(); if (m_objectlist->RemoveValue(newobj)) ret = newobj->Release(); if (m_tempObjectList->RemoveValue(newobj)) diff --git a/source/gameengine/Ketsji/KX_SoundActuator.cpp b/source/gameengine/Ketsji/KX_SoundActuator.cpp index 34a3baec093..f75a1ee5c62 100644 --- a/source/gameengine/Ketsji/KX_SoundActuator.cpp +++ b/source/gameengine/Ketsji/KX_SoundActuator.cpp @@ -69,11 +69,11 @@ KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj, KX_SoundActuator::~KX_SoundActuator() { - //m_soundScene->RemoveObject(this->m_soundObject); - //(this->m_soundObject)->DeleteWhenFinished(); - m_soundScene->RemoveActiveObject(m_soundObject); -// m_soundScene->DeleteObjectWhenFinished(m_soundObject); - m_soundScene->DeleteObject(m_soundObject); + if (m_soundObject) + { + m_soundScene->RemoveActiveObject(m_soundObject); + m_soundScene->DeleteObject(m_soundObject); + } } @@ -82,9 +82,12 @@ CValue* KX_SoundActuator::GetReplica() { KX_SoundActuator* replica = new KX_SoundActuator(*this); replica->ProcessReplica(); - SND_SoundObject* soundobj = new SND_SoundObject(*m_soundObject); - replica->setSoundObject(soundobj); - m_soundScene->AddObject(soundobj); + if (m_soundObject) + { + SND_SoundObject* soundobj = new SND_SoundObject(*m_soundObject); + replica->setSoundObject(soundobj); + m_soundScene->AddObject(soundobj); + } // this will copy properties and so on... CValue::AddDataToReplica(replica); @@ -104,6 +107,9 @@ bool KX_SoundActuator::Update(double curtime, bool frame) RemoveAllEvents(); + if (!m_soundObject) + return false; + if (m_pino) { bNegativeEvent = true; @@ -287,6 +293,10 @@ PyObject* KX_SoundActuator::PySetFilename(PyObject* self, PyObject* args, PyObje PyObject* KX_SoundActuator::PyGetFilename(PyObject* self, PyObject* args, PyObject* kwds) { + if (!m_soundObject) + { + return PyString_FromString(""); + } STR_String objectname = m_soundObject->GetObjectName(); char* name = objectname.Ptr(); @@ -301,7 +311,8 @@ PyObject* KX_SoundActuator::PyGetFilename(PyObject* self, PyObject* args, PyObje PyObject* KX_SoundActuator::PyStartSound(PyObject* self, PyObject* args, PyObject* kwds) { - m_soundObject->StartSound(); + if (m_soundObject) + m_soundObject->StartSound(); Py_Return; } @@ -309,7 +320,8 @@ PyObject* KX_SoundActuator::PyStartSound(PyObject* self, PyObject* args, PyObjec PyObject* KX_SoundActuator::PyPauseSound(PyObject* self, PyObject* args, PyObject* kwds) { - m_soundObject->PauseSound(); + if (m_soundObject) + m_soundObject->PauseSound(); Py_Return; } @@ -317,7 +329,8 @@ PyObject* KX_SoundActuator::PyPauseSound(PyObject* self, PyObject* args, PyObjec PyObject* KX_SoundActuator::PyStopSound(PyObject* self, PyObject* args, PyObject* kwds) { - m_soundObject->StopSound(); + if (m_soundObject) + m_soundObject->StopSound(); Py_Return; } @@ -329,7 +342,8 @@ PyObject* KX_SoundActuator::PySetGain(PyObject* self, PyObject* args, PyObject* if (!PyArg_ParseTuple(args, "f", &gain)) return NULL; - m_soundObject->SetGain(gain); + if (m_soundObject) + m_soundObject->SetGain(gain); Py_Return; } @@ -338,7 +352,7 @@ PyObject* KX_SoundActuator::PySetGain(PyObject* self, PyObject* args, PyObject* PyObject* KX_SoundActuator::PyGetGain(PyObject* self, PyObject* args, PyObject* kwds) { - float gain = m_soundObject->GetGain(); + float gain = (m_soundObject) ? m_soundObject->GetGain() : 1.0f; PyObject* result = PyFloat_FromDouble(gain); return result; @@ -352,7 +366,8 @@ PyObject* KX_SoundActuator::PySetPitch(PyObject* self, PyObject* args, PyObject* if (!PyArg_ParseTuple(args, "f", &pitch)) return NULL; - m_soundObject->SetPitch(pitch); + if (m_soundObject) + m_soundObject->SetPitch(pitch); Py_Return; } @@ -361,7 +376,7 @@ PyObject* KX_SoundActuator::PySetPitch(PyObject* self, PyObject* args, PyObject* PyObject* KX_SoundActuator::PyGetPitch(PyObject* self, PyObject* args, PyObject* kwds) { - float pitch = m_soundObject->GetPitch(); + float pitch = (m_soundObject) ? m_soundObject->GetPitch() : 1.0; PyObject* result = PyFloat_FromDouble(pitch); return result; @@ -375,7 +390,8 @@ PyObject* KX_SoundActuator::PySetRollOffFactor(PyObject* self, PyObject* args, P if (!PyArg_ParseTuple(args, "f", &rollofffactor)) return NULL; - m_soundObject->SetRollOffFactor(rollofffactor); + if (m_soundObject) + m_soundObject->SetRollOffFactor(rollofffactor); Py_Return; } @@ -384,7 +400,7 @@ PyObject* KX_SoundActuator::PySetRollOffFactor(PyObject* self, PyObject* args, P PyObject* KX_SoundActuator::PyGetRollOffFactor(PyObject* self, PyObject* args, PyObject* kwds) { - float rollofffactor = m_soundObject->GetRollOffFactor(); + float rollofffactor = (m_soundObject) ? m_soundObject->GetRollOffFactor() : 1.0; PyObject* result = PyFloat_FromDouble(rollofffactor); return result; @@ -398,7 +414,8 @@ PyObject* KX_SoundActuator::PySetLooping(PyObject* self, PyObject* args, PyObjec if (!PyArg_ParseTuple(args, "i", &looping)) return NULL; - m_soundObject->SetLoopMode(looping); + if (m_soundObject) + m_soundObject->SetLoopMode(looping); Py_Return; } @@ -407,7 +424,7 @@ PyObject* KX_SoundActuator::PySetLooping(PyObject* self, PyObject* args, PyObjec PyObject* KX_SoundActuator::PyGetLooping(PyObject* self, PyObject* args, PyObject* kwds) { - int looping = m_soundObject->GetLoopMode(); + int looping = (m_soundObject) ? m_soundObject->GetLoopMode() : SND_LOOP_OFF; PyObject* result = PyInt_FromLong(looping); return result; @@ -425,7 +442,8 @@ PyObject* KX_SoundActuator::PySetPosition(PyObject* self, PyObject* args, PyObje if (!PyArg_ParseTuple(args, "fff", &pos[0], &pos[1], &pos[2])) return NULL; - m_soundObject->SetPosition(pos); + if (m_soundObject) + m_soundObject->SetPosition(pos); Py_Return; } @@ -442,7 +460,8 @@ PyObject* KX_SoundActuator::PySetVelocity(PyObject* self, PyObject* args, PyObje if (!PyArg_ParseTuple(args, "fff", &vel[0], &vel[1], &vel[2])) return NULL; - m_soundObject->SetVelocity(vel); + if (m_soundObject) + m_soundObject->SetVelocity(vel); Py_Return; } @@ -465,7 +484,8 @@ PyObject* KX_SoundActuator::PySetOrientation(PyObject* self, PyObject* args, PyO if (!PyArg_ParseTuple(args, "fffffffff", &ori[0][0], &ori[0][1], &ori[0][2], &ori[1][0], &ori[1][1], &ori[1][2], &ori[2][0], &ori[2][1], &ori[2][2])) return NULL; - m_soundObject->SetOrientation(ori); + if (m_soundObject) + m_soundObject->SetOrientation(ori); Py_Return; } diff --git a/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp index a04560aaf09..6d9b41e08d2 100644 --- a/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp +++ b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.cpp @@ -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 diff --git a/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h index 7c61902f8e2..dcc87d614c0 100644 --- a/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h +++ b/source/gameengine/Physics/BlOde/OdePhysicsEnvironment.h @@ -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 diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp index a49814e86b6..8fd89295b03 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp @@ -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; @@ -833,6 +835,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) @@ -849,6 +852,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++; } } @@ -861,6 +865,7 @@ bool CcdShapeConstructionInfo::SetMesh(RAS_MeshObject* meshobj, bool polytope) m_shapeType = PHY_SHAPE_NONE; return false; } + m_meshObject = meshobj; return true; } diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.h b/source/gameengine/Physics/Bullet/CcdPhysicsController.h index 7dc325dc800..a3fbb502c08 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.h @@ -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 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 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 diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index d8e05fab839..a34c70143e5 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -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(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(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(rayCallback.m_collisionObject->getUserPointer()); - hitX = rayCallback.m_hitPointWorld.getX(); - hitY = rayCallback.m_hitPointWorld.getY(); - hitZ = rayCallback.m_hitPointWorld.getZ(); + CcdPhysicsController* controller = static_cast(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; igetNumChildShapes(); 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; } diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h index 825a5e525f2..667e310dcb3 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h @@ -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 diff --git a/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.cpp b/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.cpp index f512d44c9f2..d78958b746c 100644 --- a/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.cpp @@ -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; diff --git a/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.h b/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.h index b5a61f72e4a..975be84f2a7 100644 --- a/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.h +++ b/source/gameengine/Physics/Dummy/DummyPhysicsEnvironment.h @@ -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 diff --git a/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.cpp b/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.cpp index 65018d2523e..80e4dc4044e 100644 --- a/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.cpp @@ -26,6 +26,7 @@ * * ***** END GPL LICENSE BLOCK ***** */ +#include // 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 (ignoreClientCtrl); + SumoPhysicsController* ignoreCtr = static_cast (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) diff --git a/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.h b/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.h index 8b9fb463034..100adf969d5 100644 --- a/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.h +++ b/source/gameengine/Physics/Sumo/SumoPhysicsEnvironment.h @@ -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 diff --git a/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h b/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h index 5b275066665..98496fb7f9e 100644 --- a/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h +++ b/source/gameengine/Physics/common/PHY_IPhysicsEnvironment.h @@ -33,6 +33,50 @@ #include #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 diff --git a/source/gameengine/PyDoc/KX_GameObject.py b/source/gameengine/PyDoc/KX_GameObject.py index 033b2864351..2136ce54e30 100644 --- a/source/gameengine/PyDoc/KX_GameObject.py +++ b/source/gameengine/PyDoc/KX_GameObject.py @@ -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 """ diff --git a/source/gameengine/PyDoc/KX_MeshProxy.py b/source/gameengine/PyDoc/KX_MeshProxy.py index e43fa3598f0..03bc36b6ac1 100644 --- a/source/gameengine/PyDoc/KX_MeshProxy.py +++ b/source/gameengine/PyDoc/KX_MeshProxy.py @@ -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/Rasterizer/RAS_MeshObject.cpp b/source/gameengine/Rasterizer/RAS_MeshObject.cpp index 63378328251..a907994bf57 100644 --- a/source/gameengine/Rasterizer/RAS_MeshObject.cpp +++ b/source/gameengine/Rasterizer/RAS_MeshObject.cpp @@ -155,7 +155,7 @@ int RAS_MeshObject::NumPolygons() -RAS_Polygon* RAS_MeshObject::GetPolygon(int num) +RAS_Polygon* RAS_MeshObject::GetPolygon(int num) const { return m_Polygons[num]; } diff --git a/source/gameengine/Rasterizer/RAS_MeshObject.h b/source/gameengine/Rasterizer/RAS_MeshObject.h index eb8baed2dd4..0d35a2f402b 100644 --- a/source/gameengine/Rasterizer/RAS_MeshObject.h +++ b/source/gameengine/Rasterizer/RAS_MeshObject.h @@ -126,7 +126,7 @@ public: RAS_TexVert* GetVertex(unsigned int matid, unsigned int index); int NumPolygons(); - RAS_Polygon* GetPolygon(int num); + RAS_Polygon* GetPolygon(int num) const; /* buckets */ virtual void AddMeshUser(void *clientobj); -- cgit v1.2.3