From da66d49b8a7bcc808df201bee5c7cb787b6f30b7 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 13 Dec 2009 18:49:36 +0100 Subject: Basic infrastructure --- .gitignore | 5 + BSPTree.cpp | 149 ++++++++++++++++++++++ BSPTree.h | 172 +++++++++++++++++++++++++ CMakeLists.txt | 18 +++ Game.cpp | 81 ++++++++++++ Game.h | 44 +++++++ Renderer.cpp | 53 ++++++++ Renderer.h | 46 +++++++ Triangle.h | 47 +++++++ config.h | 27 ++++ gl.h | 12 ++ zoom.cpp | 386 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 1040 insertions(+) create mode 100644 .gitignore create mode 100644 BSPTree.cpp create mode 100644 BSPTree.h create mode 100644 CMakeLists.txt create mode 100644 Game.cpp create mode 100644 Game.h create mode 100644 Renderer.cpp create mode 100644 Renderer.h create mode 100644 Triangle.h create mode 100644 config.h create mode 100644 gl.h create mode 100644 zoom.cpp 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 *front, std::list *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 &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->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 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 &triangles) { + vmml::vec3f v; + + for(std::list::const_iterator t = triangles.begin(); t != triangles.end(); ++t) { + v += t->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->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 +#include +#include + +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 *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) { + doVisit(visitor, p); + } + + template + void visit(T& visitor, const vmml::vec3f &p) { + doVisit(visitor, p); + } + + 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) { + if(plane.isBehind(p)) { + if(frontTree) + frontTree->visit(visitor, p); + + for(std::list::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::iterator t = triangles.begin(); t != triangles.end(); ++t) { + visitor(*t); + } + + if(frontTree) + frontTree->visit(visitor, p); + } + } + + 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/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 + * + * 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 "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 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(); +} + +} diff --git a/Game.h b/Game.h new file mode 100644 index 0000000..dbd129a --- /dev/null +++ b/Game.h @@ -0,0 +1,44 @@ +/* + * 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" + +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 + * + * 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" +#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 + * + * 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_ + +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 +#include + +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 + * + * 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 16 +#define DEFAULT_WIDTH 800 +#define DEFAULT_HEIGHT 600 + +#endif /* ZOOM_CONFIG_H_ */ diff --git a/gl.h b/gl.h new file mode 100644 index 0000000..fb5face --- /dev/null +++ b/gl.h @@ -0,0 +1,12 @@ +#ifndef _GL_H_ +#define _GL_H_ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include + +#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 +#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; + } + + 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; + + 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