scummvm/engines/sword25/gfx/animation.cpp
2015-11-30 23:54:27 +01:00

682 lines
20 KiB
C++
Raw Blame 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.
*
*/
/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
*
* Licensed under GNU GPL v2
*
*/
#include "sword25/gfx/animation.h"
#include "sword25/kernel/kernel.h"
#include "sword25/kernel/resmanager.h"
#include "sword25/kernel/inputpersistenceblock.h"
#include "sword25/kernel/outputpersistenceblock.h"
#include "sword25/package/packagemanager.h"
#include "sword25/gfx/image/image.h"
#include "sword25/gfx/animationtemplate.h"
#include "sword25/gfx/animationtemplateregistry.h"
#include "sword25/gfx/animationresource.h"
#include "sword25/gfx/bitmapresource.h"
#include "sword25/gfx/graphicengine.h"
namespace Sword25 {
Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName) :
TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
// Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
if (!_initSuccess)
return;
initMembers();
// Vom negativen Fall ausgehen.
_initSuccess = false;
initializeAnimationResource(fileName);
// Erfolg signalisieren.
_initSuccess = true;
}
Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &templ) :
TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
// Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
if (!_initSuccess)
return;
initMembers();
// Vom negativen Fall ausgehen.
_initSuccess = false;
_animationTemplateHandle = AnimationTemplate::create(templ);
// Erfolg signalisieren.
_initSuccess = true;
}
Animation::Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION, handle) {
// Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
if (!_initSuccess)
return;
initMembers();
// Objekt vom Stream laden.
_initSuccess = unpersist(reader);
}
void Animation::initializeAnimationResource(const Common::String &fileName) {
// Die Resource wird f<>r die gesamte Lebensdauer des Animations-Objektes gelockt.
Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(fileName);
if (resourcePtr && resourcePtr->getType() == Resource::TYPE_ANIMATION)
_animationResourcePtr = static_cast<AnimationResource *>(resourcePtr);
else {
error("The resource \"%s\" could not be requested. The Animation can't be created.", fileName.c_str());
return;
}
// Gr<47><72>e und Position der Animation anhand des aktuellen Frames bestimmen.
computeCurrentCharacteristics();
}
void Animation::initMembers() {
_currentFrame = 0;
_currentFrameTime = 0;
_direction = FORWARD;
_running = false;
_finished = false;
_relX = 0;
_relY = 0;
_scaleFactorX = 1.0f;
_scaleFactorY = 1.0f;
_modulationColor = 0xffffffff;
_animationResourcePtr = 0;
_animationTemplateHandle = 0;
_framesLocked = false;
_loopPointCallback = 0;
_actionCallback = 0;
_deleteCallback = 0;
}
Animation::~Animation() {
if (getAnimationDescription()) {
stop();
getAnimationDescription()->unlock();
}
// Invoke the "delete" callback
if (_deleteCallback)
(_deleteCallback)(getHandle());
}
void Animation::play() {
// If the animation was completed, then play it again from the start.
if (_finished)
stop();
_running = true;
lockAllFrames();
}
void Animation::pause() {
_running = false;
unlockAllFrames();
}
void Animation::stop() {
_currentFrame = 0;
_currentFrameTime = 0;
_direction = FORWARD;
pause();
}
void Animation::setFrame(uint nr) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
if (nr >= animationDescriptionPtr->getFrameCount()) {
error("Tried to set animation to illegal frame (%d). Value must be between 0 and %d.",
nr, animationDescriptionPtr->getFrameCount());
return;
}
_currentFrame = nr;
_currentFrameTime = 0;
computeCurrentCharacteristics();
forceRefresh();
}
bool Animation::doRender(RectangleList *updateRects) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
assert(_currentFrame < animationDescriptionPtr->getFrameCount());
// Bitmap des aktuellen Frames holen
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(_currentFrame).fileName);
assert(pResource);
assert(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
// Framebufferobjekt holen
GraphicEngine *pGfx = Kernel::getInstance()->getGfx();
assert(pGfx);
// Bitmap zeichnen
bool result;
if (isScalingAllowed() && (_width != pBitmapResource->getWidth() || _height != pBitmapResource->getHeight())) {
result = pBitmapResource->blit(_absoluteX, _absoluteY,
(animationDescriptionPtr->getFrame(_currentFrame).flipV ? Graphics::FLIP_V : 0) |
(animationDescriptionPtr->getFrame(_currentFrame).flipH ? Graphics::FLIP_H : 0),
0, _modulationColor, _width, _height,
updateRects);
} else {
result = pBitmapResource->blit(_absoluteX, _absoluteY,
(animationDescriptionPtr->getFrame(_currentFrame).flipV ? Graphics::FLIP_V : 0) |
(animationDescriptionPtr->getFrame(_currentFrame).flipH ? Graphics::FLIP_H : 0),
0, _modulationColor, -1, -1,
updateRects);
}
// Resource freigeben
pBitmapResource->release();
return result;
}
void Animation::frameNotification(int timeElapsed) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
assert(timeElapsed >= 0);
// Nur wenn die Animation l<>uft wird sie auch weiterbewegt
if (_running) {
// Gesamte vergangene Zeit bestimmen (inkl. Restzeit des aktuellen Frames)
_currentFrameTime += timeElapsed;
// Anzahl an zu <20>berpringenden Frames bestimmen
int skipFrames = animationDescriptionPtr->getMillisPerFrame() == 0 ? 0 : _currentFrameTime / animationDescriptionPtr->getMillisPerFrame();
// Neue Frame-Restzeit bestimmen
_currentFrameTime -= animationDescriptionPtr->getMillisPerFrame() * skipFrames;
// Neuen Frame bestimmen (je nach aktuellener Abspielrichtung wird addiert oder subtrahiert)
int tmpCurFrame = _currentFrame;
switch (_direction) {
case FORWARD:
tmpCurFrame += skipFrames;
break;
case BACKWARD:
tmpCurFrame -= skipFrames;
break;
default:
assert(0);
}
// Deal with overflows
if (tmpCurFrame < 0) {
// Loop-Point callback
if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
_loopPointCallback = 0;
// An underflow may only occur if the animation type is JOJO.
assert(animationDescriptionPtr->getAnimationType() == AT_JOJO);
tmpCurFrame = - tmpCurFrame;
_direction = FORWARD;
} else if (static_cast<uint>(tmpCurFrame) >= animationDescriptionPtr->getFrameCount()) {
// Loop-Point callback
if (_loopPointCallback && !(_loopPointCallback)(getHandle()))
_loopPointCallback = 0;
switch (animationDescriptionPtr->getAnimationType()) {
case AT_ONESHOT:
tmpCurFrame = animationDescriptionPtr->getFrameCount() - 1;
_finished = true;
pause();
break;
case AT_LOOP:
tmpCurFrame = tmpCurFrame % animationDescriptionPtr->getFrameCount();
break;
case AT_JOJO:
tmpCurFrame = animationDescriptionPtr->getFrameCount() - (tmpCurFrame % animationDescriptionPtr->getFrameCount()) - 1;
_direction = BACKWARD;
break;
default:
assert(0);
}
}
if ((int)_currentFrame != tmpCurFrame) {
forceRefresh();
if (animationDescriptionPtr->getFrame(_currentFrame).action != "") {
// action callback
if (_actionCallback && !(_actionCallback)(getHandle()))
_actionCallback = 0;
}
}
_currentFrame = static_cast<uint>(tmpCurFrame);
}
// Gr<47><72>e und Position der Animation anhand des aktuellen Frames bestimmen
computeCurrentCharacteristics();
assert(_currentFrame < animationDescriptionPtr->getFrameCount());
assert(_currentFrameTime >= 0);
}
void Animation::computeCurrentCharacteristics() {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
assert(pResource);
assert(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
// Gr<47><72>e des Bitmaps auf die Animation <20>bertragen
_width = static_cast<int>(pBitmap->getWidth() * _scaleFactorX);
_height = static_cast<int>(pBitmap->getHeight() * _scaleFactorY);
// Position anhand des Hotspots berechnen und setzen
int posX = _relX + computeXModifier();
int posY = _relY + computeYModifier();
RenderObject::setPos(posX, posY);
pBitmap->release();
}
bool Animation::lockAllFrames() {
if (!_framesLocked) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
if (!Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName)) {
error("Could not lock all animation frames.");
return false;
}
}
_framesLocked = true;
}
return true;
}
bool Animation::unlockAllFrames() {
if (_framesLocked) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
Resource *pResource;
if (!(pResource = Kernel::getInstance()->getResourceManager()->requestResource(animationDescriptionPtr->getFrame(i).fileName))) {
error("Could not unlock all animation frames.");
return false;
}
// Zwei mal freigeben um den Request von LockAllFrames() und den jetzigen Request aufzuheben
pResource->release();
if (pResource->getLockCount())
pResource->release();
}
_framesLocked = false;
}
return true;
}
Animation::ANIMATION_TYPES Animation::getAnimationType() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->getAnimationType();
}
int Animation::getFPS() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->getFPS();
}
int Animation::getFrameCount() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->getFrameCount();
}
bool Animation::isScalingAllowed() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->isScalingAllowed();
}
bool Animation::isAlphaAllowed() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->isAlphaAllowed();
}
bool Animation::isColorModulationAllowed() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->isColorModulationAllowed();
}
void Animation::setPos(int relX, int relY) {
_relX = relX;
_relY = relY;
computeCurrentCharacteristics();
}
void Animation::setX(int relX) {
_relX = relX;
computeCurrentCharacteristics();
}
void Animation::setY(int relY) {
_relY = relY;
computeCurrentCharacteristics();
}
void Animation::setAlpha(int alpha) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
if (!animationDescriptionPtr->isAlphaAllowed()) {
warning("Tried to set alpha value on an animation that does not support alpha. Call was ignored.");
return;
}
uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
if (newModulationColor != _modulationColor) {
_modulationColor = newModulationColor;
forceRefresh();
}
}
void Animation::setModulationColor(uint modulationColor) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
if (!animationDescriptionPtr->isColorModulationAllowed()) {
warning("Tried to set modulation color on an animation that does not support color modulation. Call was ignored");
return;
}
uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
if (newModulationColor != _modulationColor) {
_modulationColor = newModulationColor;
forceRefresh();
}
}
void Animation::setScaleFactor(float scaleFactor) {
setScaleFactorX(scaleFactor);
setScaleFactorY(scaleFactor);
}
void Animation::setScaleFactorX(float scaleFactorX) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
if (!animationDescriptionPtr->isScalingAllowed()) {
warning("Tried to set x scale factor on an animation that does not support scaling. Call was ignored");
return;
}
if (scaleFactorX != _scaleFactorX) {
_scaleFactorX = scaleFactorX;
if (_scaleFactorX <= 0.0f)
_scaleFactorX = 0.001f;
forceRefresh();
computeCurrentCharacteristics();
}
}
void Animation::setScaleFactorY(float scaleFactorY) {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
if (!animationDescriptionPtr->isScalingAllowed()) {
warning("Tried to set y scale factor on an animation that does not support scaling. Call was ignored");
return;
}
if (scaleFactorY != _scaleFactorY) {
_scaleFactorY = scaleFactorY;
if (_scaleFactorY <= 0.0f)
_scaleFactorY = 0.001f;
forceRefresh();
computeCurrentCharacteristics();
}
}
const Common::String &Animation::getCurrentAction() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
return animationDescriptionPtr->getFrame(_currentFrame).action;
}
int Animation::getX() const {
return _relX;
}
int Animation::getY() const {
return _relY;
}
int Animation::getAbsoluteX() const {
return _absoluteX + (_relX - _x);
}
int Animation::getAbsoluteY() const {
return _absoluteY + (_relY - _y);
}
int Animation::computeXModifier() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
assert(pResource);
assert(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
int result = curFrame.flipV ? - static_cast<int>((pBitmap->getWidth() - 1 - curFrame.hotspotX) * _scaleFactorX) :
- static_cast<int>(curFrame.hotspotX * _scaleFactorX);
pBitmap->release();
return result;
}
int Animation::computeYModifier() const {
AnimationDescription *animationDescriptionPtr = getAnimationDescription();
assert(animationDescriptionPtr);
const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource(curFrame.fileName);
assert(pResource);
assert(pResource->getType() == Resource::TYPE_BITMAP);
BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
int result = curFrame.flipH ? - static_cast<int>((pBitmap->getHeight() - 1 - curFrame.hotspotY) * _scaleFactorY) :
- static_cast<int>(curFrame.hotspotY * _scaleFactorY);
pBitmap->release();
return result;
}
bool Animation::persist(OutputPersistenceBlock &writer) {
bool result = true;
result &= RenderObject::persist(writer);
writer.write(_relX);
writer.write(_relY);
writer.write(_scaleFactorX);
writer.write(_scaleFactorY);
writer.write(_modulationColor);
writer.write(_currentFrame);
writer.write(_currentFrameTime);
writer.write(_running);
writer.write(_finished);
writer.write(static_cast<uint32>(_direction));
// Je nach Animationstyp entweder das Template oder die Ressource speichern.
if (_animationResourcePtr) {
uint32 marker = 0;
writer.write(marker);
writer.writeString(_animationResourcePtr->getFileName());
} else if (_animationTemplateHandle) {
uint32 marker = 1;
writer.write(marker);
writer.write(_animationTemplateHandle);
} else {
assert(false);
}
//writer.write(_AnimationDescriptionPtr);
writer.write(_framesLocked);
// The following is only there to for compatibility with older saves
// resp. the original engine.
writer.write((uint32)1);
writer.writeString("LuaLoopPointCB");
writer.write(getHandle());
writer.write((uint32)1);
writer.writeString("LuaActionCB");
writer.write(getHandle());
writer.write((uint32)1);
writer.writeString("LuaDeleteCB");
writer.write(getHandle());
result &= RenderObject::persistChildren(writer);
return result;
}
// -----------------------------------------------------------------------------
bool Animation::unpersist(InputPersistenceBlock &reader) {
bool result = true;
result &= RenderObject::unpersist(reader);
reader.read(_relX);
reader.read(_relY);
reader.read(_scaleFactorX);
reader.read(_scaleFactorY);
reader.read(_modulationColor);
reader.read(_currentFrame);
reader.read(_currentFrameTime);
reader.read(_running);
reader.read(_finished);
uint32 direction;
reader.read(direction);
_direction = static_cast<Direction>(direction);
// Animationstyp einlesen.
uint32 marker;
reader.read(marker);
if (marker == 0) {
Common::String resourceFilename;
reader.readString(resourceFilename);
initializeAnimationResource(resourceFilename);
} else if (marker == 1) {
reader.read(_animationTemplateHandle);
} else {
assert(false);
}
reader.read(_framesLocked);
if (_framesLocked)
lockAllFrames();
// The following is only there to for compatibility with older saves
// resp. the original engine.
uint32 callbackCount;
Common::String callbackFunctionName;
uint32 callbackData;
// loop point callback
reader.read(callbackCount);
assert(callbackCount == 1);
reader.readString(callbackFunctionName);
assert(callbackFunctionName == "LuaLoopPointCB");
reader.read(callbackData);
assert(callbackData == getHandle());
// loop point callback
reader.read(callbackCount);
assert(callbackCount == 1);
reader.readString(callbackFunctionName);
assert(callbackFunctionName == "LuaActionCB");
reader.read(callbackData);
assert(callbackData == getHandle());
// loop point callback
reader.read(callbackCount);
assert(callbackCount == 1);
reader.readString(callbackFunctionName);
assert(callbackFunctionName == "LuaDeleteCB");
reader.read(callbackData);
assert(callbackData == getHandle());
// Set the callbacks
setCallbacks();
result &= RenderObject::unpersistChildren(reader);
return reader.isGood() && result;
}
// -----------------------------------------------------------------------------
AnimationDescription *Animation::getAnimationDescription() const {
if (_animationResourcePtr)
return _animationResourcePtr;
else
return AnimationTemplateRegistry::instance().resolveHandle(_animationTemplateHandle);
}
} // End of namespace Sword25