Basic Tutorial 1 (Intro to SceneManager, SceneNode, and Entity) - Axiom : A 3D Rendering Engine in C#

Basic Tutorial 1 (Intro to SceneManager, SceneNode, and Entity)

From Axiom

Jump to: navigation, search

Show code samples in

Contents

Prerequisites

  • This tutorial assumes you have basic knowledge of C#, VB.NET, or Python, or other .NET language, though only the mentioned languages will have sample code.
  • It also assumes that you have successfully set up an application. If you have not see this page for Windows and this page for Linux

Getting Started

  • If you don't already, add Axiom.Framework.dll to your references folder. For IronPython, simply make sure the referenced .dll's are in a directory that IronPython can find, usually the same directory where you keep Program.py.
  • A later tutorial will explain how to setup your own application.

In your IDE of choice, navigate to where you can create a new class and add the following code:


using System;

using Axiom.Core;
using Axiom.Math;
using Axiom.Graphics;

namespace Axiom.Tutorials
{
	public sealed class Game
	{
		private readonly Root _root;
		private readonly RenderWindow _window;
		private SceneManager _scene;
		private Camera _camera;
		
		public Game( Root root, RenderWindow window )
		{
			_root = root;
			_window = window;
		}

		public void OnLoad()
		{
 			ResourceGroupManager.Instance.AddResourceLocation( "media", "Folder", true );

			_scene = _root.CreateSceneManager( "DefaultSceneManager", "DefaultSM" );
			_scene.ClearScene();

			_camera = _scene.CreateCamera( "MainCamera" );

			_camera.Position = new Vector3( 0, 10, 200 );
			_camera.LookAt( Vector3.Zero );
			_camera.Near = 5;
			_camera.AutoAspectRatio = true;

			var vp = _window.AddViewport( _camera, 0, 0, 1.0f, 1.0f, 100 );
			vp.BackgroundColor = ColorEx.CornflowerBlue;

			ResourceGroupManager.Instance.InitializeAllResourceGroups();

		}

		public void CreateScene()
		{

		}

		public void OnUnload()
		{
		}

		public void OnRenderFrame( object s, FrameEventArgs e )
		{
		}

	}
}

Imports Axiom.Core
Imports Axiom.Math
Imports Axiom.Demos
Imports Axiom.Demos.Configuration

Namespace Axiom.Tutorials
	Public Class BasicTutorial1
		Inherits TechDemo


		Public Overrides Sub CreateScene()
		End Sub
		Protected Overrides Sub OnFrameStarted(source As Object, evt As FrameEventArgs)
			MyBase.OnFrameStarted(source, evt)
		End Sub

	End Class
End Namespace

import clr
clr.AddReferenceToFile("Axiom.dll")
clr.AddReferenceToFile("Axiom.Demos.dll")
from Axiom.Core import *
from Axiom.Math import *
from Axiom.Demos import TechDemo
from Axiom.Demos.Configuration import EngineConfig
class BasicTutorial1(TechDemo):
    def CreateScene(self):
        pass
    def OnFrameStarted(self, obj, evt):
       TechDemo.OnFrameStarted(self, obj, evt)
        
if __name__ == '__main__':
    root = Root('AxiomTutorial.log')
    root.RenderSystem = root.RenderSystems['OpenGL'] #or DirectX9
    config = EngineConfig()
    config.ReadXml('EngineConfig.xml')
        
    for row in config.FilePath:
        ResourceGroupManager.Instance.AddResourceLocation( row.src, row.type, row.group, False, True );

    ta = BasicTutorial1()
    ta.Start()
  • If you're not using Python, browse to your Main function and add the following code:
using Axiom.Core;
using Axiom.Graphics;

namespace Axiom.Tutorials
{
	internal static class Program
	{
		private static void Main()
		{
			using ( var engine = new Root() )
			{
				engine.RenderSystem = engine.RenderSystems[ 0 ];
				using ( var renderWindow = engine.Initialize( true ) )
				{
					var game = new Game( engine, renderWindow );
					game.OnLoad();
					game.CreateScene();
					engine.FrameRenderingQueued += game.OnRenderFrame;
					engine.StartRendering();
					game.OnUnload();
				}
			}
		}
	}
}
Namespace AxiomTutorials
    Module Program
        Public Sub Main(ByVal args As String())
            Dim root as New Root("AxiomTutorial.log");
            root.RenderSystem = root.RenderSystems["DirectX9"] 'Or "OpenGL"
            Dim config as EngineConfig = New EngineConfig()

            ' load the config file
            ' relative from the location of debug and releases executables
            config.ReadXml("EngineConfig.xml")

' interrogate the available resource paths
For Each row As EngineConfig.FilePathRow In config.FilePath
	ResourceGroupManager.Instance.AddResourceLocation(row.src, row.type, row.group, False, True)
Next

            Dim app As New BasicTutorial1
            app.Run()
        End Sub
    End Module


  • Outside of your IDE, find your EngineConfig.xml file. You should have added this to your Working Directory (the directory with your .exe or .py). If not, find the file in your Axiom installation and copy it there now.
  • Open up EngineConfig.xml in wordpad, and make sure all paths point to your Media folder location. In Setting up your Application you should have copied a media folder into your Working Directory, if you move the Media folder, you must be sure to set the reference paths of the xml file to the new location.
  • After we explain the core concepts of Axiom, we'll come back to the code, so leave that up.

SceneManager Basics

Everything that appears on the screen is managed by the SceneManager (fancy that). When you place objects in the scene, the SceneManager is the class which keeps track of their locations. When you create Cameras to view the scene (which we will cover in a later tutorial) the SceneManager keeps track of them. When you create planes, billboards, lights ... and so on, the SceneManager keeps track of them.

There are multiple types of SceneManagers. There are SceneManagers that render terrain, there is a SceneManager for rendering BSP maps, and so on. You can see the various types of SceneManagers listed here. We will cover more about other SceneManagers as we progress through the tutorials.

Entity Basics

An Entity is one of the types of object that you can render on a scene. You can think of an Entity as being anything that's represented by a 3D mesh. A robot would be an entity, a fish would be an entity, the terrain your characters walk on would be a very large entity. Things such as Lights, billboards, Particles, Cameras, etc would not be entities.

One thing to note about Axiom is that it separates renderable objects from their location and orientation. This means that you cannot directly place an Entity in a scene. Instead you must attach the Entity to a SceneNode object, and this SceneNode contains the information about location and orientation.

SceneNode Basics

As already mentioned, SceneNodes keep track of location and orientation for all of the objects attached to it. When you create an Entity, it is not rendered in the scene until you attach it to a SceneNode. In addition a SceneNode is not an object that is displayed on the screen. Only when you create a SceneNode and attach an Entity (or other object) to it is something actually displayed on the screen.

SceneNodes can have any number of objects attached to them. Let's say you have a character walking around on the screen and you want to have him generate a light around him. The way you do this would be to first create a SceneNode, then create an Entity for the character and attach it to the SceneNode. Then you would create a Light object and attach it to the SceneNode. SceneNodes may also be attached to other SceneNodes which allows you to create entire hierarchies of nodes. We will cover more advanced uses of SceneNode attachment in a later tutorial.

One major concept to note about SceneNodes is that a SceneNode's position is always relative to its parent SceneNode, and each SceneManager contains a root node to which all other SceneNodes are attached.

Your First Axiom Application

Now go back to the code we created earlier. Find the CreateScene method. We will only be manipulating the contents of this method in this tutorial. The first thing we want to do is set the ambient light for the scene so that we can see what we are doing. We do this by setting the AmbientLight property of the SceneManger and specifying what color we want. Note that the ColorEx constructor expects values for red, green, and blue in the range between 0 and 1. After that we'll want an Entity to see in the scene. Add the following lines of code:

