DEADSOFTWARE

opengl: implement texture deallocator
[flatwaifu.git] / src / gl / render.c
1 #include "glob.h"
2 #include "render.h"
3 #include "files.h"
4 #include "memory.h"
5 #include "misc.h"
6 #include "error.h"
8 #include "menu.h"
9 #include "game.h"
10 #include "dots.h"
11 #include "items.h"
13 #include "sound.h" // snd_vol
14 #include "music.h" // mus_vol
16 #include "fx.h"
17 #include "player.h"
18 #include "monster.h"
19 #include "weapons.h"
20 #include "smoke.h"
21 #include "view.h"
22 #include "switch.h" // sw_secrets
24 #ifdef __APPLE__
25 # include <OpenGL/gl.h>
26 #else
27 # include <GL/gl.h>
28 #endif
29 #include <stdlib.h> // malloc free abs
30 #include <assert.h> // assert
31 #include <SDL.h>
33 #define VGA_TRANSPARENT_COLOR 0
34 #define DEFAULT_SKY_COLOR 0x97
35 #define MANCOLOR 0xD0
36 #define PLAYER_COLOR_OFFSET 7
37 #define MAXAIR 1091
38 #define ANIT 5
39 #define PL_FLASH 90
41 #pragma pack(1)
42 typedef struct vgaimg {
43 word w, h;
44 short x, y;
45 byte data[];
46 } vgaimg;
48 typedef struct rgb {
49 byte r, g, b;
50 } rgb;
52 typedef struct rgba {
53 byte r, g, b, a;
54 } rgba;
55 #pragma pack()
57 typedef struct node {
58 struct cache *base;
59 struct node *left, *right;
60 struct node *up;
61 int l, t, r, b;
62 int leaf;
63 } node;
65 typedef struct cache {
66 struct cache *next;
67 struct node root;
68 GLuint id;
69 } cache;
71 typedef struct image {
72 node *n;
73 GLint x, y;
74 GLuint w, h;
75 int res;
76 } image;
78 /* Render Specific */
79 int SCRW = 320; // public
80 int SCRH = 200; // public
81 static int gamma;
82 static int fullscreen;
83 static SDL_Surface *surf;
84 static rgb playpal[256];
85 static byte bright[256];
86 static GLuint lastTexture;
87 static cache *root;
89 /* Game */
90 static image scrnh[3]; // TITLEPIC INTERPIC ENDPIC
91 static image ltn[2][2];
93 /* Smoke */
94 static image smk_spr[SMSN];
95 static image smk_fspr[FLSN];
97 /* Effects */
98 static image fx_spr[15];
99 static char fx_sprd[15];
101 /* Weapons */
102 static image wp_spr[49*2];
103 static char wp_sprd[49*2];
105 /* Items */
106 static image item_spr[58];
107 static char item_sprd[58];
109 /* Player */
110 static image plr_spr[27*2];
111 static image plr_msk[27*2];
112 static char plr_sprd[27*2];
113 static image plr_wpn[11][6];
115 /* Monsters */
116 static image pl_spr[2];
117 static image pl_msk[2];
118 static image mn_spr[MN_TN][29*2];
119 static image mn_man_msk[29*2];
120 static char mn_sprd[MN_TN][29*2];
121 static image mn_fspr[8];
122 static image mn_sgun[2];
124 /* Misc */
125 static image sth[22];
126 static image bfh[160 - '!'];
127 static image sfh[160 - '!'];
128 static image stone;
129 static image stone2;
130 static image keys[3];
131 static int prx = 0;
132 static int pry = 0;
134 /* Menu */
135 static int gm_tm;
136 static image msklh[2];
137 static image mbarl;
138 static image mbarm;
139 static image mbarr;
140 static image mbaro;
141 static image mslotl;
142 static image mslotm;
143 static image mslotr;
145 /* Map */
146 static const char *anm[ANIT - 1][5] = {
147 {"WALL22_1", "WALL23_1", "WALL23_2", NULL, NULL},
148 {"WALL58_1", "WALL58_2", "WALL58_3", NULL, NULL},
149 {"W73A_1", "W73A_2", NULL, NULL, NULL},
150 {"RP2_1", "RP2_2", "RP2_3", "RP2_4", NULL}
151 };
152 static int max_wall_width;
153 static int max_wall_height;
154 static int max_textures;
155 static image walp[256];
156 static byte walani[256];
157 static image anip[ANIT][5];
158 static byte anic[ANIT];
159 static image horiz;
161 /* Texture cache */
163 // https://blackpawn.com/texts/lightmaps/
164 static node *R_node_alloc (node *p, int w, int h) {
165 assert(p);
166 assert(w > 0);
167 assert(h > 0);
168 if (p->left) {
169 assert(p->right);
170 node *n = R_node_alloc(p->left, w, h);
171 return n ? n : R_node_alloc(p->right, w, h);
172 } else {
173 int pw = p->r - p->l + 1;
174 int ph = p->b - p->t + 1;
175 if (p->leaf || pw < w || ph < h) {
176 return NULL;
177 } else if (pw == w && ph == h) {
178 p->leaf = 1;
179 return p;
180 } else {
181 p->left = malloc(sizeof(node));
182 p->right = malloc(sizeof(node));
183 if (pw - w > ph - h) {
184 *p->left = (node) {
185 .up = p,
186 .l = p->l,
187 .t = p->t,
188 .r = p->l + w - 1,
189 .b = p->b
190 };
191 *p->right = (node) {
192 .up = p,
193 .l = p->l + w,
194 .t = p->t,
195 .r = p->r,
196 .b = p->b
197 };
198 } else {
199 *p->left = (node) {
200 .up = p,
201 .l = p->l,
202 .t = p->t,
203 .r = p->r,
204 .b = p->t + h - 1
205 };
206 *p->right = (node) {
207 .up = p,
208 .l = p->l,
209 .t = p->t + h,
210 .r = p->r,
211 .b = p->b
212 };
214 return R_node_alloc(p->left, w, h);
219 static int R_node_have_leaf (node *n) {
220 return n && (n->leaf || R_node_have_leaf(n->left) || R_node_have_leaf(n->right));
223 static void R_node_free_recursive (node *n) {
224 if (n) {
225 R_node_free_recursive(n->left);
226 R_node_free_recursive(n->right);
227 free(n);
231 static void R_node_free (node *n) {
232 if (n) {
233 //logo("free node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
234 assert(n->leaf);
235 assert(n->left == NULL);
236 assert(n->right == NULL);
237 n->leaf = 0;
238 n->base = NULL;
239 node *p = n->up;
240 while (p != NULL) {
241 assert(p->leaf == 0);
242 assert(p->left);
243 assert(p->right);
244 if (R_node_have_leaf(p) == 0) {
245 R_node_free_recursive(p->left);
246 p->left = NULL;
247 R_node_free_recursive(p->right);
248 p->right = NULL;
249 p = p->up;
250 } else {
251 p = NULL;
257 static void R_gl_bind_texture (GLuint id) {
258 if (id != lastTexture) {
259 glBindTexture(GL_TEXTURE_2D, id);
263 static cache *R_cache_new (void) {
264 GLuint id = 0;
265 GLint size = 0;
266 cache *c = NULL;
267 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
268 size = size < 512 ? size : 512; // more can be buggy on older hardware
269 if (size) {
270 glGenTextures(1, &id);
271 if (id) {
272 R_gl_bind_texture(id);
273 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
274 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
275 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
276 c = malloc(sizeof(cache));
277 if (c != NULL) {
278 *c = (cache) {
279 .id = id,
280 .root.r = size - 1,
281 .root.b = size - 1
282 };
283 } else {
284 glDeleteTextures(1, &id);
288 //logo("new cache %p\n", c);
289 return c;
292 static void R_cache_free (cache *root, int freetexture) {
293 cache *next;
294 cache *c = root;
295 while (c != NULL) {
296 next = c->next;
297 R_node_free_recursive(c->root.left);
298 R_node_free_recursive(c->root.right);
299 if (freetexture && c->id != 0) {
300 glDeleteTextures(1, &c->id);
302 free(c);
303 c = next;
307 static node *R_cache_alloc (cache *root, int w, int h) {
308 assert(root);
309 assert(w > 0 && h > 0);
310 node *n = NULL;
311 cache *p = NULL;
312 cache *c = root;
313 // TODO return null if required size greater than maximum
314 while (c && !n) {
315 n = R_node_alloc(&c->root, w, h);
316 if (n) {
317 assert(n->leaf);
318 n->base = c;
320 p = c;
321 c = c->next;
323 if (!n) {
324 c = R_cache_new();
325 if (c) {
326 p->next = c;
327 n = R_node_alloc(&c->root, w, h);
328 if (n) {
329 assert(n->leaf);
330 n->base = c;
334 if (n) {
335 //logo("new node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
336 } else {
337 logo("new node failed\n");
339 return n;
342 static void R_cache_update (node *n, const void *data, int w, int h) {
343 assert(n);
344 assert(n->leaf);
345 assert(n->base);
346 assert(data);
347 int nw = n->r - n->l + 1;
348 int nh = n->b - n->t + 1;
349 assert(w == nw);
350 assert(h == nh);
351 R_gl_bind_texture(n->base->id);
352 glTexSubImage2D(GL_TEXTURE_2D, 0, n->l, n->t, nw, nh, GL_RGBA, GL_UNSIGNED_BYTE, data);
355 /* Generic helpers */
357 static void R_init_playpal (void) {
358 int i;
359 byte *vgapal = M_lock(F_getresid("PLAYPAL"));
360 for (i = 0; i < 256; i++) {
361 playpal[i] = (rgb) {
362 .r = vgapal[i * 3 + 0] * 255 / 63,
363 .g = vgapal[i * 3 + 1] * 255 / 63,
364 .b = vgapal[i * 3 + 2] * 255 / 63,
365 };
366 bright[i] = ((int)vgapal[i * 3 + 0] + vgapal[i * 3 + 1] + vgapal[i * 3 + 2]) * 8 / (63 * 3);
368 M_unlock(vgapal);
371 static vgaimg *R_getvga (int id) {
372 int loaded = M_was_locked(id);
373 vgaimg *v = M_lock(id);
374 if (v != NULL && !loaded) {
375 v->w = short2host(v->w);
376 v->h = short2host(v->h);
377 v->x = short2host(v->x);
378 v->y = short2host(v->y);
380 return v;
383 static rgba *R_extract_flame_spr (vgaimg *v) {
384 static const byte flametab[16] = {
385 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0xD5,
386 0xD6, 0xD7, 0xA1, 0xA0, 0xE3, 0xE2, 0xE1, 0xE0
387 };
388 int i, j;
389 rgba *s = malloc(v->w * v->h * sizeof(rgba));
390 if (s != NULL) {
391 for (j = 0; j < v->h; j++) {
392 for (i = 0; i < v->w; i++) {
393 int k = j * v->w + i;
394 byte c = v->data[k] + bright[DEFAULT_SKY_COLOR];
395 s[k] = (rgba) {
396 .r = playpal[flametab[c]].r,
397 .g = playpal[flametab[c]].g,
398 .b = playpal[flametab[c]].b,
399 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
400 };
404 return s;
407 static rgba *R_extract_smoke_spr (vgaimg *v) {
408 int i, j;
409 rgba *s = malloc(v->w * v->h * sizeof(rgba));
410 if (s != NULL) {
411 for (j = 0; j < v->h; j++) {
412 for (i = 0; i < v->w; i++) {
413 int k = j * v->w + i;
414 byte c = ((v->data[k] + bright[DEFAULT_SKY_COLOR]) + 0x60) ^ 0x0F;
415 byte a = 0xFF - ((int)playpal[c].r + playpal[c].g + playpal[c].b) / 3;
416 s[k] = (rgba) {
417 .r = playpal[c].r,
418 .g = playpal[c].g,
419 .b = playpal[c].b,
420 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : a,
421 };
425 return s;
428 static rgba *R_extract_mask_spr (vgaimg *v) {
429 int i, j;
430 rgba *s = malloc(v->w * v->h * sizeof(rgba));
431 if (s != NULL) {
432 for (j = 0; j < v->h; j++) {
433 for (i = 0; i < v->w; i++) {
434 int k = j * v->w + i;
435 byte c = v->data[k];
436 if (c >= 0x70 && c <= 0x7F) {
437 byte mask = c - 0x70;
438 mask = 0xFF - ((mask << 4) | mask);
439 s[k] = (rgba) {
440 .r = mask,
441 .g = mask,
442 .b = mask,
443 .a = 0xFF,
444 };
445 } else {
446 s[k] = (rgba) {
447 .r = 0,
448 .g = 0,
449 .b = 0,
450 .a = 0,
451 };
456 return s;
459 static rgba *R_extract_rgba_spr (vgaimg *v) {
460 int i, j;
461 rgba *s = malloc(v->w * v->h * sizeof(rgba));
462 if (s != NULL) {
463 for (j = 0; j < v->h; j++) {
464 for (i = 0; i < v->w; i++) {
465 int k = j * v->w + i;
466 byte c = v->data[k];
467 s[k] = (rgba) {
468 .r = playpal[c].r,
469 .g = playpal[c].g,
470 .b = playpal[c].b,
471 .a = c == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
472 };
476 return s;
479 /* OpenGL helpers */
481 static image R_gl_create_image (const rgba *buf, int w, int h) {
482 node *n = R_cache_alloc(root, w, h);
483 if (n) {
484 R_cache_update(n, buf, w, h);
486 return (image) {
487 .n = n,
488 .w = w,
489 .h = h,
490 .res = -1
491 };
494 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
495 image img;
496 vgaimg *v = R_getvga(id);
497 if (v != NULL) {
498 rgba *buf = (*fn)(v);
499 img = R_gl_create_image(buf, v->w, v->h);
500 img.x = v->x;
501 img.y = v->y;
502 img.res = id;
503 M_unlock(v);
504 free(buf);
505 } else {
506 img = (image) {
507 .res = id
508 };
510 return img;
513 static image R_gl_getimage (int id) {
514 return R_gl_get_special_image(id, &R_extract_rgba_spr);
517 static image R_gl_loadimage (const char name[8]) {
518 return R_gl_getimage(F_getresid(name));
521 static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vgaimg*)) {
522 return R_gl_get_special_image(F_getsprid(n, s, d), fn);
525 static void R_gl_free_image (image *img) {
526 if (img->n != NULL && img->res >= 0) {
527 R_node_free(img->n);
529 img->n = NULL;
530 img->res = -1;
533 static void R_gl_draw_quad (int x, int y, int w, int h) {
534 glBegin(GL_QUADS);
535 glVertex2i(x + w, y);
536 glVertex2i(x, y);
537 glVertex2i(x, y + h);
538 glVertex2i(x + w, y + h);
539 glEnd();
542 static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip) {
543 if (img->n) {
544 GLfloat nw = img->n->base->root.r + 1;
545 GLfloat nh = img->n->base->root.b + 1;
546 GLfloat ax = (flip ? img->n->l : img->n->r + 1) / nw;
547 GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
548 GLfloat ay = (img->n->t) / nw;
549 GLfloat by = (img->n->b + 1) / nh;
550 R_gl_bind_texture(img->n->base->id);
551 glEnable(GL_TEXTURE_2D);
552 glBegin(GL_QUADS);
553 glTexCoord2f(ax, ay); glVertex2i(x + w, y);
554 glTexCoord2f(bx, ay); glVertex2i(x, y);
555 glTexCoord2f(bx, by); glVertex2i(x, y + h);
556 glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
557 glEnd();
558 } else {
559 glColor3ub(255, 0, 0);
560 glDisable(GL_BLEND);
561 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
562 glDisable(GL_TEXTURE_2D);
563 R_gl_draw_quad(x, y, w, h);
567 /* fit image into rectangle without applying offset and transparency */
568 static void R_gl_draw_image_ext (image *img, int x, int y, int w, int h) {
569 glDisable(GL_BLEND);
570 glColor3ub(255, 255, 255);
571 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
572 R_gl_draw_textured(img, x, y, w, h, 0);
575 /* draw sprite with offset and coloring */
576 static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
577 int xx = flip ? x - img->w + img->x : x - img->x;
578 glEnable(GL_BLEND);
579 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
580 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
583 /* draw sprite with offset */
584 static void R_gl_draw_image (image *img, int x, int y, int flip) {
585 glColor3ub(255, 255, 255);
586 R_gl_draw_image_color(img, x, y, flip);
589 static void R_gl_set_color (byte c) {
590 glColor3ub(playpal[c].r, playpal[c].g, playpal[c].b);
593 static void R_gl_setclip (int x, int y, int w, int h) {
594 glScissor(x, SCRH - h - y, w, h);
597 static void R_gl_setmatrix (void) {
598 glScissor(0, 0, SCRW, SCRH);
599 glViewport(0, 0, SCRW, SCRH);
600 glMatrixMode(GL_PROJECTION);
601 glLoadIdentity();
602 glOrtho(0, SCRW, SCRH, 0, 0, 1);
603 glMatrixMode(GL_MODELVIEW);
604 glLoadIdentity();
607 /* --- Misc --- */
609 static image Z_getspr (const char n[4], int s, int d, char *dir) {
610 int h = F_getsprid(n, s, d);
611 if (dir != NULL) {
612 *dir = (h & 0x8000) ? 1 : 0;
614 return R_gl_getimage(h);
617 static void Z_putch_generic (image img[], int off, int ch) {
618 image *p = NULL;
619 if (ch > 32 && ch < 160) {
620 p = &img[ch - '!'];
622 if (p != NULL) {
623 R_gl_draw_image(p, prx, pry, 0);
624 prx += p->w - 1;
625 } else {
626 prx += off;
630 static void Z_printf_generic (image img[], int off, const char *fmt, va_list ap) {
631 int i;
632 char buf[80];
633 vsprintf(buf, fmt, ap);
634 for (i = 0; buf[i]; ++i) {
635 switch (buf[i]) {
636 case '\n':
637 pry += off + 1;
638 case '\r':
639 prx = 0;
640 break;
641 default:
642 Z_putch_generic(img, off, (byte)buf[i]);
647 static void Z_gotoxy (int x, int y) {
648 prx = x;
649 pry = y;
652 static void Z_printbf (const char *fmt, ...) {
653 va_list a;
654 va_start(a, fmt);
655 Z_printf_generic(bfh, 12, fmt, a);
656 va_end(a);
659 static void Z_printsf (const char *fmt, ...) {
660 va_list a;
661 va_start(a, fmt);
662 Z_printf_generic(sfh, 7, fmt, a);
663 va_end(a);
666 static void Z_printhf (const char *fmt, ...) {
667 int i, c;
668 char buf[80];
669 va_list a;
670 va_start(a, fmt);
671 vsprintf(buf, fmt, a);
672 va_end(a);
673 for (i = 0; buf[i]; ++i) {
674 switch (buf[i]) {
675 case '0':
676 case '1':
677 case '2':
678 case '3':
679 case '4':
680 case '5':
681 case '6':
682 case '7':
683 case '8':
684 case '9':
685 c = buf[i] - '0';
686 break;
687 case '-':
688 c = 10;
689 break;
690 case '%':
691 c = 11;
692 break;
693 case '\n':
694 pry += 19;
695 case '\r':
696 c = -1;
697 prx = 0;
698 break;
699 default:
700 c = -1;
701 break;
703 if (c >= 0) {
704 R_gl_draw_image(&sth[c], prx, pry, 0);
706 prx += 14;
710 /* --- Menu --- */
712 static image *PL_getspr (int s, int d, int msk) {
713 int i = (s - 'A') * 2 + d;
714 return msk ? &plr_msk[i] : &plr_spr[i];
717 static void GM_draw (void) {
718 enum {MENU, MSG}; // copypasted from menu.c!
719 enum {
720 CANCEL, NEWGAME, LOADGAME, SAVEGAME, OPTIONS, QUITGAME, QUIT, ENDGAME, ENDGM,
721 PLR1, PLR2, COOP, DM, VOLUME, GAMMA, LOAD, SAVE, PLCOLOR, PLCEND, MUSIC, INTERP,
722 SVOLM, SVOLP, MVOLM, MVOLP, GAMMAM, GAMMAP, PL1CM, PL1CP, PL2CM, PL2CP
723 }; // copypasted from menu.c!
724 int i, j, k, x, y, cx, cy;
725 image *img;
726 gm_tm += 1;
727 if (mnu != NULL) {
728 cx = SCRW / 2;
729 cy = SCRH / 2;
730 if (mnu->type == MENU) {
731 y = cy - (mnu->n * 16 - 20) / 2;
732 Z_gotoxy(cx - mnu->x, y - 10); Z_printbf("%s", mnu->ttl);
733 for (i = 0; i < mnu->n; i++) {
734 if (mnu->t[i] == LOAD || mnu->t[i] == SAVE) {
735 j = y + i * 16 + 29;
736 R_gl_draw_image(&mslotl, cx - mnu->x, j, 0);
737 for (k = 8; k < 184; k += 8) {
738 R_gl_draw_image(&mslotm, cx - mnu->x + k, j, 0);
740 R_gl_draw_image(&mslotr, cx - mnu->x + 184, j, 0);
741 Z_gotoxy(cx - mnu->x + 4, j - 8);
742 if (input && i == save_mnu.cur) {
743 Z_printsf("%s_", ibuf);
744 } else {
745 Z_printsf("%s", savname[i]);
747 } else {
748 x = mnu->t[i] >= SVOLM ? (mnu->t[i] >= PL1CM ? 50 : 152) : 0;
749 Z_gotoxy(cx - mnu->x + x, y + i * 16 + 20);
750 Z_printbf("%s", mnu->m[i]);
751 switch (mnu->t[i]) {
752 case MUSIC:
753 Z_printbf(" '%s'", g_music);
754 break;
755 case INTERP:
756 Z_printbf("%s", fullscreen ? "ON" : "OFF");
757 break;
758 case PL1CM:
759 case PL1CP:
760 case PL2CM:
761 case PL2CP:
762 img = PL_getspr(*panimp, 0, 0);
763 R_gl_draw_image(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
764 img = PL_getspr(*panimp, 0, 1);
765 R_gl_set_color(pcolortab[(mnu->t[i] == PL1CM) ? p1color : p2color] + PLAYER_COLOR_OFFSET);
766 R_gl_draw_image_color(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
767 break;
768 case SVOLM:
769 case SVOLP:
770 case MVOLM:
771 case MVOLP:
772 case GAMMAM:
773 case GAMMAP:
774 j = y + i * 16 + 20;
775 R_gl_draw_image(&mbarl, cx - mnu->x, j, 0);
776 for (k = 8; k < 144; k += 8) {
777 R_gl_draw_image(&mbarm, cx - mnu->x + k, j, 0);
779 R_gl_draw_image(&mbarr, cx - mnu->x + 144, j, 0);
780 switch (mnu->t[i]) {
781 case SVOLM:
782 k = snd_vol;
783 break;
784 case MVOLM:
785 k = mus_vol;
786 break;
787 case GAMMAM:
788 k = gamma << 5;
789 break;
790 default:
791 k = 0;
792 break;
794 R_gl_draw_image(&mbaro, cx - mnu->x + 8 + k, j, 0);
795 break;
799 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], cx - mnu->x - 25, y + mnu->cur * 16 + 20 - 8, 0);
800 } else if (mnu->type == MSG) {
801 Z_gotoxy(cx - strlen(mnu->ttl) * 7 / 2, cy - 10); Z_printsf(mnu->ttl);
802 Z_gotoxy(cx - 24, SCRH / 2); Z_printsf("(Y/N)");
803 } else {
804 ERR_fatal("Unknown menu type %i\n", mnu->type);
809 /* --- View --- */
811 static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int fg) {
812 int i, j;
813 assert(minx >= 0 && minx <= FLDW);
814 assert(miny >= 0 && miny <= FLDH);
815 assert(maxx >= 0 && maxx <= FLDW);
816 assert(maxy >= 0 && maxy <= FLDH);
817 for (j = miny; j < maxy; j++) {
818 for (i = minx; i < maxx; i++) {
819 byte id = fld[j * FLDW + i];
820 if (id != 0) {
821 if (walp[id].res < 0) {
822 if (fg) {
823 switch (R_get_special_id(id)) {
824 case 1:
825 glColor4ub(0, 0, 255, 127);
826 break;
827 case 2:
828 glColor4ub(0, 127, 0, 127);
829 break;
830 case 3:
831 glColor4ub(127, 0, 0, 127);
832 break;
833 default:
834 glColor4ub(0, 0, 0, 127);
835 break;
837 glEnable(GL_BLEND);
838 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
839 glDisable(GL_TEXTURE_2D);
840 R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
842 } else {
843 R_gl_draw_image(&walp[id], i * CELW, j * CELH, 0);
850 static void R_draw_dots (void) {
851 int i;
852 glDisable(GL_BLEND);
853 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
854 glDisable(GL_TEXTURE_2D);
855 glBegin(GL_POINTS);
856 for (i = 0; i < MAXDOT; i++) {
857 if (dot[i].t != 0) {
858 R_gl_set_color(dot[i].c);
859 glVertex2i(dot[i].o.x, dot[i].o.y + 1);
862 glEnd();
865 static void R_draw_items (void) {
866 int i, s;
867 for (i = 0; i < MAXITEM; ++i) {
868 s = -1;
869 if (it[i].t && it[i].s >= 0) {
870 switch (it[i].t & 0x7FFF) {
871 case I_ARM1:
872 s = it[i].s / 9 + 18;
873 break;
874 case I_ARM2:
875 s = it[i].s / 9 + 20;
876 break;
877 case I_MEGA:
878 s = it[i].s / 2 + 22;
879 break;
880 case I_INVL:
881 s = it[i].s / 2 + 26;
882 break;
883 case I_SUPER:
884 case I_RTORCH:
885 case I_GTORCH:
886 case I_BTORCH:
887 s = it[i].s / 2 + (it[i].t - I_SUPER) * 4 + 35;
888 break;
889 case I_GOR1: case I_FCAN:
890 s = it[i].s / 2 + (it[i].t - I_GOR1) * 3 + 51;
891 break;
892 case I_AQUA:
893 s = 30;
894 break;
895 case I_SUIT:
896 s = 34;
897 break;
898 case I_KEYR:
899 case I_KEYG:
900 case I_KEYB:
901 s = (it[i].t & 0x7FFF) - I_KEYR + 31;
902 break;
903 case I_GUN2:
904 s = 57;
905 break;
906 default:
907 s = (it[i].t & 0x7FFF) - 1;
910 if (s >= 0) {
911 R_gl_draw_image(&item_spr[s], it[i].o.x, it[i].o.y, item_sprd[s]);
916 static int standspr (player_t *p) {
917 if (p->f & PLF_UP) {
918 return 'X';
919 } else if (p->f & PLF_DOWN) {
920 return 'Z';
921 } else {
922 return 'E';
926 static int wpnspr (player_t *p) {
927 if (p->f & PLF_UP) {
928 return 'C';
929 } else if(p->f & PLF_DOWN) {
930 return 'E';
931 } else {
932 return 'A';
936 static void R_draw_player (player_t *p) {
937 enum {STAND, GO, DIE, SLOP, DEAD, MESS, OUT, FALL}; // copypasted from player.c!
938 static const int wytab[] = {-1, -2, -1, 0};
939 int s = 'A';
940 int w = 0;
941 int wx = 0;
942 int wy = 0;
943 switch (p->st) {
944 case STAND:
945 if (p->f & PLF_FIRE) {
946 s = standspr(p) + 1;
947 w = wpnspr(p) + 1;
948 } else if (p->pain) {
949 s = 'G';
950 w = 'A';
951 wx = p->d ? 2 : -2;
952 wy = 1;
953 } else {
954 s = standspr(p);
955 w = wpnspr(p);
957 break;
958 case DEAD:
959 s = 'N';
960 break;
961 case MESS:
962 s = 'W';
963 break;
964 case GO:
965 if (p->pain) {
966 s = 'G';
967 w = 'A';
968 wx = p->d ? 2 : -2;
969 wy = 1;
970 } else {
971 s = plr_goanim[p->s / 8];
972 w = (p->f & PLF_FIRE) ? 'B' : 'A';
973 wx = p->d ? 2 : -2;
974 wy = 1 + wytab[s - 'A'];
976 break;
977 case DIE:
978 s = plr_dieanim[p->s];
979 break;
980 case SLOP:
981 s = plr_slopanim[p->s];
982 break;
983 case OUT:
984 s = 0;
985 break;
987 if (p->wpn == 0) {
988 w = 0;
990 if (w) {
991 R_gl_draw_image(&plr_wpn[(int)p->wpn][w -'A'], p->o.x + wx, p->o.y + wy, p->d);
993 if (s) {
994 R_gl_draw_image(&plr_spr[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
995 R_gl_set_color(p->color + PLAYER_COLOR_OFFSET);
996 R_gl_draw_image_color(&plr_msk[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
1000 static void R_draw_monsters (void) {
1001 enum {SLEEP, GO, RUN, CLIMB, DIE, DEAD, ATTACK, SHOOT, PAIN, WAIT, REVIVE, RUNOUT}; // copypasted from monster.c!
1002 int i;
1003 for (i = 0; i < MAXMN; i++) {
1004 if (mn[i].t != MN_NONE) {
1005 int x = mn[i].o.x;
1006 int y = mn[i].o.y;
1007 if (mn[i].t < MN__LAST) {
1008 if ((mn[i].t != MN_SOUL && mn[i].t != MN_PAIN) || mn[i].st != DEAD) {
1009 int ap = mn[i].ap[mn[i].ac];
1010 int d = (ap - 'A') * 2 + mn[i].d;
1011 int dir = mn_sprd[mn[i].t - 1][d];
1012 if (mn[i].t == MN_MAN && (ap == 'E' || ap == 'F')) {
1013 R_gl_draw_image(&mn_sgun[ap - 'E'], x, y, mn[i].d);
1015 R_gl_draw_image(&mn_spr[mn[i].t - 1][d], x, y, dir);
1016 if (mn[i].t == MN_MAN) {
1017 R_gl_set_color(MANCOLOR + PLAYER_COLOR_OFFSET);
1018 R_gl_draw_image_color(&mn_man_msk[d], x, y, dir);
1021 if (mn[i].t == MN_VILE && mn[i].st == SHOOT) {
1022 R_gl_draw_image(&mn_fspr[mn[i].ac / 3], mn[i].tx, mn[i].ty, 0);
1024 } else if (mn[i].t == MN_PL_DEAD || mn[i].t == MN_PL_MESS) {
1025 int type = mn[i].t - MN_PL_DEAD;
1026 R_gl_draw_image(&pl_spr[type], x, y, 0);
1027 R_gl_set_color(mn[i].d);
1028 R_gl_draw_image_color(&pl_msk[type], x, y, 0);
1034 static void R_draw_weapons (void) {
1035 enum {NONE, ROCKET, PLASMA, APLASMA, BALL1, BALL2, BALL7, BFGBALL, BFGHIT, MANF, REVF, FIRE}; // copypasted from weapons.c!
1036 int i, s, d, x, y;
1037 for (i = 0; i < MAXWPN; ++i) {
1038 s = -1;
1039 d = 0;
1040 switch (wp[i].t) {
1041 case REVF:
1042 case ROCKET:
1043 d = wp[i].s;
1044 if (d < 2) {
1045 d = wp[i].o.xv > 0 ? 1 : 0;
1046 x = abs(wp[i].o.xv);
1047 y = wp[i].o.yv;
1048 s = 0;
1049 if (y < 0) {
1050 if (-y >= x) {
1051 s = 30;
1053 } else if (y > 0) {
1054 if (y >= x / 2) {
1055 s = 31;
1058 } else {
1059 s = (d - 2) / 2 + 1;
1060 d = 0;
1062 break;
1063 case MANF:
1064 s=wp[i].s;
1065 if (s >= 2) {
1066 s /= 2;
1067 break;
1069 case PLASMA:
1070 case APLASMA:
1071 case BALL1:
1072 case BALL7:
1073 case BALL2:
1074 s = wp[i].s;
1075 if (s >= 2) {
1076 s = s / 2 + 1;
1078 switch (wp[i].t) {
1079 case PLASMA:
1080 s += 4;
1081 break;
1082 case APLASMA:
1083 s += 11;
1084 break;
1085 case BALL1:
1086 s += 32;
1087 break;
1088 case BALL2:
1089 s += 42;
1090 break;
1091 case BALL7:
1092 s += 37;
1093 d = wp[i].o.xv >= 0 ? 1 : 0;
1094 break;
1095 case MANF:
1096 s += 47;
1097 d= wp[i].o.xv>=0 ? 1 : 0;
1098 break;
1100 break;
1101 case BFGBALL:
1102 s = wp[i].s;
1103 if (s >= 2) {
1104 s = s / 2 + 1;
1106 s += 18;
1107 break;
1108 case BFGHIT:
1109 s = wp[i].s / 2 + 26;
1110 break;
1112 if (s >= 0) {
1113 R_gl_draw_image(&wp_spr[s * 2 + d], wp[i].o.x, wp[i].o.y, wp_sprd[s * 2 + d]);
1118 static void R_draw_smoke (void) {
1119 int i, s;
1120 for (i = 0; i < MAXSMOK; ++i) {
1121 if (sm[i].t) {
1122 switch (sm[i].s) {
1123 case 0:
1124 s = sm[i].t;
1125 if (s >= (SMSN - 1) * 3) {
1126 s = 0;
1127 } else {
1128 s = SMSN - 1 - s / 3;
1130 R_gl_draw_image(&smk_spr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1131 break;
1132 case 1:
1133 s = sm[i].t;
1134 if (s >= FLSN - 1) {
1135 s = 0;
1136 } else {
1137 s = FLSN - 1 - s;
1139 R_gl_draw_image(&smk_fspr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1140 break;
1146 static void R_draw_effects (void) {
1147 enum {NONE, TFOG, IFOG, BUBL}; // copypasted from fx.c
1148 int i, s;
1149 for (i = 0; i < MAXFX; ++i) {
1150 switch (fx[i].t) {
1151 case TFOG:
1152 s = fx[i].s / 2;
1153 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1154 break;
1155 case IFOG:
1156 s = fx[i].s / 2 + 10;
1157 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1158 break;
1159 case BUBL:
1160 glDisable(GL_BLEND);
1161 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1162 glDisable(GL_TEXTURE_2D);
1163 glBegin(GL_POINTS);
1164 R_gl_set_color(0xC0 + fx[i].s);
1165 glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
1166 glEnd();
1167 break;
1172 static int get_pu_st (int t) {
1173 if (t >= PL_FLASH) {
1174 return 1;
1175 } else if((t / 9) & 1) {
1176 return 0;
1177 } else {
1178 return 1;
1182 static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
1183 glPushMatrix();
1184 R_gl_setclip(x, y, w, h);
1185 glTranslatef(x, y, 0);
1186 if (w_horiz && horiz.n != NULL) {
1187 R_gl_draw_image_ext(&horiz, 0, 0, w, h);
1188 if (sky_type == 2 && lt_time < 0) {
1189 image *tanderbolt = &ltn[lt_type][lt_time < -5 ? 0 : 1];
1190 if (!lt_side) {
1191 R_gl_draw_image(tanderbolt, 0, lt_ypos, 0);
1192 } else {
1193 R_gl_draw_image(tanderbolt, w - 1, lt_ypos, 1);
1196 } else {
1197 glDisable(GL_BLEND);
1198 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1199 glDisable(GL_TEXTURE_2D);
1200 R_gl_set_color(DEFAULT_SKY_COLOR);
1201 R_gl_draw_quad(0, 0, w, h);
1203 int maxx = min((camx + w) / CELW + 1, FLDW);
1204 int maxy = min((camy + h) / CELH + 1, FLDH);
1205 int minx = max((camx - max_wall_width) / CELW, 0);
1206 int miny = max((camy - max_wall_height) / CELH, 0);
1207 glTranslatef(-camx, -camy, 0);
1208 R_draw_fld((byte*)fldb, minx, miny, maxx, maxy, 0);
1209 R_draw_dots();
1210 R_draw_items();
1211 R_draw_player(&pl1);
1212 if (_2pl) {
1213 R_draw_player(&pl2);
1215 R_draw_monsters();
1216 R_draw_weapons();
1217 R_draw_smoke();
1218 R_draw_effects();
1219 R_draw_fld((byte*)fldf, minx, miny, maxx, maxy, 1);
1220 glTranslatef(camx, camy, 0);
1221 if (sky_type == 2 && (lt_time == -4 || lt_time == -2)) {
1222 glColor4ub(255, 255, 255, 255);
1223 glEnable(GL_BLEND);
1224 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1225 glDisable(GL_TEXTURE_2D);
1226 R_gl_draw_quad(0, 0, w, h);
1228 glPopMatrix();
1231 static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
1232 p->looky = min(max(p->looky, -SCRH / 4), SCRH / 4); // TODO remove writeback
1233 int st = stone.w;
1234 int cw = w - st;
1235 int cx = min(max(p->o.x, cw / 2), FLDW * CELW - cw / 2);
1236 int cy = min(max(p->o.y - 12 + p->looky, h / 2), FLDH * CELH - h / 2);
1237 int camx = max(cx - cw / 2, 0);
1238 int camy = max(cy - h / 2, 0);
1239 glPushMatrix();
1240 R_draw_view(x, y + 1, cw, h - 2, camx, camy);
1241 glTranslatef(x, y, 0);
1242 if (p->invl) {
1243 if (get_pu_st(p->invl)) {
1244 glEnable(GL_BLEND);
1245 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
1246 glDisable(GL_TEXTURE_2D);
1247 glColor4ub(191, 191, 191, 255);
1248 R_gl_draw_quad(0, 0, cw, h);
1250 } else {
1251 if (p->suit && get_pu_st(p->suit)) {
1252 glEnable(GL_BLEND);
1253 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1254 glDisable(GL_TEXTURE_2D);
1255 glColor4ub(0, 255, 0, 192);
1256 R_gl_draw_quad(0, 0, cw, h);
1258 int f = min(max(p->pain * 3, 0), 255);
1259 if (f > 0) {
1260 glEnable(GL_BLEND);
1261 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1262 glDisable(GL_TEXTURE_2D);
1263 glColor4ub(255, 0, 0, f);
1264 R_gl_draw_quad(0, 0, cw, h);
1267 R_gl_setclip(x, y, w, h);
1268 glTranslatef(-x + cw, 0, 0);
1269 R_gl_draw_image(&stone, 0, 0, 0);
1270 int i = stone.h;
1271 while (i < h) {
1272 R_gl_draw_image(&stone2, 0, i, 0);
1273 i += stone2.h;
1275 if (p->drawst & PL_DRAWAIR) {
1276 if (p->air < PL_AIR) {
1277 int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
1278 glDisable(GL_BLEND);
1279 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1280 glDisable(GL_TEXTURE_2D);
1281 R_gl_set_color(0xC8);
1282 R_gl_draw_quad(10, 49, a, 2);
1285 if (p->drawst & PL_DRAWLIFE) {
1286 Z_gotoxy(10, 7);
1287 Z_printhf("%3d%%", p->life);
1289 if (p->drawst & PL_DRAWARMOR) {
1290 Z_gotoxy(10, 7 + 19);
1291 Z_printhf("%3d%%", p->armor);
1293 if (p->drawst & PL_DRAWWPN) {
1294 switch(p->wpn) {
1295 case 2:
1296 case 5:
1297 i = p->ammo;
1298 break;
1299 case 3:
1300 case 4:
1301 case 9:
1302 i = p->shel;
1303 break;
1304 case 6:
1305 i = p->rock;
1306 break;
1307 case 7:
1308 case 8:
1309 i = p->cell;
1310 break;
1311 case 10:
1312 i = p->fuel;
1313 break;
1314 default:
1315 i = -1;
1316 break;
1318 // weapon
1319 if (p->wpn >= 0) {
1320 R_gl_draw_image(&sth[12 + p->wpn], st - 88, 58 + 19, 0);
1322 // ammo
1323 if (p->wpn >= 2) {
1324 Z_gotoxy(st - 10 - 5 * 14, 58 + 2);
1325 Z_printhf("%5d", i);
1328 if (p->drawst & PL_DRAWFRAG && g_dm) {
1329 Z_gotoxy(st - 5 - 5 * 14, 77 + 5);
1330 Z_printhf("%5d", p->frag);
1332 if (p->drawst & PL_DRAWKEYS) {
1333 int x, k, n;
1334 for (k = p->keys >> 4, n = 0, x = st - 75; n < 3; n++, k >>= 1, x += 9) {
1335 if (k & 1) {
1336 R_gl_draw_image(&keys[n], x, 91, 0);
1340 if (p->drawst & PL_DRAWLIVES && !_2pl) {
1341 Z_gotoxy(st - 35, 17);
1342 Z_printhf("%d", p->lives);
1344 glPopMatrix();
1347 /* --- Game --- */
1349 static void pl_info (player_t *p, int x, int y) {
1350 dword t = p->kills * 10920 / g_time;
1351 Z_gotoxy(x + 25, y); Z_printbf("KILLS");
1352 Z_gotoxy(x + 25, y + 15); Z_printbf("KPM");
1353 Z_gotoxy(x + 25, y + 30); Z_printbf("SECRETS %u / %u", p->secrets, sw_secrets);
1354 Z_gotoxy(x + 255, y); Z_printbf("%u", p->kills);
1355 Z_gotoxy(x + 255, y + 15); Z_printbf("%u.%u", t / 10, t % 10);
1358 static void R_draw_intermission (void) {
1359 int cx = SCRW / 2;
1360 word hr, mn, sc, h;
1361 Z_gotoxy(cx - 14*12/2, 20);
1362 Z_printbf("LEVEL COMPLETE");
1363 Z_calc_time(g_time, &hr, &mn, &sc);
1364 Z_gotoxy(cx - 12*12/2, 40);
1365 Z_printbf("TIME %u:%02u:%02u", hr, mn, sc);
1366 h = 40 + SCRH / 10;
1367 if (_2pl) {
1368 Z_gotoxy(cx - 10*12/2, h);
1369 Z_printbf("PLAYER ONE");
1370 h += 20;
1372 pl_info(&pl1, cx - 160, h);
1373 if (_2pl) {
1374 h += 30 + SCRH / 10;
1375 Z_gotoxy(cx - 10*12/2, h);
1376 Z_printbf("PLAYER TWO");
1377 h += 20;
1378 pl_info(&pl2, cx - 160, h);
1382 static void W_act (void) {
1383 int i, a;
1384 if (g_time % 3 == 0) {
1385 for (i = 1; i < max_textures; i++) {
1386 a = walani[i];
1387 if (a != 0) {
1388 anic[a]++;
1389 if (anip[a][anic[a]].res == -1) {
1390 anic[a] = 0;
1392 walp[i] = anip[a][anic[a]];
1398 void R_draw (void) {
1399 W_act();
1400 glClearColor(0, 0, 0, 1);
1401 glClear(GL_COLOR_BUFFER_BIT);
1402 glEnable(GL_SCISSOR_TEST);
1403 R_gl_setmatrix();
1404 switch (g_st) {
1405 case GS_ENDANIM:
1406 case GS_END2ANIM:
1407 case GS_DARKEN:
1408 case GS_BVIDEO:
1409 case GS_EVIDEO:
1410 case GS_END3ANIM:
1411 break;
1412 case GS_TITLE:
1413 R_gl_draw_image_ext(&scrnh[0], 0, 0, SCRW, SCRH);
1414 break;
1415 case GS_INTER:
1416 R_gl_draw_image_ext(&scrnh[1], 0, 0, SCRW, SCRH);
1417 R_draw_intermission();
1418 break;
1419 case GS_ENDSCR:
1420 R_gl_draw_image_ext(&scrnh[2], 0, 0, SCRW, SCRH);
1421 break;
1422 case GS_GAME:
1423 if (_2pl) {
1424 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH / 2);
1425 R_draw_player_view(&pl2, 0, SCRH / 2, SCRW, SCRH / 2);
1426 } else {
1427 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH);
1429 R_gl_setclip(0, 0, SCRW, SCRH);
1430 break;
1432 GM_draw();
1433 SDL_GL_SwapBuffers();
1436 void R_alloc (void) {
1437 char s[10];
1438 int i, j, n;
1439 logo("R_alloc: load graphics\n");
1440 /* Game */
1441 scrnh[0] = R_gl_loadimage("TITLEPIC");
1442 assert(scrnh[0].n);
1443 scrnh[1] = R_gl_loadimage("INTERPIC");
1444 scrnh[2] = R_gl_loadimage("ENDPIC");
1445 for (i = 0; i < 2; i++) {
1446 sprintf(s, "LTN%c", '1' + i);
1447 for (j = 0; j < 2; j++) {
1448 ltn[i][j] = Z_getspr(s, j, 0, NULL);
1451 /* Smoke */
1452 for (i = 0; i < SMSN; i++) {
1453 smk_spr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_smoke_spr);
1455 for (i = 0; i < FLSN; i++) {
1456 smk_fspr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_flame_spr);
1458 /* Effects */
1459 for (i = 0; i < 10; i++) {
1460 fx_spr[i] = Z_getspr("TFOG", i, 0, fx_sprd + i);
1462 for (; i < 15; i++) {
1463 fx_spr[i] = Z_getspr("IFOG", i - 10, 0, fx_sprd + i);
1465 /* Weapons */
1466 for (i = 0; i < 4; i++) {
1467 wp_spr[i * 2] = Z_getspr("MISL", i, 1, wp_sprd + i * 2);
1468 wp_spr[i * 2 + 1] = Z_getspr("MISL", i, 2, wp_sprd + i * 2 + 1);
1470 for (; i < 6; i++) {
1471 wp_spr[i * 2] = Z_getspr("PLSS", i - 4, 1, wp_sprd + i * 2);
1472 wp_spr[i * 2 + 1] = Z_getspr("PLSS", i - 4, 2, wp_sprd + i * 2 + 1);
1474 for (; i < 11; i++) {
1475 wp_spr[i * 2] = Z_getspr("PLSE", i - 6, 1, wp_sprd + i * 2);
1476 wp_spr[i * 2 + 1] = Z_getspr("PLSE", i - 6, 2, wp_sprd + i * 2 + 1);
1478 for (; i < 13; i++) {
1479 wp_spr[i * 2] = Z_getspr("APLS", i - 11, 1, wp_sprd + i * 2);
1480 wp_spr[i * 2 + 1] = Z_getspr("APLS", i - 11, 2, wp_sprd + i * 2 + 1);
1482 for (; i < 18; i++) {
1483 wp_spr[i * 2] = Z_getspr("APBX", i - 13, 1, wp_sprd + i * 2);
1484 wp_spr[i * 2 + 1] = Z_getspr("APBX", i - 13, 2, wp_sprd + i * 2 + 1);
1486 for(; i < 20; i++) {
1487 wp_spr[i * 2] = Z_getspr("BFS1", i - 18, 1, wp_sprd + i * 2);
1488 wp_spr[i * 2 + 1] = Z_getspr("BFS1", i - 18, 2, wp_sprd + i * 2 + 1);
1490 for (; i < 26; i++) {
1491 wp_spr[i * 2] = Z_getspr("BFE1", i - 20, 1, wp_sprd + i * 2);
1492 wp_spr[i * 2 + 1] = Z_getspr("BFE1", i - 20, 2, wp_sprd + i * 2 + 1);
1494 for (; i < 30; i++) {
1495 wp_spr[i * 2] = Z_getspr("BFE2", i - 26, 1, wp_sprd + i * 2);
1496 wp_spr[i * 2 + 1] = Z_getspr("BFE2", i - 26, 2, wp_sprd + i * 2 + 1);
1498 for (; i < 32; i++) {
1499 wp_spr[i * 2] = Z_getspr("MISL", i - 30 + 4, 1, wp_sprd + i * 2);
1500 wp_spr[i * 2 + 1] = Z_getspr("MISL", i - 30 + 4, 2, wp_sprd + i * 2 + 1);
1502 for (; i < 37; i++) {
1503 wp_spr[i * 2] = Z_getspr("BAL1", i - 32, 1, wp_sprd + i * 2);
1504 wp_spr[i * 2 + 1] = Z_getspr("BAL1", i - 32, 2, wp_sprd + i * 2 + 1);
1506 for (; i < 42; i++) {
1507 wp_spr[i * 2] = Z_getspr("BAL7", i - 37, 1, wp_sprd + i * 2);
1508 wp_spr[i * 2 + 1] = Z_getspr("BAL7", i - 37, 2, wp_sprd + i * 2 + 1);
1510 for (; i < 47; i++) {
1511 wp_spr[i * 2] = Z_getspr("BAL2", i - 42, 1, wp_sprd + i * 2);
1512 wp_spr[i * 2 + 1] = Z_getspr("BAL2", i - 42, 2, wp_sprd + i * 2 + 1);
1514 for (; i < 49; i++) {
1515 wp_spr[i * 2] = Z_getspr("MANF", i - 47, 1, wp_sprd + i * 2);
1516 wp_spr[i * 2 + 1] = Z_getspr("MANF", i - 47, 2, wp_sprd + i * 2 + 1);
1518 /* Items */
1519 static const char snm[18][4] = {
1520 "CLIP", "SHEL", "ROCK", "CELL", "AMMO", "SBOX", "BROK", "CELP",
1521 "STIM", "MEDI", "BPAK",
1522 "CSAW", "SHOT", "SGN2", "MGUN", "LAUN", "PLAS", "BFUG"
1523 };
1524 static const char n4[4][4] = {
1525 "SOUL", "SMRT", "SMGT", "SMBT"
1526 };
1527 static const char n3[2][4] = {
1528 "GOR1", "FCAN"
1529 };
1530 for (i = 0; i < 18; i++) {
1531 item_spr[i] = Z_getspr(snm[i], 0, 0, item_sprd + i);
1533 for (; i < 20; i++) {
1534 item_spr[i] = Z_getspr("ARM1", i - 18, 0, item_sprd + i);
1535 item_spr[i + 2] = Z_getspr("ARM2", i - 18, 0, item_sprd + i);
1537 i+=2;
1538 for (; i < 26; i++) {
1539 item_spr[i] = Z_getspr("MEGA", i - 22, 0, item_sprd + i);
1541 for (; i < 30; i++) {
1542 item_spr[i] = Z_getspr("PINV", i - 26, 0, item_sprd + i);
1544 item_spr[30] = Z_getspr("AQUA", 0, 0, item_sprd + 30);
1545 item_spr[31] = Z_getspr("KEYR", 0, 0, item_sprd + 31);
1546 item_spr[32] = Z_getspr("KEYG", 0, 0, item_sprd + 32);
1547 item_spr[33] = Z_getspr("KEYB", 0, 0, item_sprd + 33);
1548 item_spr[34] = Z_getspr("SUIT", 0, 0, item_sprd + 34);
1549 for (n = 35, j = 0; j < 4; j++) {
1550 for (i = 0; i < 4; i++, n++) {
1551 item_spr[n] = Z_getspr(n4[j], i, 0, item_sprd + n);
1554 for (j = 0; j < 2; j++) {
1555 for (i = 0; i < 3; i++, n++) {
1556 item_spr[n] = Z_getspr(n3[j], i, 0, item_sprd + n);
1559 item_spr[57] = Z_getspr("GUN2", 0, 0, item_sprd + 57);
1560 /* Player */
1561 for (i = 0; i < 27; i++) {
1562 plr_spr[i * 2] = Z_getspr("PLAY", i, 1, plr_sprd + i * 2);
1563 plr_msk[i * 2] = R_gl_get_special_spr("PLAY", i, 1, &R_extract_mask_spr);
1564 plr_spr[i * 2 + 1] = Z_getspr("PLAY", i, 2, plr_sprd + i * 2 + 1);
1565 plr_msk[i * 2 + 1] = R_gl_get_special_spr("PLAY", i, 2, &R_extract_mask_spr);
1567 strncpy(s, "PWPx", 4);
1568 for (i = 1; i < 11; i++) {
1569 s[3] = (i < 10 ? '0' : 'A' - 10) + i;
1570 for (j = 0; j < 6; j++) {
1571 plr_wpn[i][j] = Z_getspr(s, j, 1, NULL);
1574 /* Monsters */
1575 static const char msn[MN_TN][4] = {
1576 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
1577 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
1578 };
1579 static const int mms[MN_TN] = {
1580 14*2, 21*2, 21*2, 21*2, 16*2, 20*2, 15*2, 15*2, 12*2, 11*2,
1581 13*2, 19*2, 16*2, 20*2, 17*2, 29*2, 6*2, 2*2, 17*2, 23*2
1582 };
1583 mn_sgun[0] = Z_getspr("PWP4", 0, 1, NULL);
1584 mn_sgun[1] = Z_getspr("PWP4", 1, 1, NULL);
1585 for (j = 0; j < MN_TN; j++) {
1586 for (i = 0; i < mms[j]; i++) {
1587 mn_spr[j][i] = Z_getspr(msn[j], i / 2, (i & 1) + 1, &mn_sprd[j][i]);
1588 if (j == MN_MAN - 1) {
1589 mn_man_msk[i] = R_gl_get_special_spr(msn[j], i / 2, (i & 1) + 1, &R_extract_mask_spr);
1592 if (j == MN_BARREL - 1) {
1593 for (i = 4; i < 14; i++) {
1594 mn_spr[j][i] = Z_getspr("BEXP", i / 2 - 2, (i & 1) + 1, &mn_sprd[j][i]);
1598 for (i = 0; i < 8; i++) {
1599 mn_fspr[i] = Z_getspr("FIRE", i, 0, NULL);
1601 pl_spr[0] = Z_getspr("PLAY", 'N' - 'A', 0, NULL);
1602 pl_msk[0] = R_gl_get_special_spr("PLAY", 'N' - 'A', 0, &R_extract_mask_spr);
1603 pl_spr[1] = Z_getspr("PLAY", 'W' - 'A', 0, NULL);
1604 pl_msk[1] = R_gl_get_special_spr("PLAY", 'W' - 'A', 0, &R_extract_mask_spr);
1605 /* Misc */
1606 static const char mnm[22][8]={
1607 "STTNUM0", "STTNUM1", "STTNUM2", "STTNUM3", "STTNUM4",
1608 "STTNUM5", "STTNUM6", "STTNUM7", "STTNUM8", "STTNUM9",
1609 "STTMINUS", "STTPRCNT",
1610 "FISTA0", "CSAWA0", "PISTA0", "SHOTA0", "SGN2A0", "MGUNA0", "LAUNA0",
1611 "PLASA0", "BFUGA0", "GUN2A0"
1612 };
1613 stone = R_gl_loadimage("STONE");
1614 stone2 = R_gl_loadimage("STONE2");
1615 keys[0] = R_gl_loadimage("KEYRA0");
1616 keys[1] = R_gl_loadimage("KEYGA0");
1617 keys[2] = R_gl_loadimage("KEYBA0");
1618 for (i = 0; i < 22; i++) {
1619 sth[i] = R_gl_loadimage(mnm[i]);
1621 strcpy(s, "STBF_*");
1622 for (i = '!'; i < 160; i++) {
1623 s[5] = i;
1624 bfh[i - '!'] = R_gl_getimage(F_findres(s));
1626 for (i = '!'; i < 160; i++) {
1627 sprintf(s, "STCFN%03d", i);
1628 sfh[i - '!'] = R_gl_getimage(F_findres(s));
1630 strcpy(s, "WINUM*");
1631 for (i = '0'; i <= '9'; i++) {
1632 s[5] = i;
1633 bfh[i - '!'] = R_gl_loadimage(s);
1635 bfh[':' - '!'] = R_gl_loadimage("WICOLON");
1636 // menu
1637 msklh[0] = R_gl_loadimage("M_SKULL1");
1638 msklh[1] = R_gl_loadimage("M_SKULL2");
1639 mbarl = R_gl_loadimage("M_THERML");
1640 mbarm = R_gl_loadimage("M_THERMM");
1641 mbarr = R_gl_loadimage("M_THERMR");
1642 mbaro = R_gl_loadimage("M_THERMO");
1643 mslotl = R_gl_loadimage("M_LSLEFT");
1644 mslotm = R_gl_loadimage("M_LSCNTR");
1645 mslotr = R_gl_loadimage("M_LSRGHT");
1646 // walls
1647 for (i = 1; i < ANIT; i++) {
1648 for (j = 0; anm[i - 1][j]; j++) {
1649 anip[i][j] = R_gl_loadimage(anm[i - 1][j]);
1651 for(; j < 5; j++) {
1652 anip[i][j] = (image) {
1653 .n = NULL,
1654 .w = 8,
1655 .h = 8,
1656 .res = -1,
1657 };
1662 void R_init (void) {
1663 Uint32 flags = SDL_OPENGL;
1664 if (fullscreen) {
1665 flags = flags | SDL_FULLSCREEN;
1667 if (SCRW <= 0 || SCRH <= 0) {
1668 ERR_failinit("Invalid screen size %ix%i\n", SCRW, SCRH);
1670 if (surf == NULL) {
1671 R_init_playpal(); // only onece
1673 surf = SDL_SetVideoMode(SCRW, SCRH, 0, flags);
1674 if (surf == NULL) {
1675 ERR_failinit("Unable to set video mode: %s\n", SDL_GetError());
1677 root = R_cache_new();
1678 assert(root);
1679 R_alloc();
1682 void R_done (void) {
1683 R_cache_free(root, 1);
1686 void R_setgamma (int g) {
1687 gamma = g < 0 ? 0 : (g > 4 ? 4 : g);
1690 int R_getgamma (void) {
1691 return gamma;
1694 void R_toggle_fullscreen (void) {
1695 fullscreen = !fullscreen;
1696 if (surf) {
1697 R_init(); // recreate window
1701 void R_get_name (int n, char s[8]) {
1702 assert(n >= 0 && n < 256);
1703 if (walp[n].res == -1) {
1704 memset(s, 0, 8);
1705 } else if (walp[n].res == -2) {
1706 memcpy(s, "_WATER_", 8);
1707 s[7] = '0' + (intptr_t)walp[n].n - 1;
1708 } else if (walani[n] > 0) {
1709 memcpy(s, anm[walani[n] - 1][0], 8);
1710 } else {
1711 F_getresname(s, walp[n].res & 0x7FFF);
1715 static short getani (char n[8]) {
1716 short i = 0;
1717 while (i < ANIT - 1 && strncasecmp(n, anm[i][0], 8) != 0) {
1718 i++;
1720 return i < ANIT - 1 ? i + 1 : 0;
1723 int R_get_special_id (int n) {
1724 assert(n >= 0 && n <= 256);
1725 return walp[n].res == -2 ? (intptr_t)walp[n].n : -1;
1728 void R_begin_load (void) {
1729 int i;
1730 for (i = 0; i < 256; i++) {
1731 if (walp[i].n != NULL && walp[i].res >= 0 && walani[i] == 0) {
1732 R_gl_free_image(&walp[i]);
1734 memset(&walp[i], 0, sizeof(image));
1735 walp[i].res = -1;
1736 walswp[i] = i;
1737 walani[i] = 0;
1739 memset(anic, 0, sizeof(anic));
1740 max_wall_width = 0;
1741 max_wall_height = 0;
1742 max_textures = 1;
1745 void R_load (char s[8], int f) {
1746 assert(max_textures < 256);
1747 if (!s[0]) {
1748 walp[max_textures] = (image) {
1749 .n = NULL,
1750 .x = 0,
1751 .y = 0,
1752 .w = 0,
1753 .h = 0,
1754 .res = -1,
1755 };
1756 } else if (strncasecmp(s, "_WATER_", 7) == 0) {
1757 walp[max_textures] = (image) {
1758 .n = (void*)((intptr_t)s[7] - '0' + 1),
1759 .x = 0,
1760 .y = 0,
1761 .w = 8,
1762 .h = 8,
1763 .res = -2,
1764 };
1765 } else {
1766 walp[max_textures] = R_gl_loadimage(s);
1767 if (f) {
1768 walp[max_textures].res |= 0x8000;
1770 if (s[0] == 'S' && s[1] == 'W' && s[4] == '_') {
1771 walswp[max_textures] = 0;
1773 walani[max_textures] = getani(s);
1775 max_wall_width = max(max_wall_width, walp[max_textures].w);
1776 max_wall_height = max(max_wall_height, walp[max_textures].h);
1777 max_textures++;
1780 void R_end_load (void) {
1781 int i, j, k, g;
1782 char s[8];
1783 j = max_textures;
1784 for (i = 1; i < 256 && j < 256; i++) {
1785 if (walswp[i] == 0) {
1786 R_get_name(i, s);
1787 s[5] ^= 1;
1788 g = F_getresid(s) | (walp[i].res & 0x8000);
1789 k = 1;
1790 while (k < 256 && walp[k].res != g) {
1791 k += 1;
1793 if (k >= 256) {
1794 k = j;
1795 j += 1;
1796 walp[k] = R_gl_getimage(g);
1797 walf[k] = g & 0x8000 ? 1 : 0;
1799 walswp[i] = k;
1800 walswp[k] = i;
1805 void R_loadsky (int sky) {
1806 char s[6];
1807 strcpy(s, "RSKYx");
1808 s[4] = '0' + sky;
1809 R_gl_free_image(&horiz);
1810 horiz = R_gl_loadimage(s);