delta3d与ode物理引擎的结合。

      使用delta3d有些日子了,对物理引擎这块没有细看过,最近研究了一下。主要分为两大部分,第一在dtCore中对ode的封装,第二通过dtPhysics,使用PAL(phys abstract layer)对三种物理引擎 bullet、ode、phys的封装。

  这里先介绍下dtCore中对delta3d中的封装。总结起来就是如下几点:

    • dtCore::ODEWolrdWrap  主要是包装了ODE world的功能。这个类不是供用户直接使用的。
    • dtCore::ODESpaceWrap  主要包装了ODE space的功能,主要用于碰撞检测,这个类也不是直接给用户使用的。
    • dtCore::ODEController   这个类主要用于管理ODE 物理系统,提供注册物理对象,应用全局重力,迭代物理系统(包括碰撞检测和刚体动力)。提供一个默认的碰撞回调函数。里边引用了dtCore::ODEWolrdWrap和      dtCore::ODESpaceWrap这两个类。这个类主要是被dtCore::Scene类调用。
    • dtCore::ODEGeomWrap  这个类主要是包装了ODE Geom。主要被space用来进行碰撞检测。
    • dtCore::ODEBodyWrap  包装了ODE physics body。主要进行动力学的应用。
    • dtCore::Transformable   这个类是一切可移动物体的父类,里边引用了dtCore::ODEGemoWrap。如果想要设置用于碰撞检测的几何体(球,正方体等,)任何它的子类都可以设置。
    • dtCore::Physical  这个类引用了dtCore::ODEBodyWrap ,所以继承这个类的子类对象可以设置一个表现刚性物体的属性。并且这个类的父类是dtCore::Transformable,所以可以设置碰撞几何体来应用于碰撞检测和重力特性。
    • dtCore::Scene  场景类,引用了ODEController类,进行物理系统的管理。

关系大致就是这样,我就不画图表示了。

总之,在delta3D中应用ODE的时候,可以使用Scene类调用ODEController,通过ODEController中配置ODESpaceWrap,进行碰撞检测设置。因为Physical类中引用了ODEBodyWrap,所以他的子类可以进行动力学相关设置。

下面就上述的碰撞信息的数据流在delta3d中的代码中走一遍。

1、在scene的Scene::OnMessage(MessageData* data)方法中

 1 if (data->message == dtCore::System::MESSAGE_PRE_FRAME)
2 {
3 double dt = *static_cast<double*>(data->userData);
4 if (mImpl->mPhysicsController.valid())
5 {
6 mImpl->mPhysicsController->Iterate(dt);
7 }
8 }
2、这个方法每帧都在监听,如果物理控制器有效的时候,就调用
9 ODEController::Iterate(double deltaFrameTime)进行遍历,如下
10 void dtCore::ODEController::Iterate(double deltaFrameTime)
11 {
12 double stepSize = deltaFrameTime;
13
14 // if step size is set, use it instead of the delta frame time
15 if (GetPhysicsStepSize() > 0.0)
16 {
17 stepSize = GetPhysicsStepSize();
18 }
19
20 //calc the number of steps to take
21 const int numSteps = int(deltaFrameTime/stepSize);
22
23 TransformableVector::const_iterator it;
24
25 for (it = GetRegisteredCollidables().begin();
26 it != GetRegisteredCollidables().end();
27 ++it)
28 {
29 (*it)->PrePhysicsStepUpdate();
30 }
31
32 for (int i=0; i<numSteps; ++i)
33 {
34 Step(stepSize);
35 }
36
37 const double leftOver = deltaFrameTime - (numSteps * stepSize);
38
39 if (leftOver > 0.0)
40 {
41 Step(leftOver);
42 }
43
44 for (it = GetRegisteredCollidables().begin();
45 it != GetRegisteredCollidables().end();
46 ++it)
47 {
48 (*it)->PostPhysicsStepUpdate();
49 }
50 }

3、在上一步中主要是调用ODEController中的Step函数进行碰撞处理。

如下:

 1 void dtCore::ODEController::Step(double stepSize)
2 {
3 if (mMsgSender.valid())
4 {
5 mMsgSender->SendMessage(ODEController::MESSAGE_PHYSICS_STEP, &stepSize);
6 }
7
8 if (mSpaceWrapper.valid()) { mSpaceWrapper->Collide(); }
9
10 if (mWorldWrapper.valid()) { mWorldWrapper->Step(stepSize); }
11
12 if (mSpaceWrapper.valid()) { mSpaceWrapper->PostCollide(); }
13 }

显然,就是先判断下是否存在发送者,存在的话就发送一个MESSAGE_PHYSICS_STEP(这个消息忘了在那进行处理了,见谅)的消息,就是起到调整物理时间步的。然后就调用sapce中的碰撞检测函数。

4、也就是如下代码:

 1 void dtCore::ODESpaceWrap::Collide()
2 {
3 if (mUserNearCallback)
4 {
5 dSpaceCollide(mSpaceID, mUserNearCallbackData, mUserNearCallback);
6 }
7 else
8 {
9 dSpaceCollide(mSpaceID, this, DefaultNearCallback);
10 }
11 }

