由于国内关于Havok的教程很少,而关于如何在Directx中使用Havok的教程就几乎没有了.花了很多时间从国外的网站上淘来了一篇关于如何在Directx3D中使用Havok物理引擎的教程.
共产蛋sb就是没办法,连国外的博客都不让上.简直就是在自取灭亡.
闲话不多说了,以下是英文教程
Havok in D3d basic
Posted at January 26, 2011 @ 2:43 pm by Menglin in Uncategorized
Havok is a good physics engine, and its open source. you can download it on their website http://www.havok.com/.
Recently, I'm quite interested in havok physics, and trying to use it in my games.
They have very good documents, from which you can learn how to programming a console havok physics program in havok visual debuger easily. the example is under the demo/standalongdemo folder. However, in demo folder, they also have a demo which can run without havok debuger, and in this tutorial, I'll show you how to use havok in d3d.
I assume that you already know how to setup a havok world, and know how to use directX. If not, you can learn both of them quickly form their document.
at here, I created my own havok class called MLHKUTIL, and a directx class called MLDXUTIL. I'll not show how each class worked because it all the basic setup for havok physics and directx. and I skipped the windowsAPI part, let just assume that the initialize function is called my_AppInit(), and the main loop is my_Main() funtion, the cleanup function ismy_Shutdown().
what we gonna to do today, is simulate a parabola of a ball in the physics world.
The initial function is like below
void my_AppInit()
{
// Init D3D
// create dxutil object
mydx = new MLDX9UTIL();
if (isWindowed)
mydx->fnInitD3D9(hWndMain, &d3ddev, D3DDEVTYPE_HAL, WINDOW_WIDTH, WINDOW_HEIGHT, isWindowed);
else
mydx->fnInitD3D9(hWndMain, &d3ddev, D3DDEVTYPE_HAL, SCREEN_WIDTH, SCREEN_HEIGHT, isWindowed);
// init graphics
mydx->fnInitGraphics(d3dInitGraphics);
// init light
mydx->fnInitLight(d3dInitLight);
// End Init D3D
// Init Havok
myhk = new MLHKUTIL(setupPhysics, 1000);
// End Init Havok
}
We already know that the MLDX9UTIL() functions created a d3d object, and the d3ddev is a global variables
LPDIRECT3DDEVICE9 d3ddev;
all our d3d rendering after, will use this d3d device object.
after initialize the d3d object mydx.
the constructor of myhk, called a function called setupphysics which we'll introduce after, and set the world size to 1000.0f.
we call 2 function of it. the parameter of them are the pointers to the graphic and lighting setup functions. It no matter with the intergrate of havok and d3d, we just dont need to care about it.
finally, we create a havok instance called myhk.
In the main loop,
void my_AppMain()
{
if (KEYDOWN(VK_ESCAPE))
SendMessage(hWndMain,WM_CLOSE,0,0);
// Main Functions
// rending D3D
myhk->run(d3dRender, 60.0f, 30.0f);
// End Main Functions
}
there are only calling myhk's run function, the run method of myhk object is call the graphic rendering function d3dRender(), and set the simulate time to 60sec, while the frame is 30/sec.
ok, now lets see what each functions doing.
first, we need to setup our world,
void d3dInitGraphics()
{
if (d3ddev) // Only use Device methods if we have a valid device.
{
// Create the ground box
D3DXCreateBox(d3ddev,40.0f,2.0f,80.0f, &groundbox,0);
// create the ball
D3DXCreateSphere(d3ddev, 1.5f, 20, 20, &d3d_ball, 0);
} // end if
}
Ok, now we created a ground and a ball in our D3d world.
then, we need to set up the havok physics world. notice that the havok is based on vector, so we cant rendering a havok rigid directly in directx. what we do is create another similar object in havok world, and synchronize it to our d3d world time by time.
void setupPhysics(hkpWorld* physicsWorld)
{
//
// Create the ground box
//
{
hkVector4 groundRadii( 20.0f, 2.0f, 40.0f );
hkpConvexShape* shape = new hkpBoxShape( groundRadii , 0 );
hkpRigidBodyCinfo ci;
ci.m_shape = shape;
ci.m_motionType = hkpMotion::MOTION_FIXED;
ci.m_position = hkVector4( 0.0f, -2.0f, 0.0f );
ci.m_qualityType = HK_COLLIDABLE_QUALITY_FIXED;
physicsWorld->addEntity( new hkpRigidBody( ci ) )->removeReference();
shape->removeReference();
}
hkVector4 groundPos( 0.0f, 0.0f, 0.0f );
hkVector4 posy = groundPos;
//
// Create a ball moving towards the walls
//
const hkReal radius = 1.5f;
const hkReal sphereMass = 30.0f;
hkVector4 relPos( 0.0f,radius + 0.0f, 0.0f );
hkpRigidBodyCinfo info;
hkpMassProperties massProperties;
hkpInertiaTensorComputer::computeSphereVolumeMassProperties(radius, sphereMass, massProperties);
info.m_mass = massProperties.m_mass;
info.m_centerOfMass = massProperties.m_centerOfMass;
info.m_inertiaTensor = massProperties.m_inertiaTensor;
info.m_shape = new hkpSphereShape( radius );
info.m_position.setAdd4(posy, relPos );
info.m_motionType = hkpMotion::MOTION_BOX_INERTIA;
info.m_qualityType = HK_COLLIDABLE_QUALITY_BULLET;
hkpRigidBody* sphereRigidBody = new hkpRigidBody( info );
g_ball = sphereRigidBody;
physicsWorld->addEntity( sphereRigidBody );
sphereRigidBody->removeReference();
info.m_shape->removeReference();
hkVector4 vel( 0.0f,19.6f, 8.0f );
sphereRigidBody->setLinearVelocity( vel );
}
now, we created an exactly same initial world in havok with our d3d world, and give the ball a initial velocity 8 on z axis, and 2g on y axis.
the next is the improtant part.
void d3dRender()
{
if (d3ddev) // Only use Device methods if we have a valid device.
{
// clean back buffer
D3DCOLOR_XRGB(45, 50, 170)
d3ddev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0 );
// set z buffer
d3ddev->Clear( 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0 );
// start painting at back buffer
if (SUCCEEDED(d3ddev->BeginScene()))
{
// SET UP THE GEOMETRY PIPELINE
// 1. World Transformation
D3DXMATRIX worldMX;
D3DXMATRIX ballMX;
// paint the groundbox
D3DXMatrixTranslation(&worldMX, 0.0f, -2.0f, 0.0f);
// tell Direct3D world transform
d3ddev->SetTransform(D3DTS_WORLD, &worldMX);
groundbox->DrawSubset(0);
// synchronize the ball
hkVector4 pos = g_ball->getPosition();
D3DXMatrixTranslation(&ballMX, pos(0), pos(1), pos(2));
// tell Direct3D world transform
d3ddev->SetTransform(D3DTS_WORLD, &ballMX);
// paint the ball
d3d_ball->DrawSubset(0);
// no world transform
// 2. View Transformation
// the view transform matrix
D3DXMATRIX matView;
D3DXVECTOR3 pEye(45.0f, 25.0f, 60.0f);
D3DXVECTOR3 pAt(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 pUp(0.0f, 1.0f, 0.0f);
D3DXMatrixLookAtLH(&matView,
&pEye, // the camera position
&pAt, // the look-at position
&pUp); // the up direction
d3ddev->SetTransform(D3DTS_VIEW, &matView); // set the view transform to matView
// 3. Projection Transformation
D3DXMATRIX matProjection; // the projection transform matrix
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(75), // the horizontal field of view
(FLOAT)WINDOW_WIDTH / (FLOAT)WINDOW_WIDTH, // aspect ratio
1.0f, // the near view-plane
180.0f); // the far view-plane
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection
// END SET UP THE GEOMETRY PIPELINE
// end painting
d3ddev->EndScene();
}
// Swap the back and front buffers.
d3ddev->Present(NULL, NULL, NULL, NULL);
} // end if
}
the only part we need to care is
// synchronize the ball
hkVector4 pos = g_ball->getPosition();
D3DXMatrixTranslation(&ballMX, pos(0), pos(1), pos(2));
this get the havok ball's position and set it to the d3d ball.
you can also get the rotation in this way as well.
the last, dont forget to do the clean up.
there are only one thing need to care about in the clean up,
is havok has 2 cleanup function
hkBaseSystem::quit();
hkMemoryInitUtil::quit();
once these two functions are called, all the havok related things are all cleaned up. if you dident cleaned every thing your self in the destructor, please call this 2 cleanup function in my_Shutdown function after you delete myhk object. otherwise, you may got a crash when your application trying to quit.
Finally, we got our program running like below: