scummvm/engines/sludge/backdrop.cpp

557 lines
15 KiB
C++
Raw Normal View History

/* 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 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/debug.h"
2017-06-05 19:10:47 +02:00
#include "common/rect.h"
#include "image/png.h"
#include "graphics/surface.h"
#include "graphics/transparent_surface.h"
2017-05-29 10:27:23 +02:00
#include "graphics/palette.h"
2017-06-05 19:10:47 +02:00
#include "sludge/allfiles.h"
#include "sludge/backdrop.h"
#include "sludge/event.h"
2017-06-05 19:10:47 +02:00
#include "sludge/fileset.h"
#include "sludge/graphics.h"
#include "sludge/imgloader.h"
2017-06-05 19:10:47 +02:00
#include "sludge/moreio.h"
#include "sludge/newfatal.h"
2017-12-19 21:24:31 +01:00
#include "sludge/speech.h"
2017-07-15 15:33:43 +02:00
#include "sludge/statusba.h"
2017-06-05 19:10:47 +02:00
#include "sludge/zbuffer.h"
#include "sludge/sludge.h"
#include "sludge/sludger.h"
#include "sludge/variable.h"
2017-07-20 00:41:13 +02:00
#include "sludge/version.h"
2017-05-26 21:25:11 +02:00
namespace Sludge {
2017-07-20 00:41:13 +02:00
Parallax::Parallax() {
_parallaxLayers.clear();
}
Parallax::~Parallax() {
kill();
}
void Parallax::kill() {
ParallaxLayers::iterator it;
for (it = _parallaxLayers.begin(); it != _parallaxLayers.end(); ++it) {
(*it)->surface.free();
delete (*it);
(*it) = nullptr;
}
_parallaxLayers.clear();
}
bool Parallax::add(uint16 v, uint16 fracX, uint16 fracY) {
setResourceForFatal(v);
if (!g_sludge->_resMan->openFileFromNum(v))
return fatal("Can't open parallax image");
ParallaxLayer *nP = new ParallaxLayer;
if (!checkNew(nP))
return false;
_parallaxLayers.push_back(nP);
if (!ImgLoader::loadImage(g_sludge->_resMan->getData(), &nP->surface, 0))
return false;
nP->fileNum = v;
nP->fractionX = fracX;
nP->fractionY = fracY;
// 65535 is the value of AUTOFIT constant in Sludge
if (fracX == 65535) {
nP->wrapS = false;
2017-07-20 00:41:13 +02:00
// if (nP->surface.w < _winWidth) {
// fatal("For AUTOFIT parallax backgrounds, the image must be at least as wide as the game window/screen.");
// return false;
// }
} else {
nP->wrapS = true;
}
if (fracY == 65535) {
nP->wrapT = false;
2017-07-20 00:41:13 +02:00
// if (nP->surface.h < _winHeight) {
// fatal("For AUTOFIT parallax backgrounds, the image must be at least as tall as the game window/screen.");
// return false;
// }
} else {
nP->wrapT = true;
}
// TODO: reinterpret this part
#if 0
if (nP->wrapS)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
if (nP->wrapT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
g_sludge->_resMan->finishAccess();
setResourceForFatal(-1);
return true;
}
void Parallax::draw() {
// draw parallaxStuff
if (!_parallaxLayers.empty()) {
// TODO: simulate image repeating effect
warning("Drawing parallaxStuff");
#if 0
// display parallax from bottom to top
ParallaxLayers::iterator it;
for (it = _parallax.begin(); it != _parallax.end(); ++it) {
(*it)->cameraX = sortOutPCamera(cameraX, (*it)->fractionX, (int)(sceneWidth - (float)winWidth / cameraZoom), (int)((*it)->surface.w - (float)winWidth / cameraZoom));
(*it)->cameraY = sortOutPCamera(cameraY, (*it)->fractionY, (int)(sceneHeight - (float)winHeight / cameraZoom), (int)((*it)->surface.h - (float)winHeight / cameraZoom));
uint w = ((*it)->wrapS) ? sceneWidth : (*it)->surface.w;
uint h = ((*it)->wrapT) ? sceneHeight : (*it)->surface.h;
const GLfloat vertices[] = {
(GLfloat) - (*it)->cameraX, (GLfloat) - (*it)->cameraY, 0.1f,
w - (*it)->cameraX, (GLfloat) - (*it)->cameraY, 0.1f,
(GLfloat) - (*it)->cameraX, h - (*it)->cameraY, 0.1f,
w - (*it)->cameraX, h - (*it)->cameraY, 0.1f
};
const GLfloat texCoords[] = {
0.0f, 0.0f,
texw, 0.0f,
0.0f, texh,
texw, texh
};
drawQuad(shader.smartScaler, vertices, 1, texCoords);
}
#endif
}
}
void Parallax::save(Common::WriteStream *stream) {
ParallaxLayers::iterator it;
for (it = _parallaxLayers.begin(); it != _parallaxLayers.end(); ++it) {
stream->writeByte(1);
stream->writeUint16BE((*it)->fileNum);
stream->writeUint16BE((*it)->fractionX);
stream->writeUint16BE((*it)->fractionY);
}
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::nosnapshot() {
if (_snapshotSurface.getPixels())
_snapshotSurface.free();
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::saveSnapshot(Common::WriteStream *stream) {
if (_snapshotSurface.getPixels()) {
stream->writeByte(1); // 1 for snapshot follows
2017-07-20 00:41:13 +02:00
Image::writePNG(*stream, _snapshotSurface);
} else {
stream->writeByte(0);
}
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::snapshot() {
nosnapshot();
if (!freeze())
return false;
2017-07-20 02:48:21 +02:00
// draw snapshot to rendersurface
2017-07-15 15:33:43 +02:00
displayBase();
2017-12-19 21:24:31 +01:00
_vm->_speechMan->display();
drawStatusBar();
2017-07-15 15:33:43 +02:00
// copy backdrop to snapshot
2017-07-20 02:48:21 +02:00
_snapshotSurface.copyFrom(_renderSurface);
2017-07-15 15:33:43 +02:00
unfreeze(false);
return true;
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::restoreSnapshot(Common::SeekableReadStream *stream) {
if (!(ImgLoader::loadImage(stream, &_snapshotSurface))) {
return false;
}
return true;
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::killBackDrop() {
if (_backdropSurface.getPixels())
_backdropSurface.free();
_backdropExists = false;
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::killLightMap() {
if (_lightMap.getPixels()) {
_lightMap.free();
}
2017-07-20 00:41:13 +02:00
_lightMapNumber = 0;
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::reserveBackdrop() {
_cameraX = 0;
_cameraY = 0;
_vm->_evtMan->mouseX() = (int)((float)_vm->_evtMan->mouseX() * _cameraZoom);
_vm->_evtMan->mouseY() = (int)((float)_vm->_evtMan->mouseY() * _cameraZoom);
2017-07-20 00:41:13 +02:00
_cameraZoom = 1.0;
_vm->_evtMan->mouseX() = (int)((float)_vm->_evtMan->mouseX() / _cameraZoom);
_vm->_evtMan->mouseY() = (int)((float)_vm->_evtMan->mouseY() / _cameraZoom);
2017-08-03 08:04:00 +02:00
_backdropSurface.create(_sceneWidth, _sceneHeight, *_vm->getScreenPixelFormat());
return true;
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::killAllBackDrop() {
killLightMap();
killBackDrop();
g_sludge->_gfxMan->killParallax();
killZBuffer();
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::resizeBackdrop(int x, int y) {
2017-08-02 16:35:09 +02:00
debugC(1, kSludgeDebugGraphics, "Load HSI");
2017-07-20 00:41:13 +02:00
_sceneWidth = x;
_sceneHeight = y;
return reserveBackdrop();
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::killResizeBackdrop(int x, int y) {
killAllBackDrop();
return resizeBackdrop(x, y);
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::loadBackDrop(int fileNum, int x, int y) {
2017-08-02 16:35:09 +02:00
debugC(1, kSludgeDebugGraphics, "Load back drop of num %i at position %i, %i", fileNum, x, y);
setResourceForFatal(fileNum);
2017-07-18 19:03:45 +02:00
if (!g_sludge->_resMan->openFileFromNum(fileNum)) {
fatal("Can't load overlay image");
return;
}
2017-07-18 19:03:45 +02:00
if (!loadHSI(g_sludge->_resMan->getData(), x, y, false)) {
2017-07-20 00:41:13 +02:00
Common::String mess = Common::String::format("Can't paste overlay image outside scene dimensions\n\nX = %i\nY = %i\nWidth = %i\nHeight = %i", x, y, _sceneWidth, _sceneHeight);
fatal(mess);
}
2017-07-18 19:03:45 +02:00
g_sludge->_resMan->finishAccess();
setResourceForFatal(-1);
// reset zBuffer
if (_zBuffer->originalNum >= 0) {
setZBuffer(_zBuffer->originalNum);
}
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::mixBackDrop(int fileNum, int x, int y) {
2017-08-02 16:35:09 +02:00
debugC(1, kSludgeDebugGraphics, "Mix back drop of num %i at position %i, %i", fileNum, x, y);
setResourceForFatal(fileNum);
2017-07-18 19:03:45 +02:00
if (!g_sludge->_resMan->openFileFromNum(fileNum)) {
fatal("Can't load overlay image");
return;
}
2017-07-18 19:03:45 +02:00
if (!mixHSI(g_sludge->_resMan->getData(), x, y)) {
fatal("Can't paste overlay image outside screen dimensions");
}
2017-07-18 19:03:45 +02:00
g_sludge->_resMan->finishAccess();
setResourceForFatal(-1);
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::blankScreen(int x1, int y1, int x2, int y2) {
// in case of no backdrop added at all, create it
2017-07-20 00:41:13 +02:00
if (!_backdropSurface.getPixels()) {
_backdropSurface.create(_winWidth, _winHeight, _renderSurface.format);
2017-07-04 23:59:02 +02:00
}
if (y1 < 0)
y1 = 0;
if (x1 < 0)
x1 = 0;
2017-07-20 00:41:13 +02:00
if (x2 > (int)_sceneWidth)
x2 = (int)_sceneWidth;
if (y2 > (int)_sceneHeight)
y2 = (int)_sceneHeight;
2017-07-20 00:41:13 +02:00
_backdropSurface.fillRect(Common::Rect(x1, y1, x2, y2), _currentBlankColour);
// reset zBuffer
if (_zBuffer->originalNum >= 0) {
setZBuffer(_zBuffer->originalNum);
}
2017-07-20 00:41:13 +02:00
}
void GraphicsManager::blankAllScreen() {
blankScreen(0, 0, _sceneWidth, _sceneHeight);
}
2017-07-15 16:20:16 +02:00
// This function is very useful for scrolling credits, but very little else
2017-07-20 00:41:13 +02:00
void GraphicsManager::hardScroll(int distance) {
2017-07-15 16:20:16 +02:00
// scroll 0 distance, return
if (!distance)
return;
2017-07-15 16:20:16 +02:00
// blank screen
2017-07-20 00:41:13 +02:00
blankAllScreen();
2017-07-15 16:20:16 +02:00
// scroll more than backdrop height, screen stay blank
2017-07-20 00:41:13 +02:00
if (ABS(distance) >= (int)_sceneHeight) {
2017-07-15 16:20:16 +02:00
return;
}
2017-07-15 16:20:16 +02:00
// copy part of the backdrop to it
if (distance > 0) {
2017-07-20 00:41:13 +02:00
_backdropSurface.copyRectToSurface(_origBackdropSurface, 0, 0,
Common::Rect(0, distance, _backdropSurface.w, _backdropSurface.h));
2017-07-15 16:20:16 +02:00
} else {
2017-07-20 00:41:13 +02:00
_backdropSurface.copyRectToSurface(_origBackdropSurface, 0, -distance,
Common::Rect(0, 0, _backdropSurface.w, _backdropSurface.h + distance));
}
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::drawLine(uint x1, uint y1, uint x2, uint y2) {
_backdropSurface.drawLine(x1, y1, x2, y2, _backdropSurface.format.ARGBToColor(255, 0, 0, 0));
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::drawVerticalLine(uint x, uint y1, uint y2) {
drawLine(x, y1, x, y2);
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::drawHorizontalLine(uint x1, uint y, uint x2) {
drawLine(x1, y, x2, y);
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::darkScreen() {
Graphics::TransparentSurface tmp(_backdropSurface, false);
tmp.blit(_backdropSurface, 0, 0, Graphics::FLIP_NONE, nullptr, TS_ARGB(0, 255 >> 1, 0, 0));
// reset zBuffer
if (_zBuffer->originalNum >= 0) {
setZBuffer(_zBuffer->originalNum);
}
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::drawBackDrop() {
2017-07-15 15:58:28 +02:00
// TODO: apply lightmap shader
2017-07-20 00:41:13 +02:00
drawParallax();
2017-07-20 00:41:13 +02:00
if (!_backdropExists)
return;
2017-07-15 15:58:28 +02:00
// draw backdrop
2017-07-20 00:41:13 +02:00
Graphics::TransparentSurface tmp(_backdropSurface, false);
tmp.blit(_renderSurface, -_cameraX, -_cameraY);
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::loadLightMap(int v) {
setResourceForFatal(v);
2017-07-18 19:03:45 +02:00
if (!g_sludge->_resMan->openFileFromNum(v))
return fatal("Can't open light map.");
killLightMap();
2017-07-20 00:41:13 +02:00
_lightMapNumber = v;
2017-08-08 18:15:36 +02:00
_lightMap.create(_sceneWidth, _sceneWidth, *_vm->getScreenPixelFormat());
2017-08-08 18:15:36 +02:00
Graphics::TransparentSurface tmp;
if (!ImgLoader::loadImage(g_sludge->_resMan->getData(), &tmp))
return false;
2017-08-08 18:15:36 +02:00
if (tmp.w != _sceneWidth || tmp.h != _sceneHeight) {
if (_lightMapMode == LIGHTMAPMODE_HOTSPOT) {
return fatal("Light map width and height don't match scene width and height. That is required for lightmaps in HOTSPOT mode.");
2017-08-08 18:15:36 +02:00
} else if (_lightMapMode == LIGHTMAPMODE_PIXEL) {
2019-10-03 04:57:18 +01:00
tmp.blit(_lightMap, 0, 0, Graphics::FLIP_NONE, nullptr, TS_ARGB((uint)255, (uint)255, (uint)255, (uint)255), (int)_sceneWidth, (int)_sceneHeight);
2017-08-08 18:15:36 +02:00
} else {
_lightMap.copyFrom(tmp);
}
2017-08-08 18:15:36 +02:00
} else {
_lightMap.copyFrom(tmp);
}
2017-08-08 18:15:36 +02:00
tmp.free();
2017-07-18 19:03:45 +02:00
g_sludge->_resMan->finishAccess();
setResourceForFatal(-1);
2017-08-10 11:05:33 +02:00
// Debug code to output light map image
#if 0
Common::DumpFile *outFile = new Common::DumpFile();
Common::String outName = Common::String::format("lightmap_%i.png", v);
outFile->open(outName);
Image::writePNG(*outFile, _lightMap);
outFile->finalize();
outFile->close();
delete outFile;
#endif
return true;
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::saveLightMap(Common::WriteStream *stream) {
if (_lightMap.getPixels()) {
stream->writeByte(1);
stream->writeUint16BE(_lightMapNumber);
} else {
stream->writeByte(0);
}
stream->writeByte(_lightMapMode);
stream->writeByte(_fadeMode);
2017-07-20 00:41:13 +02:00
}
bool GraphicsManager::loadLightMap(int ssgVersion, Common::SeekableReadStream *stream) {
if (stream->readByte()) {
if (!loadLightMap(stream->readUint16BE()))
return false;
}
if (ssgVersion >= VERSION(1, 4)) {
_lightMapMode = stream->readByte() % 3;
}
_fadeMode = stream->readByte();
2017-07-20 00:41:13 +02:00
return true;
}
bool GraphicsManager::loadHSI(Common::SeekableReadStream *stream, int x, int y, bool reserve) {
2017-08-02 16:35:09 +02:00
debugC(1, kSludgeDebugGraphics, "Load HSI");
if (reserve) {
killAllBackDrop(); // kill all
}
Graphics::Surface tmp;
if (!ImgLoader::loadImage(stream, &tmp, (int)reserve))
return false;
uint realPicWidth = tmp.w;
uint realPicHeight = tmp.h;
2017-07-15 17:33:10 +02:00
// resize backdrop
if (reserve) {
if (!resizeBackdrop(realPicWidth, realPicHeight))
return false;
}
if (x == IN_THE_CENTRE)
2017-07-20 00:41:13 +02:00
x = (_sceneWidth - realPicWidth) >> 1;
if (y == IN_THE_CENTRE)
2017-07-20 00:41:13 +02:00
y = (_sceneHeight - realPicHeight) >> 1;
if (x < 0 || x + realPicWidth > _sceneWidth || y < 0 || y + realPicHeight > _sceneHeight) {
2017-08-02 16:35:09 +02:00
debugC(0, kSludgeDebugGraphics, "Illegal back drop size");
return false;
}
// copy surface loaded to backdrop
2017-08-03 03:50:15 +02:00
Graphics::TransparentSurface tmp_trans(tmp, false);
tmp_trans.blit(_backdropSurface, x, y);
tmp.free();
2017-07-20 00:41:13 +02:00
_origBackdropSurface.copyFrom(_backdropSurface);
_backdropExists = true;
return true;
}
2017-07-20 00:41:13 +02:00
bool GraphicsManager::mixHSI(Common::SeekableReadStream *stream, int x, int y) {
2017-08-02 16:35:09 +02:00
debugC(1, kSludgeDebugGraphics, "Load mixHSI");
Graphics::Surface mixSurface;
if (!ImgLoader::loadImage(stream, &mixSurface, 0))
return false;
2017-07-13 23:26:47 +02:00
uint realPicWidth = mixSurface.w;
uint realPicHeight = mixSurface.h;
if (x == IN_THE_CENTRE)
2017-07-20 00:41:13 +02:00
x = (_sceneWidth - realPicWidth) >> 1;
if (y == IN_THE_CENTRE)
2017-07-20 00:41:13 +02:00
y = (_sceneHeight - realPicHeight) >> 1;
if (x < 0 || x + realPicWidth > _sceneWidth || y < 0 || y + realPicHeight > _sceneHeight)
return false;
Graphics::TransparentSurface tmp(mixSurface, false);
2017-07-20 00:41:13 +02:00
tmp.blit(_backdropSurface, x, y, Graphics::FLIP_NONE, nullptr, TS_ARGB(255, 255 >> 1, 255, 255));
mixSurface.free();
return true;
}
2017-07-20 00:41:13 +02:00
void GraphicsManager::saveHSI(Common::WriteStream *stream) {
Image::writePNG(*stream, _backdropSurface);
}
void GraphicsManager::saveBackdrop(Common::WriteStream *stream) {
stream->writeUint16BE(_cameraX);
stream->writeUint16BE(_cameraY);
stream->writeFloatLE(_cameraZoom);
stream->writeByte(_brightnessLevel);
saveHSI(stream);
}
void GraphicsManager::loadBackdrop(int ssgVersion, Common::SeekableReadStream *stream) {
_cameraX = stream->readUint16BE();
_cameraY = stream->readUint16BE();
if (ssgVersion >= VERSION(2, 0)) {
_cameraZoom = stream->readFloatLE();
} else {
_cameraZoom = 1.0;
}
_brightnessLevel = stream->readByte();
loadHSI(stream, 0, 0, true);
}
bool GraphicsManager::getRGBIntoStack(uint x, uint y, StackHandler *sH) {
2017-07-20 00:41:13 +02:00
if (x >= _sceneWidth || y >= _sceneHeight) {
return fatal("Co-ordinates are outside current scene!");
}
Variable newValue;
newValue.varType = SVT_NULL;
2017-07-20 00:41:13 +02:00
byte *target = (byte *)_renderSurface.getBasePtr(x, y);
newValue.setVariable(SVT_INT, target[1]);
if (!addVarToStackQuick(newValue, sH->first)) return false;
sH->last = sH->first;
newValue.setVariable(SVT_INT, target[2]);
if (!addVarToStackQuick(newValue, sH->first)) return false;
newValue.setVariable(SVT_INT, target[3]);
if (!addVarToStackQuick(newValue, sH->first)) return false;
2017-07-15 16:28:19 +02:00
return true;
}
2017-05-26 21:25:11 +02:00
} // End of namespace Sludge