Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Mode 7 limitations

Credits go to Thomas Harte for helping out!
This thread is locked; no one can reply to it. rss feed Print
Mode 7 limitations
ngiacomelli
Member #5,114
October 2004

Well, my primary question is: how limited is Mode 7? I've written the rendering code, my movement code. I've written a nice structure to contain objects and sprites. I've optimized it (though not fully). It runs smoothly. But can I make a game with what I have here?

What I wanted was a basic representation of a 3d playfield. I was prepared to totally fake this because I'm absolutely hopeless with math. I knew that I didn't want any kind of terrain or complex structures. I just wanted a flat plane that I could fly around in and display sprites with. Which is what I have now! Hurray!

Now, I have a few specific questions. When the engine started out I gave the controller the ability to adjust the cameras Z position. I soon ran into a few problems with this method (due to the way I was rendering). After a bit of investigation, it looks like most Mode 7 titles seem to use a static image for a backdrop. I was wondering if this was a limitation of the technique? Or of the hardware?

In terms of coding, my current problem is finding the location of my mouse pointer in terms of 3d space? If anyone could give me a hand with that, that would be fantastic.

Thomas Harte
Member #33
April 2000
avatar

Quote:

Now, I have a few specific questions. When the engine started out I gave the controller the ability to adjust the cameras Z position. I soon ran into a few problems with this method (due to the way I was rendering). After a bit of investigation, it looks like most Mode 7 titles seem to use a static image for a backdrop. I was wondering if this was a limitation of the technique? Or of the hardware?

There is no limitation to the positioning in 3d space of a camera in a Mode 7 engine. It may also be freely rotated around the vertical and horizontal axes. If you really wanted to rotate around the forward axis you could do something with bitmap rotation, I'm sure.

Quote:

In terms of coding, my current problem is finding the location of my mouse pointer in terms of 3d space? If anyone could give me a hand with that, that would be fantastic.

How are you drawing? Are you thinking in terms of a start offset at the left of the screen and a vector with which you step across the tile map per screen pixel to draw a scanline? If so just use the mouse_y to get that vector and start position as you are for drawing, then use mouse_x to add a multiple of the vector to the start position.

Sirocco
Member #88
April 2000
avatar

Quote:

I was prepared to totally fake this because I'm absolutely hopeless with math.

Unfortunately, this is usually a bad idea, as you'll eventually want to interact with what you're drawing in some way, and you'll probably want 'proper' math to do that. Faking your display may preclude easy solutions to baffling problems, like locating sprites, mouse cursors, etc in 'space'.

Amarillion has written a fantastic primer on the subject, which may prove useful.

-->
Graphic file formats used to fascinate me, but now I find them rather satanic.

ngiacomelli
Member #5,114
October 2004

Having read the article I've done a few code replacements.

I noticed this code from the tutorial:

