SNES MODE 7 examples:

FZERO 1990 (c)Nintendo


MARIO KART 1992 (c)Nintendo



Do you remember these times
Almost no one reminds himself back to the beginning of 3d, where everthing
starts with a stretched plane that simulated a 3d-space mixed with some nice painted sprites.


But if you want to inform yourself about the "making of" you only meet faulty or incomplete code.
So I've decided to publish a version of mine here.

For those who doesn't know what Mode-7 actually is.
MODE 7 WIKIPEDIA^^

I hope the lines are well commented and I hope you guys have some fun with it.
Maybe I hand in the mathematical part later, it's to hard to explain it in engl.
PS. some feedback would be nice and some hints about performance-improvement.
Thanks

Code:
// Pseudo-Mode-7!
/* written in August 2012 by Tommy - "tommy.draeger@googlemail.com" */

#include <acknex.h>
#include <litec.h>

#define xres 320
#define yres 240

BMAP* blank_16;
BMAP* blank_32;
BMAP* sphere 			= "...";
BMAP* displace 		= "...";
BMAP* background 		= "...";

PANEL* framebuffer 	= {	layer = 10;	flags = VISIBLE; }
PANEL* framedepth  	= {	layer = 11;	flags = VISIBLE; }
PANEL* obj_01 			= {	layer = 12; flags = VISIBLE | OVERLAY; bmap = sphere; }

function mode_7_level(BMAP* screen, BMAP* source, BMAP* distfog, BMAP* backdrop);
function mode_7_sprite(PANEL* sprite,int px, int py, float obj_dir, float obj_org);

typedef struct /* MODE 7 SETTINGS */
{
	int foclength;//FOCALLENGTH
	int horizon;
	int scale;//SCALEFACTOR FOR LEVELGROUND
	int obj_scale;//SCALEFACTOR FOR SPRITES
}M7_OPT;


//THESE ARE VALUES THAT WORKS FINE FOR
//ME AT A RESOLUTION OF 320 X 240
M7_OPT* m7 =
{
	foclength = 400;
	horizon = 80;
	scale = 60;
	obj_scale = 15;
}

float angle = 0;
float xOffset = 0;
float yOffset = 0;

//SCREEN AND DISTFOG ARE THE BMAPS
//TO DRAW ON. THE OTHERS SERVES AS
//LEVELGROUND AND SKYBACKDROP
function mode_7_level(BMAP* screen, BMAP* source, BMAP* distfog, BMAP* backdrop)
{
fixed count;
DWORD blend;

int x,y;
//FRAME
int frm;
//JUST FOR PERFORMANCE IMPROVEMENT
int dx,dy;
int mask_x,mask_y;
int mask_x_backdrop;

float space_x, space_y, space_z;
float screen_x, screen_y;
float px, py, pz;
float sx, sy;

mask_x = bmap_width(source) 	-1;
mask_y = bmap_height(source) 	-1;
mask_x_backdrop = bmap_width(backdrop) 	-1;

	while(1)
	{
	//STEERING
	angle 	+= (key_cur - key_cul)*time_step / 15;
	xOffset 	+= (key_cuu - key_cud)*sin(angle)*time_step*8;
	yOffset 	-= (key_cuu - key_cud)*cos(angle)*time_step*8;
	
	bmap_lock(screen,4444);
	bmap_lock(source,888);
	bmap_lock(distfog,4444);
	bmap_lock(backdrop,888);
	
	for(y = -yres/2; y < yres/2; y++)
	{
		dy = y+yres/2;
		
		//"BLEND" HOLDS THE ALPHAVALUE
		//FOR THE DISTANCE-FOGGING
		blend = ((dy)-60) * -100/110 - 10;
		blend = clamp(blend,-100,0);
		//JUST TO CREATE A NON-LINEAR EFFECT
		//505 WAS DETERMINE BY ME 
		blend &= 505;
		
		//MAKE A SLICE AT 70
		//TO RENDER THE BACKDROP
		if(y >= (-yres/2)+70)
		{
		for(x = -xres/2; x < xres/2; x++)
		{
			dx = x+xres/2;
			
			/******* ACTUAL MODE 7 EFFECT ********/
			
			//ASSIGN SOME TEMPVARS FOR
			//A BETTER OVERVIEW
			px = x; 
			py = y + m7.foclength;
			pz = y + m7.horizon;
			
			//MATHEMATICAL PROJECTION 
			//FOR TURNING A 3D POINT TO
			//A 2D SCREENPIXEL
			//Y STANDS FOR Z RESP. THE DEPTH
			space_x = px / pz;
			space_y = py / pz * -1;
			//INVERT THE Y DIRECTION
			//SO THAT EVERTHING IS 
			//IN A CORRECT WAY
			
			//A TRIGONOMETRIC EQUATION TO BE ABLE
			//TO ROTATE THE BMAP SO YOU CAN 
			//LOOK AROUND 360°
			screen_x = space_x * cos(angle) - space_y * sin(angle);
  			screen_y = space_x * sin(angle) + space_y * cos(angle);
			
			//FINAL TRANSFORMATION AND SCALING
			sx = screen_x * m7.scale + xOffset;
			sy = screen_y * m7.scale + yOffset;
			
			//USE THE AND-OPERATOR TO CREATE AN INFINTIY-PATTERN
			pixel_to_bmap(screen,dx,dy,pixel_for_bmap(source,(int)sx & mask_x,(int)sy & mask_y)+0x001200);
			pixel_to_bmap(distfog,dx,dy,pixel_for_vec(vector(240,254,160),blend,8888));
			
			/***************************************/
		}
		}
		else
		{
		for(x = 0; x < xres; x++)
			pixel_to_bmap(screen,x,dy,pixel_for_bmap(backdrop,(int)(x+angle*200) & mask_x_backdrop,dy));	
		}
	}
	
	//EVERYTIME YOU WANT TO ADD AN OBJECT YOU'VE
	//TO PLACE A NEW MODE_7_SPRITE FUNCTION HERE
	//IT'S PLACED HERE BECAUSE OF THE PERFORMANCE
	mode_7_sprite(obj_01, 10 , -100 );
	
	bmap_unlock(backdrop);
	bmap_unlock(distfog);
	bmap_unlock(source);
	bmap_unlock(screen);
	
	//THE NEXT LINES AREN'T NECESSARY 
	//THESE JUST CREATING AN ANIMATED 
	//WATERSURFACE, YOU CAN DELETE
	//THESE IF YOU LIKE
	
	//*******//
	
	count += (int)4*time_step;
	if(count > 10){count %= 10;frm++;}
	if(frm >= 7)frm = 0;	//if(frm == 7)frm %= 7;	//frm &= 7; 
	
	switch(frm)
	{
		case 0:bmap_purge(source);
		case 1:bmap_load(source,"...",0);
		case 2:bmap_load(source,"...",0);
		case 3:bmap_load(source,"...",0);
		case 4:bmap_load(source,"...",0);
		case 5:bmap_load(source,"...",0);
		case 6:bmap_load(source,"...",0);
		case 7:bmap_load(source,"...",0);
	}
	
	//*******//
	
	wait(1);
	}
}


