2015-02-04 16:16:06 +00:00
# include "loader.hh"
2015-04-25 20:29:37 +00:00
# include "main.hh"
2015-03-06 14:24:02 +00:00
# include <ACGL/OpenGL/Objects/VertexArrayObject.hh>
2015-02-04 16:16:06 +00:00
using namespace tinyxml2 ;
2015-03-21 17:57:14 +00:00
const int chunkSize = 25 ;
2015-03-17 11:08:49 +00:00
2015-02-04 16:16:06 +00:00
Loader : : Loader ( ) {
}
2015-02-07 19:08:59 +00:00
void Loader : : loadConfig ( Application * application ) {
XMLDocument * config = new XMLDocument ( ) ;
const char * xmlFile = " ../data/config.xml " ;
config - > LoadFile ( xmlFile ) ;
if ( config - > ErrorID ( ) ! = 0 ) {
printf ( " Could not open config.xml! \n " ) ;
exit ( - 1 ) ;
}
XMLElement * resolution = config - > FirstChildElement ( " resolution " ) ;
2015-02-18 19:10:30 +00:00
application - > setWindowWidth ( queryInt ( resolution , " width " ) ) ;
application - > setWindowHeight ( queryInt ( resolution , " height " ) ) ;
2015-03-15 16:48:46 +00:00
application - > setFullscreen ( queryBool ( config , " windowedFullscreen " ) ) ;
2015-02-18 19:10:30 +00:00
application - > setShadowCubeSize ( queryInt ( config , " shadowCubeSize " ) ) ;
application - > setFarPlane ( queryFloat ( config , " farPlane " ) ) ;
application - > setMaxShadowRenderCount ( queryInt ( config , " maxShadowRenderCount " ) ) ;
application - > setCompositionsPath ( queryString ( config , " compositionsPath " ) ) ;
application - > setShaderPath ( queryString ( config , " shaderPath " ) ) ;
application - > setGeometryPath ( queryString ( config , " geometryPath " ) ) ;
2015-03-07 18:59:52 +00:00
std : : string texturePath = queryString ( config , " texturePath " ) ;
application - > setTexturePath ( texturePath ) ;
2015-02-18 19:10:30 +00:00
application - > setScriptPath ( queryString ( config , " scriptPath " ) ) ;
application - > setHeightmapPath ( queryString ( config , " heightmapPath " ) ) ;
2015-03-14 13:57:42 +00:00
std : : string levelXmlPath = queryString ( config , " levelXmlPath " ) ;
application - > setLevelXmlPath ( levelXmlPath + queryString ( config , " levelXML " ) ) ;
2015-03-07 18:59:52 +00:00
XMLElement * loadingScreen = config - > FirstChildElement ( " loadingScreen " ) ;
if ( loadingScreen ! = NULL ) {
std : : string screenPath = queryString ( loadingScreen , " screenPath " ) ;
std : : string screenContinuePath = queryString ( loadingScreen , " screenContinuePath " ) ;
std : : string screenCheckPath = " ../ " + texturePath + screenPath ;
std : : string screenContinueCheckPath = " ../ " + texturePath + screenPath ;
struct stat buf ;
if ( stat ( screenCheckPath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < screenCheckPath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
if ( stat ( screenContinueCheckPath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < screenContinueCheckPath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
application - > setLoadingScreenPath ( screenPath ) ;
application - > setLoadingScreenContinuePath ( screenContinuePath ) ;
}
else {
printf ( " Could not find loading screen settings in config.xml. Exiting. \n " ) ;
exit ( - 1 ) ;
}
2015-02-07 19:08:59 +00:00
}
2015-03-14 12:00:47 +00:00
void Loader : : load ( std : : string filePath , Level * level , std : : string compositionsPath , std : : string scriptPath ,
2015-04-25 20:29:37 +00:00
std : : string globalGeometryPath , std : : string globalTexturePath , std : : string heightmapPath , Graphics * graphics ) {
2015-03-14 12:00:47 +00:00
struct stat buf ;
2015-02-04 16:16:06 +00:00
//Loading from xml:
XMLDocument * doc = new XMLDocument ( ) ;
2015-02-06 17:00:14 +00:00
const char * xmlFile = filePath . c_str ( ) ;
2015-02-04 16:16:06 +00:00
doc - > LoadFile ( xmlFile ) ;
if ( doc - > ErrorID ( ) ! = 0 ) {
printf ( " Could not open ObjectSetupXml! \n " ) ;
exit ( - 1 ) ;
}
//load global physic parameter
XMLElement * physicsElement = doc - > FirstChildElement ( " physics " ) ;
2015-02-18 20:20:29 +00:00
float strength = queryFloat ( physicsElement , " strength " ) ;
float friction = queryFloat ( physicsElement , " friction " ) ;
2015-02-04 16:16:06 +00:00
level - > setStrength ( strength ) ;
2015-02-06 11:51:53 +00:00
// load the terrain
2015-03-14 12:00:47 +00:00
XMLElement * terrainElement = doc - > FirstChildElement ( " terrain " ) ;
std : : string levelHeightmapPath = heightmapPath + queryString ( terrainElement , " heightmap " ) ;
if ( stat ( levelHeightmapPath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The heightmap file " < < levelHeightmapPath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
Terrain terrain = Terrain ( levelHeightmapPath ) ;
level - > setTerrain ( terrain ) ;
2015-03-17 11:08:49 +00:00
// Because object gets copied a lot, load it when it has reached it's final destination
2015-02-06 11:51:53 +00:00
level - > getTerrain ( ) - > load ( ) ;
2015-03-17 11:08:49 +00:00
// generate appropriate amount of chunks depending on the heightmap size.
level - > generateChunks ( chunkSize ) ;
2015-02-18 20:20:29 +00:00
std : : string terrainTexture = queryString ( terrainElement , " texture " ) ;
float terrainAmbientFactor = queryFloat ( terrainElement , " ambientFactor " ) ;
float terrainDiffuseFactor = queryFloat ( terrainElement , " diffuseFactor " ) ;
float terrainSpecularFactor = queryFloat ( terrainElement , " specularFactor " ) ;
float terrainShininess = queryFloat ( terrainElement , " shininess " ) ;
2015-03-03 17:45:02 +00:00
std : : string terrainTexturePath = " ../ " + globalTexturePath + terrainTexture ;
if ( stat ( terrainTexturePath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < terrainTexturePath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
2015-02-06 11:51:53 +00:00
Material terrainMaterial = Material ( terrainTexture , terrainAmbientFactor , terrainDiffuseFactor , terrainSpecularFactor , terrainShininess ) ;
2015-03-17 11:08:49 +00:00
for ( unsigned int i = 0 ; i < level - > getChunks ( ) - > size ( ) ; i + + ) {
for ( unsigned int j = 0 ; j < level - > getChunks ( ) - > at ( i ) . size ( ) ; j + + ) {
Model terrainModel = Model ( level - > getTerrain ( ) - > makeTriangleMesh ( i * chunkSize , j * chunkSize , ( i + 1 ) * chunkSize + 1 , ( j + 1 ) * chunkSize + 1 ) ) ;
Object * terrainObject = new Object ( terrainModel , terrainMaterial ,
glm : : vec3 ( - 0.5 * ( ( float ) level - > getTerrain ( ) - > getHeightmapHeight ( ) - 1 ) , 0.0f , - 0.5f * ( ( float ) level - > getTerrain ( ) - > getHeightmapWidth ( ) - 1 ) ) ,
glm : : vec3 ( 0.0f , 0.0f , 0.0f ) , true ) ;
2015-03-17 13:54:18 +00:00
level - > addToSpecificChunk ( terrainObject , i , j ) ;
2015-03-17 11:08:49 +00:00
}
}
2015-02-13 12:46:41 +00:00
level - > getPhysics ( ) - > addTerrain ( level - > getTerrain ( ) - > getHeightmapWidth ( ) , level - > getTerrain ( ) - > getHeightmapHeight ( ) , level - > getTerrain ( ) - > getHeightmap ( ) ) ;
2015-02-04 16:16:06 +00:00
//load the skydome
XMLElement * skydomeElement = doc - > FirstChildElement ( " skydome " ) ;
2015-03-04 20:05:24 +00:00
std : : string skydomeModelFileName = queryString ( skydomeElement , " model " ) ;
std : : string skydomePath = " ../ " + globalGeometryPath + skydomeModelFileName ;
2015-03-03 17:45:02 +00:00
if ( stat ( skydomePath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The object file " < < skydomePath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
2015-03-04 20:05:24 +00:00
Model skydomeModel = Model ( skydomeModelFileName , level - > getSkydomeSize ( ) ) ;
2015-03-04 20:28:46 +00:00
std : : string skydomeTexture = queryString ( skydomeElement , " texture " ) ;
2015-03-03 17:45:02 +00:00
std : : string skydomeTexturePath = " ../ " + globalTexturePath + skydomeTexture ;
if ( stat ( skydomeTexturePath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < skydomeTexturePath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
2015-03-04 20:28:46 +00:00
std : : string nightTexture = queryString ( skydomeElement , " nightTexture " ) ;
std : : string nightTexturePath = " ../ " + globalTexturePath + nightTexture ;
if ( stat ( nightTexturePath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < nightTexturePath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
2015-04-12 19:06:59 +00:00
Skydome skydomeObject = Skydome ( skydomeModel , skydomeTexture , nightTexture ) ;
2015-02-04 16:16:06 +00:00
level - > setSkydomeObject ( skydomeObject ) ;
2015-02-13 12:46:41 +00:00
2015-03-06 14:24:02 +00:00
//load the waterPlane
XMLElement * waterElement = doc - > FirstChildElement ( " waterPlane " ) ;
if ( waterElement ! = NULL ) {
float waterHeight = queryFloat ( waterElement , " yPosition " ) ;
std : : string waterTexture = queryString ( waterElement , " texture " ) ;
std : : string waterTexturePath = " ../ " + globalTexturePath + waterTexture ;
if ( stat ( waterTexturePath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < waterTexturePath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
float heightmapHeight = level - > getTerrain ( ) - > getHeightmapHeight ( ) ;
float heightmapWidth = level - > getTerrain ( ) - > getHeightmapWidth ( ) ;
float planeData [ ] = {
2015-03-07 11:48:07 +00:00
- heightmapWidth / 2.0f , waterHeight , - heightmapHeight / 2.0f , 0.0f , heightmapHeight , 0.0f , 1.0f , 0.0f ,
2015-03-06 14:24:02 +00:00
- heightmapWidth / 2.0f , waterHeight , heightmapHeight / 2.0f , 0.0f , 0.0f , 0.0f , 1.0f , 0.0f ,
2015-03-07 11:48:07 +00:00
heightmapWidth / 2.0f , waterHeight , - heightmapHeight / 2.0f , heightmapWidth , heightmapHeight , 0.0f , 1.0f , 0.0f ,
2015-03-06 14:24:02 +00:00
2015-03-07 11:48:07 +00:00
heightmapWidth / 2.0f , waterHeight , heightmapHeight / 2.0f , heightmapWidth , 0.0f , 0.0f , 1.0f , 0.0f ,
2015-03-06 14:24:02 +00:00
- heightmapWidth / 2.0f , waterHeight , heightmapHeight / 2.0f , 0.0f , 0.0f , 0.0f , 1.0f , 0.0f ,
2015-03-07 11:48:07 +00:00
heightmapWidth / 2.0f , waterHeight , - heightmapHeight / 2.0f , heightmapWidth , heightmapHeight , 0.0f , 1.0f , 0.0f
2015-03-06 14:24:02 +00:00
} ;
ACGL : : OpenGL : : SharedArrayBuffer water_ab = ACGL : : OpenGL : : SharedArrayBuffer ( new ACGL : : OpenGL : : ArrayBuffer ( ) ) ;
water_ab - > defineAttribute ( " aPosition " , GL_FLOAT , 3 ) ;
water_ab - > defineAttribute ( " aTexCoord " , GL_FLOAT , 2 ) ;
water_ab - > defineAttribute ( " aNormal " , GL_FLOAT , 3 ) ;
water_ab - > setDataElements ( 6 , planeData ) ;
ACGL : : OpenGL : : SharedVertexArrayObject water_vao = ACGL : : OpenGL : : SharedVertexArrayObject ( new ACGL : : OpenGL : : VertexArrayObject ( ) ) ;
water_vao - > bind ( ) ;
water_vao - > setMode ( GL_TRIANGLES ) ;
water_vao - > attachAllAttributes ( water_ab ) ;
2015-03-09 15:01:03 +00:00
Material water_material = Material ( waterTexture , 0.13f , 0.5f , 0.5f , 10.0f , true ) ;
2015-03-06 14:24:02 +00:00
Object * water_object = new Object ( water_vao , water_material , glm : : vec3 ( 0.0f , 0.0f , 0.0f ) , glm : : vec3 ( 0.0f , 0.0f , 0.0f ) , true ) ;
2015-03-09 13:28:09 +00:00
level - > setWaterPlane ( water_object ) ;
2015-03-06 14:24:02 +00:00
}
2015-02-04 16:16:06 +00:00
//load lighting parameters
float rColour , gColour , bColour , alpha , xOffset , yOffset , zOffset , intensity ;
XMLElement * ambientElement = doc - > FirstChildElement ( " ambientLight " ) ;
2015-02-18 20:20:29 +00:00
rColour = queryFloat ( ambientElement , " rColour " ) ;
gColour = queryFloat ( ambientElement , " gColour " ) ;
bColour = queryFloat ( ambientElement , " bColour " ) ;
2015-02-04 16:16:06 +00:00
level - > setAmbientLight ( glm : : vec3 ( rColour , gColour , bColour ) ) ;
2015-02-13 12:46:41 +00:00
2015-03-08 23:39:59 +00:00
XMLElement * fogElement = doc - > FirstChildElement ( " fogColourDay " ) ;
2015-02-18 20:20:29 +00:00
rColour = queryFloat ( fogElement , " rColour " ) ;
gColour = queryFloat ( fogElement , " gColour " ) ;
bColour = queryFloat ( fogElement , " bColour " ) ;
alpha = queryFloat ( fogElement , " alpha " ) ;
2015-03-08 23:39:59 +00:00
level - > setFogColourDay ( glm : : vec4 ( rColour , gColour , bColour , alpha ) ) ;
2015-02-13 12:46:41 +00:00
2015-03-08 23:39:59 +00:00
fogElement = doc - > FirstChildElement ( " fogColourRise " ) ;
rColour = queryFloat ( fogElement , " rColour " ) ;
gColour = queryFloat ( fogElement , " gColour " ) ;
bColour = queryFloat ( fogElement , " bColour " ) ;
alpha = queryFloat ( fogElement , " alpha " ) ;
level - > setFogColourRise ( glm : : vec4 ( rColour , gColour , bColour , alpha ) ) ;
fogElement = doc - > FirstChildElement ( " fogColourNight " ) ;
rColour = queryFloat ( fogElement , " rColour " ) ;
gColour = queryFloat ( fogElement , " gColour " ) ;
bColour = queryFloat ( fogElement , " bColour " ) ;
alpha = queryFloat ( fogElement , " alpha " ) ;
level - > setFogColourNight ( glm : : vec4 ( rColour , gColour , bColour , alpha ) ) ;
2015-02-04 16:16:06 +00:00
XMLElement * directionalElement = doc - > FirstChildElement ( " directionalLight " ) ;
2015-02-18 20:20:29 +00:00
xOffset = queryFloat ( directionalElement , " xOffset " ) ;
yOffset = queryFloat ( directionalElement , " yOffset " ) ;
zOffset = queryFloat ( directionalElement , " zOffset " ) ;
rColour = queryFloat ( directionalElement , " rColour " ) ;
gColour = queryFloat ( directionalElement , " gColour " ) ;
bColour = queryFloat ( directionalElement , " bColour " ) ;
intensity = queryFloat ( directionalElement , " intensity " ) ;
2015-02-04 16:16:06 +00:00
level - > setDirectionalLight ( Light ( glm : : vec3 ( xOffset , yOffset , zOffset ) , glm : : vec3 ( rColour , gColour , bColour ) , intensity ) ) ;
2015-02-13 12:46:41 +00:00
2015-02-04 16:16:06 +00:00
//load Objects
std : : vector < std : : vector < int > > objectIdentifiers = std : : vector < std : : vector < int > > ( ) ; //The first entry is the index in objects, the second one the index in physicObjects, the others are idGreen, idBlue and objectNum.
XMLDocument * compositions = new XMLDocument ( ) ;
2015-02-06 17:00:14 +00:00
const char * compositionsFile = compositionsPath . c_str ( ) ;
2015-02-04 16:16:06 +00:00
compositions - > LoadFile ( compositionsFile ) ;
if ( compositions - > ErrorID ( ) ! = 0 ) {
printf ( " Could not open Compositions! \n " ) ;
exit ( - 1 ) ;
}
//iterate over all compositions in Level.xml
XMLElement * thisComposition = doc - > FirstChildElement ( " composition " ) ;
for ( ; thisComposition ; thisComposition = thisComposition - > NextSiblingElement ( " composition " ) ) {
2015-02-18 20:20:29 +00:00
int thisType = queryInt ( thisComposition , " typeID " ) ;
2015-02-04 16:16:06 +00:00
//iterate over all compositions in Compositions.xml to find the one corresponding to the current composition
XMLElement * composition = compositions - > FirstChildElement ( " composition " ) ;
2015-03-04 17:04:17 +00:00
bool typeExists = false ;
2015-02-04 16:16:06 +00:00
for ( ; composition ; composition = composition - > NextSiblingElement ( " composition " ) ) {
2015-02-18 20:20:29 +00:00
int compositionType = queryInt ( composition , " typeID " ) ;
2015-02-04 16:16:06 +00:00
//corect composition found
if ( thisType = = compositionType ) {
2015-04-25 20:29:37 +00:00
// update loading screen
glfwPollEvents ( ) ;
graphics - > render ( 0.0f ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , 0 ) ;
glfwSwapBuffers ( window ) ;
2015-03-04 17:04:17 +00:00
typeExists = true ;
2015-02-04 16:16:06 +00:00
//iterate over all objects of the composition
XMLElement * xmlObject = composition - > FirstChildElement ( " object " ) ;
int objectNum = 0 ;
for ( ; xmlObject ; xmlObject = xmlObject - > NextSiblingElement ( " object " ) ) {
2015-02-18 20:20:29 +00:00
std : : string modelPath = queryString ( xmlObject , " modelPath " ) ;
float objectScale = queryFloat ( xmlObject , " scale " ) ;
float compScale = queryFloat ( thisComposition , " scale " ) ;
2015-02-04 16:16:06 +00:00
//find the objectData for the current object
XMLElement * objectData = compositions - > FirstChildElement ( " objectData " ) ;
for ( ; objectData ; objectData = objectData - > NextSiblingElement ( " objectData " ) ) {
2015-02-18 20:20:29 +00:00
std : : string dataModelPath = queryString ( objectData , " modelPath " ) ;
2015-02-04 16:16:06 +00:00
//objectData found
if ( dataModelPath . compare ( modelPath ) = = 0 ) {
2015-02-18 20:20:29 +00:00
bool renderable = queryBool ( objectData , " renderable " ) ;
2015-02-04 16:16:06 +00:00
//create the object
2015-02-07 18:09:49 +00:00
Material material ;
Model model ;
if ( renderable ) {
2015-02-18 20:20:29 +00:00
float ambientFactor = queryFloat ( objectData , " ambientFactor " ) ;
float diffuseFactor = queryFloat ( objectData , " diffuseFactor " ) ;
float specularFactor = queryFloat ( objectData , " specularFactor " ) ;
float shininess = queryFloat ( objectData , " shininess " ) ;
std : : string texturePath = queryString ( objectData , " texturePath " ) ;
2015-03-03 17:45:02 +00:00
std : : string entireTexturePath = " ../ " + globalTexturePath + texturePath ;
if ( stat ( entireTexturePath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The texture file " < < entireTexturePath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
2015-02-07 18:09:49 +00:00
material = Material ( texturePath , ambientFactor , diffuseFactor , specularFactor , shininess ) ;
2015-03-03 17:45:02 +00:00
std : : string entireModelPath = " ../ " + globalGeometryPath + modelPath ;
if ( stat ( entireModelPath . c_str ( ) , & buf ) ! = 0 ) {
std : : cout < < " The object file " < < entireModelPath < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
2015-02-07 18:09:49 +00:00
model = Model ( modelPath , objectScale * compScale ) ;
2015-02-04 16:16:06 +00:00
}
2015-02-18 20:20:29 +00:00
float compXPos = queryFloat ( thisComposition , " xPos " ) ;
float compYOffset = queryFloat ( thisComposition , " yOffset " ) ;
float compZPos = queryFloat ( thisComposition , " zPos " ) ;
2015-02-04 16:16:06 +00:00
glm : : vec3 objectOffset , compRot ;
2015-02-18 20:20:29 +00:00
objectOffset [ 0 ] = queryFloat ( xmlObject , " xOffset " ) ;
objectOffset [ 1 ] = queryFloat ( xmlObject , " yOffset " ) ;
objectOffset [ 2 ] = queryFloat ( xmlObject , " zOffset " ) ;
compRot [ 0 ] = queryFloat ( thisComposition , " xRot " ) ;
compRot [ 1 ] = queryFloat ( thisComposition , " yRot " ) ;
compRot [ 2 ] = queryFloat ( thisComposition , " zRot " ) ;
2015-02-04 16:16:06 +00:00
compRot * = 0.0174532925 ; //transform degrees to radians
2015-02-18 20:20:29 +00:00
bool ignoreHeightmap = queryBool ( composition , " ignoreHeightmap " ) ;
2015-02-04 16:16:06 +00:00
if ( ! ignoreHeightmap ) {
compYOffset = compYOffset + level - > getTerrain ( ) - > getHeightmap ( ) [ int ( compXPos - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapHeight ( ) ) ]
[ int ( compZPos - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapWidth ( ) ) ] ;
}
glm : : vec3 compPos = glm : : vec3 ( compXPos , compYOffset , compZPos ) ;
objectOffset = objectOffset * compScale ;
glm : : vec4 rotatedObjectOffset = glm : : rotate ( compRot . x , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) )
* glm : : rotate ( compRot . y , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) )
* glm : : rotate ( compRot . z , glm : : vec3 ( 0.0f , 0.0f , 1.0f ) )
* glm : : vec4 ( objectOffset , 0 ) ;
glm : : vec3 objectPosition = compPos + glm : : vec3 ( rotatedObjectOffset . x , rotatedObjectOffset . y , rotatedObjectOffset . z ) ;
glm : : vec3 objectRot ;
2015-02-18 20:20:29 +00:00
objectRot [ 0 ] = queryFloat ( xmlObject , " xRot " ) ;
objectRot [ 1 ] = queryFloat ( xmlObject , " yRot " ) ;
objectRot [ 2 ] = queryFloat ( xmlObject , " zRot " ) ;
2015-02-04 16:16:06 +00:00
objectRot * = 0.0174532925 ; //transform degrees to radians
2015-02-07 18:09:49 +00:00
Object * object = new Object ( model , material , objectPosition , compRot + objectRot , renderable ) ;
2015-03-18 07:54:38 +00:00
bool crossesChunks = queryBool ( composition , " crossesChunks " ) ;
level - > addObject ( object , crossesChunks ) ;
2015-02-13 12:46:41 +00:00
2015-02-04 16:16:06 +00:00
//add object to physics
2015-02-18 20:20:29 +00:00
std : : string physicType = queryString ( objectData , " physicType " ) ;
float mass = queryFloat ( xmlObject , " mass " ) ;
2015-02-04 16:16:06 +00:00
XMLElement * constraint = thisComposition - > FirstChildElement ( " positionConstraint " ) ;
bool rotate = ( constraint = = NULL ) ;
2015-02-24 09:50:11 +00:00
float dampningL = 0.0f , dampningA = 0.0f ;
2015-02-18 20:20:29 +00:00
if ( physicType . compare ( " None " ) ! = 0 ) {
dampningL = queryFloat ( objectData , " dampningL " ) ;
dampningA = queryFloat ( objectData , " dampningA " ) ;
}
2015-02-04 16:16:06 +00:00
if ( physicType . compare ( " Player " ) = = 0 ) {
2015-02-18 20:20:29 +00:00
float radius = queryFloat ( objectData , " radius " ) ;
2015-02-04 16:16:06 +00:00
radius * = objectScale * compScale ;
2015-03-16 15:05:36 +00:00
if ( mass ! = 0.0f ) {
level - > addPhysicsObject ( object ) ;
}
2015-02-04 16:16:06 +00:00
level - > getPhysics ( ) - > addPlayer ( friction , radius , * object , mass , dampningL , dampningA , level - > getPhysicsObjectsVectorSize ( ) ) ;
} else if ( physicType . compare ( " Box " ) = = 0 ) {
2015-02-18 20:20:29 +00:00
float width = queryFloat ( objectData , " width " ) ;
float height = queryFloat ( objectData , " height " ) ;
float length = queryFloat ( objectData , " length " ) ;
2015-02-04 16:16:06 +00:00
width * = objectScale * compScale ;
height * = objectScale * compScale ;
length * = objectScale * compScale ;
2015-03-16 15:05:36 +00:00
if ( mass ! = 0.0f ) {
level - > addPhysicsObject ( object ) ;
}
2015-02-04 16:16:06 +00:00
level - > getPhysics ( ) - > addBox ( width , height , length , * object , mass , dampningL , dampningA , level - > getPhysicsObjectsVectorSize ( ) , rotate ) ;
} else if ( physicType . compare ( " Button " ) = = 0 ) {
2015-02-18 20:20:29 +00:00
float width = queryFloat ( objectData , " width " ) ;
float height = queryFloat ( objectData , " height " ) ;
float length = queryFloat ( objectData , " length " ) ;
2015-02-04 16:16:06 +00:00
width * = objectScale * compScale ;
height * = objectScale * compScale ;
length * = objectScale * compScale ;
2015-03-16 15:05:36 +00:00
if ( mass ! = 0.0f ) {
level - > addPhysicsObject ( object ) ;
}
2015-02-04 16:16:06 +00:00
level - > getPhysics ( ) - > addButton ( width , height , length , * object , mass , dampningL , dampningA , level - > getPhysicsObjectsVectorSize ( ) , rotate ) ;
} else if ( physicType . compare ( " TriangleMesh " ) = = 0 ) {
2015-03-16 15:05:36 +00:00
if ( mass ! = 0.0f ) {
level - > addPhysicsObject ( object ) ;
}
2015-02-04 16:16:06 +00:00
level - > getPhysics ( ) - > addTriangleMeshBody ( * object , modelPath , mass , dampningL , dampningA , level - > getPhysicsObjectsVectorSize ( ) , objectScale * compScale , rotate ) ;
2015-02-07 18:23:27 +00:00
} else if ( physicType . compare ( " None " ) = = 0 ) {
2015-02-04 16:16:06 +00:00
} else {
printf ( " XMLError: Not a valid physicType. \n " ) ;
exit ( - 1 ) ;
}
2015-02-13 12:46:41 +00:00
2015-02-07 18:23:27 +00:00
//create an identifier for this object
std : : vector < int > objectIdentifier = std : : vector < int > ( 5 ) ;
2015-03-16 15:58:50 +00:00
objectIdentifier [ 0 ] = level - > getAllObjects ( ) - > size ( ) - 1 ;
2015-02-07 18:23:27 +00:00
if ( physicType . compare ( " None " ) = = 0 ) {
objectIdentifier [ 1 ] = 0 ;
} else {
objectIdentifier [ 1 ] = level - > getPhysicsObjectsVectorSize ( ) - 1 ;
}
2015-02-18 20:20:29 +00:00
int idGreen = queryInt ( thisComposition , " idGreen " ) ;
int idBlue = queryInt ( thisComposition , " idBlue " ) ;
2015-02-07 18:23:27 +00:00
objectIdentifier [ 2 ] = idGreen ;
objectIdentifier [ 3 ] = idBlue ;
objectIdentifier [ 4 ] = objectNum ;
objectIdentifiers . push_back ( objectIdentifier ) ;
2015-02-04 16:16:06 +00:00
if ( compositionType = = 20 ) {
level - > setCameraCenter ( object ) ;
2015-03-04 14:52:28 +00:00
level - > setPlayerIndex ( objectIdentifier [ 1 ] ) ;
2015-02-04 16:16:06 +00:00
}
} //objectData found
} //finding the objectData
objectNum = objectNum + 1 ;
} //iterating over all objects of the composition
2015-02-13 12:46:41 +00:00
2015-02-04 16:16:06 +00:00
//iterate over all lights of the composition
XMLElement * xmlLight = composition - > FirstChildElement ( " light " ) ;
for ( ; xmlLight ; xmlLight = xmlLight - > NextSiblingElement ( " light " ) ) {
glm : : vec3 compRot , lightOffset , lightColour ;
2015-02-18 20:20:29 +00:00
compRot [ 0 ] = queryFloat ( thisComposition , " xRot " ) ;
compRot [ 1 ] = queryFloat ( thisComposition , " yRot " ) ;
compRot [ 2 ] = queryFloat ( thisComposition , " zRot " ) ;
2015-03-15 20:11:56 +00:00
compRot * = 0.0174532925 ; //transform degrees to radians
2015-02-18 20:20:29 +00:00
lightOffset [ 0 ] = queryFloat ( xmlLight , " xOffset " ) ;
lightOffset [ 1 ] = queryFloat ( xmlLight , " yOffset " ) ;
lightOffset [ 2 ] = queryFloat ( xmlLight , " zOffset " ) ;
lightColour [ 0 ] = queryFloat ( xmlLight , " rColour " ) ;
lightColour [ 1 ] = queryFloat ( xmlLight , " gColour " ) ;
lightColour [ 2 ] = queryFloat ( xmlLight , " bColour " ) ;
float compScale = queryFloat ( thisComposition , " scale " ) ;
float compXPos = queryFloat ( thisComposition , " xPos " ) ;
float compYOffset = queryFloat ( thisComposition , " yOffset " ) ;
float compZPos = queryFloat ( thisComposition , " zPos " ) ;
float lightIntensity = queryFloat ( xmlLight , " intensity " ) ;
2015-03-15 19:12:55 +00:00
bool ignoreHeightmap = queryBool ( composition , " ignoreHeightmap " ) ;
if ( ! ignoreHeightmap ) {
2015-03-15 20:11:56 +00:00
compYOffset = compYOffset + level - > getTerrain ( ) - > getHeightmap ( ) [ int ( compXPos - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapHeight ( ) ) ]
[ int ( compZPos - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapWidth ( ) ) ] ;
2015-03-15 19:12:55 +00:00
}
2015-03-15 20:11:56 +00:00
glm : : vec3 compPos = glm : : vec3 ( compXPos , compYOffset , compZPos ) ;
2015-02-04 16:16:06 +00:00
lightOffset = lightOffset * compScale ;
glm : : vec4 rotatedLightOffset = glm : : rotate ( compRot . x , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) )
* glm : : rotate ( compRot . y , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) )
* glm : : rotate ( compRot . z , glm : : vec3 ( 0.0f , 0.0f , 1.0f ) )
* glm : : vec4 ( lightOffset , 0 ) ;
glm : : vec3 lightPosition = compPos + glm : : vec3 ( rotatedLightOffset . x , rotatedLightOffset . y , rotatedLightOffset . z ) ;
2015-02-17 11:54:21 +00:00
XMLElement * flameOffset = NULL ;
flameOffset = xmlLight - > FirstChildElement ( " flameOffset " ) ;
if ( flameOffset ! = NULL ) {
float offset = 0 ;
errorCheck ( flameOffset - > QueryFloatText ( & offset ) ) ;
2015-03-07 21:48:44 +00:00
float flameHeight = queryFloat ( xmlLight , " flameHeight " ) ;
float flameWidth = queryFloat ( xmlLight , " flameWidth " ) ;
2015-03-04 14:22:07 +00:00
Light light = Light ( lightPosition , lightColour , lightIntensity , offset , flameHeight , flameWidth ) ;
2015-02-24 11:56:40 +00:00
level - > addLight ( light ) ;
}
else {
Light light = Light ( lightPosition , lightColour , lightIntensity ) ;
level - > addLight ( light ) ;
2015-02-17 11:54:21 +00:00
}
2015-02-04 16:16:06 +00:00
} //iterating over all lights of the composition
} //corect composition found
} //iterating over all compositions in Compositions.xml
2015-03-04 17:04:17 +00:00
if ( ! typeExists ) {
std : : cout < < " The typeID " < < thisType < < " exists in the level xml but is not a Compositions. " < < std : : endl ;
}
2015-02-04 16:16:06 +00:00
} //iterating over all compositions in Level.xml
//load triggers
2015-02-15 18:32:19 +00:00
2015-02-18 20:20:29 +00:00
// call init.lua to initialize the module load path in Lua
2015-02-15 18:32:19 +00:00
std : : string initLuaPath = scriptPath + " init.lua " ;
luaL_dofile ( level - > getLuaState ( ) , initLuaPath . c_str ( ) ) ;
2015-02-04 16:16:06 +00:00
XMLElement * composition = doc - > FirstChildElement ( " composition " ) ;
for ( ; composition ; composition = composition - > NextSiblingElement ( " composition " ) ) {
XMLElement * xmlTrigger = composition - > FirstChildElement ( " trigger " ) ;
for ( ; xmlTrigger ; xmlTrigger = xmlTrigger - > NextSiblingElement ( " trigger " ) ) {
2015-02-18 20:20:29 +00:00
std : : string name = queryString ( xmlTrigger , " name " ) ;
2015-02-04 16:16:06 +00:00
if ( name . compare ( " - " ) ! = 0 ) {
2015-02-18 20:20:29 +00:00
float xPos = queryFloat ( xmlTrigger , " xPosition " ) ;
float yPos = queryFloat ( xmlTrigger , " yPosition " ) ;
float zPos = queryFloat ( xmlTrigger , " zPosition " ) ;
2015-03-04 13:35:14 +00:00
bool undo = queryBool ( xmlTrigger , " undo " ) ;
2015-02-04 16:16:06 +00:00
glm : : vec3 position = glm : : vec3 ( xPos , yPos , zPos ) ;
2015-02-18 20:20:29 +00:00
std : : string stringTarget = queryString ( xmlTrigger , " targetIdGreen " ) ;
2015-02-04 16:16:06 +00:00
if ( stringTarget . compare ( " - " ) ! = 0 ) {
2015-02-18 20:20:29 +00:00
int targetIdGreen = queryInt ( xmlTrigger , " targetIdGreen " ) ;
int targetIdBlue = queryInt ( xmlTrigger , " targetIdBlue " ) ;
2015-02-04 16:16:06 +00:00
XMLElement * thisComposition = doc - > FirstChildElement ( " composition " ) ;
for ( ; thisComposition ; thisComposition = thisComposition - > NextSiblingElement ( " composition " ) ) {
2015-02-18 20:20:29 +00:00
int thisIdGreen = queryInt ( thisComposition , " idGreen " ) ;
int thisIdBlue = queryInt ( thisComposition , " idBlue " ) ;
2015-02-04 16:16:06 +00:00
if ( targetIdGreen = = thisIdGreen & & targetIdBlue = = thisIdBlue ) {
glm : : vec3 targetPosition ;
2015-02-18 20:20:29 +00:00
targetPosition [ 0 ] = queryFloat ( thisComposition , " xPos " ) ;
targetPosition [ 1 ] = queryFloat ( thisComposition , " yOffset " ) ;
targetPosition [ 2 ] = queryFloat ( thisComposition , " zPos " ) ;
2015-02-04 16:16:06 +00:00
targetPosition [ 1 ] + = level - > getTerrain ( ) - > getHeightmap ( ) [ int ( targetPosition [ 0 ] - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapHeight ( ) ) ]
[ int ( targetPosition [ 2 ] - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapWidth ( ) ) ] ;
position + = targetPosition ;
}
}
}
2015-02-18 20:20:29 +00:00
float distance = queryFloat ( xmlTrigger , " distance " ) ;
bool isBigger = queryBool ( xmlTrigger , " isBiggerThan " ) ;
int idGreen = queryInt ( composition , " idGreen " ) ;
int idBlue = queryInt ( composition , " idBlue " ) ;
int objectNum = queryInt ( xmlTrigger , " objectNum " ) ;
2015-02-04 16:16:06 +00:00
Object * object = 0 ;
bool ok = false ;
for ( unsigned int i = 0 ; i < objectIdentifiers . size ( ) ; i + + ) {
if ( objectIdentifiers [ i ] [ 2 ] = = idGreen & & objectIdentifiers [ i ] [ 3 ] = = idBlue & & objectIdentifiers [ i ] [ 4 ] = = objectNum ) {
object = level - > getPhysicsObjects ( ) - > at ( objectIdentifiers [ i ] [ 1 ] ) ; //Index in physics objects
if ( ok ) {
printf ( " 2 objects have the same ID while loading triggers. " ) ;
exit ( - 1 ) ;
}
ok = true ;
}
}
if ( ! ok ) {
printf ( " No index found for a trigger object while loading triggers. " ) ;
exit ( - 1 ) ;
}
2015-02-18 20:20:29 +00:00
std : : string luaScript = queryString ( xmlTrigger , " luaScript " ) ;
2015-02-04 16:16:06 +00:00
2015-02-18 20:20:29 +00:00
int toChangeIdGreen = queryInt ( xmlTrigger , " toChangeIdGreen " ) ;
int toChangeIdBlue = queryInt ( xmlTrigger , " toChangeIdBlue " ) ;
int toChangeObjNum = queryInt ( xmlTrigger , " toChangeObjNum " ) ;
int objectToChange = - 1 ;
2015-02-04 16:16:06 +00:00
for ( unsigned int i = 0 ; i < objectIdentifiers . size ( ) ; i + + ) {
if ( objectIdentifiers [ i ] [ 2 ] = = toChangeIdGreen & & objectIdentifiers [ i ] [ 3 ] = = toChangeIdBlue & & objectIdentifiers [ i ] [ 4 ] = = toChangeObjNum ) {
objectToChange = objectIdentifiers [ i ] [ 1 ] ; //Index in physic objects
}
}
2015-02-18 20:20:29 +00:00
if ( objectToChange = = - 1 ) {
printf ( " No Identifier found for an object that was to be changed by a trigger. \n " ) ;
}
2015-02-04 16:16:06 +00:00
if ( object ! = 0 ) {
2015-03-04 13:35:14 +00:00
Trigger trigger = Trigger ( position , distance , isBigger , object , luaScript , level - > getLuaState ( ) , objectToChange , scriptPath , undo ) ;
2015-02-04 16:16:06 +00:00
level - > addTrigger ( trigger ) ;
}
else {
printf ( " Triggering object not found. \n " ) ;
exit ( - 1 ) ;
}
}
}
} //triggers
//load positionConstraints
composition = doc - > FirstChildElement ( " composition " ) ;
for ( ; composition ; composition = composition - > NextSiblingElement ( " composition " ) ) {
XMLElement * positionConstraint = composition - > FirstChildElement ( " positionConstraint " ) ;
for ( ; positionConstraint ; positionConstraint = positionConstraint - > NextSiblingElement ( " positionConstraint " ) ) {
2015-02-18 20:20:29 +00:00
float xPos = queryFloat ( positionConstraint , " xPosition " ) ;
float yPos = queryFloat ( positionConstraint , " yPosition " ) ;
float zPos = queryFloat ( positionConstraint , " zPosition " ) ;
float strength = queryFloat ( positionConstraint , " strength " ) ;
int objectNum = queryInt ( positionConstraint , " objectNum " ) ;
int idGreen = queryInt ( composition , " idGreen " ) ;
int idBlue = queryInt ( composition , " idBlue " ) ;
2015-02-24 09:50:11 +00:00
int objectIndex = 0 ;
2015-02-04 16:16:06 +00:00
bool ok = false ;
for ( unsigned int i = 0 ; i < objectIdentifiers . size ( ) ; i + + ) {
if ( objectIdentifiers [ i ] [ 2 ] = = idGreen & & objectIdentifiers [ i ] [ 3 ] = = idBlue & & objectIdentifiers [ i ] [ 4 ] = = objectNum ) {
if ( ok ) {
printf ( " 2 objects have the same ID while loading constraints. " ) ;
exit ( - 1 ) ;
}
2015-02-18 20:20:29 +00:00
objectIndex = objectIdentifiers [ i ] [ 1 ] ; //Index in physic objects
2015-02-04 16:16:06 +00:00
ok = true ;
}
}
if ( ! ok ) {
printf ( " No index found for a trigger object while loading constraints. " ) ;
exit ( - 1 ) ;
}
glm : : vec3 position = glm : : vec3 ( xPos , yPos , zPos ) ;
level - > getPhysics ( ) - > addPositionConstraint ( objectIndex , strength , position ) ;
}
} //positionConstraints
}
2015-02-13 16:14:29 +00:00
glm : : vec3 Loader : : reloadPlayerPosition ( std : : string filePath , Level * level ) {
XMLDocument * doc = new XMLDocument ( ) ;
const char * xmlFile = filePath . c_str ( ) ;
doc - > LoadFile ( xmlFile ) ;
if ( doc - > ErrorID ( ) ! = 0 ) {
printf ( " Could not open ObjectSetupXml! \n " ) ;
exit ( - 1 ) ;
}
//iterate over all compositions in Level.xml
XMLElement * thisComposition = doc - > FirstChildElement ( " composition " ) ;
for ( ; thisComposition ; thisComposition = thisComposition - > NextSiblingElement ( " composition " ) ) {
2015-02-18 20:20:29 +00:00
int thisType = queryInt ( thisComposition , " typeID " ) ;
2015-02-13 16:14:29 +00:00
if ( thisType = = 20 ) {
2015-02-18 20:20:29 +00:00
float compXPos = queryFloat ( thisComposition , " xPos " ) ;
float compYOffset = queryFloat ( thisComposition , " yOffset " ) ;
float compZPos = queryFloat ( thisComposition , " zPos " ) ;
2015-02-13 16:14:29 +00:00
compYOffset + = level - > getTerrain ( ) - > getHeightmap ( ) [ int ( compXPos - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapHeight ( ) ) ]
[ int ( compZPos - 0.5 + 0.5 * level - > getTerrain ( ) - > getHeightmapWidth ( ) ) ] ;
glm : : vec3 position = glm : : vec3 ( compXPos , compYOffset , compZPos ) ;
return position ;
}
}
printf ( " Level.xml contains no player. " ) ;
exit ( - 1 ) ;
}
2015-02-18 19:10:30 +00:00
float Loader : : queryFloat ( XMLElement * element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
float ret ;
errorCheck ( attributeElement - > QueryFloatText ( & ret ) ) ;
return ret ;
}
float Loader : : queryFloat ( XMLDocument * & element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
float ret ;
errorCheck ( attributeElement - > QueryFloatText ( & ret ) ) ;
return ret ;
}
2015-02-13 16:14:29 +00:00
2015-02-18 19:10:30 +00:00
int Loader : : queryInt ( XMLElement * element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
int ret ;
errorCheck ( attributeElement - > QueryIntText ( & ret ) ) ;
return ret ;
}
int Loader : : queryInt ( XMLDocument * & element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
int ret ;
errorCheck ( attributeElement - > QueryIntText ( & ret ) ) ;
return ret ;
}
bool Loader : : queryBool ( XMLElement * element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
bool ret ;
errorCheck ( attributeElement - > QueryBoolText ( & ret ) ) ;
return ret ;
}
bool Loader : : queryBool ( XMLDocument * & element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
bool ret ;
errorCheck ( attributeElement - > QueryBoolText ( & ret ) ) ;
return ret ;
}
std : : string Loader : : queryString ( XMLElement * element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
const char * charRet = attributeElement - > GetText ( ) ;
if ( charRet = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " could not be loaded. " < < std : : endl ;
exit ( - 1 ) ;
}
std : : string ret = charRet ;
return ret ;
}
std : : string Loader : : queryString ( XMLDocument * & element , const char * attribute ) {
XMLElement * attributeElement = element - > FirstChildElement ( attribute ) ;
if ( attributeElement = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " does not exist. " < < std : : endl ;
exit ( - 1 ) ;
}
const char * charRet = attributeElement - > GetText ( ) ;
if ( charRet = = NULL ) {
std : : cout < < " XMLError: Attribute " < < attribute < < " could not be loaded. " < < std : : endl ;
exit ( - 1 ) ;
}
std : : string ret = charRet ;
return ret ;
}
2015-02-13 16:14:29 +00:00
2015-02-04 16:16:06 +00:00
void Loader : : errorCheck ( XMLError error ) {
if ( error ) {
printf ( " XMLError: " ) ;
if ( error = = XML_WRONG_ATTRIBUTE_TYPE ) {
printf ( " Wrong attribute type. \n " ) ;
}
else if ( error = = XML_NO_ATTRIBUTE ) {
printf ( " No attribute. \n " ) ;
}
else if ( error = = XML_CAN_NOT_CONVERT_TEXT ) {
printf ( " Can not convert text. \n " ) ;
}
else if ( error = = XML_NO_TEXT_NODE ) {
printf ( " No text. \n " ) ;
}
else {
printf ( " Unknown error. \n " ) ;
}
exit ( - 1 ) ;
}
}