Export POVray animations (demo_POST_povray.cpp)

Tutorial that teaches how to use the POSTPROCESS module to create animations with POVray.

When the simulation is run, a set of .pov and .ini files are saved on disk, so that one can use POVray later to do high-quality rendering of simulations.

Note: the same assets can be used to render animations in real-time in the interactive 3D view of Irrlicht, as explained in demo_IRR_assets.cpp

Example 1

Create a chrono::ChBody, and attach some 'assets' that define 3D shapes. These shapes can be shown by Irrlicht, OpenGL, or POV postprocessing. Note: these assets are independent from collision shapes.

// Create a ChBody, and attach some 'assets'
// that define 3D shapes. These shapes can be shown
// by Irrlicht or POV postprocessing, etc...
// Note: these assets are independent from collision shapes!
// Create a rigid body as usual, and add it
// to the physical system:
auto floor = chrono_types::make_shared<ChBody>();
floor->SetFixed(true);
// Define a collision shape
auto floor_mat = chrono_types::make_shared<ChContactMaterialNSC>();
auto floor_shape = chrono_types::make_shared<ChCollisionShapeBox>(floor_mat, 20, 1, 20);
floor->AddCollisionShape(floor_shape, ChFrame<>(ChVector3d(0, -1, 0), QUNIT));
floor->EnableCollision(true);
// Add body to system
sys.Add(floor);
// ==Asset== attach a 'box' shape.
auto boxfloor = chrono_types::make_shared<ChVisualShapeBox>(20, 1, 20);
boxfloor->SetColor(ChColor(0.3f, 0.3f, 0.6f));
floor->AddVisualShape(boxfloor, ChFrame<>(ChVector3d(0, -1, 0)));

Example 2

Textures, colors, asset levels with transformations. This section shows how to add more advanced types of assets.

// Textures, colors, asset levels with transformations.
// Create the rigid body as usual (this won't move, it is only for visualization tests)
auto body = chrono_types::make_shared<ChBody>();
body->SetFixed(true);
sys.Add(body);
// ==Asset== Attach a 'sphere' shape
auto sphere = chrono_types::make_shared<ChVisualShapeSphere>(0.5);
body->AddVisualShape(sphere, ChFrame<>(ChVector3d(-1, 0, 0)));
// ==Asset== Attach also a 'box' shape
auto mbox = chrono_types::make_shared<ChVisualShapeBox>(0.4, 1.0, 0.2);
body->AddVisualShape(mbox, ChFrame<>(ChVector3d(1, 0, 0)));
// ==Asset== Attach also a 'cylinder' shape
auto cyl = chrono_types::make_shared<ChVisualShapeCylinder>(0.3, 0.7);
body->AddVisualShape(cyl, ChFrame<>(ChVector3d(2, 0.15, 0), QuatFromAngleX(CH_PI_2)));
body->AddVisualShape(chrono_types::make_shared<ChVisualShapeSphere>(0.03),
ChFrame<>(ChVector3d(2, -0.2, 0), QUNIT));
body->AddVisualShape(chrono_types::make_shared<ChVisualShapeSphere>(0.03),
ChFrame<>(ChVector3d(2, +0.5, 0), QUNIT));
// ==Asset== Attach a 'Wavefront mesh' asset, referencing a .obj file:
auto objmesh = chrono_types::make_shared<ChVisualShapeModelFile>();
objmesh->SetFilename(GetChronoDataFile("models/forklift/body.obj"));
objmesh->SetTexture(GetChronoDataFile("textures/bluewhite.png"));
body->AddVisualShape(objmesh, ChFrame<>(ChVector3d(0, 0, 2)));
// ==Asset== Attach an array of boxes, each rotated to make a spiral
for (int j = 0; j < 20; j++) {
auto smallbox = chrono_types::make_shared<ChVisualShapeBox>(0.2, 0.2, 0.02);
smallbox->SetColor(ChColor(j * 0.05f, 1 - j * 0.05f, 0.0f));
ChMatrix33<> rot(QuatFromAngleY(j * 21 * CH_DEG_TO_RAD));
ChVector3d pos = rot * ChVector3d(0.4, 0, 0) + ChVector3d(0, j * 0.02, 0);
body->AddVisualShape(smallbox, ChFrame<>(pos, rot));
}
// ==Asset== Attach a video camera.
// Note that a camera can also be attached to a moving object.
auto camera = chrono_types::make_shared<ChCamera>();
camera->SetAngle(50);
camera->SetPosition(ChVector3d(-3, 4, -5));
camera->SetAimPoint(ChVector3d(0, 1, 0));
body->AddCamera(camera);