_scene.AmbientLight = new ColorEx(1, 1, 1);
Entity ent = _scene.CreateEntity("Head", "penguin.mesh");
scene.AmbientLight = New ColorEx(1, 1, 1);
Dim ent as Entity = scene.CreateEntity("Head", "penguin.mesh")
self.scene.AmbientLight = ColorEx(1, 1, 1)
ent = self. scene.CreateEntity("Head", "penguin.mesh")

Okay, several questions should pop up. First of all, where did the variable 'scene' come from, and what are the parameters we are calling the method with? The scene variable contains the current SceneManager object which has already been set up by Game. The first parameter to CreateEntity is the name of the Entity we are creating. All entities must have a unique name. You will get an error if you try to create two entities with the same name. The ogrehead.mesh parameter specifies the mesh we want to use for the Entity. penguin.mesh is a resource which comes with Axiom and is located within the Media folder.

If you already tried running it, you might have been disappointed with the lack of penguin appearing. What gives?! Well as mentioned before, while an Entity represents a visual representation, it must be attached to a SceneNode which represents a position and orientation in 3d space. So let's go ahead and do that. Add the following underneath where you created 'ent':

SceneNode node = _scene.RootSceneNode.CreateChildSceneNode("HeadNode");
node.AttachObject(ent);
Dim node as SceneNode = scene.RootSceneNode.CreateChildSceneNode("HeadNode")
ent.AttachObject(ent)
node = self.scene.RootSceneNode.CreateChildSceneNode('HeadNode')
node.AttachObject(ent)

So the first thing you'll notice is the RootSceneNode property of scene. All SceneNodes are children of RootSceneNode. You'll then see the CreateChildSceneNode method, which in turn creates a SceneNode. Like Entities, all SceneNodes MUST have a unique name, or Axiom will throw an Exception.

Coordinates and Vectors

Before we go any further, we need to talk about screen coordinates and Axiom Vector objects. Axiom (like many graphics engines) uses the x and z axis as the horizontal plane, and the y axis as your vertical axis. As you are looking at your monitor now, the x axis would run from the left side to the right side of your monitor, with the right side being the positive x direction. The y axis would run from the bottom of your monitor to the top of your monitor, with the top being the positive y direction. The z axis would run into and out of your screen, with "out of the" screen being the positive z direction and the farther into your screen being negative.

http://www.ogre3d.org/tikiwiki/display1921

Notice how our Ogre Head is facing towards the left, along the -x direction? This is a property of the mesh and the camera position and facing. Cameras are covered in a later tutorial but for now just recognize that the Ogre's head is located at the position (0,0,0) and that our view of it is from in front. The direction that the head is facing is a result of how the mesh was oriented when it was created and saved by the artist. Axiom make no assumptions about how you orient your models. Each mesh that you load may have a different "starting direction" which it is facing.

Axiom uses the Vector class to represent both position and direction. There are vectors defined for 2 (Vector2), 3 (Vector3), and 4 (Vector4) dimensions, with Vector3 being the most commonly used. If you are not familiar with Vectors, I suggest you brush up on it(external link) before doing anything serious with Axiom. The math behind Vectors will become very useful when you start working on complex programs.

Adding another object

Now that you understand how the coordinate systems work, we can go back to our code. In the three lines we wrote, nowhere did we specify the exact location where we wanted our Ogre head to appear. A large majority of the functions in Axiom have default parameters for them. For example, the SceneNode.CreateChildSceneNode method in Axiom has three parameters: the name of the SceneNode, the position of the SceneNode, and the initial rotation (orientation) the SceneNode is facing. The position, as you can see, has been set for us to the coordinates (0, 0, 0). Let's create another SceneNode, but this time we'll specify the starting location to be something other than the origin:

