/* * Level.cpp * * Copyright (C) 2009 Matthias Schiffer * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this program. If not, see . */ #include "Level.h" #include "Texture.h" #include #include #include namespace Zoom { boost::shared_ptr Level::loadLevel(const std::string &filename) { boost::shared_ptr level; xmlDocPtr doc = xmlParseFile(("levels/"+filename).c_str()); if(doc) { if(validateLevel(doc)) { xmlNodePtr root = xmlDocGetRootElement(doc); if(root && !xmlStrcmp(root->name, (xmlChar*)"level")) { level = boost::shared_ptr(new Level); for(xmlNodePtr node = root->children; node != 0; node = node->next) { if(node->type != XML_ELEMENT_NODE) continue; if(!xmlStrcmp(node->name, (xmlChar*)"info")) { level->loadLevelInfo(node); } else if(!xmlStrcmp(node->name, (xmlChar*)"rooms")) { level->loadRooms(node); } else if(!xmlStrcmp(node->name, (xmlChar*)"textures")) { level->loadTextures(node); } } for(std::list::iterator room = level->rooms.begin(); room != level->rooms.end(); ++room) { for(std::list::iterator wall = room->walls.begin(); wall != room->walls.end(); ++wall) { boost::shared_ptr wallData = boost::dynamic_pointer_cast(wall->getData()); if(!wallData->texture.empty()) { std::map::iterator texture = level->textures.find(wallData->texture); if(texture != level->textures.end()) wall->getTriangle().setTexture(texture->second); } } } } } xmlFreeDoc(doc); } xmlCleanupParser(); return level; } void Level::loadLevelInfo(xmlNodePtr infoNode) { for(xmlNodePtr node = infoNode->children; node != 0; node = node->next) { if(node->type != XML_ELEMENT_NODE) continue; if(!xmlStrcmp(node->name, (xmlChar*)"name")) { xmlChar *data = xmlNodeGetContent(node); name = (char*)data; xmlFree(data); } else if(!xmlStrcmp(node->name, (xmlChar*)"desc")) { xmlChar *data = xmlNodeGetContent(node); description = (char*)data; xmlFree(data); } else if(!xmlStrcmp(node->name, (xmlChar*)"start")) { startPoint = loadVector(node); } } } void Level::loadRooms(xmlNodePtr roomsNode) { for(xmlNodePtr node = roomsNode->children; node != 0; node = node->next) { if(node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, (xmlChar*)"room")) { loadRoom(node); } } } void Level::loadRoom(xmlNodePtr roomNode) { rooms.push_back(Room()); xmlChar *data = xmlGetProp(roomNode, (xmlChar*)"id"); if(data) { rooms.back().id = (char*)data; xmlFree(data); } for(xmlNodePtr node = roomNode->children; node != 0; node = node->next) { if(node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, (xmlChar*)"triangle")) { rooms.back().walls.push_back(loadWall(node)); } } } void Level::loadTextures(xmlNodePtr texturesNode) { for(xmlNodePtr node = texturesNode->children; node != 0; node = node->next) { if(node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, (xmlChar*)"texture")) { std::string id, name; xmlChar *data = xmlGetProp(node, (xmlChar*)"id"); if(data) { id = (char*)data; xmlFree(data); } data = xmlGetProp(node, (xmlChar*)"name"); if(data) { name = (char*)data; xmlFree(data); } if(!id.empty() && !name.empty()) { unsigned texture = Texture::loadTexture(name); if(texture) { textures.insert(std::make_pair(id, texture)); } } } } } TriangleRecord Level::loadWall(xmlNodePtr wallNode) { boost::shared_ptr wallData(new WallData); TriangleRecord wall; wall.setData(wallData); xmlChar *data = xmlGetProp(wallNode, (xmlChar*)"texture"); if(data) { wallData->texture = (char*)data; xmlFree(data); } int vertexNum = -1; for(xmlNodePtr node = wallNode->children; node != 0; node = node->next) { if(node->type != XML_ELEMENT_NODE) continue; if(!xmlStrcmp(node->name, (xmlChar*)"vertex")) { if(++vertexNum > 2) break; wall.getTriangle().setVertex(vertexNum, loadVector(node), false); } else if(!xmlStrcmp(node->name, (xmlChar*)"normal")) { if(vertexNum < 0) continue; wall.getTriangle().setVertexNormal(vertexNum, loadVector(node)); } else if(!xmlStrcmp(node->name, (xmlChar*)"texcoords")) { if(vertexNum < 0) continue; data = xmlGetProp(node, (xmlChar*)"s"); if(data) { wall.getTriangle().getTexCoords(vertexNum)[0] = atof((char*)data); xmlFree(data); } data = xmlGetProp(node, (xmlChar*)"t"); if(data) { wall.getTriangle().getTexCoords(vertexNum)[1] = atof((char*)data); xmlFree(data); } } } wall.getTriangle().updateNormal(); vmml::vec3f normal = wall.getTriangle().getNormal(); for(int i = 0; i < 3; ++i) { if(wall.getTriangle().getVertexNormal(i).squared_length() == 0) wall.getTriangle().setVertexNormal(i, normal); } return wall; } vmml::vec3f Level::loadVector(xmlNodePtr node) { vmml::vec3f ret(vmml::vec3f::ZERO); xmlChar *data = xmlGetProp(node, (xmlChar*)"x"); if(data) { ret.x() = atof((char*)data); xmlFree(data); } data = xmlGetProp(node, (xmlChar*)"y"); if(data) { ret.y() = atof((char*)data); xmlFree(data); } data = xmlGetProp(node, (xmlChar*)"z"); if(data) { ret.z() = atof((char*)data); xmlFree(data); } return ret; } bool Level::validateLevel(xmlDocPtr doc) { bool ret = false; xmlDtdPtr dtd = xmlParseDTD((xmlChar*)"-//libzoom//DTD level 0.1//EN", (xmlChar*)"levels/level.dtd"); if(dtd) { xmlValidCtxtPtr validCtxt = xmlNewValidCtxt(); if(validCtxt) { if(xmlValidateDtd(validCtxt, doc, dtd)) ret = true; xmlFreeValidCtxt(validCtxt); } xmlFreeDtd(dtd); } return ret; } }