Tutorial 103: - Adding Joints

From Newton Wiki
Jump to: navigation, search

Template:Languages
This tutorial I assume you are familiar with the concept of Basic integration of Newton into a graphics engine and you also know how to add static and rigid bodies to a graphic scene. If you are no familiar with these ideas please check out tutorials 101 and 102 in the previous page.

Joints are the object use by the Newton engine to create any kind of physical contraction between two rigid bodies. At the fundamental level a Joint is an object that restrict the relative motion of two bodies along some degree of freedom. The easiest example of a joint between two bodies is a Hinge. A hinge is an object that only allows relative rotation between two bodies along the axis of the hinge, and denied the rotation and translation along the other 5 degree of freedom.

In this tutorial we will use the Hinge joint to demonstrate how two rigid bodies can be connected with a joint.

Select project Tutorial_103_AddingJoints as Startup project in visual studio and open the file TutorialCode.cpp in the editor. You will find function CreateScene

void CreateScene (NewtonWorld* world, SceneManager* sceneManager)
{
	Entity* floor;
	Entity* smilly;
	Entity* frowny;
	NewtonBody* floorBody;
	NewtonBody* smillyBody;
	NewtonBody* frownyBody;
	NewtonCollision* shape;

	// Create a large body to be the floor
	floor = sceneManager->CreateEntity();
	floor->LoadMesh ("FloorBox.dat");

	// add static floor Physics
	shape = CreateNewtonBox (world, floor, 0);
	floorBody = CreateRigidBody (world, floor, shape, 0.0f);
	NewtonReleaseCollision (world, shape);

	// set the Transformation Matrix for this rigid body
	dMatrix matrix (floor->m_curRotation, floor->m_curPosition);
	NewtonBodySetMatrix (floorBody, &matrix[0][0]);

	// now we will use the properties of this body to set a proper world size.
	dVector minBox;
	dVector maxBox;
	NewtonCollisionCalculateAABB (shape, &matrix[0][0], &minBox[0], &maxBox[0]);

	// add some extra padding
	minBox.m_x -=  50.0f;
	minBox.m_y -= 500.0f;
	minBox.m_z -=  50.0f;

	maxBox.m_x +=  50.0f;
	maxBox.m_y += 500.0f;
	maxBox.m_z +=  50.0f;

	// set the new world size
	NewtonSetWorldSize (world, &minBox[0], &maxBox[0]);


	// add some visual entities.
	smilly = sceneManager->CreateEntity();
	smilly->LoadMesh ("Smilly.dat");
	smilly->m_curPosition.m_y = 10.0f;
	smilly->m_prevPosition = smilly->m_curPosition;

	// add a body with a box shape
	shape = CreateNewtonBox (world, smilly, 0);
	smillyBody = CreateRigidBody (world, smilly, shape, 10.0f);
	NewtonReleaseCollision (world, shape);


	// add some visual entities.
	frowny = sceneManager->CreateEntity();
	frowny->LoadMesh ("Frowny.dat");
	frowny->m_curPosition.m_z = 0.4f;
	frowny->m_curPosition.m_y = 10.0f + 0.4f;
	frowny->m_prevPosition = frowny->m_curPosition;

	// add a body with a Convex hull shape
	shape = CreateNewtonConvex (world, frowny, 0);
	frownyBody = CreateRigidBody (world, frowny, shape, 10.0f);
	NewtonReleaseCollision (world, shape);

	
	// we will Link the Frowny Body to the world with Hinge regenerated from a Generic 6DOF joint.
	 matrix = GetIdentityMatrix();
	 matrix.m_posit = frowny->m_curPosition;
	 matrix.m_posit.m_z += 0.2f;
	 matrix.m_posit.m_y += 0.4f;

	 // specify the limits for defining a Hinge around the x axis
	 dVector minLinearLimits (0.0f, 0.0f, 0.0f, 0.0f);
	 dVector maxLinearLimits (0.0f, 0.0f, 0.0f, 0.0f);

	 dVector minAngulaLimits (-0.5f * 3.1416f, 0.0f, 0.0f, 0.0f);
	 dVector maxAngulaLimits ( 0.5f * 3.1416f, 0.0f, 0.0f, 0.0f);

	 NewtonUserJoint* frownyHinge;
	 // Create a 6DOF joint 
	 frownyHinge = CreateCustomJoint6DOF (&matrix[0][0], &matrix[0][0], frownyBody, NULL);

	 // set the hinge Limits
 	 CustomJoint6DOF_SetLinearLimits (frownyHinge, &minLinearLimits[0], &maxLinearLimits[0]);
 	 CustomJoint6DOF_SetAngularLimits (frownyHinge, &minAngulaLimits[0], &maxAngulaLimits[0]);

	
	// now we will link the body of Smilly and Frowny with a specialize Hinge Joint 
	 NewtonUserJoint* smillyFrownyHinge;
	 matrix.m_posit = smilly->m_curPosition;
	 matrix.m_posit.m_z += 0.2f;
	 matrix.m_posit.m_y += 0.4f;
	 smillyFrownyHinge = CreateCustomHinge (&matrix[0][0], smillyBody, frownyBody);
	 EnableLimits (smillyFrownyHinge, 1);
	 SetLimis (smillyFrownyHinge, -0.5f * 3.1416f, 0.5f * 3.1416f);
}

