Kajetan Johannes Hammerle 5 gadi atpakaļ
revīzija
d8be305d8a
11 mainītis faili ar 1349 papildinājumiem un 0 dzēšanām
  1. 2 0
      .gitignore
  2. 94 0
      Control.c
  3. 21 0
      Control.h
  4. 351 0
      GameEngine.c
  5. 22 0
      GameEngine.h
  6. 680 0
      Main.c
  7. 14 0
      Makefile
  8. 101 0
      Vector3D.c
  9. 32 0
      Vector3D.h
  10. 14 0
      fragment.fs
  11. 18 0
      vertex.vs

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/nbproject
+game

+ 94 - 0
Control.c

@@ -0,0 +1,94 @@
+#include <GLFW/glfw3.h>
+#include "Control.h"
+
+typedef struct Key
+{
+    int key;
+    int isDown;
+    int downTime;
+} Key;
+
+static Key keys[GLFW_KEY_LAST + 2];
+
+typedef struct MouseButton
+{
+    int button;
+    int isDown;
+    int downTime;
+    int fastClick;
+} MouseButton;
+
+static MouseButton buttons[GLFW_MOUSE_BUTTON_LAST + 1];
+
+void controlInit()
+{
+    for(int i = 0; i < GLFW_KEY_LAST + 2; i++)
+    {
+        keys[i].key = 0;
+        keys[i].isDown = 0;
+        keys[i].downTime = 0;
+    }
+    for(int i = 0; i < GLFW_MOUSE_BUTTON_LAST + 1; i++)
+    {
+        buttons[i].button = 0;
+        buttons[i].isDown = 0;
+        buttons[i].downTime = 0;
+        buttons[i].fastClick = 0;
+    }
+}
+
+void keyPress(int key)
+{
+    keys[key + 1].isDown = 1;
+}
+
+void keyRelease(int key)
+{
+    keys[key + 1].isDown = 0;
+    keys[key + 1].downTime = 0;
+}
+
+void controlTick()
+{
+    for(int i = 0; i < GLFW_KEY_LAST + 2; i++)
+    {
+        keys[i].downTime += keys[i].isDown;
+    }
+    for(int i = 0; i < GLFW_MOUSE_BUTTON_LAST + 1; i++)
+    {
+        buttons[i].downTime += buttons[i].isDown;
+        buttons[i].fastClick--;
+    }
+}
+
+int keyIsDown(int key)
+{
+    return keys[key + 1].isDown;
+}
+
+int keyIsJustDown(int key)
+{
+    return keys[key + 1].downTime == 1;
+}
+
+void mousePress(int button)
+{
+    buttons[button].isDown = 1;
+    buttons[button].fastClick = 2;
+}
+
+void mouseRelease(int button)
+{
+    buttons[button].isDown = 0;
+    buttons[button].downTime = 0;
+}
+
+int mouseIsDown(int button)
+{
+    return buttons[button].isDown;
+}
+
+int mouseIsJustDown(int button)
+{
+    return buttons[button].downTime == 1 || buttons[button].fastClick > 0;
+}

+ 21 - 0
Control.h

@@ -0,0 +1,21 @@
+#ifndef KEY_H
+#define KEY_H
+
+void controlInit();
+void controlTick();
+
+void keyPress(int key);
+void keyRelease(int key);
+
+int keyIsDown(int key);
+int keyIsJustDown(int key);
+
+void mousePress(int button);
+void mouseRelease(int button);
+
+int mouseIsDown(int button);
+int mouseIsJustDown(int button);
+
+
+#endif
+

+ 351 - 0
GameEngine.c

