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