RyanHub - file viewer
filename: src/render/renderer.c
branch: main
back to repo
// renderer.c

#include "renderer.h"
#include "shader.h"
#include "texture.h"
#include "light.h"
#include "model.h"
#include "cglm/cglm.h"

static vec3 cam_pos = { 0.0f, 2.0f, 5.0f };
static float cam_yaw = 0.0f;
static float cam_pitch = -15.0f;

int window_width = 800;
int window_height = 600;

static GLuint shader_program;
static Model* default_model = NULL;
static Texture* default_texture = NULL;

GLFWwindow* window = NULL;

#define MAX_DRAW_CALLS 1024

typedef struct {
    const Model* model;
    const Texture* texture;
    vec4 color;
    vec3 position;
    vec3 rotation;
    vec3 scale;
} DrawCall;

static DrawCall draw_queue[MAX_DRAW_CALLS];
static int draw_queue_count = 0;

static mat4 view_matrix;
static mat4 proj_matrix;


void window_init() {
    glfwInit();
    window = glfwCreateWindow(window_width, window_height, "My Game", NULL, NULL);
    glfwMakeContextCurrent(window);
    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);

    glfwGetFramebufferSize(window, &window_width, &window_height);
    glViewport(0, 0, window_width, window_height);
}

void renderer_init() {
    glEnable(GL_DEPTH_TEST);

    shader_program = create_shader_program(
        "assets/shaders/default.vert",
        "assets/shaders/default.frag"
    );
    if (!shader_program) {
        printf("Failed to create shader program\n");
        return;
    }

    default_texture = load_texture("assets/textures/default.png");
    if (!default_texture) {
        printf("Failed to load default texture\n");
    }

    float triangle[] = {
        // pos         normal         uv
        -0.5f,-0.5f,   0.0f, 0,0,1,   0.0f,0.0f,
         0.5f,-0.5f,   0.0f, 0,0,1,   1.0f,0.0f,
         0.0f, 0.5f,   0.0f, 0,0,1,   0.5f,1.0f,
    };
    int attrs[] = { 3, 3, 2 };
    default_model = model_create(triangle, 3, attrs, 3);

    if (!default_model) {
        printf("Failed to create default model\n");
    }

    ASSERT_UNIFORM(shader_program, "uModel");
    ASSERT_UNIFORM(shader_program, "uView");
    ASSERT_UNIFORM(shader_program, "uProj");
    ASSERT_UNIFORM(shader_program, "uTexture");
    ASSERT_UNIFORM(shader_program, "uColor");

    ASSERT_UNIFORM(shader_program, "light_positions");
    ASSERT_UNIFORM(shader_program, "light_colors");
    ASSERT_UNIFORM(shader_program, "light_intensities");
    ASSERT_UNIFORM(shader_program, "num_lights");

}

void renderer_begin_frame() {
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    float move_speed = 0.1f;
    float turn_speed = 1.0f;

    static double last_x = 0, last_y = 0;
    static int first_mouse = 1;
    double xpos, ypos;
    glfwGetCursorPos(window, &xpos, &ypos);

    if (first_mouse) {
        last_x = xpos;
        last_y = ypos;
        first_mouse = 0;
    }

    float xoffset = (float)(xpos - last_x);
    float yoffset = (float)(last_y - ypos);
    last_x = xpos;
    last_y = ypos;

    float sensitivity = 0.3f;
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    cam_yaw += xoffset;
    cam_pitch += yoffset;

    if (cam_pitch > 89.0f) cam_pitch = 89.0f;
    if (cam_pitch < -89.0f) cam_pitch = -89.0f;

    vec3 front;
    front[0] = cosf(glm_rad(cam_yaw)) * cosf(glm_rad(cam_pitch));
    front[1] = sinf(glm_rad(cam_pitch));
    front[2] = sinf(glm_rad(cam_yaw)) * cosf(glm_rad(cam_pitch));
    glm_normalize(front);

    vec3 right;
    glm_vec3_crossn(front, (vec3) { 0.0f, 1.0f, 0.0f }, right);

    vec3 up;
    glm_vec3_crossn(right, front, up);

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        glm_vec3_add(cam_pos, (vec3) { front[0] * move_speed, front[1] * move_speed, front[2] * move_speed }, cam_pos);
    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        glm_vec3_sub(cam_pos, (vec3) { front[0] * move_speed, front[1] * move_speed, front[2] * move_speed }, cam_pos);
    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        glm_vec3_sub(cam_pos, (vec3) { right[0] * move_speed, right[1] * move_speed, right[2] * move_speed }, cam_pos);
    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        glm_vec3_add(cam_pos, (vec3) { right[0] * move_speed, right[1] * move_speed, right[2] * move_speed }, cam_pos);
    if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
        cam_pos[1] -= move_speed;
    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
        cam_pos[1] += move_speed;

    vec3 center;
    glm_vec3_add(cam_pos, front, center);

    mat4 view, proj;
    glm_lookat(cam_pos, center, up, view);

    glfwGetFramebufferSize(window, &window_width, &window_height);
    glViewport(0, 0, window_width, window_height);

    float aspect = (float)window_width / (float)window_height;
    glm_perspective(glm_rad(45.0f), aspect, 0.1f, 100.0f, proj);

    renderer_set_view_proj(view, proj);
}

