From 524dcab7be7fe1c8c49558c4bd0de7ec26edadeb Mon Sep 17 00:00:00 2001 From: Furkan Mudanyali Date: Sun, 28 May 2023 05:36:53 +0300 Subject: [PATCH] gears scene working with the sheer power of iman --- .clang-format | 2 +- .clangd | 3 +- CMakeLists.txt | 6 +- src/game/game.cpp | 8 +- src/graphics/graphics.cpp | 178 ++++++++++++++ src/graphics/graphics.h | 93 +++++++- src/graphics/opengl/opengl.cpp | 82 ------- src/graphics/opengl/opengl.h | 25 -- src/input/input.cpp | 2 +- src/scene/example/example.cpp | 16 -- src/scene/example/example.h | 12 - src/scene/gears/gears.cpp | 411 +++++++++++++++++++++++++++++++++ src/scene/gears/gears.h | 114 +++++++++ src/scene/scene.h | 2 +- 14 files changed, 802 insertions(+), 152 deletions(-) create mode 100644 src/graphics/graphics.cpp delete mode 100644 src/graphics/opengl/opengl.cpp delete mode 100644 src/graphics/opengl/opengl.h delete mode 100644 src/scene/example/example.cpp delete mode 100644 src/scene/example/example.h create mode 100644 src/scene/gears/gears.cpp create mode 100644 src/scene/gears/gears.h diff --git a/.clang-format b/.clang-format index b53a3a4..acf7780 100644 --- a/.clang-format +++ b/.clang-format @@ -5,4 +5,4 @@ SortIncludes: false Language: Cpp # Force pointers to the type for C++. DerivePointerAlignment: false -PointerAlignment: Left \ No newline at end of file +PointerAlignment: Left diff --git a/.clangd b/.clangd index f4c3933..69b4996 100644 --- a/.clangd +++ b/.clangd @@ -11,7 +11,8 @@ Diagnostics: modernize-avoid-c-arrays, readability-magic-numbers, readability-implicit-bool-conversion, - readability-identifier-length + readability-identifier-length, + bugprone-easily-swappable-parameters ] Completion: diff --git a/CMakeLists.txt b/CMakeLists.txt index 3accde9..90cc835 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,11 +67,11 @@ SET(CMAKE_CXX_FLAGS SET(SOURCE_FILES src/game/game.cpp - src/graphics/opengl/opengl.cpp + src/graphics/graphics.cpp src/input/input.cpp - src/scene/example/example.cpp + src/scene/gears/gears.cpp src/window/window.cpp @@ -114,8 +114,8 @@ ENDIF() TARGET_LINK_LIBRARIES(HomdEngine sdl2 - glew32 opengl32 + glew32 ${win32_link} ) diff --git a/src/game/game.cpp b/src/game/game.cpp index 2a0563e..274fcae 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -4,14 +4,14 @@ */ #include -#include -#include +#include +#include Game::Game() { this->pWindow = new Window(this); - this->pRenderer = new OpenGL(this); + this->pRenderer = new Graphics(this); this->pInput = new Input(this); - this->scenes.push(new Example(this)); + this->scenes.push(new GearsScene(this)); } void Game::loop() { diff --git a/src/graphics/graphics.cpp b/src/graphics/graphics.cpp new file mode 100644 index 0000000..5d8c596 --- /dev/null +++ b/src/graphics/graphics.cpp @@ -0,0 +1,178 @@ +/** + * Copyright (c) 2023, Furkan Mudanyali + * All rights reserved + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +void GLAPIENTRY MessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) { + fprintf(stderr, + "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, + severity, message); +} + +Graphics::Graphics(Game* game) { + this->pGame = game; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetSwapInterval(0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + this->context = SDL_GL_CreateContext(this->pGame->pWindow->window); + + glewExperimental = GL_TRUE; + glewInit(); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(MessageCallback, nullptr); +} + +void Graphics::storeVertexBufObj(GLuint& dest, GLsizeiptr size, int* target) { + // Store the vertices in a vertex buffer object + glGenBuffers(1, &dest); + glBindBuffer(GL_ARRAY_BUFFER, dest); + glBufferData(GL_ARRAY_BUFFER, size, target, GL_STATIC_DRAW); +} + +void Graphics::multiply4x4Matrices(GLfloat* m, const GLfloat* n) { + GLfloat tmp[16]; + const GLfloat* row; + const GLfloat* column; + div_t d; + + for (int i = 0; i < 16; i++) { + tmp[i] = 0; + d = div(i, 4); + row = n + d.quot * 4; + column = m + d.rem; + for (int j = 0; j < 4; j++) { + tmp[i] += row[j] * column[j * 4]; + } + } + memcpy(m, &tmp, sizeof tmp); +} + +void Graphics::rotate4x4Matrix(GLfloat* m, + GLfloat angle, + GLfloat x, + GLfloat y, + GLfloat z) { + double sin; + double cos; + + sincos(angle, &sin, &cos); + GLfloat r[16] = {(GLfloat)(x * x * (1 - cos) + cos), + (GLfloat)(y * x * (1 - cos) + z * sin), + (GLfloat)(x * z * (1 - cos) - y * sin), + 0, + (GLfloat)(x * y * (1 - cos) - z * sin), + (GLfloat)(y * y * (1 - cos) + cos), + (GLfloat)(y * z * (1 - cos) + x * sin), + 0, + (GLfloat)(x * z * (1 - cos) + y * sin), + (GLfloat)(y * z * (1 - cos) - x * sin), + (GLfloat)(z * z * (1 - cos) + cos), + 0, + 0, + 0, + 0, + 1}; + + multiply4x4Matrices(m, r); +} + +void Graphics::translate4x4Matrix(GLfloat* m, GLfloat x, GLfloat y, GLfloat z) { + GLfloat t[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1}; + + multiply4x4Matrices(m, t); +} + +void Graphics::create4x4IdentityMatrix(GLfloat* m) { + GLfloat t[16] = { + 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, + }; + + memcpy(m, t, sizeof(t)); +} + +void Graphics::transpose4x4Matrix(GLfloat* m) { + GLfloat t[16] = {m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]}; + + memcpy(m, t, sizeof(t)); +} + +void Graphics::invert4x4Matrix(GLfloat* m) { + GLfloat t[16]; + create4x4IdentityMatrix(t); + + // Extract and invert the translation part 't'. The inverse of a + // translation matrix can be calculated by negating the translation + // coordinates. + t[12] = -m[12]; + t[13] = -m[13]; + t[14] = -m[14]; + + // Invert the rotation part 'r'. The inverse of a rotation matrix is + // equal to its transpose. + m[12] = m[13] = m[14] = 0; + transpose4x4Matrix(m); + + // inv(m) = inv(r) * inv(t) + multiply4x4Matrices(m, t); +} + +void Graphics::calcPerspectiveProjectTransformation(GLfloat* m, + GLfloat yFOV, + GLfloat aspect, + GLfloat zNear, + GLfloat zFar) { + GLfloat tmp[16]; + create4x4IdentityMatrix(tmp); + + double sine; + double cosine; + double cotangent; + double deltaZ; + GLfloat radians = yFOV / 2.0F * (float)M_PI / 180.0F; + + deltaZ = zFar - zNear; + sincos(radians, &sine, &cosine); + + if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) { + return; + } + + cotangent = cosine / sine; + + tmp[0] = (float)cotangent / aspect; + tmp[5] = (float)cotangent; + tmp[10] = -(zFar + zNear) / (float)deltaZ; + tmp[11] = -1; + tmp[14] = -2 * zNear * zFar / (float)deltaZ; + tmp[15] = 0; + + memcpy(m, tmp, sizeof(tmp)); +} + +void Graphics::draw() { + SDL_GL_SwapWindow(this->pGame->pWindow->window); +} \ No newline at end of file diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 3bf35cd..bcb8f0b 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -3,17 +3,98 @@ * All rights reserved */ -#ifndef _HOMD_GRAPHICS -#define _HOMD_GRAPHICS +#ifndef _HOMD_GRAPHICS_GL +#define _HOMD_GRAPHICS_GL + +#define GLEW_STATIC + +#include +#include +#include class Game; +class Window; class Graphics { + Game* pGame; + SDL_GLContext context = nullptr; + public: - Game* game; - Graphics() = default; - virtual ~Graphics() = default; - virtual void drawSomething() = 0; + Graphics(Game*); + ~Graphics() = default; + + void setGLContext(); + + void draw(); + + static void storeVertexBufObj(GLuint&, GLsizeiptr, int*); + + /** + * Multiplies two 4x4 matrices + * + * The result is stored in matrix m. + * + * @param m first matrix + * @param n second matrix + */ + static void multiply4x4Matrices(GLfloat*, const GLfloat*); + + /** + * Rotates a 4x4 matrix + * + * @param[in,out] m the matrix to rotate + * @param angle the angle to rotate + * @param x the x component of the direction to rotate to + * @param y the y component of the direction to rotate to + * @param z the z component of the direction to rotate to + */ + static void rotate4x4Matrix(GLfloat*, GLfloat, GLfloat, GLfloat, GLfloat); + + /** + * Translates a 4x4 matrix + * + * @param[in,out] m the matrix to translate + * @param x the x component of the direction to translate to + * @param y the y component of the direction to translate to + * @param z the z component of the direction to translate to + */ + static void translate4x4Matrix(GLfloat*, GLfloat, GLfloat, GLfloat); + + /** + * Creates a 4x4 identity matrix + * + * @param m the matrix to convert to an identity matrix + */ + static void create4x4IdentityMatrix(GLfloat*); + + /** + * Transposes a 4x4 matrix. + * + * @param m the matrix to transpose + */ + static void transpose4x4Matrix(GLfloat*); + + /** + * Inverts a 4x4 matrix. + * + * @param m the matrix to invert + */ + static void invert4x4Matrix(GLfloat*); + + /** + * Calculate a perspective projection transformation. + * + * @param m the matrix to save the transformation in + * @param yFOV the field of view in the y direction + * @param aspect the view aspect ratio + * @param zNear the near clipping plane + * @param zFar the far clipping plane + */ + static void calcPerspectiveProjectTransformation(GLfloat*, + GLfloat, + GLfloat, + GLfloat, + GLfloat); }; #endif \ No newline at end of file diff --git a/src/graphics/opengl/opengl.cpp b/src/graphics/opengl/opengl.cpp deleted file mode 100644 index 7d51911..0000000 --- a/src/graphics/opengl/opengl.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) 2023, Furkan Mudanyali - * All rights reserved - */ - -#include -#define GLEW_STATIC -#include -#include - -#include -#include -#include -#include -#include - -static const GLchar* vertexShaderSrc = R"( - attribute vec4 position; - void main() { - gl_Position = vec4(position.xyz, 1.0); - } -)"; - -static const GLchar* fragmentShaderSrc = R"( - precision mediump float; - void main() { - gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); - } -)"; - -static GLfloat vertices[] = {0.0, 0.8, -0.8, -0.8, 0.8, -0.8}; - -OpenGL::OpenGL(Game* pGame) { - glewExperimental = GL_TRUE; - this->game = pGame; - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetSwapInterval(0); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - - this->context = SDL_GL_CreateContext(this->game->pWindow->window); - - GLenum err = glewInit(); - std::cout << glewGetErrorString(err); - - GLuint vertexArrObj; - glGenVertexArrays(1, &vertexArrObj); - glBindVertexArray(vertexArrObj); - - GLuint vertexBufObj; - glGenBuffers(1, &vertexBufObj); - - glBindBuffer(GL_ARRAY_BUFFER, vertexBufObj); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertexShader, 1, &vertexShaderSrc, nullptr); - glCompileShader(vertexShader); - - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShader, 1, &fragmentShaderSrc, nullptr); - glCompileShader(fragmentShader); - - GLuint shaderProgram = glCreateProgram(); - glAttachShader(shaderProgram, vertexShader); - glAttachShader(shaderProgram, fragmentShader); - glLinkProgram(shaderProgram); - glUseProgram(shaderProgram); - - GLint posAttribute = glGetAttribLocation(shaderProgram, "position"); - glEnableVertexAttribArray(posAttribute); - glVertexAttribPointer(posAttribute, 2, GL_FLOAT, GL_FALSE, 0, nullptr); -} - -void OpenGL::drawSomething() { - glClearColor(0.0F, 0.0F, 0.0F, 1.0F); - glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLES, 0, 3); - SDL_GL_SwapWindow(this->game->pWindow->window); -} \ No newline at end of file diff --git a/src/graphics/opengl/opengl.h b/src/graphics/opengl/opengl.h deleted file mode 100644 index 9ed2fc3..0000000 --- a/src/graphics/opengl/opengl.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2023, Furkan Mudanyali - * All rights reserved - */ - -#ifndef _HOMD_GRAPHICS_GL -#define _HOMD_GRAPHICS_GL - -#include -#include - -class Window; - -class OpenGL : public Graphics { - SDL_GLContext context = nullptr; - - public: - OpenGL(Game*); - ~OpenGL() override = default; - - void setGLContext(); - void drawSomething() override; -}; - -#endif \ No newline at end of file diff --git a/src/input/input.cpp b/src/input/input.cpp index b6b02a0..00bb96b 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -3,8 +3,8 @@ * All rights reserved */ -#include #include +#include Input::Input(Game* pGame) { this->game = pGame; diff --git a/src/scene/example/example.cpp b/src/scene/example/example.cpp deleted file mode 100644 index e356631..0000000 --- a/src/scene/example/example.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) 2023, Furkan Mudanyali - * All rights reserved - */ - -#include -#include -#include - -Example::Example(Game* pGame) { - this->game = pGame; -} - -void Example::draw() { - this->game->pRenderer->drawSomething(); -} \ No newline at end of file diff --git a/src/scene/example/example.h b/src/scene/example/example.h deleted file mode 100644 index 331ce9c..0000000 --- a/src/scene/example/example.h +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2023, Furkan Mudanyali - * All rights reserved - */ - -#include - -class Example : public Scene { - public: - Example(Game*); - void draw() override; -}; \ No newline at end of file diff --git a/src/scene/gears/gears.cpp b/src/scene/gears/gears.cpp new file mode 100644 index 0000000..a60be05 --- /dev/null +++ b/src/scene/gears/gears.cpp @@ -0,0 +1,411 @@ +/** + * Copyright (c) 2023, Furkan Mudanyali + * All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* vertexShader = R"( + attribute vec3 position; + attribute vec3 normal; + + uniform mat4 ModelViewProjectionMatrix; + uniform mat4 NormalMatrix; + uniform vec4 LightSourcePosition; + uniform vec4 MaterialColor; + + varying vec4 Color; + + void main(void) { + // Transform the normal to eye coordinates + vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0))); + + // The LightSourcePosition is the direction for directional light + vec3 L = normalize(LightSourcePosition.xyz); + + float diffuse = max(dot(N, L), 0.0); + float ambient = 0.2; + + // Multiply the diffuse value by the vertex color + // to get the actual color that will be used to draw the vertex with + + // Color = diffuse * MaterialColor; + Color = vec4((ambient + diffuse) * MaterialColor.xyz, MaterialColor.a); + + gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0); + } +)"; + +static const char* fragmentShader = R"( + #ifdef GL_ES + precision mediump float; + #endif + varying vec4 Color; + + void main(void) { + gl_FragColor = Color; + } +)"; + +GearsScene::GearsScene(Game* pGame) { + this->pGame = pGame; + char msg[512]; + const char* p; + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + GLuint vertex; + GLuint fragment; + GLuint program; + + p = vertexShader; + vertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex, 1, &p, nullptr); + glCompileShader(vertex); + glGetShaderInfoLog(vertex, sizeof msg, nullptr, msg); + std::cout << "Vertex shader info: " << msg << "\n"; + + p = fragmentShader; + fragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment, 1, &p, nullptr); + glCompileShader(fragment); + glGetShaderInfoLog(fragment, sizeof msg, nullptr, msg); + std::cout << "Fragment shader info: " << msg << "\n"; + + program = glCreateProgram(); + glAttachShader(program, vertex); + glAttachShader(program, fragment); + glBindAttribLocation(program, 0, "position"); + glBindAttribLocation(program, 1, "normal"); + + glLinkProgram(program); + glGetProgramInfoLog(program, sizeof msg, nullptr, msg); + std::cout << "Program info: " << msg << "\n"; + + glUseProgram(program); + + this->modelViewProjectionMatrixLoc = + glGetUniformLocation(program, "ModelViewProjectionMatrix"); + this->normalMatrixLoc = glGetUniformLocation(program, "NormalMatrix"); + this->lightSrcPosLoc = glGetUniformLocation(program, "LightSourcePosition"); + this->materialColorLoc = glGetUniformLocation(program, "MaterialColor"); + + glUniform4fv((GLint)this->lightSrcPosLoc, 1, lightSourcePos); + + gears[0] = createGear(1.0, 4.0, 1.0, 20, 0.7); + gears[1] = createGear(0.5, 2.0, 2.0, 10, 0.7); + gears[2] = createGear(1.3, 2.0, 0.5, 10, 0.7); +} + +GearVertex* GearsScene::fillGearVertex(GearVertex* gearVertex, + GLfloat x, + GLfloat y, + GLfloat z, + const GLfloat n[3]) { + gearVertex[0][0] = x; + gearVertex[0][1] = y; + gearVertex[0][2] = z; + gearVertex[0][3] = n[0]; + gearVertex[0][4] = n[1]; + gearVertex[0][5] = n[2]; + + return gearVertex + 1; +} + +Gear* GearsScene::createGear(GLfloat innerRad, + GLfloat outerRad, + GLfloat width, + GLfloat teeth, + GLfloat toothDepth) { + GLfloat rad0; + GLfloat rad1; + GLfloat rad2; + GLfloat diameter; + + GearVertex* vertex; + Gear* gear; + + double sinArr[5]; + double cosArr[5]; + GLfloat normal[3]; + + int currentStrip = 0; + + gear = (Gear*)malloc(sizeof *gear); + + // Calculate the radii used in the gear + rad0 = innerRad; + rad1 = outerRad - toothDepth / 2.0F; + rad2 = outerRad + toothDepth / 2.0F; + + diameter = 2.0F * (float)M_PI / teeth / 4.0F; + + // Allocate triangle strip information + gear->nStrips = STRIPS_PER_TOOTH * teeth; + gear->strips = (VertexStrip*)calloc(gear->nStrips, sizeof(*gear->vertices)); + + // Allocate the vertices + gear->vertices = (GearVertex*)calloc(VERTICES_PER_TOOTH * teeth, + sizeof(*gear->vertices)); + vertex = gear->vertices; + + // Calculate needed sin/cos for various angles + for (int i = 0; i < (int)teeth; ++i) { + sincos((float)i * 2.0F * M_PI / teeth, &sinArr[0], &cosArr[0]); + sincos((float)i * 2.0F * M_PI / teeth + diameter, &sinArr[1], + &cosArr[1]); + sincos((float)i * 2.0F * M_PI / teeth + diameter * 2, &sinArr[2], + &cosArr[2]); + sincos((float)i * 2.0F * M_PI / teeth + diameter * 3, &sinArr[3], + &cosArr[3]); + sincos((float)i * 2.0F * M_PI / teeth + diameter * 4, &sinArr[4], + &cosArr[4]); + + // Macros that do shit idk +#define GEAR_POINT(r, da) \ + { (float)((r)*cosArr[(da)]), (float)((r)*sinArr[(da)]) } +#define SET_NORMAL(x, y, z) \ + do { \ + normal[0] = (x); \ + normal[1] = (y); \ + normal[2] = (z); \ + } while (0) + +#define GEAR_VERT(v, point, sign) \ + fillGearVertex((v), points[(point)].x, points[(point)].y, \ + (sign)*width * 0.5, normal) + +#define START_STRIP \ + do { \ + gear->strips[currentStrip].first = vertex - gear->vertices; \ + } while (0); + +#define END_STRIP \ + do { \ + int _tmp = (vertex - gear->vertices); \ + gear->strips[currentStrip].count = \ + _tmp - gear->strips[currentStrip].first; \ + currentStrip++; \ + } while (0) + +#define QUAD_WITH_NORMAL(p1, p2) \ + do { \ + SET_NORMAL((points[(p1)].y - points[(p2)].y), \ + -(points[(p1)].x - points[(p2)].x), 0); \ + vertex = GEAR_VERT(vertex, (p1), -1); \ + vertex = GEAR_VERT(vertex, (p1), 1); \ + vertex = GEAR_VERT(vertex, (p2), -1); \ + vertex = GEAR_VERT(vertex, (p2), 1); \ + } while (0) + + using Point = struct { + GLfloat x; + GLfloat y; + }; + + // Create 7 points (x,y coords) that make up a tooth + Point points[7] = { + GEAR_POINT(rad2, 1), GEAR_POINT(rad2, 2), GEAR_POINT(rad1, 0), + GEAR_POINT(rad1, 3), GEAR_POINT(rad0, 0), GEAR_POINT(rad1, 4), + GEAR_POINT(rad0, 4), + }; + + // Front face + START_STRIP; + SET_NORMAL(0, 0, 1.0); + vertex = GEAR_VERT(vertex, 0, +1); + vertex = GEAR_VERT(vertex, 1, +1); + vertex = GEAR_VERT(vertex, 2, +1); + vertex = GEAR_VERT(vertex, 3, +1); + vertex = GEAR_VERT(vertex, 4, +1); + vertex = GEAR_VERT(vertex, 5, +1); + vertex = GEAR_VERT(vertex, 6, +1); + END_STRIP; + + // Inner face + START_STRIP; + QUAD_WITH_NORMAL(4, 6); + END_STRIP; + + // Back face + START_STRIP; + SET_NORMAL(0, 0, -1.0); + vertex = GEAR_VERT(vertex, 6, -1); + vertex = GEAR_VERT(vertex, 5, -1); + vertex = GEAR_VERT(vertex, 4, -1); + vertex = GEAR_VERT(vertex, 3, -1); + vertex = GEAR_VERT(vertex, 2, -1); + vertex = GEAR_VERT(vertex, 1, -1); + vertex = GEAR_VERT(vertex, 0, -1); + END_STRIP; + + // Outer face + START_STRIP; + QUAD_WITH_NORMAL(0, 2); + END_STRIP; + + START_STRIP; + QUAD_WITH_NORMAL(1, 0); + END_STRIP; + + START_STRIP; + QUAD_WITH_NORMAL(3, 1); + END_STRIP; + + START_STRIP; + QUAD_WITH_NORMAL(5, 3); + END_STRIP; + } + + gear->nVertices = (int)(vertex - gear->vertices); + glGenBuffers(1, &gear->vertexBufObj); + glBindBuffer(GL_ARRAY_BUFFER, gear->vertexBufObj); + glBufferData(GL_ARRAY_BUFFER, + (GLsizeiptr)(gear->nVertices * sizeof(GearVertex)), + gear->vertices, GL_STATIC_DRAW); + + return gear; +} + +void GearsScene::drawGear(Gear* gear, + GLfloat* transform, + GLfloat x, + GLfloat y, + GLfloat angle, + const GLfloat color[4]) { + GLfloat modelView[16]; + GLfloat normalMatrix[16]; + GLfloat modelViewProjection[16]; + + // Translate and rotate the gear + memcpy(modelView, transform, sizeof(modelView)); + Graphics::translate4x4Matrix(modelView, x, y, 0); + Graphics::rotate4x4Matrix(modelView, 2.0F * (float)M_PI * angle / 360.0F, 0, + 0, 1); + + /* Create and set the ModelViewProjectionMatrix */ + memcpy(modelViewProjection, this->projectionMatrix, + sizeof(modelViewProjection)); + Graphics::multiply4x4Matrices(modelViewProjection, modelView); + + glUniformMatrix4fv((GLint)this->modelViewProjectionMatrixLoc, 1, GL_FALSE, + modelViewProjection); + + /* + * Create and set the NormalMatrix. It's the inverse transpose of the + * ModelView matrix. + */ + memcpy(normalMatrix, modelView, sizeof(normalMatrix)); + Graphics::invert4x4Matrix(normalMatrix); + Graphics::transpose4x4Matrix(normalMatrix); + glUniformMatrix4fv((GLint)this->normalMatrixLoc, 1, GL_FALSE, normalMatrix); + + /* Set the gear color */ + glUniform4fv((GLint)this->materialColorLoc, 1, color); + + /* Set the vertex buffer object to use */ + glBindBuffer(GL_ARRAY_BUFFER, gear->vertexBufObj); + + /* Set up the position of the attributes in the vertex buffer object */ + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), + nullptr); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), + (GLfloat*)(sizeof(GLfloat) * 3)); + + /* Enable the attributes */ + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + /* Draw the triangle strips that comprise the gear */ + glDrawArrays(GL_TRIANGLE_STRIP, 0, gear->nVertices); + + /* Disable the attributes */ + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); +} + +void GearsScene::reshape() { + if (this->width != this->pGame->pWindow->getWidth() || + this->height != this->pGame->pWindow->getHeight()) { + this->width = this->pGame->pWindow->getWidth(); + this->height = this->pGame->pWindow->getHeight(); + + Graphics::calcPerspectiveProjectTransformation( + this->projectionMatrix, 60.0, + (float)this->width / (float)this->height, 1.0, 1024.0); + glViewport(0, 0, (GLint)this->width, (GLint)this->height); + } +} + +void GearsScene::idle() { + static int frames = 0; + static double tRot0 = -1.0; + static double tRate0 = -1.0; + double dt; + double t = SDL_GetTicks() / 1000.0; + + if (tRot0 < 0.0) { + tRot0 = t; + } + dt = t - tRot0; + tRot0 = t; + + /* advance rotation for next frame */ + this->currentAngle += 70.0 * dt; /* 70 degrees per second */ + if (this->currentAngle > 3600.0) { + this->currentAngle -= 3600.0; + } + + this->pGame->pRenderer->draw(); + frames++; + + if (tRate0 < 0.0) { + tRate0 = t; + } + if (t - tRate0 >= 5.0) { + GLfloat seconds = t - tRate0; + GLfloat fps = frames / seconds; + printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds, + fps); + tRate0 = t; + frames = 0; + } +} + +void GearsScene::draw() { + const static GLfloat red[4] = {0.8, 0.1, 0.0, 1.0}; + const static GLfloat green[4] = {0.0, 0.8, 0.2, 1.0}; + const static GLfloat blue[4] = {0.2, 0.2, 1.0, 1.0}; + GLfloat transform[16]; + Graphics::create4x4IdentityMatrix(transform); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* Translate and rotate the view */ + Graphics::translate4x4Matrix(transform, 0, 0, -20); + Graphics::rotate4x4Matrix( + transform, 2.0F * (float)M_PI * viewRotation[0] / 360.0F, 1, 0, 0); + Graphics::rotate4x4Matrix( + transform, 2.0F * (float)M_PI * viewRotation[1] / 360.0F, 0, 1, 0); + Graphics::rotate4x4Matrix( + transform, 2.0F * (float)M_PI * viewRotation[2] / 360.0F, 0, 0, 1); + + /* Draw the gears */ + drawGear(gears[0], transform, -3.0, -2.0, currentAngle, red); + drawGear(gears[1], transform, 3.1, -2.0, (float)-2 * currentAngle - 9.0F, + green); + drawGear(gears[2], transform, -3.1, 4.2, (float)-2 * currentAngle - 25.0F, + blue); + + reshape(); + idle(); +} \ No newline at end of file diff --git a/src/scene/gears/gears.h b/src/scene/gears/gears.h new file mode 100644 index 0000000..400a3d4 --- /dev/null +++ b/src/scene/gears/gears.h @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2023, Furkan Mudanyali + * All rights reserved + */ + +#include +#include +#include + +#define STRIPS_PER_TOOTH 7 +#define VERTICES_PER_TOOTH 35 +#define GEAR_VERTEX_STRIDE 6 + +// Class describing the vertices in triangle strip +using VertexStrip = struct { + // First vertex in the strip + GLint first; + // Number of consecutive verices in the strip after the first + GLint count; +}; + +// Each vertex consists of GEAR_VERTEX_STRIDE GLfloat attributes +using GearVertex = GLfloat[GEAR_VERTEX_STRIDE]; + +// Class representing a gear +using Gear = struct { + // Array of vertices comprising the gear + GearVertex* vertices; + // Number of vertices comprising the gear + int nVertices; + // Array of triangle strips comprising the gear + VertexStrip* strips; + // Number of triangle strips comprising the gear + int nStrips; + // Vertex buffer object holding the vertices in the GPU + GLuint vertexBufObj; +}; + +class GearsScene : public Scene { + // The view rotation [x, y, z] + GLfloat viewRotation[3] = {20.0, 30.0, 0.0}; + // The gears + Gear* gears[3]; + // The current gear rotation angle + GLfloat currentAngle = 0.0; + // The location of the shader uniforms + GLuint modelViewProjectionMatrixLoc; + GLuint normalMatrixLoc; + GLuint lightSrcPosLoc; + GLuint materialColorLoc; + // The projection matrix + GLfloat projectionMatrix[16]; + + int width; + int height; + + void idle(); + void reshape(); + // The direction of the directional light for the scene + const GLfloat lightSourcePos[4] = {5.0, 5.0, 10.0, 1.0}; + + /** + * Fills a gear vertex. + * + * @param gearVertex the vertex to fill + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + * @param n pointer to the normal table + * + * @return the operation error code + */ + static GearVertex* fillGearVertex(GearVertex* gearVertex, + GLfloat x, + GLfloat y, + GLfloat z, + const GLfloat n[3]); + + /** + * Create a gear wheel. + * + * @param innerRad radius of the hole at the center + * @param outerRad radius at the center of the teth + * @param width width of the gear + * @param teeth the number of teeth + * @param toothDepth the depth of the teeth + * + * @return the pointer to the constructed gear struct + */ + static Gear* createGear(GLfloat innerRad, + GLfloat outerRad, + GLfloat width, + GLfloat teeth, + GLfloat toothDepth); + + /** + * Draws a gear + * + * @param gear the gear to draw + * @param transform the current transformation matrix + * @param x the x pos to draw the gear at + * @param y the y pos to draw the gear at + * @param angle the rotation angle of the gear + * @param color the color of the gear + */ + void drawGear(Gear*, GLfloat*, GLfloat, GLfloat, GLfloat, const GLfloat[4]); + + // Draws all gears + void drawAllGears(); + + public: + GearsScene(Game*); + void draw() override; +}; \ No newline at end of file diff --git a/src/scene/scene.h b/src/scene/scene.h index f2b8f5d..c667daa 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -10,7 +10,7 @@ class Game; class Scene { public: - Game* game; + Game* pGame; bool destroy = false; Scene() = default;