Example 3

Create a chrono::ChParticleCloud cluster, and attach 'assets' that define a single "sample" 3D shape. This will be shown N times in POV or Irrlicht.

// Create a ChParticleClones cluster, and attach 'assets'
// that define a single "sample" 3D shape. This will be shown
// N times in Irrlicht.
// Create the ChParticleClones, populate it with some random particles,
// and add it to physical system:
auto particles = chrono_types::make_shared<ChParticleCloud>();
// Note: coll. shape, if needed, must be specified before creating particles
auto particle_mat = chrono_types::make_shared<ChContactMaterialNSC>();
auto particle_shape = chrono_types::make_shared<ChCollisionShapeSphere>(particle_mat, 0.05);
particles->AddCollisionShape(particle_shape);
particles->EnableCollision(true);
// Create the random particles
for (int np = 0; np < 100; ++np)
particles->AddParticle(ChCoordsys<>(ChVector3d(ChRandom::Get() - 2, 1, ChRandom::Get() - 0.5)));
// Do not forget to add the particle cluster to the system:
sys.Add(particles);
// ==Asset== Attach a 'sphere' shape asset.. it will be used as a sample
// shape to display all particles when rendering in 3D!
auto sphereparticle = chrono_types::make_shared<ChVisualShapeSphere>(0.05);
particles->AddVisualShape(sphereparticle);

The POV exporter

The following part is very important because this is what makes this demo different from the demo_IRR_assets, that used Irrlicht. We need to create a postprocessor of type chrono::postprocess::ChPovRay and tell him that we are going to export our visualization assets:

// Create an exporter to POVray
ChPovRay pov_exporter = ChPovRay(&sys);
// Important: set the path to the template:
pov_exporter.SetTemplateFile(GetChronoDataFile("POVRay_chrono_template.pov"));
// Set the path where it will save all .pov, .ini, .asset and .dat files, a directory will be created if not
// existing
pov_exporter.SetBasePath(out_dir);
// Optional: change the default naming of the generated files:
// pov_exporter.SetOutputScriptFile("rendering_frames.pov");
// pov_exporter.SetOutputDataFilebase("my_state");
// pov_exporter.SetPictureFilebase("picture");
// --Optional: modify default light
pov_exporter.SetLight(ChVector3d(-3, 4, 2), ChColor(0.15f, 0.15f, 0.12f), false);
// --Optional: add further POV commands, for example in this case:
// create an area light for soft shadows
// create a Grid object; Grid() parameters: step, linewidth, linecolor, planecolor
// Remember to use \ at the end of each line for a multiple-line string.
pov_exporter.SetCustomPOVcommandsScript(
" \
light_source { \
<2, 10, -3> \
color rgb<1.2,1.2,1.2> \
area_light <4, 0, 0>, <0, 0, 4>, 8, 8 \
adaptive 1 \
jitter\
} \
object{ Grid(1,0.02, rgb<0.7,0.8,0.8>, rgbt<1,1,1,1>) rotate <0, 0, 90> } \
");

The simulation loop

Now you have to write the usual while() loop to perform the simulation. Note that before running the loop you need to use pov_exporter.ExportScript(); , and for each timestep you must use pov_exporter.ExportData(); actually this is the instruction that creates the many .dat and .pov files in the output directory.

//
// RUN THE SIMULATION AND SAVE THE POVray FILES AT EACH FRAME
//
// 1) Create the two .pov and .ini files for POV-Ray (this must be done
// only once at the beginning of the simulation).
pov_exporter.ExportScript();
while (sys.GetChTime() < 1.5) {
sys.DoStepDynamics(0.01);
std::cout << "time= " << sys.GetChTime() << std::endl;
// 2) Create the incremental nnnn.dat and nnnn.pov files that will be load
// by the pov .ini script in POV-Ray (do this at each simulation timestep)
pov_exporter.ExportData();
}
// That's all! If all worked ok, this python script should
// have created a "rendering_frames.pov.ini" file that you can
// load in POV-Ray, then when you press 'RUN' you will see that
// POV-Ray will start rendering a short animation, saving the frames
// in the directory 'anim'.

Executing and rendering with POVray

