1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-02-27 08:34:02 +00:00

Merge branch 'more-bspline' into tile-optimization

This commit is contained in:
fgenesis 2024-07-07 05:36:51 +02:00
commit d4b80525a9
8 changed files with 1187 additions and 128 deletions

View file

@ -240,6 +240,7 @@ void AnimationEditor::applyState()
editingBone = 0;
currentKey = 0;
splinegrid = 0;
assistedSplineEdit = true;
editSprite = new SkeletalSprite();
editSprite->cull = false;
@ -302,6 +303,8 @@ void AnimationEditor::applyState()
addAction(MakeFunctionEvent(AnimationEditor, decrTimelineGrid), KEY_O, 0);
addAction(MakeFunctionEvent(AnimationEditor, incrTimelineGrid), KEY_P, 0);
addAction(MakeFunctionEvent(AnimationEditor, toggleSplineMode), KEY_W, 0);
addAction(ACTION_SWIMLEFT, KEY_J, -1);
@ -462,6 +465,12 @@ void AnimationEditor::applyState()
reverseAnim->event.set(MakeFunctionEvent(AnimationEditor, reverseAnim));
addRenderObject(reverseAnim, LR_MENU);
DebugButton *bAssist = new DebugButton(0, 0, 150);
bAssist->position = Vector(10, 510);
bAssist->event.set(MakeFunctionEvent(AnimationEditor, toggleSplineMode));
addRenderObject(bAssist, LR_MENU);
bSplineAssist = bAssist;
OutlineRect *rect = new OutlineRect;
rect->setWidthHeight(400,400);
@ -494,6 +503,7 @@ void AnimationEditor::applyState()
updateTimelineGrid();
updateTimelineUnit();
updateButtonLabels();
}
void AnimationEditor::clearUndoHistory()
@ -1005,18 +1015,24 @@ void AnimationEditor::editStripKey()
bgGrad->makeVertical(Vector(0.4f, 0.6f, 0.4f), Vector(0.8f, 1, 0.8f));
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == interp->bsp.ctrlX() * interp->bsp.ctrlY());
const size_t totalcp = interp->bsp.ctrlX() * interp->bsp.ctrlY();
const bool reset = bk->controlpoints.empty();
bk->controlpoints.resize(totalcp);
assert(!splinegrid);
splinegrid = new SplineGrid;
splinegrid = new SplineGrid();
DynamicRenderGrid *rgrid = splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), grid->width(), grid->height(), interp->bsp.degX(), interp->bsp.degY());
rgrid->setDrawOrder(grid->getDrawOrder());
splinegrid->setTexture(editingBone->texture->name);
splinegrid->setWidthHeight(editingBone->width, editingBone->height);
splinegrid->position = Vector(400, 300);
//splinegrid->followCamera = 1;
splinegrid->importControlPoints(&bk->controlpoints[0]);
//editSprite->addChild(splinegrid, PM_STATIC, RBP_OFF, CHILD_FRONT);
//editSprite->alphaMod = 0.5f;
splinegrid->setAssist(assistedSplineEdit);
if(reset)
splinegrid->resetControlPoints();
else
splinegrid->importKeyframe(bk);
addRenderObject(splinegrid, LR_PARTICLES_TOP);
}
else
@ -1616,6 +1632,23 @@ void AnimationEditor::decrTimelineGrid()
updateTimelineGrid();
}
void AnimationEditor::toggleSplineMode()
{
assistedSplineEdit = !assistedSplineEdit;
updateButtonLabels();
if(splinegrid)
splinegrid->setAssist(assistedSplineEdit);
}
void AnimationEditor::updateButtonLabels()
{
{
std::ostringstream os;
os << "S.Assist (W)(" << (assistedSplineEdit ? "on" : "off") << ")";
bSplineAssist->label->setText(os.str());
}
}
void AnimationEditor::updateTimelineGrid()
{
std::ostringstream os;
@ -1636,9 +1669,9 @@ void AnimationEditor::applyBoneToSplineGrid()
{
Animation *a = editSprite->getCurrentAnimation();
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == splinegrid->getSpline().ctrlX() * splinegrid->getSpline().ctrlY());
assert(bk->grid.size() == editingBone->getGrid()->linearsize());
splinegrid->importControlPoints(&bk->controlpoints[0]);
splinegrid->importKeyframe(bk);
}
}
@ -1648,10 +1681,10 @@ void AnimationEditor::applySplineGridToBone()
{
Animation *a = editSprite->getCurrentAnimation();
BoneKeyframe *bk = a->getKeyframe(currentKey)->getBoneKeyframe(editingBone->boneIdx);
assert(bk->controlpoints.size() == splinegrid->getSpline().ctrlX() * splinegrid->getSpline().ctrlY());
assert(bk->grid.size() == editingBone->getGrid()->linearsize());
splinegrid->exportControlPoints(&bk->controlpoints[0]);
splinegrid->exportKeyframe(bk);
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
interp->updateGridAndBone(*bk, editingBone);
}
}