void renderer_end_frame() {
    if (!shader_program || !default_model) {
        printf("Cannot render: shader or model not initialized\n");
        return;
    }

    glUseProgram(shader_program);

    GLint loc_model = glGetUniformLocation(shader_program, "uModel");
    GLint loc_view = glGetUniformLocation(shader_program, "uView");
    GLint loc_proj = glGetUniformLocation(shader_program, "uProj");
    GLint loc_tex = glGetUniformLocation(shader_program, "uTexture");
    GLint loc_color = glGetUniformLocation(shader_program, "uColor");

    if (loc_model < 0 || loc_view < 0 || loc_proj < 0 || loc_tex < 0) {
        printf("Shader uniforms not found: model=%d view=%d proj=%d tex=%d\n", loc_model, loc_view, loc_proj, loc_tex);
        return;
    }

    glUniformMatrix4fv(loc_view, 1, GL_FALSE, (float*)view_matrix);
    glUniformMatrix4fv(loc_proj, 1, GL_FALSE, (float*)proj_matrix);

    vec3 positions[MAX_LIGHTS];
    vec3 colors[MAX_LIGHTS];
    float intensities[MAX_LIGHTS];
    int num_lights = 0;

    for (int i = 0; i < g_light_count; ++i) {
        if (!g_lights[i].enabled) continue;

        glm_vec3_copy(g_lights[i].position, positions[num_lights]);
        glm_vec3_copy(g_lights[i].color, colors[num_lights]);
        intensities[num_lights] = g_lights[i].intensity;
        ++num_lights;
    }

    GLint loc_pos = glGetUniformLocation(shader_program, "light_positions");
    GLint loc_col = glGetUniformLocation(shader_program, "light_colors");
    GLint loc_int = glGetUniformLocation(shader_program, "light_intensities");
    GLint loc_num = glGetUniformLocation(shader_program, "num_lights");
    glUniform3fv(loc_pos, num_lights, (float*)positions);
    glUniform3fv(loc_col, num_lights, (float*)colors);
    glUniform1fv(loc_int, num_lights, intensities);
    glUniform1i(loc_num, num_lights);

    GLenum err = glGetError();
    if (err != GL_NO_ERROR) {
        printf("OpenGL Error: %d\n", err);
    }


    //printf("Drawing %d models\n", draw_queue_count);

    for (int i = 0; i < draw_queue_count; i++) {
        DrawCall* call = &draw_queue[i];

        const Model* m = call->model ? call->model : default_model;
        const Texture* t = call->texture ? call->texture : default_texture;

        //if (!call->model) {
        //    printf("using default model\n");
        //}

        mat4 model;
        glm_mat4_identity(model);
        glm_translate(model, call->position);
        glm_rotate_z(model, call->rotation[2], model);
        glm_rotate_y(model, call->rotation[1], model);
        glm_rotate_x(model, call->rotation[0], model);
        glm_scale(model, call->scale);


        glUniformMatrix4fv(loc_model, 1, GL_FALSE, (float*)model);
        glUniform4fv(loc_color, 1, call->color);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, t ? t->id : 0);
        glUniform1i(loc_tex, 0);

        glBindVertexArray(m->vao);
        glDrawArrays(GL_TRIANGLES, 0, m->vertex_count);

        GL_CHECK_ERROR("glDrawArrays");
    }

    draw_queue_count = 0;
    glfwSwapBuffers(window);
}

void renderer_draw_model(const Model* model, const Texture* texture, const vec4 color, const vec3 pos, const vec3 rot, const vec3 scale) {
    //printf("Drawing: pos.y=%.2f, scale.y=%.2f\n", pos[1], scale[1]);

    if (draw_queue_count >= MAX_DRAW_CALLS) {
        #if defined DEBUG || _DEBUG
                fprintf(stderr, "[Renderer] Draw queue overflow.\n");
                assert(0 && "Too many draw calls this frame");
        #endif
                return;
    }

    #if defined DEBUG || _DEBUG
        if (model && model->vao == 0) {
            fprintf(stderr, "[Renderer] Model VAO is 0 (likely uninitialized)\n");
            assert(0 && "Model VAO invalid");
        }

        if (texture && texture->id == 0) {
            fprintf(stderr, "[Renderer] Texture ID is 0 (likely uninitialized)\n");
            assert(0 && "Texture ID invalid");
        }
    #endif

    DrawCall* dc = &draw_queue[draw_queue_count++];
    dc->model = model;
    dc->texture = texture;

    const float zero3[3] = { 0.0f, 0.0f, 0.0f };
    const float one3[3] = { 1.0f, 1.0f, 1.0f };
    const float default_color[4] = { 1, 1, 1, 1 };

    glm_vec4_copy(color ? color : default_color, dc->color);
    glm_vec3_copy(pos ? pos : zero3, dc->position);
    glm_vec3_copy(rot ? rot : zero3, dc->rotation);
    glm_vec3_copy(scale ? scale : one3, dc->scale);
}


void renderer_set_view_proj(mat4 view, mat4 proj) {
    glm_mat4_copy(view, view_matrix);
    glm_mat4_copy(proj, proj_matrix);
}