History: MadMarx Tutorial 9 - Part 2
Source of version: 13 (current)
Copy to clipboard
{img fileId=2387 thumb="y" button="y" rel="box" alt="a screenshot from the app" width="300"}
!Foreword.
If you prefer tutorials that come with a framework => check the other wiki tutorial series.
If you prefer tutorials that go step by step without a framework => this page should be ok.
I assume you know C++. If not, this tutorial will probably be hard to understand !
This tutorial presents only a few elements of Ogre3D.
You can download the code and media for this tutorial at the bottom of this wiki page.
This little tutorial is an extract of a bigger project which contains more tutorials & helper classes.
This bigger project is avaible there :
https://sourceforge.net/projects/so3dtools/
Also, make sure you read these tutorials in order!
{maketoc}
!!Tutorial Description
In this program, I create a ((-Texture|texture)), in which I will render the scene.
I create a ((-Material|material)) that uses this ((-Texture|texture)), and I apply it on a mesh.
This ((-technique|technique)) is called a Render-To-((-Texture|Texture)) (RTT).
Please note that depending on your video card driver, you might get very different results.
For example, on my nvidia Geforce 9600M, RTT fails with 'FBO'.
So I modified the SimpleOgreInit.cpp (at the 'step 3'), so that it uses "Copy" instead of FBO.
!!Before root initialisation
{CODE(wrap="1",colors="c++")}
¤ const Ogre::RenderSystemList& lRenderSystemList = mRoot->getAvailableRenderers();
if( lRenderSystemList.size() == 0 )
{
MWARNING("Sorry, no rendersystem was found.");
return result;
}
Ogre::RenderSystem *lRenderSystem = lRenderSystemList.at(0);
{CODE}
In order to have a working RTT, I check if I can select another mode than "FBO".
I suppose that the 'Copy' mode works for everyone under opengl.
{CODE(wrap="1",colors="c++")}
¤ Ogre::ConfigOptionMap& lConfigMap = lRenderSystem->getConfigOptions();
if(lConfigMap.find("RTT Preferred Mode") != lConfigMap.end())
{
lRenderSystem->setConfigOption("RTT Preferred Mode","Copy");
{CODE}
{CODE(wrap="1",colors="c++")}
¤ }
mRoot->setRenderSystem(lRenderSystem);
{CODE}
!!Don't forget the includes
{CODE(wrap="1",colors="c++")}
¤#include "OGRE/OgreRoot.h"
#include "OGRE/OgreRenderSystem.h"
#include "OGRE/OgreRenderWindow.h"
#include "OGRE/OgreWindowEventUtilities.h"
#include "OGRE/OgreManualObject.h"
#include "OGRE/OgreEntity.h"
#include "OGRE/OgreMaterialManager.h"
#include "OGRE/OgreHardwarePixelBuffer.h"
{CODE}
!!After resource loading & viewport creation
Now I create a special ((-Texture|texture)). This ((-Texture|texture)) allows to do what is called a
'render to ((-Texture|texture))'. Which means that you can render your scene into
it, and then use this ((-Texture|texture)) as any other in a ((-Material|material)).
In order to keep a good framerate, I set its number of mipmaps to 0.
{CODE(wrap="1",colors="c++")}
¤ Ogre::TextureManager& lTextureManager = Ogre::TextureManager::getSingleton();
Ogre::String lTextureName = "MyFirstRtt";
bool lGammaCorrection = false;
unsigned int lAntiAliasing = 0;
unsigned int lNumMipmaps = 0;
Ogre::TexturePtr lTextureWithRtt = lTextureManager.createManual(lTextureName, lNameOfResourceGroup,
Ogre::TEX_TYPE_2D, 512, 512, lNumMipmaps,
Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET, 0, lGammaCorrection, lAntiAliasing);
{CODE}
now I will link this ((-Texture|texture)) to a ((-Camera|camera)), by creating a ((-Viewport|viewport)) in the ((-Texture|texture)).
{CODE(wrap="1",colors="c++")}
¤ Ogre::RenderTexture* lRenderTarget = NULL;
{
Ogre::HardwarePixelBufferSharedPtr lRttBuffer = lTextureWithRtt->getBuffer();
lRenderTarget = lRttBuffer->getRenderTarget();
lRenderTarget->setAutoUpdated(true);
{CODE}
I create a ((-Camera|camera)) so that it has a beautiful '1' aspect ratio.
{CODE(wrap="1",colors="c++")}
¤ Ogre::Camera * lRttCamera = lScene->createCamera("RttCamera");
lRttCamera->setNearClipDistance(1.5f);
lRttCamera->setFarClipDistance(3000.0f);
lRttCamera->setAspectRatio(1.0f);
{CODE}
I attach this ((-Camera|camera)) to the same node than main ((-Camera|camera)).
{CODE(wrap="1",colors="c++")}
¤ lCameraNode->attachObject(lRttCamera);
{CODE}
In the ((-Texture|texture)) I will draw first a Big Blue ((-Viewport|Viewport)).
{CODE(wrap="1",colors="c++")}
¤ Ogre::Viewport* lRttViewport1 = lRenderTarget->addViewport(lRttCamera, 50, 0.00f, 0.00f, 1.0f, 1.0f);
lRttViewport1->setAutoUpdated(true);
Ogre::ColourValue lBgColor1(1.0,0.0,0.0,1.0);
lRttViewport1->setBackgroundColour(lBgColor1);
{CODE}
In the ((-Texture|texture)) I will draw then a more little red ((-Viewport|Viewport)).
The previous ((-Viewport|viewport)) with the ZOrder at 50 will be drawn first (50 < 100).
{CODE(wrap="1",colors="c++")}
¤ Ogre::Viewport* lRttViewport2 = lRenderTarget->addViewport(lRttCamera, 100, 0.05f, 0.05f, 0.9f, 0.9f);
lRttViewport2->setAutoUpdated(true);
Ogre::ColourValue lBgColor2(0.0,0.0,1.0,1.0);
lRttViewport2->setBackgroundColour(lBgColor2);
{CODE}
This will draw a scene, within a scene, within a scene etc...
Because the quad that will be drawn is contained 'inside' the ((-Texture|texture))!
{CODE(wrap="1",colors="c++")}
¤ }
{CODE}
now I create a ((-Material|material)) using this ((-Texture|texture)).
{CODE(wrap="1",colors="c++")}
¤ Ogre::String lMaterialName = "MyRttMaterial";
{
{CODE}
I get a reference on the ((-Material|material)) manager, which is a ((-Singleton|singleton)).
{CODE(wrap="1",colors="c++")}
¤ Ogre::MaterialManager& lMaterialManager = Ogre::MaterialManager::getSingleton();
Ogre::MaterialPtr lMaterial = lMaterialManager.create(lMaterialName, lNameOfResourceGroup);
Ogre::Technique * lTechnique = lMaterial->getTechnique(0);
Ogre::Pass* lPass = lTechnique->getPass(0);
Ogre::TextureUnitState* lTextureUnit = lPass->createTextureUnitState();
lTextureUnit->setTextureName(lTextureName);
{CODE}
I use no mipmap, and I just use some bilinear filtering on the result.
{CODE(wrap="1",colors="c++")}
¤ lTextureUnit->setNumMipmaps(0);
lTextureUnit->setTextureFiltering(Ogre::TFO_BILINEAR);
{CODE}
I make the ((-Texture|texture)) rotate.
The ((-Material|material)) can handle 'special effects' on the ((-Texture|texture)) coordinates.
Here I rotate the ((-Texture|texture)) coordinates.
{CODE(wrap="1",colors="c++")}
¤ float lRotateSpeed = 0.01f;
lTextureUnit->setRotateAnimation(lRotateSpeed);
{CODE}
Uncomment the following line to see something funnier. :-D.
lTextureUnit->setTransformAnimation(Ogre::TextureUnitState::TT_SCALE_U, Ogre::WFT_SINE, 0.9f, 0.5f, 0.0f, 0.2f);
{CODE(wrap="1",colors="c++")}
¤ }
{CODE}
Now I will create a manualobject quad, and convert it to a mesh...
{CODE(wrap="1",colors="c++")}
¤ Ogre::String lNameOfTheMesh = "MyQuad";
{
Ogre::ManualObject * lManualObject = NULL;
Ogre::String lManualObjectName = "SomeQuad";
lManualObject = lScene->createManualObject(lManualObjectName);
{CODE}
Always tell if you want to update the 3D (vertex/index) later or not.
{CODE(wrap="1",colors="c++")}
¤ bool lDoIWantToUpdateItLater = false;
lManualObject->setDynamic(lDoIWantToUpdateItLater);
{CODE}
BaseWhiteNoLighting is the name of a ((-Material|material)) that already exist inside Ogre.
Ogre::RenderOperation::OT_TRIANGLE_LIST is a kind of primitive.
{CODE(wrap="1",colors="c++")}
¤ float lSize = 0.7f;
lManualObject->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST);
{
float cp = 1.0f * lSize ;
float cm = -1.0f * lSize;
float lNumberOfTiles = 1.0f;
lManualObject->position(cm, cp, 0.0f);// a vertex
lManualObject->textureCoord(0.0f, 0.0f);
lManualObject->position(cp, cp, 0.0f);// a vertex
lManualObject->textureCoord(lNumberOfTiles, 0.0f);
lManualObject->position(cp, cm, 0.0f);// a vertex
lManualObject->textureCoord(lNumberOfTiles, lNumberOfTiles);
lManualObject->position(cm, cm, 0.0f);// a vertex
lManualObject->textureCoord(0.0, lNumberOfTiles);
lManualObject->triangle(2,1,0);
lManualObject->triangle(0,3,2);
}
lManualObject->end();
lManualObject->convertToMesh(lNameOfTheMesh);
lScene->destroyManualObject(lManualObject);
}
{CODE}
Now I will create the corresponding ((-Entity|entity)), and its scenenode.
{CODE(wrap="1",colors="c++")}
¤ {
Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh);
lEntity->setMaterialName(lMaterialName);
{CODE}
Now I attach it to a scenenode, so that it becomes present in the scene.
{CODE(wrap="1",colors="c++")}
¤ Ogre::SceneNode* lNodeWithEntity = lNodeWithEntity = lRootSceneNode->createChildSceneNode();
lNodeWithEntity->attachObject(lEntity);
{CODE}
I move the ((-SceneNode|SceneNode)) so that it is visible to the ((-Camera|camera)).
{CODE(wrap="1",colors="c++")}
¤ lNodeWithEntity->setPosition(0.0f, 0.0f, -1.8f);
}
{CODE}
! main.cpp , main.cs
{VERSIONS(nav="y",default="c++")}
{CODE(wrap="1",colors="c++")}
// In this program, I create a texture, in which I will render the scene.
// I create a material that uses this texture, and I apply it on a mesh.
// This technique is called a Render-To-Texture (RTT).
// Please note that depending on your video card driver, you might get very different results.
// For example, on my nvidia Geforce 9600M, RTT fails with 'FBO'.
// So I modified the SimpleOgreInit.cpp (at the 'step 3'), so that it uses "Copy" instead of FBO.
// I will use std::auto_ptr so I need to include 'memory'.
// If you don't know std::auto_ptr, you should check some C++ tutorials/lesson on this matter.
#include <memory>
// I will check for std::exception. If you don't know what exception/try/catch means, you should learn C++ first.
#include <exception>
// These are some files that we need to include to use Ogre3D. Note that you can at the beginnings use directly "Ogre.h", to include lots of commonly used classes.
#include "OGRE/OgreRoot.h"
#include "OGRE/OgreRenderSystem.h"
#include "OGRE/OgreRenderWindow.h"
#include "OGRE/OgreWindowEventUtilities.h"
#include "OGRE/OgreManualObject.h"
#include "OGRE/OgreEntity.h"
#include "OGRE/OgreMaterialManager.h"
#include "OGRE/OgreHardwarePixelBuffer.h"
//Here I include my other files, like the one for SimpleOgreInit...
#include "SimpleOgreInit.h"
#include "EasyDefines.h"
// I declare a function in which I will make my whole application.
// This is easy then to add more things later in that function.
// The main will call this function and take care of the global try/catch.
void AnOgreApplication()
{
// I construct my object that will allow me to initialise Ogre easily.
OgreEasy::SimpleOgreInit lOgreInit;
if(!lOgreInit.initOgre())
{
std::cout<<"Impossible to init Ogre correctly."<<std::endl;
return;
}
//I prefer to be able to access my variables directly.
Ogre::Root* lRoot = lOgreInit.mRoot.get();
Ogre::RenderWindow* lWindow = lOgreInit.mWindow;
// I create a scenemanager. This is like a 'Scene', in which I can put lights, 3d objects, etc...
// The scenemanager contains an arborescent graph of 'SceneNodes'. To manage elements of the scene,
// I will create SceneNodes in the SceneManager, and attach the elements to the scenenodes.
// First parameter : I select a kind of SceneManager. This may have a huge impact on performance.
// Depending on your scene, some are better than other. The default one does no optimization at all.
// Second parameter : I give a name to the scenemanager.
// Note : It is easy to have more than one scenemanager (If you got 2 different scenes for example).
Ogre::SceneManager* lScene = lRoot->createSceneManager(Ogre::ST_GENERIC, "MyFirstSceneManager");
// The 'root SceneNode' is the only scenenode at the beginning in the SceneManager.
// The SceneNodes can be seen as 'transformation' containers <=> it contains scale/position/rotation
// of the objects. There is only 1 root scenenode, and all other scenenode are
// its direct or indirect children.
Ogre::SceneNode* lRootSceneNode = lScene->getRootSceneNode();
// I create a camera. It represent a 'point of view' in the scene.
Ogre::Camera* lCamera = lScene->createCamera("MyFirstCamera");
// I attach the camera to a new SceneNode. It will be easier then to move it in the scene.
Ogre::SceneNode* lCameraNode = lRootSceneNode->createChildSceneNode("MyFirstCameraNode");
lCameraNode->attachObject(lCamera);
// We create a viewport on a part of the window.
// A viewport is the link between 1 camera and 1 drawing surface (here the window).
// I can then call 'update();' on it to make it draw the Scene from the camera.
// You can have several viewports on 1 window.
// Check API for details on parameters.
unsigned short lMainViewportZOrder = 100;
Ogre::Viewport * vp = lWindow->addViewport(lCamera, lMainViewportZOrder);
// I want the viewport to draw the scene automatically
// when I will call lWindow->update();
vp->setAutoUpdated(true);
// I choose a color for this viewport.
// I prefer to have a bright color, to detect holes in geometry etc...
vp->setBackgroundColour(Ogre::ColourValue(1,0,1));
// I choose the visual ratio of the camera. To make it looks real, I want it the same as the viewport.
float ratio = float(vp->getActualWidth()) / float(vp->getActualHeight());
lCamera->setAspectRatio(ratio);
// I choose the clipping far& near planes. if far/near>2000, you can get z buffer problem.
// eg : far/near = 10000/5 = 2000 . it's ok.
// If (far/near)>2000 then you will likely get 'z fighting' issues.
lCamera->setNearClipDistance(1.5f);
lCamera->setFarClipDistance(3000.0f);
// I want my window to be active
lWindow->setActive(true);
// I want to update myself the content of the window, not automatically.
lWindow->setAutoUpdated(false);
// Here I choose a name for a resource group. Then I create it.
// Often, a resourcegroup is a good way to store the data corresponding
// to a level in a game.
Ogre::String lNameOfResourceGroup = "Mission 1 : Deliver Tom";
{
Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton();
lRgMgr.createResourceGroup(lNameOfResourceGroup);
// The function 'initialiseResourceGroup' parses scripts if any in the locations.
lRgMgr.initialiseResourceGroup(lNameOfResourceGroup);
// Files that can be loaded are loaded.
lRgMgr.loadResourceGroup(lNameOfResourceGroup);
}
// Now I create a special texture. This texture allows to do what is called a
// 'render to texture'. Which means that you can render your scene into
// it, and then use this texture as any other in a material.
// In order to keep a good framerate, I set its number of mipmaps to 0.
Ogre::TextureManager& lTextureManager = Ogre::TextureManager::getSingleton();
Ogre::String lTextureName = "MyFirstRtt";
bool lGammaCorrection = false;
unsigned int lAntiAliasing = 0;
unsigned int lNumMipmaps = 0;
Ogre::TexturePtr lTextureWithRtt = lTextureManager.createManual(lTextureName, lNameOfResourceGroup,
Ogre::TEX_TYPE_2D, 512, 512, lNumMipmaps,
Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET, 0, lGammaCorrection, lAntiAliasing);
// now I will link this texture to a camera, by creating a viewport in the texture.
Ogre::RenderTexture* lRenderTarget = NULL;
{
Ogre::HardwarePixelBufferSharedPtr lRttBuffer = lTextureWithRtt->getBuffer();
lRenderTarget = lRttBuffer->getRenderTarget();
lRenderTarget->setAutoUpdated(true);
// I create a camera so that it has a beautiful '1' aspect ratio.
Ogre::Camera * lRttCamera = lScene->createCamera("RttCamera");
lRttCamera->setNearClipDistance(1.5f);
lRttCamera->setFarClipDistance(3000.0f);
lRttCamera->setAspectRatio(1.0f);
// I attach this camera to the same node than main camera.
lCameraNode->attachObject(lRttCamera);
// In the texture I will draw first a Big Blue Viewport.
Ogre::Viewport* lRttViewport1 = lRenderTarget->addViewport(lRttCamera, 50, 0.00f, 0.00f, 1.0f, 1.0f);
lRttViewport1->setAutoUpdated(true);
Ogre::ColourValue lBgColor1(1.0,0.0,0.0,1.0);
lRttViewport1->setBackgroundColour(lBgColor1);
// In the texture I will draw then a more little red Viewport.
// The previous viewport with the ZOrder at 50 will be drawn first (50 < 100).
Ogre::Viewport* lRttViewport2 = lRenderTarget->addViewport(lRttCamera, 100, 0.05f, 0.05f, 0.9f, 0.9f);
lRttViewport2->setAutoUpdated(true);
Ogre::ColourValue lBgColor2(0.0,0.0,1.0,1.0);
lRttViewport2->setBackgroundColour(lBgColor2);
// This will draw a scene, within a scene, within a scene etc...
// Because the quad that will be drawn is contained 'inside' the texture!
}
// now I create a material using this texture.
Ogre::String lMaterialName = "MyRttMaterial";
{
// I get a reference on the material manager, which is a singleton.
Ogre::MaterialManager& lMaterialManager = Ogre::MaterialManager::getSingleton();
Ogre::MaterialPtr lMaterial = lMaterialManager.create(lMaterialName, lNameOfResourceGroup);
Ogre::Technique * lTechnique = lMaterial->getTechnique(0);
Ogre::Pass* lPass = lTechnique->getPass(0);
Ogre::TextureUnitState* lTextureUnit = lPass->createTextureUnitState();
lTextureUnit->setTextureName(lTextureName);
//I use no mipmap, and I just use some bilinear filtering on the result.
lTextureUnit->setNumMipmaps(0);
lTextureUnit->setTextureFiltering(Ogre::TFO_BILINEAR);
// I make the texture rotate.
// The material can handle 'special effects' on the texture coordinates.
// Here I rotate the texture coordinates.
float lRotateSpeed = 0.01f;
lTextureUnit->setRotateAnimation(lRotateSpeed);
// Uncomment the following line to see something funnier. :-D.
//lTextureUnit->setTransformAnimation(Ogre::TextureUnitState::TT_SCALE_U, Ogre::WFT_SINE, 0.9f, 0.5f, 0.0f, 0.2f);
}
// Now I will create a manualobject quad, and convert it to a mesh...
Ogre::String lNameOfTheMesh = "MyQuad";
{
Ogre::ManualObject * lManualObject = NULL;
Ogre::String lManualObjectName = "SomeQuad";
lManualObject = lScene->createManualObject(lManualObjectName);
// Always tell if you want to update the 3D (vertex/index) later or not.
bool lDoIWantToUpdateItLater = false;
lManualObject->setDynamic(lDoIWantToUpdateItLater);
// BaseWhiteNoLighting is the name of a material that already exist inside Ogre.
// Ogre::RenderOperation::OT_TRIANGLE_LIST is a kind of primitive.
float lSize = 0.7f;
lManualObject->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST);
{
float cp = 1.0f * lSize ;
float cm = -1.0f * lSize;
float lNumberOfTiles = 1.0f;
lManualObject->position(cm, cp, 0.0f);// a vertex
lManualObject->textureCoord(0.0f, 0.0f);
lManualObject->position(cp, cp, 0.0f);// a vertex
lManualObject->textureCoord(lNumberOfTiles, 0.0f);
lManualObject->position(cp, cm, 0.0f);// a vertex
lManualObject->textureCoord(lNumberOfTiles, lNumberOfTiles);
lManualObject->position(cm, cm, 0.0f);// a vertex
lManualObject->textureCoord(0.0, lNumberOfTiles);
lManualObject->triangle(2,1,0);
lManualObject->triangle(0,3,2);
}
lManualObject->end();
lManualObject->convertToMesh(lNameOfTheMesh);
lScene->destroyManualObject(lManualObject);
}
// Now I will create the corresponding entity, and its scenenode.
{
Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh);
lEntity->setMaterialName(lMaterialName);
// Now I attach it to a scenenode, so that it becomes present in the scene.
Ogre::SceneNode* lNodeWithEntity = lNodeWithEntity = lRootSceneNode->createChildSceneNode();
lNodeWithEntity->attachObject(lEntity);
// I move the SceneNode so that it is visible to the camera.
lNodeWithEntity->setPosition(0.0f, 0.0f, -1.8f);
}
// cleaning of windows events managed by Ogre::WindowEventUtilities::...
// I call it after a 'pause in window updating', in order to maintain smoothness.
// Explanation : if you clicked 2000 times when the windows was being created, there are
// at least 2000 messages created by the OS to listen to. This is made to clean them.
lRoot->clearEventTimes();
// I wait until the window is closed.
// The "message pump" thing is something you will see in most GUI application.
// It allow the binding of messages between the application and the OS.
// These messages are most of the time : keystroke, mouse moved, ... or window closed.
// If I don't do this, the message are never caught, and the window won't close.
while(!lOgreInit.mWindow->isClosed())
{
// Drawings
// the window update its content.
// each viewport that is 'autoupdated' will be redrawn now,
// in order given by its z-order.
lWindow->update(false);
// The drawn surface is then shown on the screen
// (google "double buffering" if you want more details).
// I always use vertical synchro.
bool lVerticalSynchro = true;
lWindow->swapBuffers(lVerticalSynchro);
// This update some internal counters and listeners.
// Each render surface (window/rtt/mrt) that is 'auto-updated' has got its 'update' function called.
lRoot->renderOneFrame();
Ogre::WindowEventUtilities::messagePump();
}
// Let's cleanup!
{
lWindow->removeAllViewports();
}
{
lScene->destroyAllCameras();
lScene->destroyAllManualObjects();
lScene->destroyAllEntities();
lScene->destroyAllLights();
lRootSceneNode->removeAndDestroyAllChildren();
}
{
Ogre::RenderSystem* lRenderSystem = lRoot->getRenderSystem();
lRenderSystem->destroyRenderTarget(lTextureName);
}
{
Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton();
lRgMgr.destroyResourceGroup(lNameOfResourceGroup);
}
return;
}
int main()
{
try
{
AnOgreApplication();
std::cout<<"end of the program"<<std::endl;
}catch(Ogre::Exception &e)
{
MWARNING("!!!!Ogre::Exception!!!!\n"<<e.what());
}catch(std::exception &e)
{
MWARNING("!!!!std::exception!!!!\n"<<e.what());
}
return 0;
}
{CODE}
Full source :
http://sourceforge.net/projects/so3dtools/files/Ogre3DWiki/09_RenderToTexture1.7z/download
---(C#)---
__NOTE__ This is a quick semiautomatic convert from C++ and it works with MOGRE SDK 1.7.1 r72
Any problems you encounter while working with MOGRE should be posted to the [http://www.ogre3d.org/addonforums/viewforum.php?f=8|MOGRE Forum].
{CODE(wrap="1", colors="c#")}
//TODO Fix accessViolation error on close window (Dispose correctly). Surround prt with using(){} does not fix this
using System;
using Mogre;
using System.Collections.Generic;
namespace OgreTutorials
{
public static class GlobalMembersMain
{
// In this program, I create a texture, in which I will render the scene.
// I create a material that uses this texture, and I apply it on a mesh.
// This technique is called a Render-To-Texture (RTT).
// Please note that depending on your video card driver, you might get very different results.
// For example, on my nvidia Geforce 9600M, RTT fails with 'FBO'.
// So I modified the SimpleOgreInit.cpp (at the 'step 3'), so that it uses "Copy" instead of FBO.
// I declare a function in which I will make my whole application.
// This is easy then to add more things later in that function.
// The main will call this function and take care of the global try/catch.
public static void AnOgreApplication()
{
// I construct my object that will allow me to initialise Ogre easily.
OgreEasy.SimpleOgreInit lOgreInit = new OgreEasy.SimpleOgreInit();
if (!lOgreInit.initOgre())
{
Console.Write("Impossible to init Ogre correctly.");
Console.Write("\n");
return;
}
//I prefer to be able to access my variables directly.
Root lRoot = lOgreInit.mRoot;
RenderWindow lWindow = lOgreInit.mWindow;
// I create a scenemanager. This is like a 'Scene', in which I can put lights, 3d objects, etc...
// The scenemanager contains an arborescent graph of 'SceneNodes'. To manage elements of the scene,
// I will create SceneNodes in the SceneManager, and attach the elements to the scenenodes.
// First parameter : I select a kind of SceneManager. This may have a huge impact on performance.
// Depending on your scene, some are better than other. The default one does no optimization at all.
// Second parameter : I give a name to the scenemanager.
// Note : It is easy to have more than one scenemanager (If you got 2 different scenes for example).
SceneManager lScene = lRoot.CreateSceneManager(SceneType.ST_GENERIC, "MyFirstSceneManager");
// The 'root SceneNode' is the only scenenode at the beginning in the SceneManager.
// The SceneNodes can be seen as 'transformation' containers <=> it contains scale/position/rotation
// of the objects. There is only 1 root scenenode, and all other scenenode are
// its direct or indirect children.
SceneNode lRootSceneNode = lScene.RootSceneNode;
// I create a camera. It represent a 'point of view' in the scene.
Camera lCamera = lScene.CreateCamera("MyFirstCamera");
// I attach the camera to a new SceneNode. It will be easier then to move it in the scene.
SceneNode lCameraNode = lRootSceneNode.CreateChildSceneNode("MyFirstCameraNode");
lCameraNode.AttachObject(lCamera);
// We create a viewport on a part of the window.
// A viewport is the link between 1 camera and 1 drawing surface (here the window).
// I can then call 'update();' on it to make it draw the Scene from the camera.
// You can have several viewports on 1 window.
// Check API for details on parameters.
ushort lMainViewportZOrder = 100;
Viewport vp = lWindow.AddViewport(lCamera, lMainViewportZOrder);
// I want the viewport to draw the scene automatically
// when I will call lWindow->update();
vp.SetAutoUpdated(true);
// I choose a color for this viewport.
// I prefer to have a bright color, to detect holes in geometry etc...
vp.BackgroundColour = new ColourValue(1, 0, 1);
// I choose the visual ratio of the camera. To make it looks real, I want it the same as the viewport.
float ratio = vp.ActualWidth / vp.ActualHeight;
lCamera.AspectRatio = ratio;
// I choose the clipping far& near planes. if far/near>2000, you can get z buffer problem.
// eg : far/near = 10000/5 = 2000 . it's ok.
// If (far/near)>2000 then you will likely get 'z fighting' issues.
lCamera.NearClipDistance = 1.5f;
lCamera.FarClipDistance = 3000.0f;
// I want my window to be active
lWindow.IsActive = true;
// I want to update myself the content of the window, not automatically.
lWindow.IsAutoUpdated = false;
// Here I choose a name for a resource group. Then I create it.
// Often, a resourcegroup is a good way to store the data corresponding
// to a level in a game.
string lNameOfResourceGroup = "Mission 1 : Deliver Tom";
{
ResourceGroupManager lRgMgr = ResourceGroupManager.Singleton;
lRgMgr.CreateResourceGroup(lNameOfResourceGroup);
// The function 'initialiseResourceGroup' parses scripts if any in the locations.
lRgMgr.InitialiseResourceGroup(lNameOfResourceGroup);
// Files that can be loaded are loaded.
lRgMgr.LoadResourceGroup(lNameOfResourceGroup);
}
// Now I create a special texture. This texture allows to do what is called a
// 'render to texture'. Which means that you can render your scene into
// it, and then use this texture as any other in a material.
// In order to keep a good framerate, I set its number of mipmaps to 0.
TextureManager lTextureManager = TextureManager.Singleton;
string lTextureName = "MyFirstRtt";
bool lGammaCorrection = false;
uint lAntiAliasing = 0;
int lNumMipmaps = 0;
TexturePtr lTextureWithRtt = lTextureManager.CreateManual(lTextureName, lNameOfResourceGroup, TextureType.TEX_TYPE_2D, 512, 512, lNumMipmaps, PixelFormat.PF_R8G8B8, (int)TextureUsage.TU_RENDERTARGET, null, lGammaCorrection, lAntiAliasing);
// now I will link this texture to a camera, by creating a viewport in the texture.
RenderTexture lRenderTarget = null;
{
HardwarePixelBufferSharedPtr lRttBuffer = lTextureWithRtt.GetBuffer();
lRenderTarget = lRttBuffer.GetRenderTarget();
lRenderTarget.IsAutoUpdated = true;
// I create a camera so that it has a beautiful '1' aspect ratio.
Camera lRttCamera = lScene.CreateCamera("RttCamera");
lRttCamera.NearClipDistance = 1.5f;
lRttCamera.FarClipDistance = 3000.0f;
lRttCamera.AspectRatio = 1.0f;
// I attach this camera to the same node than main camera.
lCameraNode.AttachObject(lRttCamera);
// In the texture I will draw first a Big Blue Viewport.
Viewport lRttViewport1 = lRenderTarget.AddViewport(lRttCamera, 50, 0.00f, 0.00f, 1.0f, 1.0f);
lRttViewport1.SetAutoUpdated(true);
lRttViewport1.BackgroundColour = new ColourValue(1.0f, 0.0f, 0.0f, 1.0f);
// In the texture I will draw then a more little red Viewport.
// The previous viewport with the ZOrder at 50 will be drawn first (50 < 100).
Viewport lRttViewport2 = lRenderTarget.AddViewport(lRttCamera, 100, 0.05f, 0.05f, 0.9f, 0.9f);
lRttViewport2.SetAutoUpdated(true);
lRttViewport2.BackgroundColour = new ColourValue(0.0f, 0.0f, 1.0f, 1.0f);
// This will draw a scene, within a scene, within a scene etc...
// Because the quad that will be drawn is contained 'inside' the texture!
}
// now I create a material using this texture.
string lMaterialName = "MyRttMaterial";
{
// I get a reference on the material manager, which is a singleton.
MaterialManager lMaterialManager = MaterialManager.Singleton;
MaterialPtr lMaterial = lMaterialManager.Create(lMaterialName, lNameOfResourceGroup);
Technique lTechnique = lMaterial.GetTechnique(0);
Pass lPass = lTechnique.GetPass(0);
TextureUnitState lTextureUnit = lPass.CreateTextureUnitState();
lTextureUnit.SetTextureName(lTextureName);
//I use no mipmap, and I just use some bilinear filtering on the result.
lTextureUnit.NumMipmaps = 0;
lTextureUnit.SetTextureFiltering(TextureFilterOptions.TFO_BILINEAR);
// I make the texture rotate.
// The material can handle 'special effects' on the texture coordinates.
// Here I rotate the texture coordinates.
float lRotateSpeed = 0.01f;
lTextureUnit.SetRotateAnimation(lRotateSpeed);
// Uncomment the following line to see something funnier. :-D.
//NOTE lTextureUnit.SetTransformAnimation(TextureUnitState.TextureTransformType.TT_SCALE_U,WaveformType.WFT_SINE, 0.9f, 0.5f, 0.0f, 0.2f);
}
// Now I will create a manualobject quad, and convert it to a mesh...
string lNameOfTheMesh = "MyQuad";
{
ManualObject lManualObject = null;
string lManualObjectName = "SomeQuad";
lManualObject = lScene.CreateManualObject(lManualObjectName);
// Always tell if you want to update the 3D (vertex/index) later or not.
bool lDoIWantToUpdateItLater = false;
lManualObject.Dynamic = lDoIWantToUpdateItLater;
// BaseWhiteNoLighting is the name of a material that already exist inside
// Ogre::RenderOperation::OT_TRIANGLE_LIST is a kind of primitive.
float lSize = 0.7f;
lManualObject.Begin("BaseWhiteNoLighting", RenderOperation.OperationTypes.OT_TRIANGLE_LIST);
{
float cp = 1.0f * lSize;
float cm = -1.0f * lSize;
float lNumberOfTiles = 1.0f;
lManualObject.Position(cm, cp, 0.0f); // a vertex
lManualObject.TextureCoord(0.0f, 0.0f);
lManualObject.Position(cp, cp, 0.0f); // a vertex
lManualObject.TextureCoord(lNumberOfTiles, 0.0f);
lManualObject.Position(cp, cm, 0.0f); // a vertex
lManualObject.TextureCoord(lNumberOfTiles, lNumberOfTiles);
lManualObject.Position(cm, cm, 0.0f); // a vertex
lManualObject.TextureCoord(0.0f, lNumberOfTiles);
lManualObject.Triangle(2, 1, 0);
lManualObject.Triangle(0, 3, 2);
}
lManualObject.End();
lManualObject.ConvertToMesh(lNameOfTheMesh);
lScene.DestroyManualObject(lManualObject);
}
// Now I will create the corresponding entity, and its scenenode.
{
Entity lEntity = lScene.CreateEntity(lNameOfTheMesh);
lEntity.SetMaterialName(lMaterialName);
// Now I attach it to a scenenode, so that it becomes present in the scene.
SceneNode lNodeWithEntity = lRootSceneNode.CreateChildSceneNode();
lNodeWithEntity.AttachObject(lEntity);
// I move the SceneNode so that it is visible to the camera.
lNodeWithEntity.Position = new Vector3(0.0f, 0.0f, -1.8f);
}
// cleaning of windows events managed by Ogre::WindowEventUtilities::...
// I call it after a 'pause in window updating', in order to maintain smoothness.
// Explanation : if you clicked 2000 times when the windows was being created, there are
// at least 2000 messages created by the OS to listen to. This is made to clean them.
lRoot.ClearEventTimes();
// I wait until the window is closed.
// The "message pump" thing is something you will see in most GUI application.
// It allow the binding of messages between the application and the OS.
// These messages are most of the time : keystroke, mouse moved, ... or window closed.
// If I don't do this, the message are never caught, and the window won't close.
while (!lOgreInit.mWindow.IsClosed)
{
// Drawings
// the window update its content.
// each viewport that is 'autoupdated' will be redrawn now,
// in order given by its z-order.
lWindow.Update(false);
// The drawn surface is then shown on the screen
// (google "double buffering" if you want more details).
// I always use vertical synchro.
bool lVerticalSynchro = true;
lWindow.SwapBuffers(lVerticalSynchro);
// This update some internal counters and listeners.
// Each render surface (window/rtt/mrt) that is 'auto-updated' has got its 'update' function called.
lRoot.RenderOneFrame();
WindowEventUtilities.MessagePump();
}
// Let's cleanup!
{
lWindow.RemoveAllViewports();
}
{
lScene.DestroyAllCameras();
lScene.DestroyAllManualObjects();
lScene.DestroyAllEntities();
lScene.DestroyAllLights();
lRootSceneNode.RemoveAndDestroyAllChildren();
}
{
RenderSystem lRenderSystem = lRoot.RenderSystem;
lRenderSystem.DestroyRenderTarget(lTextureName);
}
{
ResourceGroupManager lRgMgr = ResourceGroupManager.Singleton;
lRgMgr.DestroyResourceGroup(lNameOfResourceGroup);
}
return;
}
public static void Main()
{
try
{
AnOgreApplication();
}
catch (Exception e)
{
OgreEasy.SimpleOgreInit.ShowOgreException();
}
}
}
}
// The classes of this tutorial are put in a namespace called OgreEasy.
// So that it can be reused without difficulty.
namespace OgreEasy
{
/// \brief This class contains a function that help to initialise Ogre3d in one go.
/// the code of this function is inspired by the first tutorial of OgreEasy.
/// It was written for tutorial purpose.
public class SimpleOgreInit
{
// The constructor does nothing but initialisation to NULL or empty values.
// the constructor uses the initialisation list to have a proper state.
public SimpleOgreInit()
{
mRoot = null;
mWindow = null;
}
// the destructor frees memory allocated by the class.
//public void Dispose() //TODO
//{
// mRoot.Dispose();
// mRoot = null;
// mWindow = null;
// //mRoot.Reset(); // I was not obliged to do that...
//}
///\brief This function will create 1 ogre root and 1 window and store them in its members mRoot and mWindow.
///\return false if an error occurs, true otherwise.
public bool initOgre()
{
bool result = false;
// This try/catch will catch potential exception launched by ogre or by my program.
// Ogre can launch 'Ogre::Exception' for example.
try
{
// STEP 1/ First, we will need to create the Ogre::Root object.
// It is an object that must be created to use ogre correctly, and delete once we are finished using
// This is the name of an optionnal textual configuration file for the rendersystem.
// I won't use it.
string lConfigFileName = "";
// This is the name of an optionnal textual configuration file, which lists the available plugins.
// I won't use it.
string lPluginsFileName = "";
// This is the name of the log file. A log file is a file in which you can write things during the program execution.
// Ogre use it to display general informations about the rendersystem.
// You are not obliged to generate one, and ogre can even transmit the log data to you own class if you want.
// Here we only ask the root to create the file.
string lLogFileName = "Ogre.log";
mRoot = new Root(lConfigFileName, lPluginsFileName, lLogFileName);
// STEP 2/ Then we need to load plugins. It means that there are functions that are stored inside dynamic libraries.
// These libraries are .dll or .so files. Most projects Ogre Project do not need all functions to be usable.
// That way, only a subset of all function can be loaded. It also means you can create your own plugins if you want.
// If you want to know more on the subject, you 'll need to dig into a C++ tutorial.
// Anyway, for our use, we will need to load at least a 'RenderSystem' plugin, which means something to drive opengl or directx.
// The basic plugins you are the most likely to use are the RenderSystems, the particle FX and the Cgprogram.
{
// Here I list all the plugins I want to load.
// I let those I don't want to use in comments.
// Opengl rendersystem is supposed to work everywhere.
// But in reality a rendersystem may fail on your computer.
// It is likely do to bad/old graphic card driver/installation,
// or too old directx version on windows (try update).
// Often, when one rendersystem fail, the other at least kind-a-work.
// I put them in a std::vector, because then I can factorise operations and calls (do a 'for').
List<string> lPluginNames = new List<string>
{
"RenderSystem_GL",
//"RenderSystem_Direct3D9",
"Plugin_ParticleFX",
"Plugin_CgProgramManager",
//"Plugin_PCZSceneManager",
//"Plugin_OctreeZone",
"Plugin_OctreeSceneManager",
//"Plugin_BSPSceneManager"
};
{
foreach (var lPluginName in lPluginNames)
{
//TODO if (OGRE_DEBUG_MODE) lPluginName + "_d"
mRoot.LoadPlugin(lPluginName);
}
}
}
// STEP 3/ Then, we can select from the loaded plugins the unique RenderSystem we want to use.
{
// the root provide a method if you want to select
// the rendersystem and its options visually (lRoot->showConfigDialog()).
// in that case, you don't need to set the render system manually
Const_RenderSystemList lRenderSystemList = mRoot.GetAvailableRenderers();
if (lRenderSystemList.Count == 0)
{
System.Windows.Forms.MessageBox.Show("Sorry, no rendersystem was found.");
return result;
}
RenderSystem lRenderSystem = lRenderSystemList[0];
// In order to have a working RTT, I check if I can select another mode than "FBO".
// I suppose that the 'Copy' mode works for everyone under opengl.
ConfigOptionMap lConfigMap = lRenderSystem.GetConfigOptions();
if (lConfigMap.Find("RTT Preferred Mode") != lConfigMap.End())
{
lRenderSystem.SetConfigOption("RTT Preferred Mode", "Copy");
//lRenderSystem.SetConfigOption("RTT Preferred Mode","PBuffer");
//lRenderSystem.SetConfigOption("RTT Preferred Mode","FBO");
}
mRoot.RenderSystem = lRenderSystem;
}
// STEP 4/ When the RenderSystem is selected, we can initialise the Root. The root can be initialised only when a rendersystem has been selected.
{
// I can create a window automatically, but I won't do it.
bool lCreateAWindowAutomatically = false;
// name of the automatically generated window. empty for me.
string lWindowTitle = "";
// custom capabilities of the rendersystem. It's a feature for advanced use.
string lCustomCapacities = "";
mRoot.Initialise(lCreateAWindowAutomatically, lWindowTitle, lCustomCapacities);
}
// STEP 5/ Then we can ask to the RenderSystem to create a window.
{
string lWindowTitle = "Hello Ogre World";
uint lSizeX = 800;
uint lSizeY = 600;
//I don't want to use fullscreen during development.
bool lFullscreen = false;
// This is just an example of parameters that we can put. Check the API for more details.
NameValuePairList lParams = new NameValuePairList();
// fullscreen antialiasing. (check wikipedia if needed).
lParams["FSAA"] = "0";
// vertical synchronisation will prevent some image-tearing, but also
// will provide smooth framerate in windowed mode.(check wikipedia if needed).
lParams["vsync"] = "true";
mWindow = mRoot.CreateRenderWindow(lWindowTitle, lSizeX, lSizeY, lFullscreen, lParams);
}
result = true;
}
catch (Exception e) //TODO
{
ShowOgreException();
result = false;
}
return result;
}
// I put the member in public because there is no need to put them private in these tutorials.
// It will allow very simple access to these useful members.
///\brief the root of ogre will be contained in this member.
/// it will be initialised in initOgre().
public Root mRoot = new Root();
///\brief the window created in the initOgre(). NULL otherwise.
/// This is just a handle, not a real aggregation.
/// The destruction of the Root will imply its destruction.
public RenderWindow mWindow;
public static void ShowOgreException()
{
if (OgreException.IsThrown)
System.Windows.Forms.MessageBox.Show(OgreException.LastException.FullDescription, "An exception has occured!", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
}
{CODE}
{VERSIONS}