Files
ugly/source/app/src/App.cpp
2025-07-31 07:12:50 -04:00

198 lines
6.2 KiB
C++

#include "App.hpp"
#include <array>
#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"
constexpr std::array<std::array<float, 6>, 36> getCubeVertices();
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{} {
// To allow key_callback_s to access member functions
glfwSetWindowUserPointer(mpWindow, this);
glfwSetKeyCallback(mpWindow, key_callback_s);
// Prepare shader
mShaderProgram = ShaderProgramBuilder{}
.attachFromFile(GL_VERTEX_SHADER, "assets/shaders/shader.vert")
.attachFromFile(GL_FRAGMENT_SHADER, "assets/shaders/shader.frag")
.link();
GLint posAttrib = glGetAttribLocation(mShaderProgram, "inPosition");
GLint colorAttrib = glGetAttribLocation(mShaderProgram, "inColor");
glCreateVertexArrays(1, &mVAO);
glBindVertexArray(mVAO);
// Upload vertice attributes (position and color)
const auto vertices = getCubeVertices();
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), (void*)(&vertices), GL_STATIC_DRAW);
// Vertex format = { x, y, z, r, g, b }
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), 0);
glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float)));
glEnableVertexAttribArray(posAttrib);
glEnableVertexAttribArray(colorAttrib);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void App::game_loop() {
// Generate view and projection tranforms
int width, height;
glfwGetWindowSize(mpWindow, &width, &height);
auto projMatrix = glm::perspective(glm::radians(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}
);
GLint matrixUniform = glGetUniformLocation(mShaderProgram, "inTransformation");
float startTime = glfwGetTime();
while(!glfwWindowShouldClose(mpWindow)) {
glfwPollEvents();
// Rotate the cube each frame
float dt = glfwGetTime() - startTime;
auto modelMatrix = glm::identity<glm::mat4>();
modelMatrix = glm::translate(modelMatrix, glm::vec3{0.f, 0.f, 2.f});
modelMatrix = glm::rotate(modelMatrix, glm::radians(20.f * dt), glm::vec3{1.f, 1.f, 0.f});
auto finalMatrix = projMatrix * viewMatrix * modelMatrix;
// Draw logic
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glUseProgram(mShaderProgram);
glBindVertexArray(mVAO);
glUniformMatrix4fv(matrixUniform, 1, GL_FALSE, glm::value_ptr(finalMatrix));
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glUseProgram(0);
glDisable(GL_DEPTH_TEST);
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);
}
}
}
constexpr std::array<std::array<float, 6>, 36> getCubeVertices() {
// The eight vertices of a cube
const float cubeVertices[8][3] = {
{ -0.5f, -0.5f, -0.5f },
{ -0.5f, +0.5f, -0.5f },
{ +0.5f, +0.5f, -0.5f },
{ +0.5f, -0.5f, -0.5f },
{ -0.5f, -0.5f, +0.5f },
{ -0.5f, +0.5f, +0.5f },
{ +0.5f, +0.5f, +0.5f },
{ +0.5f, -0.5f, +0.5f }
};
// Color for each of the six faces
const float cubeColors[6][3] = {
{ 1.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f },
{ 1.0f, 1.0f, 0.0f },
{ 1.0f, 0.0f, 1.0f },
{ 0.0f, 1.0f, 1.0f }
};
// Each face consists of two triangles, so 36 unique vertices total
const int vertexIndices[36] = {
0, 1, 2, 2, 3, 0, // B
4, 5, 1, 1, 0, 4, // L
7, 6, 5, 5, 4, 7, // F
3, 2, 6, 6, 7, 3, // R
0, 3, 7, 7, 4, 0, // D
2, 1, 5, 5, 6, 2 // U
};
// Construct an array suitable for OpenGL - concatenates position and color for each vertex
// { x, y, z, r, g, b }
std::array<std::array<float, 6>, 36> vertices;
for(int i = 0; i < 36; i++) {
vertices[i][0] = cubeVertices[vertexIndices[i]][0];
vertices[i][1] = cubeVertices[vertexIndices[i]][1];
vertices[i][2] = cubeVertices[vertexIndices[i]][2];
vertices[i][3] = cubeColors[i/6][0];
vertices[i][4] = cubeColors[i/6][1];
vertices[i][5] = cubeColors[i/6][2];
}
return vertices;
}