View file

@ -8,6 +8,7 @@
class DebugFont;
class BitmapText;
class SplineGrid;
class DebugButton;
class KeyframeWidget : public Quad
{
@ -133,6 +134,7 @@ public:
SkeletalKeyframe buffer;
bool editingStrip;
bool assistedSplineEdit;
size_t selectedStripPoint;
void reverseAnim();
@ -162,6 +164,10 @@ public:
SplineGrid *splinegrid;
void applySplineGridToBone();
void applyBoneToSplineGrid();
void toggleSplineMode();
DebugButton *bSplineAssist;
void updateButtonLabels();
};

View file

@ -1,6 +1,5 @@
#include "Interpolators.h"
#include <math.h>
#include "tbsp.hh"
// usually one would expect that a bspline goes from t=0 to t=1.
// here, splines eval between these -0.5 .. +0.5.
@ -101,9 +100,10 @@ void BSpline2D::resize(size_t cx, size_t cy, unsigned degx, unsigned degy)
void BSpline2D::recalc(Vector* dst, size_t xres, size_t yres, const Vector *controlpoints)
{
const unsigned maxDeg = std::max(_degx, _degy);
std::vector<Vector> tmpv;
size_t degn = std::max(_degx, _degy);
size_t tmpn = (yres * _cpx) + degn;
size_t tmpn = (yres * _cpx) + maxDeg;
size_t tmpsz = tmpn * sizeof(Vector);
Vector *tmp;
if(tmpsz < 17*1024)
@ -113,7 +113,9 @@ void BSpline2D::recalc(Vector* dst, size_t xres, size_t yres, const Vector *cont
tmpv.resize(tmpn);
tmp = &tmpv[0];
}
Vector *work = tmp + (tmpn - degn);
// tmp[] layout: leftmost part: entries to hold the matrix as it's being built;
// rightmost part: maxDeg entries as workmem for the deBoor eval
Vector *work = tmp + (tmpn - maxDeg);
// Each column -> Y-axis interpolation
for(size_t x = 0; x < _cpx; ++x)
@ -162,3 +164,80 @@ void BSpline2DWithPoints::reset()
{
BSpline2D::reset(&controlpoints[0]);
}
bool BSpline2DControlPointGenerator::resize(size_t cx, size_t cy)
{
const size_t interpStorageSizeX = tbsp__getInterpolatorStorageSize(cx, cx);
const size_t interpStorageSizeY = tbsp__getInterpolatorStorageSize(cy, cy);
const size_t interpStorageNeeded = interpStorageSizeX + interpStorageSizeY;
floats.resize(interpStorageNeeded);
cp2d.init(cx, cy);
const size_t maxcp = std::max(cx, cy);
vectmp.resize(maxcp);
return interp.x.init(&floats[0], cx, cx)
&& interp.y.init(&floats[interpStorageSizeX], cy, cy);
}
bool BSpline2DControlPointGenerator::refresh(const float* knotsx, const float* knotsy, unsigned degx, unsigned degy)
{
const size_t maxcp = vectmp.size();
const size_t tmpn = tbsp__getInterpolatorRefreshTempSize(maxcp, maxcp);
const size_t tmpsz = tmpn * sizeof(float);
float *tmp;
std::vector<float> tmpv;
if(tmpsz < 17*1024)
tmp = (float*)alloca(tmpsz);
else
{
tmpv.resize(tmpn);
tmp = &tmpv[0];
}
return interp.x.refresh(tmp, knotsx, degx)
&& interp.y.refresh(tmp, knotsy, degy);
}
Vector* BSpline2DControlPointGenerator::generateControlPoints(const Vector *points2d)
{
const size_t cpx = interp.x.getNumInputPoints();
const size_t cpy = interp.y.getNumInputPoints();
// y direction first
for(size_t x = 0; x < cpx; ++x)
{
const Vector *src = &points2d[x];
for(size_t y = 0; y < cpy; ++y, src += cpx)
vectmp[y] = *src;
// solve in-place
interp.y.generateControlPoints<Vector>(&vectmp[0], NULL, &vectmp[0]);
for(size_t y = 0; y < cpy; ++y)
cp2d(x, y) = vectmp[y];
}
// x direction
for(size_t y = 0; y < cpy; ++y)
{
Vector *row = cp2d.row(y);
// solve in-place
interp.x.generateControlPoints<Vector>(row, NULL, row);
}
return cp2d.data();
}
bool BSpline2DControlPointGeneratorWithPoints::resize(size_t cx, size_t cy)
{
designpoints.resize(cx * cy);
return BSpline2DControlPointGenerator::resize(cx, cy);
}
Vector* BSpline2DControlPointGeneratorWithPoints::generateControlPoints()
{
return BSpline2DControlPointGenerator::generateControlPoints(&designpoints[0]);
}

View file

@ -4,6 +4,15 @@
#include <algorithm> // std::pair
#include <vector>
#include "Vector.h"
#include "tbsp.hh"
#include "DataStructures.h"
enum SplineType
{
INTERPOLATOR_BSPLINE,
INTERPOLATOR_COSINE,
INTERPOLATOR_BSPLINE_EXT
};
class CosineInterpolator
{
@ -36,18 +45,20 @@ public:
inline unsigned degX() const { return _degx; }
inline unsigned degY() const { return _degy; }
inline const float *getKnotsX() const { return &knotsX[0]; }
inline const float *getKnotsY() const { return &knotsY[0]; }
private:
size_t _cpx, _cpy; // # of control points
unsigned _degx, _degy;
float _tmin, _tmax;
std::vector<float> knotsX, knotsY;
};
class BSpline2DWithPoints : public BSpline2D
{
public:
void resize(size_t cx, size_t cy, unsigned degx, unsigned degy);
void recalc(Vector *dst, size_t xres, size_t yres);
@ -61,4 +72,31 @@ public:
}
};
class BSpline2DControlPointGenerator
{
public:
bool resize(size_t cx, size_t cy);
bool refresh(const float *knotsx, const float *knotsy, unsigned degx, unsigned degy);
Vector *generateControlPoints(const Vector *points2d);
private:
Array2d<Vector> cp2d;
struct
{
tbsp::Interpolator<float> x, y;
} interp;
std::vector<float> floats;
std::vector<Vector> vectmp;
};
class BSpline2DControlPointGeneratorWithPoints : public BSpline2DControlPointGenerator
{
public:
bool resize(size_t cx, size_t cy);
Vector *generateControlPoints();
std::vector<Vector> designpoints;
};
#endif

View file

@ -1660,7 +1660,9 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
XMLElement *animation = animations->FirstChildElement("Animation");
while(animation)
{
Animation newAnimation;
this->animations.push_back(Animation());
Animation& newAnimation = this->animations.back();
newAnimation.name = animation->Attribute("name");
if(animation->Attribute("resetOnEnd"))
newAnimation.resetOnEnd = animation->BoolAttribute("resetOnEnd");
@ -1786,8 +1788,15 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
}
// <Interpolator bone="name or idx" type="TYPE config and params" data="controlpoints; aded by editor" />
size_t numInterp = 0;
XMLElement *interp = animation->FirstChildElement("Interpolator");
for( ; interp; interp = interp->NextSiblingElement("Interpolator"))
++numInterp;
newAnimation.interpolators.resize(numInterp);
interp = animation->FirstChildElement("Interpolator");
for(numInterp = 0 ; interp; interp = interp->NextSiblingElement("Interpolator"), ++numInterp)
{
Bone *bi = NULL;
const char *sbone = interp->Attribute("bone");
@ -1817,17 +1826,16 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
continue;
}
SplineType spline = SPLINE_BSPLINE;
SplineType spline = INTERPOLATOR_BSPLINE;
unsigned cx = 3, cy = 3, degx = 3, degy = 3;
if(const char *stype = interp->Attribute("type"))
{
SimpleIStringStream is(stype, SimpleIStringStream::REUSE);
std::string ty;
is >> ty;
BoneGridInterpolator bgip;
if(ty == "bspline")
{
spline = SPLINE_BSPLINE;
spline = INTERPOLATOR_BSPLINE;
if(!(is >> cx >> cy >> degx >> degy))
{
if(!degx)
@ -1850,10 +1858,8 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
grid->gridType = GRID_INTERP;
// bone grid should have been created via <Bone grid=... /> earlier
const char *idata = interp->Attribute("data");
newAnimation.interpolators.push_back(BoneGridInterpolator());
BoneGridInterpolator& bgip = newAnimation.interpolators.back();
//bgip.type = spline;
BoneGridInterpolator& bgip = newAnimation.interpolators[numInterp];
bgip.idx = bi->boneIdx;
bgip.storeBoneByIdx = boneByIdx;
@ -1864,38 +1870,40 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
const size_t numcp = size_t(cx) * size_t(cy);
const size_t numgridp = grid->linearsize();
// data format: "W H [x y x y ... (W*H times)] W H x y x y ..."
// ^- start of 1st keyframe ^- 2nd keyframe
SimpleIStringStream is(idata ? idata : "", SimpleIStringStream::REUSE);
// fixup keyframes and recalc spline points
for(size_t k = 0; k < newAnimation.keyframes.size(); ++k)
if(const char *idata = interp->Attribute("data"))
{
SkeletalKeyframe& kf = newAnimation.keyframes[k];
BoneKeyframe *bk = kf.getBoneKeyframe(bgip.idx);
// data format: "W H [x y x y ... (W*H times)] W H x y x y ..."
// ^- start of 1st keyframe ^- 2nd keyframe
SimpleIStringStream is(idata, SimpleIStringStream::REUSE);
bk->controlpoints.resize(numcp);
bgip.bsp.reset(&bk->controlpoints[0]);
// fixup keyframes and recalc spline points
for(size_t k = 0; k < newAnimation.keyframes.size(); ++k)
{
SkeletalKeyframe& kf = newAnimation.keyframes[k];
BoneKeyframe *bk = kf.getBoneKeyframe(bgip.idx);
unsigned w = 0, h = 0;
Vector cp;
cp.z = 1; // we want all grid points at full alpha
bk->controlpoints.resize(numcp);
bgip.bsp.reset(&bk->controlpoints[0]);
if((is >> w >> h))
for(unsigned y = 0; y < h; ++y)
for(unsigned x = 0; x < w; ++x)
if((is >> cp.x >> cp.y))
if(x < cx && y < cy)
bk->controlpoints[y*size_t(cx) + x] = cp;
unsigned w = 0, h = 0;
Vector cp;
cp.z = 1; // we want all grid points at full alpha
bk->grid.resize(numgridp);
bgip.updateGridOnly(*bk, bi);
if((is >> w >> h))
for(unsigned y = 0; y < h; ++y)
for(unsigned x = 0; x < w; ++x)
if((is >> cp.x >> cp.y))
if(x < cx && y < cy)
bk->controlpoints[y*size_t(cx) + x] = cp;
bk->grid.resize(numgridp);
bgip.updateGridOnly(*bk, bi);
}
}
// ---- end bspline -----
}
animation = animation->NextSiblingElement("Animation");
this->animations.push_back(newAnimation);
}
}
}

View file

@ -1,7 +1,12 @@
#include "SplineGrid.h"
#include <assert.h>
#include "RenderBase.h"
#include "Core.h"
#include "RenderGrid.h"
#include "SkeletalSprite.h"
SplineGridCtrlPoint *SplineGridCtrlPoint::movingPoint;
@ -74,11 +79,12 @@ void SplineGridCtrlPoint::onUpdate(float dt)
}
SplineGrid::SplineGrid()
: wasModified(false), deg(0), pointscale(1)
: wasModified(false), deg(0), pointscale(1), _assistMode(true)
{
setWidthHeight(128, 128);
renderQuad = true;
renderBorder = true;
renderBorderColor = Vector(0.5f, 0.5f, 0.5f);
}
SplineGrid::~SplineGrid()
@ -87,6 +93,9 @@ SplineGrid::~SplineGrid()
DynamicRenderGrid *SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy)
{
if(!cpgen.resize(w, h))
return NULL;
size_t oldcpx = bsp.ctrlX();
size_t oldcpy = bsp.ctrlY();
@ -126,6 +135,9 @@ DynamicRenderGrid *SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yr
ref = createControlPoint(x, y);
}
if(!cpgen.refresh(bsp.getKnotsX(), bsp.getKnotsY(), bsp.degX(), bsp.degY()))
return NULL;
recalc();
return ret;
@ -133,7 +145,17 @@ DynamicRenderGrid *SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yr
void SplineGrid::recalc()
{
exportControlPoints(&bsp.controlpoints[0]);
if(_assistMode)
{
exportGridPoints(&cpgen.designpoints[0]);
_generateControlPointsFromDesignPoints();
}
else
{
exportGridPoints(&bsp.controlpoints[0]);
bsp.recalc(&cpgen.designpoints[0], bsp.ctrlX(), bsp.ctrlY());
}
if(grid)
{
bsp.recalc(grid->dataRW(), grid->width(), grid->height());
@ -141,24 +163,67 @@ void SplineGrid::recalc()
}
}
void SplineGrid::exportControlPoints(Vector* controlpoints)
void SplineGrid::exportGridPoints(Vector* pdst) const
{
for(size_t i = 0; i < ctrlp.size(); ++i)
controlpoints[i] = ctrlp[i]->getSplinePosition();
pdst[i] = ctrlp[i]->getSplinePosition();
}
void SplineGrid::importControlPoints(const Vector* controlpoints)
void SplineGrid::importGridPoints(const Vector* psrc)
{
for(size_t i = 0; i < ctrlp.size(); ++i)
ctrlp[i]->setSplinePosition(controlpoints[i]);
ctrlp[i]->setSplinePosition(psrc[i]);
}
void SplineGrid::importKeyframe(const BoneKeyframe* bk)
{
const size_t numcp = bsp.ctrlX() * bsp.ctrlY();
assert(bk->controlpoints.size() == numcp);
bsp.controlpoints = bk->controlpoints;
if(_assistMode)
{
// given control points, generate spline points (which are later caculated back into control points)
bsp.recalc(&cpgen.designpoints[0], bsp.ctrlX(), bsp.ctrlY());
importGridPoints(&cpgen.designpoints[0]);
}
else
importGridPoints(&bk->controlpoints[0]);
recalc();
}
void SplineGrid::exportKeyframe(BoneKeyframe* bk) const
{
const size_t numcp = bsp.ctrlX() * bsp.ctrlY();
assert(bk->controlpoints.size() == numcp);
bk->controlpoints = bsp.controlpoints;
}
void SplineGrid::resetControlPoints()
{
bsp.reset();
importControlPoints(&bsp.controlpoints[0]);
importGridPoints(&bsp.controlpoints[0]);
// This pushes the bspline controlpoints outwards so that all spline points line up as one would expect.
// If this weren't done, the tile's texture would be pulled inwards (more with increasing dimension);
// as if the tile was a piece of plastic foil that's seen too much heat.
//if(_assistMode) // ALWAYS DO THIS!!
{
cpgen.designpoints = bsp.controlpoints;
_generateControlPointsFromDesignPoints();
}
recalc();
}
void SplineGrid::_generateControlPointsFromDesignPoints()
{
const Vector *cp = cpgen.generateControlPoints();
memcpy(&bsp.controlpoints[0], cp, bsp.controlpoints.size() * sizeof(*cp));
}
SplineGridCtrlPoint* SplineGrid::createControlPoint(size_t x, size_t y)
@ -190,7 +255,10 @@ void SplineGrid::onRender(const RenderState& rs) const
const Vector wh2(width * 0.5f, height * 0.5f);
glLineWidth(2);
glColor4f(0.0f, 0.3f, 1.0f, 0.3f);
if(_assistMode)
glColor4f(0.0f, 0.3f, 1.0f, 0.4f);
else
glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
const size_t cpx = bsp.ctrlX();
const size_t cpy = bsp.ctrlY();
@ -219,6 +287,45 @@ void SplineGrid::onRender(const RenderState& rs) const
}
glEnd();
}
const Vector *psrc = _assistMode
? &bsp.controlpoints[0]
: &cpgen.designpoints[0];
if(RenderObject::renderCollisionShape)
{
glLineWidth(1);
glColor4f(1.0f, 0.3f, 0.3f, 0.7f);
glPushMatrix();
glScalef(width, height, 1);
// X axis
for(size_t y = 0; y < cpy; ++y)
{
glBegin(GL_LINE_STRIP);
const Vector *row = &psrc[y * cpx];
for(size_t x = 0; x < cpx; ++x)
{
const Vector p = row[x];
glVertex2f(p.x, p.y);
}
glEnd();
}
// Y axis
for(size_t x = 0; x < cpx; ++x)
{
glBegin(GL_LINE_STRIP);
for(size_t y = 0; y < cpy; ++y)
{
const Vector p = psrc[y * cpx + x];
glVertex2f(p.x, p.y);
}
glEnd();
}
glPopMatrix();
}
}
void SplineGrid::setPointScale(const float scale)
@ -230,3 +337,17 @@ void SplineGrid::setPointScale(const float scale)
ctrlp[i]->scale.y = scale;
}
}
void SplineGrid::setAssist(bool on)
{
if(on == _assistMode)
return;
if(on)
importGridPoints(&cpgen.designpoints[0]);
else
importGridPoints(&bsp.controlpoints[0]);
_assistMode = on;
recalc();
}

