Mode 7, How to do it properly?

BlitzMax Forums/BlitzMax Programming/Mode 7, How to do it properly?

Drakim(Posted 2008) [#1]
Hia!

For my own wicked reasons, I want to create a mode 7 like graphics, including 2d sprites for objects instead of 3d models.

The basic principle of how to make mode 7 like graphics can be seen in this image:


I tried coding it, and did indeed get a mode 7 like effect. However, it came to my notice that the perspective of my graphics was wrong in some aspects. I highly suspect that it's because I use faulty math for the transformation of the image. Allow me to explain:

Pitch is the value that represents at what angle you are viewing the mode 7 image. If you have 0 pitch, you are looking straight down at the image like a map. As you add pitch, this image "tilts" more and more, in a 3d like way. Here are 3 images from my engine with 3 different pitch values:


My pitch system was pretty simple. Pitch is the number that is used to determine how much smaller each graphical line is to be as you go "into" the image (as seen in the first image). My pitch system fails as you try large amounts of pitch. As you can see in the second image, it appears as if you are closer to the ground the more pitch you have. But to be close enough to the ground to give the illusion that you are standing on it (like in F-Zero or Mario Kart), the graphics screws up.

You'll also notice that as you add pitch to the image, a horizon is starting to show up. And it's supposed to be that way, after all, if you think logically about it. However, the horizon in the image is fake. It's simply a forced horizon put there by me, on an estimation based on the current level of pitch. If you pitch a lot, it's noticeable that it doesn't really follow the "3d flow" of the image as it should.

I had to conclude that my idea for how to transform the image was wrong. It just appears right as long as you don't test the engine too much.

After much searching on the net on the proper way to do it, I came up with pretty much nothing. The few mode 7 tutorials I found was either for a specific console (like the GBA), using commands and structure only available to that console, or, tutorials aimed for flash, in flash format, which I am unable to open and read (I don't have any flash creator).

I did find this live example of mode 7 like graphics, instead of a tutorial:
http://blog.nihilogic.dk/2008/05/javascript-super-mario-kart.html

Which was exactly what I needed. I searched though the sourcecode, only to find that indeed, this guy did the math for calculating the transformation of the image completely different. I sent him an email asking him for an explanation (the math part of the code isn't well commented), but have gotten no reply.


So, long post aside, I now turn to you. Does anybody here know what I'm seeking for? I need to know how exactly I'm supposed to scale the image lines, how the horizon is supposed to appear and work, and how to align 2d sprite objects according to the map.

I know I'm asking for a lot, but, if somebody knows, it would be most brilliant if they could share their knowledge.

(I don't have my source right now, but I dunno if I even should be showing it. It's a complete mess, full of errors, and is more likely to just throw you on the wrong track)


ima747(Posted 2008) [#2]
Been a while since I've toyed with mode7, but it's quite fun.

Some basic ideas that might get you going:

Horizon is basicly a looped image, drawn from the top of the "ground" and going up. your offset it along the X axis based on how far you've turned left or right (remember, it's gotta loop, so when you get to the end it re-draws the begining...) The way I would handle that in bmax in essence. I would set the image handle to the bottom left corner of the image, and draw it at the top of the "ground". Since the handle would be at the bottom of the horizon space image then the more you pitch, the lower the edge of the ground will be and the lower the bottom of the horizon will be. Then for turning if the right side of the image is being draw then draw another one next to the first to fill that space and create the loop, and the same on the other side for left. hope that makes sense

The ground is where the complicated things really are. you need to do 2 scales and a rotation.

First you need to turn the image based on the rotation of the view. If again modifying the concept for blitz, if you set the image handle to the position of the view point (i.e. where the player is standing) and draw the image centered at the bottom of the screen, everything "behind" the view point will be off the bottom and thus behind you.

Next is where the cutting the image into horizontal slices comes in. You can scale each slice along it's X and Y axis based on it's distance from the the view point, which is relative to the pitch. Past a certain point things will be scaled to a size too small to be rellevent and can be culled out.

Since splitting and image in blitz is hard, I would start with viewports and drawimagerect instead of actually Slicing the image up. So, you set the view port to the height the slice needs to be based on it's distance, and full width because you want it to fill the screen. Next you need to figure out where the slice starts based on the image rotation and distance of the slice, and set the image handle to that point, DrawImageRect at the bottom of the view port and the rect you're drawing into is the X and Y scaled size of the image for the depth you're at.

As for objects. Generally in mode 7 they're handled as an after thought to save time, since making the background is so time consuming. Scale them vertically based on pitch, or set up an array of angles for pitch. Similarly you need an array for angles based on the rotation of the object relative to the view point. And scale x and y based on distance. This is where your art assest creation time gets eaten. You need x number of images based on how many angles you want (more angles = smoother rotation of objects) usually 36, times however many frames of animation, and then multiplied by any pitch angles (usually not used) so that's why the animation is usually so basic on mode7 games, as well as why things turn in a "chunky" fashion. Assuming you only had 3 frames of animation (in mario kart, guy turning left, right or going straight ahead) and 10 degrees of rotation, no pitch images, and 10 different racers, you have 1080 sprites... plus misc objects like trees etc.

That's a lot to take in and I may have gotten some stuff wrong, but hopefully that's a good starting point.

The method I would probably use for a mode 7 like game would be to fake mode 7 in real 3D. Using MiniB3D as the basis I would create sprite objects for all of my sprites, and another sprite object for the ground. The 3D engine will take care of all the scaling and rotating etc. It would be smoother than real mode 7, MUCH faster, but still give you that billboard/popup look that makes mode 7 charming.


Drakim(Posted 2008) [#3]
Thank you for the reply.

Although you explained it nicely, I already knew most of this. I already have my engine up and running (the second image, which demonstrates 3 levels of pitch), so that aspect, with the image slicing and such, isn't a problem.

The problem is that I don't know what math formulas and such to use when determining various things like scale. For example, how do I determine how big to scale a sprite object compared to the pitch of the map? As you can see in the second image, I tried with the trees. They get bigger as you pitch down, but it's pretty clear that they don't get bigger in the same ratio as the terrain gets bigger.

Right now, my engine runs on guesses and hacks. And I really want the straight math on it, so not only that the proportions become right, but also so I get a better understanding of how it all works.


ima747(Posted 2008) [#4]
Hmmm don't know the exact math, but it's relative to the distance from the view point. It's distance will change based on the pitch. Using A squared + B squared = C squared. A would be the height of the view point, B would be the distance along the ground image from the viewpoint to the sprite's position.

Blitz's square root function is sqr

sqr( (Height * Height) + (2Ddistance + 2Ddistance) ) = 3D distance

To Convert the distance into a scale

(distance/DistanceAtWhichStuffIs1to1scale) = Scale Percent

if you don't want to figure out your pitch based on a height setting then 2D distance may be a close enough fudge for mode 7. Alternatively if all you have is a pitch angle, and not a height you can use right angle triangle calculation to figure out what your height is.

Google turned up

http://www.csgnetwork.com/righttricalc.html

as a starting point for that


Drakim(Posted 2008) [#5]
Thank you! This was the stuff I was looking for. Now I have some actual formulas to follow.

I'll use this thread when I run into other math related problems (which is bound to show up).


Drakim(Posted 2008) [#6]
Okay, so, after some thinking, I realized that this approach you described has some problems.


Here I've drawn how the situation would be. I'd have a height (A), and distance (B) of a camera, and by those values, we can calculate our way to the 3d distance (C) which we use to scale stuff.

But then I started wondering. What if I want to move the camera upwards? Like, for the camera to ascend up into the sky, without changing any angle? If I changed the height (A) value, then the effect would be that the camera changes it's angle downwards, constantly looking down upon the ground.

What my demo does (but not well), is allowing you to move the camera's X,Y and Z position, AND adjust it's pitch.


ima747(Posted 2008) [#7]
if you ascend into the sky (increase A) and B remains the same, then C will increase, giving you the apropriate distance and from the distance you can calculate the scale, the angle makes no difference to the scale the element should be drawn at. HOWEVER if the element is below the camera view it shouldn't be drawn at all... i.e. your height is going to determine how much of the ground directly bellow you is visible, and by extension how FAR an object has to be to be seen.

for example. If you are looking straight ahead you can't see your feet. If you were looking at a 90 degree angle downwards however you'd be looking straight at your feet. the scale your feet would be weather you can see them or not doesn't change, only whether they fall within your field of view based on the angle of your head.

Expanding on that example, if your head were at a 45% downward angle, your feet would be the same size, but still out of your field of vision. If you were to crouch down however then your toes would come into view because their scale relative to your view point would change.

Throughout all of that your feet are in the same place as is your head (along X and Y of the floor plane), and your altitude, and by extension their scale, only change when you crouch down.

Essentially when you move your head you move the View Port (not to be confused with the view point). If everything is drawn relative to the view port then anything falling off the screen is out of your view and isn't drawn (things behind you, things bellow you if you're not looking down far enough, or things in front of you if you're looking down too far). Regardless their scale is just a factor of their distance from the view point to them.

Hope that made sense...


Drakim(Posted 2008) [#8]
Ah, yes, that's true.

Still, I still have some trouble understanding everything here.

For example, if one wanted to see the map completely, with no pitch (like the second picture on this page, square one), then what values would you have to give to A and B?


MGE(Posted 2008) [#9]
Let's see a demo! :)


ima747(Posted 2008) [#10]
If you wanted to see the whole map straight down if you will, like picture 2, first frame.

the view point postion as well as the target position would both be the center of the ground picture. B would be 0, as you would be looking at the same place (x/y speaking) that you are, and A would have to be a height sufficient to scale the whole map to fit on the screen. and then crucially the pitch would have to be 90 degrees forward, to be looking straight down (see the above example where you can look at your own feet by tilting your head)


Drakim(Posted 2008) [#11]
MGE:
I don't have the source here, and I won't for several days I'm afraid. But I'm trying to build a new one based on these specifications.

ima747:
Hmm, buuuut....

Perhaps I'm confused, but isn't B the direct sky length from camera to any object? (and thus not something you can simply set to 0)

Could we try to use more down to earth terms, like camera x and y positions?

And at the end of your post, you start talking about the pitch being 90 degrees. But none of the math or examples we talked about earlier in your solution has any value named "pitch". It's simply A and B, and C which is their result.


ima747(Posted 2008) [#12]
correct, B is the "sky length" if you will, or the 2 dimensional distance from the view point to the object you're trying to focus on. When trying to focus on the ground you're trying to focus on the ground directly bellow the view point, so the distance of B is 0 since the difference in X and Y along the sky/ground plane is 0.

Pitch wasn't mentioned before because we were only talking about scale, which is only a function of distance. The further something is the smaller it is, the closer, the larger. Something's scale is independent of weather or not it is visible... i.e. if something is 10 feet infront of you, it's the same visual size as if it were 10 feet behind you, but if it's behind you it's out of your view port and therefore you can't see it. Pitch affects the view port, not the scale.

When I say pitch I mean the same as your second picture frames 2 and 3, "some pitch" and "more pitch". Pitch, Roll, and Yaw are the rotations of an object, in the same way that an object in 3 dimensions can be described with height, width and depth.

Pitch is the up-down tilt. Like tilting your head back to look up, or forward to look down.

Roll is the horizontal pitch if you will. Like tilting your head to the side so your ears are pointing to the cieling and the floor. Roll is not used in mode 7, but you could simulate it by rotating your final image before displaying it if you really wanted it...

Yaw is the side to side rotation. Like turning your head left or right. You will use that to determine how to rotate images before creating the mode 7 background, and also the yaw of one object relative to another will determine which angle image you need to display for it.

Rotations are around a circle, so 360 degrees to go full circle. So pitching your head forward 90 degrees would be looking straight down, pitching it back 90 would be straight up. Forward 45 degrees would be looking at the ground infront of you. 180 degrees forward or back would have you upside down looking behind yourself. 360 degrees either direction is the same as 0, and has you looking straight ahead.

Lets see if I can re-state the way you get an overhead view (picture 2, "no pitch". Which is actually 90 degrees of pitch...) First your camera X and camera Y are at world 0, the dead center of the ground. Then your pitch is 90 degrees forward so instead of looking at the horizon (which would be 0 degrees) it's looking straight down. the point it's looking at is world 0 as well so that's why B is 0, there's no difference in the 2d plane between where the camera is and where it's looking. The only difference is A, the height of the camera (camera Z if we're sticking with 2d based coordinates) which will determine how far away from the ground the camera is, which since B = 0, then C will be the same as A, because there's no 2d distance making the math all complicated, ( A squared + B squared = C squared still works to figure out C, so you don't need a new equation or anything ) and as we discussed before, scale is relative to distance (C) so you scale the whole of the ground based on that and you're done.
The end result is if you're looking straight down, and you're 2 feet off the ground, you're going to get a close up of some grass, if you're 200 feet off the ground you may see empty space around the ground because it's so far away, depending on your scale factor that is.

If I haven't mentioned before, generally the ground graphic is MUCH bigger than the screen resolution... because you don't want it to be massively pixellated when you're close to it (say just above the ground following a go kart...)


Drakim(Posted 2008) [#13]
Ah, I get it now. Thank you. ^_^

What I think my old code did, was calculating my way to C without A and B, but another hacky method. It looked okay at some values, but if you went far out or close up, it was pretty obvious that the distance formula wasn't correct.


Drakim(Posted 2008) [#14]
Okay, I'm well into the code now, and, I'm starting to wonder, is the formula really:
sqr( (Height * Height) + (2Ddistance + 2Ddistance) ) = 3D distance

Considering that you described the formula more abstractly as:
A squared + B squared = C squared

I don't see why you use Height*Height but only 2Ddistance+2Ddistance. Where do you get the difference in the above simplified formula? Aren't both supposed to be multiplied?

Edit: Also, things aren't going too well. I just can't seem to put this math into use like it's supposed to. Scaling the sprite objects is easy enough, but when it comes to the map itself, I'm lost.

You talked as if you had done something like this before. You don't happen to have the source for that, for me to look at?


Karja(Posted 2008) [#15]
Just a quick comment: yep, it should be 2Ddistance*2Ddistance, as far as I can tell. Pythagorean theorem, and all that.

Good luck getting it all into shape!


ima747(Posted 2008) [#16]
yea, my bad, typo there, 2d * 2d... Karja to the rescue!


ima747(Posted 2008) [#17]
Sadly I have no source as I made it in C about 8 or 9 years ago... and it was awful.

Where are you getting lost on the ground? base scale, pitch, etc?


Drakim(Posted 2008) [#18]
Hm, let's see. How exactly am I to get the 2d distance? Because, as we see in any pitched image (such as the second image, frame 3), the distance varies depending on where on the camera display the object stands on.

If there was a tree just right at the bottom, it would be pretty big. If it was just at the horizon, it should be small.

Yet, in my code right now, I'm not pretty sure I don't take this into account, I simply just use the camera position, which is a simple x,y position.


MGE(Posted 2008) [#19]
How would rotate for turns, etc? Hell.. I'm not doing any math or anything, just rendering segments larger than the next. In my stoopid demo you can use the arrow keys left/right/up/down but I think all I coded was something like the floor in Street Fighter. lol.. Maybe could be used for platform game though. lol.

http://jgoware.com/mge/testm7.exe


ImaginaryHuman(Posted 2008) [#20]
Why would you try to fake a three dimensional view of a plane when you can actually get the hardware to look at a plane three dimensionally with proper perspective for you? You don't need to even think about the math involved or drawing it in segments or strips.

All you need to do is write a bit of your own OpenGL or DX code. It just needs to set up a perspective projection in the projection matrix (instead of the standard Orthographic projection), then rotate the modelview matrix around the x and y axis as needed (ie push it forward 90 degrees in y) and maybe translate it so that you can move around the landscape, and then draw all of your objects as normal, even with normal Max2D drawimage commands.

The hardware will take care of the perspective projection/translation and scaling and rotation like it's supposed to. How to do Mode7 properly is to realize that mode7 came along as a very early form of texturemapping, before you really had proper rotatable matrices and all that hardware jazz. We have WAY moved on since then. Now all you need to do is draw quads on a ground plane.


Rone(Posted 2008) [#21]
All you need is miniB3d...just disable texture filtering for a great oldschool look:





ima747(Posted 2008) [#22]
The point of doing this with emulated mode 7 is just to do it I think. It would be much much easier to just use a real 3d engine but just because it doesn't make sense doesn't mean it can't be done ;0)


If you're talking about the distance of the ground at say 45 degrees of pitch, that's where slicing the ground up comes into play. you need the distance to each slice, and then you scale that slice...

MGE can you post your code? It looks like you're close except it looks like it's scaling the ground rather than moving when you press foward etc. I'm curious :0)

That's all that needs to be done for the distance calculation is scale each slice based on how far away it is.

As for rotation, you need to rotate the image then takes slices from it's rotation. So if you're facing straight forward, or north if you will, across the track, there's no rotation, you just slice it horizontally and you're set. If you want to turn the camera to the right, you actually rotate the ground to the left since the camera can't move, since it's not really a camera, just a point off which you reference other things to do your drawing. Once you turn the ground you then slice the rotated image. Reference the first frame of the first picture, first post. The lines are on an angle. If you open that in a pain program and rotate it so that the lines are horizontal, that is the rotation the ground would be when you took those slices.


Rone's pictures are what you could expect to get from a real 3D engine in practically no time, and it would be hardware accelerated, but I still like the masocism of doin it "old school" :0)


Jesse(Posted 2008) [#23]
here see if this help:

use a base 2 square image


Jesse(Posted 2008) [#24]
self made tiles:

find the ball :)


MGE(Posted 2008) [#25]
Damn impressive! Now, let's see that ball move around a track. :)


Jesse(Posted 2008) [#26]
updated to include a tile map and multiple tile use:

warning it is really slow need a bit of optimization.
you can get the source from my signature link.

[edit]
you can get the tutorial for it in the Pixelate Magazine for Allegro.
download the complete magazine including source code:
http://pixwiki.bafsoft.com/mags/5.zip
it is in c and easy to understand.


Drakim(Posted 2008) [#27]
Thank you everybody. With all this information and examples, I'm pretty confident that I can do it.


Drakim(Posted 2008) [#28]
Hi guys, I have a question about scaling:


sqr( (Height * Height) + (2Ddistance * 2Ddistance) ) = 3D distance
(distance/DistanceAtWhichStuffIs1to1scale) = Scale Percent



let's try with this example. You are 300 in distance from an object, and 100 in distance above it. Stuff scales at 200.

sqr((100*100)+(300*300)) = 316
316/200= 1.58 (Scale Percent)

But now, let's try it again. You are 600 in distance from an object, and 100 in distance above it.

sqr((100*100)+(300*300)) = 608
608/200= 3.04 (Scale Percent)

So, something is obviously wrong. The further you get away from the object, the larger it's scale percent becomes. That's not right.

What am I doing wrong?