From c91847d549cc1c30eb15504a15ea9a6d5aa48165 Mon Sep 17 00:00:00 2001 From: "chirs241097@gmail.com" Date: Sun, 12 Jan 2014 14:43:14 +0000 Subject: --- hge/graphics.cpp | 1525 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1525 insertions(+) create mode 100644 hge/graphics.cpp (limited to 'hge/graphics.cpp') diff --git a/hge/graphics.cpp b/hge/graphics.cpp new file mode 100644 index 0000000..20fc634 --- /dev/null +++ b/hge/graphics.cpp @@ -0,0 +1,1525 @@ +/* +** 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 +#include +#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 (!bTransforming) + { + if ((x == 0.0f) && (y == 0.0f) && (dx == 0.0f) && (dy == 0.0f) && (rot == 0.0f) && (hscale == 1.0f) && (vscale == 1.0f)) + return; // nothing to do here, don't call into the GL. + } + + _render_batch(); + + bTransforming = true; + + // !!! FIXME: this math is probably wrong. Re-sync with the Direct3D code. + + pOpenGLDevice->glMatrixMode(GL_MODELVIEW); + if(vscale==0.0f) pOpenGLDevice->glLoadIdentity(); + else + { + pOpenGLDevice->glTranslatef(-x, -y, 0.0f); + //pOpenGLDevice->glScalef(1.0f, -1.0f, 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 static inline const T Min(const T a, const T b) { return a < b ? a : b; } +template 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 (int i = 1; i < (sizeof(GLuint) * 8); i *= 2) + 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 != pTex->width) || (h != 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 <= pTex->width); + assert(height > 0); + assert(height <= 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; ihave_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; +} -- cgit v1.2.3