diff --git a/gemrb/plugins/SDLVideo/GLTextureSprite2D.cpp b/gemrb/plugins/SDLVideo/GLTextureSprite2D.cpp new file mode 100644 index 0000000000..6103740e31 --- /dev/null +++ b/gemrb/plugins/SDLVideo/GLTextureSprite2D.cpp @@ -0,0 +1,236 @@ +#ifdef USE_GL +#include +#else +#include +#include +#endif + +#include "SDLVideo.h" +#include "GLTextureSprite2D.h" + +using namespace GemRB; + +static Uint8 GetShiftValue(Uint32 value) +{ + for(unsigned int i=0; i> i) & 0x1) > 0) return i; + } + return 24; +} + +GLTextureSprite2D::GLTextureSprite2D (int Width, int Height, int Bpp, void* pixels, Uint32 rmask, Uint32 gmask, + Uint32 bmask, Uint32 amask) : Sprite2D(Width, Height, Bpp, pixels) +{ + currentPalette = NULL; + glTexture = 0; + glPaletteTexture = 0; + glMaskTexture = 0; + colorKeyIndex = 0; // invalid index + rMask = rmask; + gMask = gmask; + bMask = bmask; + aMask = amask; +} + +GLTextureSprite2D::~GLTextureSprite2D() +{ + glDeleteTextures(1, &glTexture); + glDeleteTextures(1, &glPaletteTexture); + glDeleteTextures(1, &glMaskTexture); +} + +GLTextureSprite2D::GLTextureSprite2D(const GLTextureSprite2D &obj) : Sprite2D(obj) +{ + // copies only 8 bit sprites + glTexture = 0; + glPaletteTexture = 0; + glMaskTexture = 0; + currentPalette = NULL; + colorKeyIndex = obj.colorKeyIndex; + rMask = obj.rMask; + gMask = obj.bMask; + bMask = obj.bMask; + aMask = obj.aMask; + SetPalette(obj.currentPalette); +} + +GLTextureSprite2D* GLTextureSprite2D::copy() const +{ + return new GLTextureSprite2D(*this); +} + + +void GLTextureSprite2D::SetPalette(Palette *pal) +{ + if(!IsPaletted() || currentPalette == pal) return; + if (pal != NULL) + { + pal->acquire(); + } + if (currentPalette != NULL) + { + currentPalette->release(); + } + currentPalette = pal; + glDeleteTextures(1, &glPaletteTexture); + glPaletteTexture = 0; +} + +Palette* GLTextureSprite2D::GetPalette() const +{ + if(!IsPaletted() || currentPalette == NULL) return NULL; + currentPalette->acquire(); + return currentPalette; +} + +void GLTextureSprite2D::SetColorKey(ieDword index) +{ + colorKeyIndex = index; + if(IsPaletted()) + { + glDeleteTextures(1, &glPaletteTexture); + glDeleteTextures(1, &glMaskTexture); + glPaletteTexture = 0; + glMaskTexture = 0; + } + else + { + glDeleteTextures(1, &glTexture); + glTexture = 0; + } +} + +Color GLTextureSprite2D::GetPixel(unsigned short x, unsigned short y) const +{ + if (x >= Width || y >= Height) return Color(); + if (Bpp == 8) + { + Uint8 pixel = ((Uint8*)pixels)[y*Width + x]; + Color color = currentPalette->col[pixel]; // hack (we have a = 0 for non-transparent pixels on palette) + if(pixel != colorKeyIndex) color.a = 255; + return color; + } + return Color(); // not supported yet +} + +void GLTextureSprite2D::createGlTexture() +{ + glDeleteTextures(1, &glTexture); + glGenTextures(1, &glTexture); + glBindTexture(GL_TEXTURE_2D, glTexture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if(Bpp == 32) // true color textures + { + int* buffer = new int[Width * Height]; + for(int i = 0; i < Width*Height; i++) + { + Uint32 src = ((Uint32*) pixels)[i]; + Uint8 r = (src & rMask) >> GetShiftValue(rMask); + Uint8 g = (src & gMask) >> GetShiftValue(gMask); + Uint8 b = (src & bMask) >> GetShiftValue(bMask); + Uint8 a = (src & aMask) >> GetShiftValue(aMask); + if (a == 0) a = 0xFF; //no transparency + if (src == colorKeyIndex) a = 0x00; // transparent + buffer[i] = r | (g << 8) | (b << 16) | (a << 24); + } + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) buffer); + delete[] buffer; + } + else if(Bpp == 8) // indexed + { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, Width, Height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, (GLvoid*) pixels); + } +} + +void GLTextureSprite2D::createGlTextureForPalette() +{ + glDeleteTextures(1, &glPaletteTexture); + glGenTextures(1, &glPaletteTexture); + glBindTexture(GL_TEXTURE_2D, glPaletteTexture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + Color* colors = new Color[256]; + memcpy(colors, currentPalette->col, sizeof(Color)*256); + for(unsigned int i=0; i<256; i++) + { + if(colors[i].a == 0) + { + colors[i].a = 0xFF; + } + if(i == colorKeyIndex) + colors[i].a = 0; + } + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) colors); + delete[] colors; +} + +void GLTextureSprite2D::createGLMaskTexture() +{ + glDeleteTextures(1, &glMaskTexture); + glGenTextures(1, &glMaskTexture); + glBindTexture(GL_TEXTURE_2D, glMaskTexture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + Uint8* mask = new Uint8[Width*Height]; + for(int i=0; i 0 && Height > 0) + { + createGlTexture(); + } + return glTexture; +} \ No newline at end of file diff --git a/gemrb/plugins/SDLVideo/GLTextureSprite2D.h b/gemrb/plugins/SDLVideo/GLTextureSprite2D.h new file mode 100644 index 0000000000..1ec015d633 --- /dev/null +++ b/gemrb/plugins/SDLVideo/GLTextureSprite2D.h @@ -0,0 +1,41 @@ +#ifndef GLTEXTURESPRITE2D_H +#define GLTEXTURESPRITE2D_H + +#include "Sprite2D.h" + +namespace GemRB +{ + class GLTextureSprite2D : public Sprite2D + { + private: + GLuint glTexture; + GLuint glPaletteTexture; + GLuint glMaskTexture; + Palette* currentPalette; + Uint32 rMask, gMask, bMask, aMask; + ieDword colorKeyIndex; + + void createGlTexture(); + void createGlTextureForPalette(); + void createGLMaskTexture(); + public: + GLuint GetTexture(); + GLuint GetPaletteTexture(Palette* pal); + GLuint GetPaletteTexture(); + GLuint GetMaskTexture(); + void SetPaletteTexture(int texture); + Palette* GetPalette() const; + const Color* GetPaletteColors() const { return currentPalette->col; } + void SetPalette(Palette *pal); + Color GetPixel(unsigned short x, unsigned short y) const; + ieDword GetColorKey() const { return colorKeyIndex; } + void SetColorKey(ieDword); + bool IsPaletted() const { return Bpp != 32; } + GLTextureSprite2D (int Width, int Height, int Bpp, void* pixels, Uint32 rmask=0, Uint32 gmask=0, Uint32 bmask=0, Uint32 amask=0); + ~GLTextureSprite2D(); + GLTextureSprite2D(const GLTextureSprite2D &obj); + GLTextureSprite2D* copy() const; + }; +} + +#endif \ No newline at end of file diff --git a/gemrb/plugins/SDLVideo/Matrix.cpp b/gemrb/plugins/SDLVideo/Matrix.cpp new file mode 100644 index 0000000000..83afdf7ab0 --- /dev/null +++ b/gemrb/plugins/SDLVideo/Matrix.cpp @@ -0,0 +1,468 @@ +#include +#include +#include "Matrix.h" +#undef near +#undef far + +#define I(_i, _j) ((_j)+ 4*(_i)) + +void Matrix::TransposeM(float* mTrans, float* m) +{ + for (int i = 0; i < 4; i++) + { + int mBase = i * 4; + mTrans[i] = m[mBase]; + mTrans[i + 4] = m[mBase + 1]; + mTrans[i + 8] = m[mBase + 2]; + mTrans[i + 12] = m[mBase + 3]; + } +} + +bool Matrix::InvertM(float* mInv, float* m) +{ + float src0 = m[0]; + float src4 = m[1]; + float src8 = m[2]; + float src12 = m[3]; + + float src1 = m[4]; + float src5 = m[5]; + float src9 = m[6]; + float src13 = m[7]; + + float src2 = m[8]; + float src6 = m[9]; + float src10 = m[10]; + float src14 = m[11]; + + float src3 = m[12]; + float src7 = m[13]; + float src11 = m[14]; + float src15 = m[15]; + + // calculate pairs for first 8 elements (cofactors) + float atmp0 = src10 * src15; + float atmp1 = src11 * src14; + float atmp2 = src9 * src15; + float atmp3 = src11 * src13; + float atmp4 = src9 * src14; + float atmp5 = src10 * src13; + float atmp6 = src8 * src15; + float atmp7 = src11 * src12; + float atmp8 = src8 * src14; + float atmp9 = src10 * src12; + float atmp10 = src8 * src13; + float atmp11 = src9 * src12; + + // calculate first 8 elements (cofactors) + float dst0 = (atmp0 * src5 + atmp3 * src6 + atmp4 * src7) - (atmp1 * src5 + atmp2 * src6 + atmp5 * src7); + float dst1 = (atmp1 * src4 + atmp6 * src6 + atmp9 * src7) - (atmp0 * src4 + atmp7 * src6 + atmp8 * src7); + float dst2 = (atmp2 * src4 + atmp7 * src5 + atmp10 * src7) - (atmp3 * src4 + atmp6 * src5 + atmp11 * src7); + float dst3 = (atmp5 * src4 + atmp8 * src5 + atmp11 * src6) - (atmp4 * src4 + atmp9 * src5 + atmp10 * src6); + float dst4 = (atmp1 * src1 + atmp2 * src2 + atmp5 * src3) - (atmp0 * src1 + atmp3 * src2 + atmp4 * src3); + float dst5 = (atmp0 * src0 + atmp7 * src2 + atmp8 * src3) - (atmp1 * src0 + atmp6 * src2 + atmp9 * src3); + float dst6 = (atmp3 * src0 + atmp6 * src1 + atmp11 * src3) - (atmp2 * src0 + atmp7 * src1 + atmp10 * src3); + float dst7 = (atmp4 * src0 + atmp9 * src1 + atmp10 * src2) - (atmp5 * src0 + atmp8 * src1 + atmp11 * src2); + + // calculate pairs for second 8 elements (cofactors) + float btmp0 = src2 * src7; + float btmp1 = src3 * src6; + float btmp2 = src1 * src7; + float btmp3 = src3 * src5; + float btmp4 = src1 * src6; + float btmp5 = src2 * src5; + float btmp6 = src0 * src7; + float btmp7 = src3 * src4; + float btmp8 = src0 * src6; + float btmp9 = src2 * src4; + float btmp10 = src0 * src5; + float btmp11 = src1 * src4; + + // calculate second 8 elements (cofactors) + float dst8 = (btmp0 * src13 + btmp3 * src14 + btmp4 * src15) - (btmp1 * src13 + btmp2 * src14 + btmp5 * src15); + float dst9 = (btmp1 * src12 + btmp6 * src14 + btmp9 * src15) - (btmp0 * src12 + btmp7 * src14 + btmp8 * src15); + float dst10 = (btmp2 * src12 + btmp7 * src13 + btmp10 * src15) - (btmp3 * src12 + btmp6 * src13 + btmp11 * src15); + float dst11 = (btmp5 * src12 + btmp8 * src13 + btmp11 * src14) - (btmp4 * src12 + btmp9 * src13 + btmp10 * src14); + float dst12 = (btmp2 * src10 + btmp5 * src11 + btmp1 * src9 ) - (btmp4 * src11 + btmp0 * src9 + btmp3 * src10); + float dst13 = (btmp8 * src11 + btmp0 * src8 + btmp7 * src10) - (btmp6 * src10 + btmp9 * src11 + btmp1 * src8 ); + float dst14 = (btmp6 * src9 + btmp11 * src11 + btmp3 * src8 ) - (btmp10 * src11 + btmp2 * src8 + btmp7 * src9 ); + float dst15 = (btmp10 * src10 + btmp4 * src8 + btmp9 * src9 ) - (btmp8 * src9 + btmp11 * src10 + btmp5 * src8 ); + + // calculate determinant + float det = src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3; + if (det == 0.0f) return false; + + // calculate matrix inverse + float invdet = 1.0f / det; + mInv[0] = dst0 * invdet; + mInv[1] = dst1 * invdet; + mInv[2] = dst2 * invdet; + mInv[3] = dst3 * invdet; + + mInv[4] = dst4 * invdet; + mInv[5] = dst5 * invdet; + mInv[6] = dst6 * invdet; + mInv[7] = dst7 * invdet; + + mInv[8] = dst8 * invdet; + mInv[9] = dst9 * invdet; + mInv[10] = dst10 * invdet; + mInv[11] = dst11 * invdet; + + mInv[12] = dst12 * invdet; + mInv[13] = dst13 * invdet; + mInv[14] = dst14 * invdet; + mInv[15] = dst15 * invdet; + + return true; +} + +void Matrix::OrthoM(float* m, float left, float right, float bottom, float top, float near, float far) +{ + if (left == right) return; + if (bottom == top) return; + if (near == far) return; + + float r_width = 1.0f / (right - left); + float r_height = 1.0f / (top - bottom); + float r_depth = 1.0f / (far - near); + float x = 2.0f * (r_width); + float y = 2.0f * (r_height); + float z = -2.0f * (r_depth); + float tx = -(right + left) * r_width; + float ty = -(top + bottom) * r_height; + float tz = -(far + near) * r_depth; + m[0] = x; + m[5] = y; + m[10] = z; + m[12] = tx; + m[13] = ty; + m[14] = tz; + m[15] = 1.0f; + m[1] = 0.0f; + m[2] = 0.0f; + m[3] = 0.0f; + m[4] = 0.0f; + m[6] = 0.0f; + m[7] = 0.0f; + m[8] = 0.0f; + m[9] = 0.0f; + m[11] = 0.0f; +} + +void Matrix::FrustumM(float* m, float left, float right, float bottom, float top, float near, float far) +{ + if (left == right) return; + if (top == bottom) return; + if (near == far) return; + if (near <= 0.0f) return; + if (far <= 0.0f) return; + float r_width = 1.0f / (right - left); + float r_height = 1.0f / (top - bottom); + float r_depth = 1.0f / (near - far); + float x = 2.0f * (near * r_width); + float y = 2.0f * (near * r_height); + float A = (right + left) * r_width; + float B = (top + bottom) * r_height; + float C = (far + near) * r_depth; + float D = 2.0f * (far * near * r_depth); + m[0] = x; + m[5] = y; + m[8] = A; + m[9] = B; + m[10] = C; + m[14] = D; + m[11] = -1.0f; + m[1] = 0.0f; + m[2] = 0.0f; + m[3] = 0.0f; + m[4] = 0.0f; + m[6] = 0.0f; + m[7] = 0.0f; + m[12] = 0.0f; + m[13] = 0.0f; + m[15] = 0.0f; +} + +void Matrix::PerspectiveM(float* m, float fovy, float aspect, float zNear, float zFar) +{ + float f = 1.0f / (float) tanf(fovy * (M_PI / 360.0)); + float rangeReciprocal = 1.0f / (zNear - zFar); + + m[0] = f / aspect; + m[1] = 0.0f; + m[2] = 0.0f; + m[3] = 0.0f; + + m[4] = 0.0f; + m[5] = f; + m[6] = 0.0f; + m[7] = 0.0f; + + m[8] = 0.0f; + m[9] = 0.0f; + m[10] = (zFar + zNear) * rangeReciprocal; + m[11] = -1.0f; + + m[12] = 0.0f; + m[13] = 0.0f; + m[14] = 2.0f * zFar * zNear * rangeReciprocal; + m[15] = 0.0f; +} + +float Matrix::Length(float x, float y, float z) +{ + return (float) sqrtf(x * x + y * y + z * z); +} + +void Matrix::SetIdentityM(float* sm) +{ + for (int i = 0 ; i < 16 ; i++) + { + sm[i] = 0; + } + for(int i = 0; i < 16; i += 5) + { + sm[i] = 1.0f; + } +} + +void Matrix::ScaleM(float* sm, float* m, float x, float y, float z) +{ + for (int i = 0 ; i < 4 ; i++) + { + sm[ i] = m[ i] * x; + sm[ 4 + i] = m[ 4 + i] * y; + sm[ 8 + i] = m[ 8 + i] * z; + sm[12 + i] = m[12 + i]; + } +} + +void Matrix::ScaleM(float* m, float x, float y, float z) +{ + for (int i = 0 ; i < 4 ; i++) + { + m[ i] *= x; + m[ 4 + i] *= y; + m[ 8 + i] *= z; + } +} + +void Matrix::TranslateM(float* tm, float* m, float x, float y, float z) +{ + for (int i = 0 ; i < 12 ; i++) + { + tm[i] = m[i]; + } + for (int i = 0 ; i < 4 ; i++) + { + tm[12 + i] = m[i] * x + m[4 + i] * y + m[8 + i] * z + m[12 + i]; + } +} + +void Matrix::TranslateM(float* m, float x, float y, float z) +{ + for (int i = 0 ; i < 4 ; i++) + { + m[12 + i] += m[i] * x + m[4 + i] * y + m[8 + i] * z; + } +} + +void Matrix::SetRotateM(float* rm, float a, float x, float y, float z) +{ + rm[3] = 0; + rm[7] = 0; + rm[11]= 0; + rm[12]= 0; + rm[13]= 0; + rm[14]= 0; + rm[15]= 1; + a *= (float) (M_PI / 180.0f); + float s = (float) sinf(a); + float c = (float) cosf(a); + if (1.0f == x && 0.0f == y && 0.0f == z) + { + rm[5] = c; rm[10]= c; + rm[6] = s; rm[9] = -s; + rm[1] = 0; rm[2] = 0; + rm[4] = 0; rm[8] = 0; + rm[0] = 1; + } + else if (0.0f == x && 1.0f == y && 0.0f == z) + { + rm[0] = c; rm[10]= c; + rm[8] = s; rm[2] = -s; + rm[1] = 0; rm[4] = 0; + rm[6] = 0; rm[9] = 0; + rm[5] = 1; + } + else if (0.0f == x && 0.0f == y && 1.0f == z) + { + rm[0] = c; rm[5] = c; + rm[1] = s; rm[4] = -s; + rm[2] = 0; rm[6] = 0; + rm[8] = 0; rm[9] = 0; + rm[10]= 1; + } + else + { + float len = Length(x, y, z); + if (1.0f != len) + { + float recipLen = 1.0f / len; + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + float nc = 1.0f - c; + float xy = x * y; + float yz = y * z; + float zx = z * x; + float xs = x * s; + float ys = y * s; + float zs = z * s; + rm[0] = x*x*nc + c; + rm[4] = xy*nc - zs; + rm[8] = zx*nc + ys; + rm[1] = xy*nc + zs; + rm[5] = y*y*nc + c; + rm[9] = yz*nc - xs; + rm[2] = zx*nc - ys; + rm[6] = yz*nc + xs; + rm[10] = z*z*nc + c; + } +} + +void Matrix::RotateM(float* rm, float* m, float a, float x, float y, float z) +{ + float sTemp[32]; + SetRotateM(sTemp, a, x, y, z); + MultiplyMM(rm, m, sTemp); +} + +void Matrix::RotateM(float* m, float a, float x, float y, float z) +{ + float sTemp[32]; + SetRotateM(sTemp, a, x, y, z); + MultiplyMM(&sTemp[16], m, sTemp); + memcpy(m, &sTemp[16], 16); +} + +void Matrix::SetRotateEulerM(float* rm, float x, float y, float z) +{ + x *= (float) (M_PI / 180.0f); + y *= (float) (M_PI / 180.0f); + z *= (float) (M_PI / 180.0f); + float cx = cosf(x); + float sx = sinf(x); + float cy = cosf(y); + float sy = sinf(y); + float cz = cosf(z); + float sz = sinf(z); + float cxsy = cx * sy; + float sxsy = sx * sy; + + rm[0] = cy * cz; + rm[1] = -cy * sz; + rm[2] = sy; + rm[3] = 0.0f; + + rm[4] = cxsy * cz + cx * sz; + rm[5] = -cxsy * sz + cx * cz; + rm[6] = -sx * cy; + rm[7] = 0.0f; + + rm[8] = -sxsy * cz + sx * sz; + rm[9] = sxsy * sz + sx * cz; + rm[10] = cx * cy; + rm[11] = 0.0f; + + rm[12] = 0.0f; + rm[13] = 0.0f; + rm[14] = 0.0f; + rm[15] = 1.0f; +} + +void Matrix::MultiplyMM(float* result, const float* lhs, const float* rhs) +{ + for (int i = 0; i < 4; i++) + { + float rhs_i0 = rhs[ I(i,0) ]; + float ri0 = lhs[ I(0,0) ] * rhs_i0; + float ri1 = lhs[ I(0,1) ] * rhs_i0; + float ri2 = lhs[ I(0,2) ] * rhs_i0; + float ri3 = lhs[ I(0,3) ] * rhs_i0; + for (int j = 1; j < 4; j++) + { + float rhs_ij = rhs[ I(i,j) ]; + ri0 += lhs[ I(j,0) ] * rhs_ij; + ri1 += lhs[ I(j,1) ] * rhs_ij; + ri2 += lhs[ I(j,2) ] * rhs_ij; + ri3 += lhs[ I(j,3) ] * rhs_ij; + } + result[ I(i,0) ] = ri0; + result[ I(i,1) ] = ri1; + result[ I(i,2) ] = ri2; + result[ I(i,3) ] = ri3; + } +} + +void Matrix::MultiplyMV(float* result, const float* lhs, const float* rhs) +{ + mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, result); +} + +void Matrix::SetLookAtM(float* rm, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, + float centerZ, float upX, float upY, float upZ) +{ + // See the OpenGL GLUT documentation for gluLookAt for a description + // of the algorithm. We implement it in a straightforward way: + + float fx = centerX - eyeX; + float fy = centerY - eyeY; + float fz = centerZ - eyeZ; + + // Normalize f + float rlf = 1.0f / Length(fx, fy, fz); + fx *= rlf; + fy *= rlf; + fz *= rlf; + + // compute s = f x up (x means "cross product") + float sx = fy * upZ - fz * upY; + float sy = fz * upX - fx * upZ; + float sz = fx * upY - fy * upX; + + // and normalize s + float rls = 1.0f / Length(sx, sy, sz); + sx *= rls; + sy *= rls; + sz *= rls; + + // compute u = s x f + float ux = sy * fz - sz * fy; + float uy = sz * fx - sx * fz; + float uz = sx * fy - sy * fx; + + rm[0] = sx; + rm[1] = ux; + rm[2] = -fx; + rm[3] = 0.0f; + + rm[4] = sy; + rm[5] = uy; + rm[6] = -fy; + rm[7] = 0.0f; + + rm[8] = sz; + rm[9] = uz; + rm[10] = -fz; + rm[11] = 0.0f; + + rm[12] = 0.0f; + rm[13] = 0.0f; + rm[14] = 0.0f; + rm[15] = 1.0f; + + TranslateM(rm, -eyeX, -eyeY, -eyeZ); +} \ No newline at end of file diff --git a/gemrb/plugins/SDLVideo/Matrix.h b/gemrb/plugins/SDLVideo/Matrix.h new file mode 100644 index 0000000000..bc5463ae13 --- /dev/null +++ b/gemrb/plugins/SDLVideo/Matrix.h @@ -0,0 +1,33 @@ +class Matrix +{ +public: + static void MultiplyMV(float* result, const float* lhs, const float* rhs); + static void MultiplyMM(float* result, const float* lhs, const float* rhs); + + static void TransposeM(float* mTrans, float* m); + static bool InvertM(float* mInv, float* m); + static void OrthoM(float* m, float left, float right, float bottom, float top, float near, float far); + static void FrustumM(float* m, float left, float right, float bottom, float top, float near, float far); + static void PerspectiveM(float* m, float fovy, float aspect, float zNear, float zFar); + static float Length(float x, float y, float z); + static void SetIdentityM(float* sm); + static void ScaleM(float* sm, float* m, float x, float y, float z); + static void ScaleM(float* m, float x, float y, float z); + static void TranslateM(float* tm, float* m, float x, float y, float z); + static void TranslateM(float* m, float x, float y, float z); + static void SetRotateM(float* rm, float a, float x, float y, float z); + static void RotateM(float* rm, float* m, float a, float x, float y, float z); + static void RotateM(float* m, float a, float x, float y, float z); + static void SetRotateEulerM(float* rm, float x, float y, float z); + static void SetLookAtM(float* rm, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, + float centerZ, float upX, float upY, float upZ); + +private: + static inline void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) + { + pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; + pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w; + pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w; + pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w; + } +}; diff --git a/gemrb/plugins/SDLVideo/SDL20GLVideo.cpp b/gemrb/plugins/SDLVideo/SDL20GLVideo.cpp new file mode 100644 index 0000000000..da330d18a8 --- /dev/null +++ b/gemrb/plugins/SDLVideo/SDL20GLVideo.cpp @@ -0,0 +1,681 @@ +#ifdef USE_GL +#include +#ifdef _MSC_VER +#pragma comment(lib, "glew32") +#pragma comment(lib, "opengl32") +#endif +#else +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "libGLESv2") +#endif +#endif +#include "SDL20GLVideo.h" +#include "Interface.h" +#include "Game.h" // for GetGlobalTint +#include "GLTextureSprite2D.h" +#include "Shader.h" +#include "Matrix.h" + +using namespace GemRB; + +const char* vertexRect= +"uniform mat4 u_matrix;\n" +"attribute vec2 a_position;\n" +"attribute vec4 a_color;\n" +"varying vec4 v_color;\n" +"void main()\n" +"{\n" +" gl_Position = u_matrix * vec4(a_position, 0.0, 1.0);\n" +" v_color = a_color;\n" +"}\n"; + +const char* fragmentRect = +"varying vec4 v_color; \n" +"void main() \n" +"{\n" +" gl_FragColor = v_color;\n" +"}\n"; + +const char* vertex = +"uniform mat4 u_matrix;\n" +"attribute vec2 a_position;\n" +"attribute vec2 a_texCoord;\n" +"attribute float a_alphaModifier;\n" +"attribute vec4 a_tint;\n" +"varying vec2 v_texCoord;\n" +"varying float v_alphaModifier;\n" +"varying vec4 v_tint;\n" +"void main()\n" +"{\n" +" gl_Position = u_matrix * vec4(a_position, 0.0, 1.0);\n" +" v_texCoord = a_texCoord;\n" +" v_alphaModifier = a_alphaModifier;\n" +" v_tint = a_tint;\n" +"}\n"; + + +const char* fragment = +#ifndef USE_GL +"precision highp float; \n" +#endif +"varying vec2 v_texCoord; \n" +"uniform sampler2D s_texture; \n" +"varying float v_alphaModifier; \n" +"varying vec4 v_tint; \n" +"void main() \n" +"{\n" +" vec4 color = texture2D(s_texture, v_texCoord); \n" +" gl_FragColor = vec4(color.r*v_tint.r, color.g*v_tint.g, color.b*v_tint.b, color.a * v_alphaModifier);\n" +"}\n"; + +const char* fragmentPal = +#ifndef USE_GL +"precision highp float; \n" +#endif +"uniform sampler2D s_texture; // own texture \n" +"uniform sampler2D s_palette; // palette 256 x 1 pixels \n" +"uniform sampler2D s_mask; // optional mask \n" +"varying vec2 v_texCoord;\n" +"varying float v_alphaModifier;\n" +"varying vec4 v_tint; \n" +"void main()\n" +"{\n" +" float alphaModifier = v_alphaModifier * texture2D(s_mask, v_texCoord).a;\n" +" float index = texture2D(s_texture, v_texCoord).a;\n" +" vec4 color = texture2D(s_palette, vec2(index, 0.0));\n" +" gl_FragColor = vec4(color.r*v_tint.r, color.g*v_tint.g, color.b*v_tint.b, color.a * alphaModifier);\n" +"}\n"; + +const char* fragmentPalGrayed = +#ifndef USE_GL +"precision highp float; \n" +#endif +"uniform sampler2D s_texture; // own texture \n" +"uniform sampler2D s_palette; // palette 256 x 1 pixels \n" +"uniform sampler2D s_mask; // optional mask \n" +"varying vec2 v_texCoord;\n" +"varying float v_alphaModifier;\n" +"varying vec4 v_tint; \n" +"void main()\n" +"{\n" +" float alphaModifier = v_alphaModifier * texture2D(s_mask, v_texCoord).a;\n" +" float index = texture2D(s_texture, v_texCoord).a;\n" +" vec4 color = texture2D(s_palette, vec2(index, 0.0));\n" +" float gray = (color.r + color.g + color.b)*0.333333;\n" +" gl_FragColor = vec4(gray, gray, gray, color.a * alphaModifier);\n" +"}\n"; + +const char* fragmentPalSepia = +#ifndef USE_GL +"precision highp float; \n" +#endif +"uniform sampler2D s_texture; // own texture \n" +"uniform sampler2D s_palette; // palette 256 x 1 pixels \n" +"uniform sampler2D s_mask; // optional mask \n" +"varying vec2 v_texCoord;\n" +"varying float v_alphaModifier;\n" +"varying vec4 v_tint; \n" +"const vec3 lightColor = vec3(0.9, 0.9, 0.5);\n" +"const vec3 darkColor = vec3(0.2, 0.05, 0.0);\n" +"void main()\n" +"{\n" +" float alphaModifier = v_alphaModifier * texture2D(s_mask, v_texCoord).a;\n" +" float index = texture2D(s_texture, v_texCoord).a;\n" +" vec4 color = texture2D(s_palette, vec2(index, 0.0));\n" +" float gray = (color.r + color.g + color.b)*0.333333;\n" +" vec3 sepia = darkColor*(1.0 - gray) + lightColor*gray;\n" +" gl_FragColor = vec4(sepia, color.a * alphaModifier);\n" +"}\n"; + +GLVideoDriver::~GLVideoDriver() +{ + glDeleteProgram(program32); + glDeleteProgram(programPal); + glDeleteProgram(programPalGrayed); + glDeleteProgram(programPalSepia); + glDeleteProgram(programRect); + SDL_GL_DeleteContext(context); +} + +int GLVideoDriver::CreateDisplay(int w, int h, int bpp, bool fs, const char* title) +{ + fullscreen=fs; + width = w, height = h; + + Log(MESSAGE, "SDL 2 GL Driver", "Creating display"); + Uint32 winFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL; +#if TARGET_OS_IPHONE || ANDROID + // this allows the user to flip the device upsidedown if they wish and have the game rotate. + // it also for some unknown reason is required for retina displays + winFlags |= SDL_WINDOW_RESIZABLE; + // this hint is set in the wrapper for iPad at a higher priority. set it here for iPhone + // don't know if Android makes use of this. + SDL_SetHintWithPriority(SDL_HINT_ORIENTATIONS, "LandscapeRight LandscapeLeft", SDL_HINT_DEFAULT); +#endif + if (fullscreen) + { + winFlags |= SDL_WINDOW_FULLSCREEN; + //This is needed to remove the status bar on Android/iOS. + //since we are in fullscreen this has no effect outside Android/iOS + winFlags |= SDL_WINDOW_BORDERLESS; + } + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +#ifndef USE_GL + SDL_GL_SetAttribute(SDL_GL_CONTEXT_EGL, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#endif + window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, winFlags); + if (window == NULL) + { + Log(ERROR, "SDL 2 GL Driver", "couldnt create window:%s", SDL_GetError()); + return GEM_ERROR; + } + + context = SDL_GL_CreateContext(window); + if (context == NULL) + { + Log(ERROR, "SDL 2 GL Driver", "couldnt create GL context:%s", SDL_GetError()); + return GEM_ERROR; + } + SDL_GL_MakeCurrent(window, context); + //GLenum e = glGetError(); + + renderer = SDL_CreateRenderer(window, -1, 0); + + if (renderer == NULL) + { + Log(ERROR, "SDL 2 GL Driver", "couldnt create renderer:%s", SDL_GetError()); + return GEM_ERROR; + } + SDL_RenderSetLogicalSize(renderer, width, height); + + Viewport.w = width; + Viewport.h = height; + + SDL_RendererInfo info; + SDL_GetRendererInfo(renderer, &info); + + Uint32 format = SDL_PIXELFORMAT_RGBA8888; + screenTexture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STREAMING, width, height); + + int access; + + SDL_QueryTexture(screenTexture, + &format, + &access, + &width, + &height); + + Uint32 r, g, b, a; + SDL_PixelFormatEnumToMasks(format, &bpp, &r, &g, &b, &a); + a = 0; //force a to 0 or screenshots will be all black! + + Log(MESSAGE, "SDL 2 GL Driver", "Creating Main Surface: w=%d h=%d fmt=%s", + width, height, SDL_GetPixelFormatName(format)); + backBuf = SDL_CreateRGBSurface( 0, width, height, bpp, r, g, b, a ); + this->bpp = bpp; + + if (!backBuf) + { + Log(ERROR, "SDL 2 GL Video", "Unable to create backbuffer of %s format: %s", + SDL_GetPixelFormatName(format), SDL_GetError()); + return GEM_ERROR; + } + disp = backBuf; + +#ifdef USE_GL + glewInit(); +#endif + createPrograms(); + glViewport(0, 0, width, height); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_SCISSOR_TEST); + spritesPerFrame = 0; + return GEM_OK; +} + +void GLVideoDriver::useProgram(GLuint program) +{ + if(lastUsedProgram == program) return; + glUseProgram(program); + lastUsedProgram = program; +} + +void GLVideoDriver::createPrograms() +{ + ShaderOperationResult* result = Shader::BuildProgram(vertex, fragment); + if(result->Id != 0) program32 = result->Id; + delete result; + glUseProgram(program32); + glUniform1i(glGetUniformLocation(program32, "s_texture"), 0); + float matrix[16]; + Matrix::SetIdentityM(matrix); + glUniformMatrix4fv(glGetUniformLocation(program32, "u_matrix"), 1, GL_FALSE, matrix); + glUseProgram(0); + + result = Shader::BuildProgram(vertex, fragmentPal); + if(result->Id != 0) programPal = result->Id; + delete result; + glUseProgram(programPal); + glUniform1i(glGetUniformLocation(programPal, "s_texture"), 0); + glUniform1i(glGetUniformLocation(programPal, "s_palette"), 1); + glUniform1i(glGetUniformLocation(programPal, "s_mask"), 2); + glUniformMatrix4fv(glGetUniformLocation(programPal, "u_matrix"), 1, GL_FALSE, matrix); + glUseProgram(0); + + result = Shader::BuildProgram(vertex, fragmentPalGrayed); + if(result->Id != 0) programPalGrayed = result->Id; + delete result; + glUseProgram(programPalGrayed); + glUniform1i(glGetUniformLocation(programPalGrayed, "s_texture"), 0); + glUniform1i(glGetUniformLocation(programPalGrayed, "s_palette"), 1); + glUniform1i(glGetUniformLocation(programPalGrayed, "s_mask"), 2); + glUniformMatrix4fv(glGetUniformLocation(programPalGrayed, "u_matrix"), 1, GL_FALSE, matrix); + glUseProgram(0); + + result = Shader::BuildProgram(vertex, fragmentPalSepia); + if(result->Id != 0) programPalSepia = result->Id; + delete result; + glUseProgram(programPalSepia); + glUniform1i(glGetUniformLocation(programPalSepia, "s_texture"), 0); + glUniform1i(glGetUniformLocation(programPalSepia, "s_palette"), 1); + glUniform1i(glGetUniformLocation(programPalSepia, "s_mask"), 2); + glUniformMatrix4fv(glGetUniformLocation(programPalSepia, "u_matrix"), 1, GL_FALSE, matrix); + glUseProgram(0); + + result = Shader::BuildProgram(vertexRect, fragmentRect); + if(result->Id != 0) programRect = result->Id; + delete result; + glUseProgram(programRect); + glUniformMatrix4fv(glGetUniformLocation(programRect, "u_matrix"), 1, GL_FALSE, matrix); + glUseProgram(0); + + lastUsedProgram = 0; +} + +Sprite2D* GLVideoDriver::CreateSprite(int w, int h, int bpp, ieDword rMask, ieDword gMask, ieDword bMask, ieDword aMask, void* pixels, bool cK, int index) +{ + GLTextureSprite2D* spr = new GLTextureSprite2D(w, h, bpp, pixels, rMask, gMask, bMask, aMask); + if (cK) spr->SetColorKey(index); + return spr; +} + +Sprite2D* GLVideoDriver::CreatePalettedSprite(int w, int h, int bpp, void* pixels, Color* palette, bool cK, int index) +{ + GLTextureSprite2D* spr = new GLTextureSprite2D(w, h, bpp, pixels); + Palette* pal = new Palette(palette); + spr->SetPalette(pal); + if (cK) spr->SetColorKey(index); + return spr; +} + +Sprite2D* GLVideoDriver::CreateSprite8(int w, int h, void* pixels, Palette* palette, bool cK, int index) +{ + return CreatePalettedSprite(w, h, 8, pixels, palette->col, cK, index); +} + +void GLVideoDriver::blitSprite(GLTextureSprite2D* spr, int x, int y, const Region* clip, unsigned int flags, const Color* tint, GLTextureSprite2D* mask) +{ + float hscale, vscale; + SDL_Rect spriteRect; + spriteRect.w = spr->Width; + spriteRect.h = spr->Height; + if(clip) + { + // test region + if(clip->x > x) { spriteRect.x = x; spriteRect.w -= (clip->x - x); } + if(clip->y > y) { spriteRect.y = y; spriteRect.h -= (clip->y - y); } + if (x + spriteRect.w > clip->x + clip -> w) { spriteRect.w = clip->x + clip->w - x; } + if (y + spriteRect.h > clip->y + clip -> h) { spriteRect.h = clip->y + clip->h - y; } + if (spriteRect.w <= 0 || spriteRect.h <= 0) return; + + glViewport(clip->x, height - (clip->y + clip->h), clip->w, clip->h); + glScissor(clip->x, height - (clip->y + clip->h), clip->w, clip->h); + spriteRect.x = x - clip->x; + spriteRect.y = y - clip->y; + spriteRect.w = spr->Width; + spriteRect.h = spr->Height; + hscale = 2.0f/(float)clip->w; + vscale = 2.0f/(float)clip->h; + } + else + { + glViewport(0, 0, width, height); + glScissor(0, 0, width, height); + spriteRect.x = x; + spriteRect.y = y; + hscale = 2.0f/(float)width; + vscale = 2.0f/(float)height; + } + + // color tint + Color colorTint; + if (tint) + colorTint = *tint; + else + colorTint.r = colorTint.b = colorTint.g = colorTint.a = 255; + + // we do flipping here + bool hflip = spr->renderFlags & RENDER_FLIP_HORIZONTAL; + bool vflip = spr->renderFlags & RENDER_FLIP_VERTICAL; + if (flags & BLIT_MIRRORX) hflip = !hflip; + if (flags & BLIT_MIRRORY) vflip = !vflip; + GLfloat* textureCoords; + GLfloat coordsHV[] = { 1.0f,1.0f, 0.0f,1.0f, 1.0f,0.0f, 0.0f,0.0f }; + GLfloat coordsH[] = { 1.0f,0.0f, 0.0f,0.0f, 1.0f,1.0f, 0.0f,1.0f }; + GLfloat coordsV[] = { 0.0f,1.0f, 1.0f,1.0f, 0.0f,0.0f, 1.0f,0.0f }; + GLfloat coordsN[] = { 0.0f,0.0f, 1.0f,0.0f, 0.0f,1.0f, 1.0f,1.0f }; + if (hflip && vflip) + { + textureCoords = coordsHV; + } + else if (hflip) + { + textureCoords = coordsH; + } + else if (vflip) + { + textureCoords = coordsV; + } + else + { + textureCoords = coordsN; + } + + // alpha modifier + GLfloat alphaModifier = flags & BLIT_HALFTRANS ? 0.5f : 1.0f; + + // data + GLfloat data[] = + { + -1.0f + spriteRect.x*hscale, 1.0f - spriteRect.y*vscale, textureCoords[0], textureCoords[1], alphaModifier, (GLfloat)colorTint.r/255, (GLfloat)colorTint.g/255, (GLfloat)colorTint.b/255, (GLfloat)colorTint.a/255, + -1.0f + (spriteRect.x + spriteRect.w)*hscale, 1.0f - spriteRect.y*vscale, textureCoords[2], textureCoords[3], alphaModifier, (GLfloat)colorTint.r/255, (GLfloat)colorTint.g/255, (GLfloat)colorTint.b/255, (GLfloat)colorTint.a/255, + -1.0f + spriteRect.x*hscale, 1.0f - (spriteRect.y + spriteRect.h)*vscale, textureCoords[4], textureCoords[5], alphaModifier, (GLfloat)colorTint.r/255, (GLfloat)colorTint.g/255, (GLfloat)colorTint.b/255, (GLfloat)colorTint.a/255, + -1.0f + (spriteRect.x + spriteRect.w)*hscale, 1.0f - (spriteRect.y + spriteRect.h)*vscale, textureCoords[6], textureCoords[7], alphaModifier, (GLfloat)colorTint.r/255, (GLfloat)colorTint.g/255, (GLfloat)colorTint.b/255, (GLfloat)colorTint.a/255 + }; + + // shader program selection + GLuint program; + if(spr->IsPaletted()) + { + if (flags & BLIT_GREY) + program = programPalGrayed; + else if (flags & BLIT_SEPIA) + program = programPalSepia; + else + program = programPal; + } + else + program = program32; + useProgram(program); + + glActiveTexture(GL_TEXTURE0); + GLuint texture = spr->GetTexture(); + glBindTexture(GL_TEXTURE_2D, texture); + + if (spr->IsPaletted()) + { + glActiveTexture(GL_TEXTURE1); + GLuint palTexture = spr->GetPaletteTexture(); + glBindTexture(GL_TEXTURE_2D, palTexture); + } + if (mask) + { + glActiveTexture(GL_TEXTURE2); + GLuint maskTexture = ((GLTextureSprite2D*)mask)->GetMaskTexture(); + glBindTexture(GL_TEXTURE_2D, maskTexture); + } + if(flags & BLIT_EXTERNAL_MASK) {} // used with external mask + else + { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + } + + GLint a_position = glGetAttribLocation(program, "a_position"); + GLint a_texCoord = glGetAttribLocation(program, "a_texCoord"); + GLint a_alphaModifier = glGetAttribLocation(program, "a_alphaModifier"); + GLint a_tint = glGetAttribLocation(program, "a_tint"); + + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + + glVertexAttribPointer(a_position, VERTEX_SIZE, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*9, 0); + glVertexAttribPointer(a_texCoord, TEX_SIZE, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*9, BUFFER_OFFSET(sizeof(GLfloat)*VERTEX_SIZE)); + glVertexAttribPointer(a_alphaModifier, 1, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*9, BUFFER_OFFSET(sizeof(GLfloat)*(VERTEX_SIZE + TEX_SIZE))); + glVertexAttribPointer(a_tint, COLOR_SIZE, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*9, BUFFER_OFFSET(sizeof(GLfloat)*(VERTEX_SIZE + TEX_SIZE + 1))); + + glEnableVertexAttribArray(a_position); + glEnableVertexAttribArray(a_texCoord); + glEnableVertexAttribArray(a_alphaModifier); + glEnableVertexAttribArray(a_tint); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(a_tint); + glDisableVertexAttribArray(a_alphaModifier); + glDisableVertexAttribArray(a_texCoord); + glDisableVertexAttribArray(a_position); + + glDeleteBuffers(1, &buffer); + spritesPerFrame++; +} + +void GLVideoDriver::BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, unsigned int flags) +{ + int tx = x - spr->XPos; + int ty = y - spr->YPos; + tx -= Viewport.x; + ty -= Viewport.y; + unsigned int blitFlags = 0; + if (flags & TILE_HALFTRANS) blitFlags |= BLIT_HALFTRANS; + if (flags & TILE_GREY) blitFlags |= BLIT_GREY; + if (flags & TILE_SEPIA) blitFlags |= BLIT_SEPIA; + if (core->GetGame()) + { + const Color* totint = core->GetGame()->GetGlobalTint(); + return blitSprite((GLTextureSprite2D*)spr, tx, ty, clip, blitFlags, totint, (GLTextureSprite2D*)mask); + } + return blitSprite((GLTextureSprite2D*)spr, tx, ty, clip, blitFlags, NULL, (GLTextureSprite2D*)mask); +} + + +void GLVideoDriver::BlitSprite(const Sprite2D* spr, int x, int y, bool anchor, const Region* clip, Palette* palette) +{ + // x, y is a position on screen (if anchor) or viewport (if !anchor) + GLTextureSprite2D* glSprite = (GLTextureSprite2D*)spr; + int tx = x - spr->XPos; + int ty = y - spr->YPos; + if (!anchor) + { + tx -= Viewport.x; + ty -= Viewport.y; + } + if (palette || glSprite->IsPaletted()) + { + if (palette) glSprite->SetPalette(palette); + } + + return blitSprite(glSprite, tx, ty, clip); +} + +void GLVideoDriver::BlitGameSprite(const Sprite2D* spr, int x, int y, unsigned int flags, Color tint, SpriteCover* cover, Palette *palette, const Region* clip, bool anchor) +{ + int tx = x - spr->XPos; + int ty = y - spr->YPos; + if (!anchor) + { + tx -= Viewport.x; + ty -= Viewport.y; + } + GLTextureSprite2D* glSprite = (GLTextureSprite2D*)spr; + GLuint coverTexture = 0; + + if(glSprite->IsPaletted()) + { + if (cover) + { + int trueX = cover->XPos - glSprite->XPos; + int trueY = cover->YPos - glSprite->YPos; + Uint8* data = new Uint8[glSprite->Width*glSprite->Height]; + Uint8* coverPointer = &cover->pixels[trueY*glSprite->Width + trueX]; + Uint8* dataPointer = data; + for(int h=0; hHeight; h++) + { + for(int w=0; wWidth; w++) + { + *dataPointer = !(*coverPointer) * 255; + dataPointer++; + coverPointer++; + } + coverPointer += cover->Width - glSprite->Width; + } + glActiveTexture(GL_TEXTURE2); + glGenTextures(1, &coverTexture); + glBindTexture(GL_TEXTURE_2D, coverTexture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, glSprite->Width, glSprite->Height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, (GLvoid*) data); + delete[] data; + flags |= BLIT_EXTERNAL_MASK; + } + + if (palette) + { + if (palette) glSprite->SetPalette(palette); + } + if (!anchor && core->GetGame()) + { + const Color *totint = core->GetGame()->GetGlobalTint(); + if (totint) + { + if (flags & BLIT_TINTED) + { + tint.r = (tint.r * totint->r) >> 8; + tint.g = (tint.g * totint->g) >> 8; + tint.b = (tint.b * totint->b) >> 8; + } + else + { + flags |= BLIT_TINTED; + tint = *totint; + } + blitSprite(glSprite, tx, ty, clip, flags, &tint); + } + } + } + else + blitSprite(glSprite, tx, ty, clip, flags); + if (coverTexture != 0) + { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &coverTexture); + } +} + +void GLVideoDriver::DrawRect(const Region& rgn, const Color& color, bool fill, bool clipped) +{ + if (fill) return drawColoredRect(rgn, color); + else + { + DrawHLine(rgn.x, rgn.y, rgn.x + rgn.w - 1, color, clipped); + DrawVLine(rgn.x, rgn.y, rgn.y + rgn.h - 1, color, clipped); + DrawHLine(rgn.x, rgn.y + rgn.h - 1, rgn.x + rgn.w - 1, color, clipped); + DrawVLine(rgn.x + rgn.w - 1, rgn.y, rgn.y + rgn.h - 1, color, clipped); + } +} + +void GLVideoDriver::DrawHLine(short x1, short y, short x2, const Color& color, bool /*clipped*/) +{ + Region rgn; + rgn.x = x1; + rgn.y = y; + rgn.h = 1; + rgn.w = x2 - x1; + return drawColoredRect(rgn, color); +} + +void GLVideoDriver::DrawVLine(short x, short y1, short y2, const Color& color, bool /*clipped*/) +{ + Region rgn; + rgn.x = x; + rgn.y = y1; + rgn.w = 1; + rgn.h = y2 - y1; + return drawColoredRect(rgn, color); +} + +void GLVideoDriver::drawColoredRect(const Region& rgn, const Color& color) +{ + if (SDL_ALPHA_TRANSPARENT == color.a) return; + + glScissor(rgn.x, height - rgn.y - rgn.h, rgn.w, rgn.h); + if (SDL_ALPHA_OPAQUE == color.a) // possible to work faster than shader but a lot... may be disable in future + { + glClearColor(color.r/255, color.g/255, color.b/255, color.a/255); + glClear(GL_COLOR_BUFFER_BIT); + } + else + { + useProgram(programRect); + glViewport(rgn.x, height - rgn.y - rgn.h, rgn.w, rgn.h); + GLfloat data[] = + { + -1.0f, 1.0f, (GLfloat)color.r/255, (GLfloat)color.g/255, (GLfloat)color.b/255, (GLfloat)color.a/255, + 1.0f, 1.0f, (GLfloat)color.r/255, (GLfloat)color.g/255, (GLfloat)color.b/255, (GLfloat)color.a/255, + -1.0f, -1.0f, (GLfloat)color.r/255, (GLfloat)color.g/255, (GLfloat)color.b/255, (GLfloat)color.a/255, + 1.0f, -1.0f, (GLfloat)color.r/255, (GLfloat)color.g/255, (GLfloat)color.b/255, (GLfloat)color.a/255 + }; + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + + GLint a_position = glGetAttribLocation(programRect, "a_position"); + GLint a_color = glGetAttribLocation(programRect, "a_color"); + + glVertexAttribPointer(a_position, VERTEX_SIZE, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, 0); + glVertexAttribPointer(a_color, COLOR_SIZE, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, BUFFER_OFFSET(sizeof(GLfloat)*VERTEX_SIZE)); + + glEnableVertexAttribArray(a_position); + glEnableVertexAttribArray(a_color); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(a_color); + glDisableVertexAttribArray(a_position); + + glDeleteBuffers(1, &buffer); + } +} + +int GLVideoDriver::SwapBuffers() +{ + int val = SDLVideoDriver::SwapBuffers(); + SDL_GL_SwapWindow(window); + core->RedrawAll(); + spritesPerFrame = 0; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_SCISSOR_TEST); + + return val; +} + +#include "plugindef.h" + +GEMRB_PLUGIN(0xDBAAB50, "SDL Video Driver") +PLUGIN_DRIVER(GLVideoDriver, "sdl") +END_PLUGIN() diff --git a/gemrb/plugins/SDLVideo/SDL20GLVideo.h b/gemrb/plugins/SDLVideo/SDL20GLVideo.h new file mode 100644 index 0000000000..4cff3380d1 --- /dev/null +++ b/gemrb/plugins/SDLVideo/SDL20GLVideo.h @@ -0,0 +1,54 @@ + +#ifndef GLVideoDRIVER_H +#define GLVideoDRIVER_H + +#include "SDL20Video.h" + +#define BUFFER_OFFSET(i) ((char *)NULL + (i)) +#define VERTEX_SIZE 2 +#define TEX_SIZE 2 +#define COLOR_SIZE 4 + +#define BLIT_EXTERNAL_MASK 0x100 + +namespace GemRB +{ + class GLTextureSprite2D; + + class GLVideoDriver : public SDL20VideoDriver + { + private: + SDL_GLContext context; // opengl context + // Shader programs + GLuint program32; // shader program for 32bpp sprites + GLuint programPal; // shader program for paletted sprites + GLuint programPalGrayed; // shader program for paletted sprites with grayscale effect + GLuint programPalSepia; // shader program for paletted sprites with sepia effect + GLuint programRect; // shader program for drawing rects and lines + + Uint32 spritesPerFrame; // sprites counter + GLuint lastUsedProgram; // stores last used program to prevent switching if possible (switching may cause performance lack) + + void useProgram(GLuint program); // use this instead glUseProgram + void createPrograms(); + void blitSprite(GLTextureSprite2D* spr, int x, int y, const Region* clip, unsigned int flags = 0, const Color* tint = NULL, GLTextureSprite2D* mask = NULL); + void drawColoredRect(const Region& rgn, const Color& color); + + public: + ~GLVideoDriver(); + int SwapBuffers(); + int CreateDisplay(int w, int h, int b, bool fs, const char* title); + bool SupportsBAMSprites() { return false; } + void BlitSprite(const Sprite2D* spr, int x, int y, bool anchor = false, const Region* clip = NULL, Palette* palette = NULL); + void BlitGameSprite(const Sprite2D* spr, int x, int y, unsigned int flags, Color tint, SpriteCover* cover, Palette *palette = NULL, const Region* clip = NULL, bool anchor = false); + void BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, unsigned int flags); + Sprite2D* CreateSprite(int w, int h, int bpp, ieDword rMask, ieDword gMask, ieDword bMask, ieDword aMask, void* pixels, bool cK = false, int index = 0); + Sprite2D* CreateSprite8(int w, int h, void* pixels, Palette* palette, bool cK, int index); + Sprite2D* CreatePalettedSprite(int w, int h, int bpp, void* pixels, Color* palette, bool cK = false, int index = 0); + void DrawRect(const Region& rgn, const Color& color, bool fill = true, bool clipped = false); + void DrawHLine(short x1, short y, short x2, const Color& color, bool clipped = false); + void DrawVLine(short x, short y1, short y2, const Color& color, bool clipped = false); + }; +} + +#endif \ No newline at end of file diff --git a/gemrb/plugins/SDLVideo/SDL20Video.cpp b/gemrb/plugins/SDLVideo/SDL20Video.cpp index 5145e0e3db..cc9572fffc 100644 --- a/gemrb/plugins/SDLVideo/SDL20Video.cpp +++ b/gemrb/plugins/SDLVideo/SDL20Video.cpp @@ -822,8 +822,10 @@ float SDL20VideoDriver::ScaleCoordinateVertical(float y) return y * height; } +/* #include "plugindef.h" GEMRB_PLUGIN(0xDBAAB50, "SDL Video Driver") PLUGIN_DRIVER(SDL20VideoDriver, "sdl") END_PLUGIN() +*/ diff --git a/gemrb/plugins/SDLVideo/SDL20Video.h b/gemrb/plugins/SDLVideo/SDL20Video.h index 6bc4ebe7c9..80558c52f3 100644 --- a/gemrb/plugins/SDLVideo/SDL20Video.h +++ b/gemrb/plugins/SDLVideo/SDL20Video.h @@ -42,15 +42,15 @@ struct MultiGesture { class SDL20VideoDriver : public SDLVideoDriver { private: - SDL_Texture* screenTexture; - SDL_Window* window; - SDL_Renderer* renderer; - // touch input vars int ignoreNextFingerUp; SDL_TouchFingerEvent firstFingerDown; unsigned long firstFingerDownTime; MultiGesture currentGesture; +protected: + SDL_Window* window; + SDL_Texture* screenTexture; + SDL_Renderer* renderer; public: SDL20VideoDriver(void); ~SDL20VideoDriver(void); diff --git a/gemrb/plugins/SDLVideo/SDLVideo.h b/gemrb/plugins/SDLVideo/SDLVideo.h index 23079cde5a..4bf82416bb 100644 --- a/gemrb/plugins/SDLVideo/SDLVideo.h +++ b/gemrb/plugins/SDLVideo/SDLVideo.h @@ -82,25 +82,25 @@ class SDLVideoDriver : public Video { void MouseMovement(int x, int y); void ClickMouse(unsigned int button); void MouseClickEvent(SDL_EventType type, Uint8 button); - Sprite2D* CreateSprite(int w, int h, int bpp, ieDword rMask, + virtual Sprite2D* CreateSprite(int w, int h, int bpp, ieDword rMask, ieDword gMask, ieDword bMask, ieDword aMask, void* pixels, bool cK = false, int index = 0); - Sprite2D* CreateSprite8(int w, int h, void* pixels, + virtual Sprite2D* CreateSprite8(int w, int h, void* pixels, Palette* palette, bool cK, int index); - Sprite2D* CreatePalettedSprite(int w, int h, int bpp, void* pixels, + virtual Sprite2D* CreatePalettedSprite(int w, int h, int bpp, void* pixels, Color* palette, bool cK = false, int index = 0); - bool SupportsBAMSprites() { return true; } - void BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, unsigned int flags); - void BlitSprite(const Sprite2D* spr, int x, int y, bool anchor = false, + virtual bool SupportsBAMSprites() { return true; } + virtual void BlitTile(const Sprite2D* spr, const Sprite2D* mask, int x, int y, const Region* clip, unsigned int flags); + virtual void BlitSprite(const Sprite2D* spr, int x, int y, bool anchor = false, const Region* clip = NULL, Palette* palette = NULL); - void BlitGameSprite(const Sprite2D* spr, int x, int y, + virtual void BlitGameSprite(const Sprite2D* spr, int x, int y, unsigned int flags, Color tint, SpriteCover* cover, Palette *palette = NULL, const Region* clip = NULL, bool anchor = false); Sprite2D* GetScreenshot( Region r ); /** This function Draws the Border of a Rectangle as described by the Region parameter. The Color used to draw the rectangle is passes via the Color parameter. */ - void DrawRect(const Region& rgn, const Color& color, bool fill = true, bool clipped = false); + virtual void DrawRect(const Region& rgn, const Color& color, bool fill = true, bool clipped = false); void DrawRectSprite(const Region& rgn, const Color& color, const Sprite2D* sprite); /** This functions Draws a Circle */ void SetPixel(short x, short y, const Color& color, bool clipped = true); @@ -115,8 +115,8 @@ class SDLVideoDriver : public Video { const Color& color, bool clipped = true); /** This function Draws a Polygon on the Screen */ void DrawPolyline(Gem_Polygon* poly, const Color& color, bool fill = false); - void DrawHLine(short x1, short y, short x2, const Color& color, bool clipped = false); - void DrawVLine(short x, short y1, short y2, const Color& color, bool clipped = false); + virtual void DrawHLine(short x1, short y, short x2, const Color& color, bool clipped = false); + virtual void DrawVLine(short x, short y1, short y2, const Color& color, bool clipped = false); void DrawLine(short x1, short y1, short x2, short y2, const Color& color, bool clipped = false); /** Blits a Sprite filling the Region */ void BlitTiled(Region rgn, const Sprite2D* img, bool anchor = false); diff --git a/gemrb/plugins/SDLVideo/Shader.cpp b/gemrb/plugins/SDLVideo/Shader.cpp new file mode 100644 index 0000000000..d6bad4feaa --- /dev/null +++ b/gemrb/plugins/SDLVideo/Shader.cpp @@ -0,0 +1,64 @@ +#ifdef USE_GL +#include +#else +#include +#include +#endif +#include +#include + +#include "Shader.h" + + +ShaderOperationResult* Shader::BuildShader(GLenum type, const char* source) +{ + ShaderOperationResult* opResult = new ShaderOperationResult(); + GLuint id = glCreateShader(type); + glShaderSource(id, 1, &source, 0); + glCompileShader(id); + opResult->Id = id; + GLint result = GL_FALSE; + glGetShaderiv(id, GL_COMPILE_STATUS, &result); + if (result != GL_TRUE) + { + char tmp[2048]; + glGetShaderInfoLog(id, sizeof(tmp), 0, tmp); + opResult->Id = 0; + //sprintf(tmp, "%s shader compile error: %s", (type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", tmp); + opResult->Message = strdup(tmp); + } + return opResult; +} + +ShaderOperationResult* Shader::BuildProgram(const char* vertexSource, const char* fragmentSource) +{ + GLuint id = glCreateProgram(); + + ShaderOperationResult* vertexShader = BuildShader(GL_VERTEX_SHADER, vertexSource); + if (vertexShader->Id == 0) return vertexShader; + ShaderOperationResult* fragmentShader = BuildShader(GL_FRAGMENT_SHADER, fragmentSource); + if (fragmentShader->Id == 0) return fragmentShader; + + glAttachShader(id, vertexShader->Id); + glAttachShader(id, fragmentShader->Id); + + ShaderOperationResult* opResult = new ShaderOperationResult(); + opResult->Id = id; + + glLinkProgram(id); + GLint result = GL_FALSE; + glGetProgramiv(id, GL_LINK_STATUS, &result); + if (result != GL_TRUE) + { + char tmp[2048]; + opResult->Id = 0; + glGetProgramInfoLog(id, sizeof(tmp), 0, tmp); + tmp[strlen(tmp)]='\0'; + opResult->Message = strdup(tmp); + } + + glDeleteShader(vertexShader->Id); + glDeleteShader(fragmentShader->Id); + + return opResult; +} diff --git a/gemrb/plugins/SDLVideo/Shader.h b/gemrb/plugins/SDLVideo/Shader.h new file mode 100644 index 0000000000..71e2a30775 --- /dev/null +++ b/gemrb/plugins/SDLVideo/Shader.h @@ -0,0 +1,14 @@ + +class ShaderOperationResult +{ +public: + GLuint Id; + char* Message; +}; + +class Shader +{ +public: + static ShaderOperationResult* BuildShader(GLenum type, const char* source); + static ShaderOperationResult* BuildProgram(const char* vertexSource, const char* fragmentSource); +};