DEADSOFTWARE

opengl: fix node allocation error on start
[flatwaifu.git] / src / gl / render.c
1 #include "glob.h"
2 #include "render.h"
3 #include "system.h"
4 #include "files.h"
5 #include "memory.h"
6 #include "misc.h"
7 #include "error.h"
9 #include "menu.h"
10 #include "game.h"
11 #include "dots.h"
12 #include "items.h"
14 #include "sound.h" // snd_vol
15 #include "music.h" // mus_vol
17 #include "fx.h"
18 #include "player.h"
19 #include "monster.h"
20 #include "weapons.h"
21 #include "smoke.h"
22 #include "view.h"
23 #include "switch.h" // sw_secrets
25 #ifdef __APPLE__
26 # include <OpenGL/gl.h>
27 #else
28 # include <GL/gl.h>
29 #endif
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
35 #define VGA_TRANSPARENT_COLOR 0
36 #define DEFAULT_SKY_COLOR 0x97
37 #define MANCOLOR 0xD0
38 #define PLAYER_COLOR_OFFSET 7
39 #define MAXAIR 1091
40 #define ANIT 5
41 #define PL_FLASH 90
43 #pragma pack(1)
44 typedef struct vgaimg {
45 word w, h;
46 short x, y;
47 byte data[];
48 } vgaimg;
50 typedef struct rgb {
51 byte r, g, b;
52 } rgb;
54 typedef struct rgba {
55 byte r, g, b, a;
56 } rgba;
57 #pragma pack()
59 typedef struct node {
60 struct cache *base;
61 struct node *left, *right;
62 struct node *up;
63 int l, t, r, b;
64 int leaf;
65 } node;
67 typedef struct cache {
68 struct cache *next;
69 struct node root;
70 GLuint id;
71 } cache;
73 typedef struct image {
74 node *n;
75 GLint x, y;
76 GLuint w, h;
77 int res;
78 } image;
80 /* Render Specific */
81 int SCRW = 320; // public
82 int SCRH = 200; // public
83 static int gamma;
84 static int fullscreen;
85 static rgb playpal[256];
86 static byte bright[256];
87 static GLuint lastTexture;
88 static cache *root;
90 /* Game */
91 static image scrnh[3]; // TITLEPIC INTERPIC ENDPIC
92 static image ltn[2][2];
94 /* Smoke */
95 static image smk_spr[SMSN];
96 static image smk_fspr[FLSN];
98 /* Effects */
99 static image fx_spr[15];
100 static char fx_sprd[15];
102 /* Weapons */
103 static image wp_spr[49*2];
104 static char wp_sprd[49*2];
106 /* Items */
107 static image item_spr[58];
108 static char item_sprd[58];
110 /* Player */
111 static image plr_spr[27*2];
112 static image plr_msk[27*2];
113 static char plr_sprd[27*2];
114 static image plr_wpn[11][6];
116 /* Monsters */
117 static image pl_spr[2];
118 static image pl_msk[2];
119 static image mn_spr[MN_TN][29*2];
120 static image mn_man_msk[29*2];
121 static char mn_sprd[MN_TN][29*2];
122 static image mn_fspr[8];
123 static image mn_sgun[2];
125 /* Misc */
126 static image sth[22];
127 static image bfh[160 - '!'];
128 static image sfh[160 - '!'];
129 static image stone;
130 static image stone2;
131 static image keys[3];
132 static int prx = 0;
133 static int pry = 0;
135 /* Menu */
136 static int gm_tm;
137 static image msklh[2];
138 static image mbarl;
139 static image mbarm;
140 static image mbarr;
141 static image mbaro;
142 static image mslotl;
143 static image mslotm;
144 static image mslotr;
146 /* Map */
147 static const char *anm[ANIT - 1][5] = {
148 {"WALL22_1", "WALL23_1", "WALL23_2", NULL, NULL},
149 {"WALL58_1", "WALL58_2", "WALL58_3", NULL, NULL},
150 {"W73A_1", "W73A_2", NULL, NULL, NULL},
151 {"RP2_1", "RP2_2", "RP2_3", "RP2_4", NULL}
152 };
153 static int max_wall_width;
154 static int max_wall_height;
155 static int max_textures;
156 static image walp[256];
157 static byte walani[256];
158 static image anip[ANIT][5];
159 static byte anic[ANIT];
160 static image horiz;
162 /* Texture cache */
164 // https://blackpawn.com/texts/lightmaps/
165 static node *R_node_alloc (node *p, int w, int h) {
166 assert(p);
167 assert(w > 0);
168 assert(h > 0);
169 if (p->left) {
170 assert(p->right);
171 node *n = R_node_alloc(p->left, w, h);
172 return n ? n : R_node_alloc(p->right, w, h);
173 } else {
174 int pw = p->r - p->l + 1;
175 int ph = p->b - p->t + 1;
176 if (p->leaf || pw < w || ph < h) {
177 return NULL;
178 } else if (pw == w && ph == h) {
179 p->leaf = 1;
180 return p;
181 } else {
182 p->left = malloc(sizeof(node));
183 p->right = malloc(sizeof(node));
184 if (pw - w > ph - h) {
185 *p->left = (node) {
186 .up = p,
187 .l = p->l,
188 .t = p->t,
189 .r = p->l + w - 1,
190 .b = p->b
191 };
192 *p->right = (node) {
193 .up = p,
194 .l = p->l + w,
195 .t = p->t,
196 .r = p->r,
197 .b = p->b
198 };
199 } else {
200 *p->left = (node) {
201 .up = p,
202 .l = p->l,
203 .t = p->t,
204 .r = p->r,
205 .b = p->t + h - 1
206 };
207 *p->right = (node) {
208 .up = p,
209 .l = p->l,
210 .t = p->t + h,
211 .r = p->r,
212 .b = p->b
213 };
215 return R_node_alloc(p->left, w, h);
220 static int R_node_have_leaf (node *n) {
221 return n && (n->leaf || R_node_have_leaf(n->left) || R_node_have_leaf(n->right));
224 static void R_node_free_recursive (node *n) {
225 if (n) {
226 R_node_free_recursive(n->left);
227 R_node_free_recursive(n->right);
228 free(n);
232 static void R_node_free (node *n) {
233 if (n) {
234 //logo("free node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
235 assert(n->leaf);
236 assert(n->left == NULL);
237 assert(n->right == NULL);
238 n->leaf = 0;
239 n->base = NULL;
240 node *p = n->up;
241 while (p != NULL) {
242 assert(p->leaf == 0);
243 assert(p->left);
244 assert(p->right);
245 if (R_node_have_leaf(p) == 0) {
246 R_node_free_recursive(p->left);
247 p->left = NULL;
248 R_node_free_recursive(p->right);
249 p->right = NULL;
250 p = p->up;
251 } else {
252 p = NULL;
258 static void R_cache_get_max_texture_size (int *w, int *h) {
259 GLint size = 0;
260 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
261 size = min(max(size, 0), 512); // more can be buggy on older hardware
262 *w = size;
263 *h = size;
266 static void R_gl_bind_texture (GLuint id) {
267 if (id != lastTexture) {
268 glBindTexture(GL_TEXTURE_2D, id);
272 static cache *R_cache_new (void) {
273 int w, h;
274 GLuint id;
275 cache *c = NULL;
276 R_cache_get_max_texture_size(&w, &h);
277 if (w && h) {
278 glGenTextures(1, &id);
279 if (id) {
280 R_gl_bind_texture(id);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
283 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
284 c = malloc(sizeof(cache));
285 if (c != NULL) {
286 *c = (cache) {
287 .id = id,
288 .root.r = w - 1,
289 .root.b = h - 1
290 };
291 } else {
292 glDeleteTextures(1, &id);
296 //logo("new cache %p\n", c);
297 return c;
300 static void R_cache_free (cache *root, int freetexture) {
301 cache *next;
302 cache *c = root;
303 while (c != NULL) {
304 next = c->next;
305 R_node_free_recursive(c->root.left);
306 R_node_free_recursive(c->root.right);
307 if (freetexture && c->id != 0) {
308 glDeleteTextures(1, &c->id);
310 free(c);
311 c = next;
315 static node *R_cache_alloc (cache *root, int w, int h) {
316 assert(root);
317 assert(w > 0 && h > 0);
318 node *n = NULL;
319 cache *p = NULL;
320 cache *c = root;
321 int maxw, maxh;
322 R_cache_get_max_texture_size(&maxw, &maxh);
323 if (w <= maxw && h <= maxh) {
324 while (c && !n) {
325 n = R_node_alloc(&c->root, w, h);
326 if (n) {
327 assert(n->leaf);
328 n->base = c;
330 p = c;
331 c = c->next;
333 if (!n) {
334 c = R_cache_new();
335 if (c) {
336 p->next = c;
337 n = R_node_alloc(&c->root, w, h);
338 if (n) {
339 assert(n->leaf);
340 n->base = c;
345 if (n) {
346 //logo("new node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
347 } else {
348 logo("new node failed {%i:%i}\n", w, h);
350 return n;
353 static void R_cache_update (node *n, const void *data, int x, int y, int w, int h) {
354 assert(n);
355 assert(n->leaf);
356 assert(n->base);
357 assert(data);
358 assert(x >= 0);
359 assert(y >= 0);
360 assert(n->l + x + w - 1 <= n->r);
361 assert(n->t + y + h - 1 <= n->b);
362 R_gl_bind_texture(n->base->id);
363 glTexSubImage2D(GL_TEXTURE_2D, 0, n->l + x, n->t + y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
366 /* Generic helpers */
368 static void R_init_playpal (void) {
369 int i;
370 byte *vgapal = M_lock(F_getresid("PLAYPAL"));
371 for (i = 0; i < 256; i++) {
372 playpal[i] = (rgb) {
373 .r = vgapal[i * 3 + 0] * 255 / 63,
374 .g = vgapal[i * 3 + 1] * 255 / 63,
375 .b = vgapal[i * 3 + 2] * 255 / 63,
376 };
377 bright[i] = ((int)vgapal[i * 3 + 0] + vgapal[i * 3 + 1] + vgapal[i * 3 + 2]) * 8 / (63 * 3);
379 M_unlock(vgapal);
382 static vgaimg *R_getvga (int id) {
383 int loaded = M_was_locked(id);
384 vgaimg *v = M_lock(id);
385 if (v != NULL && !loaded) {
386 v->w = short2host(v->w);
387 v->h = short2host(v->h);
388 v->x = short2host(v->x);
389 v->y = short2host(v->y);
391 return v;
394 static rgba *R_extract_flame_spr (vgaimg *v) {
395 static const byte flametab[16] = {
396 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0xD5,
397 0xD6, 0xD7, 0xA1, 0xA0, 0xE3, 0xE2, 0xE1, 0xE0
398 };
399 int i, j;
400 rgba *s = malloc(v->w * v->h * sizeof(rgba));
401 if (s != NULL) {
402 for (j = 0; j < v->h; j++) {
403 for (i = 0; i < v->w; i++) {
404 int k = j * v->w + i;
405 byte c = v->data[k] + bright[DEFAULT_SKY_COLOR];
406 s[k] = (rgba) {
407 .r = playpal[flametab[c]].r,
408 .g = playpal[flametab[c]].g,
409 .b = playpal[flametab[c]].b,
410 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
411 };
415 return s;
418 static rgba *R_extract_smoke_spr (vgaimg *v) {
419 int i, j;
420 rgba *s = malloc(v->w * v->h * sizeof(rgba));
421 if (s != NULL) {
422 for (j = 0; j < v->h; j++) {
423 for (i = 0; i < v->w; i++) {
424 int k = j * v->w + i;
425 byte c = ((v->data[k] + bright[DEFAULT_SKY_COLOR]) + 0x60) ^ 0x0F;
426 byte a = 0xFF - ((int)playpal[c].r + playpal[c].g + playpal[c].b) / 3;
427 s[k] = (rgba) {
428 .r = playpal[c].r,
429 .g = playpal[c].g,
430 .b = playpal[c].b,
431 .a = v->data[k] == VGA_TRANSPARENT_COLOR ? 0x00 : a,
432 };
436 return s;
439 static rgba *R_extract_mask_spr (vgaimg *v) {
440 int i, j;
441 rgba *s = malloc(v->w * v->h * sizeof(rgba));
442 if (s != NULL) {
443 for (j = 0; j < v->h; j++) {
444 for (i = 0; i < v->w; i++) {
445 int k = j * v->w + i;
446 byte c = v->data[k];
447 if (c >= 0x70 && c <= 0x7F) {
448 byte mask = c - 0x70;
449 mask = 0xFF - ((mask << 4) | mask);
450 s[k] = (rgba) {
451 .r = mask,
452 .g = mask,
453 .b = mask,
454 .a = 0xFF,
455 };
456 } else {
457 s[k] = (rgba) {
458 .r = 0,
459 .g = 0,
460 .b = 0,
461 .a = 0,
462 };
467 return s;
470 static rgba *R_extract_rgba_spr (vgaimg *v) {
471 int i, j;
472 rgba *s = malloc(v->w * v->h * sizeof(rgba));
473 if (s != NULL) {
474 for (j = 0; j < v->h; j++) {
475 for (i = 0; i < v->w; i++) {
476 int k = j * v->w + i;
477 byte c = v->data[k];
478 s[k] = (rgba) {
479 .r = playpal[c].r,
480 .g = playpal[c].g,
481 .b = playpal[c].b,
482 .a = c == VGA_TRANSPARENT_COLOR ? 0x00 : 0xFF,
483 };
487 return s;
490 /* OpenGL helpers */
492 static image R_gl_create_image (const rgba *buf, int w, int h) {
493 node *n = R_cache_alloc(root, w, h);
494 if (n) {
495 R_cache_update(n, buf, 0, 0, w, h);
497 return (image) {
498 .n = n,
499 .w = w,
500 .h = h,
501 .res = -1
502 };
505 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
506 image img;
507 //char name[8];
508 //F_getresname(name, id);
509 //logo("load image: %.8s\n", name);
510 vgaimg *v = R_getvga(id);
511 if (v != NULL) {
512 rgba *buf = (*fn)(v);
513 img = R_gl_create_image(buf, v->w, v->h);
514 img.x = v->x;
515 img.y = v->y;
516 img.res = id;
517 M_unlock(v);
518 free(buf);
519 } else {
520 img = (image) {
521 .res = id
522 };
524 return img;
527 static image R_gl_getimage (int id) {
528 return R_gl_get_special_image(id, &R_extract_rgba_spr);
531 static image R_gl_loadimage (const char name[8]) {
532 return R_gl_getimage(F_getresid(name));
535 static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vgaimg*)) {
536 return R_gl_get_special_image(F_getsprid(n, s, d), fn);
539 static void R_gl_free_image (image *img) {
540 if (img->n != NULL && img->res >= 0) {
541 R_node_free(img->n);
543 img->n = NULL;
544 img->res = -1;
547 static void R_gl_draw_quad (int x, int y, int w, int h) {
548 glBegin(GL_QUADS);
549 glVertex2i(x + w, y);
550 glVertex2i(x, y);
551 glVertex2i(x, y + h);
552 glVertex2i(x + w, y + h);
553 glEnd();
556 static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip) {
557 if (img->n) {
558 GLfloat nw = img->n->base->root.r + 1;
559 GLfloat nh = img->n->base->root.b + 1;
560 GLfloat ax = (flip ? img->n->l : img->n->r + 1) / nw;
561 GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
562 GLfloat ay = (img->n->t) / nw;
563 GLfloat by = (img->n->b + 1) / nh;
564 R_gl_bind_texture(img->n->base->id);
565 glEnable(GL_TEXTURE_2D);
566 glBegin(GL_QUADS);
567 glTexCoord2f(ax, ay); glVertex2i(x + w, y);
568 glTexCoord2f(bx, ay); glVertex2i(x, y);
569 glTexCoord2f(bx, by); glVertex2i(x, y + h);
570 glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
571 glEnd();
572 } else {
573 glColor3ub(255, 0, 0);
574 glDisable(GL_BLEND);
575 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
576 glDisable(GL_TEXTURE_2D);
577 R_gl_draw_quad(x, y, w, h);
581 /* fit image into rectangle without applying offset and transparency */
582 static void R_gl_draw_image_ext (image *img, int x, int y, int w, int h) {
583 glDisable(GL_BLEND);
584 glColor3ub(255, 255, 255);
585 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
586 R_gl_draw_textured(img, x, y, w, h, 0);
589 /* draw sprite with offset and coloring */
590 static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
591 int xx = flip ? x - img->w + img->x : x - img->x;
592 glEnable(GL_BLEND);
593 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
594 R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
597 /* draw sprite with offset */
598 static void R_gl_draw_image (image *img, int x, int y, int flip) {
599 glColor3ub(255, 255, 255);
600 R_gl_draw_image_color(img, x, y, flip);
603 static void R_gl_set_color (byte c) {
604 glColor3ub(playpal[c].r, playpal[c].g, playpal[c].b);
607 static void R_gl_setclip (int x, int y, int w, int h) {
608 glScissor(x, SCRH - h - y, w, h);
611 static void R_gl_setmatrix (void) {
612 glScissor(0, 0, SCRW, SCRH);
613 glViewport(0, 0, SCRW, SCRH);
614 glMatrixMode(GL_PROJECTION);
615 glLoadIdentity();
616 glOrtho(0, SCRW, SCRH, 0, 0, 1);
617 glMatrixMode(GL_MODELVIEW);
618 glLoadIdentity();
621 /* --- Misc --- */
623 static image Z_getspr (const char n[4], int s, int d, char *dir) {
624 int h = F_getsprid(n, s, d);
625 if (dir != NULL) {
626 *dir = (h & 0x8000) ? 1 : 0;
628 return R_gl_getimage(h);
631 static void Z_putch_generic (image img[], int off, int ch) {
632 image *p = NULL;
633 if (ch > 32 && ch < 160) {
634 p = &img[ch - '!'];
636 if (p != NULL) {
637 R_gl_draw_image(p, prx, pry, 0);
638 prx += p->w - 1;
639 } else {
640 prx += off;
644 static void Z_printf_generic (image img[], int off, const char *fmt, va_list ap) {
645 int i;
646 char buf[80];
647 vsprintf(buf, fmt, ap);
648 for (i = 0; buf[i]; ++i) {
649 switch (buf[i]) {
650 case '\n':
651 pry += off + 1;
652 case '\r':
653 prx = 0;
654 break;
655 default:
656 Z_putch_generic(img, off, (byte)buf[i]);
661 static void Z_gotoxy (int x, int y) {
662 prx = x;
663 pry = y;
666 static void Z_printbf (const char *fmt, ...) {
667 va_list a;
668 va_start(a, fmt);
669 Z_printf_generic(bfh, 12, fmt, a);
670 va_end(a);
673 static void Z_printsf (const char *fmt, ...) {
674 va_list a;
675 va_start(a, fmt);
676 Z_printf_generic(sfh, 7, fmt, a);
677 va_end(a);
680 static void Z_printhf (const char *fmt, ...) {
681 int i, c;
682 char buf[80];
683 va_list a;
684 va_start(a, fmt);
685 vsprintf(buf, fmt, a);
686 va_end(a);
687 for (i = 0; buf[i]; ++i) {
688 switch (buf[i]) {
689 case '0':
690 case '1':
691 case '2':
692 case '3':
693 case '4':
694 case '5':
695 case '6':
696 case '7':
697 case '8':
698 case '9':
699 c = buf[i] - '0';
700 break;
701 case '-':
702 c = 10;
703 break;
704 case '%':
705 c = 11;
706 break;
707 case '\n':
708 pry += 19;
709 case '\r':
710 c = -1;
711 prx = 0;
712 break;
713 default:
714 c = -1;
715 break;
717 if (c >= 0) {
718 R_gl_draw_image(&sth[c], prx, pry, 0);
720 prx += 14;
724 /* --- Menu --- */
726 static image *PL_getspr (int s, int d, int msk) {
727 int i = (s - 'A') * 2 + d;
728 return msk ? &plr_msk[i] : &plr_spr[i];
731 static void GM_draw (void) {
732 enum {MENU, MSG}; // copypasted from menu.c!
733 enum {
734 CANCEL, NEWGAME, LOADGAME, SAVEGAME, OPTIONS, QUITGAME, QUIT, ENDGAME, ENDGM,
735 PLR1, PLR2, COOP, DM, VOLUME, GAMMA, LOAD, SAVE, PLCOLOR, PLCEND, MUSIC, INTERP,
736 SVOLM, SVOLP, MVOLM, MVOLP, GAMMAM, GAMMAP, PL1CM, PL1CP, PL2CM, PL2CP
737 }; // copypasted from menu.c!
738 int i, j, k, x, y, cx, cy;
739 image *img;
740 gm_tm += 1;
741 if (mnu != NULL) {
742 cx = SCRW / 2;
743 cy = SCRH / 2;
744 if (mnu->type == MENU) {
745 y = cy - (mnu->n * 16 - 20) / 2;
746 Z_gotoxy(cx - mnu->x, y - 10); Z_printbf("%s", mnu->ttl);
747 for (i = 0; i < mnu->n; i++) {
748 if (mnu->t[i] == LOAD || mnu->t[i] == SAVE) {
749 j = y + i * 16 + 29;
750 R_gl_draw_image(&mslotl, cx - mnu->x, j, 0);
751 for (k = 8; k < 184; k += 8) {
752 R_gl_draw_image(&mslotm, cx - mnu->x + k, j, 0);
754 R_gl_draw_image(&mslotr, cx - mnu->x + 184, j, 0);
755 Z_gotoxy(cx - mnu->x + 4, j - 8);
756 if (input && i == save_mnu.cur) {
757 Z_printsf("%s_", ibuf);
758 } else {
759 Z_printsf("%s", savname[i]);
761 } else {
762 x = mnu->t[i] >= SVOLM ? (mnu->t[i] >= PL1CM ? 50 : 152) : 0;
763 Z_gotoxy(cx - mnu->x + x, y + i * 16 + 20);
764 Z_printbf("%s", mnu->m[i]);
765 switch (mnu->t[i]) {
766 case MUSIC:
767 Z_printbf(" '%s'", g_music);
768 break;
769 case INTERP:
770 Z_printbf("%s", fullscreen ? "ON" : "OFF");
771 break;
772 case PL1CM:
773 case PL1CP:
774 case PL2CM:
775 case PL2CP:
776 img = PL_getspr(*panimp, 0, 0);
777 R_gl_draw_image(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
778 img = PL_getspr(*panimp, 0, 1);
779 R_gl_set_color(pcolortab[(mnu->t[i] == PL1CM) ? p1color : p2color] + PLAYER_COLOR_OFFSET);
780 R_gl_draw_image_color(img, cx - mnu->x + (mnu->t[i] == PL1CM ? 15 : 35), y + i * 16 + 20 + 14, 0);
781 break;
782 case SVOLM:
783 case SVOLP:
784 case MVOLM:
785 case MVOLP:
786 case GAMMAM:
787 case GAMMAP:
788 j = y + i * 16 + 20;
789 R_gl_draw_image(&mbarl, cx - mnu->x, j, 0);
790 for (k = 8; k < 144; k += 8) {
791 R_gl_draw_image(&mbarm, cx - mnu->x + k, j, 0);
793 R_gl_draw_image(&mbarr, cx - mnu->x + 144, j, 0);
794 switch (mnu->t[i]) {
795 case SVOLM:
796 k = snd_vol;
797 break;
798 case MVOLM:
799 k = mus_vol;
800 break;
801 case GAMMAM:
802 k = gamma << 5;
803 break;
804 default:
805 k = 0;
806 break;
808 R_gl_draw_image(&mbaro, cx - mnu->x + 8 + k, j, 0);
809 break;
813 R_gl_draw_image(&msklh[(gm_tm / 6) & 1], cx - mnu->x - 25, y + mnu->cur * 16 + 20 - 8, 0);
814 } else if (mnu->type == MSG) {
815 Z_gotoxy(cx - strlen(mnu->ttl) * 7 / 2, cy - 10); Z_printsf(mnu->ttl);
816 Z_gotoxy(cx - 24, SCRH / 2); Z_printsf("(Y/N)");
817 } else {
818 ERR_fatal("Unknown menu type %i\n", mnu->type);
823 /* --- View --- */
825 static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int fg) {
826 int i, j;
827 assert(minx >= 0 && minx <= FLDW);
828 assert(miny >= 0 && miny <= FLDH);
829 assert(maxx >= 0 && maxx <= FLDW);
830 assert(maxy >= 0 && maxy <= FLDH);
831 for (j = miny; j < maxy; j++) {
832 for (i = minx; i < maxx; i++) {
833 byte id = fld[j * FLDW + i];
834 if (id != 0) {
835 if (walp[id].res < 0) {
836 if (fg) {
837 switch (R_get_special_id(id)) {
838 case 1:
839 glColor4ub(0, 0, 255, 127);
840 break;
841 case 2:
842 glColor4ub(0, 127, 0, 127);
843 break;
844 case 3:
845 glColor4ub(127, 0, 0, 127);
846 break;
847 default:
848 glColor4ub(0, 0, 0, 127);
849 break;
851 glEnable(GL_BLEND);
852 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
853 glDisable(GL_TEXTURE_2D);
854 R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
856 } else {
857 R_gl_draw_image(&walp[id], i * CELW, j * CELH, 0);
864 static void R_draw_dots (void) {
865 int i;
866 glDisable(GL_BLEND);
867 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
868 glDisable(GL_TEXTURE_2D);
869 glBegin(GL_POINTS);
870 for (i = 0; i < MAXDOT; i++) {
871 if (dot[i].t != 0) {
872 R_gl_set_color(dot[i].c);
873 glVertex2i(dot[i].o.x, dot[i].o.y + 1);
876 glEnd();
879 static void R_draw_items (void) {
880 int i, s;
881 for (i = 0; i < MAXITEM; ++i) {
882 s = -1;
883 if (it[i].t && it[i].s >= 0) {
884 switch (it[i].t & 0x7FFF) {
885 case I_ARM1:
886 s = it[i].s / 9 + 18;
887 break;
888 case I_ARM2:
889 s = it[i].s / 9 + 20;
890 break;
891 case I_MEGA:
892 s = it[i].s / 2 + 22;
893 break;
894 case I_INVL:
895 s = it[i].s / 2 + 26;
896 break;
897 case I_SUPER:
898 case I_RTORCH:
899 case I_GTORCH:
900 case I_BTORCH:
901 s = it[i].s / 2 + (it[i].t - I_SUPER) * 4 + 35;
902 break;
903 case I_GOR1: case I_FCAN:
904 s = it[i].s / 2 + (it[i].t - I_GOR1) * 3 + 51;
905 break;
906 case I_AQUA:
907 s = 30;
908 break;
909 case I_SUIT:
910 s = 34;
911 break;
912 case I_KEYR:
913 case I_KEYG:
914 case I_KEYB:
915 s = (it[i].t & 0x7FFF) - I_KEYR + 31;
916 break;
917 case I_GUN2:
918 s = 57;
919 break;
920 default:
921 s = (it[i].t & 0x7FFF) - 1;
924 if (s >= 0) {
925 R_gl_draw_image(&item_spr[s], it[i].o.x, it[i].o.y, item_sprd[s]);
930 static int standspr (player_t *p) {
931 if (p->f & PLF_UP) {
932 return 'X';
933 } else if (p->f & PLF_DOWN) {
934 return 'Z';
935 } else {
936 return 'E';
940 static int wpnspr (player_t *p) {
941 if (p->f & PLF_UP) {
942 return 'C';
943 } else if(p->f & PLF_DOWN) {
944 return 'E';
945 } else {
946 return 'A';
950 static void R_draw_player (player_t *p) {
951 enum {STAND, GO, DIE, SLOP, DEAD, MESS, OUT, FALL}; // copypasted from player.c!
952 static const int wytab[] = {-1, -2, -1, 0};
953 int s = 'A';
954 int w = 0;
955 int wx = 0;
956 int wy = 0;
957 switch (p->st) {
958 case STAND:
959 if (p->f & PLF_FIRE) {
960 s = standspr(p) + 1;
961 w = wpnspr(p) + 1;
962 } else if (p->pain) {
963 s = 'G';
964 w = 'A';
965 wx = p->d ? 2 : -2;
966 wy = 1;
967 } else {
968 s = standspr(p);
969 w = wpnspr(p);
971 break;
972 case DEAD:
973 s = 'N';
974 break;
975 case MESS:
976 s = 'W';
977 break;
978 case GO:
979 if (p->pain) {
980 s = 'G';
981 w = 'A';
982 wx = p->d ? 2 : -2;
983 wy = 1;
984 } else {
985 s = plr_goanim[p->s / 8];
986 w = (p->f & PLF_FIRE) ? 'B' : 'A';
987 wx = p->d ? 2 : -2;
988 wy = 1 + wytab[s - 'A'];
990 break;
991 case DIE:
992 s = plr_dieanim[p->s];
993 break;
994 case SLOP:
995 s = plr_slopanim[p->s];
996 break;
997 case OUT:
998 s = 0;
999 break;
1001 if (p->wpn == 0) {
1002 w = 0;
1004 if (w) {
1005 R_gl_draw_image(&plr_wpn[(int)p->wpn][w -'A'], p->o.x + wx, p->o.y + wy, p->d);
1007 if (s) {
1008 R_gl_draw_image(&plr_spr[(s - 'A') * 2 + p->d], p->o.x, p->o.y, plr_sprd[(s - 'A') * 2 + p->d]);
1009 R_gl_set_color(p->color + PLAYER_COLOR_OFFSET);
1010 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]);
1014 static void R_draw_monsters (void) {
1015 enum {SLEEP, GO, RUN, CLIMB, DIE, DEAD, ATTACK, SHOOT, PAIN, WAIT, REVIVE, RUNOUT}; // copypasted from monster.c!
1016 int i;
1017 for (i = 0; i < MAXMN; i++) {
1018 if (mn[i].t != MN_NONE) {
1019 int x = mn[i].o.x;
1020 int y = mn[i].o.y;
1021 if (mn[i].t < MN__LAST) {
1022 if ((mn[i].t != MN_SOUL && mn[i].t != MN_PAIN) || mn[i].st != DEAD) {
1023 int ap = mn[i].ap[mn[i].ac];
1024 int d = (ap - 'A') * 2 + mn[i].d;
1025 int dir = mn_sprd[mn[i].t - 1][d];
1026 if (mn[i].t == MN_MAN && (ap == 'E' || ap == 'F')) {
1027 R_gl_draw_image(&mn_sgun[ap - 'E'], x, y, mn[i].d);
1029 R_gl_draw_image(&mn_spr[mn[i].t - 1][d], x, y, dir);
1030 if (mn[i].t == MN_MAN) {
1031 R_gl_set_color(MANCOLOR + PLAYER_COLOR_OFFSET);
1032 R_gl_draw_image_color(&mn_man_msk[d], x, y, dir);
1035 if (mn[i].t == MN_VILE && mn[i].st == SHOOT) {
1036 R_gl_draw_image(&mn_fspr[mn[i].ac / 3], mn[i].tx, mn[i].ty, 0);
1038 } else if (mn[i].t == MN_PL_DEAD || mn[i].t == MN_PL_MESS) {
1039 int type = mn[i].t - MN_PL_DEAD;
1040 R_gl_draw_image(&pl_spr[type], x, y, 0);
1041 R_gl_set_color(mn[i].d);
1042 R_gl_draw_image_color(&pl_msk[type], x, y, 0);
1048 static void R_draw_weapons (void) {
1049 enum {NONE, ROCKET, PLASMA, APLASMA, BALL1, BALL2, BALL7, BFGBALL, BFGHIT, MANF, REVF, FIRE}; // copypasted from weapons.c!
1050 int i, s, d, x, y;
1051 for (i = 0; i < MAXWPN; ++i) {
1052 s = -1;
1053 d = 0;
1054 switch (wp[i].t) {
1055 case REVF:
1056 case ROCKET:
1057 d = wp[i].s;
1058 if (d < 2) {
1059 d = wp[i].o.xv > 0 ? 1 : 0;
1060 x = abs(wp[i].o.xv);
1061 y = wp[i].o.yv;
1062 s = 0;
1063 if (y < 0) {
1064 if (-y >= x) {
1065 s = 30;
1067 } else if (y > 0) {
1068 if (y >= x / 2) {
1069 s = 31;
1072 } else {
1073 s = (d - 2) / 2 + 1;
1074 d = 0;
1076 break;
1077 case MANF:
1078 s=wp[i].s;
1079 if (s >= 2) {
1080 s /= 2;
1081 break;
1083 case PLASMA:
1084 case APLASMA:
1085 case BALL1:
1086 case BALL7:
1087 case BALL2:
1088 s = wp[i].s;
1089 if (s >= 2) {
1090 s = s / 2 + 1;
1092 switch (wp[i].t) {
1093 case PLASMA:
1094 s += 4;
1095 break;
1096 case APLASMA:
1097 s += 11;
1098 break;
1099 case BALL1:
1100 s += 32;
1101 break;
1102 case BALL2:
1103 s += 42;
1104 break;
1105 case BALL7:
1106 s += 37;
1107 d = wp[i].o.xv >= 0 ? 1 : 0;
1108 break;
1109 case MANF:
1110 s += 47;
1111 d= wp[i].o.xv>=0 ? 1 : 0;
1112 break;
1114 break;
1115 case BFGBALL:
1116 s = wp[i].s;
1117 if (s >= 2) {
1118 s = s / 2 + 1;
1120 s += 18;
1121 break;
1122 case BFGHIT:
1123 s = wp[i].s / 2 + 26;
1124 break;
1126 if (s >= 0) {
1127 R_gl_draw_image(&wp_spr[s * 2 + d], wp[i].o.x, wp[i].o.y, wp_sprd[s * 2 + d]);
1132 static void R_draw_smoke (void) {
1133 int i, s;
1134 for (i = 0; i < MAXSMOK; ++i) {
1135 if (sm[i].t) {
1136 switch (sm[i].s) {
1137 case 0:
1138 s = sm[i].t;
1139 if (s >= (SMSN - 1) * 3) {
1140 s = 0;
1141 } else {
1142 s = SMSN - 1 - s / 3;
1144 R_gl_draw_image(&smk_spr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1145 break;
1146 case 1:
1147 s = sm[i].t;
1148 if (s >= FLSN - 1) {
1149 s = 0;
1150 } else {
1151 s = FLSN - 1 - s;
1153 R_gl_draw_image(&smk_fspr[s], sm[i].x >> 8, (sm[i].y >> 8) + 1, 0);
1154 break;
1160 static void R_draw_effects (void) {
1161 enum {NONE, TFOG, IFOG, BUBL}; // copypasted from fx.c
1162 int i, s;
1163 for (i = 0; i < MAXFX; ++i) {
1164 switch (fx[i].t) {
1165 case TFOG:
1166 s = fx[i].s / 2;
1167 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1168 break;
1169 case IFOG:
1170 s = fx[i].s / 2 + 10;
1171 R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
1172 break;
1173 case BUBL:
1174 glDisable(GL_BLEND);
1175 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1176 glDisable(GL_TEXTURE_2D);
1177 glBegin(GL_POINTS);
1178 R_gl_set_color(0xC0 + fx[i].s);
1179 glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
1180 glEnd();
1181 break;
1186 static int get_pu_st (int t) {
1187 if (t >= PL_FLASH) {
1188 return 1;
1189 } else if((t / 9) & 1) {
1190 return 0;
1191 } else {
1192 return 1;
1196 static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
1197 glPushMatrix();
1198 R_gl_setclip(x, y, w, h);
1199 glTranslatef(x, y, 0);
1200 if (w_horiz && horiz.n != NULL) {
1201 R_gl_draw_image_ext(&horiz, 0, 0, w, h);
1202 if (sky_type == 2 && lt_time < 0) {
1203 image *tanderbolt = &ltn[lt_type][lt_time < -5 ? 0 : 1];
1204 if (!lt_side) {
1205 R_gl_draw_image(tanderbolt, 0, lt_ypos, 0);
1206 } else {
1207 R_gl_draw_image(tanderbolt, w - 1, lt_ypos, 1);
1210 } else {
1211 glDisable(GL_BLEND);
1212 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1213 glDisable(GL_TEXTURE_2D);
1214 R_gl_set_color(DEFAULT_SKY_COLOR);
1215 R_gl_draw_quad(0, 0, w, h);
1217 int maxx = min((camx + w) / CELW + 1, FLDW);
1218 int maxy = min((camy + h) / CELH + 1, FLDH);
1219 int minx = max((camx - max_wall_width) / CELW, 0);
1220 int miny = max((camy - max_wall_height) / CELH, 0);
1221 glTranslatef(-camx, -camy, 0);
1222 R_draw_fld((byte*)fldb, minx, miny, maxx, maxy, 0);
1223 R_draw_dots();
1224 R_draw_items();
1225 R_draw_player(&pl1);
1226 if (_2pl) {
1227 R_draw_player(&pl2);
1229 R_draw_monsters();
1230 R_draw_weapons();
1231 R_draw_smoke();
1232 R_draw_effects();
1233 R_draw_fld((byte*)fldf, minx, miny, maxx, maxy, 1);
1234 glTranslatef(camx, camy, 0);
1235 if (sky_type == 2 && (lt_time == -4 || lt_time == -2)) {
1236 glColor4ub(255, 255, 255, 255);
1237 glEnable(GL_BLEND);
1238 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1239 glDisable(GL_TEXTURE_2D);
1240 R_gl_draw_quad(0, 0, w, h);
1242 glPopMatrix();
1245 static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
1246 p->looky = min(max(p->looky, -SCRH / 4), SCRH / 4); // TODO remove writeback
1247 int st = stone.w;
1248 int cw = w - st;
1249 int cx = min(max(p->o.x, cw / 2), FLDW * CELW - cw / 2);
1250 int cy = min(max(p->o.y - 12 + p->looky, h / 2), FLDH * CELH - h / 2);
1251 int camx = max(cx - cw / 2, 0);
1252 int camy = max(cy - h / 2, 0);
1253 glPushMatrix();
1254 R_draw_view(x, y + 1, cw, h - 2, camx, camy);
1255 glTranslatef(x, y, 0);
1256 if (p->invl) {
1257 if (get_pu_st(p->invl)) {
1258 glEnable(GL_BLEND);
1259 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
1260 glDisable(GL_TEXTURE_2D);
1261 glColor4ub(191, 191, 191, 255);
1262 R_gl_draw_quad(0, 0, cw, h);
1264 } else {
1265 if (p->suit && get_pu_st(p->suit)) {
1266 glEnable(GL_BLEND);
1267 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1268 glDisable(GL_TEXTURE_2D);
1269 glColor4ub(0, 255, 0, 192);
1270 R_gl_draw_quad(0, 0, cw, h);
1272 int f = min(max(p->pain * 3, 0), 255);
1273 if (f > 0) {
1274 glEnable(GL_BLEND);
1275 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1276 glDisable(GL_TEXTURE_2D);
1277 glColor4ub(255, 0, 0, f);
1278 R_gl_draw_quad(0, 0, cw, h);
1281 R_gl_setclip(x, y, w, h);
1282 glTranslatef(-x + cw, 0, 0);
1283 R_gl_draw_image(&stone, 0, 0, 0);
1284 int i = stone.h;
1285 while (i < h) {
1286 R_gl_draw_image(&stone2, 0, i, 0);
1287 i += stone2.h;
1289 if (p->drawst & PL_DRAWAIR) {
1290 if (p->air < PL_AIR) {
1291 int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
1292 glDisable(GL_BLEND);
1293 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1294 glDisable(GL_TEXTURE_2D);
1295 R_gl_set_color(0xC8);
1296 R_gl_draw_quad(10, 49, a, 2);
1299 if (p->drawst & PL_DRAWLIFE) {
1300 Z_gotoxy(10, 7);
1301 Z_printhf("%3d%%", p->life);
1303 if (p->drawst & PL_DRAWARMOR) {
1304 Z_gotoxy(10, 7 + 19);
1305 Z_printhf("%3d%%", p->armor);
1307 if (p->drawst & PL_DRAWWPN) {
1308 switch(p->wpn) {
1309 case 2:
1310 case 5:
1311 i = p->ammo;
1312 break;
1313 case 3:
1314 case 4:
1315 case 9:
1316 i = p->shel;
1317 break;
1318 case 6:
1319 i = p->rock;
1320 break;
1321 case 7:
1322 case 8:
1323 i = p->cell;
1324 break;
1325 case 10:
1326 i = p->fuel;
1327 break;
1328 default:
1329 i = -1;
1330 break;
1332 // weapon
1333 if (p->wpn >= 0) {
1334 R_gl_draw_image(&sth[12 + p->wpn], st - 88, 58 + 19, 0);
1336 // ammo
1337 if (p->wpn >= 2) {
1338 Z_gotoxy(st - 10 - 5 * 14, 58 + 2);
1339 Z_printhf("%5d", i);
1342 if (p->drawst & PL_DRAWFRAG && g_dm) {
1343 Z_gotoxy(st - 5 - 5 * 14, 77 + 5);
1344 Z_printhf("%5d", p->frag);
1346 if (p->drawst & PL_DRAWKEYS) {
1347 int x, k, n;
1348 for (k = p->keys >> 4, n = 0, x = st - 75; n < 3; n++, k >>= 1, x += 9) {
1349 if (k & 1) {
1350 R_gl_draw_image(&keys[n], x, 91, 0);
1354 if (p->drawst & PL_DRAWLIVES && !_2pl) {
1355 Z_gotoxy(st - 35, 17);
1356 Z_printhf("%d", p->lives);
1358 glPopMatrix();
1361 /* --- Game --- */
1363 static void pl_info (player_t *p, int x, int y) {
1364 dword t = p->kills * 10920 / g_time;
1365 Z_gotoxy(x + 25, y); Z_printbf("KILLS");
1366 Z_gotoxy(x + 25, y + 15); Z_printbf("KPM");
1367 Z_gotoxy(x + 25, y + 30); Z_printbf("SECRETS %u / %u", p->secrets, sw_secrets);
1368 Z_gotoxy(x + 255, y); Z_printbf("%u", p->kills);
1369 Z_gotoxy(x + 255, y + 15); Z_printbf("%u.%u", t / 10, t % 10);
1372 static void R_draw_intermission (void) {
1373 int cx = SCRW / 2;
1374 word hr, mn, sc, h;
1375 Z_gotoxy(cx - 14*12/2, 20);
1376 Z_printbf("LEVEL COMPLETE");
1377 Z_calc_time(g_time, &hr, &mn, &sc);
1378 Z_gotoxy(cx - 12*12/2, 40);
1379 Z_printbf("TIME %u:%02u:%02u", hr, mn, sc);
1380 h = 40 + SCRH / 10;
1381 if (_2pl) {
1382 Z_gotoxy(cx - 10*12/2, h);
1383 Z_printbf("PLAYER ONE");
1384 h += 20;
1386 pl_info(&pl1, cx - 160, h);
1387 if (_2pl) {
1388 h += 30 + SCRH / 10;
1389 Z_gotoxy(cx - 10*12/2, h);
1390 Z_printbf("PLAYER TWO");
1391 h += 20;
1392 pl_info(&pl2, cx - 160, h);
1396 static void W_act (void) {
1397 int i, a;
1398 if (g_time % 3 == 0) {
1399 for (i = 1; i < max_textures; i++) {
1400 a = walani[i];
1401 if (a != 0) {
1402 anic[a]++;
1403 if (anip[a][anic[a]].res == -1) {
1404 anic[a] = 0;
1406 walp[i] = anip[a][anic[a]];
1412 void R_draw (void) {
1413 W_act();
1414 glClearColor(0, 0, 0, 1);
1415 glClear(GL_COLOR_BUFFER_BIT);
1416 glEnable(GL_SCISSOR_TEST);
1417 R_gl_setmatrix();
1418 switch (g_st) {
1419 case GS_ENDANIM:
1420 case GS_END2ANIM:
1421 case GS_DARKEN:
1422 case GS_BVIDEO:
1423 case GS_EVIDEO:
1424 case GS_END3ANIM:
1425 break;
1426 case GS_TITLE:
1427 R_gl_draw_image_ext(&scrnh[0], 0, 0, SCRW, SCRH);
1428 break;
1429 case GS_INTER:
1430 R_gl_draw_image_ext(&scrnh[1], 0, 0, SCRW, SCRH);
1431 R_draw_intermission();
1432 break;
1433 case GS_ENDSCR:
1434 R_gl_draw_image_ext(&scrnh[2], 0, 0, SCRW, SCRH);
1435 break;
1436 case GS_GAME:
1437 if (_2pl) {
1438 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH / 2);
1439 R_draw_player_view(&pl2, 0, SCRH / 2, SCRW, SCRH / 2);
1440 } else {
1441 R_draw_player_view(&pl1, 0, 0, SCRW, SCRH);
1443 R_gl_setclip(0, 0, SCRW, SCRH);
1444 break;
1446 GM_draw();
1447 Y_swap_buffers();
1450 void R_alloc (void) {
1451 char s[10];
1452 int i, j, n;
1453 logo("R_alloc: load graphics\n");
1454 /* Game */
1455 scrnh[0] = R_gl_loadimage("TITLEPIC");
1456 scrnh[1] = R_gl_loadimage("INTERPIC");
1457 scrnh[2] = R_gl_loadimage("ENDPIC");
1458 for (i = 0; i < 2; i++) {
1459 sprintf(s, "LTN%c", '1' + i);
1460 for (j = 0; j < 2; j++) {
1461 ltn[i][j] = Z_getspr(s, j, 0, NULL);
1464 /* Smoke */
1465 for (i = 0; i < SMSN; i++) {
1466 smk_spr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_smoke_spr);
1468 for (i = 0; i < FLSN; i++) {
1469 smk_fspr[i] = R_gl_get_special_spr("SMOK", i, 0, &R_extract_flame_spr);
1471 /* Effects */
1472 for (i = 0; i < 10; i++) {
1473 fx_spr[i] = Z_getspr("TFOG", i, 0, fx_sprd + i);
1475 for (; i < 15; i++) {
1476 fx_spr[i] = Z_getspr("IFOG", i - 10, 0, fx_sprd + i);
1478 /* Weapons */
1479 for (i = 0; i < 4; i++) {
1480 wp_spr[i * 2] = Z_getspr("MISL", i, 1, wp_sprd + i * 2);
1481 wp_spr[i * 2 + 1] = Z_getspr("MISL", i, 2, wp_sprd + i * 2 + 1);
1483 for (; i < 6; i++) {
1484 wp_spr[i * 2] = Z_getspr("PLSS", i - 4, 1, wp_sprd + i * 2);
1485 wp_spr[i * 2 + 1] = Z_getspr("PLSS", i - 4, 2, wp_sprd + i * 2 + 1);
1487 for (; i < 11; i++) {
1488 wp_spr[i * 2] = Z_getspr("PLSE", i - 6, 1, wp_sprd + i * 2);
1489 wp_spr[i * 2 + 1] = Z_getspr("PLSE", i - 6, 2, wp_sprd + i * 2 + 1);
1491 for (; i < 13; i++) {
1492 wp_spr[i * 2] = Z_getspr("APLS", i - 11, 1, wp_sprd + i * 2);
1493 wp_spr[i * 2 + 1] = Z_getspr("APLS", i - 11, 2, wp_sprd + i * 2 + 1);
1495 for (; i < 18; i++) {
1496 wp_spr[i * 2] = Z_getspr("APBX", i - 13, 1, wp_sprd + i * 2);
1497 wp_spr[i * 2 + 1] = Z_getspr("APBX", i - 13, 2, wp_sprd + i * 2 + 1);
1499 for(; i < 20; i++) {
1500 wp_spr[i * 2] = Z_getspr("BFS1", i - 18, 1, wp_sprd + i * 2);
1501 wp_spr[i * 2 + 1] = Z_getspr("BFS1", i - 18, 2, wp_sprd + i * 2 + 1);
1503 for (; i < 26; i++) {
1504 wp_spr[i * 2] = Z_getspr("BFE1", i - 20, 1, wp_sprd + i * 2);
1505 wp_spr[i * 2 + 1] = Z_getspr("BFE1", i - 20, 2, wp_sprd + i * 2 + 1);
1507 for (; i < 30; i++) {
1508 wp_spr[i * 2] = Z_getspr("BFE2", i - 26, 1, wp_sprd + i * 2);
1509 wp_spr[i * 2 + 1] = Z_getspr("BFE2", i - 26, 2, wp_sprd + i * 2 + 1);
1511 for (; i < 32; i++) {
1512 wp_spr[i * 2] = Z_getspr("MISL", i - 30 + 4, 1, wp_sprd + i * 2);
1513 wp_spr[i * 2 + 1] = Z_getspr("MISL", i - 30 + 4, 2, wp_sprd + i * 2 + 1);
1515 for (; i < 37; i++) {
1516 wp_spr[i * 2] = Z_getspr("BAL1", i - 32, 1, wp_sprd + i * 2);
1517 wp_spr[i * 2 + 1] = Z_getspr("BAL1", i - 32, 2, wp_sprd + i * 2 + 1);
1519 for (; i < 42; i++) {
1520 wp_spr[i * 2] = Z_getspr("BAL7", i - 37, 1, wp_sprd + i * 2);
1521 wp_spr[i * 2 + 1] = Z_getspr("BAL7", i - 37, 2, wp_sprd + i * 2 + 1);
1523 for (; i < 47; i++) {
1524 wp_spr[i * 2] = Z_getspr("BAL2", i - 42, 1, wp_sprd + i * 2);
1525 wp_spr[i * 2 + 1] = Z_getspr("BAL2", i - 42, 2, wp_sprd + i * 2 + 1);
1527 for (; i < 49; i++) {
1528 wp_spr[i * 2] = Z_getspr("MANF", i - 47, 1, wp_sprd + i * 2);
1529 wp_spr[i * 2 + 1] = Z_getspr("MANF", i - 47, 2, wp_sprd + i * 2 + 1);
1531 /* Items */
1532 static const char snm[18][4] = {
1533 "CLIP", "SHEL", "ROCK", "CELL", "AMMO", "SBOX", "BROK", "CELP",
1534 "STIM", "MEDI", "BPAK",
1535 "CSAW", "SHOT", "SGN2", "MGUN", "LAUN", "PLAS", "BFUG"
1536 };
1537 static const char n4[4][4] = {
1538 "SOUL", "SMRT", "SMGT", "SMBT"
1539 };
1540 static const char n3[2][4] = {
1541 "GOR1", "FCAN"
1542 };
1543 for (i = 0; i < 18; i++) {
1544 item_spr[i] = Z_getspr(snm[i], 0, 0, item_sprd + i);
1546 for (; i < 20; i++) {
1547 item_spr[i] = Z_getspr("ARM1", i - 18, 0, item_sprd + i);
1548 item_spr[i + 2] = Z_getspr("ARM2", i - 18, 0, item_sprd + i);
1550 i+=2;
1551 for (; i < 26; i++) {
1552 item_spr[i] = Z_getspr("MEGA", i - 22, 0, item_sprd + i);
1554 for (; i < 30; i++) {
1555 item_spr[i] = Z_getspr("PINV", i - 26, 0, item_sprd + i);
1557 item_spr[30] = Z_getspr("AQUA", 0, 0, item_sprd + 30);
1558 item_spr[31] = Z_getspr("KEYR", 0, 0, item_sprd + 31);
1559 item_spr[32] = Z_getspr("KEYG", 0, 0, item_sprd + 32);
1560 item_spr[33] = Z_getspr("KEYB", 0, 0, item_sprd + 33);
1561 item_spr[34] = Z_getspr("SUIT", 0, 0, item_sprd + 34);
1562 for (n = 35, j = 0; j < 4; j++) {
1563 for (i = 0; i < 4; i++, n++) {
1564 item_spr[n] = Z_getspr(n4[j], i, 0, item_sprd + n);
1567 for (j = 0; j < 2; j++) {
1568 for (i = 0; i < 3; i++, n++) {
1569 item_spr[n] = Z_getspr(n3[j], i, 0, item_sprd + n);
1572 item_spr[57] = Z_getspr("GUN2", 0, 0, item_sprd + 57);
1573 /* Player */
1574 for (i = 0; i < 27; i++) {
1575 plr_spr[i * 2] = Z_getspr("PLAY", i, 1, plr_sprd + i * 2);
1576 plr_msk[i * 2] = R_gl_get_special_spr("PLAY", i, 1, &R_extract_mask_spr);
1577 plr_spr[i * 2 + 1] = Z_getspr("PLAY", i, 2, plr_sprd + i * 2 + 1);
1578 plr_msk[i * 2 + 1] = R_gl_get_special_spr("PLAY", i, 2, &R_extract_mask_spr);
1580 strncpy(s, "PWPx", 4);
1581 for (i = 1; i < 11; i++) {
1582 s[3] = (i < 10 ? '0' : 'A' - 10) + i;
1583 for (j = 0; j < 6; j++) {
1584 plr_wpn[i][j] = Z_getspr(s, j, 1, NULL);
1587 /* Monsters */
1588 static const char msn[MN_TN][4] = {
1589 "SARG", "TROO", "POSS", "SPOS", "CYBR", "CPOS", "BOSS", "BOS2", "HEAD", "SKUL",
1590 "PAIN", "SPID", "BSPI", "FATT", "SKEL", "VILE", "FISH", "BAR1", "ROBO", "PLAY"
1591 };
1592 static const int mms[MN_TN] = {
1593 14*2, 21*2, 21*2, 21*2, 16*2, 20*2, 15*2, 15*2, 12*2, 11*2,
1594 13*2, 19*2, 16*2, 20*2, 17*2, 29*2, 6*2, 2*2, 17*2, 23*2
1595 };
1596 mn_sgun[0] = Z_getspr("PWP4", 0, 1, NULL);
1597 mn_sgun[1] = Z_getspr("PWP4", 1, 1, NULL);
1598 for (j = 0; j < MN_TN; j++) {
1599 for (i = 0; i < mms[j]; i++) {
1600 mn_spr[j][i] = Z_getspr(msn[j], i / 2, (i & 1) + 1, &mn_sprd[j][i]);
1601 if (j == MN_MAN - 1) {
1602 mn_man_msk[i] = R_gl_get_special_spr(msn[j], i / 2, (i & 1) + 1, &R_extract_mask_spr);
1605 if (j == MN_BARREL - 1) {
1606 for (i = 4; i < 14; i++) {
1607 mn_spr[j][i] = Z_getspr("BEXP", i / 2 - 2, (i & 1) + 1, &mn_sprd[j][i]);
1611 for (i = 0; i < 8; i++) {
1612 mn_fspr[i] = Z_getspr("FIRE", i, 0, NULL);
1614 pl_spr[0] = Z_getspr("PLAY", 'N' - 'A', 0, NULL);
1615 pl_msk[0] = R_gl_get_special_spr("PLAY", 'N' - 'A', 0, &R_extract_mask_spr);
1616 pl_spr[1] = Z_getspr("PLAY", 'W' - 'A', 0, NULL);
1617 pl_msk[1] = R_gl_get_special_spr("PLAY", 'W' - 'A', 0, &R_extract_mask_spr);
1618 /* Misc */
1619 static const char mnm[22][8]={
1620 "STTNUM0", "STTNUM1", "STTNUM2", "STTNUM3", "STTNUM4",
1621 "STTNUM5", "STTNUM6", "STTNUM7", "STTNUM8", "STTNUM9",
1622 "STTMINUS", "STTPRCNT",
1623 "FISTA0", "CSAWA0", "PISTA0", "SHOTA0", "SGN2A0", "MGUNA0", "LAUNA0",
1624 "PLASA0", "BFUGA0", "GUN2A0"
1625 };
1626 stone = R_gl_loadimage("STONE");
1627 stone2 = R_gl_loadimage("STONE2");
1628 keys[0] = R_gl_loadimage("KEYRA0");
1629 keys[1] = R_gl_loadimage("KEYGA0");
1630 keys[2] = R_gl_loadimage("KEYBA0");
1631 for (i = 0; i < 22; i++) {
1632 sth[i] = R_gl_loadimage(mnm[i]);
1634 strcpy(s, "STBF_*");
1635 for (i = '!'; i < 160; i++) {
1636 s[5] = i;
1637 bfh[i - '!'] = R_gl_getimage(F_findres(s));
1639 for (i = '!'; i < 160; i++) {
1640 sprintf(s, "STCFN%03d", i);
1641 sfh[i - '!'] = R_gl_getimage(F_findres(s));
1643 strcpy(s, "WINUM*");
1644 for (i = '0'; i <= '9'; i++) {
1645 s[5] = i;
1646 bfh[i - '!'] = R_gl_loadimage(s);
1648 bfh[':' - '!'] = R_gl_loadimage("WICOLON");
1649 // menu
1650 msklh[0] = R_gl_loadimage("M_SKULL1");
1651 msklh[1] = R_gl_loadimage("M_SKULL2");
1652 mbarl = R_gl_loadimage("M_THERML");
1653 mbarm = R_gl_loadimage("M_THERMM");
1654 mbarr = R_gl_loadimage("M_THERMR");
1655 mbaro = R_gl_loadimage("M_THERMO");
1656 mslotl = R_gl_loadimage("M_LSLEFT");
1657 mslotm = R_gl_loadimage("M_LSCNTR");
1658 mslotr = R_gl_loadimage("M_LSRGHT");
1659 // walls
1660 for (i = 1; i < ANIT; i++) {
1661 for (j = 0; j < 5 && anm[i - 1][j]; j++) {
1662 anip[i][j] = R_gl_loadimage(anm[i - 1][j]);
1664 for(; j < 5; j++) {
1665 anip[i][j] = (image) {
1666 .n = NULL,
1667 .w = 8,
1668 .h = 8,
1669 .res = -1,
1670 };
1675 static void R_reload_textures (void);
1677 void R_set_videomode (int w, int h, int fullscreen) {
1678 assert(w > 0);
1679 assert(h > 0);
1680 int was = Y_videomode_setted();
1681 int flags = SYSTEM_USE_OPENGL;
1682 if (fullscreen) {
1683 flags |= SYSTEM_USE_FULLSCREEN;
1685 if (root != NULL) {
1686 R_cache_free(root, 0);
1687 root = NULL;
1689 int res = Y_set_videomode(w, h, flags);
1690 if (res == 0) {
1691 if (was == 0) {
1692 ERR_failinit("Unable to set video mode\n");
1694 } else {
1695 Y_get_videomode(&SCRW, &SCRH);
1696 root = R_cache_new();
1697 assert(root);
1698 R_alloc();
1699 R_reload_textures();
1703 void R_toggle_fullscreen (void) {
1704 R_cache_free(root, 0);
1705 Y_set_fullscreen(!Y_get_fullscreen());
1706 fullscreen = Y_get_fullscreen();
1707 Y_get_videomode(&SCRW, &SCRH);
1708 root = R_cache_new();
1709 assert(root);
1710 R_alloc();
1711 R_reload_textures();
1714 void R_init (void) {
1715 logo("R_init: intialize opengl render\n");
1716 R_init_playpal();
1717 R_set_videomode(SCRW, SCRH, fullscreen);
1720 void R_done (void) {
1721 R_cache_free(root, 1);
1722 Y_unset_videomode();
1723 root = NULL;
1726 void R_setgamma (int g) {
1727 gamma = g < 0 ? 0 : (g > 4 ? 4 : g);
1730 int R_getgamma (void) {
1731 return gamma;
1734 void R_get_name (int n, char s[8]) {
1735 assert(n >= 0 && n < 256);
1736 if (walp[n].res == -1) {
1737 memset(s, 0, 8);
1738 } else if (walp[n].res == -2) {
1739 memcpy(s, "_WATER_", 8);
1740 s[7] = '0' + (intptr_t)walp[n].n - 1;
1741 } else if (walani[n] > 0) {
1742 memcpy(s, anm[walani[n] - 1][0], 8);
1743 } else {
1744 F_getresname(s, walp[n].res & 0x7FFF);
1748 static short getani (char n[8]) {
1749 short i = 0;
1750 while (i < ANIT - 1 && strncasecmp(n, anm[i][0], 8) != 0) {
1751 i++;
1753 return i < ANIT - 1 ? i + 1 : 0;
1756 int R_get_special_id (int n) {
1757 assert(n >= 0 && n <= 256);
1758 return walp[n].res == -2 ? (intptr_t)walp[n].n : -1;
1761 static void R_reload_textures (void) {
1762 int i;
1763 char s[8];
1764 for (i = 0; i < max_textures; i++) {
1765 R_get_name(i, s);
1766 if (walp[i].res >= 0) {
1767 walp[i] = R_gl_getimage(walp[i].res);
1770 if (horiz.n) {
1771 horiz = R_gl_getimage(horiz.res);
1775 void R_begin_load (void) {
1776 int i;
1777 for (i = 0; i < 256; i++) {
1778 if (walp[i].n != NULL && walp[i].res >= 0 && walani[i] == 0) {
1779 R_gl_free_image(&walp[i]);
1781 memset(&walp[i], 0, sizeof(image));
1782 walp[i].res = -1;
1783 walswp[i] = i;
1784 walani[i] = 0;
1786 memset(anic, 0, sizeof(anic));
1787 max_wall_width = 0;
1788 max_wall_height = 0;
1789 max_textures = 1;
1792 void R_load (char s[8], int f) {
1793 assert(max_textures < 256);
1794 if (!s[0]) {
1795 walp[max_textures] = (image) {
1796 .n = NULL,
1797 .x = 0,
1798 .y = 0,
1799 .w = 0,
1800 .h = 0,
1801 .res = -1,
1802 };
1803 } else if (strncasecmp(s, "_WATER_", 7) == 0) {
1804 walp[max_textures] = (image) {
1805 .n = (void*)((intptr_t)s[7] - '0' + 1),
1806 .x = 0,
1807 .y = 0,
1808 .w = 8,
1809 .h = 8,
1810 .res = -2,
1811 };
1812 } else {
1813 walp[max_textures] = R_gl_loadimage(s);
1814 if (f) {
1815 walp[max_textures].res |= 0x8000;
1817 if (s[0] == 'S' && s[1] == 'W' && s[4] == '_') {
1818 walswp[max_textures] = 0;
1820 walani[max_textures] = getani(s);
1822 max_wall_width = max(max_wall_width, walp[max_textures].w);
1823 max_wall_height = max(max_wall_height, walp[max_textures].h);
1824 max_textures++;
1827 void R_end_load (void) {
1828 int i, j, k, g;
1829 char s[8];
1830 j = max_textures;
1831 for (i = 1; i < 256 && j < 256; i++) {
1832 if (walswp[i] == 0) {
1833 R_get_name(i, s);
1834 s[5] ^= 1;
1835 g = F_getresid(s) | (walp[i].res & 0x8000);
1836 k = 1;
1837 while (k < 256 && walp[k].res != g) {
1838 k += 1;
1840 if (k >= 256) {
1841 k = j;
1842 j += 1;
1843 max_textures += 1;
1844 walp[k] = R_gl_getimage(g);
1845 walf[k] = g & 0x8000 ? 1 : 0;
1847 walswp[i] = k;
1848 walswp[k] = i;
1853 void R_loadsky (int sky) {
1854 char s[6];
1855 strcpy(s, "RSKYx");
1856 s[4] = '0' + sky;
1857 R_gl_free_image(&horiz);
1858 horiz = R_gl_loadimage(s);