Creating a Simple Mechanism Using Various Types of Joints
This example shows how to:
- Create a simple mechanism using various types of joints.
- Animate the mechanism using joint motors.
The basic workflow of creating and animating a simple mechanism is as follows:
- Create geometry for all parts of the mechanism.
- Assign bodies and collision shapes to the parts.
- Set up masses for the parts.It is very important to ensure mass balance – avoid connection of too heavy bodies to light ones, otherwise the joints may become unstable!
- Connect all parts of the mechanism using appropriate types of joints. Set up joint parameters.
- Animate the mechanism using joint motors.
Creating Geometry and Adding Some Physics
The first thing we are going to address in this tutorial is the geometry of our mechanism (see the picture above). We are going to create the following parts:
- 2 blue guide bars
- a red piston
- a green rod
- a black wheel
- 2 dummy objects with dummy bodies to attach the parts of our mechanism to.
/// method, creating a named dummy body of a specified size at pos
ObjectDummy createBodyDummy(String name, vec3 size, dmat4 transform)
{
// creating a dummy object
ObjectDummy dummy = new ObjectDummy();
// setting parameters
dummy.setWorldTransform(transform);
dummy.setName(name);
//assigning a dummy body to the dummy object and adding a box shape to it
new BodyDummy(dummy.getObject());
dummy.getBody().addShape(new ShapeBox(size).getShape(), MathLib.translate(0.0f, 0.0f, 0.0f));
return dummy;
}
/// method, creating a named box having a specified size, color and transformation with a body and a shape
ObjectMeshDynamic createBodyBox(String name, vec3 size, float mass, vec4 color, dmat4 transform)
{
// creating geometry and setting up its parameters (name, material, property and transformation)
ObjectMeshDynamic OMD = Primitives.createBox(size);
OMD.setWorldTransform(transform);
OMD.setMaterial("mesh_base", "*");
OMD.setMaterialParameter("albedo_color", color, 0);
OMD.setProperty("surface_base", "*");
OMD.setName(name);
// adding physics, i.e. a rigid body and a box shape with specified mass
BodyRigid body = new BodyRigid(OMD.getObject());
body.addShape(new ShapeBox(size).getShape(), MathLib.translate(new vec3(0.0f)));
OMD.getBody().getShape(0).setMass(mass);
return OMD;
}
/// method, creating a named cylinder having a specified size, color and transformation with a body and a shape
ObjectMeshDynamic createBodyCylinder(String name, float radius, float height, float mass, vec4 color, dmat4 transform)
{
// creating geometry and setting up its parameters (name, material, property and transformation)
ObjectMeshDynamic OMD = Primitives.createCylinder(radius, height, 1, 32);
OMD.setWorldTransform(transform);
OMD.setMaterial("mesh_base", "*");
OMD.setMaterialParameter("albedo_color", color, 0);
OMD.setProperty("surface_base", "*");
OMD.setName(name);
// adding physics, i.e. a rigid body and a cylinder shape with specified mass
BodyRigid body = new BodyRigid(OMD.getObject());
body.addShape(new ShapeCylinder(radius, height).getShape(), MathLib.translate(new vec3(0.0f)));
OMD.getBody().getShape(0).setMass(mass);
return OMD;
}
Now using these functions we can create our mechanism. We are going to use DynamicMesh objects for the parts and Dummy objects for mounting points.
// creating parts of the mechanism
dmat4 transform = new dmat4(MathLib.translate(0.0f, 0.0f, 10.0f) * MathLib.rotateY(90.0f));
piston = createBodyBox("piston", new vec3(1.0f, 2.0f, 0.5f), 15.0f, new vec4(1.0f, 0.1f, 0.1f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, 3.5f, 0.0f)));
guide_bar1 = createBodyBox("guide_bar1", new vec3(1.0f, 9.0f, 0.5f), 15.0f, new vec4(0.0f, 0.1f, 0.7f, 1.0f), transform * new dmat4(MathLib.translate(1.0f, 3.0f, 0.0f)));
guide_bar2 = createBodyBox("guide_bar2", new vec3(1.0f, 9.0f, 0.5f), 15.0f, new vec4(0.0f, 0.1f, 0.7f, 1.0f), transform * new dmat4(MathLib.translate(-1.0f, 3.0f, 0.0f)));
wheel = createBodyCylinder("wheel", 3.0f, 0.25f, 25.0f, new vec4(0.1f, 0.1f, 0.1f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, -15.0f, -0.5f)));
rod = createBodyCylinder("rod", 0.1f, 15.0f, 5.0f, new vec4(0.1f, 1.1f, 0.1f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, -5.0f, 0.0f) * MathLib.rotateX(90.0f)));
//creating mounting points
dummy1 = createBodyDummy("dummy1", new vec3(1.0f, 1.0f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, -15.0f, -1.5f)));
dummy2 = createBodyDummy("dummy2", new vec3(1.0f, 1.0f, 0.5f), transform * new dmat4(MathLib.translate(0.0f, 7.0f, 0.0f)));
Adding and Setting Up Joints
Now that we have created all parts of our mechanism with physical bodies and shapes, let us link them together with joints.
First, we are going to attach the wheel to the first mounting point(dummy1). The wheel is going to rotate around its axis. Let us use a cylindrical joint here. As the center of the wheel is aligned with the center of dummy1, we may use a simple constructor JointCylindrical() and specify only bodies, the anchor point will be placed automatically between their centers of mass.
Then we must set the coordinates (in the world space) of the axis of wheel rotation. In our case it is (1.0f, 0.0f, 0.0f). And finally set other joint parameters.
// creating a cylindrical joint
jc = new JointCylindrical(dummy1.getBody(), wheel.getBody());
// setting rotation axis in world coordinates
jc.setWorldAxis(new vec3(1.0f, 0.0f, 0.0f));
// setting common joint constraint parameters
jc.setLinearRestitution(0.4f);
jc.setAngularRestitution(0.4f);
jc.setLinearSoftness(0.4f);
jc.setAngularSoftness(0.4f);
// setting linear and angular damping
jc.setLinearDamping(4.0f);
jc.setAngularDamping(2.0f);
// setting small linear limits as we are not going to use this degree of freedom to the full extent
jc.setLinearLimitFrom(-0.0005f);
jc.setLinearLimitTo(0.0005f);
// setting number of iterations
jc.setNumIterations(16);
Now let us attach the rod to the wheel and to the piston. This is where we need a hinge joint. For both of these joints we have to specify anchor point and joint axis coordinates in the JointHinge() constructor
// creating hinge joints
jh = new JointHinge(wheel.getBody(), rod.getBody(), new dvec3(0.0f, -12.5f, 10.0f), new vec3(1.0f, 0.0f, 0.0f));
jh2 = new JointHinge(rod.getBody(), piston.getBody(), new dvec3(0.0f, 2.5f, 10.0f), new vec3(1.0f, 0.0f, 0.0f));
// setting number of iterations
jh.setNumIterations(8);
jh2.setNumIterations(8);
The next thing we are going to do is to attach 2 guide bars to the second mounting point(dummy2) using a pair of fixed joints. For both of these joints we will specify anchor point coordinates in the JointFixed() constructor
// creating fixed joints
jf1 = new JointFixed(dummy2.getBody(), guide_bar1.getBody(), new dvec3(0.0f, 7.0f, 9.0f));
jf2 = new JointFixed(dummy2.getBody(), guide_bar2.getBody(), new dvec3(0.0f, 7.0f, 11.0f));
// setting number of iterations
jf1.setNumIterations(1);
jf2.setNumIterations(1);
And the last joint we are going to use is a prismatic joint to attach the piston to the second mounting point(dummy2). Here we will specify joint axis coordinates, linear limits to determine the range of motion for the piston and linear softness
// creating a prismatic joint
jp = new JointPrismatic(piston.getBody(), dummy2.getBody());
jp.setWorldAxis(new vec3(0.0f, 1.0f, 0.0f));
// setting linear limits [-5.0; 0.0] and softness
jp.setLinearLimitFrom(-5.0f);
jp.setLinearLimitTo(0.0f);
jp.setLinearSoftness(0.5f);
// setting number of iterations
jp.setNumIterations(8);
Using Joint Motors
To animate the mechanism we are going to use the motor of our cylindrical joint. In order to make it move we must set angular velocity and torque:
// setting up motor parameters for a cylindrical joint to animate the whole mechanism
jc.setAngularVelocity(1000.0f);
jc.setAngularTorque(150.0f);
Putting it All Together
In this section let us sum up all described above. The final code for our tutorial will be as follows:
// AppWorldLogic.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unigine;
namespace UnigineApp
{
class AppWorldLogic : WorldLogic
{
PlayerSpectator player;
// parts of the mechanism
ObjectDummy dummy1;
ObjectDummy dummy2;
ObjectMeshDynamic rod;
ObjectMeshDynamic wheel;
ObjectMeshDynamic piston;
ObjectMeshDynamic guide_bar1;
ObjectMeshDynamic guide_bar2;
// joints of the mechanism
JointCylindrical jc;
JointHinge jh;
JointHinge jh2;
JointFixed jf1;
JointFixed jf2;
JointPrismatic jp;
/// method, creating a named dummy body of a specified size at pos
ObjectDummy createBodyDummy(String name, vec3 size, dmat4 transform)
{
// creating a dummy object
ObjectDummy dummy = new ObjectDummy();
// setting parameters
dummy.setWorldTransform(transform);
dummy.setName(name);
//assigning a dummy body to the dummy object and adding a box shape to it
new BodyDummy(dummy.getObject());
dummy.getBody().addShape(new ShapeBox(size).getShape(), MathLib.translate(0.0f, 0.0f, 0.0f));
return dummy;
}
/// method, creating a named box having a specified size, color and transformation with a body and a shape
ObjectMeshDynamic createBodyBox(String name, vec3 size, float mass, vec4 color, dmat4 transform)
{
// creating geometry and setting up its parameters (name, material, property and transformation)
ObjectMeshDynamic OMD = Primitives.createBox(size);
OMD.setWorldTransform(transform);
OMD.setMaterial("mesh_base", "*");
OMD.setMaterialParameter("albedo_color", color, 0);
OMD.setProperty("surface_base", "*");
OMD.setName(name);
// adding physics, i.e. a rigid body and a box shape with specified mass
BodyRigid body = new BodyRigid(OMD.getObject());
body.addShape(new ShapeBox(size).getShape(), MathLib.translate(new vec3(0.0f)));
OMD.getBody().getShape(0).setMass(mass);
return OMD;
}
/// method, creating a named cylinder having a specified size, color and transformation with a body and a shape
ObjectMeshDynamic createBodyCylinder(String name, float radius, float height, float mass, vec4 color, dmat4 transform)
{
// creating geometry and setting up its parameters (name, material, property and transformation)
ObjectMeshDynamic OMD = Primitives.createCylinder(radius, height, 1, 32);
OMD.setWorldTransform(transform);
OMD.setMaterial("mesh_base", "*");
OMD.setMaterialParameter("albedo_color", color, 0);
OMD.setProperty("surface_base", "*");
OMD.setName(name);
// adding physics, i.e. a rigid body and a cylinder shape with specified mass
BodyRigid body = new BodyRigid(OMD.getObject());
body.addShape(new ShapeCylinder(radius, height).getShape(), MathLib.translate(new vec3(0.0f)));
OMD.getBody().getShape(0).setMass(mass);
return OMD;
}
/* .. */
public override int init()
{
// setting up physics parameters
Physics.get().setGravity(new vec3(0.0f, 0.0f, -9.8f * 2.0f));
Physics.get().setFrozenLinearVelocity(0.1f);
Physics.get().setFrozenAngularVelocity(0.1f);
// setting up player
player = new PlayerSpectator();
player.release();
player.setPosition(new dvec3(22.0f, -2.0f, 10.0f));
player.setDirection(new vec3(-10.0f, -2.0f, 0.0f), new vec3(0.0f, 0.0f, -1.0f));
Game.get().setPlayer(player.getPlayer());
// creating parts of the mechanism
dmat4 transform = new dmat4(MathLib.translate(0.0f, 0.0f, 10.0f) * MathLib.rotateY(90.0f));
piston = createBodyBox("piston", new vec3(1.0f, 2.0f, 0.5f), 15.0f, new vec4(1.0f, 0.1f, 0.1f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, 3.5f, 0.0f)));
guide_bar1 = createBodyBox("guide_bar1", new vec3(1.0f, 9.0f, 0.5f), 15.0f, new vec4(0.0f, 0.1f, 0.7f, 1.0f), transform * new dmat4(MathLib.translate(1.0f, 3.0f, 0.0f)));
guide_bar2 = createBodyBox("guide_bar2", new vec3(1.0f, 9.0f, 0.5f), 15.0f, new vec4(0.0f, 0.1f, 0.7f, 1.0f), transform * new dmat4(MathLib.translate(-1.0f, 3.0f, 0.0f)));
wheel = createBodyCylinder("wheel", 3.0f, 0.25f, 25.0f, new vec4(0.1f, 0.1f, 0.1f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, -15.0f, -0.5f)));
rod = createBodyCylinder("rod", 0.1f, 15.0f, 5.0f, new vec4(0.1f, 1.1f, 0.1f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, -5.0f, 0.0f) * MathLib.rotateX(90.0f)));
//creating mounting points
dummy1 = createBodyDummy("dummy1", new vec3(1.0f, 1.0f, 1.0f), transform * new dmat4(MathLib.translate(0.0f, -15.0f, -1.5f)));
dummy2 = createBodyDummy("dummy2", new vec3(1.0f, 1.0f, 0.5f), transform * new dmat4(MathLib.translate(0.0f, 7.0f, 0.0f)));
// creating a cylindrical joint
jc = new JointCylindrical(dummy1.getBody(), wheel.getBody());
// setting rotation axis in world coordinates
jc.setWorldAxis(new vec3(1.0f, 0.0f, 0.0f));
// setting common joint constraint parameters
jc.setLinearRestitution(0.4f);
jc.setAngularRestitution(0.4f);
jc.setLinearSoftness(0.4f);
jc.setAngularSoftness(0.4f);
// setting linear and angular damping
jc.setLinearDamping(4.0f);
jc.setAngularDamping(2.0f);
// setting small linear limits as we are not going to use this degree of freedom to the full extent
jc.setLinearLimitFrom(-0.0005f);
jc.setLinearLimitTo(0.0005f);
// setting number of iterations
jc.setNumIterations(16);
// creating hinge joints
jh = new JointHinge(wheel.getBody(), rod.getBody(), new dvec3(0.0f, -12.5f, 10.0f), new vec3(1.0f, 0.0f, 0.0f));
jh2 = new JointHinge(rod.getBody(), piston.getBody(), new dvec3(0.0f, 2.5f, 10.0f), new vec3(1.0f, 0.0f, 0.0f));
// setting number of iterations
jh.setNumIterations(8);
jh2.setNumIterations(8);
// creating fixed joints
jf1 = new JointFixed(dummy2.getBody(), guide_bar1.getBody(), new dvec3(0.0f, 7.0f, 9.0f));
jf2 = new JointFixed(dummy2.getBody(), guide_bar2.getBody(), new dvec3(0.0f, 7.0f, 11.0f));
// setting number of iterations
jf1.setNumIterations(1);
jf2.setNumIterations(1);
// creating a prismatic joint
jp = new JointPrismatic(piston.getBody(), dummy2.getBody());
jp.setWorldAxis(new vec3(0.0f, 1.0f, 0.0f));
// setting linear limits [-5.0; 0.0] and softness
jp.setLinearLimitFrom(-5.0f);
jp.setLinearLimitTo(0.0f);
jp.setLinearSoftness(0.5f);
// setting number of iterations
jp.setNumIterations(8);
// setting up motor parameters for a cylindrical joint to animate the whole mechanism
jc.setAngularVelocity(1000.0f);
jc.setAngularTorque(150.0f);
return 1;
}
/* .. */
}
}