@@ -0,0 +1,351 @@
+#include "GameEngine.h"
+#include "Control.h"
+ 
+static GLuint program;
+static GLuint vShader;
+static GLuint fShader;
+static WindowResize windowResize;
+static MouseMove mouseMove;
+static int activeFocus = 0;
+
+static double oldMouseX = 0;
+static double oldMouseY = 0;
+
+static void flushOutput()
+{
+    fflush(stdout);
+}
+
+static void onKeyEvent(GLFWwindow* w, int key, int scancode, int action, int mods)
+{
+    if(action == GLFW_RELEASE)
+    {
+        if(key == GLFW_KEY_ESCAPE)
+        {
+            activeFocus = 0;
+            glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+        }
+        keyRelease(key);
+    }
+    if(action == GLFW_PRESS)
+    {
+        keyPress(key);
+    }
+}
+
+static void onMouseClick(GLFWwindow* w, int button, int action, int mods)
+{
+    if(action == GLFW_PRESS)
+    {
+        if(!activeFocus)
+        {
+            glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+            oldMouseX = 0;
+            oldMouseY = 0;
+            activeFocus = 1;
+        }
+        else
+        {
+            mousePress(button);
+        }
+    }
+    else if(action == GLFW_RELEASE)
+    {
+        mouseRelease(button);
+    }
+}
+
+static void onMouseMove(GLFWwindow* w, double x, double y)
+{
+    if(activeFocus)
+    {
+        if(oldMouseX == 0 && oldMouseY == 0)
+        {
+            oldMouseX = x;
+            oldMouseY = y;
+        }
+        else
+        {
+            mouseMove(x - oldMouseX, y - oldMouseY);
+            oldMouseX = x;
+            oldMouseY = y;
+        }
+    }
+}
+
+static void onWindowResize(GLFWwindow* w, int width, int height)
+{
+    glViewport(0, 0, width, height);
+    windowResize(width, height);
+}
+
+static GLchar* readFile(char* name)
+{
+    FILE* file = fopen(name, "r");
+    if(file != NULL)
+    {
+        int size = 128;
+        int index = 0;
+        GLchar* content = malloc(sizeof(GLchar) * size);
+        
+        while(1)
+        {
+            int c = fgetc(file);
+            if(c == EOF)
+            {
+                break;
+            }
+            if(index >= size)
+            {
+                size *= 2;
+                content = realloc(content, size);
+            }
+            content[index] = c;
+            index++;
+        }
+        
+        if(index >= size)
+        {
+            size++;
+            content = realloc(content, size);
+        }
+        content[index] = '\0';
+        index++;
+        
+        fclose(file);
+        return content;
+    }
+    return NULL;
+}
+
+int checkShaderErrors(char* name, GLuint shader)
+{
+    int returnValue = 0;
+    
+    printf("compiling %s shader ...\n", name);
+    GLenum error = glGetError();
+    if(error)
+    {
+        fprintf(stderr, "error: %u\n", glGetError());
+        returnValue = 1;
+    }
+    else
+    {
+        printf("no error occured ...\n");
+    }
+    
+    GLint compiled[1];
+    glGetShaderiv(shader, GL_COMPILE_STATUS, compiled);
+    if(compiled[0])
+    {
+        printf("%s shader successfully compiled\n", name);
+    }
+    else
+    {
+        fprintf(stderr, "compiling of %s shader failed:\n", name);
+        GLchar buffer[512];
+        GLsizei bufferSize = 512;
+        GLsizei charsUsed = 0;
+        glGetShaderInfoLog(shader, bufferSize, &charsUsed, buffer);
+        buffer[bufferSize - 1] = '\0';
+        fprintf(stderr, "%s\n", buffer);
+        returnValue = 1;
+    }
+    return returnValue;
+}
+
+static GLuint compileProgram(const GLchar* vertex, const GLchar* fragment)
+{
+    vShader = glCreateShader(GL_VERTEX_SHADER);
+    glShaderSource(vShader, 1, &vertex, NULL);
+    glCompileShader(vShader);
+
+    if(checkShaderErrors("vertex", vShader))
+    {
+        return 0;
+    }    
+    
+    fShader = glCreateShader(GL_FRAGMENT_SHADER);
+    glShaderSource(fShader, 1, &fragment, NULL);
+    glCompileShader(fShader);
+
+    if(checkShaderErrors("fragment", fShader))
+    {
+        return 0;
+    }    
+
+    GLuint program = glCreateProgram();
+    glAttachShader(program, vShader);
+    glAttachShader(program, fShader);
+    glLinkProgram(program);
+    
+    printf("linking shaders to program ...\n");
+    GLenum error = glGetError();
+    if(error)
+    {
+        fprintf(stderr, "error: %u\n", glGetError());
+        return 0;
+    }
+    else
+    {
+        printf("no error occured ...\n");
+    }
+
+    GLint compiled[1];
+    glGetProgramiv(program, GL_LINK_STATUS, compiled);
+    if(compiled[0])
+    {
+        printf("shaders successfully linked\n");
+    }
+    else
+    {
+        fprintf(stderr, "linking of shaders failed:\n");
+        GLchar buffer[512];
+        GLsizei bufferSize = 512;
+        GLsizei charsUsed = 0;
+        glGetProgramInfoLog(program, bufferSize, &charsUsed, buffer);
+        buffer[bufferSize - 1] = '\0';
+        fprintf(stderr, "%s\n", buffer);
+        return 0;
+    }
+    
+    glUseProgram(program);
+    return program;
+}
+
+static GLuint createProgram()
+{
+    GLchar* vertex = readFile("vertex.vs");
+    if(vertex == NULL)
+    {
+        fprintf(stderr, "cannot read vertex.vs\n");
+        return 0;
+    }
+    GLchar* fragment = readFile("fragment.fs");
+    if(fragment == NULL)
+    {
+        fprintf(stderr, "cannot read fragment.fs\n");
+        free(vertex);
+        return 0;
+    }
+    
+    GLuint program = compileProgram(vertex, fragment);
+    flushOutput();
+    free(vertex);
+    free(fragment);
+    return program;
+}
+
+static void onTerm(GLFWwindow* window)
+{
+    glfwDestroyWindow(window);
+    glfwTerminate();
+}
+
+int startGame(char* name, InitFunction init, TickFunction tick, RenderTickFunction renderTick, WindowResize inWindowResize, MouseMove move)
+{
+    if(!glfwInit())
+    {
+        fprintf(stderr, "could not initialize GLFW");
+        return 1;
+    }   
+    
+    windowResize = inWindowResize;
+    mouseMove = move;
+
+    glfwDefaultWindowHints();
+    glfwWindowHint(GLFW_VISIBLE, 0);
+    glfwWindowHint(GLFW_RESIZABLE, 1);
+    GLFWwindow* window = glfwCreateWindow(640, 480, name, NULL, NULL);
+    if(!window)
+    {
+        fprintf(stderr, "could not create window");
+        glfwTerminate();
+        return 1;
+    }
+    
+    glfwSetKeyCallback(window, onKeyEvent);
+    glfwSetFramebufferSizeCallback(window, onWindowResize);
+    glfwSetMouseButtonCallback(window, onMouseClick);
+    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+    activeFocus = 1;
+    glfwSetCursorPosCallback(window, onMouseMove);
+
+    glfwMakeContextCurrent(window);
+    glfwSwapInterval(1);
+    
+    glfwShowWindow(window);
+    
+    GLenum err = glewInit();
+    if (GLEW_OK != err)
+    {
+        fprintf(stderr, "Could not initialize GLEW: %s\n", glewGetErrorString(err));
+        return 1;
+    }
+    printf("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
+
+    program = createProgram();
+    if(program == 0)
+    {
+        onTerm(window);
+        return 1;
+    }
+    
+    //glEnable(GL_CULL_FACE);
+    glEnable(GL_DEPTH_TEST);
+    glDepthFunc(GL_LEQUAL);
+    init(program);
+    
+    //printf("%lu\n", glfwGetTimerFrequency());
+    //printf("%lu\n", glfwGetTimerValue());
+    //printf("%lu\n", glfwGetTimerValue());
+    
+    long newTime = glfwGetTimerValue();
+    long oldTime = newTime;
+    long lag = 0;
+    while(!glfwWindowShouldClose(window))
+    {
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+        oldTime = newTime;
+        newTime = glfwGetTimerValue();
+        lag += newTime - oldTime;
+
+        int ticksPerFrame = 0;
+        while(lag >= NANOS_PER_TICK)
+        {
+            lag -= NANOS_PER_TICK;
+
+            controlTick();
+            tick(program);
+            ticksPerFrame++;
+            
+            if(ticksPerFrame >= MAX_TICKS_PER_FRAME)
+            {
+                long skip = lag / NANOS_PER_TICK;
+                lag -= skip * NANOS_PER_TICK;
+                if(skip > 0)
+                {
+                    printf("skipped %ld game ticks %ld\n", skip, lag);
+                }
+                break;
+            }
+        }
+
+        renderTick(program, (float) lag / NANOS_PER_TICK);
+        
+        glfwSwapBuffers(window);
+        glfwPollEvents();
+    }
+
+    glDeleteShader(vShader);
+    glDeleteShader(fShader);
+    glDeleteProgram(program);
+    onTerm(window);
+    return 0;
+}
+
+GLuint getProgram()
+{
+    return program;
+}

+ 22 - 0
GameEngine.h

@@ -0,0 +1,22 @@
+#ifndef GAMEENGINE_H
+#define GAMEENGINE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glew.h>
+#include <GLFW/glfw3.h>
+
+static const long NANOS_PER_TICK = 50000000;
+static const int MAX_TICKS_PER_FRAME = 5;
+
+typedef void (*InitFunction) (int);
+typedef void (*TickFunction) (int);
+typedef void (*RenderTickFunction) (int, float);
+typedef void (*WindowResize) (int, int);
+typedef void (*MouseMove) (float, float);
+
+int startGame(char* name, InitFunction init, TickFunction tick, RenderTickFunction renderTick, WindowResize windowResize, MouseMove move);
+GLuint getProgram();
+
+#endif
+

+ 680 - 0
Main.c

@@ -0,0 +1,680 @@
+#include <math.h>
+#include "Vector3D.h"
+#include "GameEngine.h"
+#include "Control.h"
+
+long tickCounter = 0;
+long renderTickCounter = 0;
+
+long tickSum = 0;
+long renderTickSum = 0;
+
+long tickTime = -1;
+long renderTickTime = -1;
+
+typedef struct GameField
+{
+    int id;
+    float r;
+    float g;
+    float b;
+    float a;
+} GameField;
+
+int amountGameFields = -1;
+GameField* gameFields = NULL;
+
+GLuint vbo = -1;
+GLuint vba = -1;
+
+int fieldSize = 0;
+int quality = 0;
+int vertices = 0;
+GLsizeiptr size = 0;
+GLfloat* data = NULL;
+
+Vector3D oldCamera;
+Vector3D camera;
+float oldLengthAngle;
+float lengthAngle;
+float oldWidthAngle;
+float widthAngle;
+
+float mouseX = 0.0f;
+float mouseY = 0.0f;
+
+Vector3D front;
+Vector3D back;
+Vector3D right;
+Vector3D left;
+Vector3D up;
+Vector3D down;
+
+int selectedIndex = -1;
+int selectedLayer = -1;
+
+GameField* getGameField(int layer, int index)
+{   
+    if(layer == 0)
+    {
+        return &gameFields[0];
+    }
+    else if(layer == 1 + fieldSize)
+    {
+        return &gameFields[1];
+    }
+    
+    index += (layer - 1) * fieldSize + 2;
+    if(index < 2 || index >= amountGameFields)
+    {
+        return NULL;
+    }
+    return &gameFields[index];
+}
+
+float interpolate(float lag, float old, float new)
+{
+    return old + lag * (new - old);
+}
+
+void colorField(GameField* field)
+{
+    if(field->id)
+    {
+        field->r = 1.0f;
+        field->g = 0.0f;
+        field->b = 0.0f;
+        field->a = 1.0f;
+    }
+    else
+    {
+        field->r = 1.0f;
+        field->g = 1.0f;
+        field->b = 1.0f;
+        field->a = 1.0f;
+    }
+}
+
+void generateSphere()
+{
+    int triangles = fieldSize * quality;
+    int layers = (2 + fieldSize) * quality;
+    
+    if(gameFields == NULL)
+    {
+        amountGameFields = 2 + fieldSize * fieldSize;
+        gameFields = malloc(sizeof(GameField) * amountGameFields);
+        for(int i = 0; i < amountGameFields; i++)
+        {
+            gameFields[i].id = (rand() < RAND_MAX / 2) ? 1 : 0;
+            colorField(&gameFields[i]);
+        }
+    }
+    
+    if(data == NULL)
+    {
+        vertices = triangles * layers * 3 * 2;
+        size = sizeof(GLfloat) * 9 * vertices;
+        data = malloc(size);
+    }
+
+    for(int l = 0; l < layers; l++)
+    {
+        float high1 = cos((M_PI * l) / layers);
+        float high2 = cos((M_PI * (l + 1)) / layers);
+        
+        float texHigh1 = (l % quality) * (1.0f / quality);  
+        float texHigh2 = texHigh1 + (1.0f / quality);  
+        
+        float r1 = sqrt(1 - high1 * high1);
+        float r2 = sqrt(1 - high2 * high2);
+        
+        int flag = (l / quality) == 0;
+        if(flag)
+        {
+            texHigh1 = 1 - texHigh1;
+            texHigh2 = 1 - texHigh2;
+        }
+        flag |= (l / quality) >= fieldSize + 1;
+        
+        for(int i = 0; i < triangles; i++)
+        {
+            float first = 2 * M_PI * i / triangles;
+            float second = 2 * M_PI * (i + 1) / triangles;
+
+            int off = 27 * 2 * i + l * triangles * 27 * 2;
+            
+            float texWidth1 = (i % quality) * (1.0f / quality);  
+            float texWidth2 = texWidth1 + (1.0f / quality);  
+            
+            float r = 0.0f;
+            float g = 0.0f;
+            float b = 0.0f;
+            float a = 0.0f;
+            
+            GameField* field = getGameField(l / quality, i / quality);
+            if(field != NULL)
+            {
+                r = field->r;
+                g = field->g;
+                b = field->b;
+                a = field->a;
+            }
+            
+            for(int j = 0; j < 54; j += 9)
+            {
+                data[3 + off + j] = r;
+                data[4 + off + j] = g;
+                data[5 + off + j] = b;
+                data[6 + off + j] = a;
+            }
+            
+            data[0 + off] = r2 * cos(first);
+            data[1 + off] = high2;
+            data[2 + off] = r2 * sin(first);
+            
+            data[9 + off] = r1 * cos(first);
+            data[10 + off] = high1;
+            data[11 + off] = r1 * sin(first);
+            
+            data[18 + off] = r1 * cos(second);
+            data[19 + off] = high1;
+            data[20 + off] = r1 * sin(second);
+            
+            if(flag)
+            {
+                data[7 + off] = texHigh2 * 0.5f;
+                data[8 + off] = texHigh2 * 0.5f;
+
+                data[16 + off] = texHigh1 * 0.5f;
+                data[17 + off] = texHigh1 * 0.5f;
+
+                data[25 + off] = texHigh1 * 0.5f;
+                data[26 + off] = texHigh1 * 0.5f;
+            }
+            else
+            {
+                data[7 + off] = texWidth1;
+                data[8 + off] = texHigh2;
+
+                data[16 + off] = texWidth1;
+                data[17 + off] = texHigh1;
+
+                data[25 + off] = texWidth2;
+                data[26 + off] = texHigh1;
+            }
+
+            data[27 + off] = r2 * cos(first);
+            data[28 + off] = high2;
+            data[29 + off] = r2 * sin(first);            
+
+            data[36 + off] = r1 * cos(second);
+            data[37 + off] = high1;
+            data[38 + off] = r1 * sin(second);
+            
+            data[45 + off] = r2 * cos(second);
+            data[46 + off] = high2;
+            data[47 + off] = r2 * sin(second);
+            
+            if(flag)
+            {
+                data[34 + off] = texHigh2 * 0.5f;
+                data[35 + off] = texHigh2 * 0.5f;
+
+                data[43 + off] = texHigh1 * 0.5f;
+                data[44 + off] = texHigh1 * 0.5f;
+
+                data[52 + off] = texHigh2 * 0.5f;
+                data[53 + off] = texHigh2 * 0.5f;
+            }
+            else
+            {
+                data[34 + off] = texWidth1;
+                data[35 + off] = texHigh2;
+
+                data[43 + off] = texWidth2;
+                data[44 + off] = texHigh1;
+
+                data[52 + off] = texWidth2;
+                data[53 + off] = texHigh2;
+            }
+        }
+    }
+
+    glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
+}
+
+void updateProjection(float aspect, int program)
+{
+    float fovY = 60.0f;
+    float nearClip = 0.1f;
+    float farClip = 1000;
+    
+    float q = 1.0f / (float) tan((0.5f * fovY) * M_PI / 180.0f);
+    float a = q / aspect;
+    float b = (nearClip + farClip) / (nearClip - farClip);
+    float c = (2.0f * nearClip * farClip) / (nearClip - farClip);
+
+    GLfloat data[16];
+    data[0] = a;
+    data[1] = 0.0f;
+    data[2] = 0.0f;
+    data[3] = 0.0f;
+    
+    data[4] = 0.0f;
+    data[5] = q;
+    data[6] = 0.0f;
+    data[7] = 0.0f;
+    
+    data[8] = 0.0f;
+    data[9] = 0.0f;
+    data[10] = b;
+    data[11] = -1.0f;
+    
+    data[12] = 0.0f;
+    data[13] = 0.0f;
+    data[14] = c;
+    data[15] = 0;
+    
+    GLint loc = glGetUniformLocation(program, "projMatrix");
+    glUniformMatrix4fv(loc, 1, 0, data);
+}
+
+float chooseSmallerPositive(float a, float b)
+{
+    if(a < 0)
+    {
+        return b;
+    }
+    else if(b < 0 || b > a)
+    {
+        return a;
+    }
+    return b;
+}
+
+void updateView(int program, float lag)
+{
+    // front
+    vectorSetAngles(&front, interpolate(lag, oldLengthAngle, lengthAngle), interpolate(lag, oldWidthAngle, widthAngle));
+    
+    // calculate selected tile
+    float a = camera.x * camera.x + camera.y * camera.y + camera.z * camera.z - 1;
+    float b = 2 * (camera.x * front.x + camera.y * front.y + camera.z * front.z);
+    float c = front.x * front.x + front.y * front.y + front.z * front.z;
+
+    float p = b / c;
+    float q = a / c;
+
+    float det = p * p * 0.25f - q;
+    if(det >= 0)
+    {
+        float k = sqrt(det);
+        k = chooseSmallerPositive(-0.5f * p + k, -0.5f * p - k);
+        if(k >= 0)
+        {
+            Vector3D v;
+            vectorSetTo(&v, &camera);
+            vectorAddMul(&v, &front, k);
+
+            selectedLayer = (asin(-v.y) + M_PI_2) * M_1_PI * (2 + fieldSize);
+            if(selectedLayer == 2 + fieldSize)
+            {
+                selectedLayer = fieldSize + 1;
+            }
+
+            float tan = atan2(v.z, v.x) * M_1_PI * 0.5;
+            tan += (tan < 0);
+            selectedIndex = tan * fieldSize;
+        }
+    }
+
+    // back
+    vectorSetToInverse(&back, &front);
+
+    // right
+    vectorSetTo(&right, &front);
+    vectorCross(&right, 0.0f, 1.0f, 0.0f);
+    vectorNormalize(&right);
+    
+    // left
+    vectorSetToInverse(&left, &right);
+
+    // up
+    vectorSetTo(&up, &front);
+    vectorCrossWith(&up, &left);
+    vectorNormalize(&up);
+    
+    // down
+    vectorSetToInverse(&down, &up);
+
+    GLfloat data[16];
+    data[0] = right.x;
+    data[1] = up.x;
+    data[2] = back.x;
+    data[3] = 0.0f;
+    
+    data[4] = right.y;
+    data[5] = up.y;
+    data[6] = back.y;
+    data[7] = 0.0f;
+    
+    data[8] = right.z;
+    data[9] = up.z;
+    data[10] = back.z;
+    data[11] = 0.0f;
+    
+    Vector3D interCam;
+    vectorSetTo(&interCam, &oldCamera);
+    vectorAddMul(&interCam, &camera, lag);
+    vectorAddMul(&interCam, &oldCamera, -lag);
+    
+    data[12] = vectorDotInverse(&right, &interCam);
+    data[13] = vectorDotInverse(&up, &interCam);
+    data[14] = vectorDotInverse(&back, &interCam);
+    data[15] = 1.0f;
+    
+    //printf("%f %f %f %f\n", data[0], data[4], data[8], data[12]);
+    //printf("%f %f %f %f\n", data[1], data[5], data[9], data[13]);
+    //printf("%f %f %f %f\n", data[2], data[6], data[10], data[14]);
+    //printf("%f %f %f %f\n", data[3], data[7], data[11], data[15]);
+    
+    // front
+    vectorSetAngles(&front, interpolate(lag, oldLengthAngle, lengthAngle), interpolate(lag, oldWidthAngle, widthAngle));
+    front.y = 0;
+    vectorNormalize(&front);
+
+    // back
+    vectorSetToInverse(&back, &front);
+
+    // right
+    vectorSetTo(&right, &front);
+    vectorCross(&right, 0.0f, 1.0f, 0.0f);
+    vectorNormalize(&right);
+    
+    // left
+    vectorSetToInverse(&left, &right);
+
+    // up
+    vectorSet(&up, 0.0f, 1.0f, 0.0f);
+    
+    // down
+    vectorSetToInverse(&down, &up);
+    
+    GLint loc = glGetUniformLocation(program, "viewMatrix");
+    glUniformMatrix4fv(loc, 1, 0, data);
+}
+
+void init(int program)
+{
+    printf("Init\n");
+    
+    glGenBuffers(1, &vbo);
+    glGenVertexArrays(1, &vba);
+    
+    glBindBuffer(GL_ARRAY_BUFFER, vbo);
+    glBindVertexArray(vba);
+
+    glVertexAttribPointer(0, 3, GL_FLOAT, 0, 36, (GLvoid*) 0);
+    glEnableVertexAttribArray(0);
+
+    glVertexAttribPointer(1, 4, GL_FLOAT, 0, 36, (GLvoid*) 12);
+    glEnableVertexAttribArray(1);       
+    
+    glVertexAttribPointer(2, 2, GL_FLOAT, 0, 36, (GLvoid*) 28);
+    glEnableVertexAttribArray(2);    
+    
+    generateSphere();
+    
+    vectorSet(&camera, 0.0f, 0.0f, -5.0f);
+    lengthAngle = 0.0f;
+    widthAngle = 0.0f;
+    
+    updateProjection(4.0f / 3.0f, program);
+}
+
+void invertField(int layer, int index)
+{
+    GameField* field = getGameField(layer, index);
+    if(field != NULL)
+    {
+        field->id = 1 - field->id;
+        colorField(field);
+    }
+}
+
+void invertAt(int layer, int index)
+{
+    if(layer == 0)
+    {
+        invertField(layer, index);
+        layer++;
+        for(int i = 0; i < fieldSize ; i++)
+        {
+            invertField(layer, i);
+        }
+    }
+    else if(layer == fieldSize + 1)
+    {
+        invertField(layer, index);
+        layer--;
+        for(int i = 0; i < fieldSize ; i++)
+        {
+            invertField(layer, i);
+        }
+    }
+    else
+    {
+        invertField(layer, index);
+        invertField(layer, (index + 1) % fieldSize);
+        invertField(layer, (index - 1 + fieldSize) % fieldSize);
+        invertField(layer + 1, index);
+        invertField(layer - 1, index);
+    }
+}
+
+void tick(int program)
+{
+    if(tickTime == -1)
+    {
+        tickTime = glfwGetTimerValue();
+    }
+    else
+    {
+        long time = glfwGetTimerValue();
+        tickSum += time - tickTime;
+        tickTime = time;
+        
+        tickCounter++;
+    }
+    
+    vectorSetTo(&oldCamera, &camera);
+    oldLengthAngle = lengthAngle;
+    oldWidthAngle = widthAngle;
+    
+    float factor = 0.05f;
+    if(keyIsDown(GLFW_KEY_A))
+    {
+        vectorAddMul(&camera, &left, factor);
+    }
+    if(keyIsDown(GLFW_KEY_D))
+    {
+        vectorAddMul(&camera, &right, factor);
+    }
+    if(keyIsDown(GLFW_KEY_W))
+    {
+        vectorAddMul(&camera, &front, factor);
+    }
+    if(keyIsDown(GLFW_KEY_S))
+    {
+        vectorAddMul(&camera, &back, factor);
+    }
+    if(keyIsDown(GLFW_KEY_SPACE))
+    {
+        vectorAddMul(&camera, &up, factor);
+    }
+    if(keyIsDown(GLFW_KEY_LEFT_SHIFT))
+    {
+        vectorAddMul(&camera, &down, factor);
+    }
+    
+    if(mouseIsJustDown(GLFW_MOUSE_BUTTON_1) && selectedLayer != -1 && selectedIndex != -1)
+    {
+        invertAt(selectedLayer, selectedIndex);
+        generateSphere();
+        selectedLayer = -1;
+        selectedIndex = -1;
+    }
+    
+    widthAngle -= mouseY * 0.1f;
+    lengthAngle -= mouseX * 0.1f;
+    
+    if(widthAngle >= 89.5)
+    {
+        widthAngle = 89.5f;
+    }
+    else if(widthAngle <= -89.5)
+    {
+        widthAngle = -89.5f;
+    }
+    
+    mouseX = 0.0f;
+    mouseY = 0.0f;
+}
+
+void renderTick(int program, float lag)
+{
+    if(renderTickTime == -1)
+    {
+        renderTickTime = glfwGetTimerValue();
+    }
+    else
+    {
+        long time = glfwGetTimerValue();
+        renderTickSum += time - renderTickTime;
+        renderTickTime = time;
+        
+        renderTickCounter++;
+    }
+    
+    updateView(program, lag);
+    
+    glBindBuffer(GL_ARRAY_BUFFER, vbo);
+    glBindVertexArray(vba);
+    glDrawArrays(GL_TRIANGLES, 0, vertices);
+}
+
+void onWindowResize(int width, int height)
+{
+    updateProjection((float) width / height, getProgram());
+}
+
+void onMouseMove(float x, float y)
+{
+    mouseX += x;
+    mouseY += y;
+}
+
+int readPositiveInt(int from, int to, char* message, int error)
+{
+    fputs(message, stdout);
+    fflush(stdout);
+    
+    int size = 16;
+    int index = 0;
+    char* buffer = malloc(sizeof(char) * size);
+    
+    while(1)
+    {
+        int c = fgetc(stdin);
+        if(c == EOF)
+        {
+            free(buffer);
+            return error;
+        }
+        else if(c == '\n')
+        {
+            int number = 0;
+            int i = 0;
+            while(i < index)
+            {
+                int n = buffer[i] - '0';
+                if(n < 0 || n > 9)
+                {
+                    break;
+                }
+                number = number * 10 + n;
+                if(number > to)
+                {
+                    break;
+                }
+                i++;
+            }
+            
+            if(i >= index && number >= from)
+            {
+                free(buffer);
+                return number;
+            }
+            
+            fputs(message, stdout);
+            fflush(stdout);
+            index = 0;
+            continue;
+        }
+        
+        if(index >= size)
+        {
+            size *= 2;
+            buffer = realloc(buffer, sizeof(char) * size);
+        }
+        buffer[index] = c;
+        index++;
+    }
+}
+
+int main()
+{   
+    /*fieldSize = readPositiveInt(5, 20, "Select a game size from 5 to 20:\n", 0);
+    if(fieldSize == 0)
+    {
+        printf("Cannot read from stdin\n");
+        return EXIT_SUCCESS;
+    }
+    quality = readPositiveInt(1, 9, "Select a rendering quality from 1 to 9:\n", 0);
+    if(quality == 0)
+    {
+        printf("Cannot read from stdin\n");
+        return EXIT_SUCCESS;
+    }*/
+    fieldSize = 5;
+    quality = 8;
+    
+    if(startGame("Test Game", init, tick, renderTick, onWindowResize, onMouseMove))
+    {
+        fprintf(stderr, "Exited with error\n");
+        //return EXIT_FAILURE;
+    }
+    
+    if(data != NULL)
+    {
+        free(data);
+    }
+    if(gameFields != NULL)
+    {
+        free(gameFields);
+    }
+    
+    glDeleteBuffers(1, &vbo);
+    glDeleteVertexArrays(1, &vba);
+    
+    printf("_______________TPS_______________\n");
+    printf("%ld  %ld  %ld\n", tickCounter, tickSum, tickTime);
+    printf("%lf\n", (double) tickSum / tickCounter);
+    printf("%lf\n", 1000000000.0 * tickCounter / tickSum);
+    printf("_______________FPS_______________\n");
+    printf("%ld  %ld  %ld\n", renderTickCounter, renderTickSum, renderTickTime);
+    printf("%lf\n", (double) renderTickSum / renderTickCounter);
+    printf("%lf\n", 1000000000.0 * renderTickCounter / renderTickSum);
+    return EXIT_SUCCESS;
+}

+ 14 - 0
Makefile

@@ -0,0 +1,14 @@
+CFLAGS = $(shell pkg-config --cflags glfw3)
+LDFLAGS = $(shell pkg-config --static --libs glfw3) -lGL -lGLEW
+
+all: game
+
+run: game
+	#./game
+	vblank_mode=0 optirun ./game
+	
+game: *.c *.h
+	gcc -o game *.c $(LDFLAGS)
+	
+clean:
+	rm -f game

+ 101 - 0
Vector3D.c

@@ -0,0 +1,101 @@
+#include "Vector3D.h"
+#include <math.h>
+
+void vectorSet(Vector3D* v, float x, float y, float z)
+{
+    v->x = x;
+    v->y = y;
+    v->z = z;
+}
+
+void vectorSetTo(Vector3D* v1, Vector3D* v2)
+{
+    v1->x = v2->x;
+    v1->y = v2->y;
+    v1->z = v2->z;
+}
+
+void vectorSetToInverse(Vector3D* v1, Vector3D* v2)
+{
+    v1->x = -v2->x;
+    v1->y = -v2->y;
+    v1->z = -v2->z;
+}
+
+void vectorSetMul(Vector3D* v1, Vector3D* v2, float f)
+{
+    v1->x = v2->x * f;
+    v1->y = v2->y * f;
+    v1->z = v2->z * f;
+}
+
+void vectorSetAngles(Vector3D* v, float lengthAngle, float widthAngle)
+{
+    lengthAngle = lengthAngle * M_PI / 180.0f;
+    widthAngle = widthAngle * M_PI / 180.0f;
+    v->x = cos(widthAngle) * sin(lengthAngle);
+    v->z = cos(widthAngle) * cos(lengthAngle);
+    v->y = sin(widthAngle);   
+}
+
+void vectorAdd(Vector3D* v1, Vector3D* v2)
+{
+    v1->x += v2->x;
+    v1->y += v2->y;
+    v1->z += v2->z;
+}
+
+void vectorSub(Vector3D* v1, Vector3D* v2)
+{
+    v1->x -= v2->x;
+    v1->y -= v2->y;
+    v1->z -= v2->z;
+}
+
+void vectorMul(Vector3D* v1, float f)
+{
+    v1->x *= f;
+    v1->y *= f;
+    v1->z *= f;
+}
+
+void vectorAddMul(Vector3D* v1, Vector3D* v2, float f)
+{
+    v1->x += v2->x * f;
+    v1->y += v2->y * f;
+    v1->z += v2->z * f;
+}
+
+void vectorCross(Vector3D* v, float x, float y, float z)
+{
+    vectorSet(v, v->y * z - v->z * y, v->z * x - v->x * z, v->x * y - v->y * x);
+}
+
+void vectorCrossWith(Vector3D* v1, Vector3D* v2)
+{
+    vectorSet(v1, v1->y * v2->z - v1->z * v2->y, v1->z * v2->x - v1->x * v2->z, v1->x * v2->y - v1->y * v2->x);
+}
+
+void vectorNormalize(Vector3D* v)
+{
+    float f = vectorLength(v);
+    v->x /= f;
+    v->y /= f;
+    v->z /= f;
+}
+
+float vectorLength(Vector3D* v)
+{
+    return sqrt(v->x * v->x + v->y * v->y + v->z * v->z);
+}
+    
+float vectorDot(Vector3D* v1, Vector3D* v2)
+{
+    return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z;
+}
+
+float vectorDotInverse(Vector3D* v1, Vector3D* v2)
+{
+    return v1->x * (-v2->x) + v1->y * (-v2->y) + v1->z * (-v2->z);
+}
+

+ 32 - 0
Vector3D.h

@@ -0,0 +1,32 @@
+#ifndef VECTOR3D_H
+#define VECTOR3D_H
+
+typedef struct Vector3D
+{
+    float x;
+    float y;
+    float z;
+} Vector3D;
+
+void vectorSet(Vector3D* v, float x, float y, float z);
+void vectorSetTo(Vector3D* v1, Vector3D* v2);
+void vectorSetToInverse(Vector3D* v1, Vector3D* v2);
+void vectorSetMul(Vector3D* v1, Vector3D* v2, float f);
+void vectorSetAngles(Vector3D* v, float lengthAngle, float widthAngle);
+
+void vectorAdd(Vector3D* v1, Vector3D* v2);
+void vectorSub(Vector3D* v1, Vector3D* v2);
+void vectorMul(Vector3D* v1, float f);
+void vectorAddMul(Vector3D* v1, Vector3D* v2, float f);
+
+void vectorCross(Vector3D* v, float x, float y, float z);
+void vectorCrossWith(Vector3D* v1, Vector3D* v2);
+
+void vectorNormalize(Vector3D* v);
+float vectorLength(Vector3D* v);
+    
+float vectorDot(Vector3D* v1, Vector3D* v2);
+float vectorDotInverse(Vector3D* v1, Vector3D* v2);
+
+#endif
+

+ 14 - 0
fragment.fs

@@ -0,0 +1,14 @@
+#version 430
+
+uniform mat4 projMatrix;
+uniform mat4 viewMatrix;
+
+in vec4 outColor;
+in vec2 outTex;
+out vec4 color;
+
+void main(void)
+{
+    //color = outColor * (length(outTex - vec2(0.5, 0.5)) + 0.5);
+    color = outColor * (max(abs(outTex.y - 0.5), abs(outTex.x - 0.5)) + 0.2);
+}

+ 18 - 0
vertex.vs

@@ -0,0 +1,18 @@
+#version 430
+
+layout (location = 0) in vec3 pos;
+layout (location = 1) in vec4 color;
+layout (location = 2) in vec2 tex;
+
+uniform mat4 projMatrix;
+uniform mat4 viewMatrix;
+
+out vec4 outColor;
+out vec2 outTex;
+
+void main(void)
+{ 
+    outTex = tex;
+    outColor = color;
+    gl_Position = projMatrix * viewMatrix * vec4(pos, 1.0);
+}