使用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(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 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中留有接口,可以直接设置碰撞检测函数),没有的话,就调用默认碰撞函数。
最终的碰撞检测函数如下:
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(data); 9 10 Transformable* c1 = static_cast (dGeomGetData(o1)); 11 Transformable* c2 = static_cast (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 (c1); 62 Physical* p2 = dynamic_cast (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。欢迎加入!