1 //----------------------------------------------------------------------------
2 //  EDGE 2D DRAWING STUFF
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 
19 #include "i_defs.h"
20 #include "i_defs_gl.h"
21 
22 #include "ddf/font.h"
23 
24 #include "g_game.h"
25 #include "r_misc.h"
26 #include "r_gldefs.h"
27 #include "r_units.h"
28 #include "r_colormap.h"
29 #include "hu_draw.h"
30 #include "r_modes.h"
31 #include "r_image.h"
32 #include "r_misc.h"     //  R_Render
33 
34 
35 #define DUMMY_WIDTH(font)  (4)
36 
37 #define HU_CHAR(ch)  (islower(ch) ? toupper(ch) : (ch))
38 #define HU_INDEX(c)  ((unsigned char) HU_CHAR(c))
39 
40 static font_c *default_font;
41 
42 
43 float pixel_aspect = 1.0f;
44 
45 
46 // current state
47 static float cur_coord_W;
48 static float cur_coord_H;
49 
50 static font_c *cur_font;
51 static rgbcol_t cur_color;
52 
53 static float cur_scale, cur_alpha;
54 static int cur_x_align, cur_y_align;
55 
56 static float margin_X;
57 static float margin_Y;
58 static float margin_W;
59 static float margin_H;
60 
61 #define COORD_X(x)  (margin_X + (x) * margin_W / cur_coord_W)
62 #define COORD_Y(y)  (margin_Y - (y) * margin_H / cur_coord_H)
63 
64 #define VERT_SPACING  2.0f
65 
66 
HUD_SetCoordSys(int width,int height)67 void HUD_SetCoordSys(int width, int height)
68 {
69 	cur_coord_W = width;
70 	cur_coord_H = height;
71 }
72 
HUD_SetFont(font_c * font)73 void HUD_SetFont(font_c *font)
74 {
75 	cur_font = font ? font : default_font;
76 }
77 
HUD_SetScale(float scale)78 void HUD_SetScale(float scale)
79 {
80 	cur_scale = scale;
81 }
82 
HUD_SetTextColor(rgbcol_t color)83 void HUD_SetTextColor(rgbcol_t color)
84 {
85 	cur_color = color;
86 }
87 
HUD_SetAlpha(float alpha)88 void HUD_SetAlpha(float alpha)
89 {
90 	cur_alpha = alpha;
91 }
92 
HUD_SetAlignment(int xa,int ya)93 void HUD_SetAlignment(int xa, int ya)
94 {
95 	cur_x_align = xa;
96 	cur_y_align = ya;
97 }
98 
99 
HUD_Reset()100 void HUD_Reset()
101 {
102 	HUD_SetCoordSys(320, 200);
103 
104 	cur_font  = default_font;
105 	cur_color = RGB_NO_VALUE;
106 	cur_scale = 1.0f;
107 	cur_alpha = 1.0f;
108 	cur_x_align = cur_y_align = -1;
109 }
110 
111 
HUD_FrameSetup(void)112 void HUD_FrameSetup(void)
113 {
114 	if (! default_font)
115 	{
116 		// FIXME: get default font from DDF gamedef
117 		fontdef_c *DEF = fontdefs.Lookup("DOOM");
118 		SYS_ASSERT(DEF);
119 
120 		default_font = hu_fonts.Lookup(DEF);
121 		SYS_ASSERT(default_font);
122 	}
123 
124 	HUD_Reset();
125 
126 	// determine pixel_aspect
127 	float aspect = CLAMP(0.2, r_aspect.f, 5.0);
128 
129 	if (FULLSCREEN)
130 	{
131 		pixel_aspect = aspect * SCREENHEIGHT / (float)SCREENWIDTH;
132 	}
133 	else
134 	{
135 		int width, height;
136 
137 		I_GetDesktopSize(&width, &height);
138 
139 		pixel_aspect = aspect * height / (float)width;
140 	}
141 
142 	// setup letterboxing for wide screens (etc)
143 	margin_H = SCREENHEIGHT;
144 
145 	margin_W = margin_H * DOOM_ASPECT * (DOOM_PIXEL_ASPECT / pixel_aspect);
146 
147 	if (margin_W > SCREENWIDTH)
148 	{
149 		margin_H *= (float)SCREENWIDTH / margin_W;
150 		margin_W = SCREENWIDTH;
151 	}
152 
153 	margin_X = (SCREENWIDTH  - margin_W) / 2.0;
154 	margin_Y = (SCREENHEIGHT + margin_H) / 2.0;
155 }
156 
157 
158 #define MAX_SCISSOR_STACK  10
159 
160 static int scissor_stack[MAX_SCISSOR_STACK][4];
161 static int sci_stack_top = 0;
162 
163 
HUD_PushScissor(float x1,float y1,float x2,float y2,bool expand)164 void HUD_PushScissor(float x1, float y1, float x2, float y2, bool expand)
165 {
166 	SYS_ASSERT(sci_stack_top < MAX_SCISSOR_STACK);
167 
168 	// expand rendered view to cover whole screen
169 	if (expand && x1 < 1 && x2 > cur_coord_W-1)
170 	{
171 		x1 = 0;
172 		x2 = SCREENWIDTH;
173 	}
174 	else
175 	{
176 		x1 = COORD_X(x1);
177 		x2 = COORD_X(x2);
178 	}
179 
180 	std::swap(y1, y2);
181 
182 	y1 = COORD_Y(y1);
183 	y2 = COORD_Y(y2);
184 
185 	int sx1 = I_ROUND(x1); int sy1 = I_ROUND(y1);
186 	int sx2 = I_ROUND(x2); int sy2 = I_ROUND(y2);
187 
188 	if (sci_stack_top == 0)
189 	{
190 		glEnable(GL_SCISSOR_TEST);
191 
192 		sx1 = MAX(sx1, 0);
193 		sy1 = MAX(sy1, 0);
194 
195 		sx2 = MIN(sx2, SCREENWIDTH);
196 		sy2 = MIN(sy2, SCREENHEIGHT);
197 	}
198 	else
199 	{
200 		// clip to previous scissor
201 		int *xy = scissor_stack[sci_stack_top-1];
202 
203 		sx1 = MAX(sx1, xy[0]);
204 		sy1 = MAX(sy1, xy[1]);
205 
206 		sx2 = MIN(sx2, xy[2]);
207 		sy2 = MIN(sy2, xy[3]);
208 	}
209 
210 	SYS_ASSERT(sx2 >= sx1);
211 	SYS_ASSERT(sy2 >= sy1);
212 
213 	glScissor(sx1, sy1, sx2 - sx1, sy2 - sy1);
214 
215 	// push current scissor
216 	int *xy = scissor_stack[sci_stack_top];
217 
218 	xy[0] = sx1; xy[1] = sy1;
219 	xy[2] = sx2; xy[3] = sy2;
220 
221 	sci_stack_top++;
222 }
223 
224 
HUD_PopScissor()225 void HUD_PopScissor()
226 {
227 	SYS_ASSERT(sci_stack_top > 0);
228 
229 	sci_stack_top--;
230 
231 	if (sci_stack_top == 0)
232 	{
233 		glDisable(GL_SCISSOR_TEST);
234 	}
235 	else
236 	{
237 		// restore previous scissor
238 		int *xy = scissor_stack[sci_stack_top];
239 
240 		glScissor(xy[0], xy[1], xy[2]-xy[0], xy[3]-xy[1]);
241 	}
242 }
243 
244 
HUD_ScissorTest(float x1,float y1,float x2,float y2)245 bool HUD_ScissorTest(float x1, float y1, float x2, float y2)
246 {
247 	if (sci_stack_top == 0)
248 		return true;
249 
250 	if (x1 > x2) std::swap(x1, x2);
251 	if (y1 < y2) std::swap(y1, y2);
252 
253 	x1 = COORD_X(x1); y1 = COORD_Y(y1);
254 	x2 = COORD_X(x2); y2 = COORD_Y(y2);
255 
256 	int *xy = scissor_stack[sci_stack_top-1];
257 
258 	return ! (x2 < xy[0] || x1 > xy[2] || y2 < xy[1] || y1 > xy[3]);
259 }
260 
261 
262 //----------------------------------------------------------------------------
263 
HUD_RawImage(float hx1,float hy1,float hx2,float hy2,const image_c * image,float tx1,float ty1,float tx2,float ty2,float alpha,rgbcol_t text_col,const colourmap_c * palremap)264 void HUD_RawImage(float hx1, float hy1, float hx2, float hy2,
265                   const image_c *image,
266 				  float tx1, float ty1, float tx2, float ty2,
267 				  float alpha, rgbcol_t text_col,
268 				  const colourmap_c *palremap)
269 {
270 	int x1 = I_ROUND(hx1);
271 	int y1 = I_ROUND(hy1);
272 	int x2 = I_ROUND(hx2+0.25f);
273 	int y2 = I_ROUND(hy2+0.25f);
274 
275 	if (x1 >= x2 || y1 >= y2)
276 		return;
277 
278 	if (x2 < 0 || x1 > SCREENWIDTH ||
279 		y2 < 0 || y1 > SCREENHEIGHT)
280 		return;
281 
282 	float r = 1.0f, g = 1.0f, b = 1.0f;
283 
284 	if (text_col != RGB_NO_VALUE)
285 	{
286 		r = RGB_RED(text_col) / 255.0;
287 		g = RGB_GRN(text_col) / 255.0;
288 		b = RGB_BLU(text_col) / 255.0;
289 
290 		palremap = font_whiten_map;
291 	}
292 
293 	GLuint tex_id = W_ImageCache(image, true, palremap);
294 
295 	glEnable(GL_TEXTURE_2D);
296 	glBindTexture(GL_TEXTURE_2D, tex_id);
297 
298 	if (alpha >= 0.99f && image->opacity == OPAC_Solid)
299 		glDisable(GL_ALPHA_TEST);
300 	else
301 	{
302 		glEnable(GL_ALPHA_TEST);
303 
304 		if (! (alpha < 0.11f || image->opacity == OPAC_Complex))
305 			glAlphaFunc(GL_GREATER, alpha * 0.66f);
306 	}
307 
308 	if (image->opacity == OPAC_Complex || alpha < 0.99f)
309 		glEnable(GL_BLEND);
310 
311 	glColor4f(r, g, b, alpha);
312 
313 	glBegin(GL_QUADS);
314 
315 	glTexCoord2f(tx1, ty1);
316 	glVertex2i(x1, y1);
317 
318 	glTexCoord2f(tx2, ty1);
319 	glVertex2i(x2, y1);
320 
321 	glTexCoord2f(tx2, ty2);
322 	glVertex2i(x2, y2);
323 
324 	glTexCoord2f(tx1, ty2);
325 	glVertex2i(x1, y2);
326 
327 	glEnd();
328 
329 
330 	glDisable(GL_TEXTURE_2D);
331 	glDisable(GL_ALPHA_TEST);
332 	glDisable(GL_BLEND);
333 
334 	glAlphaFunc(GL_GREATER, 0);
335 }
336 
337 
HUD_StretchImage(float x,float y,float w,float h,const image_c * img)338 void HUD_StretchImage(float x, float y, float w, float h, const image_c *img)
339 {
340 	if (cur_x_align >= 0)
341 		x -= w / (cur_x_align == 0 ? 2.0f : 1.0f);
342 
343 	if (cur_y_align >= 0)
344 		y -= h / (cur_y_align == 0 ? 2.0f : 1.0f);
345 
346 	x -= IM_OFFSETX(img);
347 	y -= IM_OFFSETY(img);
348 
349 	float x1 = COORD_X(x);
350 	float x2 = COORD_X(x+w);
351 
352 	float y1 = COORD_Y(y+h);
353 	float y2 = COORD_Y(y);
354 
355     HUD_RawImage(x1, y1, x2, y2, img, 0, 0, IM_RIGHT(img), IM_TOP(img), cur_alpha);
356 }
357 
358 
HUD_DrawImage(float x,float y,const image_c * img)359 void HUD_DrawImage(float x, float y, const image_c *img)
360 {
361 	float w = IM_WIDTH(img)  * cur_scale;
362 	float h = IM_HEIGHT(img) * cur_scale;
363 
364     HUD_StretchImage(x, y, w, h, img);
365 }
366 
367 
HUD_TileImage(float x,float y,float w,float h,const image_c * img,float offset_x,float offset_y)368 void HUD_TileImage(float x, float y, float w, float h, const image_c *img,
369 				   float offset_x, float offset_y)
370 {
371 	if (cur_x_align >= 0)
372 		x -= w / (cur_x_align == 0 ? 2.0f : 1.0f);
373 
374 	if (cur_y_align >= 0)
375 		y -= h / (cur_y_align == 0 ? 2.0f : 1.0f);
376 
377 	offset_x /=  w;
378 	offset_y /= -h;
379 
380 	float tx_scale = w / IM_TOTAL_WIDTH(img)  / cur_scale;
381 	float ty_scale = h / IM_TOTAL_HEIGHT(img) / cur_scale;
382 
383 	float x1 = COORD_X(x);
384 	float x2 = COORD_X(x+w);
385 
386 	float y1 = COORD_Y(y+h);
387 	float y2 = COORD_Y(y);
388 
389 	HUD_RawImage(x1, y1, x2, y2, img,
390 				 (offset_x) * tx_scale,
391 				 (offset_y) * ty_scale,
392 				 (offset_x + 1) * tx_scale,
393 				 (offset_y + 1) * ty_scale,
394 				 cur_alpha);
395 }
396 
397 
HUD_SolidBox(float x1,float y1,float x2,float y2,rgbcol_t col)398 void HUD_SolidBox(float x1, float y1, float x2, float y2, rgbcol_t col)
399 {
400 	// expand to cover wide screens
401 	if (x1 < 1 && x2 > cur_coord_W-1 && y1 < 1 && y2 > cur_coord_H-1)
402 	{
403 		x1 = 0; x2 = SCREENWIDTH;
404 		y1 = 0; y2 = SCREENHEIGHT;
405 	}
406 	else
407 	{
408 		std::swap(y1, y2);
409 
410 		x1 = COORD_X(x1); y1 = COORD_Y(y1);
411 		x2 = COORD_X(x2); y2 = COORD_Y(y2);
412 	}
413 
414 	if (cur_alpha < 0.99f)
415 		glEnable(GL_BLEND);
416 
417 	glColor4f(RGB_RED(col)/255.0, RGB_GRN(col)/255.0, RGB_BLU(col)/255.0, cur_alpha);
418 
419 	glBegin(GL_QUADS);
420 
421 	glVertex2f(x1, y1);
422 	glVertex2f(x1, y2);
423 	glVertex2f(x2, y2);
424 	glVertex2f(x2, y1);
425 
426 	glEnd();
427 	glDisable(GL_BLEND);
428 }
429 
430 
HUD_SolidLine(float x1,float y1,float x2,float y2,rgbcol_t col,bool thick,bool smooth,float dx,float dy)431 void HUD_SolidLine(float x1, float y1, float x2, float y2, rgbcol_t col,
432                    bool thick, bool smooth, float dx, float dy)
433 {
434 	x1 = COORD_X(x1); y1 = COORD_Y(y1);
435 	x2 = COORD_X(x2); y2 = COORD_Y(y2);
436 
437 	dx = COORD_X(dx) - COORD_X(0);
438 	dy = COORD_Y( 0) - COORD_Y(dy);
439 
440 	if (thick)
441 		glLineWidth(1.5f);
442 
443 	if (smooth)
444 		glEnable(GL_LINE_SMOOTH);
445 
446 	if (smooth || cur_alpha < 0.99f)
447 		glEnable(GL_BLEND);
448 
449 	glColor4f(RGB_RED(col)/255.0, RGB_GRN(col)/255.0, RGB_BLU(col)/255.0, cur_alpha);
450 
451 	glBegin(GL_LINES);
452 
453 	glVertex2i((int)x1 + (int)dx, (int)y1 + (int)dy);
454 	glVertex2i((int)x2 + (int)dx, (int)y2 + (int)dy);
455 
456 	glEnd();
457 
458 	glDisable(GL_BLEND);
459 	glDisable(GL_LINE_SMOOTH);
460 	glLineWidth(1.0f);
461 }
462 
463 
HUD_ThinBox(float x1,float y1,float x2,float y2,rgbcol_t col)464 void HUD_ThinBox(float x1, float y1, float x2, float y2, rgbcol_t col)
465 {
466 	std::swap(y1, y2);
467 
468 	x1 = COORD_X(x1); y1 = COORD_Y(y1);
469 	x2 = COORD_X(x2); y2 = COORD_Y(y2);
470 
471 	if (cur_alpha < 0.99f)
472 		glEnable(GL_BLEND);
473 
474 	glColor4f(RGB_RED(col)/255.0, RGB_GRN(col)/255.0, RGB_BLU(col)/255.0, cur_alpha);
475 
476 	glBegin(GL_QUADS);
477 	glVertex2f(x1,   y1); glVertex2f(x1,   y2);
478 	glVertex2f(x1+2, y2); glVertex2f(x1+2, y1);
479 	glEnd();
480 
481 	glBegin(GL_QUADS);
482 	glVertex2f(x2-2, y1); glVertex2f(x2-2, y2);
483 	glVertex2f(x2,   y2); glVertex2f(x2,   y1);
484 	glEnd();
485 
486 	glBegin(GL_QUADS);
487 	glVertex2f(x1+2, y1);   glVertex2f(x1+2, y1+2);
488 	glVertex2f(x2-2, y1+2); glVertex2f(x2-2, y1);
489 	glEnd();
490 
491 	glBegin(GL_QUADS);
492 	glVertex2f(x1+2,  y2-2); glVertex2f(x1+2, y2);
493 	glVertex2f(x2-2,  y2);   glVertex2f(x2-2, y2-2);
494 	glEnd();
495 
496 	glDisable(GL_BLEND);
497 }
498 
499 
HUD_GradientBox(float x1,float y1,float x2,float y2,rgbcol_t * cols)500 void HUD_GradientBox(float x1, float y1, float x2, float y2, rgbcol_t *cols)
501 {
502 	std::swap(y1, y2);
503 
504 	x1 = COORD_X(x1); y1 = COORD_Y(y1);
505 	x2 = COORD_X(x2); y2 = COORD_Y(y2);
506 
507 	if (cur_alpha < 0.99f)
508 		glEnable(GL_BLEND);
509 
510 	glBegin(GL_QUADS);
511 
512 	glColor4f(RGB_RED(cols[1])/255.0, RGB_GRN(cols[1])/255.0,
513 	          RGB_BLU(cols[1])/255.0, cur_alpha);
514 	glVertex2f(x1, y1);
515 
516 	glColor4f(RGB_RED(cols[0])/255.0, RGB_GRN(cols[0])/255.0,
517 	          RGB_BLU(cols[0])/255.0, cur_alpha);
518 	glVertex2f(x1, y2);
519 
520 	glColor4f(RGB_RED(cols[2])/255.0, RGB_GRN(cols[2])/255.0,
521 	          RGB_BLU(cols[2])/255.0, cur_alpha);
522 	glVertex2f(x2, y2);
523 
524 	glColor4f(RGB_RED(cols[3])/255.0, RGB_GRN(cols[3])/255.0,
525 	          RGB_BLU(cols[3])/255.0, cur_alpha);
526 	glVertex2f(x2, y1);
527 
528 	glEnd();
529 	glDisable(GL_BLEND);
530 }
531 
532 
HUD_FontWidth(void)533 float HUD_FontWidth(void)
534 {
535 	return cur_scale * cur_font->NominalWidth();
536 }
537 
HUD_FontHeight(void)538 float HUD_FontHeight(void)
539 {
540 	return cur_scale * cur_font->NominalHeight();
541 }
542 
543 
HUD_StringWidth(const char * str)544 float HUD_StringWidth(const char *str)
545 {
546 	return cur_scale * cur_font->StringWidth(str);
547 }
548 
HUD_StringHeight(const char * str)549 float HUD_StringHeight(const char *str)
550 {
551 	int lines = cur_font->StringLines(str);
552 
553 	return lines * HUD_FontHeight() + (lines - 1) * VERT_SPACING;
554 }
555 
556 
HUD_DrawChar(float left_x,float top_y,const image_c * img)557 void HUD_DrawChar(float left_x, float top_y, const image_c *img)
558 {
559 	float sc_x = cur_scale; // TODO * aspect;
560 	float sc_y = cur_scale;
561 
562 	float x = left_x - IM_OFFSETX(img) * sc_x;
563 	float y = top_y  - IM_OFFSETY(img) * sc_y;
564 
565 	float w = IM_WIDTH(img)  * sc_x;
566 	float h = IM_HEIGHT(img) * sc_y;
567 
568 	float x1 = COORD_X(x);
569 	float x2 = COORD_X(x+w);
570 
571 	float y1 = COORD_Y(y+h);
572 	float y2 = COORD_Y(y);
573 
574     HUD_RawImage(x1, y1, x2, y2, img, 0, 0, IM_RIGHT(img), IM_TOP(img),
575 				  cur_alpha, cur_color);
576 }
577 
578 
579 //
580 // Write a string using the current font
581 //
HUD_DrawText(float x,float y,const char * str)582 void HUD_DrawText(float x, float y, const char *str)
583 {
584 	SYS_ASSERT(cur_font);
585 
586 	float cy = y;
587 
588 	if (cur_y_align >= 0)
589 	{
590 		float total_h = HUD_StringHeight(str);
591 
592 		if (cur_y_align == 0)
593 			total_h /= 2.0f;
594 
595 		cy -= total_h;
596 	}
597 
598 	// handle each line
599 	while (*str)
600 	{
601 		// get the length of the line
602 		int len = 0;
603 		while (str[len] && str[len] != '\n')
604 			len++;
605 
606 		float cx = x;
607 		float total_w = 0;
608 
609 		for (int i = 0; i < len; i++)
610 			total_w += cur_font->CharWidth(str[i]) * cur_scale;
611 
612 		if (cur_x_align >= 0)
613 		{
614 			if (cur_x_align == 0)
615 				total_w /= 2.0f;
616 
617 			cx -= total_w;
618 		}
619 
620 		for (int k = 0; k < len; k++)
621 		{
622 			char ch = str[k];
623 
624 			const image_c *img = cur_font->CharImage(ch);
625 
626 			if (img)
627 				HUD_DrawChar(cx, cy, img);
628 
629 			cx += cur_font->CharWidth(ch) * cur_scale;
630 		}
631 
632 		if (str[len] == 0)
633 			break;
634 
635 		str += (len + 1);
636 		cy  += HUD_FontHeight() + VERT_SPACING;
637 	}
638 }
639 
640 
HUD_RenderWorld(float x1,float y1,float x2,float y2,mobj_t * camera)641 void HUD_RenderWorld(float x1, float y1, float x2, float y2, mobj_t *camera)
642 {
643 	HUD_PushScissor(x1, y1, x2, y2, true);
644 
645 	int *xy = scissor_stack[sci_stack_top-1];
646 
647 	bool full_height = (y2 - y1) > cur_coord_H * 0.95;
648 
649 	float width = COORD_X(x2) - COORD_X(x1);
650 	float expand_w = (xy[2] - xy[0]) / width;
651 
652 	R_Render(xy[0], xy[1], xy[2]-xy[0], xy[3]-xy[1],
653 	         camera, full_height, expand_w);
654 
655 	HUD_PopScissor();
656 }
657 
658 
HUD_GetCastPosition(float * x,float * y,float * scale_x,float * scale_y)659 void HUD_GetCastPosition(float *x, float *y, float *scale_x, float *scale_y)
660 {
661 	*x = COORD_X(160);
662 	*y = COORD_Y(170);
663 
664 	*scale_y = margin_H / cur_coord_H;
665 	*scale_x = *scale_y / pixel_aspect;
666 }
667 
668 //--- editor settings ---
669 // vi:ts=4:sw=4:noexpandtab
670