1 /*
2 * This code is released under the GNU General Public License. See COPYING for
3 * details. Copyright 2003 John Spray: spray_john@users.sourceforge.net
4 */
5
6 //Visual::SDL_GL_LoadTexture(SDL_Surface*, float*) is Copyright Sam Lantinga.
7
8 #include <SDL_opengl.h>
9 #define GL_MIRRORED_REPEAT 0x8370
10 #include <SDL.h>
11 #include <string.h>
12 #include <string>
13 using namespace std;
14 #include <physfs.h>
15 #include <SDL_image.h>
16 #include <math.h>
17 #ifndef M_2PI
18 #define M_2PI 6.28318
19 #endif
20
21 #include "Visual.h"
22 #include "LList.h"
23 #include "Particle.h"
24 #include "Texture.h"
25 #include "Camera.h"
26 #include "Config.h"
27
28
29
30 extern Config GLOB_conf;
31
~Visual()32 Visual::~Visual()
33 {
34 LListItem<Texture>* texitem;
35
36 texitem=texturelist.head;
37 while(texitem){
38 glDeleteTextures(1,(GLuint*)&texitem->data.glid);
39 texitem=texitem->next;
40 }
41
42 cameralist.Flush();
43
44 glFinish();
45
46 if(scorefont){
47 TTF_CloseFont(scorefont);
48 if(verbose)
49 printf("Visual::~Visual: Closing scorefont\n");
50 }
51
52 if(menufont){
53 TTF_CloseFont(menufont);
54 if(verbose)
55 printf("Visual::~Visual: Closing menufont\n");
56 }
57
58 }
59
GetMainCam()60 Camera* Visual::GetMainCam()
61 {
62 return &cameralist.head->data;
63 }
64
Visual(Game * newgame)65 Visual::Visual(Game* newgame)
66 {
67 string fontpath,datadir,fontname;
68
69 fontname="helmetr.ttf";
70 datadir=DATADIR;
71 fontpath=datadir+fontname;
72
73 game=newgame;
74
75 texerrors=0;
76
77 verbose=newgame->verbose;
78
79 score=newgame->score;
80
81 scorefont=NULL;
82 if(verbose)
83 printf("Visual::Visual: opening scorefont\n");
84 scorefont=TTF_OpenFont(fontpath.c_str(), 34);
85 if(!scorefont){
86 printf("Visual::Visual: failed to open scorefont with error %s\n",SDL_GetError());
87 return;
88 }
89
90 menufont=NULL;
91 if(verbose)
92 printf("Visual::Visual: opening menufont\n");
93 menufont=TTF_OpenFont(fontpath.c_str(),40);
94 if(!menufont){
95 printf("Visual::Visual: failed to open menufont with error %s\n",SDL_GetError());
96 return;
97 }
98
99 strcpy(curtex,"NOTHING");
100
101 ResetCams();
102 }
103
ResetCams()104 void Visual::ResetCams()
105 {
106
107 cameralist.Flush();
108
109 Camera* newcam=new Camera();
110 if(!GLOB_conf.twoplayer){
111 newcam->scorefont=scorefont;
112 newcam->visual=this;
113 newcam->viewportwidth=game->screen->w;
114 newcam->viewportheight=game->screen->h;
115 newcam->viewporty=0;
116 newcam->targetplayer=game->player;
117 cameralist.Add(newcam);
118 }
119 else{
120 newcam->scorefont=scorefont;
121 newcam->visual=this;
122 newcam->viewportwidth=game->screen->w;
123 newcam->viewportheight=game->screen->h/2;
124 newcam->viewporty=0;
125 newcam->targetplayer=game->player;
126 cameralist.Add(newcam);
127 newcam->viewporty=game->screen->h/2;
128 newcam->targetplayer=game->player2;
129 cameralist.Add(newcam);
130 }
131 delete newcam;
132
133 }
134
Draw()135 void Visual::Draw()
136 {
137 if(score<game->score){
138 score+=(int)game->dtf;
139 if(score>game->score)
140 score=game->score;
141 }
142
143 glClear(GL_COLOR_BUFFER_BIT);
144
145 LListItem<Camera>* item=cameralist.head;
146 while(item){
147 item->data.Animate();
148 item->data.Draw();
149 item=item->next;
150 }
151
152 SDL_GL_SwapBuffers();
153 }
154
UpdateParticles()155 void Visual::UpdateParticles()
156 {
157 LListItem<Particle>* item;
158
159 item=particlelist.head;
160 while(item){
161 item->data.Animate(game->dtf);
162 if(item->data.life<=0){
163 item=particlelist.Del(item);
164 continue;
165 }
166 if(item->data.collide) if(item->data.Collide(game)){
167 item=particlelist.Del(item);
168 continue;
169 }
170 item=item->next;
171 }
172 }
173
174
175
LoadMesh(char * meshfile)176 int Visual::LoadMesh(char* meshfile)
177 {
178 LListItem<Mesh>* item;
179 Mesh* newmesh;
180
181 item=meshlist.head;
182 while(item){
183 if(!strcmp(meshfile,item->data.filename)){
184 printf("Visual::LoadMesh: mesh %s already loaded!\n",meshfile);
185 return 0;
186 }
187 item=item->next;
188 }
189
190 newmesh=new Mesh();
191 newmesh->visual=this;
192 if(newmesh->Load(meshfile)){
193 printf("Visual::LoadMesh: mesh %s could not be loaded\n",meshfile);
194 return -1;
195 }
196
197 meshlist.Add(newmesh);
198 return 0;
199 //deleting it segfaults!
200 //delete newmesh;
201 }
202
DrawMesh(char * meshfile)203 void Visual::DrawMesh(char* meshfile)
204 {
205 LListItem<Mesh>* item;
206
207 item=meshlist.head;
208 while(item){
209 if(!strcmp(meshfile,item->data.filename)){
210 item->data.Draw();
211 return;
212 }
213 item=item->next;
214 }
215
216 fprintf(stderr,"Visual::DrawMesh: %s is not loaded!\n",meshfile);
217 }
218
LoadTexture(char * filename)219 int Visual::LoadTexture(char* filename)
220 {
221
222 LListItem<Texture>* item;
223
224 item=texturelist.head;
225 while(item){
226 if(!strcmp(filename,item->data.filename)){
227 printf("Visual::LoadTexture: texture %s already loaded!\n",filename);
228 return 0;
229 }
230 item=item->next;
231 }
232
233 Texture newtex;
234 SDL_Surface *TextureImage;
235 SDL_RWops *RW;
236 PHYSFS_file* filehandle;
237 void* filedata;
238 long filesize=0;
239
240
241
242 strcpy(newtex.filename,filename);
243
244 filehandle=PHYSFS_openRead(filename);
245
246 if(!filehandle) {printf("Visual::LoadTexture: PHYSFS_openRead failed on %s with error %s!\n",filename,PHYSFS_getLastError()); return 1;}
247
248 filesize=PHYSFS_fileLength(filehandle);
249
250 filedata=malloc(filesize);
251 if(!filedata) {printf("Visual::LoadTexture: malloc failed! Out of memory?\n"); return 1;}
252 if(PHYSFS_read(filehandle,filedata,1,filesize)!=filesize){
253 printf("Visual::LoadTexture: PHYSFS_read and PHYSFS_fileLength disagree! Aborting.\n");
254 free(filedata);
255 if(!PHYSFS_close(filehandle))
256 printf("ScoreBoard::Load: PHYSFS_close failed with error %s\n",PHYSFS_getLastError());
257 return 1;
258 }
259
260 RW=SDL_RWFromMem(filedata,filesize);
261 if(!RW){
262 printf("Visual::LoadTexture: SDL_RWFromMem failed with error %s\n",SDL_GetError());
263 free(filedata);
264 if(!PHYSFS_close(filehandle))
265 printf("ScoreBoard::Load: PHYSFS_close failed with error %s\n",PHYSFS_getLastError());
266 return 1;
267 }
268
269 TextureImage=IMG_LoadPNG_RW(RW);
270 if(!TextureImage){
271 printf("Visual::LoadTexture: IMG_LoadPNG_RW failed with error %s\n",IMG_GetError());
272 if(!PHYSFS_close(filehandle))
273 printf("ScoreBoard::Load: PHYSFS_close failed with error %s\n",PHYSFS_getLastError());
274 SDL_FreeRW(RW);
275 free(filedata);
276 return 1;
277 }
278
279 free(filedata);
280 if(!PHYSFS_close(filehandle))
281 printf("ScoreBoard::Load: PHYSFS_close failed with error %s\n",PHYSFS_getLastError());
282 SDL_FreeRW(RW);
283
284
285
286 int x;
287 int y;
288 unsigned char* pixels=(unsigned char*)TextureImage->pixels;
289 unsigned char* pixels2=(unsigned char*)malloc(TextureImage->w*TextureImage->h*4);
290 memcpy(pixels2,pixels,TextureImage->w*TextureImage->h*4);
291 SDL_LockSurface(TextureImage);
292 //all incoming surfaces get flipped in y:
293 for(x=0;x<4*(TextureImage->w);x+=4){
294 for(y=0;y<4*(TextureImage->h);y+=4){
295 pixels[x+TextureImage->w*y+0]=pixels2[x+TextureImage->w*(TextureImage->h*4-y-4)+0];
296 pixels[x+TextureImage->w*y+1]=pixels2[x+TextureImage->w*(TextureImage->h*4-y-4)+1];
297 pixels[x+TextureImage->w*y+2]=pixels2[x+TextureImage->w*(TextureImage->h*4-y-4)+2];
298 pixels[x+TextureImage->w*y+3]=pixels2[x+TextureImage->w*(TextureImage->h*4-y-4)+3];
299
300 }
301 }
302 int scalefactor=GLOB_conf.texscale;
303 //and scaled down if scalefactor says so
304 if(scalefactor!=1){
305 memcpy(pixels2,pixels,TextureImage->w*TextureImage->h*4);
306 for(x=0;x<4*(TextureImage->w/scalefactor);x+=4){
307 for(y=0;y<4*(TextureImage->h/scalefactor);y+=4){
308 pixels[x+(TextureImage->w/scalefactor)*y+0]=pixels2[(x*scalefactor)+TextureImage->w*(y*scalefactor)+0];
309 pixels[x+(TextureImage->w/scalefactor)*y+1]=pixels2[(x*scalefactor)+TextureImage->w*(y*scalefactor)+1];
310 pixels[x+(TextureImage->w/scalefactor)*y+2]=pixels2[(x*scalefactor)+TextureImage->w*(y*scalefactor)+2];
311 pixels[x+(TextureImage->w/scalefactor)*y+3]=pixels2[(x*scalefactor)+TextureImage->w*(y*scalefactor)+3];
312 }
313 }
314 }
315 free(pixels2);
316 SDL_UnlockSurface(TextureImage);
317
318
319 glGenTextures( 1, (GLuint*)&newtex.glid );
320
321
322 glBindTexture(GL_TEXTURE_2D,newtex.glid);
323
324 glTexImage2D( GL_TEXTURE_2D, 0, 4, TextureImage->w/scalefactor,
325 TextureImage->h/scalefactor, 0, GL_RGBA,
326 GL_UNSIGNED_BYTE, TextureImage->pixels );
327
328
329 gluBuild2DMipmaps( GL_TEXTURE_2D, 4,TextureImage->w/scalefactor,
330 TextureImage->h/scalefactor, GL_RGBA,
331 GL_UNSIGNED_BYTE, TextureImage->pixels );
332
333 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
334 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
335 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
336 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
337
338 SDL_FreeSurface( TextureImage );
339
340 texturelist.Add(&newtex);
341
342 int errornumber;
343 while((errornumber=glGetError())!=GL_NO_ERROR){
344 printf("Visual::LoadTexture: OpenGL error: %s while loading %s\n",HandleGlError(errornumber),filename);
345 }
346
347 if(verbose)
348 printf("Visual::LoadTexture: Successfully loaded %s into glTexture %d\n",newtex.filename,newtex.glid);
349
350 return 0;
351 }
352
NewParticle(Particle * newpart)353 void Visual::NewParticle(Particle* newpart)
354 {
355 newpart->startlife=newpart->life;
356 newpart->spinoffset=((float)rand()/(float)RAND_MAX)*180;
357 particlelist.Add(newpart);
358 }
359
InitGL()360 int Visual::InitGL()
361 {
362 GLfloat backcolor[4]={0.0f,1.0f,0.0f,0.1f};
363 // GLfloat fogcolor[4]={0.8f,0.8f,0.8f,0.1f};
364
365 if(GLOB_conf.verbose)
366 printf("Visual::InitGL: SDL_Surface w=%d,h=%d,bpp=%d\n",game->screen->w,game->screen->h,game->screen->format->BitsPerPixel);
367
368 glShadeModel( GL_SMOOTH );
369
370 glEnable(GL_TEXTURE_2D);
371
372 glCullFace( GL_BACK );
373 glFrontFace( GL_CCW );
374 glEnable( GL_CULL_FACE );
375 glEnable(GL_DEPTH_TEST);
376 glDepthFunc(GL_LEQUAL);
377
378 glClearColor( backcolor[0],backcolor[1],backcolor[2],backcolor[3] );
379
380 /*glFogi(GL_FOG_MODE, GL_LINEAR);
381 glFogfv(GL_FOG_COLOR, fogcolor);
382 glFogf(GL_FOG_DENSITY, 0.35f);
383 glHint(GL_FOG_HINT, GL_NICEST);
384 glFogf(GL_FOG_START, 50);
385 glFogf(GL_FOG_END, 100);
386 glEnable(GL_FOG);*/
387
388 glEnable(GL_BLEND);
389 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
390
391 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
392
393 return 0;
394 }
395
UseTexture(char * filename)396 int Visual::UseTexture(char* filename)
397 {
398 LListItem<Texture>* item=NULL;
399
400 if(!strcmp(filename,curtex))
401 return 0;
402
403 item=texturelist.head;
404 while(item){
405 if(!strcmp(filename,item->data.filename)){
406 glBindTexture(GL_TEXTURE_2D,item->data.glid);
407 strcpy(curtex,filename);
408 return 0;
409 }
410 item=item->next;
411 }
412
413 #ifndef RELEASE
414
415 if(texerrors>50)
416 return -1;
417 texerrors++;
418
419 printf("Visual::UseTexture: %s not loaded! Loading.\n",filename);
420 if(LoadTexture(filename))
421 return -1;
422 return UseTexture(filename);
423
424 #else
425
426 printf("ERROR: Visual::UseTexture: %s not loaded!\n",filename);
427 return -1;
428
429 #endif //RELEASE
430 }
431
UnLoadTexture(char * filename)432 int Visual::UnLoadTexture(char* filename)
433 {
434 LListItem<Texture>* item;
435
436 item=texturelist.head;
437 while(item){
438 if(!strcmp(filename,item->data.filename)){
439 glDeleteTextures(1,(GLuint*)&item->data.glid);
440 if(!strcmp(curtex,filename))
441 strcpy(curtex,"NOTHING");
442 item=texturelist.Del(item);
443 if(verbose) printf("Unloaded texture %s successfully\n",filename);
444 return 0;
445 }
446 item=item->next;
447 }
448
449 printf("Visual::UnLoadTexture: Can't! %s not loaded!\n",filename);
450 return -1;
451
452 }
453
UnLoadMesh(char * filename)454 int Visual::UnLoadMesh(char* filename)
455 {
456 LListItem<Mesh>* item;
457
458 item=meshlist.head;
459 while(item){
460 if(!strcmp(filename,item->data.filename)){
461 item=meshlist.Del(item);
462 if(verbose) printf("Unloaded mesh %s successfully\n",filename);
463 return 0;
464 }
465 item=item->next;
466 }
467
468 printf("Visual::UnLoadMesh: Can't! %s not loaded!\n",filename);
469 return -1;
470
471 }
472
HandleGlError(int errornumber)473 char* Visual::HandleGlError(int errornumber)
474 {
475 //FIXME: waste of memory. Do this properly.
476 static char errorstring[128];
477
478 switch(errornumber){
479 case GL_INVALID_VALUE:
480 strcpy(errorstring,"GL_INVALID_VALUE");
481 break;
482 case GL_INVALID_ENUM:
483 strcpy(errorstring,"GL_INVALID_ENUM");
484 break;
485 case GL_INVALID_OPERATION:
486 strcpy(errorstring,"GL_INVALID_OPERATION");
487 break;
488 case GL_STACK_OVERFLOW:
489 strcpy(errorstring,"GL_STACK_OVERFLOW");
490 break;
491 case GL_STACK_UNDERFLOW:
492 strcpy(errorstring,"GL_STACK_UNDERFLOW");
493 break;
494 case GL_OUT_OF_MEMORY:
495 strcpy(errorstring,"GL_OUT_OF_MEMORY");
496 break;
497 case GL_TABLE_TOO_LARGE:
498 strcpy(errorstring,"GL_TABLE_TOO_LARGE");
499 break;
500 default:
501 strcpy(errorstring,"Unknown error code");
502 break;
503 }
504 return errorstring;
505 }
506
MakeHash(char * source)507 unsigned int Visual::MakeHash(char* source){
508 unsigned int hash=0;
509 int i=0;
510 while(source[i]!=0){
511 hash^=source[i++];
512 }
513 return hash;
514 }
515
516
power_of_two(int target)517 int power_of_two(int target)
518 {
519 int x=1;
520 while(x<target)
521 x*=2;
522 return x;
523 }
524
525 //This function from SDL_ttf example programs, (c) Sam Lantinga
SDL_GL_LoadTexture(SDL_Surface * surface,float * texcoord)526 int Visual::SDL_GL_LoadTexture(SDL_Surface *surface, float *texcoord)
527 {
528 GLuint texture;
529 int w, h;
530 SDL_Surface *image;
531 SDL_Rect area;
532 Uint32 saved_flags;
533 Uint8 saved_alpha;
534
535 /* Use the surface width and height expanded to powers of 2 */
536 w = power_of_two(surface->w);
537 h = power_of_two(surface->h);
538 texcoord[0] = 0.0f; /* Min X */
539 texcoord[1] = 0.0f; /* Min Y */
540 texcoord[2] = (GLfloat)surface->w / w; /* Max X */
541 texcoord[3] = (GLfloat)surface->h / h; /* Max Y */
542
543 image = SDL_CreateRGBSurface(
544 SDL_SWSURFACE,
545 w, h,
546 32,
547 #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
548 0x000000FF,
549 0x0000FF00,
550 0x00FF0000,
551 0xFF000000
552 #else
553 0xFF000000,
554 0x00FF0000,
555 0x0000FF00,
556 0x000000FF
557 #endif
558 );
559 if ( image == NULL ) {
560 return 0;
561 }
562
563 /* Save the alpha blending attributes */
564 saved_flags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
565 saved_alpha = surface->format->alpha;
566 if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
567 SDL_SetAlpha(surface, 0, 0);
568 }
569
570 /* Copy the surface into the GL texture image */
571 area.x = 0;
572 area.y = 0;
573 area.w = surface->w;
574 area.h = surface->h;
575 SDL_BlitSurface(surface, &area, image, &area);
576
577 /* Restore the alpha blending attributes */
578 if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
579 SDL_SetAlpha(surface, saved_flags, saved_alpha);
580 }
581
582 /* Create an OpenGL texture for the image */
583 glGenTextures(1, &texture);
584 glBindTexture(GL_TEXTURE_2D, texture);
585 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
586 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
587 glTexImage2D(GL_TEXTURE_2D,
588 0,
589 GL_RGBA,
590 w, h,
591 0,
592 GL_RGBA,
593 GL_UNSIGNED_BYTE,
594 image->pixels);
595 SDL_FreeSurface(image); /* No longer needed */
596
597 strcpy(curtex,"NOTHING");
598
599 return texture;
600 }
601
ShowLoading(float howfar)602 void Visual::ShowLoading(float howfar)
603 {
604 glDisable(GL_DEPTH_TEST);
605 glDisable(GL_LIGHTING);
606 glMatrixMode(GL_PROJECTION);
607 glPushMatrix();
608 glLoadIdentity();
609 gluOrtho2D(0,game->screen->w,0,game->screen->h);
610 glMatrixMode(GL_MODELVIEW);
611 glPushMatrix();
612 glLoadIdentity();
613
614 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
615
616 LoadTexture("loading.png");
617 if(UseTexture("loading.png")==-1){
618 printf("Visual::ShowLoading: UseTexture returned -1\n");
619 glPopMatrix();
620 glMatrixMode(GL_PROJECTION);
621 glPopMatrix();
622 return;
623 }
624
625 glColor4f(1.0f,1.0f,1.0f,1.0f);
626 glBegin(GL_QUADS);
627 glTexCoord2f(0.0f,0.0f);
628 glVertex2f(0.0f,0.0f);
629 glTexCoord2f(1.0f,0.0f);
630 glVertex2f(game->screen->w,0.0f);
631 glTexCoord2f(1.0f,1.0f);
632 glVertex2f(game->screen->w,game->screen->h);
633 glTexCoord2f(0.0f,1.0f);
634 glVertex2f(0,game->screen->h);
635 glEnd();
636
637 if(howfar!=-1.0f){
638 glDisable(GL_TEXTURE_2D);
639 glBegin(GL_QUADS);
640 glColor4f(1.0f,0.0f,0.0f,0.7f);
641 glVertex2f(game->screen->w/4,game->screen->h*0.2f);
642 glVertex2f(game->screen->w/4,game->screen->h*0.17f);
643 glColor4f(0.0f,howfar,0.0f,0.7f);
644 glVertex2f(game->screen->w/4+(game->screen->w/2)*howfar,game->screen->h*0.17f);
645 glVertex2f(game->screen->w/4+(game->screen->w/2)*howfar,game->screen->h*0.2f);
646 glEnd();
647 glEnable(GL_TEXTURE_2D);
648 }
649
650 glPopMatrix();
651
652 SDL_GL_SwapBuffers();
653
654 UnLoadTexture("loading.png");
655
656 glMatrixMode(GL_PROJECTION);
657 glPopMatrix();
658 glMatrixMode(GL_MODELVIEW);
659 glEnable(GL_DEPTH_TEST);
660 glEnable(GL_LIGHTING);
661 }
662
663