#include #include #include #include "glad/glad.h" #include #include "cglm/cglm.h" #include "lib.h" #include "stb_image.h" #include "rendering.h" #include "animation.h" #include "libcyaml/include/cyaml/cyaml.h" #define PI 3.14159f int SCREEN_WIDTH = 1024; int SCREEN_HEIGHT = 768; typedef struct State { float camX; float camY; float px; float py; int spriteY; int currentAnim; } State; void framebuffer_size_callback(GLFWwindow* window, int width, int height) { (void)window; glViewport(0, 0, width, height); SCREEN_WIDTH = width; SCREEN_HEIGHT = height; } void processInput(GLFWwindow *window, State* state) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } float amount = 1.0f; if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) { state->currentAnim = 0; } if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) { state->currentAnim = 1; } if (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS) { state->currentAnim = 2; } if (glfwGetKey(window, GLFW_KEY_4) == GLFW_PRESS) { state->currentAnim = 3; } if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) { state->camY -= amount; } if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) { state->camY += amount; } if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) { state->camX -= amount; } if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) { state->camX += amount; } amount = 2.1f; if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { state->py -= amount; } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { state->py += amount; } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { state->px -= amount; } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { state->px += amount; } } int main(void) { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = checkPtr(glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "2D Engine", NULL, NULL), "Error creating GLFW window"); if (window == NULL) glfwTerminate(); glfwMakeContextCurrent(window); checkCode(gladLoadGLLoader((GLADloadproc)glfwGetProcAddress), "Error initializing GLAD"); glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); unsigned int shaderProgram = compileShaderProgram("shaders/main.vert", "shaders/main.frag", NULL); glEnable(GL_FRAMEBUFFER_SRGB); glDisable(0x809D); glEnable(GL_BLEND); // CHECK: Do we need this? // glEnable(GL_DEPTH_TEST); // glDepthFunc(GL_GREATER); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); float vertices[] = { // pos // tex 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(VAO); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); mat4 camOrtho; glm_ortho(0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, -1.0f, 1.0f, camOrtho); unsigned int textureID; int width, height, channels; byte* data = (char*)stbi_load("assets/player.png", &width, &height, &channels, 4); // char* data = (char*)stbi_load("imports/sprites/player/idle.png", &width, &height, &channels, 4); if (data == NULL) { fprintf(stderr, "Could not load texture\n"); return -1; } SpriteSheet spriteSheet = { .name = "player", .imgData = data, .animCount = 4, .width = width, .height = height, }; Animation idle = { .name = "idle", .posX = 0, .posY = 0, .rows = 1, .cols = 10, .width = 48, .height = 48, }; Animation run = { .name = "run", .posX = 0, .posY = 48 * 3, .rows = 1, .cols = 8, .width = 48, .height = 48, }; Animation jump = { .name = "jump", .posX = 0, .posY = 48, .rows = 1, .cols = 3, .width = 48, .height = 48, }; Animation land = { .name = "land", .posX = 0, .posY = 48 * 2, .rows = 1, .cols = 9, .width = 48, .height = 48, }; Animation playerAnims[4] = {idle, run, jump, land}; glGenTextures(1, &textureID); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID); // // set Texture wrap and filter modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); stbi_image_free(data); // glBindTexture(GL_TEXTURE_2D, 0); // float deltaTime = 0.0f; // float lastFrame = 0.0f; State state = {0}; int frameCount = 0; float elapsed = 0.0f; float lastFrame; while (!glfwWindowShouldClose(window)) { float currentFrame = glfwGetTime(); float deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; processInput(window, &state); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); mat4 model; mat4 projection; glm_mat4_identity(model); glm_mat4_identity(projection); float pivotx = 0.5f; float pivoty = 0.5f; glm_translate(model, (vec3){state.px, state.py, 0.0f}); glm_translate(model, (vec3){0.f, 0.f, 0.0f}); glm_translate(model, (vec3){pivotx, pivoty, 0.0f}); glm_scale(model, (vec3){100.0f, 100.0f, 0.0f}); glm_translate(model, (vec3){-pivotx, -pivoty, 0.0f}); float worldUnitSizeInPixels = 2; // Desired size of a 1x1 area on the screen float hw = (SCREEN_WIDTH / 2.f) / worldUnitSizeInPixels; float hh = (SCREEN_HEIGHT / 2.f) / worldUnitSizeInPixels; glm_ortho(-hw, hw, hh, -hh, -1.0f, 1.0f, projection); glm_translate(projection, (vec3){0.0f + state.camX, 0.0f + state.camY, 0.0f}); GLint location = glGetUniformLocation(shaderProgram, "model"); glUniformMatrix4fv(location, 1, GL_FALSE, model[0]); location = glGetUniformLocation(shaderProgram, "projection"); glUniformMatrix4fv(location, 1, GL_FALSE, projection[0]); glUniform1i(glGetUniformLocation(shaderProgram, "image"), 0); Animation anim = playerAnims[state.currentAnim]; elapsed += deltaTime; if (elapsed > 0.1f) { frameCount += 1; if (frameCount >= anim.cols) { frameCount = 0; } elapsed = 0.0f; } location = glGetUniformLocation(shaderProgram, "posX"); glUniform1f(location, anim.posX); location = glGetUniformLocation(shaderProgram, "posY"); glUniform1f(location, anim.posY); location = glGetUniformLocation(shaderProgram, "animRow"); glUniform1i(location, frameCount); // We don't animate on the Y axis yet location = glGetUniformLocation(shaderProgram, "animCol"); glUniform1i(location, 0); location = glGetUniformLocation(shaderProgram, "rows"); glUniform1i(location, anim.rows); location = glGetUniformLocation(shaderProgram, "cols"); glUniform1i(location, anim.cols); location = glGetUniformLocation(shaderProgram, "spriteWidth"); glUniform1i(location, anim.width); location = glGetUniformLocation(shaderProgram, "spriteHeight"); glUniform1i(location, anim.height); location = glGetUniformLocation(shaderProgram, "atlasWidth"); glUniform1i(location, spriteSheet.width); location = glGetUniformLocation(shaderProgram, "atlasHeight"); glUniform1i(location, spriteSheet.height); // location = glGetUniformLocation(shaderProgram, "spriteColor"); // glUniform3f(location, 1.0f, 1.0f, 1.0f); // SpriteSheet spriteSheet = spriteSheet; // printf("%i\n", state.currentAnim); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glfwTerminate(); return 0; }