Picking can be implemented in numerous ways. We will cast a ray from the clicked position on the screen to the 3D world using the hkpWorld::castRay function provided by the Havok Physics SDK. The user clicks on the screen at a position(x,y). We implement this in the PickActor function. The first thing we need is to determine the ray that has its start position at the clicked position(x,y)on screen and an end position that is at the far clip plane.
Determining the ray from the clicked position
We first determine the z position of the clicked screen space point. We know from the projection transformation that a given point (x,y), has z=0 at the near clip plane and z=1 at the far clip plane. We can use this knowledge to create two 3D points from the given screen space point, one at near clip plane (x,y,0) and another at the far clip plane (x,y,1). We then unproject these two 3D points to obtain the world space points for the two 3D points. These are the ray start and the ray end points that we will pass to the hkpWorld::castRay function.
The ViewUnProject is a utility function that we define as follows. Note that this function definition will be modified if you use DirectX or any other graphics library.
This function uses the current viewport to determine the screen size. It then passes the given x,y and z position, the current modelview and projection matrices and the current viewport to the gluUnProject function from the OpenGL utility library. Of course if you are using any other library, you will have to find the appropriate function or create your own function. After the unproject call, we get the world space point for the given point.
At the end of these calls, we have the ray start and ray end points.
Casting picking ray using hkpWorld::castRay function
OK now we have our ray, we can call g_pWorld->castRay function. If we look at the Havok SDK reference, we can see that the castRay function takes two parameters. hkpWorldRaycastInput and hkpClosestRayHitCollector. You will have to include the following new headers for these
The hkpRaycastInput object has three fields, m_from which is the ray start point, m_to which is the ray end point, m_filterInfo which we pass 0. We then lock the hkpWorld, castRay and then unlock hkpWorld as shown below.
If any rigidbody in the physics world intersects with the given ray, the hkpClosestRayHitCollector::hasHit() function returns true. We call this function to determine the possible hit condition. Calling hkpClosestRayHitCollector::getHit() function returns the hkpWorldRaycastOutput object. This object contains the m_rootCollidable field which is the intersected rigid body. To get a rigidbody pointer from the m_rootCollidable object, we call hkpGetRigidBody function. Now since our world contains both static and dynamic rigid bodies, we are only interested in the dynamic rigid bodies so we determine the motion type field of the returned rigid body.
After we are sure that we have a dynamic rigid body, we then find the intersected point on the rigid body and then project the point to get the 3D hit point. This is just to render the intersected point. Next, we set the motion state of the picked rigid body as MOTION_FIXED to make the picked rigid body a static rigid body. This is to ensure that when we move the rigidbody around, it does not undergo motion.
In the mouse move event handler, we determine if the user has picked a rigid body. If there is a picked rigid body, we set the translation field of the rigid body's transform to the unprojected mouse point as was shown earlier.
Finally, when the mouse button up event is raised, we set the motion state of the picked rigid body to MOTION_DYNAMIC.to ensure that the picked rigid body is physically simulated again.
That's all, you will see a box falling due to gravity. You can pick the box by left clicking and dragging the mouse to reposition it as shown in the following figure.
You can get the full source code from my github repo https://github.com/mmmovania/HavokPhysicsTutorials
Left click to rotate, left click on box to pick and reposition
Middle click to zoom
Right click to pan
In the next tutorial, I will show you how to create a simple distance joint.