aboutsummaryrefslogtreecommitdiff
path: root/archive/hge/graphics.cpp
diff options
context:
space:
mode:
authorGravatar Chris Xiong <chirs241097@gmail.com> 2015-10-26 22:52:36 +0800
committerGravatar Chris Xiong <chirs241097@gmail.com> 2015-10-26 22:52:36 +0800
commit3bd383baf6a17e734329e1fc677c7e86283db772 (patch)
tree69a9148087577f797624ceb9c71323a2563d6bb4 /archive/hge/graphics.cpp
parent543e4f570be9b279ba558ca61cc02cda251af384 (diff)
downloadbullet-lab-remix-3bd383baf6a17e734329e1fc677c7e86283db772.tar.xz
Added support for relative line numbers.
Added instructions for, brk and cont. (They are still untested...) Parser code cleanup. Removed garbage output to stderr. Reorganize the repository structure. Updated BLR2 code move it into archive. Added BLR1 files.
Diffstat (limited to 'archive/hge/graphics.cpp')
-rw-r--r--archive/hge/graphics.cpp1524
1 files changed, 1524 insertions, 0 deletions
diff --git a/archive/hge/graphics.cpp b/archive/hge/graphics.cpp
new file mode 100644
index 0000000..6ee9578
--- /dev/null
+++ b/archive/hge/graphics.cpp
@@ -0,0 +1,1524 @@
+/*
+** Haaf's Game Engine 1.8
+** Copyright (C) 2003-2007, Relish Games
+** hge.relishgames.com
+**
+** Core functions implementation: graphics
+*/
+
+// !!! FIXME: the texture data when locking/unlocking textures is in GL_BGRA format, not GL_RGBA.
+// !!! FIXME: ...but this mistake wasn't noticed for several games, since most didn't lock outside
+// !!! FIXME: of a piece of code that was #ifdef'd for Unix anyhow.
+// !!! FIXME: But if you lock textures and the colors are wrong, that's what happened. We need to
+// !!! FIXME: sort out all the places where we're passing things around in RGBA to fix this.
+// !!! FIXME: In the mean time, it's usually easier to just change your application to expect
+// !!! FIXME: locked textures to be RGBA instead of BGRA.
+
+// !!! FIXME: ...apparently we're locking textures upside down, too?
+
+#include "hge_impl.h"
+
+#define SUPPORT_CXIMAGE 1
+#if SUPPORT_CXIMAGE
+// conflict with Mac OS X 10.3.9 SDK...
+#ifdef _T
+#undef _T
+#endif
+#include "CxImage/ximage.h"
+#else
+/* Use DevIL instead of CXImage */
+#include <IL/il.h>
+#include <IL/ilu.h>
+#endif
+
+// avoiding glext.h here ...
+#ifndef GL_TEXTURE_RECTANGLE_ARB
+#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
+#endif
+#ifndef GL_FRAMEBUFFER_EXT
+#define GL_FRAMEBUFFER_EXT 0x8D40
+#endif
+#ifndef GL_RENDERBUFFER_EXT
+#define GL_RENDERBUFFER_EXT 0x8D41
+#endif
+#ifndef GL_COLOR_ATTACHMENT0_EXT
+#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0
+#endif
+#ifndef GL_DEPTH_ATTACHMENT_EXT
+#define GL_DEPTH_ATTACHMENT_EXT 0x8D00
+#endif
+#ifndef GL_FRAMEBUFFER_COMPLETE_EXT
+#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+#ifndef GL_YCBCR_422_APPLE
+#define GL_YCBCR_422_APPLE 0x85B9
+#endif
+#ifndef GL_UNSIGNED_SHORT_8_8_APPLE
+#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA
+#endif
+#ifndef GL_UNSIGNED_SHORT_8_8_REV_APPLE
+#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB
+#endif
+static const char* GRAPHICS_SRC_FN="hge/graphics.cpp";
+struct gltexture
+{
+ GLuint name;
+ GLuint width;
+ GLuint height;
+ GLuint potw; // Power-of-two width.
+ GLuint poth; // Power-of-two height.
+ const char *filename; // if backed by a file, not a managed buffer.
+ DWORD *pixels; // original rgba data.
+ DWORD *lock_pixels; // for locked texture
+ bool is_render_target;
+ bool lost;
+ bool lock_readonly;
+ GLint lock_x;
+ GLint lock_y;
+ GLint lock_width;
+ GLint lock_height;
+};
+
+static DWORD *_DecodeImage(BYTE *data, const char *fname, DWORD size, int &width, int &height)
+{
+ width = height = 0;
+
+ DWORD *pixels = NULL;
+ const size_t fnamelen = fname ? strlen(fname) : 0;
+ if ( (fnamelen > 5) && (strcasecmp((fname + fnamelen) - 5, ".rgba") == 0) )
+ {
+ DWORD *ptr = (DWORD *) data;
+ DWORD w = ptr[0];
+ DWORD h = ptr[1];
+ BYTESWAP(w);
+ BYTESWAP(h);
+ if ( ((w * h * 4) + 8) == size ) // not truncated?
+ {
+ width = (int) w;
+ height = (int) h;
+ pixels = new DWORD[width * height];
+ memcpy(pixels, ptr + 2, w * h * 4); // !!! FIXME: ignores pitch.
+ }
+ return pixels;
+ }
+
+#if SUPPORT_CXIMAGE
+ CxImage img;
+ img.Decode(data, size, CXIMAGE_FORMAT_UNKNOWN);
+ if (img.IsValid())
+ {
+ width = img.GetWidth();
+ height = img.GetHeight();
+ pixels = new DWORD[width * height];
+ BYTE *wptr = (BYTE *) pixels;
+ const bool hasalpha = img.AlphaIsValid();
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ const RGBQUAD rgb = img.GetPixelColor(x, y, true);
+ *(wptr++) = rgb.rgbRed;
+ *(wptr++) = rgb.rgbGreen;
+ *(wptr++) = rgb.rgbBlue;
+ *(wptr++) = hasalpha ? rgb.rgbReserved : 0xFF; // alpha.
+ }
+ }
+ }
+#else
+ ilInit();
+ iluInit();
+
+ ILuint id;
+ ilGenImages(1, &id);
+
+ if(ilLoadImage(fname)) {
+ printf("success: %s\n", fname);
+ ILinfo info;
+ iluGetImageInfo(&info);
+ width = info.Width;
+ height = info.Height;
+ size = info.SizeOfData;
+ pixels = new DWORD[width * height];
+ ilCopyPixels(0, 0, 0, width, height, 0, IL_RGBA, IL_UNSIGNED_INT, pixels);
+ ilShutDown();
+ }
+#endif
+
+ return pixels;
+}
+
+
+void HGE_Impl::_BindTexture(gltexture *t)
+{
+ // The Direct3D renderer is using managed textures, so they aren't every
+ // actually "lost" ... we may have to rebuild them here, though.
+ if ((t != NULL) && (t->lost))
+ _ConfigureTexture(t, t->width, t->height, t->pixels);
+
+ if ( ((HTEXTURE)t) != CurTexture )
+ {
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, t ? t->name : 0);
+ CurTexture = (HTEXTURE) t;
+ }
+}
+
+void CALL HGE_Impl::Gfx_Clear(DWORD color)
+{
+ GLbitfield flags = GL_COLOR_BUFFER_BIT;
+ if ( ((pCurTarget) && (pCurTarget->depth)) || bZBuffer )
+ flags |= GL_DEPTH_BUFFER_BIT;
+
+ const GLfloat a = ((GLfloat) ((color >> 24) & 0xFF)) / 255.0f;
+ const GLfloat r = ((GLfloat) ((color >> 16) & 0xFF)) / 255.0f;
+ const GLfloat g = ((GLfloat) ((color >> 8) & 0xFF)) / 255.0f;
+ const GLfloat b = ((GLfloat) ((color >> 0) & 0xFF)) / 255.0f;
+ pOpenGLDevice->glClearColor(r, g, b, a);
+ pOpenGLDevice->glClear(flags);
+}
+
+void CALL HGE_Impl::Gfx_SetClipping(int x, int y, int w, int h)
+{
+ int scr_width, scr_height;
+ struct { int X; int Y; int Width; int Height; float MinZ; float MaxZ; } vp;
+
+ if(!pCurTarget) {
+ scr_width=pHGE->System_GetStateInt(HGE_SCREENWIDTH);
+ scr_height=pHGE->System_GetStateInt(HGE_SCREENHEIGHT);
+ }
+ else {
+ scr_width=Texture_GetWidth(pCurTarget->tex);
+ scr_height=Texture_GetHeight(pCurTarget->tex);
+ }
+
+ if(!w) {
+ vp.X=0;
+ vp.Y=0;
+ vp.Width=scr_width;
+ vp.Height=scr_height;
+ }
+ else
+ {
+ if(x<0) { w+=x; x=0; }
+ if(y<0) { h+=y; y=0; }
+
+ if(x+w > scr_width) w=scr_width-x;
+ if(y+h > scr_height) h=scr_height-y;
+
+ vp.X=x;
+ vp.Y=y;
+ vp.Width=w;
+ vp.Height=h;
+ }
+
+ if ((clipX == vp.X) && (clipY == vp.Y) && (clipW == vp.Width) && (clipH == vp.Height))
+ return; // nothing to do here, don't call into the GL.
+
+ vp.MinZ=0.0f;
+ vp.MaxZ=1.0f;
+
+ _render_batch();
+
+ clipX = vp.X;
+ clipY = vp.Y;
+ clipW = vp.Width;
+ clipH = vp.Height;
+ pOpenGLDevice->glScissor(vp.X, (scr_height-vp.Y)-vp.Height, vp.Width, vp.Height);
+}
+
+void CALL HGE_Impl::Gfx_SetTransform(float x, float y, float dx, float dy, float rot, float hscale, float vscale)
+{
+ if ((x == 0.0f) && (y == 0.0f) && (dx == 0.0f) && (dy == 0.0f) && (rot == 0.0f) && (hscale == 0.0f) && (vscale == 0.0f))
+ {
+ //reset everything
+ pOpenGLDevice->glMatrixMode(GL_MODELVIEW);
+ pOpenGLDevice->glLoadIdentity();
+ bTransforming=false;
+ return;
+ }
+
+ _render_batch();
+ bTransforming = true;
+
+ pOpenGLDevice->glMatrixMode(GL_MODELVIEW);
+ //we have to reset the matrix in all cases.
+ //or this would cause insane transforming...
+ pOpenGLDevice->glLoadIdentity();
+ pOpenGLDevice->glTranslatef(-x, -y, 0.0f);
+ pOpenGLDevice->glScalef(hscale, vscale, 1.0f);
+ pOpenGLDevice->glRotatef(rot, 0.0f, 0.0f, 1.0f);
+ pOpenGLDevice->glTranslatef(x+dx, y+dy, 0.0f);
+}
+
+bool CALL HGE_Impl::Gfx_BeginScene(HTARGET targ)
+{
+ CRenderTargetList *target=(CRenderTargetList *)targ;
+
+ if(VertArray)
+ {
+ _PostError("Gfx_BeginScene: Scene is already being rendered");
+ return false;
+ }
+
+ if(target != pCurTarget)
+ {
+ if (pOpenGLDevice->have_GL_EXT_framebuffer_object)
+ pOpenGLDevice->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (target) ? target->frame : 0);
+
+ if ( ((target) && (target->depth)) || (bZBuffer) )
+ pOpenGLDevice->glEnable(GL_DEPTH_TEST);
+ else
+ pOpenGLDevice->glDisable(GL_DEPTH_TEST);
+
+ // d3d's SetRenderTarget() forces the viewport to surface size...
+ if (target)
+ {
+ pOpenGLDevice->glScissor(0, 0, target->width, target->height);
+ pOpenGLDevice->glViewport(0, 0, target->width, target->height);
+ _SetProjectionMatrix(target->width, target->height);
+ }
+ else
+ {
+ pOpenGLDevice->glScissor(0, 0, nScreenWidth, nScreenHeight);
+ pOpenGLDevice->glViewport(0, 0, nScreenWidth, nScreenHeight);
+ _SetProjectionMatrix(nScreenWidth, nScreenHeight);
+ }
+
+ pOpenGLDevice->glMatrixMode(GL_MODELVIEW);
+ pOpenGLDevice->glLoadIdentity();
+
+ pCurTarget=target;
+ }
+
+ VertArray = pVB;
+ return true;
+}
+
+void CALL HGE_Impl::Gfx_EndScene()
+{
+ _render_batch(true);
+
+ // no "real" render targets? Push the framebuffer to a texture.
+ // This is not going to work in lots of legitimate scenarios, but it will
+ // most of the time, so it's better than nothing when you lack FBOs.
+ if ((pCurTarget) && (!pOpenGLDevice->have_GL_EXT_framebuffer_object))
+ {
+ gltexture *pTex = (gltexture *) pCurTarget->tex;
+ if ((pTex != NULL) && (pTex->lost))
+ _ConfigureTexture(pTex, pTex->width, pTex->height, pTex->pixels);
+
+ const int width = pCurTarget->width;
+ const int height = pCurTarget->height;
+ pOpenGLDevice->glFinish();
+ DWORD *pixels = new DWORD[width * height];
+ pOpenGLDevice->glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, pTex->name);
+ pOpenGLDevice->glTexSubImage2D(pOpenGLDevice->TextureTarget, 0, 0, 0, width, height,
+ GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, CurTexture ? (((gltexture *) CurTexture)->name) : 0);
+ delete[] pixels;
+ }
+
+ if(!pCurTarget) SDL_GL_SwapBuffers();
+ //const GLenum err = pOpenGLDevice->glGetError();
+ //if (err != GL_NO_ERROR) printf("GL error! 0x%X\n", (int) err);
+ //Gfx_Clear(0xFF | (0xFF<<24) | (random() & 0xFF << 16) | (random() & 0xFF << 8));
+ //Gfx_Clear(0xFF000000);
+}
+
+void HGE_Impl::_SetTextureFilter()
+{
+ const GLenum filter = (bTextureFilter) ? GL_LINEAR : GL_NEAREST;
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_MIN_FILTER, filter);
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_MAG_FILTER, filter);
+}
+
+
+bool HGE_Impl::_PrimsOutsideClipping(const hgeVertex *v, const int verts)
+{
+ if (bTransforming)
+ return false; // screw it, let the GL do the clipping.
+
+ const int maxX = clipX + clipW;
+ const int maxY = clipY + clipH;
+ for (int i = 0; i < verts; i++, v++)
+ {
+ const int x = v->x;
+ const int y = v->y;
+ if ((x > clipX) && (x < maxX) && (y > clipY) && (y < maxY))
+ return false;
+ }
+ return true;
+}
+
+
+void CALL HGE_Impl::Gfx_RenderLine(float x1, float y1, float x2, float y2, DWORD color, float z)
+{
+ if (VertArray)
+ {
+ if(CurPrimType!=HGEPRIM_LINES || nPrim>=VERTEX_BUFFER_SIZE/HGEPRIM_LINES || CurTexture || CurBlendMode!=BLEND_DEFAULT)
+ {
+ _render_batch();
+
+ CurPrimType=HGEPRIM_LINES;
+ if(CurBlendMode != BLEND_DEFAULT) _SetBlendMode(BLEND_DEFAULT);
+ _BindTexture(NULL);
+ }
+
+ int i=nPrim*HGEPRIM_LINES;
+ VertArray[i].x = x1; VertArray[i+1].x = x2;
+ VertArray[i].y = y1; VertArray[i+1].y = y2;
+ VertArray[i].z = VertArray[i+1].z = z;
+ VertArray[i].col = VertArray[i+1].col = color;
+ VertArray[i].tx = VertArray[i+1].tx =
+ VertArray[i].ty = VertArray[i+1].ty = 0.0f;
+
+ if (!_PrimsOutsideClipping(&VertArray[i], HGEPRIM_LINES))
+ nPrim++;
+ }
+}
+
+template <class T> static inline const T Min(const T a, const T b) { return a < b ? a : b; }
+template <class T> static inline const T Max(const T a, const T b) { return a > b ? a : b; }
+
+void CALL HGE_Impl::Gfx_RenderTriple(const hgeTriple *triple)
+{
+ if (VertArray)
+ {
+ const hgeVertex *v = triple->v;
+ if (_PrimsOutsideClipping(v, HGEPRIM_TRIPLES))
+ {
+ // check for overlap, despite triangle points being outside clipping...
+ const int maxX = clipX + clipW;
+ const int maxY = clipY + clipH;
+ const int leftmost = Min(Min(v[0].x, v[1].x), v[2].x);
+ const int rightmost = Max(Max(v[0].x, v[1].x), v[2].x);
+ const int topmost = Min(Min(v[0].y, v[1].y), v[2].y);
+ const int bottommost = Max(Max(v[0].y, v[1].y), v[2].y);
+ if ( ((clipX < leftmost) || (clipX > rightmost)) &&
+ ((maxX < leftmost) || (maxX > rightmost)) &&
+ ((clipY < topmost) || (clipY > bottommost)) &&
+ ((maxY < topmost) || (maxY > bottommost)) )
+ return; // no, this is really totally clipped.
+ }
+
+ if(CurPrimType!=HGEPRIM_TRIPLES || nPrim>=VERTEX_BUFFER_SIZE/HGEPRIM_TRIPLES || CurTexture!=triple->tex || CurBlendMode!=triple->blend)
+ {
+ _render_batch();
+
+ CurPrimType=HGEPRIM_TRIPLES;
+ if(CurBlendMode != triple->blend) _SetBlendMode(triple->blend);
+ _BindTexture((gltexture *) triple->tex);
+ }
+
+ memcpy(&VertArray[nPrim*HGEPRIM_TRIPLES], triple->v, sizeof(hgeVertex)*HGEPRIM_TRIPLES);
+ nPrim++;
+ }
+}
+
+void CALL HGE_Impl::Gfx_RenderQuad(const hgeQuad *quad)
+{
+ if (VertArray)
+ {
+ const hgeVertex *v = quad->v;
+ if (_PrimsOutsideClipping(v, HGEPRIM_QUADS))
+ {
+ // check for overlap, despite quad points being outside clipping...
+ const int maxX = clipX + clipW;
+ const int maxY = clipY + clipH;
+ const int leftmost = Min(Min(Min(v[0].x, v[1].x), v[2].x), v[3].x);
+ const int rightmost = Max(Max(Max(v[0].x, v[1].x), v[2].x), v[3].x);
+ const int topmost = Min(Min(Min(v[0].y, v[1].y), v[2].y), v[3].y);
+ const int bottommost = Max(Max(Max(v[0].y, v[1].y), v[2].y), v[3].y);
+ if ( ((clipX < leftmost) || (clipX > rightmost)) &&
+ ((maxX < leftmost) || (maxX > rightmost)) &&
+ ((clipY < topmost) || (clipY > bottommost)) &&
+ ((maxY < topmost) || (maxY > bottommost)) )
+ return; // no, this is really totally clipped.
+ }
+
+ if(CurPrimType!=HGEPRIM_QUADS || nPrim>=VERTEX_BUFFER_SIZE/HGEPRIM_QUADS || CurTexture!=quad->tex || CurBlendMode!=quad->blend)
+ {
+ _render_batch();
+
+ CurPrimType=HGEPRIM_QUADS;
+ if(CurBlendMode != quad->blend) _SetBlendMode(quad->blend);
+ _BindTexture((gltexture *) quad->tex);
+ }
+
+ memcpy(&VertArray[nPrim*HGEPRIM_QUADS], quad->v, sizeof(hgeVertex)*HGEPRIM_QUADS);
+ nPrim++;
+ }
+}
+
+hgeVertex* CALL HGE_Impl::Gfx_StartBatch(int prim_type, HTEXTURE tex, int blend, int *max_prim)
+{
+ if(VertArray)
+ {
+ _render_batch();
+
+ CurPrimType=prim_type;
+ if(CurBlendMode != blend) _SetBlendMode(blend);
+ _BindTexture((gltexture *) tex);
+ *max_prim=VERTEX_BUFFER_SIZE / prim_type;
+ return VertArray;
+ }
+ else return 0;
+}
+
+void CALL HGE_Impl::Gfx_FinishBatch(int nprim)
+{
+ nPrim = nprim;
+}
+
+bool HGE_Impl::_BuildTarget(CRenderTargetList *pTarget, GLuint texname, int width, int height, bool zbuffer)
+{
+ bool okay = true; // no FBOs? Fake success by default.
+ if (pOpenGLDevice->have_GL_EXT_framebuffer_object)
+ {
+ pOpenGLDevice->glGenFramebuffersEXT(1, &pTarget->frame);
+ if (zbuffer)
+ pOpenGLDevice->glGenRenderbuffersEXT(1, &pTarget->depth);
+ pOpenGLDevice->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pTarget->frame);
+ pOpenGLDevice->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, pOpenGLDevice->TextureTarget, texname, 0);
+ if (zbuffer)
+ {
+ pOpenGLDevice->glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, pTarget->depth);
+ pOpenGLDevice->glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);
+ pOpenGLDevice->glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, pTarget->depth);
+ }
+
+ GLenum rc = pOpenGLDevice->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if ((rc == GL_FRAMEBUFFER_COMPLETE_EXT) && (pOpenGLDevice->glGetError() == GL_NO_ERROR))
+ {
+ pOpenGLDevice->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ okay = true;
+ }
+ else
+ {
+ pOpenGLDevice->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ pOpenGLDevice->glDeleteRenderbuffersEXT(1, &pTarget->depth);
+ pOpenGLDevice->glDeleteFramebuffersEXT(1, &pTarget->frame);
+ }
+ pOpenGLDevice->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pCurTarget ? pCurTarget->frame : 0);
+ }
+
+ return okay;
+}
+
+HTARGET CALL HGE_Impl::Target_Create(int width, int height, bool zbuffer)
+{
+ bool okay = false;
+ CRenderTargetList *pTarget = new CRenderTargetList;
+ memset(pTarget, '\0', sizeof (CRenderTargetList));
+
+ pTarget->tex = _BuildTexture(width, height, NULL);
+ gltexture *gltex = (gltexture *) pTarget->tex;
+ gltex->is_render_target = true;
+ gltex->lost = false;
+ _ConfigureTexture(gltex, width, height, NULL);
+
+ pTarget->width = width;
+ pTarget->height = height;
+
+ okay = _BuildTarget(pTarget, gltex->name, width, height, zbuffer);
+ if (!okay)
+ {
+ System_Log("%s: OpenGL: Failed to create render target!",GRAPHICS_SRC_FN);
+ Texture_Free(pTarget->tex);
+ delete pTarget;
+ return 0;
+ }
+
+ pTarget->next=pTargets;
+ pTargets=pTarget;
+
+ return (HTARGET)pTarget;
+}
+
+void CALL HGE_Impl::Target_Free(HTARGET target)
+{
+ CRenderTargetList *pTarget=pTargets, *pPrevTarget=NULL;
+
+ while(pTarget)
+ {
+ if((CRenderTargetList *)target == pTarget)
+ {
+ if(pPrevTarget)
+ pPrevTarget->next = pTarget->next;
+ else
+ pTargets = pTarget->next;
+
+ if (pOpenGLDevice->have_GL_EXT_framebuffer_object)
+ {
+ if (pCurTarget == (CRenderTargetList *)target)
+ pOpenGLDevice->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ if (pTarget->depth)
+ pOpenGLDevice->glDeleteRenderbuffersEXT(1, &pTarget->depth);
+ pOpenGLDevice->glDeleteFramebuffersEXT(1, &pTarget->frame);
+ }
+
+ if (pCurTarget == (CRenderTargetList *)target)
+ pCurTarget = 0;
+
+ Texture_Free(pTarget->tex);
+ delete pTarget;
+ return;
+ }
+
+ pPrevTarget = pTarget;
+ pTarget = pTarget->next;
+ }
+}
+
+HTEXTURE CALL HGE_Impl::Target_GetTexture(HTARGET target)
+{
+ CRenderTargetList *targ=(CRenderTargetList *)target;
+ if(target) return targ->tex;
+ else return 0;
+}
+
+static inline bool _IsPowerOfTwo(const GLuint x)
+{
+ return ((x & (x - 1)) == 0);
+}
+
+static inline GLuint _NextPowerOfTwo(GLuint x)
+{
+ x--;
+ for (unsigned i = 1; i < (sizeof(GLuint) * 8); i <<= 1)
+ x |= x >> i;
+ return x + 1;
+}
+
+void HGE_Impl::_ConfigureTexture(gltexture *t, int width, int height, DWORD *pixels)
+{
+ GLuint tex = 0;
+ pOpenGLDevice->glGenTextures(1, &tex);
+
+ t->lost = false;
+ t->name = tex;
+ t->width = width;
+ t->height = height;
+ t->pixels = pixels;
+ t->potw = 0;
+ t->poth = 0;
+
+ // see if we're backed by a file and not RAM.
+ const bool loadFromFile = ((pixels == NULL) && (t->filename != NULL));
+ if (loadFromFile)
+ {
+ DWORD size = 0;
+ BYTE *data = (BYTE *) pHGE->Resource_Load(t->filename, &size);
+ if (data != NULL)
+ {
+ int w, h;
+ pixels = _DecodeImage(data, t->filename, size, w, h);
+ if ((w != width) || (h != height)) // yikes, file changed?
+ {
+ delete[] pixels;
+ pixels = NULL;
+ }
+ Resource_Free(data);
+ }
+ }
+
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, tex);
+ if (pOpenGLDevice->TextureTarget != GL_TEXTURE_RECTANGLE_ARB)
+ {
+ pOpenGLDevice->glTexParameterf(pOpenGLDevice->TextureTarget, GL_TEXTURE_MIN_LOD, 0.0f);
+ pOpenGLDevice->glTexParameterf(pOpenGLDevice->TextureTarget, GL_TEXTURE_MAX_LOD, 0.0f);
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_BASE_LEVEL, 0);
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_MAX_LEVEL, 0);
+ }
+ const GLenum intfmt = pOpenGLDevice->have_GL_EXT_texture_compression_s3tc ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_RGBA;
+ if ((pOpenGLDevice->have_GL_ARB_texture_rectangle) || (pOpenGLDevice->have_GL_ARB_texture_non_power_of_two) || (_IsPowerOfTwo(width) && _IsPowerOfTwo(height))) {
+ pOpenGLDevice->glTexImage2D(pOpenGLDevice->TextureTarget, 0, intfmt, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ }
+ else
+ {
+ t->potw = _NextPowerOfTwo(width);
+ t->poth = _NextPowerOfTwo(height);
+ pOpenGLDevice->glTexImage2D(pOpenGLDevice->TextureTarget, 0, intfmt, t->potw, t->poth, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ pOpenGLDevice->glTexSubImage2D(pOpenGLDevice->TextureTarget, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ }
+
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, CurTexture ? (((gltexture *) CurTexture)->name) : 0);
+
+ if (loadFromFile)
+ delete[] pixels;
+}
+
+HTEXTURE HGE_Impl::_BuildTexture(int width, int height, DWORD *pixels)
+{
+ gltexture *retval = new gltexture;
+ memset(retval, '\0', sizeof (gltexture));
+ retval->lost = true; // we'll actually generate a texture and upload when forced.
+ retval->width = width;
+ retval->height = height;
+ retval->pixels = pixels;
+ return (HTEXTURE)retval;
+}
+
+HTEXTURE CALL HGE_Impl::Texture_Create(int width, int height)
+{
+ DWORD *pixels = new DWORD[width * height];
+ memset(pixels, '\0', sizeof (DWORD) * width * height);
+ HTEXTURE retval = _BuildTexture(width, height, pixels);
+
+ // the Direct3D renderer doesn't add these to the (textures) list, but we need them for when we "lose" the GL context.
+ if (retval != 0)
+ {
+ CTextureList *texItem=new CTextureList;
+ texItem->tex=retval;
+ texItem->width=width;
+ texItem->height=height;
+ texItem->next=textures;
+ textures=texItem;
+ }
+
+ return retval;
+}
+
+HTEXTURE CALL HGE_Impl::Texture_Load(const char *filename, DWORD size, bool bMipmap)
+{
+ HTEXTURE retval = 0;
+ int width = 0;
+ int height = 0;
+
+ void *data;
+ DWORD _size;
+ CTextureList *texItem;
+ const char *fname = NULL;
+ if(size) { data=(void *)filename; _size=size; }
+ else
+ {
+ fname = filename;
+ data=pHGE->Resource_Load(filename, &_size);
+ if(!data) return 0;
+ }
+
+ DWORD *pixels = _DecodeImage((BYTE *) data, fname, _size, width, height);
+ if (pixels != NULL)
+ retval = _BuildTexture(width, height, pixels);
+
+ if(!size) Resource_Free(data);
+
+ if (retval == 0)
+ {
+ STUBBED("texture load fail!");
+ _PostError("Can't create texture");
+ }
+ else
+ {
+ texItem=new CTextureList;
+ texItem->tex=retval;
+ texItem->width=width;
+ texItem->height=height;
+ texItem->next=textures;
+ textures=texItem;
+
+ // force an upload to the GL and lose our copy if it's backed by
+ // a file. We won't keep it here to conserve system RAM.
+ if (!size)
+ {
+ gltexture *t = (gltexture *) retval;
+ _ConfigureTexture(t, t->width, t->height, t->pixels);
+ delete[] t->pixels;
+ t->pixels = NULL;
+ t->filename = strcpy(new char[strlen(filename) + 1], filename);
+ }
+ }
+
+ return retval;
+}
+
+void CALL HGE_Impl::Texture_Free(HTEXTURE tex)
+{
+ if (pOpenGLDevice == NULL)
+ return; // in case we already shut down.
+
+ CTextureList *texItem=textures, *texPrev=0;
+
+ while(texItem)
+ {
+ if(texItem->tex==tex)
+ {
+ if(texPrev) texPrev->next=texItem->next;
+ else textures=texItem->next;
+ delete texItem;
+ break;
+ }
+ texPrev=texItem;
+ texItem=texItem->next;
+ }
+ if(tex)
+ {
+ gltexture *pTex = (gltexture *) tex;
+ delete[] pTex->filename;
+ delete[] pTex->lock_pixels;
+ delete[] pTex->pixels;
+ pOpenGLDevice->glDeleteTextures(1, &pTex->name);
+ delete pTex;
+ }
+}
+
+int CALL HGE_Impl::Texture_GetWidth(HTEXTURE tex, bool bOriginal)
+{
+ CTextureList *texItem=textures;
+
+ if(bOriginal)
+ {
+ while(texItem)
+ {
+ if(texItem->tex==tex) return texItem->width;
+ texItem=texItem->next;
+ }
+ }
+ else
+ {
+ return ((gltexture*)tex)->width;
+ }
+ return 0;
+}
+
+
+int CALL HGE_Impl::Texture_GetHeight(HTEXTURE tex, bool bOriginal)
+{
+ CTextureList *texItem=textures;
+
+ if(bOriginal)
+ {
+ while(texItem)
+ {
+ if(texItem->tex==tex) return texItem->height;
+ texItem=texItem->next;
+ }
+ }
+ else
+ {
+ return ((gltexture*)tex)->height;
+ }
+ return 0;
+}
+
+// HGE extension!
+// fast path for pushing YUV video to a texture instead of having to
+// lock/convert-to-rgba/unlock...current HGE semantics involve a
+// lot of unnecessary overhead on this, not to mention the conversion
+// on the CPU is painful on PowerPC chips.
+// This lets us use OpenGL extensions to move data to the hardware
+// without conversion.
+// Don't taunt this function. Side effects are probably rampant.
+bool CALL HGE_Impl::HGEEXT_Texture_PushYUV422(HTEXTURE tex, const BYTE *yuv)
+{
+ if (!pOpenGLDevice->have_GL_APPLE_ycbcr_422)
+ return false;
+
+ gltexture *pTex=(gltexture*)tex;
+ assert(!pTex->lock_pixels);
+
+ if (pTex->lost) // just reupload the whole thing.
+ _ConfigureTexture(pTex, pTex->width, pTex->height, pTex->pixels);
+
+ // Any existing pixels aren't valid anymore.
+ if (pTex->pixels)
+ {
+ delete[] pTex->pixels;
+ pTex->pixels = NULL;
+ }
+
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, pTex->name);
+ pOpenGLDevice->glTexSubImage2D(pOpenGLDevice->TextureTarget, 0, 0, 0,
+ pTex->width, pTex->height, GL_YCBCR_422_APPLE,
+ GL_UNSIGNED_SHORT_8_8_APPLE, yuv);
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, CurTexture ? (((gltexture *) CurTexture)->name) : 0);
+ return true;
+}
+
+DWORD * CALL HGE_Impl::Texture_Lock(HTEXTURE tex, bool bReadOnly, int left, int top, int width, int height)
+{
+ gltexture *pTex=(gltexture*)tex;
+
+ if (pTex->lock_pixels)
+ {
+ assert(false && "multiple lock of texture...");
+ return 0;
+ }
+
+ // see if we're backed by a file and not RAM.
+ const bool loadFromFile = ((pTex->pixels == NULL) && (pTex->filename != NULL));
+ if (loadFromFile)
+ {
+ DWORD size = 0;
+ BYTE *data = (BYTE *) pHGE->Resource_Load(pTex->filename, &size);
+ if (data != NULL)
+ {
+ int w, h;
+ pTex->pixels = _DecodeImage(data, pTex->filename, size, w, h);
+ if ((w != (int)pTex->width) || (h != (int)pTex->height)) // yikes, file changed?
+ {
+ delete[] pTex->pixels;
+ pTex->pixels = NULL;
+ }
+ Resource_Free(data);
+ }
+ if (pTex->pixels != NULL)
+ {
+ // can't go back to file after we lock, since app might change data.
+ if (!bReadOnly)
+ {
+ delete[] pTex->filename;
+ pTex->filename = NULL;
+ }
+ }
+ }
+
+ if ((pTex->pixels == NULL) && (!pTex->is_render_target)) // can't lock this texture...?!
+ return 0;
+
+ // !!! FIXME: is this right?
+ if((width == 0) && (height == 0))
+ {
+ width = pTex->width;
+ height = pTex->height;
+ }
+
+ // !!! FIXME: do something with this?
+ assert(width > 0);
+ assert(width <= (int)pTex->width);
+ assert(height > 0);
+ assert(height <= (int)pTex->height);
+ assert(left >= 0);
+ assert(left <= width);
+ assert(top >= 0);
+ assert(top <= height);
+
+ pTex->lock_readonly = bReadOnly;
+ pTex->lock_x = left;
+ pTex->lock_y = top;
+ pTex->lock_width = width;
+ pTex->lock_height = height;
+ pTex->lock_pixels = new DWORD[width * height];
+
+ DWORD *dst = pTex->lock_pixels;
+
+ if (pTex->is_render_target)
+ {
+ assert(false && "need to bind fbo before glReadPixels...");
+ DWORD *upsideDown = new DWORD[width * height];
+ DWORD *src = upsideDown + ((height-1) * width);
+ pOpenGLDevice->glReadPixels(left, (pTex->height-top)-height, width, height, GL_RGBA, GL_UNSIGNED_BYTE, upsideDown);
+ for (int i = 0; i < height; i++)
+ {
+ memcpy(dst, src, width * sizeof (DWORD));
+ dst += width;
+ src -= width;
+ }
+ delete[] upsideDown;
+ }
+ else
+ {
+ DWORD *src = pTex->pixels + ((top*pTex->width) + left);
+ for (int i = 0; i < height; i++)
+ {
+ memcpy(dst, src, width * sizeof (DWORD));
+ dst += width;
+ src += pTex->width;
+ }
+ }
+
+ return pTex->lock_pixels;
+}
+
+
+void CALL HGE_Impl::Texture_Unlock(HTEXTURE tex)
+{
+ gltexture *pTex=(gltexture*)tex;
+
+ if (pTex->lock_pixels == NULL) return; // not locked.
+
+ if (!pTex->lock_readonly) // have to reupload to the hardware.
+ {
+ // need to update pTex->pixels ...
+ const DWORD *src = pTex->lock_pixels;
+ DWORD *dst = pTex->pixels + ((pTex->lock_y*pTex->width) + pTex->lock_x);
+ for (int i = 0; i < pTex->lock_height; i++)
+ {
+ memcpy(dst, src, pTex->lock_width * sizeof (DWORD));
+ dst += pTex->width;
+ src += pTex->lock_width;
+ }
+
+ if (pTex->lost) // just reupload the whole thing.
+ _ConfigureTexture(pTex, pTex->width, pTex->height, pTex->pixels);
+ else
+ {
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, pTex->name);
+ pOpenGLDevice->glTexSubImage2D(pOpenGLDevice->TextureTarget, 0, pTex->lock_x,
+ (pTex->height-pTex->lock_y)-pTex->lock_height,
+ pTex->lock_width, pTex->lock_height, GL_RGBA,
+ GL_UNSIGNED_BYTE, pTex->lock_pixels);
+ pOpenGLDevice->glBindTexture(pOpenGLDevice->TextureTarget, CurTexture ? (((gltexture *) CurTexture)->name) : 0);
+ }
+ }
+
+ // if we were read-only and we're backed by a file, ditch the uncompressed copy in system RAM.
+ if ((pTex->filename != NULL) && (pTex->lock_readonly))
+ {
+ delete[] pTex->pixels;
+ pTex->pixels = NULL;
+ }
+
+ delete[] pTex->lock_pixels;
+ pTex->lock_pixels = NULL;
+ pTex->lock_readonly = false;
+ pTex->lock_x = -1;
+ pTex->lock_y = -1;
+ pTex->lock_width = -1;
+ pTex->lock_height = -1;
+}
+
+//////// Implementation ////////
+
+#define DEBUG_VERTICES 0
+#if DEBUG_VERTICES
+static inline void print_vertex(const hgeVertex *v)
+{
+ printf(" (%f, %f, %f), 0x%X, (%f, %f)\n", v->x, v->y, v->z, v->col, v->tx, v->ty);
+}
+#endif
+
+void HGE_Impl::_render_batch(bool bEndScene)
+{
+ if(VertArray)
+ {
+ if(nPrim)
+ {
+ const float h = (float) ((pCurTarget) ? pCurTarget->height : nScreenHeight);
+
+ // texture rectangles range from 0 to size, not 0 to 1. :/
+ float texwmult = 1.0f;
+ float texhmult = 1.0f;
+
+ if (CurTexture)
+ {
+ _SetTextureFilter();
+ const gltexture *pTex = ((gltexture *)CurTexture);
+ if (pOpenGLDevice->TextureTarget == GL_TEXTURE_RECTANGLE_ARB)
+ {
+ texwmult = pTex->width;
+ texhmult = pTex->height;
+ }
+ else if ((pTex->potw != 0) && (pTex->poth != 0))
+ {
+ texwmult = ( ((float)pTex->width) / ((float)pTex->potw) );
+ texhmult = ( ((float)pTex->height) / ((float)pTex->poth) );
+ }
+ }
+
+ for (int i = 0; i < nPrim*CurPrimType; i++)
+ {
+ // (0, 0) is the lower left in OpenGL, upper left in D3D.
+ VertArray[i].y = h - VertArray[i].y;
+
+ // Z axis is inverted in OpenGL from D3D.
+ VertArray[i].z = -VertArray[i].z;
+
+ // (0, 0) is lower left texcoord in OpenGL, upper left in D3D.
+ // Also, scale for texture rectangles vs. 2D textures.
+ VertArray[i].tx *= texwmult;
+ VertArray[i].ty = (1.0f - VertArray[i].ty) * texhmult;
+
+ // Colors are RGBA in OpenGL, ARGB in Direct3D.
+ const DWORD color = VertArray[i].col;
+ BYTE *col = (BYTE *) &VertArray[i].col;
+ const BYTE a = ((color >> 24) & 0xFF);
+ const BYTE r = ((color >> 16) & 0xFF);
+ const BYTE g = ((color >> 8) & 0xFF);
+ const BYTE b = ((color >> 0) & 0xFF);
+ col[0] = r;
+ col[1] = g;
+ col[2] = b;
+ col[3] = a;
+ }
+
+ switch(CurPrimType)
+ {
+ case HGEPRIM_QUADS:
+ pOpenGLDevice->glDrawElements(GL_TRIANGLES, nPrim * 6, GL_UNSIGNED_SHORT, pIB);
+ #if DEBUG_VERTICES
+ for (int i = 0; i < nPrim*6; i+=3)
+ {
+ printf("QUAD'S TRIANGLE:\n");
+ print_vertex(&pVB[pIB[i+0]]);
+ print_vertex(&pVB[pIB[i+1]]);
+ print_vertex(&pVB[pIB[i+2]]);
+ }
+ printf("DONE.\n");
+ #endif
+ break;
+
+ case HGEPRIM_TRIPLES:
+ pOpenGLDevice->glDrawArrays(GL_TRIANGLES, 0, nPrim * 3);
+ break;
+
+ case HGEPRIM_LINES:
+ pOpenGLDevice->glDrawArrays(GL_LINES, 0, nPrim * 2);
+ break;
+ }
+
+ nPrim=0;
+ }
+ if(bEndScene) VertArray = 0;
+ else VertArray = pVB;
+ }
+}
+
+void HGE_Impl::_SetBlendMode(int blend)
+{
+ if((blend & BLEND_ALPHABLEND) != (CurBlendMode & BLEND_ALPHABLEND))
+ {
+ if(blend & BLEND_ALPHABLEND) pOpenGLDevice->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ else pOpenGLDevice->glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ }
+
+ if((blend & BLEND_ZWRITE) != (CurBlendMode & BLEND_ZWRITE))
+ {
+ if(blend & BLEND_ZWRITE) pOpenGLDevice->glDepthMask(GL_TRUE);
+ else pOpenGLDevice->glDepthMask(GL_FALSE);
+ }
+
+ if((blend & BLEND_COLORADD) != (CurBlendMode & BLEND_COLORADD))
+ {
+ if(blend & BLEND_COLORADD) pOpenGLDevice->glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
+ else pOpenGLDevice->glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+
+ CurBlendMode = blend;
+}
+
+void HGE_Impl::_SetProjectionMatrix(int width, int height)
+{
+ pOpenGLDevice->glMatrixMode(GL_PROJECTION);
+ pOpenGLDevice->glLoadIdentity();
+ pOpenGLDevice->glOrtho(0, (float)width, 0, (float)height, 0.0f, 1.0f);
+ bTransforming = false;
+ clipX = 0;
+ clipY = 0;
+ clipW = width;
+ clipH = height;
+}
+
+void HGE_Impl::_UnloadOpenGLEntryPoints()
+{
+ #define GL_PROC(ext,fn,call,ret,params) pOpenGLDevice->fn = NULL;
+ #include "hge_glfuncs.h"
+ #undef GL_PROC
+}
+
+bool HGE_Impl::_HaveOpenGLExtension(const char *extlist, const char *ext)
+{
+ const char *ptr = strstr(extlist, ext);
+ if (ptr == NULL)
+ return false;
+
+ const char endchar = ptr[strlen(ext)];
+ if ((endchar == '\0') || (endchar == ' '))
+ return true; // extension is in the list.
+
+ return false; // just not supported, fail.
+}
+
+bool HGE_Impl::_LoadOpenGLEntryPoints()
+{
+ System_Log("%s: OpenGL: loading entry points and examining extensions...",GRAPHICS_SRC_FN);
+
+ // these can be reset to false below...
+ pOpenGLDevice->have_base_opengl = true;
+ pOpenGLDevice->have_GL_ARB_texture_rectangle = true;
+ pOpenGLDevice->have_GL_ARB_texture_non_power_of_two = true;
+ pOpenGLDevice->have_GL_EXT_framebuffer_object = true;
+ pOpenGLDevice->have_GL_EXT_texture_compression_s3tc = true;
+ pOpenGLDevice->have_GL_ARB_vertex_buffer_object = true;
+ pOpenGLDevice->have_GL_APPLE_ycbcr_422 = true;
+
+ #define GL_PROC(ext,fn,call,ret,params) \
+ if (pOpenGLDevice->have_##ext) { \
+ if ((pOpenGLDevice->fn = (_HGE_PFN_##fn) SDL_GL_GetProcAddress(#fn)) == NULL) { \
+ System_Log("Failed to load OpenGL entry point '" #fn "'"); \
+ pOpenGLDevice->have_##ext = false; \
+ } \
+ } else {}
+ #include "hge_glfuncs.h"
+ #undef GL_PROC
+
+ if (!pOpenGLDevice->have_base_opengl)
+ {
+ _UnloadOpenGLEntryPoints();
+ return false;
+ }
+
+ System_Log("%s: GL_RENDERER: %s",GRAPHICS_SRC_FN, (const char *) pOpenGLDevice->glGetString(GL_RENDERER));
+ System_Log("%s: GL_VENDOR: %s",GRAPHICS_SRC_FN, (const char *) pOpenGLDevice->glGetString(GL_VENDOR));
+ System_Log("%s: GL_VERSION: %s",GRAPHICS_SRC_FN, (const char *) pOpenGLDevice->glGetString(GL_VERSION));
+
+ const char *verstr = (const char *) pOpenGLDevice->glGetString(GL_VERSION);
+ int maj = 0;
+ int min = 0;
+ sscanf(verstr, "%d.%d", &maj, &min);
+
+ if ( (maj < 1) || ((maj == 1) && (min < 2)) )
+ {
+ _PostError("OpenGL implementation must be at least version 1.2");
+ _UnloadOpenGLEntryPoints();
+ return false;
+ }
+
+ const char *exts = (const char *) pOpenGLDevice->glGetString(GL_EXTENSIONS);
+
+ // NPOT texture support ...
+
+ if (_HaveOpenGLExtension(exts, "GL_ARB_texture_rectangle"))
+ pOpenGLDevice->have_GL_ARB_texture_rectangle = true;
+ else if (_HaveOpenGLExtension(exts, "GL_EXT_texture_rectangle"))
+ pOpenGLDevice->have_GL_ARB_texture_rectangle = true;
+ else if (_HaveOpenGLExtension(exts, "GL_NV_texture_rectangle"))
+ pOpenGLDevice->have_GL_ARB_texture_rectangle = true;
+ else
+ pOpenGLDevice->have_GL_ARB_texture_rectangle = false;
+
+ if (maj >= 2)
+ pOpenGLDevice->have_GL_ARB_texture_non_power_of_two = true;
+ else if (_HaveOpenGLExtension(exts, "GL_ARB_texture_non_power_of_two"))
+ pOpenGLDevice->have_GL_ARB_texture_non_power_of_two = true;
+ else
+ pOpenGLDevice->have_GL_ARB_texture_non_power_of_two = false;
+
+ if (pOpenGLDevice->have_GL_ARB_texture_rectangle)
+ {
+ System_Log("%s: OpenGL: Using GL_ARB_texture_rectangle",GRAPHICS_SRC_FN);
+ pOpenGLDevice->TextureTarget = GL_TEXTURE_RECTANGLE_ARB;
+ }
+ else if (pOpenGLDevice->have_GL_ARB_texture_non_power_of_two)
+ {
+ System_Log("%s: OpenGL: Using GL_ARB_texture_non_power_of_two",GRAPHICS_SRC_FN);
+ pOpenGLDevice->TextureTarget = GL_TEXTURE_2D;
+ }
+ else
+ {
+ // We can fake this with POT textures. Get a real OpenGL!
+ System_Log("%s: OpenGL: Using power-of-two textures. This costs more memory!",GRAPHICS_SRC_FN);
+ pOpenGLDevice->TextureTarget = GL_TEXTURE_2D;
+ }
+
+ // render-to-texture support ...
+
+ // is false if an entry point is missing, but we still need to check for the extension string...
+ if (pOpenGLDevice->have_GL_EXT_framebuffer_object)
+ {
+ // Disable this on Mac OS X Tiger, since some drivers appear to be buggy.
+ if ((pHGE->MacOSXVersion) && (pHGE->MacOSXVersion < 0x1050))
+ pOpenGLDevice->have_GL_EXT_framebuffer_object = false;
+ else if (_HaveOpenGLExtension(exts, "GL_EXT_framebuffer_object"))
+ pOpenGLDevice->have_GL_EXT_framebuffer_object = true;
+ else
+ pOpenGLDevice->have_GL_EXT_framebuffer_object = false;
+ }
+
+ if (pOpenGLDevice->have_GL_EXT_framebuffer_object)
+ System_Log("%s: OpenGL: Using GL_EXT_framebuffer_object",GRAPHICS_SRC_FN);
+ else
+ System_Log("%s: OpenGL: WARNING! No render-to-texture support. Things may render badly.",GRAPHICS_SRC_FN);
+
+
+ // Texture compression ...
+
+ if (bForceTextureCompression &&
+ _HaveOpenGLExtension(exts, "GL_ARB_texture_compression") &&
+ _HaveOpenGLExtension(exts, "GL_EXT_texture_compression_s3tc"))
+ pOpenGLDevice->have_GL_EXT_texture_compression_s3tc = true;
+ else
+ pOpenGLDevice->have_GL_EXT_texture_compression_s3tc = false;
+
+ if (pOpenGLDevice->have_GL_EXT_texture_compression_s3tc)
+ System_Log("%s: OpenGL: Using GL_EXT_texture_compression_s3tc",GRAPHICS_SRC_FN);
+ else if (bForceTextureCompression)
+ {
+ bForceTextureCompression = false; // oh well.
+ System_Log("%s: OpenGL: WARNING: no texture compression support, in a low-memory system.",GRAPHICS_SRC_FN);
+ System_Log("%s: OpenGL: Performance may be very bad!",GRAPHICS_SRC_FN);
+ }
+
+ // YUV textures...
+
+ if (_HaveOpenGLExtension(exts, "GL_APPLE_ycbcr_422"))
+ pOpenGLDevice->have_GL_APPLE_ycbcr_422 = true;
+ else
+ pOpenGLDevice->have_GL_APPLE_ycbcr_422 = false;
+
+ if (pOpenGLDevice->have_GL_APPLE_ycbcr_422)
+ System_Log("%s: OpenGL: Using GL_APPLE_ycbcr_422 to render YUV frames.",GRAPHICS_SRC_FN);
+ else
+ System_Log("%s: OpenGL: WARNING: no YUV texture support; videos may render slowly.",GRAPHICS_SRC_FN);
+
+ // VBOs...
+
+ // is false if an entry point is missing, but we still need to check for the extension string...
+ if (pOpenGLDevice->have_GL_ARB_vertex_buffer_object)
+ {
+ if (_HaveOpenGLExtension(exts, "GL_ARB_vertex_buffer_object"))
+ pOpenGLDevice->have_GL_ARB_vertex_buffer_object = true;
+ else
+ pOpenGLDevice->have_GL_ARB_vertex_buffer_object = false;
+ }
+
+ if (pOpenGLDevice->have_GL_ARB_vertex_buffer_object)
+ System_Log("%s: OpenGL: Using GL_ARB_vertex_buffer_object",GRAPHICS_SRC_FN);
+ else
+ System_Log("%s: OpenGL: WARNING! No VBO support; performance may suffer.",GRAPHICS_SRC_FN);
+
+ return true;
+}
+
+bool HGE_Impl::_GfxInit()
+{
+ CurTexture = 0;
+
+// Init OpenGL ... SDL should have created a context at this point.
+ assert(pOpenGLDevice == NULL);
+ pOpenGLDevice = new COpenGLDevice;
+ if (!_LoadOpenGLEntryPoints())
+ return false; // already called _PostError().
+
+ nScreenBPP=SDL_GetVideoSurface()->format->BitsPerPixel;
+
+ _AdjustWindow();
+
+ System_Log("%s: Mode: %d x %d\n",GRAPHICS_SRC_FN,nScreenWidth,nScreenHeight);
+// Create vertex batch buffer
+
+ VertArray=0;
+ textures=0;
+ IndexBufferObject=0;
+
+// Init all stuff that can be lost
+
+ if(!_init_lost()) return false;
+
+ // make sure the framebuffers are cleared and force to screen
+ pOpenGLDevice->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ SDL_GL_SwapBuffers();
+ pOpenGLDevice->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ SDL_GL_SwapBuffers();
+
+ return true;
+}
+
+void HGE_Impl::_AdjustWindow()
+{
+ // no-op.
+}
+
+void HGE_Impl::_Resize(int width, int height)
+{
+ if(hwndParent)
+ {
+ //if(procFocusLostFunc) procFocusLostFunc();
+ STUBBED("resize");
+ #if 0
+ d3dppW.BackBufferWidth=width;
+ d3dppW.BackBufferHeight=height;
+ nScreenWidth=width;
+ nScreenHeight=height;
+
+ _SetProjectionMatrix(nScreenWidth, nScreenHeight);
+ _GfxRestore();
+ #endif
+
+ //if(procFocusGainFunc) procFocusGainFunc();
+ }
+}
+
+void HGE_Impl::_GfxDone()
+{
+ //CRenderTargetList *target=pTargets;
+
+ while(textures) Texture_Free(textures->tex);
+ while(pTargets) Target_Free((HTARGET) pTargets);
+ textures=0;
+ pTargets=0;
+
+ VertArray = 0;
+ delete[] pVB;
+ pVB=0;
+ delete[] pIB;
+ pIB=0;
+
+ if(pOpenGLDevice)
+ {
+ if (pOpenGLDevice->have_GL_ARB_vertex_buffer_object)
+ {
+ if (IndexBufferObject != 0)
+ {
+ pOpenGLDevice->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, 0);
+ pOpenGLDevice->glDeleteBuffersARB(1, &IndexBufferObject);
+ IndexBufferObject = 0;
+ }
+ }
+
+ delete pOpenGLDevice;
+ pOpenGLDevice=0;
+ }
+}
+
+
+bool HGE_Impl::_GfxRestore()
+{
+ if(!pOpenGLDevice) return false;
+
+ delete[] pVB;
+ pVB=0;
+
+ delete[] pIB;
+ pIB=0;
+
+ _UnloadOpenGLEntryPoints();
+ if (!_LoadOpenGLEntryPoints())
+ return false;
+
+ if(!_init_lost()) return false;
+
+ if(procGfxRestoreFunc) return procGfxRestoreFunc();
+
+ return true;
+}
+
+
+bool HGE_Impl::_init_lost()
+{
+ _BindTexture(NULL); // make sure nothing is bound, so everything that we do bind regenerates.
+
+ for (CTextureList *item = textures; item != NULL; item = item->next)
+ {
+ gltexture *t = (gltexture *) item->tex;
+ if (t == NULL) continue;
+ t->lost = true;
+ t->name = 0;
+ }
+
+ CRenderTargetList *target=pTargets;
+ while(target)
+ {
+ gltexture *tex = (gltexture *) target->tex;
+ _BindTexture(tex); // force texture recreation.
+ _BindTexture(NULL);
+ _BuildTarget(target, tex ? tex->name : 0, target->width, target->height, target->depth != 0);
+ target=target->next;
+ }
+
+// Create Vertex buffer
+ // We just use a client-side array, since you can reasonably count on support
+ // existing in any GL, and it basically offers the same functionality that
+ // HGE uses in Direct3D: it locks the vertex buffer, unlocks in time to
+ // draw something, then relocks again immediately...more or less, that method
+ // offers the same performance metrics as a client-side array.
+ // We _will_ stuff the indices in a buffer object, though, if possible,
+ // since they never change...this matches the D3D behaviour better, since
+ // they lock, store, and forget about it, but without a buffer object,
+ // we'd have to pass the array over the bus every glDrawElements() call.
+ // It's not worth the tapdance for vertex buffer objects though, due to
+ // HGE's usage patterns.
+ pVB = new hgeVertex[VERTEX_BUFFER_SIZE];
+
+// Create and setup Index buffer
+ pIB = new GLushort[VERTEX_BUFFER_SIZE*6/4];
+ GLushort *pIndices = pIB;
+ int n = 0;
+ for(int i=0; i<VERTEX_BUFFER_SIZE/4; i++) {
+ *pIndices++=n;
+ *pIndices++=n+1;
+ *pIndices++=n+2;
+ *pIndices++=n+2;
+ *pIndices++=n+3;
+ *pIndices++=n;
+ n+=4;
+ }
+
+ #if !DEBUG_VERTICES // need pIB for DEBUG_VERTICES.
+ if (pOpenGLDevice->have_GL_ARB_vertex_buffer_object)
+ {
+ // stay bound forever. The Index Buffer Object never changes.
+ pOpenGLDevice->glGenBuffersARB(1, &IndexBufferObject);
+ pOpenGLDevice->glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
+ pOpenGLDevice->glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, sizeof (GLushort) * ((VERTEX_BUFFER_SIZE*6)/4), pIB, GL_STATIC_DRAW);
+ delete[] pIB;
+ pIB=0;
+ }
+ #endif
+
+ // always use client-side arrays; set it up once at startup.
+ pOpenGLDevice->glVertexPointer(3, GL_FLOAT, sizeof (hgeVertex), &pVB[0].x);
+ pOpenGLDevice->glColorPointer(4, GL_UNSIGNED_BYTE, sizeof (hgeVertex), &pVB[0].col);
+ pOpenGLDevice->glTexCoordPointer(2, GL_FLOAT, sizeof (hgeVertex), &pVB[0].tx);
+ pOpenGLDevice->glEnableClientState(GL_VERTEX_ARRAY);
+ pOpenGLDevice->glEnableClientState(GL_COLOR_ARRAY);
+ pOpenGLDevice->glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+// Set common render states
+
+ pOpenGLDevice->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ pOpenGLDevice->glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+ //pD3DDevice->SetRenderState( D3DRS_LASTPIXEL, FALSE );
+ pOpenGLDevice->glDisable(GL_TEXTURE_2D);
+ if (pOpenGLDevice->have_GL_ARB_texture_rectangle)
+ pOpenGLDevice->glDisable(GL_TEXTURE_RECTANGLE_ARB);
+ pOpenGLDevice->glEnable(pOpenGLDevice->TextureTarget);
+ pOpenGLDevice->glEnable(GL_SCISSOR_TEST);
+ pOpenGLDevice->glDisable(GL_CULL_FACE);
+ pOpenGLDevice->glDisable(GL_LIGHTING);
+ pOpenGLDevice->glDepthFunc(GL_GEQUAL);
+
+ if (bZBuffer)
+ pOpenGLDevice->glEnable(GL_DEPTH_TEST);
+ else
+ pOpenGLDevice->glDisable(GL_DEPTH_TEST);
+
+ pOpenGLDevice->glEnable(GL_BLEND);
+ pOpenGLDevice->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ pOpenGLDevice->glEnable(GL_ALPHA_TEST);
+ pOpenGLDevice->glAlphaFunc(GL_GEQUAL, 1.0f / 255.0f);
+
+ pOpenGLDevice->glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ _SetTextureFilter();
+
+ // !!! FIXME: this isn't what HGE's Direct3D code does, but the game I'm working with
+ // !!! FIXME: forces clamping outside of HGE, so I just wedged it in here.
+ // Apple says texture rectangle on ATI X1000 chips only supports CLAMP_TO_EDGE.
+ // Texture rectangle only supports CLAMP* wrap modes anyhow.
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ pOpenGLDevice->glTexParameteri(pOpenGLDevice->TextureTarget, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+ nPrim=0;
+ CurPrimType=HGEPRIM_QUADS;
+ CurBlendMode = BLEND_DEFAULT;
+ CurTexture = 0;
+
+ pOpenGLDevice->glScissor(0, 0, nScreenWidth, nScreenHeight);
+ pOpenGLDevice->glViewport(0, 0, nScreenWidth, nScreenHeight);
+
+ // make sure the framebuffer is cleared and force to screen
+ pOpenGLDevice->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ _SetProjectionMatrix(nScreenWidth, nScreenHeight);
+ pOpenGLDevice->glMatrixMode(GL_MODELVIEW);
+ pOpenGLDevice->glLoadIdentity();
+
+ return true;
+}