This commit is contained in:
WaywardHeart 2025-03-26 03:45:46 -04:00 committed by GitHub
commit 4b1745de13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 180 additions and 18 deletions

View file

@ -6,7 +6,10 @@ uniform lowp vec4 tone;
uniform lowp float opacity;
uniform lowp vec4 color;
uniform float bushDepth;
uniform bool bushY;
uniform bool bushUnder;
uniform float bushSlope;
uniform float bushIntercept;
uniform lowp float bushOpacity;
uniform sampler2D pattern;
@ -102,8 +105,9 @@ void main()
}
/* Apply bush alpha by mathematical if */
lowp float underBush = float(v_texCoord.y < bushDepth);
frag.a *= clamp(bushOpacity + underBush, 0.0, 1.0);
bool underBush = (float(bushY) * v_texCoord.y + float(!bushY) * v_texCoord.x) <
(bushSlope * (float(bushY) * v_texCoord.x + float(!bushY) * v_texCoord.y) + bushIntercept);
frag.a *= clamp(bushOpacity + float(underBush == bushUnder), 0.0, 1.0);
gl_FragColor = frag;
}

View file

@ -532,7 +532,10 @@ SpriteShader::SpriteShader()
GET_U(tone);
GET_U(color);
GET_U(opacity);
GET_U(bushDepth);
GET_U(bushY);
GET_U(bushUnder);
GET_U(bushSlope);
GET_U(bushIntercept);
GET_U(bushOpacity);
GET_U(pattern);
GET_U(patternBlendType);
@ -565,9 +568,12 @@ void SpriteShader::setOpacity(float value)
gl.Uniform1f(u_opacity, value);
}
void SpriteShader::setBushDepth(float value)
void SpriteShader::setBushDepth(bool bushY, bool bushUnder, float bushSlope, float bushIntercept)
{
gl.Uniform1f(u_bushDepth, value);
gl.Uniform1f(u_bushY, bushY);
gl.Uniform1f(u_bushUnder, bushUnder);
gl.Uniform1f(u_bushSlope, bushSlope);
gl.Uniform1f(u_bushIntercept, bushIntercept);
}
void SpriteShader::setBushOpacity(float value)

View file

@ -190,7 +190,7 @@ public:
void setTone(const Vec4 &value);
void setColor(const Vec4 &value);
void setOpacity(float value);
void setBushDepth(float value);
void setBushDepth(bool bushY, bool bushUnder, float bushSlope, float bushIntercept);
void setBushOpacity(float value);
void setPattern(const TEX::ID pattern, const Vec2 &dimensions);
void setPatternBlendType(int blendType);
@ -202,7 +202,8 @@ public:
void setInvert(bool value);
private:
GLint u_spriteMat, u_tone, u_opacity, u_color, u_bushDepth, u_bushOpacity, u_pattern, u_renderPattern,
GLint u_spriteMat, u_tone, u_opacity, u_color,
u_bushY, u_bushUnder, u_bushSlope, u_bushIntercept, u_bushOpacity, u_pattern, u_renderPattern,
u_patternBlendType, u_patternSizeInv, u_patternTile, u_patternOpacity, u_patternScroll, u_patternZoom, u_invert;
};

View file

@ -163,4 +163,22 @@ private:
bool dirty;
};
// Rotates a point around an origin point, counter-clockwise
// https://stackoverflow.com/a/2259502
static inline Vec2 rotate_point(const Vec2 &origin, const float &angle, Vec2 point)
{
float s = sin(angle);
float c = cos(angle);
// translate point back to origin:
point.x -= origin.x;
point.y -= origin.y;
// rotate point
float xnew = point.x * c - point.y * s;
float ynew = point.x * s + point.y * c;
// translate point back:
point.x = xnew + origin.x;
point.y = ynew + origin.y;
return point;
}
#endif // TRANSFORM_H

View file

