crank Tutorial: demo_crank.cpp

The simpliest way to integrate Chrono::Engine in the Irrlicht 3D visualization library: in fact the coordinates of the joints are simply used as end-points of simple polygonal lines which are drawn in the 3D space for each frame redraw, to show a very simplified 'skeleton' of a slider-crank mechanism.
The demo also teaches how to:
- create constraints and 'engine' objects
- make a real-time application, where Chrono::Engine adapts the integration step to the speed of the CPU.

Let's start
First of all, include some headers needed for this example, use required namespaces, initialize the library, etc:

#include "physics/CHapidll.h" 
#include "physics/CHsystem.h"
#include "irrlicht_interface/CHbodySceneNode.h"
#include "irrlicht_interface/CHbodySceneNodeTools.h" 
#include "irrlicht_interface/CHdisplayTools.h" 
#include "irrlicht_interface/CHirrWizard.h"
#include "core/CHrealtimeStep.h"
#include <irrlicht.h>

// Use the namespace of Chrono
using namespace chrono;

// Use the main namespaces of Irrlicht
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

int main(int argc, char* argv[])
{
// In CHRONO engine, The DLL_CreateGlobals() - DLL_DeleteGlobals(); pair is needed if // global functions are needed.
ChGlobals* GLOBAL_Vars = DLL_CreateGlobals();

Now remember that you are going to use Irrlicht, so you need to initialize also the Irrlicht device:
// Create the IRRLICHT context (device, etc.)
IrrlichtDevice* device = createDevice(video::EDT_DIRECT3D9, core::dimension2d<s32>(640, 480), 24, false, false, true);
if
(device == 0)
{

GetLog() << "Cannot use DirectX - switch to OpenGL \n"; device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480));
if
(!device) return 1;
}

device->setWindowCaption(L"SIMPLIEST example of integration of Chrono::Engine and Irrlicht");
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* msceneManager = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();


Thank to  the functions contained in the ChIrrWizard class, you can easily create a Chrono::Engine logo on the top left corner of the window, a sky dome with 'smooth horizon effect', two default lights and a camera looking forward.
ChIrrWizard::add_typical_Logo(device);
ChIrrWizard::add_typical_Sky(device); ChIrrWizard::add_typical_Lights(device);
ChIrrWizard::add_typical_Camera(device, core::vector3df(0,0,-6));

Now comes the important part of the tutorial: how to build the mechanical system.
First of all, create the physical system:
 ChSystem my_system;
Create the three rigid bodies of the slider-crank mechanical system (a crank, a rod, a truss), maybe setting position/mass/inertias of their center of mass (COG) etc.
Remember that each body must be added to the physical system via the AddBody() function.

// ..the truss
ChSharedBodyPtr my_body_A(new ChBody);
my_system.AddBody(my_body_A);
my_body_A->SetBodyFixed(true); // truss does not move!

// ..the crank
ChSharedBodyPtr my_body_B(new ChBody);
my_system.AddBody(my_body_B);
my_body_B->SetPos(ChVector<>(1,0,0)); // position of COG of crank

// ..the rod
ChSharedBodyPtr my_body_C(new ChBody);
my_system.AddBody(my_body_C);
my_body_C->SetPos(ChVector<>(4,0,0)); // position of COG of rod
Create the three constraints (the mechanical joints between the rigid bodies).
Note that the engine is a particular kind of constraint as well (an object of class ChLinkEngine). It can work in different ways: imposing speed, imposing rotation as a function of time, imposing torque, etc. In this case, we use the 'impose speed' mode, with speed=constant.
// .. a revolute joint between crank and rod. 
ChSharedPtr<ChLinkLockRevolute> my_link_BC(new ChLinkLockRevolute); my_link_BC->Initialize(my_body_B, my_body_C, ChCoordsys<>(ChVector<>(2,0,0)));
my_system.AddLink(my_link_BC); // .. a slider joint between rod and truss
ChSharedPtr<ChLinkLockPointLine> my_link_CA(new ChLinkLockPointLine);
my_link_CA->Initialize(my_body_C, my_body_A, ChCoordsys<>(ChVector<>(6,0,0)));
my_system.AddLink(my_link_CA);

