In this tutorial we will be exploring how to manipulate terrain, sky, and fog in your Mogre applications. After this tutorial you should understand the differences between Skyboxes, Skyplanes, and Skydomes, and be able to use them. You will also know the difference between the different types of fog, and how to use them. |
Prerequisites
- This tutorial assumes you have knowledge of C# programming and are able to setup and compile a Mogre application.
- This tutorial also assumes that you have created a project using the Mogre Wiki Tutorial Framework.
- This tutorial builds on the previous tutorials, and it assumes you have already worked through them.
Table of contents
Getting Started
As with the previous tutorials, we will be using the Mogre Wiki Tutorial Framework as our starting point. We will be adding code to the CreateScene and ChooseSceneManager methods. Add the following code to your tutorial class:
protected override void CreateScene() { } protected override void ChooseSceneManager() { }
NOTE: This code won't run yet until we add the right code to the ChooseSceneManager method.
A Note About Paging
Version 1.7 of Ogre introduced a new Terrain component with paging ability (creating big worlds from "tiles" of 3d terrain). Work on introducing this functionality into Mogre is underway as of this writing. However, until this functionality is properly integrated into Mogre and declared stable, this tutorial deals with the old terrain functionality.
The Root Object and SceneManager Creation
Root
In this demo we will be rendering Terrain in Ogre. To do this we need to use the TerrainSceneManager instead of the default one that the the tutorial framework provides for us. Add the following code to your ChooseSceneManager method:
protected override void ChooseSceneManager() { mSceneMgr = mRoot.CreateSceneManager(SceneType.ST_EXTERIOR_CLOSE); }
The Root object (mRoot is an instance of Root) is the "core" Ogre object. You can see a UML diagram of the relationships between Ogre objects here. You have now seen almost all of these objects in practice, with the exception of the RenderSystem. In this chunk of code, we are telling the Root node that we want a SceneManager of the ST_EXTERIOR_CLOSE type. The Root object then queries the SceneManagerEnumerator to find the SceneManager of the type you requested and returns it.
After your application is set up, you rarely ever have to deal with Ogre's Root object, and you don't ever interact with the SceneManagerEnumerator directly.
SceneManager Creation
There is an important note about SceneManager creation and storage that will save you confusion in the future. SceneManagers are not Singletons. You can create as many of them as you want, and unlike SceneNodes/Lights/etc you can create them directly with a "new SceneManager()" statement (you do not have to use Root's getSceneManager method). You can have multiple SceneManagers populated with multiple separate geometries and entities at the same time. You can swap between any of these at any time by recreating the Viewport (this is covered in Intermediate Tutorial 4) or display multiple SceneManagers at the same time using multiple Viewports.
Why do we use the CreateSceneManager function instead of creating our SceneManager object manually? Well, the plugin system in Ogre gives us a great deal of flexibility when working with single SceneManagers. There are only a few scene types defined in the SceneType enum. Until now, the MogreFramework has been choosing ST_GENERIC as our SceneManager. You might think that this is the base SceneManager class, but as long as you did not fiddle with the plugins.cfg file, then that's not what you have been using! The OctreeSceneManager registers itself as ST_GENERIC and overrides the base SceneManager class if you are using the OctreeSceneManager plugin. The OctreeSceneManager uses a system of culling items that are not actually on the Scene (and thus it's generally faster than the regular SceneManager). If you removed the OctreeSceneManager plugin from your plugins.cfg you would *probably* be using the basic SceneManager when you request ST_GENERIC.
This is pretty neat, but you can run into some problems. Especially considering the fact that you aren't guaranteed to get the SceneManager you ask for (some plugins conflict and try to overwrite the same ST_* type), but this rarely happens in practice. Normally if you need to do advanced things with SceneManagers then you should create them yourself.
Terrain
Adding Terrain to the Scene
Now that we have that cleared up, time to actually create Terrain. The base SceneManager defines the SetWorldGeometry method, which subclasses use for most scene creation purposes. With the TerrainSceneManager class, it expects a filename from which to load a terrain configuration. Add the following code to your CreateScene method:
mSceneMgr.SetWorldGeometry("terrain.cfg");
Compile and run your program. It's that easy.
The terrain.cfg File
There are many options in the terrain.cfg file, and we will only cover the most basic for changing the images used to generate the terrain. A more detailed explanation of the terrain config file can be found here.
The TerrainSceneManager uses Heightmaps to generate terrain. You can specify the heightmap you want to use by setting the "Heightmap.image" property. You can set the texture that is used for the terrain by setting the WorldTexture property. The terrain scene manager also allows you to specify a "DetailTexture" property, which is interlaced with the WorldTexture to make the terrain look more realistic. You should find each of the images currently specified by terrain.cfg and take a look at them (they should be in the Media/materials/textures folder).
Details on how to create heightmaps have been discussed ad nauseum on the forums. Search for heightmap and you are sure to find something that you are looking for.
Lighting Terrain
We just spent the entire previous tutorial going over lights and shadows, but the bad news is it's not easy to get this to work in the TerrainSceneManager. This is better supported by the new paging functionality and it becomes available this tutorial will be modified to describe how to do this.
Sky
SkyBoxes
A SkyBox is basically a giant cube that surrounds all of the objects in the scene. The best way to describe it is to just show it to you. Add the following code to your CreateScene method:
mSceneMgr.SetSkyBox(true, "Examples/SpaceSkyBox");
Compile and run the program. Neat huh? (Note the SkyBox is grainy because the actual texture is low resolution; a higher resolution SkyBox would look much better).
There are several useful parameters for SkyBoxes that we can set when calling SetSkyBox. The first option is whether or not to enable the SkyBox. If you want to later disable the SkyBox simply call 'mSceneMgr.SetSkyBox(false, "");'. The second parameter is the material script to use for the SkyBox.
The third parameter and fourth parameters to setSkyBox are fairly important to understand. The third parameter sets the distance that the SkyBox is away from the Camera, and the fourth parameter sets whether or not the SkyBox is drawn before the rest of the scene or afterwards. So, lets see what happens when you change the distance parameter for the SkyBox from the default 5000 units to something very close:
mSceneMgr.SetSkyBox(true, "Examples/SpaceSkyBox", 10);
Nothing changed! This is because the fourth parameter that controls whether to draw the SkyBox first or not is set to true by default. If the SkyBox is drawn first, then anything rendered afterwards (like our Terrain) will be drawn on top of it, thus making the SkyBox always appear in the background. (Note that you shouldn't set the distance above to be closer than the near clip distance on the Camera or it will not be shown!) It is not actually desirable to draw the SkyBox first, because the full thing is rendered. When you draw it last, only the visible portions are drawn, which will provide a modest speed improvement. So, lets try setting our SkyBox to be drawn last:
mSceneMgr.SetSkyBox(true, "Examples/SpaceSkyBox", 5000, false);
Again, this looks just like it did before, but now the parts of the SkyBox that are not visible won't be rendered. There is one thing you have to be careful about when using this technique though. If you set the SkyBox to be too close, you could be cutting part of the scene geometry off. For example, try this:
mSceneMgr.SetSkyBox(true, "Examples/SpaceSkyBox", 100, false);
As you can see now, the terrain "pokes through" the SkyBox. Definitely not what we want. If you use SkyBoxes in your application you will have to decide how you want to use them. The speedup you get from rendering the SkyBox after the terrain is very modest, and you have to be careful not to obscure your geometry (unless that is what you are going for). Generally speaking, leaving everything past the second parameter as default is a very safe choice.
SkyDomes
SkyDomes are very similar to SkyBoxes, and you use them by calling SetSkyDome A giant cube is created around the Camera and rendered onto, but the bigest difference is the texture is "projected" onto the SkyBox in a spherical manner. You are still looking at a cube, but it looks as if the texture is wrapped around the surface of a sphere. The primary drawback to this method is that the bottom of the cube will be untextured, so you always need to have some type of terrain that hides the base.
The example texture that Ogre provides for SkyDomes will let you see this clearly. Clear out the SetSkyBox call from CreateScene method and add the following code instead:
mSceneMgr.SetSkyDome(true, "Examples/CloudySky", 5, 8);
When you run this, move the Camera to the dead center of the terrain and move the Camera so that's positioned fairly close to the surface of the terrain (this looks the best). After looking at this, press the "R" button to switch to the mesh view. As you can see, we are still looking at a cube (without the base), but it looks as if the clouds are wrapped around a sphere at the top. Also note that the movement of the clouds is a property of the "Examples/CloudySky" material, not of SkyDomes in general.
The first two paramaters of SetSkyDome are the same as SetSkyBox, and you can turn the SkyDome off by calling 'mSceneMgr.SetSkyDome(false, "");'. The third parameter is the curvature used for the SkyDome. The API reference suggests using values between 2 and 65. lower for better distance effect, but higher values for less distortion and a smoother effect. Try setting the third paramater to 2 and 65 and look at the difference. The distance effect that the API reference was referring to can be clearly seen in these screenshots. This is setting the curvature to 2:
This is setting the curvature to 64:
The fourth parameter is the number of times the texture is tiled, which you will need to tweak depending on the size of your texture. Be sure to note that this parameter is a Real value (floating point) and not an integer. You can tile it 1.234 times, if that's what looks good for your application. The fifth and sixth parameters are distance and drawFirst, respectively, which we have already covered in the SkyBox section.
SkyPlanes
SkyPlanes are very different from SkyBoxes and SkyDomes. Instead of a cube to render the sky texture on, we use just a single plane. (Note for all of the following SkyPlane configurations you need to be somewhere towards the middle of the terrain and close to the ground.) The first thing we are going to do is create a plane, and face it downwards. The SetSkyPlane method that we will be calling does not have a distance parameter like SkyBox and SkyPlane. Instead that parameter is set in the d variable of Plane.
Replace the SkyDome code in the CreateScene method with the following code:
Plane plane; plane.d = 1000; plane.normal = Vector3.NEGATIVE_UNIT_Y;
Now that we have the plane defined, we can create the SkyPlane. Note that the fourth parameter is the size of the SkyPlane (in this case 1500x1500 units) and the fifth parameter is how many times to tile the texture:
mSceneMgr.SetSkyPlane(true, plane, "Examples/SpaceSkyPlane", 1500, 75);