* Added scaling support

* Made the dragon scale when it is in different parts of the room
* Added getters for relative coordinates (Animation::getRelativeX() and Animation::getRelativeY())
* Commented Game::loop() and Sprite::draw*() methods in more detail

svn-id: r42627
This commit is contained in:
Denis Kasak 2009-07-20 17:25:57 +00:00
parent 18301b6f78
commit a2a71cb8fb
6 changed files with 217 additions and 16 deletions

View File

@ -33,6 +33,8 @@ Animation::Animation(DraciEngine *vm) : _vm(vm) {
_z = 0;
_relX = 0;
_relY = 0;
_scaleX = 1.0;
_scaleY = 1.0;
setPlaying(false);
_looping = false;
_tick = _vm->_system->getMillis();
@ -50,7 +52,14 @@ bool Animation::isLooping() {
void Animation::setRelative(int relx, int rely) {
// Delete the previous frame
Common::Rect frameRect = _frames[_currentFrame]->getRect();
Common::Rect frameRect;
if (isScaled()) {
frameRect = getFrame()->getScaledRect(_scaleX, _scaleY);
} else {
frameRect = getFrame()->getRect();
}
frameRect.translate(_relX, _relY);
_vm->_screen->getSurface()->markDirtyRect(frameRect);
@ -58,6 +67,24 @@ void Animation::setRelative(int relx, int rely) {
_relY = rely;
}
void Animation::setScaling(double scalex, double scaley) {
_scaleX = scalex;
_scaleY = scaley;
}
bool Animation::isScaled() const {
return !(_scaleX == 1.0 && _scaleY == 1.0);
}
double Animation::getScaleX() const {
return _scaleX;
}
double Animation::getScaleY() const {
return _scaleY;
}
void Animation::setLooping(bool looping) {
_looping = looping;
debugC(7, kDraciAnimationDebugLevel, "Setting looping to %d on animation %d",
@ -72,7 +99,13 @@ void Animation::nextFrame(bool force) {
Drawable *frame = _frames[_currentFrame];
Common::Rect frameRect = frame->getRect();
Common::Rect frameRect;
if (isScaled()) {
frameRect = frame->getScaledRect(_scaleX, _scaleY);
} else {
frameRect = frame->getRect();
}
// Translate rectangle to compensate for relative coordinates
frameRect.translate(_relX, _relY);
@ -111,19 +144,29 @@ void Animation::drawFrame(Surface *surface) {
return;
if (_id == kOverlayImage) {
_frames[_currentFrame]->draw(surface, false);
_frames[_currentFrame]->drawScaled(surface, _scaleX, _scaleY, false);
}
else {
Drawable *ptr = _frames[_currentFrame];
int x = ptr->getX();
int y = ptr->getY();
// Take account relative coordinates
int newX = x + _relX;
int newY = y + _relY;
// Translate the frame to those relative coordinates
ptr->setX(newX);
ptr->setY(newY);
ptr->draw(surface, true);
// Draw frame
if (isScaled())
ptr->drawScaled(surface, _scaleX, _scaleY, true);
else
ptr->drawScaled(surface, _scaleX, _scaleY, true);
// Revert back to old coordinates
ptr->setX(x);
ptr->setY(y);
}
@ -145,6 +188,14 @@ uint Animation::getZ() {
return _z;
}
int Animation::getRelativeX() {
return _relX;
}
int Animation::getRelativeY() {
return _relY;
}
bool Animation::isPlaying() {
return _playing;
}
@ -208,8 +259,15 @@ void AnimationManager::stop(int id) {
Animation *anim = getAnimation(id);
// Clean up the last frame that was drawn before stopping
Common::Rect frameRect = anim->getFrame()->getRect();
frameRect.translate(anim->_relX, anim->_relY);
Common::Rect frameRect;
if (anim->isScaled()) {
frameRect = anim->getFrame()->getScaledRect(anim->getScaleX(), anim->getScaleY());
} else {
frameRect = anim->getFrame()->getRect();
}
frameRect.translate(anim->getRelativeX(), anim->getRelativeY());
_vm->_screen->getSurface()->markDirtyRect(frameRect);
if (anim) {

View File

@ -37,7 +37,7 @@ enum { kCurrentFrame = -1 };
class DraciEngine;
class Animation {
public:
Animation(DraciEngine *vm);
~Animation();
@ -62,10 +62,14 @@ public:
bool isLooping();
void setLooping(bool looping);
void setRelative(int relx, int rely);
double getScaleX() const;
double getScaleY() const;
void setScaling(double scalex, double scaley);
bool isScaled() const;
int _relX;
int _relY;
void setRelative(int relx, int rely);
int getRelativeX();
int getRelativeY();
private:
@ -75,6 +79,12 @@ private:
uint _currentFrame;
uint _z;
int _relX;
int _relY;
double _scaleX;
double _scaleY;
uint _tick;
bool _playing;
bool _looping;

View File

@ -171,16 +171,33 @@ void Game::loop() {
if (_vm->_mouse->lButtonPressed() && _currentRoom._walkingMap.isWalkable(x, y)) {
// Fetch dragon's animation ID
// FIXME: Need to add proper walking (this only warps the dragon to position)
int animID = getObject(kDragonObject)->_anims[0];
Animation *anim = _vm->_anims->getAnimation(animID);
Drawable *frame = anim->getFrame();
// Calculate scaling factor
double scaleX = _currentRoom._pers0 + _currentRoom._persStep * y;
double scaleY = scaleX;
// Calculate scaled height of sprite
int height = frame->getScaledHeight(scaleY);
// Set the Z coordinate for the dragon's animation
anim->setZ(y+1);
y -= frame->getHeight();
anim->setRelative(x, y);
// We naturally want the dragon to position its feet to the location of the
// click but sprites are drawn from their top-left corner so we subtract
// the height of the dragon's sprite
y -= height;
anim->setRelative(x, y);
// Set the scaling factor
anim->setScaling(scaleX, scaleY);
// Play the animation
_vm->_anims->play(animID);
debugC(4, kDraciLogicDebugLevel, "Walk to x: %d y: %d", x, y);
@ -221,6 +238,7 @@ void Game::loadRoom(int roomNum) {
for (int i = 5; i >= 0; --i) {
real[i] = roomReader.readByte();
debug(2, "%d", real[i]);
}
_currentRoom._pers0 = real_to_double(real);

View File

@ -134,6 +134,9 @@ struct Room {
class Game {
// HACK: Remove this before committing; if anyone sees this, remind me :D
friend class Animation;
public:
Game(DraciEngine *vm);

View File

@ -29,6 +29,8 @@
#include "draci/sprite.h"
#include "draci/font.h"
#include <cmath>
namespace Draci {
/**
@ -115,6 +117,87 @@ void Sprite::setMirrorOff() {
_mirror = false;
}
// TODO: Research what kind of sampling the original player uses
void Sprite::drawScaled(Surface *surface, double scaleX, double scaleY, bool markDirty) const {
Common::Rect sourceRect(0, 0, _width, _height);
Common::Rect destRect(_x, _y, _x + getScaledWidth(scaleX), _y + getScaledHeight(scaleY));
Common::Rect surfaceRect(0, 0, surface->w, surface->h);
Common::Rect clippedDestRect(destRect);
clippedDestRect.clip(surfaceRect);
// Calculate by how much we need to adjust the source rectangle to account for cropping
const int adjustLeft = clippedDestRect.left - destRect.left;
const int adjustRight = clippedDestRect.right - destRect.right;
const int adjustTop = clippedDestRect.top - destRect.top;
const int adjustBottom = clippedDestRect.bottom - destRect.bottom;
// Resize source rectangle
sourceRect.left += adjustLeft;
sourceRect.right += adjustRight;
sourceRect.top += adjustTop;
sourceRect.bottom += adjustBottom;
// Get pointers to source and destination buffers
byte *dst = (byte *)surface->getBasePtr(clippedDestRect.left, clippedDestRect.top);
byte *src = _data;
const int transparent = surface->getTransparentColour();
// Calculate how many rows and columns we need to draw
const int rows = clippedDestRect.bottom - clippedDestRect.top;
const int columns = clippedDestRect.right - clippedDestRect.left;
int *rowIndices = new int[rows];
int *columnIndices = new int[columns];
// Precalculate pixel indexes
for (int i = 0; i < rows; ++i) {
rowIndices[i] = lround(i / scaleY);
}
for (int j = 0; j < columns; ++j) {
columnIndices[j] = lround(j / scaleX);
}
// Blit the sprite to the surface
for (int i = 0; i < rows; ++i) {
// Fetch index of current row to be drawn
int row = rowIndices[i];
for (int j = 0, q = sourceRect.left; j < columns; ++j, ++q) {
// Fetch index of current column to be drawn
int column = columnIndices[j];
// Don't blit if the pixel is transparent on the target surface
if (src[row * _width + column] != transparent) {
// Draw the sprite mirrored if the _mirror flag is set
if (_mirror) {
dst[sourceRect.right - q - 1] = src[row * _width + column];
} else {
dst[q] = src[row * _width + column];
}
}
}
// Advance to next row
dst += surface->pitch;
}
// Mark the sprite's rectangle dirty
if (markDirty) {
surface->markDirtyRect(destRect);
}
delete[] rowIndices;
delete[] columnIndices;
}
/**
* @brief Draws the sprite to a Draci::Surface
* @param surface Pointer to a Draci::Surface
@ -131,16 +214,19 @@ void Sprite::draw(Surface *surface, bool markDirty) const {
clippedDestRect.clip(surfaceRect);
int adjustLeft = clippedDestRect.left - destRect.left;
int adjustRight = clippedDestRect.right - destRect.right;
int adjustTop = clippedDestRect.top - destRect.top;
int adjustBottom = clippedDestRect.bottom - destRect.bottom;
// Calculate by how much we need to adjust the source rectangle to account for cropping
const int adjustLeft = clippedDestRect.left - destRect.left;
const int adjustRight = clippedDestRect.right - destRect.right;
const int adjustTop = clippedDestRect.top - destRect.top;
const int adjustBottom = clippedDestRect.bottom - destRect.bottom;
// Resize source rectangle
sourceRect.left += adjustLeft;
sourceRect.right += adjustRight;
sourceRect.top += adjustTop;
sourceRect.bottom += adjustBottom;
// Get pointers to source and destination buffers
byte *dst = (byte *)surface->getBasePtr(clippedDestRect.left, clippedDestRect.top);
byte *src = _data;
@ -162,6 +248,7 @@ void Sprite::draw(Surface *surface, bool markDirty) const {
}
}
// Advance to next row
dst += surface->pitch;
}
@ -175,6 +262,18 @@ Common::Rect Sprite::getRect() const {
return Common::Rect(_x, _y, _x + _width, _y + _height);
}
Common::Rect Sprite::getScaledRect(double scaleX, double scaleY) const {
return Common::Rect(_x, _y, _x + getScaledWidth(scaleX), _y + getScaledHeight(scaleY));
}
uint Sprite::getScaledHeight(double scaleY) const {
return lround(scaleY * _height);
}
uint Sprite::getScaledWidth(double scaleX) const {
return lround(scaleX * _width);
}
Text::Text(const Common::String &str, Font *font, byte fontColour,
int x, int y, uint spacing) {
uint len = str.size();

View File

@ -38,11 +38,17 @@ friend class Text;
public:
virtual void draw(Surface *surface, bool markDirty = true) const = 0;
virtual void drawScaled(Surface *surface, double scaleX, double scaleY,
bool markDirty = true) const = 0;
virtual ~Drawable() {};
virtual uint16 getWidth() { return _width; }
virtual uint16 getHeight() { return _height; }
virtual uint getScaledWidth(double scaleX) const = 0;
virtual uint getScaledHeight(double scaleY) const = 0;
virtual int getX() { return _x; }
virtual int getY() { return _y; }
@ -53,6 +59,7 @@ public:
int getDelay() { return _delay; }
virtual Common::Rect getRect() const = 0;
virtual Common::Rect getScaledRect(double scaleX, double scaleY) const = 0;
private:
uint16 _width; //!< Width of the sprite
@ -89,11 +96,17 @@ public:
~Sprite();
void draw(Surface *surface, bool markDirty = true) const;
void drawScaled(Surface *surface, double scaleX, double scaleY, bool markDirty = true) const;
void setMirrorOn();
void setMirrorOff();
virtual Common::Rect getRect() const;
Common::Rect getScaledRect(double scaleX, double scaleY) const;
virtual uint getScaledWidth(double scaleX) const;
virtual uint getScaledHeight(double scaleY) const;
const byte *getBuffer() const { return _data; }
private: