Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Sin & Cos Questions (Amarillion's tutorial)

This thread is locked; no one can reply to it. rss feed Print
Sin & Cos Questions (Amarillion's tutorial)
scriptX
Member #6,574
November 2005
avatar

Nearing the end of Amarillion's tutorial, [url http://pixwiki.bafsoft.com/mags/5/articles/circle/sincos.htm]Sin & Cos: The Programmer's Pals![/url], I started getting confused with some things, namely the whole "space cordinates to screen coordinates" concept. I was hoping someone could clear this up for me by justifying the math behind his code (circ11.c and circ12.c [url http://pixwiki.bafsoft.com/mags/5/articles/circle/circle_examples.zip]here[/url]) or coming up with some crazy analogies :P

PS:
Go easy on the math terms, I don't want to look anything up (which is pointless because for every mathematical term, there's an accompanying 99 that you have to look up as well...)

circ12.c:

#SelectExpand
1/* 2 CIRCLE 12 3 Written by Amarillion (amarillion@yahoo.com) 4 5 This program uses "Mode 7" to map a bitmap on to a horizontal plane. 6 The mode_7 function is actually pretty similar to the rotate_sprite 7 function. 8 9 This is almost the same as CIRCLE 11, but this example also shows 10 how to draw objects on the surface of the map. 11 12 You can move around with the up, down, left and right keys. Use the 13 following keys to change the Mode 7 parameters: 14 H / J : move the horizon up / down 15 Q / W : change the x scale 16 E / R : change the y scale 17 Z / X : change the camera height 18*/ 19 20#include <allegro.h> 21 22/* MODE_7_PARAMS is a struct containing all the different parameters 23that are relevant for Mode 7, so you can pass them to the functions 24as a single unit */ 25typedef struct MODE_7_PARAMS 26{ 27 fixed space_z; // this is the height of the camera above the plane 28 int horizon; // this is the number of pixels line 0 is below the horizon 29 fixed scale_x, scale_y; // this determines the scale of space coordinates 30 // to screen coordinates 31 fixed obj_scale_x, obj_scale_y; // this determines the relative size of 32 // the objects 33} MODE_7_PARAMS; 34 35/* draw_object just draws a single object at a fixed position, although 36this can easily be modified to allow for more objects. 37bmp = bitmap to draw to. obj = sprite for the object. 38angle, cx, cy define the camera position. 39*/ 40void draw_object (BITMAP *bmp, BITMAP *obj, fixed angle, fixed cx, fixed cy, MODE_7_PARAMS params) 41{ 42 int width, height; 43 int screen_y, screen_x; 44 45 // The object in this case is at a fixed position of (160, 100). 46 // Calculate the position relative to the camera. 47 fixed obj_x = itofix(160) - cx; 48 fixed obj_y = itofix(100) - cy; 49 50 // use a rotation transformation to rotate the object by the camera 51 // angle 52 fixed space_x = fmul (obj_x, fcos (angle)) + fmul (obj_y, fsin (angle)); 53 fixed space_y = -fmul (obj_x, fsin (angle)) + fmul (obj_y, fcos (angle)); 54 55 // calculate the screen coordinates that go with these space coordinates 56 // by dividing everything by space_x (the distance) 57 screen_x = bmp->w/2 + fixtoi (fmul (fdiv (params.scale_x, space_x), space_y)); 58 screen_y = fixtoi (fdiv (fmul (params.space_z, params.scale_y), space_x)) - params.horizon; 59 60 // the size of the object has to be scaled according to the distance 61 height = fixtoi (obj->h * fdiv(params.obj_scale_y, space_x)); 62 width = fixtoi (obj->w * fdiv(params.obj_scale_x, space_x)); 63 64 // draw the object 65 stretch_sprite (bmp, obj, screen_x - width / 2, screen_y - height, width, height); 66} 67 68void mode_7 (BITMAP *bmp, BITMAP *tile, fixed angle, fixed cx, fixed cy, MODE_7_PARAMS params) 69{ 70 // current screen position 71 int screen_x, screen_y; 72 73 // the distance and horizontal scale of the line we are drawing 74 fixed distance, horizontal_scale; 75 76 // masks to make sure we don't read pixels outside the tile 77 int mask_x = (tile->w - 1); 78 int mask_y = (tile->h -1); 79 80 // step for points in space between two pixels on a horizontal line 81 fixed line_dx, line_dy; 82 83 // current space position 84 fixed space_x, space_y; 85 86 for (screen_y = 0; screen_y < bmp->h; screen_y++) 87 { 88 // first calculate the distance of the line we are drawing 89 distance = fdiv (fmul (params.space_z, params.scale_y), 90 itofix (screen_y + params.horizon)); 91 // then calculate the horizontal scale, or the distance between 92 // space points on this horizontal line 93 horizontal_scale = fdiv (distance, params.scale_x); 94 95 // calculate the dx and dy of points in space when we step 96 // through all points on this line 97 line_dx = fmul (-fsin(angle), horizontal_scale); 98 line_dy = fmul (fcos(angle), horizontal_scale); 99 100 // calculate the starting position 101 space_x = cx + fmul (distance, fcos(angle)) - bmp->w/2 * line_dx; 102 space_y = cy + fmul (distance, fsin(angle)) - bmp->w/2 * line_dy; 103 104 // go through all points in this screen line 105 for (screen_x = 0; screen_x < bmp->w; screen_x++) 106 { 107 // get a pixel from the tile and put it on the screen 108 putpixel (bmp, screen_x, screen_y, 109 getpixel (tile, 110 fixtoi (space_x) & mask_x, 111 fixtoi (space_y) & mask_y )); 112 // advance to the next position in space 113 space_x += line_dx; 114 space_y += line_dy; 115 } 116 } 117} 118 119void test_mode_7 () 120{ 121 MODE_7_PARAMS params; 122 BITMAP *tile, *sprite, *buffer; 123 PALETTE pal; 124 int quit = FALSE; 125 fixed angle = itofix (0); 126 fixed x = 0, y = 0; 127 fixed dx = 0, dy = 0; 128 fixed speed = 0; 129 int i, j, r2; 130 131 params.space_z = itofix (50); 132 params.scale_x = ftofix (200.0); 133 params.scale_y = ftofix (200.0); 134 params.obj_scale_x = ftofix (50.0); 135 params.obj_scale_y = ftofix (50.0); 136 params.horizon = 20; 137 138 // to avoid flicker the program makes use of a double-buffering system 139 buffer = create_bitmap (screen->w, screen->h); 140 // create a 64x64 tile bitmap and draw something on it. 141 tile = create_bitmap (64, 64); 142 for (i = 0; i < 32; i++) 143 { 144 for (j = i; j < 32; j++) 145 { 146 putpixel (tile, i, j, i); 147 putpixel (tile, i, 63-j, i); 148 putpixel (tile, 63-i, j, i); 149 putpixel (tile, 63-i, 63-j, i); 150 putpixel (tile, j, i, i); 151 putpixel (tile, j, 63-i, i); 152 putpixel (tile, 63-j, i, i); 153 putpixel (tile, 63-j, 63-i, i); 154 } 155 } 156 text_mode (-1); 157 158 // Create another bitmap and draw something to it. 159 // This bitmap contains the object. 160 sprite = create_bitmap (64, 64); 161 clear (sprite); 162 for (i = 0; i < 64; i++) 163 { 164 for (j = 0; j < 64; j++) 165 { 166 r2 = (32 - i) * (32 - i) + (32 - j) * (32 - j); 167 if (r2 < 30 * 30) 168 { 169 r2 = (24 - i) * (24 - i) + (24 - j) * (24 - j); 170 putpixel (sprite, i, j, 127 - fixtoi(fsqrt(itofix(r2)))); 171 } 172 } 173 } 174 175 // create a palette 176 // colors for the tiles 177 for (i = 0; i < 64; i++) 178 { 179 pal<i>.r = i; 180 pal<i>.g = i; 181 pal<i>.b = 0; 182 } 183 // colors for the object 184 for (i = 0; i < 32; i++) 185 { 186 pal[i+64].r = 0; 187 pal[i+64].g = 0; 188 pal[i+64].b = 2 * i; 189 pal[i+96].r = 2 * i; 190 pal[i+96].g = 2 * i; 191 pal[i+96].b = 63; 192 } 193 set_palette (pal); 194 195 while (!quit) 196 { 197 // act on keyboard input 198 if (key[KEY_ESC]) quit = TRUE; 199 if (key[KEY_UP] && speed < itofix (5)) 200 speed += ftofix (0.1); 201 if (key[KEY_DOWN] && speed > itofix (-5)) 202 speed -= ftofix (0.1); 203 if (key[KEY_LEFT]) 204 angle = (angle - itofix (3)) & 0xFFFFFF; 205 if (key[KEY_RIGHT]) 206 angle = (angle + itofix (3)) & 0xFFFFFF; 207 if (key[KEY_Z]) 208 params.space_z += itofix(5); 209 if (key[KEY_X]) 210 params.space_z -= itofix(5); 211 if (key[KEY_Q]) 212 params.scale_x = fmul (params.scale_x, ftofix (1.5)); 213 if (key[KEY_W]) 214 params.scale_x = fdiv (params.scale_x, ftofix (1.5)); 215 if (key[KEY_E]) 216 params.scale_y = fmul (params.scale_y, ftofix (1.5)); 217 if (key[KEY_R]) 218 params.scale_y = fdiv (params.scale_y, ftofix (1.5)); 219 if (key[KEY_H]) 220 params.horizon++; 221 if (key[KEY_J]) 222 params.horizon--; 223 224 dx = fmul (speed, fcos (angle)); 225 dy = fmul (speed, fsin (angle)); 226 227 x += dx; 228 y += dy; 229 230 mode_7 (buffer, tile, angle, x, y, params); 231 draw_object (buffer, sprite, angle, x, y, params); 232 vsync(); 233 blit (buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 234 235 } 236 destroy_bitmap (tile); 237 destroy_bitmap (sprite); 238 destroy_bitmap (buffer); 239} 240 241int main () 242{ 243 // initialize Allegro 244 if (allegro_init () < 0) 245 { 246 allegro_message ("Error: Could not initialize Allegro"); 247 return -1; 248 } 249 // initialize gfx mode 250 if (set_gfx_mode (GFX_AUTODETECT, 320, 200, 0, 0) < 0) 251 { 252 allegro_message ("Error: Could not set graphics mode"); 253 return -1; 254 } 255 // initialize keyboard 256 install_keyboard (); 257 clear_keybuf (); 258 259 // call the example function 260 test_mode_7 (); 261 262 // exit Allegro 263 allegro_exit (); 264 265 return 0; 266 267} END_OF_MAIN ();

NyanKoneko
Member #5,617
March 2005
avatar

Space coordinate as what you, as the programmer, make up. In OpenGL, for example, you will often find programmers using 1.0f to represent the top of the screen, -1.0f for the bottom, -1.0f for the left side of the screen, and 1.0f for the right side of the screen.

So a dot at the right side of the screen in the middle would be defined (in code) as (1.0f, 0.0f). A dot in the middle of the screen would be (0.0f, 0.0f). A dot at the bottom of the screen in the middle would be (0.0f, -1.0f).

(-1, 1) ------- (0, 1) ---------- (1, 1)
|
|
|
(-1, 0)         (0, 0)            (1, 0)
|
|
|
(-1,-1) --------- (0, -1) ------- (1, -1)

Now the screen, in memory, can really defined by a 2 dimentional array defiend as video_mem[height][width]. This is what's called screen coordinates. It usually goes from (0,0) - Upper Left, to (width_resolution, height_resolution) - Lower Right. So to convert world coordinates to screen coordinates, you would have to take the coordiantes you made up, like from -1 to 1, and convert them into which pixels in the array. So if you're running in 640 x 480, you would convert the word coordinates (1, -1) from above as (639, 479) in screen coordinates to represent the bottom right hand side of the screen.

In other words, World Coordinates are what the programmer makes up to best fit whatever the program is supposed to do, while screen coordinates are made up by the hardware and video memory.

Zaphos
Member #1,468
August 2001

In the sin/cos tutorial, those sections are describing a technique for representing a 3D world in 2D. To do this, you need to be able to take points in 3D space and know where to place them on the screen -- i.e. in 2D space. The simplest method for this is to just divide the 3D coordinates by their 'z' value, or their distance from the screen, which has the effect of squishing together pixels which are farther from the screen.
So this gives us his equation:
space_x / space_z = screen_x
space_y / spaze_z = screen_y
space_z / space_z = 1

So now we have a way of 'translating' pixels in 3D space to pixels in 2D space. But we can't just pick pixels in 3D space, translate them, and fill the screen -- what if we pick pixels that are too far apart, and we miss parts of the screen? So we need a way of saying, 'for this pixel on the screen, what part of 3D space did it come from?'. Note that he is trying to draw a plane of fixed 'y', so space_y is a known constant.
This gives us his equations:
space_z = space_y / screen_y
space_x = screen_x * space_y / screen_y

Due to the nature of the technique he gives for rendering, he wants to turn the space_x variable into a value which means 'the amount of movement in x, in space, we need to move one pixel in x, in the screen', so he replaces screen_x with the constant 1:
space_x = 1 * space_y / screen_y

His variables scale_x and scale_y are introduced to solve the issues described by Nyan; they scale the space coordinates so that the screen coordinates that result from this translation correspond to the right amount of screen-space. They're constants that you may adjust, and should be set to 'whatever looks good'.

edit: I missed one: The horizon variable is also a constant which can be set to 'whatever looks good' -- it determines where on the screen the horizon should be ... it's just an offset you add to screen_y.
Also, horizontal_scale in the code is essentially space_x in the description.
There's more to the algorithm, but this should cover most of the details of the world-space to screen-space translation. Hope it helps :)

scriptX
Member #6,574
November 2005
avatar

Slowly but surely.. -.-

Thanks guys.

Go to: