diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | BSPTree.cpp | 149 | ||||
-rw-r--r-- | BSPTree.h | 172 | ||||
-rw-r--r-- | CMakeLists.txt | 18 | ||||
-rw-r--r-- | Game.cpp | 81 | ||||
-rw-r--r-- | Game.h | 44 | ||||
-rw-r--r-- | Renderer.cpp | 53 | ||||
-rw-r--r-- | Renderer.h | 46 | ||||
-rw-r--r-- | Triangle.h | 47 | ||||
-rw-r--r-- | config.h | 27 | ||||
-rw-r--r-- | gl.h | 12 | ||||
-rw-r--r-- | zoom.cpp | 386 |
12 files changed, 1040 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2837841 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/build + +/.project +/.cproject +/.settings diff --git a/BSPTree.cpp b/BSPTree.cpp new file mode 100644 index 0000000..fa2f1c0 --- /dev/null +++ b/BSPTree.cpp @@ -0,0 +1,149 @@ +#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 Triangle &t, std::list<Triangle> *front, std::list<Triangle> *back) const { + for(int i = 0; i < 3; ++i) { + if(contains(t.getVertex(i))) { + const vmml::vec3f *v[3] = {&t.getVertex(i), &t.getVertex((i+1)%3), &t.getVertex((i+2)%3)}; + + vmml::vec3f is = intersection(*v[1], *v[2]-*v[1]); + + if(isInFront(*v[1])) { + front->push_back(Triangle(*v[0], *v[1], is, t.getColor())); + back->push_back(Triangle(*v[0], is, *v[2], t.getColor())); + } + else { + back->push_back(Triangle(*v[0], *v[1], is, t.getColor())); + front->push_back(Triangle(*v[0], is, *v[2], t.getColor())); + } + + return; + } + } + + for(int i = 0; i < 3; ++i) { + const vmml::vec3f *v[3] = {&t.getVertex(i), &t.getVertex((i+1)%3), &t.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(Triangle(*v[0], is1, is2, t.getColor())); + back->push_back(Triangle(is1, *v[1], is2, t.getColor())); + back->push_back(Triangle(*v[1], *v[2], is2, t.getColor())); + } + else { + back->push_back(Triangle(*v[0], is1, is2, t.getColor())); + front->push_back(Triangle(is1, *v[1], is2, t.getColor())); + front->push_back(Triangle(*v[1], *v[2], is2, t.getColor())); + } + + return; + } + } +} + + +BSPTree::BSPTree(const std::list<Triangle> &triangles) : frontTree(0), backTree(0) { + const Triangle *planeT = findNearestTriangle(triangles, findCenter(triangles)); + + if(!planeT) + return; + + plane = Plane(*planeT); + + std::list<Triangle> front, back; + + for(std::list<Triangle>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + if(t->getNormal().squared_length() == 0) + continue; + + if(plane.contains(*t)) { + this->triangles.push_back(*t); + continue; + } + else if(plane.isInFront(*t)) { + front.push_back(*t); + continue; + } + else if(plane.isBehind(*t)) { + back.push_back(*t); + continue; + } + + std::list<Triangle> frontPart, backPart; + plane.partition(*t, &frontPart, &backPart); + front.splice(front.end(), frontPart); + back.splice(back.end(), backPart); + } + + 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<Triangle> &triangles) { + vmml::vec3f v; + + for(std::list<Triangle>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + v += t->getCenter(); + } + + return v/triangles.size(); +} + +const Triangle* BSPTree::findNearestTriangle(const std::list<Triangle> &triangles, const vmml::vec3f &v) { + const Triangle *current = 0; + float distanceSq; + + for(std::list<Triangle>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + if(t->getNormal().squared_length() == 0) + continue; + + float d = t->getCenter().squared_distance(v); + + if(!current || d < distanceSq) { + current = &*t; + distanceSq = d; + } + } + + return current; +} + +} diff --git a/BSPTree.h b/BSPTree.h new file mode 100644 index 0000000..47ba411 --- /dev/null +++ b/BSPTree.h @@ -0,0 +1,172 @@ +#ifndef ZOOM_BSPTREE_H_ +#define ZOOM_BSPTREE_H_ + +#include "Triangle.h" +#include <list> +#include <cmath> +#include <vmmlib/vector.hpp> + +namespace Zoom { + +class BSPTree { + private: + class Plane { + public: + Plane() : d(0) {} + Plane(const vmml::vec3f &n, float d0) : normal(n), d(d0) {} + Plane(const Triangle &t) : normal(t.getNormal()), 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 Triangle &t, std::list<Triangle> *front, std::list<Triangle> *back) const; + + private: + vmml::vec3f normal; + float d; + }; + + public: + BSPTree(const std::list<Triangle> &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) { + doVisit<const T>(visitor, p); + } + + template<typename T> + void visit(T& visitor, const vmml::vec3f &p) { + doVisit(visitor, p); + } + + 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<Triangle> triangles; + BSPTree *frontTree, *backTree; + + template<typename T> + void doVisit(T& visitor, const vmml::vec3f &p) { + if(plane.isBehind(p)) { + if(frontTree) + frontTree->visit(visitor, p); + + for(std::list<Triangle>::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<Triangle>::iterator t = triangles.begin(); t != triangles.end(); ++t) { + visitor(*t); + } + + if(frontTree) + frontTree->visit(visitor, p); + } + } + + 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<Triangle>::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<Triangle>::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + visitor(*t); + } + + if(frontTree) + frontTree->visit(visitor, p); + } + } + + static vmml::vec3f findCenter(const std::list<Triangle> &triangles); + static const Triangle* findNearestTriangle(const std::list<Triangle> &triangles, const vmml::vec3f &v); +}; + +} + +#endif /* ZOOM_BSPTREE_H_ */ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..da27b7b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.6) +project(ZOOM) + +find_package(OpenGL REQUIRED) + +include_directories(${OPENGL_INCLUDE_DIR}) + +add_executable(zoom + BSPTree.cpp BSPTree.h + Game.cpp Game.h + config.h + gl.h + Renderer.cpp Renderer.h + Triangle.h + zoom.cpp +) + +target_link_libraries(zoom ${OPENGL_LIBRARIES}) diff --git a/Game.cpp b/Game.cpp new file mode 100644 index 0000000..bd028c2 --- /dev/null +++ b/Game.cpp @@ -0,0 +1,81 @@ +/* + * 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 "Triangle.h" +#include "gl.h" + +namespace Zoom { + +Game::Game(bool multisample) : angle(0) { + glClearColor(0.0, 0.0, 0.0, 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 + + /*glEnable(GL_LIGHTING); + static const float light[] = {1, 1, 1, 0}; + static const float lightColor[] = {1, 1, 1, 1}; + glLightfv(GL_LIGHT0, GL_POSITION, light); + glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor); + glEnable(GL_LIGHT0);*/ + + //glEnable(GL_CULL_FACE); + //glFrontFace(GL_CCW); + + glLoadIdentity(); + glTranslatef(0, 0, -5); +} + +void Game::run(int delta) { + angle += delta*0.2; + + if(angle >= 360) + angle -= 360; +} + +void Game::render() { + std::list<Triangle> triangles; + + triangles.push_back(Triangle(vmml::vec3f(-1, -1, 0), vmml::vec3f(1, -1, 0), vmml::vec3f(1, 1, 0), vmml::vec3f(0, 1, 0))); + triangles.push_back(Triangle(vmml::vec3f(-1, -1, 0), vmml::vec3f(-1, 1, 0), vmml::vec3f(1, 1, 0), vmml::vec3f(0, 1, 0))); + triangles.push_back(Triangle(vmml::vec3f(-1, -1, -1), vmml::vec3f(1, -1, -1), vmml::vec3f(1, 1, -1), vmml::vec3f(0, 0, 1))); + triangles.push_back(Triangle(vmml::vec3f(-1, -1, -1), vmml::vec3f(-1, 1, -1), vmml::vec3f(1, 1, -1), vmml::vec3f(0, 0, 1))); + + BSPTree tree(triangles); + + glClear(GL_COLOR_BUFFER_BIT); + + glPushMatrix(); + glRotatef(angle, 1, 2, 1); + + renderer.render(tree); + + glPopMatrix(); +} + +} @@ -0,0 +1,44 @@ +/* + * 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" + +namespace Zoom { + +class Triangle; + +class Game { + public: + Game(bool multisample); + + void run(int delta); + void render(); + + private: + Renderer renderer; + + float angle; +}; + +} + +#endif /* ZOOM_GAME_H_ */ diff --git a/Renderer.cpp b/Renderer.cpp new file mode 100644 index 0000000..bf9aadb --- /dev/null +++ b/Renderer.cpp @@ -0,0 +1,53 @@ +/* + * 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" +#include "BSPTree.h" + +namespace Zoom { + +const Renderer::RenderVisitor Renderer::renderVisitor = Renderer::RenderVisitor(); + +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); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, t.getColor().array); + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (t.getColor()/2).array); + + glNormal3fv(t.getNormal().array); + + for(int i = 0; i < 3; ++i) { + glVertex3fv(t.getVertex(i).array); + } +} + +} diff --git a/Renderer.h b/Renderer.h new file mode 100644 index 0000000..d64a4fe --- /dev/null +++ b/Renderer.h @@ -0,0 +1,46 @@ +/* + * 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_ + +namespace Zoom { + +class Triangle; +class BSPTree; + +class Renderer { + public: + void render(const BSPTree &tree); + + private: + static void renderTriangle(const Triangle &t); + + struct RenderVisitor { + void operator() (const Triangle &t) const { + renderTriangle(t); + } + }; + + static const RenderVisitor renderVisitor; +}; + +} + +#endif /* ZOOM_RENDERER_H_ */ diff --git a/Triangle.h b/Triangle.h new file mode 100644 index 0000000..16b0d73 --- /dev/null +++ b/Triangle.h @@ -0,0 +1,47 @@ +#ifndef ZOOM_TRIANGLE_H_ +#define ZOOM_TRIANGLE_H_ + +#include "gl.h" +#include <vmmlib/vector.hpp> +#include <vmmlib/matrix.hpp> + +namespace Zoom { + +class Triangle { + public: + Triangle() : c(vmml::vec4f::ONE) { + v[0] = v[1] = v[2] = vmml::vec3f::ZERO; + } + + Triangle(const vmml::vec3f &v1, const vmml::vec3f &v2, const vmml::vec3f &v3, const vmml::vec4f &c0) : c(c0) { + v[0] = v1; + v[1] = v2; + v[2] = v3; + } + + const vmml::vec3f& getVertex(int i) const {return v[i];} + const vmml::vec4f& getColor() const {return c;} + + vmml::vec3f getNormal() const { + return v[0].compute_normal(v[1], v[2]); + } + + void transform(const vmml::mat4f &m) { + for(int i = 0; i < 3; ++i) { + v[i] = m*v[i]; + } + } + + vmml::vec3f getCenter() const { + return (v[0]+v[1]+v[2])/3; + } + + private: + vmml::vec3f v[3]; + vmml::vec4f c; +}; + +} + +#endif /*ZOOM_TRIANGLE_H_*/ + diff --git a/config.h b/config.h new file mode 100644 index 0000000..1c2e879 --- /dev/null +++ b/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 16 +#define DEFAULT_WIDTH 800 +#define DEFAULT_HEIGHT 600 + +#endif /* ZOOM_CONFIG_H_ */ @@ -0,0 +1,12 @@ +#ifndef _GL_H_ +#define _GL_H_ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + +#include <GL/gl.h> +#include <GL/glu.h> + +#endif /* _GL_H_ */ diff --git a/zoom.cpp b/zoom.cpp new file mode 100644 index 0000000..9535b8a --- /dev/null +++ b/zoom.cpp @@ -0,0 +1,386 @@ +#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; + } + + 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; + + 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 |