diff --git a/.gitignore b/.gitignore index 678b679..dd476ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ obj/* Game +*.xcf diff --git a/res/glsl/fragment.glsl b/res/glsl/fragment.glsl new file mode 100644 index 0000000..b1c49d2 --- /dev/null +++ b/res/glsl/fragment.glsl @@ -0,0 +1,13 @@ +#version 450 core + +in vec3 textureCoord; + +out vec4 color; + +uniform sampler2DArray textureSampler; + +void main() +{ + color = texture(textureSampler, textureCoord); + if (color.w < 0.1) discard; +} diff --git a/res/glsl/vertex.glsl b/res/glsl/vertex.glsl new file mode 100644 index 0000000..b9aa740 --- /dev/null +++ b/res/glsl/vertex.glsl @@ -0,0 +1,17 @@ +#version 450 core + +layout (location = 0) in vec3 position; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in float texIndex; +layout (location = 3) in mat4 theModelMatrix; + +out vec3 textureCoord; + +uniform mat4 theViewMatrix; +uniform mat4 theProjMatrix; + +void main() +{ + gl_Position = theProjMatrix * theViewMatrix * theModelMatrix * vec4(position, 1.0); + textureCoord = vec3(1.0 - texCoord.x, 1.0 - texCoord.y, texIndex); +} diff --git a/res/tex/texture.png b/res/tex/texture.png new file mode 100644 index 0000000..58c3ab2 Binary files /dev/null and b/res/tex/texture.png differ diff --git a/src/camera.c b/src/camera.c new file mode 100644 index 0000000..9a80666 --- /dev/null +++ b/src/camera.c @@ -0,0 +1,53 @@ +#include +#include "camera.h" + +void Camera_Init(Camera* c) +{ + c->position[0] = -50.0f; + c->position[1] = 130.0f; + c->position[2] = 0.0f; + c->width[0] = 0.6f; + c->width[1] = 5.8f; + c->width[2] = 0.6f; + glm_vec3_zero(c->velocity); + glm_vec3_zero(c->look); + glm_vec3_zero(c->up); + glm_vec3_zero(c->front); + glm_vec3_zero(c->right); + glm_vec3_zero(c->rotation); +} + +void Camera_GetViewMatrix(Camera* c, mat4 m) +{ + glm_lookat(c->position, c->look, c->up, m); +} + +void Camera_UpdateVectors(Camera* c) +{ + if (c->rotation[0] > 180.0f) c->rotation[0] -= 360.0f; + else if (c->rotation[0] <= -180.0f) c->rotation[0] += 360.0f; + + c->rotation[1] = glm_clamp(c->rotation[1], -89.0f, 89.0f); + c->rotation[2] = glm_clamp(c->rotation[2], -30.0f, 30.0f); + + // calculate the front facing unit vector by yaw and pitch + c->front[0] = cos(glm_rad(c->rotation[0])) * cos(glm_rad(c->rotation[1])); + c->front[1] = sin(glm_rad(c->rotation[1])); + c->front[2] = sin(glm_rad(c->rotation[0])) * cos(glm_rad(c->rotation[1])); + + // the look vector is the position + front + glm_vec3_add(c->position, c->front, c->look); + + // reset the up vector + c->up[0] = 0.0f; + c->up[1] = 1.0f; + c->up[2] = 0.0f; + // cross product points to the right, perpendicular to both front and up + glm_vec3_cross(c->front, c->up, c->right); + // pitch up or down, around the axis of the cross product + glm_vec3_rotate(c->up, glm_rad(c->rotation[1]), c->right); + // roll around the axis of the front vector + glm_vec3_rotate(c->up, glm_rad(c->rotation[2]), c->front); + // recalculate right vector after rolling + glm_vec3_cross(c->front, c->up, c->right); +} diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 0000000..a8eefeb --- /dev/null +++ b/src/camera.h @@ -0,0 +1,19 @@ +#pragma once + +#include "cglm/cglm.h" + +typedef struct +{ + vec3 position; + vec3 width; + vec3 velocity; + vec3 look; + vec3 up; + vec3 front; + vec3 right; + vec3 rotation; +} Camera; + +void Camera_Init(Camera* c); +void Camera_GetViewMatrix(Camera* c, mat4 m); +void Camera_UpdateVectors(Camera* c); diff --git a/src/game.c b/src/game.c index 78b230f..554ebb2 100644 --- a/src/game.c +++ b/src/game.c @@ -1,32 +1,15 @@ #include #include -#include "SDL2/SDL.h" #include "SDL2/SDL_timer.h" -#include "GL/glew.h" -#include "SDL2/SDL_opengl.h" #include "game.h" +#include "render.h" +#include "camera.h" GameState *Game_New() { GameState *gs = calloc(1, sizeof(GameState)); - SDL_Init(SDL_INIT_VIDEO); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - - gs->window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);; - gs->glContext = SDL_GL_CreateContext(gs->window); - printf("Created OpenGL window.\n"); - - glewInit(); - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - printf("Initialized OpenGL.\n"); + if (!Render_Init(gs)) return NULL; gs->running = true; return gs; @@ -34,12 +17,20 @@ GameState *Game_New() void Game_Update(GameState *gs) { - SDL_Delay(100); + vec3 move; + glm_vec3_zero(move); + if (gs->input.w) move[0] = 0.3f; + else if (gs->input.s) move[0] = -0.3f; + if (gs->input.a) move[1] = 0.3f; + else if (gs->input.d) move[1] = -0.3f; + glm_vec3_add(gs->camera.position, move, gs->camera.position); + + Camera_UpdateVectors(&gs->camera); + SDL_Delay(5); } void Game_Destroy(GameState *gs) { - SDL_GL_DeleteContext(gs->glContext); - SDL_DestroyWindow(gs->window); + Render_Destroy(gs); free(gs); } diff --git a/src/game.h b/src/game.h index 52ecff9..fb09d17 100644 --- a/src/game.h +++ b/src/game.h @@ -1,12 +1,31 @@ #pragma once #include +#include "SDL2/SDL.h" +#include "cglm/cglm.h" +#include "camera.h" +#include "shape.h" + +typedef struct +{ + bool w; + bool a; + bool s; + bool d; +} InputState; typedef struct { bool running; + InputState input; SDL_Window *window; SDL_GLContext *glContext; + GLuint textureId; + int shaderProgramId; + Camera camera; + Shape *testShape; + mat4 projMatrix; + mat4 viewMatrix; } GameState; GameState *Game_New(); diff --git a/src/input.c b/src/input.c index 7a1bd02..2dc003a 100644 --- a/src/input.c +++ b/src/input.c @@ -1,5 +1,6 @@ #include "SDL2/SDL.h" #include "input.h" +#include "camera.h" #include "game.h" static void HandleKeyDown(GameState *gs, SDL_KeyCode sym) @@ -21,11 +22,40 @@ static void HandleKeyDown(GameState *gs, SDL_KeyCode sym) SDL_SetWindowFullscreen(gs->window, windowFlags); } break; + + case SDLK_w: gs->input.w = true; break; + case SDLK_a: gs->input.a = true; break; + case SDLK_s: gs->input.s = true; break; + case SDLK_d: gs->input.d = true; break; } } static void HandleKeyUp(GameState *gs, SDL_KeyCode sym) { + switch (sym) + { + case SDLK_w: gs->input.w = false; break; + case SDLK_a: gs->input.a = false; break; + case SDLK_s: gs->input.s = false; break; + case SDLK_d: gs->input.d = false; break; + } +} + +static void HandleMouseMotion(SDL_MouseMotionEvent e, GameState* gs) +{ + const float sensitivity = 0.4f; + Camera *camera = &gs->camera; + camera->rotation[0] += e.xrel * sensitivity; + camera->rotation[1] -= e.yrel * sensitivity; +} + +static void HandleMouseDown(SDL_MouseButtonEvent e, GameState* gs) +{ + switch (e.button) + { + case 1: + break; + } } void Input_Poll(GameState *gs) @@ -49,6 +79,12 @@ void Input_Poll(GameState *gs) case SDL_KEYUP: HandleKeyUp(gs, sdlEvent.key.keysym.sym); break; + case SDL_MOUSEMOTION: + HandleMouseMotion(sdlEvent.motion, gs); + break; + case SDL_MOUSEBUTTONDOWN: + HandleMouseDown(sdlEvent.button, gs); + break; } } } diff --git a/src/main.c b/src/main.c index ac3e631..ff6f9de 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ #include "SDL2/SDL.h" #include "game.h" #include "input.h" +#include "render.h" int main(int argc, char* argv[]) { @@ -18,6 +19,7 @@ int main(int argc, char* argv[]) { Input_Poll(gs); Game_Update(gs); + Render_Draw(gs); } printf("Shutting down...\n"); diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..387c7c5 --- /dev/null +++ b/src/render.c @@ -0,0 +1,336 @@ +#include +#include +#include +#include "SDL2/SDL.h" +#include "GL/glew.h" +#include "SDL2/SDL_opengl.h" +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#include "cglm/cglm.h" +#include "render.h" +#include "camera.h" +#include "game.h" +#include "shape.h" + +static void LoadTextureArray(GLuint texture, const char* filePath, int nCols, int nRows) +{ + const int nMipmaps = 1; + int nTiles = nCols * nRows; + int tWidth, tHeight, tChannels; + + unsigned char* textureBytes = stbi_load(filePath, &tWidth, &tHeight, &tChannels, 0); + + tWidth /= nCols; + tHeight /= nRows; + + glBindTexture(GL_TEXTURE_2D_ARRAY, texture); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, tWidth, tHeight, nTiles, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + int tBytes = 4 * tWidth * tHeight; + unsigned char* buffer = malloc(tBytes * sizeof(unsigned char)); + + for (int z = 0; z < nTiles; z++) + { + for (int b = 0; b < tBytes; b++) + { + int bWidth = 4 * tWidth; // width of each tile in bytes + int i = ((z / nCols) * nCols * bWidth * tHeight) // size (in bytes) of all tiles above + + ((b / bWidth) * bWidth * nCols) // size of pixel rows above, within the current tile row + + ((z % nCols) * bWidth) // width of tiles to the left + + (b % bWidth); // width from the left side of the current tile + + buffer[b] = textureBytes[i]; + } + + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, z, tWidth, tHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + } + + free(buffer); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + + stbi_image_free(textureBytes); +} + +static void LoadTexture(GameState* gs) +{ + glGenTextures(1, &gs->textureId); + glActiveTexture(GL_TEXTURE0); + glUniform1i(glGetUniformLocation(gs->shaderProgramId, "textureSampler"), 0); + LoadTextureArray(gs->textureId, "res/tex/texture.png", 8, 4); + printf("Loaded the texture array.\n"); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static char **ReadFileLines(const char *path, int *n) +{ + const int MAX_LINES = 128; + const int MAX_LINE_LENGTH = 256; + + FILE *file = fopen(path, "r"); + char line[MAX_LINE_LENGTH]; + char **lines = calloc(MAX_LINES, sizeof(char*)); + *n = 0; + + if (lines == NULL) return NULL; + + while (fgets(&(line[0]), MAX_LINE_LENGTH, file) != NULL && *n < MAX_LINES) + { + GLchar *l = strdup(&(line[0])); + if (l == NULL) return NULL; + lines[*n] = l; + (*n)++; + } + + fclose(file); + int s = sizeof(char*) * (*n); + lines = realloc(lines, s); + + return lines; +} + +static void FreeLines(char **lines, int n) +{ + for (int i = 0; i < n; i++) + { + free(lines[i]); + } + + free(lines); +} + +static int CompileShader(GLchar *sourcePath, GLenum shaderType, char ***outCode, int *outNumLines) +{ + GLint success; + *outCode = ReadFileLines(sourcePath, outNumLines); + GLuint shaderId = glCreateShader(shaderType); + glShaderSource(shaderId, *outNumLines, (void*)(*outCode), NULL); + glCompileShader(shaderId); + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); + + if (!success) + { + GLchar log[512]; + glGetShaderInfoLog(shaderId, 512, NULL, log); + printf("%s\n", log); + return -1; + } + + return shaderId; +} + +static int LoadShaders() +{ + const char *vertShaderFilePath = "./res/glsl/vertex.glsl"; + char **vertCode = NULL; + int vertNumLines; + int vertShaderId = CompileShader(vertShaderFilePath, GL_VERTEX_SHADER, &vertCode, &vertNumLines); + if (vertShaderId < 0) return vertShaderId; + + const char *fragShaderFilePath = "./res/glsl/fragment.glsl"; + char **fragCode = NULL; + int fragNumLines; + int fragShaderId = CompileShader(fragShaderFilePath, GL_FRAGMENT_SHADER, &fragCode, &fragNumLines); + if (fragShaderId < 0 ) return fragShaderId; + + GLint success; + GLuint shaderProgramId = glCreateProgram(); + glAttachShader(shaderProgramId, vertShaderId); + glAttachShader(shaderProgramId, fragShaderId); + glLinkProgram(shaderProgramId); + glGetProgramiv(shaderProgramId, GL_LINK_STATUS, &success); + + if (!success) + { + GLchar log[512]; + glGetProgramInfoLog(shaderProgramId, 512, NULL, log); + printf("%s", log); + return -1; + } + + glDeleteShader(vertShaderId); + glDeleteShader(fragShaderId); + FreeLines(vertCode, vertNumLines); + FreeLines(fragCode, fragNumLines); + + return shaderProgramId; +} + +static void InitShapeBuffers(Shape *shape) +{ + glBindVertexArray(shape->VAO); + + // vertex buffer: contains vertex and texture coords + glBindBuffer(GL_ARRAY_BUFFER, shape->VBO); + glBufferData(GL_ARRAY_BUFFER, shape->numVertices * sizeof(GLfloat), shape->vertices, GL_STATIC_DRAW); + GLsizei stride = 5 * sizeof(GLfloat); + + // vertex coordinates + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0); + + // texture coordinates + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(3 * sizeof(GLfloat))); + + // instance buffer: contains per-instance texture index and model transformation matrix + // therefore each instance of the same shape, in the same draw call, can have a different texture and transformation + glBindBuffer(GL_ARRAY_BUFFER, shape->IBO); + stride = SHAPE_INSTANCE_SIZE; + + // texture index: basically another tex coord in the third dimension because a texture array is used + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0); + glVertexAttribDivisor(2, 1); + + // model matrix: contains 16 floats, each vert attrib has space for 4 floats, therefore it spans 4 attrib locations + glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); + glEnableVertexAttribArray(6); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(1 * sizeof(GLfloat))); + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(5 * sizeof(GLfloat))); + glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(9 * sizeof(GLfloat))); + glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(13 * sizeof(GLfloat))); + glVertexAttribDivisor(3, 1); + glVertexAttribDivisor(4, 1); + glVertexAttribDivisor(5, 1); + glVertexAttribDivisor(6, 1); + + // index buffer: contains vertex indices, reducing the data to be buffered on the GPU + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, shape->EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->numIndices * sizeof(GLushort), shape->indices, GL_STATIC_DRAW); + + glBindVertexArray(0); +} + +bool Render_Init(GameState *gs) +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + gs->window = SDL_CreateWindow("Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);; + gs->glContext = SDL_GL_CreateContext(gs->window); + printf("Created OpenGL window.\n"); + + glewInit(); + gs->shaderProgramId = LoadShaders(); + + if (gs->shaderProgramId < 0) + { + printf("Failed to load shaders.\n"); + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + printf("Initialized OpenGL.\n"); + + Shape *shape = Shape_MakePyramid(3); + gs->testShape = shape; + glGenVertexArrays(1, &shape->VAO); + glGenBuffers(1, &shape->VBO); + glGenBuffers(1, &shape->IBO); + glGenBuffers(1, &shape->EBO); + + LoadTexture(gs); + Camera_Init(&gs->camera); + + InitShapeBuffers(gs->testShape); +} + +void Render_Destroy(GameState *gs) +{ + SDL_GL_DeleteContext(gs->glContext); + SDL_DestroyWindow(gs->window); +} + +// Sets the GL viewport while maintaining aspect ratio. +// Calculates the projection and view matrices. +static void SetViewport(GameState *gs) +{ + const float ratio = 0.5625f; // 1080/1920 + int wWidth, wHeight, offsetX, offsetY; + SDL_GL_GetDrawableSize(gs->window, &wWidth, &wHeight); + + if ((float)wHeight / (float)wWidth > ratio) + { + int temp = (int)floorf(ratio * (float)wWidth); + offsetX = 0; + offsetY = (wHeight - temp) / 2; + wHeight = temp; + } + else + { + int temp = (int)floorf((float)wHeight / ratio); + offsetX = (wWidth - temp) / 2; + offsetY = 0; + wWidth = temp; + } + + glViewport(offsetX, offsetY, wWidth, wHeight); + + glm_mat4_identity(gs->projMatrix); + glm_perspective(45.0f, (GLfloat)wWidth / (GLfloat)wHeight, 0.1f, 2000.0f, gs->projMatrix); + + glm_mat4_identity(gs->viewMatrix); + Camera_GetViewMatrix(&gs->camera, gs->viewMatrix); +} + +void Render_Draw(GameState *gs) +{ + glClearColor(0.4f, 0.6f, 0.8f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + SetViewport(gs); + + glUseProgram(gs->shaderProgramId); + GLint projLoc = glGetUniformLocation(gs->shaderProgramId, "theProjMatrix"); + glUniformMatrix4fv(projLoc, 1, GL_FALSE, (void*)(gs->projMatrix)); + GLint viewLoc = glGetUniformLocation(gs->shaderProgramId, "theViewMatrix"); + glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (void*)(gs->viewMatrix)); + glBindTexture(GL_TEXTURE_2D, gs->textureId); + vec3 scale = { 0, 0, 0 }; + + Shape *shape = gs->testShape; + glBindVertexArray(shape->VAO); + + // apply transformations to each instance + for (int j = 0; j < shape->numInstances; j++) + { + ShapeInstance* instance = shape->instances + j; + mat4* matrix = (void*)(shape->instanceData + (j * 17) + 1); + mat4 tempMat; + glm_mat4_identity(tempMat); + + glm_translate(tempMat, instance->position); + glm_rotate_x(tempMat, instance->rotation[0], tempMat); + glm_rotate_y(tempMat, instance->rotation[1], tempMat); + glm_rotate_z(tempMat, instance->rotation[2], tempMat); + + scale[0] = instance->scale; + scale[1] = instance->scale; + scale[2] = instance->scale; + glm_scale(tempMat, scale); + + memcpy(matrix, tempMat, sizeof(mat4)); + } + + // re-buffer the instance data because transformations may have changed + glBindBuffer(GL_ARRAY_BUFFER, shape->IBO); + glBufferData(GL_ARRAY_BUFFER, shape->numInstances * SHAPE_INSTANCE_SIZE, shape->instanceData, GL_DYNAMIC_DRAW); + + glDrawElementsInstanced(GL_TRIANGLES, shape->numIndices, GL_UNSIGNED_SHORT, 0, shape->numInstances); + + SDL_GL_SwapWindow(gs->window); +} diff --git a/src/render.h b/src/render.h new file mode 100644 index 0000000..cdba0ae --- /dev/null +++ b/src/render.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include "game.h" + +bool Render_Init(GameState *gs); +void Render_Destroy(GameState *gs); +void Render_Draw(GameState *gs); diff --git a/src/shape.c b/src/shape.c new file mode 100644 index 0000000..5c93668 --- /dev/null +++ b/src/shape.c @@ -0,0 +1,83 @@ +#include +#include +#include "GL/glew.h" +#include "cglm/cglm.h" +#include "shape.h" + +Shape *Shape_MakePyramid(int numInstances) +{ + GLfloat vertices[] = + { + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, + + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.5f, 1.0f, + + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.5f, 1.0f, + + -1.0f, -1.0f, 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.5f, 1.0f, + + -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.5f, 1.0f, + }; + + GLushort indices[] = + { + 0, 1, 2, + 3, 4, 5, + 6, 7, 8, + 9, 10, 11, + 12, 13, 14, + 15, 16, 17 + }; + + size_t numBytesVertices = sizeof(vertices); + size_t numBytesIndices = sizeof(indices); + + Shape *shape = malloc(sizeof(Shape) + numBytesVertices + numBytesIndices); + void *tempPtr = (void*)(shape + 1); + shape->vertices = tempPtr; + shape->numVertices = numBytesVertices / sizeof(vertices[0]); + shape->indices = tempPtr + numBytesVertices; + shape->numIndices = numBytesIndices / sizeof(indices[0]); + + memcpy(shape->vertices, vertices, numBytesVertices); + memcpy(shape->indices, indices, numBytesIndices); + + shape->instances = calloc(numInstances, sizeof(ShapeInstance) + SHAPE_INSTANCE_SIZE); + shape->instanceData = (void*)(shape->instances + numInstances); + shape->numInstances = numInstances; + + for (int i = 0; i < numInstances; i++) + { + ShapeInstance *instance = shape->instances + i; + instance->position[0] = -4.0f; + instance->position[0] = 0.0f; + instance->position[0] = (5.0f * i) - 2.0f; + instance->scale = 1.0f; + instance->collisionRadius = 1.0f; + glm_vec3_zero(instance->velocity); + glm_vec3_zero(instance->rotation); + float *textureId = shape->instanceData + (i * 17); + *textureId = 0; + mat4 *matrix = (void*)(textureId + 1); + } + + return shape; +} + +void *Shape_Destroy(Shape *shape) +{ + free(shape); +} diff --git a/src/shape.h b/src/shape.h new file mode 100644 index 0000000..6291584 --- /dev/null +++ b/src/shape.h @@ -0,0 +1,37 @@ +#pragma once + +#include "GL/glew.h" +#include "cglm/cglm.h" + +enum +{ + SHAPE_INSTANCE_SIZE = sizeof(float) + sizeof(mat4) +}; + +typedef struct +{ + vec3 position; + vec3 rotation; + vec3 velocity; + float scale; + float collisionRadius; + +} ShapeInstance; + +typedef struct +{ + GLfloat *vertices; + int numVertices; + GLushort *indices; + int numIndices; + ShapeInstance *instances; + float *instanceData; + int numInstances; + + GLuint VAO; + GLuint VBO; + GLuint IBO; + GLuint EBO; +} Shape; + +Shape *Shape_MakePyramid(int numInstances);