1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #ifdef _WIN32
13 #include <windows.h>
14 #include <windowsx.h>
15 #endif
16 
17 #include <limits.h>
18 
19 #include "globalincs/pstypes.h"
20 #include "osapi/osapi.h"
21 #include "graphics/2d.h"
22 #include "graphics/grstub.h"
23 #include "render/3d.h"
24 #include "bmpman/bmpman.h"
25 #include "palman/palman.h"
26 #include "graphics/font.h"
27 #include "graphics/grinternal.h"
28 #include "globalincs/systemvars.h"
29 #include "cmdline/cmdline.h"
30 #include "graphics/grbatch.h"
31 #include "parse/scripting.h"
32 #include "gamesequence/gamesequence.h"	//WMC - for scripting hooks in gr_flip()
33 #include "io/keycontrol.h" // m!m
34 
35 
36 #if defined(SCP_UNIX) && !defined(__APPLE__)
37 #if ( SDL_VERSION_ATLEAST(1, 2, 7) )
38 #include "SDL_cpuinfo.h"
39 #endif
40 #endif // SCP_UNIX && !__APPLE__
41 
42 // Includes for different rendering systems
43 #include "graphics/gropengl.h"
44 
45 const char *Resolution_prefixes[GR_NUM_RESOLUTIONS] = { "", "2_" };
46 
47 screen gr_screen;
48 
49 color_gun Gr_red, Gr_green, Gr_blue, Gr_alpha;
50 color_gun Gr_t_red, Gr_t_green, Gr_t_blue, Gr_t_alpha;
51 color_gun Gr_ta_red, Gr_ta_green, Gr_ta_blue, Gr_ta_alpha;
52 color_gun *Gr_current_red, *Gr_current_green, *Gr_current_blue, *Gr_current_alpha;
53 
54 
55 ubyte Gr_original_palette[768];		// The palette
56 ubyte Gr_current_palette[768];
57 char Gr_current_palette_name[128] = NOX("none");
58 
59 // cursor stuff
60 int Gr_cursor = -1;
61 int Web_cursor_bitmap = -1;
62 int Gr_cursor_size = 32;	// default w/h
63 
64 int Gr_inited = 0;
65 
66 uint Gr_signature = 0;
67 
68 float Gr_gamma = 1.8f;
69 int Gr_gamma_int = 180;
70 
71 // z-buffer stuff
72 int gr_zbuffering = 0;
73 int gr_zbuffering_mode = 0;
74 int gr_global_zbuffering = 0;
75 
76 // stencil buffer stuff
77 int gr_stencil_mode = 0;
78 
79 // alpha mask stuff
80 int gr_alpha_test = 0;
81 
82 // Default clipping distances
83 const float Default_min_draw_distance = 1.0f;
84 const float Default_max_draw_distance = 1e10;
85 float Min_draw_distance = Default_min_draw_distance;
86 float Max_draw_distance = Default_max_draw_distance;
87 
88 static int GL_cursor_nframes = 0;
89 
90 // Pre-computed screen resize vars
91 static float Gr_full_resize_X = 1.0f, Gr_full_resize_Y = 1.0f;
92 static float Gr_resize_X = 1.0f, Gr_resize_Y = 1.0f;
93 static float Gr_menu_offset_X = 0.0f, Gr_menu_offset_Y = 0.0f;
94 
95 float Gr_save_full_resize_X = 1.0f, Gr_save_full_resize_Y = 1.0f;
96 float Gr_save_resize_X = 1.0f, Gr_save_resize_Y = 1.0f;
97 float Gr_save_menu_offset_X = 0.0f, Gr_save_menu_offset_Y = 0.0f;
98 
99 bool Save_custom_screen_size;
100 
gr_set_screen_scale(int w,int h)101 void gr_set_screen_scale(int w, int h)
102 {
103 	Gr_full_resize_X = (float)gr_screen.max_w / (float)w;
104 	Gr_full_resize_Y = (float)gr_screen.max_h / (float)h;
105 
106 	if (!Cmdline_stretch_menu) {
107 		float aspect_quotient = ((float)gr_screen.max_w / (float)gr_screen.max_h) / ((float)w / (float)h);
108 
109 		Gr_resize_X = Gr_full_resize_X / ((aspect_quotient > 1.0f) ? aspect_quotient : 1.0f);
110 		Gr_resize_Y = Gr_full_resize_Y * ((aspect_quotient < 1.0f) ? aspect_quotient : 1.0f);
111 
112 		Gr_menu_offset_X = (aspect_quotient > 1.0f) ? ((gr_screen.max_w - gr_screen.max_w / aspect_quotient) / 2.0f) : 0.0f;
113 		Gr_menu_offset_Y = (aspect_quotient < 1.0f) ? ((gr_screen.max_h - gr_screen.max_h * aspect_quotient) / 2.0f) : 0.0f;
114 	} else {
115 		Gr_resize_X = Gr_full_resize_X;
116 		Gr_resize_Y = Gr_full_resize_Y;
117 
118 		Gr_menu_offset_X = 0.0f;
119 		Gr_menu_offset_Y = 0.0f;
120 	}
121 
122 	Save_custom_screen_size = gr_screen.custom_size;
123 
124 	gr_screen.custom_size = true;
125 }
126 
gr_set_screen_scale(int w,int h,int max_w,int max_h)127 void gr_set_screen_scale(int w, int h, int max_w, int max_h)
128 {
129 	Gr_resize_X = Gr_full_resize_X = (float)max_w / (float)w;
130 	Gr_resize_Y = Gr_full_resize_Y = (float)max_h / (float)h;
131 
132 	Gr_menu_offset_X = 0.0f;
133 	Gr_menu_offset_Y = 0.0f;
134 
135 	Save_custom_screen_size = gr_screen.custom_size;
136 
137 	gr_screen.custom_size = true;
138 }
139 
gr_reset_screen_scale()140 void gr_reset_screen_scale()
141 {
142 	Gr_full_resize_X = Gr_save_full_resize_X;
143 	Gr_full_resize_Y = Gr_save_full_resize_Y;
144 
145 	Gr_resize_X = Gr_save_resize_X;
146 	Gr_resize_Y = Gr_save_resize_Y;
147 
148 	Gr_menu_offset_X = Gr_save_menu_offset_X;
149 	Gr_menu_offset_Y = Gr_save_menu_offset_Y;
150 
151 	gr_screen.custom_size = Save_custom_screen_size;
152 }
153 
154 /**
155  * This function is to be called if you wish to scale GR_1024 or GR_640 x and y positions or
156  * lengths in order to keep the correctly scaled to nonstandard resolutions
157  *
158  * @param x X value, can be NULL
159  * @param y Y value, can be NULL
160  * @param w width, can be NULL
161  * @param h height, can be NULL
162  * @param resize_mode
163  * @return always true unless error
164  */
gr_resize_screen_pos(int * x,int * y,int * w,int * h,int resize_mode)165 bool gr_resize_screen_pos(int *x, int *y, int *w, int *h, int resize_mode)
166 {
167 	if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) {
168 		return false;
169 	}
170 
171 	float xy_tmp = 0.0f;
172 
173 	if ( x ) {
174 		xy_tmp = (*x) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X) + ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_X : 0.0f);
175 		(*x) = fl2i(xy_tmp);
176 	}
177 
178 	if ( y ) {
179 		xy_tmp = (*y) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y) + ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_Y : 0.0f);
180 		(*y) = fl2i(xy_tmp);
181 	}
182 
183 	if ( w ) {
184 		xy_tmp = (*w) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X);
185 		(*w) = fl2i(xy_tmp);
186 	}
187 
188 	if ( h ) {
189 		xy_tmp = (*h) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y);
190 		(*h) = fl2i(xy_tmp);
191 	}
192 
193 	return true;
194 }
195 
196 /**
197  *
198  * @param x X value, can be NULL
199  * @param y Y value, can be NULL
200  * @param w width, can be NULL
201  * @param h height, can be NULL
202  * @param resize_mode
203  * @return always true unless error
204  */
gr_unsize_screen_pos(int * x,int * y,int * w,int * h,int resize_mode)205 bool gr_unsize_screen_pos(int *x, int *y, int *w, int *h, int resize_mode)
206 {
207 	if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) {
208 		return false;
209 	}
210 
211 	float xy_tmp = 0.0f;
212 
213 	if ( x ) {
214 		xy_tmp = ((*x) - ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_X : 0.0f)) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X);
215 		(*x) = fl2i(xy_tmp);
216 	}
217 
218 	if ( y ) {
219 		xy_tmp = ((*y) - ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_Y : 0.0f)) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y);
220 		(*y) = fl2i(xy_tmp);
221 	}
222 
223 	if ( w ) {
224 		xy_tmp = (*w) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X);
225 		(*w) = fl2i(xy_tmp);
226 	}
227 
228 	if ( h ) {
229 		xy_tmp = (*h) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y);
230 		(*h) = fl2i(xy_tmp);
231 	}
232 
233 	return true;
234 }
235 
236 /**
237  * This function is to be called if you wish to scale GR_1024 or GR_640 x and y positions or
238  * lengths in order to keep the correctly scaled to nonstandard resolutions
239  *
240  * @param x X value, can be NULL
241  * @param y Y value, can be NULL
242  * @param w width, can be NULL
243  * @param h height, can be NULL
244  * @param resize_mode
245  * @return always true unless error
246  */
gr_resize_screen_posf(float * x,float * y,float * w,float * h,int resize_mode)247 bool gr_resize_screen_posf(float *x, float *y, float *w, float *h, int resize_mode)
248 {
249 	if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) {
250 		return false;
251 	}
252 
253 	float xy_tmp = 0.0f;
254 
255 	if ( x ) {
256 		xy_tmp = (*x) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X) + ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_X : 0.0f);
257 		(*x) = xy_tmp;
258 	}
259 
260 	if ( y ) {
261 		xy_tmp = (*y) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y) + ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_Y : 0.0f);
262 		(*y) = xy_tmp;
263 	}
264 
265 	if ( w ) {
266 		xy_tmp = (*w) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X);
267 		(*w) = xy_tmp;
268 	}
269 
270 	if ( h ) {
271 		xy_tmp = (*h) * ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y);
272 		(*h) = xy_tmp;
273 	}
274 
275 	return true;
276 }
277 
278 /**
279  *
280  * @param x X value, can be NULL
281  * @param y Y value, can be NULL
282  * @param w width, can be NULL
283  * @param h height, can be NULL
284  * @param resize_mode
285  * @return always true unless error
286  */
gr_unsize_screen_posf(float * x,float * y,float * w,float * h,int resize_mode)287 bool gr_unsize_screen_posf(float *x, float *y, float *w, float *h, int resize_mode)
288 {
289 	if ( resize_mode == GR_RESIZE_NONE || (!gr_screen.custom_size && (gr_screen.rendering_to_texture == -1)) ) {
290 		return false;
291 	}
292 
293 	float xy_tmp = 0.0f;
294 
295 	if ( x ) {
296 		xy_tmp = ((*x) - ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_X : 0.0f)) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X);
297 		(*x) = xy_tmp;
298 	}
299 
300 	if ( y ) {
301 		xy_tmp = ((*y) - ((resize_mode == GR_RESIZE_MENU) ? Gr_menu_offset_Y : 0.0f)) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y);
302 		(*y) = xy_tmp;
303 	}
304 
305 	if ( w ) {
306 		xy_tmp = (*w) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_X : Gr_resize_X);
307 		(*w) = xy_tmp;
308 	}
309 
310 	if ( h ) {
311 		xy_tmp = (*h) / ((resize_mode == GR_RESIZE_FULL) ? Gr_full_resize_Y : Gr_resize_Y);
312 		(*h) = xy_tmp;
313 	}
314 
315 	return true;
316 }
317 
gr_close()318 void gr_close()
319 {
320 	if ( !Gr_inited ) {
321 		return;
322 	}
323 
324 	palette_flush();
325 
326 	switch (gr_screen.mode) {
327 		case GR_OPENGL:
328 			gr_opengl_cleanup();
329 			break;
330 
331 		case GR_STUB:
332 			break;
333 
334 		default:
335 			Int3();		// Invalid graphics mode
336 	}
337 
338 	gr_font_close();
339 
340 	Gr_inited = 0;
341 }
342 
343 
344 /**
345  * Set screen clear color
346  */
347 DCF(clear_color, "set clear color r, g, b")
348 {
349 	ubyte r, g, b;
350 
351 	dc_get_arg(ARG_UBYTE);
352 	r = Dc_arg_ubyte;
353 	dc_get_arg(ARG_UBYTE);
354 	g = Dc_arg_ubyte;
355 	dc_get_arg(ARG_UBYTE);
356 	b = Dc_arg_ubyte;
357 
358 	// set the color
359 	gr_set_clear_color(r, g, b);
360 }
361 
gr_set_palette_internal(const char * name,ubyte * palette,int restrict_font_to_128)362 void gr_set_palette_internal( const char *name, ubyte * palette, int restrict_font_to_128 )
363 {
364 	if ( palette == NULL ) {
365 		// Create a default palette
366 		int r,g,b,i;
367 		i = 0;
368 
369 		for (r=0; r<6; r++ )
370 			for (g=0; g<6; g++ )
371 				for (b=0; b<6; b++ ) {
372 					Gr_current_palette[i*3+0] = (unsigned char)(r*51);
373 					Gr_current_palette[i*3+1] = (unsigned char)(g*51);
374 					Gr_current_palette[i*3+2] = (unsigned char)(b*51);
375 					i++;
376 				}
377 		for ( i=216;i<256; i++ ) {
378 			Gr_current_palette[i*3+0] = (unsigned char)((i-216)*6);
379 			Gr_current_palette[i*3+1] = (unsigned char)((i-216)*6);
380 			Gr_current_palette[i*3+2] = (unsigned char)((i-216)*6);
381 		}
382 		memmove( Gr_original_palette, Gr_current_palette, 768 );
383 	} else {
384 		memmove( Gr_original_palette, palette, 768 );
385 		memmove( Gr_current_palette, palette, 768 );
386 	}
387 
388 	if ( Gr_inited ) {
389 		if (gr_screen.gf_set_palette) {
390 			(*gr_screen.gf_set_palette)(Gr_current_palette, restrict_font_to_128 );
391 
392 			// Since the palette set code might shuffle the palette,
393 			// reload it into the source palette
394 			if ( palette ) {
395 				memmove( palette, Gr_current_palette, 768 );
396 			}
397 		}
398 
399 		// Update Palette Manager tables
400 		memmove( gr_palette, Gr_current_palette, 768 );
401 		palette_update(name, restrict_font_to_128);
402 	}
403 }
404 
405 
gr_set_palette(const char * name,ubyte * palette,int restrict_font_to_128)406 void gr_set_palette( const char *name, ubyte * palette, int restrict_font_to_128 )
407 {
408 	char *p;
409 	palette_flush();
410 	strcpy_s( Gr_current_palette_name, name );
411 	p = strchr( Gr_current_palette_name, '.' );
412 	if ( p ) *p = 0;
413 	gr_screen.signature = Gr_signature++;
414 	gr_set_palette_internal( name, palette, restrict_font_to_128 );
415 }
416 
gr_screen_resize(int width,int height)417 void gr_screen_resize(int width, int height)
418 {
419 	// this should only be called from FRED!!
420 	if ( !Fred_running ) {
421 		Int3();
422 		return;
423 	}
424 
425 	gr_screen.save_max_w = gr_screen.max_w = gr_screen.max_w_unscaled = width;
426 	gr_screen.save_max_h = gr_screen.max_h = gr_screen.max_h_unscaled = height;
427 
428 	gr_screen.offset_x = gr_screen.offset_x_unscaled = 0;
429 	gr_screen.offset_y = gr_screen.offset_y_unscaled = 0;
430 
431 	gr_screen.clip_left = gr_screen.clip_left_unscaled = 0;
432 	gr_screen.clip_top = gr_screen.clip_top_unscaled = 0;
433 	gr_screen.clip_right = gr_screen.clip_right_unscaled = gr_screen.max_w - 1;
434 	gr_screen.clip_bottom = gr_screen.clip_bottom_unscaled = gr_screen.max_h - 1;
435 	gr_screen.clip_width = gr_screen.clip_width_unscaled = gr_screen.max_w;
436 	gr_screen.clip_height = gr_screen.clip_height_unscaled = gr_screen.max_h;
437 	gr_screen.clip_aspect = i2fl(gr_screen.clip_width) / i2fl(gr_screen.clip_height);
438 
439 	if (gr_screen.custom_size) {
440 		gr_unsize_screen_pos( &gr_screen.max_w_unscaled, &gr_screen.max_h_unscaled );
441 		gr_unsize_screen_pos( &gr_screen.clip_right_unscaled, &gr_screen.clip_bottom_unscaled );
442 		gr_unsize_screen_pos( &gr_screen.clip_width_unscaled, &gr_screen.clip_height_unscaled );
443 	}
444 
445 	gr_screen.save_max_w_unscaled = gr_screen.max_w_unscaled;
446 	gr_screen.save_max_h_unscaled = gr_screen.max_h_unscaled;
447 
448 	if (gr_screen.mode == GR_OPENGL) {
449 		extern void opengl_setup_viewport();
450 		opengl_setup_viewport();
451 	}
452 }
453 
gr_init_sub(int mode,int width,int height,int depth)454 static bool gr_init_sub(int mode, int width, int height, int depth)
455 {
456 	int res = GR_1024;
457 	bool rc = false;
458 
459 	memset( &gr_screen, 0, sizeof(screen) );
460 
461 	if ( ((width == 640) && (height == 480)) || ((width == 1024) && (height == 768)) ) {
462 		gr_screen.custom_size = false;
463 	} else {
464 		gr_screen.custom_size = true;
465 	}
466 
467 	if ( (width >= 1024) && (height >= 600) ) {
468 		res = GR_1024;
469 	} else {
470 		res = GR_640;
471 	}
472 
473 	if (Fred_running) {
474 		gr_screen.custom_size = false;
475 		res = GR_640;
476 		mode = GR_OPENGL;
477 	}
478 
479 	Gr_save_full_resize_X = Gr_full_resize_X = (float)width / ((res == GR_1024) ? 1024.0f : 640.0f);
480 	Gr_save_full_resize_Y = Gr_full_resize_Y = (float)height / ((res == GR_1024) ?  768.0f : 480.0f);
481 
482 	if (gr_screen.custom_size && !Cmdline_stretch_menu) {
483 		float aspect_quotient = ((float)width / (float)height) / (4.0f / 3.0f);
484 
485 		Gr_save_resize_X = Gr_resize_X = Gr_full_resize_X / ((aspect_quotient > 1.0f) ? aspect_quotient : 1.0f);
486 		Gr_save_resize_Y = Gr_resize_Y = Gr_full_resize_Y * ((aspect_quotient < 1.0f) ? aspect_quotient : 1.0f);
487 
488 		Gr_save_menu_offset_X = Gr_menu_offset_X = (aspect_quotient > 1.0f) ? ((width - width / aspect_quotient) / 2.0f) : 0.0f;
489 		Gr_save_menu_offset_Y = Gr_menu_offset_Y = (aspect_quotient < 1.0f) ? ((height - height * aspect_quotient) / 2.0f) : 0.0f;
490 	} else {
491 		Gr_save_resize_X = Gr_resize_X = Gr_full_resize_X;
492 		Gr_save_resize_Y = Gr_resize_Y = Gr_full_resize_Y;
493 
494 		Gr_save_menu_offset_X = Gr_menu_offset_X = 0.0f;
495 		Gr_save_menu_offset_Y = Gr_menu_offset_Y = 0.0f;
496 	}
497 
498 
499 	gr_screen.signature = Gr_signature++;
500 	gr_screen.bits_per_pixel = depth;
501 	gr_screen.bytes_per_pixel= depth / 8;
502 	gr_screen.rendering_to_texture = -1;
503 	gr_screen.recording_state_block = false;
504 	gr_screen.envmap_render_target = -1;
505 	gr_screen.mode = mode;
506 	gr_screen.res = res;
507 	gr_screen.aspect = 1.0f;			// Normal PC screen
508 
509 	gr_screen.save_max_w = gr_screen.max_w = gr_screen.max_w_unscaled = width;
510 	gr_screen.save_max_h = gr_screen.max_h = gr_screen.max_h_unscaled = height;
511 
512 	gr_screen.offset_x = gr_screen.offset_x_unscaled = 0;
513 	gr_screen.offset_y = gr_screen.offset_y_unscaled = 0;
514 
515 	gr_screen.clip_left = gr_screen.clip_left_unscaled = 0;
516 	gr_screen.clip_top = gr_screen.clip_top_unscaled = 0;
517 	gr_screen.clip_right = gr_screen.clip_right_unscaled = gr_screen.max_w - 1;
518 	gr_screen.clip_bottom = gr_screen.clip_bottom_unscaled = gr_screen.max_h - 1;
519 	gr_screen.clip_width = gr_screen.clip_width_unscaled = gr_screen.max_w;
520 	gr_screen.clip_height = gr_screen.clip_height_unscaled = gr_screen.max_h;
521 	gr_screen.clip_aspect = i2fl(gr_screen.clip_width) / i2fl(gr_screen.clip_height);
522 	gr_screen.clip_center_x = (gr_screen.clip_left + gr_screen.clip_right) * 0.5f;
523 	gr_screen.clip_center_y = (gr_screen.clip_top + gr_screen.clip_bottom) * 0.5f;
524 
525 	if (gr_screen.custom_size) {
526 		gr_unsize_screen_pos( &gr_screen.max_w_unscaled, &gr_screen.max_h_unscaled );
527 		gr_unsize_screen_pos( &gr_screen.clip_right_unscaled, &gr_screen.clip_bottom_unscaled );
528 		gr_unsize_screen_pos( &gr_screen.clip_width_unscaled, &gr_screen.clip_height_unscaled );
529 	}
530 
531 	gr_screen.save_max_w_unscaled = gr_screen.max_w_unscaled;
532 	gr_screen.save_max_h_unscaled = gr_screen.max_h_unscaled;
533 
534 #ifdef WIN32
535 	// FRED doesn't need this
536 	if ( !Fred_running && !Is_standalone ) {
537 		// for Windows, we need to do this just before the *_init() calls
538 		extern void win32_create_window(int width, int height);
539 		win32_create_window( width, height );
540 	}
541 #endif
542 
543 	switch (mode) {
544 		case GR_OPENGL:
545 			rc = gr_opengl_init();
546 			break;
547 		case GR_STUB:
548 			rc = gr_stub_init();
549 			break;
550 		default:
551 			Int3();		// Invalid graphics mode
552 	}
553 
554 	if ( !rc ) {
555 		return false;
556 	}
557 
558 	return true;
559 }
560 
gr_init(int d_mode,int d_width,int d_height,int d_depth)561 bool gr_init(int d_mode, int d_width, int d_height, int d_depth)
562 {
563 	int width = 1024, height = 768, depth = 32, mode = GR_OPENGL;
564 	const char *ptr = NULL;
565 	const char *Default_video_settings = "OGL -(1024x768)x32 bit";
566 
567 	if ( !Gr_inited ) {
568 		atexit(gr_close);
569 	}
570 
571 	// If already inited, shutdown the previous graphics
572 	if (Gr_inited) {
573 		switch (gr_screen.mode) {
574 			case GR_OPENGL:
575 				gr_opengl_cleanup();
576 				break;
577 
578 			case GR_STUB:
579 				break;
580 
581 			default:
582 				Int3();		// Invalid graphics mode
583 		}
584 	}
585 
586 	// We cannot continue without this, quit, but try to help the user out first
587 	ptr = os_config_read_string(NULL, NOX("VideocardFs2open"), NULL);
588 
589 	// if we don't have a config string then construct one, using OpenGL 1024x768 32-bit as the default
590 	if (ptr == NULL) {
591 		ptr = Default_video_settings;
592 	}
593 
594 	Assert( ptr != NULL );
595 
596 	// NOTE: The "ptr+5" is to skip over the initial "????-" in the video string.
597 	//       If the format of that string changes you'll have to change this too!!!
598 	if ( sscanf(ptr+5, "(%dx%d)x%d ", &width, &height, &depth) != 3 ) {
599 		Error(LOCATION, "Can't understand 'VideocardFs2open' config entry!");
600 	}
601 
602 	if (Cmdline_res != NULL) {
603 		int tmp_width = 0;
604 		int tmp_height = 0;
605 
606 		if ( sscanf(Cmdline_res, "%dx%d", &tmp_width, &tmp_height) == 2 ) {
607 			width = tmp_width;
608 			height = tmp_height;
609 		}
610 	}
611 
612 	if (d_mode == GR_DEFAULT) {
613 		// OpenGL should be default
614 		mode = GR_OPENGL;
615 	} else {
616 		mode = d_mode;
617 	}
618 
619 	// see if we passed good values, and use those instead of the config settings
620 	if ( (d_width != GR_DEFAULT) && (d_height != GR_DEFAULT) ) {
621 		width = d_width;
622 		height = d_height;
623 	}
624 
625 	if (d_depth != GR_DEFAULT) {
626 		depth = d_depth;
627 	}
628 
629 	// check for hi-res interface files so that we can verify our width/height is correct
630 	bool has_sparky_hi = (cf_exists_full("2_ChoosePilot-m.pcx", CF_TYPE_ANY) && cf_exists_full("2_TechShipData-m.pcx", CF_TYPE_ANY));
631 
632 	// if we don't have it then fall back to 640x480 mode instead
633 	if ( !has_sparky_hi ) {
634 		if ( (width == 1024) && (height == 768) ) {
635 			width = 640;
636 			height = 480;
637 		} else {
638 			width = 800;
639 			height = 600;
640 		}
641 	}
642 
643 	// if we are in standalone mode then just use special defaults
644 	if (Is_standalone) {
645 		mode = GR_STUB;
646 		width = 640;
647 		height = 480;
648 		depth = 16;
649 	}
650 
651 // These compiler macros will force windowed mode at the specified resolution if
652 // built in debug mode.  This helps if you run with the debugger active as the
653 // game won't be switching from fullscreen to minimized every time you hit a breakpoint or
654 // warning message.
655 #ifdef _DEBUG
656 #ifdef _FORCE_DEBUG_WIDESCREEN
657 	width = 1280;
658 	height = 800;
659 	depth = 32;
660 	Cmdline_window = 1;
661 #elif defined(_FORCE_DEBUG_1024)
662 	width = 1024;
663 	height = 768;
664 	depth = 32;
665 	Cmdline_window = 1;
666 #elif defined(_FORCE_DEBUG_640)
667 	width = 640;
668 	height = 480;
669 	depth = 32;
670 	Cmdline_window = 1;
671 #endif
672 #endif
673 
674 	// now try to actually init everything...
675 	if ( gr_init_sub(mode, width, height, depth) == false ) {
676 		return false;
677 	}
678 
679 	gr_set_palette_internal(Gr_current_palette_name, NULL, 0);
680 
681 	bm_init();
682 
683 	if (Gr_cursor < 0) {
684 		int w, h;
685 
686 		Gr_cursor = bm_load( "cursor" );
687 
688 		if (Gr_cursor >= 0) {
689 			// get cursor size, so that we can be sure to account for the full thing
690 			// in later cursor hiding code
691 			bm_get_info(Gr_cursor, &w, &h);
692 			Gr_cursor_size = MAX(w, h);
693 
694 			if (Gr_cursor_size <= 0) {
695 				Int3();
696 				Gr_cursor_size = 32;
697 			}
698 		}
699 	}
700 
701 	// load the web pointer cursor bitmap
702 	if (Web_cursor_bitmap < 0) {
703 		//if it still hasn't loaded then this usually means that the executable isn't in the same directory as the main fs2 install
704 		if ( (Web_cursor_bitmap = bm_load_animation("cursorweb")) < 0 ) {
705 			Error(LOCATION, "\nWeb cursor bitmap not found.  This is most likely due to one of three reasons:\n"
706 				"\t1) You're running FreeSpace Open from somewhere other than your FreeSpace 2 folder;\n"
707 				"\t2) You've somehow corrupted your FreeSpace 2 installation, e.g. by modifying or removing the retail VP files;\n"
708 				"\t3) You haven't installed FreeSpace 2 at all.  (Note that installing FreeSpace Open does NOT remove the need for a FreeSpace 2 installation.)\n"
709 				"Number 1 can be fixed by simply moving the FreeSpace Open executable file to the FreeSpace 2 folder.  Numbers 2 and 3 can be fixed by installing or reinstalling FreeSpace 2.\n");
710 		}
711 	}
712 
713 	mprintf(("GRAPHICS: Initializing default colors...\n"));
714 
715 	gr_set_color(0,0,0);
716 	gr_set_clear_color(0, 0, 0);
717 
718 	gr_set_shader(NULL);
719 
720 	os_set_title(Osreg_title);
721 
722 	Gr_inited = 1;
723 
724 	return true;
725 }
726 
gr_force_windowed()727 void gr_force_windowed()
728 {
729 	if ( !Gr_inited ) {
730 		return;
731 	}
732 
733 	switch( gr_screen.mode ) {
734 		case GR_OPENGL:
735 			break;
736 		case GR_STUB:
737 			break;
738 		default:
739 			Int3();		// Invalid graphics mode
740 	}
741 
742 	if ( Os_debugger_running ) {
743 		Sleep(1000);
744 	}
745 }
746 
747 int gr_activated = 0;
gr_activate(int active)748 void gr_activate(int active)
749 {
750 
751 	if (gr_activated == active) {
752 		return;
753 	}
754 	gr_activated = active;
755 
756 	if ( !Gr_inited ) {
757 		return;
758 	}
759 
760 	switch( gr_screen.mode ) {
761 		case GR_OPENGL:
762 			extern void gr_opengl_activate(int active);
763 			gr_opengl_activate(active);
764 			break;
765 		case GR_STUB:
766 			break;
767 		default:
768 			Int3();		// Invalid graphics mode
769 	}
770 
771 }
772 
773 // color stuff
gr_get_color(int * r,int * g,int * b)774 void gr_get_color( int *r, int *g, int *b )
775 {
776 	if (r) *r = gr_screen.current_color.red;
777 	if (g) *g = gr_screen.current_color.green;
778 	if (b) *b = gr_screen.current_color.blue;
779 }
780 
gr_init_color(color * c,int r,int g,int b)781 void gr_init_color(color *c, int r, int g, int b)
782 {
783 	CAP(r, 0, 255);
784 	CAP(g, 0, 255);
785 	CAP(b, 0, 255);
786 
787 	c->screen_sig = gr_screen.signature;
788 	c->red = (ubyte)r;
789 	c->green = (ubyte)g;
790 	c->blue = (ubyte)b;
791 	c->alpha = 255;
792 	c->ac_type = AC_TYPE_NONE;
793 	c->alphacolor = -1;
794 	c->is_alphacolor = 0;
795 	c->magic = 0xAC01;
796 	c->raw8 = 0;
797 }
798 
gr_init_alphacolor(color * clr,int r,int g,int b,int alpha,int type)799 void gr_init_alphacolor( color *clr, int r, int g, int b, int alpha, int type )
800 {
801 	CAP(r, 0, 255);
802 	CAP(g, 0, 255);
803 	CAP(b, 0, 255);
804 	CAP(alpha, 0, 255);
805 
806 	gr_init_color( clr, r, g, b );
807 
808 	clr->alpha = (ubyte)alpha;
809 	clr->ac_type = (ubyte)type;
810 	clr->alphacolor = -1;
811 	clr->is_alphacolor = 1;
812 }
813 
gr_set_color(int r,int g,int b)814 void gr_set_color( int r, int g, int b )
815 {
816 	Assert((r >= 0) && (r < 256));
817 	Assert((g >= 0) && (g < 256));
818 	Assert((b >= 0) && (b < 256));
819 
820 	gr_init_color( &gr_screen.current_color, r, g, b );
821 }
822 
gr_set_color_fast(color * dst)823 void gr_set_color_fast(color *dst)
824 {
825 	if ( dst->screen_sig != gr_screen.signature ) {
826 		if (dst->is_alphacolor) {
827 			gr_init_alphacolor( dst, dst->red, dst->green, dst->blue, dst->alpha, dst->ac_type );
828 		} else {
829 			gr_init_color( dst, dst->red, dst->green, dst->blue );
830 		}
831 	}
832 
833 	gr_screen.current_color = *dst;
834 }
835 
836 // shader functions
gr_create_shader(shader * shade,ubyte r,ubyte g,ubyte b,ubyte c)837 void gr_create_shader(shader *shade, ubyte r, ubyte g, ubyte b, ubyte c )
838 {
839 	shade->screen_sig = gr_screen.signature;
840 	shade->r = r;
841 	shade->g = g;
842 	shade->b = b;
843 	shade->c = c;
844 }
845 
gr_set_shader(shader * shade)846 void gr_set_shader(shader *shade)
847 {
848 	if (shade) {
849 		if (shade->screen_sig != gr_screen.signature) {
850 			gr_create_shader( shade, shade->r, shade->g, shade->b, shade->c );
851 		}
852 		gr_screen.current_shader = *shade;
853 	} else {
854 		gr_create_shader( &gr_screen.current_shader, 0, 0, 0, 0 );
855 	}
856 }
857 
858 /**
859  * Set the bitmap for the mouse pointer.  This is called by the animating mouse
860  * pointer code.
861  *
862  * The lock parameter just locks basically disables the next call of this function that doesn't
863  * have an unlock feature.  If adding in more cursor-changing situations, be aware of
864  * unexpected results. You have been warned.
865  *
866  * @todo investigate memory leak of original Gr_cursor bitmap when this is called
867  */
gr_set_cursor_bitmap(int n,int lock)868 void gr_set_cursor_bitmap(int n, int lock)
869 {
870 	int w, h;
871 	static int locked = 0;
872 
873 	if ( !locked || (lock == GR_CURSOR_UNLOCK) ) {
874 		// if we are changing the cursor to something different
875 		// then unload the previous cursor's data - taylor
876 		if ( (Gr_cursor >= 0) && (Gr_cursor != n) ) {
877 			// be sure to avoid changing a cursor which is simply another frame
878 			if ( (GL_cursor_nframes < 2) || ((n - Gr_cursor) >= GL_cursor_nframes) ) {
879 				gr_unset_cursor_bitmap(Gr_cursor);
880 			}
881 		}
882 
883 		if (n != Gr_cursor) {
884 			// get cursor size, so that we can be sure to account for the full thing
885 			// in later cursor hiding code
886 			bm_get_info(n, &w, &h, NULL, &GL_cursor_nframes);
887 			Assert( GL_cursor_nframes > 0 );
888 
889 			Gr_cursor_size = MAX(w, h);
890 
891 			if (Gr_cursor_size <= 0) {
892 				Int3();
893 				Gr_cursor_size = 32;
894 			}
895 		}
896 
897 		Gr_cursor = n;
898 	} else {
899 		locked = 0;
900 	}
901 
902 	if (lock == GR_CURSOR_LOCK) {
903 		locked = 1;
904 	}
905 }
906 
gr_unset_cursor_bitmap(int n)907 void gr_unset_cursor_bitmap(int n)
908 {
909 	if (n < 0) {
910 		return;
911 	}
912 
913 	if (Gr_cursor == n) {
914 		bm_unload(Gr_cursor);
915 		Gr_cursor = -1;
916 	}
917 }
918 
919 /**
920  * Retrieves the current bitmap
921  * Used in UI_GADGET to save/restore current cursor state
922  */
gr_get_cursor_bitmap()923 int gr_get_cursor_bitmap()
924 {
925 	return Gr_cursor;
926 }
927 
928 // new bitmap functions
gr_bitmap(int _x,int _y,int resize_mode)929 void gr_bitmap(int _x, int _y, int resize_mode)
930 {
931 	int _w, _h;
932 	float x, y, w, h;
933 	vertex verts[4];
934 
935 	if (gr_screen.mode == GR_STUB) {
936 		return;
937 	}
938 
939 	bm_get_info(gr_screen.current_bitmap, &_w, &_h, NULL, NULL, NULL);
940 
941 	x = i2fl(_x);
942 	y = i2fl(_y);
943 	w = i2fl(_w);
944 	h = i2fl(_h);
945 
946 	// I will tidy this up later - RT
947 	if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) {
948 		gr_resize_screen_posf(&x, &y, &w, &h, resize_mode);
949 	}
950 
951 	memset(verts, 0, sizeof(verts));
952 
953 	verts[0].screen.xyw.x = x;
954 	verts[0].screen.xyw.y = y;
955 	verts[0].texture_position.u = 0.0f;
956 	verts[0].texture_position.v = 0.0f;
957 
958 	verts[1].screen.xyw.x = x + w;
959 	verts[1].screen.xyw.y = y;
960 	verts[1].texture_position.u = 1.0f;
961 	verts[1].texture_position.v = 0.0f;
962 
963 	verts[2].screen.xyw.x = x + w;
964 	verts[2].screen.xyw.y = y + h;
965 	verts[2].texture_position.u = 1.0f;
966 	verts[2].texture_position.v = 1.0f;
967 
968 	verts[3].screen.xyw.x = x;
969 	verts[3].screen.xyw.y = y + h;
970 	verts[3].texture_position.u = 0.0f;
971 	verts[3].texture_position.v = 1.0f;
972 
973 	// turn off zbuffering
974 	int saved_zbuffer_mode = gr_zbuffer_get();
975 	gr_zbuffer_set(GR_ZBUFF_NONE);
976 
977 	gr_render(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_INTERFACE);
978 
979 	gr_zbuffer_set(saved_zbuffer_mode);
980 }
981 
gr_bitmap_uv(int _x,int _y,int _w,int _h,float _u0,float _v0,float _u1,float _v1,int resize_mode)982 void gr_bitmap_uv(int _x, int _y, int _w, int _h, float _u0, float _v0, float _u1, float _v1, int resize_mode)
983 {
984 	float x, y, w, h;
985 	vertex verts[4];
986 
987 	if (gr_screen.mode == GR_STUB) {
988 		return;
989 	}
990 
991 	x = i2fl(_x);
992 	y = i2fl(_y);
993 	w = i2fl(_w);
994 	h = i2fl(_h);
995 
996 	// I will tidy this up later - RT
997 	if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) {
998 		gr_resize_screen_posf(&x, &y, &w, &h, resize_mode);
999 	}
1000 
1001 	memset(verts, 0, sizeof(verts));
1002 
1003 	verts[0].screen.xyw.x = x;
1004 	verts[0].screen.xyw.y = y;
1005 	verts[0].texture_position.u = _u0;
1006 	verts[0].texture_position.v = _v0;
1007 
1008 	verts[1].screen.xyw.x = x + w;
1009 	verts[1].screen.xyw.y = y;
1010 	verts[1].texture_position.u = _u1;
1011 	verts[1].texture_position.v = _v0;
1012 
1013 	verts[2].screen.xyw.x = x + w;
1014 	verts[2].screen.xyw.y = y + h;
1015 	verts[2].texture_position.u = _u1;
1016 	verts[2].texture_position.v = _v1;
1017 
1018 	verts[3].screen.xyw.x = x;
1019 	verts[3].screen.xyw.y = y + h;
1020 	verts[3].texture_position.u = _u0;
1021 	verts[3].texture_position.v = _v1;
1022 
1023 	// turn off zbuffering
1024 	int saved_zbuffer_mode = gr_zbuffer_get();
1025 	gr_zbuffer_set(GR_ZBUFF_NONE);
1026 
1027 	gr_render(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_INTERFACE);
1028 
1029 	gr_zbuffer_set(saved_zbuffer_mode);
1030 }
1031 
1032 // NEW new bitmap functions -Bobboau
gr_bitmap_list(bitmap_2d_list * list,int n_bm,int resize_mode)1033 void gr_bitmap_list(bitmap_2d_list* list, int n_bm, int resize_mode)
1034 {
1035 	for (int i = 0; i < n_bm; i++) {
1036 		bitmap_2d_list *l = &list[i];
1037 
1038 		bm_get_info(gr_screen.current_bitmap, &l->w, &l->h, NULL, NULL, NULL);
1039 
1040 		if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) {
1041 			gr_resize_screen_pos(&l->x, &l->y, &l->w, &l->h, resize_mode);
1042 		}
1043 	}
1044 
1045 	g3_draw_2d_poly_bitmap_list(list, n_bm, TMAP_FLAG_INTERFACE);
1046 }
1047 
1048 // _->NEW<-_ NEW new bitmap functions -Bobboau
1049 //takes a list of rectangles that have assosiated rectangles in a texture
gr_bitmap_list(bitmap_rect_list * list,int n_bm,int resize_mode)1050 void gr_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode)
1051 {
1052 	for(int i = 0; i < n_bm; i++) {
1053 		bitmap_2d_list *l = &list[i].screen_rect;
1054 
1055 		// if no valid hight or width values were given get some from the bitmap
1056 		if ( (l->w <= 0) || (l->h <= 0) ) {
1057 			bm_get_info(gr_screen.current_bitmap, &l->w, &l->h, NULL, NULL, NULL);
1058 		}
1059 
1060 		if ( resize_mode != GR_RESIZE_NONE && (gr_screen.custom_size || (gr_screen.rendering_to_texture != -1)) ) {
1061 			gr_resize_screen_pos(&l->x, &l->y, &l->w, &l->h, resize_mode);
1062 		}
1063 	}
1064 
1065 	g3_draw_2d_poly_bitmap_rect_list(list, n_bm, TMAP_FLAG_INTERFACE);
1066 }
1067 
1068 
1069 /**
1070  * Given endpoints, and thickness, calculate coords of the endpoint
1071  */
gr_pline_helper(vec3d * out,vec3d * in1,vec3d * in2,int thickness)1072 void gr_pline_helper(vec3d *out, vec3d *in1, vec3d *in2, int thickness)
1073 {
1074 	vec3d slope;
1075 
1076 	// slope of the line
1077 	if(vm_vec_same(in1, in2)) {
1078 		slope = vmd_zero_vector;
1079 	} else {
1080 		vm_vec_sub(&slope, in2, in1);
1081 		float temp = -slope.xyz.x;
1082 		slope.xyz.x = slope.xyz.y;
1083 		slope.xyz.y = temp;
1084 		vm_vec_normalize(&slope);
1085 	}
1086 	// get the points
1087 	vm_vec_scale_add(out, in1, &slope, (float)thickness);
1088 }
1089 
1090 /**
1091  * Special function for drawing polylines.
1092  *
1093  * This function is specifically intended for polylines where each section
1094  * is no more than 90 degrees away from a previous section.
1095  * Moreover, it is _really_ intended for use with 45 degree angles.
1096  */
gr_pline_special(vec3d ** pts,int num_pts,int thickness,int resize_mode)1097 void gr_pline_special(vec3d **pts, int num_pts, int thickness,int resize_mode)
1098 {
1099 	vec3d s1, s2, e1, e2, dir;
1100 	vec3d last_e1, last_e2;
1101 	vertex v[4];
1102 	vertex *verts[4] = {&v[0], &v[1], &v[2], &v[3]};
1103 	int saved_zbuffer_mode, idx;
1104 	int started_frame = 0;
1105 
1106 	// if we have less than 2 pts, bail
1107 	if(num_pts < 2) {
1108 		return;
1109 	}
1110 
1111 	extern int G3_count;
1112 	if(G3_count == 0) {
1113 		g3_start_frame(1);
1114 		started_frame = 1;
1115 	}
1116 
1117 	// turn off zbuffering
1118 	saved_zbuffer_mode = gr_zbuffer_get();
1119 	gr_zbuffer_set(GR_ZBUFF_NONE);
1120 
1121 	// turn off culling
1122 	int cull = gr_set_cull(0);
1123 
1124 	// draw each section
1125 	last_e1 = vmd_zero_vector;
1126 	last_e2 = vmd_zero_vector;
1127 	int j;
1128 	for(idx=0; idx<num_pts-1; idx++) {
1129 		// get the start and endpoints
1130 		s1 = *pts[idx];											// start 1 (on the line)
1131 		gr_pline_helper(&s2, pts[idx], pts[idx+1], thickness);	// start 2
1132 		e1 = *pts[idx+1];										// end 1 (on the line)
1133 		vm_vec_sub(&dir, pts[idx+1], pts[idx]);
1134 		vm_vec_add(&e2, &s2, &dir);								// end 2
1135 
1136 		// stuff coords
1137 		v[0].screen.xyw.x = (float)ceil(s1.xyz.x);
1138 		v[0].screen.xyw.y = (float)ceil(s1.xyz.y);
1139 		v[0].screen.xyw.w = 0.0f;
1140 		v[0].texture_position.u = 0.5f;
1141 		v[0].texture_position.v = 0.5f;
1142 		v[0].flags = PF_PROJECTED;
1143 		v[0].codes = 0;
1144 		v[0].r = gr_screen.current_color.red;
1145 		v[0].g = gr_screen.current_color.green;
1146 		v[0].b = gr_screen.current_color.blue;
1147 
1148 		v[1].screen.xyw.x = (float)ceil(s2.xyz.x);
1149 		v[1].screen.xyw.y = (float)ceil(s2.xyz.y);
1150 		v[1].screen.xyw.w = 0.0f;
1151 		v[1].texture_position.u = 0.5f;
1152 		v[1].texture_position.v = 0.5f;
1153 		v[1].flags = PF_PROJECTED;
1154 		v[1].codes = 0;
1155 		v[1].r = gr_screen.current_color.red;
1156 		v[1].g = gr_screen.current_color.green;
1157 		v[1].b = gr_screen.current_color.blue;
1158 
1159 		v[2].screen.xyw.x = (float)ceil(e2.xyz.x);
1160 		v[2].screen.xyw.y = (float)ceil(e2.xyz.y);
1161 		v[2].screen.xyw.w = 0.0f;
1162 		v[2].texture_position.u = 0.5f;
1163 		v[2].texture_position.v = 0.5f;
1164 		v[2].flags = PF_PROJECTED;
1165 		v[2].codes = 0;
1166 		v[2].r = gr_screen.current_color.red;
1167 		v[2].g = gr_screen.current_color.green;
1168 		v[2].b = gr_screen.current_color.blue;
1169 
1170 		v[3].screen.xyw.x = (float)ceil(e1.xyz.x);
1171 		v[3].screen.xyw.y = (float)ceil(e1.xyz.y);
1172 		v[3].screen.xyw.w = 0.0f;
1173 		v[3].texture_position.u = 0.5f;
1174 		v[3].texture_position.v = 0.5f;
1175 		v[3].flags = PF_PROJECTED;
1176 		v[3].codes = 0;
1177 		v[3].r = gr_screen.current_color.red;
1178 		v[3].g = gr_screen.current_color.green;
1179 		v[3].b = gr_screen.current_color.blue;
1180 
1181 		//We could really do this better...but oh well. _WMC
1182 		if(resize_mode != GR_RESIZE_NONE) {
1183 			for(j=0;j<4;j++) {
1184 				gr_resize_screen_posf(&v[j].screen.xyw.x,&v[j].screen.xyw.y,NULL,NULL,resize_mode);
1185 			}
1186 		}
1187 
1188 		// draw the polys
1189 		g3_draw_poly_constant_sw(4, verts, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB, 0.1f);
1190 
1191 		// if we're past the first section, draw a "patch" triangle to fill any gaps
1192 		if(idx > 0) {
1193 			// stuff coords
1194 			v[0].screen.xyw.x = (float)ceil(s1.xyz.x);
1195 			v[0].screen.xyw.y = (float)ceil(s1.xyz.y);
1196 			v[0].screen.xyw.w = 0.0f;
1197 			v[0].texture_position.u = 0.5f;
1198 			v[0].texture_position.v = 0.5f;
1199 			v[0].flags = PF_PROJECTED;
1200 			v[0].codes = 0;
1201 			v[0].r = gr_screen.current_color.red;
1202 			v[0].g = gr_screen.current_color.green;
1203 			v[0].b = gr_screen.current_color.blue;
1204 
1205 			v[1].screen.xyw.x = (float)ceil(s2.xyz.x);
1206 			v[1].screen.xyw.y = (float)ceil(s2.xyz.y);
1207 			v[1].screen.xyw.w = 0.0f;
1208 			v[1].texture_position.u = 0.5f;
1209 			v[1].texture_position.v = 0.5f;
1210 			v[1].flags = PF_PROJECTED;
1211 			v[1].codes = 0;
1212 			v[1].r = gr_screen.current_color.red;
1213 			v[1].g = gr_screen.current_color.green;
1214 			v[1].b = gr_screen.current_color.blue;
1215 
1216 
1217 			v[2].screen.xyw.x = (float)ceil(last_e2.xyz.x);
1218 			v[2].screen.xyw.y = (float)ceil(last_e2.xyz.y);
1219 			v[2].screen.xyw.w = 0.0f;
1220 			v[2].texture_position.u = 0.5f;
1221 			v[2].texture_position.v = 0.5f;
1222 			v[2].flags = PF_PROJECTED;
1223 			v[2].codes = 0;
1224 			v[2].r = gr_screen.current_color.red;
1225 			v[2].g = gr_screen.current_color.green;
1226 			v[2].b = gr_screen.current_color.blue;
1227 
1228 			//Inefficiency or flexibility? you be the judge -WMC
1229 			if(resize_mode != GR_RESIZE_NONE) {
1230 				for(j=0;j<3;j++) {
1231 					gr_resize_screen_posf(&v[j].screen.xyw.x,&v[j].screen.xyw.y,NULL,NULL,resize_mode);
1232 				}
1233 			}
1234 
1235 			g3_draw_poly_constant_sw(3, verts, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB, 0.1f);
1236 		}
1237 
1238 		// store our endpoints
1239 		last_e1 = e1;
1240 		last_e2 = e2;
1241 	}
1242 
1243 	if(started_frame) {
1244 		g3_end_frame();
1245 	}
1246 
1247 	// restore zbuffer mode
1248 	gr_zbuffer_set(saved_zbuffer_mode);
1249 
1250 	// restore culling
1251 	gr_set_cull(cull);
1252 }
1253 
find_first_vertex(int idx)1254 int poly_list::find_first_vertex(int idx)
1255 {
1256 	vec3d *o_norm = &norm[idx];
1257 	vertex *o_vert = &vert[idx];
1258 	vec3d *p_norm = &norm[0];
1259 	vertex *p_vert = &vert[0];
1260 
1261 	// we should always equal ourselves, so just use that as the stopping point
1262 	for (int i = 0; i < idx; i++) {
1263 		if ( (*p_norm == *o_norm)
1264 			&& (p_vert->world == o_vert->world)
1265 			&& (p_vert->texture_position == o_vert->texture_position) )
1266 		{
1267 			return i;
1268 		}
1269 
1270 		++p_norm;
1271 		++p_vert;
1272 	}
1273 
1274 	return idx;
1275 }
1276 
1277 /**
1278  * Given a list (plist) find the index within the indexed list that the vert at position idx within list is at
1279  */
find_index(poly_list * plist,int idx)1280 int poly_list::find_index(poly_list *plist, int idx)
1281 {
1282 	vec3d *o_norm = &plist->norm[idx];
1283 	vertex *o_vert = &plist->vert[idx];
1284 	vec3d *p_norm = &norm[0];
1285 	vertex *p_vert = &vert[0];
1286 
1287 	for (int i = 0; i < n_verts; i++) {
1288 		if ( (*p_norm == *o_norm)
1289 			&& (p_vert->world == o_vert->world)
1290 			&& (p_vert->texture_position == o_vert->texture_position))
1291 		{
1292 			return i;
1293 		}
1294 
1295 		++p_vert;
1296 		++p_norm;
1297 	}
1298 
1299 	return -1;
1300 }
1301 
1302 
allocate(int _verts)1303 void poly_list::allocate(int _verts)
1304 {
1305 	if (_verts <= currently_allocated)
1306 		return;
1307 
1308 	if (vert != NULL) {
1309 		vm_free(vert);
1310 		vert = NULL;
1311 	}
1312 
1313 	if (norm != NULL) {
1314 		vm_free(norm);
1315 		norm = NULL;
1316 	}
1317 
1318 	if (tsb != NULL) {
1319 		vm_free(tsb);
1320 		tsb = NULL;
1321 	}
1322 
1323 	if (_verts) {
1324 		vert = (vertex*)vm_malloc(sizeof(vertex) * _verts);
1325 		norm = (vec3d*)vm_malloc(sizeof(vec3d) * _verts);
1326 
1327 		if (Cmdline_normal) {
1328 			tsb = (tsb_t*)vm_malloc(sizeof(tsb_t) * _verts);
1329 		}
1330 	}
1331 
1332 	n_verts = 0;
1333 	currently_allocated = _verts;
1334 }
1335 
~poly_list()1336 poly_list::~poly_list()
1337 {
1338 	if (vert != NULL) {
1339 		vm_free(vert);
1340 		vert = NULL;
1341 	}
1342 
1343 	if (norm != NULL) {
1344 		vm_free(norm);
1345 		norm = NULL;
1346 	}
1347 
1348 	if (tsb != NULL) {
1349 		vm_free(tsb);
1350 		tsb = NULL;
1351 	}
1352 }
1353 
calculate_tangent()1354 void poly_list::calculate_tangent()
1355 {
1356 	vertex *v0, *v1, *v2;
1357 	vec3d *t0, *t1, *t2;
1358 	vec3d side0, side1;
1359 	vec3d vt0, vt1;
1360 	float deltaU0, deltaV0, deltaU1, deltaV1;
1361 	vec3d tangent, binormal, cross;
1362 	float magg, scale;
1363 
1364 	if ( !Cmdline_normal ) {
1365 		return;
1366 	}
1367 
1368 	Assert( !(n_verts % 3) );
1369 
1370 	for (int i = 0; i < n_verts; i += 3) {
1371 		// vertex (reading)
1372 		v0 = &vert[i];
1373 		v1 = &vert[i+1];
1374 		v2 = &vert[i+2];
1375 		// tangents (writing)
1376 		t0 = &tsb[i].tangent;
1377 		t1 = &tsb[i+1].tangent;
1378 		t2 = &tsb[i+2].tangent;
1379 
1380 
1381 		deltaU0 = v1->texture_position.u - v0->texture_position.u;
1382 		deltaV0 = v1->texture_position.v - v0->texture_position.v;
1383 
1384 		deltaU1 = v2->texture_position.u - v0->texture_position.u;
1385 		deltaV1 = v2->texture_position.v - v0->texture_position.v;
1386 
1387 		// quick short circuit for NULL case
1388 		float n = (deltaU0 * deltaV1) - (deltaU1 * deltaV0);
1389 
1390 		if (n == 0.0f) {
1391 			// hit NULL, so just set identity
1392 			tangent  = vmd_x_vector;
1393 			binormal = vmd_y_vector;
1394 		} else {
1395 			float blah = 1.0f / n;
1396 
1397 			vm_vec_sub(&side0, &v1->world, &v0->world);
1398 			vm_vec_sub(&side1, &v2->world, &v0->world);
1399 
1400 			// tangent
1401 			vm_vec_copy_scale(&vt0, &side0, deltaV1);
1402 			vm_vec_copy_scale(&vt1, &side1, deltaV0);
1403 			vm_vec_sub(&tangent, &vt0, &vt1);
1404 			vm_vec_scale(&tangent, blah);
1405 
1406 			// binormal
1407 			vm_vec_copy_scale(&vt0, &side0, deltaU1);
1408 			vm_vec_copy_scale(&vt1, &side1, deltaU0);
1409 			vm_vec_sub(&binormal, &vt0, &vt1);
1410 			vm_vec_scale(&binormal, blah);
1411 		}
1412 
1413 		// orthogonalize tangent (for all 3 verts)
1414 		magg = vm_vec_dot(&norm[i], &tangent);
1415 		vm_vec_scale_sub(t0, &tangent, &norm[i], magg);
1416 		vm_vec_normalize_safe(t0);
1417 
1418 		magg = vm_vec_dot(&norm[i+1], &tangent);
1419 		vm_vec_scale_sub(t1, &tangent, &norm[i+1], magg);
1420 		vm_vec_normalize_safe(t1);
1421 
1422 		magg = vm_vec_dot(&norm[i+2], &tangent);
1423 		vm_vec_scale_sub(t2, &tangent, &norm[i+2], magg);
1424 		vm_vec_normalize_safe(t2);
1425 
1426 		// compute handedness (for all 3 verts)
1427 		vm_vec_crossprod(&cross, &norm[i], &tangent);
1428 		scale = vm_vec_dot(&cross, &binormal);
1429 		tsb[i].scaler = (scale < 0.0f) ? -1.0f : 1.0f;
1430 
1431 		vm_vec_crossprod(&cross, &norm[i+1], &tangent);
1432 		scale = vm_vec_dot(&cross, &binormal);
1433 		tsb[i+1].scaler = (scale < 0.0f) ? -1.0f : 1.0f;
1434 
1435 		vm_vec_crossprod(&cross, &norm[i+2], &tangent);
1436 		scale = vm_vec_dot(&cross, &binormal);
1437 		tsb[i+2].scaler = (scale < 0.0f) ? -1.0f : 1.0f;
1438 	}
1439 }
1440 
1441 static poly_list buffer_list_internal;
1442 
make_index_buffer(SCP_vector<int> & vertex_list)1443 void poly_list::make_index_buffer(SCP_vector<int> &vertex_list)
1444 {
1445 	int nverts = 0;
1446 	int j, z = 0;
1447 	ubyte *nverts_good = NULL;
1448 
1449 	// calculate tangent space data (must be done early)
1450 	calculate_tangent();
1451 
1452 	// using vm_malloc() here rather than 'new' so we get the extra out-of-memory check
1453 	nverts_good = (ubyte *) vm_malloc(n_verts);
1454 
1455     Assert( nverts_good != NULL );
1456 	if ( nverts_good == NULL )
1457 		return;
1458 
1459 	memset( nverts_good, 0, n_verts );
1460 
1461 	vertex_list.reserve(n_verts);
1462 
1463 	for (j = 0; j < n_verts; j++) {
1464 		if (find_first_vertex(j) == j) {
1465 			nverts++;
1466 			nverts_good[j] = 1;
1467 			vertex_list.push_back(j);
1468 		}
1469 	}
1470 
1471 	// if there is nothig to change then bail
1472 	if (n_verts == nverts) {
1473 		if (nverts_good != NULL) {
1474 			vm_free(nverts_good);
1475 		}
1476 
1477 		return;
1478 	}
1479 
1480 	buffer_list_internal.n_verts = 0;
1481 	buffer_list_internal.allocate(nverts);
1482 
1483 	for (j = 0; j < n_verts; j++) {
1484 		if ( !nverts_good[j] ) {
1485 			continue;
1486 		}
1487 
1488 		buffer_list_internal.vert[z] = vert[j];
1489 		buffer_list_internal.norm[z] = norm[j];
1490 
1491 		if (Cmdline_normal) {
1492 			buffer_list_internal.tsb[z] = tsb[j];
1493 		}
1494 
1495 		buffer_list_internal.n_verts++;
1496 		z++;
1497 	}
1498 
1499 	Assert(nverts == buffer_list_internal.n_verts);
1500 
1501 	if (nverts_good != NULL) {
1502 		vm_free(nverts_good);
1503 	}
1504 
1505 	(*this) = buffer_list_internal;
1506 }
1507 
operator =(poly_list & other_list)1508 poly_list& poly_list::operator = (poly_list &other_list)
1509 {
1510 	allocate(other_list.n_verts);
1511 
1512 	memcpy(norm, other_list.norm, sizeof(vec3d) * other_list.n_verts);
1513 	memcpy(vert, other_list.vert, sizeof(vertex) * other_list.n_verts);
1514 
1515 	if (Cmdline_normal) {
1516 		memcpy(tsb, other_list.tsb, sizeof(tsb_t) * other_list.n_verts);
1517 	}
1518 
1519 	n_verts = other_list.n_verts;
1520 
1521 	return *this;
1522 }
1523 
gr_shield_icon(coord2d coords[6],int resize_mode)1524 void gr_shield_icon(coord2d coords[6], int resize_mode)
1525 {
1526 	if (gr_screen.mode == GR_STUB) {
1527 		return;
1528 	}
1529 
1530 	if (resize_mode != GR_RESIZE_NONE) {
1531 		gr_resize_screen_pos(&coords[0].x, &coords[0].y, NULL, NULL, resize_mode);
1532 		gr_resize_screen_pos(&coords[1].x, &coords[1].y, NULL, NULL, resize_mode);
1533 		gr_resize_screen_pos(&coords[2].x, &coords[2].y, NULL, NULL, resize_mode);
1534 		gr_resize_screen_pos(&coords[3].x, &coords[3].y, NULL, NULL, resize_mode);
1535 		gr_resize_screen_pos(&coords[4].x, &coords[4].y, NULL, NULL, resize_mode);
1536 		gr_resize_screen_pos(&coords[5].x, &coords[5].y, NULL, NULL, resize_mode);
1537 	}
1538 
1539 	g3_draw_2d_shield_icon(coords,
1540 		gr_screen.current_color.red,
1541 		gr_screen.current_color.green,
1542 		gr_screen.current_color.blue,
1543 		gr_screen.current_color.alpha);
1544 }
1545 
gr_rect(int x,int y,int w,int h,int resize_mode)1546 void gr_rect(int x, int y, int w, int h, int resize_mode)
1547 {
1548 	if (gr_screen.mode == GR_STUB) {
1549 		return;
1550 	}
1551 
1552 	if (resize_mode != GR_RESIZE_NONE) {
1553 		gr_resize_screen_pos(&x, &y, &w, &h, resize_mode);
1554 	}
1555 
1556 	g3_draw_2d_rect(x, y, w, h,
1557 		gr_screen.current_color.red,
1558 		gr_screen.current_color.green,
1559 		gr_screen.current_color.blue,
1560 		gr_screen.current_color.alpha);
1561 }
1562 
gr_shade(int x,int y,int w,int h,int resize_mode)1563 void gr_shade(int x, int y, int w, int h, int resize_mode)
1564 {
1565 	int r, g, b, a;
1566 
1567 	if (gr_screen.mode == GR_STUB) {
1568 		return;
1569 	}
1570 
1571 	if (resize_mode != GR_RESIZE_NONE) {
1572 		gr_resize_screen_pos(&x, &y, &w, &h, resize_mode);
1573 	}
1574 
1575 	r = (int)gr_screen.current_shader.r;
1576 	g = (int)gr_screen.current_shader.g;
1577 	b = (int)gr_screen.current_shader.b;
1578 	a = (int)gr_screen.current_shader.c;
1579 
1580 	g3_draw_2d_rect(x, y, w, h, r, g, b, a);
1581 }
1582 
gr_set_bitmap(int bitmap_num,int alphablend_mode,int bitblt_mode,float alpha)1583 void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
1584 {
1585 	gr_screen.current_alpha = alpha;
1586 	gr_screen.current_alphablend_mode = alphablend_mode;
1587 	gr_screen.current_bitblt_mode = bitblt_mode;
1588 	gr_screen.current_bitmap = bitmap_num;
1589 }
1590 
gr_flip()1591 void gr_flip()
1592 {
1593 	// m!m avoid running CHA_ONFRAME when the "Quit mission" popup is shown. See mantis 2446 for reference
1594 	if (!quit_mission_popup_shown)
1595 	{
1596 		//WMC - Evaluate global hook if not override.
1597 		Script_system.RunBytecode(Script_globalhook);
1598 		//WMC - Do conditional hooks. Yippee!
1599 		Script_system.RunCondition(CHA_ONFRAME);
1600 		//WMC - Do scripting reset stuff
1601 		Script_system.EndFrame();
1602 	}
1603 
1604 	gr_screen.gf_flip();
1605 }
1606