// // starfld.c // // Library of routines to create animated starfields. // For 3D displays only with origin in center of screen. // // richard.cutting@virgin.net // http://www.netyaroze-europe.com/~rcutting // // Please feel free to use this code as you want to for your own non // commercial use but please leave my name in the headers if you do. If // you do use it, extend it or change it then please let me know ! // // Richard Cutting 14-May-1998 // // RC 17-May-1998 Sprite option added using Robert Swans sprite library. // // THIS VERSION IS FRIGGED FOR 2D. I NEED TO DO SOME MORE WORK TO MAKE // IT WORK PROPERLY FOR 2D & 3D. #include #include #include "starfld.h" #include "sprite.h" //#include "3d.h" // We'll keep all our stars in here. static StarType *GpStarHead = NULL; // Lots of globals, mainly to avoid doing too many calculations per frame. static int G_ScrollDir; // direction scrolling static int G_ChangeDirTime; // time we'll change direction at static int G_MaxX; // maximum X static int G_MaxY; // maximum Y static int G_HalfX; // G_MaxX / 2 static int G_HalfY; // G_MaxY / 2 static int G_XLimit; // X Limit static int G_YLimit; // Y Limit static int G_Top; // Top of screen static int G_Bottom; // Bottom of screen static int G_Left; // Left hand side of screen static int G_Right; // Right hand side of screen static int G_AnimateFrame; // Frame to animate on static u_short G_ZPos; // Priority in OT static int G_Method; // Method used to create. static u_long GImageBuf[320*256];// Imagebuffer static GsSPRITE G_Sprite; // Sprite static int G_NumStars; // Number of stars // // Free the memory used by the starfield. // void free_starfield( void ) { register StarType *p, *tmp; assert( GpStarHead != ( StarType * ) NULL ); p = GpStarHead; while ( p ) { tmp = p->next; free( p ); p = tmp; } GpStarHead = ( StarType * ) NULL; } // // Initialise a starfield. // void initialise_starfield ( int method, int flag, int num_stars, int num_planes, int scroll_dir, int change_time, int animate_every_n_frame, int ot_length, u_long timaddr ) { register int i, plane; StarType *sp, *p; assert( scroll_dir == STAR_NO_SCROLL || scroll_dir == STAR_SCROLL_LEFT || scroll_dir == STAR_SCROLL_RIGHT || scroll_dir == STAR_SCROLL_UP || scroll_dir == STAR_SCROLL_DOWN ); assert( method == USE_IMAGE || method == USE_BOX || method == USE_SPRITE ); G_ScrollDir = scroll_dir; G_ChangeDirTime = change_time; G_AnimateFrame = animate_every_n_frame; G_ZPos = ( u_short ) pow( ( double ) ot_length, 2 ); G_MaxX = GsDISPENV.disp.w; G_MaxY = GsDISPENV.disp.h; G_Method = method; G_NumStars = num_stars; // If we're being re-called then free up the existing field. if ( GpStarHead != ( StarType * ) NULL ) { free_starfield(); } // As we are using 3D then we need to adjust the x,y co-ordinates of the star // as our origin ( 0, 0 ) is at the centre of the screen. So if we are using // 320 * 240 then our co-ordinates actually run from -160,-120 to 160, 120. Note // that this doesn't take into account of your origin is not the centre of the // screen - you'll have to do that yourself ! // ( Thanks to Robert Swan ! ) G_HalfX = G_MaxX / 2; G_HalfY = G_MaxY / 2; if ( flag && G_Method == USE_BOX || G_Method == USE_SPRITE ) { G_XLimit = G_HalfX; G_YLimit = G_HalfY; G_Left = G_XLimit * -1; G_Right = G_XLimit; G_Top = G_YLimit * -1; G_Bottom = G_YLimit; } else { G_XLimit = G_MaxX; G_YLimit = G_MaxY; G_Left = 0; G_Right = G_MaxX; G_Bottom = G_MaxY; G_Top = 0; } p = GpStarHead; // Initialise each star in turn. for ( i = 0; i < num_stars; i++ ) { sp = ( StarType * ) malloc( sizeof( StarType ) ); assert( sp != ( StarType * ) NULL ); sp->next = ( StarType * ) NULL; if ( flag && G_Method == USE_BOX || G_Method == USE_SPRITE ) { sp->x = ( rand()%G_MaxX ) - ( G_HalfX ); sp->y = ( rand()%G_MaxY ) - ( G_HalfY ); } else { sp->x = rand()%G_MaxX; sp->y = rand()%G_MaxY; } sp->z = 1+(rand()%1000); // We are using more than one plane. if ( num_planes > 1 ) { plane = ( rand()%num_planes ) + 1; sp->speed = ( num_planes - plane ) + 1; sp->rgb.r = sp->rgb.g = sp->rgb.b = 255 / plane; } else // single plane { sp->speed = 1; sp->rgb.r = sp->rgb.g = sp->rgb.b = 128; } // Assign star into the singly linked list. if ( GpStarHead == ( StarType * ) NULL ) { GpStarHead = p = sp; } else { p->next = sp; p = sp; } } // If we are using sprites then we will load the data into the single // sprite that we are going to use. if ( G_Method == USE_SPRITE ) { LoadTIMData( timaddr ); SetSpriteInfo( &G_Sprite, timaddr, 0, 0 ); } } // // Animate the starfield. // void scroll_starfield( void ) { register StarType *s; int absX, absY, speed; static int AnimationTimer = 0; static int ChangeTimer = 0; assert( GpStarHead != ( StarType * ) NULL ); AnimationTimer++; ChangeTimer++; // If we've got nothing to do then get out quickly. if ( G_ScrollDir == STAR_NO_SCROLL || AnimationTimer != G_AnimateFrame ) { return; } // Time to swap scrolling direction ? if ( G_ChangeDirTime == ChangeTimer ) { switch( G_ScrollDir ) { case STAR_SCROLL_RIGHT: G_ScrollDir = STAR_SCROLL_LEFT; break; case STAR_SCROLL_LEFT: G_ScrollDir = STAR_SCROLL_RIGHT; break; case STAR_SCROLL_UP: G_ScrollDir = STAR_SCROLL_DOWN; break; case STAR_SCROLL_DOWN: G_ScrollDir = STAR_SCROLL_UP; break; }; ChangeTimer = 0; } // Move each star in our linked list. s = GpStarHead; while( s ) { // Assign for speed... speed = s->speed; // Move the star by adjusting x,y co-ordinates accordingly if ( G_ScrollDir == STAR_SCROLL_RIGHT ) s->x += speed; else if ( G_ScrollDir == STAR_SCROLL_LEFT ) s->x -= speed; else if ( G_ScrollDir == STAR_SCROLL_UP ) s->y -= speed; else if ( G_ScrollDir == STAR_SCROLL_DOWN ) s->y += speed; // If it has moved off the screen then reposition on the // correct edge. absX = abs( s->x ); absY = abs( s->y ); if ( absY > G_YLimit ) { if ( G_ScrollDir == STAR_SCROLL_UP ) s->y = G_Bottom; else if ( G_ScrollDir == STAR_SCROLL_DOWN ) s->y = G_Top; } if ( absX > G_XLimit ) { if ( G_ScrollDir == STAR_SCROLL_RIGHT ) s->x = G_Left; else if ( G_ScrollDir == STAR_SCROLL_LEFT ) s->x = G_Right; } s = s->next; } AnimationTimer = 0; } // // Create flying through space effect. // void fly_through_space( void ) { register StarType *sp; int absX, absY; assert( GpStarHead != ( StarType * ) NULL ); sp = GpStarHead; while( sp ) { sp->z -= 1; if ( sp->z < 2 ) { sp->x = ( rand()%G_MaxX ) - ( G_HalfX ); sp->y = ( rand()%G_MaxY ) - ( G_HalfY ); sp->z = 1+rand()%1000; } sp->x=160+(sp->x*192)/sp->z; // Projection, K = 256 sp->y=128+(sp->y*192)/sp->z; // -------- " -------- absX = abs( sp->x ); absY = abs( sp->y ); if ( absX > G_XLimit || absY > G_YLimit ) { sp->x = ( rand()%G_MaxX ) - ( G_HalfX ); sp->y = ( rand()%G_MaxY ) - ( G_HalfY ); sp->z = 1+rand()%1000; } sp = sp->next; } } // // Initialise the effect where stars are sucked into the origin. // void init_suck_stars( int suck_to ) { register StarType *s; int x1, y1; assert( GpStarHead != ( StarType * ) NULL ); s = GpStarHead; while( s ) { if ( suck_to == TO_ORIGIN ) { s->dx = s->x; s->dy = s->y; s->orig_x = s->x; s->orig_y = s->y; } else { s->dx = -s->orig_x; s->dy = -s->orig_y; } if ( s->dx >= 0 ) { s->x_inc = -1; } else { s->x_inc = 1; s->dx = -s->dx; } if ( s->dy >= 0 ) { s->y_inc = -1; } else { s->y_inc = 1; s->dy = -s->dy; } s->error = 0; s->index = 0; s = s->next; } } // // This function uses a stepped version of Bresenhams algorithm to suck // all the stars to the origin. // int suck_stars( void ) { register StarType *s; register int dx, dy, x_inc, y_inc, error = 0, i = 0; assert( GpStarHead != ( StarType * ) NULL ); s = GpStarHead; while( s ) { dx = s->dx; dy = s->dy; error = s->error; x_inc = s->x_inc; y_inc = s->y_inc; if ( dx > dy ) { if ( s->index > dx ) { i++; } else { error += dy; if ( error > dx ) { error -= dx; s->y += y_inc; } s->x += x_inc; s->index++; } } else { if ( s->index > dy ) { i++; } else { error += dx; if ( error > 0 ) { error -= dy; s->x += x_inc; } s->y += y_inc; s->index++; } } s->error = error; s = s->next; } if ( i == G_NumStars ) { return( 1 ); } return( 0 ); } // // Load starfield into OT. // void render_starfield( void ) { register StarType *sp; sp = GpStarHead; while( sp ) { sp->box.x = sp->x; sp->box.y = sp->y; sp->box.w = sp->box.h = 1; sp->box.r = sp->rgb.r; sp->box.g = sp->rgb.g; sp->box.b = sp->rgb.b; RenderBox( &( sp->box ), G_ZPos ); sp = sp->next; } } // // If we are using the image type of starfield, generate the image and // load into the frame buffer. // void load_starfield_into_framebuffer( void ) { register StarType *s; u_long pixel; s = GpStarHead; memset( &GImageBuf[0], 0, sizeof( GImageBuf ) ); pixel = 255; while( s ) { GImageBuf[s->x + s->y * 320] = pixel; s = s->next; } LoadImage( &( GsDISPENV.disp ), &GImageBuf[0] ); DrawSync( 0 ); } // // If our starfield is comprised of sprites then load them into the OT. // void render_sprite_starfield( void ) { register StarType *sp; sp = GpStarHead; while( sp ) { G_Sprite.x = sp->x; G_Sprite.y = sp->y; G_Sprite.r = sp->rgb.r; G_Sprite.g = sp->rgb.g; G_Sprite.b = sp->rgb.b; RenderSprite( &G_Sprite, G_ZPos ); sp = sp->next; } } // // Frig to render starfield correctly at game start ! // void starfield_game_start_frig( void ) { register StarType *s; s = GpStarHead; while( s ) { s->orig_x = s->x; s->orig_y = s->y; s->x = 0; s->y = 0; s = s->next; } init_suck_stars( TO_ORIGINAL ); }