fourbar Tutorial: demo_collision.cpp

A bunch of random shapes will fall into a box, stacking in random order.

Differently from the two examples above, here a special object is used to encapsulate Chrono::Engine rigid bodies, and some 'shortcuts' are used to manage them in Irrlicht.

Learn about:

- collisions, contacts and friction
- use the easy ChBodySceneNode class for managing Irrlicht objects which encapsulates ChBody items.
- describe compound shapes

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;

static int STATIC_lcp_iters      = 5;
static int STATIC_lcp_iters_stab = 3;

The following function fill be used to populate the system with the colliding bodies: the many falling objects (created simply with a for() loop) and the five walls of the box.

The important issue here, is that we are no longer using the plain ChBody objects, but the more advanced ChBodySceneNode objects. These ChBodySceneNode objects are used directly by Irrlicht, because they are inherited from the ISceneNode class, so they can be associated to 3D meshes, textures and materials, for visualization.

Using the headers in the irrlicht_interface/ directory, Chrono::Engine gives you the opportunity to create easily these kind of ChBodySceneNode objects, thank to the functions addChBodySceneNode_easySphere() addChBodySceneNode_easyBox() etc, which create spheres, boxes and other basic geometric objects (which are autoatically added to the Chrono::Engine system and to the Irrlicht scene manager).

Since ChBodySceneNode objects wrap ChBody objects, you still have the ability of accessing the functions of the inner ChBody objects by using the method ->GetBody().
 

void create_some_falling_items(ChSystem& mphysicalSystem, ISceneManager* msceneManager, IVideoDriver* driver)
{

	ChBodySceneNode* mrigidBody; 

	for (int bi = 0; bi < 30; bi++) 
	{ 
		// Create a bunch of ChronoENGINE rigid bodies (spheres and
		// boxes) which will fall..
		// Falling bodies are Irrlicht nodes of the special class ChBodySceneNode, 
		// which encapsulates ChBody items). 
		// Note that ChBodySceneNode have collision turned ON by default (so if you
		// want to have them 'transparent' to collision detection, you may use
		// mrigidBody->GetBody()->SetCollide(false) if you need..)
 
		mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easySphere(
							&mphysicalSystem, msceneManager,
							1.0,
							ChVector<>(-5+CHrandom()*10, 4+bi*0.05, -5+CHrandom()*10),
							1.2);
  
		mrigidBody->GetBody()->SetImpactC(0.6);
		mrigidBody->GetBody()->SetSfriction(0.2);
		mrigidBody->GetBody()->SetKfriction(0.2); 
		   
		mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyBox(
							&mphysicalSystem, msceneManager,
							1.0,
							ChVector<>(-5+CHrandom()*10, 4+bi*0.05, -5+CHrandom()*10),
							ChQuaternion<>(1,0,0,0), 
							ChVector<>(1.5,1.5,1.5) );

		mrigidBody->GetBody()->SetInertiaXX(ChVector<>(1.2,1.2,1.2));
		mrigidBody->GetBody()->SetImpactC(0.6);
		mrigidBody->GetBody()->SetSfriction(0.4);
		mrigidBody->GetBody()->SetKfriction(0.4);

		mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyCylinder(
							&mphysicalSystem, msceneManager,
							1.0,
							ChVector<>(-5+CHrandom()*10, 4+bi*0.05, -5+CHrandom()*10),
							ChQuaternion<>(1,0,0,0), 
							ChVector<>(1.5,0.5,1.5) );

		mrigidBody->GetBody()->SetInertiaXX(ChVector<>(1.2,1.2,1.2));
		mrigidBody->GetBody()->SetImpactC(0.6);
		mrigidBody->GetBody()->SetSfriction(0.4);
		mrigidBody->GetBody()->SetKfriction(0.4);

	} 

	// Create the five walls of the rectangular container, using
	// fixed rigid bodies of 'box' type:

	mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyBox(
											&mphysicalSystem, msceneManager,
											1.0,
											ChVector<>(0,-5,0),
											ChQuaternion<>(1,0,0,0), 
											ChVector<>(20,1,20) );
	mrigidBody->GetBody()->SetBodyFixed(true);
	mrigidBody->GetBody()->SetImpactC(0.6);
		
	video::ITexture* cubeMap = driver->getTexture("../data/cubetexture.png");
	mrigidBody->setMaterialTexture(0,	cubeMap);


	mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyBox(
											&mphysicalSystem, msceneManager,
											1.0,
											ChVector<>(-10,0,0),
											ChQuaternion<>(1,0,0,0), 
											ChVector<>(1,10,20) );
	mrigidBody->GetBody()->SetBodyFixed(true);
	mrigidBody->setMaterialTexture(0,	cubeMap);

	mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyBox(
											&mphysicalSystem, msceneManager,
											1.0,
											ChVector<>(10,0,0),
											ChQuaternion<>(1,0,0,0), 
											ChVector<>(1,10,20) );
	mrigidBody->GetBody()->SetBodyFixed(true);
	mrigidBody->setMaterialTexture(0,	cubeMap);

	mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyBox(
											&mphysicalSystem, msceneManager,
											1.0,
											ChVector<>(0,0,-10),
											ChQuaternion<>(1,0,0,0), 
											ChVector<>(20,10,1) );
	mrigidBody->GetBody()->SetBodyFixed(true);
	mrigidBody->setMaterialTexture(0,	cubeMap);

	mrigidBody = (ChBodySceneNode*)addChBodySceneNode_easyBox(
											&mphysicalSystem, msceneManager,
											1.0,
											ChVector<>(0,0, 10),
											ChQuaternion<>(1,0,0,0), 
											ChVector<>(20,10,1) );
	mrigidBody->GetBody()->SetBodyFixed(true);
	mrigidBody->setMaterialTexture(0,	cubeMap);

	 
}