5、如果自己设置了碰撞函数,就调用自己的(scene中留有接口,可以直接设置碰撞检测函数),没有的话,就调用默认碰撞函数。

最终的碰撞检测函数如下:

View Code
 1 void dtCore::ODESpaceWrap::DefaultNearCallback(void* data, dGeomID o1, dGeomID o2)
2 {
3 if (data == 0 || o1 == 0 || o2 == 0)
4 {
5 return;
6 }
7
8 ODESpaceWrap* spaceWrap = static_cast<ODESpaceWrap*>(data);
9
10 Transformable* c1 = static_cast<Transformable*>(dGeomGetData(o1));
11 Transformable* c2 = static_cast<Transformable*>(dGeomGetData(o2));
12
13 dContactGeom contactGeoms[8];
14
15 int numContacts = dCollide(o1, o2, 8, contactGeoms, sizeof(dContactGeom));
16
17 if (numContacts > 0 && c1 != 0 && c2 != 0)
18 {
19 CollisionData cd;
20
21 cd.mBodies[0] = c1;
22 cd.mBodies[1] = c2;
23
24 cd.mLocation.set(
25 contactGeoms[0].pos[0], contactGeoms[0].pos[1], contactGeoms[0].pos[2]
26 );
27
28 cd.mNormal.set(
29 contactGeoms[0].normal[0], contactGeoms[0].normal[1], contactGeoms[0].normal[2]
30 );
31
32 cd.mDepth = contactGeoms[0].depth;
33
34 if (spaceWrap->mCollisionCBFunc.valid())
35 {
36 spaceWrap->mCollisionCBFunc(cd);
37 }
38
39 if (c1 != 0 || c2 != 0)
40 {
41 dContact contact;
42
43 for (int i = 0; i < numContacts; ++i)
44 {
45 contact.surface.mode = dContactBounce;
46 contact.surface.mu = (dReal)1000.0;
47 contact.surface.bounce = (dReal)0.75;
48 contact.surface.bounce_vel = (dReal)0.001;
49
50 contact.geom = contactGeoms[i];
51
52 // Make sure to call these both, because in the case of
53 // Trigger, meaningful stuff happens even if the return
54 // is false.
55 bool contactResult1 = c1->FilterContact(&contact, c2);
56 bool contactResult2 = c2->FilterContact(&contact, c1);
57
58 if (contactResult1 && contactResult2)
59 {
60 // All this also should be in a virtual function.
61 Physical* p1 = dynamic_cast<Physical*>(c1);
62 Physical* p2 = dynamic_cast<Physical*>(c2);
63
64 if (p1 != 0 || p2 != 0)
65 {
66 dJointID joint = dJointCreateContact(spaceWrap->mWorldWrapper->GetWorldID(),
67 spaceWrap->mContactJointGroupID,
68 &contact);
69
70 dJointAttach(joint,
71 p1 != 0 && p1->DynamicsEnabled() ? p1->GetBodyID() : 0,
72 p2 != 0 && p2->DynamicsEnabled() ? p2->GetBodyID() : 0);
73 }
74 }
75 }
76 }
77 }
78 }

6、上面说的是如何处理收到的数据,现在看一下如何把碰撞的数据发出去的,在ODEController的构造函数中,调用了一个函数

即Ctor(),这个函数的功能:如果发生碰撞,就是把碰撞时产生的数据发出去。

如下

 1 void dtCore::ODEController::Ctor()
2 {
3 RefODE();//保证是原子操作
4 //supply our method to be called when geoms actually collide
5 mSpaceWrapper->SetDefaultCollisionCBFunc(dtCore::ODESpaceWrap::CollisionCBFunc(this, &ODEController::DefaultCBFunc));
6
7 dSetMessageHandler(ODEMessageHandler);
8 dSetDebugHandler(ODEDebugHandler);
9 dSetErrorHandler(ODEErrorHandler);
10 }
11 然后调用相应的回调函数:
12 void dtCore::ODEController::DefaultCBFunc(const dtCore::ODESpaceWrap::CollisionData& data)
13 {
14 if (mMsgSender.valid())
15 {
16 //have to convert to Scene::CollisionData for backward compatibility
17 dtCore::Scene::CollisionData scd;
18 scd.mBodies[0] = data.mBodies[0];
19 scd.mBodies[1] = data.mBodies[1];
20 scd.mDepth = data.mDepth;
21 scd.mLocation = data.mLocation;
22 scd.mNormal = data.mNormal;
23
24 //if a collision took place and we have a sender pointer,
25 //send out the "collision" message
26 mMsgSender->SendMessage(ODEController::MESSAGE_COLLISION, &scd);//把相关的数据发送出去了,这个消息可以在OnMessage()接收。
27 }
28 }


就是通过这样消息循环,把如何处理碰撞数据,如何在碰撞时添加相应的操作 联系起来。

以上就是dtCore对ODE的封装。

关于dtPhysics,等待研究明白,再补上。

顺便插个广告:delta3d技术交流qq群:12483772。欢迎加入!







本文版权归作者 kanego 和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
原文地址:https://www.cnblogs.com/kanego/p/2281730.html