Allegro 5 3D projections
Mark Oates

Now that the newer version of allegro has 3D routines, I'm trying to set things up to play around with.

I've looked at ex_projection.c and tried to adapt what's going on there. I'm still at a block and haven't been able to find any general purpose tutorial, explanation on the wiki, or even any good discussions on the forums of the general "pipeline" of how a proper 3D scene could be achieved using Allegro 5.

I want to properly use vertex and index buffers, and I think I understand how they work (even though I haven't used one yet).

What do I need to set-up? How do I need to affect my models before render?
How does a camera fit into this?

Here's where I'm at:

#SelectExpand
1class Model // as in a 3D Model 2{ 3public: 4 std::vector<ALLEGRO_VERTEX> vertex; 5 ALLEGRO_VERTEX vtx[3]; 6 //placement3d place; 7 8 Model(int size) 9 { 10 vertex.push_back(build_vertex(-size, -size, 0, color::green, 0, 0)); 11 vertex.push_back(build_vertex(size, -size, 0, color::green, 0, 0)); 12 vertex.push_back(build_vertex(size, size, 0, color::blue, 0, 0)); 13 } 14 15 void draw() 16 { 17 al_draw_prim(&vertex[0], NULL, NULL, 0, vertex.size(), ALLEGRO_PRIM_TRIANGLE_STRIP); 18 } 19}; 20 21 22 23class Project : public Screen 24{ 25public: 26 ALLEGRO_TRANSFORM camera_transform; 27 Model model; 28 Project(Display *display) 29 : Screen(display) 30 , model(100) 31 {} 32 static void setup_3d_projection(ALLEGRO_TRANSFORM *projection) 33 { 34 ALLEGRO_DISPLAY *display = al_get_current_display(); 35 int dw = al_get_display_width(display); 36 int dh = al_get_display_height(display); 37 al_perspective_transform(projection, -180 * dw / dh, -180, 180, 38 180 * dw / dh, 180, 3000); 39 al_set_projection_transform(display, projection); 40 } 41 void primary_timer_func() 42 { 43 setup_3d_projection(&camera_transform); 44 model.draw(); 45 } 46};

I think if I get a bare-bones example that renders two rotating textured cubes that z-buffer properly, I would be ok.

Trezker
Mark Oates

Hmm, I was hoping I wouldn't have to describe my framework.

The Project class inherits Screen and then overrides Screen::primary_timer_func().

Afterwhich, the flip is called.

  for (unsigned d=0; d<Display::displays.size(); d++)
  {
    Display::displays[d]->set_as_target_bitmap();
    al_clear_to_color(Display::displays[d]->_background_color);
    for (unsigned i=0; i<screens.size(); i++)
    {
      if (screens[i]->display == Display::displays[d])
        screens[i]->primary_timer_func();
    }
    Display::displays[d]->flip(); // <-- here
  }

A Screen is anything, like a menu screen, a game screen, a world map screen. You can nest several screens at the same time, so you would have a world screen and a hud screen at the same time.

But that's all tangential.

TLDR; the flip is in there.

beoran

Interesting! I never thought about drawing a 3D model with an Allegro al_draw_prim... Does that even work? I'll try it myself later if no one else steps up.

SiegeLord

Not cubes and not textured, but those parts are not really relevant, are they?

#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_primitives.h> 3#include "stdio.h" 4 5int main() 6{ 7 al_init(); 8 9 auto dw = 800; 10 auto dh = 600; 11 12 al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 16, ALLEGRO_SUGGEST); 13 auto d = al_create_display(dw, dh); 14 15 if(!d) 16 { 17 printf("Failed to create display\n"); 18 return 1; 19 } 20 21 al_install_keyboard(); 22 23 auto queue = al_create_event_queue(); 24 auto timer = al_create_timer(1.0 / 60.0); 25 26 al_register_event_source(queue, al_get_timer_event_source(timer)); 27 al_register_event_source(queue, al_get_keyboard_event_source()); 28 al_start_timer(timer); 29 30 al_init_primitives_addon(); 31 32 int ind[] = 33 { 34 0, 1, 2, 35 0, 1, 3, 36 0, 2, 3, 37 1, 2, 3 38 }; 39 40 ALLEGRO_VERTEX vtx[4]; 41 vtx[0].x = -200; 42 vtx[0].y = -200; 43 vtx[0].z = -200; 44 vtx[0].color = al_map_rgb_f(0, 0, 1); 45 46 vtx[1].x = 0; 47 vtx[1].y = -200; 48 vtx[1].z = 200; 49 vtx[1].color = al_map_rgb_f(0, 1, 0); 50 51 vtx[2].x = 200; 52 vtx[2].y = -200; 53 vtx[2].z = -200; 54 vtx[2].color = al_map_rgb_f(1, 0, 1); 55 56 vtx[3].x = 0; 57 vtx[3].y = 200; 58 vtx[3].z = 0; 59 vtx[3].color = al_map_rgb_f(1, 1, 1); 60 61 float theta = 0; 62 ALLEGRO_TRANSFORM transform; 63 64 int mode = 0; 65 bool done = false; 66 bool redraw = true; 67 while(!done) 68 { 69 ALLEGRO_EVENT ev; 70 al_wait_for_event(queue, &ev); 71 switch(ev.type) 72 { 73 case ALLEGRO_EVENT_KEY_DOWN: 74 switch(ev.keyboard.keycode) 75 { 76 case ALLEGRO_KEY_ESCAPE: 77 done = true; 78 break; 79 } 80 break; 81 case ALLEGRO_EVENT_TIMER: 82 theta += 0.01; 83 redraw = true; 84 break; 85 } 86 87 if(al_is_event_queue_empty(queue) && redraw) 88 { 89 al_clear_to_color(al_map_rgb_f(0, 0, 0)); 90 al_clear_depth_buffer(1000); 91 al_set_render_state(ALLEGRO_DEPTH_TEST, 1); 92 93 al_identity_transform(&transform); 94 al_rotate_transform_3d(&transform, 0, 1, 0, theta); 95 al_translate_transform_3d(&transform, 100, 0, -800); 96 al_use_transform(&transform); 97 98 al_draw_indexed_prim(vtx, NULL, NULL, ind, 12, ALLEGRO_PRIM_TRIANGLE_LIST); 99 100 al_identity_transform(&transform); 101 al_rotate_transform_3d(&transform, 0, 1, 0, theta / 2); 102 al_translate_transform_3d(&transform, -100, 0, -800); 103 al_use_transform(&transform); 104 105 al_draw_indexed_prim(vtx, NULL, NULL, ind, 12, ALLEGRO_PRIM_TRIANGLE_LIST); 106 107 al_identity_transform(&transform); 108 al_perspective_transform(&transform, -dw / 2, -dh / 2, dw / 2, dw / 2, dh / 2, 10000); 109 al_set_projection_transform(d, &transform); 110 111 al_flip_display(); 112 113 redraw = false; 114 } 115 } 116}

Mark Oates
SiegeLord said:

Not cubes and not textured, but those parts are not really relevant, are they?

Not at all. :) Your post was tremendously helpful. I managed to make some progress. I still don't quite get that the perspective_transform/projection_transform is not related to drawing the primitives themselves. When drawing the primitives, are they not (actually) fully drawn until the flip? And how they are drawn is relative to the projection as set at some point before the flip?

Also, after some digging in my framework code, I found that a line:

al_set_target_bitmap(al_get_backbuffer(d));

is causing the 3D to not render correctly. If I put it into your code thusly:

...

if(al_is_event_queue_empty(queue) && redraw)
{
   al_set_target_bitmap(al_get_backbuffer(d));

   ...
}

...

it has the same result. I initially use that line to "reset" the drawing back so that the next screen can be drawn without any states set by the prior screen.

SiegeLord

When drawing the primitives, are they not (actually) fully drawn until the flip?

That is undefined, but that's not really what you want to know... they take into account the transformations that were active at the time of the function call.

Quote:

is causing the 3D to not render correctly

Projection transforms are very poorly implemented in A5 right now. The usual transformations ( al_use_transform) are a bitmap-local state, while the projection transformations ( al_set_projection_transform) are display local and are reset to be orthographic whenever you change target bitmaps. This is a disaster I noticed years ago, but haven't had time to fix myself.

Quote:

it has the same result.

So the solution is to do this:

#SelectExpand
1if(al_is_event_queue_empty(queue) && redraw) 2{ 3 al_set_target_bitmap(al_get_backbuffer(d)); 4 5 // Set the projection transform back 6 al_identity_transform(&transform); 7 al_perspective_transform(&transform, -dw / 2, -dh / 2, dw / 2, dw / 2, dh / 2, 10000); 8 al_set_projection_transform(d, &transform); 9 10 al_clear_to_color(al_map_rgb_f(0, 0, 0)); 11 al_clear_depth_buffer(1000); 12 al_set_render_state(ALLEGRO_DEPTH_TEST, 1); 13 14 al_identity_transform(&transform); 15 al_rotate_transform_3d(&transform, 0, 1, 0, theta); 16 al_translate_transform_3d(&transform, 100, 0, -800); 17 al_use_transform(&transform); 18 19 al_draw_indexed_prim(vtx, NULL, NULL, ind, 12, ALLEGRO_PRIM_TRIANGLE_LIST); 20 21 al_identity_transform(&transform); 22 al_rotate_transform_3d(&transform, 0, 1, 0, theta / 2); 23 al_translate_transform_3d(&transform, -100, 0, -800); 24 al_use_transform(&transform); 25 26 al_draw_indexed_prim(vtx, NULL, NULL, ind, 12, ALLEGRO_PRIM_TRIANGLE_LIST); 27 28 al_flip_display(); 29 30 redraw = false; 31}

Mark Oates

Not too terribly exciting, but I'm happy with what I've managed to accomplish so far :)

{"name":"608537","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/d\/0d38c4b169abf0c592eb6dbf494517bc.png","w":960,"h":540,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/d\/0d38c4b169abf0c592eb6dbf494517bc"}608537
{"name":"608540","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/1\/b1db0c150a901b24beae2da4b76d1717.png","w":960,"h":540,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/b\/1\/b1db0c150a901b24beae2da4b76d1717"}608540

beoran

Cool! How do you get the textures on there?

Mark Oates

Textures are very easy. In the ALLEGRO_VERTEX there are 6 values:

struct ALLEGRO_VERTEX {
  float x, y, z;
  float u, v;
  ALLEGRO_COLOR color;
};

(u, v) are the (x, y) coordinates of the texture in pixel coordinates. If you overshoot or undershoot (u, v) to the width/height of the texture then the texture will loop.

When you draw the prim (via al_draw_prim or whatnot) then you pass the ALLEGRO_BITMAP texture in to that function.

beoran

I see, I should have thought about it myself! Drat, now I want to program a classic dungeon crawler in Allegro... one day! :)

Thread #614194. Printed from Allegro.cc