scummvm/engines/grim/gfx_opengl.cpp
Einar Johan Trøan Sømåen 33572861c2 GRIM: Fix PixelFormat lookup during Remastered's startup.
Grim Fandango Remastered has an alpha channel in the
video that plays as intro.

This makes Grim Fandango Remastered start, although there
are still quite a few issues to fix.
2020-11-22 22:31:35 +01:00

2293 lines
64 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/endian.h"
#include "common/system.h"
#include "common/config-manager.h"
#if defined(USE_OPENGL_GAME) && !defined(USE_GLES2)
#include "graphics/surface.h"
#include "graphics/pixelbuffer.h"
#include "math/glmath.h"
#include "engines/grim/actor.h"
#include "engines/grim/colormap.h"
#include "engines/grim/font.h"
#include "engines/grim/material.h"
#include "engines/grim/gfx_opengl.h"
#include "engines/grim/grim.h"
#include "engines/grim/bitmap.h"
#include "engines/grim/primitives.h"
#include "engines/grim/sprite.h"
#include "engines/grim/model.h"
#include "engines/grim/set.h"
#include "engines/grim/emi/modelemi.h"
#include "engines/grim/remastered/overlay.h"
#include "engines/grim/registry.h"
#if defined (SDL_BACKEND) && defined(GL_ARB_fragment_program) && !defined(USE_GLEW)
// We need SDL.h for SDL_GL_GetProcAddress.
#include "backends/platform/sdl/sdl-sys.h"
// Extension functions needed for fragment programs.
PFNGLGENPROGRAMSARBPROC glGenProgramsARB;
PFNGLBINDPROGRAMARBPROC glBindProgramARB;
PFNGLPROGRAMSTRINGARBPROC glProgramStringARB;
PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB;
PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB;
#endif
namespace Grim {
GfxBase *CreateGfxOpenGL() {
return new GfxOpenGL();
}
// Simple ARB fragment program that writes the value from a texture to the Z-buffer.
static char fragSrc[] =
"!!ARBfp1.0\n\
TEMP d;\n\
TEX d, fragment.texcoord[0], texture[0], 2D;\n\
MOV result.depth, d.r;\n\
END\n";
static char dimFragSrc[] =
"!!ARBfp1.0\n\
PARAM level = program.local[0];\n\
TEMP color;\n\
TEMP d;\n\
TEX d, fragment.texcoord[0], texture[0], 2D;\n\
TEMP sum;\n\
MOV sum, d.r;\n\
ADD sum, sum, d.g;\n\
ADD sum, sum, d.b;\n\
MUL sum, sum, 0.33;\n\
MUL sum, sum, level.x;\n\
MOV result.color.r, sum;\n\
MOV result.color.g, sum;\n\
MOV result.color.b, sum;\n\
END\n";
GfxOpenGL::GfxOpenGL() : _smushNumTex(0),
_smushTexIds(nullptr), _smushWidth(0), _smushHeight(0),
_useDepthShader(false), _fragmentProgram(0), _useDimShader(0),
_dimFragProgram(0), _maxLights(0), _storedDisplay(nullptr),
_emergFont(0), _alpha(1.f) {
// GL_LEQUAL as glDepthFunc ensures that subsequent drawing attempts for
// the same triangles are not ignored by the depth test.
// That's necessary for EMI where some models have multiple faces which
// refer to the same vertices. The first face is usually using the
// color map and the following are using textures.
_depthFunc = (g_grim->getGameType() == GType_MONKEY4) ? GL_LEQUAL : GL_LESS;
}
GfxOpenGL::~GfxOpenGL() {
releaseMovieFrame();
delete[] _storedDisplay;
if (_emergFont && glIsList(_emergFont))
glDeleteLists(_emergFont, 128);
#ifdef GL_ARB_fragment_program
if (_useDepthShader)
glDeleteProgramsARB(1, &_fragmentProgram);
if (_useDimShader)
glDeleteProgramsARB(1, &_dimFragProgram);
#endif
for (unsigned int i = 0; i < _numSpecialtyTextures; i++) {
destroyTexture(&_specialtyTextures[i]);
}
}
void GfxOpenGL::setupScreen(int screenW, int screenH) {
_screenWidth = screenW;
_screenHeight = screenH;
_scaleW = _screenWidth / (float)_gameWidth;
_scaleH = _screenHeight / (float)_gameHeight;
_globalScaleW = _screenWidth / (float)_globalWidth;
_globalScaleH = _screenHeight / (float)_globalHeight;
_useDepthShader = false;
_useDimShader = false;
g_system->showMouse(false);
int screenSize = _screenWidth * _screenHeight * 4;
_storedDisplay = new byte[screenSize];
memset(_storedDisplay, 0, screenSize);
_smushNumTex = 0;
_currentShadowArray = nullptr;
glViewport(0, 0, _screenWidth, _screenHeight);
GLfloat ambientSource[] = { 0.0f, 0.0f, 0.0f, 1.0f };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientSource);
GLfloat diffuseReflectance[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseReflectance);
if (g_grim->getGameType() == GType_GRIM) {
glPolygonOffset(-6.0, -6.0);
}
initExtensions();
glGetIntegerv(GL_MAX_LIGHTS, &_maxLights);
}
void GfxOpenGL::initExtensions() {
if (!ConfMan.getBool("use_arb_shaders")) {
return;
}
#if defined (SDL_BACKEND) && defined(GL_ARB_fragment_program)
#ifndef USE_GLEW
union {
void *obj_ptr;
void (APIENTRY *func_ptr)();
} u;
// We're casting from an object pointer to a function pointer, the
// sizes need to be the same for this to work.
assert(sizeof(u.obj_ptr) == sizeof(u.func_ptr));
u.obj_ptr = SDL_GL_GetProcAddress("glGenProgramsARB");
glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC)u.func_ptr;
u.obj_ptr = SDL_GL_GetProcAddress("glBindProgramARB");
glBindProgramARB = (PFNGLBINDPROGRAMARBPROC)u.func_ptr;
u.obj_ptr = SDL_GL_GetProcAddress("glProgramStringARB");
glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)u.func_ptr;
u.obj_ptr = SDL_GL_GetProcAddress("glDeleteProgramsARB");
glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)u.func_ptr;
u.obj_ptr = SDL_GL_GetProcAddress("glProgramLocalParameter4fARB");
glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC)u.func_ptr;
#endif
const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
if (extensions && strstr(extensions, "ARB_fragment_program")) {
_useDepthShader = true;
_useDimShader = true;
}
if (_useDepthShader) {
glGenProgramsARB(1, &_fragmentProgram);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _fragmentProgram);
GLint errorPos;
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragSrc), fragSrc);
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
if (errorPos != -1) {
warning("Error compiling depth fragment program:\n%s", glGetString(GL_PROGRAM_ERROR_STRING_ARB));
_useDepthShader = false;
}
glGenProgramsARB(1, &_dimFragProgram);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _dimFragProgram);
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(dimFragSrc), dimFragSrc);
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
if (errorPos != -1) {
warning("Error compiling dim fragment program:\n%s", glGetString(GL_PROGRAM_ERROR_STRING_ARB));
_useDimShader = false;
}
}
#endif
}
const char *GfxOpenGL::getVideoDeviceName() {
return "OpenGL Renderer";
}
void GfxOpenGL::setupCameraFrustum(float fov, float nclip, float fclip) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float right = nclip * tan(fov / 2 * ((float)M_PI / 180));
glFrustum(-right, right, -right * 0.75, right * 0.75, nclip, fclip);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void GfxOpenGL::positionCamera(const Math::Vector3d &pos, const Math::Vector3d &interest, float roll) {
Math::Vector3d up_vec(0, 0, 1);
glRotatef(roll, 0, 0, -1);
if (pos.x() == interest.x() && pos.y() == interest.y())
up_vec = Math::Vector3d(0, 1, 0);
Math::Matrix4 lookMatrix = Math::makeLookAtMatrix(pos, interest, up_vec);
glMultMatrixf(lookMatrix.getData());
glTranslated(-pos.x(), -pos.y(), -pos.z());
}
void GfxOpenGL::positionCamera(const Math::Vector3d &pos, const Math::Matrix4 &rot) {
glScaled(1, 1, -1);
_currentPos = pos;
_currentRot = rot;
}
Math::Matrix4 GfxOpenGL::getModelView() {
Math::Matrix4 modelView;
if (g_grim->getGameType() == GType_MONKEY4) {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMultMatrixf(_currentRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
glGetFloatv(GL_MODELVIEW_MATRIX, modelView.getData());
glPopMatrix();
} else {
glGetFloatv(GL_MODELVIEW_MATRIX, modelView.getData());
}
modelView.transpose();
return modelView;
}
Math::Matrix4 GfxOpenGL::getProjection() {
Math::Matrix4 projection;
glGetFloatv(GL_PROJECTION_MATRIX, projection.getData());
projection.transpose();
return projection;
}
void GfxOpenGL::clearScreen() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void GfxOpenGL::clearDepthBuffer() {
glClear(GL_DEPTH_BUFFER_BIT);
}
void GfxOpenGL::flipBuffer() {
g_system->updateScreen();
}
bool GfxOpenGL::isHardwareAccelerated() {
return true;
}
bool GfxOpenGL::supportsShaders() {
return false;
}
static void glShadowProjection(const Math::Vector3d &light, const Math::Vector3d &plane, const Math::Vector3d &normal, bool dontNegate) {
// Based on GPL shadow projection example by
// (c) 2002-2003 Phaetos <phaetos@gaffga.de>
float d, c;
float mat[16];
float nx, ny, nz, lx, ly, lz, px, py, pz;
nx = normal.x();
ny = normal.y();
nz = normal.z();
// for some unknown for me reason normal need negation
if (!dontNegate) {
nx = -nx;
ny = -ny;
nz = -nz;
}
lx = light.x();
ly = light.y();
lz = light.z();
px = plane.x();
py = plane.y();
pz = plane.z();
d = nx * lx + ny * ly + nz * lz;
c = px * nx + py * ny + pz * nz - d;
mat[0] = lx * nx + c;
mat[4] = ny * lx;
mat[8] = nz * lx;
mat[12] = -lx * c - lx * d;
mat[1] = nx * ly;
mat[5] = ly * ny + c;
mat[9] = nz * ly;
mat[13] = -ly * c - ly * d;
mat[2] = nx * lz;
mat[6] = ny * lz;
mat[10] = lz * nz + c;
mat[14] = -lz * c - lz * d;
mat[3] = nx;
mat[7] = ny;
mat[11] = nz;
mat[15] = -d;
glMultMatrixf((GLfloat *)mat);
}
void GfxOpenGL::getScreenBoundingBox(const Mesh *model, int *x1, int *y1, int *x2, int *y2) {
if (_currentShadowArray) {
*x1 = -1;
*y1 = -1;
*x2 = -1;
*y2 = -1;
return;
}
GLdouble top = 1000;
GLdouble right = -1000;
GLdouble left = 1000;
GLdouble bottom = -1000;
for (int i = 0; i < model->_numFaces; i++) {
Math::Vector3d obj;
float *pVertices;
for (int j = 0; j < model->_faces[i].getNumVertices(); j++) {
GLdouble modelView[16], projection[16];
GLint viewPort[4];
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewPort);
pVertices = model->_vertices + 3 * model->_faces[i].getVertex(j);
obj.set(*(pVertices), *(pVertices + 1), *(pVertices + 2));
Math::Vector3d win;
Math::gluMathProject<GLdouble, GLint>(obj, modelView, projection, viewPort, win);
if (win.x() > right)
right = win.x();
if (win.x() < left)
left = win.x();
if (win.y() < top)
top = win.y();
if (win.y() > bottom)
bottom = win.y();
}
}
double t = bottom;
bottom = _gameHeight - top;
top = _gameHeight - t;
if (left < 0)
left = 0;
if (right >= _gameWidth)
right = _gameWidth - 1;
if (top < 0)
top = 0;
if (bottom >= _gameHeight)
bottom = _gameHeight - 1;
if (top >= _gameHeight || left >= _gameWidth || bottom < 0 || right < 0) {
*x1 = -1;
*y1 = -1;
*x2 = -1;
*y2 = -1;
return;
}
*x1 = (int)left;
*y1 = (int)top;
*x2 = (int)right;
*y2 = (int)bottom;
}
void GfxOpenGL::getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, int *x2, int *y2) {
if (_currentShadowArray) {
*x1 = -1;
*y1 = -1;
*x2 = -1;
*y2 = -1;
return;
}
GLdouble top = 1000;
GLdouble right = -1000;
GLdouble left = 1000;
GLdouble bottom = -1000;
GLdouble modelView[16], projection[16];
GLint viewPort[4];
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewPort);
for (uint i = 0; i < model->_numFaces; i++) {
uint16 *indices = (uint16 *)model->_faces[i]._indexes;
for (uint j = 0; j < model->_faces[i]._faceLength * 3; j++) {
uint16 index = indices[j];
Math::Vector3d obj = model->_drawVertices[index];
Math::Vector3d win;
Math::gluMathProject<GLdouble, GLint>(obj, modelView, projection, viewPort, win);
if (win.x() > right)
right = win.x();
if (win.x() < left)
left = win.x();
if (win.y() < top)
top = win.y();
if (win.y() > bottom)
bottom = win.y();
}
}
double t = bottom;
bottom = _gameHeight - top;
top = _gameHeight - t;
if (left < 0)
left = 0;
if (right >= _gameWidth)
right = _gameWidth - 1;
if (top < 0)
top = 0;
if (bottom >= _gameHeight)
bottom = _gameHeight - 1;
if (top >= _gameHeight || left >= _gameWidth || bottom < 0 || right < 0) {
*x1 = -1;
*y1 = -1;
*x2 = -1;
*y2 = -1;
return;
}
*x1 = (int)left;
*y1 = (int)top;
*x2 = (int)right;
*y2 = (int)bottom;
}
void GfxOpenGL::getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) {
// Get the actor's bounding box information (describes a 3D box)
Math::Vector3d bboxPos, bboxSize;
actor->getBBoxInfo(bboxPos, bboxSize);
// Translate the bounding box to the actor's position
Math::Matrix4 m = actor->getFinalMatrix();
bboxPos = bboxPos + actor->getWorldPos();
// Set up the camera coordinate system
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
Math::Matrix4 worldRot = _currentRot;
glMultMatrixf(worldRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
// Get the current OpenGL state
GLdouble modelView[16], projection[16];
GLint viewPort[4];
glGetDoublev(GL_MODELVIEW_MATRIX, modelView);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewPort);
// Set values outside of the screen range
p1.x = 1000;
p1.y = 1000;
p2.x = -1000;
p2.y = -1000;
// Project all of the points in the 3D bounding box
Math::Vector3d p, projected;
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
for (int z = 0; z < 2; z++) {
Math::Vector3d added(bboxSize.x() * 0.5f * (x * 2 - 1), bboxSize.y() * 0.5f * (y * 2 - 1), bboxSize.z() * 0.5f * (z * 2 - 1));
m.transform(&added, false);
p = bboxPos + added;
Math::gluMathProject<GLdouble, GLint>(p, modelView, projection, viewPort, projected);
// Find the points
if (projected.x() < p1.x)
p1.x = projected.x();
if (projected.y() < p1.y)
p1.y = projected.y();
if (projected.x() > p2.x)
p2.x = projected.x();
if (projected.y() > p2.y)
p2.y = projected.y();
}
}
}
// Swap the p1/p2 y coorindates
int16 tmp = p1.y;
p1.y = 480 - p2.y;
p2.y = 480 - tmp;
// Restore the state
glPopMatrix();
}
void GfxOpenGL::startActorDraw(const Actor *actor) {
_currentActor = actor;
glEnable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4 && !actor->isInOverworld()) {
// Apply the view transform.
glMultMatrixf(_currentRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
}
if (_currentShadowArray) {
// TODO find out why shadowMask at device in woods is null
if (!_currentShadowArray->shadowMask) {
_currentShadowArray->shadowMask = new byte[_screenWidth * _screenHeight];
_currentShadowArray->shadowMaskSize = _screenWidth * _screenHeight;
}
Sector *shadowSector = _currentShadowArray->planeList.front().sector;
glDepthMask(GL_FALSE);
glEnable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
// glColor3f(0.0f, 1.0f, 0.0f); // debug draw color
if (g_grim->getGameType() == GType_GRIM) {
glColor3ub(_shadowColorR, _shadowColorG, _shadowColorB);
} else {
glColor3ub(_currentShadowArray->color.getRed(), _currentShadowArray->color.getGreen(), _currentShadowArray->color.getBlue());
}
glShadowProjection(_currentShadowArray->pos, shadowSector->getVertices()[0], shadowSector->getNormal(), _currentShadowArray->dontNegate);
}
const float alpha = actor->getEffectiveAlpha();
if (alpha < 1.f) {
_alpha = alpha;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
if (g_grim->getGameType() == GType_MONKEY4) {
glEnable(GL_CULL_FACE);
glFrontFace(GL_CW);
if (actor->isInOverworld()) {
const Math::Vector3d &pos = actor->getWorldPos();
const Math::Quaternion &quat = actor->getRotationQuat();
// At distance 3.2, a 6.4x4.8 actor fills the screen.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float right = 1;
float top = right * 0.75;
float div = 6.0f;
glFrustum(-right / div, right / div, -top / div, top / div, 1.0f / div, 3276.8f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glScalef(1.0, 1.0, -1.0);
glTranslatef(pos.x(), pos.y(), pos.z());
glMultMatrixf(quat.toMatrix().getData());
} else {
Math::Matrix4 m = actor->getFinalMatrix();
m.transpose();
glMultMatrixf(m.getData());
}
} else {
// Grim
Math::Vector3d pos = actor->getWorldPos();
const Math::Quaternion &quat = actor->getRotationQuat();
const float &scale = actor->getScale();
glTranslatef(pos.x(), pos.y(), pos.z());
glScalef(scale, scale, scale);
glMultMatrixf(quat.toMatrix().getData());
}
}
void GfxOpenGL::finishActorDraw() {
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glDisable(GL_TEXTURE_2D);
if (_alpha < 1.f) {
glDisable(GL_BLEND);
_alpha = 1.f;
}
if (_currentShadowArray) {
glEnable(GL_LIGHTING);
glColor3f(1.0f, 1.0f, 1.0f);
glDisable(GL_POLYGON_OFFSET_FILL);
}
if (g_grim->getGameType() == GType_MONKEY4) {
glDisable(GL_CULL_FACE);
}
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
_currentActor = nullptr;
}
void GfxOpenGL::setShadow(Shadow *shadow) {
_currentShadowArray = shadow;
}
void GfxOpenGL::drawShadowPlanes() {
/* glColor3f(1.0f, 1.0f, 1.0f);
_currentShadowArray->planeList.begin();
for (SectorListType::iterator i = _currentShadowArray->planeList.begin(); i != _currentShadowArray->planeList.end(); i++) {
Sector *shadowSector = i->sector;
glBegin(GL_POLYGON);
for (int k = 0; k < shadowSector->getNumVertices(); k++) {
glVertex3f(shadowSector->getVertices()[k].x(), shadowSector->getVertices()[k].y(), shadowSector->getVertices()[k].z());
}
glEnd();
}
*/
glPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4) {
// Apply the view transform.
glMultMatrixf(_currentRot.getData());
glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z());
}
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glClearStencil(~0);
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, (GLuint)~0);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glColor4f(1, 1, 1, 1);
for (SectorListType::iterator i = _currentShadowArray->planeList.begin(); i != _currentShadowArray->planeList.end(); ++i) {
Sector *shadowSector = i->sector;
glBegin(GL_POLYGON);
for (int k = 0; k < shadowSector->getNumVertices(); k++) {
glVertex3f(shadowSector->getVertices()[k].x(), shadowSector->getVertices()[k].y(), shadowSector->getVertices()[k].z());
}
glEnd();
}
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_EQUAL, 1, (GLuint)~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glPopMatrix();
}
void GfxOpenGL::setShadowMode() {
GfxBase::setShadowMode();
}
void GfxOpenGL::clearShadowMode() {
GfxBase::clearShadowMode();
glDisable(GL_STENCIL_TEST);
glDepthMask(GL_TRUE);
}
void GfxOpenGL::setShadowColor(byte r, byte g, byte b) {
_shadowColorR = r;
_shadowColorG = g;
_shadowColorB = b;
}
void GfxOpenGL::getShadowColor(byte *r, byte *g, byte *b) {
*r = _shadowColorR;
*g = _shadowColorG;
*b = _shadowColorB;
}
void GfxOpenGL::set3DMode() {
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glDepthFunc(_depthFunc);
}
void GfxOpenGL::drawEMIModelFace(const EMIModel *model, const EMIMeshFace *face) {
uint16 *indices = (uint16 *)face->_indexes;
glEnable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_LIGHTING);
if (!_currentShadowArray && face->_hasTexture)
glEnable(GL_TEXTURE_2D);
else
glDisable(GL_TEXTURE_2D);
if (face->_flags & EMIMeshFace::kAlphaBlend || face->_flags & EMIMeshFace::kUnknownBlend || _currentActor->hasLocalAlpha() || _alpha < 1.0f)
glEnable(GL_BLEND);
glBegin(GL_TRIANGLES);
float alpha = _alpha;
if (model->_meshAlphaMode == Actor::AlphaReplace) {
alpha *= model->_meshAlpha;
}
Math::Vector3d noLighting(1.f, 1.f, 1.f);
for (uint j = 0; j < face->_faceLength * 3; j++) {
uint16 index = indices[j];
if (!_currentShadowArray) {
if (face->_hasTexture) {
glTexCoord2f(model->_texVerts[index].getX(), model->_texVerts[index].getY());
}
Math::Vector3d lighting = (face->_flags & EMIMeshFace::kNoLighting) ? noLighting : model->_lighting[index];
byte r = (byte)(model->_colorMap[index].r * lighting.x());
byte g = (byte)(model->_colorMap[index].g * lighting.y());
byte b = (byte)(model->_colorMap[index].b * lighting.z());
byte a = (int)(alpha * (model->_meshAlphaMode == Actor::AlphaReplace ? model->_colorMap[index].a * _currentActor->getLocalAlpha(index) : 255.f));
glColor4ub(r, g, b, a);
}
Math::Vector3d normal = model->_normals[index];
Math::Vector3d vertex = model->_drawVertices[index];
glNormal3fv(normal.getData());
glVertex3fv(vertex.getData());
}
glEnd();
if (!_currentShadowArray) {
glColor3f(1.0f, 1.0f, 1.0f);
}
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_ALPHA_TEST);
glEnable(GL_LIGHTING);
glDisable(GL_BLEND);
if (!_currentShadowArray)
glDepthMask(GL_TRUE);
}
void GfxOpenGL::drawModelFace(const Mesh *mesh, const MeshFace *face) {
// Support transparency in actor objects, such as the message tube
// in Manny's Office
float *vertices = mesh->_vertices;
float *vertNormals = mesh->_vertNormals;
float *textureVerts = mesh->_textureVerts;
glAlphaFunc(GL_GREATER, 0.5);
glEnable(GL_ALPHA_TEST);
glNormal3fv(face->getNormal().getData());
glBegin(GL_POLYGON);
for (int i = 0; i < face->getNumVertices(); i++) {
glNormal3fv(vertNormals + 3 * face->getVertex(i));
if (face->hasTexture())
glTexCoord2fv(textureVerts + 2 * face->getTextureVertex(i));
glVertex3fv(vertices + 3 * face->getVertex(i));
}
glEnd();
// Done with transparency-capable objects
glDisable(GL_ALPHA_TEST);
}
void GfxOpenGL::drawSprite(const Sprite *sprite) {
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
if (g_grim->getGameType() == GType_MONKEY4) {
GLdouble modelview[16];
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
Math::Matrix4 act;
act.buildAroundZ(_currentActor->getYaw());
act.transpose();
act(3, 0) = modelview[12];
act(3, 1) = modelview[13];
act(3, 2) = modelview[14];
glLoadMatrixf(act.getData());
glTranslatef(sprite->_pos.x(), sprite->_pos.y(), -sprite->_pos.z());
} else {
glTranslatef(sprite->_pos.x(), sprite->_pos.y(), sprite->_pos.z());
GLdouble modelview[16];
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
// We want screen-aligned sprites so reset the rotation part of the matrix.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) {
modelview[i * 4 + j] = 1.0f;
} else {
modelview[i * 4 + j] = 0.0f;
}
}
}
glLoadMatrixd(modelview);
}
if (sprite->_flags1 & Sprite::BlendAdditive) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
} else {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
glDisable(GL_LIGHTING);
if (g_grim->getGameType() == GType_GRIM) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 0.5f);
} else if (sprite->_flags2 & Sprite::AlphaTest) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 0.1f);
} else {
glDisable(GL_ALPHA_TEST);
}
if (sprite->_flags2 & Sprite::DepthTest) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
if (g_grim->getGameType() == GType_MONKEY4) {
glDepthMask(GL_TRUE);
float halfWidth = sprite->_width / 2;
float halfHeight = sprite->_height / 2;
float vertexX[] = { -1.0f, 1.0f, 1.0f, -1.0f };
float vertexY[] = { 1.0f, 1.0f, -1.0f, -1.0f };
glBegin(GL_POLYGON);
for (int i = 0; i < 4; ++i) {
float r = sprite->_red[i] / 255.0f;
float g = sprite->_green[i] / 255.0f;
float b = sprite->_blue[i] / 255.0f;
float a = sprite->_alpha[i] * _alpha / 255.0f;
glColor4f(r, g, b, a);
glTexCoord2f(sprite->_texCoordX[i], sprite->_texCoordY[i]);
glVertex3f(vertexX[i] * halfWidth, vertexY[i] * halfHeight, 0.0f);
}
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glEnd();
} else {
// In Grim, the bottom edge of the sprite is at y=0 and
// the texture is flipped along the X-axis.
float halfWidth = sprite->_width / 2;
float height = sprite->_height;
glBegin(GL_POLYGON);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(+halfWidth, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(+halfWidth, +height, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-halfWidth, +height, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-halfWidth, 0.0f, 0.0f);
glEnd();
}
glEnable(GL_LIGHTING);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_TRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glPopMatrix();
}
void GfxOpenGL::drawOverlay(const Overlay *overlay) {
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 0.5f);
glDisable(GL_DEPTH_TEST);
float height = overlay->getHeight() * _globalScaleH;
float width = overlay->getWidth() * _globalScaleW;
float x = overlay->_x * _globalScaleW;
float y = overlay->_y * _globalScaleH;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(x, y);
glTexCoord2f(1.0f, 0.0f);
glVertex2f((x + width), y);
glTexCoord2f(1.0f, 1.0f);
glVertex2f((x + width), (y + height));
glTexCoord2f(0.0f, 1.0f);
glVertex2f(x, (y + height));
glEnd();
glEnable(GL_LIGHTING);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_TRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glPopMatrix();
}
void GfxOpenGL::translateViewpointStart() {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
}
void GfxOpenGL::translateViewpoint(const Math::Vector3d &vec) {
glTranslatef(vec.x(), vec.y(), vec.z());
}
void GfxOpenGL::rotateViewpoint(const Math::Angle &angle, const Math::Vector3d &axis) {
glRotatef(angle.getDegrees(), axis.x(), axis.y(), axis.z());
}
void GfxOpenGL::rotateViewpoint(const Math::Matrix4 &rot) {
glMultMatrixf(rot.getData());
}
void GfxOpenGL::translateViewpointFinish() {
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void GfxOpenGL::enableLights() {
if (!isShadowModeActive()) {
glEnable(GL_LIGHTING);
}
}
void GfxOpenGL::disableLights() {
glDisable(GL_LIGHTING);
}
void GfxOpenGL::setupLight(Light *light, int lightId) {
if (lightId >= _maxLights) {
return;
}
glEnable(GL_LIGHTING);
GLfloat lightColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat lightPos[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat lightDir[] = { 0.0f, 0.0f, -1.0f };
GLfloat cutoff = 180.0f;
GLfloat spot_exp = 0.0f;
GLfloat q_attenuation = 1.0f;
GLfloat intensity = light->_scaledintensity;
lightColor[0] = (GLfloat)light->_color.getRed() * intensity;
lightColor[1] = (GLfloat)light->_color.getGreen() * intensity;
lightColor[2] = (GLfloat)light->_color.getBlue() * intensity;
if (light->_type == Light::Omni) {
lightPos[0] = light->_pos.x();
lightPos[1] = light->_pos.y();
lightPos[2] = light->_pos.z();
} else if (light->_type == Light::Direct) {
lightPos[0] = -light->_dir.x();
lightPos[1] = -light->_dir.y();
lightPos[2] = -light->_dir.z();
lightPos[3] = 0;
} else if (light->_type == Light::Spot) {
lightPos[0] = light->_pos.x();
lightPos[1] = light->_pos.y();
lightPos[2] = light->_pos.z();
lightDir[0] = light->_dir.x();
lightDir[1] = light->_dir.y();
lightDir[2] = light->_dir.z();
spot_exp = 2.0f;
cutoff = light->_penumbraangle;
q_attenuation = 0.0f;
}
glDisable(GL_LIGHT0 + lightId);
glLightfv(GL_LIGHT0 + lightId, GL_DIFFUSE, lightColor);
glLightfv(GL_LIGHT0 + lightId, GL_POSITION, lightPos);
glLightfv(GL_LIGHT0 + lightId, GL_SPOT_DIRECTION, lightDir);
glLightf(GL_LIGHT0 + lightId, GL_SPOT_EXPONENT, spot_exp);
glLightf(GL_LIGHT0 + lightId, GL_SPOT_CUTOFF, cutoff);
glLightf(GL_LIGHT0 + lightId, GL_QUADRATIC_ATTENUATION, q_attenuation);
glEnable(GL_LIGHT0 + lightId);
}
void GfxOpenGL::turnOffLight(int lightId) {
glDisable(GL_LIGHT0 + lightId);
}
#define BITMAP_TEXTURE_SIZE 256
void GfxOpenGL::createBitmap(BitmapData *bitmap) {
GLuint *textures;
if (bitmap->_format != 1) {
for (int pic = 0; pic < bitmap->_numImages; pic++) {
uint16 *zbufPtr = reinterpret_cast<uint16 *>(bitmap->getImageData(pic).getRawBuffer());
for (int i = 0; i < (bitmap->_width * bitmap->_height); i++) {
uint16 val = READ_LE_UINT16(zbufPtr + i);
// fix the value if it is incorrectly set to the bitmap transparency color
if (val == 0xf81f) {
val = 0;
}
zbufPtr[i] = 0xffff - ((uint32)val) * 0x10000 / 100 / (0x10000 - val);
}
// Flip the zbuffer image to match what GL expects
if (!_useDepthShader) {
for (int y = 0; y < bitmap->_height / 2; y++) {
uint16 *ptr1 = zbufPtr + y * bitmap->_width;
uint16 *ptr2 = zbufPtr + (bitmap->_height - 1 - y) * bitmap->_width;
for (int x = 0; x < bitmap->_width; x++, ptr1++, ptr2++) {
uint16 tmp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = tmp;
}
}
}
}
}
if (bitmap->_format == 1 || _useDepthShader) {
bitmap->_hasTransparency = false;
bitmap->_numTex = ((bitmap->_width + (BITMAP_TEXTURE_SIZE - 1)) / BITMAP_TEXTURE_SIZE) *
((bitmap->_height + (BITMAP_TEXTURE_SIZE - 1)) / BITMAP_TEXTURE_SIZE);
bitmap->_texIds = new GLuint[bitmap->_numTex * bitmap->_numImages];
textures = (GLuint *)bitmap->_texIds;
glGenTextures(bitmap->_numTex * bitmap->_numImages, textures);
byte *texData = nullptr;
byte *texOut = nullptr;
GLint format = GL_RGBA;
GLint type = GL_UNSIGNED_BYTE;
int bytes = 4;
if (bitmap->_format != 1) {
format = GL_DEPTH_COMPONENT;
type = GL_UNSIGNED_SHORT;
bytes = 2;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, bytes);
glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap->_width);
for (int pic = 0; pic < bitmap->_numImages; pic++) {
if (bitmap->_format == 1 && bitmap->_bpp == 16 && bitmap->_colorFormat != BM_RGB1555) {
if (texData == nullptr)
texData = new byte[bytes * bitmap->_width * bitmap->_height];
// Convert data to 32-bit RGBA format
byte *texDataPtr = texData;
uint16 *bitmapData = reinterpret_cast<uint16 *>(bitmap->getImageData(pic).getRawBuffer());
for (int i = 0; i < bitmap->_width * bitmap->_height; i++, texDataPtr += bytes, bitmapData++) {
uint16 pixel = *bitmapData;
int r = pixel >> 11;
texDataPtr[0] = (r << 3) | (r >> 2);
int g = (pixel >> 5) & 0x3f;
texDataPtr[1] = (g << 2) | (g >> 4);
int b = pixel & 0x1f;
texDataPtr[2] = (b << 3) | (b >> 2);
if (pixel == 0xf81f) { // transparent
texDataPtr[3] = 0;
bitmap->_hasTransparency = true;
} else {
texDataPtr[3] = 255;
}
}
texOut = texData;
} else if (bitmap->_format == 1 && bitmap->_colorFormat == BM_RGB1555) {
bitmap->convertToColorFormat(pic, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
texOut = (byte *)bitmap->getImageData(pic).getRawBuffer();
} else {
texOut = (byte *)bitmap->getImageData(pic).getRawBuffer();
}
for (int i = 0; i < bitmap->_numTex; i++) {
glBindTexture(GL_TEXTURE_2D, textures[bitmap->_numTex * pic + i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, format, BITMAP_TEXTURE_SIZE, BITMAP_TEXTURE_SIZE, 0, format, type, nullptr);
}
int cur_tex_idx = bitmap->_numTex * pic;
for (int y = 0; y < bitmap->_height; y += BITMAP_TEXTURE_SIZE) {
for (int x = 0; x < bitmap->_width; x += BITMAP_TEXTURE_SIZE) {
int width = (x + BITMAP_TEXTURE_SIZE >= bitmap->_width) ? (bitmap->_width - x) : BITMAP_TEXTURE_SIZE;
int height = (y + BITMAP_TEXTURE_SIZE >= bitmap->_height) ? (bitmap->_height - y) : BITMAP_TEXTURE_SIZE;
glBindTexture(GL_TEXTURE_2D, textures[cur_tex_idx]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type,
texOut + (y * bytes * bitmap->_width) + (bytes * x));
cur_tex_idx++;
}
}
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
delete[] texData;
bitmap->freeData();
}
}
void GfxOpenGL::drawBitmap(const Bitmap *bitmap, int dx, int dy, uint32 layer) {
// The PS2 version of EMI uses a TGA for it's splash-screen
// avoid using the TIL-code below for that, by checking
// for texture coordinates set by BitmapData::loadTile
if (g_grim->getGameType() == GType_MONKEY4 && bitmap->_data && bitmap->_data->_texc) {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(-1, 1, -1, 1, 0, 1);
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glColor3f(1.0f, 1.0f, 1.0f);
BitmapData *data = bitmap->_data;
GLuint *textures = (GLuint *)bitmap->getTexIds();
float *texc = data->_texc;
assert(layer < data->_numLayers);
uint32 offset = data->_layers[layer]._offset;
for (uint32 i = offset; i < offset + data->_layers[layer]._numImages; ++i) {
glBindTexture(GL_TEXTURE_2D, textures[data->_verts[i]._texid]);
glBegin(GL_QUADS);
uint32 ntex = data->_verts[i]._pos * 4;
for (uint32 x = 0; x < data->_verts[i]._verts; ++x) {
glTexCoord2f(texc[ntex + 2], texc[ntex + 3]);
glVertex2f(texc[ntex + 0], texc[ntex + 1]);
ntex += 4;
}
glEnd();
}
glColor3f(1.0f, 1.0f, 1.0f);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
return;
}
int format = bitmap->getFormat();
if ((format == 1 && !_renderBitmaps) || (format == 5 && !_renderZBitmaps)) {
return;
}
GLuint *textures;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
// A lot more may need to be put there : disabling Alpha test, blending, ...
// For now, just keep this here :-)
if (bitmap->getFormat() == 1 && bitmap->getHasTransparency()) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
}
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
// If drawing a Z-buffer image, but no shaders are available, fall back to the glDrawPixels method.
if (bitmap->getFormat() == 5 && !_useDepthShader) {
// Only draw the manual zbuffer when enabled
if (bitmap->getActiveImage() - 1 < bitmap->getNumImages()) {
drawDepthBitmap(dx, dy, bitmap->getWidth(), bitmap->getHeight(), (char *)bitmap->getData(bitmap->getActiveImage() - 1).getRawBuffer());
} else {
warning("zbuffer image has index out of bounds! %d/%d", bitmap->getActiveImage(), bitmap->getNumImages());
}
glEnable(GL_LIGHTING);
return;
}
if (bitmap->getFormat() == 1) { // Normal image
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
} else { // ZBuffer image
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_TRUE);
#ifdef GL_ARB_fragment_program
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _fragmentProgram);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
#endif
}
glEnable(GL_SCISSOR_TEST);
glScissor((int)(dx * _scaleW), _screenHeight - (int)(((dy + bitmap->getHeight())) * _scaleH), (int)(bitmap->getWidth() * _scaleW), (int)(bitmap->getHeight() * _scaleH));
int cur_tex_idx = bitmap->getNumTex() * (bitmap->getActiveImage() - 1);
for (int y = dy; y < (dy + bitmap->getHeight()); y += BITMAP_TEXTURE_SIZE) {
for (int x = dx; x < (dx + bitmap->getWidth()); x += BITMAP_TEXTURE_SIZE) {
textures = (GLuint *)bitmap->getTexIds();
glBindTexture(GL_TEXTURE_2D, textures[cur_tex_idx]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(x * _scaleW, y * _scaleH);
glTexCoord2f(1.0f, 0.0f);
glVertex2f((x + BITMAP_TEXTURE_SIZE) * _scaleW, y * _scaleH);
glTexCoord2f(1.0f, 1.0f);
glVertex2f((x + BITMAP_TEXTURE_SIZE) * _scaleW, (y + BITMAP_TEXTURE_SIZE) * _scaleH);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(x * _scaleW, (y + BITMAP_TEXTURE_SIZE) * _scaleH);
glEnd();
cur_tex_idx++;
}
}
glDisable(GL_SCISSOR_TEST);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
if (bitmap->getFormat() == 1) {
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
} else {
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthFunc(_depthFunc);
#ifdef GL_ARB_fragment_program
glDisable(GL_FRAGMENT_PROGRAM_ARB);
#endif
}
glEnable(GL_LIGHTING);
}
void GfxOpenGL::destroyBitmap(BitmapData *bitmap) {
GLuint *textures = (GLuint *)bitmap->_texIds;
if (textures) {
glDeleteTextures(bitmap->_numTex * bitmap->_numImages, textures);
delete[] textures;
bitmap->_texIds = nullptr;
}
}
struct FontUserData {
int size;
GLuint texture;
};
void GfxOpenGL::createFont(Font *font) {
const byte *bitmapData = font->getFontData();
uint dataSize = font->getDataSize();
uint8 bpp = 4;
uint8 charsWide = 16;
uint8 charsHigh = 16;
byte *texDataPtr = new byte[dataSize * bpp];
byte *data = texDataPtr;
for (uint i = 0; i < dataSize; i++, texDataPtr += bpp, bitmapData++) {
byte pixel = *bitmapData;
if (pixel == 0x00) {
texDataPtr[0] = texDataPtr[1] = texDataPtr[2] = texDataPtr[3] = 0;
} else if (pixel == 0x80) {
texDataPtr[0] = texDataPtr[1] = texDataPtr[2] = 0;
texDataPtr[3] = 255;
} else if (pixel == 0xFF) {
texDataPtr[0] = texDataPtr[1] = texDataPtr[2] = texDataPtr[3] = 255;
}
}
int size = 0;
for (int i = 0; i < 256; ++i) {
int width = font->getCharBitmapWidth(i), height = font->getCharBitmapHeight(i);
int m = MAX(width, height);
if (m > size)
size = m;
}
assert(size < 64);
if (size < 8)
size = 8;
if (size < 16)
size = 16;
else if (size < 32)
size = 32;
else if (size < 64)
size = 64;
uint arraySize = size * size * bpp * charsWide * charsHigh;
byte *temp = new byte[arraySize];
if (!temp)
error("Could not allocate %d bytes", arraySize);
memset(temp, 0, arraySize);
FontUserData *userData = new FontUserData;
font->setUserData(userData);
userData->texture = 0;
userData->size = size;
GLuint *texture = &(userData->texture);
glGenTextures(1, texture);
for (int i = 0, row = 0; i < 256; ++i) {
int width = font->getCharBitmapWidth(i), height = font->getCharBitmapHeight(i);
int32 d = font->getCharOffset(i);
for (int x = 0; x < height; ++x) {
// a is the offset to get to the correct row.
// b is the offset to get to the correct line in the character.
// c is the offset of the character from the start of the row.
uint a = row * size * size * bpp * charsHigh;
uint b = x * size * charsWide * bpp;
uint c = 0;
if (i != 0)
c = ((i - 1) % 16) * size * bpp;
uint pos = a + b + c;
uint pos2 = d * bpp + x * width * bpp;
assert(pos + width * bpp <= arraySize);
assert(pos2 + width * bpp <= dataSize * bpp);
memcpy(temp + pos, data + pos2, width * bpp);
}
if (i != 0 && i % charsWide == 0)
++row;
}
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size * charsWide, size * charsHigh, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
delete[] data;
delete[] temp;
}
void GfxOpenGL::destroyFont(Font *font) {
const FontUserData *data = (const FontUserData *)font->getUserData();
if (data) {
glDeleteTextures(1, &(data->texture));
delete data;
}
}
struct TextObjectUserData {
GLuint *_texids;
};
void GfxOpenGL::createTextObject(TextObject *text) {
if (g_grim->getGameType() != GType_GRIM || !(g_grim->getGameFlags() & ADGF_REMASTERED))
return;
#ifdef USE_FREETYPE2
//error("Could not get font userdata");
const Font *font = text->getFont();
const FontTTF *f = static_cast<const FontTTF *>(font);
Graphics::Font *gf = f->_font;
int numLines = text->getNumLines();
GLuint *texids = new GLuint[numLines];
glGenTextures(numLines, texids);
// Not at all correct for line-wrapping, but atleast we get all the lines now.
for (int i = 0; i < numLines; i++) {
Graphics::Surface surface;
int width = gf->getStringWidth(text->getLines()[i]);
int height = width;
surface.create(height, width, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
gf->drawString(&surface, text->getLines()[i], 0, 0, width, 0xFFFFFFFF);
byte *bitmap = (byte *)surface.getPixels();
glBindTexture(GL_TEXTURE_2D, texids[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
surface.free();
}
TextObjectUserData *ud = new TextObjectUserData;
ud->_texids = texids;
text->setUserData(ud);
#endif
}
void GfxOpenGL::drawTextObject(const TextObject *text) {
if (!text)
return;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glDepthMask(GL_FALSE);
const Color &color = text->getFGColor();
const Font *font = text->getFont();
glColor3ub(color.getRed(), color.getGreen(), color.getBlue());
const FontUserData *userData = (const FontUserData *)font->getUserData();
if (!userData) {
if (g_grim->getGameType() != GType_GRIM || !(g_grim->getGameFlags() & ADGF_REMASTERED))
error("Could not get font userdata");
#ifdef USE_FREETYPE2
const FontTTF *f = static_cast<const FontTTF *>(font);
Graphics::Font *gf = f->_font;
const TextObjectUserData *ud = (const TextObjectUserData *)text->getUserData();
int numLines = text->getNumLines();
for (int i = 0; i < numLines; ++i) {
float width = gf->getStringWidth(text->getLines()[i]);
float height = width;
float x = text->getLineX(i);
float y = text->getLineY(i);
if (text->getCoords() == 2 || text->getCoords() == 1) {
x *= _globalScaleW;
y *= _globalScaleH;
width *= _globalScaleW;
height *= _globalScaleH;
} else if (text->getCoords() == 0) {
x *= _scaleW;
y *= _scaleH;
width *= _scaleW;
height *= _scaleH;
}
glBindTexture(GL_TEXTURE_2D, ud->_texids[i]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(x, y);
glTexCoord2f(1.0f, 0.0f);
glVertex2f((x + width), y);
glTexCoord2f(1.0f, 1.0f);
glVertex2f((x + width), (y + height));
glTexCoord2f(0.0f, 1.0f);
glVertex2f(x, (y + height));
glEnd();
}
glColor3f(1, 1, 1);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
#endif
return;
}
float sizeW = userData->size * _scaleW;
float sizeH = userData->size * _scaleH;
GLuint texture = userData->texture;
const Common::String *lines = text->getLines();
int numLines = text->getNumLines();
for (int j = 0; j < numLines; ++j) {
const Common::String &line = lines[j];
int x = text->getLineX(j);
int y = text->getLineY(j);
for (uint i = 0; i < line.size(); ++i) {
uint8 character = line[i];
float w = y + font->getCharStartingLine(character);
if (g_grim->getGameType() == GType_GRIM)
w += font->getBaseOffsetY();
float z = x + font->getCharStartingCol(character);
z *= _scaleW;
w *= _scaleH;
glBindTexture(GL_TEXTURE_2D, texture);
float width = 1 / 16.f;
float cx = ((character - 1) % 16) / 16.0f;
float cy = ((character - 1) / 16) / 16.0f;
glBegin(GL_QUADS);
glTexCoord2f(cx, cy);
glVertex2f(z, w);
glTexCoord2f(cx + width, cy);
glVertex2f(z + sizeW, w);
glTexCoord2f(cx + width, cy + width);
glVertex2f(z + sizeW, w + sizeH);
glTexCoord2f(cx, cy + width);
glVertex2f(z, w + sizeH);
glEnd();
x += font->getCharKernedWidth(character);
}
}
glColor3f(1, 1, 1);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
}
void GfxOpenGL::destroyTextObject(TextObject *text) {
if (g_grim->getGameType() != GType_GRIM || !(g_grim->getGameFlags() & ADGF_REMASTERED))
return;
TextObjectUserData *ud = (TextObjectUserData *)const_cast<void *>(text->getUserData());
glDeleteTextures(text->getNumLines(), ud->_texids);
delete ud;
}
void GfxOpenGL::createTexture(Texture *texture, const uint8 *data, const CMap *cmap, bool clamp) {
texture->_texture = new GLuint[1];
glGenTextures(1, (GLuint *)texture->_texture);
uint8 *texdata = new uint8[texture->_width * texture->_height * 4];
uint8 *texdatapos = texdata;
if (cmap != nullptr) { // EMI doesn't have colour-maps
int bytes = 4;
for (int y = 0; y < texture->_height; y++) {
for (int x = 0; x < texture->_width; x++) {
uint8 col = *data;
if (col == 0) {
memset(texdatapos, 0, bytes); // transparent
if (!texture->_hasAlpha) {
texdatapos[3] = '\xff'; // fully opaque
}
} else {
memcpy(texdatapos, cmap->_colors + 3 * (col), 3);
texdatapos[3] = '\xff'; // fully opaque
}
texdatapos += bytes;
data++;
}
}
} else {
#ifdef SCUMM_BIG_ENDIAN
// Copy and swap
for (int y = 0; y < texture->_height; y++) {
for (int x = 0; x < texture->_width; x++) {
uint32 pixel = (y * texture->_width + x) * texture->_bpp;
for (int b = 0; b < texture->_bpp; b++) {
texdata[pixel + b] = data[pixel + (texture->_bpp - 1) - b];
}
}
}
#else
memcpy(texdata, data, texture->_width * texture->_height * texture->_bpp);
#endif
}
GLuint format = 0;
GLuint internalFormat = 0;
if (texture->_colorFormat == BM_RGBA) {
format = GL_RGBA;
internalFormat = GL_RGBA;
} else if (texture->_colorFormat == BM_BGRA) {
format = GL_BGRA;
internalFormat = GL_RGBA;
} else { // The only other colorFormat we load right now is BGR
format = GL_BGR;
internalFormat = GL_RGB;
}
GLuint *textures = (GLuint *)texture->_texture;
glBindTexture(GL_TEXTURE_2D, textures[0]);
//Remove darkened lines in EMI intro
if (g_grim->getGameType() == GType_MONKEY4 && clamp) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, texture->_width, texture->_height, 0, format, GL_UNSIGNED_BYTE, texdata);
delete[] texdata;
}
void GfxOpenGL::selectTexture(const Texture *texture) {
GLuint *textures = (GLuint *)texture->_texture;
glBindTexture(GL_TEXTURE_2D, textures[0]);
if (texture->_hasAlpha && g_grim->getGameType() == GType_MONKEY4) {
glEnable(GL_BLEND);
}
// Grim has inverted tex-coords, EMI doesn't
if (g_grim->getGameType() != GType_MONKEY4) {
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1.0f / texture->_width, 1.0f / texture->_height, 1);
}
}
void GfxOpenGL::destroyTexture(Texture *texture) {
GLuint *textures = (GLuint *)texture->_texture;
if (textures) {
glDeleteTextures(1, textures);
delete[] textures;
}
}
void GfxOpenGL::drawDepthBitmap(int x, int y, int w, int h, char *data) {
//if (num != 0) {
// warning("Animation not handled yet in GL texture path");
//}
if (y + h == 480) {
glRasterPos2i(x, _screenHeight - 1);
glBitmap(0, 0, 0, 0, 0, -1, nullptr);
} else
glRasterPos2i(x, y + h);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 2); // 16 bit Z depth bitmap
glDrawPixels(w, h, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, data);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthFunc(_depthFunc);
}
void GfxOpenGL::prepareMovieFrame(Graphics::Surface *frame) {
int height = frame->h;
int width = frame->w;
byte *bitmap = (byte *)frame->getPixels();
double scaleW = _scaleW;
double scaleH = _scaleH;
// Remastered hack, don't scale full-screen videos for now.
if (height == 1080) {
_scaleW = 1.0f;
_scaleH = 1.0f;
}
GLenum format;
GLenum dataType;
int bytesPerPixel = frame->format.bytesPerPixel;
// Aspyr Logo format
if (frame->format == Graphics::PixelFormat(4, 8, 8, 8, 0, 8, 16, 24, 0)) {
#if !defined(__amigaos4__)
format = GL_BGRA;
dataType = GL_UNSIGNED_INT_8_8_8_8;
#else
// AmigaOS' MiniGL does not understand GL_UNSIGNED_INT_8_8_8_8 yet.
format = GL_BGRA;
dataType = GL_UNSIGNED_BYTE;
#endif
// Used by Grim Fandango Remastered
} else if (frame->format == Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0)) {
#if !defined(__amigaos4__)
format = GL_BGRA;
dataType = GL_UNSIGNED_INT_8_8_8_8;
#else
// AmigaOS' MiniGL does not understand GL_UNSIGNED_INT_8_8_8_8 yet.
format = GL_BGRA;
dataType = GL_UNSIGNED_BYTE;
#endif
} else if (frame->format == Graphics::PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0)) {
format = GL_BGRA;
dataType = GL_UNSIGNED_INT_8_8_8_8_REV;
} else if (frame->format == Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)) {
format = GL_RGB;
dataType = GL_UNSIGNED_SHORT_5_6_5;
} else {
error("Unknown pixelformat: Bpp: %d RBits: %d GBits: %d BBits: %d ABits: %d RShift: %d GShift: %d BShift: %d AShift: %d",
frame->format.bytesPerPixel,
-(frame->format.rLoss - 8),
-(frame->format.gLoss - 8),
-(frame->format.bLoss - 8),
-(frame->format.aLoss - 8),
frame->format.rShift,
frame->format.gShift,
frame->format.bShift,
frame->format.aShift);
}
// remove if already exist
if (_smushNumTex > 0) {
glDeleteTextures(_smushNumTex, _smushTexIds);
delete[] _smushTexIds;
_smushNumTex = 0;
}
// create texture
_smushNumTex = ((width + (BITMAP_TEXTURE_SIZE - 1)) / BITMAP_TEXTURE_SIZE) *
((height + (BITMAP_TEXTURE_SIZE - 1)) / BITMAP_TEXTURE_SIZE);
_smushTexIds = new GLuint[_smushNumTex];
glGenTextures(_smushNumTex, _smushTexIds);
for (int i = 0; i < _smushNumTex; i++) {
glBindTexture(GL_TEXTURE_2D, _smushTexIds[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, BITMAP_TEXTURE_SIZE, BITMAP_TEXTURE_SIZE, 0, format, dataType, nullptr);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, bytesPerPixel); // 16 bit RGB 565 bitmap/32 bit BGR
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
int curTexIdx = 0;
for (int y = 0; y < height; y += BITMAP_TEXTURE_SIZE) {
for (int x = 0; x < width; x += BITMAP_TEXTURE_SIZE) {
int t_width = (x + BITMAP_TEXTURE_SIZE >= width) ? (width - x) : BITMAP_TEXTURE_SIZE;
int t_height = (y + BITMAP_TEXTURE_SIZE >= height) ? (height - y) : BITMAP_TEXTURE_SIZE;
glBindTexture(GL_TEXTURE_2D, _smushTexIds[curTexIdx]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, t_width, t_height, format, dataType, bitmap + (y * bytesPerPixel * width) + (bytesPerPixel * x));
curTexIdx++;
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
_smushWidth = (int)(width * _scaleW);
_smushHeight = (int)(height * _scaleH);
_scaleW = scaleW;
_scaleH = scaleH;
}
void GfxOpenGL::drawMovieFrame(int offsetX, int offsetY) {
double scaleW = _scaleW;
double scaleH = _scaleH;
// Remastered hack, don't scale full-screen videos for now.
if (_smushHeight == 1080) {
_scaleW = 1.0f;
_scaleH = 1.0f;
}
// prepare view
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
// A lot more may need to be put there : disabling Alpha test, blending, ...
// For now, just keep this here :-)
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
// draw
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_SCISSOR_TEST);
offsetX = (int)(offsetX * _scaleW);
offsetY = (int)(offsetY * _scaleH);
glScissor(offsetX, _screenHeight - (offsetY + _smushHeight), _smushWidth, _smushHeight);
int curTexIdx = 0;
for (int y = 0; y < _smushHeight; y += (int)(BITMAP_TEXTURE_SIZE * _scaleH)) {
for (int x = 0; x < _smushWidth; x += (int)(BITMAP_TEXTURE_SIZE * _scaleW)) {
glBindTexture(GL_TEXTURE_2D, _smushTexIds[curTexIdx]);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(x + offsetX, y + offsetY);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(x + offsetX + BITMAP_TEXTURE_SIZE * _scaleW, y + offsetY);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(x + offsetX + BITMAP_TEXTURE_SIZE * _scaleW, y + offsetY + BITMAP_TEXTURE_SIZE * _scaleH);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(x + offsetX, y + offsetY + BITMAP_TEXTURE_SIZE * _scaleH);
glEnd();
curTexIdx++;
}
}
glDisable(GL_SCISSOR_TEST);
glDisable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
_scaleW = scaleW;
_scaleH = scaleH;
}
void GfxOpenGL::releaseMovieFrame() {
if (_smushNumTex > 0) {
glDeleteTextures(_smushNumTex, _smushTexIds);
delete[] _smushTexIds;
_smushNumTex = 0;
}
}
void GfxOpenGL::loadEmergFont() {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 8 bit font bitmaps
_emergFont = glGenLists(128);
for (int i = 32; i < 128; i++) {
glNewList(_emergFont + i, GL_COMPILE);
glBitmap(8, 13, 0, 2, 10, 0, Font::emerFont[i - 32]);
glEndList();
}
}
void GfxOpenGL::drawEmergString(int x, int y, const char *text, const Color &fgColor) {
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glRasterPos2i(x, y);
glColor3f(1.0f, 1.0f, 1.0f);
glListBase(_emergFont);
char *list = const_cast<char *>(text);
glCallLists(strlen(text), GL_UNSIGNED_BYTE, (void *)list);
glEnable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
Bitmap *GfxOpenGL::getScreenshot(int w, int h, bool useStored) {
Graphics::PixelBuffer src(Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24), _screenWidth * _screenHeight, DisposeAfterUse::YES);
if (useStored) {
memcpy(src.getRawBuffer(), _storedDisplay, _screenWidth * _screenHeight * 4);
} else {
glReadPixels(0, 0, _screenWidth, _screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, src.getRawBuffer());
}
return createScreenshotBitmap(src, w, h, false);
}
void GfxOpenGL::storeDisplay() {
glReadPixels(0, 0, _screenWidth, _screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, _storedDisplay);
}
void GfxOpenGL::copyStoredToDisplay() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glRasterPos2i(0, _screenHeight - 1);
glBitmap(0, 0, 0, 0, 0, -1, nullptr);
glDrawPixels(_screenWidth, _screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, _storedDisplay);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
}
void GfxOpenGL::dimScreen() {
uint32 *data = (uint32 *)_storedDisplay;
for (int l = 0; l < _screenWidth * _screenHeight; l++) {
uint32 pixel = data[l];
uint8 r = (pixel & 0xFF0000) >> 16;
uint8 g = (pixel & 0x00FF00) >> 8;
uint8 b = (pixel & 0x0000FF);
uint32 color = (r + g + b) / 10;
data[l] = ((color & 0xFF) << 16) | ((color & 0xFF) << 8) | (color & 0xFF);
}
}
void GfxOpenGL::dimRegion(int x, int yReal, int w, int h, float level) {
x = (int)(x * _scaleW);
yReal = (int)(yReal * _scaleH);
w = (int)(w * _scaleW);
h = (int)(h * _scaleH);
int y = _screenHeight - yReal - h;
#ifdef GL_ARB_fragment_program
if (_useDimShader) {
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glViewport(0, 0, _screenWidth, _screenHeight);
// copy the data over to the texture
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, w, h, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, 0, _screenHeight, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_SCISSOR_TEST);
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _dimFragProgram);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, level, 0, 0, 0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(x, y);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(x + w, y);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(x + w, y + h);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(x, y + h);
glEnd();
glDisable(GL_FRAGMENT_PROGRAM_ARB);
glDeleteTextures(1, &texture);
return;
}
#endif
uint32 *data = new uint32[w * h];
y = _screenHeight - yReal;
// collect the requested area and generate the dimmed version
glReadPixels(x, y - h, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
for (int ly = 0; ly < h; ly++) {
for (int lx = 0; lx < w; lx++) {
uint32 pixel = data[ly * w + lx];
uint8 r = (pixel & 0xFF0000) >> 16;
uint8 g = (pixel & 0x00FF00) >> 8;
uint8 b = (pixel & 0x0000FF);
uint32 color = (uint32)(((r + g + b) / 3) * level);
data[ly * w + lx] = ((color & 0xFF) << 16) | ((color & 0xFF) << 8) | (color & 0xFF);
}
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
// Set the raster position and draw the bitmap
glRasterPos2i(x, yReal + h);
glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
delete[] data;
}
void GfxOpenGL::irisAroundRegion(int x1, int y1, int x2, int y2) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, _screenWidth, _screenHeight, 0.0, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glColor3f(0.0f, 0.0f, 0.0f);
//Explicitly cast to avoid problems with C++11
float fx1 = x1;
float fx2 = x2;
float fy1 = y1;
float fy2 = y2;
float width = _screenWidth;
float height = _screenHeight;
float points[20] = {
0.0f, 0.0f,
0.0f, fy1,
width, 0.0f,
fx2, fy1,
width, height,
fx2, fy2,
0.0f, height,
fx1, fy2,
0.0f, fy1,
fx1, fy1
};
glBegin(GL_TRIANGLE_STRIP);
for (int i = 0 ;i < 10; ++i) {
glVertex2fv(points+2*i);
}
glEnd();
glColor3f(1.0f, 1.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
}
void GfxOpenGL::drawRectangle(const PrimitiveObject *primitive) {
float x1 = primitive->getP1().x * _scaleW;
float y1 = primitive->getP1().y * _scaleH;
float x2 = primitive->getP2().x * _scaleW;
float y2 = primitive->getP2().y * _scaleH;
const Color color(primitive->getColor());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glColor3ub(color.getRed(), color.getGreen(), color.getBlue());
if (primitive->isFilled()) {
glBegin(GL_QUADS);
glVertex2f(x1, y1);
glVertex2f(x2 + 1, y1);
glVertex2f(x2 + 1, y2 + 1);
glVertex2f(x1, y2 + 1);
glEnd();
} else {
glLineWidth(_scaleW);
glBegin(GL_LINE_LOOP);
glVertex2f(x1, y1);
glVertex2f(x2 + 1, y1);
glVertex2f(x2 + 1, y2 + 1);
glVertex2f(x1, y2 + 1);
glEnd();
}
glColor3f(1.0f, 1.0f, 1.0f);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
}
void GfxOpenGL::drawLine(const PrimitiveObject *primitive) {
float x1 = primitive->getP1().x * _scaleW;
float y1 = primitive->getP1().y * _scaleH;
float x2 = primitive->getP2().x * _scaleW;
float y2 = primitive->getP2().y * _scaleH;
const Color &color = primitive->getColor();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glColor3ub(color.getRed(), color.getGreen(), color.getBlue());
glLineWidth(_scaleW);
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
glColor3f(1.0f, 1.0f, 1.0f);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
}
void GfxOpenGL::drawDimPlane() {
if (_dimLevel == 0.0f) return;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1.0, 1.0, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.0f, 0.0f, 0.0f, _dimLevel);
glBegin(GL_QUADS);
glVertex2f(0, 0);
glVertex2f(1.0, 0);
glVertex2f(1.0, 1.0);
glVertex2f(0, 1.0);
glEnd();
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
}
void GfxOpenGL::drawPolygon(const PrimitiveObject *primitive) {
float x1 = primitive->getP1().x * _scaleW;
float y1 = primitive->getP1().y * _scaleH;
float x2 = primitive->getP2().x * _scaleW;
float y2 = primitive->getP2().y * _scaleH;
float x3 = primitive->getP3().x * _scaleW;
float y3 = primitive->getP3().y * _scaleH;
float x4 = primitive->getP4().x * _scaleW;
float y4 = primitive->getP4().y * _scaleH;
const Color &color = primitive->getColor();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _screenWidth, _screenHeight, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#if !defined(__amigaos4__)
glDisable(GL_MULTISAMPLE);
#endif
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glColor3ub(color.getRed(), color.getGreen(), color.getBlue());
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2 + 1, y2 + 1);
glVertex2f(x3, y3 + 1);
glVertex2f(x4 + 1, y4);
glEnd();
glColor3f(1.0f, 1.0f, 1.0f);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
#if !defined(__amigaos4__)
glEnable(GL_MULTISAMPLE);
#endif
}
void GfxOpenGL::readPixels(int x, int y, int width, int height, uint8 *buffer) {
uint8 *p = buffer;
for (int i = y; i < y + height; i++) {
glReadPixels(x, _screenHeight - 1 - i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, p);
p += width * 4;
}
}
void GfxOpenGL::createSpecialtyTextureFromScreen(uint id, uint8 *data, int x, int y, int width, int height) {
readPixels(x, y, width, height, data);
createSpecialtyTexture(id, data, width, height);
}
void GfxOpenGL::setBlendMode(bool additive) {
if (additive) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
} else {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
}
} // end of namespace Grim
#endif