View file

@ -7,11 +7,8 @@
#include "Quad.h"
#include "Interpolators.h"
enum SplineType
{
SPLINE_BSPLINE,
SPLINE_COSINE,
};
class BoneKeyframe;
class SplineGridCtrlPoint : public Quad
{
@ -36,13 +33,22 @@ public:
// # of control points on each axis
DynamicRenderGrid *resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy);
void recalc();
void exportControlPoints(Vector *controlpoints);
void importControlPoints(const Vector *controlpoints);
// Export/import grid points; depending on the mode these either correspond directly to control points
// or to spline points from which the control points need to be calculated first (using cpgen)
void exportGridPoints(Vector *pdst) const;
void importGridPoints(const Vector *psrc);
void importKeyframe(const BoneKeyframe *bk);
void exportKeyframe(BoneKeyframe *bk) const;
void resetControlPoints();
void setPointScale(const float scale);
float getPointScale() const { return pointscale; }
void setAssist(bool on);
virtual void onRender(const RenderState& rs) const OVERRIDE;
virtual void onUpdate(float dt) OVERRIDE;
@ -53,6 +59,7 @@ public:
bool wasModified; // to be checked/reset by external code
private:
void _generateControlPointsFromDesignPoints();
SplineGridCtrlPoint *createControlPoint(size_t x, size_t y);
@ -60,6 +67,9 @@ private:
unsigned deg;
BSpline2DWithPoints bsp;
float pointscale;
BSpline2DControlPointGeneratorWithPoints cpgen;
bool _assistMode;
};

File diff suppressed because it is too large Load diff