Skip to content

Add a simple 3D example, simpler than ex_camera #1711

@SiegeLord

Description

@SiegeLord
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <stdio.h>
#include <math.h>

// Define inline GLSL vertex shader
const char *vertex_shader_source =
    "#version 120\n" // OpenGL ES 2.0 / WebGL compatibility
    "attribute vec4 al_pos;\n"
    "attribute vec4 al_color;\n"
    "uniform mat4 al_projview_matrix;\n"
    "varying vec4 v_color;\n"
    "void main()\n"
    "{\n"
    "   v_color = al_color;\n"
    "   gl_Position = al_projview_matrix * al_pos;\n"
    "}\n";

// Define inline GLSL fragment shader
const char *fragment_shader_source =
    "#version 120\n" // OpenGL ES 2.0 / WebGL compatibility
    "varying vec4 v_color;\n"
    "void main()\n"
    "{\n"
    "   gl_FragColor = v_color;\n"
    "}\n";

int main(int argc, char **argv)
{
    ALLEGRO_DISPLAY *display = NULL;
    ALLEGRO_EVENT_QUEUE *event_queue = NULL;
    ALLEGRO_TIMER *timer = NULL;
    ALLEGRO_SHADER *shader = NULL;
    ALLEGRO_VERTEX_BUFFER *vertex_buffer = NULL;
    ALLEGRO_INDEX_BUFFER *index_buffer = NULL;
    int ret = 0; // Return value for main

    // Cube vertices (position and color - RGBA for ALLEGRO_VERTEX)
    ALLEGRO_VERTEX vertices[] = {
        // Front face (Red)
        {.x = -0.5f, .y = -0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 0.0f)},
        {.x = 0.5f, .y = -0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 0.0f)},
        {.x = 0.5f, .y = 0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 0.0f)},
        {.x = -0.5f, .y = 0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 0.0f)},

        // Back face (Green)
        {.x = -0.5f, .y = -0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 0.0f)},
        {.x = 0.5f, .y = -0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 0.0f)},
        {.x = 0.5f, .y = 0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 0.0f)},
        {.x = -0.5f, .y = 0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 0.0f)},

        // Right face (Blue)
        {.x = 0.5f, .y = -0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 0.0f, 1.0f)},
        {.x = 0.5f, .y = -0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 0.0f, 1.0f)},
        {.x = 0.5f, .y = 0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 0.0f, 1.0f)},
        {.x = 0.5f, .y = 0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 0.0f, 1.0f)},

        // Left face (Yellow)
        {.x = -0.5f, .y = -0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 1.0f, 0.0f)},
        {.x = -0.5f, .y = -0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 1.0f, 0.0f)},
        {.x = -0.5f, .y = 0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 1.0f, 0.0f)},
        {.x = -0.5f, .y = 0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 1.0f, 0.0f)},

        // Top face (Cyan)
        {.x = -0.5f, .y = 0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 1.0f)},
        {.x = 0.5f, .y = 0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 1.0f)},
        {.x = 0.5f, .y = 0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 1.0f)},
        {.x = -0.5f, .y = 0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(0.0f, 1.0f, 1.0f)},

        // Bottom face (Magenta)
        {.x = -0.5f, .y = -0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 1.0f)},
        {.x = 0.5f, .y = -0.5f, .z = 0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 1.0f)},
        {.x = 0.5f, .y = -0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 1.0f)},
        {.x = -0.5f, .y = -0.5f, .z = -0.5f, .u = 0.0f, .v = 0.0f, .color = al_map_rgb_f(1.0f, 0.0f, 1.0f)},
    };

    // Indices for drawing the cube (two triangles per face)
    unsigned int indices[] = {
        // Front
        0, 1, 2,
        0, 2, 3,
        // Back
        4, 5, 6,
        4, 6, 7,
        // Right
        8, 9, 10,
        8, 10, 11,
        // Left
        12, 13, 14,
        12, 14, 15,
        // Top
        16, 17, 18,
        16, 18, 19,
        // Bottom
        20, 21, 22,
        20, 22, 23,
    };

    // Initialize Allegro
    if (!al_init())
    {
        fprintf(stderr, "failed to initialize allegro!\n");
        ret = -1;
        goto cleanup;
    }

    // Initialize primitives add-on
    if (!al_init_primitives_addon())
    {
        fprintf(stderr, "failed to initialize primitives addon!\n");
        ret = -1;
        goto cleanup;
    }

    // Set OpenGL version for Allegro
    al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 16, ALLEGRO_SUGGEST); // Enable depth buffer
    al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE | ALLEGRO_PROGRAMMABLE_PIPELINE | ALLEGRO_OPENGL);

    // Create display
    display = al_create_display(800, 600);
    if (!display)
    {
        fprintf(stderr, "failed to create display!\n");
        ret = -1;
        goto cleanup;
    }


    // Create event queue
    event_queue = al_create_event_queue();
    if (!event_queue)
    {
        fprintf(stderr, "failed to create event_queue!\n");
        ret = -1;
        goto cleanup;
    }

    // Create timer
    timer = al_create_timer(1.0 / 60.0); // 60 FPS
    if (!timer)
    {
        fprintf(stderr, "failed to create timer!\n");
        ret = -1;
        goto cleanup;
    }

    // Register event sources
    al_register_event_source(event_queue, al_get_display_event_source(display));
    al_register_event_source(event_queue, al_get_timer_event_source(timer));

    // Create vertex buffer using default ALLEGRO_VERTEX declaration (NULL)
    vertex_buffer = al_create_vertex_buffer(
        NULL, vertices, sizeof(vertices) / sizeof(vertices[0]), ALLEGRO_PRIM_BUFFER_STATIC);
    if (!vertex_buffer)
    {
        fprintf(stderr, "failed to create vertex buffer!\n");
        ret = -1;
        goto cleanup;
    }

    // Create index buffer
    index_buffer = al_create_index_buffer(
        sizeof(unsigned int), indices, sizeof(indices) / sizeof(indices[0]), ALLEGRO_PRIM_BUFFER_STATIC);
    if (!index_buffer)
    {
        fprintf(stderr, "failed to create index buffer!\n");
        ret = -1;
        goto cleanup;
    }

    // Create shader program from inline sources
    shader = al_create_shader(ALLEGRO_SHADER_GLSL);
    if (!shader)
    {
        fprintf(stderr, "failed to create shader object!\n");
        ret = -1;
        goto cleanup;
    }

    if (!al_attach_shader_source(shader, ALLEGRO_VERTEX_SHADER, vertex_shader_source))
    {
        fprintf(stderr, "failed to attach vertex shader source: %s\n", al_get_shader_log(shader));
        ret = -1;
        goto cleanup;
    }

    if (!al_attach_shader_source(shader, ALLEGRO_PIXEL_SHADER, fragment_shader_source))
    {
        fprintf(stderr, "failed to attach fragment shader source: %s\n", al_get_shader_log(shader));
        ret = -1;
        goto cleanup;
    }

    if (!al_build_shader(shader))
    {
        fprintf(stderr, "failed to build shader: %s\n", al_get_shader_log(shader));
        ret = -1;
        goto cleanup;
    }


    // Activate our custom shader for the backbuffer
    al_use_shader(shader);
            
    // Enable depth testing
    al_set_render_state(ALLEGRO_DEPTH_TEST, 1);

    bool redraw = true;
    bool do_exit = false;
    float angle = 0.0f;

    al_start_timer(timer);

    while (!do_exit)
    {
        ALLEGRO_EVENT ev;
        al_wait_for_event(event_queue, &ev);

        if (ev.type == ALLEGRO_EVENT_TIMER)
        {
            angle += 0.02f; // Rotate the cube
            redraw = true;
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
        {
            do_exit = true;
        }
        else if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE)
        {
            al_acknowledge_resize(display);
            redraw = true;
        }

        if (redraw && al_is_event_queue_empty(event_queue))
        {
            redraw = false;

            al_clear_to_color(al_map_rgb(0, 0, 128)); // Clear background to blue
            al_clear_depth_buffer(1.); // Clear depth to infinitely far away

            // Set up projection matrix
            ALLEGRO_TRANSFORM projection;
            al_identity_transform(&projection);
            float aspect_ratio = (float)al_get_display_height(display) / al_get_display_width(display);
            al_perspective_transform(&projection, -1, aspect_ratio, 1, 1, -aspect_ratio, 1000);
            al_use_projection_transform(&projection);

            // Set up modelview matrix
            ALLEGRO_TRANSFORM model;
            al_identity_transform(&model);
            al_rotate_transform_3d(&model, 1.0f, 0.0f, 0.0f, angle * 0.5f); // Rotate around X
            al_rotate_transform_3d(&model, 0.0f, 1.0f, 0.0f, angle);        // Rotate around Y
            al_rotate_transform_3d(&model, 0.0f, 0.0f, 1.0f, angle * 0.3f); // Rotate around Z
            al_translate_transform_3d(&model, 0.0f, 0.0f, -3.0f); // Translate

            ALLEGRO_TRANSFORM camera;
            al_build_camera_transform(&camera,
                0.5f * cos(2.0f * angle), 0.0f, 0.0f, // Camera pos, move side to side
                0.5f * cos(2.0f * angle), 0.0f, -3.0f, // Look at
                0.0f, 1.0f, 0.0f // Up
            );

            ALLEGRO_TRANSFORM modelview;
            al_identity_transform(&modelview);
            al_compose_transform(&modelview, &model);
            al_compose_transform(&modelview, &camera);
            al_use_transform(&modelview);

            // Draw the indexed primitive (cube)
            al_draw_indexed_buffer(vertex_buffer, NULL, index_buffer, 0, sizeof(indices) / sizeof(indices[0]), ALLEGRO_PRIM_TRIANGLE_LIST);

            // Flip display
            al_flip_display();
        }
    }

cleanup:
    // Cleanup resources in reverse order of allocation
    if (shader)
        al_destroy_shader(shader);
    if (index_buffer)
        al_destroy_index_buffer(index_buffer);
    if (vertex_buffer)
        al_destroy_vertex_buffer(vertex_buffer);
    if (timer)
        al_destroy_timer(timer);
    if (event_queue)
        al_destroy_event_queue(event_queue);
    if (display)
        al_destroy_display(display);

    return ret;
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions