scummvm/engines/freescape/gfx.cpp

1256 lines
35 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/system.h"
#include "gui/message.h"
#include "engines/util.h"
#if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS) || defined(USE_GLES2)
#include "graphics/opengl/context.h"
#endif
#include "freescape/gfx.h"
#include "freescape/objects/object.h"
namespace Freescape {
Renderer::Renderer(int screenW, int screenH, Common::RenderMode renderMode, bool authenticGraphics) {
_screenW = screenW;
_screenH = screenH;
_currentPixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
_palettePixelFormat = Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0);
_keyColor = -1;
_inkColor = -1;
_paperColor = -1;
_underFireBackgroundColor = -1;
_palette = nullptr;
_colorMap = nullptr;
_colorRemaps = nullptr;
_renderMode = renderMode;
_isAccelerated = false;
_authenticGraphics = authenticGraphics;
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 128; j++) {
_stipples[i][j] = 0;
}
_colorPair[i] = 0;
}
_scale = 1;
}
Renderer::~Renderer() {}
extern byte getCPCPixel(byte cpc_byte, int index, bool mode0);
byte getCPCStipple(byte cpc_byte, int back, int fore) {
int c0 = getCPCPixel(cpc_byte, 0, true);
assert(c0 == back || c0 == fore);
int c1 = getCPCPixel(cpc_byte, 1, true);
assert(c1 == back || c1 == fore);
int c2 = getCPCPixel(cpc_byte, 2, true);
assert(c2 == back || c2 == fore);
int c3 = getCPCPixel(cpc_byte, 3, true);
assert(c3 == back || c3 == fore);
byte st = 0;
if (c0 == fore)
st = st | 0x3;
if (c1 == fore)
st = st | (0x3 << 2);
if (c2 == fore)
st = st | (0x3 << 4);
if (c3 == fore)
st = st | (0x3 << 6);
return st;
}
byte getCGAPixel(byte x, int index) {
if (index == 0)
return (x >> 0) & 0x3;
else if (index == 1)
return (x >> 2) & 0x3;
else if (index == 2)
return (x >> 4) & 0x3;
else if (index == 3)
return (x >> 6) & 0x3;
else
error("Invalid index %d requested", index);
}
byte getCGAStipple(byte x, int back, int fore) {
int c0 = getCGAPixel(x, 0);
assert(c0 == back || c0 == fore || back == fore);
int c1 = getCGAPixel(x, 1);
assert(c1 == back || c1 == fore || back == fore);
int c2 = getCGAPixel(x, 2);
assert(c2 == back || c2 == fore || back == fore);
int c3 = getCGAPixel(x, 3);
assert(c3 == back || c3 == fore || back == fore);
byte st = 0;
if (c0 == fore)
st = st | 0x3;
if (c1 == fore)
st = st | (0x3 << 2);
if (c2 == fore)
st = st | (0x3 << 4);
if (c3 == fore)
st = st | (0x3 << 6);
return st;
}
void Renderer::clearColorPairArray() {
for (int i = 0; i < 16; i++)
_colorPair[i] = 0;
}
void Renderer::fillColorPairArray() {
for (int i = 4; i < 15; i++) {
byte *entry = (*_colorMap)[i];
int c1;
if (_renderMode == Common::kRenderCGA)
c1 = getCGAPixel(entry[0], 0);
else if (_renderMode == Common::kRenderCPC)
c1 = getCPCPixel(entry[0], 0, true);
else
error("Not implemented");
int c2 = -1;
for (int j = 0; j < 4; j++) {
int k, c;
for (k = 0; k < 4; k++) {
if (_renderMode == Common::kRenderCGA)
c = getCGAPixel(entry[j], k);
else if (_renderMode == Common::kRenderCPC)
c = getCPCPixel(entry[j], k, true);
else
error("Not implemented");
if (c1 != c) {
c2 = c;
break;
}
}
if (k != 4)
break;
}
assert(c2 >= 0);
assert((c1 < 16) & (c2 < 16));
_colorPair[i] = byte(c1) | (byte(c2) << 4);
}
}
uint16 duplicate_bits(uint8 byte) {
uint16 result = 0;
for (int i = 0; i < 8; i++) {
// Extract the bit at position i
uint8 bit = (byte >> i) & 1;
// Duplicate the bit
uint16 duplicated_bits = (bit << 1) | bit;
// Position the duplicated bits in the appropriate place in the result
result |= (duplicated_bits << (2 * i));
}
return result;
}
void Renderer::scaleStipplePattern(byte originalPattern[128], byte newPattern[128]) {
// Initialize the new pattern to all 0
memset(newPattern, 0, 128);
for (int i = 0; i < 64; i++) {
// Duplicate the bits of the original pattern
uint16 duplicated_bits = duplicate_bits(originalPattern[i]);
// Position the duplicated bits in the appropriate place in the new pattern
newPattern[2 * i] = (duplicated_bits >> 8) & 0xff;
newPattern[2 * i + 1] = duplicated_bits & 0xff;
}
}
void Renderer::setColorMap(ColorMap *colorMap_) {
_colorMap = colorMap_;
if (_renderMode == Common::kRenderZX || _renderMode == Common::kRenderHercG) {
for (int i = 0; i < 15; i++) {
byte *entry = (*_colorMap)[i];
for (int j = 0; j < 128; j++)
_stipples[i][j] = entry[(j / 4) % 4];
}
} else if (_renderMode == Common::kRenderCPC) {
fillColorPairArray();
for (int i = 4; i < 15; i++) {
byte pair = _colorPair[i];
byte c1 = pair & 0xf;
byte c2 = (pair >> 4) & 0xf;
byte *entry = (*_colorMap)[i];
for (int j = 0; j < 128; j++)
_stipples[i][j] = getCPCStipple(entry[(j / 8) % 4], c1, c2) ;
}
} else if (_renderMode == Common::kRenderCGA) {
fillColorPairArray();
for (int i = 4; i < 15; i++) {
byte pair = _colorPair[i];
byte c1 = pair & 0xf;
byte c2 = (pair >> 4) & 0xf;
byte *entry = (*_colorMap)[i];
for (int j = 0; j < 128; j++)
_stipples[i][j] = getCGAStipple(entry[(j / 8) % 4], c1, c2) ;
}
}
if (_isAccelerated && _authenticGraphics) {
for (int i = 1; i <= 14; i++) {
scaleStipplePattern(_stipples[i], _stipples[15]);
memcpy(_stipples[i], _stipples[15], 128);
scaleStipplePattern(_stipples[i], _stipples[15]);
memcpy(_stipples[i], _stipples[15], 128);
}
}
}
void Renderer::readFromPalette(uint8 index, uint8 &r, uint8 &g, uint8 &b) {
r = _palette[3 * index + 0];
g = _palette[3 * index + 1];
b = _palette[3 * index + 2];
}
uint8 Renderer::indexFromColor(uint8 r, uint8 g, uint8 b) {
for (int i = 0; i < 16; i++) {
if (r == _palette[3 * i + 0] && g == _palette[3 * i + 1] && b == _palette[3 * i + 2])
return i;
}
warning("color %x %x %x not found", r, g, b);
return 0;
}
void Renderer::setColorRemaps(ColorReMap *colorRemaps) {
_colorRemaps = colorRemaps;
if (_renderMode == Common::kRenderZX) {
for (auto &it : *_colorRemaps) {
if (it._key == 1)
_paperColor = it._value;
else if (it._key == 3)
_inkColor = it._value;
}
}
}
bool Renderer::getRGBAtCGA(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple) {
if (index == _keyColor)
return false;
assert (_renderMode == Common::kRenderCGA);
if (index <= 4) { // Solid colors
readFromPalette(index - 1, r1, g1, b1);
r2 = r1;
g2 = g1;
b2 = b1;
return true;
}
stipple = (byte *)_stipples[index - 1];
byte pair = _colorPair[index - 1];
byte c1 = pair & 0xf;
byte c2 = (pair >> 4) & 0xf;
readFromPalette(c1, r1, g1, b1);
readFromPalette(c2, r2, g2, b2);
return true;
}
void Renderer::extractC64Indexes(uint8 cm1, uint8 cm2, uint8 &i1, uint8 &i2) {
if (cm1 == 0xaa && cm2 == 0x5a) {
i1 = 2;
i2 = 3;
} else if (cm1 == 0x4f && cm2 == 0x46) {
i1 = 0;
i2 = 2;
} else if (cm1 == 0x56 && cm2 == 0x45) {
i1 = 0;
i2 = 1;
} else if (cm1 == 0xa0 && cm2 == 0x55) {
i1 = 1;
i2 = 3;
} else if (cm1 == 0x4c && cm2 == 0x54) {
i1 = 1;
i2 = 2;
} else if (cm1 == 0x41 && cm2 == 0x52) {
i1 = 0;
i2 = 3;
// Covered by the default of i1 = 0, i2 = 0
#if 0
} else if (cm1 == 0x5a && cm2 == 0xa5) {
i1 = 0;
i2 = 0;
} else if (cm1 == 0xbb && cm2 == 0xee) {
i1 = 0;
i2 = 0;
} else if (cm1 == 0x5f && cm2 == 0xaf) {
i1 = 0;
i2 = 0;
} else if (cm1 == 0xfb && cm2 == 0xfe) {
i1 = 0;
i2 = 0;
#endif
} else {
i1 = 0;
i2 = 0;
}
}
bool Renderer::getRGBAtC64(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2) {
if (index == _keyColor)
return false;
if (index <= 4) { // Solid colors
selectColorFromFourColorPalette(index - 1, r1, g1, b1);
r2 = r1;
g2 = g1;
b2 = b1;
return true;
}
uint8 i1, i2;
byte *entry = (*_colorMap)[index - 1];
uint8 cm1 = *(entry);
entry++;
uint8 cm2 = *(entry);
extractC64Indexes(cm1, cm2, i1, i2);
selectColorFromFourColorPalette(i1, r1, g1, b1);
selectColorFromFourColorPalette(i2, r2, g2, b2);
return true;
}
bool Renderer::getRGBAtZX(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple) {
if (index == _keyColor)
return false;
byte *entry = (*_colorMap)[index - 1];
if (entry[0] == 0 && entry[1] == 0 && entry[2] == 0 && entry[3] == 0) {
readFromPalette(_paperColor, r1, g1, b1);
readFromPalette(_paperColor, r2, g2, b2);
return true;
}
if (entry[0] == 0xff && entry[1] == 0xff && entry[2] == 0xff && entry[3] == 0xff) {
readFromPalette(_inkColor, r1, g1, b1);
readFromPalette(_inkColor, r2, g2, b2);
return true;
}
stipple = (byte *)_stipples[index - 1];
readFromPalette(_paperColor, r1, g1, b1);
readFromPalette(_inkColor, r2, g2, b2);
return true;
}
bool Renderer::getRGBAtHercules(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple) {
if (index == _keyColor)
return false;
byte *entry = (*_colorMap)[index - 1];
if (entry[0] == 0 && entry[1] == 0 && entry[2] == 0 && entry[3] == 0) {
readFromPalette(0, r1, g1, b1);
readFromPalette(0, r2, g2, b2);
return true;
}
if (entry[0] == 0xff && entry[1] == 0xff && entry[2] == 0xff && entry[3] == 0xff) {
readFromPalette(1, r1, g1, b1);
readFromPalette(1, r2, g2, b2);
return true;
}
stipple = (byte *)_stipples[index - 1];
readFromPalette(0, r1, g1, b1);
readFromPalette(1, r2, g2, b2);
return true;
}
void Renderer::selectColorFromFourColorPalette(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1) {
if (index == 0) {
r1 = 0;
g1 = 0;
b1 = 0;
} else if (index == 1) {
readFromPalette(_underFireBackgroundColor, r1, g1, b1);
} else if (index == 2) {
readFromPalette(_paperColor, r1, g1, b1);
} else if (index == 3) {
readFromPalette(_inkColor, r1, g1, b1);
} else
error("Invalid color");
}
bool Renderer::getRGBAtCPC(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple) {
if (index == _keyColor)
return false;
if (_colorRemaps && _colorRemaps->contains(index)) {
index = (*_colorRemaps)[index];
if (index == 0) {
r1 = g1 = b1 = 0;
r2 = r1;
g2 = g1;
b2 = b1;
return true;
}
readFromPalette(index, r1, g1, b1);
r2 = r1;
g2 = g1;
b2 = b1;
return true;
}
assert (_renderMode == Common::kRenderCPC);
if (index <= 4) { // Solid colors
selectColorFromFourColorPalette(index - 1, r1, g1, b1);
r2 = r1;
g2 = g1;
b2 = b1;
return true;
}
stipple = (byte *)_stipples[index - 1];
byte *entry = (*_colorMap)[index - 1];
uint8 i1 = getCPCPixel(entry[0], 0, true);
uint8 i2 = getCPCPixel(entry[0], 1, true);
selectColorFromFourColorPalette(i1, r1, g1, b1);
selectColorFromFourColorPalette(i2, r2, g2, b2);
return true;
}
uint8 Renderer::mapEGAColor(uint8 index) {
byte *entry = (*_colorMap)[index - 1];
uint8 color = 0;
uint8 acc = 1;
for (int i = 0; i < 4; i++) {
byte be = *entry;
assert (be == 0 || be == 0xff);
if (be == 0xff)
color = color + acc;
acc = acc << 1;
entry++;
}
assert(color < 16);
return color;
}
bool Renderer::getRGBAtEGA(uint8 index, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2) {
uint8 color;
if (_colorPair[index] > 0) {
color = mapEGAColor(_colorPair[index] & 0xf);
readFromPalette(color, r1, g1, b1);
color = mapEGAColor(_colorPair[index] >> 4);
readFromPalette(color, r2, g2, b2);
} else {
color = mapEGAColor(index);
if (_colorRemaps && _colorRemaps->contains(color)) {
color = (*_colorRemaps)[color];
}
readFromPalette(color, r1, g1, b1);
r2 = r1;
g2 = g1;
b2 = b1;
}
return true;
}
bool Renderer::getRGBAt(uint8 index, uint8 ecolor, uint8 &r1, uint8 &g1, uint8 &b1, uint8 &r2, uint8 &g2, uint8 &b2, byte *&stipple) {
if (index == _keyColor && ecolor == 0)
return false;
if (index == 0 && ecolor == 0) {
readFromPalette(0, r1, g1, b1);
r2 = r1;
g2 = g1;
b2 = b1;
return true;
}
if (_renderMode == Common::kRenderAmiga || _renderMode == Common::kRenderAtariST) {
if (_colorPair[index] > 0) {
int color = 0;
color = _colorPair[index] & 0xf;
readFromPalette(color, r1, g1, b1);
color = _colorPair[index] >> 4;
readFromPalette(color, r2, g2, b2);
return true;
} else if (_colorRemaps && _colorRemaps->contains(index)) {
int color = (*_colorRemaps)[index];
_texturePixelFormat.colorToRGB(color, r1, g1, b1);
} else
readFromPalette(index, r1, g1, b1);
if (ecolor > 0)
readFromPalette(ecolor, r2, g2, b2);
else {
r2 = r1;
g2 = g1;
b2 = b1;
}
return true;
} else if (_renderMode == Common::kRenderEGA)
return getRGBAtEGA(index, r1, g1, b1, r2, g2, b2);
else if (_renderMode == Common::kRenderC64)
return getRGBAtC64(index, r1, g1, b1, r2, g2, b2);
else if (_renderMode == Common::kRenderCGA)
return getRGBAtCGA(index, r1, g1, b1, r2, g2, b2, stipple);
else if (_renderMode == Common::kRenderCPC)
return getRGBAtCPC(index, r1, g1, b1, r2, g2, b2, stipple);
else if (_renderMode == Common::kRenderZX)
return getRGBAtZX(index, r1, g1, b1, r2, g2, b2, stipple);
else if (_renderMode == Common::kRenderHercG)
return getRGBAtHercules(index, r1, g1, b1, r2, g2, b2, stipple);
error("Invalid or unsupported render mode");
}
void Renderer::flipVertical(Graphics::Surface *s) {
for (int y = 0; y < s->h / 2; ++y) {
// Flip the lines
byte *line1P = (byte *)s->getBasePtr(0, y);
byte *line2P = (byte *)s->getBasePtr(0, s->h - y - 1);
for (int x = 0; x < s->pitch; ++x)
SWAP(line1P[x], line2P[x]);
}
}
Graphics::Surface *Renderer::convertImageFormatIfNecessary(Graphics::ManagedSurface *msurface) {
if (!msurface)
return nullptr;
Graphics::Surface *surface = new Graphics::Surface();
surface->copyFrom(msurface->rawSurface());
byte *palette = (byte *)malloc(sizeof(byte) * 16 * 3);
msurface->grabPalette(palette, 0, 16); // Maximum should be 16 colours
surface->convertToInPlace(_texturePixelFormat, palette, 16);
free(palette);
return surface;
}
Common::Rect Renderer::viewport() const {
return _screenViewport;
}
bool Renderer::computeScreenViewport() {
int32 screenWidth = g_system->getWidth();
int32 screenHeight = g_system->getHeight();
Common::Rect viewport;
if (g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
// Aspect ratio correction
int32 viewportWidth = MIN<int32>(screenWidth, screenHeight * float(4) / 3);
int32 viewportHeight = MIN<int32>(screenHeight, screenWidth * float(3) / 3);
viewport = Common::Rect(viewportWidth, viewportHeight);
// Pillarboxing
viewport.translate((screenWidth - viewportWidth) / 2,
(screenHeight - viewportHeight) / 2);
} else {
// Aspect ratio correction disabled, just stretch
viewport = Common::Rect(screenWidth, screenHeight);
}
if (viewport == _screenViewport) {
return false;
}
_screenViewport = viewport;
return true;
}
void Renderer::renderPyramid(const Math::Vector3d &origin, const Math::Vector3d &size, const Common::Array<float> *ordinates, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, int type) {
Math::Vector3d vertices[8] = { origin, origin, origin, origin, origin, origin, origin, origin };
switch (type) {
default:
error("Invalid pyramid type: %d", type);
case kEastPyramidType:
vertices[0] += Math::Vector3d(0, 0, size.z());
vertices[1] += Math::Vector3d(0, size.y(), size.z());
vertices[2] += Math::Vector3d(0, size.y(), 0);
vertices[4] += Math::Vector3d(size.x(), (*ordinates)[0], (*ordinates)[3]);
vertices[5] += Math::Vector3d(size.x(), (*ordinates)[2], (*ordinates)[3]);
vertices[6] += Math::Vector3d(size.x(), (*ordinates)[2], (*ordinates)[1]);
vertices[7] += Math::Vector3d(size.x(), (*ordinates)[0], (*ordinates)[1]);
break;
case kWestPyramidType:
vertices[0] += Math::Vector3d(size.x(), 0, 0);
vertices[1] += Math::Vector3d(size.x(), size.y(), 0);
vertices[2] += Math::Vector3d(size.x(), size.y(), size.z());
vertices[3] += Math::Vector3d(size.x(), 0, size.z());
vertices[4] += Math::Vector3d(0, (*ordinates)[0], (*ordinates)[1]);
vertices[5] += Math::Vector3d(0, (*ordinates)[2], (*ordinates)[1]);
vertices[6] += Math::Vector3d(0, (*ordinates)[2], (*ordinates)[3]);
vertices[7] += Math::Vector3d(0, (*ordinates)[0], (*ordinates)[3]);
break;
case kUpPyramidType:
vertices[1] += Math::Vector3d(size.x(), 0, 0);
vertices[2] += Math::Vector3d(size.x(), 0, size.z());
vertices[3] += Math::Vector3d(0, 0, size.z());
vertices[4] += Math::Vector3d((*ordinates)[0], size.y(), (*ordinates)[1]);
vertices[5] += Math::Vector3d((*ordinates)[2], size.y(), (*ordinates)[1]);
vertices[6] += Math::Vector3d((*ordinates)[2], size.y(), (*ordinates)[3]);
vertices[7] += Math::Vector3d((*ordinates)[0], size.y(), (*ordinates)[3]);
break;
case kDownPyramidType:
vertices[0] += Math::Vector3d(size.x(), size.y(), 0);
vertices[1] += Math::Vector3d(0, size.y(), 0);
vertices[2] += Math::Vector3d(0, size.y(), size.z());
vertices[3] += Math::Vector3d(size.x(), size.y(), size.z());
vertices[4] += Math::Vector3d((*ordinates)[2], 0, (*ordinates)[1]);
vertices[5] += Math::Vector3d((*ordinates)[0], 0, (*ordinates)[1]);
vertices[6] += Math::Vector3d((*ordinates)[0], 0, (*ordinates)[3]);
vertices[7] += Math::Vector3d((*ordinates)[2], 0, (*ordinates)[3]);
break;
case kNorthPyramidType:
vertices[0] += Math::Vector3d(0, size.y(), 0);
vertices[1] += Math::Vector3d(size.x(), size.y(), 0);
vertices[2] += Math::Vector3d(size.x(), 0, 0);
vertices[4] += Math::Vector3d((*ordinates)[0], (*ordinates)[3], size.z());
vertices[5] += Math::Vector3d((*ordinates)[2], (*ordinates)[3], size.z());
vertices[6] += Math::Vector3d((*ordinates)[2], (*ordinates)[1], size.z());
vertices[7] += Math::Vector3d((*ordinates)[0], (*ordinates)[1], size.z());
break;
case kSouthPyramidType:
vertices[0] += Math::Vector3d(0, 0, size.z());
vertices[1] += Math::Vector3d(size.x(), 0, size.z());
vertices[2] += Math::Vector3d(size.x(), size.y(), size.z());
vertices[3] += Math::Vector3d(0, size.y(), size.z());
vertices[4] += Math::Vector3d((*ordinates)[0], (*ordinates)[1], 0);
vertices[5] += Math::Vector3d((*ordinates)[2], (*ordinates)[1], 0);
vertices[6] += Math::Vector3d((*ordinates)[2], (*ordinates)[3], 0);
vertices[7] += Math::Vector3d((*ordinates)[0], (*ordinates)[3], 0);
break;
}
Common::Array<Math::Vector3d> face;
byte *stipple = nullptr;
uint8 r1, g1, b1, r2, g2, b2;
uint color = (*colours)[0];
uint ecolor = ecolours ? (*ecolours)[0] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(vertices[4]);
face.push_back(vertices[5]);
face.push_back(vertices[1]);
face.push_back(vertices[0]);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
face.clear();
}
color = (*colours)[1];
ecolor = ecolours ? (*ecolours)[1] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(vertices[5]);
face.push_back(vertices[6]);
face.push_back(vertices[2]);
face.push_back(vertices[1]);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
face.clear();
}
color = (*colours)[2];
ecolor = ecolours ? (*ecolours)[2] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(vertices[6]);
face.push_back(vertices[7]);
face.push_back(vertices[3]);
face.push_back(vertices[2]);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
face.clear();
}
color = (*colours)[3];
ecolor = ecolours ? (*ecolours)[3] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(vertices[7]);
face.push_back(vertices[4]);
face.push_back(vertices[0]);
face.push_back(vertices[3]);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
face.clear();
}
color = (*colours)[4];
ecolor = ecolours ? (*ecolours)[4] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(vertices[0]);
face.push_back(vertices[1]);
face.push_back(vertices[2]);
face.push_back(vertices[3]);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
face.clear();
}
color = (*colours)[5];
ecolor = ecolours ? (*ecolours)[5] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(vertices[7]);
face.push_back(vertices[6]);
face.push_back(vertices[5]);
face.push_back(vertices[4]);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
}
void Renderer::renderCube(const Math::Vector3d &originalOrigin, const Math::Vector3d &size, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, float offset) {
Math::Vector3d origin = originalOrigin;
byte *stipple = nullptr;
uint8 r1, g1, b1, r2, g2, b2;
Common::Array<Math::Vector3d> face;
uint color = (*colours)[0];
uint ecolor = ecolours ? (*ecolours)[0] : 0;
if (size.x() <= 1) {
origin.x() += offset;
} else if (size.y() <= 1) {
origin.y() += offset;
} else if (size.z() <= 1) {
origin.z() += offset;
}
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.push_back(origin);
face.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x(), origin.y() + size.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x(), origin.y() + size.y(), origin.z()));
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
color = (*colours)[1];
ecolor = ecolours ? (*ecolours)[1] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.clear();
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y(), origin.z()));
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
color = (*colours)[2];
ecolor = ecolours ? (*ecolours)[2] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.clear();
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y(), origin.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z()));
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
color = (*colours)[3];
ecolor = ecolours ? (*ecolours)[3] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.clear();
face.push_back(Math::Vector3d(origin.x(), origin.y() + size.y(), origin.z()));
face.push_back(Math::Vector3d(origin.x(), origin.y() + size.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z()));
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
color = (*colours)[4];
ecolor = ecolours ? (*ecolours)[4] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.clear();
face.push_back(Math::Vector3d(origin.x(), origin.y() + size.y(), origin.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y(), origin.z()));
face.push_back(origin);
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
color = (*colours)[5];
ecolor = ecolours ? (*ecolours)[5] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
face.clear();
face.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
face.push_back(Math::Vector3d(origin.x(), origin.y() + size.y(), origin.z() + size.z()));
renderFace(face);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(face);
useStipple(false);
}
}
}
void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math::Vector3d &originalSize, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, float offset) {
Math::Vector3d size = originalSize;
Math::Vector3d origin = originalOrigin;
if (size.x() > 0 && size.y() > 0 && size.z() > 0) {
/* According to https://www.shdon.com/freescape/
If the bounding box is has all non-zero dimensions
and is thus a cube, the rectangle is rendered as a
slope at an angle with the plane of the polygon being
parallel to the X axis (its lower edge extends from
the base corner along the positive X direction).
In that case, when the player is at a Z coordinate
greater than (i.e. north of) the base corner,
it is rendered in the front face material, otherwise it
is rendered in the back face material. This implies
that the engine does its material selection as though
it were a rectangle perpendicular to the Z axis.
TODO: fix this case.
*/
if (size.x() <= size.y() && size.x() <= size.z())
size.x() = 0;
else if (size.y() <= size.x() && size.y() <= size.z())
size.y() = 0;
else if (size.z() <= size.x() && size.z() <= size.y())
size.z() = 0;
else
error("Invalid size!");
}
float dx, dy, dz;
uint8 r1, g1, b1, r2, g2, b2;
byte *stipple = nullptr;
Common::Array<Math::Vector3d> vertices;
uint color = 0;
uint ecolor = 0;
if (size.x() == 0) {
origin.x() += offset;
} else if (size.y() == 0) {
origin.y() += offset;
} else if (size.z() == 0) {
origin.z() += offset;
}
for (int i = 0; i < 2; i++) {
color = (*colours)[i];
ecolor = ecolours ? (*ecolours)[i] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
vertices.clear();
vertices.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z()));
dx = dy = dz = 0.0;
if (size.x() == 0) {
dy = size.y();
} else if (size.y() == 0) {
dx = size.x();
} else if (size.z() == 0) {
dx = size.x();
}
vertices.push_back(Math::Vector3d(origin.x() + dx, origin.y() + dy, origin.z() + dz));
vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
vertices.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z()));
dx = dy = dz = 0.0;
if (size.x() == 0) {
dz = size.z();
} else if (size.y() == 0) {
dz = size.z();
} else if (size.z() == 0) {
dy = size.y();
}
vertices.push_back(Math::Vector3d(origin.x() + dx, origin.y() + dy, origin.z() + dz));
vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
renderFace(vertices);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(vertices);
useStipple(false);
}
}
}
polygonOffset(false);
}
void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d &size, const Common::Array<float> *originalOrdinates, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, float offset) {
Common::Array<float> *ordinates = new Common::Array<float>(*originalOrdinates);
uint8 r1, g1, b1, r2, g2, b2;
byte *stipple = nullptr;
if (ordinates->size() % 3 > 0 && ordinates->size() > 0)
error("Invalid polygon with size %f %f %f and ordinates %d", size.x(), size.y(), size.z(), ordinates->size());
Common::Array<Math::Vector3d> vertices;
uint color = 0;
uint ecolor = 0;
if (ordinates->size() == 6) { // Line
polygonOffset(true);
color = (*colours)[0];
ecolor = ecolours ? (*ecolours)[0] : 0;
assert(getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)); // It will never return false?
setStippleData(stipple);
useColor(r1, g1, b1);
for (uint i = 0; i < ordinates->size(); i = i + 3)
vertices.push_back(Math::Vector3d((*ordinates)[i], (*ordinates)[i + 1], (*ordinates)[i + 2]));
renderFace(vertices);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(vertices);
useStipple(false);
}
vertices.clear();
color = (*colours)[1];
ecolor = ecolours ? (*ecolours)[1] : 0;
assert(getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)); // It will never return false?
setStippleData(stipple);
useColor(r1, g1, b1);
for (int i = ordinates->size(); i > 0; i = i - 3)
vertices.push_back(Math::Vector3d((*ordinates)[i - 3], (*ordinates)[i - 2], (*ordinates)[i - 1]));
renderFace(vertices);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(vertices);
useStipple(false);
}
polygonOffset(false);
} else {
if (size.x() == 0) {
for (int i = 0; i < int(ordinates->size()); i++) {
if (i % 3 == 0)
(*ordinates)[i] += (offset);
}
} else if (size.y() == 0) {
for (int i = 0; i < int(ordinates->size()); i++) {
if (i % 3 == 1)
(*ordinates)[i] += (offset);
}
} else if (size.z() == 0) {
for (int i = 0; i < int(ordinates->size()); i++) {
if (i % 3 == 2)
(*ordinates)[i] += (offset);
}
}
color = (*colours)[0];
ecolor = ecolours ? (*ecolours)[0] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
for (uint i = 0; i < ordinates->size(); i = i + 3) {
vertices.push_back(Math::Vector3d((*ordinates)[i], (*ordinates)[i + 1], (*ordinates)[i + 2]));
}
renderFace(vertices);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(vertices);
useStipple(false);
}
}
vertices.clear();
color = (*colours)[1];
ecolor = ecolours ? (*ecolours)[1] : 0;
if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
setStippleData(stipple);
useColor(r1, g1, b1);
for (int i = ordinates->size(); i > 0; i = i - 3) {
vertices.push_back(Math::Vector3d((*ordinates)[i - 3], (*ordinates)[i - 2], (*ordinates)[i - 1]));
}
renderFace(vertices);
if (r1 != r2 || g1 != g2 || b1 != b2) {
useStipple(true);
useColor(r2, g2, b2);
renderFace(vertices);
useStipple(false);
}
}
}
polygonOffset(false);
delete(ordinates);
}
void Renderer::drawBackground(uint8 color) {
uint8 r1, g1, b1;
uint8 r2, g2, b2;
if (_colorRemaps && _colorRemaps->contains(color)) {
color = (*_colorRemaps)[color];
readFromPalette(color, r1, g1, b1);
clear(r1, g1, b1);
return;
}
if (color == 0) {
clear(0, 0, 0);
return;
}
byte *stipple = nullptr;
getRGBAt(color, 0, r1, g1, b1, r2, g2, b2, stipple);
clear(r1, g1, b1);
}
void Renderer::drawEclipse(byte color1, byte color2, float progress) {
Math::Vector3d sunPosition(-5000, 1200, 0);
float radius = 400.0;
drawCelestialBody(sunPosition, radius, color1);
Math::Vector3d moonPosition(-5000, 1200, 800 * progress);
drawCelestialBody(moonPosition, radius, color2);
}
Graphics::RendererType determinateRenderType() {
Common::String rendererConfig = ConfMan.get("renderer");
Graphics::RendererType desiredRendererType = Graphics::Renderer::parseTypeCode(rendererConfig);
Graphics::RendererType matchingRendererType = Graphics::Renderer::getBestMatchingAvailableType(desiredRendererType,
#if defined(USE_OPENGL_GAME)
Graphics::kRendererTypeOpenGL |
#endif
#if defined(USE_OPENGL_SHADERS)
Graphics::kRendererTypeOpenGLShaders |
#endif
#if defined(USE_TINYGL)
Graphics::kRendererTypeTinyGL |
#endif
0);
if (matchingRendererType != desiredRendererType && desiredRendererType != Graphics::kRendererTypeDefault) {
// Display a warning if unable to use the desired renderer
warning("Unable to create a '%s' renderer", rendererConfig.c_str());
}
#if defined(USE_OPENGL_GAME) && !defined(USE_GLES2)
if (matchingRendererType == Graphics::kRendererTypeOpenGL)
return matchingRendererType;
#endif
#if defined(USE_OPENGL_SHADERS)
if (matchingRendererType == Graphics::kRendererTypeOpenGLShaders)
return matchingRendererType;
#endif
#if defined(USE_TINYGL)
return Graphics::kRendererTypeTinyGL;
#endif
return Graphics::kRendererTypeDefault;
}
Renderer *createRenderer(int screenW, int screenH, Common::RenderMode renderMode, bool authenticGraphics) {
Graphics::PixelFormat pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
Graphics::RendererType rendererType = determinateRenderType();
bool isAccelerated = rendererType != Graphics::kRendererTypeTinyGL;
if (isAccelerated) {
initGraphics3d(screenW, screenH);
} else {
initGraphics(screenW, screenH, &pixelFormat);
}
#if defined(USE_OPENGL_GAME) && !defined(USE_GLES2)
if (rendererType == Graphics::kRendererTypeOpenGL) {
return CreateGfxOpenGL(screenW, screenH, renderMode, authenticGraphics);
}
#endif
#if defined(USE_OPENGL_SHADERS)
if (rendererType == Graphics::kRendererTypeOpenGLShaders) {
return CreateGfxOpenGLShader(screenW, screenH, renderMode, authenticGraphics);
}
#endif
#if defined(USE_TINYGL)
if (rendererType == Graphics::kRendererTypeTinyGL) {
// TinyGL graphics are always authentic
return CreateGfxTinyGL(screenW, screenH, renderMode);
}
#endif
GUI::MessageDialog dialog("No available renderers enabled");
// TODO: improve message with other renders
dialog.runModal();
return nullptr;
}
} // End of namespace Freescape