Newton_dynamic物理引擎简单教程

阮鸿煊
2023-12-01
 Newton物理引擎没有函数说明,没有教程文档,只有几个demo程序,看完这些demo后,整理了一下,方便以后查阅

首先跟踪一下,牛顿引擎的运行过程:

全局函数NewtonUpdate (g_world, (1.0f / DEMO_PHYSICS_FPS));
这个函数内部调用了g_world.UpdatePhysics(dgFloat32 timestep)
然后,这个函数又调用了:dgWorld::Update(dgFloat32 timestep)




退出的时候要:
NewtonDestroyAllBodies (g_world); 销毁世界中的所有body
NewtonDestroy (g_world); 销毁牛顿世界


使用流程:
设置内存分配和释放函数。
NewtonSetMemorySystem (AllocMemory, FreeMemory);
void* AllocMemory (int sizeInBytes)
{
return malloc (sizeInBytes);
}
void FreeMemory (void *ptr, int sizeInBytes)
{
free (ptr);

}


创建牛顿世界:

NewtonWorld* g_world = NewtonCreate ();
设置在那个平台使用牛顿物理引擎
NewtonSetPlatformArchitecture (g_world, 0); 这个表示在X86平台使用。
设置牛顿世界的大小:
dVector minSize (-500.0f, -500.0f, -500.0f);
dVector maxSize ( 500.0f, 500.0f, 500.0f);
NewtonSetWorldSize (g_world, &minSize[0], &maxSize[0]);
如果需要开启可视牛顿调试器:
void* g_newtonDebugger = NewtonDebuggerCreateServer ();
设置牛顿求解模型:
NewtonSetSolverModel (g_world, 1);

创建牛顿场景:
.......
.......
.......
接下来就进入渲染循环里面了:
做一些输入控制处理,现在还具体没有调用引擎的函数。
驱动牛顿引擎干活:
NewtonUpdate (g_world, (1.0f / DEMO_PHYSICS_FPS));
如果打开了牛顿可视调试器:
NewtonDebuggerServe (g_newtonDebugger, g_world);

看看怎么创建一个body:
在创建场景的时候添加牛顿的body,一个body需要一个shap:
NewtonBody* Body;
NewtonCollision* shape;
1 分析一下shap是怎么创建的:
shap是一个形状,比如盒子,球等。比如创建盒子:
dMatrix offset (GetIdentityMatrix());
offset.m_posit = origin; //盒子的中心点