Entity ent2 = scene.CreateEntity("Head2", "penguin.mesh");
SceneNode node2 = scene.RootSceneNode.CreateChildSceneNode("HeadNode2", new Vector3(100, 0, 0));
node2.AttachObject(ent2);
Dim ent2 as Entity = scene.CreateEntity("Head2", "penguin.mesh")
Dim node2 as SceneNode = scene.RootSceneNode.CreateChildSceneNode("HeadNode2", New Vector3(100, 0, 0))
node2.AttachObject(ent2)
ent2 = self.scene.CreateEntity('Head2', 'penguin.mesh')
node2 = self.scene.RootSceneNode.CreateChildSceneNode("HeadNode2",Vector3(100, 0, 0))
node2.AttachObject(ent2)

This should look familiar. We have done the exact same thing as before, with two exceptions. First of all, we have named the Entity and SceneNode something slightly different. The second thing we have done is specified that the starting position will be 100 units in the x direction away from the root SceneNode (remember that all SceneNode positions are relative to their parents). Compile and run the demo. Now there are two penguins side-by-side. You may have to move back using the 'S' key or the down arrow to see both heads.

Entities more in Depth

The Entity class is very extensive, and we will not be covering how to use every portion of it here... just enough to get you started. However there are a few immediately useful methods and properties in Entity that we'd like to point out.

The first is Entity.IsVisible. You can set any Entity to be visible or not by simply setting this property to true or false. If you need to hide an Entity, but later display it, then use this property instead of destroying the Entity and later recreating it. Note that you don't need to "pool" Entities up. Only one copy of any object's mesh and texture are ever loaded into memory, so you are not saving yourself much by trying to save them. The only thing you really save is the creation and destruction costs for the Entity object itself, which is relatively low.

The Name property returns the name of Entity, and the ParentSceneNode property returns the SceneNode that the Entity is attached to.

SceneNodes more in Depth

The SceneNode class is very complex. There are a lot of things that can be done with a SceneNode, so we'll only cover some of the most useful.

You can get and set the position of a SceneNode using the Position property (this is always relative to the parent SceneNode). You can move the object relative to its current position by adding and subtracting Vectors from this Property or using the Translate method.

SceneNodes not only set position, but they also manage the scale and rotation of the object. You can set the scale of an object with the Scale method. You can use the Pitch, Yaw, and Roll methods to rotate objects. You can use ResetOrientation to reset all rotations done to the object. You can also use the Orientation property and the Rotate method for more advanced rotations. We will not be covering Quaternions until a much later tutorial though.

You have already seen the AttachObject function. These related functions and properties are also useful if you are looking to manipulate the objects that are attached to a SceneNode: ObjectCount, GetObject (there are multiple overloads of this function), DetachObject (also multiple overloads) DetachAllObjects. There are also a whole set of functions for dealing with parent and child SceneNodes as well.

Since all positioning and translating is done relative to the parent SceneNode, we can make two SceneNodes move together very easily. We currently have this code in the application:

scene.AmbientLight = new ColorEx(1, 1, 1);

Entity    ent  = scene.CreateEntity("Head", "ogrehead.mesh");
SceneNode node = scene.RootSceneNode.CreateChildSceneNode("HeadNode");
node.AttachObject(ent);

Entity    ent2  = scene.CreateEntity("Head2", "ogrehead.mesh");
SceneNode node2 = scene.RootSceneNode.CreateChildSceneNode("HeadNode2", new Vector3(100, 0, 0));
node2.AttachObject(ent2);
scene.AmbientLight = New ColorEx(1, 1, 1)

Dim ent as Entity  = scene.CreateEntity("Head", "ogrehead.mesh")
Dim node as SceneNode = scene.RootSceneNode.CreateChildSceneNode("HeadNode")
node.AttachObject(ent)

Dim ent2 as Entity = scene.CreateEntity("Head2", "ogrehead.mesh")
Dim node2 as SceneNode = scene.RootSceneNode.CreateChildSceneNode("HeadNode2", New Vector3(100, 0, 0))
node2.AttachObject(ent2)
scene.AmbientLight = ColorEx(1, 1, 1)