Once you created your program, compile it, then:

  • execute the demo_POST_povray1.exe
  • on the console you will see a time counter showing that the system is load and it is being simulated
  • when the program ends, you must open POVray and open the rendering_frames.pov.ini file, using the Open menu or button, or drag&drop (you can find this .ini file and other POVray as they are saved in the same directory of the executable)

  • press the Run button in POVray to execute the .ini file , and you should see that POVray generates lot of frames, being saved in the directory anim.

Optional encoding into an AVI or MPEG animation

If you want to generate a .mpeg or .avi animation from the rendered .bmp images, we suggest to use the VirtualDub tool:

  • drag&drop the first .jpg frame in its interface; it will automatically load all other frames in the timeline
  • use menu Video/Compression... to setup the proper video codec (suggested: Xvid, DivX, mpeg4, etc.)
  • use menu File/Save As Avi... to encode and save the animation on disk.

Listing

In the following we report the entire source code for reference.

// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at the top level of the distribution and at
// http://projectchrono.org/license-chrono.txt.
//
// =============================================================================
// Authors: Alessandro Tasora
// =============================================================================
//
// Demo code about using the assets system to create shapes that can be shown in
// the 3D postprocessing, by using POVray.
//
// =============================================================================
#include "chrono/assets/ChVisualShapeBox.h"
#include "chrono/assets/ChCamera.h"
#include "chrono/assets/ChVisualShapeCylinder.h"
#include "chrono/assets/ChVisualShapeModelFile.h"
#include "chrono/assets/ChVisualShapeSphere.h"
#include "chrono/assets/ChTexture.h"
#include "chrono/physics/ChParticleCloud.h"
#include "chrono/physics/ChSystemNSC.h"
#include "chrono/core/ChRandom.h"
#include "chrono_postprocess/ChPovRay.h"
#include "chrono_thirdparty/filesystem/path.h"
using namespace chrono;
using namespace chrono::postprocess;
int main(int argc, char* argv[]) {
std::cout << "Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION << std::endl;
// Create output directory
std::string out_dir = GetChronoOutputPath() + "POVRAY_1";
if (!filesystem::create_directory(filesystem::path(out_dir))) {
std::cout << "Error creating directory " << out_dir << std::endl;
return 1;
}
// Create a Chrono system and set the associated collision system
/* Start example */
// Create an exporter to POVray
ChPovRay pov_exporter = ChPovRay(&sys);
// Important: set the path to the template:
pov_exporter.SetTemplateFile(GetChronoDataFile("POVRay_chrono_template.pov"));
// Set the path where it will save all .pov, .ini, .asset and .dat files, a directory will be created if not
// existing
pov_exporter.SetBasePath(out_dir);
// Optional: change the default naming of the generated files:
// pov_exporter.SetOutputScriptFile("rendering_frames.pov");
// pov_exporter.SetOutputDataFilebase("my_state");
// pov_exporter.SetPictureFilebase("picture");
// --Optional: modify default light
pov_exporter.SetLight(ChVector3d(-3, 4, 2), ChColor(0.15f, 0.15f, 0.12f), false);
// --Optional: add further POV commands, for example in this case:
// create an area light for soft shadows
// create a Grid object; Grid() parameters: step, linewidth, linecolor, planecolor
// Remember to use \ at the end of each line for a multiple-line string.
" \
light_source { \
<2, 10, -3> \
color rgb<1.2,1.2,1.2> \
area_light <4, 0, 0>, <0, 0, 4>, 8, 8 \
adaptive 1 \
jitter\
} \
object{ Grid(1,0.02, rgb<0.7,0.8,0.8>, rgbt<1,1,1,1>) rotate <0, 0, 90> } \
");
/* End example */
/* Start example */
// Create a ChBody, and attach some 'assets'
// that define 3D shapes. These shapes can be shown
// by Irrlicht or POV postprocessing, etc...
// Note: these assets are independent from collision shapes!
// Create a rigid body as usual, and add it
// to the physical system:
auto floor = chrono_types::make_shared<ChBody>();
floor->SetFixed(true);
// Define a collision shape
auto floor_mat = chrono_types::make_shared<ChContactMaterialNSC>();
auto floor_shape = chrono_types::make_shared<ChCollisionShapeBox>(floor_mat, 20, 1, 20);
floor->AddCollisionShape(floor_shape, ChFrame<>(ChVector3d(0, -1, 0), QUNIT));
floor->EnableCollision(true);
// Add body to system
sys.Add(floor);
// ==Asset== attach a 'box' shape.
auto boxfloor = chrono_types::make_shared<ChVisualShapeBox>(20, 1, 20);
boxfloor->SetColor(ChColor(0.3f, 0.3f, 0.6f));
floor->AddVisualShape(boxfloor, ChFrame<>(ChVector3d(0, -1, 0)));
/* End example */
/* Start example */
// Textures, colors, asset levels with transformations.
// Create the rigid body as usual (this won't move, it is only for visualization tests)
auto body = chrono_types::make_shared<ChBody>();
body->SetFixed(true);
sys.Add(body);
// ==Asset== Attach a 'sphere' shape
auto sphere = chrono_types::make_shared<ChVisualShapeSphere>(0.5);
body->AddVisualShape(sphere, ChFrame<>(ChVector3d(-1, 0, 0)));
// ==Asset== Attach also a 'box' shape
auto mbox = chrono_types::make_shared<ChVisualShapeBox>(0.4, 1.0, 0.2);
body->AddVisualShape(mbox, ChFrame<>(ChVector3d(1, 0, 0)));
// ==Asset== Attach also a 'cylinder' shape
auto cyl = chrono_types::make_shared<ChVisualShapeCylinder>(0.3, 0.7);
body->AddVisualShape(cyl, ChFrame<>(ChVector3d(2, 0.15, 0), QuatFromAngleX(CH_PI_2)));
body->AddVisualShape(chrono_types::make_shared<ChVisualShapeSphere>(0.03),
ChFrame<>(ChVector3d(2, -0.2, 0), QUNIT));
body->AddVisualShape(chrono_types::make_shared<ChVisualShapeSphere>(0.03),
ChFrame<>(ChVector3d(2, +0.5, 0), QUNIT));
// ==Asset== Attach a 'Wavefront mesh' asset, referencing a .obj file:
auto objmesh = chrono_types::make_shared<ChVisualShapeModelFile>();
objmesh->SetFilename(GetChronoDataFile("models/forklift/body.obj"));
objmesh->SetTexture(GetChronoDataFile("textures/bluewhite.png"));
body->AddVisualShape(objmesh, ChFrame<>(ChVector3d(0, 0, 2)));
// ==Asset== Attach an array of boxes, each rotated to make a spiral
for (int j = 0; j < 20; j++) {
auto smallbox = chrono_types::make_shared<ChVisualShapeBox>(0.2, 0.2, 0.02);
smallbox->SetColor(ChColor(j * 0.05f, 1 - j * 0.05f, 0.0f));
ChMatrix33<> rot(QuatFromAngleY(j * 21 * CH_DEG_TO_RAD));
ChVector3d pos = rot * ChVector3d(0.4, 0, 0) + ChVector3d(0, j * 0.02, 0);
body->AddVisualShape(smallbox, ChFrame<>(pos, rot));
}
// ==Asset== Attach a video camera.
// Note that a camera can also be attached to a moving object.
auto camera = chrono_types::make_shared<ChCamera>();
camera->SetAngle(50);
camera->SetPosition(ChVector3d(-3, 4, -5));
camera->SetAimPoint(ChVector3d(0, 1, 0));
body->AddCamera(camera);
/* End example */
/* Start example */
// Create a ChParticleClones cluster, and attach 'assets'
// that define a single "sample" 3D shape. This will be shown
// N times in Irrlicht.
// Create the ChParticleClones, populate it with some random particles,
// and add it to physical system:
auto particles = chrono_types::make_shared<ChParticleCloud>();
// Note: coll. shape, if needed, must be specified before creating particles
auto particle_mat = chrono_types::make_shared<ChContactMaterialNSC>();
auto particle_shape = chrono_types::make_shared<ChCollisionShapeSphere>(particle_mat, 0.05);
particles->AddCollisionShape(particle_shape);
particles->EnableCollision(true);
// Create the random particles
for (int np = 0; np < 100; ++np)
particles->AddParticle(ChCoordsys<>(ChVector3d(ChRandom::Get() - 2, 1, ChRandom::Get() - 0.5)));
// Do not forget to add the particle cluster to the system:
sys.Add(particles);
// ==Asset== Attach a 'sphere' shape asset.. it will be used as a sample
// shape to display all particles when rendering in 3D!
auto sphereparticle = chrono_types::make_shared<ChVisualShapeSphere>(0.05);
particles->AddVisualShape(sphereparticle);
/* End example */
// Export all existing visual shapes to POV-Ray
pov_exporter.AddAll();
// (Optional: tell selectively which physical items you
// want to render in the folllowing way...)
// pov_exporter.RemoveAll();
// pov_exporter.Add(floor);
// pov_exporter.Add(body);
// pov_exporter.Add(particles);
/* Start example */
//
// RUN THE SIMULATION AND SAVE THE POVray FILES AT EACH FRAME
//
// 1) Create the two .pov and .ini files for POV-Ray (this must be done
// only once at the beginning of the simulation).
pov_exporter.ExportScript();
while (sys.GetChTime() < 1.5) {
sys.DoStepDynamics(0.01);
std::cout << "time= " << sys.GetChTime() << std::endl;
// 2) Create the incremental nnnn.dat and nnnn.pov files that will be load
// by the pov .ini script in POV-Ray (do this at each simulation timestep)
pov_exporter.ExportData();
}
// That's all! If all worked ok, this python script should
// have created a "rendering_frames.pov.ini" file that you can
// load in POV-Ray, then when you press 'RUN' you will see that
// POV-Ray will start rendering a short animation, saving the frames
// in the directory 'anim'.
/* End example */
return 0;
}
std::string GetChronoDataFile(const std::string &filename)
Get the full path to the specified filename, given relative to the Chrono data directory (thread safe...
Definition: ChGlobal.cpp:37
ChQuaterniond QuatFromAngleX(double angle)
Convert from a rotation about X axis to a quaternion.
Definition: ChRotation.cpp:188
void Add(std::shared_ptr< ChPhysicsItem > item)
Attach an arbitrary ChPhysicsItem (e.g.
Definition: ChSystem.cpp:196
void SetBasePath(const std::string &mpath)
Set the path where all files (.ini, .pov, .assets etc) will be saved.
Definition: ChPovRay.h:80
Representation of a transform with translation and rotation.
Definition: ChCoordsys.h:28
const ChQuaterniond QUNIT(1., 0., 0., 0.)
Constant unit quaternion: {1, 0, 0, 0} , corresponds to no rotation (diagonal rotation matrix)
Definition: ChQuaternion.h:431
void ExportScript()
Export the script that will be used by POV to process all the exported data and render the complete a...
Definition: ChPovRay.h:184
Definition of a 3x3 fixed-size matrix to represent 3D rotations and inertia tensors.
Definition: ChMatrix33.h:31
void SetTemplateFile(const std::string &filename)
Set the filename of the template for the script generation.
Definition: ChPovRay.h:84
void ExportData()
This function is used at each timestep to export data formatted in a way that it can be load with the...
Definition: ChPovRay.cpp:752
void SetCustomPOVcommandsScript(const std::string &text)
Set a string (a text block) of custom POV commands that you can optionally append to the POV script f...
Definition: ChPovRay.h:166
Namespace with classes for the POSTPROCESS module.
Definition: ChApiPostProcess.h:54
Representation of a 3D transform.
Definition: ChFrame.h:41
ChQuaterniond QuatFromAngleY(double angle)
Convert from a rotation about Y axis to a quaternion.
Definition: ChRotation.cpp:192
Definition of a visual color.
Definition: ChColor.h:30
const std::string & GetChronoOutputPath()
Obtain the path to the output directory for Chrono demos.
Definition: ChGlobal.cpp:52
int DoStepDynamics(double step_size)
Advance the dynamics simulation by a single time step of given length.
Definition: ChSystem.cpp:1635
Bullet-based collision detection system.
ChVector3< double > ChVector3d
Alias for double-precision vectors.
Definition: ChVector3.h:283
void AddAll()
Add all ChPhysicsItem objects in the system to the list of objects to render.
Definition: ChPovRay.cpp:88
Class for post processing implementation that generates scripts for POVray.
Definition: ChPovRay.h:33
Main namespace for the Chrono package.
Definition: ChCamera.cpp:17
double GetChTime() const
Get the simulation time of this system.
Definition: ChSystem.h:154
void SetLight(ChVector3d location, ChColor color, bool cast_shadow)
Set the default light position and color - will write this in the output .pov file.
Definition: ChPovRay.cpp:157
virtual void SetCollisionSystemType(ChCollisionSystem::Type type)
Set the collision detection system used by this Chrono system to the specified type.
Definition: ChSystem.cpp:324
Class for a physical system in which contact is modeled using a non-smooth (complementarity-based) me...
Definition: ChSystemNSC.h:29