Sprite: Implement wave effect (RGSS2)
This initial implementation emulates the way RMVX splits the sprite into "chunks" of about 8 pixels, which it then scrolls left/right on a vertical sine wave. It even replicates the weird behavior when wave_amp < 0, namely "shrinking" the src_rect horizontally. As with bush_opacity, this effect in combination with rotation will render differently from RMVX.
This commit is contained in:
parent
42b10fd2ee
commit
af9039f58d
|
@ -69,6 +69,39 @@ DEF_PROP_F(Sprite, Angle)
|
|||
|
||||
DEF_PROP_B(Sprite, Mirror)
|
||||
|
||||
#ifdef RGSS2
|
||||
|
||||
RB_METHOD(spriteWidth)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
Sprite *s = getPrivateData<Sprite>(self);
|
||||
|
||||
int value;
|
||||
GUARD_EXC( value = s->getWidth(); )
|
||||
|
||||
return rb_fix_new(value);
|
||||
}
|
||||
|
||||
RB_METHOD(spriteHeight)
|
||||
{
|
||||
RB_UNUSED_PARAM;
|
||||
|
||||
Sprite *s = getPrivateData<Sprite>(self);
|
||||
|
||||
int value;
|
||||
GUARD_EXC( value = s->getHeight(); )
|
||||
|
||||
return rb_fix_new(value);
|
||||
}
|
||||
|
||||
DEF_PROP_I(Sprite, WaveAmp)
|
||||
DEF_PROP_I(Sprite, WaveLength)
|
||||
DEF_PROP_I(Sprite, WaveSpeed)
|
||||
DEF_PROP_F(Sprite, WavePhase)
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
spriteBindingInit()
|
||||
{
|
||||
|
@ -98,4 +131,14 @@ spriteBindingInit()
|
|||
INIT_PROP_BIND( Sprite, BlendType, "blend_type" );
|
||||
INIT_PROP_BIND( Sprite, Color, "color" );
|
||||
INIT_PROP_BIND( Sprite, Tone, "tone" );
|
||||
|
||||
#ifdef RGSS2
|
||||
_rb_define_method(klass, "width", spriteWidth);
|
||||
_rb_define_method(klass, "height", spriteHeight);
|
||||
|
||||
INIT_PROP_BIND( Sprite, WaveAmp, "wave_amp" );
|
||||
INIT_PROP_BIND( Sprite, WaveLength, "wave_length" );
|
||||
INIT_PROP_BIND( Sprite, WaveSpeed, "wave_speed" );
|
||||
INIT_PROP_BIND( Sprite, WavePhase, "wave_phase" );
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -68,6 +68,35 @@ DEF_PROP_F(Sprite, Angle)
|
|||
|
||||
DEF_PROP_B(Sprite, Mirror)
|
||||
|
||||
#ifdef RGSS2
|
||||
|
||||
MRB_METHOD(spriteWidth)
|
||||
{
|
||||
Sprite *s = getPrivateData<Sprite>(mrb, self);
|
||||
|
||||
int value;
|
||||
GUARD_EXC( value = s->getWidth(); )
|
||||
|
||||
return mrb_fixnum_value(value);
|
||||
}
|
||||
|
||||
MRB_METHOD(spriteHeight)
|
||||
{
|
||||
Sprite *s = getPrivateData<Sprite>(mrb, self);
|
||||
|
||||
int value;
|
||||
GUARD_EXC( value = s->getHeight(); )
|
||||
|
||||
return mrb_fixnum_value(value);
|
||||
}
|
||||
|
||||
DEF_PROP_I(Sprite, WaveAmp)
|
||||
DEF_PROP_I(Sprite, WaveLength)
|
||||
DEF_PROP_I(Sprite, WaveSpeed)
|
||||
DEF_PROP_F(Sprite, WavePhase)
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
spriteBindingInit(mrb_state *mrb)
|
||||
{
|
||||
|
@ -95,5 +124,15 @@ spriteBindingInit(mrb_state *mrb)
|
|||
INIT_PROP_BIND( Sprite, Color, "color" );
|
||||
INIT_PROP_BIND( Sprite, Tone, "tone" );
|
||||
|
||||
#ifdef RGSS2
|
||||
mrb_define_method(mrb, klass, "width", spriteWidth, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, klass, "height", spriteHeight, MRB_ARGS_NONE());
|
||||
|
||||
INIT_PROP_BIND( Sprite, WaveAmp, "wave_amp" );
|
||||
INIT_PROP_BIND( Sprite, WaveLength, "wave_length" );
|
||||
INIT_PROP_BIND( Sprite, WaveSpeed, "wave_speed" );
|
||||
INIT_PROP_BIND( Sprite, WavePhase, "wave_phase" );
|
||||
#endif
|
||||
|
||||
mrb_define_method(mrb, klass, "inspect", inspectObject, MRB_ARGS_NONE());
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
flashAlpha = flashColor.w;
|
||||
}
|
||||
|
||||
void update()
|
||||
virtual void update()
|
||||
{
|
||||
if (!flashing)
|
||||
return;
|
||||
|
|
|
@ -34,17 +34,41 @@
|
|||
typedef uint32_t index_t;
|
||||
#define _GL_INDEX_TYPE GL_UNSIGNED_INT
|
||||
|
||||
struct ColorQuadArray
|
||||
/* A small hack to get mutable QuadArray constructors */
|
||||
inline void initBufferBindings(Vertex *)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
glEnableVertexAttribArray(Shader::Color);
|
||||
glEnableVertexAttribArray(Shader::Position);
|
||||
glEnableVertexAttribArray(Shader::TexCoord);
|
||||
|
||||
glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset());
|
||||
glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset());
|
||||
glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::texPosOffset());
|
||||
}
|
||||
|
||||
inline void initBufferBindings(SVertex *)
|
||||
{
|
||||
glEnableVertexAttribArray(Shader::Position);
|
||||
glEnableVertexAttribArray(Shader::TexCoord);
|
||||
|
||||
glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), SVertex::posOffset());
|
||||
glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), SVertex::texPosOffset());
|
||||
}
|
||||
|
||||
template<class VertexType>
|
||||
struct QuadArray
|
||||
{
|
||||
std::vector<VertexType> vertices;
|
||||
|
||||
VBO::ID vbo;
|
||||
VAO::ID vao;
|
||||
|
||||
int quadCount;
|
||||
GLsizeiptr vboSize;
|
||||
|
||||
ColorQuadArray()
|
||||
: quadCount(0)
|
||||
QuadArray()
|
||||
: quadCount(0),
|
||||
vboSize(-1)
|
||||
{
|
||||
vbo = VBO::gen();
|
||||
vao = VAO::gen();
|
||||
|
@ -53,20 +77,16 @@ struct ColorQuadArray
|
|||
VBO::bind(vbo);
|
||||
shState->bindQuadIBO();
|
||||
|
||||
glEnableVertexAttribArray(Shader::Color);
|
||||
glEnableVertexAttribArray(Shader::Position);
|
||||
glEnableVertexAttribArray(Shader::TexCoord);
|
||||
|
||||
glVertexAttribPointer(Shader::Color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::colorOffset());
|
||||
glVertexAttribPointer(Shader::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::posOffset());
|
||||
glVertexAttribPointer(Shader::TexCoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), Vertex::texPosOffset());
|
||||
/* Call correct implementation here via overloading */
|
||||
VertexType *dummy = 0;
|
||||
initBufferBindings(dummy);
|
||||
|
||||
VAO::unbind();
|
||||
IBO::unbind();
|
||||
VBO::unbind();
|
||||
}
|
||||
|
||||
~ColorQuadArray()
|
||||
~QuadArray()
|
||||
{
|
||||
VBO::del(vbo);
|
||||
VAO::del(vao);
|
||||
|
@ -78,15 +98,36 @@ struct ColorQuadArray
|
|||
quadCount = size;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
vertices.clear();
|
||||
quadCount = 0;
|
||||
}
|
||||
|
||||
/* This needs to be called after the final 'append()' call
|
||||
* and previous to the first 'draw()' call. */
|
||||
void commit()
|
||||
{
|
||||
VBO::bind(vbo);
|
||||
VBO::uploadData(vertices.size() * sizeof(Vertex), &vertices[0], GL_DYNAMIC_DRAW);
|
||||
VBO::unbind();
|
||||
|
||||
shState->ensureQuadIBO(quadCount);
|
||||
GLsizeiptr size = vertices.size() * sizeof(VertexType);
|
||||
|
||||
if (size > vboSize)
|
||||
{
|
||||
/* New data exceeds already allocated size.
|
||||
* Reallocate VBO. */
|
||||
VBO::uploadData(size, &vertices[0], GL_DYNAMIC_DRAW);
|
||||
vboSize = size;
|
||||
|
||||
shState->ensureQuadIBO(quadCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* New data fits in allocated size */
|
||||
VBO::uploadSubData(0, size, &vertices[0]);
|
||||
}
|
||||
|
||||
VBO::unbind();
|
||||
}
|
||||
|
||||
void draw(size_t offset, size_t count)
|
||||
|
@ -110,4 +151,7 @@ struct ColorQuadArray
|
|||
}
|
||||
};
|
||||
|
||||
typedef QuadArray<Vertex> ColorQuadArray;
|
||||
typedef QuadArray<SVertex> SimpleQuadArray;
|
||||
|
||||
#endif // QUADARRAY_H
|
||||
|
|
194
src/sprite.cpp
194
src/sprite.cpp
|
@ -32,6 +32,9 @@
|
|||
#include "transform.h"
|
||||
#include "shader.h"
|
||||
#include "glstate.h"
|
||||
#include "quadarray.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <SDL_rect.h>
|
||||
|
||||
|
@ -63,6 +66,22 @@ struct SpritePrivate
|
|||
Color *color;
|
||||
Tone *tone;
|
||||
|
||||
#ifdef RGSS2
|
||||
struct
|
||||
{
|
||||
int amp;
|
||||
int length;
|
||||
int speed;
|
||||
float phase;
|
||||
|
||||
/* Wave effect is active (amp != 0) */
|
||||
bool active;
|
||||
/* qArray needs updating */
|
||||
bool dirty;
|
||||
SimpleQuadArray qArray;
|
||||
} wave;
|
||||
#endif
|
||||
|
||||
EtcTemps tmp;
|
||||
|
||||
sigc::connection prepareCon;
|
||||
|
@ -87,6 +106,13 @@ struct SpritePrivate
|
|||
|
||||
prepareCon = shState->prepareDraw.connect
|
||||
(sigc::mem_fun(this, &SpritePrivate::prepare));
|
||||
|
||||
#ifdef RGSS2
|
||||
wave.amp = 0;
|
||||
wave.length = 180;
|
||||
wave.speed = 360;
|
||||
wave.phase = 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
~SpritePrivate()
|
||||
|
@ -117,6 +143,10 @@ struct SpritePrivate
|
|||
|
||||
quad.setPosRect(IntRect(0, 0, srcRect->width, srcRect->height));
|
||||
recomputeBushDepth();
|
||||
|
||||
#ifdef RGSS2
|
||||
wave.dirty = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void updateSrcRectCon()
|
||||
|
@ -141,6 +171,16 @@ struct SpritePrivate
|
|||
if (!opacity)
|
||||
return;
|
||||
|
||||
#ifdef RGSS2
|
||||
if (wave.active)
|
||||
{
|
||||
/* Don't do expensive wave bounding box
|
||||
* calculations */
|
||||
isVisible = true;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Compare sprite bounding box against the scene */
|
||||
|
||||
/* If sprite is zoomed/rotated, just opt out for now
|
||||
|
@ -161,8 +201,102 @@ struct SpritePrivate
|
|||
isVisible = SDL_HasIntersection(&self, &sceneRect);
|
||||
}
|
||||
|
||||
#ifdef RGSS2
|
||||
void emitWaveChunk(SVertex *&vert, float phase, int width,
|
||||
float zoomY, int chunkY, int chunkLength)
|
||||
{
|
||||
float wavePos = phase + (chunkY / (float) wave.length) * M_PI * 2;
|
||||
float chunkX = sin(wavePos) * wave.amp;
|
||||
|
||||
FloatRect tex(0, chunkY / zoomY, width, chunkLength / zoomY);
|
||||
FloatRect pos = tex;
|
||||
pos.x = chunkX;
|
||||
|
||||
Quad::setTexPosRect(vert, tex, pos);
|
||||
vert += 4;
|
||||
}
|
||||
|
||||
void updateWave()
|
||||
{
|
||||
if (!bitmap)
|
||||
return;
|
||||
|
||||
if (wave.amp == 0)
|
||||
{
|
||||
wave.active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
wave.active = true;
|
||||
|
||||
int width = srcRect->width;
|
||||
int height = srcRect->height;
|
||||
float zoomY = trans.getScale().y;
|
||||
|
||||
if (wave.amp < -(width / 2))
|
||||
{
|
||||
wave.qArray.resize(0);
|
||||
wave.qArray.commit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* RMVX does this, and I have no fucking clue why */
|
||||
if (wave.amp < 0)
|
||||
{
|
||||
wave.qArray.resize(1);
|
||||
|
||||
int x = -wave.amp;
|
||||
int w = width - x * 2;
|
||||
|
||||
FloatRect tex(x, srcRect->y, w, srcRect->height);
|
||||
|
||||
Quad::setTexPosRect(&wave.qArray.vertices[0], tex, tex);
|
||||
wave.qArray.commit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* The length of the sprite as it appears on screen */
|
||||
int visibleLength = height * zoomY;
|
||||
|
||||
/* First chunk length (aligned to 8 pixel boundary */
|
||||
int firstLength = ((int) trans.getPosition().y) % 8;
|
||||
|
||||
/* Amount of full 8 pixel chunks in the middle */
|
||||
int chunks = (visibleLength - firstLength) / 8;
|
||||
|
||||
/* Final chunk length */
|
||||
int lastLength = (visibleLength - firstLength) % 8;
|
||||
|
||||
wave.qArray.resize(!!firstLength + chunks + !!lastLength);
|
||||
SVertex *vert = &wave.qArray.vertices[0];
|
||||
|
||||
float phase = (wave.phase * M_PI) / 180.f;
|
||||
|
||||
if (firstLength > 0)
|
||||
emitWaveChunk(vert, phase, width, zoomY, 0, firstLength);
|
||||
|
||||
for (int i = 0; i < chunks; ++i)
|
||||
emitWaveChunk(vert, phase, width, zoomY, firstLength + i * 8, 8);
|
||||
|
||||
if (lastLength > 0)
|
||||
emitWaveChunk(vert, phase, width, zoomY, firstLength + chunks * 8, lastLength);
|
||||
|
||||
wave.qArray.commit();
|
||||
}
|
||||
#endif
|
||||
|
||||
void prepare()
|
||||
{
|
||||
#ifdef RGSS2
|
||||
if (wave.dirty)
|
||||
{
|
||||
updateWave();
|
||||
wave.dirty = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
updateVisibility();
|
||||
}
|
||||
};
|
||||
|
@ -193,14 +327,21 @@ DEF_ATTR_RD_SIMPLE(Sprite, Angle, float, p->trans.getRotation())
|
|||
DEF_ATTR_RD_SIMPLE(Sprite, Mirror, bool, p->mirrored)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, BushDepth, int, p->bushDepth)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, BlendType, int, p->blendType)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height)
|
||||
|
||||
DEF_ATTR_SIMPLE(Sprite, BushOpacity, int, p->bushOpacity)
|
||||
DEF_ATTR_SIMPLE(Sprite, Opacity, int, p->opacity)
|
||||
DEF_ATTR_SIMPLE(Sprite, Color, Color*, p->color)
|
||||
DEF_ATTR_SIMPLE(Sprite, Tone, Tone*, p->tone)
|
||||
|
||||
#ifdef RGSS2
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Width, int, p->srcRect->width)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, Height, int, p->srcRect->height)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, WaveAmp, int, p->wave.amp)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, WaveLength, int, p->wave.length)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, WaveSpeed, int, p->wave.speed)
|
||||
DEF_ATTR_RD_SIMPLE(Sprite, WavePhase, float, p->wave.phase)
|
||||
#endif
|
||||
|
||||
void Sprite::setBitmap(Bitmap *bitmap)
|
||||
{
|
||||
GUARD_DISPOSED
|
||||
|
@ -218,6 +359,10 @@ void Sprite::setBitmap(Bitmap *bitmap)
|
|||
*p->srcRect = bitmap->rect();
|
||||
p->onSrcRectChange();
|
||||
p->quad.setPosRect(p->srcRect->toFloatRect());
|
||||
|
||||
#ifdef RGSS2
|
||||
p->wave.dirty = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sprite::setSrcRect(Rect *rect)
|
||||
|
@ -252,6 +397,10 @@ void Sprite::setY(int value)
|
|||
return;
|
||||
|
||||
p->trans.setPosition(Vec2(getX(), value));
|
||||
|
||||
#ifdef RGSS2
|
||||
p->wave.dirty = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sprite::setOX(int value)
|
||||
|
@ -293,6 +442,10 @@ void Sprite::setZoomY(float value)
|
|||
|
||||
p->trans.setScale(Vec2(getZoomX(), value));
|
||||
p->recomputeBushDepth();
|
||||
|
||||
#ifdef RGSS2
|
||||
p->wave.dirty = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Sprite::setAngle(float value)
|
||||
|
@ -346,6 +499,36 @@ void Sprite::setBlendType(int type)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef RGSS2
|
||||
|
||||
#define DEF_WAVE_SETTER(Name, name, type) \
|
||||
void Sprite::setWave##Name(type value) \
|
||||
{ \
|
||||
GUARD_DISPOSED; \
|
||||
if (p->wave.name == value) \
|
||||
return; \
|
||||
p->wave.name = value; \
|
||||
p->wave.dirty = true; \
|
||||
}
|
||||
|
||||
DEF_WAVE_SETTER(Amp, amp, int)
|
||||
DEF_WAVE_SETTER(Length, length, int)
|
||||
DEF_WAVE_SETTER(Speed, speed, int)
|
||||
DEF_WAVE_SETTER(Phase, phase, float)
|
||||
|
||||
#undef DEF_WAVE_SETTER
|
||||
|
||||
/* Flashable */
|
||||
void Sprite::update()
|
||||
{
|
||||
Flashable::update();
|
||||
|
||||
p->wave.phase += p->wave.speed / 180;
|
||||
p->wave.dirty = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Disposable */
|
||||
void Sprite::releaseResources()
|
||||
{
|
||||
|
@ -407,7 +590,14 @@ void Sprite::draw()
|
|||
|
||||
p->bitmap->bindTex(*base);
|
||||
|
||||
#ifdef RGSS2
|
||||
if (p->wave.active)
|
||||
p->wave.qArray.draw();
|
||||
else
|
||||
p->quad.draw();
|
||||
#else
|
||||
p->quad.draw();
|
||||
#endif
|
||||
|
||||
glState.blendMode.pop();
|
||||
}
|
||||
|
|
15
src/sprite.h
15
src/sprite.h
|
@ -41,9 +41,6 @@ public:
|
|||
Sprite(Viewport *viewport = 0);
|
||||
~Sprite();
|
||||
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
DECL_ATTR( Bitmap, Bitmap* )
|
||||
DECL_ATTR( SrcRect, Rect* )
|
||||
DECL_ATTR( X, int )
|
||||
|
@ -61,6 +58,18 @@ public:
|
|||
DECL_ATTR( Color, Color* )
|
||||
DECL_ATTR( Tone, Tone* )
|
||||
|
||||
#ifdef RGSS2
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
|
||||
DECL_ATTR( WaveAmp, int )
|
||||
DECL_ATTR( WaveLength, int )
|
||||
DECL_ATTR( WaveSpeed, int )
|
||||
DECL_ATTR( WavePhase, float )
|
||||
|
||||
void update();
|
||||
#endif
|
||||
|
||||
private:
|
||||
SpritePrivate *p;
|
||||
|
||||
|
|
Loading…
Reference in New Issue