Original Post

Does anybody have any hints on how to use the Virtual Boy’s affine mode to get a Mode-7 like project as used in Mode 0. I’ve managed to create a background and upload it, and get my affine parameters set up, but I’ve run into a wall.

From what I’ve gathered, affine mode (ignore parallax) does two things. For a given row, it permits you to choose a pixel in the original background image (as loaded in BG memory) which becomes the new starting pixel for the row as drawn on the screen. In other words, affine mode first translates the BG image before the VIP commences drawing. Next, affine mode will draw adjacent pixels for the row by consulting an increment value for skipping pixels (fractional pixels allowed- hence fixed point) in the current row of original background image. This is used to implement a scale.

Translation and scaling happen in this order, and I’ve found it difficult to take a background of parallel vertical lines and apply a perspective transform such that the parallel vertical lines converge to a single point in the distance. The affine transform needs to be varied on a line-by-line basis, and I have not found the correct combination of scale and increment to accomplish this. Has anyone had success with a perspective transform in the past? Well, since F-Zero was PORTED to VB using homebrew, I’d guess so :P.

The following code is the best I’ve been able to do so far, and… it’s not great. The lines are too curved, and the view is not centered (adding the y-axis is going to be fun XD):

#include "external/libgccvb/libgccvb.h"

const char char_line[] = {
	0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, \
	0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03 \
};

int main()
{
    int row_cnt;
    //Initial setup
    vbDisplayOn();
    setmem((void*)BGMap(0), 0x00, 0x2000); /* Just use the zeroth char for all tiles */
    
    for(row_cnt = 0; row_cnt < 224; row_cnt++)
    {
    	    short x_skip, x_offset;
    	    char * copy_base = (char *) (BGMap(1) + (16*row_cnt)); /* Calculate the base once per loop. */
    	    
    	    /* Y-coordinate should increment by row compared to original BG (1-to-1) */
    	    (* (copy_base + 4)) = (row_cnt << 3); /* 13.3 */
    	    (* (copy_base + 5)) = ((row_cnt & 0x7F) >> 3); /* 13.3 */ /* WHY is the top bit a sign bit?! */
    	    
    	    /* X-inc per pixel- 7.9 */ /* However, change the increment value per row! */
    	    x_skip = ((255 - row_cnt) << 2);
    	    x_offset = row_cnt << 3;
    	    (* (short *) (copy_base + 0)) = x_offset;
    	    (* (short *) (copy_base + 6)) = x_skip;
    	    
    	    (* (short *) (copy_base + 8)) = 0; /* Y-inc */
    }
    
    copymem((void*)CharSeg0, (void*)char_line, 16); /* Only need to use 1 char! */

    //Setup worlds
    WA[31].head = (WRLD_ON | WRLD_AFFINE | WRLD_OVR);
    WA[31].gx    = 0;
    WA[31].gp    = 0; //No parallax for now.
    WA[31].gy    = 75; // Use 2/3s of the screen.
    WA[31].w = 384;
    WA[31].h = 149;
    WA[31].ovr = 0;
    WA[31].param = 0x01000; /* Affine Parameters begins after BG 0 (0x1000*2+0x20000) */
    
    WA[30].head = WRLD_END;

    //Set brightness registers
    vbDisplayShow();

    //Main loop (Empty because we're done but don't want to reach the end)
    for(;;);

    return 0;
}


6 Replies

David Tucker has some good information on it here: http://www.goliathindustries.com/vb/VBProg.html . Check out the PDF (“A brief introduction to Affine mode programming”) at the top of the page, and his demos (“Affine Demo’s”) at the bottom.

I did “Mode 7” style on Mario Kart… though I optimized a lot of stuff, so the code is pretty ugly. David Tucker’s demos are clean, and you can directly use his libraries in your program.

DogP

Perspective is a particular brand of non-linear transformation. Consequently, it can’t be done with matrix multiplication alone, so the process will never be only one step.

The process in general is known as projection, and it’s the task of taking a 3D point and “projecting” it onto a 2D plane. In this case, you take world coordinates and map them to screen coordinates.

Graphics cards in modern computers use very low-level projections that can do perspective effects, orthographic/isometric mappings or whatever else the programmer defines. What I did for F-Zero was a one-hit wonder: it literally only does perspective from a plane in 3D to the VB’s 384×224-pixel screen.

I’ll let you research the mathematics involved, but the basic algorithm I used was this:

