back to home

Tutorial 1: Basic Animation with OpenSceneGraph

Ok it is time to get things moving! We will try to make some objects fly around the screen. The complete source code is at the end of this page so you can have a look at it and try to understand it by reading the code.

main() function

	int main() {
		Group *scene = startupScene();

		osgViewer::Viewer viewer;
		viewer.setSceneData(scene);
		viewer.setCameraManipulator(new osgGA::TrackballManipulator);
		viewer.realize();

		while (!viewer.done()) {		
			viewer.frame();
			update(0.005); // do the update advancing 5ms
		} 

		return 0;
	}

All we do here is calling the startupScene() function which will return a root-Group including all the start positions and geometry of our scene.
The function does also fill the global transform-Array with the start positions of each object in the scene.

Lines 4-7 initialize the standart Viewer and TrackballManipulator which allow us to fly through the scene with the mouse and some key combinations

Then there is the most important part to understand:

	while (!viewer.done()) {		
		viewer.frame();
		update(0.005); // do the update advancing 5ms
	}

This loop keeps running until the application quits and each run it draws one frame onto the screen! So in each frame we call the function update() with a parameter that controls the speed of the animation. (0.005 in this case)

update() function

	void update(float dt) {
		// keep track of the time
		myTimer += dt;

		int a = 3;
		int b = 4;
		double ro = ((b - 1)/b) * (PI / 2);

		// static geo in the middle
		transform[0]->setPosition(Vec3(0, 0, CHILDREN/2));

		for (int i = 1; i < CHILDREN; i++) {
			transform[i]->setPosition(Vec3(sin(a * myTimer*i + ro) * (10 + i), sin(b * myTimer*i) * (10 + i), i));
		}
	}

This is the entire code which controles all the movements of all objects in the scene. Each time this function gets called myTimer will be increased by 0.005 as defined in the call of the function in main(). Then one object gets placed in the middle (line 10) and we run though all the other objects in a for-loop to update their positions (line 12-14).
This looks a bit more complicated than it is because of the way they move:
The objects in the example are moving along Lissajous curves (wikipedia link) and most of the lines of code you see there and wonder what they do are just part of the Lissajous curve-formula to calculate the correct coordinates.

The call for the setPosition-method without all the Lissajous curve clutter looks like this:

	// call of PositionAttitudeTransform::setPosition
	transform[i]->setPosition(Vec3(myNewX, myNewY, myNewZ));

So now go ahead and play around with the setPosition()-Method to create some fancy animation!

