Files
2026-02-02 04:50:13 +01:00

370 lines
11 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 "watchmaker/game.h"
#if defined(USE_OPENGL_GAME)
#include "graphics/pixelformat.h"
#include "watchmaker/3d/render/opengl_2d.h"
#include "watchmaker/3d/render/opengl_renderer.h"
#include "watchmaker/game.h"
#include "watchmaker/rect.h"
#include "watchmaker/render.h"
#include "watchmaker/renderer.h"
#include "watchmaker/sdl_wrapper.h"
#include "watchmaker/tga_util.h"
#include "watchmaker/utils.h"
#include "watchmaker/work_dirs.h"
#include "graphics/opengl/system_headers.h"
namespace Watchmaker {
Common::Rect gBlitterExtends;
int gStencilBitDepth;
unsigned int CurLoaderFlags;
//*********************************************************************************************
unsigned int Renderer::BitmapList::acquirePosition() {
unsigned int pos = 1;
while (!bitmaps[pos].isEmpty()) {
pos++;
}
if (pos > MAX_BITMAP_LIST)
return 0;
if (pos > _numBitmaps)
_numBitmaps = pos;
return pos;
}
unsigned int Renderer::getBitmapDimX(int32 id) const {
return _bitmapList.bitmaps[id].DimX;
}
unsigned int Renderer::getBitmapDimY(int32 id) const {
return _bitmapList.bitmaps[id].DimY;
}
unsigned int Renderer::getBitmapRealDimX(int32 id) const {
return _bitmapList.bitmaps[id].RealDimX;
}
unsigned int Renderer::getBitmapRealDimY(int32 id) const {
return _bitmapList.bitmaps[id].RealDimY;
}
//************************************************************************************************************************
void rUpdateExtends(int x1, int y1, int x2, int y2) {
//Update extends
if (x1 < gBlitterExtends.left)
gBlitterExtends.left = x1;
if (y1 < gBlitterExtends.top)
gBlitterExtends.top = y1;
if (x2 > gBlitterExtends.right)
gBlitterExtends.right = x2;
if (y2 > gBlitterExtends.bottom)
gBlitterExtends.bottom = y2;
}
//************************************************************************************************************************
void rGetExtends(int *x1, int *y1, int *x2, int *y2) {
*x1 = gBlitterExtends.left;
*y1 = gBlitterExtends.top;
*x2 = gBlitterExtends.right;
*y2 = gBlitterExtends.bottom;
}
//************************************************************************************************************************
void rResetExtends() {
gBlitterExtends.left = SHRT_MAX;
gBlitterExtends.top = SHRT_MAX;
gBlitterExtends.right = SHRT_MIN;
gBlitterExtends.bottom = SHRT_MIN;
}
// TODO: Move this to Renderer
extern Common::Rect gBlitterViewport;
bool gClipToBlitterViewport(int *sposx, int *sposy, int *sdimx, int *sdimy,
int *dposx, int *dposy) {
int dwWidth, dwHeight;
dwWidth = (gBlitterViewport.right - gBlitterViewport.left);
dwHeight = (gBlitterViewport.bottom - gBlitterViewport.top);
if (((*dposx) + (*sdimx)) > dwWidth) {
(*sdimx) = (*sdimx) - ((*dposx) + (*sdimx) - dwWidth);
}
if (((*dposy) + (*sdimy)) > dwHeight) {
(*sdimy) = (*sdimy) - ((*dposy) + (*sdimy) - dwHeight);
}
if ((*dposx) < gBlitterViewport.left) {
(*sposx) += gBlitterViewport.left - (*dposx);
(*sdimx) -= gBlitterViewport.left - (*dposx);
(*dposx) = gBlitterViewport.left;
}
if ((*dposy) < gBlitterViewport.top) {
(*sposy) += gBlitterViewport.top - (*dposy);
(*sdimy) -= gBlitterViewport.top - (*dposy);
(*dposy) = gBlitterViewport.top;
}
if (((*sdimx) <= 0) || ((*sdimy) <= 0)) {
return false;
}
return true;
}
void renderTexture(WGame &game, gTexture &bitmap, Common::Rect srcRect, Common::Rect dstRect) {
checkGlError("Entering renderTexture");
glClearColor(0, 0, 1, 0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_ALPHA_TEST);
glDisable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
bitmap._texture->bind();
glLoadIdentity();
glTranslatef(0, 0, 0.0);
float bottomSrc = ((float)srcRect.bottom) / bitmap.RealDimY;
float topSrc = ((float)srcRect.top) / bitmap.RealDimY;
float leftSrc = ((float)srcRect.left) / bitmap.RealDimX;
float rightSrc = ((float)srcRect.right) / bitmap.RealDimX;
Common::Rect viewport = game._renderer->_viewport;
float bottomDst = 1.0 - ((dstRect.bottom == 0 ? 0 : ((double)dstRect.bottom) / viewport.height()) * 2.0);
float topDst = 1.0 - ((dstRect.top == 0 ? 0 : ((double)dstRect.top) / viewport.height()) * 2.0);
float leftDst = ((dstRect.left == 0 ? 0 : ((double)dstRect.left) / viewport.width()) * 2.0) - 1.0;
float rightDst = ((dstRect.right == 0 ? 0 : ((double)dstRect.right) / viewport.width()) * 2.0) - 1.0;
glBegin(GL_QUADS);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glTexCoord2f(leftSrc, bottomSrc); // Bottom Left
glVertex3f(leftDst, bottomDst, 0.0f);
glTexCoord2f(rightSrc, bottomSrc); // Bottom Right
glVertex3f(rightDst, bottomDst, 0.0f);
glTexCoord2f(rightSrc, topSrc); // Top Right
glVertex3f(rightDst, topDst, 0.0f);
glTexCoord2f(leftSrc, topSrc); // Top Left
glVertex3f(leftDst, topDst, 0.0f);
glEnd();
glFlush();
checkGlError("Exiting renderTexture");
}
void gTexture::render(WGame &game, Common::Rect src, Common::Rect dst) {
// Render self
if (_texture) {
renderTexture(game, *this, src, dst);
}
for (uint i = 0; i < _blitsOnTop.size(); i++) {
_blitsOnTop[i].texture->render(game, _blitsOnTop[i].src, _blitsOnTop[i].dst);
}
}
void Renderer::blitScreenBuffer() {
checkGlError("Entering rBlitScreenBuffer");
g_renderer->enter2Dmode();
_bitmapList.bitmaps[BACK_BUFFER].render(*_game, _game->_renderer->_viewport, _game->_renderer->_viewport);
g_renderer->exit2Dmode();
checkGlError("Exiting rBlitScreenBuffer");
}
void Renderer::clearBitmap(int dst, int dposx, int dposy, int sdimx, int sdimy, unsigned char r, unsigned char g, unsigned char b) {
warning("STUBBED: rClear(%d, %d, %d, %d, %d", dst, dposx, dposy, sdimx, sdimy);
_bitmapList.bitmaps[dst].clear();
}
//************************************************************************************************************************
void rBlitter(WGame &game, int dst, int src, int dposx, int dposy,
int sposx, int sposy, int sdimx, int sdimy) {
auto &bitmapList = game._renderer->_bitmapList.bitmaps;
// TODO: This currently gets called a bit too much.
warning("TODO: Stubbed rBlitter(%s, %d, %d, %d, %d, %d, %d, %d, %d)", bitmapList[src].name.c_str(), dst, src, dposx, dposy, sposx, sposy, sdimx, sdimy);
auto &bitmap = bitmapList[src];
assert(dst == 0);
auto &dstBitmap = bitmapList[dst];
checkGlError("rBlitter Start");
glEnable(GL_TEXTURE_2D);
int dwWidth, dwHeight;
dwWidth = game._renderer->_viewport.width();
dwHeight = game._renderer->_viewport.height();
if ((sdimx <= 0)) {
sdimx = bitmapList[src].DimX;
}
if ((sdimy <= 0)) {
sdimy = bitmapList[src].DimY;
}
if ((dposx >= dwWidth) || (dposy >= dwHeight) || (sposx >= dwWidth) || (sposy >= dwHeight) ||
((dposx + sdimx) <= 0) || ((dposy + sdimy) <= 0) || ((sposx + sdimx) <= 0) || ((sposy + sdimy) <= 0)) {
return;
}
if (dst == 0) {
if (!gClipToBlitterViewport(&sposx, &sposy, &sdimx, &sdimy, &dposx, &dposy)) {
error("gClipToBlitterViewport report an error");
return;
}
rUpdateExtends(dposx, dposy, dposx + sdimx, dposy + sdimy);
}
if ((sdimx == 0) && (sdimy == 0)) {
sdimx = bitmapList[src].DimX;
sdimy = bitmapList[src].DimY;
}
{
Common::Rect srcRect;
// Source rect
srcRect.top = sposy;
srcRect.left = sposx;
srcRect.right = sposx + sdimx;
srcRect.bottom = sposy + sdimy;
Common::Rect dstRect;
// Destination rect
// Convention in dpos is that 0,0 is upper left hand corner, increasing down the y-axis.
dstRect.top = dposy;
dstRect.left = dposx;
dstRect.right = dposx + sdimx;
dstRect.bottom = dposy + sdimy;
if (((dstRect.bottom - dstRect.top) <= 0) || ((dstRect.right - dstRect.left) <= 0) || ((srcRect.bottom - srcRect.top) <= 0) || ((srcRect.right - srcRect.left) <= 0) ||
(dstRect.right <= 0) || (srcRect.right <= 0) || (dstRect.bottom < 0) || (srcRect.bottom < 0)) {
// DebugLogWindow("gBlitter: blit not needed: dimx:%d dimy:%d", ( sr.top-sr.bottom ),( sr.left-sr.right ));
return;
}
dstBitmap.blitInto(&bitmap, srcRect, dstRect);
}
checkGlError("rBlitter End");
// DebugLogFile("gBlitter(%d %d)",dst,src);
//gBlitter(d, s, sposx, sposy, sdimx, sdimy, dposx, dposy, 0);
//#endif
}
// Straight from Wintermute:
void applyColorKey(Graphics::Surface &surf, byte ckRed, byte ckGreen, byte ckBlue, bool replaceAlpha) {
// this is taken from Graphics::TransparentSurface
// only difference is that we set the pixel
// color to transparent black, like D3DX,
// if it matches the color key
for (int y = 0; y < surf.h; y++) {
for (int x = 0; x < surf.w; x++) {
uint32 pix = ((uint32 *)surf.getPixels())[y * surf.w + x];
uint8 r, g, b, a;
surf.format.colorToARGB(pix, a, r, g, b);
if (r == ckRed && g == ckGreen && b == ckBlue) {
a = 0;
r = 0;
g = 0;
b = 0;
((uint32 *)surf.getPixels())[y * surf.w + x] = surf.format.ARGBToColor(a, r, g, b);
} else if (replaceAlpha) {
a = 255;
((uint32 *)surf.getPixels())[y * surf.w + x] = surf.format.ARGBToColor(a, r, g, b);
}
}
}
}
int rLoadBitmapImage(WGame &game, const char *TextName, unsigned char flags) {
WorkDirs &workDirs = game.workDirs;
if (flags & rTEXTURESURFACE) {
warning("TODO: support texture surface loading");
// return ((int) gLoadTexture(TextName, flags));
}
assert(TextName);
auto stream = workDirs.resolveFile(TextName);
if (!stream) {
warning("gLoadBitmapImage: Cannot find %s.", TextName);
return -1;
}
const Graphics::PixelFormat RGBA32 = Graphics::PixelFormat::createFormatRGBA32();
unsigned int pos = game._renderer->_bitmapList.acquirePosition();
if (pos == 0) {
warning("rLoadBitmap: Can't create more bitmaps");
return -1;
}
gTexture *Texture = &game._renderer->_bitmapList.bitmaps[pos];
*Texture = gTexture();
Texture->Flags = CurLoaderFlags;
auto surface = ReadTgaImage(TextName, *stream, RGBA32, Texture->Flags);
applyColorKey(*surface, 0, 0, 0, false);
auto texData = createTextureFromSurface(*surface, GL_RGBA);
Texture->_texture = createGLTexture();
Texture->_texture->assignData(*texData);
Texture->name = TextName;
if (flags & rSURFACESTRETCH) { // Also rSURFACEFLIP
static bool warned = false;
if (!warned) {
warning("TODO: rSURFACESTRETCH");
warned = true;
}
// HACK: Just set a dimension at all:
Texture->DimX = surface->w;
Texture->DimY = surface->h;
} else {
Texture->DimX = surface->w;
Texture->DimY = surface->h;
}
Texture->RealDimX = surface->w;
Texture->RealDimY = surface->h;
// TODO: Colour-keying
return pos;
}
void rSetLoaderFlags(unsigned int NewLoaderFlags) {
CurLoaderFlags = NewLoaderFlags;
}
} // End of namespace Watchmaker
#endif // USE_OPENGL_GAME