This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
neofx-zoom-plusplus/Level.cpp
2009-12-14 13:54:34 +01:00

260 lines
6.7 KiB
C++

/*
* 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;
}
}