1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20
21// draw.c -- this is the only file outside the refresh that touches the
22// vid buffer
23
24#include "quakedef.h"
25
26extern unsigned char d_15to8table[65536];
27extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor;
28
29cvar_t		gl_nobind = CVAR2("gl_nobind", "0");
30cvar_t		gl_max_size = CVAR2("gl_max_size", "1024");
31cvar_t		gl_picmip = CVAR2("gl_picmip", "0");
32
33byte		*draw_chars;				// 8*8 graphic characters
34qpic_t		*draw_disc;
35qpic_t		*draw_backtile;
36
37int			translate_texture;
38int			char_texture;
39int			cs_texture; // crosshair texture
40
41static byte cs_data[64] = {
42	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
43	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
45	0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff,
46	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
47	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48	0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
49	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
50};
51
52
53typedef struct
54{
55	int		texnum;
56	float	sl, tl, sh, th;
57} glpic_t;
58
59byte		conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)];
60qpic_t		*conback = (qpic_t *)&conback_buffer;
61
62int		gl_lightmap_format = 4;
63int		gl_solid_format = 3;
64int		gl_alpha_format = 4;
65
66int		gl_filter_min = GL_LINEAR_MIPMAP_NEAREST;
67int		gl_filter_max = GL_LINEAR;
68
69
70int		texels;
71
72typedef struct
73{
74	int		texnum;
75	char	identifier[64];
76	int		width, height;
77	qboolean	mipmap;
78} gltexture_t;
79
80#define	MAX_GLTEXTURES	1024
81gltexture_t	gltextures[MAX_GLTEXTURES];
82int			numgltextures;
83
84void GL_Bind (int texnum)
85{
86	if (gl_nobind.value)
87		texnum = char_texture;
88	if (currenttexture == texnum)
89		return;
90	currenttexture = texnum;
91#ifdef _WIN32
92	bindTexFunc (GL_TEXTURE_2D, texnum);
93#else
94	glBindTexture (GL_TEXTURE_2D, texnum);
95#endif
96}
97
98
99/*
100=============================================================================
101
102  scrap allocation
103
104  Allocate all the little status bar obejcts into a single texture
105  to crutch up stupid hardware / drivers
106
107=============================================================================
108*/
109
110#define	MAX_SCRAPS		1
111#define	BLOCK_WIDTH		256
112#define	BLOCK_HEIGHT	256
113
114int			scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
115byte		scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4];
116qboolean	scrap_dirty;
117int			scrap_texnum;
118
119// returns a texture number and the position inside it
120int Scrap_AllocBlock (int w, int h, int *x, int *y)
121{
122	int		i, j;
123	int		best, best2;
124	int		texnum;
125
126	for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
127	{
128		best = BLOCK_HEIGHT;
129
130		for (i=0 ; i<BLOCK_WIDTH-w ; i++)
131		{
132			best2 = 0;
133
134			for (j=0 ; j<w ; j++)
135			{
136				if (scrap_allocated[texnum][i+j] >= best)
137					break;
138				if (scrap_allocated[texnum][i+j] > best2)
139					best2 = scrap_allocated[texnum][i+j];
140			}
141			if (j == w)
142			{	// this is a valid spot
143				*x = i;
144				*y = best = best2;
145			}
146		}
147
148		if (best + h > BLOCK_HEIGHT)
149			continue;
150
151		for (i=0 ; i<w ; i++)
152			scrap_allocated[texnum][*x + i] = best + h;
153
154		return texnum;
155	}
156
157	Sys_Error ("Scrap_AllocBlock: full");
158	return 0;
159}
160
161int	scrap_uploads;
162
163void Scrap_Upload (void)
164{
165	scrap_uploads++;
166	GL_Bind(scrap_texnum);
167	GL_Upload8 (scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, true);
168	scrap_dirty = false;
169}
170
171//=============================================================================
172/* Support Routines */
173
174typedef struct cachepic_s
175{
176	char		name[MAX_QPATH];
177	qpic_t		pic;
178	byte		padding[32];	// for appended glpic
179} cachepic_t;
180
181#define	MAX_CACHED_PICS		128
182cachepic_t	menu_cachepics[MAX_CACHED_PICS];
183int			menu_numcachepics;
184
185byte		menuplyr_pixels[4096];
186
187int		pic_texels;
188int		pic_count;
189
190qpic_t *Draw_PicFromWad (char *name)
191{
192	qpic_t	*p;
193	glpic_t	*gl;
194
195	p = W_GetLumpName (name);
196	gl = (glpic_t *)p->data;
197
198	// load little ones into the scrap
199	if (p->width < 64 && p->height < 64)
200	{
201		int		x, y;
202		int		i, j, k;
203		int		texnum;
204
205		texnum = Scrap_AllocBlock (p->width, p->height, &x, &y);
206		scrap_dirty = true;
207		k = 0;
208		for (i=0 ; i<p->height ; i++)
209			for (j=0 ; j<p->width ; j++, k++)
210				scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k];
211		texnum += scrap_texnum;
212		gl->texnum = texnum;
213		gl->sl = (x+0.01)/(float)BLOCK_WIDTH;
214		gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH;
215		gl->tl = (y+0.01)/(float)BLOCK_WIDTH;
216		gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH;
217
218		pic_count++;
219		pic_texels += p->width*p->height;
220	}
221	else
222	{
223		gl->texnum = GL_LoadPicTexture (p);
224		gl->sl = 0;
225		gl->sh = 1;
226		gl->tl = 0;
227		gl->th = 1;
228	}
229	return p;
230}
231
232
233/*
234================
235Draw_CachePic
236================
237*/
238qpic_t	*Draw_CachePic (char *path)
239{
240	cachepic_t	*pic;
241	int			i;
242	qpic_t		*dat;
243	glpic_t		*gl;
244
245	for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
246		if (!strcmp (path, pic->name))
247			return &pic->pic;
248
249	if (menu_numcachepics == MAX_CACHED_PICS)
250		Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
251	menu_numcachepics++;
252	strcpy (pic->name, path);
253
254//
255// load the pic from disk
256//
257	dat = (qpic_t *)COM_LoadTempFile (path);
258	if (!dat)
259		Sys_Error ("Draw_CachePic: failed to load %s", path);
260	SwapPic (dat);
261
262	// HACK HACK HACK --- we need to keep the bytes for
263	// the translatable player picture just for the menu
264	// configuration dialog
265	if (!strcmp (path, "gfx/menuplyr.lmp"))
266		memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
267
268	pic->pic.width = dat->width;
269	pic->pic.height = dat->height;
270
271	gl = (glpic_t *)pic->pic.data;
272	gl->texnum = GL_LoadPicTexture (dat);
273	gl->sl = 0;
274	gl->sh = 1;
275	gl->tl = 0;
276	gl->th = 1;
277
278	return &pic->pic;
279}
280
281
282void Draw_CharToConback (int num, byte *dest)
283{
284	int		row, col;
285	byte	*source;
286	int		drawline;
287	int		x;
288
289	row = num>>4;
290	col = num&15;
291	source = draw_chars + (row<<10) + (col<<3);
292
293	drawline = 8;
294
295	while (drawline--)
296	{
297		for (x=0 ; x<8 ; x++)
298			if (source[x] != 255)
299				dest[x] = 0x60 + source[x];
300		source += 128;
301		dest += 320;
302	}
303
304}
305
306typedef struct
307{
308	char *name;
309	int	minimize, maximize;
310} glmode_t;
311
312glmode_t modes[] = {
313	{"GL_NEAREST", GL_NEAREST, GL_NEAREST},
314	{"GL_LINEAR", GL_LINEAR, GL_LINEAR},
315	{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
316	{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
317	{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
318	{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
319};
320
321/*
322===============
323Draw_TextureMode_f
324===============
325*/
326void Draw_TextureMode_f (void)
327{
328	int		i;
329	gltexture_t	*glt;
330
331	if (Cmd_Argc() == 1)
332	{
333		for (i=0 ; i< 6 ; i++)
334			if (gl_filter_min == modes[i].minimize)
335			{
336				Con_Printf ("%s\n", modes[i].name);
337				return;
338			}
339		Con_Printf ("current filter is unknown???\n");
340		return;
341	}
342
343	for (i=0 ; i< 6 ; i++)
344	{
345		if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
346			break;
347	}
348	if (i == 6)
349	{
350		Con_Printf ("bad filter name\n");
351		return;
352	}
353
354	gl_filter_min = modes[i].minimize;
355	gl_filter_max = modes[i].maximize;
356
357	// change all the existing mipmap texture objects
358	for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
359	{
360		if (glt->mipmap)
361		{
362			GL_Bind (glt->texnum);
363			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
364			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
365		}
366	}
367}
368
369/*
370===============
371Draw_Init
372===============
373*/
374void Draw_Init (void)
375{
376	int		i;
377	qpic_t	*cb;
378	byte	*dest;
379	int		x;
380	char	ver[40];
381	glpic_t	*gl;
382	int start;
383	byte    *ncdata;
384
385	Cvar_RegisterVariable (&gl_nobind);
386	Cvar_RegisterVariable (&gl_max_size);
387	Cvar_RegisterVariable (&gl_picmip);
388
389	// 3dfx can only handle 256 wide textures
390	if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) ||
391		!Q_strncasecmp ((char *)gl_renderer, "Mesa",4))
392		Cvar_Set ("gl_max_size", "256");
393
394	Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
395
396	// load the console background and the charset
397	// by hand, because we need to write the version
398	// string into the background before turning
399	// it into a texture
400	draw_chars = W_GetLumpName ("conchars");
401	for (i=0 ; i<256*64 ; i++)
402		if (draw_chars[i] == 0)
403			draw_chars[i] = 255;	// proper transparent color
404
405	// now turn them into textures
406	char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true);
407//	Draw_CrosshairAdjust();
408	cs_texture = GL_LoadTexture ("crosshair", 8, 8, cs_data, false, true);
409
410	start = Hunk_LowMark ();
411
412	cb = (qpic_t *)COM_LoadHunkFile ("gfx/conback.lmp");
413	if (!cb)
414		Sys_Error ("Couldn't load gfx/conback.lmp");
415	SwapPic (cb);
416
417	sprintf (ver, "%4.2f", VERSION);
418	dest = cb->data + 320 + 320*186 - 11 - 8*strlen(ver);
419	for (x=0 ; x< (int) strlen(ver) ; x++)
420		Draw_CharToConback (ver[x], dest+(x<<3));
421
422#if 0
423	conback->width = vid.conwidth;
424	conback->height = vid.conheight;
425
426	// scale console to vid size
427	dest = ncdata = Hunk_AllocName(vid.conwidth * vid.conheight, "conback");
428
429	for (y=0 ; y<vid.conheight ; y++, dest += vid.conwidth)
430	{
431		src = cb->data + cb->width * (y*cb->height/vid.conheight);
432		if (vid.conwidth == cb->width)
433			memcpy (dest, src, vid.conwidth);
434		else
435		{
436			f = 0;
437			fstep = cb->width*0x10000/vid.conwidth;
438			for (x=0 ; x<vid.conwidth ; x+=4)
439			{
440				dest[x] = src[f>>16];
441				f += fstep;
442				dest[x+1] = src[f>>16];
443				f += fstep;
444				dest[x+2] = src[f>>16];
445				f += fstep;
446				dest[x+3] = src[f>>16];
447				f += fstep;
448			}
449		}
450	}
451#else
452	conback->width = cb->width;
453	conback->height = cb->height;
454	ncdata = cb->data;
455#endif
456
457	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
458	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
459
460	gl = (glpic_t *)conback->data;
461	gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, false);
462	gl->sl = 0;
463	gl->sh = 1;
464	gl->tl = 0;
465	gl->th = 1;
466	conback->width = vid.conwidth;
467	conback->height = vid.conheight;
468
469	// free loaded console
470	Hunk_FreeToLowMark (start);
471
472	// save a texture slot for translated picture
473	translate_texture = texture_extension_number++;
474
475	// save slots for scraps
476	scrap_texnum = texture_extension_number;
477	texture_extension_number += MAX_SCRAPS;
478
479	//
480	// get the other pics we need
481	//
482	draw_disc = Draw_PicFromWad ("disc");
483	draw_backtile = Draw_PicFromWad ("backtile");
484}
485
486
487
488/*
489================
490Draw_Character
491
492Draws one 8*8 graphics character with 0 being transparent.
493It can be clipped to the top of the screen to allow the console to be
494smoothly scrolled off.
495================
496*/
497void Draw_Character (int x, int y, int num)
498{
499	int				row, col;
500	float			frow, fcol, size;
501
502	if (num == 32)
503		return;		// space
504
505	num &= 255;
506
507	if (y <= -8)
508		return;			// totally off screen
509
510	row = num>>4;
511	col = num&15;
512
513	frow = row*0.0625;
514	fcol = col*0.0625;
515	size = 0.0625;
516
517	GL_Bind (char_texture);
518
519#ifdef USE_OPENGLES
520	DrawQuad(x, y, 8, 8, fcol, frow, size, size);
521#else
522	glBegin (GL_QUADS);
523	glTexCoord2f (fcol, frow);
524	glVertex2f (x, y);
525	glTexCoord2f (fcol + size, frow);
526	glVertex2f (x+8, y);
527	glTexCoord2f (fcol + size, frow + size);
528	glVertex2f (x+8, y+8);
529	glTexCoord2f (fcol, frow + size);
530	glVertex2f (x, y+8);
531	glEnd ();
532#endif
533}
534
535/*
536================
537Draw_String
538================
539*/
540void Draw_String (int x, int y, char *str)
541{
542	while (*str)
543	{
544		Draw_Character (x, y, *str);
545		str++;
546		x += 8;
547	}
548}
549
550/*
551================
552Draw_Alt_String
553================
554*/
555void Draw_Alt_String (int x, int y, char *str)
556{
557	while (*str)
558	{
559		Draw_Character (x, y, (*str) | 0x80);
560		str++;
561		x += 8;
562	}
563}
564
565void Draw_Crosshair(void)
566{
567	int x, y;
568	extern vrect_t		scr_vrect;
569	unsigned char *pColor;
570
571	if (crosshair.value == 2) {
572		x = scr_vrect.x + scr_vrect.width/2 - 3 + cl_crossx.value;
573		y = scr_vrect.y + scr_vrect.height/2 - 3 + cl_crossy.value;
574
575		glTexEnvf ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
576		pColor = (unsigned char *) &d_8to24table[(byte) crosshaircolor.value];
577		glColor4ubv ( pColor );
578		GL_Bind (cs_texture);
579
580#ifdef USE_OPENGLES
581        DrawQuad(x - 4, y - 4, 16, 16, 0, 0, 1, 1);
582#else
583		glBegin (GL_QUADS);
584		glTexCoord2f (0, 0);
585		glVertex2f (x - 4, y - 4);
586		glTexCoord2f (1, 0);
587		glVertex2f (x+12, y-4);
588		glTexCoord2f (1, 1);
589		glVertex2f (x+12, y+12);
590		glTexCoord2f (0, 1);
591		glVertex2f (x - 4, y+12);
592		glEnd ();
593#endif
594
595		glTexEnvf ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
596	} else if (crosshair.value)
597		Draw_Character (scr_vrect.x + scr_vrect.width/2-4 + cl_crossx.value,
598			scr_vrect.y + scr_vrect.height/2-4 + cl_crossy.value,
599			'+');
600}
601
602
603/*
604================
605Draw_DebugChar
606
607Draws a single character directly to the upper right corner of the screen.
608This is for debugging lockups by drawing different chars in different parts
609of the code.
610================
611*/
612void Draw_DebugChar (char num)
613{
614}
615
616/*
617=============
618Draw_Pic
619=============
620*/
621void Draw_Pic (int x, int y, qpic_t *pic)
622{
623	glpic_t			*gl;
624
625	if (scrap_dirty)
626		Scrap_Upload ();
627	gl = (glpic_t *)pic->data;
628	glColor4f (1,1,1,1);
629	GL_Bind (gl->texnum);
630#ifdef USE_OPENGLES
631    DrawQuad(x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh - gl->sl, gl->th - gl->tl);
632#else
633	glBegin (GL_QUADS);
634	glTexCoord2f (gl->sl, gl->tl);
635	glVertex2f (x, y);
636	glTexCoord2f (gl->sh, gl->tl);
637	glVertex2f (x+pic->width, y);
638	glTexCoord2f (gl->sh, gl->th);
639	glVertex2f (x+pic->width, y+pic->height);
640	glTexCoord2f (gl->sl, gl->th);
641	glVertex2f (x, y+pic->height);
642	glEnd ();
643#endif
644}
645
646/*
647=============
648Draw_AlphaPic
649=============
650*/
651void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha)
652{
653	glpic_t			*gl;
654
655	if (scrap_dirty)
656		Scrap_Upload ();
657	gl = (glpic_t *)pic->data;
658	glDisable(GL_ALPHA_TEST);
659	glEnable (GL_BLEND);
660//	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
661	glCullFace(GL_FRONT);
662	glColor4f (1,1,1,alpha);
663	GL_Bind (gl->texnum);
664#ifdef USE_OPENGLES
665    DrawQuad(x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh - gl->sl, gl->th - gl->tl);
666#else
667	glBegin (GL_QUADS);
668	glTexCoord2f (gl->sl, gl->tl);
669	glVertex2f (x, y);
670	glTexCoord2f (gl->sh, gl->tl);
671	glVertex2f (x+pic->width, y);
672	glTexCoord2f (gl->sh, gl->th);
673	glVertex2f (x+pic->width, y+pic->height);
674	glTexCoord2f (gl->sl, gl->th);
675	glVertex2f (x, y+pic->height);
676	glEnd ();
677#endif
678	glColor4f (1,1,1,1);
679	glEnable(GL_ALPHA_TEST);
680	glDisable (GL_BLEND);
681}
682
683void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height)
684{
685	glpic_t			*gl;
686	float newsl, newtl, newsh, newth;
687	float oldglwidth, oldglheight;
688
689	if (scrap_dirty)
690		Scrap_Upload ();
691	gl = (glpic_t *)pic->data;
692
693	oldglwidth = gl->sh - gl->sl;
694	oldglheight = gl->th - gl->tl;
695
696	newsl = gl->sl + (srcx*oldglwidth)/pic->width;
697	newsh = newsl + (width*oldglwidth)/pic->width;
698
699	newtl = gl->tl + (srcy*oldglheight)/pic->height;
700	newth = newtl + (height*oldglheight)/pic->height;
701
702	glColor4f (1,1,1,1);
703	GL_Bind (gl->texnum);
704#ifdef USE_OPENGLES
705    DrawQuad(x, y, width, height, newsl, newtl, newsh - newsl, newth - newtl);
706#else
707	glBegin (GL_QUADS);
708	glTexCoord2f (newsl, newtl);
709	glVertex2f (x, y);
710	glTexCoord2f (newsh, newtl);
711	glVertex2f (x+width, y);
712	glTexCoord2f (newsh, newth);
713	glVertex2f (x+width, y+height);
714	glTexCoord2f (newsl, newth);
715	glVertex2f (x, y+height);
716	glEnd ();
717#endif
718}
719
720/*
721=============
722Draw_TransPic
723=============
724*/
725void Draw_TransPic (int x, int y, qpic_t *pic)
726{
727
728	if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 ||
729		 (unsigned)(y + pic->height) > vid.height)
730	{
731		Sys_Error ("Draw_TransPic: bad coordinates");
732	}
733
734	Draw_Pic (x, y, pic);
735}
736
737
738/*
739=============
740Draw_TransPicTranslate
741
742Only used for the player color selection menu
743=============
744*/
745void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation)
746{
747	int				v, u, c;
748	unsigned		trans[64*64], *dest;
749	byte			*src;
750	int				p;
751
752	GL_Bind (translate_texture);
753
754	c = pic->width * pic->height;
755
756	dest = trans;
757	for (v=0 ; v<64 ; v++, dest += 64)
758	{
759		src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width];
760		for (u=0 ; u<64 ; u++)
761		{
762			p = src[(u*pic->width)>>6];
763			if (p == 255)
764				dest[u] = p;
765			else
766				dest[u] =  d_8to24table[translation[p]];
767		}
768	}
769
770	glTexImage2DHelper (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
771
772	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
773	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
774
775	glColor3f (1,1,1);
776#ifdef USE_OPENGLES
777	DrawQuad(x, y, pic->width, pic->height, 0, 0, 1, 1);
778#else
779	glBegin (GL_QUADS);
780	glTexCoord2f (0, 0);
781	glVertex2f (x, y);
782	glTexCoord2f (1, 0);
783	glVertex2f (x+pic->width, y);
784	glTexCoord2f (1, 1);
785	glVertex2f (x+pic->width, y+pic->height);
786	glTexCoord2f (0, 1);
787	glVertex2f (x, y+pic->height);
788	glEnd ();
789#endif
790}
791
792
793/*
794================
795Draw_ConsoleBackground
796
797================
798*/
799void Draw_ConsoleBackground (int lines)
800{
801	char ver[80];
802	int x, i;
803	int y;
804
805	y = (vid.height * 3) >> 2;
806	if (lines > y)
807		Draw_Pic(0, lines-vid.height, conback);
808	else
809		Draw_AlphaPic (0, lines - vid.height, conback, (float)(1.2 * lines)/y);
810
811	// hack the version number directly into the pic
812//	y = lines-186;
813	y = lines-14;
814	if (!cls.download) {
815#ifdef __linux__
816		sprintf (ver, "LinuxGL (%4.2f) QuakeWorld", LINUX_VERSION);
817#else
818		sprintf (ver, "GL (%4.2f) QuakeWorld", GLQUAKE_VERSION);
819#endif
820		x = vid.conwidth - (strlen(ver)*8 + 11) - (vid.conwidth*8/320)*7;
821		for (i=0 ; i< (int) strlen(ver) ; i++)
822			Draw_Character (x + i * 8, y, ver[i] | 0x80);
823	}
824}
825
826
827/*
828=============
829Draw_TileClear
830
831This repeats a 64*64 tile graphic to fill the screen around a sized down
832refresh window.
833=============
834*/
835void Draw_TileClear (int x, int y, int w, int h)
836{
837	glColor3f (1,1,1);
838	GL_Bind (*(int *)draw_backtile->data);
839#ifdef USE_OPENGLES
840	DrawQuad(x, y, w, h, x/64.0, y/64.0, w/64.0, h/64.0);
841#else
842	glBegin (GL_QUADS);
843	glTexCoord2f (x/64.0, y/64.0);
844	glVertex2f (x, y);
845	glTexCoord2f ( (x+w)/64.0, y/64.0);
846	glVertex2f (x+w, y);
847	glTexCoord2f ( (x+w)/64.0, (y+h)/64.0);
848	glVertex2f (x+w, y+h);
849	glTexCoord2f ( x/64.0, (y+h)/64.0 );
850	glVertex2f (x, y+h);
851	glEnd ();
852#endif
853}
854
855
856/*
857=============
858Draw_Fill
859
860Fills a box of pixels with a single color
861=============
862*/
863void Draw_Fill (int x, int y, int w, int h, int c)
864{
865	glDisable (GL_TEXTURE_2D);
866	glColor3f (host_basepal[c*3]/255.0,
867		host_basepal[c*3+1]/255.0,
868		host_basepal[c*3+2]/255.0);
869
870#ifdef USE_OPENGLES
871	DrawQuad_NoTex(x, y, w, h);
872#else
873	glBegin (GL_QUADS);
874
875	glVertex2f (x,y);
876	glVertex2f (x+w, y);
877	glVertex2f (x+w, y+h);
878	glVertex2f (x, y+h);
879
880	glEnd ();
881#endif
882	glColor3f (1,1,1);
883	glEnable (GL_TEXTURE_2D);
884}
885//=============================================================================
886
887/*
888================
889Draw_FadeScreen
890
891================
892*/
893void Draw_FadeScreen (void)
894{
895	glEnable (GL_BLEND);
896	glDisable (GL_TEXTURE_2D);
897	glColor4f (0, 0, 0, 0.8);
898
899#ifdef USE_OPENGLES
900	DrawQuad_NoTex(0, 0, vid.width, vid.height);
901#else
902	glBegin (GL_QUADS);
903
904	glVertex2f (0,0);
905	glVertex2f (vid.width, 0);
906	glVertex2f (vid.width, vid.height);
907	glVertex2f (0, vid.height);
908
909	glEnd ();
910#endif
911	glColor4f (1,1,1,1);
912	glEnable (GL_TEXTURE_2D);
913	glDisable (GL_BLEND);
914
915	Sbar_Changed();
916}
917
918//=============================================================================
919
920/*
921================
922Draw_BeginDisc
923
924Draws the little blue disc in the corner of the screen.
925Call before beginning any disc IO.
926================
927*/
928void Draw_BeginDisc (void)
929{
930	if (!draw_disc)
931		return;
932#ifdef USE_OPENGLES
933	// !!! Implement this
934#else
935	glDrawBuffer  (GL_FRONT);
936	Draw_Pic (vid.width - 24, 0, draw_disc);
937	glDrawBuffer  (GL_BACK);
938#endif
939}
940
941
942/*
943================
944Draw_EndDisc
945
946Erases the disc icon.
947Call after completing any disc IO
948================
949*/
950void Draw_EndDisc (void)
951{
952}
953
954/*
955================
956GL_Set2D
957
958Setup as if the screen was 320*200
959================
960*/
961void GL_Set2D (void)
962{
963	glViewport (glx, gly, glwidth, glheight);
964
965	glMatrixMode(GL_PROJECTION);
966    glLoadIdentity ();
967#ifdef USE_OPENGLES
968	glOrthof (0, vid.width, vid.height, 0, -99999, 99999);
969#else
970	glOrtho  (0, vid.width, vid.height, 0, -99999, 99999);
971#endif
972
973	glMatrixMode(GL_MODELVIEW);
974    glLoadIdentity ();
975
976	glDisable (GL_DEPTH_TEST);
977	glDisable (GL_CULL_FACE);
978	glDisable (GL_BLEND);
979	glEnable (GL_ALPHA_TEST);
980//	glDisable (GL_ALPHA_TEST);
981
982	glColor4f (1,1,1,1);
983}
984
985//====================================================================
986
987/*
988================
989GL_FindTexture
990================
991*/
992int GL_FindTexture (char *identifier)
993{
994	int		i;
995	gltexture_t	*glt;
996
997	for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
998	{
999		if (!strcmp (identifier, glt->identifier))
1000			return gltextures[i].texnum;
1001	}
1002
1003	return -1;
1004}
1005
1006/*
1007================
1008GL_ResampleTexture
1009================
1010*/
1011void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out,  int outwidth, int outheight)
1012{
1013	int		i, j;
1014	unsigned	*inrow;
1015	unsigned	frac, fracstep;
1016
1017	fracstep = inwidth*0x10000/outwidth;
1018	for (i=0 ; i<outheight ; i++, out += outwidth)
1019	{
1020		inrow = in + inwidth*(i*inheight/outheight);
1021		frac = fracstep >> 1;
1022		for (j=0 ; j<outwidth ; j+=4)
1023		{
1024			out[j] = inrow[frac>>16];
1025			frac += fracstep;
1026			out[j+1] = inrow[frac>>16];
1027			frac += fracstep;
1028			out[j+2] = inrow[frac>>16];
1029			frac += fracstep;
1030			out[j+3] = inrow[frac>>16];
1031			frac += fracstep;
1032		}
1033	}
1034}
1035
1036/*
1037================
1038GL_Resample8BitTexture -- JACK
1039================
1040*/
1041void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out,  int outwidth, int outheight)
1042{
1043	int		i, j;
1044	unsigned	char *inrow;
1045	unsigned	frac, fracstep;
1046
1047	fracstep = inwidth*0x10000/outwidth;
1048	for (i=0 ; i<outheight ; i++, out += outwidth)
1049	{
1050		inrow = in + inwidth*(i*inheight/outheight);
1051		frac = fracstep >> 1;
1052		for (j=0 ; j<outwidth ; j+=4)
1053		{
1054			out[j] = inrow[frac>>16];
1055			frac += fracstep;
1056			out[j+1] = inrow[frac>>16];
1057			frac += fracstep;
1058			out[j+2] = inrow[frac>>16];
1059			frac += fracstep;
1060			out[j+3] = inrow[frac>>16];
1061			frac += fracstep;
1062		}
1063	}
1064}
1065
1066/*
1067================
1068GL_MipMap
1069
1070Operates in place, quartering the size of the texture
1071================
1072*/
1073void GL_MipMap (byte *in, int width, int height)
1074{
1075	int		i, j;
1076	byte	*out;
1077
1078	width <<=2;
1079	height >>= 1;
1080	out = in;
1081	for (i=0 ; i<height ; i++, in+=width)
1082	{
1083		for (j=0 ; j<width ; j+=8, out+=4, in+=8)
1084		{
1085			out[0] = (in[0] + in[4] + in[width+0] + in[width+4])>>2;
1086			out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2;
1087			out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2;
1088			out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2;
1089		}
1090	}
1091}
1092
1093/*
1094================
1095GL_MipMap8Bit
1096
1097Mipping for 8 bit textures
1098================
1099*/
1100void GL_MipMap8Bit (byte *in, int width, int height)
1101{
1102	int		i, j;
1103	byte	*out;
1104	unsigned short     r,g,b;
1105	byte	*at1, *at2, *at3, *at4;
1106
1107	height >>= 1;
1108	out = in;
1109	for (i=0 ; i<height ; i++, in+=width)
1110		for (j=0 ; j<width ; j+=2, out+=1, in+=2)
1111		{
1112			at1 = (byte *) &d_8to24table[in[0]];
1113			at2 = (byte *) &d_8to24table[in[1]];
1114			at3 = (byte *) &d_8to24table[in[width+0]];
1115			at4 = (byte *) &d_8to24table[in[width+1]];
1116
1117 			r = (at1[0]+at2[0]+at3[0]+at4[0]); r>>=5;
1118 			g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5;
1119 			b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5;
1120
1121			out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)];
1122		}
1123}
1124
1125void glTexImage2DHelper( GLenum target,
1126	 GLint level,
1127	 GLint internalformat,
1128	 GLsizei width,
1129	 GLsizei height,
1130	 GLint border,
1131	 GLenum format,
1132	 GLenum type,
1133	 const GLvoid *pixels )
1134{
1135	// In full OpenGL The internalformat can be 1..4, to indicate how many components of the data are valid.
1136	// OpenGL ES requires the internalformat argument match the format for glTexImage2D.
1137
1138	glTexImage2D(target, level, format, width, height, border, format, type, pixels);
1139}
1140
1141/*
1142===============
1143GL_Upload32
1144===============
1145*/
1146void GL_Upload32 (unsigned *data, int width, int height,  qboolean mipmap, qboolean alpha)
1147{
1148	int			samples;
1149static	unsigned	scaled[1024*512];	// [512*256];
1150	int			scaled_width, scaled_height;
1151
1152	for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
1153		;
1154	for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
1155		;
1156
1157	scaled_width >>= (int)gl_picmip.value;
1158	scaled_height >>= (int)gl_picmip.value;
1159
1160	if (scaled_width > (int) gl_max_size.value)
1161		scaled_width = gl_max_size.value;
1162	if (scaled_height > (int) gl_max_size.value)
1163		scaled_height = gl_max_size.value;
1164
1165	if (scaled_width * scaled_height > (int) sizeof(scaled)/4)
1166		Sys_Error ("GL_LoadTexture: too big");
1167
1168	samples = alpha ? gl_alpha_format : gl_solid_format;
1169
1170#if 0
1171	if (mipmap)
1172		gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans);
1173	else if (scaled_width == width && scaled_height == height)
1174		glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);
1175	else
1176	{
1177		gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans,
1178			scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled);
1179		glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
1180	}
1181#else
1182texels += scaled_width * scaled_height;
1183
1184	if (scaled_width == width && scaled_height == height)
1185	{
1186		if (!mipmap)
1187		{
1188			glTexImage2DHelper (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
1189			goto done;
1190		}
1191		memcpy (scaled, data, width*height*4);
1192	}
1193	else
1194		GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
1195
1196	glTexImage2DHelper (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
1197	if (mipmap)
1198	{
1199		int		miplevel;
1200
1201		miplevel = 0;
1202		while (scaled_width > 1 || scaled_height > 1)
1203		{
1204			GL_MipMap ((byte *)scaled, scaled_width, scaled_height);
1205			scaled_width >>= 1;
1206			scaled_height >>= 1;
1207			if (scaled_width < 1)
1208				scaled_width = 1;
1209			if (scaled_height < 1)
1210				scaled_height = 1;
1211			miplevel++;
1212			glTexImage2DHelper (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
1213		}
1214	}
1215done: ;
1216#endif
1217
1218
1219	if (mipmap)
1220	{
1221		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
1222		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
1223	}
1224	else
1225	{
1226		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
1227		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
1228	}
1229}
1230
1231void GL_Upload8_EXT (byte *data, int width, int height,  qboolean mipmap, qboolean alpha)
1232{
1233	int			i, s;
1234	qboolean	noalpha;
1235	int			samples;
1236    static	unsigned char scaled[1024*512];	// [512*256];
1237	int			scaled_width, scaled_height;
1238
1239	s = width*height;
1240	// if there are no transparent pixels, make it a 3 component
1241	// texture even if it was specified as otherwise
1242	if (alpha)
1243	{
1244		noalpha = true;
1245		for (i=0 ; i<s ; i++)
1246		{
1247			if (data[i] == 255)
1248				noalpha = false;
1249		}
1250
1251		if (alpha && noalpha)
1252			alpha = false;
1253	}
1254	for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
1255		;
1256	for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
1257		;
1258
1259	scaled_width >>= (int)gl_picmip.value;
1260	scaled_height >>= (int)gl_picmip.value;
1261
1262	if (scaled_width > gl_max_size.value)
1263		scaled_width = gl_max_size.value;
1264	if (scaled_height > gl_max_size.value)
1265		scaled_height = gl_max_size.value;
1266
1267	if (scaled_width * scaled_height > (int) sizeof(scaled))
1268		Sys_Error ("GL_LoadTexture: too big");
1269
1270	samples = 1; // alpha ? gl_alpha_format : gl_solid_format;
1271
1272	texels += scaled_width * scaled_height;
1273
1274	if (scaled_width == width && scaled_height == height)
1275	{
1276		if (!mipmap)
1277		{
1278#ifdef USE_OPENGLES
1279			glCompressedTexImage2D (GL_TEXTURE_2D, 0, GL_PALETTE8_RGB8_OES, scaled_width, scaled_height, 0, s, data);
1280#else
1281			glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data);
1282#endif
1283			goto done;
1284		}
1285		memcpy (scaled, data, width*height);
1286	}
1287	else
1288		GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height);
1289
1290#ifdef USE_OPENGLES
1291	glCompressedTexImage2D (GL_TEXTURE_2D, 0, GL_PALETTE8_RGB8_OES, scaled_width, scaled_height, 0, s, scaled);
1292#else
1293	glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
1294#endif
1295
1296	if (mipmap)
1297	{
1298		int		miplevel;
1299
1300		miplevel = 0;
1301		while (scaled_width > 1 || scaled_height > 1)
1302		{
1303			GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height);
1304			scaled_width >>= 1;
1305			scaled_height >>= 1;
1306			if (scaled_width < 1)
1307				scaled_width = 1;
1308			if (scaled_height < 1)
1309				scaled_height = 1;
1310			miplevel++;
1311#ifdef USE_OPENGLES
1312			glCompressedTexImage2D (GL_TEXTURE_2D, miplevel, GL_PALETTE8_RGB8_OES, scaled_width, scaled_height, 0, s, scaled);
1313#else
1314			glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
1315#endif
1316		}
1317	}
1318done: ;
1319
1320	if (mipmap)
1321	{
1322		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
1323		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
1324	}
1325	else
1326	{
1327		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
1328		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
1329	}
1330}
1331
1332extern qboolean VID_Is8bit();
1333
1334/*
1335===============
1336GL_Upload8
1337===============
1338*/
1339void GL_Upload8 (byte *data, int width, int height,  qboolean mipmap, qboolean alpha)
1340{
1341static	unsigned	trans[640*480];		// FIXME, temporary
1342	int			i, s;
1343	qboolean	noalpha;
1344	int			p;
1345
1346	s = width*height;
1347	// if there are no transparent pixels, make it a 3 component
1348	// texture even if it was specified as otherwise
1349	if (alpha)
1350	{
1351		noalpha = true;
1352		for (i=0 ; i<s ; i++)
1353		{
1354			p = data[i];
1355			if (p == 255)
1356				noalpha = false;
1357			trans[i] = d_8to24table[p];
1358		}
1359
1360		if (alpha && noalpha)
1361			alpha = false;
1362	}
1363	else
1364	{
1365		if (s&3)
1366			Sys_Error ("GL_Upload8: s&3");
1367		for (i=0 ; i<s ; i+=4)
1368		{
1369			trans[i] = d_8to24table[data[i]];
1370			trans[i+1] = d_8to24table[data[i+1]];
1371			trans[i+2] = d_8to24table[data[i+2]];
1372			trans[i+3] = d_8to24table[data[i+3]];
1373		}
1374	}
1375
1376	if (VID_Is8bit() && !alpha && (data!=scrap_texels[0])) {
1377		GL_Upload8_EXT (data, width, height, mipmap, alpha);
1378		return;
1379	}
1380
1381	GL_Upload32 (trans, width, height, mipmap, alpha);
1382}
1383
1384/*
1385================
1386GL_LoadTexture
1387================
1388*/
1389int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha)
1390{
1391	int			i;
1392	gltexture_t	*glt;
1393
1394	// see if the texture is allready present
1395	if (identifier[0])
1396	{
1397		for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
1398		{
1399			if (!strcmp (identifier, glt->identifier))
1400			{
1401				if (width != glt->width || height != glt->height)
1402					Sys_Error ("GL_LoadTexture: cache mismatch");
1403				return gltextures[i].texnum;
1404			}
1405		}
1406	}
1407	else
1408		glt = &gltextures[numgltextures];
1409	numgltextures++;
1410
1411	strcpy (glt->identifier, identifier);
1412	glt->texnum = texture_extension_number;
1413	glt->width = width;
1414	glt->height = height;
1415	glt->mipmap = mipmap;
1416
1417	GL_Bind(texture_extension_number );
1418
1419	GL_Upload8 (data, width, height, mipmap, alpha);
1420
1421	texture_extension_number++;
1422
1423	return texture_extension_number-1;
1424}
1425
1426/*
1427================
1428GL_LoadPicTexture
1429================
1430*/
1431int GL_LoadPicTexture (qpic_t *pic)
1432{
1433	return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true);
1434}
1435
1436/****************************************/
1437
1438static GLenum oldtarget = TEXTURE0_SGIS;
1439
1440void GL_SelectTexture (GLenum target)
1441{
1442	if (!gl_mtexable)
1443		return;
1444#ifdef USE_OPENGLES
1445	// !!! Implement this.
1446#else
1447#ifndef __linux__ // no multitexture under Linux yet
1448	qglSelectTextureSGIS(target);
1449#endif
1450#endif
1451	if (target == oldtarget)
1452		return;
1453	cnttextures[oldtarget-TEXTURE0_SGIS] = currenttexture;
1454	currenttexture = cnttextures[target-TEXTURE0_SGIS];
1455	oldtarget = target;
1456}
1457
1458
1459// OpenGL ES compatible DrawQuad utility
1460
1461void DrawQuad_NoTex(float x, float y, float w, float h)
1462{
1463	glEnableClientState(GL_VERTEX_ARRAY);
1464	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1465	float vertex[2*4] = {x,y,x+w,y, x+w, y+h, x, y+h};
1466	short index[4] = {0, 1, 2, 3};
1467	glVertexPointer( 2, GL_FLOAT, 0, vertex);
1468	glDrawElements(GL_TRIANGLE_FAN, 4, GL_SHORT, index);
1469}
1470
1471void DrawQuad(float x, float y, float w, float h, float u, float v, float uw, float vh)
1472{
1473	glEnableClientState(GL_VERTEX_ARRAY);
1474	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1475    float texcoord[2*4] = {u, v, u + uw, v, u + uw, v + vh, u, v + vh};
1476	float vertex[2*4] = {x,y,x+w,y, x+w, y+h, x, y+h};
1477	unsigned short index[4] = {0, 1, 2, 3};
1478	glTexCoordPointer( 2, GL_FLOAT, 0, texcoord);
1479	glVertexPointer( 2, GL_FLOAT, 0, vertex);
1480	glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_SHORT, index);
1481}
1482
1483#ifdef USE_OPENGLES
1484
1485// Reimplementation of OpenGL functions that are missing in OpenGL ES
1486
1487void glColor3f(GLfloat r, GLfloat g, GLfloat b)
1488{
1489	glColor4f(r, g, b, 1.0f);
1490}
1491
1492void glColor4fv(GLfloat* pColor)
1493{
1494	glColor4f(pColor[0], pColor[1], pColor[2], pColor[3]);
1495}
1496
1497void glColor4ubv(unsigned char* pColor)
1498{
1499	glColor4f(pColor[0] / 255.0f, pColor[1] / 255.0f, pColor[2] / 255.0f, pColor[3] / 255.0f);
1500}
1501
1502#endif
1503