/* ** 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 ((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 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 (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; 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; }