In this tutorial we load a terrain scene from an XML file using the dotscene format.
DOWNLOAD THE DEMO AND SOURCE CODE [LINUX]
DOWNLOAD THE DEMO AND SOURCE CODE [WINDOWS]
Now that we have initialised the Ogre 3D engine it’s time to start displaying some 3D objects. One of the questions that developers have to address is how a level will be defined and loaded into the Ogre engine. The easiest way, at least initially, is to simply hard code the creation of a level in C++. All of the samples that come with the Ogre 3D SDK hard code the logic for creating a terrain, displaying a 3D object, setting up a skybox etc, so it is tempting to combine the code from all these samples to make a complete level.
There are several big downsides to defining a level this way.
1. Every little change to the level requires the application to be recompiled (and that gets very time consuming).
2. It is almost impossible for anyone other than the developer to create levels.
3. It means that you cannot use 3rd party tools to create a world, because no world creation utility I know of outputs C++ code.
The alternative is to define a level in an external file. While Ogre has no native support for this, an initiative called dotscene does specify an XML format that can be used to quickly setup a scene or level. The format is also something of a standard within Ogre, and quite a few 3rd party tools support the format.
Unfortunately the dotscene initiative is not quite as feature complete as you might expect. Dotscene itself just specifies the format and leaves the actual implementation up to others. There have been many attempts to implement the specification, but all seem to have their shortcomings. Fortunately the latest implementation, referred to as the "New DotScene Loader” on the Ogre Wiki, does implement most of what we need.
Our game will use the DotSceneLoader class, and for now we will extend it to support initialising terrain scene managers. Later on we will give the DotSceneLoader class the ability to load objects specific to our game, which will deviate from the official standard, but that is fine for our purposes.
DotSceneLoader.h
#ifndef DOT_SCENELOADER_H
#define DOT_SCENELOADER_H// Includes
#include "OgreString.h"
#include "OgreVector3.h"
#include "OgreQuaternion.h"
#include "vector"// Forward declarations
class TiXmlElement;namespace Ogre
{
// Forward declarations
class SceneManager;
class SceneNode;class nodeProperty
{
public:
String nodeName;
String propertyNm;
String valueName;
String typeName;nodeProperty(const String &node, const String &propertyName, const String &value, const String &type)
: nodeName(node), propertyNm(propertyName), valueName(value), typeName(type) {}
};class DotSceneLoader
{
public:
DotSceneLoader() : mSceneMgr(0) {}
virtual ~DotSceneLoader() {}void parseDotScene(const String &SceneName, const String &groupName, SceneManager *yourSceneMgr, SceneNode *pAttachNode = NULL, const String &sPrependNode = "");
String getProperty(const String &ndNm, const String ∝);std::vector nodeProperties;
std::vector staticObjects;
std::vector dynamicObjects;protected:
void processScene(TiXmlElement *XMLRoot);void processNodes(TiXmlElement *XMLNode);
void processExternals(TiXmlElement *XMLNode);
void processEnvironment(TiXmlElement *XMLNode);
void processTerrain(TiXmlElement *XMLNode);
void processUserDataReference(TiXmlElement *XMLNode, SceneNode *pParent = 0);
void processUserDataReference(TiXmlElement *XMLNode, Entity *pEntity);
void processOctree(TiXmlElement *XMLNode);
void processLight(TiXmlElement *XMLNode, SceneNode *pParent = 0);
void processCamera(TiXmlElement *XMLNode, SceneNode *pParent = 0);void processNode(TiXmlElement *XMLNode, SceneNode *pParent = 0);
void processLookTarget(TiXmlElement *XMLNode, SceneNode *pParent);
void processTrackTarget(TiXmlElement *XMLNode, SceneNode *pParent);
void processEntity(TiXmlElement *XMLNode, SceneNode *pParent);
void processParticleSystem(TiXmlElement *XMLNode, SceneNode *pParent);
void processBillboardSet(TiXmlElement *XMLNode, SceneNode *pParent);
void processPlane(TiXmlElement *XMLNode, SceneNode *pParent);void processFog(TiXmlElement *XMLNode);
void processSkyBox(TiXmlElement *XMLNode);
void processSkyDome(TiXmlElement *XMLNode);
void processSkyPlane(TiXmlElement *XMLNode);
void processClipping(TiXmlElement *XMLNode);void processLightRange(TiXmlElement *XMLNode, Light *pLight);
void processLightAttenuation(TiXmlElement *XMLNode, Light *pLight);String getAttrib(TiXmlElement *XMLNode, const String ¶meter, const String &defaultValue = "");
Real getAttribReal(TiXmlElement *XMLNode, const String ¶meter, Real defaultValue = 0);
int getAttribInt(TiXmlElement *XMLNode, const String ¶meter, int defaultValue = 0);
bool getAttribBool(TiXmlElement *XMLNode, const String ¶meter, bool defaultValue = false);Vector3 parseVector3(TiXmlElement *XMLNode);
Quaternion parseQuaternion(TiXmlElement *XMLNode);
ColourValue parseColour(TiXmlElement *XMLNode);
SceneManager *mSceneMgr;
SceneNode *mAttachNode;
String m_sGroupName;
String m_sPrependNode;
};
}#endif // DOT_SCENELOADER_H
DotSceneLoader.cpp
#include "DotSceneLoader.h"
#include "tinyxml.h"
#include "Ogre.h"using namespace std;
using namespace Ogre;void DotSceneLoader::parseDotScene(const String &SceneName, const String &groupName, SceneManager *yourSceneMgr, SceneNode *pAttachNode, const String &sPrependNode)
{
// set up shared object values
m_sGroupName = groupName;
mSceneMgr = yourSceneMgr;
m_sPrependNode = sPrependNode;
staticObjects.clear();
dynamicObjects.clear();TiXmlDocument *XMLDoc = 0;
TiXmlElement *XMLRoot;try
{
// Strip the path
Ogre::String basename, path;
Ogre::StringUtil::splitFilename(SceneName, basename, path);DataStreamPtr pStream = ResourceGroupManager::getSingleton().
openResource( basename, groupName );//DataStreamPtr pStream = ResourceGroupManager::getSingleton().
// openResource( SceneName, groupName );String data = pStream->getAsString();
// Open the .scene File
XMLDoc = new TiXmlDocument();
XMLDoc->Parse( data.c_str() );
pStream->close();
pStream.setNull();if( XMLDoc->Error() )
{
//We'll just log, and continue on gracefully
LogManager::getSingleton().logMessage("[DotSceneLoader] The TiXmlDocument reported an error");
delete XMLDoc;
return;
}
}
catch(...)
{
//We'll just log, and continue on gracefully
LogManager::getSingleton().logMessage("[DotSceneLoader] Error creating TiXmlDocument");
delete XMLDoc;
return;
}// Validate the File
XMLRoot = XMLDoc->RootElement();
if( String( XMLRoot->Value()) != "scene" ) {
LogManager::getSingleton().logMessage( "[DotSceneLoader] Error: Invalid .scene File. Missing " );
delete XMLDoc;
return;
}// figure out where to attach any nodes we create
mAttachNode = pAttachNode;
if(!mAttachNode)
mAttachNode = mSceneMgr->getRootSceneNode();// Process the scene
processScene(XMLRoot);// Close the XML File
delete XMLDoc;
}void DotSceneLoader::processScene(TiXmlElement *XMLRoot)
{
// Process the scene parameters
String version = getAttrib(XMLRoot, "formatVersion", "unknown");String message = "[DotSceneLoader] Parsing dotScene file with version " + version;
if(XMLRoot->Attribute("ID"))
message += ", id " + String(XMLRoot->Attribute("ID"));
if(XMLRoot->Attribute("sceneManager"))
message += ", scene manager " + String(XMLRoot->Attribute("sceneManager"));
if(XMLRoot->Attribute("minOgreVersion"))
message += ", min. Ogre version " + String(XMLRoot->Attribute("minOgreVersion"));
if(XMLRoot->Attribute("author"))
message += ", author " + String(XMLRoot->Attribute("author"));LogManager::getSingleton().logMessage(message);
TiXmlElement *pElement;
// Process nodes (?)
pElement = XMLRoot->FirstChildElement("nodes");
if(pElement)
processNodes(pElement);// Process externals (?)
pElement = XMLRoot->FirstChildElement("externals");
if(pElement)
processExternals(pElement);// Process environment (?)
pElement = XMLRoot->FirstChildElement("environment");
if(pElement)
processEnvironment(pElement);// Process terrain (?)
pElement = XMLRoot->FirstChildElement("terrain");
if(pElement)
processTerrain(pElement);// Process userDataReference (?)
pElement = XMLRoot->FirstChildElement("userDataReference");
if(pElement)
processUserDataReference(pElement);// Process octree (?)
pElement = XMLRoot->FirstChildElement("octree");
if(pElement)
processOctree(pElement);// Process light (?)
pElement = XMLRoot->FirstChildElement("light");
if(pElement)
processLight(pElement);// Process camera (?)
pElement = XMLRoot->FirstChildElement("camera");
if(pElement)
processCamera(pElement);
}void DotSceneLoader::processNodes(TiXmlElement *XMLNode)
{
TiXmlElement *pElement;// Process node (*)
pElement = XMLNode->FirstChildElement("node");
while(pElement)
{
processNode(pElement);
pElement = pElement->NextSiblingElement("node");
}// Process position (?)
pElement = XMLNode->FirstChildElement("position");
if(pElement)
{
mAttachNode->setPosition(parseVector3(pElement));
mAttachNode->setInitialState();
}// Process rotation (?)
pElement = XMLNode->FirstChildElement("rotation");
if(pElement)
{
mAttachNode->setOrientation(parseQuaternion(pElement));
mAttachNode->setInitialState();
}// Process scale (?)
pElement = XMLNode->FirstChildElement("scale");
if(pElement)
{
mAttachNode->setScale(parseVector3(pElement));
mAttachNode->setInitialState();
}
}void DotSceneLoader::processExternals(TiXmlElement *XMLNode)
{
//! @todo Implement this
}void DotSceneLoader::processEnvironment(TiXmlElement *XMLNode)
{
TiXmlElement *pElement;// Process fog (?)
pElement = XMLNode->FirstChildElement("fog");
if(pElement)
processFog(pElement);// Process skyBox (?)
pElement = XMLNode->FirstChildElement("skyBox");
if(pElement)
processSkyBox(pElement);// Process skyDome (?)
pElement = XMLNode->FirstChildElement("skyDome");
if(pElement)
processSkyDome(pElement);// Process skyPlane (?)
pElement = XMLNode->FirstChildElement("skyPlane");
if(pElement)
processSkyPlane(pElement);// Process clipping (?)
pElement = XMLNode->FirstChildElement("clipping");
if(pElement)
processClipping(pElement);// Process colourAmbient (?)
pElement = XMLNode->FirstChildElement("colourAmbient");
if(pElement)
mSceneMgr->setAmbientLight(parseColour(pElement));// Process colourBackground (?)
//! @todo Set the background colour of all viewports (RenderWindow has to be provided then)
pElement = XMLNode->FirstChildElement("colourBackground");
if(pElement)
;//mSceneMgr->set(parseColour(pElement));// Process userDataReference (?)
pElement = XMLNode->FirstChildElement("userDataReference");
if(pElement)
processUserDataReference(pElement);
}void DotSceneLoader::processTerrain(TiXmlElement *XMLNode)
String worldTexture = getAttrib(XMLNode, "WorldTexture");
{
std::string terrainConfig;
if (worldTexture.size() != 0)
{
terrainConfig += "WorldTexture=";
terrainConfig += worldTexture;
terrainConfig += "\n";
}String detailTexture = getAttrib(XMLNode, "DetailTexture");
if (detailTexture.size() != 0)
{
terrainConfig += "DetailTexture=";
terrainConfig += detailTexture;
terrainConfig += "\n";
}String detailTile = getAttrib(XMLNode, "DetailTile");
String pageSource = getAttrib(XMLNode, "PageSource");
if (detailTile.size() != 0)
{
terrainConfig += "DetailTile=";
terrainConfig += detailTile;
terrainConfig += "\n";
}
if (pageSource.size() != 0)
{
terrainConfig += "PageSource=";
terrainConfig += pageSource;
terrainConfig += "\n";
}String heightmapImage = getAttrib(XMLNode, "Heightmap.image");
if (heightmapImage.size() != 0)
{
terrainConfig += "Heightmap.image=";
terrainConfig += heightmapImage;
terrainConfig += "\n";
}String heightmapRawSize = getAttrib(XMLNode, "Heightmap.raw.size");
if (heightmapRawSize.size() != 0)
{
terrainConfig += "Heightmap.raw.size=";
terrainConfig += heightmapRawSize;
terrainConfig += "\n";
}String heightmapRawBpp = getAttrib(XMLNode, "Heightmap.raw.bpp");
if (heightmapRawBpp.size() != 0)
{
terrainConfig += "Heightmap.raw.bpp=";
terrainConfig += heightmapRawBpp;
terrainConfig += "\n";
}String pageSize = getAttrib(XMLNode, "PageSize");
if (pageSize.size() != 0)
{
terrainConfig += "PageSize=";
terrainConfig += pageSize;
terrainConfig += "\n";
}String tileSize = getAttrib(XMLNode, "TileSize");
if (tileSize.size() != 0)
{
terrainConfig += "TileSize=";
terrainConfig += tileSize;
terrainConfig += "\n";
}String maxPixelError = getAttrib(XMLNode, "MaxPixelError");
if (maxPixelError.size() != 0)
{
terrainConfig += "MaxPixelError=";
terrainConfig += maxPixelError;
terrainConfig += "\n";
}String pageWorldX = getAttrib(XMLNode, "PageWorldX");
if (pageWorldX.size() != 0)
{
terrainConfig += "PageWorldX=";
terrainConfig += pageWorldX;
terrainConfig += "\n";
}String pageWorldZ = getAttrib(XMLNode, "PageWorldZ");
if (pageWorldZ.size() != 0)
{
terrainConfig += "PageWorldZ=";
terrainConfig += pageWorldZ;
terrainConfig += "\n";
}String maxHeight = getAttrib(XMLNode, "MaxHeight");
if (maxHeight.size() != 0)
{
terrainConfig += "MaxHeight=";
terrainConfig += maxHeight;
terrainConfig += "\n";
}String maxMipMapLevel = getAttrib(XMLNode, "MaxMipMapLevel");
if (maxMipMapLevel.size() != 0)
{
terrainConfig += "MaxMipMapLevel=";
terrainConfig += maxMipMapLevel;
terrainConfig += "\n";
}String vertexNormals = getAttrib(XMLNode, "VertexNormals");
if (vertexNormals.size() != 0)
{
terrainConfig += "VertexNormals=";
terrainConfig += vertexNormals;
terrainConfig += "\n";
}String vertexColors = getAttrib(XMLNode, "VertexColors");
if (vertexColors.size() != 0)
{
terrainConfig += "VertexColors=";
vertexColors += vertexColors;
terrainConfig += "\n";
}String useTriStrips = getAttrib(XMLNode, "UseTriStrips");
if (useTriStrips.size() != 0)
{
terrainConfig += "UseTriStrips=";
terrainConfig += useTriStrips;
terrainConfig += "\n";
}String vertexProgramMorph = getAttrib(XMLNode, "VertexProgramMorph");
if (vertexProgramMorph.size() != 0)
{
terrainConfig += "VertexProgramMorph=";
terrainConfig += vertexProgramMorph;
terrainConfig += "\n";
}String morphLODFactorParamName = getAttrib(XMLNode, "MorphLODFactorParamName");
if (morphLODFactorParamName.size() != 0)
{
terrainConfig += "MorphLODFactorParamName=";
terrainConfig += morphLODFactorParamName;
terrainConfig += "\n";
}String morphLODFactorParamIndex = getAttrib(XMLNode, "MorphLODFactorParamIndex");
if (morphLODFactorParamIndex.size() != 0)
{
terrainConfig += "MorphLODFactorParamIndex=";
terrainConfig += morphLODFactorParamIndex;
terrainConfig += "\n";
}String lodMorphStart = getAttrib(XMLNode, "LODMorphStart");
if (lodMorphStart.size() != 0)
{
terrainConfig += "LODMorphStart=";
terrainConfig += lodMorphStart;
terrainConfig += "\n";
}
void *pMem = OGRE_ALLOC_T(unsigned char, terrainConfig.length()+1, MEMCATEGORY_GENERAL);
memset(pMem, 0, terrainConfig.length()+1);
memcpy(pMem, terrainConfig.c_str(), terrainConfig.length() + 1);
DataStreamPtr pStr(new Ogre::MemoryDataStream(pMem, terrainConfig.length() + 1, true));this->mSceneMgr->setWorldGeometry(pStr);
}void DotSceneLoader::processUserDataReference(TiXmlElement *XMLNode, SceneNode *pParent)
{
//! @todo Implement this
}void DotSceneLoader::processOctree(TiXmlElement *XMLNode)
{
//! @todo Implement this
}void DotSceneLoader::processLight(TiXmlElement *XMLNode, SceneNode *pParent)
{
// Process attributes
String name = getAttrib(XMLNode, "name");
String id = getAttrib(XMLNode, "id");// Create the light
Light *pLight = mSceneMgr->createLight(name);
if(pParent)
pParent->attachObject(pLight);String sValue = getAttrib(XMLNode, "type");
if(sValue == "point")
pLight->setType(Light::LT_POINT);
else if(sValue == "directional")
pLight->setType(Light::LT_DIRECTIONAL);
else if(sValue == "spot")
pLight->setType(Light::LT_SPOTLIGHT);
else if(sValue == "radPoint")
pLight->setType(Light::LT_POINT);pLight->setVisible(getAttribBool(XMLNode, "visible", true));
pLight->setCastShadows(getAttribBool(XMLNode, "castShadows", true));TiXmlElement *pElement;
// Process position (?)
pElement = XMLNode->FirstChildElement("position");
if(pElement)
pLight->setPosition(parseVector3(pElement));// Process normal (?)
pElement = XMLNode->FirstChildElement("normal");
if(pElement)
pLight->setDirection(parseVector3(pElement));// Process colourDiffuse (?)
pElement = XMLNode->FirstChildElement("colourDiffuse");
if(pElement)
pLight->setDiffuseColour(parseColour(pElement));// Process colourSpecular (?)
pElement = XMLNode->FirstChildElement("colourSpecular");
if(pElement)
pLight->setSpecularColour(parseColour(pElement));// Process lightRange (?)
pElement = XMLNode->FirstChildElement("lightRange");
if(pElement)
processLightRange(pElement, pLight);// Process lightAttenuation (?)
pElement = XMLNode->FirstChildElement("lightAttenuation");
if(pElement)
processLightAttenuation(pElement, pLight);// Process userDataReference (?)
pElement = XMLNode->FirstChildElement("userDataReference");
if(pElement)
;//processUserDataReference(pElement, pLight);
}void DotSceneLoader::processCamera(TiXmlElement *XMLNode, SceneNode *pParent)
{
// Process attributes
String name = getAttrib(XMLNode, "name");
String id = getAttrib(XMLNode, "id");
Real fov = getAttribReal(XMLNode, "fov", 45);
Real aspectRatio = getAttribReal(XMLNode, "aspectRatio", 1.3333);
String projectionType = getAttrib(XMLNode, "projectionType", "perspective");// Find an existing cmaera
if (pCamera == NULL)
Camera *pCamera = mSceneMgr->getCamera(name);
{
// Create the camera
Camera *pCamera = mSceneMgr->createCamera(name);
if(pParent)
pParent->attachObject(pCamera);
}// Set the field-of-view
//! @todo Is this always in degrees?
pCamera->setFOVy(Ogre::Degree(fov));// Set the aspect ratio
pCamera->setAspectRatio(aspectRatio);// Set the projection type
if(projectionType == "perspective")
pCamera->setProjectionType(PT_PERSPECTIVE);
else if(projectionType == "orthographic")
pCamera->setProjectionType(PT_ORTHOGRAPHIC);
TiXmlElement *pElement;// Process clipping (?)
pElement = XMLNode->FirstChildElement("clipping");
if(pElement)
{
Real nearDist = getAttribReal(pElement, "near");
pCamera->setNearClipDistance(nearDist);Real farDist = getAttribReal(pElement, "far");
pCamera->setFarClipDistance(farDist);
}// Process position (?)
pElement = XMLNode->FirstChildElement("position");
if(pElement)
pCamera->setPosition(parseVector3(pElement));// Process rotation (?)
pElement = XMLNode->FirstChildElement("rotation");
if(pElement)
pCamera->setOrientation(parseQuaternion(pElement));pElement = XMLNode->FirstChildElement("lookAt");
if(pElement)
pCamera->lookAt(parseVector3(pElement));// Process normal (?)
pElement = XMLNode->FirstChildElement("normal");
if(pElement)
;//!< @todo What to do with this element?// Process lookTarget (?)
pElement = XMLNode->FirstChildElement("lookTarget");
if(pElement)
;//!< @todo Implement the camera look target// Process trackTarget (?)
pElement = XMLNode->FirstChildElement("trackTarget");
if(pElement)
;//!< @todo Implement the camera track target// Process userDataReference (?)
pElement = XMLNode->FirstChildElement("userDataReference");
if(pElement)
;//!< @todo Implement the camera user data reference
}void DotSceneLoader::processNode(TiXmlElement *XMLNode, SceneNode *pParent)
{
// Construct the node's name
String name = m_sPrependNode + getAttrib(XMLNode, "name");// Create the scene node
SceneNode *pNode;
if(name.empty())
{
// Let Ogre choose the name
if(pParent)
pNode = pParent->createChildSceneNode();
else
pNode = mAttachNode->createChildSceneNode();
}
else
{
// Provide the name
if(pParent)
pNode = pParent->createChildSceneNode(name);
else
pNode = mAttachNode->createChildSceneNode(name);
}// Process other attributes
String id = getAttrib(XMLNode, "id");
bool isTarget = getAttribBool(XMLNode, "isTarget");TiXmlElement *pElement;
// Process position (?)
pElement = XMLNode->FirstChildElement("position");
if(pElement)
{
pNode->setPosition(parseVector3(pElement));
pNode->setInitialState();
}// Process rotation (?)
pElement = XMLNode->FirstChildElement("rotation");
if(pElement)
{
pNode->setOrientation(parseQuaternion(pElement));
pNode->setInitialState();
}// Process scale (?)
pElement = XMLNode->FirstChildElement("scale");
if(pElement)
{
pNode->setScale(parseVector3(pElement));
pNode->setInitialState();
}// Process lookTarget (?)
pElement = XMLNode->FirstChildElement("lookTarget");
if(pElement)
processLookTarget(pElement, pNode);// Process trackTarget (?)
pElement = XMLNode->FirstChildElement("trackTarget");
if(pElement)
processTrackTarget(pElement, pNode);// Process node (*)
pElement = XMLNode->FirstChildElement("node");
while(pElement)
{
processNode(pElement, pNode);
pElement = pElement->NextSiblingElement("node");
}// Process entity (*)
pElement = XMLNode->FirstChildElement("entity");
while(pElement)
{
processEntity(pElement, pNode);
pElement = pElement->NextSiblingElement("entity");
}// Process light (*)
pElement = XMLNode->FirstChildElement("light");
while(pElement)
{
processLight(pElement, pNode);
pElement = pElement->NextSiblingElement("light");
}// Process camera (*)
pElement = XMLNode->FirstChildElement("camera");
while(pElement)
{
processCamera(pElement, pNode);
pElement = pElement->NextSiblingElement("camera");
}// Process particleSystem (*)
pElement = XMLNode->FirstChildElement("particleSystem");
while(pElement)
{
processParticleSystem(pElement, pNode);
pElement = pElement->NextSiblingElement("particleSystem");
}// Process billboardSet (*)
pElement = XMLNode->FirstChildElement("billboardSet");
while(pElement)
{
processBillboardSet(pElement, pNode);
pElement = pElement->NextSiblingElement("billboardSet");
}// Process plane (*)
pElement = XMLNode->FirstChildElement("plane");
while(pElement)
{
processPlane(pElement, pNode);
pElement = pElement->NextSiblingElement("plane");
}// Process userDataReference (?)
pElement = XMLNode->FirstChildElement("userDataReference");
if(pElement)
processUserDataReference(pElement, pNode);
}void DotSceneLoader::processLookTarget(TiXmlElement *XMLNode, SceneNode *pParent)
{
//! @todo Is this correct? Cause I don't have a clue actually// Process attributes
String nodeName = getAttrib(XMLNode, "nodeName");Node::TransformSpace relativeTo = Node::TS_PARENT;
String sValue = getAttrib(XMLNode, "relativeTo");
if(sValue == "local")
relativeTo = Node::TS_LOCAL;
else if(sValue == "parent")
relativeTo = Node::TS_PARENT;
else if(sValue == "world")
relativeTo = Node::TS_WORLD;TiXmlElement *pElement;
// Process position (?)
Vector3 position;
pElement = XMLNode->FirstChildElement("position");
if(pElement)
position = parseVector3(pElement);// Process localDirection (?)
Vector3 localDirection = Vector3::NEGATIVE_UNIT_Z;
pElement = XMLNode->FirstChildElement("localDirection");
if(pElement)
localDirection = parseVector3(pElement);// Setup the look target
try
{
if(!nodeName.empty())
{
SceneNode *pLookNode = mSceneMgr->getSceneNode(nodeName);
position = pLookNode->_getDerivedPosition();
}pParent->lookAt(position, relativeTo, localDirection);
}
catch(Ogre::Exception &/*e*/)
{
LogManager::getSingleton().logMessage("[DotSceneLoader] Error processing a look target!");
}
}void DotSceneLoader::processTrackTarget(TiXmlElement *XMLNode, SceneNode *pParent)
{
// Process attributes
String nodeName = getAttrib(XMLNode, "nodeName");TiXmlElement *pElement;
// Process localDirection (?)
Vector3 localDirection = Vector3::NEGATIVE_UNIT_Z;
pElement = XMLNode->FirstChildElement("localDirection");
if(pElement)
localDirection = parseVector3(pElement);// Process offset (?)
Vector3 offset = Vector3::ZERO;
pElement = XMLNode->FirstChildElement("offset");
if(pElement)
offset = parseVector3(pElement);// Setup the track target
try
{
SceneNode *pTrackNode = mSceneMgr->getSceneNode(nodeName);
pParent->setAutoTracking(true, pTrackNode, localDirection, offset);
}
catch(Ogre::Exception &/*e*/)
{
LogManager::getSingleton().logMessage("[DotSceneLoader] Error processing a track target!");
}
}void DotSceneLoader::processEntity(TiXmlElement *XMLNode, SceneNode *pParent)
{
// Process attributes
String name = getAttrib(XMLNode, "name");
String id = getAttrib(XMLNode, "id");
String meshFile = getAttrib(XMLNode, "meshFile");
String materialFile = getAttrib(XMLNode, "materialFile");
bool isStatic = getAttribBool(XMLNode, "static", false);;
bool castShadows = getAttribBool(XMLNode, "castShadows", true);// TEMP: Maintain a list of static and dynamic objects
if(isStatic)
staticObjects.push_back(name);
else
dynamicObjects.push_back(name);TiXmlElement *pElement;
// Process vertexBuffer (?)
pElement = XMLNode->FirstChildElement("vertexBuffer");
if(pElement)
;//processVertexBuffer(pElement);// Process indexBuffer (?)
pElement = XMLNode->FirstChildElement("indexBuffer");
if(pElement)
;//processIndexBuffer(pElement);// Create the entity
Entity *pEntity = 0;
try
{
MeshManager::getSingleton().load(meshFile, m_sGroupName);
pEntity = mSceneMgr->createEntity(name, meshFile);
pEntity->setCastShadows(castShadows);
pParent->attachObject(pEntity);if(!materialFile.empty())
pEntity->setMaterialName(materialFile);
}
catch(Ogre::Exception &/*e*/)
{
LogManager::getSingleton().logMessage("[DotSceneLoader] Error loading an entity!");
}// Process userDataReference (?)
pElement = XMLNode->FirstChildElement("userDataReference");
if(pElement)
processUserDataReference(pElement, pEntity);
}void DotSceneLoader::processParticleSystem(TiXmlElement *XMLNode, SceneNode *pParent)
{
// Process attributes
String name = getAttrib(XMLNode, "name");
String id = getAttrib(XMLNode, "id");
String file = getAttrib(XMLNode, "file");// Create the particle system
try
{
ParticleSystem *pParticles = mSceneMgr->createParticleSystem(name, file);
pParent->attachObject(pParticles);
}
catch(Ogre::Exception &/*e*/)
{
LogManager::getSingleton().logMessage("[DotSceneLoader] Error creating a particle system!");
}
}void DotSceneLoader::processBillboardSet(TiXmlElement *XMLNode, SceneNode *pParent)
{
//! @todo Implement this
}void DotSceneLoader::processPlane(TiXmlElement *XMLNode, SceneNode *pParent)
{
//! @todo Implement this
}void DotSceneLoader::processFog(TiXmlElement *XMLNode)
{
// Process attributes
Real expDensity = getAttribReal(XMLNode, "expDensity", 0.001);
Real linearStart = getAttribReal(XMLNode, "linearStart", 0.0);
Real linearEnd = getAttribReal(XMLNode, "linearEnd", 1.0);FogMode mode = FOG_NONE;
String sMode = getAttrib(XMLNode, "mode");
if(sMode == "none")
mode = FOG_NONE;
else if(sMode == "exp")
mode = FOG_EXP;
else if(sMode == "exp2")
mode = FOG_EXP2;
else if(sMode == "linear")
mode = FOG_LINEAR;TiXmlElement *pElement;
// Process colourDiffuse (?)
ColourValue colourDiffuse = ColourValue::White;
pElement = XMLNode->FirstChildElement("colourDiffuse");
if(pElement)
colourDiffuse = parseColour(pElement);// Setup the fog
mSceneMgr->setFog(mode, colourDiffuse, expDensity, linearStart, linearEnd);
}void DotSceneLoader::processSkyBox(TiXmlElement *XMLNode)
{
// Process attributes
String material = getAttrib(XMLNode, "material");
Real distance = getAttribReal(XMLNode, "distance", 5000);
bool drawFirst = getAttribBool(XMLNode, "drawFirst", true);TiXmlElement *pElement;
// Process rotation (?)
Quaternion rotation = Quaternion::IDENTITY;
pElement = XMLNode->FirstChildElement("rotation");
if(pElement)
rotation = parseQuaternion(pElement);// Setup the sky box
mSceneMgr->setSkyBox(true, material, distance, drawFirst, rotation, m_sGroupName);
}void DotSceneLoader::processSkyDome(TiXmlElement *XMLNode)
{
// Process attributes
String material = XMLNode->Attribute("material");
Real curvature = getAttribReal(XMLNode, "curvature", 10);
Real tiling = getAttribReal(XMLNode, "tiling", 8);
Real distance = getAttribReal(XMLNode, "distance", 4000);
bool drawFirst = getAttribBool(XMLNode, "drawFirst", true);TiXmlElement *pElement;
// Process rotation (?)
Quaternion rotation = Quaternion::IDENTITY;
pElement = XMLNode->FirstChildElement("rotation");
if(pElement)
rotation = parseQuaternion(pElement);// Setup the sky dome
mSceneMgr->setSkyDome(true, material, curvature, tiling, distance, drawFirst, rotation, 16, 16, -1, m_sGroupName);
}void DotSceneLoader::processSkyPlane(TiXmlElement *XMLNode)
{
// Process attributes
String material = getAttrib(XMLNode, "material");
Real planeX = getAttribReal(XMLNode, "planeX", 0);
Real planeY = getAttribReal(XMLNode, "planeY", -1);
Real planeZ = getAttribReal(XMLNode, "planeX", 0);
Real planeD = getAttribReal(XMLNode, "planeD", 5000);
Real scale = getAttribReal(XMLNode, "scale", 1000);
Real bow = getAttribReal(XMLNode, "bow", 0);
Real tiling = getAttribReal(XMLNode, "tiling", 10);
bool drawFirst = getAttribBool(XMLNode, "drawFirst", true);// Setup the sky plane
Plane plane;
plane.normal = Vector3(planeX, planeY, planeZ);
plane.d = planeD;
mSceneMgr->setSkyPlane(true, plane, material, scale, tiling, drawFirst, bow, 1, 1, m_sGroupName);
}void DotSceneLoader::processClipping(TiXmlElement *XMLNode)
{
//! @todo Implement this// Process attributes
Real fNear = getAttribReal(XMLNode, "near", 0);
Real fFar = getAttribReal(XMLNode, "far", 1);
}void DotSceneLoader::processLightRange(TiXmlElement *XMLNode, Light *pLight)
{
// Process attributes
Real inner = getAttribReal(XMLNode, "inner");
Real outer = getAttribReal(XMLNode, "outer");
Real falloff = getAttribReal(XMLNode, "falloff", 1.0);// Setup the light range
pLight->setSpotlightRange(Angle(inner), Angle(outer), falloff);
}void DotSceneLoader::processLightAttenuation(TiXmlElement *XMLNode, Light *pLight)
{
// Process attributes
Real range = getAttribReal(XMLNode, "range");
Real constant = getAttribReal(XMLNode, "constant");
Real linear = getAttribReal(XMLNode, "linear");
Real quadratic = getAttribReal(XMLNode, "quadratic");// Setup the light attenuation
pLight->setAttenuation(range, constant, linear, quadratic);
}
String DotSceneLoader::getAttrib(TiXmlElement *XMLNode, const String &attrib, const String &defaultValue)
{
if(XMLNode->Attribute(attrib.c_str()))
return XMLNode->Attribute(attrib.c_str());
else
return defaultValue;
}Real DotSceneLoader::getAttribReal(TiXmlElement *XMLNode, const String &attrib, Real defaultValue)
{
if(XMLNode->Attribute(attrib.c_str()))
return StringConverter::parseReal(XMLNode->Attribute(attrib.c_str()));
else
return defaultValue;
}int DotSceneLoader::getAttribInt(TiXmlElement *XMLNode, const String &attrib, int defaultValue)
{
if(XMLNode->Attribute(attrib.c_str()))
return StringConverter::parseInt(XMLNode->Attribute(attrib.c_str()));
else
return defaultValue;
}bool DotSceneLoader::getAttribBool(TiXmlElement *XMLNode, const String &attrib, bool defaultValue)
{
if(!XMLNode->Attribute(attrib.c_str()))
return defaultValue;if(String(XMLNode->Attribute(attrib.c_str())) == "true")
return true;return false;
}
Vector3 DotSceneLoader::parseVector3(TiXmlElement *XMLNode)
{
return Vector3(
StringConverter::parseReal(XMLNode->Attribute("x")),
StringConverter::parseReal(XMLNode->Attribute("y")),
StringConverter::parseReal(XMLNode->Attribute("z"))
);
}Quaternion DotSceneLoader::parseQuaternion(TiXmlElement *XMLNode)
{
//! @todo Fix this crap!Quaternion orientation;
if(XMLNode->Attribute("qx"))
{
orientation.x = StringConverter::parseReal(XMLNode->Attribute("qx"));
orientation.y = StringConverter::parseReal(XMLNode->Attribute("qy"));
orientation.z = StringConverter::parseReal(XMLNode->Attribute("qz"));
orientation.w = StringConverter::parseReal(XMLNode->Attribute("qw"));
}
else if(XMLNode->Attribute("axisX"))
{
Vector3 axis;
axis.x = StringConverter::parseReal(XMLNode->Attribute("axisX"));
axis.y = StringConverter::parseReal(XMLNode->Attribute("axisY"));
axis.z = StringConverter::parseReal(XMLNode->Attribute("axisZ"));
Real angle = StringConverter::parseReal(XMLNode->Attribute("angle"));;
orientation.FromAngleAxis(Ogre::Angle(angle), axis);
}
else if(XMLNode->Attribute("angleX"))
{
Vector3 axis;
axis.x = StringConverter::parseReal(XMLNode->Attribute("angleX"));
axis.y = StringConverter::parseReal(XMLNode->Attribute("angleY"));
axis.z = StringConverter::parseReal(XMLNode->Attribute("angleZ"));
//orientation.FromAxes(&axis);
//orientation.F
}return orientation;
}ColourValue DotSceneLoader::parseColour(TiXmlElement *XMLNode)
{
return ColourValue(
StringConverter::parseReal(XMLNode->Attribute("r")),
StringConverter::parseReal(XMLNode->Attribute("g")),
StringConverter::parseReal(XMLNode->Attribute("b")),
XMLNode->Attribute("a") != NULL ? StringConverter::parseReal(XMLNode->Attribute("a")) : 1
);
}String DotSceneLoader::getProperty(const String &ndNm, const String ∝)
{
for ( unsigned int i = 0 ; i < nodeProperties.size(); i++ )
{
if ( nodeProperties[i].nodeName == ndNm && nodeProperties[i].propertyNm == prop )
{
return nodeProperties[i].valueName;
}
}return "";
}void DotSceneLoader::processUserDataReference(TiXmlElement *XMLNode, Entity *pEntity)
{
String str = XMLNode->Attribute("id");
pEntity->setUserAny(Any(str));
}
Eventually the game levels will need to update themselves every frame to do things like scroll the camera, trigger the end of the level, trigger special effects etc. To do this we need to extend the FrameListener class, but there is a subtle, but very important, catch to be aware of when extending the FrameListener class: when you remove a FrameListener from the collection held by the Ogre Root class using the removeFrameListener function, it won’t actually be removed from the collection until the next frame. This is important because if you remove a FrameListener using the removeFrameListener function and then delete it, your application may crash because Ogre will retain a reference to that object for the remainder of the frame and may try to call the deleted objects frameStarted function.
We will address this issue by never actually deleting objects that extend the FrameListener class. Instead we will keep these objects in a pool, and unused objects will be in a deactivated state where they still exist, meaning Ogre can call their frameStarted function, but they won’t actually do anything.
The PersistentFrameListener class will implement this functionality for us. As you can see from the code below it extends the FrameListener class. If an object that extends the PersistentFrameListener class is active, it will update itself in the FrameStarted function (notice the capital F). If the object has been shutdown the FrameStarted function will not be called.
PersistentFrameListener.h
/*
* PersistentFrameListener.h
*
* Created on: 18/12/2009
* Author: Matthew Casperson
* Email: matthewcasperson@gmail.com
* Website: http://www.brighthub.com/hubfolio/matthew-casperson.aspx
*/#ifndef PERSISTENTFRAMELISTENER_H_
#define PERSISTENTFRAMELISTENER_H_#include "Ogre.h"
#include "OgreEngineManager.h"class PersistentFrameListener :
public FrameListener
{
public:
PersistentFrameListener() :
isStarted(false)
{
ENGINEMANAGER.GetRoot()->addFrameListener(this);
}
~PersistentFrameListener()
{
if (ENGINEMANAGER.GetRoot() != NULL)
ENGINEMANAGER.GetRoot()->removeFrameListener(this);
}void Startup()
{
isStarted = true;
}void Shutdown()
{
isStarted = false;
}bool frameStarted(const FrameEvent& evt)
{
if (this->isStarted)
return FrameStarted(evt);return true;
}virtual bool FrameStarted(const FrameEvent& evt) = 0;
protected:
bool isStarted;
};#endif /* PERSISTENTFRAMELISTENER_H_ */
The GameLevel class represents a level in the game. Since only one level can be active at any one time, we will make the GameLevel class a singleton. Singleton objects can also extend the PersistentFrameListener class so they will exist but not do any processing in a shutdown state. You can see in the code below that the GameLevel class calls the PersistentFrameListener Startup and Shutdown functions in its own functions of the same name.
For the moment the GameLevel class is very simple, creating a terrain scene manager, camera and viewport. It then initialises the scene using an XML file and the DotSceneLoader class in the Startup function. These resources are all cleaned up in the Shutdown function.
GameLevel.h
/*
* GameLevel.h
*
* Created on: 18/12/2009
* Author: Matthew Casperson
* Email: matthewcasperson@gmail.com
* Website: http://www.brighthub.com/hubfolio/matthew-casperson.aspx
*/#ifndef GAMELEVEL_H_
#define GAMELEVEL_H_#include "Ogre.h"
#include "PersistentFrameListener.h"#define GAMELEVEL GameLevel::Instance()
class GameLevel :
public PersistentFrameListener
{
public:
~GameLevel();
static GameLevel& Instance()
{
static GameLevel instance;
return instance;
}void Startup(std::string scene);
void Shutdown();bool FrameStarted(const FrameEvent& evt);
protected:
GameLevel();
SceneManager* sceneManager;
Camera* camera;
Viewport* viewport;};
#endif /* GAMELEVEL_H_ */
GameLevel.cpp
/*
* GameLevel.cpp
*
* Created on: 18/12/2009
* Author: Matthew Casperson
* Email: matthewcasperson@gmail.com
* Website: http://www.brighthub.com/hubfolio/matthew-casperson.aspx
*/#include "GameLevel.h"
#include "DotSceneLoader.h"static const char* LEVEL_GROUP_NAME = "General";
GameLevel::GameLevel() :
sceneManager(NULL),
camera(NULL),
viewport(NULL)
{}
GameLevel::~GameLevel()
{}
void GameLevel::Startup(std::string scene)
{
PersistentFrameListener::Startup();sceneManager = ENGINEMANAGER.GetRoot()->createSceneManager("TerrainSceneManager");
camera = sceneManager->createCamera("Camera");
viewport = ENGINEMANAGER.GetRenderWindow()->addViewport(camera);std::auto_ptr sceneLoader(new DotSceneLoader());
sceneLoader->parseDotScene(scene, LEVEL_GROUP_NAME, sceneManager);
}void GameLevel::Shutdown()
PersistentFrameListener::Shutdown();
{
if (viewport != NULL) ENGINEMANAGER.GetRenderWindow()->removeViewport(viewport->getZOrder());
if (sceneManager != NULL) ENGINEMANAGER.GetRoot()->destroySceneManager(sceneManager);
sceneManager = NULL;
camera = NULL;
viewport = NULL;
}bool GameLevel::FrameStarted(const FrameEvent& evt)
{
return true;
}
The main/WinMain function is modified to call the GameLevel Startup and Shutdown functions. We also need to add the media directory as a resource location.
Main.cpp
#include "OgreEngineManager.h"
#include "GameLevel.h"#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
if (ENGINEMANAGER.Startup(std::string("plugins.cfg"), std::string("ogre.cfg"), std::string("ogre.log")))
#else
int main(int argc, char **argv)
#endif
{
ENGINEMANAGER.AddNewResourceLocation(ResourceLocationDefinition("FileSystem", "../../media", "General"));
ENGINEMANAGER.AddNewResourceLocation(ResourceLocationDefinition("Zip", "../../media/dragon.zip", "General"));
{
GAMELEVEL.Startup("Level1.XML");
ENGINEMANAGER.StartRenderLoop();
} GAMELEVEL.Shutdown();
ENGINEMANAGER.Shutdown();
}
When you load the application up with the default settings a terrain will be loaded and the camera positioned to get a good view. If you edit the Level1.XML file you can see how the scene is changed without having to recompile the application.

Get Up to Programming Speed With Online Tutorials...
Here is a look at some great free frameworks to get started developing themes for WordPress....
The concept of software development isn't that new to our generation. But still there are so ma...
In my humble opinion, CMS Drupal has come the most close to the concept of "ideal CMS". A lot o...
Setting up WiFi services for a large event can be difficult...
In this tutorial we add mouse interactivity to the scene....
In this tutorial we allow the view of the isometric scene to be moved with the mouse....
In this tutorial we show the height of an isometric object by adding a shadow....
In this tutorial we modify the appearance of the isometric cube at runtime....
In this tutorial we add some animated isometric boxes to the scene....