Files
ugly/source/app/src/App.cpp
2025-07-31 02:09:45 -04:00

173 lines
5.7 KiB
C++

#include "App.hpp"
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "LogUtils.hpp"
#include "ShaderProgramBuilder.hpp"
#include "stb_image.h"
#include "TimestampLog.hpp"
namespace ugly {
int App::main(std::vector<std::string> &args) {
// Initialize global log.
auto cerrLog = TimestampLog{"ugly::log", std::cerr};
log = LogAlias{&cerrLog};
log("hello, world! app started.");
// Initialize GLFW.
glfwSetErrorCallback(error_callback_s);
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// Create GLFW window and OpenGL context for GLEW.
auto window = glfwCreateWindow(640, 480, "uGLy", NULL, NULL);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK) {
log("glewInit() failed!");
}
// Run app.
auto app = App{window};
app.game_loop();
// Clean up.
glfwTerminate();
glfwSetErrorCallback(NULL);
log("app quitting.");
return 0;
}
void App::error_callback_s(int error, const char *description)
{
log("(glfw error): {}", description);
}
void App::key_callback_s(GLFWwindow *window, int key, int scancode, int action, int mods) {
auto app = static_cast<App*>(glfwGetWindowUserPointer(window));
app->key_callback(key, scancode, action, mods);
}
App::App(GLFWwindow *window):
mpWindow{window},
mShaderProgram{} {
glfwSetWindowUserPointer(mpWindow, this);
glfwSetKeyCallback(mpWindow, key_callback_s);
float vertices[] = {
// Position(3), Color(3), TexCoord(2)
-1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Top-left
1.0f, 1.0f, 0.0f, 0.f, 1.0f, 0.0f, 1.0f, 0.0f, // Top-right
1.0f, -1.0f, 0.0f, 0.f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right
-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, // Bottom-left
-1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f // Top-left
};
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint shaderProgram = ShaderProgramBuilder{}
.attachFromFile(GL_VERTEX_SHADER, "assets/shaders/shader.vert")
.attachFromFile(GL_FRAGMENT_SHADER, "assets/shaders/shader.frag")
.link();
glUseProgram(shaderProgram);
mShaderProgram = shaderProgram;
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
int x, y, n;
auto data = stbi_load("assets/textures/bliss.jpg", &x, &y, &n, 3);
if(data == nullptr) {
log("stbi_load() failed!");
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLint posAttrib = glGetAttribLocation(shaderProgram, "inPos");
GLint colorAttrib = glGetAttribLocation(shaderProgram, "inColor");
GLint coordAttrib = glGetAttribLocation(shaderProgram, "inCoord");
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(3*sizeof(float)));
glVertexAttribPointer(coordAttrib, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));
glEnableVertexAttribArray(posAttrib);
glEnableVertexAttribArray(colorAttrib);
glEnableVertexAttribArray(coordAttrib);
}
void App::game_loop() {
GLint timeUniform = glGetUniformLocation(mShaderProgram, "time");
GLint modelUniform = glGetUniformLocation(mShaderProgram, "model");
GLint viewUniform = glGetUniformLocation(mShaderProgram, "view");
GLint projUniform = glGetUniformLocation(mShaderProgram, "proj");
int width, height;
glfwGetWindowSize(mpWindow, &width, &height);
auto projMatrix = glm::perspective(60.0f, float(width) / height, 0.01f, 1000.f);
auto viewMatrix = glm::lookAt(
glm::vec3{0.f, 0.f, 0.f},
glm::vec3{0.f, 0.f, -1.f},
glm::vec3{0.f, -1.f, 0.f}
);
auto modelMatrix = glm::identity<glm::mat4>();
modelMatrix = glm::translate(modelMatrix, glm::vec3{0.f, 0.f, -.2f});
glUniformMatrix4fv(viewUniform, 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(projUniform, 1, GL_FALSE, glm::value_ptr(projMatrix));
glUniformMatrix4fv(modelUniform, 1, GL_FALSE, glm::value_ptr(modelMatrix));
glEnable(GL_DEPTH_TEST);
auto prevTime = glfwGetTime();
auto elapsed = 0.0;
while(!glfwWindowShouldClose(mpWindow)) {
glfwPollEvents();
auto now = glfwGetTime();
auto dt = now - prevTime;
elapsed += dt;
prevTime = now;
modelMatrix = glm::rotate(modelMatrix, glm::radians(float(dt) * 18.0f), glm::vec3{0.08f, 0.05f, 1.f});
glUniformMatrix4fv(modelUniform, 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniform1f(timeUniform, 4.f * elapsed);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(mpWindow);
}
}
void App::key_callback(int key, int scancode, int action, int mods) {
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(mpWindow, GLFW_TRUE);
}
}
}