1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-05-09 10:34:05 +00:00

Initial implementation of bspline support in the animation editor

This commit is contained in:
fgenesis 2022-09-23 18:10:44 +02:00
parent 4c52a147b0
commit 575a83abd6
10 changed files with 515 additions and 120 deletions

View file

@ -225,8 +225,8 @@ void AnimationEditor::applyState()
StateObject::applyState();
boneEdit = 0;
editingBone = 0;
currentKey = 0;
splinegrid = 0;
editSprite = new SkeletalSprite();
editSprite->cull = false;
@ -468,15 +468,6 @@ void AnimationEditor::applyState()
tr->position = Vector(0, KEYFRAME_POS_Y);
addRenderObject(tr, LR_BLACKGROUND);
splinegrid = new SplineGrid;
splinegrid->setTexture("mithalas-house-bg-0001");
splinegrid->setWidthHeight(200, 200);
splinegrid->position = Vector(400, 300);
splinegrid->resize(3,3,10,10,3);
splinegrid->resetControlPoints();
addRenderObject(splinegrid, LR_PARTICLES_TOP);
editSprite->setSelectedBone(0);
dsq->overlay->alpha.interpolateTo(0, 0.5f);
@ -720,12 +711,18 @@ void AnimationEditor::update(float dt)
sprintf(t2buf, "Bone x: %.3f, y: %.3f, rot: %.3f strip: %u pass: %d (%d)", ebdata.x, ebdata.y, ebdata.z, (unsigned)selectedStripPoint, pass, origpass);
text2->setText(t2buf);
RenderObject *ctrlSprite;
if(splinegrid)
ctrlSprite = splinegrid;
else
ctrlSprite = editSprite;
if (core->mouse.buttons.middle)
{
editSprite->position += core->mouse.change;
ctrlSprite->position += core->mouse.change;
}
if (editingStrip)
if (editingStrip && !splinegrid)
{
if (isActing(ACTION_SWIMLEFT, -1))
moveBoneStripPoint(Vector(-dt, 0));
@ -740,23 +737,23 @@ void AnimationEditor::update(float dt)
int spd = 1;
if (core->mouse.scrollWheelChange < 0)
{
editSprite->scale -= Vector(spd*0.05f,spd*0.05f);
ctrlSprite->scale -= Vector(spd*0.05f,spd*0.05f);
}
else if (core->mouse.scrollWheelChange > 0)
{
editSprite->scale += Vector(spd*0.05f,spd*0.05f);
ctrlSprite->scale += Vector(spd*0.05f,spd*0.05f);
}
if (core->getKeyState(KEY_PGDN) && core->getShiftState())
{
editSprite->scale -= Vector(spd*0.05f,spd*0.05f);
ctrlSprite->scale -= Vector(spd*0.05f,spd*0.05f);
}
if (core->getKeyState(KEY_PGUP) && core->getShiftState())
{
editSprite->scale += Vector(spd*0.05f,spd*0.05f);
ctrlSprite->scale += Vector(spd*0.05f,spd*0.05f);
}
if (editSprite->scale.x < 0.05f)
if (ctrlSprite->scale.x < 0.05f)
{
editSprite->scale = Vector(0.05f,0.05f);
ctrlSprite->scale = Vector(0.05f,0.05f);
}
if (boneEdit == 0)
@ -768,7 +765,7 @@ void AnimationEditor::update(float dt)
}
}
if (editingBone && boneEdit == 1)
if (editingBone && boneEdit == 1 && !splinegrid)
{
Vector add = core->mouse.change;
// adjust relative mouse movement to absolute bone rotation
@ -804,11 +801,17 @@ void AnimationEditor::update(float dt)
editSprite->updateBones();
}
}
if(splinegrid && editingBone && editingStrip && splinegrid->wasModified)
{
applySplineGridToBone();
splinegrid->wasModified = false;
}
}
void AnimationEditor::copy()
{
if (dsq->isNested()) return;
if (dsq->isNested()) return;
if (core->getCtrlState())
copyBuffer = *editSprite->getCurrentAnimation()->getKeyframe(currentKey);
@ -831,7 +834,7 @@ void AnimationEditor::nextKey()
{
if (dsq->isNested()) return;
if (editingStrip)
if (editingStrip && !splinegrid)
{
selectedStripPoint++;
if (selectedStripPoint >= editSprite->getSelectedBone(false)->changeStrip.size()
@ -857,13 +860,15 @@ void AnimationEditor::nextKey()
currentKey --;
}
}
applyBoneToSplineGrid();
}
void AnimationEditor::prevKey()
{
if (dsq->isNested()) return;
if (editingStrip)
if (editingStrip && !splinegrid)
{
if(selectedStripPoint > 0) {
selectedStripPoint--;
@ -889,6 +894,8 @@ void AnimationEditor::prevKey()
}
}
}
applyBoneToSplineGrid();
}
@ -938,11 +945,58 @@ void AnimationEditor::editStripKey()
selectedStripPoint = 0;
editingStrip = false;
bgGrad->makeVertical(Vector(0.4f, 0.4f, 0.4f), Vector(0.8f, 0.8f, 0.8f));
if(splinegrid)
{
//editSprite->alphaMod = 1;
//editSprite->removeChild(splinegrid);
splinegrid->safeKill();
splinegrid = NULL;
}
}
else
{
editingStrip = true;
bgGrad->makeVertical(Vector(0.4f, 0.4f, 0.6f), Vector(0.8f, 0.8f, 1));
if(editingBone && !editingBone->getDrawGrid().empty())
{
Animation *a = editSprite->getCurrentAnimation();
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
editingStrip = true;
if(interp)
{
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());
splinegrid = new SplineGrid;
splinegrid->setTexture(editingBone->texture->name);
splinegrid->setWidthHeight(editingBone->width, editingBone->height);
splinegrid->position = Vector(400, 300);
//splinegrid->followCamera = 1;
splinegrid->resize(interp->bsp.ctrlX(), interp->bsp.ctrlY(), editingBone->getDrawGrid().width(), editingBone->getDrawGrid().height(), interp->bsp.degX(), interp->bsp.degY());
splinegrid->importControlPoints(&bk->controlpoints[0]);
//editSprite->addChild(splinegrid, PM_STATIC, RBP_OFF, CHILD_FRONT);
//editSprite->alphaMod = 0.5f;
addRenderObject(splinegrid, LR_PARTICLES_TOP);
}
else
{
bgGrad->makeVertical(Vector(0.4f, 0.4f, 0.6f), Vector(0.8f, 0.8f, 1));
}
}
else if(editingBone)
{
debugLog("Bone has no grid, cannot edit grid");
dsq->sound->playSfx("denied");
}
else
{
debugLog("No bone selected for grid edit mode");
dsq->sound->playSfx("denied");
}
}
}
@ -1097,6 +1151,8 @@ void AnimationEditor::clearRot()
{
if(core->getCtrlState())
editingBone->texture->reload();
else if(splinegrid)
splinegrid->resetControlPoints();
else
applyRotation();
}
@ -1293,6 +1349,8 @@ void AnimationEditor::loadFile()
// disable strip edit mode if still active
if (editingStrip)
editStripKey();
applyBoneToSplineGrid();
}
void AnimationEditor::goToTitle()
@ -1325,6 +1383,7 @@ void AnimationEditor::nextAnim()
void AnimationEditor::prevAnim()
{
if (dsq->isNested()) return;
if (editingStrip) return;
if (!core->getShiftState())
{
@ -1475,3 +1534,29 @@ void AnimationEditor::updateTimelineGrid()
os << "Grid: " << TIMELINE_GRIDSIZE;
gridsize->setText(os.str());
}
void AnimationEditor::applyBoneToSplineGrid()
{
if(splinegrid && editingBone)
{
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->getDrawGrid().linearsize());
splinegrid->importControlPoints(&bk->controlpoints[0]);
}
}
void AnimationEditor::applySplineGridToBone()
{
if(splinegrid && editingBone)
{
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->getDrawGrid().linearsize());
splinegrid->exportControlPoints(&bk->controlpoints[0]);
BoneGridInterpolator *interp = a->getBoneGridInterpolator(editingBone->boneIdx);
interp->updateGridAndBone(*bk, editingBone);
}
}

View file

@ -150,6 +150,8 @@ public:
DebugFont *gridsize, *unitsize;
SplineGrid *splinegrid;
void applySplineGridToBone();
void applyBoneToSplineGrid();
};

View file

@ -2,6 +2,16 @@
#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.
// this way 0 is perfectly in the center (which is a nice property to have)
// but more importantly the Quad::drawGrid in its default state
// has values in -0.5..+0.5 in its initial state.
// So we follow the same here, that the spline produces values
// in -0.5..+0.5 in its initial state.
static const float TMIN = -0.5f;
static const float TMAX = 0.5f;
CosineInterpolator::CosineInterpolator()
{
}
@ -73,9 +83,10 @@ BSpline2D::BSpline2D()
{
}
void BSpline2D::resize(size_t cx, size_t cy, unsigned degx, unsigned degy, float tmin, float tmax)
void BSpline2D::resize(size_t cx, size_t cy, unsigned degx, unsigned degy)
{
controlpoints.resize(cx * cy);
const float tmin = TMIN;
const float tmax = TMAX;
knotsX.resize(tbsp__getNumKnots(cx, degx));
knotsY.resize(tbsp__getNumKnots(cy, degy));
tbsp::fillKnotVector<float>(&knotsX[0], cx, degx, tmin, tmax);
@ -88,7 +99,7 @@ void BSpline2D::resize(size_t cx, size_t cy, unsigned degx, unsigned degy, float
_tmax = tmax;
}
void BSpline2D::recalc(Vector* dst, size_t xres, size_t yres)
void BSpline2D::recalc(Vector* dst, size_t xres, size_t yres, const Vector *controlpoints)
{
std::vector<Vector> tmpv;
size_t degn = std::max(_degx, _degy);
@ -120,3 +131,34 @@ void BSpline2D::recalc(Vector* dst, size_t xres, size_t yres)
dst += xres;
}
}
// TODO: add minx/y, maxx/y params?
// this should NOT be tmin?! probably?
void BSpline2D::reset(Vector* controlpoints)
{
const float dx = (_tmax - _tmin) / float(_cpx - 1);
const float dy = (_tmax - _tmin) / float(_cpy - 1);
float yy = _tmin;
for(size_t y = 0; y < _cpy; ++y, yy += dy)
{
float xx = _tmin;
for(size_t x = 0; x < _cpx; ++x, xx += dx)
*controlpoints++ = Vector(xx, yy);
}
}
void BSpline2DWithPoints::resize(size_t cx, size_t cy, unsigned degx, unsigned degy)
{
controlpoints.resize(cx * cy);
BSpline2D::resize(cx, cy, degx, degy);
}
void BSpline2DWithPoints::recalc(Vector* dst, size_t xres, size_t yres)
{
BSpline2D::recalc(dst, xres, yres, &controlpoints[0]);
}
void BSpline2DWithPoints::reset()
{
BSpline2D::reset(&controlpoints[0]);
}

