fixed clipping on some machines
[twcon.git] / src / engine / client / graphics.cpp
blobd1f0b8a84c5bdba05e70f03f27149d6d87c4e017
1 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2 /* If you are missing that file, acquire a complete release at teeworlds.com. */
4 #include <base/detect.h>
5 #include <base/math.h>
7 #include "SDL.h"
9 #ifdef CONF_FAMILY_WINDOWS
10 #define WIN32_LEAN_AND_MEAN
11 #include <windows.h>
12 #endif
14 #ifdef CONF_PLATFORM_MACOSX
15 #include <OpenGL/gl.h>
16 #include <OpenGL/glu.h>
17 #else
18 #include <GL/gl.h>
19 #include <GL/glu.h>
20 #endif
22 #include <base/system.h>
23 #include <engine/external/pnglite/pnglite.h>
25 #include <engine/shared/config.h>
26 #include <engine/graphics.h>
27 #include <engine/storage.h>
28 #include <engine/keys.h>
29 #include <engine/console.h>
31 #include <math.h> // cosf, sinf
33 #include "graphics.h"
35 // compressed textures
36 #define GL_COMPRESSED_RGB_ARB 0x84ED
37 #define GL_COMPRESSED_RGBA_ARB 0x84EE
38 #define GL_COMPRESSED_ALPHA_ARB 0x84E9
40 #define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
43 static CVideoMode g_aFakeModes[] = {
44 {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8},
45 {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8},
46 {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8},
47 {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8},
48 {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8},
49 {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8},
50 {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8},
51 {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8},
52 {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8},
53 {2048,1536,8,8,8},
55 {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5},
56 {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5},
57 {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5},
58 {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5},
59 {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5},
60 {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5},
61 {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5},
62 {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5},
63 {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5},
64 {2048,1536,5,6,5}
67 void CGraphics_OpenGL::Flush()
69 if(m_NumVertices == 0)
70 return;
72 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
73 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
75 glVertexPointer(3, GL_FLOAT,
76 sizeof(CVertex),
77 (char*)m_aVertices);
78 glTexCoordPointer(2, GL_FLOAT,
79 sizeof(CVertex),
80 (char*)m_aVertices + sizeof(float)*3);
81 glColorPointer(4, GL_FLOAT,
82 sizeof(CVertex),
83 (char*)m_aVertices + sizeof(float)*5);
84 glEnableClientState(GL_VERTEX_ARRAY);
85 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
86 glEnableClientState(GL_COLOR_ARRAY);
88 if(m_RenderEnable)
90 if(m_Drawing == DRAWING_QUADS)
91 glDrawArrays(GL_QUADS, 0, m_NumVertices);
92 else if(m_Drawing == DRAWING_LINES)
93 glDrawArrays(GL_LINES, 0, m_NumVertices);
96 // Reset pointer
97 m_NumVertices = 0;
100 void CGraphics_OpenGL::AddVertices(int Count)
102 m_NumVertices += Count;
103 if((m_NumVertices + Count) >= MAX_VERTICES)
104 Flush();
107 void CGraphics_OpenGL::Rotate4(const CPoint &rCenter, CVertex *pPoints)
109 float c = cosf(m_Rotation);
110 float s = sinf(m_Rotation);
111 float x, y;
112 int i;
114 for(i = 0; i < 4; i++)
116 x = pPoints[i].m_Pos.x - rCenter.x;
117 y = pPoints[i].m_Pos.y - rCenter.y;
118 pPoints[i].m_Pos.x = x * c - y * s + rCenter.x;
119 pPoints[i].m_Pos.y = x * s + y * c + rCenter.y;
123 unsigned char CGraphics_OpenGL::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp)
125 int Value = 0;
126 for(int x = 0; x < ScaleW; x++)
127 for(int y = 0; y < ScaleH; y++)
128 Value += pData[((v+y)*w+(u+x))*Bpp+Offset];
129 return Value/(ScaleW*ScaleH);
132 unsigned char *CGraphics_OpenGL::Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData)
134 unsigned char *pTmpData;
135 int ScaleW = Width/NewWidth;
136 int ScaleH = Height/NewHeight;
138 int Bpp = 3;
139 if(Format == CImageInfo::FORMAT_RGBA)
140 Bpp = 4;
142 pTmpData = (unsigned char *)mem_alloc(NewWidth*NewHeight*Bpp, 1);
144 int c = 0;
145 for(int y = 0; y < NewHeight; y++)
146 for(int x = 0; x < NewWidth; x++, c++)
148 pTmpData[c*Bpp] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 0, ScaleW, ScaleH, Bpp);
149 pTmpData[c*Bpp+1] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 1, ScaleW, ScaleH, Bpp);
150 pTmpData[c*Bpp+2] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 2, ScaleW, ScaleH, Bpp);
151 if(Bpp == 4)
152 pTmpData[c*Bpp+3] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 3, ScaleW, ScaleH, Bpp);
155 return pTmpData;
158 CGraphics_OpenGL::CGraphics_OpenGL()
160 m_NumVertices = 0;
162 m_ScreenX0 = 0;
163 m_ScreenY0 = 0;
164 m_ScreenX1 = 0;
165 m_ScreenY1 = 0;
167 m_ScreenWidth = -1;
168 m_ScreenHeight = -1;
170 m_Rotation = 0;
171 m_Drawing = 0;
172 m_InvalidTexture = 0;
174 m_TextureMemoryUsage = 0;
176 m_RenderEnable = true;
177 m_DoScreenshot = false;
180 void CGraphics_OpenGL::ClipEnable(int x, int y, int w, int h)
182 if(x < 0)
183 w += x;
184 if(y < 0)
185 h += y;
187 x = clamp(x, 0, ScreenWidth());
188 y = clamp(y, 0, ScreenHeight());
189 w = clamp(w, 0, ScreenWidth()-x);
190 h = clamp(h, 0, ScreenHeight()-y);
192 glScissor(x, ScreenHeight()-(y+h), w, h);
193 glEnable(GL_SCISSOR_TEST);
196 void CGraphics_OpenGL::ClipDisable()
198 //if(no_gfx) return;
199 glDisable(GL_SCISSOR_TEST);
202 void CGraphics_OpenGL::BlendNone()
204 glDisable(GL_BLEND);
207 void CGraphics_OpenGL::BlendNormal()
209 glEnable(GL_BLEND);
210 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
213 void CGraphics_OpenGL::BlendAdditive()
215 glEnable(GL_BLEND);
216 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
219 int CGraphics_OpenGL::MemoryUsage() const
221 return m_TextureMemoryUsage;
224 void CGraphics_OpenGL::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY)
226 m_ScreenX0 = TopLeftX;
227 m_ScreenY0 = TopLeftY;
228 m_ScreenX1 = BottomRightX;
229 m_ScreenY1 = BottomRightY;
230 glMatrixMode(GL_PROJECTION);
231 glLoadIdentity();
232 glOrtho(TopLeftX, BottomRightX, BottomRightY, TopLeftY, 1.0f, 10.f);
235 void CGraphics_OpenGL::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY)
237 *pTopLeftX = m_ScreenX0;
238 *pTopLeftY = m_ScreenY0;
239 *pBottomRightX = m_ScreenX1;
240 *pBottomRightY = m_ScreenY1;
243 void CGraphics_OpenGL::LinesBegin()
245 dbg_assert(m_Drawing == 0, "called Graphics()->LinesBegin twice");
246 m_Drawing = DRAWING_LINES;
247 SetColor(1,1,1,1);
250 void CGraphics_OpenGL::LinesEnd()
252 dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesEnd without begin");
253 Flush();
254 m_Drawing = 0;
257 void CGraphics_OpenGL::LinesDraw(const CLineItem *pArray, int Num)
259 dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesDraw without begin");
261 for(int i = 0; i < Num; ++i)
263 m_aVertices[m_NumVertices + 2*i].m_Pos.x = pArray[i].m_X0;
264 m_aVertices[m_NumVertices + 2*i].m_Pos.y = pArray[i].m_Y0;
265 m_aVertices[m_NumVertices + 2*i].m_Tex = m_aTexture[0];
266 m_aVertices[m_NumVertices + 2*i].m_Color = m_aColor[0];
268 m_aVertices[m_NumVertices + 2*i + 1].m_Pos.x = pArray[i].m_X1;
269 m_aVertices[m_NumVertices + 2*i + 1].m_Pos.y = pArray[i].m_Y1;
270 m_aVertices[m_NumVertices + 2*i + 1].m_Tex = m_aTexture[1];
271 m_aVertices[m_NumVertices + 2*i + 1].m_Color = m_aColor[1];
274 AddVertices(2*Num);
277 int CGraphics_OpenGL::UnloadTexture(int Index)
279 if(Index == m_InvalidTexture)
280 return 0;
282 if(Index < 0)
283 return 0;
285 glDeleteTextures(1, &m_aTextures[Index].m_Tex);
286 m_aTextures[Index].m_Next = m_FirstFreeTexture;
287 m_TextureMemoryUsage -= m_aTextures[Index].m_MemSize;
288 m_FirstFreeTexture = Index;
289 return 0;
293 int CGraphics_OpenGL::LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags)
295 int Mipmap = 1;
296 unsigned char *pTexData = (unsigned char *)pData;
297 unsigned char *pTmpData = 0;
298 int Oglformat = 0;
299 int StoreOglformat = 0;
300 int Tex = 0;
302 // don't waste memory on texture if we are stress testing
303 if(g_Config.m_DbgStress)
304 return m_InvalidTexture;
306 // grab texture
307 Tex = m_FirstFreeTexture;
308 m_FirstFreeTexture = m_aTextures[Tex].m_Next;
309 m_aTextures[Tex].m_Next = -1;
311 // resample if needed
312 if(!(Flags&TEXLOAD_NORESAMPLE) && (Format == CImageInfo::FORMAT_RGBA || Format == CImageInfo::FORMAT_RGB))
314 if(Width > GL_MAX_TEXTURE_SIZE || Height > GL_MAX_TEXTURE_SIZE)
316 int NewWidth = min(Width, GL_MAX_TEXTURE_SIZE);
317 int NewHeight = min(Height, GL_MAX_TEXTURE_SIZE);
318 pTmpData = Rescale(Width, Height, NewWidth, NewHeight, Format, pTexData);
319 pTexData = pTmpData;
320 Width = NewWidth;
321 Height = NewHeight;
323 else if(Width > 16 && Height > 16 && g_Config.m_GfxTextureQuality == 0)
325 pTmpData = Rescale(Width, Height, Width/2, Height/2, Format, pTexData);
326 pTexData = pTmpData;
327 Width /= 2;
328 Height /= 2;
332 Oglformat = GL_RGBA;
333 if(Format == CImageInfo::FORMAT_RGB)
334 Oglformat = GL_RGB;
335 else if(Format == CImageInfo::FORMAT_ALPHA)
336 Oglformat = GL_ALPHA;
338 // upload texture
339 if(g_Config.m_GfxTextureCompression)
341 StoreOglformat = GL_COMPRESSED_RGBA_ARB;
342 if(StoreFormat == CImageInfo::FORMAT_RGB)
343 StoreOglformat = GL_COMPRESSED_RGB_ARB;
344 else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
345 StoreOglformat = GL_COMPRESSED_ALPHA_ARB;
347 else
349 StoreOglformat = GL_RGBA;
350 if(StoreFormat == CImageInfo::FORMAT_RGB)
351 StoreOglformat = GL_RGB;
352 else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
353 StoreOglformat = GL_ALPHA;
356 glGenTextures(1, &m_aTextures[Tex].m_Tex);
357 glBindTexture(GL_TEXTURE_2D, m_aTextures[Tex].m_Tex);
358 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
359 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
360 gluBuild2DMipmaps(GL_TEXTURE_2D, StoreOglformat, Width, Height, Oglformat, GL_UNSIGNED_BYTE, pTexData);
362 // calculate memory usage
364 int PixelSize = 4;
365 if(StoreFormat == CImageInfo::FORMAT_RGB)
366 PixelSize = 3;
367 else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
368 PixelSize = 1;
370 m_aTextures[Tex].m_MemSize = Width*Height*PixelSize;
371 if(Mipmap)
373 while(Width > 2 && Height > 2)
375 Width>>=1;
376 Height>>=1;
377 m_aTextures[Tex].m_MemSize += Width*Height*PixelSize;
382 m_TextureMemoryUsage += m_aTextures[Tex].m_MemSize;
383 mem_free(pTmpData);
384 return Tex;
387 // simple uncompressed RGBA loaders
388 int CGraphics_OpenGL::LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags)
390 int l = str_length(pFilename);
391 int ID;
392 CImageInfo Img;
394 if(l < 3)
395 return -1;
396 if(LoadPNG(&Img, pFilename, StorageType))
398 if (StoreFormat == CImageInfo::FORMAT_AUTO)
399 StoreFormat = Img.m_Format;
401 ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags);
402 mem_free(Img.m_pData);
403 return ID;
406 return m_InvalidTexture;
409 int CGraphics_OpenGL::LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType)
411 char aCompleteFilename[512];
412 unsigned char *pBuffer;
413 png_t Png; // ignore_convention
415 // open file for reading
416 png_init(0,0); // ignore_convention
418 IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType, aCompleteFilename, sizeof(aCompleteFilename));
419 if(File)
420 io_close(File);
421 else
423 dbg_msg("game/png", "failed to open file. filename='%s'", pFilename);
424 return 0;
427 int Error = png_open_file(&Png, aCompleteFilename); // ignore_convention
428 if(Error != PNG_NO_ERROR)
430 dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename);
431 if(Error != PNG_FILE_ERROR)
432 png_close_file(&Png); // ignore_convention
433 return 0;
436 if(Png.depth != 8 || (Png.color_type != PNG_TRUECOLOR && Png.color_type != PNG_TRUECOLOR_ALPHA)) // ignore_convention
438 dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename);
439 png_close_file(&Png); // ignore_convention
440 return 0;
443 pBuffer = (unsigned char *)mem_alloc(Png.width * Png.height * Png.bpp, 1); // ignore_convention
444 png_get_data(&Png, pBuffer); // ignore_convention
445 png_close_file(&Png); // ignore_convention
447 pImg->m_Width = Png.width; // ignore_convention
448 pImg->m_Height = Png.height; // ignore_convention
449 if(Png.color_type == PNG_TRUECOLOR) // ignore_convention
450 pImg->m_Format = CImageInfo::FORMAT_RGB;
451 else if(Png.color_type == PNG_TRUECOLOR_ALPHA) // ignore_convention
452 pImg->m_Format = CImageInfo::FORMAT_RGBA;
453 pImg->m_pData = pBuffer;
454 return 1;
457 void CGraphics_OpenGL::ScreenshotDirect(const char *pFilename)
459 // fetch image data
460 int y;
461 int w = m_ScreenWidth;
462 int h = m_ScreenHeight;
463 unsigned char *pPixelData = (unsigned char *)mem_alloc(w*(h+1)*3, 1);
464 unsigned char *pTempRow = pPixelData+w*h*3;
465 GLint Alignment;
466 glGetIntegerv(GL_PACK_ALIGNMENT, &Alignment);
467 glPixelStorei(GL_PACK_ALIGNMENT, 1);
468 glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
469 glPixelStorei(GL_PACK_ALIGNMENT, Alignment);
471 // flip the pixel because opengl works from bottom left corner
472 for(y = 0; y < h/2; y++)
474 mem_copy(pTempRow, pPixelData+y*w*3, w*3);
475 mem_copy(pPixelData+y*w*3, pPixelData+(h-y-1)*w*3, w*3);
476 mem_copy(pPixelData+(h-y-1)*w*3, pTempRow,w*3);
479 // find filename
481 char aWholePath[1024];
482 png_t Png; // ignore_convention
484 IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath));
485 if(File)
486 io_close(File);
488 // save png
489 char aBuf[256];
490 str_format(aBuf, sizeof(aBuf), "saved screenshot to '%s'", aWholePath);
491 m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf);
492 png_open_file_write(&Png, aWholePath); // ignore_convention
493 png_set_data(&Png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pPixelData); // ignore_convention
494 png_close_file(&Png); // ignore_convention
497 // clean up
498 mem_free(pPixelData);
501 void CGraphics_OpenGL::TextureSet(int TextureID)
503 dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin");
504 if(TextureID == -1)
506 glDisable(GL_TEXTURE_2D);
508 else
510 glEnable(GL_TEXTURE_2D);
511 glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].m_Tex);
515 void CGraphics_OpenGL::Clear(float r, float g, float b)
517 glClearColor(r,g,b,0.0f);
518 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
521 void CGraphics_OpenGL::QuadsBegin()
523 dbg_assert(m_Drawing == 0, "called Graphics()->QuadsBegin twice");
524 m_Drawing = DRAWING_QUADS;
526 QuadsSetSubset(0,0,1,1);
527 QuadsSetRotation(0);
528 SetColor(1,1,1,1);
531 void CGraphics_OpenGL::QuadsEnd()
533 dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEnd without begin");
534 Flush();
535 m_Drawing = 0;
538 void CGraphics_OpenGL::QuadsSetRotation(float Angle)
540 dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin");
541 m_Rotation = Angle;
544 void CGraphics_OpenGL::SetColorVertex(const CColorVertex *pArray, int Num)
546 dbg_assert(m_Drawing != 0, "called Graphics()->SetColorVertex without begin");
548 for(int i = 0; i < Num; ++i)
550 m_aColor[pArray[i].m_Index].r = pArray[i].m_R;
551 m_aColor[pArray[i].m_Index].g = pArray[i].m_G;
552 m_aColor[pArray[i].m_Index].b = pArray[i].m_B;
553 m_aColor[pArray[i].m_Index].a = pArray[i].m_A;
557 void CGraphics_OpenGL::SetColor(float r, float g, float b, float a)
559 dbg_assert(m_Drawing != 0, "called Graphics()->SetColor without begin");
560 CColorVertex Array[4] = {
561 CColorVertex(0, r, g, b, a),
562 CColorVertex(1, r, g, b, a),
563 CColorVertex(2, r, g, b, a),
564 CColorVertex(3, r, g, b, a)};
565 SetColorVertex(Array, 4);
568 void CGraphics_OpenGL::QuadsSetSubset(float TlU, float TlV, float BrU, float BrV)
570 dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin");
572 m_aTexture[0].u = TlU; m_aTexture[1].u = BrU;
573 m_aTexture[0].v = TlV; m_aTexture[1].v = TlV;
575 m_aTexture[3].u = TlU; m_aTexture[2].u = BrU;
576 m_aTexture[3].v = BrV; m_aTexture[2].v = BrV;
579 void CGraphics_OpenGL::QuadsSetSubsetFree(
580 float x0, float y0, float x1, float y1,
581 float x2, float y2, float x3, float y3)
583 m_aTexture[0].u = x0; m_aTexture[0].v = y0;
584 m_aTexture[1].u = x1; m_aTexture[1].v = y1;
585 m_aTexture[2].u = x2; m_aTexture[2].v = y2;
586 m_aTexture[3].u = x3; m_aTexture[3].v = y3;
589 void CGraphics_OpenGL::QuadsDraw(CQuadItem *pArray, int Num)
591 for(int i = 0; i < Num; ++i)
593 pArray[i].m_X -= pArray[i].m_Width/2;
594 pArray[i].m_Y -= pArray[i].m_Height/2;
597 QuadsDrawTL(pArray, Num);
600 void CGraphics_OpenGL::QuadsDrawTL(const CQuadItem *pArray, int Num)
602 CPoint Center;
603 Center.z = 0;
605 dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawTL without begin");
607 for(int i = 0; i < Num; ++i)
609 m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X;
610 m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y;
611 m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0];
612 m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0];
614 m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
615 m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y;
616 m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1];
617 m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1];
619 m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
620 m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
621 m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[2];
622 m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[2];
624 m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X;
625 m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
626 m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[3];
627 m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[3];
629 if(m_Rotation != 0)
631 Center.x = pArray[i].m_X + pArray[i].m_Width/2;
632 Center.y = pArray[i].m_Y + pArray[i].m_Height/2;
634 Rotate4(Center, &m_aVertices[m_NumVertices + 4*i]);
638 AddVertices(4*Num);
641 void CGraphics_OpenGL::QuadsDrawFreeform(const CFreeformItem *pArray, int Num)
643 dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawFreeform without begin");
645 for(int i = 0; i < Num; ++i)
647 m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X0;
648 m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y0;
649 m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0];
650 m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0];
652 m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X1;
653 m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y1;
654 m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1];
655 m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1];
657 m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X3;
658 m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y3;
659 m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[3];
660 m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[3];
662 m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X2;
663 m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y2;
664 m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[2];
665 m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[2];
668 AddVertices(4*Num);
671 void CGraphics_OpenGL::QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText)
673 float StartX = x;
675 QuadsBegin();
676 SetColor(r,g,b,a);
678 while(*pText)
680 char c = *pText;
681 pText++;
683 if(c == '\n')
685 x = StartX;
686 y += Size;
688 else
690 QuadsSetSubset(
691 (c%16)/16.0f,
692 (c/16)/16.0f,
693 (c%16)/16.0f+1.0f/16.0f,
694 (c/16)/16.0f+1.0f/16.0f);
696 CQuadItem QuadItem(x, y, Size, Size);
697 QuadsDrawTL(&QuadItem, 1);
698 x += Size/2;
702 QuadsEnd();
705 bool CGraphics_OpenGL::Init()
707 m_pStorage = Kernel()->RequestInterface<IStorage>();
708 m_pConsole = Kernel()->RequestInterface<IConsole>();
710 // Set all z to -5.0f
711 for(int i = 0; i < MAX_VERTICES; i++)
712 m_aVertices[i].m_Pos.z = -5.0f;
714 // init textures
715 m_FirstFreeTexture = 0;
716 for(int i = 0; i < MAX_TEXTURES; i++)
717 m_aTextures[i].m_Next = i+1;
718 m_aTextures[MAX_TEXTURES-1].m_Next = -1;
720 // set some default settings
721 glEnable(GL_BLEND);
722 glDisable(GL_CULL_FACE);
723 glDisable(GL_DEPTH_TEST);
724 glMatrixMode(GL_MODELVIEW);
725 glLoadIdentity();
727 glAlphaFunc(GL_GREATER, 0);
728 glEnable(GL_ALPHA_TEST);
729 glDepthMask(0);
731 // create null texture, will get id=0
732 static const unsigned char aNullTextureData[] = {
733 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff,
734 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff,
735 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff,
736 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff,
739 m_InvalidTexture = LoadTextureRaw(4,4,CImageInfo::FORMAT_RGBA,aNullTextureData,CImageInfo::FORMAT_RGBA,TEXLOAD_NORESAMPLE);
741 return true;
744 int CGraphics_SDL::TryInit()
746 const SDL_VideoInfo *pInfo;
747 int Flags = SDL_OPENGL;
749 m_ScreenWidth = g_Config.m_GfxScreenWidth;
750 m_ScreenHeight = g_Config.m_GfxScreenHeight;
752 pInfo = SDL_GetVideoInfo();
753 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
755 // set flags
756 Flags = SDL_OPENGL;
757 Flags |= SDL_GL_DOUBLEBUFFER;
758 Flags |= SDL_HWPALETTE;
759 if(g_Config.m_DbgResizable)
760 Flags |= SDL_RESIZABLE;
762 if(pInfo->hw_available) // ignore_convention
763 Flags |= SDL_HWSURFACE;
764 else
765 Flags |= SDL_SWSURFACE;
767 if(pInfo->blit_hw) // ignore_convention
768 Flags |= SDL_HWACCEL;
770 if(g_Config.m_GfxFullscreen)
771 Flags |= SDL_FULLSCREEN;
773 // set gl attributes
774 if(g_Config.m_GfxFsaaSamples)
776 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
777 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, g_Config.m_GfxFsaaSamples);
779 else
781 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
782 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
785 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
786 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, g_Config.m_GfxVsync);
788 // set caption
789 SDL_WM_SetCaption("Teeworlds", "Teeworlds");
791 // create window
792 m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags);
793 if(m_pScreenSurface == NULL)
795 dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError());
796 return -1;
799 return 0;
803 int CGraphics_SDL::InitWindow()
805 if(TryInit() == 0)
806 return 0;
808 // try disabling fsaa
809 while(g_Config.m_GfxFsaaSamples)
811 g_Config.m_GfxFsaaSamples--;
813 if(g_Config.m_GfxFsaaSamples)
814 dbg_msg("gfx", "lowering FSAA to %d and trying again", g_Config.m_GfxFsaaSamples);
815 else
816 dbg_msg("gfx", "disabling FSAA and trying again");
818 if(TryInit() == 0)
819 return 0;
822 // try lowering the resolution
823 if(g_Config.m_GfxScreenWidth != 640 || g_Config.m_GfxScreenHeight != 480)
825 dbg_msg("gfx", "setting resolution to 640x480 and trying again");
826 g_Config.m_GfxScreenWidth = 640;
827 g_Config.m_GfxScreenHeight = 480;
829 if(TryInit() == 0)
830 return 0;
833 dbg_msg("gfx", "out of ideas. failed to init graphics");
835 return -1;
839 CGraphics_SDL::CGraphics_SDL()
841 m_pScreenSurface = 0;
844 bool CGraphics_SDL::Init()
847 int Systems = SDL_INIT_VIDEO;
849 if(g_Config.m_SndEnable)
850 Systems |= SDL_INIT_AUDIO;
852 if(g_Config.m_ClEventthread)
853 Systems |= SDL_INIT_EVENTTHREAD;
855 if(SDL_Init(Systems) < 0)
857 dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError());
858 return true;
862 atexit(SDL_Quit); // ignore_convention
864 #ifdef CONF_FAMILY_WINDOWS
865 if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention
866 putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention
867 #endif
869 if(InitWindow() != 0)
870 return true;
872 SDL_ShowCursor(0);
874 CGraphics_OpenGL::Init();
876 MapScreen(0,0,g_Config.m_GfxScreenWidth, g_Config.m_GfxScreenHeight);
877 return false;
880 void CGraphics_SDL::Shutdown()
882 // TODO: SDL, is this correct?
883 SDL_Quit();
886 void CGraphics_SDL::Minimize()
888 SDL_WM_IconifyWindow();
891 void CGraphics_SDL::Maximize()
893 // TODO: SDL
896 int CGraphics_SDL::WindowActive()
898 return SDL_GetAppState()&SDL_APPINPUTFOCUS;
901 int CGraphics_SDL::WindowOpen()
903 return SDL_GetAppState()&SDL_APPACTIVE;
907 void CGraphics_SDL::TakeScreenshot(const char *pFilename)
909 char aDate[20];
910 str_timestamp(aDate, sizeof(aDate));
911 str_format(m_aScreenshotName, sizeof(m_aScreenshotName), "screenshots/%s_%s.png", pFilename?pFilename:"screenshot", aDate);
912 m_DoScreenshot = true;
915 void CGraphics_SDL::Swap()
917 if(m_DoScreenshot)
919 ScreenshotDirect(m_aScreenshotName);
920 m_DoScreenshot = false;
923 SDL_GL_SwapBuffers();
925 if(g_Config.m_GfxFinish)
926 glFinish();
930 int CGraphics_SDL::GetVideoModes(CVideoMode *pModes, int MaxModes)
932 int NumModes = sizeof(g_aFakeModes)/sizeof(CVideoMode);
933 SDL_Rect **ppModes;
935 if(g_Config.m_GfxDisplayAllModes)
937 int Count = sizeof(g_aFakeModes)/sizeof(CVideoMode);
938 mem_copy(pModes, g_aFakeModes, sizeof(g_aFakeModes));
939 if(MaxModes < Count)
940 Count = MaxModes;
941 return Count;
944 // TODO: fix this code on osx or windows
946 ppModes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN);
947 if(ppModes == NULL)
949 // no modes
950 NumModes = 0;
952 else if(ppModes == (SDL_Rect**)-1)
954 // all modes
956 else
958 NumModes = 0;
959 for(int i = 0; ppModes[i]; ++i)
961 if(NumModes == MaxModes)
962 break;
963 pModes[NumModes].m_Width = ppModes[i]->w;
964 pModes[NumModes].m_Height = ppModes[i]->h;
965 pModes[NumModes].m_Red = 8;
966 pModes[NumModes].m_Green = 8;
967 pModes[NumModes].m_Blue = 8;
968 NumModes++;
972 return NumModes;
975 extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); }