From d9f44af7aee41a111a3d7427d8735bc821f1824f Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 15 Dec 2009 19:37:12 +0100 Subject: Moved source files to src; sort triangles by texture. --- src/BSPTree.cpp | 164 ++++++++++++++++++++++ src/BSPTree.h | 177 ++++++++++++++++++++++++ src/CMakeLists.txt | 13 ++ src/Game.cpp | 129 +++++++++++++++++ src/Game.h | 53 +++++++ src/Level.cpp | 260 ++++++++++++++++++++++++++++++++++ src/Level.h | 74 ++++++++++ src/Renderer.cpp | 69 ++++++++++ src/Renderer.h | 78 +++++++++++ src/Shader.cpp | 95 +++++++++++++ src/Shader.h | 41 ++++++ src/Texture.cpp | 44 ++++++ src/Texture.h | 40 ++++++ src/Triangle.h | 105 ++++++++++++++ src/config.h | 27 ++++ src/gl.h | 32 +++++ src/zoom.cpp | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 17 files changed, 1799 insertions(+) create mode 100644 src/BSPTree.cpp create mode 100644 src/BSPTree.h create mode 100644 src/CMakeLists.txt create mode 100644 src/Game.cpp create mode 100644 src/Game.h create mode 100644 src/Level.cpp create mode 100644 src/Level.h create mode 100644 src/Renderer.cpp create mode 100644 src/Renderer.h create mode 100644 src/Shader.cpp create mode 100644 src/Shader.h create mode 100644 src/Texture.cpp create mode 100644 src/Texture.h create mode 100644 src/Triangle.h create mode 100644 src/config.h create mode 100644 src/gl.h create mode 100644 src/zoom.cpp (limited to 'src') 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 + * + * 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 "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 *front, std::list *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 &triangles) : frontTree(0), backTree(0) { + const Triangle *planeT = findNearestTriangle(triangles, findCenter(triangles)); + + if(!planeT) + return; + + plane = Plane(*planeT); + + std::list front, back; + + for(std::list::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 &triangles) { + vmml::vec3f v; + + for(std::list::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + v += t->triangle.getCenter(); + } + + return v/triangles.size(); +} + +const Triangle* BSPTree::findNearestTriangle(const std::list &triangles, const vmml::vec3f &v) { + const Triangle *current = 0; + float distanceSq; + + for(std::list::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 + * + * 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 . + */ + +#ifndef ZOOM_BSPTREE_H_ +#define ZOOM_BSPTREE_H_ + +#include "Triangle.h" +#include +#include + +#include +#include + +namespace Zoom { + +class BSPTree { + public: + class TriangleData { + protected: + TriangleData() {} + + public: + virtual ~TriangleData() {} + }; + + struct TriangleRecord { + public: + TriangleRecord(Triangle triangle0, boost::shared_ptr data0) + : triangle(triangle0), data(data0) {} + TriangleRecord(Triangle triangle0) : triangle(triangle0) {} + TriangleRecord() {} + + Triangle triangle; + boost::shared_ptr 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 *front, std::list *back) const; + + private: + vmml::vec3f normal; + float d; + }; + + public: + BSPTree(const std::list &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 + void visit(const T& visitor, const vmml::vec3f &p) const { + doVisit(visitor, p); + } + + template + void visit(T& visitor, const vmml::vec3f &p) const { + doVisit(visitor, p); + } + + private: + Plane plane; + std::list triangles; + BSPTree *frontTree, *backTree; + + template + void doVisit(T& visitor, const vmml::vec3f &p) const { + if(plane.isBehind(p)) { + if(frontTree) + frontTree->visit(visitor, p); + + for(std::list::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::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + visitor(*t); + } + + if(frontTree) + frontTree->visit(visitor, p); + } + } + + static vmml::vec3f findCenter(const std::list &triangles); + static const Triangle* findNearestTriangle(const std::list &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 + * + * 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 "Game.h" +#include "BSPTree.h" +#include "Level.h" +#include "Shader.h" +#include "Triangle.h" +#include "gl.h" +#include + +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 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(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 + * + * 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 . + */ + +#ifndef ZOOM_GAME_H_ +#define ZOOM_GAME_H_ + +#include "Renderer.h" +#include +#include + +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; + std::vector 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 + * + * 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->data); + + if(!wallData->texture.empty()) { + std::map::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(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 + * + * 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 . + */ + +#ifndef ZOOM_LEVEL_H_ +#define ZOOM_LEVEL_H_ + +#include "BSPTree.h" + +#include +#include +#include + +#include +#include +#include + +namespace Zoom { + +class Level { + public: + static boost::shared_ptr loadLevel(const std::string &filename); + + struct WallData : public BSPTree::TriangleData { + std::string texture; + }; + + struct Room { + std::string id; + std::list walls; + }; + + const std::list &getRooms() const { + return rooms; + } + + private: + std::list rooms; + std::map 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 + * + * 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 "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 + * + * 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 . + */ + +#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 + 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 + * + * 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 "Shader.h" +#include +#include +#include +#include + +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 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 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 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 + * + * 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 . + */ + +#ifndef ZOOM_SHADER_H_ +#define ZOOM_SHADER_H_ + +#include "gl.h" +#include + +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 + * + * 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 "Texture.h" +#include "gl.h" +#include + +namespace Zoom { + +std::map Texture::textures; + +unsigned Texture::loadTexture(const std::string &name) { + std::map::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 + * + * 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 . + */ + +#ifndef ZOOM_TEXTURE_H_ +#define ZOOM_TEXTURE_H_ + +#include +#include + +namespace Zoom { + +class Texture { + public: + static unsigned loadTexture(const std::string &name); + + private: + Texture(); + + static std::map 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 + * + * 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 . + */ + +#ifndef ZOOM_TRIANGLE_H_ +#define ZOOM_TRIANGLE_H_ + +#include +#include + +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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#ifndef ZOOM_GL_H_ +#define ZOOM_GL_H_ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include +#include + +#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 + * + * 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 "Game.h" +#include "config.h" +#include "gl.h" + +#ifdef _WIN32 +#else +#include +#include +#include +#include +#include +#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("3D"); + chint.res_class = const_cast("3d"); + XSetClassHint(disp, *wnd, &chint); + + char *str = const_cast("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(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 -- cgit v1.2.3