#include "Control.h" #include "GameEngine.h" #include "Vector3D.h" #include 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; }