Complete Source Code

	#include <osg/Node>
	#include <osg/Group>
	#include <osg/Geode>
	#include <osg/Geometry>
	#include <osgViewer/Viewer>
	#include <osg/PositionAttitudeTransform>
	#include <osgGA/TrackballManipulator>
	using namespace osg;

	float myTimer = 0;

	const int CHILDREN = 10;
	PositionAttitudeTransform *transform[CHILDREN];

	Group * startupScene()
	{
		// vertex array
		Vec3Array *vertexArray = new Vec3Array();
		vertexArray->push_back(Vec3(-1, -1, 0)); // front left 
		vertexArray->push_back(Vec3(+1, -1, 0)); // front right 
		vertexArray->push_back(Vec3(+1, +1, 0)); // back right 
		vertexArray->push_back(Vec3(-1, +1, 0)); // back left 
		vertexArray->push_back(Vec3( 0,  0, sqrt(2))); // peak
		vertexArray->push_back(Vec3( 0,  0, -sqrt(2))); // lower peak

		// face array
		// give indices of vertices in counter-clockwise order
		DrawElementsUInt *faceArray = new DrawElementsUInt(PrimitiveSet::TRIANGLES, 0);
		faceArray->push_back(0); // face 0
		faceArray->push_back(1);
		faceArray->push_back(4);
		faceArray->push_back(1); // face 1
		faceArray->push_back(2);
		faceArray->push_back(4);
		faceArray->push_back(2); // face 2
		faceArray->push_back(3);
		faceArray->push_back(4);
		faceArray->push_back(3); // face 3
		faceArray->push_back(0);
		faceArray->push_back(4);
		faceArray->push_back(0); // face 4
		faceArray->push_back(5);
		faceArray->push_back(1);
		faceArray->push_back(2); // face 5
		faceArray->push_back(1);
		faceArray->push_back(5);
		faceArray->push_back(3); // face 6
		faceArray->push_back(2);
		faceArray->push_back(5);
		faceArray->push_back(0); // face 7
		faceArray->push_back(3);
		faceArray->push_back(5);

		// vertex color array
		Vec4Array *colorArray = new Vec4Array();
		colorArray->push_back(Vec4(1.0f, 0.0f, 0.0f, 1.0f) ); //index 0 red
		colorArray->push_back(Vec4(0.0f, 1.0f, 0.0f, 1.0f) ); //index 1 green
		colorArray->push_back(Vec4(0.0f, 0.0f, 1.0f, 1.0f) ); //index 2 blue
		colorArray->push_back(Vec4(1.0f, 0.0f, 1.0f, 1.0f) ); //index 3 purple
		colorArray->push_back(Vec4(1.0f, 1.0f, 1.0f, 1.0f) ); //index 4 white

		// color index
		TemplateIndexArray<unsigned int, Array::UIntArrayType, 5, 4> *colorIndexArray;
		colorIndexArray = new TemplateIndexArray<unsigned int, Array::UIntArrayType, 5, 4>();
		colorIndexArray->push_back(0); // vertex 0 assigned color array element 0
		colorIndexArray->push_back(1); // vertex 1 assigned color array element 1
		colorIndexArray->push_back(2); // vertex 2 assigned color array element 2
		colorIndexArray->push_back(3); // vertex 3 assigned color array element 3
		colorIndexArray->push_back(4); // vertex 4 assigned color array element 4
		colorIndexArray->push_back(4); // vertex 5 assigned color array element 4

		Geometry *geometry = new Geometry();
		geometry->setVertexArray(vertexArray);
		geometry->setColorArray(colorArray);
		geometry->setColorIndices(colorIndexArray);
		geometry->setColorBinding(Geometry::BIND_PER_VERTEX);
		geometry->addPrimitiveSet(faceArray);

		Geode *pyramidObject = new Geode();
		pyramidObject->addDrawable(geometry);

		Group *root = new Group();

		for (int i = 0; i < CHILDREN; i++) {
			transform[i] = new PositionAttitudeTransform;
			transform[i]->setPosition(Vec3(0,0,0));
			transform[i]->addChild(pyramidObject);

			root->addChild(transform[i]);
		}

		return root;
	}

	void update(float dt) {
		// keep track of the time
		myTimer += dt;

		int a = 3;
		int b = 4;
		double ro = ((b - 1)/b) * (PI / 2);

		// static geo in the middle
		transform[0]->setPosition(Vec3(0, 0, CHILDREN/2));

		for (int i = 1; i < CHILDREN; i++) {
			transform[i]->setPosition(Vec3(sin(a * myTimer*i + ro) * (10 + i), sin(b * myTimer*i) * (10 + i), i));
		}
	}

	int main() {
		Group *scene = startupScene();

		osgViewer::Viewer viewer;
		viewer.setSceneData(scene);
		viewer.setCameraManipulator(new osgGA::TrackballManipulator);
		viewer.realize();

		while (!viewer.done()) {		
			viewer.frame();
			update(0.005); // do the update advancing 5ms
		} 

		return 0;
	}

Slightly Advanced Example: Star-field simulation

Now you know how to animate objects in OSG. Have a look at the following example to see whats possible with the same basic tools! (Please rotate the camera inside the Star-field to see what it is about!)

	#include <osg/Node>
	#include <osg/Group>
	#include <osg/Geode>
	#include <osg/Geometry>
	#include <osgViewer/Viewer>
	#include <osg/PositionAttitudeTransform>
	#include <osgGA/TrackballManipulator>
	using namespace osg;

	float myTimer = 0;

	const int CHILDREN = 1000;
	PositionAttitudeTransform *transform[CHILDREN];

	Group * startupScene()
	{
		// vertex array
		Vec3Array *vertexArray = new Vec3Array();
		vertexArray->push_back(Vec3(-1, -1, 0)); // front left 
		vertexArray->push_back(Vec3(+1, -1, 0)); // front right 
		vertexArray->push_back(Vec3(+1, +1, 0)); // back right 
		vertexArray->push_back(Vec3(-1, +1, 0)); // back left 
		vertexArray->push_back(Vec3( 0,  0, sqrt(2))); // peak
		vertexArray->push_back(Vec3( 0,  0, -sqrt(2))); // lower peak

		// face array
		// give indices of vertices in counter-clockwise order
		DrawElementsUInt *faceArray = new DrawElementsUInt(PrimitiveSet::TRIANGLES, 0);
		faceArray->push_back(0); // face 0
		faceArray->push_back(1);
		faceArray->push_back(4);
		faceArray->push_back(1); // face 1
		faceArray->push_back(2);
		faceArray->push_back(4);
		faceArray->push_back(2); // face 2
		faceArray->push_back(3);
		faceArray->push_back(4);
		faceArray->push_back(3); // face 3
		faceArray->push_back(0);
		faceArray->push_back(4);
		faceArray->push_back(0); // face 4
		faceArray->push_back(5);
		faceArray->push_back(1);
		faceArray->push_back(2); // face 5
		faceArray->push_back(1);
		faceArray->push_back(5);
		faceArray->push_back(3); // face 6
		faceArray->push_back(2);
		faceArray->push_back(5);
		faceArray->push_back(0); // face 7
		faceArray->push_back(3);
		faceArray->push_back(5);

		// vertex color array
		Vec4Array *colorArray = new Vec4Array();
		colorArray->push_back(Vec4(1.0f, 0.0f, 0.0f, 1.0f) ); //index 0 red
		colorArray->push_back(Vec4(0.0f, 1.0f, 0.0f, 1.0f) ); //index 1 green
		colorArray->push_back(Vec4(0.0f, 0.0f, 1.0f, 1.0f) ); //index 2 blue
		colorArray->push_back(Vec4(1.0f, 0.0f, 1.0f, 1.0f) ); //index 3 purple
		colorArray->push_back(Vec4(1.0f, 1.0f, 1.0f, 1.0f) ); //index 4 white

		// color index
		TemplateIndexArray<unsigned int, Array::UIntArrayType, 5, 4> *colorIndexArray;
		colorIndexArray = new TemplateIndexArray<unsigned int, Array::UIntArrayType, 5, 4>();
		colorIndexArray->push_back(0); // vertex 0 assigned color array element 0
		colorIndexArray->push_back(1); // vertex 1 assigned color array element 1
		colorIndexArray->push_back(2); // vertex 2 assigned color array element 2
		colorIndexArray->push_back(3); // vertex 3 assigned color array element 3
		colorIndexArray->push_back(4); // vertex 4 assigned color array element 4
		colorIndexArray->push_back(4); // vertex 5 assigned color array element 4

		Geometry *geometry = new Geometry();
		geometry->setVertexArray(vertexArray);
		geometry->setColorArray(colorArray);
		geometry->setColorIndices(colorIndexArray);
		geometry->setColorBinding(Geometry::BIND_PER_VERTEX);
		geometry->addPrimitiveSet(faceArray);

		Geode *pyramidObject = new Geode();
		pyramidObject->addDrawable(geometry);

		Group *root = new Group();

		for (int i = 0; i < CHILDREN; i++) {
			transform[i] = new PositionAttitudeTransform;
			transform[i]->setPosition(Vec3(((float)rand() / RAND_MAX*4) * 100, ((float)rand() / RAND_MAX*4) * 100, 0));
			transform[i]->addChild(pyramidObject);

			root->addChild(transform[i]);
		}

		return root;
	}

	void update(float dt) {
		// keep track of the time
		myTimer += dt;

		//float myRand = ((float)rand() / RAND_MAX);

		for (int i = 0; i < CHILDREN; i++) {
			Vec3d currentPosition = transform[i]->getPosition();
			Vec3d *newPosition;


			if (currentPosition.z() < 1000) {
				// moves element up by dt multiplied with additional speed
				newPosition = new Vec3d(currentPosition.x(), currentPosition.y(), currentPosition.z() + dt * (i+1)/10);
			} else {
				// brings element back to z=0 when z>1000
				newPosition = new Vec3d(currentPosition.x(), currentPosition.y(), 0);
			}

			transform[i]->setPosition(Vec3(newPosition->x(), newPosition->y(), newPosition->z()));
		}
	}

	int main() {
		Group *scene = startupScene();

		osgViewer::Viewer viewer;
		viewer.setSceneData(scene);
		viewer.setCameraManipulator(new osgGA::TrackballManipulator);
		viewer.realize();

		while (!viewer.done()) {		
			viewer.frame();
			update(0.5); // do the update advancing 500ms
		} 

		return 0;
	}