Creating a scene node
We will learn how to create a new scene node and attach our 3D model to it.
How to create a scene node with Ogre 3D
We will follow these steps:
In the old version of our code, we had the following two lines in the createScene() function:
Ogre::Entity* ent = mSceneMgr->createEntity("MyEntity","Sinbad.mesh");
mSceneMgr->getRootSceneNode()->attachObject(ent);
Replace the last line with the following:
Ogre::SceneNode* node = mSceneMgr->createSceneNode("Node1");
Then add the following two lines; the order of those two lines is irrelevant forthe resulting scene:
mSceneMgr->getRootSceneNode()->addChild(node);
node->attachObject(ent);
Compile and start the application.
What just happened?
We created a new scene node named Node 1. Then we added the scene node to the root scene node. After this, we attached our previously created 3D model to the newly created scene node so it would be visible.
How to work with the RootSceneNode
The call mSceneMgr->getRootSceneNode() returns the root scene node. This scene node is a member variable of the scene manager. When we want something to be visible, we need to attach it to the root scene node or a node which is a child or a descendent in any way. In short, there needs to be a chain of child relations from the root node to the node; otherwise it won't be rendered.
As the name suggests, the root scene node is the root of the scene. So the entire scene will be, in some way, attached to the root scene node. Ogre 3D uses a so-called scene graph to organize the scene. This graph is like a tree, it has one root, the root scene node, and each node can have children. We already have used this characteristic when we called mSceneMgr->getRootSceneNode()->addChild(node);. There we added the created scene node as a child to the root. Directly afterwards, we added another kind of child to the scene node with node->attachObject(ent);. Here, we added an entity to the scene node. We have two different kinds of objects we can add to a scene node.
Firstly, we have other scene nodes, which can be added as children and have children themselves. Secondly, we have entities that we want rendered. Entities aren't children and can't have children themselves. They are data objects which are associated with the node and can be thought of as leaves of the tree. There are a lot of other things we can add to a scene, like lights, particle systems, and so on. We will later learn what these things are and how to use them. Right now, we only need entities. Our current scene graph looks like this:
The first thing we need to understand is what a scene graph is and what it does. A scene graph is used to represent how different parts of a scene are related to each other in 3D space.
3D space
Ogre 3D is a 3D rendering engine, so we need to understand some basic 3D concepts. The most basic construct in 3D is a vector, which is represented by an ordered triple (x,y,z).
Each position in a 3D space can be represented by such a triple using the Euclidean coordination system for three dimensions. It is important to know that there are different kinds of coordinate systems in 3D space. The only difference between the systems is the orientation of the axis and the positive rotation direction. There are two systems that are widely used, namely, the left-handed and the right-handed versions. In the following image, we see both systems—on the left side, we see the left-handed version; and on the right side, we see the right-handed one.
Source:http://en.wikipedia.org/wiki/File:Cartesian_coordinate_system_handedness.svg
The names left-and right-handed are based on the fact that the orientation of the axis can be reconstructed using the left and right hand. The thumb is the x-axis, the index finger the y-axis, and the middle finger the z-axis. We need to hold our hands so that we have a ninety-degree angle between thumb and index finger and also between middle and index finger. When using the right hand, we get a right-handed coordination system. When using the left hand, we get the left-handed version.
Ogre uses the right-handed system, but rotates it so that the positive part of the x-axis is pointing right and the negative part of the x-axis points to the left. The y-axis is pointing up and the z-axis is pointing out of the screen and it is known as the y-up convention. This sounds irritating at first, but we will soon learn to think in this coordinate system. The website http://viz.aset.psu.edu/gho/sem_notes/3d_fundamentals/html/3d_coordinates.html contains a rather good picture-based explanation of the different coordination systems and how they relate to each other.
Scene graph
A scene graph is one of the most used concepts in graphics programming. Simply put, it's a way to store information about a scene. We already discussed that a scene graph has a root and is organized like a tree. But we didn't touch on the most important function of a scene graph. Each node of a scene graph has a list of its children as well as a transformation in the 3D space. The transformation is composed of three aspects, namely, the position, the rotation, and the scale. The position is a triple (x,y,z), which obviously describes the position of the node in the scene. The rotation is stored using a quaternion, a mathematical concept for storing rotations in 3D space, but we can think of rotations as a single floating point value for each axis, describing how the node is rotated using radians as units. Scaling is quite easy; again, it uses a triple (x,y,z), and each part of the triple is simply the factor to scale the axis with.
The important thing about a scene graph is that the transformation is relative to the parent of the node. If we modify the orientation of the parent, the children will also be affected by this change. When we move the parent 10 units along the x-axis, all children will also be moved by 10 units along the x-axis. The final orientation of each child is computed using the orientation of all parents. This fact will become clearer with the next diagram.
The position of MyEntity in this scene will be (10,0,0) and MyEntity2 will be at (10,10,20).
Let's try this in Ogre 3D.
Pop quiz – finding the position of scene nodes
Look at the following tree and determine the end positions of MyEntity and MyEntity2:
MyEntity(60,60,60) and MyEntity2(0,0,0)
MyEntity(70,50,60) and MyEntity2(10,-10,0)
MyEntity(60,60,60) and MyEntity2(10,10,10)
Setting the position of a scene node
Now, we will try to create the setup of the scene from the diagram before the previous image.
Time for action – setting the position of a scene node
Add this new line after the creation of the scene node:
node->setPosition(10,0,0);
To create a second entity, add this line at the end of the createScene() function:
Ogre::Entity* ent2 = mSceneMgr->createEntity("MyEntity2","Sinbad.
mesh");
Then create a second scene node:
Ogre::SceneNode* node2 = mSceneMgr->createSceneNode("Node2");
Add the second node to the first one:
node->addChild(node2);
Set the position of the second node:
node2->setPosition(0,10,20);
Attach the second entity to the second node:
node2->attachObject(ent2);
Compile the program and you should see two instances of Sinbad:
What just happened?
We created a scene which matches the preceding diagram. The first new function we used was at step 1. Easily guessed, the function setPosition(x,y,z) sets the position of the node to the given triple. Keep in mind that this position is relative to the parent. We wanted MyEntity2 to be at (10,10,20), because we added node2, which holds MyEntity2, to a scene node which already was at the position (10,0,0). We only needed to set the position of node2 to (0,10,20). When both positions combine, MyEntity2 will be at (10,10,20).
Pop quiz – playing with scene nodes
We have the scene node node1 at (0,20,0) and we have a child scene node node2, which has an entity attached to it. If we want the entity to be rendered at (10,10,10), at which position would we need to set node2?
(10,10,10)
(10,-10,10)
(-10,10,-10)
Have a go hero – adding a Sinbad
Add a third instance of Sinbad and let it be rendered at the position (10,10,30).
Rotating a scene node
We already know how to set the position of a scene node. Now, we will learn how to rotate a scene node and another way to modify the position of a scene node.
Time for action – rotating a scene node
We will use the previous code, but create completely new code for the createScene() function.
Remove all code from the createScene() function.
First create an instance of Sinbad.mesh and then create a new scene node. Set the position of the scene node to (10,10,0), at the end attach the entity to the node, and add the node to the root scene node as a child:
Ogre::Entity* ent = mSceneMgr->createEntity("MyEntity","Sinbad.
mesh");
Ogre::SceneNode* node = mSceneMgr->createSceneNode("Node1");
node->setPosition(10,10,0);
mSceneMgr->getRootSceneNode()->addChild(node);
node->attachObject(ent);
Again, create a new instance of the model, also a new scene node, and set the position to (10,0,0):
Ogre::Entity* ent2 = mSceneMgr->createEntity("MyEntity2","Sinbad.
mesh");
Ogre::SceneNode* node2 = mSceneMgr->createSceneNode("Node2");
node->addChild(node2);
node2->setPosition(10,0,0);
Now add the following two lines to rotate the model and attach the entity to the scene node:
node2->pitch(Ogre::Radian(Ogre::Math::HALF_PI));
node2->attachObject(ent2);
Do the same again, but this time use the function yaw instead of the function pitch and the translate function instead of the setPosition function:
Ogre::Entity* ent3 = mSceneMgr->createEntity("MyEntity3","Sinbad.
mesh");
Ogre::SceneNode* node3 = mSceneMgr->createSceneNode("Node3",);
node->addChild(node3);
node3->translate(20,0,0);
node3->yaw(Ogre::Degree(90.0f));
node3->attachObject(ent3);
And the same again with roll instead of yaw or pitch:
Ogre::Entity* ent4 = mSceneMgr->createEntity("MyEntity4","Sinbad.
mesh");
Ogre::SceneNode* node4 = mSceneMgr->createSceneNode("Node4");
node->addChild(node4);
node4->setPosition(30,0,0);
node4->roll(Ogre::Radian(Ogre::Math::HALF_PI));
node4->attachObject(ent4);
Compile and run the program, and you should see the following screenshot:
What just happened?
We repeated the code we had before four times and always changed some small details. The first repeat is nothing special. It is just the code we had before and this instance of the model will be our reference model to see what happens to the other three instances we made afterwards.
In step 4, we added one following additional line:
node2->pitch(Ogre::Radian(Ogre::Math::HALF_PI));
The function pitch(Ogre::Radian(Ogre::Math::HALF_PI)) rotates a scene node around the x-axis. As said before, this function expects a radian as parameter and we used half of pi, which means a rotation of ninety degrees.
In step 5, we replaced the function call setPosition(x,y,z) with translate(x,y,z). The difference between setPosition(x,y,z) and translate(x,y,z) is that setPosition sets the position—no surprises here. translate adds the given values to the position of the scene node, so it moves the node relatively to its current position. If a scene node has the position (10,20,30) and we call setPosition(30,20,10), the node will then have the position (30,20,10). On the other hand, if we call translate(30,20,10), the node will have the position (40,40,40). It's a small, but important, difference. Both functions can be useful if used in the correct circumstances, like when we want to position in a scene, we would use the setPosition(x,y,z) function. However, when we want to move a node already positioned in the scene, we would use translate(x,y,z).
Also, we replaced pitch(Ogre::Radian(Ogre::Math::HALF_PI))with yaw(Ogre::Degree(90.0f)). The yaw() function rotates the scene node around the y-axis. Instead of Ogre::Radian(), we used Ogre::Degree(). Of course, Pitch and yaw still need a radian to be used. However, Ogre 3D offers the class Degree(), which has a cast operator so the compiler can automatically cast into a Radian(). Therefore, the programmer is free to use a radian or degree to rotate scene nodes. The mandatory use of the classes makes sure that it's always clear which is used, to prevent confusion and possible error sources.
Step 6 introduces the last of the three different rotate function a scene node has, namely, roll(). This function rotates the scene node around the z-axis. Again, we could use roll(Ogre::Degree(90.0f)) instead of roll(Ogre::Radian(Ogre::Math::HALF_PI)).
The program when run shows a non-rotated model and all three possible rotations. The left model isn't rotated, the model to the right of the left model is rotated around the x-axis, the model to the left of the right model is rotated around the y-axis, and the right model is rotated around the z-axis. Each of these instances shows the effect of a different rotate function. In short, pitch() rotates around the x-axis, yaw() around the y-axis, and roll() around the z-axis. We can either use Ogre::Degree(degree) or Ogre::Radian(radian) to specify how much we want to rotate.
Pop quiz – rotating a scene node
Which are the three functions to rotate a scene node?
pitch, yawn, roll
pitch, yaw, roll
pitching, yaw, roll
Have a go hero – using Ogre::Degree
Remodel the code we wrote for the previous section in such a way that each occurrence of Ogre::Radian is replaced with an Ogre::Degree and vice versa, and the rotation is still the same.
Scaling a scene node
We already have covered two of the three basic operations we can use to manipulate our scene graph. Now it's time for the last one, namely, scaling.
Time for action – scaling a scene node
Once again, we start with the same code block we used before.
Remove all code from the createScene() function and insert the following code block:
Ogre::Entity* ent = mSceneMgr->createEntity("MyEntity","Sinbad.
mesh");
Ogre::SceneNode* node = mSceneMgr->createSceneNode("Node1");
node->setPosition(10,10,0);
mSceneMgr->getRootSceneNode()->addChild(node);
node->attachObject(ent);
Again, create a new entity:
Ogre::Entity* ent2 = mSceneMgr->createEntity("MyEntity2","Sinbad.
mesh");
Now we use a function that creates the scene node and adds it automatically as a child. Then we do the same thing we did before:
Ogre::SceneNode* node2 = node->createChildSceneNode("node2");
node2->setPosition(10,0,0);
node2->attachObject(ent2);
Now, after the setPosition() function, call the following line to scale the model:
node2->scale(2.0f,2.0f,2.0f);
Create a new entity:
Ogre::Entity* ent3 = mSceneMgr->createEntity("MyEntity3","Sinbad.
mesh");
Now we call the same function as in step 3, but with an additional parameter:
Ogre::SceneNode* node3 = node->createChildSceneNode("node3",Ogre::
Vector3(20,0,0));
After the function call, insert this line to scale the model:
node3->scale(0.2f,0.2f,0.2f);
Compile the program and run it, and you should see the following image:
Read more