NOTE: Instead of creating five separate 'box' bodies to make the walls of the bowl, you could have used a single body made of five box shapes, which build a single collision description, as in the following alternative approach.

Pay attention that a basic 'empty' ChBodySceneNode object is created, using addChBodySceneNode(), not any kind of easy-to-use spere or box. So you can customize its collision shape by a description enclosed between the ClearModel() and BuildModel() functions pair, which are mandatory.

		// create a plain ChBodySceneNode (no colliding shape nor visualization mesh is used yet)
	mrigidBody = (ChBodySceneNode*)addChBodySceneNode(
											&mphysicalSystem, msceneManager,
											0, 1.0, 
											ChVector<>(0,0,0)  );

		// set the ChBodySceneNode as fixed body, and turn collision ON, otherwise no collide by default
	mrigidBody->GetBody()->SetBodyFixed(true);	
	mrigidBody->GetBody()->SetCollide(true);	 
		
		// Clear model. The colliding shape description MUST be between  ClearModel() .. BuildModel() pair.
	mrigidBody->GetBody()->GetCollisionModel()->ClearModel();
		// Describe the colliding shape by adding five boxes (the walls and floor)
	mrigidBody->GetBody()->GetCollisionModel()->AddBox(20,1,20, &ChVector<>(  0,-10,  0)); 
	mrigidBody->GetBody()->GetCollisionModel()->AddBox(1,40,20, &ChVector<>(-11,  0,  0));
	mrigidBody->GetBody()->GetCollisionModel()->AddBox(1,40,20, &ChVector<>( 11,  0,  0));
	mrigidBody->GetBody()->GetCollisionModel()->AddBox(20,40,1, &ChVector<>(  0,  0,-11));
	mrigidBody->GetBody()->GetCollisionModel()->AddBox(20,40,1, &ChVector<>(  0,  0, 11));
		// Complete the description.
	mrigidBody->GetBody()->GetCollisionModel()->BuildModel();

		// Maybe that you want to add a mesh for Irrlicht 3D viewing, maybe a
		// mesh representing the 5 walls, and use it for this ChBodySceneNode
	IAnimatedMesh* wallsMesh = msceneManager->getMesh("../data/nice_box_with_walls.obj");
	mrigidBody->setMesh(wallsMesh);

The following is the main function, where - as usual - the Irrlicht device is created, the Chrono::Engine globals are initialized, and the real-time simulation loop is performed. See the previous tutorials for more explainations.


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();
   
	// Create the IRRLICHT context (device, etc.)
	IrrlichtDevice* device = createDevice(video::EDT_DIRECT3D9, core::dimension2d<s32>(640, 480));
	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"Collisions between objects");

	IVideoDriver* driver           = device->getVideoDriver();
	ISceneManager*	 msceneManager = device->getSceneManager();
	IGUIEnvironment* guienv        = device->getGUIEnvironment();
	timer = device->getTimer(); // timer declared in CHbodySceneNode.h
 
	// Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
	ChIrrWizard::add_typical_Logo(device);
	ChIrrWizard::add_typical_Sky(device);
	ChIrrWizard::add_typical_Lights(device);
	ChIrrWizard::add_typical_Camera(device, core::vector3df(0,14,-20));
 

  
	// 
	// HERE YOU CREATE THE MECHANICAL SYSTEM OF CHRONO...
	//  
  
	// create a ChronoENGINE physical system
	ChSystem mphysicalSystem;


	// Create all the rigid bodies.
	create_some_falling_items(mphysicalSystem, msceneManager, driver);
 

	// Prepare the physical system for the simulation, maybe that LCP solver iterations require higer values for more stable stacking simulation
  
	mphysicalSystem.SetIntegration(INT_TASORA); // can handle collisions & large penetrations
	mphysicalSystem.SetIterLCPmaxItersSpeed(STATIC_lcp_iters);
	mphysicalSystem.SetIterLCPmaxItersStab(STATIC_lcp_iters_stab);

  
	//
	// THE SOFT-REAL-TIME CYCLE
	//


	while(device->run()) 
	{
		driver->beginScene(true, true, SColor(255,140,161,192));
	

		msceneManager->drawAll();
		guienv->drawAll();

		mphysicalSystem.DoStepDynamics( 0.01);

		driver->endScene();  
	}
	
 
	// This safely delete every Irrlicht item (including the 
	// scene nodes, so also the encapsulated Chrono bodies will
	// be deleted)
	device->drop(); 
 

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

	return 0;
}
  
that's all.