graphics change
[Lilanci.git] / gr.c
blob47f58600e1c3e1503ec540168df39c543111c2b1
1 #include "config.h"
2 #include "gr.h"
3 #include <string.h>
4 #include <stdlib.h>
5 #include <limits.h>
7 #define TRECT(x,y,w,h, tw, th) glBegin(GL_TRIANGLE_STRIP);glTexCoord2f(0.0,h/th);glVertex2f(x, y+1*h);glTexCoord2f(0.0,0.0);glVertex2f(x, y);glTexCoord2f(w/tw,h/th);glVertex2f(x+1*w, y+1*h);glTexCoord2f(w/tw,0.0);glVertex2f(x+1*w, y);glEnd();
8 #define RECT(x,y,w,h) glBegin(GL_TRIANGLE_STRIP);glVertex2f(x, y+1*h);glVertex2f(x, y);glVertex2f(x+1*w, y+1*h);glVertex2f(x+1*w, y);glEnd();
11 unsigned int occupied=0;
13 typedef struct{
14 GLuint *surfaces;
15 unsigned int allocated;
16 }SurfaceList;
18 SurfaceList gsprite_surfaces;
19 int AddSurface(GLuint s);
20 void RemoveSurface(GLuint s);
21 void RemoveSID(int SID);
22 void FlushSpriteSurfaces();
23 int SetVideoMode(int width, int height);
24 SDL_Surface *gscreen;
26 typedef struct{
27 Sprite **sprites;
28 unsigned int allocated;
29 }SpriteList;
31 SpriteList gsprites;
32 int AddSprite(Sprite *s);
33 void RemoveSprite(Sprite *s);
35 typedef struct{
36 Sprite *sprite;
37 double x;
38 double y;
39 int z; //z
40 }SpriteQueueItem;
42 typedef struct{
43 SpriteQueueItem *items;
44 unsigned int allocated;
45 unsigned int next_id;
46 }SpriteQueue;
47 SpriteQueue gsprite_queue;
48 SpriteQueue gsprite_queue_sorted;
49 void FlushSpriteQueue();
50 void RotateSurface(int SID, int rotation);
52 int ginvalidsprites;
54 GLuint load_svg (char *file, int width, int height, double *pdw, double *pdh);
56 int G2SX(double x);
57 int G2SY(double y);
59 int G2SX(double x){
60 return (x/(double)SCREEN_WIDTH)*GetConfig().screen_width;
62 int G2SY(double y){
63 return (y/(double)SCREEN_HEIGHT)*GetConfig().screen_height;
66 int SetVideoMode(int width, int height){
67 SDL_Rect **modes;
68 int ibest;
69 int bestdif;
70 int i;
71 Uint32 flags;
73 Config config=GetConfig();
75 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
76 flags = SDL_OPENGL ;
77 if(config.fullscreen)
78 flags = flags |SDL_FULLSCREEN;
79 /* Get available fullscreen/hardware modes */
80 modes=SDL_ListModes(NULL, flags);
82 /* Check if there are any modes available */
83 if(modes == (SDL_Rect **)0){
84 printf("No modes available!\n");
85 exit(-1);
88 /* Check if our resolution is restricted */
89 if(modes == (SDL_Rect **)-1){
90 printf("All resolutions available.\n");
91 gscreen = SDL_SetVideoMode(config.screen_width, config.screen_height, 0, flags);
93 else{
94 /* Print valid modes */
95 printf("Available Modes\n");
96 bestdif = abs((modes[0]->w - config.screen_width) + (modes[0]->h - config.screen_height));
97 ibest = 0;
98 for(i=0;modes[i];++i){
99 printf(" %d x %d\n", modes[i]->w, modes[i]->h);
100 if(bestdif > abs((modes[i]->w - config.screen_width) + (modes[i]->h - config.screen_height))){
101 bestdif = abs((modes[i]->w - config.screen_width) + (modes[i]->h - config.screen_height));
102 ibest = i;
105 printf("Using: %d x %d\n", modes[ibest]->w, modes[ibest]->h);
106 gscreen = SDL_SetVideoMode(modes[ibest]->w, modes[ibest]->h, 0, flags);
107 config.screen_height = modes[ibest]->h;
108 config.screen_width = modes[ibest]->w;
109 SetConfig(config);
113 int AddSurface(GLuint s){ //returns -1 on error, SID if success
114 int i;
115 GLuint *tmp;
116 for(i=0;i<gsprite_surfaces.allocated;i++)
117 if( gsprite_surfaces.surfaces[i]==s ){
118 return i;
121 for(i=0;i<gsprite_surfaces.allocated;i++)
122 if( gsprite_surfaces.surfaces[i]==0 ){
123 gsprite_surfaces.surfaces[i]=s;
124 return i;
126 i=gsprite_surfaces.allocated;
127 tmp = (GLuint*) realloc(gsprite_surfaces.surfaces, sizeof(GLuint)*i*2); //if the allocated space is not wide enough, then allocate double space than previous
128 if(tmp==0)
129 return -1;
130 gsprite_surfaces.surfaces=tmp;
131 memset(&gsprite_surfaces.surfaces[i],0, sizeof(GLuint)*i); //zero newly allocated space
132 gsprite_surfaces.allocated=i*2;
133 gsprite_surfaces.surfaces[i]=s;
134 return i;
137 void RemoveSurface(GLuint s){
138 int i,iwannakillyoubitch;
140 iwannakillyoubitch=1;
141 for(i=0;i<gsprite_surfaces.allocated;i++){
142 if( gsprite_surfaces.surfaces[i]==s ){
143 gsprite_surfaces.surfaces[i]=0;
145 if( i>=gsprite_surfaces.allocated/2 && iwannakillyoubitch && gsprite_surfaces.surfaces[i]!=0 )
146 iwannakillyoubitch=0;
149 if(iwannakillyoubitch){
150 gsprite_surfaces.allocated /=2;
151 gsprite_surfaces.surfaces = (GLuint *)realloc(gsprite_surfaces.surfaces, sizeof(GLuint)*gsprite_surfaces.allocated);//TODO:check return value
155 void RemoveSID(int SID){
156 int i,iwannakillyoubitch;
158 iwannakillyoubitch=1;
159 for(i=0;i<gsprite_surfaces.allocated;i++){
160 if( i==SID ){
161 gsprite_surfaces.surfaces[i]=0;
163 if( i>=gsprite_surfaces.allocated/2 && iwannakillyoubitch && gsprite_surfaces.surfaces[i]!=0 )
164 iwannakillyoubitch=0;
167 if(iwannakillyoubitch){
168 gsprite_surfaces.allocated /=2;
169 gsprite_surfaces.surfaces = (GLuint *)realloc(gsprite_surfaces.surfaces, sizeof(GLuint)*gsprite_surfaces.allocated);//TODO:check return value
173 int AddSprite(Sprite *s){//returns 0 on error, Sprite ID on success
174 int i;
175 Sprite **tmp;
176 for(i=0;i<gsprites.allocated;i++)
177 if( gsprites.sprites[i]==s ){
178 return i;
181 for(i=0;i<gsprites.allocated;i++)
182 if( gsprites.sprites[i]==0 ){
183 gsprites.sprites[i]=s;
184 return i;
186 i=gsprites.allocated;
187 tmp=(Sprite **)realloc(gsprites.sprites, sizeof(Sprite *)*i*2); //if the allocated space is not wide enough, then allocate double space than previous
188 if(tmp==0)
189 return 0;
190 gsprites.sprites=tmp;
191 memset(&gsprites.sprites[i],0, sizeof(Sprite *)*i); //zero newly allocated space
192 gsprites.allocated=i*2;
193 gsprites.sprites[i]=s;
194 return i;
197 void RemoveSprite(Sprite *s){
198 int i,iwannakillyoubitch;
200 iwannakillyoubitch=1;
201 for(i=0;i<gsprites.allocated;i++){
202 if( gsprites.sprites[i]==s ){
203 gsprites.sprites[i]=0;
205 if( i>=gsprites.allocated/2 && iwannakillyoubitch && gsprites.sprites[i]!=0 )
206 iwannakillyoubitch=0;
209 if(iwannakillyoubitch){
210 gsprites.allocated /=2;
211 gsprites.sprites = (Sprite **) realloc(gsprites.sprites, sizeof(Sprite *)*gsprites.allocated);//TODO:check return value
215 int GrKill(){
216 Config config;
217 int i;
219 FlushSpriteQueue();
220 FlushSpriteSurfaces();
222 config=GetConfig();
223 free(gsprite_surfaces.surfaces);
224 gsprite_surfaces.surfaces=0;
227 for(i=0; i<gsprites.allocated; i++)
228 if(gsprites.sprites[i]){
229 free(gsprites.sprites[i]->fname);
230 free(gsprites.sprites[i]);
231 gsprites.sprites[i]=0;
234 gsprites.allocated = 0;
237 SetVideoMode(config.screen_width, config.screen_height);
239 return 0;
242 int GrInit(){
243 Config config;
245 config=GetConfig();
246 gsprite_surfaces.surfaces = (GLuint*) calloc(2, sizeof(GLuint));
247 if( gsprite_surfaces.surfaces == 0 )
248 return 1;
250 gsprite_surfaces.allocated = 2;
251 memset(gsprite_surfaces.surfaces, 0, gsprite_surfaces.allocated * sizeof(GLuint));
253 gsprites.sprites = (Sprite**) calloc(2,sizeof(Sprite*));
254 if( gsprites.sprites == 0 )
255 return 2;
256 memset(gsprites.sprites, 0, gsprites.allocated * sizeof(Sprite*));
257 gsprites.allocated = 2;
258 ginvalidsprites=1;
260 gsprite_queue.allocated=2;
261 gsprite_queue.items=(SpriteQueueItem*) malloc(sizeof(SpriteQueueItem)*2);
262 gsprite_queue.next_id=0;
264 SetVideoMode(config.screen_width, config.screen_height);
266 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
267 glClearDepth(1.0);
268 glDisable(GL_DEPTH_TEST);
269 //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
270 glEnable(GL_BLEND);
271 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
272 //glEnable(GL_ALPHA_TEST);
273 //glAlphaFunc(GL_GREATER, 0);
274 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
276 glViewport(0, 0, config.screen_width, config.screen_height);
277 glMatrixMode(GL_PROJECTION);
278 glLoadIdentity();
280 glOrtho(0.0f,config.screen_width, config.screen_height,0.0f,-1.0f,1.0f);
282 glMatrixMode(GL_MODELVIEW);
283 glLoadIdentity();
285 return 0;
288 void FlushSpriteSurfaces(){
289 int i;
291 for(i=0;i<gsprite_surfaces.allocated;i++){
292 if(gsprite_surfaces.surfaces[i]){
293 glDeleteTextures(1, &gsprite_surfaces.surfaces[i]);
294 gsprite_surfaces.surfaces[i]=0;
297 occupied =0;
300 int RegenerateSprites(){
301 int i,SID;
302 Config config;
303 Sprite *tmpsprite;
304 GLuint tmpsurface;
305 int xx,yy;
307 config=GetConfig();
310 FlushSpriteSurfaces();
312 SetVideoMode(config.screen_width, config.screen_height);
315 for(i=0;i<gsprites.allocated;i++)
316 if(gsprites.sprites[i]){
317 tmpsprite=gsprites.sprites[i];
318 xx = (tmpsprite->width/SCREEN_WIDTH)*config.screen_width;
319 yy = (tmpsprite->height/SCREEN_HEIGHT)*config.screen_height;
321 tmpsurface=load_svg(tmpsprite->fname, xx, yy, &tmpsprite->twidth, &tmpsprite->theight);
323 SID=AddSurface(tmpsurface);
324 if(SID==-1){
325 return 1;
327 RotateSurface(SID,tmpsprite->rotation);
331 ginvalidsprites=0;
332 return 0;
334 void InvalidateSprites(){
335 printf("Invalid\n");
336 ginvalidsprites=1;
339 void FlushSpriteQueue(){
340 int i;
342 for(i=0;i<gsprite_queue.allocated;i++){
343 gsprite_queue.items[i].sprite = 0;
345 if(gsprite_queue.next_id < gsprite_queue.allocated/2){
346 //TODO:dopsat zmenseni fronty
348 gsprite_queue.next_id = 0;
351 void SwapSpriteQueueItems(int aid, int bid){
352 SpriteQueueItem c;
353 c.sprite = gsprite_queue_sorted.items[aid].sprite;
354 c.x = gsprite_queue_sorted.items[aid].x;
355 c.y = gsprite_queue_sorted.items[aid].y;
356 c.z = gsprite_queue_sorted.items[aid].z;
358 gsprite_queue_sorted.items[aid].sprite = gsprite_queue_sorted.items[bid].sprite ;
359 gsprite_queue_sorted.items[aid].x = gsprite_queue_sorted.items[bid].x ;
360 gsprite_queue_sorted.items[aid].y = gsprite_queue_sorted.items[bid].y ;
361 gsprite_queue_sorted.items[aid].z = gsprite_queue_sorted.items[bid].z ;
363 gsprite_queue_sorted.items[bid].sprite = c.sprite;
364 gsprite_queue_sorted.items[bid].x = c.x;
365 gsprite_queue_sorted.items[bid].y = c.y;
366 gsprite_queue_sorted.items[bid].z = c.z;
369 void YSortSpriteQueue(int from, int to){
370 int i;
371 int y1, y2;
372 int TheEnd = 0;
373 //TODO:validity check
374 while(!TheEnd){
375 TheEnd = 1;
376 for(i = from; i < to ; i++){
377 y1 = gsprite_queue_sorted.items[i].sprite->height * 0.5 + gsprite_queue_sorted.items[i].y;
378 y2 = gsprite_queue_sorted.items[i+1].sprite->height * 0.5 + gsprite_queue_sorted.items[i+1].y;
379 if(y1 > y2){
380 TheEnd = 0;
381 SwapSpriteQueueItems(i+1, i);
387 void ZSortSpriteQueue(){
388 int minz, lastminz, found;
389 int i;
390 int ysortfrom, ysortto;
391 lastminz = INT_MIN;
394 found = 1;
395 gsprite_queue_sorted.next_id= 0;
396 while(found){
397 found = 0;
398 minz = INT_MAX;
399 //we seek for smallest z bigger than z in last iteration
400 for(i=0; i<gsprite_queue.next_id; i++){
401 if( gsprite_queue.items[i].z < minz && gsprite_queue.items[i].z > lastminz){
402 found = 1;
403 minz = gsprite_queue.items[i].z;
406 if(!found)
407 break;
408 //now we copy all items on z==minz
409 ysortfrom = gsprite_queue_sorted.next_id;
410 for(i=0; i < gsprite_queue.next_id; i++){
411 if(gsprite_queue.items[i].z == minz){
412 memcpy(&gsprite_queue_sorted.items[gsprite_queue_sorted.next_id], &gsprite_queue.items[i], sizeof(SpriteQueueItem));
413 ysortto = gsprite_queue_sorted.next_id;
414 gsprite_queue_sorted.next_id++;
417 YSortSpriteQueue(ysortfrom, ysortto);
418 lastminz=minz;
423 int DrawSprites(){
424 int i;
425 SDL_Rect dest;
426 SDL_Rect src;
427 GLuint ds;
428 Sprite *dspr;
430 if(ginvalidsprites){
431 printf("invalid\n");
432 RegenerateSprites();
435 gsprite_queue_sorted.allocated = gsprite_queue.allocated;
436 gsprite_queue_sorted.next_id = 0;
437 gsprite_queue_sorted.items = malloc(sizeof(SpriteQueueItem)* gsprite_queue_sorted.allocated);
438 ZSortSpriteQueue();
440 for(i=0;i<gsprite_queue_sorted.next_id;i++)
441 if(gsprite_queue_sorted.items[i].sprite){
442 dspr = gsprite_queue_sorted.items[i].sprite;
443 ds = gsprite_surfaces.surfaces[dspr->SID];
444 dest.x = G2SX(gsprite_queue_sorted.items[i].x);
445 dest.y = G2SY(gsprite_queue_sorted.items[i].y);
446 dest.w = G2SX(gsprite_queue_sorted.items[i].sprite->width);
447 dest.h = G2SX(gsprite_queue_sorted.items[i].sprite->height);
448 src.x = 0;
449 src.y = 0;
450 src.w = G2SX(gsprite_queue_sorted.items[i].sprite->width);
451 src.h = G2SX(gsprite_queue_sorted.items[i].sprite->height);
452 glBindTexture(GL_TEXTURE_2D, ds);
453 glEnable(GL_TEXTURE_2D);
454 TRECT(dest.x, dest.y, dest.w, dest.h, dspr->twidth, dspr->theight);
455 glDisable(GL_TEXTURE_2D);
458 gsprite_queue_sorted.allocated = 0;
459 gsprite_queue_sorted.next_id = 0;
460 free(gsprite_queue_sorted.items);
461 gsprite_queue_sorted.items = 0;
462 FlushSpriteQueue();
464 SDL_GL_SwapBuffers();
466 return 0;
469 int ClrScr(){
470 //SDL_FillRect(gscreen, 0, 0x0);
473 Sprite* LoadSpriteSVG(char *fname, double width, double height){
474 Sprite *sprite;
475 GLuint surface;
476 int xx,yy;
477 Config conf=GetConfig();
479 xx = width*(double)conf.screen_width/SCREEN_WIDTH;
480 yy = height*(double)conf.screen_height/SCREEN_HEIGHT;
483 sprite = (Sprite*)malloc(sizeof(Sprite));
484 surface = load_svg(fname, xx, yy, &sprite->twidth, &sprite->theight);
485 sprite->fname = malloc(sizeof(char)*(strlen(fname)+2));
486 strcpy(sprite->fname, fname);
487 sprite->width = width;
488 sprite->height = height;
489 sprite->rotation = 0;
490 sprite->SID = AddSurface(surface);
491 AddSprite(sprite);
493 return sprite;
495 void DestroySprite(Sprite *sprite){
496 int i;
498 RemoveSprite(sprite);
499 free(sprite->fname);
500 sprite->fname=0;
501 glDeleteTextures(1, &gsprite_surfaces.surfaces[sprite->SID]);
502 RemoveSID(sprite->SID);
503 free(sprite);
506 Sprite* CopySprite(Sprite *old){
507 printf("CopySprite not Implemented\n");
509 return 0;
513 void RotateSurface(int SID, int rotation){
514 int i,x,y;
515 int size;
517 fprintf(stderr, "Rotation of sprites not implemented :(\n");
520 void RotateClockwise(Sprite *sprite){
521 RotateSurface(sprite->SID,1);
522 sprite->rotation=1;
525 void QueueDrawSprite(Sprite *sprite, double x, double y, int z){
526 SpriteQueueItem *item;
527 if(!sprite){
528 printf("NULL sprite*\n");
529 return;
531 if(gsprite_queue.next_id >=gsprite_queue.allocated){
532 item = (SpriteQueueItem *) realloc(gsprite_queue.items, sizeof(SpriteQueueItem)*gsprite_queue.allocated*2);
533 if(item == 0){
534 fprintf(stderr, "Can't allocate enough memory for sprite queue\n");
535 return;
537 gsprite_queue.allocated *= 2;
538 gsprite_queue.items = item;
541 item = &gsprite_queue.items[gsprite_queue.next_id];
542 item->sprite = sprite;
543 item->x = x;
544 item->y = y;
545 item->z = z;
547 gsprite_queue.next_id ++;
551 unsigned int closestpoweroftwo(unsigned int i){
552 int p;
553 p=0;
554 while(i){
555 i=i>>1;
556 p++;
558 return 1<<(p-0);
562 //load_svg borrowed from cairo demo
563 #include <stdio.h>
564 #include <stdlib.h>
565 #include <sys/types.h>
566 #include <cairo.h>
567 #include <svg.h>
568 #include <svg-cairo.h>
570 /* load_svg: This is what you're probably interested in!
571 * -----------------------------------------------------
572 * If width and height are greater than 0, the image will
573 * be scaled to that size. wscale and hscale would be ignored.
575 * If width and height are less than 1, wscale and hscale will
576 * resize the width and the height to the specified scales.
578 * If width and height are less than 1, and wscale and hscale
579 * are either 0 or 1, then the image will be loaded at it's
580 * natural size.
582 * See main() for examples.
584 GLuint load_svg (char *file, int width, int height, double *pdw, double *pdh) {
585 svg_cairo_t *scr;
586 int bpp;
587 int btpp;
588 unsigned int rwidth;
589 unsigned int rheight;
590 unsigned int pw,ph; //power of two dimensions
591 double wscale,hscale;
592 Config config=GetConfig();
594 // Create the SVG cairo stuff.
595 svg_cairo_create(&scr);
596 svg_cairo_parse (scr, file);
598 svg_cairo_get_size (scr, &rwidth, &rheight);
599 *pdw = closestpoweroftwo(width);
600 *pdh = closestpoweroftwo(height);
601 width = width>>config.texture_lod;
602 height = height>>config.texture_lod;
604 /*Calculate final width and height of our surface based on the parameters passed */
605 if (width > 0) {
606 wscale=(float)width/(float)rwidth;
607 } else {
608 width=(int)(rwidth*wscale);
610 if (height > 0) {
611 hscale=(float)height/(float)rheight;
612 } else {
613 height=(int)(rheight*hscale);
617 if(hscale>wscale){
618 hscale=wscale;
619 }else{
620 wscale=hscale;
623 /* We will create a CAIRO_FORMAT_ARGB32 surface. We don't need to match
624 the screen SDL format, but we are interested in the alpha bit */
625 bpp=32; /*bits per pixel*/
626 btpp=4; /*bytes per pixel*/
628 /* scanline width */
629 int stride=width * btpp;
631 /* Allocate an image */
632 unsigned char *image=calloc(stride*height, 1);
634 /* Create the cairo surface with the adjusted width and height */
635 cairo_surface_t *cairo_surface;
636 cairo_surface = cairo_image_surface_create_for_data (image,
637 CAIRO_FORMAT_ARGB32,
638 width, height, stride);
640 cairo_t *cr=cairo_create(cairo_surface);
641 cairo_scale (cr, wscale, hscale);
643 /* Render SVG to our surface */
644 svg_cairo_render (scr, cr);
646 /* Cleanup cairo */
647 cairo_surface_destroy (cairo_surface);
648 cairo_destroy (cr);
650 /*Destroy the svg_cairo structure */
651 svg_cairo_destroy (scr);
653 /*Adjust the SDL surface mask to ARGB, matching the cairo surface created.*/
654 Uint32 rmask, gmask, bmask, amask;
656 rmask = 0x00ff0000;
657 gmask = 0x0000ff00;
658 bmask = 0x000000ff;
659 amask = 0xff000000;
661 /* Create the SDL surface using the pixel data stored. It will automatically be set to use alpha using these mask values */
662 SDL_Surface *sdl_surface=SDL_CreateRGBSurfaceFrom( (void *) image, width, height, bpp, stride, rmask, gmask, bmask, amask);
663 SDL_SetAlpha(sdl_surface, 0, 255);
664 rmask = 0x000000ff;
665 gmask = 0x0000ff00;
666 bmask = 0x00ff0000;
667 amask = 0xff000000;
668 pw = closestpoweroftwo(width);
669 ph = closestpoweroftwo(height);
670 SDL_Surface *sdl_rgba_surface=SDL_CreateRGBSurface( SDL_SWSURFACE, pw, ph, bpp, rmask, gmask, bmask, amask);
671 SDL_BlitSurface(sdl_surface, 0, sdl_rgba_surface, 0);
672 SDL_FreeSurface(sdl_surface);
673 cfree(image);
675 SDL_LockSurface(sdl_rgba_surface);
677 GLuint tid;
678 glGenTextures(1, &tid);
679 glBindTexture(GL_TEXTURE_2D, tid);
680 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
681 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture
682 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pw, ph, 0, GL_RGBA, GL_UNSIGNED_BYTE, sdl_rgba_surface->pixels);
683 SDL_UnlockSurface(sdl_rgba_surface);
684 SDL_FreeSurface(sdl_rgba_surface);
685 occupied += pw*ph;
686 printf("Occupied: %u\n", occupied);
688 return tid;