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:
floor->SetFixed(true);
// Define a collision shape
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)
body->SetFixed(true);
sys.Add(body);
// ==Asset== Attach a 'sphere' shape
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
body->AddVisualShape(cyl, ChFrame<>(ChVector3d(2, 0.15, 0), QuatFromAngleX(CH_PI_2)));
ChFrame<>(ChVector3d(2, -0.2, 0), QUNIT));
ChFrame<>(ChVector3d(2, +0.5, 0), QUNIT));
// ==Asset== Attach a 'Wavefront mesh' asset, referencing a .obj file:
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.
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:
// Note: coll. shape, if needed, must be specified before creating particles
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!
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.
" \
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
sys.SetCollisionSystemType(ChCollisionSystem::Type::BULLET);
/* 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:
floor->SetFixed(true);
// Define a collision shape
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)
body->SetFixed(true);
sys.Add(body);
// ==Asset== Attach a 'sphere' shape
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
body->AddVisualShape(cyl, ChFrame<>(ChVector3d(2, 0.15, 0), QuatFromAngleX(CH_PI_2)));
ChFrame<>(ChVector3d(2, -0.2, 0), QUNIT));
ChFrame<>(ChVector3d(2, +0.5, 0), QUNIT));
// ==Asset== Attach a 'Wavefront mesh' asset, referencing a .obj file:
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.
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:
// Note: coll. shape, if needed, must be specified before creating particles
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!
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;
}
Definition of a visual color.
Definition ChColor.h:30
Representation of a transform with translation and rotation.
Definition ChCoordsys.h:28
Representation of a 3D transform.
Definition ChFrame.h:33
Definition of a 3x3 fixed-size matrix to represent 3D rotations and inertia tensors.
Definition ChMatrix33.h:31
virtual void SetCollisionSystemType(ChCollisionSystem::Type type)
Set the collision detection system used by this Chrono system to the specified type.
Definition ChSystem.cpp:324
int DoStepDynamics(double step_size)
Advance the dynamics simulation by a single time step of given length.
Definition ChSystem.cpp:1632
double GetChTime() const
Get the simulation time of this system.
Definition ChSystem.h:154
void Add(std::shared_ptr< ChPhysicsItem > item)
Attach an arbitrary ChPhysicsItem (e.g.
Definition ChSystem.cpp:196
Class for a physical system in which contact is modeled using a non-smooth (complementarity-based) me...
Definition ChSystemNSC.h:29
Class for post processing implementation that generates scripts for POVray.
Definition ChPovRay.h:33
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
void ExportData()
This function is used at each timestep to export data formatted in a way that it can be load with the...
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
void SetLight(ChVector3d location, ChColor color, bool cast_shadow)
Set the default light position and color - will write this in the output .pov file.
void SetTemplateFile(const std::string &filename)
Set the filename of the template for the script generation.
Definition ChPovRay.h:84
void AddAll()
Add all ChPhysicsItem objects in the system to the list of objects to render.
void SetBasePath(const std::string &mpath)
Set the path where all files (.ini, .pov, .assets etc) will be saved.
Definition ChPovRay.h:80
Namespace with classes for the POSTPROCESS module.
Definition ChApiPostProcess.h:54
std::shared_ptr< T > make_shared(Args &&... args)
Replacement for make_shared guaranteed to use operator new rather than placement new in order to avoi...
Definition ChTypes.h:66
Main namespace for the Chrono package.
Definition ChCamera.cpp:17