1for (screen_y = 0; screen_y < bmp->h; screen_y++)
2 {
3 // first calculate the distance of the line we are drawing
4 distance = fdiv (fmul (params.space_z, params.scale_y),
5 itofix (screen_y + params.horizon));
6 // then calculate the horizontal scale, or the distance between
7 // space points on this horizontal line
8 horizontal_scale = fdiv (distance, params.scale_x);
9 
10 // calculate the dx and dy of points in space when we step
11 // through all points on this line
12 line_dx = fmul (-fsin(angle), horizontal_scale);
13 line_dy = fmul (fcos(angle), horizontal_scale);
14 
15 // calculate the starting position
16 space_x = cx + fmul (distance, fcos(angle)) - bmp->w/2 * line_dx;
17 space_y = cy + fmul (distance, fsin(angle)) - bmp->w/2 * line_dy;
18 
19 // go through all points in this screen line
20 for (screen_x = 0; screen_x < bmp->w; screen_x++)
21 {

Which is, I can only assume, the code that Thomas was talking about. I'm still a little unclear on the technique that was suggested, though:

        distance = fdiv (fmul (params.space_z, params.scale_y),
            itofix (screen_y + params.horizon));

I take it I'd change the above line to something like:

        distance = fdiv (fmul (params.space_z, params.scale_y),
            itofix (mouse_y + params.horizon));

To calculate the distance of the horizontal segment that the mouse lands on. From there... I'm lost!

Thomas Harte
Member #33
April 2000
avatar

Well, Amarillion seems to have made it more complicated than I would, but let's not complicate things - note the line_dx/y and space_x/y variables. space_x/y is used at every screen pixel to determine what to draw, and line_dx/y are added to it every pixel to figure out where the next one should be.

So, having calculated line_dx/y and space_x/y as a function of mouse_y, just plug in mouse_x:

cursor_x = space_x + mouse_x*line_dx;
cursor_y = space_y + mouse_x*line_dy;

Think of it as a short cut that saves running through the "for (screen_x = 0; screen_x < bmp->w; screen_x++)" loop until screen_x == mouse_x.

Alternatively, as a more expensive but more simple cheat you could just check inside the "for (screen_x = 0; screen_x < bmp->w; screen_x++)" loop that draws your pixels if the pixel you are plotting is on mouse_x, mouse_y and if so store space_x/y as the thing the mouse is over.

ngiacomelli
Member #5,114
October 2004

It seems I'm having trouble understanding Amarillions code:

From your suggestion, I've added this inside of the screen_x for loop:

            if( screen_x == mouse_x && screen_y == mouse_y ) { 
                application.m_x = fixtoi (space_x); 
                application.m_y = fixtoi (space_y); 
            }

Now, at first this wasn't working. I use a linked-list to hold all of the game objects and was simply doing a test where, having pressed mouse_b & 1, a new node would be created with its x and y value set to m_x and m_y. Having looked at the output, the numbers seemed correct so I did some digging around in his render_object code:

1void render_object (BITMAP *bmp, BITMAP *obj, fixed angle, fixed cx, fixed cy, CAMERA_PARAMS camera)
2{
3
4 int width, height;
5 int screen_y, screen_x;
6 
7 fixed obj_x = itofix(160) - cx;
8 fixed obj_y = itofix(100) - cy;
9 
10 fixed space_x = fmul (obj_x, fcos (angle)) + fmul (obj_y, fsin (angle));
11 fixed space_y = -fmul (obj_x, fsin (angle)) + fmul (obj_y, fcos (angle));
12 
13 screen_x = bmp->w/2 + fixtoi (fmul (fdiv (camera.s_x, space_x), space_y));
14 screen_y = fixtoi (fdiv (fmul (camera.z, camera.s_y), space_x)) - camera.horizon;
15 
16 height = fixtoi (obj->h * fdiv(camera.obj_s_y, space_x));
17 width = fixtoi (obj->w * fdiv(camera.obj_s_x, space_x));
18 
19 stretch_sprite (bmp, obj, screen_x - width / 2, screen_y - height, width, height);
20 
21}

I noticed this:

    fixed obj_x = itofix(160) - cx;
    fixed obj_y = itofix(100) - cy;

I don't understand why he has to add those values to the object x and y location before subtracting cx and cy. Here's how I call draw_object:

void render_objects() {
  
     obj_list *tmp, *next;
     for(tmp = head; tmp; tmp = next){
             
           next = tmp->next;
           
           if(tmp->visible)
              render_object (buffer, sprite, camera.angle, camera.x + itofix(tmp->x), camera.y + itofix(tmp->y), camera);

     }

}

Now, if I make the following changes in render_object:

    fixed obj_x = cx;
    fixed obj_y = cy;

The code seems to work so long as I don't move the camera. The minute that I do, everything goes out of sync. Objects are no longer placed where I click the mouse (they're either far away or in a different direction). Also, by removing those values, whenever I shift the camera, all of the sprites move as if attached to the camera.

I can understand that this is because I pass the current camera position, but I don't see how (or why) add 160 to the x value and 100 to the y would solve this?

Thomas Harte
Member #33
April 2000
avatar

I haven't read his tutorial, but presumably he starts with a 320 x 200 screen and something that rotates around the centre of the screen, i.e. position 160,100? Then he probably extrapolates that to produce a 3d floor by doing some scaling in scanline starts and lengths?

If so then 160, 100 is a position relative to the viewer around which the floor moves. So if you rotate left or right, the floor rotates around the offset 160, 100. If you need to, think of it as balancing a piece of paper on your finger and looking at it with your eyes. When you rotate, you rotate the piece of paper on your finger, and 160,100 is the offset from your eyes to your finger.

As a simple fix without diving into Amarillion's code too deeply, if this works:

    fixed obj_x = cx;
    fixed obj_y = cy;

And this doesn't:

    fixed obj_x = itofix(160) - cx;
    fixed obj_y = itofix(100) - cy;

Then why not just adjust your cx, cy values? When you grab the mouse location, do this:

                application.m_x = itofix(160) - fixtoi (space_x); 
                application.m_y = itofix(100) - fixtoi (space_y);

Which has the effect that, until the camera moves, obj_x, obj_y end up with the same value as if you had taken the 160, 100 out of render_object.

ngiacomelli
Member #5,114
October 2004

So simple! Thanks for taking the time out to explain this. Works wonderfully!

Cookies!

Go to: