Drawing 3D graphics to a bitmap smaller than the display.
roger levy

I'm trying to draw some 3D graphics to a bitmap and then scaling that up to get a pixelated effect.

I'm having trouble figuring out the right math and transformations to get the graphics to appear at the right scale and position. Currently things are appearing too big and not centered. I guess they the projection matrix should be smaller and offset by some amount but I don't know what.

Erin Maus

Can you post your code to construct the projection, view, and world matrices (whichever applicable)? As well the code you use to draw the model?

The easiest method would be to create a projection matrix then use al_build_camera_transform to center the camera at a specific distance (position) from the target (look) using a proper up vector (usually [0, 1, 0]). If the model is centered origin, then look would be [0, 0, 0] and eye would be (for example) [0, 0, distance_from_model (or zoom)].

*edit:* correction

roger levy

How would al_build_camera_transform know about the size of the drawing surface?

I never even heard of the world matrix before now. (Help)

I'll post the code but standard disclaimer about it being in Forth etc

transform p 
80 value fov

: f  fov >rad 2 / tan ;
: asp  viewwh / ;

: ofs  displaywh -2 2 2/ viewwh 2 -2 2/ 2+ ;  \ absolutely no idea what i'm doing here

\ sets up the projection
: 3d
    p al_identity_transform
    p 0 0 -1 3af al_translate_transform_3d
    p   asp f * negate 1af   f 1af           1 1af
        asp f * 1af          f negate 1af    1000 1af
    al_perspective_transform
\     p 1 globalscale / dup dup  3af al_scale_transform_3d
\     p ofs 0 3af al_translate_transform_3d
    p al_use_projection_transform
    
    ALLEGRO_DEPTH_TEST #1 al_set_render_state
    1 1af al_clear_depth_buf
;

transform t
: modelview
    t al_identity_transform
    t scl 3@ 3af al_scale_transform_3d
    t 0 0 1 3af rtn z@ >rad 1af al_rotate_transform_3d
    t 0 1 0 3af rtn y@ >rad 1af al_rotate_transform_3d
    t 1 0 0 3af rtn x@ >rad 1af al_rotate_transform_3d
    t pos 3@ 3af al_translate_transform_3d
    t al_use_transform
;

\ drawing the model
: texture@  tex @ dup if >bmp then ;

: model  ( -- )
    mdl @ -exit
    modelview    
    mdl @ indices @ if 
        mdl @ vertices @
        0 \ vertex decl
        texture@
        mdl @ indices 2@ 1i
        mdl @ primtype @
        al_draw_indexed_prim
    else
        mdl @ vertices 2@ 1i >r
        0 \ vertex decl
        texture@
        0 \ first vertex
        r> \ last vertex
        mdl @ primtype @
        al_draw_prim
    then ;

Here is the code for the "upscaler", it uses other functions in my library so just assume that it's calling al_set_target_bitmap.

depend ramen/lib/draw.f
nativewh canvas: canv
: (upscale)  canv >bmp onto>  unmount call  ;
: upscale>  ( -- <code> )  r>  (upscale)  mount  0 0 at  canv >bmp blit ;

Erin Maus

I have no idea what the Forth code is doing unfortunately.

This is how you draw a model:

#SelectExpand
1ALLEGRO_TRANSFORM alx_not_useless_and_dumb_perspective( 2 ALLEGRO_TRANSFORM *transform, 3 float fov_y, 4 float aspect, 5 float near, float far) 6{ 7 float h = tan(fov_y / 2) * near; 8 float w = h * aspect; 9 10 al_identity_transform(transform); 11 12 // this is a very idiosyncratic function 13 // it annoys me /a lot/ 14 al_perspective_transform(transform, -w, -h, near, w, h, far); 15} 16 17void prepare_camera(const model *model, const ALLEGRO_BITMAP *surface) 18{ 19 float min_x, min_y, min_z; 20 float max_x, max_y, max_z; 21 get_model_bounds(model, &min_x, &min_y, &min_z, &max_x, &max_y, &max_z); 22 23 ALLEGRO_TRANSFORM projection; 24 alx_not_useless_and_dumb_perspective( 25 &projection, 26 ALLEGRO_PI / 3 /* whatever, tweak this */, 27 (float)al_get_bitmap_width(surface) / (float)al_get_bitmap_height(surface), 28 1.0f, 29 max_z - min_z + 1.0f); 30 31 ALLEGRO_TRANSFORM view; 32 al_identity_transform(&view); 33 al_build_camera_transform( 34 &view, 35 min_x + (max_x - min_x) / 2.0f, 36 min_y + (max_y - min_y) / 2.0f, 37 min_z - 1.0, 38 min_x + (max_x - min_x) / 2.0f, 39 min_y + (max_y - min_y) / 2.0f, 40 max_z + 1.0f, 41 0.0f, 42 1.0f, 43 0.0f); 44 45 al_use_projection_matrix(&projection); 46 al_use_transform(&view); 47}

The idea is you build a perspective matrix (tweak fov_y until it looks good), then build a camera transform that has the viewport contain the model. The perspective really doesn't matter for this; we just want near / far set to values that don't result in the model getting clipped.

Depending on which way your model is facing, you may need to look down the X or Y axes. Currently it looks down the Z axis, which requires models to be looking down Z. (For example, if you open Blender and press NumPad 1 (Front Ortho), you should see the model's face.)

al_perspective_transform should be renamed to al_perspective_frustum_transform or something... It's not a very well named function, and its argument order is wack.

Not tested at all but it's identical in logic to how I render 3D previews of models in my game editor.

roger levy

Forgive me, but I don't understand how this takes into account the actual size of the bitmap. I see that your perspective function uses the aspect ratio, but the bitmap is not mentioned anywhere else so how does Allegro know where the "center" of the bitmap is supposed to be and how big it should draw things?

Keep in mind I am not having any trouble rendering to the display, things look fine there - only when I try to render my bitmap.

EDIT: I solved it. After analyzing the Allegro source code I found out glViewport is called only by al_set_perspective_transform, and it depends on the size of the target bitmap. I had set my target bitmap to the wrong size. After setting it to the right size, it works!

depend ramen/lib/draw.f
nativewh canvas: canv

: (size)  viewwh canv resize-canvas ;   \ <-- what I wasn't doing!
: (upscale)  canv >bmp onto>  red backdrop   unmount call ;
: upscale>  ( -- <code> )  (size)  r>  (upscale)  mount  0 0 at  canv >bmp blit ;

Video -> https://twitter.com/RamenEngine/status/1051203794552406016

Edgar Reynaldo

Yeah, use glViewport to set the size of the target bitmap. Sorry I didn't say that earlier, could have saved you some trouble.

Thread #617581. Printed from Allegro.cc