// .. an engine between crank and truss ChSharedPtr<ChLinkEngine> my_link_AB(new ChLinkEngine);
my_link_AB->Initialize(my_body_A, my_body_B, ChCoordsys<>(ChVector<>(0,0,0)));
my_link_AB->Set_eng_mode(ChLinkEngine::ENG_MODE_SPEED);
my_link_AB->Get_spe_funct()->Set_yconst(CH_C_PI); // speed w=3.145 rad/sec my_system.AddLink(my_link_AB);


Ok, all the items have been added to the physical system. We can show the simulation.
Since the simulation is shown in the Irrlicht 3D display, we create an endless loop, which continuosly redraws the 3D window. For each frame in the loop, we need to call some Irrlicht functions: beginScene() is used to clean the display by drawing the background and resetting for the next redraw, then drawAll() will draw all 3D objects, if any, then we call drawGrid() to draw a 3D reference grid, then we call multiple times the drawSegment() function to paint the polygonal line which represent the 'skeleton' of the moving slider-crank. Also the drawCircle() function is used to create the schematic representation of a revolute joint and of the flywheel.

// This will help choosing an integration step which matches the
// real-time step of the simulation..
ChRealtimeStepTimer m_realtime_timer;

while(device->run())
{

// Irrlicht must prepare frame to draw
driver->beginScene(true, true, SColor(255,140,161,192));

// Irrlicht now draws simple lines in 3D world representing a
// skeleton of the mechanism, in this instant:
//
// .. draw items belonging to Irrlicht scene, if any
msceneManager->drawAll();
// .. draw GUI items belonging to Irrlicht screen, if any
guienv->drawAll();
// .. draw a grid
ChIrrTools::drawGrid(driver, 0.5, 0.5);
// .. draw the rod (from joint BC to joint CA)
ChIrrTools::drawSegment(driver, my_link_BC->GetMarker1()->GetAbsCoord().pos, my_link_CA->GetMarker1()->GetAbsCoord().pos,
video::SColor(255, 0,255,0));
// .. draw the crank (from joint AB to joint BC)
ChIrrTools::drawSegment(driver, my_link_AB->GetMarker1()->GetAbsCoord().pos, my_link_BC->GetMarker1()->GetAbsCoord().pos,
video::SColor(255, 255,0,0));
// .. draw a small circle at crank origin
ChIrrTools::drawCircle(driver, 0.1, ChCoordsys<>(ChVector<>(0,0,0), QUNIT));

// HERE CHRONO INTEGRATION IS PERFORMED: THE
// TIME OF THE SIMULATION ADVANCES FOR A SINGLE STEP:

my_system.DoStepDynamics( m_realtime_timer.SuggestSimulationStep(0.02) );

// Irrlicht must finish drawing the frame
driver->endScene();
}

Note! The simulation is advanced by the DoStepDynamics() function, which takes the time step as an argument. In this example, this function is called each time a new frame is displayed.

The interesting issue here, is that if you had simply used DoStepDynamics(0.01), for example, you would have seen the pendulum going faster on recent CPU, and going slower on less performant CPU (because maybe a fast computer can compute DoStepDynamics() and display the 3D view very quickly, say at 500fps,  while the simulated mechanism represents something which advances 0.01s per frame... This would mean that the CPU would run 5 times faster than real-time, and the fast-motion on the pendulum won't be very nice.
To avoid this problem, and to force Chrono::Engine to advance the 'right amount of time step' for each frame update, you can use the ChRealtimeStepTimer object, as in this example, above. In fact this tool is able to tell 'how much the simulation should advance' in order to keep it at the same pace of the real-time display update. That is,  using the SuggestSimulationStep() function you get the proper dt for the DoStepDynamics() function, and the pace of the simulation should be about realtime (and the same on computer with different CPU speed). However, remember that if the computer is slow (or the simulation is complex) maybe that the suggested timestep could be too large, causing unstable and inprecise simulaitons - for this reason, the SuggestSimulationStep() can use a parameter (we used 0.02 in our example) which enforce an upper limit on the duration of the suggested step.  

To finish with Irrlicht and Chrono::Engine libraries, you simply do the following:
	// This safely delete every Irrlicht item..
device->drop();

// Remember this at the end of the program, if you started
// with DLL_CreateGlobals();
DLL_DeleteGlobals();

return 0;
}
that's all.