* Set up a background map with the course graphics. I only used one 64×64-character map, and it updates whatever is farthest from the current position as you move around.
* Treat the BG map as a 3D plane. Since BG coordinates are in X,Y, I opted to imagine the BG as being on the “wall” rather than the “floor”, such that X and Y remained BG coordinates and Z became the “distance” coordinate.
* Create a 3D point somewhat away from the BG to represent the camera’s position.
* Create a rectangle between the camera position and the BG, angled somewhat, to represent the window’s viewport.
* Cast a line from the camera point through the left and right edges of the rectangle (starting and ending at the corners) to determine the X,Y coordinates on the BG map where the reverse projection will fall. You’ll need to do this at various spots along the left and right edges according to how many pixels tall the output will be.
* Use those BG XY coordinates to configure the affine parameters.

I’ll be watching this thread, so if you need more assistance, I’m here to help. (-:

Thank you GuyPerfect, I appreciate the interest.

The goal is to render sprites/fg objects in 3d, while having the VIP render the background using precalculated tables, and cycling tiles/offsets to give the impression of a moving background.

This way, the CPU does not have to worry about doing matrix calculations and rendering the background/ground- the VIP will do it for me, and I can add 3D objects on top of the BG. Space is cheap compared to processing power (even light calculations could be stored with the ground).

This has been a good resource for getting back up to speed- it’s been a while since I had to deal with the gfx pipeline.

After I play around with some images/polygons, and texture mapping in MATLAB, I’ll see if I can’t find the equivalent operations that the VIP should perform on a scanline-by-scanline basis.

cr1901 wrote:
The goal is to render sprites/fg objects in 3d, while having the VIP render the background using precalculated tables, and cycling tiles/offsets to give the impression of a moving background.

To save processing time in my F-Zero project, I pre-calculated all the affine parameters and stored them in ROM. This might sound like a lot, but only the first 90 degrees’ worth needs to be stored, since the other three quadrants just require simple negation or reordering of the first quadrant’s values.

Regarding the parallax setting, don’t go for anything fancy: just use linear interpolation between the foremost and backmost row of pixels. It may not be perspective-correct that way, but since you’ve only got 30 pixels or so before you start to see double images, it’s not like you can make it look realistic anyway.

Something I didn’t realize about F-Zero until about 2 minutes ago- the camera is slightly angled downward. This permits the perspective projection to take up more than 50% of the screen.

Something I forgot until tonight: If my camera is looking straight down the negative z-axis (or just looking directly down any axis, for that matter), and I place two parallel lines within my camera’s point of view, those parallel lines will converge toward the middle of the screen, such as the attached image of a long rectangle with a camera looking down the z-axis. That means, unless I tilt my camera, a mathematically-sound perspective effect can only take up to 50% of the screen (likely less, since 50% is the limiting case).

In F-Zero, the perspective projection (Mode 7 effects), takes up 75% of the screen!

Contrast to Star Fox, from Mode 2 horizon boundary maxes out at looks like 50% of the screen (i.e. the bottom of the camera’s field-of-view IS crashing into the solid ground :P)!

Therefore, my perspective transform as is (horizon is at 66% of the screen, camera looking straight down negative z-axis), is not possible XD. I’ll rework it and see if I can come up with a better one. Tilting my camera downwards slightly was not something I really had in mind (I’d rather the camera strictly follow the backside of my main 3d object, but that might not be a good idea either).

Attachments:

Guy Perfect wrote:

I’ll be watching this thread, so if you need more assistance, I’m here to help. (-:

Source tree is available here. Not much substance right now, but at least I’ve got a project tree that I like. Sadly, there’s a number of “single function headers”- haven’t decided if I’ll keep them or just declare them as extern in the source files which use them. */

Compilation requires SCons, which in turn requires Python (either the Windows, Cygwin, or POSIX version should work fine). Most people I’ve seen on this board have been using a Cygwin version of GCC 2.95 for gccVB, but I’ve been using Runnerpack’s MinGW version of GCC 4.4. Both should work, but if they do not (especially Cygwin’s), let me know. A generic Makefile is not out of the question in the future, especially if I decide to make the source VUCC-compatible as well (which requires rewriting interrupt logic).

The source tree expects to see the Github-hosted version of libgccvb in “#/external/libgccvb”, where “#” is the project root. To get it with git, simply run: git submodule update in “#/external/libgccvb”, or download a zip and extract it to that directory.

 

Write a reply

You must be logged in to reply to this topic.