mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-09-10 12:02:53 +02:00
Massively improve animation frame timing
This commit is contained in:
parent
260260531d
commit
6fe980abc8
6 changed files with 53 additions and 37 deletions
|
@ -699,7 +699,7 @@ RB_METHOD(bitmapEachFrame) {
|
||||||
|
|
||||||
GUARD_EXC
|
GUARD_EXC
|
||||||
(
|
(
|
||||||
b->ensureNotPlaying();
|
b->
|
||||||
int cur_frame = b->currentFrameI();
|
int cur_frame = b->currentFrameI();
|
||||||
for (int i = 0; i < b->numFrames(); i++) {
|
for (int i = 0; i < b->numFrames(); i++) {
|
||||||
b->gotoAndStop(i);
|
b->gotoAndStop(i);
|
||||||
|
|
|
@ -77,13 +77,6 @@ extern "C" {
|
||||||
"Operation not supported for static bitmaps"); \
|
"Operation not supported for static bitmaps"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GUARD_PLAYING \
|
|
||||||
{ \
|
|
||||||
if (p->animation.playing) \
|
|
||||||
throw Exception(Exception::MKXPError, \
|
|
||||||
"Operation not supported while bitmap's animation is being played"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define OUTLINE_SIZE 1
|
#define OUTLINE_SIZE 1
|
||||||
|
|
||||||
/* Normalize (= ensure width and
|
/* Normalize (= ensure width and
|
||||||
|
@ -166,39 +159,55 @@ struct BitmapPrivate
|
||||||
|
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool playing;
|
bool playing;
|
||||||
|
bool needsReset;
|
||||||
bool loop;
|
bool loop;
|
||||||
std::vector<TEXFBO> frames;
|
std::vector<TEXFBO> frames;
|
||||||
float fps;
|
float fps;
|
||||||
int lastFrame;
|
int lastFrame;
|
||||||
unsigned long long startTime;
|
unsigned long long startTime;
|
||||||
|
unsigned long long playTime;
|
||||||
|
|
||||||
inline int currentFrameIRaw() {
|
inline int currentFrameIRaw() {
|
||||||
return round(lastFrame + ((shState->runTime() - startTime) / ((1 / fps) * 1000000)));
|
if (fps <= 0) return lastFrame;
|
||||||
|
return floor(lastFrame + (playTime / ((1 / fps) * 1000000)));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int currentFrameI() {
|
int currentFrameI() {
|
||||||
if (!playing || fps <= 0) return lastFrame;
|
if (!playing || needsReset) return lastFrame;
|
||||||
int i = currentFrameIRaw();
|
int i = currentFrameIRaw();
|
||||||
return (loop) ? fmod(i, frames.size()) : (i > (int)frames.size() - 1) ? (int)frames.size() - 1 : i;
|
return (loop) ? fmod(i, frames.size()) : (i > (int)frames.size() - 1) ? (int)frames.size() - 1 : i;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEXFBO ¤tFrame() {
|
inline TEXFBO ¤tFrame() {
|
||||||
return frames[currentFrameI()];
|
int i = currentFrameI();
|
||||||
|
return frames[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void play() {
|
inline void play() {
|
||||||
playing = true;
|
playing = true;
|
||||||
startTime = shState->runTime();
|
needsReset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
inline void stop() {
|
||||||
lastFrame = currentFrameI();
|
lastFrame = currentFrameI();
|
||||||
playing = false;
|
playing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void seek(int frame) {
|
inline void seek(int frame) {
|
||||||
lastFrame = clamp(frame, 0, (int)frames.size());
|
lastFrame = clamp(frame, 0, (int)frames.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateTimer() {
|
||||||
|
if (needsReset) {
|
||||||
|
playTime = 0;
|
||||||
|
startTime = shState->graphics().lastUpdate();
|
||||||
|
needsReset = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playTime = shState->graphics().lastUpdate() - startTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
} animation;
|
} animation;
|
||||||
|
|
||||||
TEXFBO gl;
|
TEXFBO gl;
|
||||||
|
@ -237,6 +246,7 @@ struct BitmapPrivate
|
||||||
animation.enabled = false;
|
animation.enabled = false;
|
||||||
animation.playing = false;
|
animation.playing = false;
|
||||||
animation.loop = true;
|
animation.loop = true;
|
||||||
|
animation.playTime = 0;
|
||||||
animation.startTime = 0;
|
animation.startTime = 0;
|
||||||
animation.fps = 0;
|
animation.fps = 0;
|
||||||
animation.lastFrame = 0;
|
animation.lastFrame = 0;
|
||||||
|
@ -648,7 +658,6 @@ Bitmap::Bitmap(const Bitmap &other, int frame)
|
||||||
|
|
||||||
// TODO: Clean me up
|
// TODO: Clean me up
|
||||||
if (!other.isAnimated() || frame >= -1) {
|
if (!other.isAnimated() || frame >= -1) {
|
||||||
if (frame == -1) other.ensureNotPlaying();
|
|
||||||
p->gl = shState->texPool().request(other.width(), other.height());
|
p->gl = shState->texPool().request(other.width(), other.height());
|
||||||
|
|
||||||
GLMeta::blitBegin(p->gl);
|
GLMeta::blitBegin(p->gl);
|
||||||
|
@ -669,6 +678,8 @@ Bitmap::Bitmap(const Bitmap &other, int frame)
|
||||||
p->animation.width = other.width();
|
p->animation.width = other.width();
|
||||||
p->animation.height = other.height();
|
p->animation.height = other.height();
|
||||||
p->animation.lastFrame = 0;
|
p->animation.lastFrame = 0;
|
||||||
|
p->animation.playTime = 0;
|
||||||
|
p->animation.startTime = 0;
|
||||||
p->animation.loop = other.getLooping();
|
p->animation.loop = other.getLooping();
|
||||||
|
|
||||||
for (TEXFBO &sourceframe : other.getFrames()) {
|
for (TEXFBO &sourceframe : other.getFrames()) {
|
||||||
|
@ -776,8 +787,6 @@ void Bitmap::stretchBlt(const IntRect &destRect,
|
||||||
|
|
||||||
// Don't need this, right? This function is fine with megasurfaces it seems
|
// Don't need this, right? This function is fine with megasurfaces it seems
|
||||||
//GUARD_MEGA;
|
//GUARD_MEGA;
|
||||||
|
|
||||||
GUARD_PLAYING;
|
|
||||||
|
|
||||||
if (source.isDisposed())
|
if (source.isDisposed())
|
||||||
return;
|
return;
|
||||||
|
@ -1249,8 +1258,6 @@ bool Bitmap::getRaw(void *output, int output_size)
|
||||||
|
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_PLAYING;
|
|
||||||
|
|
||||||
if (!p->animation.enabled && (p->surface || p->megaSurface)) {
|
if (!p->animation.enabled && (p->surface || p->megaSurface)) {
|
||||||
void *src = (p->megaSurface) ? p->megaSurface->pixels : p->surface->pixels;
|
void *src = (p->megaSurface) ? p->megaSurface->pixels : p->surface->pixels;
|
||||||
memcpy(output, src, output_size);
|
memcpy(output, src, output_size);
|
||||||
|
@ -1267,7 +1274,6 @@ void Bitmap::replaceRaw(void *pixel_data, int size)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
GUARD_PLAYING;
|
|
||||||
|
|
||||||
int w = width();
|
int w = width();
|
||||||
int h = height();
|
int h = height();
|
||||||
|
@ -1288,7 +1294,6 @@ void Bitmap::saveToFile(const char *filename)
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
GUARD_PLAYING;
|
|
||||||
|
|
||||||
SDL_Surface *surf = SDL_CreateRGBSurface(0, width(), height(),p->format->BitsPerPixel, p->format->Rmask,p->format->Gmask,p->format->Bmask,p->format->Amask);
|
SDL_Surface *surf = SDL_CreateRGBSurface(0, width(), height(),p->format->BitsPerPixel, p->format->Rmask,p->format->Gmask,p->format->Bmask,p->format->Amask);
|
||||||
|
|
||||||
|
@ -1821,14 +1826,6 @@ void Bitmap::ensureAnimated() const
|
||||||
GUARD_UNANIMATED;
|
GUARD_UNANIMATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bitmap::ensureNotPlaying() const
|
|
||||||
{
|
|
||||||
if (isDisposed())
|
|
||||||
return;
|
|
||||||
|
|
||||||
GUARD_PLAYING;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bitmap::stop()
|
void Bitmap::stop()
|
||||||
{
|
{
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
@ -1903,8 +1900,6 @@ int Bitmap::addFrame(Bitmap &source, int position)
|
||||||
|
|
||||||
GUARD_MEGA;
|
GUARD_MEGA;
|
||||||
|
|
||||||
source.ensureNotPlaying();
|
|
||||||
|
|
||||||
if (source.height() != height() || source.width() != width())
|
if (source.height() != height() || source.width() != width())
|
||||||
throw Exception(Exception::MKXPError, "Animations with varying dimensions are not supported (%ix%i vs %ix%i)",
|
throw Exception(Exception::MKXPError, "Animations with varying dimensions are not supported (%ix%i vs %ix%i)",
|
||||||
source.width(), source.height(), width(), height());
|
source.width(), source.height(), width(), height());
|
||||||
|
@ -1917,6 +1912,8 @@ int Bitmap::addFrame(Bitmap &source, int position)
|
||||||
p->animation.height = p->gl.height;
|
p->animation.height = p->gl.height;
|
||||||
p->animation.enabled = true;
|
p->animation.enabled = true;
|
||||||
p->animation.lastFrame = 0;
|
p->animation.lastFrame = 0;
|
||||||
|
p->animation.playTime = 0;
|
||||||
|
p->animation.startTime = 0;
|
||||||
|
|
||||||
if (p->animation.fps <= 0)
|
if (p->animation.fps <= 0)
|
||||||
p->animation.fps = shState->graphics().getFrameRate();
|
p->animation.fps = shState->graphics().getFrameRate();
|
||||||
|
@ -1982,7 +1979,7 @@ void Bitmap::removeFrame(int position) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bitmap::nextFrame()
|
void Bitmap::nextFrame(bool iteration)
|
||||||
{
|
{
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
|
@ -2003,9 +2000,8 @@ void Bitmap::previousFrame()
|
||||||
guardDisposed();
|
guardDisposed();
|
||||||
|
|
||||||
GUARD_UNANIMATED;
|
GUARD_UNANIMATED;
|
||||||
|
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
if (p->animation.lastFrame <= 0) {
|
if (p->animation.lastFrame <= 0) {
|
||||||
if (!p->animation.loop) {
|
if (!p->animation.loop) {
|
||||||
p->animation.lastFrame = 0;
|
p->animation.lastFrame = 0;
|
||||||
|
@ -2062,6 +2058,17 @@ bool Bitmap::getLooping() const
|
||||||
return p->animation.loop;
|
return p->animation.loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called when a sprite attached to this Bitmap is being drawn
|
||||||
|
// Makes sure the bitmap begins its animation on time, on top of
|
||||||
|
// making sure that the bitmap doesn't begin actually moving until
|
||||||
|
// Graphics.update is called once
|
||||||
|
void Bitmap::syncAnimationTimer()
|
||||||
|
{
|
||||||
|
if (!p->animation.enabled || isDisposed()) return;
|
||||||
|
p->animation.updateTimer();
|
||||||
|
}
|
||||||
|
|
||||||
void Bitmap::bindTex(ShaderBase &shader)
|
void Bitmap::bindTex(ShaderBase &shader)
|
||||||
{
|
{
|
||||||
p->bindTexture(shader);
|
p->bindTexture(shader);
|
||||||
|
|
|
@ -146,6 +146,8 @@ public:
|
||||||
bool getLooping() const;
|
bool getLooping() const;
|
||||||
|
|
||||||
void ensureNotPlaying() const;
|
void ensureNotPlaying() const;
|
||||||
|
|
||||||
|
void syncAnimationTimer();
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
/* Binds the backing texture and sets the correct
|
/* Binds the backing texture and sets the correct
|
||||||
|
|
|
@ -631,6 +631,10 @@ unsigned long long Graphics::getDelta() {
|
||||||
return shState->runTime() - p->last_update;
|
return shState->runTime() - p->last_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long long Graphics::lastUpdate() {
|
||||||
|
return p->last_update;
|
||||||
|
}
|
||||||
|
|
||||||
void Graphics::update() {
|
void Graphics::update() {
|
||||||
p->last_update = shState->runTime();
|
p->last_update = shState->runTime();
|
||||||
p->checkShutDownReset();
|
p->checkShutDownReset();
|
||||||
|
|
|
@ -35,6 +35,7 @@ class Graphics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unsigned long long getDelta();
|
unsigned long long getDelta();
|
||||||
|
unsigned long long lastUpdate();
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
void freeze();
|
void freeze();
|
||||||
|
|
|
@ -579,6 +579,8 @@ void Sprite::draw()
|
||||||
p->wave.qArray.draw();
|
p->wave.qArray.draw();
|
||||||
else
|
else
|
||||||
p->quad.draw();
|
p->quad.draw();
|
||||||
|
|
||||||
|
p->bitmap->syncAnimationTimer();
|
||||||
|
|
||||||
glState.blendMode.pop();
|
glState.blendMode.pop();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue