MYST3: Add transition support

This commit is contained in:
Bastien Bouclet 2014-07-01 17:43:28 +02:00
parent 7712eb6c5e
commit 4d5d5d6a81
11 changed files with 305 additions and 20 deletions

View File

@ -260,7 +260,7 @@ bool Console::Cmd_Go(int argc, const char **argv) {
_vm->_state->setLocationNextRoom(roomID);
_vm->_state->setLocationNextNode(nodeId);
_vm->goToNode(0, 1);
_vm->goToNode(0, kTransitionFade);
return false;
}

View File

@ -244,7 +244,7 @@ void Inventory::openBook(uint16 age, uint16 room, uint16 node) {
_vm->_state->setLocationNextAge(age);
_vm->_state->setLocationNextRoom(room);
_vm->goToNode(node, 1);
_vm->goToNode(node, kTransitionFade);
}
void Inventory::addSaavedroChapter(uint16 var) {

View File

@ -142,7 +142,7 @@ void Menu::goToNode(uint16 node) {
_vm->_state->setMenuEscapePressed(0);
_vm->_state->setLocationNextAge(9);
_vm->_state->setLocationNextRoom(901);
_vm->goToNode(node, 2);
_vm->goToNode(node, kTransitionNone);
}
Dialog::Dialog(Myst3Engine *vm, uint id):
@ -330,7 +330,7 @@ void Menu::loadMenuLoad() {
_vm->_state->setMenuSavedRoom(0);
_vm->_state->setMenuSavedNode(0);
_vm->goToNode(0, 1);
_vm->goToNode(0, kTransitionFade);
}
void Menu::saveMenuOpen() {

View File

@ -27,7 +27,8 @@ MODULE_OBJS := \
script.o \
sound.o \
state.o \
subtitles.o
subtitles.o \
transition.o
# This module can be built as a plugin
ifeq ($(ENABLE_MYST3), DYNAMIC_PLUGIN)

View File

@ -47,6 +47,7 @@
#include "engines/myst3/menu.h"
#include "engines/myst3/sound.h"
#include "engines/myst3/ambient.h"
#include "engines/myst3/transition.h"
#include "image/jpeg.h"
@ -500,7 +501,7 @@ void Myst3Engine::processInput(bool lookOnly) {
}
}
void Myst3Engine::drawFrame() {
void Myst3Engine::drawFrame(bool noSwap) {
_sound->update();
_gfx->clear();
@ -573,9 +574,11 @@ void Myst3Engine::drawFrame() {
if (_cursor->isVisible())
_cursor->draw();
_system->updateScreen();
_system->delayMillis(10);
_state->updateFrameCounters();
if (!noSwap) {
_system->updateScreen();
_system->delayMillis(10);
_state->updateFrameCounters();
}
}
bool Myst3Engine::isInventoryVisible() {
@ -588,7 +591,7 @@ bool Myst3Engine::isInventoryVisible() {
return true;
}
void Myst3Engine::goToNode(uint16 nodeID, uint transition) {
void Myst3Engine::goToNode(uint16 nodeID, TransitionType transitionType) {
uint16 node = _state->getLocationNextNode();
if (node == 0)
node = nodeID;
@ -596,7 +599,10 @@ void Myst3Engine::goToNode(uint16 nodeID, uint transition) {
uint16 room = _state->getLocationNextRoom();
uint16 age = _state->getLocationNextAge();
if (_state->getViewType() == kCube) {
Transition *transition = Transition::initialize(this, transitionType);
ViewType sourceViewType = _state->getViewType();
if (sourceViewType == kCube) {
// The lookat direction in the next node should be
// the direction of the mouse cursor
float pitch, heading;
@ -613,6 +619,11 @@ void Myst3Engine::goToNode(uint16 nodeID, uint transition) {
if (_state->getAmbiantPreviousFadeOutDelay() > 0) {
_ambient->playCurrentNode(100, _state->getAmbiantPreviousFadeOutDelay());
}
if (transition) {
transition->draw();
delete transition;
}
}
void Myst3Engine::loadNode(uint16 nodeID, uint32 roomID, uint32 ageID) {
@ -1180,7 +1191,7 @@ Common::Error Myst3Engine::loadGameState(int slot) {
_state->setMenuSavedRoom(0);
_state->setMenuSavedNode(0);
goToNode(0, 1);
goToNode(0, kTransitionFade);
return Common::kNoError;
}

View File

@ -67,6 +67,14 @@ enum {
kDebugScript = (1 << 3)
};
enum TransitionType {
kTransitionFade = 1,
kTransitionNone,
kTransitionZip,
kTransitionLeftToRight,
kTransitionRightToLeft
};
class Console;
class GameState;
class HotSpot;
@ -123,7 +131,7 @@ public:
Graphics::Surface *loadTexture(uint16 id);
static Graphics::Surface *decodeJpeg(const DirectorySubEntry *jpegDesc);
void goToNode(uint16 nodeID, uint transition);
void goToNode(uint16 nodeID, TransitionType transition);
void loadNode(uint16 nodeID, uint32 roomID = 0, uint32 ageID = 0);
void unloadNode();
void loadNodeCubeFaces(uint16 nodeID);
@ -161,7 +169,7 @@ public:
void getMovieLookAt(uint16 id, bool start, float &pitch, float &heading);
void processInput(bool lookOnly);
void drawFrame();
void drawFrame(bool noSwap = false);
bool inputValidatePressed();
bool inputEscapePressed();
@ -208,6 +216,9 @@ private:
bool isInventoryVisible();
void transitionDraw(TransitionType type, Graphics::Surface *source);
void transitionDrawStep(TransitionType type, uint32 *target, uint targetPitch, uint32 *source, uint sourcePitch, uint32 *destination, uint destinationPitch, uint destinationHeight, uint completion);
friend class Console;
};

View File

@ -1898,19 +1898,19 @@ void Script::chooseNextNode(Context &c, const Opcode &cmd) {
void Script::goToNodeTransition(Context &c, const Opcode &cmd) {
debugC(kDebugScript, "Opcode %d: Go to node %d with transition %d", cmd.op, cmd.args[0], cmd.args[1]);
_vm->goToNode(cmd.args[0], cmd.args[1]);
_vm->goToNode(cmd.args[0], static_cast<TransitionType>(cmd.args[1]));
}
void Script::goToNodeTrans2(Context &c, const Opcode &cmd) {
debugC(kDebugScript, "Opcode %d: Go to node %d", cmd.op, cmd.args[0]);
_vm->goToNode(cmd.args[0], 2);
_vm->goToNode(cmd.args[0], kTransitionNone);
}
void Script::goToNodeTrans1(Context &c, const Opcode &cmd) {
debugC(kDebugScript, "Opcode %d: Go to node %d", cmd.op, cmd.args[0]);
_vm->goToNode(cmd.args[0], 1);
_vm->goToNode(cmd.args[0], kTransitionFade);
}
void Script::goToRoomNode(Context &c, const Opcode &cmd) {
@ -1919,13 +1919,13 @@ void Script::goToRoomNode(Context &c, const Opcode &cmd) {
_vm->_state->setLocationNextRoom(cmd.args[0]);
_vm->_state->setLocationNextNode(cmd.args[1]);
_vm->goToNode(0, 1);
_vm->goToNode(0, kTransitionFade);
}
void Script::zipToNode(Context &c, const Opcode &cmd) {
debugC(kDebugScript, "Opcode %d: Zip to node %d", cmd.op, cmd.args[0]);
_vm->goToNode(cmd.args[0], 3);
_vm->goToNode(cmd.args[0], kTransitionZip);
}
void Script::zipToRoomNode(Context &c, const Opcode &cmd) {
@ -1934,7 +1934,7 @@ void Script::zipToRoomNode(Context &c, const Opcode &cmd) {
_vm->_state->setLocationNextRoom(cmd.args[0]);
_vm->_state->setLocationNextNode(cmd.args[1]);
_vm->goToNode(0, 3);
_vm->goToNode(0, kTransitionZip);
}
void Script::reloadNode(Context &c, const Opcode &cmd) {

View File

@ -204,6 +204,8 @@ GameState::GameState(Myst3Engine *vm):
VAR(185, CameraMoveSpeed, false)
VAR(187, TransitionSound, false)
VAR(188, TransitionSoundVolume, false)
VAR(189, LocationNextNode, false)
VAR(190, LocationNextRoom, false)
VAR(191, LocationNextAge, false)

View File

@ -176,6 +176,9 @@ public:
DECLARE_VAR(185, CameraMoveSpeed)
DECLARE_VAR(187, TransitionSound)
DECLARE_VAR(188, TransitionSoundVolume)
DECLARE_VAR(189, LocationNextNode)
DECLARE_VAR(190, LocationNextRoom)
DECLARE_VAR(191, LocationNextAge)

View File

@ -0,0 +1,206 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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/config-manager.h"
#include "engines/myst3/transition.h"
#include "engines/myst3/sound.h"
#include "engines/myst3/state.h"
#include "graphics/colormasks.h"
namespace Myst3 {
Transition *Transition::initialize(Myst3Engine *vm, TransitionType type) {
return new Transition(vm, type);
}
Transition::Transition(Myst3Engine *vm, TransitionType type) :
_vm(vm),
_type(type),
_sourceScreenshot(nullptr) {
// Capture a screenshot of the source node
if (type != kTransitionNone && computeDuration() != 0) {
_sourceScreenshot = _vm->_gfx->getScreenshot();
}
}
Transition::~Transition() {
if (_sourceScreenshot) {
_sourceScreenshot->free();
delete _sourceScreenshot;
}
}
int Transition::computeDuration() {
int durationFrames = 30 * (100 - ConfMan.getInt("transition_speed")) / 100;
if (_type == kTransitionZip) {
durationFrames >>= 1;
}
return durationFrames;
}
void Transition::playSound() {
if (_vm->_state->getTransitionSound()) {
_vm->_sound->playEffect(_vm->_state->getTransitionSound(),
_vm->_state->getTransitionSoundVolume());
}
_vm->_state->setTransitionSound(0);
}
void Transition::draw() {
// Play the transition sound
playSound();
// Got any transition to draw?
if (!_sourceScreenshot) {
return;
}
// Capture a screenshot of the destination node
_vm->drawFrame(true);
Graphics::Surface *target = _vm->_gfx->getScreenshot();
// Create the temporary surface and texture for the transition
Graphics::Surface *frame = new Graphics::Surface();
frame->create(target->w, target->h, target->format);
Common::Rect frameRect = Common::Rect(frame->w, frame->h);
Texture *frameTexture = _vm->_gfx->createTexture(frame);
// Compute the start and end frames for the animation
int durationFrames = computeDuration();
int startFrame = _vm->_state->getFrameCount();
uint endFrame = startFrame + durationFrames;
// Draw each step until completion
int completion = 0;
while (_vm->_state->getFrameCount() <= endFrame || completion < 100) {
completion = CLIP<int>(100 * (_vm->_state->getFrameCount() - startFrame) / durationFrames, 0, 100);
uint32 *targetPtr = (uint32 *)target->getPixels();
uint32 *sourcePtr = (uint32 *)_sourceScreenshot->getPixels();
uint32 *framePtr = (uint32 *)frame->getPixels();
drawStep(targetPtr, target->pitch, sourcePtr, _sourceScreenshot->pitch, framePtr, frame->pitch, frame->h, completion);
frameTexture->update(frame);
_vm->_gfx->drawTexturedRect2D(frameRect, frameRect, frameTexture);
g_system->updateScreen();
g_system->delayMillis(10);
_vm->_state->updateFrameCounters();
}
_vm->_gfx->freeTexture(frameTexture);
frame->free();
delete frame;
target->free();
delete target;
}
void Transition::drawStep(uint32 *target, uint targetPitch, uint32 *source, uint sourcePitch, uint32 *destination, uint destinationPitch, uint destinationHeight, uint completion) {
switch (_type) {
case kTransitionNone:
break;
case kTransitionFade:
case kTransitionZip: {
for (uint y = 0; y < destinationHeight; y++) {
uint32 *sourcePtr = source + (destinationHeight - y - 1) * (sourcePitch / 4);
uint32 *targetPtr = target + (destinationHeight - y - 1) * (targetPitch / 4);
uint32 *destinationPtr = destination;
for (uint x = 0; x < 640; x++) {
byte sourceR, sourceG, sourceB;
byte targetR, targetG, targetB;
byte destR, destG, destB;
Graphics::colorToRGB< Graphics::ColorMasks<8888> >(*sourcePtr++, sourceR, sourceG, sourceB);
Graphics::colorToRGB< Graphics::ColorMasks<8888> >(*targetPtr++, targetR, targetG, targetB);
// TODO: optimize ?
destR = sourceR * (100 - completion) / 100 + targetR * completion / 100;
destG = sourceG * (100 - completion) / 100 + targetG * completion / 100;
destB = sourceB * (100 - completion) / 100 + targetB * completion / 100;
*destinationPtr++ = Graphics::RGBToColor< Graphics::ColorMasks<8888> >(destR, destG, destB);
}
destination += (destinationPitch / 4);
}
}
break;
case kTransitionLeftToRight: {
uint transitionX = (640 * 100 - 640 * completion) / 100;
for (uint y = 0; y < destinationHeight; y++) {
uint32 *sourcePtr = source + (destinationHeight - y - 1) * (sourcePitch / 4);
uint32 *destinationPtr = destination;
for (uint x = 0; x < transitionX; x++) {
*destinationPtr = *sourcePtr;
destinationPtr++;
sourcePtr++;
}
uint32 *targetPtr = target + (destinationHeight - y - 1) * (targetPitch / 4) + transitionX;
for (uint x = transitionX; x < 640; x++) {
*destinationPtr = *targetPtr;
destinationPtr++;
targetPtr++;
}
destination += (destinationPitch / 4);
}
}
break;
case kTransitionRightToLeft: {
uint transitionX = 640 * completion / 100;
for (uint y = 0; y < destinationHeight; y++) {
uint32 *targetPtr = target + (destinationHeight - y - 1) * (targetPitch / 4);
uint32 *destinationPtr = destination;
for (uint x = 0; x < transitionX; x++) {
*destinationPtr = *targetPtr;
destinationPtr++;
targetPtr++;
}
uint32 *sourcePtr = source + (destinationHeight - y - 1) * (sourcePitch / 4) + transitionX;
for (uint x = transitionX; x < 640; x++) {
*destinationPtr = *sourcePtr;
destinationPtr++;
sourcePtr++;
}
destination += (destinationPitch / 4);
}
}
break;
}
}
} /* namespace Myst3 */

View File

@ -0,0 +1,51 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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.
*
*/
#ifndef TRANSITION_H_
#define TRANSITION_H_
#include "engines/myst3/myst3.h"
namespace Myst3 {
class Transition {
public:
static Transition *initialize(Myst3Engine *vm, TransitionType type);
virtual ~Transition();
void draw();
private:
Transition(Myst3Engine *vm, TransitionType type);
void drawStep(uint32 *target, uint targetPitch, uint32 *source, uint sourcePitch, uint32 *destination, uint destinationPitch, uint destinationHeight, uint completion);
int computeDuration();
void playSound();
Myst3Engine *_vm;
TransitionType _type;
Graphics::Surface *_sourceScreenshot;
};
} /* namespace Myst3 */
#endif /* TRANSITION_H_ */