1: /* File: fbn.c
     2:    Submitted as part of Project 4 for EECS 672, Spring 2007
     3:    by Douglas McClendon, KUID 0536810
     4: */
     5: 
     6: 
     7: /*
     8:  * fbn.c
     9:  *
    10:  * This is a Game Development Toolkit, providing mini-api's for
    11:  * sound, opengl text rendering, joystick, and more...
    12:  *
    13:  */
    14: #include "TexFont.h" 
    15: #include "fbn.h" 
    16: #include <pthread.h> 
    17: 
    18: /*
    19:  * Globals
    20:  */
    21: int fbnSoundSupported = 1;
    22: pthread_mutex_t dsp_mutex;
    23: 
    24: int fbnSoundInit(){
    25:   /* returns 1 if sound is supported, 0 if not */
    26: 
    27:   int format = FBN_SND_FORMAT;
    28:   int stereo = FBN_SND_STEREO;     
    29:   int speed = FBN_SND_SPEED;
    30:   
    31:   if ((audio_fd = open("/dev/dsp", O_WRONLY)) == -1){
    32:     perror("/dev/dsp");
    33:     fbnSoundSupported = 0;
    34:   }
    35: 
    36:   if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1){
    37:     perror("SNDCTL_DSP_SETFMT");
    38:     fbnSoundSupported = 0;
    39:   }
    40: 
    41:   if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1){
    42:     perror("SNDCTL_DSP_STEREO");
    43:     fbnSoundSupported = 0;
    44:   }
    45: 
    46:   if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1){
    47:     perror("SNDCTL_DSP_SPEED");
    48:     fbnSoundSupported = 0;
    49:   }
    50: 
    51:   // create a semaphore/mutex so that only one thread can 
    52:   // write to the dsp at any given time.  In the abscence of
    53:   // any mixer know-how, this will create a buffered but non-
    54:   // blocking scenario
    55:   if(fbnSoundSupported)
    56:     pthread_mutex_init(&dsp_mutex, NULL);
    57: 
    58:   return(fbnSoundSupported);
    59: }
    60: 
    61: void fbnSoundFini(){
    62:   if(fbnSoundSupported){
    63:     pthread_mutex_lock(&dsp_mutex);
    64:     if(close(audio_fd) == -1){
    65:       debug("ignoring fbnSoundStop failure\n");
    66:     }
    67:     pthread_mutex_destroy(&dsp_mutex);
    68:   }
    69: }
    70: 
    71: /*
    72:  * fbnSoundLoadData: Allocate memory and load all sounds for a given soundset
    73:  */
    74: void fbnSoundLoadData(SoundSet *soundset){
    75:   int i;
    76:   FILE *file;
    77:   
    78:   for(i=0;i<soundset->numsounds;i++){
    79:     soundset->sounds[i].data_length = 
    80:       fbnSizeofFile(soundset->sounds[i].sound_file);
    81:     if( soundset->sounds[i].data_length <= 0){
    82:       debug("fbnSoundLoadData problem loading sound %d out of %d\n", i, MAX_SOUNDS);
    83:       exit(1);
    84:     }
    85:     soundset->sounds[i].data = (void *)malloc(soundset->sounds[i].data_length);
    86:     if(soundset->sounds[i].data == NULL){
    87:       debug("fbnSoundLoadData failed to malloc memory\n");
    88:       exit(1);
    89:     }
    90:     file = fopen(soundset->sounds[i].sound_file, "r");
    91:     if(soundset->sounds[i].data == NULL){
    92:       debug("fbnSoundLoadData failed to open sound datafile\n");
    93:       exit(1);
    94:     }
    95:     fread(soundset->sounds[i].data, 1, soundset->sounds[i].data_length, file);
    96:   }
    97: }
    98: 
    99: /*
   100:  * fbnSoundUnloadData: Free memory for a given soundset
   101:  */
   102: void fbnSoundUnloadData(SoundSet *soundset){
   103:   int i;
   104:   
   105:   pthread_mutex_lock(&dsp_mutex);
   106:   for(i=0;i<MAX_SOUNDS;i++){
   107:     free(soundset->sounds[i].data);
   108:     soundset->sounds[i].data = NULL;
   109:   }
   110:   pthread_mutex_unlock(&dsp_mutex);
   111: }
   112: 
   113: void fbnSoundPlay(int sound_id, SoundSet *soundset){
   114:   // fbnSoundPlay:  play a sound effect.  Spawn a new
   115:   //     pthread to go off and play the sound so that
   116:   //     the sound subsystem does not block the main 
   117:   //     game thread.
   118: 
   119:   pthread_t thread;
   120:   int len;
   121:   
   122:   if(!fbnSoundSupported) return;
   123: 
   124:   pthread_create(&thread, NULL,
   125: 		 (void*)&fbnSoundPlay_subthread,
   126: 		 (void*)&(soundset->sounds[sound_id]));
   127: }
   128:  
   129: void fbnSoundPlay_subthread(void *ptr){
   130:   // play a sound
   131:   Sound *snd;
   132:   int len;
   133: 
   134:   snd = (Sound *)ptr;
   135:   
   136:   pthread_mutex_lock(&dsp_mutex);
   137:   len = write(audio_fd, snd->data, snd->data_length);
   138:   pthread_mutex_unlock(&dsp_mutex);
   139:   if(len == -1){
   140:       debug("fbnSoundPlay problem\n");
   141:       exit(1);
   142:   } 
   143: }
   144: 
   145: void fbnSoundWait(void){
   146:   if(fbnSoundSupported){
   147:     pthread_mutex_lock(&dsp_mutex);
   148:     pthread_mutex_unlock(&dsp_mutex);
   149:   }
   150: }
   151: 
   152: int fbnSizeofFile(const char *filename){
   153:   struct stat thing;
   154:   
   155:   if(stat(filename, &thing) == -1){
   156:     debug("bnSizeofFile %s failed\n", filename);
   157:     exit(1);
   158:   } else {
   159:     return(thing.st_size);
   160:   }
   161: }
   162: 
   163: void fbnTxfGLStateSet(void){
   164:   // fbnTxfGLStateSet: do all setup to start issuing calls to texfont routines
   165: 
   166:   // Save/push projection and modelview matrices.  Load a new ortho projection
   167:   // matrix.
   168:   glMatrixMode(GL_PROJECTION);
   169:   glPushMatrix();
   170:   glLoadIdentity();
   171: 
   172:   glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0);
   173: 
   174:   glMatrixMode(GL_MODELVIEW);
   175:   glPushMatrix();
   176:   glLoadIdentity();
   177: 
   178:   /* setup text rendering state */
   179:   glDisable(GL_LIGHTING);
   180:   glEnable(GL_TEXTURE_2D);
   181:   glAlphaFunc(GL_GEQUAL, 0.0625);
   182:   glEnable(GL_ALPHA_TEST);
   183:   glEnable(GL_BLEND);
   184:   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   185:   // this is irrelevent unless I want to texture the text _onto_ a world object
   186:   // rather than just using it as an overlay
   187:   glEnable(GL_POLYGON_OFFSET_FILL);
   188:   glPolygonOffset(0.0, -3);
   189: }
   190: 
   191: void fbnTxfGLStateUnset(void){
   192:   // fbnTxfGLStateUnset: clean up from fbnTxfGLStateSet
   193: 
   194:   /* Return rendering state to normal */
   195:   glDisable(GL_TEXTURE_2D);
   196:   glDisable(GL_ALPHA_TEST);
   197:   glDisable(GL_BLEND);
   198:   glDisable(GL_POLYGON_OFFSET_FILL);
   199:   glEnable(GL_LIGHTING);
   200: 
   201:   /* restore projection and modelview matrices */
   202:   glMatrixMode(GL_PROJECTION);
   203:   glPopMatrix();
   204:   glMatrixMode(GL_MODELVIEW);
   205:   glPopMatrix();
   206: }
   207: 
   208: void fbnTxfRenderString(char *screen_string, char *maxstring,
   209: 			 float x, float y, float w, float h,
   210: 			 float r, float g, float b, float a,
   211: 			 TexFont *our_txf)
   212: {
   213:   // fbnTxfRenderString: draw the given text centered at x,y with
   214:   //     the specified width and height
   215:   int width, ascent, descent;
   216:   float txf_scale_factor;
   217: 
   218:   if(!maxstring) maxstring = screen_string;
   219: 
   220:   txfBindFontTexture(our_txf);
   221:   txfGetStringMetrics(our_txf, maxstring, strlen(maxstring), 
   222: 		      &width, &ascent, &descent);
   223:   glMatrixMode(GL_MODELVIEW);
   224:   glLoadIdentity();
   225: 
   226:   if(getenv("FBN_TXF_DEBUG")){
   227:     // debug, show a black background rectangle
   228:     glDisable(GL_TEXTURE_2D);
   229:     glDisable(GL_DEPTH_TEST);
   230:     glDepthMask(GL_FALSE);
   231:     glBegin(GL_TRIANGLE_STRIP);
   232:       glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
   233:       glVertex3f(x - w / 2.0f, y + h / 2.0f, 0.0f);
   234:       glVertex3f(x + w / 2.0f, y + h / 2.0f, 0.0f);
   235:       glVertex3f(x - w / 2.0f, y - h / 2.0f, 0.0f);
   236:       glVertex3f(x + w / 2.0f, y - h / 2.0f, 0.0f);
   237:     glEnd();
   238:     glDepthMask(GL_TRUE);
   239:     glEnable(GL_DEPTH_TEST);
   240:     glEnable(GL_TEXTURE_2D);
   241:   } // end debug
   242: 
   243:   glTranslatef(x - w / 2.0f, y - h / 2.0f, 0.0f);
   244:   glScalef(w / width, h / ascent, 1.0f);
   245:   glColor4f(r, g, b, a);
   246:   txfRenderString(our_txf, screen_string, strlen(screen_string));
   247: }
   248: 
   249: void fbnTxfRenderStringExtruded(char *string, char *maxstring,
   250: 				float center_x, float center_y,
   251: 				float width, float height, 
   252: 				float extrude_x, float extrude_y,
   253: 				int steps,
   254: 				float fg_r, 
   255: 				float fg_g, 
   256: 				float fg_b, 
   257: 				float fg_a, 
   258: 				float bg_r, 
   259: 				float bg_g, 
   260: 				float bg_b, 
   261: 				float bg_a, 
   262: 				TexFont *txf){
   263:   int i;
   264:   float effect_delta_x;
   265:   float effect_delta_y;
   266:   float effect_delta_r;
   267:   float effect_delta_g;
   268:   float effect_delta_b;
   269:   float effect_delta_a;
   270:   
   271:   effect_delta_x = extrude_x / (float)steps;
   272:   effect_delta_y = extrude_y / (float)steps;
   273:   effect_delta_r = (fg_r - bg_r) / (float)steps;
   274:   effect_delta_g = (fg_g - bg_g) / (float)steps;
   275:   effect_delta_b = (fg_b - bg_b) / (float)steps;
   276:   effect_delta_a = (fg_a - bg_a) / (float)steps;
   277: 
   278:   glDisable(GL_DEPTH_TEST);
   279:   for(i=0; i<steps; i++)
   280:     fbnTxfRenderString(string, maxstring,
   281: 		       center_x + i * effect_delta_x,
   282: 		       center_y + i * effect_delta_y,
   283: 		       width, height,
   284: 		       bg_r + i * effect_delta_r,
   285: 		       bg_g + i * effect_delta_g,
   286: 		       bg_b + i * effect_delta_b,
   287: 		       bg_a + i * effect_delta_a,
   288: 		       txf);
   289:   glEnable(GL_DEPTH_TEST);
   290: }
   291: 
   292: void fbnSetPlaceString(char *placestring, int place){
   293:   // fbnSetPlaceString: given an integer place/rank, return
   294:   //     placestring, i.e. "1st", "2nd", "11th", "101st"
   295: 
   296:   switch(place % 100){
   297:   case 11:
   298:   case 12:
   299:   case 13:
   300:     sprintf(placestring, "%dth", place);
   301:     break;
   302:   default:
   303:     switch(place % 10){
   304:     case 1:
   305:       sprintf(placestring, "%dst", place);
   306:       break;
   307:     case 2:
   308:       sprintf(placestring, "%dnd", place);
   309:       break;
   310:     case 3:
   311:       sprintf(placestring, "%drd", place);
   312:       break;
   313:     default:
   314:       sprintf(placestring, "%dth", place);
   315:       break;
   316:     }
   317:     break;
   318:   }
   319: }
   320: