Posted By: Tails01
SNES Mode-7 - 08/13/12 21:05
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
So this is what it looks like.
ANIMATED VERSION
DOWNLOAD Mode7_Example
Gruß Tommy
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