// 下面这个函数返回NewtonCollision* shap;shap的ID不知是不是固定的,我看到box的id是0.
return NewtonCreateBox (world, size.m_x, size.m_y, size.m_z, shapeId, &offset[0][0]);
2 shap有了之后就可以创建body了:
1 Body = NewtonCreateBody (world, shap);
2 NewtonBodySetDestructorCallback (Body, DestroyBodyCallback);设置body的销毁函数
销毁函数可能主要用于释放内存吧,
3 NewtonBodySetUserData (Body, ent); //设置自定义用户数据,自定义的Entity*
4 NewtonBodySetMatrix(const NewtonBody* const bodyPtr, const dFloat* const matrixPtr)
设置body的位置和方向矩阵,就是用一个矩阵表示就行了。
5 下面需要body的质量的重心位置,以及惯性矩阵inertia matrix(质量矩阵)。
Newton中有一个函数可以根据一个shap来计算得到这两个东西:
void NewtonConvexCollisionCalculateInertialMatrix(const NewtonCollision* convexCollision,
dFloat* const inertia, dFloat* const origin)第二和第三个参数都是dVector* 其实。
NewtonConvexCollisionCalculateInertialMatrix (shap, &inertia[0], &origin[0]);
6 设置质量矩阵: mass是质量,float类型
NewtonBodySetMassMatrix (body, mass, mass * inertia.m_x, mass * inertia.m_y, mass * inertia.m_z);
7 设置刚体原点:
NewtonBodySetCentreOfMass (body, &origin[0]);
8 设置力和转矩回调函数:
NewtonBodySetForceAndTorqueCallback (body, ApplyForceAndTorqueCallback);
void ApplyForceAndTorqueCallback (const NewtonBody* body, dFloat timestep, int threadIndex)
{
dFloat Ixx;
dFloat Iyy;
dFloat Izz;
dFloat mass;

// for this tutorial the only external force in the Gravity
NewtonBodyGetMassMatrix (body, &mass, &Ixx, &Iyy, &Izz);

dVector gravityForce (0.0f, mass * GRAVITY, 0.0f, 1.0f);
NewtonBodySetForce(body, &gravityForce[0]); 设置一个外力
}
8 设置方位变化的回调函数:
NewtonBodySetTransformCallback (body, SetTransformCallback);
void SetTransformCallback (const NewtonBody* body, const dFloat* matrix, int threadIndex)
{
//在这个函数里,你可以设置自定义的Entity的方位
//通过传进来的matrix可以得到位置,
//可以通过一个函数得到旋转dQuaternion rotation;
//NewtonBodyGetRotation(body, &rotation.m_q0);

Entity* ent;
ent = (Entity*) NewtonBodyGetUserData(body);
ent->m_prevPosition = ent->m_curPosition;
ent->m_prevRotation = ent->m_curRotation;
if (ent->m_curRotation.DotProduct (rotation) < 0.0f) {
ent->m_prevRotation.Scale(-1.0f);
ent->m_curPosition = posit;
ent->m_curRotation = rotation;
}
3 最后还要调用一下这个函数:
NewtonReleaseCollision (world, shape); 这是由于NewtonCollision对象是索引计数对象
其他shape的创建方法:
创建一个与实体网格形状相同的shape:第三个参数是单个顶点占的字节数,这里面的shapId也是0。
NewtonCreateConvexHull(world, ent->m_vertexCount, ent->m_vertex, 3 * sizeof (dFloat), 0.1f, shapeId, NULL);
下面看看怎么创建joint:
joint是物体之间的一种连接关系,比如铰链连接,等,所以要有两个body。
1 对第一个物体,单它的body创建好后,创建Joint:
NewtonUserJoint* frownyHinge;
frownyHinge = CreateCustomJoint6DOF (&matrix[0][0], &matrix[0][0], frownyBody, NULL);
看看这个函数的声明:
NewtonUserJoint *CreateCustomJoint6DOF (const dFloat* pinsAndPivotChildFrame,
const dFloat* pinsAndPivotParentFrame, const NewtonBody* child, const NewtonBody* parent);
2 设置Joint的一些限制:第二三个参数都是dvector类型。
CustomJoint6DOF_SetLinearLimits (frownyHinge, &minLinearLimits[0], &maxLinearLimits[0]);
CustomJoint6DOF_SetAngularLimits (frownyHinge, &minAngulaLimits[0], &maxAngulaLimits[0]);

3 设置前面的body与另外一个body通过Hinge Joint相连。
NewtonUserJoint* smillyFrownyHinge;

NewtonUserJoint *CreateCustomHinge (const dFloat* pinsAndPivotChildFrame, const NewtonBody* child,
const NewtonBody* parent);
smillyFrownyHinge = CreateCustomHinge (&matrix[0][0], smillyBody, frownyBody);

HingeEnableLimits (smillyFrownyHinge, 1);
HingeSetLimits (smillyFrownyHinge, -0.5f * 3.1416f, 0.5f * 3.1416f);//第二三个函数分别表示最小角度和最大角度
这样两个body通过hinge Joint相连了。

Kinematic control joint这是另外一种Joint:可以自己控制它的运动路径
NewtonUserJoint* g_pathFollow;
g_pathFollow = CreateCustomKinematicController (frownyBody, &matrix.m_posit[0]);
CustomKinematicControllerSetPickMode (g_pathFollow, 1);
CustomKinematicControllerSetMaxAngularFriction (g_pathFollow, 200.0f);
CustomKinematicControllerSetMaxLinearFriction (g_pathFollow, 1000.0f);
CustomSetUserData(g_pathFollow, path);
CustomSetDestructorCallback(g_pathFollow, FollowPath::Destroy);
CustomSetSubmitContraintCallback (g_pathFollow, FollowPath::EvaluatePath);

如何使用材质:
1 首先要创建材质:
g_floorMaterial = NewtonMaterialCreateGroupID (world);
g_woodMaterial = NewtonMaterialCreateGroupID (world);
g_metalMaterial = NewtonMaterialCreateGroupID (world);
2 定义材质碰撞后产生的效果
NewtonMaterialSetCollisionCallback (world, g_woodMaterial, g_floorMaterial,
&woodOnFloor, NULL, GenericContactProcess);
回调函数的形式是这样的:
static void GenericContactProcess (const NewtonJoint* contactJoint, dFloat timestep, int threadIndex)
在这个回调函数里面得到下面的知识:
有一种joint叫做contact joint,通过这个joint连接起来的body可能有多个,可以获得第一个body:
NewtonBody* const body0 = NewtonJointGetBody0(contactJoint);
可以枚举所有这种contactjoint的所有contact:
void* contact = NewtonContactJointGetFirstContact (contactJoint);//获得第一个
contact = NewtonContactJointGetNextContact (contactJoint, contact);//获得下一个

通过contact可以获得接触的材质句柄。
NewtonMaterial* material = NewtonContactGetMaterial (contact); //这个函数 只能在这个回调函数里面调用。
获得接触的法线方向上的速度,如果是负数就表示是回弹。
float contactNormalSpeed = NewtonMaterialGetContactNormalSpeed (material);
获得接触的位置和法线方向:
NewtonMaterialGetContactPositionAndNormal (material, body0, &contactPosit[0], &normal[0]);
获得材质对的用户数据:
NewtonMaterialGetMaterialPairUserData (material);

contact joint--contact--material--接触位置,法线,接触速度,用户数据。
contact joint--接触的body。
3 给body赋予材质:
NewtonBodySetMaterialGroupID (floorBody, g_floorMaterial);
下面分析一下tree collision这种shape:
1 创建tree shape:
NewtonCollision* collision = NewtonCreateTreeCollision(world,0);
2 添加面到tree collision里面:
1 开始添加:NewtonTreeCollisionBeginBuild (collision);
2 遍历所有Entity的subEntity,把subEntity里面的所有面加入到tree collision
NewtonTreeCollisionAddFace(collision, 3, &face[0].m_x, sizeof (dVector), shapeIdArray[i]);
3 结束添加:NewtonTreeCollisionEndBuild (collision, 1);
3 注册一个回调函数,在一条射线与这个tree collisioin的面相交的时候,会被调用。
NewtonTreeCollisionSetUserRayCastCallback(collision, UserMeshCollisionCallback);
static dFloat UserMeshCollisionCallback (const NewtonBody* const body,
const NewtonCollision* const collisionTree, dFloat interception,
dFloat* normal, int faceId, void* usedData)
{
return 1.0f;
}

下面分析下HeightFieldCollision这种shape:
顾名思义这个东西是从一张高度图文件创建这种shape的。
NewtonCollision* NewtonCreateHeightFieldCollision(指定宽度,高度,高度数据,属性等)
设置射线与这个collision相交时调用的回调函数
NewtonHeightFieldSetUserRayCastCallback().

还要根据高度值创建地形网格。
2 获取collision的相关信息:
NewtonCollisionInfoRecord collisionInfo;
memset (&collisionInfo, 0, sizeof (NewtonCollisionInfoRecord));
NewtonCollisionGetInfo (collision, &collisionInfo);
可以自己查看这个结构体的定义。
可以将多个shape合并成一个复合shape:
NewtonCreateCompoundCollision (world, ent->m_subMeshCount, shapedArray, 0);
第二个参数是shape的数量,第三个参数是shape的数组,第四个参数是shapeID。

还可以从多个凹网格创建一个复合shape:
NewtonCollision* NewtonCreateCompoundCollisionFromMesh (const NewtonWorld* const newtonWorld,
const NewtonMesh* const convexAproximation, dFloat hullTolerance, int shapeID, int subShapeID)
创建出来的是与凹网格尽可能逼近的凸网格shape,然后再由这些shape组成复合网格。


下面分析一下鼠标拾取物体扔来扔去是怎么实现的:
首先获取鼠标光标坐标,转换到3D空间中,沿着光标点做一条垂线到3D空间
求出这条射线与那个物体相交,记下这个物体。NewtonWorldRayCast()由这个函数实现。

然后给拾取到的body创建定制joint:
// Create PickBody Joint
bodyPickController = CreateCustomKinematicController (pickedBody, &p[0]);

设置pickable模式为0:
CustomKinematicControllerSetPickMode (bodyPickController, 0);
设置各种摩擦系数:
CustomKinematicControllerSetMaxAngularFriction (bodyPickController, 10.0f);
CustomKinematicControllerSetMaxLinearFriction (bodyPickController, MAX_FRICTION_LINEAR_ACCELERATION);
设置joint的目标位置:
CustomKinematicControllerSetTargetPosit (bodyPickController, &p[0]);

当不需要joint的时候销毁它:
CustomDestroyJoint (bodyPickController);
 类似资料: