diff --git a/binding-mri/sprite-binding.cpp b/binding-mri/sprite-binding.cpp index e29c6a4..2d515cd 100644 --- a/binding-mri/sprite-binding.cpp +++ b/binding-mri/sprite-binding.cpp @@ -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(self); + + int value; + GUARD_EXC( value = s->getWidth(); ) + + return rb_fix_new(value); +} + +RB_METHOD(spriteHeight) +{ + RB_UNUSED_PARAM; + + Sprite *s = getPrivateData(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 } diff --git a/binding-mruby/sprite-binding.cpp b/binding-mruby/sprite-binding.cpp index b37feb1..2eabc71 100644 --- a/binding-mruby/sprite-binding.cpp +++ b/binding-mruby/sprite-binding.cpp @@ -68,6 +68,35 @@ DEF_PROP_F(Sprite, Angle) DEF_PROP_B(Sprite, Mirror) +#ifdef RGSS2 + +MRB_METHOD(spriteWidth) +{ + Sprite *s = getPrivateData(mrb, self); + + int value; + GUARD_EXC( value = s->getWidth(); ) + + return mrb_fixnum_value(value); +} + +MRB_METHOD(spriteHeight) +{ + Sprite *s = getPrivateData(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()); } diff --git a/src/flashable.h b/src/flashable.h index e32c89f..bd7e21d 100644 --- a/src/flashable.h +++ b/src/flashable.h @@ -55,7 +55,7 @@ public: flashAlpha = flashColor.w; } - void update() + virtual void update() { if (!flashing) return; diff --git a/src/quadarray.h b/src/quadarray.h index 249bf8e..0d6d4b8 100644 --- a/src/quadarray.h +++ b/src/quadarray.h @@ -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 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 +struct QuadArray +{ + std::vector 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 ColorQuadArray; +typedef QuadArray SimpleQuadArray; + #endif // QUADARRAY_H diff --git a/src/sprite.cpp b/src/sprite.cpp index 21bdf92..b5888c1 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp @@ -32,6 +32,9 @@ #include "transform.h" #include "shader.h" #include "glstate.h" +#include "quadarray.h" + +#include #include @@ -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(); } diff --git a/src/sprite.h b/src/sprite.h index fd2f631..22ee697 100644 --- a/src/sprite.h +++ b/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;