370 lines
11 KiB
C++
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
|