ent  = self.scene.CreateEntity('Head', 'ogrehead.mesh')
node = self.scene.RootSceneNode.CreateChildSceneNode('HeadNode')
node.AttachObject(ent)

ent2  = self.scene.CreateEntity('Head2', 'ogrehead.mesh')
node2 = self.scene.RootSceneNode.CreateChildSceneNode('HeadNode2', Vector3(100, 0, 0))
node2.AttachObject(ent2)

If we change the 6th line to say:

SceneNode node2 = node.CreateChildSceneNode("HeadNode2", new Vector3(100, 0, 0));
Dim node2 as SceneNode = node.CreateChildSceneNode("HeadNode2", New Vector3(100, 0, 0))
node2 = node.CreateChildSceneNode('HeadNode2', Vector3(100, 0, 0));

Then we have made node2 a child of node. Moving node will move node2 along with it, but moving node2 will not affect node. For example this code would move only node2:

node2.Position += new Vector3(10, 0, 10);
node2.Position += New Vector3(10, 0, 10)
node2.Position += Vector3(10, 0, 10)

Whereas the following code would move node, and since node2 is a child of node, node2 would be moved as well:

node.Position += new Vector3(25, 0, 0);
node.Position += New Vector3(25, 0, 0)
node.Position += Vector3(25, 0, 0)

If you are having trouble with this, the easiest thing to do is to start at the root SceneNode and go downwards. Let's say (as in this case), we started node at (0, 0, 0) and translated it by (25, 0, 0), thus node's position is (25, 0, 0) relative to its parent. node2 started at (50, 0, 0) and we translated it by (10, 0, 10), so its new position is (60, 0, 10) relative to its parent.

Now let's figure out where these things really are. Start at the root SceneNode. Its position is always (0, 0, 0). Now, node's position is (root + node): (0, 0, 0) + (25, 0, 0) = (25, 0, 0). Not surprising. Now, node2 is a child of node, so its position is (root + node + node2): (0, 0, 0) + (25, 0, 0) + (60, 0, 10) = (85, 0, 10). This is just an example to explain how to think about SceneNode position inheritance. You will rarely ever need to calculate the absolute position of your nodes.

Lastly, note that you can get both SceneNodes and Entities by their name by calling GetSceneNode and GetEntity methods of the SceneManager, so you don't have to keep a reference to every SceneNode you create. You should hang on to the ones you use often though.

Things to Try

By now you should have a basic grasp of Entities, SceneNodes, and the SceneManager. I suggest starting with the code above and adding and removing objects from the scene. Once you have done that, clear all the contents out of the CreateScene method, and play with each of the following code segments:

Scale

You can scale the mesh by calling the Scale method in SceneNode. Try changing the values in Scale and see what you get:

node.Scale = new Vector3(0.5f, 1, 2);

This line will Set the scale of node to the new Vector3. As in the SceneNode's Scale becomes what you assign it, regardless of what it was before (typically 1,1,1).

node2.ScaleBy(new Vector3(10, 10, 10));

This line causes the scale of node2 to be combined with the Vector3 passed to it. Suppose that node2.Scale was equal to Vector3(2, 2, 2) and this line was applied. The Scale would then become equal to Vector3(20,20,20) </pre>

Rotation

You can rotate the object by using the Yaw, Pitch, and Roll methods passing a float in Degrees to it. Pitch is rotation around the x axis, yaw is around the y axis, and roll is around the z axis. Image

Using your right hand as a guide: point your thumb in the direction of an axis, curl your remaining fingers. The direction of the curl matches the positive rotation around that axis

Conclusions

By now you should have a very basic knowledge of the SceneManager, SceneNode, and Entity constructs. You can now proceed to Basic Tutorial 2 (Cameras, Lights, and Shadows)

Views
Powered by MediaWiki GNU Free Documentation License 1.2