zoukankan      html  css  js  c++  java
  • 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 和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    js正则验证邮箱格式
    PHP面向对象简易验证码类
    php 中 instanceof 操作符
    防止SQL注入
    通过实例详细讲解PHP垃圾回收机制
    PHP实现上传视频的功能
    二维数组分组
    时间戳转换为几分钟、几小时、几天、几周、几月、几年前
    自定义接口错误响应格式
    laravel写crontab定时任务(发送邮件)和laravel crontab不执行的问题
  • 原文地址:https://www.cnblogs.com/kanego/p/2281730.html
Copyright © 2011-2022 走看看