function mode_7_sprite(PANEL* sprite, int px, int py, float obj_dir, float obj_org)
{
	
	float width, height;
	float space_x, space_y;
	float screen_x, screen_y;
	
	float obj_x = px + xOffset; 
	float obj_y = py - yOffset; 

	//EQUATIONS STANDS IN EVERY FORMULARY
	float distance = sqrt(pow(obj_x,2)+pow(obj_y,2));
	
	space_x = obj_x * cos(angle) - obj_y * sin(angle);
  	space_y = obj_x * sin(angle) + obj_y * cos(angle);

	//SPACE_Y IS THE DEPTH
	//IF YOU WANT TO REPRODUCE THESE EQUATION JUST LOOK
	//FOR "MATHEMATICAL PROJECTION" RESP. "SIMILAR TRIANGLES"
	screen_x = xres/2 + (space_x * m7.foclength) / space_y;
	screen_y = (m7.foclength / space_y) + m7.horizon - 10;
	
	//CALCULATE THE NEW HEIGHT:
	//INVERSELY PROPORTIONAL TO THE
	//DISTANCE
	height	= (m7.scale / distance);
	width		= (m7.scale / distance);
	
	sprite.scale_x = width;
	sprite.scale_y = height;

	sprite.pos_x = screen_x - (width*sprite.size_x/2);
	sprite.pos_y = screen_y + (height*sprite.size_y/2);	

	//JUST A SMALL SOLUTION FOR CULLING 
	//SO YOU DOESN'T SEE THE SPRITE TWICE

	//CONVERT VECTOR TO +-180 ANGLE
	vec_to_angle(obj_dir,vector(space_x,space_y,0));
	vec_to_angle(obj_org,vector(px,py,0));
	
	//CONVERT +-180 ANGLE TO 0...360 ANGLE
	obj_dir = cycle(obj_dir,0,360);
	obj_org = cycle(obj_org,0,360);
	
	//DETERMINE WHETER THE OBJECT IS IN EYESIGHT
	if(obj_dir > (obj_org - 90) && obj_dir < (obj_org + 90))	{	sprite.flags |= VISIBLE;	}
	else																	 	{	sprite.flags &= ~VISIBLE;	}
	return;
}

function main()
{
	level_load("");
	wait(2);
	video_set(xres,yres,16,0);
	
	//CREATE TWO BLANK BMAPS FOR THE SCREEN 
	//AND THE DISTANCE FOGGING (BECAUSE OF
	//THE TRANSPERANTY WE WANT TO USE)
	blank_16 = bmap_createblack(xres, yres, 16);
	blank_32 = bmap_createblack(xres, yres, 32);
	
	framebuffer.bmap = blank_16;
	framedepth.bmap = blank_32;
	
	//fps_max = 20;	
	mode_7_level(blank_16, displace, blank_32, background);
}



So this is what it looks like.


ANIMATED VERSION
DOWNLOAD Mode7_Example

Gruß Tommy

Last edited by Tails01; 10/26/12 18:46.

An implemented second-rate idea is better than a good idea, which gathering dust in your brain.