diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index 4107cd85b..bb888c608 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -1689,28 +1689,21 @@ void CClient::InitInterfaces()
m_Friends.Init();
}
-
-enum
+void CClient::Run()
{
- GFXSTATE_ERROR = -1,
- GFXSTATE_INIT = 0,
- GFXSTATE_IDLE,
- GFXSTATE_RENDERING,
- GFXSTATE_SWAPPING,
-};
+ int64 ReportTime = time_get();
+ int64 ReportInterval = time_freq()*1;
+
+ m_LocalStartTime = time_get();
+ m_SnapshotParts = 0;
-void CClient::GraphicsThread()
-{
// init graphics
if(m_pGraphics->Init() != 0)
{
- m_GfxState = GFXSTATE_ERROR;
- m_GfxStateSemaphore.signal();
- m_GfxState = GFXSTATE_ERROR;
+ dbg_msg("client", "couldn't init graphics");
return;
}
-
// open socket
{
NETADDR BindAddr;
@@ -1719,7 +1712,6 @@ void CClient::GraphicsThread()
if(!m_NetClient.Open(BindAddr, 0))
{
dbg_msg("client", "couldn't start network");
- m_GfxState = GFXSTATE_ERROR;
return;
}
}
@@ -1741,59 +1733,10 @@ void CClient::GraphicsThread()
// load data
if(!LoadData())
- {
- m_GfxState = GFXSTATE_ERROR;
return;
- }
GameClient()->OnInit();
-
- while(1)
- {
- // do idle
- sync_barrier();
- m_GfxState = GFXSTATE_IDLE;
- m_GfxStateSemaphore.signal();
- m_GfxRenderSemaphore.wait();
-
- // do render
- m_GfxState = GFXSTATE_RENDERING;
- m_GfxStateSemaphore.signal();
- Render();
- sync_barrier();
-
- // do swap
- m_GfxState = GFXSTATE_SWAPPING;
- m_GfxStateSemaphore.signal();
- m_pGraphics->Swap();
- }
-
- // do shutdown
-}
-
-void CClient::Run()
-{
- int64 ReportTime = time_get();
- int64 ReportInterval = time_freq()*1;
-
- m_LocalStartTime = time_get();
- m_SnapshotParts = 0;
-
- m_GfxState = GFXSTATE_INIT;
- thread_create(GraphicsThreadProxy, this);
-
- // wait for gfx to init
- while(1)
- {
- m_GfxStateSemaphore.wait();
- if(m_GfxState == GFXSTATE_ERROR)
- return;
- if(m_GfxState != GFXSTATE_INIT)
- break;
- }
-
-
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "version %s", GameClient()->NetVersion());
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf);
@@ -1910,26 +1853,6 @@ void CClient::Run()
Update();
-
- if(m_GfxState == GFXSTATE_IDLE)
- {
- // issue new rendering
- m_GfxRenderSemaphore.signal();
-
- // wait for gfx to finish rendering
- while(m_GfxState != GFXSTATE_SWAPPING)
- m_GfxStateSemaphore.wait();
-
- }
-
- /*
- if(m_pGraphics->AsyncSwapIsDone())
- {
- m_pGraphics->BeginScene();
- Render();
- m_pGraphics->EndScene();
- }*/
- /*
if(g_Config.m_DbgStress)
{
if((m_Frames%10) == 0)
@@ -1942,7 +1865,7 @@ void CClient::Run()
{
Render();
m_pGraphics->Swap();
- }*/
+ }
}
AutoScreenshot_Cleanup();
diff --git a/src/engine/client/graphics.cpp b/src/engine/client/graphics.cpp
index 617162aee..2d034f3f7 100644
--- a/src/engine/client/graphics.cpp
+++ b/src/engine/client/graphics.cpp
@@ -950,4 +950,4 @@ int CGraphics_SDL::GetVideoModes(CVideoMode *pModes, int MaxModes)
return NumModes;
}
-extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); }
+//extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); }
diff --git a/src/engine/client/graphics.h b/src/engine/client/graphics.h
index 95e9769ad..84142d9d5 100644
--- a/src/engine/client/graphics.h
+++ b/src/engine/client/graphics.h
@@ -3,6 +3,348 @@
#ifndef ENGINE_CLIENT_GRAPHICS_H
#define ENGINE_CLIENT_GRAPHICS_H
+#include
+
+class CCommandBuffer
+{
+ class CBuffer
+ {
+ unsigned char *m_pData;
+ unsigned m_Size;
+ unsigned m_Used;
+ public:
+ CBuffer(unsigned BufferSize)
+ {
+ m_Size = BufferSize;
+ m_pData = new unsigned char[m_Size];
+ m_Used = 0;
+ }
+
+ ~CBuffer()
+ {
+ delete [] m_pData;
+ m_pData = 0x0;
+ m_Used = 0;
+ m_Size = 0;
+ }
+
+ void Reset()
+ {
+ m_Used = 0;
+ }
+
+ void *Alloc(unsigned Requested)
+ {
+ if(Requested + m_Used > m_Size)
+ return 0;
+ void *pPtr = &m_pData[m_Used];
+ m_Used += Requested;
+ return pPtr;
+ }
+
+ unsigned char *DataPtr() { return m_pData; }
+ unsigned DataSize() { return m_Size; }
+ unsigned DataUsed() { return m_Used; }
+ };
+
+ CBuffer m_CmdBuffer;
+ CBuffer m_DataBuffer;
+public:
+ enum
+ {
+ //
+ CMD_NOP = 0,
+
+ //
+ CMD_INIT,
+ CMD_SHUTDOWN,
+
+ //
+ CMD_RUNBUFFER,
+
+ // syncronization
+ CMD_SIGNAL,
+
+ // texture commands
+ CMD_TEXTURE_CREATE,
+ CMD_TEXTURE_DESTROY,
+
+ // rendering
+ CMD_CLEAR,
+ CMD_RENDER,
+
+ // swap
+ CMD_SWAP,
+ };
+
+ enum
+ {
+ //
+ PRIMTYPE_INVALID = 0,
+ PRIMTYPE_LINES,
+ PRIMTYPE_QUADS,
+ };
+
+ enum
+ {
+ BLEND_NONE = 0,
+ BLEND_ALPHA,
+ BLEND_ADDITIVE,
+ };
+
+ struct SPoint { float x, y, z; };
+ struct STexCoord { float u, v; };
+ struct SColor { float r, g, b, a; };
+
+ struct SVertex
+ {
+ SPoint m_Pos;
+ STexCoord m_Tex;
+ SColor m_Color;
+ } ;
+
+ struct SCommand
+ {
+ public:
+ SCommand(unsigned Cmd) : m_Cmd(Cmd), m_Size(0) {}
+ unsigned m_Cmd;
+ unsigned m_Size;
+ };
+
+ struct SState
+ {
+ int m_BlendMode;
+ int m_Texture;
+ SPoint m_ScreenTL;
+ SPoint m_ScreenBR;
+
+ // clip
+ bool m_ClipEnable;
+ int m_ClipX;
+ int m_ClipY;
+ int m_ClipW;
+ int m_ClipH;
+ };
+
+ struct SCommand_Clear : public SCommand
+ {
+ SCommand_Clear() : SCommand(CMD_CLEAR) {}
+ SColor m_Color;
+ };
+
+
+ struct SCommand_Init : public SCommand
+ {
+ SCommand_Init() : SCommand(CMD_INIT) {}
+ volatile int *m_pResult;
+ };
+
+ struct SCommand_RunBuffer : public SCommand
+ {
+ SCommand_RunBuffer() : SCommand(CMD_RUNBUFFER) {}
+ CCommandBuffer *m_pOtherBuffer;
+ };
+
+ struct SCommand_Render : public SCommand
+ {
+ SCommand_Render() : SCommand(CMD_RENDER) {}
+ SState m_State;
+ unsigned m_PrimType;
+ unsigned m_PrimCount;
+ SVertex *m_pVertices;
+ };
+
+ struct SCommand_Swap : public SCommand
+ {
+ SCommand_Swap() : SCommand(CMD_SWAP) {}
+ };
+
+ //
+ CCommandBuffer(unsigned CmdBufferSize, unsigned DataBufferSize)
+ : m_CmdBuffer(CmdBufferSize), m_DataBuffer(DataBufferSize)
+ {
+ }
+
+ void *AllocData(unsigned WantedSize)
+ {
+ return m_DataBuffer.Alloc(WantedSize);
+ }
+
+ template
+ void AddCommand(const T &Command)
+ {
+ SCommand *pCmd = (SCommand *)m_CmdBuffer.Alloc(sizeof(Command));
+ if(!pCmd)
+ return;
+
+ mem_copy(pCmd, &Command, sizeof(Command));
+ pCmd->m_Size = sizeof(Command);
+ }
+
+ SCommand *GetCommand(unsigned *pIndex)
+ {
+ if(*pIndex >= m_CmdBuffer.DataUsed())
+ return NULL;
+
+ SCommand *pCommand = (SCommand *)&m_CmdBuffer.DataPtr()[*pIndex];
+ *pIndex += pCommand->m_Size;
+ return pCommand;
+ }
+
+ void Reset()
+ {
+ m_CmdBuffer.Reset();
+ m_DataBuffer.Reset();
+ }
+};
+
+class ICommandProcessor
+{
+public:
+ virtual ~ICommandProcessor() {}
+ virtual void RunBuffer(CCommandBuffer *pBuffer) = 0;
+};
+
+
+class CCommandProcessorHandler
+{
+ ICommandProcessor *m_pProcessor;
+ CCommandBuffer * volatile m_pBuffer;
+ volatile bool m_Shutdown;
+ semaphore m_Activity;
+ semaphore m_BufferDone;
+ void *m_pThread;
+
+ static void ThreadFunc(void *pUser);
+
+public:
+ CCommandProcessorHandler();
+ void Start(ICommandProcessor *pProcessor);
+ void RunBuffer(CCommandBuffer *pBuffer);
+ bool IsIdle() const { return m_pBuffer == 0; }
+ void WaitForIdle();
+};
+
+class CGraphics_Threaded : public IEngineGraphics
+{
+ CCommandBuffer::SState m_State;
+ CCommandProcessorHandler m_Handler;
+
+ CCommandBuffer *m_apCommandBuffers[2];
+ CCommandBuffer *m_pCommandBuffer;
+ unsigned m_CurrentCommandBuffer;
+
+ //
+ class IStorage *m_pStorage;
+ class IConsole *m_pConsole;
+
+ enum
+ {
+ MAX_VERTICES = 32*1024,
+ MAX_TEXTURES = 1024*4,
+
+ DRAWING_QUADS=1,
+ DRAWING_LINES=2
+ };
+
+ CCommandBuffer::SVertex m_aVertices[MAX_VERTICES];
+ int m_NumVertices;
+
+ CCommandBuffer::SColor m_aColor[4];
+ CCommandBuffer::STexCoord m_aTexture[4];
+
+ bool m_RenderEnable;
+
+ float m_Rotation;
+ int m_Drawing;
+ bool m_DoScreenshot;
+ char m_aScreenshotName[128];
+
+ int m_InvalidTexture;
+
+ struct CTexture
+ {
+ GLuint m_Tex;
+ int m_MemSize;
+ int m_Flags;
+ int m_Next;
+ };
+
+ CTexture m_aTextures[MAX_TEXTURES];
+ int m_FirstFreeTexture;
+ int m_TextureMemoryUsage;
+
+ void Flush();
+ void AddVertices(int Count);
+ void Rotate4(const CCommandBuffer::SPoint &rCenter, CCommandBuffer::SVertex *pPoints);
+
+ static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp);
+ static unsigned char *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData);
+public:
+ CGraphics_Threaded();
+
+ virtual void ClipEnable(int x, int y, int w, int h);
+ virtual void ClipDisable();
+
+ virtual void BlendNone();
+ virtual void BlendNormal();
+ virtual void BlendAdditive();
+
+ virtual int MemoryUsage() const;
+
+ virtual void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY);
+ virtual void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY);
+
+ virtual void LinesBegin();
+ virtual void LinesEnd();
+ virtual void LinesDraw(const CLineItem *pArray, int Num);
+
+ virtual int UnloadTexture(int Index);
+ virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags);
+
+ // simple uncompressed RGBA loaders
+ virtual int LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags);
+ virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType);
+
+ void ScreenshotDirect(const char *pFilename);
+
+ virtual void TextureSet(int TextureID);
+
+ virtual void Clear(float r, float g, float b);
+
+ virtual void QuadsBegin();
+ virtual void QuadsEnd();
+ virtual void QuadsSetRotation(float Angle);
+
+ virtual void SetColorVertex(const CColorVertex *pArray, int Num);
+ virtual void SetColor(float r, float g, float b, float a);
+
+ virtual void QuadsSetSubset(float TlU, float TlV, float BrU, float BrV);
+ virtual void QuadsSetSubsetFree(
+ float x0, float y0, float x1, float y1,
+ float x2, float y2, float x3, float y3);
+
+ virtual void QuadsDraw(CQuadItem *pArray, int Num);
+ virtual void QuadsDrawTL(const CQuadItem *pArray, int Num);
+ virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num);
+ virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText);
+
+ virtual void Minimize();
+ virtual void Maximize();
+
+ virtual int WindowActive();
+ virtual int WindowOpen();
+
+ virtual bool Init();
+ virtual void Shutdown();
+
+ virtual void TakeScreenshot(const char *pFilename);
+ virtual void Swap();
+
+ virtual int GetVideoModes(CVideoMode *pModes, int MaxModes);
+
+};
+
class CGraphics_OpenGL : public IEngineGraphics
{
protected:
diff --git a/src/engine/client/graphics_threaded.cpp b/src/engine/client/graphics_threaded.cpp
new file mode 100644
index 000000000..c227dbd52
--- /dev/null
+++ b/src/engine/client/graphics_threaded.cpp
@@ -0,0 +1,1225 @@
+/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
+/* If you are missing that file, acquire a complete release at teeworlds.com. */
+
+#include
+#include
+
+#include "SDL.h"
+#include "SDL_opengl.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include // cosf, sinf
+
+#include "graphics.h"
+
+
+static CVideoMode g_aFakeModes[] = {
+ {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8},
+ {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8},
+ {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8},
+ {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8},
+ {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8},
+ {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8},
+ {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8},
+ {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8},
+ {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8},
+ {2048,1536,8,8,8},
+
+ {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5},
+ {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5},
+ {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5},
+ {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5},
+ {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5},
+ {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5},
+ {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5},
+ {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5},
+ {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5},
+ {2048,1536,5,6,5}
+};
+
+
+class CCommandProcessorFragment_OpenGL
+{
+public:
+ void SetState(const CCommandBuffer::SState &State)
+ {
+ // blend
+ switch(State.m_BlendMode)
+ {
+ case CCommandBuffer::BLEND_NONE:
+ glDisable(GL_BLEND);
+ break;
+ case CCommandBuffer::BLEND_ALPHA:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case CCommandBuffer::BLEND_ADDITIVE:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ break;
+ default:
+ dbg_msg("render", "unknown blendmode %d\n", State.m_BlendMode);
+ };
+
+ // clip
+ /*if(State.m_ClipEnable)
+ {
+ glScissor(State.m_ClipX, State.m_ClipY+State.m_ClipH, State.m_ClipW, State.m_ClipH);
+ glEnable(GL_SCISSOR_TEST);
+ }
+ else*/
+ glDisable(GL_SCISSOR_TEST);
+
+ // texture
+ glDisable(GL_TEXTURE_2D);
+
+ // screen mapping
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(State.m_ScreenTL.x, State.m_ScreenBR.x, State.m_ScreenBR.y, State.m_ScreenTL.y, 1.0f, 10.f);
+
+ }
+
+ bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand)
+ {
+
+ switch(pBaseCommand->m_Cmd)
+ {
+ case CCommandBuffer::CMD_TEXTURE_CREATE:
+ break;
+ case CCommandBuffer::CMD_TEXTURE_DESTROY:
+ break;
+ case CCommandBuffer::CMD_CLEAR:
+ {
+ const CCommandBuffer::SCommand_Clear *pCommand = (CCommandBuffer::SCommand_Clear *)pBaseCommand;
+ glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ } break;
+ case CCommandBuffer::CMD_RENDER:
+ {
+ const CCommandBuffer::SCommand_Render *pCommand = (CCommandBuffer::SCommand_Render *)pBaseCommand;
+ SetState(pCommand->m_State);
+
+ glVertexPointer(3, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices);
+ glTexCoordPointer(2, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices + sizeof(float)*3);
+ glColorPointer(4, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices + sizeof(float)*5);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ switch(pCommand->m_PrimType)
+ {
+ case CCommandBuffer::PRIMTYPE_QUADS:
+ glDrawArrays(GL_QUADS, 0, pCommand->m_PrimCount*4);
+ break;
+ case CCommandBuffer::PRIMTYPE_LINES:
+ glDrawArrays(GL_LINES, 0, pCommand->m_PrimCount*2);
+ break;
+ default:
+ dbg_msg("render", "unknown primtype %d\n", pCommand->m_Cmd);
+ };
+ } break;
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+ }
+};
+
+class CCommandProcessorFragment_SDL
+{
+ // SDL stuff
+ SDL_Surface *m_pScreenSurface;
+
+ int m_ScreenWidth;
+ int m_ScreenHeight;
+
+ int TryInit()
+ {
+ m_ScreenWidth = g_Config.m_GfxScreenWidth;
+ m_ScreenHeight = g_Config.m_GfxScreenHeight;
+
+ const SDL_VideoInfo *pInfo = SDL_GetVideoInfo();
+ SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+
+ // set flags
+ int Flags = SDL_OPENGL;
+ if(g_Config.m_DbgResizable)
+ Flags |= SDL_RESIZABLE;
+
+ if(pInfo->hw_available) // ignore_convention
+ Flags |= SDL_HWSURFACE;
+ else
+ Flags |= SDL_SWSURFACE;
+
+ if(pInfo->blit_hw) // ignore_convention
+ Flags |= SDL_HWACCEL;
+
+ if(g_Config.m_GfxFullscreen)
+ Flags |= SDL_FULLSCREEN;
+
+ // set gl attributes
+ if(g_Config.m_GfxFsaaSamples)
+ {
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, g_Config.m_GfxFsaaSamples);
+ }
+ else
+ {
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, g_Config.m_GfxVsync);
+
+ // set caption
+ SDL_WM_SetCaption("Teeworlds", "Teeworlds");
+
+ // create window
+ m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags);
+ if(m_pScreenSurface == NULL)
+ {
+ dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError());
+ return -1;
+ }
+
+ return 0;
+ }
+
+
+ int InitWindow()
+ {
+ if(TryInit() == 0)
+ return 0;
+
+ // try disabling fsaa
+ while(g_Config.m_GfxFsaaSamples)
+ {
+ g_Config.m_GfxFsaaSamples--;
+
+ if(g_Config.m_GfxFsaaSamples)
+ dbg_msg("gfx", "lowering FSAA to %d and trying again", g_Config.m_GfxFsaaSamples);
+ else
+ dbg_msg("gfx", "disabling FSAA and trying again");
+
+ if(TryInit() == 0)
+ return 0;
+ }
+
+ // try lowering the resolution
+ if(g_Config.m_GfxScreenWidth != 640 || g_Config.m_GfxScreenHeight != 480)
+ {
+ dbg_msg("gfx", "setting resolution to 640x480 and trying again");
+ g_Config.m_GfxScreenWidth = 640;
+ g_Config.m_GfxScreenHeight = 480;
+
+ if(TryInit() == 0)
+ return 0;
+ }
+
+ dbg_msg("gfx", "out of ideas. failed to init graphics");
+
+ return -1;
+ }
+
+ int Init()
+ {
+ {
+ int Systems = SDL_INIT_VIDEO;
+
+ if(g_Config.m_SndEnable)
+ Systems |= SDL_INIT_AUDIO;
+
+ if(g_Config.m_ClEventthread)
+ Systems |= SDL_INIT_EVENTTHREAD;
+
+ if(SDL_Init(Systems) < 0)
+ {
+ dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError());
+ return -1;
+ }
+ }
+
+ atexit(SDL_Quit); // ignore_convention
+
+ #ifdef CONF_FAMILY_WINDOWS
+ if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention
+ putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention
+ #endif
+
+ if(InitWindow() != 0)
+ return -1;
+
+ SDL_ShowCursor(0);
+
+ // set some default settings
+ glEnable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glAlphaFunc(GL_GREATER, 0);
+ glEnable(GL_ALPHA_TEST);
+ glDepthMask(0);
+
+ return 0;
+ }
+
+
+public:
+ CCommandProcessorFragment_SDL()
+ {
+ m_pScreenSurface = 0x0;
+ }
+
+ bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand)
+ {
+
+ switch(pBaseCommand->m_Cmd)
+ {
+ case CCommandBuffer::CMD_NOP:
+ break;
+ case CCommandBuffer::CMD_INIT:
+ {
+ const CCommandBuffer::SCommand_Init *pCommand = (CCommandBuffer::SCommand_Init *)pBaseCommand;
+ *pCommand->m_pResult = Init();
+ } break;
+ case CCommandBuffer::CMD_SHUTDOWN:
+ break;
+ case CCommandBuffer::CMD_SWAP:
+ {
+ SDL_GL_SwapBuffers();
+ } break;
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+ }
+};
+
+
+class CCommandProcessor_SDL_OpenGL : public ICommandProcessor
+{
+ CCommandProcessorFragment_OpenGL m_OpenGL;
+ CCommandProcessorFragment_SDL m_SDL;
+ public:
+ virtual void RunBuffer(CCommandBuffer *pBuffer)
+ {
+ unsigned CmdIndex = 0;
+ while(1)
+ {
+ CCommandBuffer::SCommand * const pBaseCommand = pBuffer->GetCommand(&CmdIndex);
+ if(pBaseCommand == 0x0)
+ break;
+
+ if(m_OpenGL.RunCommand(pBaseCommand))
+ continue;
+
+ if(m_SDL.RunCommand(pBaseCommand))
+ continue;
+
+ dbg_msg("graphics", "unknown command %d", pBaseCommand->m_Cmd);
+ }
+ }
+};
+
+void CCommandProcessorHandler::ThreadFunc(void *pUser)
+{
+ CCommandProcessorHandler *pThis = (CCommandProcessorHandler *)pUser;
+
+ while(!pThis->m_Shutdown)
+ {
+ pThis->m_Activity.wait();
+ if(pThis->m_pBuffer)
+ {
+ pThis->m_pProcessor->RunBuffer(pThis->m_pBuffer);
+ sync_barrier();
+ pThis->m_pBuffer = 0x0;
+ pThis->m_BufferDone.signal();
+ }
+ }
+}
+
+CCommandProcessorHandler::CCommandProcessorHandler()
+{
+ m_pBuffer = 0x0;
+ m_pProcessor = 0x0;
+ m_pThread = 0x0;
+}
+
+void CCommandProcessorHandler::Start(ICommandProcessor *pProcessor)
+{
+ m_Shutdown = false;
+ m_pProcessor = pProcessor;
+ m_pThread = thread_create(ThreadFunc, this);
+ m_BufferDone.signal();
+}
+
+void CCommandProcessorHandler::RunBuffer(CCommandBuffer *pBuffer)
+{
+ WaitForIdle();
+ m_pBuffer = pBuffer;
+ m_Activity.signal();
+}
+
+void CCommandProcessorHandler::WaitForIdle()
+{
+ while(m_pBuffer != 0x0)
+ m_BufferDone.wait();
+}
+
+
+/*
+void RenderThread()
+{
+ .wait()
+ if(m_pCommandBuffer)
+ RunBuffer(m_pCommandBuffer);
+ // reset buffer?
+ m_pCommandBuffer = 0;
+}*/
+
+void CGraphics_Threaded::Flush()
+{
+ if(m_NumVertices == 0)
+ return;
+
+ int NumVerts = m_NumVertices;
+ m_NumVertices = 0;
+
+ CCommandBuffer::SCommand_Render Cmd;
+ Cmd.m_State = m_State;
+
+ if(m_Drawing == DRAWING_QUADS)
+ {
+ Cmd.m_PrimType = CCommandBuffer::PRIMTYPE_QUADS;
+ Cmd.m_PrimCount = NumVerts/4;
+ }
+ else if(m_Drawing == DRAWING_LINES)
+ {
+ Cmd.m_PrimType = CCommandBuffer::PRIMTYPE_LINES;
+ Cmd.m_PrimCount = NumVerts/2;
+ }
+ else
+ return;
+
+ Cmd.m_pVertices = (CCommandBuffer::SVertex *)m_pCommandBuffer->AllocData(sizeof(CCommandBuffer::SVertex)*NumVerts);
+ if(Cmd.m_pVertices == 0x0)
+ return;
+
+ mem_copy(Cmd.m_pVertices, m_aVertices, sizeof(CCommandBuffer::SVertex)*NumVerts);
+ m_pCommandBuffer->AddCommand(Cmd);
+}
+
+void CGraphics_Threaded::AddVertices(int Count)
+{
+ m_NumVertices += Count;
+ if((m_NumVertices + Count) >= MAX_VERTICES)
+ Flush();
+}
+
+void CGraphics_Threaded::Rotate4(const CCommandBuffer::SPoint &rCenter, CCommandBuffer::SVertex *pPoints)
+{
+ float c = cosf(m_Rotation);
+ float s = sinf(m_Rotation);
+ float x, y;
+ int i;
+
+ for(i = 0; i < 4; i++)
+ {
+ x = pPoints[i].m_Pos.x - rCenter.x;
+ y = pPoints[i].m_Pos.y - rCenter.y;
+ pPoints[i].m_Pos.x = x * c - y * s + rCenter.x;
+ pPoints[i].m_Pos.y = x * s + y * c + rCenter.y;
+ }
+}
+
+unsigned char CGraphics_Threaded::Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp)
+{
+ int Value = 0;
+ for(int x = 0; x < ScaleW; x++)
+ for(int y = 0; y < ScaleH; y++)
+ Value += pData[((v+y)*w+(u+x))*Bpp+Offset];
+ return Value/(ScaleW*ScaleH);
+}
+
+unsigned char *CGraphics_Threaded::Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData)
+{
+ unsigned char *pTmpData;
+ int ScaleW = Width/NewWidth;
+ int ScaleH = Height/NewHeight;
+
+ int Bpp = 3;
+ if(Format == CImageInfo::FORMAT_RGBA)
+ Bpp = 4;
+
+ pTmpData = (unsigned char *)mem_alloc(NewWidth*NewHeight*Bpp, 1);
+
+ int c = 0;
+ for(int y = 0; y < NewHeight; y++)
+ for(int x = 0; x < NewWidth; x++, c++)
+ {
+ pTmpData[c*Bpp] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 0, ScaleW, ScaleH, Bpp);
+ pTmpData[c*Bpp+1] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 1, ScaleW, ScaleH, Bpp);
+ pTmpData[c*Bpp+2] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 2, ScaleW, ScaleH, Bpp);
+ if(Bpp == 4)
+ pTmpData[c*Bpp+3] = Sample(Width, Height, pData, x*ScaleW, y*ScaleH, 3, ScaleW, ScaleH, Bpp);
+ }
+
+ return pTmpData;
+}
+
+CGraphics_Threaded::CGraphics_Threaded()
+{
+ m_State.m_ScreenTL.x = 0;
+ m_State.m_ScreenTL.y = 0;
+ m_State.m_ScreenBR.x = 0;
+ m_State.m_ScreenBR.y = 0;
+ m_State.m_ClipEnable = false;
+ m_State.m_ClipX = 0;
+ m_State.m_ClipY = 0;
+ m_State.m_ClipW = 0;
+ m_State.m_ClipH = 0;
+ m_State.m_Texture = -1;
+ m_State.m_BlendMode = CCommandBuffer::BLEND_NONE;
+
+
+
+ m_NumVertices = 0;
+
+ m_ScreenWidth = -1;
+ m_ScreenHeight = -1;
+
+ m_Rotation = 0;
+ m_Drawing = 0;
+ m_InvalidTexture = 0;
+
+ m_TextureMemoryUsage = 0;
+
+ m_RenderEnable = true;
+ m_DoScreenshot = false;
+}
+
+void CGraphics_Threaded::ClipEnable(int x, int y, int w, int h)
+{
+ if(x < 0)
+ w += x;
+ if(y < 0)
+ h += y;
+
+ x = clamp(x, 0, ScreenWidth());
+ y = clamp(y, 0, ScreenHeight());
+ w = clamp(w, 0, ScreenWidth()-x);
+ h = clamp(h, 0, ScreenHeight()-y);
+
+ m_State.m_ClipEnable = true;
+ m_State.m_ClipX = x;
+ m_State.m_ClipY = ScreenHeight()-(y+h);
+ m_State.m_ClipW = w;
+ m_State.m_ClipH = h;
+}
+
+void CGraphics_Threaded::ClipDisable()
+{
+ m_State.m_ClipEnable = false;
+}
+
+void CGraphics_Threaded::BlendNone()
+{
+ m_State.m_BlendMode = CCommandBuffer::BLEND_NONE;
+}
+
+void CGraphics_Threaded::BlendNormal()
+{
+ m_State.m_BlendMode = CCommandBuffer::BLEND_ALPHA;
+}
+
+void CGraphics_Threaded::BlendAdditive()
+{
+ m_State.m_BlendMode = CCommandBuffer::BLEND_ADDITIVE;
+}
+
+int CGraphics_Threaded::MemoryUsage() const
+{
+ return m_TextureMemoryUsage;
+}
+
+void CGraphics_Threaded::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY)
+{
+ m_State.m_ScreenTL.x = TopLeftX;
+ m_State.m_ScreenTL.y = TopLeftY;
+ m_State.m_ScreenBR.x = BottomRightX;
+ m_State.m_ScreenBR.y = BottomRightY;
+}
+
+void CGraphics_Threaded::GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY)
+{
+ *pTopLeftX = m_State.m_ScreenTL.x;
+ *pTopLeftY = m_State.m_ScreenTL.y;
+ *pBottomRightX = m_State.m_ScreenBR.x;
+ *pBottomRightY = m_State.m_ScreenBR.y;
+}
+
+void CGraphics_Threaded::LinesBegin()
+{
+ dbg_assert(m_Drawing == 0, "called Graphics()->LinesBegin twice");
+ m_Drawing = DRAWING_LINES;
+ SetColor(1,1,1,1);
+}
+
+void CGraphics_Threaded::LinesEnd()
+{
+ dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesEnd without begin");
+ Flush();
+ m_Drawing = 0;
+}
+
+void CGraphics_Threaded::LinesDraw(const CLineItem *pArray, int Num)
+{
+ dbg_assert(m_Drawing == DRAWING_LINES, "called Graphics()->LinesDraw without begin");
+
+ for(int i = 0; i < Num; ++i)
+ {
+ m_aVertices[m_NumVertices + 2*i].m_Pos.x = pArray[i].m_X0;
+ m_aVertices[m_NumVertices + 2*i].m_Pos.y = pArray[i].m_Y0;
+ m_aVertices[m_NumVertices + 2*i].m_Tex = m_aTexture[0];
+ m_aVertices[m_NumVertices + 2*i].m_Color = m_aColor[0];
+
+ m_aVertices[m_NumVertices + 2*i + 1].m_Pos.x = pArray[i].m_X1;
+ m_aVertices[m_NumVertices + 2*i + 1].m_Pos.y = pArray[i].m_Y1;
+ m_aVertices[m_NumVertices + 2*i + 1].m_Tex = m_aTexture[1];
+ m_aVertices[m_NumVertices + 2*i + 1].m_Color = m_aColor[1];
+ }
+
+ AddVertices(2*Num);
+}
+
+int CGraphics_Threaded::UnloadTexture(int Index)
+{
+ return 0;
+ if(Index == m_InvalidTexture)
+ return 0;
+
+ if(Index < 0)
+ return 0;
+
+ glDeleteTextures(1, &m_aTextures[Index].m_Tex);
+ m_aTextures[Index].m_Next = m_FirstFreeTexture;
+ m_TextureMemoryUsage -= m_aTextures[Index].m_MemSize;
+ m_FirstFreeTexture = Index;
+ return 0;
+}
+
+
+int CGraphics_Threaded::LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags)
+{
+ return 0;
+
+ int Mipmap = 1;
+ unsigned char *pTexData = (unsigned char *)pData;
+ unsigned char *pTmpData = 0;
+ int Oglformat = 0;
+ int StoreOglformat = 0;
+ int Tex = 0;
+
+ // don't waste memory on texture if we are stress testing
+ if(g_Config.m_DbgStress)
+ return m_InvalidTexture;
+
+ // grab texture
+ Tex = m_FirstFreeTexture;
+ m_FirstFreeTexture = m_aTextures[Tex].m_Next;
+ m_aTextures[Tex].m_Next = -1;
+
+ // resample if needed
+ if(!(Flags&TEXLOAD_NORESAMPLE) && (Format == CImageInfo::FORMAT_RGBA || Format == CImageInfo::FORMAT_RGB))
+ {
+ if(Width > GL_MAX_TEXTURE_SIZE || Height > GL_MAX_TEXTURE_SIZE)
+ {
+ int NewWidth = min(Width, GL_MAX_TEXTURE_SIZE);
+ int NewHeight = min(Height, GL_MAX_TEXTURE_SIZE);
+ pTmpData = Rescale(Width, Height, NewWidth, NewHeight, Format, pTexData);
+ pTexData = pTmpData;
+ Width = NewWidth;
+ Height = NewHeight;
+ }
+ else if(Width > 16 && Height > 16 && g_Config.m_GfxTextureQuality == 0)
+ {
+ pTmpData = Rescale(Width, Height, Width/2, Height/2, Format, pTexData);
+ pTexData = pTmpData;
+ Width /= 2;
+ Height /= 2;
+ }
+ }
+
+ Oglformat = GL_RGBA;
+ if(Format == CImageInfo::FORMAT_RGB)
+ Oglformat = GL_RGB;
+ else if(Format == CImageInfo::FORMAT_ALPHA)
+ Oglformat = GL_ALPHA;
+
+ // upload texture
+ if(g_Config.m_GfxTextureCompression)
+ {
+ StoreOglformat = GL_COMPRESSED_RGBA_ARB;
+ if(StoreFormat == CImageInfo::FORMAT_RGB)
+ StoreOglformat = GL_COMPRESSED_RGB_ARB;
+ else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
+ StoreOglformat = GL_COMPRESSED_ALPHA_ARB;
+ }
+ else
+ {
+ StoreOglformat = GL_RGBA;
+ if(StoreFormat == CImageInfo::FORMAT_RGB)
+ StoreOglformat = GL_RGB;
+ else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
+ StoreOglformat = GL_ALPHA;
+ }
+
+ glGenTextures(1, &m_aTextures[Tex].m_Tex);
+ glBindTexture(GL_TEXTURE_2D, m_aTextures[Tex].m_Tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+ gluBuild2DMipmaps(GL_TEXTURE_2D, StoreOglformat, Width, Height, Oglformat, GL_UNSIGNED_BYTE, pTexData);
+
+ // calculate memory usage
+ {
+ int PixelSize = 4;
+ if(StoreFormat == CImageInfo::FORMAT_RGB)
+ PixelSize = 3;
+ else if(StoreFormat == CImageInfo::FORMAT_ALPHA)
+ PixelSize = 1;
+
+ m_aTextures[Tex].m_MemSize = Width*Height*PixelSize;
+ if(Mipmap)
+ {
+ while(Width > 2 && Height > 2)
+ {
+ Width>>=1;
+ Height>>=1;
+ m_aTextures[Tex].m_MemSize += Width*Height*PixelSize;
+ }
+ }
+ }
+
+ m_TextureMemoryUsage += m_aTextures[Tex].m_MemSize;
+ mem_free(pTmpData);
+ return Tex;
+}
+
+// simple uncompressed RGBA loaders
+int CGraphics_Threaded::LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags)
+{
+ return 0;
+
+ int l = str_length(pFilename);
+ int ID;
+ CImageInfo Img;
+
+ if(l < 3)
+ return -1;
+ if(LoadPNG(&Img, pFilename, StorageType))
+ {
+ if (StoreFormat == CImageInfo::FORMAT_AUTO)
+ StoreFormat = Img.m_Format;
+
+ ID = LoadTextureRaw(Img.m_Width, Img.m_Height, Img.m_Format, Img.m_pData, StoreFormat, Flags);
+ mem_free(Img.m_pData);
+ if(ID != m_InvalidTexture && g_Config.m_Debug)
+ dbg_msg("graphics/texture", "loaded %s", pFilename);
+ return ID;
+ }
+
+ return m_InvalidTexture;
+}
+
+int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType)
+{
+ return 0;
+
+ char aCompleteFilename[512];
+ unsigned char *pBuffer;
+ png_t Png; // ignore_convention
+
+ // open file for reading
+ png_init(0,0); // ignore_convention
+
+ IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, StorageType, aCompleteFilename, sizeof(aCompleteFilename));
+ if(File)
+ io_close(File);
+ else
+ {
+ dbg_msg("game/png", "failed to open file. filename='%s'", pFilename);
+ return 0;
+ }
+
+ int Error = png_open_file(&Png, aCompleteFilename); // ignore_convention
+ if(Error != PNG_NO_ERROR)
+ {
+ dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename);
+ if(Error != PNG_FILE_ERROR)
+ png_close_file(&Png); // ignore_convention
+ return 0;
+ }
+
+ if(Png.depth != 8 || (Png.color_type != PNG_TRUECOLOR && Png.color_type != PNG_TRUECOLOR_ALPHA)) // ignore_convention
+ {
+ dbg_msg("game/png", "invalid format. filename='%s'", aCompleteFilename);
+ png_close_file(&Png); // ignore_convention
+ return 0;
+ }
+
+ pBuffer = (unsigned char *)mem_alloc(Png.width * Png.height * Png.bpp, 1); // ignore_convention
+ png_get_data(&Png, pBuffer); // ignore_convention
+ png_close_file(&Png); // ignore_convention
+
+ pImg->m_Width = Png.width; // ignore_convention
+ pImg->m_Height = Png.height; // ignore_convention
+ if(Png.color_type == PNG_TRUECOLOR) // ignore_convention
+ pImg->m_Format = CImageInfo::FORMAT_RGB;
+ else if(Png.color_type == PNG_TRUECOLOR_ALPHA) // ignore_convention
+ pImg->m_Format = CImageInfo::FORMAT_RGBA;
+ pImg->m_pData = pBuffer;
+ return 1;
+}
+
+void CGraphics_Threaded::ScreenshotDirect(const char *pFilename)
+{
+ // fetch image data
+ int y;
+ int w = m_ScreenWidth;
+ int h = m_ScreenHeight;
+ unsigned char *pPixelData = (unsigned char *)mem_alloc(w*(h+1)*3, 1);
+ unsigned char *pTempRow = pPixelData+w*h*3;
+ GLint Alignment;
+ glGetIntegerv(GL_PACK_ALIGNMENT, &Alignment);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pPixelData);
+ glPixelStorei(GL_PACK_ALIGNMENT, Alignment);
+
+ // flip the pixel because opengl works from bottom left corner
+ for(y = 0; y < h/2; y++)
+ {
+ mem_copy(pTempRow, pPixelData+y*w*3, w*3);
+ mem_copy(pPixelData+y*w*3, pPixelData+(h-y-1)*w*3, w*3);
+ mem_copy(pPixelData+(h-y-1)*w*3, pTempRow,w*3);
+ }
+
+ // find filename
+ {
+ char aWholePath[1024];
+ png_t Png; // ignore_convention
+
+ IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE, aWholePath, sizeof(aWholePath));
+ if(File)
+ io_close(File);
+
+ // save png
+ char aBuf[256];
+ str_format(aBuf, sizeof(aBuf), "saved screenshot to '%s'", aWholePath);
+ m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf);
+ png_open_file_write(&Png, aWholePath); // ignore_convention
+ png_set_data(&Png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pPixelData); // ignore_convention
+ png_close_file(&Png); // ignore_convention
+ }
+
+ // clean up
+ mem_free(pPixelData);
+}
+
+void CGraphics_Threaded::TextureSet(int TextureID)
+{
+ return;
+
+ dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin");
+ if(TextureID == -1)
+ {
+ glDisable(GL_TEXTURE_2D);
+ }
+ else
+ {
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].m_Tex);
+ }
+}
+
+void CGraphics_Threaded::Clear(float r, float g, float b)
+{
+ CCommandBuffer::SCommand_Clear Cmd;
+ Cmd.m_Color.r = r;
+ Cmd.m_Color.g = g;
+ Cmd.m_Color.b = b;
+ Cmd.m_Color.a = 0;
+ m_pCommandBuffer->AddCommand(Cmd);
+}
+
+void CGraphics_Threaded::QuadsBegin()
+{
+ dbg_assert(m_Drawing == 0, "called Graphics()->QuadsBegin twice");
+ m_Drawing = DRAWING_QUADS;
+
+ QuadsSetSubset(0,0,1,1);
+ QuadsSetRotation(0);
+ SetColor(1,1,1,1);
+}
+
+void CGraphics_Threaded::QuadsEnd()
+{
+ dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsEnd without begin");
+ Flush();
+ m_Drawing = 0;
+}
+
+void CGraphics_Threaded::QuadsSetRotation(float Angle)
+{
+ dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin");
+ m_Rotation = Angle;
+}
+
+void CGraphics_Threaded::SetColorVertex(const CColorVertex *pArray, int Num)
+{
+ dbg_assert(m_Drawing != 0, "called Graphics()->SetColorVertex without begin");
+
+ for(int i = 0; i < Num; ++i)
+ {
+ m_aColor[pArray[i].m_Index].r = pArray[i].m_R;
+ m_aColor[pArray[i].m_Index].g = pArray[i].m_G;
+ m_aColor[pArray[i].m_Index].b = pArray[i].m_B;
+ m_aColor[pArray[i].m_Index].a = pArray[i].m_A;
+ }
+}
+
+void CGraphics_Threaded::SetColor(float r, float g, float b, float a)
+{
+ dbg_assert(m_Drawing != 0, "called Graphics()->SetColor without begin");
+ CColorVertex Array[4] = {
+ CColorVertex(0, r, g, b, a),
+ CColorVertex(1, r, g, b, a),
+ CColorVertex(2, r, g, b, a),
+ CColorVertex(3, r, g, b, a)};
+ SetColorVertex(Array, 4);
+}
+
+void CGraphics_Threaded::QuadsSetSubset(float TlU, float TlV, float BrU, float BrV)
+{
+ dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin");
+
+ m_aTexture[0].u = TlU; m_aTexture[1].u = BrU;
+ m_aTexture[0].v = TlV; m_aTexture[1].v = TlV;
+
+ m_aTexture[3].u = TlU; m_aTexture[2].u = BrU;
+ m_aTexture[3].v = BrV; m_aTexture[2].v = BrV;
+}
+
+void CGraphics_Threaded::QuadsSetSubsetFree(
+ float x0, float y0, float x1, float y1,
+ float x2, float y2, float x3, float y3)
+{
+ m_aTexture[0].u = x0; m_aTexture[0].v = y0;
+ m_aTexture[1].u = x1; m_aTexture[1].v = y1;
+ m_aTexture[2].u = x2; m_aTexture[2].v = y2;
+ m_aTexture[3].u = x3; m_aTexture[3].v = y3;
+}
+
+void CGraphics_Threaded::QuadsDraw(CQuadItem *pArray, int Num)
+{
+ for(int i = 0; i < Num; ++i)
+ {
+ pArray[i].m_X -= pArray[i].m_Width/2;
+ pArray[i].m_Y -= pArray[i].m_Height/2;
+ }
+
+ QuadsDrawTL(pArray, Num);
+}
+
+void CGraphics_Threaded::QuadsDrawTL(const CQuadItem *pArray, int Num)
+{
+ CCommandBuffer::SPoint Center;
+ Center.z = 0;
+
+ dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawTL without begin");
+
+ for(int i = 0; i < Num; ++i)
+ {
+ m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X;
+ m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y;
+ m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0];
+ m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0];
+
+ m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
+ m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y;
+ m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1];
+ m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1];
+
+ m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X + pArray[i].m_Width;
+ m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
+ m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[2];
+ m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[2];
+
+ m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X;
+ m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y + pArray[i].m_Height;
+ m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[3];
+ m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[3];
+
+ if(m_Rotation != 0)
+ {
+ Center.x = pArray[i].m_X + pArray[i].m_Width/2;
+ Center.y = pArray[i].m_Y + pArray[i].m_Height/2;
+
+ Rotate4(Center, &m_aVertices[m_NumVertices + 4*i]);
+ }
+ }
+
+ AddVertices(4*Num);
+}
+
+void CGraphics_Threaded::QuadsDrawFreeform(const CFreeformItem *pArray, int Num)
+{
+ dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawFreeform without begin");
+
+ for(int i = 0; i < Num; ++i)
+ {
+ m_aVertices[m_NumVertices + 4*i].m_Pos.x = pArray[i].m_X0;
+ m_aVertices[m_NumVertices + 4*i].m_Pos.y = pArray[i].m_Y0;
+ m_aVertices[m_NumVertices + 4*i].m_Tex = m_aTexture[0];
+ m_aVertices[m_NumVertices + 4*i].m_Color = m_aColor[0];
+
+ m_aVertices[m_NumVertices + 4*i + 1].m_Pos.x = pArray[i].m_X1;
+ m_aVertices[m_NumVertices + 4*i + 1].m_Pos.y = pArray[i].m_Y1;
+ m_aVertices[m_NumVertices + 4*i + 1].m_Tex = m_aTexture[1];
+ m_aVertices[m_NumVertices + 4*i + 1].m_Color = m_aColor[1];
+
+ m_aVertices[m_NumVertices + 4*i + 2].m_Pos.x = pArray[i].m_X3;
+ m_aVertices[m_NumVertices + 4*i + 2].m_Pos.y = pArray[i].m_Y3;
+ m_aVertices[m_NumVertices + 4*i + 2].m_Tex = m_aTexture[3];
+ m_aVertices[m_NumVertices + 4*i + 2].m_Color = m_aColor[3];
+
+ m_aVertices[m_NumVertices + 4*i + 3].m_Pos.x = pArray[i].m_X2;
+ m_aVertices[m_NumVertices + 4*i + 3].m_Pos.y = pArray[i].m_Y2;
+ m_aVertices[m_NumVertices + 4*i + 3].m_Tex = m_aTexture[2];
+ m_aVertices[m_NumVertices + 4*i + 3].m_Color = m_aColor[2];
+ }
+
+ AddVertices(4*Num);
+}
+
+void CGraphics_Threaded::QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText)
+{
+ float StartX = x;
+
+ QuadsBegin();
+ SetColor(r,g,b,a);
+
+ while(*pText)
+ {
+ char c = *pText;
+ pText++;
+
+ if(c == '\n')
+ {
+ x = StartX;
+ y += Size;
+ }
+ else
+ {
+ QuadsSetSubset(
+ (c%16)/16.0f,
+ (c/16)/16.0f,
+ (c%16)/16.0f+1.0f/16.0f,
+ (c/16)/16.0f+1.0f/16.0f);
+
+ CQuadItem QuadItem(x, y, Size, Size);
+ QuadsDrawTL(&QuadItem, 1);
+ x += Size/2;
+ }
+ }
+
+ QuadsEnd();
+}
+
+bool CGraphics_Threaded::Init()
+{
+ // fetch pointers
+ m_pStorage = Kernel()->RequestInterface();
+ m_pConsole = Kernel()->RequestInterface();
+
+ // Set all z to -5.0f
+ for(int i = 0; i < MAX_VERTICES; i++)
+ m_aVertices[i].m_Pos.z = -5.0f;
+
+ // init textures
+ m_FirstFreeTexture = 0;
+ for(int i = 0; i < MAX_TEXTURES; i++)
+ m_aTextures[i].m_Next = i+1;
+ m_aTextures[MAX_TEXTURES-1].m_Next = -1;
+
+ // start the command processor
+ ICommandProcessor *pProcessor = new CCommandProcessor_SDL_OpenGL;
+ m_Handler.Start(pProcessor);
+
+ // create command buffers
+ m_apCommandBuffers[0] = new CCommandBuffer(1024*512, 1024*1024);
+ m_apCommandBuffers[1] = new CCommandBuffer(1024*512, 1024*1024);
+ m_pCommandBuffer = m_apCommandBuffers[0];
+
+ // issue init command
+ m_pCommandBuffer->Reset();
+ volatile int Result;
+ CCommandBuffer::SCommand_Init Cmd;
+ Cmd.m_pResult = &Result;
+ m_pCommandBuffer->AddCommand(Cmd);
+ m_Handler.RunBuffer(m_pCommandBuffer);
+ m_Handler.WaitForIdle();
+
+
+ if(Result == 0)
+ {
+ // create null texture, will get id=0
+ static const unsigned char aNullTextureData[] = {
+ 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff,
+ 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff,
+ 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff,
+ 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff,
+ };
+
+ m_InvalidTexture = LoadTextureRaw(4,4,CImageInfo::FORMAT_RGBA,aNullTextureData,CImageInfo::FORMAT_RGBA,TEXLOAD_NORESAMPLE);
+ }
+
+ return Result;
+}
+
+void CGraphics_Threaded::Shutdown()
+{
+ // TODO: SDL, is this correct?
+ SDL_Quit();
+}
+
+void CGraphics_Threaded::Minimize()
+{
+ SDL_WM_IconifyWindow();
+}
+
+void CGraphics_Threaded::Maximize()
+{
+ // TODO: SDL
+}
+
+int CGraphics_Threaded::WindowActive()
+{
+ return SDL_GetAppState()&SDL_APPINPUTFOCUS;
+}
+
+int CGraphics_Threaded::WindowOpen()
+{
+ return SDL_GetAppState()&SDL_APPACTIVE;
+
+}
+
+void CGraphics_Threaded::TakeScreenshot(const char *pFilename)
+{
+ // TODO: screenshot support
+ return;
+ /*
+ char aDate[20];
+ str_timestamp(aDate, sizeof(aDate));
+ str_format(m_aScreenshotName, sizeof(m_aScreenshotName), "screenshots/%s_%s.png", pFilename?pFilename:"screenshot", aDate);
+ m_DoScreenshot = true;
+ */
+}
+
+void CGraphics_Threaded::Swap()
+{
+ if(0) {
+ CCommandBuffer::SCommand_Clear Cmd;
+ Cmd.m_Color.r = 1.0f;
+ Cmd.m_Color.g = 0.0f;
+ Cmd.m_Color.b = 0.0f;
+ Cmd.m_Color.a = 0.0f;
+ m_pCommandBuffer->AddCommand(Cmd);
+ }
+
+ CCommandBuffer::SCommand_Swap Cmd;
+ m_pCommandBuffer->AddCommand(Cmd);
+
+ m_Handler.RunBuffer(m_pCommandBuffer);
+ m_Handler.WaitForIdle();
+ m_pCommandBuffer->Reset();
+
+ // TODO: screenshot support
+ /*
+ if(m_DoScreenshot)
+ {
+ ScreenshotDirect(m_aScreenshotName);
+ m_DoScreenshot = false;
+ }*/
+
+ //SDL_GL_SwapBuffers();
+
+ //if(g_Config.m_GfxFinish)
+ // glFinish();
+}
+
+
+int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes)
+{
+ // TODO: fix support for video modes, using fake modes for now
+ //int NumModes = sizeof(g_aFakeModes)/sizeof(CVideoMode);
+ //SDL_Rect **ppModes;
+
+ //if(g_Config.m_GfxDisplayAllModes)
+ {
+ int Count = sizeof(g_aFakeModes)/sizeof(CVideoMode);
+ mem_copy(pModes, g_aFakeModes, sizeof(g_aFakeModes));
+ if(MaxModes < Count)
+ Count = MaxModes;
+ return Count;
+ }
+
+ // TODO: fix this code on osx or windows
+ /*
+
+ ppModes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN);
+ if(ppModes == NULL)
+ {
+ // no modes
+ NumModes = 0;
+ }
+ else if(ppModes == (SDL_Rect**)-1)
+ {
+ // all modes
+ }
+ else
+ {
+ NumModes = 0;
+ for(int i = 0; ppModes[i]; ++i)
+ {
+ if(NumModes == MaxModes)
+ break;
+ pModes[NumModes].m_Width = ppModes[i]->w;
+ pModes[NumModes].m_Height = ppModes[i]->h;
+ pModes[NumModes].m_Red = 8;
+ pModes[NumModes].m_Green = 8;
+ pModes[NumModes].m_Blue = 8;
+ NumModes++;
+ }
+ }
+
+ return NumModes;*/
+}
+
+
+extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_Threaded(); }
diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp
index fc1ca2db0..0e61ec6e1 100644
--- a/src/game/client/components/menus.cpp
+++ b/src/game/client/components/menus.cpp
@@ -628,7 +628,6 @@ int CMenus::RenderMenubar(CUIRect r)
void CMenus::RenderLoading()
{
// TODO: not supported right now due to separate render thread
- return;
static int64 LastLoadRender = 0;
float Percent = m_LoadCurrent++/(float)m_LoadTotal;