summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias Schiffer <matthias@gamezock.de>2009-12-15 19:37:12 +0100
committerMatthias Schiffer <matthias@gamezock.de>2009-12-15 19:37:12 +0100
commitd9f44af7aee41a111a3d7427d8735bc821f1824f (patch)
tree4814f3dea17eefac6a06d0c6af0da31d87488ff6 /src
parenta4fa46a4fda967348ea18961c177330491bdb953 (diff)
downloadzoom++-d9f44af7aee41a111a3d7427d8735bc821f1824f.tar
zoom++-d9f44af7aee41a111a3d7427d8735bc821f1824f.zip
Moved source files to src; sort triangles by texture.
Diffstat (limited to 'src')
-rw-r--r--src/BSPTree.cpp164
-rw-r--r--src/BSPTree.h177
-rw-r--r--src/CMakeLists.txt13
-rw-r--r--src/Game.cpp129
-rw-r--r--src/Game.h53
-rw-r--r--src/Level.cpp260
-rw-r--r--src/Level.h74
-rw-r--r--src/Renderer.cpp69
-rw-r--r--src/Renderer.h78
-rw-r--r--src/Shader.cpp95
-rw-r--r--src/Shader.h41
-rw-r--r--src/Texture.cpp44
-rw-r--r--src/Texture.h40
-rw-r--r--src/Triangle.h105
-rw-r--r--src/config.h27
-rw-r--r--src/gl.h32
-rw-r--r--src/zoom.cpp398
17 files changed, 1799 insertions, 0 deletions
diff --git a/src/BSPTree.cpp b/src/BSPTree.cpp
new file mode 100644
index 0000000..a94c77a
--- /dev/null
+++ b/src/BSPTree.cpp
@@ -0,0 +1,164 @@
+/*
+ * BSPTree.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 "BSPTree.h"
+
+namespace Zoom {
+
+vmml::vec3f BSPTree::Plane::intersection(const vmml::vec3f &p, const vmml::vec3f &dir) const {
+ float r = (d - p.dot(normal))/dir.dot(normal);
+
+ return p + r*dir;
+}
+
+void BSPTree::Plane::partition(const TriangleRecord &t, std::list<TriangleRecord> *front, std::list<TriangleRecord> *back) const {
+ for(int i = 0; i < 3; ++i) {
+ if(contains(t.triangle.getVertex(i))) {
+ const vmml::vec3f *v[3] = {&t.triangle.getVertex(i), &t.triangle.getVertex((i+1)%3), &t.triangle.getVertex((i+2)%3)};
+
+ vmml::vec3f is = intersection(*v[1], *v[2]-*v[1]);
+
+ if(isInFront(*v[1])) {
+ front->push_back(TriangleRecord(Triangle(*v[0], *v[1], is, t.triangle.getColor()), t.data));
+ back->push_back(TriangleRecord(Triangle(*v[0], is, *v[2], t.triangle.getColor()), t.data));
+ }
+ else {
+ back->push_back(TriangleRecord(Triangle(*v[0], *v[1], is, t.triangle.getColor()), t.data));
+ front->push_back(TriangleRecord(Triangle(*v[0], is, *v[2], t.triangle.getColor()), t.data));
+ }
+
+ return;
+ }
+ }
+
+ for(int i = 0; i < 3; ++i) {
+ const vmml::vec3f *v[3] = {&t.triangle.getVertex(i), &t.triangle.getVertex((i+1)%3), &t.triangle.getVertex((i+2)%3)};
+
+ if((isInFront(*v[0]) && isBehind(*v[1]) && isBehind(*v[2]))
+ || (isBehind(*v[0]) && isInFront(*v[1]) && isInFront(*v[2]))) {
+ vmml::vec3f is1 = intersection(*v[0], *v[1]-*v[0]);
+ vmml::vec3f is2 = intersection(*v[0], *v[2]-*v[0]);
+
+ if(isInFront(*v[0])) {
+ front->push_back(TriangleRecord(Triangle(*v[0], is1, is2, t.triangle.getColor()), t.data));
+ back->push_back(TriangleRecord(Triangle(is1, *v[1], is2, t.triangle.getColor()), t.data));
+ back->push_back(TriangleRecord(Triangle(*v[1], *v[2], is2, t.triangle.getColor()), t.data));
+ }
+ else {
+ back->push_back(TriangleRecord(Triangle(*v[0], is1, is2, t.triangle.getColor()), t.data));
+ front->push_back(TriangleRecord(Triangle(is1, *v[1], is2, t.triangle.getColor()), t.data));
+ front->push_back(TriangleRecord(Triangle(*v[1], *v[2], is2, t.triangle.getColor()), t.data));
+ }
+
+ return;
+ }
+ }
+}
+
+
+BSPTree::BSPTree(const std::list<TriangleRecord> &triangles) : frontTree(0), backTree(0) {
+ const Triangle *planeT = findNearestTriangle(triangles, findCenter(triangles));
+
+ if(!planeT)
+ return;
+
+ plane = Plane(*planeT);
+
+ std::list<TriangleRecord> front, back;
+
+ for(std::list<TriangleRecord>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) {
+ if(t->triangle.isDegenerate())
+ continue;
+
+ if(plane.contains(t->triangle)) {
+ this->triangles.push_back(*t);
+ continue;
+ }
+ else if(plane.isInFront(t->triangle)) {
+ front.push_back(*t);
+ continue;
+ }
+ else if(plane.isBehind(t->triangle)) {
+ back.push_back(*t);
+ continue;
+ }
+
+ plane.partition(*t, &front, &back);
+ }
+
+ if(!front.empty())
+ frontTree = new BSPTree(front);
+
+ if(!back.empty())
+ backTree = new BSPTree(back);
+}
+
+BSPTree& BSPTree::operator=(const BSPTree &tree) {
+ if(frontTree) {
+ delete frontTree;
+ frontTree = 0;
+ }
+
+ if(backTree) {
+ delete backTree;
+ backTree = 0;
+ }
+
+ plane = tree.plane;
+ triangles = tree.triangles;
+
+ if(tree.frontTree)
+ frontTree = new BSPTree(*tree.frontTree);
+
+ if(tree.backTree)
+ backTree = new BSPTree(*tree.backTree);
+
+ return *this;
+}
+
+vmml::vec3f BSPTree::findCenter(const std::list<TriangleRecord> &triangles) {
+ vmml::vec3f v;
+
+ for(std::list<TriangleRecord>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) {
+ v += t->triangle.getCenter();
+ }
+
+ return v/triangles.size();
+}
+
+const Triangle* BSPTree::findNearestTriangle(const std::list<TriangleRecord> &triangles, const vmml::vec3f &v) {
+ const Triangle *current = 0;
+ float distanceSq;
+
+ for(std::list<TriangleRecord>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) {
+ if(t->triangle.isDegenerate())
+ continue;
+
+ float d = t->triangle.getCenter().squared_distance(v);
+
+ if(!current || d < distanceSq) {
+ current = &t->triangle;
+ distanceSq = d;
+ }
+ }
+
+ return current;
+}
+
+}
diff --git a/src/BSPTree.h b/src/BSPTree.h
new file mode 100644
index 0000000..1944ef1
--- /dev/null
+++ b/src/BSPTree.h
@@ -0,0 +1,177 @@
+/*
+ * BSPTree.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_BSPTREE_H_
+#define ZOOM_BSPTREE_H_
+
+#include "Triangle.h"
+#include <list>
+#include <cmath>
+
+#include <vmmlib/vector.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Zoom {
+
+class BSPTree {
+ public:
+ class TriangleData {
+ protected:
+ TriangleData() {}
+
+ public:
+ virtual ~TriangleData() {}
+ };
+
+ struct TriangleRecord {
+ public:
+ TriangleRecord(Triangle triangle0, boost::shared_ptr<TriangleData> data0)
+ : triangle(triangle0), data(data0) {}
+ TriangleRecord(Triangle triangle0) : triangle(triangle0) {}
+ TriangleRecord() {}
+
+ Triangle triangle;
+ boost::shared_ptr<TriangleData> data;
+ };
+
+ private:
+ class Plane {
+ public:
+ Plane() : d(0) {}
+ Plane(const vmml::vec3f &n, float d0) : normal(n), d(d0) {}
+ Plane(const Triangle &t) : normal(t.computeNormal()), d(t.getVertex(0).dot(normal)) {}
+
+ bool contains(const vmml::vec3f &v) const {
+ return (fabsf(normal.dot(v) - d) < 1E-6);
+ }
+
+ bool isBehind(const vmml::vec3f &v) const {
+ return (normal.dot(v) - d) < 0;
+ }
+
+ bool isInFront(const vmml::vec3f &v) const {
+ return (normal.dot(v) - d) > 0;
+ }
+
+
+ bool contains(const Triangle &t) const {
+ for(int i = 0; i < 3; ++i) {
+ if(!contains(t.getVertex(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isBehind(const Triangle &t) const {
+ for(int i = 0; i < 3; ++i) {
+ if(!isBehind(t.getVertex(i)) && !contains(t.getVertex(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isInFront(const Triangle &t) const {
+ for(int i = 0; i < 3; ++i) {
+ if(!isInFront(t.getVertex(i)) && !contains(t.getVertex(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+
+ const vmml::vec3f& getNormal() const {
+ return normal;
+ }
+
+ vmml::vec3f intersection(const vmml::vec3f &p, const vmml::vec3f &dir) const;
+ void partition(const TriangleRecord &t, std::list<TriangleRecord> *front, std::list<TriangleRecord> *back) const;
+
+ private:
+ vmml::vec3f normal;
+ float d;
+ };
+
+ public:
+ BSPTree(const std::list<TriangleRecord> &triangles);
+
+ BSPTree(const BSPTree &tree) : frontTree(0), backTree(0) {
+ *this = tree;
+ }
+
+ virtual ~BSPTree() {
+ if(frontTree)
+ delete frontTree;
+
+ if(backTree)
+ delete backTree;
+ }
+
+ BSPTree& operator=(const BSPTree &tree);
+
+ template<typename T>
+ void visit(const T& visitor, const vmml::vec3f &p) const {
+ doVisit<const T>(visitor, p);
+ }
+
+ template<typename T>
+ void visit(T& visitor, const vmml::vec3f &p) const {
+ doVisit(visitor, p);
+ }
+
+ private:
+ Plane plane;
+ std::list<TriangleRecord> triangles;
+ BSPTree *frontTree, *backTree;
+
+ template<typename T>
+ void doVisit(T& visitor, const vmml::vec3f &p) const {
+ if(plane.isBehind(p)) {
+ if(frontTree)
+ frontTree->visit(visitor, p);
+
+ for(std::list<TriangleRecord>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) {
+ visitor(*t);
+ }
+
+ if(backTree)
+ backTree->visit(visitor, p);
+ }
+ else {
+ if(backTree)
+ backTree->visit(visitor, p);
+
+ for(std::list<TriangleRecord>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) {
+ visitor(*t);
+ }
+
+ if(frontTree)
+ frontTree->visit(visitor, p);
+ }
+ }
+
+ static vmml::vec3f findCenter(const std::list<TriangleRecord> &triangles);
+ static const Triangle* findNearestTriangle(const std::list<TriangleRecord> &triangles, const vmml::vec3f &v);
+};
+
+}
+
+#endif /* ZOOM_BSPTREE_H_ */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..e1c9378
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_executable(zoom
+ BSPTree.cpp BSPTree.h
+ Game.cpp Game.h
+ config.h
+ gl.h
+ Level.cpp Level.h
+ Renderer.cpp Renderer.h
+ Shader.cpp Shader.h
+ Texture.cpp Texture.h
+ Triangle.h
+ zoom.cpp
+)
+target_link_libraries(zoom ${Boost_LIBRARIES} ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} ${GLPNG_LIBRARY} ${LIBXML2_LIBRARIES})
diff --git a/src/Game.cpp b/src/Game.cpp
new file mode 100644
index 0000000..c738080
--- /dev/null
+++ b/src/Game.cpp
@@ -0,0 +1,129 @@
+/*
+ * Game.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 "Game.h"
+#include "BSPTree.h"
+#include "Level.h"
+#include "Shader.h"
+#include "Triangle.h"
+#include "gl.h"
+#include <algorithm>
+
+namespace Zoom {
+
+Game::Game(bool multisample) : angle(0), lightPos(0) {
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClearDepth(1.0);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+
+ //glEnable(GL_BLEND);
+ //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+#ifndef _WIN32
+ if(multisample)
+ glEnable(GL_MULTISAMPLE_ARB);
+#endif
+
+ glShadeModel(GL_SMOOTH);
+
+ glEnable(GL_LIGHTING);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, vmml::vec4f::ZERO.array);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, vmml::vec4f::ONE.array);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, vmml::vec4f::ONE.array);
+ glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0);
+ glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.3);
+ glEnable(GL_LIGHT0);
+
+ glEnable(GL_COLOR_MATERIAL);
+ glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
+
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, vmml::vec4f::ONE.array);
+ glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 128);
+
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CCW);
+
+ Shader::loadProgram("default.vert", "default.frag");
+
+ loadLevel("level.xml");
+ triangles.insert(triangles.end(), level->getRooms().front().walls.begin(), level->getRooms().front().walls.end());
+ std::sort(triangles.begin(), triangles.end(), Renderer::TextureSorter());
+}
+
+bool Game::loadLevel(const std::string &name) {
+ level = Level::loadLevel(name);
+
+ return level;
+}
+
+void Game::run(int delta) {
+ angle += delta*0.01;
+
+ if(angle >= 360)
+ angle -= 360;
+
+ lightPos += delta;
+ lightPos %= 24000;
+}
+
+void Game::render() {
+ int i;
+ float light[] = {0, 0, 0, 1};
+
+ if(lightPos < 12000) i = lightPos;
+ else i = 24000 - lightPos;
+
+ if(i < 4000) {
+ light[0] = 0.0;
+ light[2] = -i * 0.001;
+ }
+ else if(i < 8000) {
+ light[0] = (i-4000) * 0.001;
+ light[2] = -4.0;
+ }
+ else if(i < 12000) {
+ light[0] = 4.0;
+ light[2] = -4.0 - (i-8000) * 0.001;
+ }
+ else {
+ light[0] = 4.0;
+ light[2] = -8.0;
+ }
+
+ /*std::vector<BSPTree::TriangleRecord> triangles(level->getRooms().front().walls.begin(),
+ level->getRooms().front().walls.end());
+ std::sort(triangles.begin(), triangles.end(), Renderer::TextureSorter());*/
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+ glRotatef(15, 1, 2, 1);
+ /*glRotatef(5*angle, 0, -1, 2);
+ glRotatef(7*angle, 2, -1, 0);
+ glRotatef(11*angle, 2, -1, 2);*/
+
+ glLightfv(GL_LIGHT0, GL_POSITION, light);
+ //renderer.render(std::list<BSPTree::TriangleRecord>(triangles.begin(), triangles.end()));
+ renderer.render(triangles);
+
+ glPopMatrix();
+}
+
+}
diff --git a/src/Game.h b/src/Game.h
new file mode 100644
index 0000000..0fe7233
--- /dev/null
+++ b/src/Game.h
@@ -0,0 +1,53 @@
+/*
+ * Game.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_GAME_H_
+#define ZOOM_GAME_H_
+
+#include "Renderer.h"
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace Zoom {
+
+class Level;
+class Triangle;
+
+class Game {
+ public:
+ Game(bool multisample);
+
+ bool loadLevel(const std::string &name);
+
+ void run(int delta);
+ void render();
+
+ private:
+ Renderer renderer;
+
+ boost::shared_ptr<Level> level;
+ std::vector<BSPTree::TriangleRecord> triangles;
+
+ float angle;
+ int lightPos;
+};
+
+}
+
+#endif /* ZOOM_GAME_H_ */
diff --git a/src/Level.cpp b/src/Level.cpp
new file mode 100644
index 0000000..2867ab9
--- /dev/null
+++ b/src/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;
+}
+
+}
diff --git a/src/Level.h b/src/Level.h
new file mode 100644
index 0000000..277b6e7
--- /dev/null
+++ b/src/Level.h
@@ -0,0 +1,74 @@
+/*
+ * Level.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_LEVEL_H_
+#define ZOOM_LEVEL_H_
+
+#include "BSPTree.h"
+
+#include <string>
+#include <map>
+#include <list>
+
+#include <boost/shared_ptr.hpp>
+#include <libxml/parser.h>
+#include <vmmlib/vector.hpp>
+
+namespace Zoom {
+
+class Level {
+ public:
+ static boost::shared_ptr<Level> loadLevel(const std::string &filename);
+
+ struct WallData : public BSPTree::TriangleData {
+ std::string texture;
+ };
+
+ struct Room {
+ std::string id;
+ std::list<BSPTree::TriangleRecord> walls;
+ };
+
+ const std::list<Room> &getRooms() const {
+ return rooms;
+ }
+
+ private:
+ std::list<Room> rooms;
+ std::map<std::string, unsigned> textures;
+
+ std::string name, description;
+ vmml::vec3f startPoint;
+
+ Level() {}
+
+ void loadLevelInfo(xmlNodePtr infoNode);
+ void loadRooms(xmlNodePtr roomsNode);
+ void loadRoom(xmlNodePtr roomNode);
+ void loadTextures(xmlNodePtr texturesNode);
+
+ static BSPTree::TriangleRecord loadWall(xmlNodePtr wallNode);
+ static vmml::vec3f loadVector(xmlNodePtr node);
+
+ static bool validateLevel(xmlDocPtr doc);
+};
+
+}
+
+#endif /* ZOOM_LEVEL_H_ */
diff --git a/src/Renderer.cpp b/src/Renderer.cpp
new file mode 100644
index 0000000..ad4849b
--- /dev/null
+++ b/src/Renderer.cpp
@@ -0,0 +1,69 @@
+/*
+ * Renderer.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 "Renderer.h"
+
+namespace Zoom {
+
+void Renderer::render(const BSPTree &tree) {
+ vmml::mat4f transform, inverseTransform;
+ glGetFloatv(GL_MODELVIEW_MATRIX, transform.array);
+
+ transform.inverse(inverseTransform);
+
+ vmml::vec3f viewPoint = inverseTransform*vmml::vec3f::ZERO;
+
+ glBegin(GL_TRIANGLES);
+ tree.visit(renderVisitor, viewPoint);
+ glEnd();
+
+}
+
+void Renderer::renderTriangle(const Triangle &t) {
+ glColor4fv(t.getColor().array);
+
+ if(t.getTexture() != activeTexture) {
+ glEnd();
+ useTexture(t.getTexture());
+ glBegin(GL_TRIANGLES);
+ }
+
+ for(int i = 0; i < 3; ++i) {
+ glTexCoord2fv(t.getTexCoords(i).array);
+ glNormal3fv(t.getNormal(i).array);
+ glVertex3fv(t.getVertex(i).array);
+ }
+}
+
+void Renderer::useTexture(unsigned texture) {
+ if(texture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ if(activeTexture) {
+ glEnable(GL_TEXTURE_2D);
+ }
+ }
+ else {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ activeTexture = texture;
+}
+
+}
diff --git a/src/Renderer.h b/src/Renderer.h
new file mode 100644
index 0000000..fb98e38
--- /dev/null
+++ b/src/Renderer.h
@@ -0,0 +1,78 @@
+/*
+ * Renderer.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_RENDERER_H_
+#define ZOOM_RENDERER_H_
+
+#include "BSPTree.h"
+#include "gl.h"
+
+namespace Zoom {
+
+class Renderer {
+ public:
+ Renderer() : activeTexture(0), renderVisitor(this) {}
+
+ void render(const BSPTree &tree);
+
+ template <typename T>
+ void render(const T &triangles) {
+ typename T::const_iterator t = triangles.begin();
+ if(t == triangles.end())
+ return;
+
+ useTexture(t->triangle.getTexture());
+
+ glBegin(GL_TRIANGLES);
+ for(; t != triangles.end(); ++t) {
+ renderTriangle(t->triangle);
+ }
+ glEnd();
+ }
+
+ struct TextureSorter {
+ bool operator() (const BSPTree::TriangleRecord &t1, const BSPTree::TriangleRecord &t2) {
+ return (t1.triangle.getTexture() < t2.triangle.getTexture());
+ }
+ };
+
+ private:
+ void renderTriangle(const Triangle &t);
+ void useTexture(unsigned texture);
+
+ class RenderVisitor {
+ public:
+ RenderVisitor(Renderer *renderer0) : renderer(renderer0) {}
+
+ void operator() (const BSPTree::TriangleRecord &t) const {
+ renderer->renderTriangle(t.triangle);
+ }
+
+ private:
+ Renderer *renderer;
+ };
+
+ unsigned activeTexture;
+
+ const RenderVisitor renderVisitor;
+};
+
+}
+
+#endif /* ZOOM_RENDERER_H_ */
diff --git a/src/Shader.cpp b/src/Shader.cpp
new file mode 100644
index 0000000..420b6c6
--- /dev/null
+++ b/src/Shader.cpp
@@ -0,0 +1,95 @@
+/*
+ * Shader.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 "Shader.h"
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <boost/scoped_array.hpp>
+
+namespace Zoom {
+
+bool Shader::loadProgram(const std::string &vertexShader, const std::string &fragmentShader) {
+ GLhandleARB program, vs = 0, fs = 0;
+
+ if(!vertexShader.empty())
+ vs = loadShader(vertexShader, GL_VERTEX_SHADER_ARB);
+
+ if(!fragmentShader.empty())
+ fs = loadShader(fragmentShader, GL_FRAGMENT_SHADER_ARB);
+
+ program = glCreateProgramObjectARB();
+
+ if(vs)
+ glAttachObjectARB(program, vs);
+
+ if(fs)
+ glAttachObjectARB(program, fs);
+
+ glLinkProgramARB(program);
+ printInfoLog(program);
+
+ glUseProgramObjectARB(program);
+
+ return true;
+}
+
+GLhandleARB Shader::loadShader(const std::string &name, GLenum type) {
+ std::vector<std::string> lines;
+ std::ifstream file(("shader/" + name).c_str(), std::ios_base::in);
+ if(!file.good())
+ throw std::ios::failure("Can't read file '" + name + "'");
+
+ while(file.good() && !file.eof()) {
+ std::string line;
+ std::getline(file, line);
+
+ if(!line.empty())
+ lines.push_back(line);
+ }
+
+ boost::scoped_array<const char *> strings(new const char*[lines.size()]);
+ for(std::size_t i = 0; i < lines.size(); ++i) {
+ strings[i] = lines[i].c_str();
+ }
+
+ GLhandleARB shader = glCreateShaderObjectARB(type);
+ glShaderSourceARB(shader, lines.size(), strings.get(), 0);
+ glCompileShaderARB(shader);
+ printInfoLog(shader);
+
+ return shader;
+}
+
+void Shader::printInfoLog(GLhandleARB obj) {
+ int length = 0;
+
+ glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
+
+ if(length > 0) {
+ boost::scoped_array<char> log(new char[length]);
+
+ int foo;
+ glGetInfoLogARB(obj, length, &foo, log.get());
+ std::cerr << log.get() << std::endl;
+ }
+}
+
+
+}
diff --git a/src/Shader.h b/src/Shader.h
new file mode 100644
index 0000000..8111f0c
--- /dev/null
+++ b/src/Shader.h
@@ -0,0 +1,41 @@
+/*
+ * Shader.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_SHADER_H_
+#define ZOOM_SHADER_H_
+
+#include "gl.h"
+#include <string>
+
+namespace Zoom {
+
+class Shader {
+ public:
+ static bool loadProgram(const std::string &vertexShader, const std::string &fragmentShader);
+
+ private:
+ Shader();
+
+ static GLhandleARB loadShader(const std::string &name, GLenum type);
+ static void printInfoLog(GLhandleARB obj);
+};
+
+}
+
+#endif /* ZOOM_SHADER_H_ */
diff --git a/src/Texture.cpp b/src/Texture.cpp
new file mode 100644
index 0000000..d5ec1f2
--- /dev/null
+++ b/src/Texture.cpp
@@ -0,0 +1,44 @@
+/*
+ * Texture.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 "Texture.h"
+#include "gl.h"
+#include <GL/glpng.h>
+
+namespace Zoom {
+
+std::map<std::string, unsigned> Texture::textures;
+
+unsigned Texture::loadTexture(const std::string &name) {
+ std::map<std::string, unsigned>::iterator it = textures.find(name);
+
+ if(it != textures.end())
+ return it->second;
+
+ pngInfo info;
+ unsigned texture = pngBind(("tex/" + name).c_str(), PNG_NOMIPMAP, PNG_ALPHA, &info, GL_REPEAT, GL_LINEAR, GL_LINEAR);
+
+ if(texture) {
+ textures.insert(std::make_pair(name, texture));
+ }
+
+ return texture;
+}
+
+}
diff --git a/src/Texture.h b/src/Texture.h
new file mode 100644
index 0000000..fd078f3
--- /dev/null
+++ b/src/Texture.h
@@ -0,0 +1,40 @@
+/*
+ * Texture.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_TEXTURE_H_
+#define ZOOM_TEXTURE_H_
+
+#include <string>
+#include <map>
+
+namespace Zoom {
+
+class Texture {
+ public:
+ static unsigned loadTexture(const std::string &name);
+
+ private:
+ Texture();
+
+ static std::map<std::string, unsigned> textures;
+};
+
+}
+
+#endif /* ZOOM_TEXTURE_H_ */
diff --git a/src/Triangle.h b/src/Triangle.h
new file mode 100644
index 0000000..2b34c3f
--- /dev/null
+++ b/src/Triangle.h
@@ -0,0 +1,105 @@
+/*
+ * Triangle.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_TRIANGLE_H_
+#define ZOOM_TRIANGLE_H_
+
+#include <vmmlib/vector.hpp>
+#include <vmmlib/matrix.hpp>
+
+namespace Zoom {
+
+class Triangle {
+ public:
+ Triangle() : color(vmml::vec4f::ONE), texture(0) {
+ vertices[0] = vertices[1] = vertices[2] = vmml::vec3f::ZERO;
+ normals[0] = normals[1] = normals[2] = vmml::vec3f::ZERO;
+ texcoords[0] = texcoords[1] = texcoords[2] = vmml::vec2f::ZERO;
+ }
+
+ Triangle(const vmml::vec3f &v1, const vmml::vec3f &v2, const vmml::vec3f &v3, const vmml::vec4f &color0) : color(color0), texture(0) {
+ vertices[0] = v1;
+ vertices[1] = v2;
+ vertices[2] = v3;
+
+ normals[0] = normals[1] = normals[2] = vmml::vec3f::ZERO;
+ texcoords[0] = texcoords[1] = texcoords[2] = vmml::vec2f::ZERO;
+ }
+
+ const vmml::vec3f& getVertex(int i) const {return vertices[i];}
+ vmml::vec3f& getVertex(int i) {return vertices[i];}
+ void setVertex(int i, vmml::vec3f v) {
+ vertices[i] = v;
+ }
+
+ const vmml::vec3f& getNormal(int i) const {return normals[i];}
+ vmml::vec3f& getNormal(int i) {return normals[i];}
+ void setNormal(int i, vmml::vec3f n) {
+ normals[i] = n;
+ }
+
+ const vmml::vec2f& getTexCoords(int i) const {return texcoords[i];}
+ vmml::vec2f& getTexCoords(int i) {return texcoords[i];}
+ void setTexCoords(int i, vmml::vec2f t) {
+ texcoords[i] = t;
+ }
+
+ unsigned getTexture() const {
+ return texture;
+ }
+ void setTexture(unsigned tex) {
+ texture = tex;
+ }
+
+ const vmml::vec4f& getColor() const {return color;}
+ vmml::vec4f& getColor() {return color;}
+ void setColor(vmml::vec4f c) {
+ color = c;
+ }
+
+ vmml::vec3f computeNormal() const {
+ return vertices[0].compute_normal(vertices[1], vertices[2]);
+ }
+
+ bool isDegenerate() const {
+ return (computeNormal().squared_length() == 0);
+ }
+
+ void transform(const vmml::mat4f &matrix) {
+ for(int i = 0; i < 3; ++i) {
+ vertices[i] = matrix*vertices[i];
+ }
+ }
+
+ vmml::vec3f getCenter() const {
+ return (vertices[0]+vertices[1]+vertices[2])/3;
+ }
+
+ private:
+ vmml::vec3f vertices[3];
+ vmml::vec3f normals[3];
+ vmml::vec2f texcoords[3];
+ vmml::vec4f color;
+ unsigned texture;
+};
+
+}
+
+#endif /*ZOOM_TRIANGLE_H_*/
+
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..9af944d
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,27 @@
+/*
+ * config.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_CONFIG_H_
+#define ZOOM_CONFIG_H_
+
+#define MIN_FRAME_DELTA 10
+#define DEFAULT_WIDTH 800
+#define DEFAULT_HEIGHT 600
+
+#endif /* ZOOM_CONFIG_H_ */
diff --git a/src/gl.h b/src/gl.h
new file mode 100644
index 0000000..3272b25
--- /dev/null
+++ b/src/gl.h
@@ -0,0 +1,32 @@
+/*
+ * gl.h
+ *
+ * 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/>.
+ */
+
+#ifndef ZOOM_GL_H_
+#define ZOOM_GL_H_
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#endif /* ZOOM_GL_H_ */
diff --git a/src/zoom.cpp b/src/zoom.cpp
new file mode 100644
index 0000000..6fab527
--- /dev/null
+++ b/src/zoom.cpp
@@ -0,0 +1,398 @@
+/*
+ * zoom.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 "Game.h"
+#include "config.h"
+#include "gl.h"
+
+#ifdef _WIN32
+#else
+#include <iostream>
+#include <X11/X.h>
+#include <GL/glx.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+void resize(int width, int height)
+{
+ if(height == 0) {
+ height = 1;
+ }
+
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(50.0f, (GLfloat)width/(GLfloat)height, 0.1, 1000);
+
+ glMatrixMode(GL_MODELVIEW);
+
+ glViewport(0, 0, width, height);
+}
+
+
+#ifdef _WIN32
+
+bool WGLInit(HINSTANCE hInstance, HWND *hWnd, HDC *hDC, HGLRC *hRC);
+void WGLUninit(HWND hWnd, HDC hDC, HGLRC hRC);
+LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+bool WGLInit(HINSTANCE hInstance, HWND *hWnd, HDC *hDC, HGLRC *hRC) {
+ WNDCLASS wc;
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wc.lpfnWndProc = (WNDPROC)wndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = 0;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = "3D";
+
+ if (!RegisterClass(&wc)) {
+ MessageBox(0, "Failed To Register The Window Class.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false; // Exit And Return FALSE
+ }
+
+ DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+ DWORD dwStyle = WS_OVERLAPPEDWINDOW;
+
+ RECT windowRect;
+ windowRect.left = 0;
+ windowRect.right = DEFAULT_WIDTH;
+ windowRect.top = 0;
+ windowRect.bottom = DEFAULT_HEIGHT;
+
+ AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
+
+ *hWnd = CreateWindowEx(dwExStyle, "3D" /* Class Name */, "3D" /* Title*/, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwStyle,
+ 0, 0, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, 0, 0, hInstance, 0);
+ if(!*hWnd) {
+ MessageBox(0, "Window Creation Error.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false;
+ }
+
+ static PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
+ 1, // Version Number
+ PFD_DRAW_TO_WINDOW | // Format Must Support Window
+ PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
+ PFD_DOUBLEBUFFER, // Must Support Double Buffering
+ PFD_TYPE_RGBA, // Request An RGBA Format
+ 16, // Select Our Color Depth
+ 0, 0, 0, 0, 0, 0, // Color Bits Ignored
+ 0, // No Alpha Buffer
+ 0, // Shift Bit Ignored
+ 0, // No Accumulation Buffer
+ 0, 0, 0, 0, // Accumulation Bits Ignored
+ 16, // 16Bit Z-Buffer (Depth Buffer)
+ 0, // No Stencil Buffer
+ 0, // No Auxiliary Buffer
+ PFD_MAIN_PLANE, // Main Drawing Layer
+ 0, // Reserved
+ 0, 0, 0 // Layer Masks Ignored
+ };
+
+ *hDC=GetDC(*hWnd);
+ if (!*hDC) {
+ WGLUninit(*hWnd, 0, 0);
+ MessageBox(0, "Can't Create A GL Device Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false;
+ }
+
+ GLuint pixelFormat = ChoosePixelFormat(*hDC, &pfd);
+ if(!pixelFormat) {
+ WGLUninit(*hWnd, *hDC, 0);
+ MessageBox(0, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false;
+ }
+
+ if(!SetPixelFormat(*hDC, pixelFormat, &pfd)) {
+ WGLUninit(*hWnd, *hDC, 0);
+ MessageBox(0, "Can't Set The PixelFormat.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false;
+ }
+
+ *hRC=wglCreateContext(*hDC);
+ if(!*hRC) {
+ WGLUninit(*hWnd, *hDC, 0);
+ MessageBox(0, "Can't Create A GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false;
+ }
+
+ if(!wglMakeCurrent(*hDC, *hRC)) {
+ WGLUninit(*hWnd, *hDC, *hRC);
+ MessageBox(0, "Can't Activate The GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
+ return false;
+ }
+
+ ShowWindow(*hWnd, SW_SHOW);
+ SetForegroundWindow(*hWnd);
+ SetFocus(*hWnd);
+
+ return true;
+}
+
+void WGLUninit(HWND hWnd, HDC hDC, HGLRC hRC) {
+ wglMakeCurrent(0, 0);
+
+ if(hRC)
+ wglDeleteContext(hRC);
+
+ if(hDC)
+ ReleaseDC(hWnd, hDC);
+
+ if(hWnd)
+ DestroyWindow(hWnd);
+}
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
+ HWND hWnd = 0;
+ HDC hDC = 0;
+ HGLRC hRC = 0;
+
+ if(!WGLInit(hInstance, &hWnd, &hDC, &hRC)) {
+ return 0;
+ }
+
+ glewInit();
+
+ resize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+
+ bool running = true;
+ MSG msg;
+
+ while(running)
+ {
+ unsigned long delta = 0;
+ unsigned long ticks = GetTickCount();
+
+ while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if(msg.message == WM_QUIT)
+ {
+ running = false;
+ break;
+ }
+ else // If Not, Deal With Window Messages
+ {
+ TranslateMessage(&msg); // Translate The Message
+ DispatchMessage(&msg); // Dispatch The Message
+ }
+ }
+
+ delta = GetTickCount()-ticks;
+
+ if(delta < MIN_FRAME_DELTA) {
+ Sleep(MIN_FRAME_DELTA - delta);
+ delta = GetTickCount()-ticks;
+ }
+
+ ticks += delta;
+
+ static DisplayClass render;
+ render.renderScene(delta);
+ SwapBuffers(hDC);
+ }
+
+ WGLUninit(hWnd, hDC, hRC);
+ return msg.wParam;
+}
+
+LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch(uMsg) {
+ case WM_CLOSE:
+ PostQuitMessage(0);
+ return 0;
+
+ case WM_SIZE:
+ resize(LOWORD(lParam), HIWORD(lParam));
+ return 0;
+
+ default:
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+}
+
+#else
+
+bool GLXinit(Display *disp, Atom windele, Window *wnd, GLXContext *gc, bool *multisample) {
+ static const int msAttributeList[] = {GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ GLX_X_RENDERABLE, True,
+ GLX_DOUBLEBUFFER, True,
+ GLX_DEPTH_SIZE, 1,
+ GLX_SAMPLE_BUFFERS, 1,
+ GLX_SAMPLES, 4,
+ None};
+
+ static const int attributeList[] = {GLX_RENDER_TYPE, GLX_RGBA_BIT,
+ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+ GLX_X_RENDERABLE, True,
+ GLX_DOUBLEBUFFER, True,
+ GLX_DEPTH_SIZE, 1,
+ None};
+
+ bool ok = false;
+ *multisample = true;
+
+ int nElements;
+ GLXFBConfig *fbConfig = glXChooseFBConfig(disp, DefaultScreen(disp), msAttributeList, &nElements);
+ if(!fbConfig || !nElements) {
+ if(fbConfig) {
+ XFree(fbConfig);
+ }
+
+ *multisample = false;
+ fbConfig = glXChooseFBConfig(disp, DefaultScreen(disp), attributeList, &nElements);
+ }
+
+ if(fbConfig && nElements) {
+ XVisualInfo *vi = glXGetVisualFromFBConfig(disp, *fbConfig);
+
+ if(vi) {
+ Colormap cmap = XCreateColormap(disp, RootWindow(disp, vi->screen), vi->visual, AllocNone);
+ XSetWindowAttributes swa;
+ swa.colormap = cmap;
+ swa.border_pixel = 0;
+ swa.event_mask = StructureNotifyMask/* | PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask*/;
+
+ *wnd = XCreateWindow(disp, RootWindow(disp, vi->screen), 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0, vi->depth, InputOutput,
+ vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa);
+
+ XClassHint chint;
+ chint.res_name = const_cast<char*>("3D");
+ chint.res_class = const_cast<char*>("3d");
+ XSetClassHint(disp, *wnd, &chint);
+
+ char *str = const_cast<char*>("3D");
+ XTextProperty name;
+ if(XStringListToTextProperty(&str, 1, &name) != 0)
+ {
+ XSetWMName(disp, *wnd, &name);
+ XFree(name.value);
+ }
+
+ XSetWMProtocols(disp, *wnd, &windele, 1);
+
+ XMapWindow(disp, *wnd);
+
+ *gc = glXCreateContext(disp, vi, NULL, True);
+
+ glXMakeCurrent(disp, *wnd, *gc);
+
+ ok = true;
+
+ XFree(vi);
+ }
+ }
+
+ if(fbConfig)
+ XFree(fbConfig);
+
+ XSync(disp, 0);
+
+ return ok;
+}
+
+int main() {
+ Display *disp = XOpenDisplay(0);
+ Atom windele = XInternAtom(disp, "WM_DELETE_WINDOW", False);
+ Window wnd;
+ GLXContext gc;
+
+ bool multisample;
+ if(!GLXinit(disp, windele, &wnd, &gc, &multisample))
+ return 1;
+
+ glewInit();
+
+ resize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+
+ Zoom::Game game(multisample);
+
+ GLint samples;
+ glGetIntegerv(GL_SAMPLES, &samples);
+ std::cerr << "Using " << samples << " samples" << std::endl;
+
+ bool running = true;
+
+ unsigned long delta = 0;
+ unsigned long frames = 0, tocks = 0;
+
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ unsigned long ticks = tv.tv_usec;
+
+ while(running) {
+ while(XPending(disp)) {
+ XEvent event;
+ XNextEvent(disp, &event);
+ switch(event.type) {
+ case ConfigureNotify:
+ resize(event.xconfigure.width, event.xconfigure.height);
+ break;
+
+ case ClientMessage:
+ if(static_cast<Atom>(event.xclient.data.l[0]) == windele)
+ running = false;
+ }
+ }
+
+ if(!running) break;
+
+ game.run(delta);
+
+ game.render();
+ glXSwapBuffers(disp, wnd);
+ XSync(disp, 0);
+
+ long slept = 0;
+ gettimeofday(&tv, NULL);
+ delta = ((tv.tv_usec + 1000000 - ticks)%1000000)/1000;
+
+ if(delta < MIN_FRAME_DELTA) {
+ usleep((MIN_FRAME_DELTA-delta)*1000);
+ slept += (MIN_FRAME_DELTA-delta);
+
+ gettimeofday(&tv, NULL);
+ delta = ((tv.tv_usec + 1000000 - ticks)%1000000)/1000;
+ }
+
+ ticks = (ticks + delta*1000) % 1000000;
+
+ frames++;
+ tocks += delta*1000;
+ if(tocks > 1000000) {
+ std::cerr << frames << " fps; slept a total of " << slept << " ms" << std::endl;
+ frames = 0;
+ tocks -= 1000000;
+ slept = 0;
+ }
+ }
+
+ XDestroyWindow(disp, wnd);
+ glXDestroyContext(disp, gc);
+ XCloseDisplay(disp);
+
+ return 0;
+}
+
+#endif