View file

@ -19,24 +19,16 @@ private:
std::vector<std::pair<float, float> > pxy;
};
#endif
class BSpline2D
{
public:
BSpline2D();
// # of control points on each axis
void resize(size_t cx, size_t cy, unsigned degx, unsigned degy, float tmin, float tmax);
void recalc(Vector *dst, size_t xres, size_t yres);
std::vector<Vector> controlpoints;
inline Vector& controlpoint(size_t x, size_t y)
{
return controlpoints[y * _cpx + x];
}
void resize(size_t cx, size_t cy, unsigned degx, unsigned degy);
void recalc(Vector *dst, size_t xres, size_t yres, const Vector *controlpoints);
void reset(Vector *controlpoints);
inline size_t ctrlX() const { return _cpx; }
inline size_t ctrlY() const { return _cpy; }
@ -50,3 +42,23 @@ private:
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);
void reset();
std::vector<Vector> controlpoints;
inline Vector& controlpoint(size_t x, size_t y)
{
return controlpoints[y * ctrlX() + x];
}
};
#endif

View file

@ -100,23 +100,50 @@ void Quad::setStripPoints(bool vert, const Vector *points, size_t n)
}
}
void Quad::resetGrid()
void Quad::ResetGrid(Vector* dst, size_t w, size_t h)
{
const float yMulF = 1.0f / (float)(drawGrid.height()-1);
const float xMulF = 1.0f / (float)(drawGrid.width()-1);
assert(w > 1 && h > 1);
const float xMulF = 1.0f / (float)(w-1);
const float yMulF = 1.0f / (float)(h-1);
for (size_t y = 0; y < drawGrid.height(); y++)
for (size_t y = 0; y < h; y++)
{
Vector *row = drawGrid.row(y);
const float yval = float(y)*yMulF-0.5f;
for (size_t x = 0; x < drawGrid.width(); x++)
for (size_t x = 0; x < w; x++)
{
row[x].x = float(x)*xMulF-0.5f;
row[x].y = yval;
dst->x = float(x)*xMulF-0.5f;
dst->y = yval;
++dst;
}
}
}
void Quad::ResetGridAndAlpha(Vector* dst, size_t w, size_t h, float alpha)
{
assert(w > 1 && h > 1);
const float xMulF = 1.0f / (float)(w-1);
const float yMulF = 1.0f / (float)(h-1);
for (size_t y = 0; y < h; y++)
{
const float yval = float(y)*yMulF-0.5f;
for (size_t x = 0; x < w; x++)
{
dst->x = float(x)*xMulF-0.5f;
dst->y = yval;
dst->z = alpha;
++dst;
}
}
}
void Quad::resetGrid()
{
if (drawGrid.empty()) return;
ResetGrid(drawGrid.data(), drawGrid.width(), drawGrid.height());
}
void Quad::initQuad()
{
repeatToFillScale = Vector(1,1);

View file

@ -93,6 +93,10 @@ public:
float borderAlpha;
Vector repeatToFillScale;
static void ResetGrid(Vector *dst, size_t w, size_t h);
static void ResetGridAndAlpha(Vector *dst, size_t w, size_t h, float alpha = 1.0f);
protected:
float gridTimer;
Array2d<Vector> drawGrid;

View file

@ -748,6 +748,19 @@ size_t Animation::getSkeletalKeyframeIndex(SkeletalKeyframe *skey)
return -1;
}
BoneGridInterpolator * Animation::getBoneGridInterpolator(size_t boneIdx)
{
for(size_t i = 0; i < interpolators.size(); ++i)
{
BoneGridInterpolator& bgip = interpolators[i];
if(bgip.idx == boneIdx)
{
return &bgip;
}
}
}
BoneKeyframe *SkeletalKeyframe::getBoneKeyframe(size_t idx)
{
for (size_t i = 0; i < keyframes.size(); i++)
@ -1012,7 +1025,13 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
bone->SetAttribute("offy", this->bones[i]->offset.y);
if (!this->bones[i]->prt.empty())
bone->SetAttribute("prt", this->bones[i]->prt.c_str());
if (!this->bones[i]->changeStrip.empty())
if(!this->bones[i]->getDrawGrid().empty() && this->bones[i]->gridType != Quad::GRID_STRIP)
{
std::ostringstream os;
os << this->bones[i]->getDrawGrid().width() << " " << this->bones[i]->getDrawGrid().height();
bone->SetAttribute("grid", os.str().c_str());
}
else if (!this->bones[i]->changeStrip.empty())
{
std::ostringstream os;
os << this->bones[i]->stripVert << " " << this->bones[i]->changeStrip.size();
@ -1064,6 +1083,45 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
animation->SetAttribute("name", a->name.c_str());
if(a->resetPassOnEnd)
animation->SetAttribute("resetPassOnEnd", a->resetPassOnEnd);
for (size_t j = 0; j < a->interpolators.size(); ++j)
{
const BoneGridInterpolator& bgip = a->interpolators[j];
XMLElement *interp = xml->NewElement("Interpolator");
Bone *bone = this->getBoneByIdx(bgip.idx);
assert(bone->gridType == Quad::GRID_INTERP);
if(bgip.storeBoneByIdx)
interp->SetAttribute("bone", (int)bone->boneIdx);
else
interp->SetAttribute("bone", bone->name.c_str());
{
std::ostringstream osty;
osty << "bspline"
<< " " <<bgip.bsp.ctrlX()
<< " " <<bgip.bsp.ctrlY()
<< " " <<bgip.bsp.degX()
<< " " <<bgip.bsp.degY();
interp->SetAttribute("type", osty.str().c_str());
}
{
std::ostringstream osd;
for (size_t k = 0; k < a->keyframes.size(); k++)
{
SkeletalKeyframe& sk = a->keyframes[k];
BoneKeyframe *bk = sk.getBoneKeyframe(bgip.idx);
assert(bk->controlpoints.size() == bgip.bsp.ctrlX() * bgip.bsp.ctrlY());
osd << bgip.bsp.ctrlX() << " " << bgip.bsp.ctrlY();
for(size_t p = 0; p < bk->controlpoints.size(); ++p)
osd << " " << bk->controlpoints[p].x << " " << bk->controlpoints[p].y;
osd << " ";
}
interp->SetAttribute("data", osd.str().c_str());
}
animation->InsertEndChild(interp);
}
for (size_t j = 0; j < a->keyframes.size(); j++)
{
XMLElement *key = xml->NewElement("Key");
@ -1083,16 +1141,16 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
for (size_t k = 0; k < a->keyframes[j].keyframes.size(); k++)
{
BoneKeyframe *b = &a->keyframes[j].keyframes[k];
Bone *bone = this->getBoneByIdx(b->idx);
os << b->idx << " " << b->x << " " << b->y << " " << b->rot << " ";
os << b->grid.size() << " ";
for (size_t i = 0; i < b->grid.size(); i++)
{
os << b->grid[i].x << " " << b->grid[i].y << " ";
}
// don't want to store grid points if they can be regenerated automatically
size_t usedGridSize = bone->gridType == Quad::GRID_INTERP ? 0 : b->grid.size();
os << usedGridSize << " ";
if(usedGridSize)
for (size_t i = 0; i < usedGridSize; i++)
os << b->grid[i].x << " " << b->grid[i].y << " ";
if (b->doScale)
{
szos << b->idx << " " << b->sx << " " << b->sy << " ";
}
}
std::string szoss = szos.str();
if (!szoss.empty())
@ -1104,6 +1162,7 @@ bool SkeletalSprite::saveSkeletal(const std::string &fn)
}
animations->InsertEndChild(animation);
}
xml->InsertEndChild(animations);
return xml->SaveFile(file.c_str()) == XML_SUCCESS;
}
@ -1376,7 +1435,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
{
if (bones->Attribute("scale"))
{
SimpleIStringStream is(bones->Attribute("scale"));
SimpleIStringStream is(bones->Attribute("scale"), SimpleIStringStream::REUSE);
is >> scale.x >> scale.y;
}
@ -1411,7 +1470,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (bone->Attribute("prt"))
{
newb->prt = bone->Attribute("prt");
SimpleIStringStream is(newb->prt);
SimpleIStringStream is(newb->prt.c_str(), SimpleIStringStream::REUSE);
int slot;
std::string pfile;
while (is >> slot)
@ -1477,13 +1536,13 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
}
if (bone->Attribute("io"))
{
SimpleIStringStream is(bone->Attribute("io"));
SimpleIStringStream is(bone->Attribute("io"), SimpleIStringStream::REUSE);
is >> newb->internalOffset.x >> newb->internalOffset.y;
}
if (bone->Attribute("strip"))
{
SimpleIStringStream is(bone->Attribute("strip"));
SimpleIStringStream is(bone->Attribute("strip"), SimpleIStringStream::REUSE);
bool vert;
int num;
is >> vert >> num;
@ -1492,7 +1551,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (bone->Attribute("sz"))
{
float sx, sy;
SimpleIStringStream is(bone->Attribute("sz"));
SimpleIStringStream is(bone->Attribute("sz"), SimpleIStringStream::REUSE);
is >> sx >> sy;
newb->scale = newb->originalScale = Vector(sx,sy);
@ -1512,7 +1571,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (bone->Attribute("alpha"))
{
float a=1.0;
SimpleIStringStream is(bone->Attribute("alpha"));
SimpleIStringStream is(bone->Attribute("alpha"), SimpleIStringStream::REUSE);
is >> a;
newb->alpha = a;
}
@ -1520,7 +1579,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (bone->Attribute("alphaMod"))
{
float a=1.0;
SimpleIStringStream is(bone->Attribute("alphaMod"));
SimpleIStringStream is(bone->Attribute("alphaMod"), SimpleIStringStream::REUSE);
is >> a;
newb->alphaMod = a;
}
@ -1530,14 +1589,14 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
int x, y;
float dgox, dgoy, dgmx, dgmy, dgtm;
bool dgo;
SimpleIStringStream is(bone->Attribute("segs"));
SimpleIStringStream is(bone->Attribute("segs"), SimpleIStringStream::REUSE);
is >> x >> y >> dgox >> dgoy >> dgmx >> dgmy >> dgtm >> dgo;
newb->setSegs(x, y, dgox, dgoy, dgmx, dgmy, dgtm, dgo);
}
if (bone->Attribute("color"))
{
SimpleIStringStream in(bone->Attribute("color"));
SimpleIStringStream in(bone->Attribute("color"), SimpleIStringStream::REUSE);
in >> newb->color.x >> newb->color.y >> newb->color.z;
}
if (bone->Attribute("sel"))
@ -1546,10 +1605,19 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
}
if (bone->Attribute("grid"))
{
SimpleIStringStream is(bone->Attribute("grid"));
int x, y;
is >> x >> y;
newb->createGrid(x, y);
if(newb->getDrawGrid().empty())
{
SimpleIStringStream is(bone->Attribute("grid"), SimpleIStringStream::REUSE);
int x, y;
is >> x >> y;
newb->createGrid(x, y);
}
else
{
std::ostringstream os;
os << "Bone idx " << newb->idx << " already has a DrawGrid, ignoring \"grid\" attribute";
errorLog(os.str());
}
}
bone = bone->NextSiblingElement("Bone");
}
@ -1566,6 +1634,12 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
os << "Parent bone not found, index: " << b->pidx << " from bone idx: " << b->getIdx();
debugLog(os.str());
}
else if(b == pb) // self-loop would crash
{
std::ostringstream os;
os << "Bone index " << b->pidx << " has itself as parent, this is bad, ignoring";
errorLog(os.str());
}
else
{
pb->addChild(b, PM_POINTER);
@ -1586,7 +1660,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
AnimationLayer newAnimationLayer;
if (animationLayer->Attribute("ignore"))
{
SimpleIStringStream is(animationLayer->Attribute("ignore"));
SimpleIStringStream is(animationLayer->Attribute("ignore"), SimpleIStringStream::REUSE);
int t;
while (is >> t)
{
@ -1595,7 +1669,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
}
if (animationLayer->Attribute("include"))
{
SimpleIStringStream is(animationLayer->Attribute("include"));
SimpleIStringStream is(animationLayer->Attribute("include"), SimpleIStringStream::REUSE);
int t;
while (is >> t)
{
@ -1631,7 +1705,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (key->Attribute("e"))
{
float time;
SimpleIStringStream is(key->Attribute("e"));
SimpleIStringStream is(key->Attribute("e"), SimpleIStringStream::REUSE);
is >> time;
int idx, x, y, rot, strip;
newSkeletalKeyframe.t = time;
@ -1662,7 +1736,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
}
if (key->Attribute("sz"))
{
SimpleIStringStream is2(key->Attribute("sz"));
SimpleIStringStream is2(key->Attribute("sz"), SimpleIStringStream::REUSE);
int midx;
float bsx, bsy;
while (is2 >> midx)
@ -1684,7 +1758,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (key->Attribute("d"))
{
float time;
SimpleIStringStream is(key->Attribute("d"));
SimpleIStringStream is(key->Attribute("d"), SimpleIStringStream::REUSE);
is >> time;
int idx, x, y, rot;
@ -1707,7 +1781,7 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
if (key->Attribute("cmd"))
{
newSkeletalKeyframe.cmd = key->Attribute("cmd");
SimpleIStringStream is(newSkeletalKeyframe.cmd);
SimpleIStringStream is(newSkeletalKeyframe.cmd.c_str(), SimpleIStringStream::REUSE);
int bidx;
while (is >> bidx)
{
@ -1730,55 +1804,125 @@ void SkeletalSprite::loadSkeletal(const std::string &fn)
// generate empty bone keys
for (size_t i = 0; i < this->bones.size(); i++)
{
if (newSkeletalKeyframe.getBoneKeyframe(this->bones[i]->boneIdx))
{
}
else
Bone *bone = this->bones[i];
const Array2d<Vector>& dg = bone->getDrawGrid();
BoneKeyframe *bk = newSkeletalKeyframe.getBoneKeyframe(bone->boneIdx);
if(!bk)
{
BoneKeyframe b;
b.idx = this->bones[i]->boneIdx;
newSkeletalKeyframe.keyframes.push_back(b);
}
}
newAnimation.keyframes.push_back(newSkeletalKeyframe);
key = key->NextSiblingElement("Key");
}
// <Interpolator bone="name or idx" type="TYPE config and params" data="controlpoints; aded by editor" />
XMLElement *interp = animation->FirstChildElement("Interpolator");
while(interp)
for( ; interp; interp = interp->NextSiblingElement("Interpolator"))
{
Bone *bi = NULL;
if(const char *sbone = interp->Attribute("bone"))
bi = getBoneByIdx(atoi(sbone));
const char *sbone = interp->Attribute("bone");
bool boneByIdx = false;
if(sbone)
{
bi = getBoneByName(sbone);
if(!bi)
{
bi = getBoneByIdx(atoi(sbone));
boneByIdx = true;
}
}
if(!bi)
{
std::ostringstream os;
os << "Interpolator specifies non-existing bone [" << (sbone ? sbone : "(null)") << "]";
debugLog(os.str());
continue;
//SplineType spline = SPLINE_BSPLINE;
}
if(bi->getDrawGrid().empty())
{
std::ostringstream os;
os << "Interpolator specifies bone [" << bi->boneIdx << "] that has no grid";
debugLog(os.str());
continue;
}
SplineType spline = SPLINE_BSPLINE;
int cx = 3, cy = 3, degx = 3, degy = 3;
if(const char *stype = interp->Attribute("type"))
{
SimpleIStringStream is(stype);
SimpleIStringStream is(stype, SimpleIStringStream::REUSE);
std::string ty;
is >> ty;
BoneGridInterpolator bgip;
if(ty == "bspline")
{
//spline = SPLINE_BSPLINE;
is >> cx >> cy >> degx >> degy;
spline = SPLINE_BSPLINE;
if(!(is >> cx >> cy >> degx >> degy))
{
if(!degx)
degx = 1;
if(!degy)
degy = 1;
}
if(cx < 2)
cx = 2;
if(cy < 2)
cy = 2;
}
else
{
errorLog("Unknown interpolator spline type [" + ty + "]");
continue;
}
}
BoneGridInterpolator bgip;
bi->gridType = Quad::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;
bgip.idx = bi->boneIdx;
bgip.pointsX = cx;
bgip.pointsY = cy;
bgip.degreeX = degx;
bgip.degreeY = degy;
bgip.storeBoneByIdx = boneByIdx;
// TODO
interp = interp->NextSiblingElement("Interpolator");
// ---- bspline -----
bgip.bsp.resize(cx, cy, degx, degy);
const size_t numcp = size_t(cx) * size_t(cy);
const size_t numgridp = bi->getDrawGrid().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)
{
SkeletalKeyframe& kf = newAnimation.keyframes[k];
BoneKeyframe *bk = kf.getBoneKeyframe(bgip.idx);
bk->controlpoints.resize(numcp);
bgip.bsp.reset(&bk->controlpoints[0]);
unsigned w = 0, h = 0;
Vector cp;
cp.z = 1; // we want all grid points at full alpha
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");
@ -1913,7 +2057,7 @@ void AnimationLayer::updateBones()
b->scale.x = lerp(bkey1->sx, bkey2->sx, dt, lerpType);
b->scale.y = lerp(bkey1->sy, bkey2->sy, dt, lerpType);
}
if (b->animated==Bone::ANIM_ALL && !b->changeStrip.empty())
if (b->animated==Bone::ANIM_ALL && !b->changeStrip.empty() && b->gridType == Quad::GRID_STRIP)
{
if (bkey2->grid.size() < b->changeStrip.size())
bkey2->grid.resize(b->changeStrip.size());
@ -1925,6 +2069,28 @@ void AnimationLayer::updateBones()
}
b->setStripPoints(b->stripVert, &b->changeStrip[0], b->changeStrip.size());
}
if (b->animated==Bone::ANIM_ALL && b->gridType == Quad::GRID_INTERP)
{
Array2d<Vector>& dg = b->getDrawGrid();
const size_t N = dg.linearsize();
if(bkey1->grid.size() < N)
{
bkey1->grid.resize(N);
Quad::ResetGridAndAlpha(&bkey1->grid[0], dg.width(), dg.height(), 1.0f);
}
if(bkey2->grid.size() < N)
{
bkey2->grid.resize(N);
Quad::ResetGridAndAlpha(&bkey2->grid[0], dg.width(), dg.height(), 1.0f);
}
Vector *dst = dg.data();
for(size_t i = 0; i < N; ++i)
{
dst[i].x = lerp(bkey1->grid[i].x, bkey2->grid[i].x, dt, lerpType);
dst[i].y = lerp(bkey1->grid[i].y, bkey2->grid[i].y, dt, lerpType);
}
}
}
}
}
@ -2046,4 +2212,18 @@ void SkeletalSprite::selectNextBone()
updateSelectedBoneColor();
}
void BoneGridInterpolator::updateGridOnly(BoneKeyframe& bk, const Bone *bone)
{
const Array2d<Vector>& dg = bone->getDrawGrid();
assert(bone->boneIdx == bk.idx);
assert(bk.grid.size() == dg.linearsize());
bsp.recalc(&bk.grid[0], dg.width(), dg.height(), &bk.controlpoints[0]);
}
void BoneGridInterpolator::updateGridAndBone(BoneKeyframe& bk, Bone *bone)
{
updateGridOnly(bk, bone);
Vector *dst = bone->getDrawGrid().data();
std::copy(bk.grid.begin(), bk.grid.end(), dst);
}

View file

@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "Quad.h"
#include "SimpleIStringStream.h"
#include <map>
#include "Interpolators.h"
// for 2d system only
enum AnimationCommand
@ -155,9 +156,10 @@ class BoneGridInterpolator
{
public:
size_t idx;
unsigned pointsX, pointsY;
unsigned degreeX, degreeY;
void updateGrid(BoneKeyframe& bk);
BSpline2D bsp;
bool storeBoneByIdx;
void updateGridOnly(BoneKeyframe& bk, const Bone *bone);
void updateGridAndBone(BoneKeyframe& bk, Bone *bone);
};
class Animation
@ -181,6 +183,7 @@ public:
void reverse();
bool resetPassOnEnd;
BoneGridInterpolator *getBoneGridInterpolator(size_t boneIdx);
typedef std::vector <BoneGridInterpolator> Interpolators;
Interpolators interpolators;
};

View file

@ -14,27 +14,57 @@ Vector SplineGridCtrlPoint::getSplinePosition() const
{
SplineGridCtrlPoint *p = (SplineGridCtrlPoint*)getParent();
// always return alpha == 1
// points within the quad result in in -0.5 .. +0.5 on both axes
return Vector(position.x / p->width, position.y / p->height, 1.0f);
}
void SplineGridCtrlPoint::setSplinePosition(Vector pos)
{
SplineGridCtrlPoint *p = (SplineGridCtrlPoint*)getParent();
position.x = pos.x * p->width;
position.y = pos.y * p->height;
}
void SplineGridCtrlPoint::onUpdate(float dt)
{
const bool lmb = core->mouse.buttons.left;
// FIXME: selected point tracking should be done by the parent
Vector cur = core->mouse.position;
// bool wouldpick = isCoordinateInside(cur); // doesn't work
Vector wp = getWorldPosition();
const bool wouldpick = (cur - wp).isLength2DIn(10);
if(wouldpick)
color = Vector(1,0,0);
else
color = Vector(1,1,1);
if(lmb)
{
if(!movingPoint && isCoordinateInside(core->mouse.position))
if(!movingPoint && wouldpick)
{
movingPoint = this;
}
}
else
else if(movingPoint)
{
movingPoint->color = Vector(1,1,1);
movingPoint = NULL;
}
if(movingPoint == this)
{
SplineGrid *p = (SplineGrid*)getParent();
const Vector parentPos = p->getWorldPosition();
position = core->mouse.position - parentPos;
p->recalc();
const Vector invscale = Vector(1.0f / p->scale.x, 1.0f / p->scale.y);
Vector newpos = (cur - parentPos) * invscale;
if(position != newpos)
{
position = newpos;
p->recalc();
}
}
}
@ -50,7 +80,7 @@ SplineGrid::~SplineGrid()
{
}
void SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned deg)
void SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned degx, unsigned degy)
{
size_t oldcpx = bsp.ctrlX();
size_t oldcpy = bsp.ctrlY();
@ -74,7 +104,7 @@ void SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned d
}
}
bsp.resize(w, h, deg, deg, -1.0f, 1.0f);
bsp.resize(w, h, degx, degy);
// kill any excess points
for(size_t i = 0; i < oldp.size(); ++i)
@ -95,27 +125,29 @@ void SplineGrid::resize(size_t w, size_t h, size_t xres, size_t yres, unsigned d
void SplineGrid::recalc()
{
for(size_t i = 0; i < ctrlp.size(); ++i)
bsp.controlpoints[i] = ctrlp[i]->getSplinePosition();
exportControlPoints(&bsp.controlpoints[0]);
bsp.recalc(drawGrid.data(), drawGrid.width(), drawGrid.height());
wasModified = true;
}
void SplineGrid::exportControlPoints(Vector* controlpoints)
{
for(size_t i = 0; i < ctrlp.size(); ++i)
controlpoints[i] = ctrlp[i]->getSplinePosition();
}
void SplineGrid::importControlPoints(const Vector* controlpoints)
{
for(size_t i = 0; i < ctrlp.size(); ++i)
ctrlp[i]->setSplinePosition(controlpoints[i]);
recalc();
}
void SplineGrid::resetControlPoints()
{
const size_t cpx = bsp.ctrlX();
const size_t cpy = bsp.ctrlY();
const float dx = width / float(cpx - 1);
const float dy = height / float(cpy - 1);
float yy = height * -0.5f;
for(size_t y = 0; y < cpy; ++y, yy += dy)
{
float xx = width * -0.5f;
SplineGridCtrlPoint **row = &ctrlp[y * cpx];
for(size_t x = 0; x < cpx; ++x, xx += dx)
row[x]->position = Vector(xx, yy);
}
bsp.reset();
importControlPoints(&bsp.controlpoints[0]);
}
SplineGridCtrlPoint* SplineGrid::createControlPoint(size_t x, size_t y)
@ -145,7 +177,7 @@ void SplineGrid::onRender(const RenderState& rs) const
const Vector wh2(width * 0.5f, height * 0.5f);
glLineWidth(2);
glColor4f(0.0f, 1.0f, 0.3f, 0.3f);
glColor4f(0.0f, 0.3f, 1.0f, 0.3f);
const size_t cpx = bsp.ctrlX();
const size_t cpy = bsp.ctrlY();

View file

@ -19,6 +19,7 @@ public:
SplineGridCtrlPoint();
virtual void onUpdate(float dt) OVERRIDE;
Vector getSplinePosition() const;
void setSplinePosition(Vector pos);
static SplineGridCtrlPoint *movingPoint;
};
@ -32,21 +33,28 @@ public:
~SplineGrid();
// # of control points on each axis
void resize(size_t w, size_t h, size_t xres, size_t yres, unsigned deg);
void 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);
void resetControlPoints();
virtual void onRender(const RenderState& rs) const OVERRIDE;
virtual void onUpdate(float dt) OVERRIDE;
BSpline2D& getSpline() { return bsp; }
const BSpline2D& getSpline() const { return bsp; }
bool wasModified; // to be checked/reset by external code
private:
SplineGridCtrlPoint *createControlPoint(size_t x, size_t y);
std::vector<SplineGridCtrlPoint*> ctrlp;
unsigned deg;
BSpline2D bsp;
BSpline2DWithPoints bsp;
};