@ -45,6 +45,12 @@
#include "sigslot/signal.hpp"
static float fwrap(float value, float range)
{
float res = fmod(value, range);
return res < 0 ? res + range : res;
}
struct SpritePrivate
{
Bitmap *bitmap;
@ -60,6 +66,11 @@ struct SpritePrivate
bool mirrored;
int bushDepth;
float efBushDepth;
float bushSlope;
float bushIntercept;
bool bushY;
bool bushUnder;
bool bushDirty;
NormValue bushOpacity;
NormValue opacity;
BlendType blendType;
@ -106,7 +117,11 @@ struct SpritePrivate
srcRect(&tmp.rect),
mirrored(false),
bushDepth(0),
efBushDepth(0),
bushSlope(0),
bushIntercept(1.0f),
bushY(true),
bushUnder(true),
bushDirty(true),
bushOpacity(128),
opacity(255),
blendType(BlendNormal),
@ -155,12 +170,97 @@ struct SpritePrivate
if (nullOrDisposed(bitmap))
return;
/* Calculate effective (normalized) bush depth */
float texBushDepth = (bushDepth / trans.getScale().y) -
(srcRect->y + srcRect->height) +
bitmap->height();
bushDirty = false;
efBushDepth = 1.0f - texBushDepth / bitmap->height();
if (bushDepth <= 0)
{
bushSlope = 0;
bushIntercept = 1.0f;
bushY = true;
bushUnder = true;
return;
}
// Invert the angle if mirrored
int mirror = mirrored ? -1 : 1;
float angle = fwrap(mirror * trans.getRotation(), 360);
// If it's not rotated, then we can skip all of those other calculations.
if (angle == 0.0f)
{
bushSlope = 0;
bushIntercept = (srcRect->y + srcRect->height - (bushDepth / trans.getScale().y)) / bitmap->height();
bushY = true;
bushUnder = true;
return;
}
// Calculate the slope in segments of 45deg, so I don't have to deal with near-infinite slopes
bushSlope = tan(abs(fwrap(angle - 45, 90) - 45) * M_PI / 180.0f);
// Manually set negative slopes
bushSlope *= fwrap(angle, 180) > 90 ? -1 : 1;
// If the angle is within 45deg of 90deg or 270deg we use the x-axis instead
// Additionally, since the shader's coordinates are percentage based, we need to make
// the slope relative to the scaled bitmap's ratio
float scaledW = bitmap->width() * trans.getScale().x;
float scaledH = bitmap->height() * trans.getScale().y;
if (fwrap(angle + 45, 180) < 90)
{
bushY = true;
bushSlope = bushSlope * scaledW / scaledH;
}
else
{
bushY = false;
bushSlope = bushSlope * scaledH / scaledW;
}
// Invert the check when we switch from the y-axis to the x-axis
bushUnder = angle < 45 || angle >= 225;
// Zoom and rotate the srcRect
FloatRect src = srcRect->toFloatRect();
// Mirrored sprites whose src_rects extend beyond the bounds of the bitmap
// need to swap the overflows
if (mirrored)
{
float overflowX = std::max(src.x + src.w - bitmap->width(), 0.0f);
if (src.x < 0)
{
src.x = 0;
}
src.x -= overflowX;
}
src.x *= trans.getScale().x;
src.y *= trans.getScale().y;
src.w *= trans.getScale().x;
src.h *= trans.getScale().y;
// We use a "left-handed" coordinate system, with positive y values being below the x-axis,
// so we need to use the negative of the angle to get the proper y values.
float rotation = -angle * M_PI / 180.0f;
// p1 doesn't change, so we can skip rotating it
Vec2 p1 = src.topLeft();
Vec2 p2 = rotate_point(p1, rotation, src.topRight());
Vec2 p3 = rotate_point(p1, rotation, src.bottomLeft());
Vec2 p4 = rotate_point(p1, rotation, src.bottomRight());
// Find the upper boundary of the bush effect and rotate it back.
// The rotated slope is a horizontal line, so any x value will work.
Vec2 point(0, std::max(std::max(p1.y, p2.y), std::max(p3.y, p4.y)) - bushDepth);
point = rotate_point(p1, -rotation, point);
// Unzoom the point and convert it into a percentage
point.y = point.y / trans.getScale().y / bitmap->height();
point.x = point.x / trans.getScale().x / bitmap->width();
if (bushY)
bushIntercept = (point.y - (bushSlope * point.x));
else
bushIntercept = (point.x - (bushSlope * point.y));
}
void onSrcRectChange()
@ -197,7 +297,7 @@ struct SpritePrivate
}
quad.setPosRect(FloatRect(0, 0, rect.w, rect.h));
recomputeBushDepth();
bushDirty = true;
wave.dirty = true;
}
@ -341,6 +441,9 @@ struct SpritePrivate
}
updateVisibility();
if (isVisible && bushDirty)
recomputeBushDepth();
}
};
@ -481,7 +584,7 @@ void Sprite::setZoomY(float value)
return;
p->trans.setScale(Vec2(getZoomX(), value));
p->recomputeBushDepth();
p->bushDirty = true;
if (rgssVer >= 2)
p->wave.dirty = true;
@ -495,6 +598,8 @@ void Sprite::setAngle(float value)
return;
p->trans.setRotation(value);
p->bushDirty = true;
}
void Sprite::setMirror(bool mirrored)
@ -516,7 +621,7 @@ void Sprite::setBushDepth(int value)
return;
p->bushDepth = value;
p->recomputeBushDepth();
p->bushDirty = true;
}
void Sprite::setBlendType(int type)
@ -680,7 +785,7 @@ void Sprite::draw()
shader.setTone(p->tone->norm);
shader.setOpacity(p->opacity.norm);
shader.setBushDepth(p->efBushDepth);
shader.setBushDepth(p->bushY, p->bushUnder, p->bushSlope, p->bushIntercept);
shader.setBushOpacity(p->bushOpacity.norm);
if (p->pattern && p->patternOpacity > 0) {

View file

@ -0,0 +1,28 @@
b = Bitmap.new(400, 400)
s = Sprite.new
s.bitmap = b
s.src_rect = Rect.new(100, 100, 200, 200)
b.gradient_fill_rect(s.src_rect, Color.new(255,0,0), Color.new(0,0,255))
s.x = Graphics.width / 2
s.y = Graphics.height / 2
s.ox = s.src_rect.width / 2
s.oy = s.src_rect.height / 2
s.bush_depth = 100
s.bush_opacity = 128
s.zoom_x = 2
def rotate(s)
seconds_per_rotation = 12
s.angle += (360.0 / Graphics.frame_rate) / seconds_per_rotation
end
loop {
Graphics.update
Input.update
rotate(s)
}