2011-08-03 20:05:33 +00:00
|
|
|
|
/*
|
|
|
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
|
|
|
|
|
|
This file is part of Aquaria.
|
|
|
|
|
|
|
|
|
|
Aquaria 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.
|
|
|
|
|
|
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
*/
|
|
|
|
|
#include "SkeletalSprite.h"
|
|
|
|
|
#include "Core.h"
|
|
|
|
|
#include "Particles.h"
|
|
|
|
|
#include "MathFunctions.h"
|
|
|
|
|
#include "SimpleIStringStream.h"
|
2016-07-09 02:18:40 +00:00
|
|
|
|
#include "ReadXML.h"
|
2022-05-18 00:15:19 +00:00
|
|
|
|
#include "RenderBase.h"
|
2022-09-13 16:38:44 +00:00
|
|
|
|
#include "SplineGrid.h"
|
2023-07-10 15:23:19 +00:00
|
|
|
|
#include "RenderGrid.h"
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2017-02-09 20:22:59 +00:00
|
|
|
|
#include <tinyxml2.h>
|
2014-06-08 20:11:23 +00:00
|
|
|
|
using namespace tinyxml2;
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
std::string SkeletalSprite::animationPath = "data/animations/";
|
|
|
|
|
std::string SkeletalSprite::skinPath = "skins/";
|
|
|
|
|
|
|
|
|
|
std::string SkeletalSprite::secondaryAnimationPath = "";
|
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
static std::map<std::string, XMLDocument*> skelCache;
|
2012-02-19 03:57:04 +00:00
|
|
|
|
|
2014-09-15 22:29:57 +00:00
|
|
|
|
static XMLDocument *_retrieveSkeletalXML(const std::string& name, bool keepEmpty)
|
2012-02-19 03:57:04 +00:00
|
|
|
|
{
|
2014-06-09 21:39:33 +00:00
|
|
|
|
std::map<std::string, XMLDocument*>::iterator it = skelCache.find(name);
|
|
|
|
|
if(it != skelCache.end())
|
|
|
|
|
return it->second;
|
|
|
|
|
|
2014-09-15 22:29:57 +00:00
|
|
|
|
XMLDocument *doc = readXML(name, NULL, keepEmpty);
|
2014-06-09 21:39:33 +00:00
|
|
|
|
if(doc)
|
2014-06-08 20:11:23 +00:00
|
|
|
|
skelCache[name] = doc;
|
2014-06-09 21:39:33 +00:00
|
|
|
|
|
2012-02-19 03:57:04 +00:00
|
|
|
|
return doc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::clearCache()
|
|
|
|
|
{
|
2014-09-15 22:29:57 +00:00
|
|
|
|
for(std::map<std::string, XMLDocument*>::iterator it = skelCache.begin(); it != skelCache.end(); ++it)
|
|
|
|
|
delete it->second;
|
2012-02-19 03:57:04 +00:00
|
|
|
|
skelCache.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
void SkeletalKeyframe::copyAllButTime(SkeletalKeyframe *copy)
|
|
|
|
|
{
|
|
|
|
|
if (!copy) return;
|
|
|
|
|
|
|
|
|
|
float t = this->t;
|
|
|
|
|
(*this) = (*copy);
|
|
|
|
|
this->t = t;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-18 17:44:42 +00:00
|
|
|
|
Bone::Bone() : CollideQuad()
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2012-01-03 03:38:28 +00:00
|
|
|
|
addType(SCO_BONE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
fileRenderQuad = true;
|
|
|
|
|
skeleton = 0;
|
|
|
|
|
generateCollisionMask = true;
|
2022-05-17 23:18:27 +00:00
|
|
|
|
enableCollision = true;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
animated = ANIM_ALL;
|
|
|
|
|
originalScale = Vector(1,1);
|
|
|
|
|
boneIdx = pidx = -1;
|
2017-01-19 22:44:30 +00:00
|
|
|
|
rbp = false;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
segmentChain = 0;
|
2022-05-18 00:15:19 +00:00
|
|
|
|
collisionMaskRadius = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
minDist = maxDist = 128;
|
|
|
|
|
reverse = false;
|
2016-04-18 20:08:36 +00:00
|
|
|
|
selectable = true;
|
2016-04-17 12:33:23 +00:00
|
|
|
|
originalRenderPass = 0;
|
2022-05-21 15:31:50 +00:00
|
|
|
|
stripVert = false;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2022-07-18 21:00:22 +00:00
|
|
|
|
Bone::~Bone()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
|
ParticleEffect *Bone::getEmitter(unsigned slot) const
|
|
|
|
|
{
|
|
|
|
|
return slot < emitters.size() ? emitters[slot] : NULL;
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
void Bone::destroy()
|
|
|
|
|
{
|
|
|
|
|
Quad::destroy();
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < segments.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
segments[i]->setLife(1.0);
|
|
|
|
|
segments[i]->setDecayRate(10);
|
|
|
|
|
segments[i]->alpha = 0;
|
|
|
|
|
}
|
|
|
|
|
segments.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 23:18:27 +00:00
|
|
|
|
bool Bone::canCollide() const
|
|
|
|
|
{
|
|
|
|
|
return this->enableCollision && this->alpha.x == 1 && this->renderQuad && (!this->collisionMask.empty() || this->collideRadius);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
void Bone::addSegment(Bone *b)
|
|
|
|
|
{
|
|
|
|
|
segments.push_back(b);
|
|
|
|
|
|
|
|
|
|
b->segmentChain = 2;
|
|
|
|
|
|
|
|
|
|
skeleton->removeChild(b);
|
|
|
|
|
|
|
|
|
|
core->getTopStateData()->addRenderObject(b, skeleton->getTopLayer());
|
|
|
|
|
b->position = this->getWorldPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Bone::createStrip(bool vert, int num)
|
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid *grid = vert ? createGrid(2, num) : createGrid(num, 2);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
stripVert = vert;
|
2023-07-10 15:23:19 +00:00
|
|
|
|
grid->gridType = GRID_STRIP;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
changeStrip.resize(num);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-12-21 05:04:09 +00:00
|
|
|
|
void Bone::addFrame(const std::string &gfx)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2023-12-21 05:04:09 +00:00
|
|
|
|
framegfx.push_back(gfx);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Bone::showFrame(int idx)
|
|
|
|
|
{
|
2023-12-21 05:04:09 +00:00
|
|
|
|
size_t i = idx;
|
|
|
|
|
if(i < framegfx.size())
|
|
|
|
|
setTexture(framegfx[i]);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Bone::setAnimated(int b)
|
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
animated = b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Bone::setSegmentProps(int minDist, int maxDist, bool reverse)
|
|
|
|
|
{
|
|
|
|
|
this->minDist = minDist;
|
|
|
|
|
this->maxDist = maxDist;
|
|
|
|
|
this->reverse = reverse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Bone::updateSegment(Bone *b, const Vector &diff)
|
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
float angle = -1;
|
|
|
|
|
if (diff.getSquaredLength2D() > sqr(maxDist))
|
|
|
|
|
{
|
|
|
|
|
Vector useDiff = diff;
|
|
|
|
|
useDiff.setLength2D(maxDist);
|
|
|
|
|
Vector reallyUseDiff = diff - useDiff;
|
|
|
|
|
b->position += reallyUseDiff;
|
|
|
|
|
|
|
|
|
|
MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), diff, angle);
|
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
else if (diff.getSquaredLength2D() > sqr(minDist))
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
b->position += diff*0.05f;
|
|
|
|
|
|
|
|
|
|
MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), diff, angle);
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (angle != -1)
|
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
if (b->rotation.z >= 270 && angle < 90)
|
|
|
|
|
{
|
|
|
|
|
b->rotation.stop();
|
|
|
|
|
b->rotation.z -= 360;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (b->rotation.z <= 90 && angle > 270)
|
|
|
|
|
{
|
|
|
|
|
b->rotation.stop();
|
|
|
|
|
b->rotation.z += 360;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-01-19 22:31:56 +00:00
|
|
|
|
b->rotation.interpolateTo(Vector(0,0,angle),0.2f);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Bone::updateSegments()
|
|
|
|
|
{
|
|
|
|
|
if (segmentChain>0 && !segments.empty())
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!reverse)
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < segments.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
Vector diff;
|
|
|
|
|
if (i == 0)
|
|
|
|
|
{
|
|
|
|
|
Vector world = getWorldCollidePosition(segmentOffset);
|
|
|
|
|
diff = world - segments[i]->getWorldPosition();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
diff = segments[i-1]->getWorldPosition() - segments[i]->getWorldPosition();
|
|
|
|
|
|
|
|
|
|
updateSegment(segments[i], diff);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int top = segments.size()-1;
|
|
|
|
|
for (int i = top; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
Vector diff;
|
|
|
|
|
if (i == top)
|
|
|
|
|
{
|
|
|
|
|
Vector world = getWorldCollidePosition(segmentOffset);
|
|
|
|
|
diff = world - segments[i]->getWorldPosition();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
diff = segments[i+1]->getWorldPosition() - segments[i]->getWorldPosition();
|
|
|
|
|
|
|
|
|
|
updateSegment(segments[i], diff);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 23:18:27 +00:00
|
|
|
|
void Bone::spawnParticlesFromCollisionMask(const char *p, unsigned intv, int layer, float rotz)
|
|
|
|
|
{
|
|
|
|
|
for (size_t j = 0; j < this->collisionMask.size(); j+=intv)
|
|
|
|
|
{
|
|
|
|
|
Vector pos = this->getWorldCollidePosition(this->collisionMask[j]);
|
|
|
|
|
core->createParticleEffect(p, pos, layer, rotz);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-19 23:04:19 +00:00
|
|
|
|
void Bone::renderCollision(const RenderState& rs) const
|
2022-05-18 00:15:19 +00:00
|
|
|
|
{
|
|
|
|
|
if (!collisionMask.empty())
|
|
|
|
|
{
|
|
|
|
|
glPushMatrix();
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2024-01-11 22:05:47 +00:00
|
|
|
|
RenderObject::lastTextureApplied = 0;
|
2022-05-18 00:15:19 +00:00
|
|
|
|
|
|
|
|
|
glLoadIdentity();
|
|
|
|
|
core->setupRenderPositionAndScale();
|
|
|
|
|
|
2024-01-11 22:05:47 +00:00
|
|
|
|
rs.gpu.setBlend(BLEND_DEFAULT);
|
2022-05-18 00:15:19 +00:00
|
|
|
|
|
|
|
|
|
glColor4f(1,1,0,0.5);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < transformedCollisionMask.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Vector collide = this->transformedCollisionMask[i];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glTranslatef(collide.x, collide.y, 0);
|
2024-04-28 11:15:41 +00:00
|
|
|
|
const RenderObject *parent = this->getTopParent();
|
2022-05-18 00:15:19 +00:00
|
|
|
|
if (parent)
|
|
|
|
|
drawCircle(collideRadius*parent->scale.x, 45);
|
|
|
|
|
glTranslatef(-collide.x, -collide.y, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glPopMatrix();
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-05-19 23:04:19 +00:00
|
|
|
|
CollideQuad::renderCollision(rs);
|
2022-05-18 00:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-17 23:54:12 +00:00
|
|
|
|
Vector Bone::getCollisionMaskNormal(Vector pos, float dist) const
|
2022-05-18 00:15:19 +00:00
|
|
|
|
{
|
|
|
|
|
Vector sum;
|
|
|
|
|
for (size_t i = 0; i < this->transformedCollisionMask.size(); i++)
|
|
|
|
|
{
|
2023-04-17 23:54:12 +00:00
|
|
|
|
Vector diff = pos - transformedCollisionMask[i];
|
|
|
|
|
if (diff.isLength2DIn(dist))
|
|
|
|
|
sum += diff;
|
2022-05-18 00:15:19 +00:00
|
|
|
|
}
|
2023-04-17 23:54:12 +00:00
|
|
|
|
sum.normalize2D();
|
2022-05-18 00:15:19 +00:00
|
|
|
|
return sum;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-17 23:18:27 +00:00
|
|
|
|
|
2016-04-17 13:16:55 +00:00
|
|
|
|
bool BoneCommand::parse(Bone *b, SimpleIStringStream &is)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
std::string type;
|
|
|
|
|
is >> type;
|
|
|
|
|
this->b = b;
|
|
|
|
|
if (type=="AC_PRT_LOAD")
|
|
|
|
|
{
|
|
|
|
|
command = AC_PRT_LOAD;
|
|
|
|
|
is >> slot >> file;
|
|
|
|
|
}
|
|
|
|
|
else if (type=="AC_SND_PLAY")
|
|
|
|
|
{
|
|
|
|
|
command = AC_SND_PLAY;
|
|
|
|
|
is >> file;
|
|
|
|
|
}
|
|
|
|
|
else if (type=="AC_FRM_SHOW")
|
|
|
|
|
{
|
|
|
|
|
command = AC_FRM_SHOW;
|
|
|
|
|
is >> slot;
|
|
|
|
|
}
|
|
|
|
|
else if (type=="AC_PRT_START")
|
|
|
|
|
{
|
|
|
|
|
command = AC_PRT_START;
|
|
|
|
|
is >> slot;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
else if (type=="AC_PRT_STOP")
|
|
|
|
|
{
|
|
|
|
|
command = AC_PRT_STOP;
|
|
|
|
|
is >> slot;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
else if (type=="AC_SEGS_START")
|
|
|
|
|
command = AC_SEGS_START;
|
|
|
|
|
else if (type=="AC_SEGS_STOP")
|
|
|
|
|
command = AC_SEGS_STOP;
|
2016-04-17 12:33:23 +00:00
|
|
|
|
else if (type == "AC_SET_PASS")
|
|
|
|
|
{
|
|
|
|
|
command = AC_SET_PASS;
|
|
|
|
|
is >> slot;
|
|
|
|
|
}
|
|
|
|
|
else if(type == "AC_RESET_PASS")
|
|
|
|
|
command = AC_RESET_PASS;
|
2023-12-21 05:04:09 +00:00
|
|
|
|
else if(type == "AC_SET_FH")
|
|
|
|
|
{
|
|
|
|
|
command = AC_SET_FH;
|
|
|
|
|
is >> slot;
|
|
|
|
|
}
|
2016-04-17 13:16:55 +00:00
|
|
|
|
else // fail
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "Failed to parse bone command string: invalid command: " << type;
|
|
|
|
|
errorLog(os.str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BoneCommand::run()
|
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
switch(command)
|
|
|
|
|
{
|
|
|
|
|
case AC_SND_PLAY:
|
|
|
|
|
{
|
|
|
|
|
core->sound->playSfx(file);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AC_FRM_SHOW:
|
|
|
|
|
{
|
|
|
|
|
b->showFrame(slot);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AC_PRT_LOAD:
|
|
|
|
|
{
|
2016-07-09 02:18:40 +00:00
|
|
|
|
ParticleEffect *e = b->getEmitter(slot);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (e)
|
|
|
|
|
{
|
|
|
|
|
e->load(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AC_PRT_START:
|
|
|
|
|
{
|
2016-07-09 02:18:40 +00:00
|
|
|
|
ParticleEffect *e = b->getEmitter(slot);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (e)
|
|
|
|
|
e->start();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AC_PRT_STOP:
|
|
|
|
|
{
|
2016-07-09 02:18:40 +00:00
|
|
|
|
ParticleEffect *e = b->getEmitter(slot);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (e)
|
|
|
|
|
e->stop();
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-04-17 12:33:23 +00:00
|
|
|
|
case AC_SET_PASS:
|
|
|
|
|
b->setRenderPass(slot);
|
|
|
|
|
break;
|
|
|
|
|
case AC_RESET_PASS:
|
|
|
|
|
b->setRenderPass(b->originalRenderPass);
|
|
|
|
|
break;
|
2023-12-21 05:04:09 +00:00
|
|
|
|
case AC_SET_FH:
|
|
|
|
|
{
|
|
|
|
|
bool should = false;
|
|
|
|
|
switch(slot)
|
|
|
|
|
{
|
|
|
|
|
case 0: should = b->originalFH; break;
|
|
|
|
|
case 1: should = !b->originalFH; break;
|
|
|
|
|
case 2: should = true; break;
|
|
|
|
|
default: should = false; break;
|
|
|
|
|
}
|
|
|
|
|
b->fhTo(should);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-01-12 21:51:46 +00:00
|
|
|
|
case AC_SEGS_START:
|
|
|
|
|
case AC_SEGS_STOP:
|
|
|
|
|
break;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AnimationLayer::AnimationLayer()
|
|
|
|
|
{
|
|
|
|
|
lastNewKey = 0;
|
|
|
|
|
fallThru= 0;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
timer = 0;
|
|
|
|
|
loop = 0;
|
|
|
|
|
enqueuedAnimationLoop = 0;
|
|
|
|
|
timeMultiplier = 1;
|
|
|
|
|
animationLength = 0;
|
|
|
|
|
currentAnimation = 0;
|
|
|
|
|
animating = false;
|
|
|
|
|
fallThruSpeed = 0;
|
|
|
|
|
s = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::setTimeMultiplier(float t)
|
|
|
|
|
{
|
|
|
|
|
timeMultiplier = t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::playCurrentAnimation(int loop)
|
|
|
|
|
{
|
|
|
|
|
playAnimation(currentAnimation, loop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::animate(const std::string &a, int loop)
|
|
|
|
|
{
|
2017-01-12 21:51:46 +00:00
|
|
|
|
std::string animation = a;
|
|
|
|
|
stringToLower(animation);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
bool played = false;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < s->animations.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (s->animations[i].name == animation)
|
|
|
|
|
{
|
|
|
|
|
playAnimation(i, loop);
|
|
|
|
|
played = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!played)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "Could not find animation: " << animation;
|
|
|
|
|
debugLog(os.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::playAnimation(int idx, int loop)
|
|
|
|
|
{
|
|
|
|
|
if (!(&s->animLayers[0] == this))
|
|
|
|
|
{
|
|
|
|
|
fallThru = 1;
|
|
|
|
|
fallThruSpeed = 10;
|
|
|
|
|
}
|
|
|
|
|
timeMultiplier = 1;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
currentAnimation = idx;
|
|
|
|
|
timer = 0;
|
|
|
|
|
animating = true;
|
|
|
|
|
|
|
|
|
|
this->loop = loop;
|
|
|
|
|
|
|
|
|
|
animationLength = getCurrentAnimation()->getAnimationLength();
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-02-03 15:13:07 +00:00
|
|
|
|
void AnimationLayer::enqueueAnimation(const std::string& anim, int loop)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
enqueuedAnimation = anim;
|
|
|
|
|
enqueuedAnimationLoop = loop;
|
|
|
|
|
stringToLower(enqueuedAnimation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float AnimationLayer::transitionAnimate(std::string anim, float time, int loop)
|
|
|
|
|
{
|
2017-01-12 21:51:46 +00:00
|
|
|
|
stringToLower(anim);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
float totalTime =0;
|
2022-11-16 21:46:56 +00:00
|
|
|
|
if(Animation *a = this->s->getAnimation(anim))
|
|
|
|
|
{
|
|
|
|
|
if (time <= 0) // no transition?
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2022-11-16 21:46:56 +00:00
|
|
|
|
animate(anim, loop);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
createTransitionAnimation(*a, time);
|
|
|
|
|
timeMultiplier = 1;
|
|
|
|
|
|
|
|
|
|
currentAnimation = -1;
|
|
|
|
|
this->loop = 0;
|
|
|
|
|
timer = 0;
|
|
|
|
|
animating = 1;
|
|
|
|
|
animationLength = getCurrentAnimation()->getAnimationLength();
|
|
|
|
|
enqueueAnimation(anim, loop);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2022-11-16 21:46:56 +00:00
|
|
|
|
if (loop > -1)
|
|
|
|
|
totalTime = a->getAnimationLength()*(loop+1) + time;
|
|
|
|
|
else
|
|
|
|
|
totalTime = a->getAnimationLength() + time;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
return totalTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::setSkeletalSprite(SkeletalSprite *s)
|
|
|
|
|
{
|
|
|
|
|
this->s = s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Animation* AnimationLayer::getCurrentAnimation()
|
|
|
|
|
{
|
|
|
|
|
if (currentAnimation == -1)
|
|
|
|
|
return &blendAnimation;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
if (currentAnimation >= s->animations.size())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "skel: " << s->filenameLoaded << " currentAnimation: " << currentAnimation << " is out of range\n error in anim file?";
|
2013-06-23 16:50:10 +00:00
|
|
|
|
exit_error(os.str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return &s->animations[currentAnimation];
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 21:46:56 +00:00
|
|
|
|
void AnimationLayer::createTransitionAnimation(Animation& to, float time)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
blendAnimation.keyframes.clear();
|
|
|
|
|
SkeletalKeyframe k;
|
|
|
|
|
k.t = 0;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < s->bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
BoneKeyframe b;
|
|
|
|
|
b.idx = s->bones[i]->boneIdx;
|
|
|
|
|
b.x = s->bones[i]->position.x;
|
|
|
|
|
b.y = s->bones[i]->position.y;
|
|
|
|
|
b.rot = s->bones[i]->rotation.z;
|
|
|
|
|
b.sx = s->bones[i]->scale.x;
|
|
|
|
|
b.sy = s->bones[i]->scale.y;
|
|
|
|
|
k.keyframes.push_back(b);
|
|
|
|
|
}
|
|
|
|
|
blendAnimation.keyframes.push_back(k);
|
|
|
|
|
|
|
|
|
|
SkeletalKeyframe k2;
|
2022-11-16 21:46:56 +00:00
|
|
|
|
k2 = *to.getKeyframe(0);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
k2.t = time;
|
|
|
|
|
blendAnimation.keyframes.push_back(k2);
|
|
|
|
|
|
2022-11-16 21:46:56 +00:00
|
|
|
|
blendAnimation.name = to.name;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::stopAnimation()
|
|
|
|
|
{
|
2023-12-21 05:04:09 +00:00
|
|
|
|
if(s->loaded && getCurrentAnimation()->resetOnEnd)
|
2016-04-17 12:33:23 +00:00
|
|
|
|
resetPass();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
animating = false;
|
|
|
|
|
if (!enqueuedAnimation.empty())
|
|
|
|
|
{
|
|
|
|
|
animate(enqueuedAnimation, enqueuedAnimationLoop);
|
|
|
|
|
enqueuedAnimation = "";
|
2013-02-03 15:13:07 +00:00
|
|
|
|
enqueuedAnimationLoop = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AnimationLayer::isAnimating()
|
|
|
|
|
{
|
|
|
|
|
return animating;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float AnimationLayer::getAnimationLength()
|
|
|
|
|
{
|
|
|
|
|
return animationLength;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-17 12:33:23 +00:00
|
|
|
|
Animation::Animation()
|
2023-12-21 05:04:09 +00:00
|
|
|
|
: resetOnEnd(true)
|
2016-04-17 12:33:23 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t Animation::getNumKeyframes()
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
return keyframes.size();
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
SkeletalKeyframe *Animation::getKeyframe(size_t key)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
if (key >= keyframes.size()) return 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
return &keyframes[key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Animation::reverse()
|
|
|
|
|
{
|
|
|
|
|
Keyframes copy = keyframes;
|
|
|
|
|
Keyframes copy2 = keyframes;
|
|
|
|
|
keyframes.clear();
|
|
|
|
|
int sz = copy.size()-1;
|
|
|
|
|
for (int i = sz; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
keyframes.push_back(copy[i]);
|
|
|
|
|
keyframes[keyframes.size()-1].t = copy2[sz-i].t;
|
|
|
|
|
}
|
|
|
|
|
reorderKeyframes();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Animation::getAnimationLength()
|
|
|
|
|
{
|
|
|
|
|
return getLastKeyframe()->t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkeletalKeyframe *Animation::getLastKeyframe()
|
|
|
|
|
{
|
|
|
|
|
if (!keyframes.empty())
|
|
|
|
|
return &keyframes[keyframes.size()-1];
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkeletalKeyframe *Animation::getFirstKeyframe()
|
|
|
|
|
{
|
|
|
|
|
if (!keyframes.empty())
|
|
|
|
|
return &keyframes[0];
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Animation::reorderKeyframes()
|
|
|
|
|
{
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < keyframes.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t j = 0; j < keyframes.size()-1; j++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (keyframes[j].t > keyframes[j+1].t)
|
|
|
|
|
{
|
|
|
|
|
SkeletalKeyframe temp = keyframes[j+1];
|
|
|
|
|
keyframes[j+1] = keyframes[j];
|
|
|
|
|
keyframes[j] = temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
void Animation::cloneKey(size_t key, float toffset)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
std::vector<SkeletalKeyframe> copy = this->keyframes;
|
|
|
|
|
keyframes.clear();
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t i = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
for (i = 0; i <= key; i++)
|
|
|
|
|
keyframes.push_back(copy[i]);
|
|
|
|
|
for (i = key; i < copy.size(); i++)
|
|
|
|
|
keyframes.push_back(copy[i]);
|
|
|
|
|
keyframes[key+1].t += toffset;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
void Animation::deleteKey(size_t key)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
std::vector<SkeletalKeyframe> copy = this->keyframes;
|
|
|
|
|
keyframes.clear();
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t i = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
for (i = 0; i < key; i++)
|
|
|
|
|
keyframes.push_back(copy[i]);
|
|
|
|
|
for (i = key+1; i < copy.size(); i++)
|
|
|
|
|
keyframes.push_back(copy[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t Animation::getSkeletalKeyframeIndex(SkeletalKeyframe *skey)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < keyframes.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (&keyframes[i] == skey)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-25 14:58:08 +00:00
|
|
|
|
return 0;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
BoneKeyframe *SkeletalKeyframe::getBoneKeyframe(size_t idx)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < keyframes.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (keyframes[i].idx == idx)
|
|
|
|
|
{
|
|
|
|
|
return &keyframes[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkeletalKeyframe *Animation::getPrevKeyframe(float t)
|
|
|
|
|
{
|
2017-01-14 18:22:37 +00:00
|
|
|
|
size_t kf = -1;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = keyframes.size(); i-- > 0; )
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (t >= keyframes[i].t)
|
|
|
|
|
{
|
|
|
|
|
kf = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (kf == -1)
|
|
|
|
|
return 0;
|
|
|
|
|
if (kf >= keyframes.size())
|
|
|
|
|
kf = keyframes.size()-1;
|
|
|
|
|
return &keyframes[kf];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkeletalKeyframe *Animation::getNextKeyframe(float t)
|
|
|
|
|
{
|
2017-01-14 18:22:37 +00:00
|
|
|
|
size_t kf = -1;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < keyframes.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (t <= keyframes[i].t)
|
|
|
|
|
{
|
|
|
|
|
kf = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (kf == -1)
|
|
|
|
|
return 0;
|
|
|
|
|
if (kf >= keyframes.size())
|
|
|
|
|
kf = keyframes.size()-1;
|
|
|
|
|
return &keyframes[kf];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SkeletalSprite::SkeletalSprite() : RenderObject()
|
|
|
|
|
{
|
|
|
|
|
frozen = false;
|
|
|
|
|
animKeyNotify = 0;
|
|
|
|
|
loaded = false;
|
|
|
|
|
animLayers.resize(10);
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < animLayers.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
animLayers[i].setSkeletalSprite(this);
|
|
|
|
|
selectedBone = -1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 01:27:15 +00:00
|
|
|
|
SkeletalSprite::~SkeletalSprite()
|
|
|
|
|
{
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2022-07-18 21:00:22 +00:00
|
|
|
|
void SkeletalSprite::destroy()
|
|
|
|
|
{
|
|
|
|
|
bones.clear(); // they are added as children too, so the next call will do the actual deletion
|
|
|
|
|
RenderObject::destroy();
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
void SkeletalSprite::setAnimationKeyNotify(RenderObject *r)
|
|
|
|
|
{
|
|
|
|
|
animKeyNotify = r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::animate(const std::string &animation, int loop, int layer)
|
|
|
|
|
{
|
|
|
|
|
animLayers[layer].animate(animation, loop);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-13 23:58:59 +00:00
|
|
|
|
float SkeletalSprite::transitionAnimate(const std::string& anim, float time, int loop, int layer)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
AnimationLayer *animLayer = getAnimationLayer(layer);
|
|
|
|
|
if (animLayer)
|
|
|
|
|
return animLayer->transitionAnimate(anim, time, loop);
|
|
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "playing animation on invalid layer: " << layer;
|
|
|
|
|
errorLog(os.str());
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
AnimationLayer* SkeletalSprite::getAnimationLayer(size_t l)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
if (l < animLayers.size())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
return &animLayers[l];
|
|
|
|
|
}
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "couldn't get animLayer: " << l;
|
|
|
|
|
debugLog(os.str());
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SkeletalSprite::isLoaded()
|
|
|
|
|
{
|
|
|
|
|
return loaded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::onUpdate(float dt)
|
|
|
|
|
{
|
|
|
|
|
if (frozen) return;
|
|
|
|
|
RenderObject::onUpdate(dt);
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t i = 0;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < bones.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Bone *b = bones[i];
|
|
|
|
|
if (b && !b->collisionMask.empty())
|
|
|
|
|
{
|
|
|
|
|
if (b->collisionMask.size() != b->transformedCollisionMask.size())
|
|
|
|
|
{
|
|
|
|
|
b->transformedCollisionMask.resize(b->collisionMask.size());
|
|
|
|
|
}
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < b->collisionMask.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
b->transformedCollisionMask[i] = b->getWorldCollidePosition(b->collisionMask[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
for (i = 0; i < animLayers.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
animLayers[i].update(dt);
|
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimationLayer::update(float dt)
|
|
|
|
|
{
|
|
|
|
|
timeMultiplier.update(dt);
|
|
|
|
|
if (animating)
|
|
|
|
|
{
|
|
|
|
|
timer += dt*timeMultiplier.x;
|
|
|
|
|
|
2022-11-16 21:46:56 +00:00
|
|
|
|
if (timer >= animationLength)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
float leftover;
|
|
|
|
|
if (animationLength > 0)
|
|
|
|
|
leftover = fmodf(timer, animationLength);
|
|
|
|
|
else
|
|
|
|
|
leftover = 0;
|
|
|
|
|
timer = animationLength;
|
|
|
|
|
if (loop==-1 || loop > 0)
|
|
|
|
|
{
|
|
|
|
|
playAnimation(this->currentAnimation, loop);
|
|
|
|
|
if (loop > 0)
|
|
|
|
|
loop --;
|
|
|
|
|
timer = leftover;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
stopAnimation();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
updateBones();
|
|
|
|
|
}
|
|
|
|
|
else if (!animating)
|
|
|
|
|
{
|
|
|
|
|
if (fallThru > 0)
|
|
|
|
|
{
|
|
|
|
|
fallThru -= dt * fallThruSpeed;
|
|
|
|
|
if (fallThru < 0)
|
|
|
|
|
fallThru = 0;
|
|
|
|
|
updateBones();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-28 03:42:02 +00:00
|
|
|
|
bool SkeletalSprite::saveSkeletal(const std::string &fn)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
std::string file, filename=fn;
|
|
|
|
|
stringToLower(filename);
|
|
|
|
|
|
|
|
|
|
if (!secondaryAnimationPath.empty())
|
2013-12-28 03:42:02 +00:00
|
|
|
|
{
|
|
|
|
|
createDir(secondaryAnimationPath);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
file = secondaryAnimationPath + filename + ".xml";
|
2013-12-28 03:42:02 +00:00
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
else
|
2013-12-28 03:42:02 +00:00
|
|
|
|
{
|
2011-08-03 20:05:33 +00:00
|
|
|
|
file = animationPath + filename + ".xml";
|
2013-12-28 03:42:02 +00:00
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t i = 0;
|
2014-09-15 22:29:57 +00:00
|
|
|
|
XMLDocument *xml = _retrieveSkeletalXML(file, true);
|
2014-06-08 20:11:23 +00:00
|
|
|
|
xml->Clear();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animationLayers = xml->NewElement("AnimationLayers");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
for (i = 0; i < animLayers.size(); i++)
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animationLayer = xml->NewElement("AnimationLayer");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (animLayers[i].ignoreBones.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t j = 0; j < animLayers[i].ignoreBones.size(); j++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
os << animLayers[i].ignoreBones[j] << " ";
|
|
|
|
|
}
|
2014-06-08 20:11:23 +00:00
|
|
|
|
animationLayer->SetAttribute("ignore", os.str().c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (animLayers[i].includeBones.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t j = 0; j < animLayers[i].includeBones.size(); j++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
os << animLayers[i].includeBones[j] << " ";
|
|
|
|
|
}
|
2014-06-08 20:11:23 +00:00
|
|
|
|
animationLayer->SetAttribute("include", os.str().c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (!animLayers[i].name.empty())
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
animationLayer->SetAttribute("name", animLayers[i].name.c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
animationLayers->InsertEndChild(animationLayer);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2014-06-08 20:11:23 +00:00
|
|
|
|
xml->InsertEndChild(animationLayers);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *bones = xml->NewElement("Bones");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
for (i = 0; i < this->bones.size(); i++)
|
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
|
const DynamicRenderGrid * const grid = this->bones[i]->getGrid();
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *bone = xml->NewElement("Bone");
|
2017-01-14 17:10:20 +00:00
|
|
|
|
bone->SetAttribute("idx", (unsigned int) this->bones[i]->boneIdx);
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("gfx", this->bones[i]->gfx.c_str());
|
|
|
|
|
bone->SetAttribute("pidx", this->bones[i]->pidx);
|
|
|
|
|
bone->SetAttribute("name", this->bones[i]->name.c_str());
|
2023-12-21 05:04:09 +00:00
|
|
|
|
bone->SetAttribute("fh", this->bones[i]->originalFH);
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("fv", this->bones[i]->isfv());
|
|
|
|
|
bone->SetAttribute("gc", this->bones[i]->generateCollisionMask);
|
|
|
|
|
bone->SetAttribute("cr", this->bones[i]->collideRadius);
|
2022-05-17 23:18:27 +00:00
|
|
|
|
if(!this->bones[i]->enableCollision)
|
|
|
|
|
bone->SetAttribute("c", this->bones[i]->enableCollision);
|
2019-09-06 00:56:53 +00:00
|
|
|
|
if (!this->bones[i]->fileRenderQuad)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("rq", this->bones[i]->fileRenderQuad);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2016-04-18 20:08:36 +00:00
|
|
|
|
if (!this->bones[i]->selectable)
|
|
|
|
|
{
|
|
|
|
|
bone->SetAttribute("sel", this->bones[i]->selectable);
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (this->bones[i]->rbp)
|
2017-01-19 22:44:30 +00:00
|
|
|
|
bone->SetAttribute("rbp", (int)this->bones[i]->rbp);
|
2016-04-17 12:33:23 +00:00
|
|
|
|
if (this->bones[i]->originalRenderPass)
|
|
|
|
|
bone->SetAttribute("pass", this->bones[i]->originalRenderPass);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (this->bones[i]->offset.x)
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("offx", this->bones[i]->offset.x);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (this->bones[i]->offset.y)
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("offy", this->bones[i]->offset.y);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (!this->bones[i]->prt.empty())
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("prt", this->bones[i]->prt.c_str());
|
2023-07-10 15:23:19 +00:00
|
|
|
|
if(grid && grid->gridType != GRID_STRIP)
|
2022-09-23 16:10:44 +00:00
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
2023-07-10 15:23:19 +00:00
|
|
|
|
os << grid->width() << " " << grid->height();
|
2022-09-23 16:10:44 +00:00
|
|
|
|
bone->SetAttribute("grid", os.str().c_str());
|
|
|
|
|
}
|
|
|
|
|
else if (!this->bones[i]->changeStrip.empty())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << this->bones[i]->stripVert << " " << this->bones[i]->changeStrip.size();
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("strip", os.str().c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (!this->bones[i]->internalOffset.isZero())
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << this->bones[i]->internalOffset.x << " " << this->bones[i]->internalOffset.y;
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("io", os.str().c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (this->bones[i]->isRepeatingTextureToFill())
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("rt", 1);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (this->bones[i]->originalScale.x != 1 || this->bones[i]->originalScale.y != 1)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << this->bones[i]->originalScale.x << " " << this->bones[i]->originalScale.y;
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bone->SetAttribute("sz", os.str().c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2023-07-10 15:23:19 +00:00
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
|
if(grid && grid->getDrawOrder() != GRID_DRAW_DEFAULT)
|
2022-09-25 02:30:38 +00:00
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
|
bone->SetAttribute("gridDrawOrder", (int)grid->getDrawOrder());
|
2022-09-25 02:30:38 +00:00
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2023-12-21 05:04:09 +00:00
|
|
|
|
for(size_t j = 0; j < this->bones[i]->framegfx.size(); ++j)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2023-12-21 05:04:09 +00:00
|
|
|
|
XMLElement *frame = xml->NewElement("Frame");
|
|
|
|
|
frame->SetAttribute("gfx", this->bones[i]->framegfx[j].c_str());
|
|
|
|
|
bone->InsertEndChild(frame);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2014-06-08 20:11:23 +00:00
|
|
|
|
bones->InsertEndChild(bone);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2014-06-08 20:11:23 +00:00
|
|
|
|
xml->InsertEndChild(bones);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animations = xml->NewElement("Animations");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
for (i = 0; i < this->animations.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Animation *a = &this->animations[i];
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animation = xml->NewElement("Animation");
|
|
|
|
|
animation->SetAttribute("name", a->name.c_str());
|
2023-12-21 05:04:09 +00:00
|
|
|
|
if(!a->resetOnEnd)
|
|
|
|
|
animation->SetAttribute("resetOnEnd", a->resetOnEnd);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
|
|
|
|
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);
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid *grid = bone->getGrid();
|
|
|
|
|
assert(grid && grid->gridType == GRID_INTERP);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
}
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t j = 0; j < a->keyframes.size(); j++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *key = xml->NewElement("Key");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (!a->keyframes[j].sound.empty())
|
2014-06-08 20:11:23 +00:00
|
|
|
|
key->SetAttribute("sound", a->keyframes[j].sound.c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (!a->keyframes[j].cmd.empty())
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
key->SetAttribute("cmd", a->keyframes[j].cmd.c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (a->keyframes[j].lerpType != 0)
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
key->SetAttribute("lerp", a->keyframes[j].lerpType);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << a->keyframes[j].t << " ";
|
|
|
|
|
std::ostringstream szos;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t k = 0; k < a->keyframes[j].keyframes.size(); k++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
BoneKeyframe *b = &a->keyframes[j].keyframes[k];
|
2022-09-23 16:10:44 +00:00
|
|
|
|
Bone *bone = this->getBoneByIdx(b->idx);
|
2022-10-12 16:59:25 +00:00
|
|
|
|
if(bone)
|
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
|
const DynamicRenderGrid * const bgrid = bone->getGrid();
|
2022-10-12 16:59:25 +00:00
|
|
|
|
os << b->idx << " " << b->x << " " << b->y << " " << b->rot << " ";
|
|
|
|
|
// don't want to store grid points if they can be regenerated automatically
|
2023-07-10 15:23:19 +00:00
|
|
|
|
size_t usedGridSize = (!bgrid || bgrid->gridType == GRID_INTERP) ? 0 : b->grid.size();
|
2022-10-12 16:59:25 +00:00
|
|
|
|
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 << " ";
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
std::string szoss = szos.str();
|
|
|
|
|
if (!szoss.empty())
|
2014-06-08 20:11:23 +00:00
|
|
|
|
key->SetAttribute("sz", szoss.c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
key->SetAttribute("e", os.str().c_str());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
animation->InsertEndChild(key);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2014-06-08 20:11:23 +00:00
|
|
|
|
animations->InsertEndChild(animation);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
xml->InsertEndChild(animations);
|
2014-06-10 00:18:55 +00:00
|
|
|
|
return xml->SaveFile(file.c_str()) == XML_SUCCESS;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
size_t SkeletalSprite::getBoneIdx(Bone *b)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (bones[i] == b)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
void SkeletalSprite::toggleBone(size_t idx, int v)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
if (idx < bones.size())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
bones[idx]->alpha.x = v;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bone *SkeletalSprite::getBoneByName(const std::string &name)
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (bones[i]->name == name)
|
|
|
|
|
return bones[i];
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
Bone *SkeletalSprite::getBoneByIdx(size_t idx)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (bones[i]->boneIdx == idx)
|
|
|
|
|
return bones[i];
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-19 22:44:30 +00:00
|
|
|
|
Bone *SkeletalSprite::initBone(int idx, std::string gfx, int pidx, bool rbp, std::string name, float cr, bool fh, bool fv)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
Bone *b = new Bone;
|
|
|
|
|
b->boneIdx = idx;
|
|
|
|
|
b->setTexture(gfx);
|
|
|
|
|
b->skeleton = this;
|
|
|
|
|
b->gfx = gfx;
|
|
|
|
|
b->rbp = rbp;
|
|
|
|
|
b->renderBeforeParent = rbp;
|
|
|
|
|
b->pidx = pidx;
|
|
|
|
|
b->collideRadius = cr;
|
|
|
|
|
b->name = name;
|
2023-12-21 05:04:09 +00:00
|
|
|
|
b->originalFH = fh;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (fh)
|
|
|
|
|
b->flipHorizontal();
|
|
|
|
|
if (fv)
|
|
|
|
|
b->flipVertical();
|
|
|
|
|
bones.push_back(b);
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::firstAnimation()
|
|
|
|
|
{
|
|
|
|
|
stopAnimation();
|
|
|
|
|
animLayers[0].currentAnimation = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-18 00:52:46 +00:00
|
|
|
|
bool SkeletalSprite::selectAnimation(const char* name)
|
|
|
|
|
{
|
|
|
|
|
for(size_t i = 0; i < animations.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
if(animations[i].name == name)
|
|
|
|
|
{
|
|
|
|
|
stopAnimation();
|
|
|
|
|
animLayers[0].currentAnimation = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
void SkeletalSprite::lastAnimation()
|
|
|
|
|
{
|
|
|
|
|
stopAnimation();
|
|
|
|
|
animLayers[0].currentAnimation = animations.size()-1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::nextAnimation()
|
|
|
|
|
{
|
|
|
|
|
stopAnimation();
|
|
|
|
|
animLayers[0].currentAnimation++;
|
|
|
|
|
if (animLayers[0].currentAnimation >= animations.size())
|
|
|
|
|
animLayers[0].currentAnimation = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::prevAnimation()
|
|
|
|
|
{
|
|
|
|
|
stopAnimation();
|
|
|
|
|
animLayers[0].currentAnimation--;
|
2017-01-14 17:23:53 +00:00
|
|
|
|
if (animLayers[0].currentAnimation >= animations.size())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
animLayers[0].currentAnimation = animations.size()-1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::deleteBones()
|
|
|
|
|
{
|
|
|
|
|
bones.clear();
|
2014-07-21 20:21:22 +00:00
|
|
|
|
for(Children::iterator it = children.begin(); it != children.end(); ++it)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2014-07-21 20:21:22 +00:00
|
|
|
|
(*it)->safeKill();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-03 15:13:07 +00:00
|
|
|
|
Animation *SkeletalSprite::getAnimation(const std::string& anim)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < animations.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (animations[i].name == anim)
|
|
|
|
|
return &animations[i];
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::loadSkin(const std::string &fn)
|
|
|
|
|
{
|
|
|
|
|
std::string file;
|
|
|
|
|
|
|
|
|
|
if (!secondaryAnimationPath.empty())
|
|
|
|
|
{
|
|
|
|
|
file = secondaryAnimationPath + skinPath + fn + ".xml";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file.empty() || !exists(file, false))
|
|
|
|
|
{
|
|
|
|
|
file = animationPath + skinPath + fn + ".xml";
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
|
file = adjustFilenameCase(file);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2013-02-27 00:54:38 +00:00
|
|
|
|
if (!exists(file))
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2014-09-15 22:29:57 +00:00
|
|
|
|
errorLog("Could not load skin[" + file + "] - File not found.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
XMLDocument *d = _retrieveSkeletalXML(file, false);
|
|
|
|
|
if(!d)
|
|
|
|
|
{
|
|
|
|
|
errorLog("Could not load skin[" + file + "] - Malformed XML.");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *bonesXml = d->FirstChildElement("Bones");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (bonesXml)
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *boneXml = bonesXml->FirstChildElement("Bone");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
while (boneXml)
|
|
|
|
|
{
|
|
|
|
|
int idx = atoi(boneXml->Attribute("idx"));
|
|
|
|
|
Bone *b = getBoneByIdx(idx);
|
|
|
|
|
if (b)
|
|
|
|
|
{
|
|
|
|
|
if (boneXml->Attribute("rq"))
|
|
|
|
|
{
|
|
|
|
|
int rq = atoi(boneXml->Attribute("rq"));
|
2017-01-19 22:44:30 +00:00
|
|
|
|
b->renderQuad = !!rq;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string gfx;
|
|
|
|
|
if (boneXml->Attribute("gfx"))
|
|
|
|
|
{
|
|
|
|
|
gfx = boneXml->Attribute("gfx");
|
|
|
|
|
if (!gfx.empty())
|
|
|
|
|
{
|
|
|
|
|
b->gfx = gfx;
|
|
|
|
|
b->setTexture(b->gfx);
|
|
|
|
|
b->renderQuad = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gfx.empty())
|
|
|
|
|
{
|
|
|
|
|
b->renderQuad = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (boneXml->Attribute("fh"))
|
|
|
|
|
{
|
|
|
|
|
int fh = atoi(boneXml->Attribute("fh"));
|
|
|
|
|
if (fh)
|
|
|
|
|
b->flipHorizontal();
|
|
|
|
|
}
|
|
|
|
|
if (boneXml->Attribute("fv"))
|
|
|
|
|
{
|
|
|
|
|
int fv = atoi(boneXml->Attribute("fv"));
|
|
|
|
|
if (fv)
|
|
|
|
|
b->flipVertical();
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "SkinLoad: Could not find idx[" << idx << "]";
|
|
|
|
|
debugLog(os.str());
|
|
|
|
|
}
|
|
|
|
|
boneXml = boneXml->NextSiblingElement("Bone");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::stopAnimation(int layer)
|
|
|
|
|
{
|
2017-04-19 00:05:57 +00:00
|
|
|
|
if(size_t(layer) < animLayers.size())
|
|
|
|
|
animLayers[layer].stopAnimation();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::stopAllAnimations()
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < animLayers.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
animLayers[i].stopAnimation();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::playCurrentAnimation(int loop, int layer)
|
|
|
|
|
{
|
2017-04-19 00:05:57 +00:00
|
|
|
|
if(size_t(layer) < animLayers.size())
|
|
|
|
|
animLayers[layer].playCurrentAnimation(loop);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::loadSkeletal(const std::string &fn)
|
|
|
|
|
{
|
|
|
|
|
filenameLoaded = "";
|
|
|
|
|
loaded = false;
|
|
|
|
|
stopAnimation();
|
2015-06-03 02:04:19 +00:00
|
|
|
|
animLayers.clear();
|
2011-08-03 20:05:33 +00:00
|
|
|
|
deleteBones();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
filenameLoaded = fn;
|
|
|
|
|
stringToLower(filenameLoaded);
|
|
|
|
|
|
|
|
|
|
std::string file;
|
|
|
|
|
|
|
|
|
|
if (!secondaryAnimationPath.empty())
|
|
|
|
|
{
|
|
|
|
|
file = secondaryAnimationPath + filenameLoaded + ".xml";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file.empty() || !exists(file, false))
|
|
|
|
|
{
|
|
|
|
|
file = animationPath + filenameLoaded + ".xml";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!exists(file))
|
|
|
|
|
{
|
|
|
|
|
filenameLoaded = "";
|
2014-06-09 21:39:33 +00:00
|
|
|
|
errorLog("Could not load skeletal[" + file + "] - File not found.");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-09 02:18:40 +00:00
|
|
|
|
file = adjustFilenameCase(file);
|
2012-05-25 16:23:30 +00:00
|
|
|
|
|
2014-09-15 22:29:57 +00:00
|
|
|
|
XMLDocument *xml = _retrieveSkeletalXML(file, false);
|
2014-06-09 21:39:33 +00:00
|
|
|
|
if(!xml)
|
|
|
|
|
{
|
|
|
|
|
filenameLoaded = "";
|
|
|
|
|
errorLog("Could not load skeletal[" + file + "] - Malformed XML.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loaded = true;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *bones = xml->FirstChildElement("Bones");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (bones)
|
|
|
|
|
{
|
|
|
|
|
if (bones->Attribute("scale"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bones->Attribute("scale"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> scale.x >> scale.y;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *bone = bones->FirstChildElement("Bone");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
while(bone)
|
|
|
|
|
{
|
|
|
|
|
int idx = atoi(bone->Attribute("idx"));
|
|
|
|
|
int pidx = -1, rbp=0, cr=0, fh=0, fv=0;
|
|
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
|
if (bone->Attribute("pidx"))
|
|
|
|
|
pidx = atoi(bone->Attribute("pidx"));
|
|
|
|
|
if (bone->Attribute("rbp"))
|
|
|
|
|
rbp = atoi(bone->Attribute("rbp"));
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("name"))
|
|
|
|
|
name = bone->Attribute("name");
|
|
|
|
|
if (bone->Attribute("cr"))
|
|
|
|
|
cr = atoi(bone->Attribute("cr"));
|
|
|
|
|
if (bone->Attribute("fh"))
|
|
|
|
|
fh = atoi(bone->Attribute("fh"));
|
|
|
|
|
if (bone->Attribute("fv"))
|
|
|
|
|
fv = atoi(bone->Attribute("fv"));
|
|
|
|
|
|
|
|
|
|
std::string gfx = bone->Attribute("gfx");
|
2016-05-06 22:47:45 +00:00
|
|
|
|
Bone *newb = initBone(idx, gfx, pidx, rbp, name, cr, fh, fv);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (bone->Attribute("offx"))
|
|
|
|
|
newb->offset.x = atoi(bone->Attribute("offx"));
|
|
|
|
|
if (bone->Attribute("offy"))
|
|
|
|
|
newb->offset.y = atoi(bone->Attribute("offy"));
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("prt"))
|
|
|
|
|
{
|
|
|
|
|
newb->prt = bone->Attribute("prt");
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(newb->prt.c_str(), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
int slot;
|
2016-07-09 02:18:40 +00:00
|
|
|
|
std::string pfile;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
while (is >> slot)
|
|
|
|
|
{
|
2016-08-02 00:44:50 +00:00
|
|
|
|
if(slot < 0)
|
2016-07-09 02:18:40 +00:00
|
|
|
|
{
|
2016-08-02 00:44:50 +00:00
|
|
|
|
errorLog("particle slot < 0");
|
|
|
|
|
break;
|
2016-07-09 02:18:40 +00:00
|
|
|
|
}
|
2016-08-02 00:44:50 +00:00
|
|
|
|
is >> pfile;
|
|
|
|
|
// add particle system + load
|
|
|
|
|
ParticleEffect *e = new ParticleEffect;
|
|
|
|
|
if(newb->emitters.size() <= (size_t)slot)
|
|
|
|
|
newb->emitters.resize(slot+4, NULL);
|
|
|
|
|
newb->emitters[slot] = e;
|
|
|
|
|
newb->addChild(e, PM_POINTER);
|
|
|
|
|
e->load(pfile);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bone->Attribute("pass"))
|
|
|
|
|
{
|
2016-04-17 12:33:23 +00:00
|
|
|
|
int pass = atoi(bone->Attribute("pass"));
|
|
|
|
|
newb->originalRenderPass = pass;
|
|
|
|
|
newb->setRenderPass(pass);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
if (bone->Attribute("gc"))
|
|
|
|
|
{
|
|
|
|
|
newb->generateCollisionMask = atoi(bone->Attribute("gc"));
|
|
|
|
|
}
|
2022-05-17 23:18:27 +00:00
|
|
|
|
if (bone->Attribute("c"))
|
|
|
|
|
{
|
|
|
|
|
newb->enableCollision = atoi(bone->Attribute("c"));
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (bone->Attribute("rq"))
|
|
|
|
|
{
|
|
|
|
|
newb->renderQuad = newb->fileRenderQuad = atoi(bone->Attribute("rq"));
|
|
|
|
|
}
|
|
|
|
|
if (bone->Attribute("io"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bone->Attribute("io"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> newb->internalOffset.x >> newb->internalOffset.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("strip"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bone->Attribute("strip"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
bool vert;
|
|
|
|
|
int num;
|
|
|
|
|
is >> vert >> num;
|
|
|
|
|
newb->createStrip(vert, num);
|
|
|
|
|
}
|
|
|
|
|
if (bone->Attribute("sz"))
|
|
|
|
|
{
|
|
|
|
|
float sx, sy;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bone->Attribute("sz"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> sx >> sy;
|
|
|
|
|
|
|
|
|
|
newb->scale = newb->originalScale = Vector(sx,sy);
|
|
|
|
|
}
|
|
|
|
|
if (bone->Attribute("rt"))
|
|
|
|
|
{
|
|
|
|
|
newb->repeatTextureToFill(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("blend"))
|
|
|
|
|
{
|
|
|
|
|
//if (bone->Attribute("blend")=="add")
|
2022-05-18 23:34:31 +00:00
|
|
|
|
newb->setBlendType(BLEND_ADD);
|
|
|
|
|
//this->setBlendType(BLEND_ADD); // FIXME: seems wrong to do this here -- fg
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("alpha"))
|
|
|
|
|
{
|
|
|
|
|
float a=1.0;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bone->Attribute("alpha"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> a;
|
|
|
|
|
newb->alpha = a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("alphaMod"))
|
|
|
|
|
{
|
|
|
|
|
float a=1.0;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bone->Attribute("alphaMod"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> a;
|
|
|
|
|
newb->alphaMod = a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("segs"))
|
|
|
|
|
{
|
|
|
|
|
int x, y;
|
|
|
|
|
float dgox, dgoy, dgmx, dgmy, dgtm;
|
|
|
|
|
bool dgo;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(bone->Attribute("segs"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> x >> y >> dgox >> dgoy >> dgmx >> dgmy >> dgtm >> dgo;
|
|
|
|
|
newb->setSegs(x, y, dgox, dgoy, dgmx, dgmy, dgtm, dgo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bone->Attribute("color"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream in(bone->Attribute("color"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
in >> newb->color.x >> newb->color.y >> newb->color.z;
|
|
|
|
|
}
|
2016-04-18 20:08:36 +00:00
|
|
|
|
if (bone->Attribute("sel"))
|
|
|
|
|
{
|
|
|
|
|
newb->selectable = bone->BoolAttribute("sel");
|
|
|
|
|
}
|
2022-09-05 15:19:34 +00:00
|
|
|
|
if (bone->Attribute("grid"))
|
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid *grid = newb->getGrid();
|
2023-07-10 15:23:19 +00:00
|
|
|
|
if(!grid)
|
2022-09-23 16:10:44 +00:00
|
|
|
|
{
|
|
|
|
|
SimpleIStringStream is(bone->Attribute("grid"), SimpleIStringStream::REUSE);
|
|
|
|
|
int x, y;
|
|
|
|
|
is >> x >> y;
|
2023-07-10 15:23:19 +00:00
|
|
|
|
grid = newb->createGrid(x, y);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "Bone idx " << newb->idx << " already has a DrawGrid, ignoring \"grid\" attribute";
|
|
|
|
|
errorLog(os.str());
|
|
|
|
|
}
|
2023-07-10 15:23:19 +00:00
|
|
|
|
if(const char *gdo = bone->Attribute("gridDrawOrder"))
|
|
|
|
|
{
|
|
|
|
|
int ord = atoi(gdo);
|
2023-08-09 00:41:04 +00:00
|
|
|
|
grid->setDrawOrder((GridDrawOrder)ord);
|
2023-07-10 15:23:19 +00:00
|
|
|
|
}
|
2022-09-25 02:30:38 +00:00
|
|
|
|
}
|
2023-12-29 21:43:05 +00:00
|
|
|
|
if(XMLElement *fr = bone->FirstChildElement("Frame"))
|
|
|
|
|
{
|
|
|
|
|
int frc = 0;
|
|
|
|
|
while(fr)
|
|
|
|
|
{
|
|
|
|
|
std::string gfx;
|
|
|
|
|
if (fr->Attribute("gfx"))
|
|
|
|
|
{
|
|
|
|
|
gfx = fr->Attribute("gfx");
|
|
|
|
|
newb->addFrame(gfx);
|
|
|
|
|
}
|
|
|
|
|
fr = fr->NextSiblingElement("Frame");
|
|
|
|
|
frc++;
|
|
|
|
|
}
|
|
|
|
|
if (frc)
|
|
|
|
|
{
|
|
|
|
|
newb->showFrame(0);
|
|
|
|
|
}
|
|
|
|
|
newb->renderQuad = true;
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
bone = bone->NextSiblingElement("Bone");
|
|
|
|
|
}
|
|
|
|
|
// attach bones
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < this->bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
Bone *b = this->bones[i];
|
|
|
|
|
if (b->pidx != -1)
|
|
|
|
|
{
|
|
|
|
|
Bone *pb = getBoneByIdx(b->pidx);
|
|
|
|
|
if (!pb)
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "Parent bone not found, index: " << b->pidx << " from bone idx: " << b->getIdx();
|
|
|
|
|
debugLog(os.str());
|
|
|
|
|
}
|
2022-09-23 16:10:44 +00:00
|
|
|
|
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());
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pb->addChild(b, PM_POINTER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
addChild(b, PM_POINTER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
animLayers.clear();
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animationLayers = xml->FirstChildElement("AnimationLayers");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (animationLayers)
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animationLayer = animationLayers->FirstChildElement("AnimationLayer");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
while (animationLayer)
|
|
|
|
|
{
|
|
|
|
|
AnimationLayer newAnimationLayer;
|
|
|
|
|
if (animationLayer->Attribute("ignore"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(animationLayer->Attribute("ignore"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
int t;
|
|
|
|
|
while (is >> t)
|
|
|
|
|
{
|
|
|
|
|
newAnimationLayer.ignoreBones.push_back(t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (animationLayer->Attribute("include"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(animationLayer->Attribute("include"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
int t;
|
|
|
|
|
while (is >> t)
|
|
|
|
|
{
|
|
|
|
|
newAnimationLayer.includeBones.push_back(t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (animationLayer->Attribute("name"))
|
|
|
|
|
{
|
|
|
|
|
newAnimationLayer.name = animationLayer->Attribute("name");
|
|
|
|
|
}
|
|
|
|
|
newAnimationLayer.setSkeletalSprite(this);
|
|
|
|
|
animLayers.push_back(newAnimationLayer);
|
|
|
|
|
animationLayer = animationLayer->NextSiblingElement("AnimationLayer");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
animations.clear();
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animations = xml->FirstChildElement("Animations");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
if (animations)
|
|
|
|
|
{
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *animation = animations->FirstChildElement("Animation");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
while(animation)
|
|
|
|
|
{
|
2024-07-04 19:40:31 +00:00
|
|
|
|
this->animations.push_back(Animation());
|
|
|
|
|
Animation& newAnimation = this->animations.back();
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
newAnimation.name = animation->Attribute("name");
|
2023-12-21 05:04:09 +00:00
|
|
|
|
if(animation->Attribute("resetOnEnd"))
|
|
|
|
|
newAnimation.resetOnEnd = animation->BoolAttribute("resetOnEnd");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
stringToLower(newAnimation.name);
|
|
|
|
|
|
2014-06-08 20:11:23 +00:00
|
|
|
|
XMLElement *key = animation->FirstChildElement("Key");
|
2011-08-03 20:05:33 +00:00
|
|
|
|
while (key)
|
|
|
|
|
{
|
|
|
|
|
SkeletalKeyframe newSkeletalKeyframe;
|
|
|
|
|
if (key->Attribute("e"))
|
|
|
|
|
{
|
|
|
|
|
float time;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(key->Attribute("e"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> time;
|
|
|
|
|
int idx, x, y, rot, strip;
|
|
|
|
|
newSkeletalKeyframe.t = time;
|
|
|
|
|
if (key->Attribute("sound"))
|
|
|
|
|
{
|
|
|
|
|
newSkeletalKeyframe.sound = key->Attribute("sound");
|
|
|
|
|
}
|
|
|
|
|
if (key->Attribute("lerp"))
|
|
|
|
|
{
|
|
|
|
|
newSkeletalKeyframe.lerpType = atoi(key->Attribute("lerp"));
|
|
|
|
|
}
|
|
|
|
|
while (is >> idx)
|
|
|
|
|
{
|
|
|
|
|
BoneKeyframe b;
|
|
|
|
|
is >> x >> y >> rot >> strip;
|
|
|
|
|
b.idx = idx;
|
|
|
|
|
b.x = x;
|
|
|
|
|
b.y = y;
|
|
|
|
|
b.rot = rot;
|
|
|
|
|
if (strip>0)
|
|
|
|
|
{
|
2022-09-13 16:38:44 +00:00
|
|
|
|
b.grid.resize(strip);
|
|
|
|
|
for (size_t i = 0; i < b.grid.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2022-09-13 16:38:44 +00:00
|
|
|
|
is >> b.grid[i].x >> b.grid[i].y;
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (key->Attribute("sz"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is2(key->Attribute("sz"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
int midx;
|
|
|
|
|
float bsx, bsy;
|
|
|
|
|
while (is2 >> midx)
|
|
|
|
|
{
|
|
|
|
|
is2 >> bsx >> bsy;
|
|
|
|
|
if (midx == idx)
|
|
|
|
|
{
|
|
|
|
|
b.doScale = true;
|
|
|
|
|
b.sx = bsx;
|
|
|
|
|
b.sy = bsy;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newSkeletalKeyframe.keyframes.push_back(b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (key->Attribute("d"))
|
|
|
|
|
{
|
|
|
|
|
float time;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(key->Attribute("d"), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
is >> time;
|
|
|
|
|
int idx, x, y, rot;
|
|
|
|
|
|
|
|
|
|
newSkeletalKeyframe.t = time;
|
|
|
|
|
if (key->Attribute("sound"))
|
|
|
|
|
{
|
|
|
|
|
newSkeletalKeyframe.sound = key->Attribute("sound");
|
|
|
|
|
}
|
|
|
|
|
while (is >> idx)
|
|
|
|
|
{
|
|
|
|
|
is >> x >> y >> rot;
|
|
|
|
|
BoneKeyframe b;
|
|
|
|
|
b.idx = idx;
|
|
|
|
|
b.x = x;
|
|
|
|
|
b.y = y;
|
|
|
|
|
b.rot = rot;
|
|
|
|
|
newSkeletalKeyframe.keyframes.push_back(b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (key->Attribute("cmd"))
|
|
|
|
|
{
|
|
|
|
|
newSkeletalKeyframe.cmd = key->Attribute("cmd");
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(newSkeletalKeyframe.cmd.c_str(), SimpleIStringStream::REUSE);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
int bidx;
|
|
|
|
|
while (is >> bidx)
|
|
|
|
|
{
|
|
|
|
|
Bone *b = this->getBoneByIdx(bidx);
|
|
|
|
|
if (b)
|
|
|
|
|
{
|
|
|
|
|
BoneCommand bcmd;
|
2016-04-17 13:16:55 +00:00
|
|
|
|
if(!bcmd.parse(b, is))
|
|
|
|
|
break;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
newSkeletalKeyframe.commands.push_back(bcmd);
|
|
|
|
|
}
|
2016-04-17 13:16:55 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "SkeletalSprite::loadSkeletal: File " << fn << " anim " << newAnimation.name << " specifies non-existing bone idx " << bidx;
|
|
|
|
|
errorLog(os.str());
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// generate empty bone keys
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < this->bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
Bone *bone = this->bones[i];
|
|
|
|
|
BoneKeyframe *bk = newSkeletalKeyframe.getBoneKeyframe(bone->boneIdx);
|
|
|
|
|
if(!bk)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
BoneKeyframe b;
|
2022-11-13 11:29:11 +00:00
|
|
|
|
b.idx = bone->boneIdx;
|
|
|
|
|
newSkeletalKeyframe.keyframes.push_back(b);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newAnimation.keyframes.push_back(newSkeletalKeyframe);
|
|
|
|
|
key = key->NextSiblingElement("Key");
|
|
|
|
|
}
|
2022-09-05 15:19:34 +00:00
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
// <Interpolator bone="name or idx" type="TYPE config and params" data="controlpoints; aded by editor" />
|
2024-07-04 19:40:31 +00:00
|
|
|
|
size_t numInterp = 0;
|
2022-09-05 15:19:34 +00:00
|
|
|
|
XMLElement *interp = animation->FirstChildElement("Interpolator");
|
2022-09-23 16:10:44 +00:00
|
|
|
|
for( ; interp; interp = interp->NextSiblingElement("Interpolator"))
|
2024-07-04 19:40:31 +00:00
|
|
|
|
++numInterp;
|
|
|
|
|
|
|
|
|
|
newAnimation.interpolators.resize(numInterp);
|
|
|
|
|
|
|
|
|
|
interp = animation->FirstChildElement("Interpolator");
|
|
|
|
|
for(numInterp = 0 ; interp; interp = interp->NextSiblingElement("Interpolator"), ++numInterp)
|
2022-09-05 15:19:34 +00:00
|
|
|
|
{
|
2022-09-13 16:38:44 +00:00
|
|
|
|
Bone *bi = NULL;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
const char *sbone = interp->Attribute("bone");
|
|
|
|
|
bool boneByIdx = false;
|
|
|
|
|
if(sbone)
|
|
|
|
|
{
|
|
|
|
|
bi = getBoneByName(sbone);
|
|
|
|
|
if(!bi)
|
|
|
|
|
{
|
|
|
|
|
bi = getBoneByIdx(atoi(sbone));
|
|
|
|
|
boneByIdx = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-13 16:38:44 +00:00
|
|
|
|
if(!bi)
|
2022-09-23 16:10:44 +00:00
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "Interpolator specifies non-existing bone [" << (sbone ? sbone : "(null)") << "]";
|
|
|
|
|
debugLog(os.str());
|
2022-09-13 16:38:44 +00:00
|
|
|
|
continue;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid *grid = bi->getGrid();
|
2023-07-10 15:23:19 +00:00
|
|
|
|
if(!grid)
|
2022-09-23 16:10:44 +00:00
|
|
|
|
{
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
os << "Interpolator specifies bone [" << bi->boneIdx << "] that has no grid";
|
|
|
|
|
debugLog(os.str());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 19:40:31 +00:00
|
|
|
|
SplineType spline = INTERPOLATOR_BSPLINE;
|
2023-07-10 15:23:19 +00:00
|
|
|
|
unsigned cx = 3, cy = 3, degx = 3, degy = 3;
|
2022-09-13 16:38:44 +00:00
|
|
|
|
if(const char *stype = interp->Attribute("type"))
|
|
|
|
|
{
|
2022-09-23 16:10:44 +00:00
|
|
|
|
SimpleIStringStream is(stype, SimpleIStringStream::REUSE);
|
2022-09-13 16:38:44 +00:00
|
|
|
|
std::string ty;
|
|
|
|
|
is >> ty;
|
|
|
|
|
if(ty == "bspline")
|
|
|
|
|
{
|
2024-07-04 19:40:31 +00:00
|
|
|
|
spline = INTERPOLATOR_BSPLINE;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
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;
|
2022-09-13 16:38:44 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-10 15:23:19 +00:00
|
|
|
|
grid->gridType = GRID_INTERP;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
// bone grid should have been created via <Bone grid=... /> earlier
|
|
|
|
|
|
2024-07-07 01:30:54 +00:00
|
|
|
|
|
2024-07-04 19:40:31 +00:00
|
|
|
|
BoneGridInterpolator& bgip = newAnimation.interpolators[numInterp];
|
2022-09-13 16:38:44 +00:00
|
|
|
|
bgip.idx = bi->boneIdx;
|
2022-09-23 16:10:44 +00:00
|
|
|
|
bgip.storeBoneByIdx = boneByIdx;
|
|
|
|
|
|
2022-09-05 15:19:34 +00:00
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
// ---- bspline -----
|
|
|
|
|
bgip.bsp.resize(cx, cy, degx, degy);
|
2022-09-05 15:19:34 +00:00
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
const size_t numcp = size_t(cx) * size_t(cy);
|
2023-07-10 15:23:19 +00:00
|
|
|
|
const size_t numgridp = grid->linearsize();
|
2022-09-13 16:38:44 +00:00
|
|
|
|
|
2024-07-07 01:30:54 +00:00
|
|
|
|
if(const char *idata = interp->Attribute("data"))
|
2022-09-23 16:10:44 +00:00
|
|
|
|
{
|
2024-07-07 01:30:54 +00:00
|
|
|
|
// 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);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
2024-07-07 01:30:54 +00:00
|
|
|
|
// 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);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
2024-07-07 01:30:54 +00:00
|
|
|
|
bk->controlpoints.resize(numcp);
|
|
|
|
|
bgip.bsp.reset(&bk->controlpoints[0]);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
2024-07-07 01:30:54 +00:00
|
|
|
|
unsigned w = 0, h = 0;
|
|
|
|
|
Vector cp;
|
|
|
|
|
cp.z = 1; // we want all grid points at full alpha
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
2024-07-07 01:30:54 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|
|
|
|
|
// ---- end bspline -----
|
2022-09-05 15:19:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
animation = animation->NextSiblingElement("Animation");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
Animation *SkeletalSprite::getCurrentAnimation(size_t layer)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2014-05-15 22:04:56 +00:00
|
|
|
|
return layer < animLayers.size() ? animLayers[layer].getCurrentAnimation() : NULL;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
void SkeletalSprite::setTime(float time, size_t layer)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2014-05-15 22:04:56 +00:00
|
|
|
|
if(layer < animLayers.size())
|
|
|
|
|
animLayers[layer].timer = time;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-17 12:33:23 +00:00
|
|
|
|
void AnimationLayer::resetPass()
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < s->bones.size(); i++)
|
2016-04-17 12:33:23 +00:00
|
|
|
|
{
|
|
|
|
|
Bone *b = s->bones[i];
|
|
|
|
|
if (contains(b))
|
2023-12-21 05:04:09 +00:00
|
|
|
|
{
|
2016-04-17 12:33:23 +00:00
|
|
|
|
b->setRenderPass(b->originalRenderPass);
|
2023-12-21 05:04:09 +00:00
|
|
|
|
b->fhTo(b->originalFH);
|
|
|
|
|
}
|
2016-04-17 12:33:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AnimationLayer::contains(const Bone *b) const
|
|
|
|
|
{
|
|
|
|
|
const int idx = b->boneIdx;
|
|
|
|
|
if (!ignoreBones.empty())
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t j = 0; j < ignoreBones.size(); j++)
|
2016-04-17 12:33:23 +00:00
|
|
|
|
if (idx == ignoreBones[j])
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else if (!includeBones.empty())
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t j = 0; j < includeBones.size(); j++)
|
2016-04-17 12:33:23 +00:00
|
|
|
|
if (idx == includeBones[j])
|
|
|
|
|
return true;
|
2016-04-18 19:07:46 +00:00
|
|
|
|
return false;
|
2016-04-17 12:33:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
void AnimationLayer::updateBones()
|
|
|
|
|
{
|
|
|
|
|
if (!animating && !(&s->animLayers[0] == this) && fallThru == 0) return;
|
|
|
|
|
|
|
|
|
|
SkeletalKeyframe *key1 = getCurrentAnimation()->getPrevKeyframe(timer);
|
|
|
|
|
SkeletalKeyframe *key2 = getCurrentAnimation()->getNextKeyframe(timer);
|
|
|
|
|
if (!key1 || !key2) return;
|
|
|
|
|
float t1 = key1->t;
|
|
|
|
|
float t2 = key2->t;
|
|
|
|
|
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
float diff = t2-t1;
|
|
|
|
|
float dt;
|
|
|
|
|
if (diff != 0)
|
|
|
|
|
dt = (timer - t1)/(t2-t1);
|
|
|
|
|
else
|
|
|
|
|
dt = 0;
|
|
|
|
|
|
|
|
|
|
if (lastNewKey != key2)
|
|
|
|
|
{
|
|
|
|
|
if (!key2->sound.empty())
|
|
|
|
|
{
|
|
|
|
|
core->sound->playSfx(key2->sound);
|
|
|
|
|
}
|
|
|
|
|
if (!key2->commands.empty())
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < key2->commands.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
key2->commands[i].run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (s->animKeyNotify)
|
|
|
|
|
{
|
|
|
|
|
s->animKeyNotify->onAnimationKeyPassed(getCurrentAnimation()->getSkeletalKeyframeIndex(lastNewKey));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lastNewKey = key2;
|
|
|
|
|
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < s->bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
Bone *b = s->bones[i];
|
|
|
|
|
|
|
|
|
|
if (b->segmentChain == 1)
|
|
|
|
|
{
|
|
|
|
|
b->updateSegments();
|
|
|
|
|
}
|
|
|
|
|
if (b->segmentChain < 2)
|
|
|
|
|
{
|
2016-04-17 12:33:23 +00:00
|
|
|
|
if (b->animated != Bone::ANIM_NONE && contains(b))
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2016-04-17 12:33:23 +00:00
|
|
|
|
int idx = b->boneIdx;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
BoneKeyframe *bkey1 = key1->getBoneKeyframe(idx);
|
|
|
|
|
BoneKeyframe *bkey2 = key2->getBoneKeyframe(idx);
|
|
|
|
|
if (bkey1 && bkey2)
|
|
|
|
|
{
|
|
|
|
|
if (!animating && fallThru > 0)
|
|
|
|
|
{
|
|
|
|
|
//HACK: TODO: fix this up nice like below
|
|
|
|
|
Vector p = Vector((bkey2->x-bkey1->x)*dt+bkey1->x, (bkey2->y-bkey1->y)*dt+bkey1->y);
|
|
|
|
|
float rot = (bkey2->rot - bkey1->rot)*dt + bkey1->rot;
|
|
|
|
|
p = Vector((p.x-b->position.x)*fallThru+b->position.x, (p.y-b->position.y)*fallThru+b->position.y);
|
|
|
|
|
rot = (rot-b->rotation.z)*fallThru + b->rotation.z;
|
2022-09-14 03:20:37 +00:00
|
|
|
|
if (b->animated & Bone::ANIM_POS)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
b->position = p;
|
2022-09-14 03:20:37 +00:00
|
|
|
|
if (b->animated & Bone::ANIM_ROT)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
b->rotation.z = rot;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int lerpType = key2->lerpType;
|
|
|
|
|
//k(0)×(2u3-3u2+1) + k(1)×(3u2-2u3)
|
2022-09-14 03:20:37 +00:00
|
|
|
|
if (b->animated & Bone::ANIM_POS)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
b->position = Vector(lerp(bkey1->x, bkey2->x, dt, lerpType), lerp(bkey1->y, bkey2->y, dt, lerpType));
|
|
|
|
|
}
|
2022-09-14 03:20:37 +00:00
|
|
|
|
if (b->animated & Bone::ANIM_ROT)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
b->rotation.z = lerp(bkey1->rot, bkey2->rot, dt, lerpType);
|
|
|
|
|
}
|
|
|
|
|
if (b->animated==Bone::ANIM_ALL && (bkey1->doScale || bkey2->doScale))
|
|
|
|
|
{
|
|
|
|
|
b->scale.x = lerp(bkey1->sx, bkey2->sx, dt, lerpType);
|
|
|
|
|
b->scale.y = lerp(bkey1->sy, bkey2->sy, dt, lerpType);
|
|
|
|
|
}
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid *grid = b->getGrid();
|
2023-07-10 15:23:19 +00:00
|
|
|
|
if (grid && b->animated==Bone::ANIM_ALL && !b->changeStrip.empty() && grid->gridType == GRID_STRIP)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2022-09-13 16:38:44 +00:00
|
|
|
|
if (bkey2->grid.size() < b->changeStrip.size())
|
|
|
|
|
bkey2->grid.resize(b->changeStrip.size());
|
|
|
|
|
if (bkey1->grid.size() < b->changeStrip.size())
|
|
|
|
|
bkey1->grid.resize(b->changeStrip.size());
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < b->changeStrip.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2022-09-13 16:38:44 +00:00
|
|
|
|
b->changeStrip[i] = Vector(lerp(bkey1->grid[i].x, bkey2->grid[i].x, dt, lerpType), lerp(bkey1->grid[i].y, bkey2->grid[i].y, dt, lerpType));
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2022-09-14 03:11:56 +00:00
|
|
|
|
b->setStripPoints(b->stripVert, &b->changeStrip[0], b->changeStrip.size());
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
2023-07-10 15:23:19 +00:00
|
|
|
|
if (grid && b->animated==Bone::ANIM_ALL && grid->gridType == GRID_INTERP)
|
2022-09-23 16:10:44 +00:00
|
|
|
|
{
|
2023-07-10 15:23:19 +00:00
|
|
|
|
const size_t N = grid->linearsize();
|
2022-09-23 16:10:44 +00:00
|
|
|
|
if(bkey1->grid.size() < N)
|
|
|
|
|
{
|
|
|
|
|
bkey1->grid.resize(N);
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid::ResetWithAlpha(&bkey1->grid[0], grid->width(), grid->height(), 1.0f);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|
|
|
|
|
if(bkey2->grid.size() < N)
|
|
|
|
|
{
|
|
|
|
|
bkey2->grid.resize(N);
|
2023-08-09 00:41:04 +00:00
|
|
|
|
DynamicRenderGrid::ResetWithAlpha(&bkey2->grid[0], grid->width(), grid->height(), 1.0f);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 00:36:38 +00:00
|
|
|
|
Vector *dst = grid->dataRW();
|
2022-09-23 16:10:44 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::setFreeze(bool f)
|
|
|
|
|
{
|
|
|
|
|
frozen = f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::updateBones()
|
|
|
|
|
{
|
|
|
|
|
if (!frozen)
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < animLayers.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
animLayers[i].updateBones();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-05 17:40:28 +00:00
|
|
|
|
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SkeletalSprite::isAnimating(int layer)
|
|
|
|
|
{
|
|
|
|
|
return animLayers[layer].animating;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::setTimeMultiplier(float t, int layer)
|
|
|
|
|
{
|
|
|
|
|
animLayers[layer].timeMultiplier = t;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-31 17:02:18 +00:00
|
|
|
|
Bone* SkeletalSprite::getSelectedBone(bool mouseBased)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (!loaded) return 0;
|
|
|
|
|
if (mouseBased)
|
|
|
|
|
{
|
2012-01-31 17:02:18 +00:00
|
|
|
|
float closestDist = HUGE_VALF;
|
2011-08-03 20:05:33 +00:00
|
|
|
|
Bone *b = 0;
|
|
|
|
|
Vector p = core->mouse.position;
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2012-01-31 17:02:18 +00:00
|
|
|
|
if (bones[i]->renderQuad || core->getShiftState())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2012-01-31 17:02:18 +00:00
|
|
|
|
bones[i]->color = Vector(1,1,1);
|
2016-04-18 20:08:36 +00:00
|
|
|
|
if (bones[i]->selectable && bones[i]->renderQuad && bones[i]->isCoordinateInsideWorld(p))
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
2012-01-31 17:02:18 +00:00
|
|
|
|
float dist = (bones[i]->getWorldPosition() - p).getSquaredLength2D();
|
|
|
|
|
if (dist <= closestDist)
|
|
|
|
|
{
|
|
|
|
|
closestDist = dist;
|
|
|
|
|
b = bones[i];
|
|
|
|
|
selectedBone = i;
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (b)
|
|
|
|
|
{
|
|
|
|
|
b->color = Vector(1,0,0);
|
|
|
|
|
}
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
// else
|
2017-01-14 17:10:20 +00:00
|
|
|
|
if (!bones.empty() && selectedBone < bones.size())
|
2011-08-03 20:05:33 +00:00
|
|
|
|
return bones[selectedBone];
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::updateSelectedBoneColor()
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
for (size_t i = 0; i < bones.size(); i++)
|
2011-08-03 20:05:33 +00:00
|
|
|
|
{
|
|
|
|
|
bones[i]->color = Vector(1,1,1);
|
|
|
|
|
}
|
|
|
|
|
Bone *b = bones[selectedBone];
|
|
|
|
|
if (b)
|
|
|
|
|
b->color = Vector(0.5,0.5,1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::setSelectedBone(int b)
|
|
|
|
|
{
|
|
|
|
|
selectedBone = b;
|
|
|
|
|
updateSelectedBoneColor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::selectPrevBone()
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
const size_t oldsel = selectedBone;
|
2016-04-18 20:08:36 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
selectedBone++;
|
|
|
|
|
if(selectedBone == oldsel)
|
|
|
|
|
break;
|
|
|
|
|
if (selectedBone >= bones.size())
|
|
|
|
|
selectedBone = 0;
|
|
|
|
|
}
|
|
|
|
|
while (!bones[selectedBone]->selectable);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
updateSelectedBoneColor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SkeletalSprite::selectNextBone()
|
|
|
|
|
{
|
2017-01-14 17:10:20 +00:00
|
|
|
|
const size_t oldsel = selectedBone;
|
2016-04-18 20:08:36 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
selectedBone--;
|
|
|
|
|
if(selectedBone == oldsel)
|
|
|
|
|
break;
|
2017-01-14 17:23:53 +00:00
|
|
|
|
if (selectedBone >= bones.size())
|
2016-04-18 20:08:36 +00:00
|
|
|
|
selectedBone = bones.size()-1;
|
|
|
|
|
}
|
|
|
|
|
while (!bones[selectedBone]->selectable);
|
2011-08-03 20:05:33 +00:00
|
|
|
|
updateSelectedBoneColor();
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
void BoneGridInterpolator::updateGridOnly(BoneKeyframe& bk, const Bone *bone)
|
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
|
const DynamicRenderGrid *grid = bone->getGrid();
|
2022-09-23 16:10:44 +00:00
|
|
|
|
assert(bone->boneIdx == bk.idx);
|
2023-07-10 15:23:19 +00:00
|
|
|
|
assert(bk.grid.size() == grid->linearsize());
|
|
|
|
|
bsp.recalc(&bk.grid[0], grid->width(), grid->height(), &bk.controlpoints[0]);
|
2022-09-23 16:10:44 +00:00
|
|
|
|
|
|
|
|
|
}
|
2011-08-03 20:05:33 +00:00
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
void BoneGridInterpolator::updateGridAndBone(BoneKeyframe& bk, Bone *bone)
|
|
|
|
|
{
|
|
|
|
|
updateGridOnly(bk, bone);
|
2024-02-06 00:36:38 +00:00
|
|
|
|
DynamicRenderGrid *g = bone->getGrid();
|
|
|
|
|
Vector *dst = g->dataRW();
|
2022-09-23 16:10:44 +00:00
|
|
|
|
std::copy(bk.grid.begin(), bk.grid.end(), dst);
|
2024-02-06 00:36:38 +00:00
|
|
|
|
|
2022-09-23 16:10:44 +00:00
|
|
|
|
}
|