// RAYCASTING ENGINE V0.001 // ANDRE MICHELLE // // -- boolean appletAlive = true; public void stop() { appletAlive = false; } BImage floor; BImage ceil; BImage wall; Raycast View; //-- COUNT FPS BitmapFont dfps; int ms; int frame; void setup() { size( 450 , 325 ); //-- SOME TEXTURES --// wall = loadImage( "wall.gif" ); floor = loadImage( "floor.gif" ); ceil = loadImage( "ceil.gif" ); View = new Raycast( width , height ); //-- FPS DISPLAY --// dfps = new BitmapFont( "letters.gif" , "0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX" , 16 , 22 ); dfps.setText( "000" ); int ms = millis(); for( int i = 0 ; i < 100 ; i++ ) { View.render(); } println( millis() - ms); //update(); frame = 0; //-- IMPULS --// Timer t = new Timer(); t.scheduleAtFixedRate( (Impuls)new Impuls(t) , 0 , 1 ); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class Impuls extends TimerTask { Timer t; Impuls( Timer t ) { this.t = t; }; public void run() { if ( !appletAlive ) t.cancel(); //int ms = millis(); solve(); //while( millis() - ms < 15 ){}; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void solve() { frame++; if ( millis() > ms + 1000 ) { dfps.setText( frame + " fps" ); frame = 0; ms = millis(); } float x = View.x; float y = View.y; if ( Key_RT ) { View.angle -= 0.05; } if ( Key_LT ) { View.angle += 0.05; } if ( Key_UP ) { if ( Key_SH ) { if ( View.roll < View.height ) View.roll += 8; } else if( Key_CT ) { if ( View.z < 60 ) View.z += 4; } else { x += cos( View.angle ) * 8; y += sin( View.angle ) * 8; } } if ( Key_DN ) { if ( Key_SH ) { if ( View.roll > -View.height ) View.roll -= 8; } else if( Key_CT ) { if ( View.z > 4 ) View.z -= 4; } else { x -= cos( View.angle ) * 8; y -= sin( View.angle ) * 8; } } float radius = 24.0; int tx = (int)x >> 6; int ty = (int)y >> 6; float rx = x % 64; float ry = y % 64; if( ry - radius < 0 && map[ ty - 1 ][ tx ] > 0 ) y += radius - ry; if( ry + radius > 64 && map[ ty + 1 ][ tx ] > 0 ) y -= radius + ry - 64; if( rx - radius < 0 && map[ ty ][ tx - 1 ] > 0 ) x += radius - rx; if( rx + radius > 64 && map[ ty ][ tx + 1 ] > 0 ) x -= radius + rx - 64; View.x = x; View.y = y; View.render(); dfps.draw(4,4); update(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Raycast { //-- WORLD COORDINATES --// float x, y, z, angle, roll; //-- PROJECTION PLANE --// int width, height; int map_dim; //-- PRE COMPUTE --// float fov, eyeDistance, scaleFactor, subRayAngle, p_center; Raycast( int width, int height ) { this.width = width; this.height = height; x = 16 * 64 + 32; y = 15 * 64 + 32; z = 40; angle = 0; roll = 0; fov = 60 * PI / 180; init(); } void init() { eyeDistance = ( width / 2 ) / tan( fov / 2 ); subRayAngle = fov / width; map_dim = map.length; } void render() { //-- LOCAL VARIABLES --// int rx = int( x / 64 ); int ry = int( y / 64 ); int tx = rx; int ty = ry; float ax, ay, dx, dy, distance; float offset = 0.0; float nearest; float beta; float ht; float hta; float hta_overOne; float cs; float sn; float a = angle + fov * .5; int sx, sy; //-- LOCAL REFERENCES --// int m[][] = map; //-- PRECOMPUTE --// p_center = height / 2 - roll; float oz = 64 - z; for ( sx = 0 ; sx < width ; sx++ ) { nearest = 999999999; hta = tan( -a ); hta_overOne = 1 / hta; rx = tx; ry = ty; sn = sin( a ); cs = cos( a ); if ( sn < 0 ) { while( true ) { if( ry == 0 ) break; ay = ry-- << 6; ax = x - ( ay - y ) * hta_overOne; if ( ax < 0 ) break; rx = int( ax ) >> 6; if ( rx >= map_dim ) break; if ( m[ ry ][ rx ] > 0 ) { nearest = ( dx = ax - x ) * dx + ( dy = ay - y ) * dy; offset = ax % 64; break; } } } else { while( true ) { if ( ++ry >= map_dim ) break; ax = x - ( ( ay = ry << 6 ) - y ) * hta_overOne; if ( ax < 0 ) break; rx = int( ax ) >> 6; if ( rx >= map_dim ) break; if ( m[ ry ][ rx ] > 0 ) { nearest = ( dx = ax - x ) * dx + ( dy = ay - y ) * dy; offset = 64 - ax % 64; break; } } } rx = tx; ry = ty; if ( cs < 0 ) { while( true ) { if ( rx == 0 ) break; ay = y - ( ( ax = rx-- << 6 ) - x ) * hta; if ( ay < 0 ) break; ry = int( ay ) >> 6; if ( ry >= map_dim ) break; if ( m[ ry ][ rx ] > 0 ) { if ( ( distance = ( dx = ax - x ) * dx + ( dy = ay - y ) * dy ) < nearest ) { nearest = distance; offset = 64 - ay % 64; } break; } } } else { while( true ) { if ( ++rx >= map_dim ) break; ay = y - ( ( ax = rx << 6 ) - x ) * hta; if ( ay < 0 ) break; ry = int( ay ) >> 6; if ( ry >= map_dim ) break; if ( m[ ry ][ rx ] > 0 ) { if ( ( distance = ( dx = ax - x ) * dx + ( dy = ay - y ) * dy ) < nearest ) { nearest = distance; offset = ay % 64; } break; } } } int intoffset = int(offset); beta = cos( a - angle ); float distort = eyeDistance / beta; ht = distort / sqrt( nearest ); float htOverOne = 1 / ht; float cf = oz * distort; float ff = z * distort; int c0 = int( p_center - ht * oz ); int c1 = int( p_center + ht * z ); int pi, iy; for( sy = 0 ; sy < height ; sy++ ) { if( sy < c0 ) { //-- CEILING --// distance = cf / ( p_center - sy ); pi = ( ( (int)( x + cs * distance ) % 64 ) << 6 ) + (int)( y + sn * distance ) % 64; if ( pi < 4096 ) { if ( pi >= 0 ) { pixels[ sy * width + sx ] = ceil.pixels[ pi ]; } } } else if( sy >= c1 ) { //-- FLOOR TILES --// distance = ff / ( sy - p_center ); pi = ( ( (int)( x + cs * distance ) % 64 ) << 6 ) + (int)( y + sn * distance ) % 64; if ( pi < 4096 ) { if ( pi >= 0 ) { pixels[ sy * width + sx ] = floor.pixels[ pi ]; } } } else { //-- BLOCKS --// iy = int( ( sy - c0 ) * htOverOne ); if ( iy >= 0 && iy < 64 && intoffset < 64 && intoffset >= 0 ) { pixels[ sy * width + sx ] = wall.pixels[ ( iy << 6 ) + intoffset ]; } } } //-- DRAW SLICE --// a -= subRayAngle; } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class BitmapFont { BImage fontmap; BImage resultImage; HashMap letters; int letterW; int letterH; int offset = 4; char newline; BitmapFont( String fontName , String content, int letterW, int letterH ) { fontmap = loadImage( fontName ); this.letterW = letterW; this.letterH = letterH; letters = new HashMap(); for( int i = 0 ; i < content.length() ; i++ ) { char key = content.charAt( i ); letters.put( key + "" , new Integer( i ) ); } newline = '*'; } void setText( String txt ) { int tw = txt.length(); int i; int linebreaks = 0; int maxWidth = 0; int lastBreak = 0; for ( i = 0 ; i < tw ; i++ ) { if( txt.charAt( i ) == newline ) { linebreaks++; maxWidth = max( maxWidth, i - lastBreak ); lastBreak = i; } } maxWidth = max( maxWidth, i - lastBreak ); //-- 1 row --// if( maxWidth == 0 ) maxWidth = tw; resultImage = new BImage( --maxWidth * letterW , letterH * ++linebreaks + offset * --linebreaks ); int col = 0; int row = 0; for( i = 0 ; i < tw ; i++ ) { char letter = txt.charAt( i ); if ( letter == newline ) { row++; col = 0; } else { Integer hashobj = (Integer)letters.get( letter + "" ); if ( hashobj != null ) { int position = hashobj.intValue(); int sx = position * letterW; int dx = col * letterW; int dy = row * letterH + row * offset; resultImage.replicate( fontmap, sx, 0 , sx + letterW , letterH , dx , dy , dx + letterW , dy + letterH ); } col++; } } } void draw( int x, int y ) { if ( resultImage != null ) { image( resultImage , x , y ); } }; void draw() { if ( resultImage != null ) { image( resultImage , width / 2 - resultImage.width / 2 , height / 2 - resultImage.height / 2 ); } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int map[][]={ {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0, 0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0, 0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0, 0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1 }, {1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1, 0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1, 0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1, 0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1, 0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1 }, {1,0,0,0,0,0,1,1,0,0,1,1,1,0,0,0, 0,0,0,1,1,1,0,0,1,1,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0, 0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0, 0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// boolean Key_UP = false; boolean Key_DN = false; boolean Key_LT = false; boolean Key_RT = false; boolean Key_CT = false; boolean Key_SH = false; void keyPressed() { if ( key == 37 ) Key_LT = true; if ( key == 38 ) Key_UP = true; if ( key == 39 ) Key_RT = true; if ( key == 40 ) Key_DN = true; if ( key == 16 ) Key_SH = true; if ( key == 17 ) Key_CT = true; } void keyReleased() { if ( key == 37 ) Key_LT = false; if ( key == 38 ) Key_UP = false; if ( key == 39 ) Key_RT = false; if ( key == 40 ) Key_DN = false; if ( key == 16 ) Key_SH = false; if ( key == 17 ) Key_CT = false; }