History: MadMarx Tutorial 6
Source of version: 9 (current)
Copy to clipboard
!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 add a ((-Light|light)) to the program. I display a mesh that use a basic ((-Material|material)) that use this ((-Light|light)). I also animate a little the scene, by rotating the ((-Light|light)) between each frame. !!After viewport creation I create a ((-Light|light)). The scenemanager will contain it. I will attach this ((-Light|light)) to a scenenode, so that I can move it easily. (This will add 1 transformation matrix, but I prefer to control motion homogeneously for all elements). {CODE(wrap="1",colors="c++")} ¤ Ogre::SceneNode* lLightSceneNode = NULL; { Ogre::Light* lLight = lScene->createLight(); {CODE} I can set some attributes of the ((-Light|light)). The basic ((-Light|light)) type can be : pointlight (like a candle?) spotlight (kind of 'conic' ((-Light|light))) directional ((-Light|light)) (like the sun in an outdoor scene). Directional ((-Light|light)) is like parallel rays coming from 1 direction. {CODE(wrap="1",colors="c++")} ¤ lLight->setType(Ogre::Light::LT_DIRECTIONAL); {CODE} Here I choose the color of the ((-Light|light)). The diffuse color is the main color of the ((-Light|light)). The specular color is its color when reflected on an imperfect surface. For example, when my bald head skin reflect the sun, it makes a bright round of specular color. The final color of an object also depends on its ((-Material|material)). Color values vary between 0.0(minimum) to 1.0 (maximum). {CODE(wrap="1",colors="c++")} ¤ lLight->setDiffuseColour(0.8f, 0.3f, 0.3f); // this will be a red light lLight->setSpecularColour(1.0f, 1.0f, 1.0f);// color of 'reflected' light lLightSceneNode = lRootSceneNode->createChildSceneNode(); lLightSceneNode->attachObject(lLight); } {CODE} I add an ambient color. The ambient color is managed in the scenemanager. If you want to learn more about ambient/specular/diffuse color, check the 'basic ((-Material|material)) tutorial' from this serie. {CODE(wrap="1",colors="c++")} ¤ Ogre::ColourValue lAmbientColour(0.2f, 0.2f, 0.2f, 1.0f); lScene->setAmbientLight(lAmbientColour); {CODE} !!Then add a scene to display. {CODE(wrap="1",colors="c++")} ¤ Ogre::String lNameOfResourceGroup = "Mission 1 : Deliver Tom"; { Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton(); lRgMgr.createResourceGroup(lNameOfResourceGroup); {CODE} We say which directories will be loaded by this resourcegroup. I can add many directories, which will be loaded in same the ORDER. This ORDER is extremely important : if a ((-Material|material)) is loaded <i>after</i> a mesh using this ((-Material|material)), this mesh won't be able to find the ((-Material|material)) during its loading! I advise you not to use a recursive load (which load the full directory tree). Seriously, 'recursive loading' often leads to problems. {CODE(wrap="1",colors="c++")} ¤ Ogre::String lDirectoryToLoad = "../../media/mesh"; bool lIsRecursive = false; lRgMgr.addResourceLocation(lDirectoryToLoad, "FileSystem", lNameOfResourceGroup, lIsRecursive); {CODE} The function 'initialiseResourceGroup' parses scripts if any in the locations. {CODE(wrap="1",colors="c++")} ¤ lRgMgr.initialiseResourceGroup(lNameOfResourceGroup); {CODE} Files that can be loaded are loaded. {CODE(wrap="1",colors="c++")} ¤ lRgMgr.loadResourceGroup(lNameOfResourceGroup); {CODE} Now the loaded ((-Mesh|Mesh)) is available from its ResourceGroup, as well as from the Ogre::MeshManager. A shared pointer to it can be accessed by : Ogre::MeshManager::getSingleton().getByName(name_of_the_mesh); Now I can create Entities using that mesh. {CODE(wrap="1",colors="c++")} ¤ Ogre::String lNameOfTheMesh = "MonsterHead.mesh"; int lNumberOfEntities = 5; for(int iter = 0; iter < lNumberOfEntities; ++iter) { Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh); {CODE} Now I attach it to a scenenode, so that it becomes present in the scene. {CODE(wrap="1",colors="c++")} ¤ Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode(); lNode->attachObject(lEntity); {CODE} I move the ((-SceneNode|SceneNode)) so that it is visible to the ((-Camera|camera)). {CODE(wrap="1",colors="c++")} ¤ float lPositionOffset = float(1+ iter * 2) - (float(lNumberOfEntities)); lPositionOffset = lPositionOffset * 20; lNode->translate(lPositionOffset, lPositionOffset, -200.0f); {CODE} 'BaseWhite' is a ((-Material|material)) already available. It is grey / white at ((-Light|light)). Since the ((-Light|light)) is red, the result will be red/rosa. {CODE(wrap="1",colors="c++")} ¤ lEntity->setMaterialName("BaseWhite"); } } {CODE} !!In the while() I make the ((-Light|light)) rotate. {CODE(wrap="1",colors="c++")} ¤ Ogre::Degree lAngle(2.5); lLightSceneNode->yaw(lAngle); {CODE} ! main.cpp {CODE(wrap="1",colors="c++")} // In this program, I add a light to the program. // I display a mesh that use a basic material that use this light. // I also animate a little the scene, by rotating the light between each frame. // 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" //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. float lViewportWidth = 0.88f; float lViewportHeight = 0.88f; float lViewportLeft = (1.0f - lViewportWidth) * 0.5f; float lViewportTop = (1.0f - lViewportHeight) * 0.5f; unsigned short lMainViewportZOrder = 100; Ogre::Viewport * vp = lWindow->addViewport(lCamera, lMainViewportZOrder, lViewportLeft, lViewportTop, lViewportWidth, lViewportHeight); // 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); // I create a light. The scenemanager will contain it. // I will attach this light to a scenenode, so that I can move it easily. // (This will add 1 transformation matrix, but I prefer to control motion homogeneously for all elements). Ogre::SceneNode* lLightSceneNode = NULL; { Ogre::Light* lLight = lScene->createLight(); // I can set some attributes of the light. // The basic light type can be : // pointlight (like a candle?) // spotlight (kind of 'conic' light) // directional light (like the sun in an outdoor scene). // Directional light is like parallel rays coming from 1 direction. lLight->setType(Ogre::Light::LT_DIRECTIONAL); // Here I choose the color of the light. // The diffuse color is the main color of the light. // The specular color is its color when reflected on an imperfect surface. // For example, when my bald head skin reflect the sun, it makes a bright round of specular color. // // The final color of an object also depends on its material. // Color values vary between 0.0(minimum) to 1.0 (maximum). lLight->setDiffuseColour(0.8f, 0.3f, 0.3f); // this will be a red light lLight->setSpecularColour(1.0f, 1.0f, 1.0f);// color of 'reflected' light lLightSceneNode = lRootSceneNode->createChildSceneNode(); lLightSceneNode->attachObject(lLight); } // I add an ambient color. The ambient color is managed in the scenemanager. // If you want to learn more about ambient/specular/diffuse color, check the 'basic material tutorial' // from this serie. Ogre::ColourValue lAmbientColour(0.2f, 0.2f, 0.2f, 1.0f); lScene->setAmbientLight(lAmbientColour); // 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); // We say which directories will be loaded by this resourcegroup. // I can add many directories, which will be loaded in same the ORDER. // This ORDER is extremely important : if a material is loaded <i>after</i> a mesh using this material, // this mesh won't be able to find the material during its loading! // I advise you not to use a recursive load (which load the full directory tree). // Seriously, 'recursive loading' often leads to problems. Ogre::String lDirectoryToLoad = "../../media/mesh"; bool lIsRecursive = false; lRgMgr.addResourceLocation(lDirectoryToLoad, "FileSystem", lNameOfResourceGroup, lIsRecursive); // The function 'initialiseResourceGroup' parses scripts if any in the locations. lRgMgr.initialiseResourceGroup(lNameOfResourceGroup); // Files that can be loaded are loaded. lRgMgr.loadResourceGroup(lNameOfResourceGroup); // Now the loaded Mesh is available from its ResourceGroup, // as well as from the Ogre::MeshManager. A shared pointer to // it can be accessed by : Ogre::MeshManager::getSingleton().getByName(name_of_the_mesh); // Now I can create Entities using that mesh. Ogre::String lNameOfTheMesh = "MonsterHead.mesh"; int lNumberOfEntities = 5; for(int iter = 0; iter < lNumberOfEntities; ++iter) { Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh); // Now I attach it to a scenenode, so that it becomes present in the scene. Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode(); lNode->attachObject(lEntity); // I move the SceneNode so that it is visible to the camera. float lPositionOffset = float(1+ iter * 2) - (float(lNumberOfEntities)); lPositionOffset = lPositionOffset * 20; lNode->translate(lPositionOffset, lPositionOffset, -200.0f); // 'BaseWhite' is a material already available. // It is grey / white at light. Since the light is red, the result will be red/rosa. lEntity->setMaterialName("BaseWhite"); } } // 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()) { // Here I update the scene between 2 frames. { // I make the light rotate. Ogre::Degree lAngle(2.5); lLightSceneNode->yaw(lAngle); } // 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::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 program sources : http://sourceforge.net/projects/so3dtools/files/Ogre3DWiki/06_BasicLight.7z/download