This commit is contained in:
WaywardHeart 2025-03-16 10:28:38 -07:00 committed by GitHub
commit 2b8a1d3078
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 160 additions and 52 deletions

View file

@ -68,17 +68,19 @@ public:
Vec2 &getPosition() { return position; }
Vec2 &getOrigin() { return origin; }
Vec2 &getSrcRectOrigin() { return srcRectOrigin; }
Vec2 &getScale() { return scale; }
float getRotation() { return rotation; }
Vec2i &getGlobalOffset() { return offset; }
Vec2i getPositionI() const
{
return Vec2i(position.x, position.y);
}
Vec2i getOriginI() const
Vec2i getAdjustedOriginI() const
{
return Vec2i(origin.x, origin.y);
return Vec2i(adjustedOrigin.x, adjustedOrigin.y);
}
void setPosition(const Vec2 &value)
@ -90,6 +92,18 @@ public:
void setOrigin(const Vec2 &value)
{
origin = value;
adjustedOrigin.x = origin.x + srcRectOrigin.x;
adjustedOrigin.y = origin.y + srcRectOrigin.y;
dirty = true;
}
/* For Sprites. Set to the srcRect's position if it's negative,
* or to 0 if it's positive. */
void setSrcRectOrigin(const Vec2 &value)
{
srcRectOrigin = value;
adjustedOrigin.x = origin.x + srcRectOrigin.x;
adjustedOrigin.y = origin.y + srcRectOrigin.y;
dirty = true;
}
@ -139,8 +153,8 @@ private:
float syc = scale.y * cosine;
float sxs = scale.x * sine;
float sys = scale.y * sine;
float tx = -origin.x * sxc - origin.y * sys + position.x + offset.x;
float ty = origin.x * sxs - origin.y * syc + position.y + offset.y;
float tx = -adjustedOrigin.x * sxc - adjustedOrigin.y * sys + position.x + offset.x;
float ty = adjustedOrigin.x * sxs - adjustedOrigin.y * syc + position.y + offset.y;
matrix[0] = sxc;
matrix[1] = -sxs;
@ -152,6 +166,8 @@ private:
Vec2 position;
Vec2 origin;
Vec2 srcRectOrigin;
Vec2 adjustedOrigin;
Vec2 scale;
float rotation;

View file

@ -55,6 +55,7 @@ struct SpritePrivate
Transform trans;
Rect *srcRect;
FloatRect adjustedSrcRect;
sigslot::connection srcRectCon;
bool mirrored;
@ -73,8 +74,7 @@ struct SpritePrivate
bool invert;
IntRect sceneRect;
Vec2i sceneOrig;
Scene::Geometry sceneGeo;
/* Would this sprite be visible on
* the screen if drawn? */
@ -119,8 +119,6 @@ struct SpritePrivate
tone(&tmp.tone)
{
sceneRect.x = sceneRect.y = 0;
updateSrcRectCon();
prepareCon = shState->prepareDraw.connect
@ -165,7 +163,8 @@ struct SpritePrivate
void onSrcRectChange()
{
FloatRect rect = srcRect->toFloatRect();
adjustedSrcRect = srcRect->toFloatRect();
FloatRect &rect = adjustedSrcRect;
Vec2i bmSize;
Vec2i bmSizeHires;
@ -180,6 +179,22 @@ struct SpritePrivate
/* Clamp the rectangle so it doesn't reach outside
* the bitmap bounds */
if (rect.x < 0)
{
rect.w += rect.x;
trans.setSrcRectOrigin(Vec2(rect.x, trans.getSrcRectOrigin().y));
}
else if(trans.getSrcRectOrigin().x != 0)
trans.setSrcRectOrigin(Vec2(0, trans.getSrcRectOrigin().y));
if (rect.y < 0)
{
rect.h += rect.y;
trans.setSrcRectOrigin(Vec2(trans.getSrcRectOrigin().x, rect.y));
}
else if(trans.getSrcRectOrigin().y != 0)
trans.setSrcRectOrigin(Vec2(trans.getSrcRectOrigin().x, 0));
rect.x = clamp<int>(rect.x, 0, bmSize.x);
rect.y = clamp<int>(rect.y, 0, bmSize.y);
rect.w = clamp<int>(rect.w, 0, bmSize.x-rect.x);
rect.h = clamp<int>(rect.h, 0, bmSize.y-rect.y);
@ -199,7 +214,8 @@ struct SpritePrivate
quad.setPosRect(FloatRect(0, 0, rect.w, rect.h));
recomputeBushDepth();
wave.dirty = true;
if (wave.active)
wave.dirty = true;
}
void updateSrcRectCon()
@ -225,7 +241,7 @@ struct SpritePrivate
{
/* Don't do expensive wave bounding box
* calculations */
isVisible = true;
isVisible = wave.qArray.quadCount != 0;
return;
}
@ -240,30 +256,53 @@ struct SpritePrivate
return;
}
IntRect self;
self.setPos(trans.getPositionI() - (trans.getOriginI() + sceneOrig));
self.w = bitmap->width();
self.h = bitmap->height();
IntRect self = adjustedSrcRect;
self.setPos(trans.getPositionI() + trans.getGlobalOffset() - trans.getAdjustedOriginI());
isVisible = SDL_HasIntersection(&self, &sceneRect);
isVisible = SDL_HasIntersection(&self, &sceneGeo.rect);
}
void emitWaveChunk(SVertex *&vert, float phase, int width,
float zoomY, int chunkY, int chunkLength)
const Vec2 &zoom, int chunkY, int chunkLength, int offsetLength)
{
float wavePos = phase + (chunkY / (float) wave.length) * (float) (M_PI * 2);
float chunkX = sin(wavePos) * wave.amp;
float wavePos = phase + ((offsetLength + chunkY) / (float) wave.length) * (float) (M_PI * 2);
float chunkX = sin(wavePos) * wave.amp / zoom.x;
FloatRect tex(0, chunkY / zoomY, width, chunkLength / zoomY);
FloatRect pos = tex;
pos.x = chunkX;
FloatRect pos(chunkX, chunkY / zoom.y, adjustedSrcRect.w, chunkLength / zoom.y);
Quad::setTexPosRect(vert, mirrored ? tex.hFlipped() : tex, pos);
/* For some bizarre reason, combining a positive wave.amp with
* a non-zero angle (including multiples of 360) reduces the width.
* That being said, RGSS applies the wave effect after rotation
* and we're applying it before, so since we're deviating anyway
* and this behavior is weird I'm choosing to not do it. */
//if (p->trans.getRotation())
// pos.w *= pos.w / (pos.w + (2.0f * wave.amp));
FloatRect tex = mirrored ? adjustedSrcRect.hFlipped() : adjustedSrcRect;
tex.y += pos.y;
tex.h = pos.h;
if (bitmap->hasHires())
{
Vec2 bmSize = Vec2(bitmap->width(), bitmap->height());
Vec2 bmSizeHires = Vec2(bitmap->getHires()->width(), bitmap->getHires()->height());
if (bmSizeHires.x && bmSizeHires.y && bmSize.x && bmSize.y)
{
tex.x *= bmSizeHires.x / bmSize.x;
tex.y *= bmSizeHires.y / bmSize.y;
tex.w *= bmSizeHires.x / bmSize.x;
tex.h *= bmSizeHires.y / bmSize.y;
}
}
Quad::setTexPosRect(vert, tex, pos);
vert += 4;
}
void updateWave()
{
wave.dirty = false;
if (nullOrDisposed(bitmap))
return;
@ -275,11 +314,14 @@ struct SpritePrivate
wave.active = true;
int width = srcRect->width;
int height = srcRect->height;
float zoomY = trans.getScale().y;
int width = adjustedSrcRect.w;
int height = adjustedSrcRect.h;
const Vec2 &zoom = trans.getScale();
if (wave.amp < -(width / 2))
/* The length of the sprite as it appears on screen */
int visibleLength = height * zoom.y;
if (!visibleLength || !width)
{
wave.qArray.resize(0);
wave.qArray.commit();
@ -290,24 +332,60 @@ struct SpritePrivate
/* RMVX does this, and I have no fucking clue why */
if (wave.amp < 0)
{
float scaledAmp = wave.amp / zoom.x;
FloatRect tex = mirrored ? adjustedSrcRect.hFlipped() : adjustedSrcRect;
FloatRect pos(0, 0, 0, adjustedSrcRect.h);
float mult = (scaledAmp * 2) / (float)srcRect->width;
pos.x = -scaledAmp - (trans.getSrcRectOrigin().x * mult);
pos.w = tex.w * (1 + mult);
if ((pos.w * zoom.x) < 0.5f)
{
wave.qArray.resize(0);
wave.qArray.commit();
return;
}
if (bitmap->hasHires())
{
Vec2 bmSize = Vec2(bitmap->width(), bitmap->height());
Vec2 bmSizeHires = Vec2(bitmap->getHires()->width(), bitmap->getHires()->height());
if (bmSizeHires.x && bmSizeHires.y && bmSize.x && bmSize.y)
{
tex.x *= bmSizeHires.x / bmSize.x;
tex.y *= bmSizeHires.y / bmSize.y;
tex.w *= bmSizeHires.x / bmSize.x;
tex.h *= bmSizeHires.y / bmSize.y;
}
}
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);
Quad::setTexPosRect(&wave.qArray.vertices[0], tex, pos);
wave.qArray.commit();
return;
}
/* The length of the sprite as it appears on screen */
int visibleLength = height * zoomY;
/* A negative position in the srcRect affects the wave position. */
int offsetLength = abs(trans.getSrcRectOrigin().y * zoom.y);
/* First chunk length (aligned to 8 pixel boundary */
int firstLength = ((int) trans.getPosition().y) % 8;
/* First chunk length (aligned to 8 pixel boundary) */
int posY = (int) trans.getPosition().y - (int) (trans.getOrigin().y * zoom.y) + trans.getGlobalOffset().y;
int firstLength = 8 - ((posY + offsetLength) % 8);
firstLength = std::min(firstLength % 8, visibleLength);
/* If the position is negative, then the first chunk's alignment
* needs a little more fiddling */
int firstOffset;
if (offsetLength && firstLength)
{
firstOffset = 8 - (posY % 8);
firstOffset = std::min(firstOffset % 8, offsetLength);
firstOffset += (offsetLength - firstOffset) & (~7);
} else {
firstOffset = 0;
}
/* Amount of full 8 pixel chunks in the middle */
int chunks = (visibleLength - firstLength) / 8;
@ -321,13 +399,13 @@ struct SpritePrivate
float phase = (wave.phase * (float) M_PI) / 180.0f;
if (firstLength > 0)
emitWaveChunk(vert, phase, width, zoomY, 0, firstLength);
emitWaveChunk(vert, phase, width, zoom, 0, firstLength, firstOffset);
for (int i = 0; i < chunks; ++i)
emitWaveChunk(vert, phase, width, zoomY, firstLength + i * 8, 8);
emitWaveChunk(vert, phase, width, zoom, firstLength + i * 8, 8, offsetLength);
if (lastLength > 0)
emitWaveChunk(vert, phase, width, zoomY, firstLength + chunks * 8, lastLength);
emitWaveChunk(vert, phase, width, zoom, firstLength + chunks * 8, lastLength, offsetLength);
wave.qArray.commit();
}
@ -337,7 +415,6 @@ struct SpritePrivate
if (wave.dirty)
{
updateWave();
wave.dirty = false;
}
updateVisibility();
@ -414,7 +491,8 @@ void Sprite::setBitmap(Bitmap *bitmap)
p->onSrcRectChange();
p->quad.setPosRect(p->srcRect->toFloatRect());
p->wave.dirty = true;
if (p->wave.active)
p->wave.dirty = true;
}
void Sprite::setX(int value)
@ -436,11 +514,11 @@ void Sprite::setY(int value)
p->trans.setPosition(Vec2(getX(), value));
if (rgssVer >= 2)
{
if (p->wave.active)
p->wave.dirty = true;
if (rgssVer >= 2)
setSpriteY(value);
}
}
void Sprite::setOX(int value)
@ -461,6 +539,9 @@ void Sprite::setOY(int value)
return;
p->trans.setOrigin(Vec2(getOX(), value));
if (p->wave.active)
p->wave.dirty = true;
}
void Sprite::setZoomX(float value)
@ -471,6 +552,9 @@ void Sprite::setZoomX(float value)
return;
p->trans.setScale(Vec2(value, getZoomY()));
if (p->wave.active)
p->wave.dirty = true;
}
void Sprite::setZoomY(float value)
@ -483,7 +567,7 @@ void Sprite::setZoomY(float value)
p->trans.setScale(Vec2(getZoomX(), value));
p->recomputeBushDepth();
if (rgssVer >= 2)
if (p->wave.active)
p->wave.dirty = true;
}
@ -506,6 +590,9 @@ void Sprite::setMirror(bool mirrored)
p->mirrored = mirrored;
p->onSrcRectChange();
if (p->wave.active)
p->wave.dirty = true;
}
void Sprite::setBushDepth(int value)
@ -603,8 +690,11 @@ void Sprite::update()
Flashable::update();
p->wave.phase += p->wave.speed / 180;
p->wave.dirty = true;
if (p->wave.speed != 0)
{
p->wave.phase += p->wave.speed / 180;
p->wave.dirty = true;
}
}
/* SceneElement */
@ -808,10 +898,12 @@ void Sprite::onGeometryChange(const Scene::Geometry &geo)
{
/* Offset at which the sprite will be drawn
* relative to screen origin */
p->trans.setGlobalOffset(geo.offset());
const Vec2i &offset = geo.offset();
if (p->wave.active && p->trans.getGlobalOffset().y != offset.y)
p->wave.dirty = true;
p->trans.setGlobalOffset(offset);
p->sceneRect.setSize(geo.rect.size());
p->sceneOrig = geo.orig;
p->sceneGeo = geo;
}
void Sprite::releaseResources()