1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1998-2000 by DooM Legacy Team.
4 // Copyright (C) 1999-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file hw_md2.c
11 /// \brief 3D Model Handling
12 /// Inspired from md2.c by Mete Ciragan (mete@swissquake.ch)
13
14 #ifdef __GNUC__
15 #include <unistd.h>
16 #endif
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <math.h>
21
22 #include "../d_main.h"
23 #include "../doomdef.h"
24 #include "../doomstat.h"
25 #include "../fastcmp.h"
26
27 #ifdef HWRENDER
28 #include "hw_drv.h"
29 #include "hw_light.h"
30 #include "hw_md2.h"
31 #include "../d_main.h"
32 #include "../r_bsp.h"
33 #include "../r_main.h"
34 #include "../m_misc.h"
35 #include "../w_wad.h"
36 #include "../z_zone.h"
37 #include "../r_things.h"
38 #include "../r_draw.h"
39 #include "../p_tick.h"
40 #include "hw_model.h"
41
42 #include "hw_main.h"
43 #include "../v_video.h"
44 #ifdef HAVE_PNG
45
46 #ifndef _MSC_VER
47 #ifndef _LARGEFILE64_SOURCE
48 #define _LARGEFILE64_SOURCE
49 #endif
50 #endif
51
52 #ifndef _LFS64_LARGEFILE
53 #define _LFS64_LARGEFILE
54 #endif
55
56 #ifndef _FILE_OFFSET_BITS
57 #define _FILE_OFFSET_BITS 0
58 #endif
59
60 #include "png.h"
61 #ifndef PNG_READ_SUPPORTED
62 #undef HAVE_PNG
63 #endif
64 #if PNG_LIBPNG_VER < 100207
65 //#undef HAVE_PNG
66 #endif
67 #endif
68
69 #ifndef errno
70 #include "errno.h"
71 #endif
72
73 md2_t md2_models[NUMSPRITES];
74 md2_t md2_playermodels[MAXSKINS];
75
76
77 /*
78 * free model
79 */
80 #if 0
81 static void md2_freeModel (model_t *model)
82 {
83 UnloadModel(model);
84 }
85 #endif
86
87
88 //
89 // load model
90 //
91 // Hurdler: the current path is the Legacy.exe path
md2_readModel(const char * filename)92 static model_t *md2_readModel(const char *filename)
93 {
94 //Filename checking fixed ~Monster Iestyn and Golden
95 if (FIL_FileExists(va("%s"PATHSEP"%s", srb2home, filename)))
96 return LoadModel(va("%s"PATHSEP"%s", srb2home, filename), PU_STATIC);
97
98 if (FIL_FileExists(va("%s"PATHSEP"%s", srb2path, filename)))
99 return LoadModel(va("%s"PATHSEP"%s", srb2path, filename), PU_STATIC);
100
101 return NULL;
102 }
103
md2_printModelInfo(model_t * model)104 static inline void md2_printModelInfo (model_t *model)
105 {
106 #if 0
107 INT32 i;
108
109 CONS_Debug(DBG_RENDER, "magic:\t\t\t%c%c%c%c\n", model->header.magic>>24,
110 (model->header.magic>>16)&0xff,
111 (model->header.magic>>8)&0xff,
112 model->header.magic&0xff);
113 CONS_Debug(DBG_RENDER, "version:\t\t%d\n", model->header.version);
114 CONS_Debug(DBG_RENDER, "skinWidth:\t\t%d\n", model->header.skinWidth);
115 CONS_Debug(DBG_RENDER, "skinHeight:\t\t%d\n", model->header.skinHeight);
116 CONS_Debug(DBG_RENDER, "frameSize:\t\t%d\n", model->header.frameSize);
117 CONS_Debug(DBG_RENDER, "numSkins:\t\t%d\n", model->header.numSkins);
118 CONS_Debug(DBG_RENDER, "numVertices:\t\t%d\n", model->header.numVertices);
119 CONS_Debug(DBG_RENDER, "numTexCoords:\t\t%d\n", model->header.numTexCoords);
120 CONS_Debug(DBG_RENDER, "numTriangles:\t\t%d\n", model->header.numTriangles);
121 CONS_Debug(DBG_RENDER, "numGlCommands:\t\t%d\n", model->header.numGlCommands);
122 CONS_Debug(DBG_RENDER, "numFrames:\t\t%d\n", model->header.numFrames);
123 CONS_Debug(DBG_RENDER, "offsetSkins:\t\t%d\n", model->header.offsetSkins);
124 CONS_Debug(DBG_RENDER, "offsetTexCoords:\t%d\n", model->header.offsetTexCoords);
125 CONS_Debug(DBG_RENDER, "offsetTriangles:\t%d\n", model->header.offsetTriangles);
126 CONS_Debug(DBG_RENDER, "offsetFrames:\t\t%d\n", model->header.offsetFrames);
127 CONS_Debug(DBG_RENDER, "offsetGlCommands:\t%d\n", model->header.offsetGlCommands);
128 CONS_Debug(DBG_RENDER, "offsetEnd:\t\t%d\n", model->header.offsetEnd);
129
130 for (i = 0; i < model->header.numFrames; i++)
131 CONS_Debug(DBG_RENDER, "%s ", model->frames[i].name);
132 CONS_Debug(DBG_RENDER, "\n");
133 #else
134 (void)model;
135 #endif
136 }
137
138 #ifdef HAVE_PNG
PNG_error(png_structp PNG,png_const_charp pngtext)139 static void PNG_error(png_structp PNG, png_const_charp pngtext)
140 {
141 CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext);
142 //I_Error("libpng error at %p: %s", PNG, pngtext);
143 }
144
PNG_warn(png_structp PNG,png_const_charp pngtext)145 static void PNG_warn(png_structp PNG, png_const_charp pngtext)
146 {
147 CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext);
148 }
149
PNG_Load(const char * filename,int * w,int * h,GLPatch_t * grpatch)150 static GLTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_t *grpatch)
151 {
152 png_structp png_ptr;
153 png_infop png_info_ptr;
154 png_uint_32 width, height;
155 int bit_depth, color_type;
156 #ifdef PNG_SETJMP_SUPPORTED
157 #ifdef USE_FAR_KEYWORD
158 jmp_buf jmpbuf;
159 #endif
160 #endif
161 volatile png_FILE_p png_FILE;
162 //Filename checking fixed ~Monster Iestyn and Golden
163 char *pngfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2home, filename);
164
165 FIL_ForceExtension(pngfilename, ".png");
166 png_FILE = fopen(pngfilename, "rb");
167 if (!png_FILE)
168 {
169 pngfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2path, filename);
170 FIL_ForceExtension(pngfilename, ".png");
171 png_FILE = fopen(pngfilename, "rb");
172 //CONS_Debug(DBG_RENDER, "M_SavePNG: Error on opening %s for loading\n", filename);
173 if (!png_FILE)
174 return 0;
175 }
176
177 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
178 PNG_error, PNG_warn);
179 if (!png_ptr)
180 {
181 CONS_Debug(DBG_RENDER, "PNG_Load: Error on initialize libpng\n");
182 fclose(png_FILE);
183 return 0;
184 }
185
186 png_info_ptr = png_create_info_struct(png_ptr);
187 if (!png_info_ptr)
188 {
189 CONS_Debug(DBG_RENDER, "PNG_Load: Error on allocate for libpng\n");
190 png_destroy_read_struct(&png_ptr, NULL, NULL);
191 fclose(png_FILE);
192 return 0;
193 }
194
195 #ifdef USE_FAR_KEYWORD
196 if (setjmp(jmpbuf))
197 #else
198 if (setjmp(png_jmpbuf(png_ptr)))
199 #endif
200 {
201 //CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename);
202 png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
203 fclose(png_FILE);
204 Z_Free(grpatch->mipmap->data);
205 return 0;
206 }
207 #ifdef USE_FAR_KEYWORD
208 png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf);
209 #endif
210
211 png_init_io(png_ptr, png_FILE);
212
213 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
214 png_set_user_limits(png_ptr, 2048, 2048);
215 #endif
216
217 png_read_info(png_ptr, png_info_ptr);
218
219 png_get_IHDR(png_ptr, png_info_ptr, &width, &height, &bit_depth, &color_type,
220 NULL, NULL, NULL);
221
222 if (bit_depth == 16)
223 png_set_strip_16(png_ptr);
224
225 if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
226 png_set_gray_to_rgb(png_ptr);
227 else if (color_type == PNG_COLOR_TYPE_PALETTE)
228 png_set_palette_to_rgb(png_ptr);
229
230 if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS))
231 png_set_tRNS_to_alpha(png_ptr);
232 else if (color_type != PNG_COLOR_TYPE_RGB_ALPHA && color_type != PNG_COLOR_TYPE_GRAY_ALPHA)
233 {
234 #if PNG_LIBPNG_VER < 10207
235 png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
236 #else
237 png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
238 #endif
239 }
240
241 png_read_update_info(png_ptr, png_info_ptr);
242
243 {
244 png_uint_32 i, pitch = png_get_rowbytes(png_ptr, png_info_ptr);
245 png_bytep PNG_image = Z_Malloc(pitch*height, PU_HWRMODELTEXTURE, &grpatch->mipmap->data);
246 png_bytepp row_pointers = png_malloc(png_ptr, height * sizeof (png_bytep));
247 for (i = 0; i < height; i++)
248 row_pointers[i] = PNG_image + i*pitch;
249 png_read_image(png_ptr, row_pointers);
250 png_free(png_ptr, (png_voidp)row_pointers);
251 }
252
253 png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
254
255 fclose(png_FILE);
256 *w = (int)width;
257 *h = (int)height;
258 return GL_TEXFMT_RGBA;
259 }
260 #endif
261
262 typedef struct
263 {
264 UINT8 manufacturer;
265 UINT8 version;
266 UINT8 encoding;
267 UINT8 bitsPerPixel;
268 INT16 xmin;
269 INT16 ymin;
270 INT16 xmax;
271 INT16 ymax;
272 INT16 hDpi;
273 INT16 vDpi;
274 UINT8 colorMap[48];
275 UINT8 reserved;
276 UINT8 numPlanes;
277 INT16 bytesPerLine;
278 INT16 paletteInfo;
279 INT16 hScreenSize;
280 INT16 vScreenSize;
281 UINT8 filler[54];
282 } PcxHeader;
283
PCX_Load(const char * filename,int * w,int * h,GLPatch_t * grpatch)284 static GLTextureFormat_t PCX_Load(const char *filename, int *w, int *h,
285 GLPatch_t *grpatch)
286 {
287 PcxHeader header;
288 #define PALSIZE 768
289 UINT8 palette[PALSIZE];
290 const UINT8 *pal;
291 RGBA_t *image;
292 size_t pw, ph, size, ptr = 0;
293 INT32 ch, rep;
294 FILE *file;
295 //Filename checking fixed ~Monster Iestyn and Golden
296 char *pcxfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2home, filename);
297
298 FIL_ForceExtension(pcxfilename, ".pcx");
299 file = fopen(pcxfilename, "rb");
300 if (!file)
301 {
302 pcxfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2path, filename);
303 FIL_ForceExtension(pcxfilename, ".pcx");
304 file = fopen(pcxfilename, "rb");
305 if (!file)
306 return 0;
307 }
308
309 if (fread(&header, sizeof (PcxHeader), 1, file) != 1)
310 {
311 fclose(file);
312 return 0;
313 }
314
315 if (header.bitsPerPixel != 8)
316 {
317 fclose(file);
318 return 0;
319 }
320
321 fseek(file, -PALSIZE, SEEK_END);
322
323 pw = *w = header.xmax - header.xmin + 1;
324 ph = *h = header.ymax - header.ymin + 1;
325 image = Z_Malloc(pw*ph*4, PU_HWRMODELTEXTURE, &grpatch->mipmap->data);
326
327 if (fread(palette, sizeof (UINT8), PALSIZE, file) != PALSIZE)
328 {
329 Z_Free(image);
330 fclose(file);
331 return 0;
332 }
333 fseek(file, sizeof (PcxHeader), SEEK_SET);
334
335 size = pw * ph;
336 while (ptr < size)
337 {
338 ch = fgetc(file); //Hurdler: beurk
339 if (ch >= 192)
340 {
341 rep = ch - 192;
342 ch = fgetc(file);
343 }
344 else
345 {
346 rep = 1;
347 }
348 while (rep--)
349 {
350 pal = palette + ch*3;
351 image[ptr].s.red = *pal++;
352 image[ptr].s.green = *pal++;
353 image[ptr].s.blue = *pal++;
354 image[ptr].s.alpha = 0xFF;
355 ptr++;
356 }
357 }
358 fclose(file);
359 return GL_TEXFMT_RGBA;
360 }
361
362 // -----------------+
363 // md2_loadTexture : Download a pcx or png texture for models
364 // -----------------+
md2_loadTexture(md2_t * model)365 static void md2_loadTexture(md2_t *model)
366 {
367 patch_t *patch;
368 GLPatch_t *grPatch = NULL;
369 const char *filename = model->filename;
370
371 if (model->grpatch)
372 {
373 patch = model->grpatch;
374 grPatch = (GLPatch_t *)(patch->hardware);
375 if (grPatch)
376 Z_Free(grPatch->mipmap->data);
377 }
378 else
379 model->grpatch = patch = Patch_Create(NULL, 0, NULL);
380
381 if (!patch->hardware)
382 Patch_AllocateHardwarePatch(patch);
383
384 if (grPatch == NULL)
385 grPatch = (GLPatch_t *)(patch->hardware);
386
387 if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
388 {
389 int w = 0, h = 0;
390 UINT32 size;
391 RGBA_t *image;
392
393 #ifdef HAVE_PNG
394 grPatch->mipmap->format = PNG_Load(filename, &w, &h, grPatch);
395 if (grPatch->mipmap->format == 0)
396 #endif
397 grPatch->mipmap->format = PCX_Load(filename, &w, &h, grPatch);
398 if (grPatch->mipmap->format == 0)
399 {
400 model->notexturefile = true; // mark it so its not searched for again repeatedly
401 return;
402 }
403
404 grPatch->mipmap->downloaded = 0;
405 grPatch->mipmap->flags = 0;
406
407 patch->width = (INT16)w;
408 patch->height = (INT16)h;
409 grPatch->mipmap->width = (UINT16)w;
410 grPatch->mipmap->height = (UINT16)h;
411
412 // Lactozilla: Apply colour cube
413 image = grPatch->mipmap->data;
414 size = w*h;
415 while (size--)
416 {
417 V_CubeApply(&image->s.red, &image->s.green, &image->s.blue);
418 image++;
419 }
420 }
421 HWD.pfnSetTexture(grPatch->mipmap);
422 }
423
424 // -----------------+
425 // md2_loadBlendTexture : Download a pcx or png texture for blending MD2 models
426 // -----------------+
md2_loadBlendTexture(md2_t * model)427 static void md2_loadBlendTexture(md2_t *model)
428 {
429 patch_t *patch;
430 GLPatch_t *grPatch = NULL;
431 char *filename = Z_Malloc(strlen(model->filename)+7, PU_STATIC, NULL);
432
433 strcpy(filename, model->filename);
434 FIL_ForceExtension(filename, "_blend.png");
435
436 if (model->blendgrpatch)
437 {
438 patch = model->blendgrpatch;
439 grPatch = (GLPatch_t *)(patch->hardware);
440 if (grPatch)
441 Z_Free(grPatch->mipmap->data);
442 }
443 else
444 model->blendgrpatch = patch = Patch_Create(NULL, 0, NULL);
445
446 if (!patch->hardware)
447 Patch_AllocateHardwarePatch(patch);
448
449 if (grPatch == NULL)
450 grPatch = (GLPatch_t *)(patch->hardware);
451
452 if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
453 {
454 int w = 0, h = 0;
455 #ifdef HAVE_PNG
456 grPatch->mipmap->format = PNG_Load(filename, &w, &h, grPatch);
457 if (grPatch->mipmap->format == 0)
458 #endif
459 grPatch->mipmap->format = PCX_Load(filename, &w, &h, grPatch);
460 if (grPatch->mipmap->format == 0)
461 {
462 model->noblendfile = true; // mark it so its not searched for again repeatedly
463 Z_Free(filename);
464 return;
465 }
466
467 grPatch->mipmap->downloaded = 0;
468 grPatch->mipmap->flags = 0;
469
470 patch->width = (INT16)w;
471 patch->height = (INT16)h;
472 grPatch->mipmap->width = (UINT16)w;
473 grPatch->mipmap->height = (UINT16)h;
474 }
475 HWD.pfnSetTexture(grPatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary
476
477 Z_Free(filename);
478 }
479
480 // Don't spam the console, or the OS with fopen requests!
481 static boolean nomd2s = false;
482
HWR_InitModels(void)483 void HWR_InitModels(void)
484 {
485 size_t i;
486 INT32 s;
487 FILE *f;
488 char name[24], filename[32];
489 float scale, offset;
490 size_t prefixlen;
491
492 CONS_Printf("HWR_InitModels()...\n");
493 for (s = 0; s < MAXSKINS; s++)
494 {
495 md2_playermodels[s].scale = -1.0f;
496 md2_playermodels[s].model = NULL;
497 md2_playermodels[s].grpatch = NULL;
498 md2_playermodels[s].notexturefile = false;
499 md2_playermodels[s].noblendfile = false;
500 md2_playermodels[s].skin = -1;
501 md2_playermodels[s].notfound = true;
502 md2_playermodels[s].error = false;
503 }
504 for (i = 0; i < NUMSPRITES; i++)
505 {
506 md2_models[i].scale = -1.0f;
507 md2_models[i].model = NULL;
508 md2_models[i].grpatch = NULL;
509 md2_models[i].notexturefile = false;
510 md2_models[i].noblendfile = false;
511 md2_models[i].skin = -1;
512 md2_models[i].notfound = true;
513 md2_models[i].error = false;
514 }
515
516 // read the models.dat file
517 //Filename checking fixed ~Monster Iestyn and Golden
518 f = fopen(va("%s"PATHSEP"%s", srb2home, "models.dat"), "rt");
519
520 if (!f)
521 {
522 f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt");
523 if (!f)
524 {
525 CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno));
526 nomd2s = true;
527 return;
528 }
529 }
530
531 // length of the player model prefix
532 prefixlen = strlen(PLAYERMODELPREFIX);
533
534 while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4)
535 {
536 char *skinname = name;
537 size_t len = strlen(name);
538
539 // check for the player model prefix.
540 if (!strnicmp(name, PLAYERMODELPREFIX, prefixlen) && (len > prefixlen))
541 {
542 skinname += prefixlen;
543 goto addskinmodel;
544 }
545
546 // add sprite model
547 if (len == 4) // must be 4 characters long exactly. otherwise it's not a sprite name.
548 {
549 for (i = 0; i < NUMSPRITES; i++)
550 {
551 if (stricmp(name, sprnames[i]) == 0)
552 {
553 md2_models[i].scale = scale;
554 md2_models[i].offset = offset;
555 md2_models[i].notfound = false;
556 strcpy(md2_models[i].filename, filename);
557 goto modelfound;
558 }
559 }
560 }
561
562 addskinmodel:
563 // add player model
564 for (s = 0; s < MAXSKINS; s++)
565 {
566 if (stricmp(skinname, skins[s].name) == 0)
567 {
568 md2_playermodels[s].skin = s;
569 md2_playermodels[s].scale = scale;
570 md2_playermodels[s].offset = offset;
571 md2_playermodels[s].notfound = false;
572 strcpy(md2_playermodels[s].filename, filename);
573 goto modelfound;
574 }
575 }
576
577 modelfound:
578 // move on to next line...
579 continue;
580 }
581 fclose(f);
582 }
583
HWR_AddPlayerModel(int skin)584 void HWR_AddPlayerModel(int skin) // For skins that were added after startup
585 {
586 FILE *f;
587 char name[24], filename[32];
588 float scale, offset;
589 size_t prefixlen;
590
591 if (nomd2s)
592 return;
593
594 //CONS_Printf("HWR_AddPlayerModel()...\n");
595
596 // read the models.dat file
597 //Filename checking fixed ~Monster Iestyn and Golden
598 f = fopen(va("%s"PATHSEP"%s", srb2home, "models.dat"), "rt");
599
600 if (!f)
601 {
602 f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt");
603 if (!f)
604 {
605 CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno));
606 nomd2s = true;
607 return;
608 }
609 }
610
611 // length of the player model prefix
612 prefixlen = strlen(PLAYERMODELPREFIX);
613
614 // Check for any models that match the names of player skins!
615 while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4)
616 {
617 char *skinname = name;
618 size_t len = strlen(name);
619
620 // ignore the player model prefix.
621 if (!strnicmp(name, PLAYERMODELPREFIX, prefixlen) && (len > prefixlen))
622 skinname += prefixlen;
623
624 if (stricmp(skinname, skins[skin].name) == 0)
625 {
626 md2_playermodels[skin].skin = skin;
627 md2_playermodels[skin].scale = scale;
628 md2_playermodels[skin].offset = offset;
629 md2_playermodels[skin].notfound = false;
630 strcpy(md2_playermodels[skin].filename, filename);
631 goto playermodelfound;
632 }
633 }
634
635 md2_playermodels[skin].notfound = true;
636 playermodelfound:
637 fclose(f);
638 }
639
HWR_AddSpriteModel(size_t spritenum)640 void HWR_AddSpriteModel(size_t spritenum) // For sprites that were added after startup
641 {
642 FILE *f;
643 // name[24] is used to check for names in the models.dat file that match with sprites or player skins
644 // sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long
645 // PLAYERMODELPREFIX is 6 characters long
646 char name[24], filename[32];
647 float scale, offset;
648
649 if (nomd2s)
650 return;
651
652 if (spritenum == SPR_PLAY) // Handled already NEWMD2: Per sprite, per-skin check
653 return;
654
655 // Read the models.dat file
656 //Filename checking fixed ~Monster Iestyn and Golden
657 f = fopen(va("%s"PATHSEP"%s", srb2home, "models.dat"), "rt");
658
659 if (!f)
660 {
661 f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt");
662 if (!f)
663 {
664 CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno));
665 nomd2s = true;
666 return;
667 }
668 }
669
670 // Check for any models that match the names of sprite names!
671 while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4)
672 {
673 // length of the sprite name
674 size_t len = strlen(name);
675 if (len != 4) // must be 4 characters long exactly. otherwise it's not a sprite name.
676 continue;
677
678 // check for the player model prefix.
679 if (!strnicmp(name, PLAYERMODELPREFIX, strlen(PLAYERMODELPREFIX)))
680 continue; // that's not a sprite...
681
682 if (stricmp(name, sprnames[spritenum]) == 0)
683 {
684 md2_models[spritenum].scale = scale;
685 md2_models[spritenum].offset = offset;
686 md2_models[spritenum].notfound = false;
687 strcpy(md2_models[spritenum].filename, filename);
688 goto spritemodelfound;
689 }
690 }
691
692 md2_models[spritenum].notfound = true;
693 spritemodelfound:
694 fclose(f);
695 }
696
697 // Define for getting accurate color brightness readings according to how the human eye sees them.
698 // https://en.wikipedia.org/wiki/Relative_luminance
699 // 0.2126 to red
700 // 0.7152 to green
701 // 0.0722 to blue
702 #define SETBRIGHTNESS(brightness,r,g,b) \
703 brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000))
704
HWR_CreateBlendedTexture(patch_t * gpatch,patch_t * blendgpatch,GLMipmap_t * grMipmap,INT32 skinnum,skincolornum_t color)705 static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMipmap_t *grMipmap, INT32 skinnum, skincolornum_t color)
706 {
707 GLPatch_t *hwrPatch = gpatch->hardware;
708 GLPatch_t *hwrBlendPatch = blendgpatch->hardware;
709 UINT16 w = gpatch->width, h = gpatch->height;
710 UINT32 size = w*h;
711 RGBA_t *image, *blendimage, *cur, blendcolor;
712 UINT16 translation[16]; // First the color index
713 UINT8 cutoff[16]; // Brightness cutoff before using the next color
714 UINT8 translen = 0;
715 UINT8 i;
716
717 blendcolor = V_GetColor(0); // initialize
718 memset(translation, 0, sizeof(translation));
719 memset(cutoff, 0, sizeof(cutoff));
720
721 if (grMipmap->width == 0)
722 {
723 grMipmap->width = gpatch->width;
724 grMipmap->height = gpatch->height;
725
726 // no wrap around, no chroma key
727 grMipmap->flags = 0;
728
729 // setup the texture info
730 grMipmap->format = GL_TEXFMT_RGBA;
731 }
732
733 if (grMipmap->data)
734 {
735 Z_Free(grMipmap->data);
736 grMipmap->data = NULL;
737 }
738
739 cur = Z_Malloc(size*4, PU_HWRMODELTEXTURE, &grMipmap->data);
740 memset(cur, 0x00, size*4);
741
742 image = hwrPatch->mipmap->data;
743 blendimage = hwrBlendPatch->mipmap->data;
744
745 // TC_METALSONIC includes an actual skincolor translation, on top of its flashing.
746 if (skinnum == TC_METALSONIC)
747 color = SKINCOLOR_COBALT;
748
749 if (color != SKINCOLOR_NONE && color < numskincolors)
750 {
751 UINT8 numdupes = 1;
752
753 translation[translen] = skincolors[color].ramp[0];
754 cutoff[translen] = 255;
755
756 for (i = 1; i < 16; i++)
757 {
758 if (translation[translen] == skincolors[color].ramp[i])
759 {
760 numdupes++;
761 continue;
762 }
763
764 if (translen > 0)
765 {
766 cutoff[translen] = cutoff[translen-1] - (256 / (16 / numdupes));
767 }
768
769 numdupes = 1;
770 translen++;
771
772 translation[translen] = (UINT16)skincolors[color].ramp[i];
773 }
774
775 translen++;
776 }
777
778 while (size--)
779 {
780 if (skinnum == TC_BOSS)
781 {
782 // Turn everything below a certain threshold white
783 if ((image->s.red == image->s.green) && (image->s.green == image->s.blue) && image->s.blue < 127)
784 {
785 // Lactozilla: Invert the colors
786 cur->s.red = cur->s.green = cur->s.blue = (255 - image->s.blue);
787 }
788 else
789 {
790 cur->s.red = image->s.red;
791 cur->s.green = image->s.green;
792 cur->s.blue = image->s.blue;
793 }
794
795 cur->s.alpha = image->s.alpha;
796 }
797 else if (skinnum == TC_ALLWHITE)
798 {
799 // Turn everything white
800 cur->s.red = cur->s.green = cur->s.blue = 255;
801 cur->s.alpha = image->s.alpha;
802 }
803 else
804 {
805 // Everything below requires a blend image
806 if (blendimage == NULL)
807 {
808 cur->rgba = image->rgba;
809 goto skippixel;
810 }
811
812 // Metal Sonic dash mode
813 if (skinnum == TC_DASHMODE)
814 {
815 if (image->s.alpha == 0 && blendimage->s.alpha == 0)
816 {
817 // Don't bother with blending the pixel if the alpha of the blend pixel is 0
818 cur->rgba = image->rgba;
819 }
820 else
821 {
822 UINT8 ialpha = 255 - blendimage->s.alpha, balpha = blendimage->s.alpha;
823 RGBA_t icolor = *image, bcolor;
824
825 memset(&bcolor, 0x00, sizeof(RGBA_t));
826
827 if (blendimage->s.alpha)
828 {
829 bcolor.s.blue = 0;
830 bcolor.s.red = 255;
831 bcolor.s.green = (blendimage->s.red + blendimage->s.green + blendimage->s.blue) / 3;
832 }
833
834 if (image->s.alpha && image->s.red > image->s.green << 1) // this is pretty arbitrary, but it works well for Metal Sonic
835 {
836 icolor.s.red = image->s.blue;
837 icolor.s.blue = image->s.red;
838 }
839
840 cur->s.red = (ialpha * icolor.s.red + balpha * bcolor.s.red)/255;
841 cur->s.green = (ialpha * icolor.s.green + balpha * bcolor.s.green)/255;
842 cur->s.blue = (ialpha * icolor.s.blue + balpha * bcolor.s.blue)/255;
843 cur->s.alpha = image->s.alpha;
844 }
845 }
846 else
847 {
848 // All settings that use skincolors!
849 UINT16 brightness;
850
851 if (translen <= 0)
852 {
853 cur->rgba = image->rgba;
854 goto skippixel;
855 }
856
857 // Don't bother with blending the pixel if the alpha of the blend pixel is 0
858 if (skinnum == TC_RAINBOW)
859 {
860 if (image->s.alpha == 0 && blendimage->s.alpha == 0)
861 {
862 cur->rgba = image->rgba;
863 goto skippixel;
864 }
865 else
866 {
867 UINT16 imagebright, blendbright;
868 SETBRIGHTNESS(imagebright,image->s.red,image->s.green,image->s.blue);
869 SETBRIGHTNESS(blendbright,blendimage->s.red,blendimage->s.green,blendimage->s.blue);
870 // slightly dumb average between the blend image color and base image colour, usually one or the other will be fully opaque anyway
871 brightness = (imagebright*(255-blendimage->s.alpha))/255 + (blendbright*blendimage->s.alpha)/255;
872 }
873 }
874 else
875 {
876 if (blendimage->s.alpha == 0)
877 {
878 cur->rgba = image->rgba;
879 goto skippixel; // for metal sonic blend
880 }
881 else
882 {
883 SETBRIGHTNESS(brightness,blendimage->s.red,blendimage->s.green,blendimage->s.blue);
884 }
885 }
886
887 // Calculate a sort of "gradient" for the skincolor
888 // (Me splitting this into a function didn't work, so I had to ruin this entire function's groove...)
889 {
890 RGBA_t nextcolor;
891 UINT8 firsti, secondi, mul, mulmax;
892 INT32 r, g, b;
893
894 // Rainbow needs to find the closest match to the textures themselves, instead of matching brightnesses to other colors.
895 // Ensue horrible mess.
896 if (skinnum == TC_RAINBOW)
897 {
898 UINT16 brightdif = 256;
899 UINT8 colorbrightnesses[16];
900 INT32 compare, m, d;
901
902 // Ignore pure white & pitch black
903 if (brightness > 253 || brightness < 2)
904 {
905 cur->rgba = image->rgba;
906 cur++; image++; blendimage++;
907 continue;
908 }
909
910 firsti = 0;
911 mul = 0;
912 mulmax = 1;
913
914 for (i = 0; i < translen; i++)
915 {
916 RGBA_t tempc = V_GetColor(translation[i]);
917 SETBRIGHTNESS(colorbrightnesses[i], tempc.s.red, tempc.s.green, tempc.s.blue); // store brightnesses for comparison
918 }
919
920 for (i = 0; i < translen; i++)
921 {
922 if (brightness > colorbrightnesses[i]) // don't allow greater matches (because calculating a makeshift gradient for this is already a huge mess as is)
923 continue;
924
925 compare = abs((INT16)(colorbrightnesses[i]) - (INT16)(brightness));
926
927 if (compare < brightdif)
928 {
929 brightdif = (UINT16)compare;
930 firsti = i; // best matching color that's equal brightness or darker
931 }
932 }
933
934 secondi = firsti+1; // next color in line
935 if (secondi >= translen)
936 {
937 m = (INT16)brightness; // - 0;
938 d = (INT16)colorbrightnesses[firsti]; // - 0;
939 }
940 else
941 {
942 m = (INT16)brightness - (INT16)colorbrightnesses[secondi];
943 d = (INT16)colorbrightnesses[firsti] - (INT16)colorbrightnesses[secondi];
944 }
945
946 if (m >= d)
947 m = d-1;
948
949 mulmax = 16;
950
951 // calculate the "gradient" multiplier based on how close this color is to the one next in line
952 if (m <= 0 || d <= 0)
953 mul = 0;
954 else
955 mul = (mulmax-1) - ((m * mulmax) / d);
956 }
957 else
958 {
959 // Just convert brightness to a skincolor value, use distance to next position to find the gradient multipler
960 firsti = 0;
961
962 for (i = 1; i < translen; i++)
963 {
964 if (brightness >= cutoff[i])
965 break;
966 firsti = i;
967 }
968
969 secondi = firsti+1;
970
971 mulmax = cutoff[firsti];
972 if (secondi < translen)
973 mulmax -= cutoff[secondi];
974
975 mul = cutoff[firsti] - brightness;
976 }
977
978 blendcolor = V_GetColor(translation[firsti]);
979
980 if (secondi >= translen)
981 mul = 0;
982
983 if (mul > 0) // If it's 0, then we only need the first color.
984 {
985 #if 0
986 if (secondi >= translen)
987 {
988 // blend to black
989 nextcolor = V_GetColor(31);
990 }
991 else
992 #endif
993 nextcolor = V_GetColor(translation[secondi]);
994
995 // Find difference between points
996 r = (INT32)(nextcolor.s.red - blendcolor.s.red);
997 g = (INT32)(nextcolor.s.green - blendcolor.s.green);
998 b = (INT32)(nextcolor.s.blue - blendcolor.s.blue);
999
1000 // Find the gradient of the two points
1001 r = ((mul * r) / mulmax);
1002 g = ((mul * g) / mulmax);
1003 b = ((mul * b) / mulmax);
1004
1005 // Add gradient value to color
1006 blendcolor.s.red += r;
1007 blendcolor.s.green += g;
1008 blendcolor.s.blue += b;
1009 }
1010 }
1011
1012 if (skinnum == TC_RAINBOW)
1013 {
1014 UINT32 tempcolor;
1015 UINT16 colorbright;
1016
1017 SETBRIGHTNESS(colorbright,blendcolor.s.red,blendcolor.s.green,blendcolor.s.blue);
1018 if (colorbright == 0)
1019 colorbright = 1; // no dividing by 0 please
1020
1021 tempcolor = (brightness * blendcolor.s.red) / colorbright;
1022 tempcolor = min(255, tempcolor);
1023 cur->s.red = (UINT8)tempcolor;
1024
1025 tempcolor = (brightness * blendcolor.s.green) / colorbright;
1026 tempcolor = min(255, tempcolor);
1027 cur->s.green = (UINT8)tempcolor;
1028
1029 tempcolor = (brightness * blendcolor.s.blue) / colorbright;
1030 tempcolor = min(255, tempcolor);
1031 cur->s.blue = (UINT8)tempcolor;
1032 cur->s.alpha = image->s.alpha;
1033 }
1034 else
1035 {
1036 // Color strength depends on image alpha
1037 INT32 tempcolor;
1038
1039 tempcolor = ((image->s.red * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.red * blendimage->s.alpha) / 255);
1040 tempcolor = min(255, tempcolor);
1041 cur->s.red = (UINT8)tempcolor;
1042
1043 tempcolor = ((image->s.green * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.green * blendimage->s.alpha) / 255);
1044 tempcolor = min(255, tempcolor);
1045 cur->s.green = (UINT8)tempcolor;
1046
1047 tempcolor = ((image->s.blue * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.blue * blendimage->s.alpha) / 255);
1048 tempcolor = min(255, tempcolor);
1049 cur->s.blue = (UINT8)tempcolor;
1050 cur->s.alpha = image->s.alpha;
1051 }
1052
1053 skippixel:
1054
1055 // *Now* we can do Metal Sonic's flashing
1056 if (skinnum == TC_METALSONIC)
1057 {
1058 // Blend dark blue into white
1059 if (cur->s.alpha > 0 && cur->s.red == 0 && cur->s.green == 0 && cur->s.blue < 255 && cur->s.blue > 31)
1060 {
1061 // Sal: Invert non-blue
1062 cur->s.red = cur->s.green = (255 - cur->s.blue);
1063 cur->s.blue = 255;
1064 }
1065
1066 cur->s.alpha = image->s.alpha;
1067 }
1068 }
1069 }
1070
1071 cur++; image++;
1072
1073 if (blendimage != NULL)
1074 blendimage++;
1075 }
1076
1077 return;
1078 }
1079
1080 #undef SETBRIGHTNESS
1081
HWR_GetBlendedTexture(patch_t * patch,patch_t * blendpatch,INT32 skinnum,const UINT8 * colormap,skincolornum_t color)1082 static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 skinnum, const UINT8 *colormap, skincolornum_t color)
1083 {
1084 // mostly copied from HWR_GetMappedPatch, hence the similarities and comment
1085 GLPatch_t *grPatch = patch->hardware;
1086 GLPatch_t *grBlendPatch = NULL;
1087 GLMipmap_t *grMipmap, *newMipmap;
1088
1089 if (blendpatch == NULL || colormap == colormaps || colormap == NULL)
1090 {
1091 // Don't do any blending
1092 HWD.pfnSetTexture(grPatch->mipmap);
1093 return;
1094 }
1095
1096 if ((blendpatch && (grBlendPatch = blendpatch->hardware) && grBlendPatch->mipmap->format)
1097 && (patch->width != blendpatch->width || patch->height != blendpatch->height))
1098 {
1099 // Blend image exists, but it's bad.
1100 HWD.pfnSetTexture(grPatch->mipmap);
1101 return;
1102 }
1103
1104 // search for the mipmap
1105 // skip the first (no colormap translated)
1106 for (grMipmap = grPatch->mipmap; grMipmap->nextcolormap; )
1107 {
1108 grMipmap = grMipmap->nextcolormap;
1109 if (grMipmap->colormap && grMipmap->colormap->source == colormap)
1110 {
1111 if (grMipmap->downloaded && grMipmap->data)
1112 {
1113 if (memcmp(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8)))
1114 {
1115 M_Memcpy(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8));
1116 HWR_CreateBlendedTexture(patch, blendpatch, grMipmap, skinnum, color);
1117 HWD.pfnUpdateTexture(grMipmap);
1118 }
1119 else
1120 HWD.pfnSetTexture(grMipmap); // found the colormap, set it to the correct texture
1121
1122 Z_ChangeTag(grMipmap->data, PU_HWRMODELTEXTURE_UNLOCKED);
1123 return;
1124 }
1125 }
1126 }
1127
1128 // If here, the blended texture has not been created
1129 // So we create it
1130
1131 //BP: WARNING: don't free it manually without clearing the cache of harware renderer
1132 // (it have a liste of mipmap)
1133 // this malloc is cleared in HWR_FreeColormapCache
1134 // (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
1135 newMipmap = calloc(1, sizeof (*newMipmap));
1136 if (newMipmap == NULL)
1137 I_Error("%s: Out of memory", "HWR_GetBlendedTexture");
1138 grMipmap->nextcolormap = newMipmap;
1139
1140 newMipmap->colormap = Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL);
1141 newMipmap->colormap->source = colormap;
1142 M_Memcpy(newMipmap->colormap->data, colormap, 256 * sizeof(UINT8));
1143
1144 HWR_CreateBlendedTexture(patch, blendpatch, newMipmap, skinnum, color);
1145
1146 HWD.pfnSetTexture(newMipmap);
1147 Z_ChangeTag(newMipmap->data, PU_HWRMODELTEXTURE_UNLOCKED);
1148 }
1149
1150 #define NORMALFOG 0x00000000
1151 #define FADEFOG 0x19000000
1152
HWR_AllowModel(mobj_t * mobj)1153 static boolean HWR_AllowModel(mobj_t *mobj)
1154 {
1155 // Signpost overlay. Not needed.
1156 if (mobj->state-states == S_PLAY_SIGN)
1157 return false;
1158
1159 // Otherwise, render the model.
1160 return true;
1161 }
1162
HWR_CanInterpolateModel(mobj_t * mobj,model_t * model)1163 static boolean HWR_CanInterpolateModel(mobj_t *mobj, model_t *model)
1164 {
1165 if (cv_glmodelinterpolation.value == 2) // Always interpolate
1166 return true;
1167 return model->interpolate[(mobj->frame & FF_FRAMEMASK)];
1168 }
1169
HWR_CanInterpolateSprite2(modelspr2frames_t * spr2frame)1170 static boolean HWR_CanInterpolateSprite2(modelspr2frames_t *spr2frame)
1171 {
1172 if (cv_glmodelinterpolation.value == 2) // Always interpolate
1173 return true;
1174 return spr2frame->interpolate;
1175 }
1176
1177 //
1178 // HWR_GetModelSprite2 (see P_GetSkinSprite2)
1179 // For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
1180 // For super players, does the same as above - but tries the super equivalent for each sprite2 before the non-super version.
1181 //
1182
HWR_GetModelSprite2(md2_t * md2,skin_t * skin,UINT8 spr2,player_t * player)1183 static UINT8 HWR_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *player)
1184 {
1185 UINT8 super = 0, i = 0;
1186
1187 if (!md2 || !md2->model || !md2->model->spr2frames || !skin)
1188 return 0;
1189
1190 if ((playersprite_t)(spr2 & ~FF_SPR2SUPER) >= free_spr2)
1191 return 0;
1192
1193 while (!md2->model->spr2frames[spr2].numframes
1194 && spr2 != SPR2_STND
1195 && ++i != 32) // recursion limiter
1196 {
1197 if (spr2 & FF_SPR2SUPER)
1198 {
1199 super = FF_SPR2SUPER;
1200 spr2 &= ~FF_SPR2SUPER;
1201 continue;
1202 }
1203
1204 switch(spr2)
1205 {
1206 // Normal special cases.
1207 case SPR2_JUMP:
1208 spr2 = ((player
1209 ? player->charflags
1210 : skin->flags)
1211 & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL;
1212 break;
1213 case SPR2_TIRE:
1214 spr2 = ((player
1215 ? player->charability
1216 : skin->ability)
1217 == CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
1218 break;
1219 // Use the handy list, that's what it's there for!
1220 default:
1221 spr2 = spr2defaults[spr2];
1222 break;
1223 }
1224
1225 spr2 |= super;
1226 }
1227
1228 if (i >= 32) // probably an infinite loop...
1229 return 0;
1230
1231 return spr2;
1232 }
1233
1234 // Adjust texture coords of model to fit into a patch's max_s and max_t
adjustTextureCoords(model_t * model,patch_t * patch)1235 static void adjustTextureCoords(model_t *model, patch_t *patch)
1236 {
1237 int i;
1238 GLPatch_t *gpatch = ((GLPatch_t *)patch->hardware);
1239
1240 for (i = 0; i < model->numMeshes; i++)
1241 {
1242 int j;
1243 mesh_t *mesh = &model->meshes[i];
1244 int numVertices;
1245 float *uvReadPtr = mesh->originaluvs;
1246 float *uvWritePtr;
1247
1248 // i dont know if this is actually possible, just logical conclusion of structure in CreateModelVBOs
1249 if (!mesh->frames && !mesh->tinyframes) continue;
1250
1251 if (mesh->frames) // again CreateModelVBO and CreateModelVBOTiny iterate like this so I'm gonna do that too
1252 numVertices = mesh->numTriangles * 3;
1253 else
1254 numVertices = mesh->numVertices;
1255
1256 // if originaluvs points to uvs, we need to allocate new memory for adjusted uvs
1257 // the old uvs are kept around for use in possible readjustments
1258 if (mesh->uvs == mesh->originaluvs)
1259 mesh->uvs = Z_Malloc(numVertices * 2 * sizeof(float), PU_STATIC, NULL);
1260
1261 uvWritePtr = mesh->uvs;
1262
1263 // fix uvs (texture coordinates) to take into account that the actual texture
1264 // has empty space added until the next power of two
1265 for (j = 0; j < numVertices; j++)
1266 {
1267 *uvWritePtr++ = *uvReadPtr++ * gpatch->max_s;
1268 *uvWritePtr++ = *uvReadPtr++ * gpatch->max_t;
1269 }
1270 }
1271 // Save the values we adjusted the uvs for
1272 model->max_s = gpatch->max_s;
1273 model->max_t = gpatch->max_t;
1274 }
1275
1276 //
1277 // HWR_DrawModel
1278 //
1279
HWR_DrawModel(gl_vissprite_t * spr)1280 boolean HWR_DrawModel(gl_vissprite_t *spr)
1281 {
1282 md2_t *md2;
1283
1284 char filename[64];
1285 INT32 frame = 0;
1286 INT32 nextFrame = -1;
1287 UINT8 spr2 = 0;
1288 FTransform p;
1289 FSurfaceInfo Surf;
1290
1291 if (!cv_glmodels.value)
1292 return false;
1293
1294 if (spr->precip)
1295 return false;
1296
1297 // Lactozilla: Disallow certain models from rendering
1298 if (!HWR_AllowModel(spr->mobj))
1299 return false;
1300
1301 memset(&p, 0x00, sizeof(FTransform));
1302
1303 // MD2 colormap fix
1304 // colormap test
1305 if (spr->mobj->subsector)
1306 {
1307 sector_t *sector = spr->mobj->subsector->sector;
1308 UINT8 lightlevel = 255;
1309 extracolormap_t *colormap = NULL;
1310
1311 if (sector->numlights)
1312 {
1313 INT32 light;
1314
1315 light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before
1316
1317 if (!R_ThingIsFullBright(spr->mobj))
1318 lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel;
1319
1320 if (*sector->lightlist[light].extra_colormap)
1321 colormap = *sector->lightlist[light].extra_colormap;
1322 }
1323 else
1324 {
1325 if (!R_ThingIsFullBright(spr->mobj))
1326 lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel;
1327
1328 if (sector->extra_colormap)
1329 colormap = sector->extra_colormap;
1330 }
1331
1332 HWR_Lighting(&Surf, lightlevel, colormap);
1333 }
1334 else
1335 Surf.PolyColor.rgba = 0xFFFFFFFF;
1336
1337 // Look at HWR_ProjectSprite for more
1338 {
1339 patch_t *gpatch, *blendgpatch;
1340 GLPatch_t *hwrPatch = NULL, *hwrBlendPatch = NULL;
1341 INT32 durs = spr->mobj->state->tics;
1342 INT32 tics = spr->mobj->tics;
1343 const boolean papersprite = (R_ThingIsPaperSprite(spr->mobj) && !R_ThingIsFloorSprite(spr->mobj));
1344 const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(spr->mobj));
1345 const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !R_ThingHorizontallyFlipped(spr->mobj));
1346 spritedef_t *sprdef;
1347 spriteframe_t *sprframe;
1348 spriteinfo_t *sprinfo;
1349 angle_t ang;
1350 INT32 mod;
1351 float finalscale;
1352
1353 // Apparently people don't like jump frames like that, so back it goes
1354 //if (tics > durs)
1355 //durs = tics;
1356
1357 if (spr->mobj->frame & FF_TRANSMASK)
1358 Surf.PolyFlags = HWR_SurfaceBlend(spr->mobj->blendmode, (spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf);
1359 else
1360 {
1361 Surf.PolyColor.s.alpha = (spr->mobj->flags2 & MF2_SHADOW) ? 0x40 : 0xff;
1362 Surf.PolyFlags = HWR_GetBlendModeFlag(spr->mobj->blendmode);
1363 }
1364
1365 // don't forget to enable the depth test because we can't do this
1366 // like before: model polygons are not sorted
1367
1368 // 1. load model+texture if not already loaded
1369 // 2. draw model with correct position, rotation,...
1370 if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) // Use the player MD2 list if the mobj has a skin and is using the player sprites
1371 {
1372 md2 = &md2_playermodels[(skin_t*)spr->mobj->skin-skins];
1373 md2->skin = (skin_t*)spr->mobj->skin-skins;
1374 sprinfo = &((skin_t *)spr->mobj->skin)->sprinfo[spr->mobj->sprite2];
1375 }
1376 else
1377 {
1378 md2 = &md2_models[spr->mobj->sprite];
1379 sprinfo = &spriteinfo[spr->mobj->sprite];
1380 }
1381
1382 // texture loading before model init, so it knows if sprite graphics are used, which
1383 // means that texture coordinates have to be adjusted
1384 gpatch = md2->grpatch;
1385 if (gpatch)
1386 hwrPatch = ((GLPatch_t *)gpatch->hardware);
1387
1388 if (!gpatch || !hwrPatch
1389 || ((!hwrPatch->mipmap->format || !hwrPatch->mipmap->downloaded) && !md2->notexturefile))
1390 md2_loadTexture(md2);
1391
1392 // Load it again, because it isn't being loaded into gpatch after md2_loadtexture...
1393 gpatch = md2->grpatch;
1394 if (gpatch)
1395 hwrPatch = ((GLPatch_t *)gpatch->hardware);
1396
1397 // Load blend texture
1398 blendgpatch = md2->blendgrpatch;
1399 if (blendgpatch)
1400 hwrBlendPatch = ((GLPatch_t *)blendgpatch->hardware);
1401
1402 if ((gpatch && hwrPatch && hwrPatch->mipmap->format) // don't load the blend texture if the base texture isn't available
1403 && (!blendgpatch || !hwrBlendPatch
1404 || ((!hwrBlendPatch->mipmap->format || !hwrBlendPatch->mipmap->downloaded) && !md2->noblendfile)))
1405 md2_loadBlendTexture(md2);
1406
1407 // Load it again, because it isn't being loaded into blendgpatch after md2_loadblendtexture...
1408 blendgpatch = md2->blendgrpatch;
1409 if (blendgpatch)
1410 hwrBlendPatch = ((GLPatch_t *)blendgpatch->hardware);
1411
1412 if (md2->error)
1413 return false; // we already failed loading this before :(
1414 if (!md2->model)
1415 {
1416 //CONS_Debug(DBG_RENDER, "Loading model... (%s)", sprnames[spr->mobj->sprite]);
1417 sprintf(filename, "models/%s", md2->filename);
1418 md2->model = md2_readModel(filename);
1419
1420 if (md2->model)
1421 {
1422 md2_printModelInfo(md2->model);
1423 // If model uses sprite patch as texture, then
1424 // adjust texture coordinates to take power of two textures into account
1425 if (!gpatch || !hwrPatch->mipmap->format)
1426 adjustTextureCoords(md2->model, spr->gpatch);
1427 // note down the max_s and max_t that end up in the VBO
1428 md2->model->vbo_max_s = md2->model->max_s;
1429 md2->model->vbo_max_t = md2->model->max_t;
1430 HWD.pfnCreateModelVBOs(md2->model);
1431 }
1432 else
1433 {
1434 //CONS_Debug(DBG_RENDER, " FAILED\n");
1435 md2->error = true; // prevent endless fail
1436 return false;
1437 }
1438 }
1439
1440 //HWD.pfnSetBlend(blend); // This seems to actually break translucency?
1441 finalscale = md2->scale;
1442 //Hurdler: arf, I don't like that implementation at all... too much crappy
1443
1444 if (gpatch && hwrPatch && hwrPatch->mipmap->format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
1445 {
1446 INT32 skinnum = TC_DEFAULT;
1447
1448 if ((spr->mobj->flags & (MF_ENEMY|MF_BOSS)) && (spr->mobj->flags2 & MF2_FRET) && !(spr->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash"
1449 {
1450 if (spr->mobj->type == MT_CYBRAKDEMON || spr->mobj->colorized)
1451 skinnum = TC_ALLWHITE;
1452 else if (spr->mobj->type == MT_METALSONIC_BATTLE)
1453 skinnum = TC_METALSONIC;
1454 else
1455 skinnum = TC_BOSS;
1456 }
1457 else if ((skincolornum_t)spr->mobj->color != SKINCOLOR_NONE)
1458 {
1459 if (spr->mobj->colorized)
1460 skinnum = TC_RAINBOW;
1461 else if (spr->mobj->player && spr->mobj->player->dashmode >= DASHMODE_THRESHOLD
1462 && (spr->mobj->player->charflags & SF_DASHMODE)
1463 && ((leveltime/2) & 1))
1464 {
1465 if (spr->mobj->player->charflags & SF_MACHINE)
1466 skinnum = TC_DASHMODE;
1467 else
1468 skinnum = TC_RAINBOW;
1469 }
1470 else if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
1471 skinnum = (INT32)((skin_t*)spr->mobj->skin-skins);
1472 else
1473 skinnum = TC_DEFAULT;
1474 }
1475
1476 // Translation or skin number found
1477 HWR_GetBlendedTexture(gpatch, blendgpatch, skinnum, spr->colormap, (skincolornum_t)spr->mobj->color);
1478 }
1479 else // Sprite
1480 {
1481 // Check if sprite dimensions are different from previously used sprite.
1482 // If so, uvs need to be readjusted.
1483 // Comparing floats with the != operator here should be okay because they
1484 // are just copies of glpatches' max_s and max_t values.
1485 // Instead of the != operator, memcmp is used to avoid a compiler warning.
1486 if (memcmp(&(hwrPatch->max_s), &(md2->model->max_s), sizeof(md2->model->max_s)) != 0 ||
1487 memcmp(&(hwrPatch->max_t), &(md2->model->max_t), sizeof(md2->model->max_t)) != 0)
1488 adjustTextureCoords(md2->model, spr->gpatch);
1489 HWR_GetMappedPatch(spr->gpatch, spr->colormap);
1490 }
1491
1492 if (spr->mobj->frame & FF_ANIMATE)
1493 {
1494 // set duration and tics to be the correct values for FF_ANIMATE states
1495 durs = spr->mobj->state->var2;
1496 tics = spr->mobj->anim_duration;
1497 }
1498
1499 frame = (spr->mobj->frame & FF_FRAMEMASK);
1500 if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY && md2->model->spr2frames)
1501 {
1502 spr2 = HWR_GetModelSprite2(md2, spr->mobj->skin, spr->mobj->sprite2, spr->mobj->player);
1503 mod = md2->model->spr2frames[spr2].numframes;
1504 #ifndef DONTHIDEDIFFANIMLENGTH // by default, different anim length is masked by the mod
1505 if (mod > (INT32)((skin_t *)spr->mobj->skin)->sprites[spr2].numframes)
1506 mod = ((skin_t *)spr->mobj->skin)->sprites[spr2].numframes;
1507 #endif
1508 if (!mod)
1509 mod = 1;
1510 frame = md2->model->spr2frames[spr2].frames[frame%mod];
1511 }
1512 else
1513 {
1514 mod = md2->model->meshes[0].numFrames;
1515 if (!mod)
1516 mod = 1;
1517 }
1518
1519 #ifdef USE_MODEL_NEXTFRAME
1520 #define INTERPOLERATION_LIMIT TICRATE/4
1521 if (cv_glmodelinterpolation.value && tics <= durs && tics <= INTERPOLERATION_LIMIT)
1522 {
1523 if (durs > INTERPOLERATION_LIMIT)
1524 durs = INTERPOLERATION_LIMIT;
1525
1526 if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY && md2->model->spr2frames)
1527 {
1528 if (HWR_CanInterpolateSprite2(&md2->model->spr2frames[spr2])
1529 && (spr->mobj->frame & FF_ANIMATE
1530 || (spr->mobj->state->nextstate != S_NULL
1531 && states[spr->mobj->state->nextstate].sprite == SPR_PLAY
1532 && ((P_GetSkinSprite2(spr->mobj->skin, (((spr->mobj->player && spr->mobj->player->powers[pw_super]) ? FF_SPR2SUPER : 0)|states[spr->mobj->state->nextstate].frame) & FF_FRAMEMASK, spr->mobj->player) == spr->mobj->sprite2)))))
1533 {
1534 nextFrame = (spr->mobj->frame & FF_FRAMEMASK) + 1;
1535 if (nextFrame >= mod)
1536 nextFrame = 0;
1537 if (frame || !(spr->mobj->state->frame & FF_SPR2ENDSTATE))
1538 nextFrame = md2->model->spr2frames[spr2].frames[nextFrame];
1539 else
1540 nextFrame = -1;
1541 }
1542 }
1543 else if (HWR_CanInterpolateModel(spr->mobj, md2->model))
1544 {
1545 // frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation
1546 if (spr->mobj->frame & FF_ANIMATE)
1547 {
1548 nextFrame = (spr->mobj->frame & FF_FRAMEMASK) + 1;
1549 if (nextFrame >= (INT32)(spr->mobj->state->var1 + (spr->mobj->state->frame & FF_FRAMEMASK)))
1550 nextFrame = (spr->mobj->state->frame & FF_FRAMEMASK) % mod;
1551 }
1552 else
1553 {
1554 if (spr->mobj->state->nextstate != S_NULL && states[spr->mobj->state->nextstate].sprite != SPR_NULL
1555 && !(spr->mobj->player && (spr->mobj->state->nextstate == S_PLAY_WAIT) && spr->mobj->state == &states[S_PLAY_STND]))
1556 nextFrame = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % mod;
1557 }
1558 }
1559 }
1560 #undef INTERPOLERATION_LIMIT
1561 #endif
1562
1563 //Hurdler: it seems there is still a small problem with mobj angle
1564 p.x = FIXED_TO_FLOAT(spr->mobj->x);
1565 p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset;
1566
1567 if (flip)
1568 p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height);
1569 else
1570 p.z = FIXED_TO_FLOAT(spr->mobj->z);
1571
1572 if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
1573 sprdef = &((skin_t *)spr->mobj->skin)->sprites[spr->mobj->sprite2];
1574 else
1575 sprdef = &sprites[spr->mobj->sprite];
1576
1577 sprframe = &sprdef->spriteframes[spr->mobj->frame & FF_FRAMEMASK];
1578
1579 if (sprframe->rotate || papersprite)
1580 {
1581 fixed_t anglef = AngleFixed(spr->mobj->angle);
1582
1583 if (spr->mobj->player)
1584 anglef = AngleFixed(spr->mobj->player->drawangle);
1585
1586 p.angley = FIXED_TO_FLOAT(anglef);
1587 }
1588 else
1589 {
1590 const fixed_t anglef = AngleFixed((R_PointToAngle(spr->mobj->x, spr->mobj->y))-ANGLE_180);
1591 p.angley = FIXED_TO_FLOAT(anglef);
1592 }
1593
1594 p.rollangle = 0.0f;
1595 p.rollflip = 1;
1596 p.rotaxis = 0;
1597 if (spr->mobj->rollangle)
1598 {
1599 fixed_t anglef = AngleFixed(spr->mobj->rollangle);
1600 p.rollangle = FIXED_TO_FLOAT(anglef);
1601 p.roll = true;
1602
1603 // rotation pivot
1604 p.centerx = FIXED_TO_FLOAT(spr->mobj->radius/2);
1605 p.centery = FIXED_TO_FLOAT(spr->mobj->height/(flip ? -2 : 2));
1606
1607 // rotation axis
1608 if (sprinfo->available)
1609 p.rotaxis = (UINT8)(sprinfo->pivot[(spr->mobj->frame & FF_FRAMEMASK)].rotaxis);
1610
1611 // for NiGHTS specifically but should work everywhere else
1612 ang = R_PointToAngle (spr->mobj->x, spr->mobj->y) - (spr->mobj->player ? spr->mobj->player->drawangle : spr->mobj->angle);
1613 if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
1614 p.rollflip = 1;
1615 else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
1616 p.rollflip = -1;
1617
1618 if (flip)
1619 p.rollflip *= -1;
1620 }
1621
1622 p.anglex = 0.0f;
1623
1624 #ifdef USE_FTRANSFORM_ANGLEZ
1625 // Slope rotation from Kart
1626 p.anglez = 0.0f;
1627 if (spr->mobj->standingslope)
1628 {
1629 fixed_t tempz = spr->mobj->standingslope->normal.z;
1630 fixed_t tempy = spr->mobj->standingslope->normal.y;
1631 fixed_t tempx = spr->mobj->standingslope->normal.x;
1632 fixed_t tempangle = AngleFixed(R_PointToAngle2(0, 0, FixedSqrt(FixedMul(tempy, tempy) + FixedMul(tempz, tempz)), tempx));
1633 p.anglez = FIXED_TO_FLOAT(tempangle);
1634 tempangle = -AngleFixed(R_PointToAngle2(0, 0, tempz, tempy));
1635 p.anglex = FIXED_TO_FLOAT(tempangle);
1636 }
1637 #endif
1638
1639 // SRB2CBTODO: MD2 scaling support
1640 finalscale *= FIXED_TO_FLOAT(spr->mobj->scale);
1641
1642 p.flip = atransform.flip;
1643 #ifdef USE_FTRANSFORM_MIRROR
1644 p.mirror = atransform.mirror; // from Kart
1645 #endif
1646
1647 HWD.pfnSetShader(SHADER_MODEL); // model shader
1648 HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf);
1649 }
1650
1651 return true;
1652 }
1653
1654 #endif //HWRENDER
1655