diff --git a/binding/sprite-binding.cpp b/binding/sprite-binding.cpp index 890cf1df..402af549 100644 --- a/binding/sprite-binding.cpp +++ b/binding/sprite-binding.cpp @@ -52,6 +52,7 @@ RB_METHOD(spriteInitialize) { } DEF_GFX_PROP_OBJ_REF(Sprite, Bitmap, Bitmap, "bitmap") +DEF_GFX_PROP_OBJ_REF(Sprite, Bitmap, Pattern, "pattern") DEF_GFX_PROP_OBJ_VAL(Sprite, Rect, SrcRect, "src_rect") DEF_GFX_PROP_OBJ_VAL(Sprite, Color, Color, "color") DEF_GFX_PROP_OBJ_VAL(Sprite, Tone, Tone, "tone") @@ -64,6 +65,9 @@ DEF_GFX_PROP_I(Sprite, BushDepth) DEF_GFX_PROP_I(Sprite, BushOpacity) DEF_GFX_PROP_I(Sprite, Opacity) DEF_GFX_PROP_I(Sprite, BlendType) +DEF_GFX_PROP_I(Sprite, PatternOpacity) +DEF_GFX_PROP_I(Sprite, PatternScrollX) +DEF_GFX_PROP_I(Sprite, PatternScrollY) DEF_GFX_PROP_I(Sprite, WaveAmp) DEF_GFX_PROP_I(Sprite, WaveLength) DEF_GFX_PROP_I(Sprite, WaveSpeed) @@ -132,6 +136,11 @@ void spriteBindingInit() { INIT_PROP_BIND(Sprite, BushOpacity, "bush_opacity"); + INIT_PROP_BIND(Sprite, Pattern, "pattern"); + INIT_PROP_BIND(Sprite, PatternOpacity, "pattern_opacity"); + INIT_PROP_BIND(Sprite, PatternScrollX, "pattern_scroll_x"); + INIT_PROP_BIND(Sprite, PatternScrollY, "pattern_scroll_y"); + INIT_PROP_BIND(Sprite, WaveAmp, "wave_amp"); INIT_PROP_BIND(Sprite, WaveLength, "wave_length"); INIT_PROP_BIND(Sprite, WaveSpeed, "wave_speed"); diff --git a/shader/sprite.frag b/shader/sprite.frag index 93c35f38..b8e485dd 100644 --- a/shader/sprite.frag +++ b/shader/sprite.frag @@ -9,14 +9,26 @@ uniform lowp vec4 color; uniform float bushDepth; uniform lowp float bushOpacity; +uniform sampler2D pattern; +uniform lowp float patternOpacity; +uniform bool renderPattern; + varying vec2 v_texCoord; +varying vec2 v_patCoord; const vec3 lumaF = vec3(.299, .587, .114); +const vec2 repeat = vec2(1, 1); void main() { /* Sample source color */ vec4 frag = texture2D(texture, v_texCoord); + + /* Apply the pattern if needed */ + if (renderPattern) { + vec4 pattfrag = texture2D(pattern, mod(v_patCoord, repeat)); + frag.rgb = mix(frag.rgb, pattfrag.rgb, pattfrag.a * patternOpacity); + } /* Apply gray */ float luma = dot(frag.rgb, lumaF); diff --git a/shader/sprite.vert b/shader/sprite.vert index a048ae58..17e2224a 100644 --- a/shader/sprite.vert +++ b/shader/sprite.vert @@ -4,14 +4,22 @@ uniform mat4 projMat; uniform mat4 spriteMat; uniform vec2 texSizeInv; +uniform vec2 patternSizeInv; +uniform vec2 patternScroll; +uniform bool renderPattern; attribute vec2 position; attribute vec2 texCoord; varying vec2 v_texCoord; +varying vec2 v_patCoord; void main() { gl_Position = projMat * spriteMat * vec4(position, 0, 1); + v_texCoord = texCoord * texSizeInv; + if (renderPattern) { + v_patCoord = (texCoord * patternSizeInv) - (patternScroll * patternSizeInv); + } } diff --git a/src/display/gl/shader.cpp b/src/display/gl/shader.cpp index 995aa200..996e9da2 100644 --- a/src/display/gl/shader.cpp +++ b/src/display/gl/shader.cpp @@ -245,6 +245,11 @@ void Shader::initFromFile(const char *_vertFile, const char *_fragFile, _vertFile, _fragFile, programName); } +void Shader::setVec2Uniform(GLint location, const Vec2 &vec) +{ + gl.Uniform2f(location, vec.x, vec.y); +} + void Shader::setVec4Uniform(GLint location, const Vec4 &vec) { gl.Uniform4f(location, vec.x, vec.y, vec.z, vec.w); @@ -463,6 +468,11 @@ SpriteShader::SpriteShader() GET_U(opacity); GET_U(bushDepth); GET_U(bushOpacity); + GET_U(pattern); + GET_U(renderPattern); + GET_U(patternSizeInv); + GET_U(patternOpacity); + GET_U(patternScroll); } void SpriteShader::setSpriteMat(const float value[16]) @@ -495,6 +505,27 @@ void SpriteShader::setBushOpacity(float value) gl.Uniform1f(u_bushOpacity, value); } +void SpriteShader::setPattern(const TEX::ID pattern, const Vec2 &dimensions) +{ + setTexUniform(u_pattern, 1, pattern); + gl.Uniform2f(u_patternSizeInv, 1.f / dimensions.x, 1.f / dimensions.y); +} + +void SpriteShader::setShouldRenderPattern(bool value) +{ + gl.Uniform1i(u_renderPattern, value); +} + +void SpriteShader::setPatternOpacity(float value) +{ + gl.Uniform1f(u_patternOpacity, value); +} + +void SpriteShader::setPatternScroll(const Vec2 &scroll) +{ + setVec2Uniform(u_patternScroll, scroll); +} + PlaneShader::PlaneShader() { diff --git a/src/display/gl/shader.h b/src/display/gl/shader.h index 8d828434..e4b5d599 100644 --- a/src/display/gl/shader.h +++ b/src/display/gl/shader.h @@ -53,6 +53,7 @@ protected: const char *programName); static void setVec4Uniform(GLint location, const Vec4 &vec); + static void setVec2Uniform(GLint location, const Vec2 &vec); static void setTexUniform(GLint location, unsigned unitIndex, TEX::ID texture); GLuint vertShader, fragShader; @@ -190,9 +191,13 @@ public: void setOpacity(float value); void setBushDepth(float value); void setBushOpacity(float value); + void setPattern(const TEX::ID pattern, const Vec2 &dimensions); + void setShouldRenderPattern(bool value); + void setPatternOpacity(float value); + void setPatternScroll(const Vec2 &scroll); private: - GLint u_spriteMat, u_tone, u_opacity, u_color, u_bushDepth, u_bushOpacity; + GLint u_spriteMat, u_tone, u_opacity, u_color, u_bushDepth, u_bushOpacity, u_pattern, u_renderPattern, u_patternSizeInv, u_patternOpacity, u_patternScroll; }; class PlaneShader : public ShaderBase diff --git a/src/display/sprite.cpp b/src/display/sprite.cpp index 097cda64..cbed7cf4 100644 --- a/src/display/sprite.cpp +++ b/src/display/sprite.cpp @@ -1,23 +1,23 @@ /* -** sprite.cpp -** -** This file is part of mkxp. -** -** Copyright (C) 2013 Jonas Kulla -** -** mkxp 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. -** -** mkxp 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 mkxp. If not, see . -*/ + ** sprite.cpp + ** + ** This file is part of mkxp. + ** + ** Copyright (C) 2013 Jonas Kulla + ** + ** mkxp 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. + ** + ** mkxp 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 mkxp. If not, see . + */ #include "sprite.h" @@ -45,269 +45,277 @@ struct SpritePrivate { - Bitmap *bitmap; - - Quad quad; - Transform trans; - - Rect *srcRect; - sigslot::connection srcRectCon; - - bool mirrored; - int bushDepth; - float efBushDepth; - NormValue bushOpacity; - NormValue opacity; - BlendType blendType; - - IntRect sceneRect; - Vec2i sceneOrig; - - /* Would this sprite be visible on - * the screen if drawn? */ - bool isVisible; - - Color *color; - Tone *tone; - - 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; - - EtcTemps tmp; - - sigslot::connection prepareCon; - - SpritePrivate() - : bitmap(0), - srcRect(&tmp.rect), - mirrored(false), - bushDepth(0), - efBushDepth(0), - bushOpacity(128), - opacity(255), - blendType(BlendNormal), - isVisible(false), - color(&tmp.color), - tone(&tmp.tone) - - { - sceneRect.x = sceneRect.y = 0; - - updateSrcRectCon(); - - prepareCon = shState->prepareDraw.connect - (&SpritePrivate::prepare, this); - - wave.amp = 0; - wave.length = 180; - wave.speed = 360; - wave.phase = 0.0f; - wave.dirty = false; - } - - ~SpritePrivate() - { - srcRectCon.disconnect(); - prepareCon.disconnect(); - } - - void recomputeBushDepth() - { - if (nullOrDisposed(bitmap)) - return; - - /* Calculate effective (normalized) bush depth */ - float texBushDepth = (bushDepth / trans.getScale().y) - - (srcRect->y + srcRect->height) + - bitmap->height(); - - efBushDepth = 1.0f - texBushDepth / bitmap->height(); - } - - void onSrcRectChange() - { - FloatRect rect = srcRect->toFloatRect(); - Vec2i bmSize; - - if (!nullOrDisposed(bitmap)) - bmSize = Vec2i(bitmap->width(), bitmap->height()); - - /* Clamp the rectangle so it doesn't reach outside - * the bitmap bounds */ - rect.w = clamp(rect.w, 0, bmSize.x-rect.x); - rect.h = clamp(rect.h, 0, bmSize.y-rect.y); - - quad.setTexRect(mirrored ? rect.hFlipped() : rect); - - quad.setPosRect(FloatRect(0, 0, rect.w, rect.h)); - recomputeBushDepth(); - - wave.dirty = true; - } - - void updateSrcRectCon() - { - /* Cut old connection */ - srcRectCon.disconnect(); - /* Create new one */ - srcRectCon = srcRect->valueChanged.connect - (&SpritePrivate::onSrcRectChange, this); - } - - void updateVisibility() - { - isVisible = false; - - if (nullOrDisposed(bitmap)) - return; - - if (!opacity) - return; - - if (wave.active) - { - /* Don't do expensive wave bounding box - * calculations */ - isVisible = true; - return; - } - - /* Compare sprite bounding box against the scene */ - - /* If sprite is zoomed/rotated, just opt out for now - * for simplicity's sake */ - const Vec2 &scale = trans.getScale(); - if (scale.x != 1 || scale.y != 1 || trans.getRotation() != 0) - { - isVisible = true; - return; - } - - IntRect self; - self.setPos(trans.getPositionI() - (trans.getOriginI() + sceneOrig)); - self.w = bitmap->width(); - self.h = bitmap->height(); - - isVisible = SDL_HasIntersection(&self, &sceneRect); - } - - void emitWaveChunk(SVertex *&vert, float phase, int width, - float zoomY, int chunkY, int chunkLength) - { - float wavePos = phase + (chunkY / (float) wave.length) * (float) (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, mirrored ? tex.hFlipped() : tex, pos); - vert += 4; - } - - void updateWave() - { - if (nullOrDisposed(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 * (float) M_PI) / 180.0f; - - 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(); - } - - void prepare() - { - if (wave.dirty) - { - updateWave(); - wave.dirty = false; - } - - updateVisibility(); - } + Bitmap *bitmap; + + Quad quad; + Transform trans; + + Rect *srcRect; + sigslot::connection srcRectCon; + + bool mirrored; + int bushDepth; + float efBushDepth; + NormValue bushOpacity; + NormValue opacity; + BlendType blendType; + + Bitmap *pattern; + NormValue patternOpacity; + Vec2 patternScroll; + + IntRect sceneRect; + Vec2i sceneOrig; + + /* Would this sprite be visible on + * the screen if drawn? */ + bool isVisible; + + Color *color; + Tone *tone; + + 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; + + EtcTemps tmp; + + sigslot::connection prepareCon; + + SpritePrivate() + : bitmap(0), + srcRect(&tmp.rect), + mirrored(false), + bushDepth(0), + efBushDepth(0), + bushOpacity(128), + opacity(255), + blendType(BlendNormal), + pattern(0), + patternOpacity(255), + isVisible(false), + color(&tmp.color), + tone(&tmp.tone) + + { + sceneRect.x = sceneRect.y = 0; + + updateSrcRectCon(); + + prepareCon = shState->prepareDraw.connect + (&SpritePrivate::prepare, this); + + patternScroll = Vec2(0,0); + + wave.amp = 0; + wave.length = 180; + wave.speed = 360; + wave.phase = 0.0f; + wave.dirty = false; + } + + ~SpritePrivate() + { + srcRectCon.disconnect(); + prepareCon.disconnect(); + } + + void recomputeBushDepth() + { + if (nullOrDisposed(bitmap)) + return; + + /* Calculate effective (normalized) bush depth */ + float texBushDepth = (bushDepth / trans.getScale().y) - + (srcRect->y + srcRect->height) + + bitmap->height(); + + efBushDepth = 1.0f - texBushDepth / bitmap->height(); + } + + void onSrcRectChange() + { + FloatRect rect = srcRect->toFloatRect(); + Vec2i bmSize; + + if (!nullOrDisposed(bitmap)) + bmSize = Vec2i(bitmap->width(), bitmap->height()); + + /* Clamp the rectangle so it doesn't reach outside + * the bitmap bounds */ + rect.w = clamp(rect.w, 0, bmSize.x-rect.x); + rect.h = clamp(rect.h, 0, bmSize.y-rect.y); + + quad.setTexRect(mirrored ? rect.hFlipped() : rect); + + quad.setPosRect(FloatRect(0, 0, rect.w, rect.h)); + recomputeBushDepth(); + + wave.dirty = true; + } + + void updateSrcRectCon() + { + /* Cut old connection */ + srcRectCon.disconnect(); + /* Create new one */ + srcRectCon = srcRect->valueChanged.connect + (&SpritePrivate::onSrcRectChange, this); + } + + void updateVisibility() + { + isVisible = false; + + if (nullOrDisposed(bitmap)) + return; + + if (!opacity) + return; + + if (wave.active) + { + /* Don't do expensive wave bounding box + * calculations */ + isVisible = true; + return; + } + + /* Compare sprite bounding box against the scene */ + + /* If sprite is zoomed/rotated, just opt out for now + * for simplicity's sake */ + const Vec2 &scale = trans.getScale(); + if (scale.x != 1 || scale.y != 1 || trans.getRotation() != 0) + { + isVisible = true; + return; + } + + IntRect self; + self.setPos(trans.getPositionI() - (trans.getOriginI() + sceneOrig)); + self.w = bitmap->width(); + self.h = bitmap->height(); + + isVisible = SDL_HasIntersection(&self, &sceneRect); + } + + void emitWaveChunk(SVertex *&vert, float phase, int width, + float zoomY, int chunkY, int chunkLength) + { + float wavePos = phase + (chunkY / (float) wave.length) * (float) (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, mirrored ? tex.hFlipped() : tex, pos); + vert += 4; + } + + void updateWave() + { + if (nullOrDisposed(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 * (float) M_PI) / 180.0f; + + 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(); + } + + void prepare() + { + if (wave.dirty) + { + updateWave(); + wave.dirty = false; + } + + updateVisibility(); + } }; Sprite::Sprite(Viewport *viewport) - : ViewportElement(viewport) +: ViewportElement(viewport) { - p = new SpritePrivate; - onGeometryChange(scene->getGeometry()); + p = new SpritePrivate; + onGeometryChange(scene->getGeometry()); } Sprite::~Sprite() { - dispose(); + dispose(); } DEF_ATTR_RD_SIMPLE(Sprite, Bitmap, Bitmap*, p->bitmap) @@ -321,6 +329,7 @@ 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, Pattern, Bitmap*, p->pattern) 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) @@ -333,158 +342,174 @@ DEF_ATTR_SIMPLE(Sprite, Opacity, int, p->opacity) DEF_ATTR_SIMPLE(Sprite, SrcRect, Rect&, *p->srcRect) DEF_ATTR_SIMPLE(Sprite, Color, Color&, *p->color) DEF_ATTR_SIMPLE(Sprite, Tone, Tone&, *p->tone) +DEF_ATTR_SIMPLE(Sprite, PatternOpacity, int, p->patternOpacity) +DEF_ATTR_SIMPLE(Sprite, PatternScrollX, int, p->patternScroll.x) +DEF_ATTR_SIMPLE(Sprite, PatternScrollY, int, p->patternScroll.y) void Sprite::setBitmap(Bitmap *bitmap) { - guardDisposed(); - - if (p->bitmap == bitmap) - return; - - p->bitmap = bitmap; - - if (nullOrDisposed(bitmap)) - return; - - bitmap->ensureNonMega(); - - *p->srcRect = bitmap->rect(); - p->onSrcRectChange(); - p->quad.setPosRect(p->srcRect->toFloatRect()); - - p->wave.dirty = true; + guardDisposed(); + + if (p->bitmap == bitmap) + return; + + p->bitmap = bitmap; + + if (nullOrDisposed(bitmap)) + return; + + bitmap->ensureNonMega(); + + *p->srcRect = bitmap->rect(); + p->onSrcRectChange(); + p->quad.setPosRect(p->srcRect->toFloatRect()); + + p->wave.dirty = true; } void Sprite::setX(int value) { - guardDisposed(); - - if (p->trans.getPosition().x == value) - return; - - p->trans.setPosition(Vec2(value, getY())); + guardDisposed(); + + if (p->trans.getPosition().x == value) + return; + + p->trans.setPosition(Vec2(value, getY())); } void Sprite::setY(int value) { - guardDisposed(); - - if (p->trans.getPosition().y == value) - return; - - p->trans.setPosition(Vec2(getX(), value)); - - if (rgssVer >= 2) - { - p->wave.dirty = true; - setSpriteY(value); - } + guardDisposed(); + + if (p->trans.getPosition().y == value) + return; + + p->trans.setPosition(Vec2(getX(), value)); + + if (rgssVer >= 2) + { + p->wave.dirty = true; + setSpriteY(value); + } } void Sprite::setOX(int value) { - guardDisposed(); - - if (p->trans.getOrigin().x == value) - return; - - p->trans.setOrigin(Vec2(value, getOY())); + guardDisposed(); + + if (p->trans.getOrigin().x == value) + return; + + p->trans.setOrigin(Vec2(value, getOY())); } void Sprite::setOY(int value) { - guardDisposed(); - - if (p->trans.getOrigin().y == value) - return; - - p->trans.setOrigin(Vec2(getOX(), value)); + guardDisposed(); + + if (p->trans.getOrigin().y == value) + return; + + p->trans.setOrigin(Vec2(getOX(), value)); } void Sprite::setZoomX(float value) { - guardDisposed(); - - if (p->trans.getScale().x == value) - return; - - p->trans.setScale(Vec2(value, getZoomY())); + guardDisposed(); + + if (p->trans.getScale().x == value) + return; + + p->trans.setScale(Vec2(value, getZoomY())); } void Sprite::setZoomY(float value) { - guardDisposed(); - - if (p->trans.getScale().y == value) - return; - - p->trans.setScale(Vec2(getZoomX(), value)); - p->recomputeBushDepth(); - - if (rgssVer >= 2) - p->wave.dirty = true; + guardDisposed(); + + if (p->trans.getScale().y == value) + return; + + p->trans.setScale(Vec2(getZoomX(), value)); + p->recomputeBushDepth(); + + if (rgssVer >= 2) + p->wave.dirty = true; } void Sprite::setAngle(float value) { - guardDisposed(); - - if (p->trans.getRotation() == value) - return; - - p->trans.setRotation(value); + guardDisposed(); + + if (p->trans.getRotation() == value) + return; + + p->trans.setRotation(value); } void Sprite::setMirror(bool mirrored) { - guardDisposed(); - - if (p->mirrored == mirrored) - return; - - p->mirrored = mirrored; - p->onSrcRectChange(); + guardDisposed(); + + if (p->mirrored == mirrored) + return; + + p->mirrored = mirrored; + p->onSrcRectChange(); } void Sprite::setBushDepth(int value) { - guardDisposed(); - - if (p->bushDepth == value) - return; - - p->bushDepth = value; - p->recomputeBushDepth(); + guardDisposed(); + + if (p->bushDepth == value) + return; + + p->bushDepth = value; + p->recomputeBushDepth(); } void Sprite::setBlendType(int type) { - guardDisposed(); + guardDisposed(); + + switch (type) + { + default : + case BlendNormal : + p->blendType = BlendNormal; + return; + case BlendAddition : + p->blendType = BlendAddition; + return; + case BlendSubstraction : + p->blendType = BlendSubstraction; + return; + } +} - switch (type) - { - default : - case BlendNormal : - p->blendType = BlendNormal; - return; - case BlendAddition : - p->blendType = BlendAddition; - return; - case BlendSubstraction : - p->blendType = BlendSubstraction; - return; - } +void Sprite::setPattern(Bitmap *value) +{ + guardDisposed(); + + if (p->pattern == value) + return; + + p->pattern = value; + + if (!nullOrDisposed(value)) + value->ensureNonMega(); } #define DEF_WAVE_SETTER(Name, name, type) \ - void Sprite::setWave##Name(type value) \ - { \ - guardDisposed(); \ - if (p->wave.name == value) \ - return; \ - p->wave.name = value; \ - p->wave.dirty = true; \ - } +void Sprite::setWave##Name(type value) \ +{ \ +guardDisposed(); \ +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) @@ -495,107 +520,118 @@ DEF_WAVE_SETTER(Phase, phase, float) void Sprite::initDynAttribs() { - p->srcRect = new Rect; - p->color = new Color; - p->tone = new Tone; - - p->updateSrcRectCon(); + p->srcRect = new Rect; + p->color = new Color; + p->tone = new Tone; + + p->updateSrcRectCon(); } /* Flashable */ void Sprite::update() { - guardDisposed(); - - Flashable::update(); - - p->wave.phase += p->wave.speed / 180; - p->wave.dirty = true; + guardDisposed(); + + Flashable::update(); + + p->wave.phase += p->wave.speed / 180; + p->wave.dirty = true; } /* SceneElement */ void Sprite::draw() { - if (!p->isVisible) - return; - - if (emptyFlashFlag) - return; - - ShaderBase *base; - - bool renderEffect = p->color->hasEffect() || - p->tone->hasEffect() || - flashing || - p->bushDepth != 0; - - if (renderEffect) - { - SpriteShader &shader = shState->shaders().sprite; - - shader.bind(); - shader.applyViewportProj(); - shader.setSpriteMat(p->trans.getMatrix()); - - shader.setTone(p->tone->norm); - shader.setOpacity(p->opacity.norm); - shader.setBushDepth(p->efBushDepth); - shader.setBushOpacity(p->bushOpacity.norm); - - /* When both flashing and effective color are set, - * the one with higher alpha will be blended */ - const Vec4 *blend = (flashing && flashColor.w > p->color->norm.w) ? - &flashColor : &p->color->norm; - - shader.setColor(*blend); - - base = &shader; - } - else if (p->opacity != 255) - { - AlphaSpriteShader &shader = shState->shaders().alphaSprite; - shader.bind(); - - shader.setSpriteMat(p->trans.getMatrix()); - shader.setAlpha(p->opacity.norm); - shader.applyViewportProj(); - base = &shader; - } - else - { - SimpleSpriteShader &shader = shState->shaders().simpleSprite; - shader.bind(); - - shader.setSpriteMat(p->trans.getMatrix()); - shader.applyViewportProj(); - base = &shader; - } - - glState.blendMode.pushSet(p->blendType); - - p->bitmap->bindTex(*base); - + if (!p->isVisible) + return; + + if (emptyFlashFlag) + return; + + ShaderBase *base; + + bool renderEffect = p->color->hasEffect() || + p->tone->hasEffect() || + flashing || + p->bushDepth != 0 || + (p->pattern && !p->pattern->isDisposed()); + + if (renderEffect) + { + SpriteShader &shader = shState->shaders().sprite; + + shader.bind(); + shader.applyViewportProj(); + shader.setSpriteMat(p->trans.getMatrix()); + + shader.setTone(p->tone->norm); + shader.setOpacity(p->opacity.norm); + shader.setBushDepth(p->efBushDepth); + shader.setBushOpacity(p->bushOpacity.norm); + + if (p->pattern) { + shader.setPattern(p->pattern->getGLTypes().tex, Vec2(p->pattern->width(), p->pattern->height())); + shader.setPatternOpacity(p->patternOpacity.norm); + shader.setPatternScroll(p->patternScroll); + shader.setShouldRenderPattern(true); + } + else { + shader.setShouldRenderPattern(false); + } + + /* When both flashing and effective color are set, + * the one with higher alpha will be blended */ + const Vec4 *blend = (flashing && flashColor.w > p->color->norm.w) ? + &flashColor : &p->color->norm; + + shader.setColor(*blend); + + base = &shader; + } + else if (p->opacity != 255) + { + AlphaSpriteShader &shader = shState->shaders().alphaSprite; + shader.bind(); + + shader.setSpriteMat(p->trans.getMatrix()); + shader.setAlpha(p->opacity.norm); + shader.applyViewportProj(); + base = &shader; + } + else + { + SimpleSpriteShader &shader = shState->shaders().simpleSprite; + shader.bind(); + + shader.setSpriteMat(p->trans.getMatrix()); + shader.applyViewportProj(); + base = &shader; + } + + glState.blendMode.pushSet(p->blendType); + + p->bitmap->bindTex(*base); + if (p->wave.active) - p->wave.qArray.draw(); - else - p->quad.draw(); - - glState.blendMode.pop(); + p->wave.qArray.draw(); + else + p->quad.draw(); + + glState.blendMode.pop(); } void Sprite::onGeometryChange(const Scene::Geometry &geo) { - /* Offset at which the sprite will be drawn - * relative to screen origin */ - p->trans.setGlobalOffset(geo.offset()); - - p->sceneRect.setSize(geo.rect.size()); - p->sceneOrig = geo.orig; + /* Offset at which the sprite will be drawn + * relative to screen origin */ + p->trans.setGlobalOffset(geo.offset()); + + p->sceneRect.setSize(geo.rect.size()); + p->sceneOrig = geo.orig; } void Sprite::releaseResources() { - unlink(); - - delete p; + unlink(); + + delete p; } diff --git a/src/display/sprite.h b/src/display/sprite.h index 3b82f04a..f055b458 100644 --- a/src/display/sprite.h +++ b/src/display/sprite.h @@ -62,6 +62,10 @@ public: DECL_ATTR( BlendType, int ) DECL_ATTR( Color, Color& ) DECL_ATTR( Tone, Tone& ) + DECL_ATTR( Pattern, Bitmap* ) + DECL_ATTR( PatternOpacity, int ) + DECL_ATTR( PatternScrollX, int ) + DECL_ATTR( PatternScrollY, int ) DECL_ATTR( WaveAmp, int ) DECL_ATTR( WaveLength, int ) DECL_ATTR( WaveSpeed, int )