This function put two small boxes in the scene which will be connected with hinges to form a double pendulum.


The interesting part of function CreateScene is the second half where two hinges are added

	// we will Link the Frowny Body to the world with Hinge regenerated from a Generic 6DOF joint.
	 matrix = GetIdentityMatrix();
	 matrix.m_posit = frowny->m_curPosition;
	 matrix.m_posit.m_z += 0.2f;
	 matrix.m_posit.m_y += 0.4f;

	 // specify the limits for defining a Hinge around the x axis
	 dVector minLinearLimits (0.0f, 0.0f, 0.0f, 0.0f);
	 dVector maxLinearLimits (0.0f, 0.0f, 0.0f, 0.0f);

	 dVector minAngulaLimits (-0.5f * 3.1416f, 0.0f, 0.0f, 0.0f);
	 dVector maxAngulaLimits ( 0.5f * 3.1416f, 0.0f, 0.0f, 0.0f);

	 NewtonUserJoint* frownyHinge;
	 // Create a 6DOF joint 
	 frownyHinge = CreateCustomJoint6DOF (&matrix[0][0], &matrix[0][0], frownyBody, NULL);

	 // set the hinge Limits
 	 CustomJoint6DOF_SetLinearLimits (frownyHinge, &minLinearLimits[0], &maxLinearLimits[0]);
 	 CustomJoint6DOF_SetAngularLimits (frownyHinge, &minAngulaLimits[0], &maxAngulaLimits[0]);

	
	// now we will link the body of Smilly and Frowny with a specialize Hinge Joint 
	 NewtonUserJoint* smillyFrownyHinge;
	 matrix.m_posit = smilly->m_curPosition;
	 matrix.m_posit.m_z += 0.2f;
	 matrix.m_posit.m_y += 0.4f;
	 smillyFrownyHinge = CreateCustomHinge (&matrix[0][0], smillyBody, frownyBody);
	 EnableLimits (smillyFrownyHinge, 1);
	 SetLimis (smillyFrownyHinge, -0.5f * 3.1416f, 0.5f * 3.1416f);

There is more than one way to achieve the same joint in Newton, in the case of a Hinge we provide the internal build in Hinge joint, the Cutom6DOF joint, and the Custom designed Hinge.

The internal Hinge joint is a legacy join from previous version that we do not recommend continue using. The Cutom6DOF is a General propose Joint that create joint based on the traditional Analytical definition that the motion of a rigid body can be specified with three translational degree of freedoms and three rotational degree of freedoms, It is a limited joint in the sense that is can only make Joints that are defined by the 6 DOF definition, but at the same time it is a very flexible joint that allows a large variety of joints to be created very quickly, examples for such joints are Slider, Ball and Socket, Hinges, and so on.

The third hinge is the Custom designed Hinge, this is the strongest and it is carefully created by using the Geometrical definition of a Hinge instead of the Analytical definition of the Hinge.

In this tutorial we will show who to usage of Cutom6DOF and CustomHinge.


the first part of this code fragment attach the first Box to the static world with a CustomJoint3DOF.

	// we will Link the Frowny Body to the world with Hinge regenerated from a Generic 6DOF joint.
	 matrix = GetIdentityMatrix();
	 matrix.m_posit = frowny->m_curPosition;
	 matrix.m_posit.m_z += 0.2f;
	 matrix.m_posit.m_y += 0.4f;

	 // specify the limits for defining a Hinge around the x axis
	 dVector minLinearLimits (0.0f, 0.0f, 0.0f, 0.0f);
	 dVector maxLinearLimits (0.0f, 0.0f, 0.0f, 0.0f);

	 dVector minAngulaLimits (-0.5f * 3.1416f, 0.0f, 0.0f, 0.0f);
	 dVector maxAngulaLimits ( 0.5f * 3.1416f, 0.0f, 0.0f, 0.0f);

	 NewtonUserJoint* frownyHinge;
	 // Create a 6DOF joint 
	 frownyHinge = CreateCustomJoint6DOF (&matrix[0][0], &matrix[0][0], frownyBody, NULL);

	 // set the hinge Limits
 	 CustomJoint6DOF_SetLinearLimits (frownyHinge, &minLinearLimits[0], &maxLinearLimits[0]);
 	 CustomJoint6DOF_SetAngularLimits (frownyHinge, &minAngulaLimits[0], &maxAngulaLimits[0]);

For this the first thing we need to determine is the position and orientation of the axis of rotation of the hinge. This is done by creating a transformation matrix, in which the axis of rotation is the first row, the second and third row are not used but they have to conform to a normalized 4 x 4 transformation matrix. The fourth row is a point on the line of action of the hinge. With this matrix we call function CreateCustomJoint6DOF from the custom joint library.

Now we need to specify the degrees of freedom that will make this joint behave like a hinge. A hinge is an object that prevents relative translation between the two jointed bodies; this is specified by setting the minLinearLimit and maxLinearLimit to zero in all three directions and then calling function CustomJoint6DOF_SetLinearLimits.

A hinge only allows the jointed bodies spins around the X axis, but in addition we will limit the amount of rotation to -90 and plus 90 degrees from the set position. The others angular limits are set to zero. This is done by setting those limits in the vectors minAngularLimit and maxAngularLimits and calling function CustomJoint6DOF_SetAngularLimits. This is all we need to do to make a hinge using the Custom3DOF joint.

There is one more thing we need to say: when we create the joint we passed as argument to the second rigid body a NULL pointer. In the Newton engine Joints that links a rigid body to a NULL have the special meaning that their function call back is always called first. This property makes it possible for this joint to be used to affect the behaviors of other joints in the same contraption. We call these kind of joints Controller.

A controller is a joint that connect a rigid body to a NULL object. Controllers can be used to regulate more advance contractions made of collection of rigid bodies connected by joints, like rigid body cars, Rag dolls, player controller, etc.

The second joint we will demonstrate is the custom made hinge.

	// now we will link the body of Smilly and Frowny with a specialize Hinge Joint 
	 NewtonUserJoint* smillyFrownyHinge;
	 matrix.m_posit = smilly->m_curPosition;
	 matrix.m_posit.m_z += 0.2f;
	 matrix.m_posit.m_y += 0.4f;
	 smillyFrownyHinge = CreateCustomHinge (&matrix[0][0], smillyBody, frownyBody);
	 EnableLimits (smillyFrownyHinge, 1);
	 SetLimis (smillyFrownyHinge, -0.5f * 3.1416f, 0.5f * 3.1416f);

The Hinge will joints the second and first body at an edge. We could have used the same Custom 6DOF but the purpose of this tutorial is to show the you how to use the most number of features.

As before we need to create the transformation matrix that will define the axis of rotation of the Hinge, and the point on the line of action of the hinge axis. After having the matrix we call function CreateCustomHinge from the joint library.

The function returns the pointer to a Custom Hinge Joint that will let the two bodies rotate freely around the pin. To set the Limits we call a function member Enables Limit and then we set the desired Limits.

The third joint we are going to show here is a very interesting one. I call it CustomKinematicController.

	 {
		// adding two Kinematic controlled object
		frowny = sceneManager->CreateEntity();
		frowny->LoadMesh ("Frowny.dat");
		frowny->m_curPosition.m_z = 4.0f;
		frowny->m_curPosition.m_y = 7.0f;
		frowny->m_prevPosition = frowny->m_curPosition;
		shape = CreateNewtonConvex (world, frowny, 0);
		frownyBody = CreateRigidBody (world, frowny, shape, 10.0f);
		NewtonReleaseCollision (world, shape);
		 
		 
		FollowPath* path;
		NewtonUserJoint* g_pathFollow;

		path = (FollowPath*) malloc (sizeof (FollowPath));

		NewtonBodyGetMatrix (frownyBody, &matrix[0][0]);
		path->m_origin = matrix.m_posit;
		path->m_angle = 0.0f;
		path->radius = 3.0f;

		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);
		CustomSetSubmitContraionCallback (g_pathFollow, FollowPath::EvaluatePath);

		matrix = path->BuildMatrix (0.0f);
		NewtonBodySetMatrix(frownyBody, &matrix[0][0]);
	 }

This function is a fix joint that keep a rigid body fixed to a position and orientation in space. The definition sound a lot simpler that how useful it is, Because this joint can be used to force a body to do thing like follow a Path, implement simple elements of game play like Doors, Elevators, It can also be used for ray picking, and for body picking objects.

With this joint the application can take any dynamics rigid body and make it follow a point in space, by animating the point the body can follow a path, it also can constraint the body to follow any orientation. The position and orientation can be controlled simultaneously of independent. The body controlled by the joint still acts physics when interact with other bodies in the scene, this is possible because the joint calculates the necessary force and torque to enforce the constraint, but the force and torque can never exceed the maximum limits specified in the jint MaxFrictionForce and MaxFrictionTorque, which means that if the body collides with other bodies and these limits are violated, then the body will move off the path by the effect of the exceeding force.

In this demo one thing interesting is that It add a custom code to update the integration of the path autoatically,

struct FollowPath
{
	dFloat radius;
	dVector m_origin;
	dFloat m_angle;

	// this calculate the desired position and orientation in the path
	dMatrix BuildMatrix (dFloat timestep)
	{
		dMatrix matrix (dPitchMatrix(-m_angle) * dRollMatrix(-m_angle));
		matrix.m_posit = m_origin;
		matrix.m_posit.m_z += radius * dSin (m_angle);
		matrix.m_posit.m_y += radius * dCos (m_angle);
		return matrix;
	}

	// destructor to be call when the joint is destroyed
	static void Destroy (const NewtonUserJoint* me)
	{
		FollowPath* path;

		path = (FollowPath*) CustomGetUserData(me);
		free (path);
	}

	// the call back is call before any of the constraint rows and collumns to inforce the joint are summited.
	static void EvaluatePath (const NewtonUserJoint* me, dFloat timestep, int threadIndex)
	{
		FollowPath* path;
		path = (FollowPath*) CustomGetUserData(me);
		path->m_angle = dMod (path->m_angle + (45.0f * 3.1416f / 180.0f) * timestep, 2.0f * 3.1416f);

		dMatrix matrix (path->BuildMatrix (timestep));
		CustomPickBodySetTargetMatrix (me, &matrix[0][0]); 
	}
};

for this, it uses the joint common interface to add a custom local data, custom constraint callback and custom destructor callback for destroying the local data on destruction of the joint. By doing this the joint is self maintained, and not significant game logic have to be implemented to control the joint. This technique will be use later on a more advances Triggers and Scene collision tutorial to control scene objects that are self operated when some action happens. Stuff like automatic door that open when the player enter a trigger and close when the player leave the trigger.


This conclude this tutorial. we showed how to implement the most basic joints in the engine, It does no sound like it is much but believe men with time and experience the joints demonstrated in this tutorial, cover and in some case surpasses all of the joint provide by competing engines. Plus we also have other special joint that requires their own tutorials to be explained best.