diff options
Diffstat (limited to 'Level.cpp')
-rw-r--r-- | Level.cpp | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/Level.cpp b/Level.cpp new file mode 100644 index 0000000..2867ab9 --- /dev/null +++ b/Level.cpp @@ -0,0 +1,260 @@ +/* + * Level.cpp + * + * Copyright (C) 2009 Matthias Schiffer <matthias@gamezock.de> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Level.h" +#include "Texture.h" +#include <libxml/tree.h> +#include <libxml/valid.h> + +#include <iostream> + +namespace Zoom { + +boost::shared_ptr<Level> Level::loadLevel(const std::string &filename) { + boost::shared_ptr<Level> 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<Level>(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<Room>::iterator room = level->rooms.begin(); room != level->rooms.end(); ++room) { + for(std::list<BSPTree::TriangleRecord>::iterator wall = room->walls.begin(); wall != room->walls.end(); ++wall) { + boost::shared_ptr<WallData> wallData = boost::dynamic_pointer_cast<WallData>(wall->data); + + if(!wallData->texture.empty()) { + std::map<std::string, unsigned>::iterator texture = level->textures.find(wallData->texture); + + if(texture != level->textures.end()) + wall->triangle.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)); + } + } + } + } +} + +BSPTree::TriangleRecord Level::loadWall(xmlNodePtr wallNode) { + boost::shared_ptr<WallData> wallData(new WallData); + + BSPTree::TriangleRecord wall; + wall.data = 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.triangle.setVertex(vertexNum, loadVector(node)); + } + else if(!xmlStrcmp(node->name, (xmlChar*)"normal")) { + if(vertexNum < 0) + continue; + + wall.triangle.setNormal(vertexNum, loadVector(node)); + } + else if(!xmlStrcmp(node->name, (xmlChar*)"texcoords")) { + if(vertexNum < 0) continue; + + data = xmlGetProp(node, (xmlChar*)"s"); + if(data) { + wall.triangle.getTexCoords(vertexNum)[0] = atof((char*)data); + xmlFree(data); + } + + data = xmlGetProp(node, (xmlChar*)"t"); + if(data) { + wall.triangle.getTexCoords(vertexNum)[1] = atof((char*)data); + xmlFree(data); + } + } + } + + vmml::vec3f normal = wall.triangle.computeNormal(); + + if(normal.squared_length() > 0) { + normal.normalize(); + + for(int i = 0; i < 3; ++i) { + if(wall.triangle.getNormal(i).squared_length() == 0) + wall.triangle.setNormal(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; +} + +} |