/* ScummVM - Graphic Adventure Engine
*
* ScummVM 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 3 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, see .
*
*/
#include "engines/wintermute/ad/ad_block.h"
#include "engines/wintermute/ad/ad_game.h"
#include "engines/wintermute/ad/ad_generic.h"
#include "engines/wintermute/ad/ad_scene.h"
#include "engines/wintermute/ad/ad_scene_geometry.h"
#include "engines/wintermute/ad/ad_walkplane.h"
#include "engines/wintermute/ad/ad_waypoint_group3d.h"
#include "engines/wintermute/base/base_game.h"
#include "engines/wintermute/base/gfx/base_image.h"
#include "engines/wintermute/base/gfx/3dcamera.h"
#include "engines/wintermute/base/gfx/3dlight.h"
#include "engines/wintermute/platform_osystem.h"
#include "graphics/opengl/system_headers.h"
#include "common/config-manager.h"
#if defined(USE_OPENGL_GAME)
#include "engines/wintermute/base/gfx/3dutils.h"
#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
#include "engines/wintermute/base/gfx/opengl/base_surface_opengl3d.h"
#include "engines/wintermute/base/gfx/opengl/mesh3ds_opengl.h"
#include "engines/wintermute/base/gfx/opengl/meshx_opengl.h"
#include "engines/wintermute/base/gfx/opengl/shadow_volume_opengl.h"
namespace Wintermute {
BaseRenderer3D *makeOpenGL3DRenderer(BaseGame *inGame) {
return new BaseRenderOpenGL3D(inGame);
}
BaseRenderOpenGL3D::BaseRenderOpenGL3D(BaseGame *inGame) : BaseRenderer3D(inGame) {
}
BaseRenderOpenGL3D::~BaseRenderOpenGL3D() {
_camera = nullptr; // ref only
}
bool BaseRenderOpenGL3D::initRenderer(int width, int height, bool windowed) {
_simpleShadow[0].x = -1.0f;
_simpleShadow[0].y = 0.0f;
_simpleShadow[0].z = 1.0f;
_simpleShadow[0].nx = 0.0f;
_simpleShadow[0].ny = 1.0f;
_simpleShadow[0].nz = 0.0f;
_simpleShadow[0].u = 0.0f;
_simpleShadow[0].v = 1.0f;
_simpleShadow[1].x = -1.0f;
_simpleShadow[1].y = 0.0f;
_simpleShadow[1].z = -1.0f;
_simpleShadow[1].nx = 0.0f;
_simpleShadow[1].ny = 1.0f;
_simpleShadow[1].nz = 0.0f;
_simpleShadow[1].u = 1.0f;
_simpleShadow[1].v = 1.0f;
_simpleShadow[2].x = 1.0f;
_simpleShadow[2].y = 0.0f;
_simpleShadow[2].z = 1.0f;
_simpleShadow[2].nx = 0.0f;
_simpleShadow[2].ny = 1.0f;
_simpleShadow[2].nz = 0.0f;
_simpleShadow[2].u = 0.0f;
_simpleShadow[2].v = 0.0f;
_simpleShadow[3].x = 1.0f;
_simpleShadow[3].y = 0.0f;
_simpleShadow[3].z = -1.0f;
_simpleShadow[3].nx = 0.0f;
_simpleShadow[3].ny = 1.0f;
_simpleShadow[3].nz = 0.0f;
_simpleShadow[3].u = 1.0f;
_simpleShadow[3].v = 0.0f;
// filter post process: greyscale, sepia
glGenTextures(1, &_postfilterTexture);
glBindTexture(GL_TEXTURE_2D, _postfilterTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
setSpriteBlendMode(Graphics::BLEND_NORMAL, true);
_windowed = !ConfMan.getBool("fullscreen");
_width = width;
_height = height;
g_system->showMouse(false);
setViewport(0, 0, width, height);
setProjection();
_postFilterMode = kPostFilterOff;
_active = true;
_game->_supportsRealTimeShadows = true;
setDefaultAmbientLightColor();
_lightPositions.resize(getMaxActiveLights());
_lightDirections.resize(getMaxActiveLights());
return true;
}
bool BaseRenderOpenGL3D::flip() {
_lastTexture = nullptr;
// Store blend mode and cull face mode
GLboolean stateBlend, stateCullFace, stateAlpha;
glGetBooleanv(GL_BLEND, &stateBlend);
glGetBooleanv(GL_CULL_FACE, &stateCullFace);
glGetBooleanv(GL_ALPHA_TEST, &stateAlpha);
postfilter();
// Disable blend mode and cull face to prevent interfere with backend renderer
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
g_system->updateScreen();
// Restore blend mode and cull face state
if (stateBlend)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
if (stateCullFace)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
if (stateAlpha)
glEnable(GL_ALPHA_TEST);
else
glDisable(GL_ALPHA_TEST);
_state = RSTATE_NONE;
return true;
}
bool BaseRenderOpenGL3D::clear() {
if(!_game->_editorMode) {
glViewport(0, _height, _width, _height);
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
return true;
}
bool BaseRenderOpenGL3D::setup2D(bool force) {
if (_state != RSTATE_2D || force) {
_state = RSTATE_2D;
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
setSpriteBlendMode(Graphics::BLEND_NORMAL);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GEQUAL, 0.0f);
glFrontFace(GL_CW); // WME DX have CCW
glEnable(GL_CULL_FACE);
glDisable(GL_STENCIL_TEST);
glDisable(GL_FOG);
}
return true;
}
bool BaseRenderOpenGL3D::setup3D(Camera3D *camera, bool force) {
if (_state != RSTATE_3D || force) {
_state = RSTATE_3D;
glEnable(GL_NORMALIZE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_ALPHA_TEST);
// WME uses 8 as a reference value and Direct3D expects it to be in the range [0, 255]
glAlphaFunc(GL_GEQUAL, 8 / 255.0f);
setAmbientLightRenderState();
if (camera)
_camera = camera;
if (_camera) {
DXMatrix viewMatrix;
_camera->getViewMatrix(&viewMatrix);
setViewTransform(viewMatrix);
_fov = _camera->_fov;
if (_camera->_nearClipPlane >= 0.0f) {
_nearClipPlane = _camera->_nearClipPlane;
} else {
_nearClipPlane = DEFAULT_NEAR_PLANE;
}
if (_camera->_farClipPlane >= 0.0f) {
_farClipPlane = _camera->_farClipPlane;
} else {
_farClipPlane = DEFAULT_FAR_PLANE;
}
} else {
_nearClipPlane = DEFAULT_NEAR_PLANE;
_farClipPlane = DEFAULT_FAR_PLANE;
}
// lighting
glEnable(GL_LIGHTING);
for (int i = 0; i < getMaxActiveLights(); ++i) {
glLightfv(GL_LIGHT0 + i, GL_POSITION, _lightPositions[i]);
glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, _lightDirections[i]);
}
// fog
bool fogEnabled;
uint32 fogColor;
float fogStart, fogEnd;
_game->getFogParams(&fogEnabled, &fogColor, &fogStart, &fogEnd);
if (fogEnabled) {
glEnable(GL_FOG);
GLfloat color[4] = { RGBCOLGetR(fogColor) / 255.0f,
RGBCOLGetG(fogColor) / 255.0f,
RGBCOLGetB(fogColor) / 255.0f,
RGBCOLGetA(fogColor) / 255.0f };
glFogfv(GL_FOG_COLOR, color);
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogf(GL_FOG_START, fogStart);
glFogf(GL_FOG_END, fogEnd);
} else {
glDisable(GL_FOG);
}
setProjection();
}
return true;
}
void BaseRenderOpenGL3D::setAmbientLightRenderState() {
byte a = 0;
byte r = 0;
byte g = 0;
byte b = 0;
if (_ambientLightOverride) {
a = RGBCOLGetA(_ambientLightColor);
r = RGBCOLGetR(_ambientLightColor);
g = RGBCOLGetG(_ambientLightColor);
b = RGBCOLGetB(_ambientLightColor);
} else {
uint32 color = _game->getAmbientLightColor();
a = RGBCOLGetA(color);
r = RGBCOLGetR(color);
g = RGBCOLGetG(color);
b = RGBCOLGetB(color);
}
float value[] = { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, value);
}
bool BaseRenderOpenGL3D::setupLines() {
if (_state != RSTATE_LINES) {
_state = RSTATE_LINES;
float value[] = { 0, 0, 0, 0 };
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glFrontFace(GL_CW); // WME DX have CCW
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, value);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
_lastTexture = nullptr;
}
return true;
}
bool BaseRenderOpenGL3D::drawSpriteEx(BaseSurface *tex, const Common::Rect32 &rect,
const DXVector2 &pos, const DXVector2 &rot,
const DXVector2 &scale,
float angle, uint32 color, bool alphaDisable,
Graphics::TSpriteBlendMode blendMode,
bool mirrorX, bool mirrorY) {
BaseSurfaceOpenGL3D *texture = dynamic_cast(tex);
if (!texture)
return false;
if (_forceAlphaColor != 0) {
color = _forceAlphaColor;
}
float width = (rect.right - rect.left) * scale._x;
float height = (rect.bottom - rect.top) * scale._y;
int texWidth = texture->getGLTextureWidth();
int texHeight = texture->getGLTextureHeight();
float texLeft = (float)rect.left / (float)texWidth;
float texTop = (float)rect.top / (float)texHeight;
float texRight = (float)rect.right / (float)texWidth;
float texBottom = (float)rect.bottom / (float)texHeight;
if (mirrorX) {
SWAP(texLeft, texRight);
}
if (mirrorY) {
SWAP(texTop, texBottom);
}
SpriteVertex vertices[4];
// texture coords
vertices[0].u = texLeft;
vertices[0].v = texBottom;
vertices[1].u = texLeft;
vertices[1].v = texTop;
vertices[2].u = texRight;
vertices[2].v = texBottom;
vertices[3].u = texRight;
vertices[3].v = texTop;
// position coords
vertices[0].x = pos._x;
vertices[0].y = pos._y + height;
vertices[0].z = 0.9f;
vertices[1].x = pos._x;
vertices[1].y = pos._y;
vertices[1].z = 0.9f;
vertices[2].x = pos._x + width;
vertices[2].y = pos._y + height;
vertices[2].z = 0.9f;
vertices[3].x = pos._x + width;
vertices[3].y = pos._y;
vertices[3].z = 0.9f;
if (angle != 0) {
DXVector2 sc(1.0f, 1.0f);
DXVector2 rotation(rot._x, rot._y);
transformVertices(vertices, &rotation, &sc, angle);
}
for (int i = 0; i < 4; i++) {
vertices[i].x += _drawOffsetX;
vertices[i].y += _drawOffsetY;
}
byte a = RGBCOLGetA(color);
byte r = RGBCOLGetR(color);
byte g = RGBCOLGetG(color);
byte b = RGBCOLGetB(color);
for (int i = 0; i < 4; ++i) {
vertices[i].r = r / 255.0f;
vertices[i].g = g / 255.0f;
vertices[i].b = b / 255.0f;
vertices[i].a = a / 255.0f;
}
setSpriteBlendMode(blendMode);
if (alphaDisable) {
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
}
if (_lastTexture != texture) {
_lastTexture = texture;
glBindTexture(GL_TEXTURE_2D, texture->getTextureName());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// for sprites we clamp to the edge, to avoid line fragments at the edges
// this is not done by wme, but centering pixel by 0.5
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glEnable(GL_TEXTURE_2D);
}
glViewport(0, 0, _width, _height);
setProjection2D();
glFrontFace(GL_CW);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(SpriteVertex), &vertices[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(SpriteVertex), &vertices[0].u);
glColorPointer(4, GL_FLOAT, sizeof(SpriteVertex), &vertices[0].r);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (alphaDisable) {
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
}
return true;
}
bool BaseRenderOpenGL3D::commitSpriteBatch() {
// nothing to implement
return true;
}
bool BaseRenderOpenGL3D::startSpriteBatch() {
// nothing to implement
return true;
}
bool BaseRenderOpenGL3D::endSpriteBatch() {
// nothing to implement
return true;
}
DXMatrix *BaseRenderOpenGL3D::buildMatrix(DXMatrix* out, const DXVector2 *centre, const DXVector2 *scaling, float angle) {
DXMatrix matrices[5];
DXMatrixTranslation(&matrices[0], -centre->_x, -centre->_y, 0);
DXMatrixScaling(&matrices[1], scaling->_x, scaling->_y, 1);
DXMatrixIdentity(&matrices[2]);
DXMatrixIdentity(&matrices[3]);
DXMatrixRotationZ(&matrices[2], angle);
DXMatrixTranslation(&matrices[3], centre->_x, centre->_y, 0);
matrices[4] = matrices[0] * matrices[1] * matrices[2] * matrices[3];
*out = matrices[4];
return out;
}
void BaseRenderOpenGL3D::transformVertices(struct SpriteVertex *vertices, const DXVector2 *centre, const DXVector2 *scaling, float angle) {
DXMatrix matTransf, matVerts, matNew;
buildMatrix(&matTransf, centre, scaling, angle);
int cr;
for (cr = 0; cr < 4; cr++) {
matVerts(cr, 0) = vertices[cr].x;
matVerts(cr, 1) = vertices[cr].y;
matVerts(cr, 2) = vertices[cr].z;
matVerts(cr, 3) = 1.0f;
}
matNew = matVerts * matTransf;
for (cr = 0; cr < 4; cr++) {
vertices[cr].x = matNew(cr, 0);
vertices[cr].y = matNew(cr, 1);
vertices[cr].z = matNew(cr, 2);
}
}
bool BaseRenderOpenGL3D::setProjection() {
DXMatrix matProj;
float resWidth, resHeight;
float layerWidth, layerHeight;
float modWidth, modHeight;
bool customViewport;
getProjectionParams(&resWidth, &resHeight, &layerWidth, &layerHeight, &modWidth, &modHeight, &customViewport);
Common::Rect32 rc;
_game->getCurrentViewportRect(&rc);
float viewportWidth = (float)rc.right - (float)rc.left;
float viewportHeight = (float)rc.bottom - (float)rc.top;
// margins
int mleft = rc.left;
int mright = resWidth - viewportWidth - rc.left;
int mtop = rc.top;
int mbottom = resHeight - viewportHeight - rc.top;
DXMatrixPerspectiveFovLH(&matProj, _fov, viewportWidth / viewportHeight, _nearClipPlane, _farClipPlane);
float scaleMod = resHeight / viewportHeight;
float scaleRatio = MAX(layerWidth / resWidth, layerHeight / resHeight) /** 1.05*/;
float offsetX = (float)_game->_offsetX;
float offsetY = (float)_game->_offsetY;
if (!customViewport) {
offsetX -= _drawOffsetX;
offsetY -= _drawOffsetY;
}
matProj.matrix._11 *= scaleRatio * scaleMod;
matProj.matrix._22 *= scaleRatio * scaleMod;
matProj.matrix._31 = -(offsetX + (mleft - mright) / 2 - modWidth) / viewportWidth * 2.0f;
matProj.matrix._32 = (offsetY + (mtop - mbottom) / 2 - modHeight) / viewportHeight * 2.0f;
return setProjectionTransform(matProj);
}
bool BaseRenderOpenGL3D::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
setupLines();
x1 += _drawOffsetX;
x2 += _drawOffsetX;
y1 += _drawOffsetY;
y2 += _drawOffsetY;
// position coords
RectangleVertex vertices[2];
vertices[0].x = x1;
vertices[0].y = y1;
vertices[0].z = 0.9f;
vertices[1].x = x2;
vertices[1].y = y2;
vertices[1].z = 0.9f;
byte a = RGBCOLGetA(color);
byte r = RGBCOLGetR(color);
byte g = RGBCOLGetG(color);
byte b = RGBCOLGetB(color);
glViewport(0, 0, _width, _height);
setProjection2D();
glColor4ub(r, g, b, a);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(RectangleVertex), &vertices[0].x);
glDrawArrays(GL_LINES, 0, 2);
glDisableClientState(GL_VERTEX_ARRAY);
return true;
}
bool BaseRenderOpenGL3D::fillRect(int x, int y, int w, int h, uint32 color) {
setupLines();
x += _drawOffsetX;
y += _drawOffsetY;
// position coords
RectangleVertex vertices[4];
vertices[0].x = x;
vertices[0].y = y + h;
vertices[0].z = 0.9f;
vertices[1].x = x;
vertices[1].y = y;
vertices[1].z = 0.9f;
vertices[2].x = x + w;
vertices[2].y = y + h;
vertices[2].z = 0.9f;
vertices[3].x = x + w;
vertices[3].y = y;
vertices[3].z = 0.9f;
byte a = RGBCOLGetA(color);
byte r = RGBCOLGetR(color);
byte g = RGBCOLGetG(color);
byte b = RGBCOLGetB(color);
glViewport(0, 0, _width, _height);
setProjection2D();
glColor4ub(r, g, b, a);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(RectangleVertex), &vertices[0].x);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
setup2D();
return true;
}
bool BaseRenderOpenGL3D::fadeToColor(byte r, byte g, byte b, byte a) {
float left, right, bottom, top;
left = _viewportRect.left;
right = _viewportRect.right;
bottom = _viewportRect.bottom;
top = _viewportRect.top;
// position coords
RectangleVertex vertices[4];
vertices[0].x = left;
vertices[0].y = bottom;
vertices[0].z = 0.0f;
vertices[1].x = left;
vertices[1].y = top;
vertices[1].z = 0.0f;
vertices[2].x = right;
vertices[2].y = bottom;
vertices[2].z = 0.0f;
vertices[3].x = right;
vertices[3].y = top;
vertices[3].z = 0.0f;
glEnable(GL_BLEND);
setSpriteBlendMode(Graphics::BLEND_NORMAL);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
_lastTexture = nullptr;
glViewport(0, 0, _width, _height);
setProjection2D();
glColor4ub(r, g, b, a);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(RectangleVertex), &vertices[0].x);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
setup2D(true);
return true;
}
BaseImage *BaseRenderOpenGL3D::takeScreenshot(int newWidth, int newHeight) {
BaseImage *screenshot = new BaseImage();
Graphics::Surface *surface = new Graphics::Surface();
Graphics::PixelFormat format = Graphics::PixelFormat::createFormatRGBA32();
surface->create(_viewportRect.width(), _viewportRect.height(), format);
glReadPixels(_viewportRect.left, _viewportRect.height() - _viewportRect.bottom,
_viewportRect.width(), _viewportRect.height(),
GL_RGBA, GL_UNSIGNED_BYTE, surface->getPixels());
screenshot->copyFrom(surface, newWidth, newHeight, Graphics::FLIP_V);
delete surface;
return screenshot;
}
bool BaseRenderOpenGL3D::enableShadows() {
return true;
}
bool BaseRenderOpenGL3D::disableShadows() {
return true;
}
void BaseRenderOpenGL3D::displaySimpleShadow(BaseObject *object) {
if (!_ready || !object)
return;
BaseSurface *shadowImage;
if (object->_shadowImage) {
shadowImage = object->_shadowImage;
} else {
shadowImage = _game->_shadowImage;
}
if (!shadowImage) {
return;
}
DXMatrix scale, trans, rot, finalm;
DXMatrixScaling(&scale, object->_shadowSize * object->_scale3D, 1.0f, object->_shadowSize * object->_scale3D);
DXMatrixRotationY(&rot, degToRad(object->_angle));
DXMatrixTranslation(&trans, object->_posVector._x, object->_posVector._y, object->_posVector._z);
DXMatrixMultiply(&finalm, &scale, &rot);
DXMatrixMultiply(&finalm, &finalm, &trans);
setWorldTransform(finalm);
glFrontFace(GL_CCW);
glDepthMask(GL_FALSE);
glEnable(GL_TEXTURE_2D);
static_cast(shadowImage)->setTexture();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(SimpleShadowVertex), &_simpleShadow[0].x);
glNormalPointer(GL_FLOAT, sizeof(SimpleShadowVertex), &_simpleShadow[0].nx);
glTexCoordPointer(2, GL_FLOAT, sizeof(SimpleShadowVertex), &_simpleShadow[0].u);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
}
void BaseRenderOpenGL3D::setSpriteBlendMode(Graphics::TSpriteBlendMode blendMode, bool forceChange) {
if (blendMode == _blendMode && !forceChange)
return;
_blendMode = blendMode;
switch (_blendMode) {
case Graphics::BLEND_NORMAL:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case Graphics::BLEND_ADDITIVE:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
case Graphics::BLEND_SUBTRACTIVE:
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
break;
default:
break;
}
}
bool BaseRenderOpenGL3D::shadowVolumeSupported() {
return true;
}
int BaseRenderOpenGL3D::getMaxActiveLights() {
GLint maxLightCount = 0;
glGetIntegerv(GL_MAX_LIGHTS, &maxLightCount);
return maxLightCount;
}
bool BaseRenderOpenGL3D::invalidateTexture(BaseSurface *texture) {
if (_lastTexture == texture)
_lastTexture = nullptr;
return true;
}
bool BaseRenderOpenGL3D::invalidateDeviceObjects() {
return STATUS_OK;
}
bool BaseRenderOpenGL3D::restoreDeviceObjects() {
return STATUS_OK;
}
bool BaseRenderOpenGL3D::resetDevice() {
return STATUS_OK;
}
// implements D3D LightEnable()
void BaseRenderOpenGL3D::lightEnable(int index, bool enable) {
if (enable)
glEnable(GL_LIGHT0 + index);
else
glDisable(GL_LIGHT0 + index);
}
// backend layer 3DLight::SetLight
void BaseRenderOpenGL3D::setLightParameters(int index, const DXVector3 &position, const DXVector3 &direction, const DXVector4 &diffuse, bool spotlight) {
float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
glLightfv(GL_LIGHT0 + index, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0 + index, GL_AMBIENT, zero);
glLightfv(GL_LIGHT0 + index, GL_SPECULAR, zero);
_lightPositions[index]._x = position._x;
_lightPositions[index]._y = position._y;
_lightPositions[index]._z = position._z;
_lightPositions[index]._w = 1.0f;
if (spotlight) {
_lightDirections[index] = direction;
glLightfv(GL_LIGHT0 + index, GL_SPOT_DIRECTION, direction);
glLightf(GL_LIGHT0 + index, GL_SPOT_EXPONENT, 0.0f);
// WME sets the theta angle to 0.5 (radians) and (28.64789 degree) - inner cone
// WME sets the phi angle to 1.0 (radians) and (57.29578 degree) - outer cone
// inner cone - angle within which the spotlight has maximum intensity
// outer cone - angle at the edge of the spotlight's cone
// 0 <-> 28.64789 - maximum light intensity
// 28.64789 <-> 57.29578 - light fades smoothly to zero
// 57.29578 <-> 90 - there is no light
// The smooth transition between inner code and outer cone create soft spotlight
// There is no replacement for smooth transition in fixed OpenGL lights.
// So, inner cone angle is used instead for better visual match
glLightf(GL_LIGHT0 + index, GL_SPOT_CUTOFF, 0.5f * (180.0f / (float)M_PI));
} else {
glLightf(GL_LIGHT0 + index, GL_SPOT_CUTOFF, 180.0f);
}
}
// backend layer AdSceneGeometry::Render
void BaseRenderOpenGL3D::renderSceneGeometry(const BaseArray &planes, const BaseArray &blocks,
const BaseArray &generics, const BaseArray &lights, Camera3D *camera) {
DXMatrix matIdentity;
DXMatrixIdentity(&matIdentity);
if (camera)
_game->_renderer3D->setup3D(camera, true);
setWorldTransform(matIdentity);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glFrontFace(GL_CW); // WME DX have CCW
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
// render walk planes
for (int32 i = 0; i < planes.getSize(); i++) {
if (planes[i]->_active) {
planes[i]->_mesh->render(true);
}
}
// render blocks
for (int32 i = 0; i < blocks.getSize(); i++) {
if (blocks[i]->_active) {
blocks[i]->_mesh->render(true);
}
}
// render generic objects
for (int32 i = 0; i < generics.getSize(); i++) {
if (generics[i]->_active) {
generics[i]->_mesh->render(true);
}
}
// render waypoints
AdScene *scene = ((AdGame *)_game)->_scene;
AdSceneGeometry *geom = scene->_geom;
if (geom && geom->_wptMarker) {
DXMatrix viewMat, projMat, worldMat;
DXVector3 vec2d(0.0f, 0.0f, 0.0f);
getViewTransform(&viewMat);
getProjectionTransform(&projMat);
DXMatrixIdentity(&worldMat);
DXViewport vport = getViewPort();
setup2D();
for (int32 i = 0; i < geom->_waypointGroups.getSize(); i++) {
for (int32 j = 0; j < geom->_waypointGroups[i]->_points.getSize(); j++) {
DXVec3Project(&vec2d, geom->_waypointGroups[i]->_points[j], &vport, &projMat, &viewMat, &worldMat);
geom->_wptMarker->display(vec2d._x + scene->getOffsetLeft() - _drawOffsetX, vec2d._y + scene->getOffsetTop() - _drawOffsetY);
}
}
}
glDisable(GL_COLOR_MATERIAL);
}
// backend layer 3DShadowVolume::Render()
void BaseRenderOpenGL3D::renderShadowGeometry(const BaseArray &planes,
const BaseArray &blocks,
const BaseArray &generics, Camera3D *camera) {
DXMatrix matIdentity;
DXMatrixIdentity(&matIdentity);
if (camera)
_game->_renderer3D->setup3D(camera, true);
setWorldTransform(matIdentity);
// disable color write
setSpriteBlendMode(Graphics::BLEND_UNKNOWN);
glBlendFunc(GL_ZERO, GL_ONE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// no texture
_lastTexture = nullptr;
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glFrontFace(GL_CW); // WME DX have CCW
// render blocks
for (int32 i = 0; i < blocks.getSize(); i++) {
if (blocks[i]->_active && blocks[i]->_receiveShadows) {
blocks[i]->_mesh->render();
}
}
// render walk planes
for (int32 i = 0; i < planes.getSize(); i++) {
if (planes[i]->_active && planes[i]->_receiveShadows) {
planes[i]->_mesh->render();
}
}
// render generic objects
for (int32 i = 0; i < generics.getSize(); i++) {
if (generics[i]->_active && generics[i]->_receiveShadows) {
generics[i]->_mesh->render();
}
}
setSpriteBlendMode(Graphics::BLEND_NORMAL);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
// implements D3D SetRenderState() D3DRS_CULLMODE - CCW
void BaseRenderOpenGL3D::enableCulling() {
glFrontFace(GL_CW); // WME DX have CCW
glEnable(GL_CULL_FACE);
}
// implements D3D SetRenderState() D3DRS_CULLMODE - NONE
void BaseRenderOpenGL3D::disableCulling() {
glDisable(GL_CULL_FACE);
}
// implements D3D SetViewport() for 2D renderer
bool BaseRenderOpenGL3D::setViewport(int left, int top, int right, int bottom) {
BasePlatform::setRect(&_viewportRect, left, top, right, bottom);
_viewport._x = left;
_viewport._y = top;
_viewport._width = right - left;
_viewport._height = bottom - top;
glViewport(left, top, right - left, bottom - top);
return true;
}
// implements D3D SetViewport() for 3D renderer
bool BaseRenderOpenGL3D::setViewport3D(DXViewport *viewport) {
_viewport = *viewport;
glViewport(_viewport._x, _height - _viewport._height, _viewport._width, _viewport._height);
return true;
}
bool BaseRenderOpenGL3D::setProjection2D() {
DXMatrix matrix2D;
DXMatrixIdentity(&matrix2D);
DXMatrixOrthoOffCenterLH(&matrix2D, 0, _width, _height, 0, 0.0f, 1.0f);
// convert DX [0, 1] depth range to OpenGL [-1, 1] depth range.
matrix2D.matrix._33 = 2.0f;
matrix2D.matrix._43 = -1.0f;
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrix2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return true;
}
// implements SetTransform() D3DTS_WORLD
bool BaseRenderOpenGL3D::setWorldTransform(const DXMatrix &transform) {
_worldMatrix = transform;
DXMatrix newModelViewTransform, world = transform;
DXMatrixMultiply(&newModelViewTransform, &world, &_viewMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(newModelViewTransform);
return true;
}
// implements SetTransform() D3DTS_WIEW
bool BaseRenderOpenGL3D::setViewTransform(const DXMatrix &transform) {
_viewMatrix = transform;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(transform);
return true;
}
// implements SetTransform() D3DTS_PROJECTION
bool BaseRenderOpenGL3D::setProjectionTransform(const DXMatrix &transform) {
_projectionMatrix = transform;
// convert DX [0, 1] depth range to OpenGL [-1, 1] depth range.
DXMatrix finalMatrix = transform;
float range = 2.0f / (_farClipPlane - _nearClipPlane);
finalMatrix.matrix._33 = range;
finalMatrix.matrix._43 = -(_nearClipPlane + _farClipPlane) * range / 2;
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(finalMatrix);
glMatrixMode(GL_MODELVIEW);
glViewport(_viewportRect.left, _height - _viewportRect.bottom, _viewportRect.width(), _viewportRect.height());
return true;
}
void BaseRenderOpenGL3D::postfilter() {
if (_postFilterMode == kPostFilterOff)
return;
setup2D();
glViewport(0, 0, _width, _height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
1.0f, 1.0f,
-1.0f, 1.0f
};
GLfloat texCoords[] = {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
if (_postFilterMode == kPostFilterBlackAndWhite ||
_postFilterMode == kPostFilterSepia) {
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _postfilterTexture);
g_system->presentBuffer();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, _width, _height, 0);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
GLfloat grayscaleWeights[] = {0.333f, 0.333f, 0.333f};
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, grayscaleWeights);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
g_system->presentBuffer();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, _width, _height, 0);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
GLfloat whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, whiteColor);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
g_system->presentBuffer();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, _width, _height, 0);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
GLfloat luminanceWeights[] = { 0.65f, 0.65f, 0.65f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, luminanceWeights);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (_postFilterMode == kPostFilterSepia) {
g_system->presentBuffer();
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, _width, _height, 0);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
GLfloat sepiaWeights[] = { 1.0f, 0.88f, 0.71f, 1.0f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, sepiaWeights);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
}
BaseSurface *BaseRenderOpenGL3D::createSurface() {
return new BaseSurfaceOpenGL3D(_game, this);
}
Mesh3DS *BaseRenderOpenGL3D::createMesh3DS() {
return new Mesh3DSOpenGL(_game);
}
XMesh *BaseRenderOpenGL3D::createXMesh() {
return new XMeshOpenGL(_game);
}
ShadowVolume *BaseRenderOpenGL3D::createShadowVolume() {
return new ShadowVolumeOpenGL(_game);
}
// ScummVM specific ends <--
} // namespace Wintermute
#endif // defined(USE_OPENGL_GAME)