mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-12-26 06:35:51 +00:00
eeaa723cd7
Bone positioning now takes into account its parent's absolute rotation, and compensates it. That means bones with rotated parents follow exactly the mouse when dragged, instead of going anywhere except where they should. Repaired selecting bones with the mouse, and made that the default (can be switched to keyboard with M key). The timeline grid size and timestep unit size are now variable, and can be changed with the U, I, O, P keys or the added UI buttons. Bone borders and joint points can be displayed with B key. Removed the ignorebone0 button and related functionality. Minor cosmetical things.
1927 lines
40 KiB
C++
1927 lines
40 KiB
C++
/*
|
||
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 "../ExternalLibs/tinyxml.h"
|
||
#include "Core.h"
|
||
#include "Particles.h"
|
||
#include "MathFunctions.h"
|
||
#include "SimpleIStringStream.h"
|
||
|
||
std::string SkeletalSprite::animationPath = "data/animations/";
|
||
std::string SkeletalSprite::skinPath = "skins/";
|
||
|
||
std::string SkeletalSprite::secondaryAnimationPath = "";
|
||
|
||
|
||
void SkeletalKeyframe::copyAllButTime(SkeletalKeyframe *copy)
|
||
{
|
||
if (!copy) return;
|
||
|
||
float t = this->t;
|
||
(*this) = (*copy);
|
||
this->t = t;
|
||
}
|
||
|
||
Bone::Bone() : Quad()
|
||
{
|
||
addType(SCO_BONE);
|
||
fileRenderQuad = true;
|
||
skeleton = 0;
|
||
generateCollisionMask = true;
|
||
animated = ANIM_ALL;
|
||
originalScale = Vector(1,1);
|
||
boneIdx = pidx = -1;
|
||
rbp = 0;
|
||
segmentChain = 0;
|
||
|
||
minDist = maxDist = 128;
|
||
reverse = false;
|
||
}
|
||
/*
|
||
void Bone::createStrip(bool vert, int num)
|
||
{
|
||
Quad::createStrip(vert, num);
|
||
changeStrip.resize(num);
|
||
}
|
||
*/
|
||
|
||
void Bone::destroy()
|
||
{
|
||
Quad::destroy();
|
||
|
||
for (int i = 0; i < segments.size(); i++)
|
||
{
|
||
segments[i]->setLife(1.0);
|
||
segments[i]->setDecayRate(10);
|
||
segments[i]->alpha = 0;
|
||
}
|
||
segments.clear();
|
||
}
|
||
|
||
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)
|
||
{
|
||
if (!vert)
|
||
{
|
||
createGrid(num, 2);
|
||
}
|
||
else
|
||
{
|
||
createGrid(2, num);
|
||
}
|
||
stripVert = vert;
|
||
gridType = GRID_SET;
|
||
changeStrip.resize(num);
|
||
setGridPoints(vert, strip);
|
||
}
|
||
|
||
|
||
Quad* Bone::addFrame(const std::string &gfx)
|
||
{
|
||
renderQuad = false;
|
||
Quad *q = new Quad();
|
||
q->setTexture(gfx);
|
||
q->renderBeforeParent = 1;
|
||
addChild(q, PM_POINTER);
|
||
return q;
|
||
}
|
||
|
||
void Bone::showFrame(int idx)
|
||
{
|
||
//float t = 0.1;
|
||
int c = 0;
|
||
for (Children::iterator i = children.begin(); i != children.end(); i++)
|
||
{
|
||
RenderObject *r = (*i);
|
||
if (idx == c)
|
||
{
|
||
if (r->alpha == 0)
|
||
{
|
||
r->alpha = 1;
|
||
|
||
// add option to turn on alpha fading
|
||
//r->alpha.interpolateTo(1, t);
|
||
}
|
||
else
|
||
{
|
||
r->alpha = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (r->alpha == 1)
|
||
{
|
||
r->alpha = 0;
|
||
//r->alpha.interpolateTo(0, t*2);
|
||
}
|
||
else
|
||
{
|
||
r->alpha = 0;
|
||
}
|
||
}
|
||
c++;
|
||
}
|
||
}
|
||
|
||
|
||
void Bone::setAnimated(int b)
|
||
{
|
||
/*
|
||
std::ostringstream os;
|
||
os << "setting animated: " << b;
|
||
debugLog(os.str());
|
||
*/
|
||
|
||
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)
|
||
{
|
||
/*
|
||
int maxDist, minDist;
|
||
maxDist = minDist = 128;
|
||
*/
|
||
|
||
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);
|
||
}
|
||
else if (diff.getSquaredLength2D() > sqr(minDist)) // 6.3
|
||
{
|
||
b->position += diff*0.05f;
|
||
|
||
MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), diff, angle);
|
||
|
||
//b->rotation.interpolateTo(Vector(0,0,angle),0.2);
|
||
}
|
||
if (angle != -1)
|
||
{
|
||
/*
|
||
std::ostringstream os;
|
||
os << "rotz: " << b->rotation.z << " angle: " << angle;
|
||
debugLog(os.str());
|
||
*/
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
b->rotation.interpolateTo(Vector(0,0,angle),0.2);
|
||
}
|
||
/*
|
||
else
|
||
{
|
||
float angle;
|
||
MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), diff, angle);
|
||
b->rotation.interpolateTo(Vector(0,0,angle),0);
|
||
}
|
||
*/
|
||
}
|
||
|
||
void Bone::updateSegments()
|
||
{
|
||
if (segmentChain>0 && !segments.empty())
|
||
{
|
||
//bool reverse = true;
|
||
|
||
/*
|
||
std::vector<Bone*> segments;
|
||
Bone *child = (Bone*)(this->children.front());
|
||
while (child)
|
||
{
|
||
segments.push_back(child);
|
||
|
||
if (child->children.empty())
|
||
child = 0;
|
||
else
|
||
child = (Bone*)(child->children.front());
|
||
}
|
||
*/
|
||
|
||
if (!reverse)
|
||
{
|
||
for (int i = 0; i < segments.size(); i++)
|
||
{
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void BoneCommand::parse(Bone *b, SimpleIStringStream &is)
|
||
{
|
||
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;
|
||
/*
|
||
Emitter *e = b->emitters[slot];
|
||
if (e)
|
||
{
|
||
e->start();
|
||
}
|
||
*/
|
||
}
|
||
else if (type=="AC_PRT_STOP")
|
||
{
|
||
command = AC_PRT_STOP;
|
||
is >> slot;
|
||
/*
|
||
Emitter *e = b->emitters[slot];
|
||
if (e)
|
||
{
|
||
e->stop();
|
||
}
|
||
*/
|
||
}
|
||
else if (type=="AC_SEGS_START")
|
||
command = AC_SEGS_START;
|
||
else if (type=="AC_SEGS_STOP")
|
||
command = AC_SEGS_STOP;
|
||
}
|
||
|
||
void BoneCommand::run()
|
||
{
|
||
//debugLog("running CMD");
|
||
switch(command)
|
||
{
|
||
case AC_SND_PLAY:
|
||
{
|
||
core->sound->playSfx(file);
|
||
}
|
||
break;
|
||
case AC_FRM_SHOW:
|
||
{
|
||
b->showFrame(slot);
|
||
}
|
||
break;
|
||
case AC_PRT_LOAD:
|
||
{
|
||
ParticleEffect *e = b->emitters[slot];
|
||
if (e)
|
||
{
|
||
e->load(file);
|
||
}
|
||
}
|
||
break;
|
||
case AC_PRT_START:
|
||
{
|
||
ParticleEffect *e = b->emitters[slot];
|
||
if (e)
|
||
e->start();
|
||
}
|
||
break;
|
||
case AC_PRT_STOP:
|
||
{
|
||
ParticleEffect *e = b->emitters[slot];
|
||
if (e)
|
||
e->stop();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
AnimationLayer::AnimationLayer()
|
||
{
|
||
lastNewKey = 0;
|
||
fallThru= 0;
|
||
//index = -1;
|
||
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)
|
||
{
|
||
std::string animation = a;
|
||
stringToLower(animation);
|
||
|
||
bool played = false;
|
||
for (int i = 0; i < s->animations.size(); i++)
|
||
{
|
||
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;
|
||
//currentKeyframe = 0;
|
||
currentAnimation = idx;
|
||
timer = 0;
|
||
animating = true;
|
||
|
||
this->loop = loop;
|
||
|
||
animationLength = getCurrentAnimation()->getAnimationLength();
|
||
//doNextKeyframe();
|
||
}
|
||
|
||
void AnimationLayer::enqueueAnimation(std::string anim, int loop)
|
||
{
|
||
enqueuedAnimation = anim;
|
||
enqueuedAnimationLoop = loop;
|
||
stringToLower(enqueuedAnimation);
|
||
}
|
||
|
||
float AnimationLayer::transitionAnimate(std::string anim, float time, int loop)
|
||
{
|
||
stringToLower(anim);
|
||
float totalTime =0;
|
||
if (createTransitionAnimation(anim, time))
|
||
{
|
||
timeMultiplier = 1;
|
||
|
||
currentAnimation = -1;
|
||
this->loop = 0;
|
||
timer = 0;
|
||
animating = 1;
|
||
animationLength = getCurrentAnimation()->getAnimationLength();
|
||
enqueueAnimation(anim, loop);
|
||
Animation *a = this->s->getAnimation(anim);
|
||
if (a)
|
||
{
|
||
if (loop > -1)
|
||
totalTime = a->getAnimationLength()*(loop+1) + time;
|
||
else
|
||
totalTime = a->getAnimationLength() + time;
|
||
}
|
||
}
|
||
return totalTime;
|
||
}
|
||
|
||
void AnimationLayer::setSkeletalSprite(SkeletalSprite *s)
|
||
{
|
||
this->s = s;
|
||
}
|
||
|
||
Animation* AnimationLayer::getCurrentAnimation()
|
||
{
|
||
if (currentAnimation == -1)
|
||
return &blendAnimation;
|
||
if (currentAnimation < 0 || currentAnimation >= s->animations.size())
|
||
{
|
||
std::ostringstream os;
|
||
os << "skel: " << s->filenameLoaded << " currentAnimation: " << currentAnimation << " is out of range\n error in anim file?";
|
||
errorLog(os.str());
|
||
exit(-1);
|
||
return 0;
|
||
}
|
||
return &s->animations[currentAnimation];
|
||
}
|
||
|
||
bool AnimationLayer::createTransitionAnimation(std::string anim, float time)
|
||
{
|
||
//Animation *a = getCurrentAnimation();
|
||
Animation *to = s->getAnimation(anim);
|
||
if (!to) return false;
|
||
blendAnimation.keyframes.clear();
|
||
SkeletalKeyframe k;
|
||
k.t = 0;
|
||
for (int i = 0; i < s->bones.size(); i++)
|
||
{
|
||
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.strip = s->bones[i]->strip;
|
||
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;
|
||
SkeletalKeyframe *rk = to->getKeyframe(0);
|
||
if (!rk) return false;
|
||
k2 = *rk;
|
||
k2.t = time;
|
||
blendAnimation.keyframes.push_back(k2);
|
||
|
||
blendAnimation.name = anim;
|
||
return true;
|
||
}
|
||
|
||
|
||
void AnimationLayer::stopAnimation()
|
||
{
|
||
animating = false;
|
||
if (!enqueuedAnimation.empty())
|
||
{
|
||
animate(enqueuedAnimation, enqueuedAnimationLoop);
|
||
enqueuedAnimation = "";
|
||
}
|
||
}
|
||
|
||
bool AnimationLayer::isAnimating()
|
||
{
|
||
return animating;
|
||
}
|
||
|
||
float AnimationLayer::getAnimationLength()
|
||
{
|
||
return animationLength;
|
||
}
|
||
|
||
int Animation::getNumKeyframes()
|
||
{
|
||
return keyframes.size();
|
||
}
|
||
|
||
SkeletalKeyframe *Animation::getKeyframe(int key)
|
||
{
|
||
if (key < 0 || key >= keyframes.size()) return 0;
|
||
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()
|
||
{
|
||
/*
|
||
std::vector<SkeletalKeyframe> copy = this->keyframes;
|
||
keyframes.clear();
|
||
*/
|
||
for (int i = 0; i < keyframes.size(); i++)
|
||
{
|
||
for (int j = 0; j < keyframes.size()-1; j++)
|
||
{
|
||
if (keyframes[j].t > keyframes[j+1].t)
|
||
{
|
||
SkeletalKeyframe temp = keyframes[j+1];
|
||
keyframes[j+1] = keyframes[j];
|
||
keyframes[j] = temp;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void Animation::cloneKey(int key, float toffset)
|
||
{
|
||
std::vector<SkeletalKeyframe> copy = this->keyframes;
|
||
keyframes.clear();
|
||
int i = 0;
|
||
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;
|
||
}
|
||
|
||
void Animation::deleteKey(int key)
|
||
{
|
||
std::vector<SkeletalKeyframe> copy = this->keyframes;
|
||
keyframes.clear();
|
||
int i = 0;
|
||
for (i = 0; i < key; i++)
|
||
keyframes.push_back(copy[i]);
|
||
for (i = key+1; i < copy.size(); i++)
|
||
keyframes.push_back(copy[i]);
|
||
}
|
||
|
||
int Animation::getSkeletalKeyframeIndex(SkeletalKeyframe *skey)
|
||
{
|
||
for (int i = 0; i < keyframes.size(); i++)
|
||
{
|
||
if (&keyframes[i] == skey)
|
||
return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
BoneKeyframe *SkeletalKeyframe::getBoneKeyframe(int idx)
|
||
{
|
||
for (int i = 0; i < keyframes.size(); i++)
|
||
{
|
||
if (keyframes[i].idx == idx)
|
||
{
|
||
return &keyframes[i];
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
SkeletalKeyframe *Animation::getPrevKeyframe(float t)
|
||
{
|
||
int kf = -1;
|
||
for (int i = keyframes.size()-1; i >= 0; i--)
|
||
{
|
||
if (t >= keyframes[i].t)
|
||
{
|
||
kf = i;
|
||
break;
|
||
}
|
||
}
|
||
if (kf == -1)
|
||
return 0;
|
||
if (kf >= keyframes.size())
|
||
kf = keyframes.size()-1;
|
||
if (kf < 0)
|
||
kf = 0;
|
||
return &keyframes[kf];
|
||
}
|
||
|
||
SkeletalKeyframe *Animation::getNextKeyframe(float t)
|
||
{
|
||
int kf = -1;
|
||
for (int i = 0; i < keyframes.size(); i++)
|
||
{
|
||
if (t <= keyframes[i].t)
|
||
{
|
||
kf = i;
|
||
break;
|
||
}
|
||
}
|
||
// kf++;
|
||
if (kf == -1)
|
||
return 0;
|
||
if (kf >= keyframes.size())
|
||
kf = keyframes.size()-1;
|
||
if (kf < 0)
|
||
kf = 0;
|
||
return &keyframes[kf];
|
||
}
|
||
|
||
SkeletalSprite::SkeletalSprite() : RenderObject()
|
||
{
|
||
frozen = false;
|
||
animKeyNotify = 0;
|
||
loaded = false;
|
||
animLayers.resize(10);
|
||
for (int i = 0; i < animLayers.size(); i++)
|
||
animLayers[i].setSkeletalSprite(this);
|
||
selectedBone = -1;
|
||
}
|
||
|
||
|
||
void SkeletalSprite::setAnimationKeyNotify(RenderObject *r)
|
||
{
|
||
animKeyNotify = r;
|
||
}
|
||
|
||
void SkeletalSprite::animate(const std::string &animation, int loop, int layer)
|
||
{
|
||
animLayers[layer].animate(animation, loop);
|
||
}
|
||
|
||
float SkeletalSprite::transitionAnimate(std::string anim, float time, int loop, int layer)
|
||
{
|
||
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;
|
||
}
|
||
|
||
AnimationLayer* SkeletalSprite::getAnimationLayer(int l)
|
||
{
|
||
if (l >= 0 && l < animLayers.size())
|
||
{
|
||
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);
|
||
|
||
int i = 0;
|
||
|
||
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());
|
||
}
|
||
for (int i = 0; i < b->collisionMask.size(); i++)
|
||
{
|
||
b->transformedCollisionMask[i] = b->getWorldCollidePosition(b->collisionMask[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
for (int i = 0; i < bones.size(); i++)
|
||
{
|
||
bones[i]->update(dt);
|
||
}
|
||
*/
|
||
for (i = 0; i < animLayers.size(); i++)
|
||
{
|
||
animLayers[i].update(dt);
|
||
}
|
||
//updateBones();
|
||
}
|
||
|
||
void AnimationLayer::update(float dt)
|
||
{
|
||
timeMultiplier.update(dt);
|
||
if (animating)
|
||
{
|
||
timer += dt*timeMultiplier.x;
|
||
|
||
if (timer > animationLength)
|
||
{
|
||
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();
|
||
}
|
||
}
|
||
}
|
||
|
||
void SkeletalSprite::saveSkeletal(const std::string &fn)
|
||
{
|
||
std::string file, filename=fn;
|
||
stringToLower(filename);
|
||
|
||
if (!secondaryAnimationPath.empty())
|
||
file = secondaryAnimationPath + filename + ".xml";
|
||
else
|
||
file = animationPath + filename + ".xml";
|
||
|
||
int i = 0;
|
||
TiXmlDocument xml;
|
||
|
||
TiXmlElement animationLayers("AnimationLayers");
|
||
for (i = 0; i < animLayers.size(); i++)
|
||
{
|
||
TiXmlElement animationLayer("AnimationLayer");
|
||
if (animLayers[i].ignoreBones.size() > 0)
|
||
{
|
||
std::ostringstream os;
|
||
for (int j = 0; j < animLayers[i].ignoreBones.size(); j++)
|
||
{
|
||
os << animLayers[i].ignoreBones[j] << " ";
|
||
}
|
||
animationLayer.SetAttribute("ignore", os.str());
|
||
}
|
||
if (animLayers[i].includeBones.size() > 0)
|
||
{
|
||
std::ostringstream os;
|
||
for (int j = 0; j < animLayers[i].includeBones.size(); j++)
|
||
{
|
||
os << animLayers[i].includeBones[j] << " ";
|
||
}
|
||
animationLayer.SetAttribute("include", os.str());
|
||
}
|
||
if (!animLayers[i].name.empty())
|
||
{
|
||
animationLayer.SetAttribute("name", animLayers[i].name);
|
||
}
|
||
|
||
animationLayers.InsertEndChild(animationLayer);
|
||
}
|
||
xml.InsertEndChild(animationLayers);
|
||
|
||
|
||
TiXmlElement bones("Bones");
|
||
for (i = 0; i < this->bones.size(); i++)
|
||
{
|
||
TiXmlElement bone("Bone");
|
||
bone.SetAttribute("idx", this->bones[i]->boneIdx);
|
||
bone.SetAttribute("gfx", this->bones[i]->gfx);
|
||
bone.SetAttribute("pidx", this->bones[i]->pidx);
|
||
bone.SetAttribute("name", this->bones[i]->name);
|
||
bone.SetAttribute("fh", this->bones[i]->isfh());
|
||
bone.SetAttribute("fv", this->bones[i]->isfv());
|
||
bone.SetAttribute("gc", this->bones[i]->generateCollisionMask);
|
||
bone.SetAttribute("cr", this->bones[i]->collideRadius);
|
||
if (!this->bones[i]->renderQuad)
|
||
{
|
||
bone.SetAttribute("rq", this->bones[i]->fileRenderQuad);
|
||
}
|
||
if (!this->bones[i]->collisionRects.empty())
|
||
{
|
||
std::ostringstream os;
|
||
os << this->bones[i]->collisionRects.size() << " ";
|
||
for (int j = 0; j < this->bones[i]->collisionRects.size(); j++)
|
||
{
|
||
RectShape *r = &this->bones[i]->collisionRects[j];
|
||
int x, y, w, h;
|
||
r->getCWH(&x, &y, &w, &h);
|
||
os << x << " " << y << " " << w << " " << h << " ";
|
||
}
|
||
bone.SetAttribute("crects", os.str());
|
||
}
|
||
std::ostringstream os;
|
||
os << this->bones[i]->collidePosition.x << " " << this->bones[i]->collidePosition.y;
|
||
bone.SetAttribute("cp", os.str());
|
||
if (this->bones[i]->rbp)
|
||
bone.SetAttribute("rbp", this->bones[i]->rbp);
|
||
if (this->bones[i]->getRenderPass())
|
||
bone.SetAttribute("pass", this->bones[i]->getRenderPass());
|
||
if (this->bones[i]->offset.x)
|
||
bone.SetAttribute("offx", this->bones[i]->offset.x);
|
||
if (this->bones[i]->offset.y)
|
||
bone.SetAttribute("offy", this->bones[i]->offset.y);
|
||
if (!this->bones[i]->prt.empty())
|
||
bone.SetAttribute("prt", this->bones[i]->prt);
|
||
if (!this->bones[i]->changeStrip.empty())
|
||
{
|
||
std::ostringstream os;
|
||
os << this->bones[i]->stripVert << " " << this->bones[i]->changeStrip.size();
|
||
bone.SetAttribute("strip", os.str());
|
||
}
|
||
if (!this->bones[i]->internalOffset.isZero())
|
||
{
|
||
std::ostringstream os;
|
||
os << this->bones[i]->internalOffset.x << " " << this->bones[i]->internalOffset.y;
|
||
bone.SetAttribute("io", os.str());
|
||
}
|
||
if (this->bones[i]->isRepeatingTextureToFill())
|
||
{
|
||
bone.SetAttribute("rt", 1);
|
||
}
|
||
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;
|
||
bone.SetAttribute("sz", os.str());
|
||
}
|
||
/*
|
||
if (this->bones[i]->color.x != 1 || this->bones[i]->color.y != 1 || this->bones[i]->color.z != 1)
|
||
{
|
||
std::ostringstream os;
|
||
os << this->bones[i]->color.x << " " << this->bones[i]->color.y << " " << this->bones[i]->color.z;
|
||
bone.SetAttribute("color", os.str());
|
||
}
|
||
*/
|
||
|
||
for (Children::iterator j = this->bones[i]->children.begin(); j != this->bones[i]->children.end(); j++)
|
||
{
|
||
Bone *b = dynamic_cast<Bone*>(*j);
|
||
Quad *q = dynamic_cast<Quad*>(*j);
|
||
Particle *p = dynamic_cast<Particle*>(*j);
|
||
if (q && !b && !p)
|
||
{
|
||
TiXmlElement frame("Frame");
|
||
frame.SetAttribute("gfx", q->texture->name);
|
||
if (q->getRenderPass() != 0)
|
||
{
|
||
frame.SetAttribute("pass", q->getRenderPass());
|
||
}
|
||
bone.InsertEndChild(frame);
|
||
}
|
||
}
|
||
bones.InsertEndChild(bone);
|
||
}
|
||
xml.InsertEndChild(bones);
|
||
|
||
TiXmlElement animations("Animations");
|
||
for (i = 0; i < this->animations.size(); i++)
|
||
{
|
||
Animation *a = &this->animations[i];
|
||
TiXmlElement animation("Animation");
|
||
animation.SetAttribute("name", a->name);
|
||
for (int j = 0; j < a->keyframes.size(); j++)
|
||
{
|
||
TiXmlElement key("Key");
|
||
if (!a->keyframes[j].sound.empty())
|
||
key.SetAttribute("sound", a->keyframes[j].sound);
|
||
if (!a->keyframes[j].cmd.empty())
|
||
{
|
||
key.SetAttribute("cmd", a->keyframes[j].cmd);
|
||
}
|
||
if (a->keyframes[j].lerpType != 0)
|
||
{
|
||
key.SetAttribute("lerp", a->keyframes[j].lerpType);
|
||
}
|
||
std::ostringstream os;
|
||
os << a->keyframes[j].t << " ";
|
||
std::ostringstream szos;
|
||
for (int k = 0; k < a->keyframes[j].keyframes.size(); k++)
|
||
{
|
||
BoneKeyframe *b = &a->keyframes[j].keyframes[k];
|
||
os << b->idx << " " << b->x << " " << b->y << " " << b->rot << " ";
|
||
os << b->strip.size() << " ";
|
||
for (int i = 0; i < b->strip.size(); i++)
|
||
{
|
||
os << b->strip[i].x << " " << b->strip[i].y << " ";
|
||
}
|
||
if (b->doScale)
|
||
{
|
||
szos << b->idx << " " << b->sx << " " << b->sy << " ";
|
||
}
|
||
}
|
||
std::string szoss = szos.str();
|
||
if (!szoss.empty())
|
||
key.SetAttribute("sz", szoss.c_str());
|
||
|
||
key.SetAttribute("e", os.str());
|
||
|
||
animation.InsertEndChild(key);
|
||
}
|
||
animations.InsertEndChild(animation);
|
||
}
|
||
xml.InsertEndChild(animations);
|
||
xml.SaveFile(file);
|
||
}
|
||
|
||
int SkeletalSprite::getBoneIdx(Bone *b)
|
||
{
|
||
for (int i = 0; i < bones.size(); i++)
|
||
{
|
||
if (bones[i] == b)
|
||
return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
void SkeletalSprite::toggleBone(int idx, int v)
|
||
{
|
||
if (idx >= 0 && idx < bones.size())
|
||
{
|
||
bones[idx]->alpha.x = v;
|
||
}
|
||
}
|
||
|
||
Bone *SkeletalSprite::getBoneByName(const std::string &name)
|
||
{
|
||
for (int i = 0; i < bones.size(); i++)
|
||
{
|
||
if (bones[i]->name == name)
|
||
return bones[i];
|
||
}
|
||
std::ostringstream os;
|
||
os << "Could not find bone with name[" << name << "]";
|
||
debugLog(os.str());
|
||
return 0;
|
||
}
|
||
|
||
Bone *SkeletalSprite::getBoneByIdx(int idx)
|
||
{
|
||
for (int i = 0; i < bones.size(); i++)
|
||
{
|
||
if (bones[i]->boneIdx == idx)
|
||
return bones[i];
|
||
}
|
||
std::ostringstream os;
|
||
os << "Could not find bone with idx[" << idx << "]";
|
||
debugLog(os.str());
|
||
return 0;
|
||
}
|
||
|
||
Bone *SkeletalSprite::initBone(int idx, std::string gfx, int pidx, int rbp, std::string name, int cr, bool fh, bool fv, const Vector &cp)
|
||
{
|
||
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->collidePosition = cp;
|
||
b->name = name;
|
||
//core->generateCollisionMask(b);
|
||
if (fh)
|
||
b->flipHorizontal();
|
||
if (fv)
|
||
b->flipVertical();
|
||
bones.push_back(b);
|
||
return b;
|
||
}
|
||
|
||
void SkeletalSprite::firstAnimation()
|
||
{
|
||
stopAnimation();
|
||
animLayers[0].currentAnimation = 0;
|
||
}
|
||
|
||
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--;
|
||
if (animLayers[0].currentAnimation < 0)
|
||
animLayers[0].currentAnimation = animations.size()-1;
|
||
}
|
||
|
||
void SkeletalSprite::deleteBones()
|
||
{
|
||
bones.clear();
|
||
if (!children.empty())
|
||
{
|
||
// remove child had better be recursive
|
||
Bone *b = (Bone*)children.front();
|
||
//removeChild(b);
|
||
b->destroy();
|
||
delete b;
|
||
deleteBones();
|
||
}
|
||
children.clear();
|
||
bones.clear();
|
||
}
|
||
|
||
Animation *SkeletalSprite::getAnimation(std::string anim)
|
||
{
|
||
for (int i = 0; i < animations.size(); i++)
|
||
{
|
||
if (animations[i].name == anim)
|
||
return &animations[i];
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void SkeletalSprite::loadSkin(const std::string &fn)
|
||
{
|
||
TiXmlDocument d;
|
||
|
||
std::string file;
|
||
|
||
if (!secondaryAnimationPath.empty())
|
||
{
|
||
file = secondaryAnimationPath + skinPath + fn + ".xml";
|
||
}
|
||
|
||
if (file.empty() || !exists(file, false))
|
||
{
|
||
file = animationPath + skinPath + fn + ".xml";
|
||
}
|
||
|
||
stringToLower(file);
|
||
|
||
if (!exists(file,1))
|
||
{
|
||
errorLog("Could not load skin[" + file + "]");
|
||
return;
|
||
}
|
||
d.LoadFile(file);
|
||
|
||
TiXmlElement *bonesXml = d.FirstChildElement("Bones");
|
||
if (bonesXml)
|
||
{
|
||
TiXmlElement *boneXml = bonesXml->FirstChildElement("Bone");
|
||
while (boneXml)
|
||
{
|
||
int idx = atoi(boneXml->Attribute("idx"));
|
||
Bone *b = getBoneByIdx(idx);
|
||
if (b)
|
||
{
|
||
if (boneXml->Attribute("rq"))
|
||
{
|
||
int rq = atoi(boneXml->Attribute("rq"));
|
||
b->renderQuad = rq;
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
/*
|
||
if (boneXml->Attribute("a"))
|
||
{
|
||
float alpha = 0;
|
||
boneXml->Attribute("a", &alpha);
|
||
b->alpha = alpha;
|
||
}
|
||
*/
|
||
/*
|
||
// this is for SKINS
|
||
if (boneXml->Attribute("sz"))
|
||
{
|
||
float v1, v2;
|
||
SimpleIStringStream is(boneXml->Attribute("sz"));
|
||
is >> v1 >> v2;
|
||
b->scale = Vector(v1,v2);
|
||
b->originalScale = b->scale;
|
||
}
|
||
*/
|
||
}
|
||
else
|
||
{
|
||
std::ostringstream os;
|
||
os << "SkinLoad: Could not find idx[" << idx << "]";
|
||
debugLog(os.str());
|
||
}
|
||
boneXml = boneXml->NextSiblingElement("Bone");
|
||
}
|
||
}
|
||
}
|
||
|
||
void SkeletalSprite::stopAnimation(int layer)
|
||
{
|
||
animLayers[layer].stopAnimation();
|
||
}
|
||
|
||
void SkeletalSprite::stopAllAnimations()
|
||
{
|
||
for (int i = 0; i < animLayers.size(); i++)
|
||
{
|
||
animLayers[i].stopAnimation();
|
||
}
|
||
}
|
||
|
||
void SkeletalSprite::playCurrentAnimation(int loop, int layer)
|
||
{
|
||
animLayers[layer].playCurrentAnimation(loop);
|
||
}
|
||
|
||
void SkeletalSprite::loadSkeletal(const std::string &fn)
|
||
{
|
||
filenameLoaded = "";
|
||
loaded = false;
|
||
stopAnimation();
|
||
for (int i = 0; i < animLayers.size(); i++)
|
||
{
|
||
animLayers[i].currentAnimation = 0;
|
||
}
|
||
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 = "";
|
||
return;
|
||
}
|
||
|
||
loaded = true;
|
||
|
||
TiXmlDocument xml;
|
||
xml.LoadFile(file.c_str());
|
||
|
||
TiXmlElement *bones = xml.FirstChildElement("Bones");
|
||
if (bones)
|
||
{
|
||
if (bones->Attribute("scale"))
|
||
{
|
||
SimpleIStringStream is(bones->Attribute("scale"));
|
||
is >> scale.x >> scale.y;
|
||
}
|
||
|
||
TiXmlElement *bone = bones->FirstChildElement("Bone");
|
||
while(bone)
|
||
{
|
||
int idx = atoi(bone->Attribute("idx"));
|
||
int pidx = -1, rbp=0, cr=0, fh=0, fv=0;
|
||
|
||
std::string name;
|
||
Vector cp;
|
||
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"));
|
||
if (bone->Attribute("cp"))
|
||
{
|
||
SimpleIStringStream is(bone->Attribute("cp"));
|
||
is >> cp.x >> cp.y;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
std::string gfx = bone->Attribute("gfx");
|
||
Bone *newb = initBone(idx, gfx, pidx, rbp, name, cr, fh, fv, cp);
|
||
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("crects"))
|
||
{
|
||
SimpleIStringStream is(bone->Attribute("crects"));
|
||
int num = 0;
|
||
is >> num;
|
||
for (int i = 0; i < num; i++)
|
||
{
|
||
RectShape r;
|
||
int x, y, w, h;
|
||
is >> x >> y >> w >> h;
|
||
r.setCWH(x, y, w, h);
|
||
|
||
newb->collisionRects.push_back(r);
|
||
}
|
||
}
|
||
|
||
if (bone->Attribute("prt"))
|
||
{
|
||
newb->prt = bone->Attribute("prt");
|
||
SimpleIStringStream is(newb->prt);
|
||
int slot;
|
||
while (is >> slot)
|
||
{
|
||
std::string pfile;
|
||
is >> pfile;
|
||
// add particle system + load
|
||
newb->emitters[slot] = new ParticleEffect;
|
||
ParticleEffect *e = newb->emitters[slot];
|
||
newb->addChild(e, PM_POINTER);
|
||
e->load(pfile);
|
||
// hack for now:
|
||
//e->start();
|
||
}
|
||
}
|
||
TiXmlElement *fr=0;
|
||
fr = bone->FirstChildElement("Frame");
|
||
int frc=0;
|
||
while(fr)
|
||
{
|
||
Quad *q=0;
|
||
std::string gfx;
|
||
if (fr->Attribute("gfx"))
|
||
{
|
||
gfx = fr->Attribute("gfx");
|
||
q = newb->addFrame(gfx);
|
||
}
|
||
if (fr->Attribute("pass"))
|
||
{
|
||
if (q)
|
||
{
|
||
q->setRenderPass(atoi(fr->Attribute("pass")));
|
||
}
|
||
}
|
||
fr = fr->NextSiblingElement("Frame");
|
||
frc++;
|
||
}
|
||
if (frc)
|
||
{
|
||
newb->showFrame(0);
|
||
}
|
||
if (bone->Attribute("pass"))
|
||
{
|
||
newb->setRenderPass(atoi(bone->Attribute("pass")));
|
||
}
|
||
if (bone->Attribute("gc"))
|
||
{
|
||
newb->generateCollisionMask = atoi(bone->Attribute("gc"));
|
||
}
|
||
if (bone->Attribute("rq"))
|
||
{
|
||
newb->renderQuad = newb->fileRenderQuad = atoi(bone->Attribute("rq"));
|
||
}
|
||
if (bone->Attribute("io"))
|
||
{
|
||
SimpleIStringStream is(bone->Attribute("io"));
|
||
is >> newb->internalOffset.x >> newb->internalOffset.y;
|
||
}
|
||
|
||
if (bone->Attribute("strip"))
|
||
{
|
||
SimpleIStringStream is(bone->Attribute("strip"));
|
||
bool vert;
|
||
int num;
|
||
is >> vert >> num;
|
||
newb->createStrip(vert, num);
|
||
}
|
||
if (bone->Attribute("sz"))
|
||
{
|
||
float sx, sy;
|
||
SimpleIStringStream is(bone->Attribute("sz"));
|
||
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")
|
||
newb->blendType = blendType = BLEND_ADD;
|
||
}
|
||
|
||
if (bone->Attribute("alpha"))
|
||
{
|
||
float a=1.0;
|
||
SimpleIStringStream is(bone->Attribute("alpha"));
|
||
is >> a;
|
||
newb->alpha = a;
|
||
}
|
||
|
||
if (bone->Attribute("alphaMod"))
|
||
{
|
||
float a=1.0;
|
||
SimpleIStringStream is(bone->Attribute("alphaMod"));
|
||
is >> a;
|
||
newb->alphaMod = a;
|
||
}
|
||
|
||
if (bone->Attribute("segs"))
|
||
{
|
||
int x, y;
|
||
float dgox, dgoy, dgmx, dgmy, dgtm;
|
||
bool dgo;
|
||
SimpleIStringStream is(bone->Attribute("segs"));
|
||
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"));
|
||
in >> newb->color.x >> newb->color.y >> newb->color.z;
|
||
}
|
||
bone = bone->NextSiblingElement("Bone");
|
||
}
|
||
// attach bones
|
||
for (int i = 0; i < this->bones.size(); i++)
|
||
{
|
||
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());
|
||
}
|
||
else
|
||
{
|
||
pb->addChild(b, PM_POINTER);
|
||
}
|
||
}
|
||
else
|
||
addChild(b, PM_POINTER);
|
||
}
|
||
}
|
||
|
||
animLayers.clear();
|
||
TiXmlElement *animationLayers = xml.FirstChildElement("AnimationLayers");
|
||
if (animationLayers)
|
||
{
|
||
TiXmlElement *animationLayer = animationLayers->FirstChildElement("AnimationLayer");
|
||
while (animationLayer)
|
||
{
|
||
AnimationLayer newAnimationLayer;
|
||
if (animationLayer->Attribute("ignore"))
|
||
{
|
||
SimpleIStringStream is(animationLayer->Attribute("ignore"));
|
||
int t;
|
||
while (is >> t)
|
||
{
|
||
newAnimationLayer.ignoreBones.push_back(t);
|
||
}
|
||
}
|
||
if (animationLayer->Attribute("include"))
|
||
{
|
||
SimpleIStringStream is(animationLayer->Attribute("include"));
|
||
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();
|
||
TiXmlElement *animations = xml.FirstChildElement("Animations");
|
||
if (animations)
|
||
{
|
||
TiXmlElement *animation = animations->FirstChildElement("Animation");
|
||
while(animation)
|
||
{
|
||
Animation newAnimation;
|
||
newAnimation.name = animation->Attribute("name");
|
||
stringToLower(newAnimation.name);
|
||
|
||
TiXmlElement *key = animation->FirstChildElement("Key");
|
||
while (key)
|
||
{
|
||
SkeletalKeyframe newSkeletalKeyframe;
|
||
if (key->Attribute("e"))
|
||
{
|
||
float time;
|
||
SimpleIStringStream is(key->Attribute("e"));
|
||
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)
|
||
{
|
||
b.strip.resize(strip);
|
||
for (int i = 0; i < b.strip.size(); i++)
|
||
{
|
||
is >> b.strip[i].x >> b.strip[i].y;
|
||
//b.strip[i].y *= 10;
|
||
}
|
||
}
|
||
if (key->Attribute("sz"))
|
||
{
|
||
SimpleIStringStream is2(key->Attribute("sz"));
|
||
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;
|
||
SimpleIStringStream is(key->Attribute("d"));
|
||
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");
|
||
SimpleIStringStream is(newSkeletalKeyframe.cmd);
|
||
int bidx;
|
||
while (is >> bidx)
|
||
{
|
||
Bone *b = this->getBoneByIdx(bidx);
|
||
if (b)
|
||
{
|
||
BoneCommand bcmd;
|
||
bcmd.parse(b, is);
|
||
newSkeletalKeyframe.commands.push_back(bcmd);
|
||
}
|
||
}
|
||
}
|
||
// generate empty bone keys
|
||
for (int i = 0; i < this->bones.size(); i++)
|
||
{
|
||
if (newSkeletalKeyframe.getBoneKeyframe(this->bones[i]->boneIdx))
|
||
{
|
||
}
|
||
else
|
||
{
|
||
BoneKeyframe b;
|
||
b.idx = this->bones[i]->boneIdx;
|
||
newSkeletalKeyframe.keyframes.push_back(b);
|
||
}
|
||
}
|
||
newAnimation.keyframes.push_back(newSkeletalKeyframe);
|
||
key = key->NextSiblingElement("Key");
|
||
}
|
||
animation = animation->NextSiblingElement("Animation");
|
||
this->animations.push_back(newAnimation);
|
||
}
|
||
}
|
||
}
|
||
|
||
Animation *SkeletalSprite::getCurrentAnimation(int layer)
|
||
{
|
||
return animLayers[layer].getCurrentAnimation();
|
||
}
|
||
|
||
void SkeletalSprite::setTime(float time, int layer)
|
||
{
|
||
animLayers[layer].timer = time;
|
||
}
|
||
|
||
// hack:
|
||
// calculate based on frames
|
||
const int lerpAvg = 3;
|
||
|
||
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;
|
||
|
||
/*
|
||
if (key1 == key2)
|
||
stopAnimation();
|
||
*/
|
||
|
||
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())
|
||
{
|
||
for (int i = 0; i < key2->commands.size(); i++)
|
||
{
|
||
key2->commands[i].run();
|
||
}
|
||
}
|
||
if (s->animKeyNotify)
|
||
{
|
||
s->animKeyNotify->onAnimationKeyPassed(getCurrentAnimation()->getSkeletalKeyframeIndex(lastNewKey));
|
||
}
|
||
}
|
||
lastNewKey = key2;
|
||
|
||
bool c = 0;
|
||
for (int i = 0; i < s->bones.size(); i++)
|
||
{
|
||
int idx = s->bones[i]->boneIdx;
|
||
Bone *b = s->bones[i];
|
||
|
||
if (b->segmentChain == 1)
|
||
{
|
||
b->updateSegments();
|
||
}
|
||
if (b->segmentChain < 2)
|
||
{
|
||
c=0;
|
||
if (!ignoreBones.empty())
|
||
{
|
||
for (int j = 0; j < ignoreBones.size(); j++)
|
||
{
|
||
if (idx == ignoreBones[j])
|
||
{ c=1; break; }
|
||
}
|
||
}
|
||
else if (!includeBones.empty())
|
||
{
|
||
c = 1;
|
||
for (int j = 0; j < includeBones.size(); j++)
|
||
{
|
||
if (idx == includeBones[j])
|
||
{ c=0; break; }
|
||
}
|
||
}
|
||
if (b->animated==Bone::ANIM_NONE)
|
||
{
|
||
c = 1;
|
||
}
|
||
if (!c)
|
||
{
|
||
|
||
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;
|
||
if (b->animated==Bone::ANIM_ALL || b->animated==Bone::ANIM_POS)
|
||
b->position = p;
|
||
if (b->animated==Bone::ANIM_ALL || b->animated==Bone::ANIM_ROT)
|
||
b->rotation.z = rot;
|
||
}
|
||
else
|
||
{
|
||
int lerpType = key2->lerpType;
|
||
//k(0)×(2u3-3u2+1) + k(1)×(3u2-2u3)
|
||
if (b->animated==Bone::ANIM_ALL || b->animated==Bone::ANIM_POS)
|
||
{
|
||
b->position = Vector(lerp(bkey1->x, bkey2->x, dt, lerpType), lerp(bkey1->y, bkey2->y, dt, lerpType));
|
||
}
|
||
if (b->animated==Bone::ANIM_ALL || b->animated==Bone::ANIM_ROT)
|
||
{
|
||
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);
|
||
}
|
||
if (b->animated==Bone::ANIM_ALL && !b->changeStrip.empty())
|
||
{
|
||
if (bkey2->strip.size() < b->changeStrip.size())
|
||
bkey2->strip.resize(b->changeStrip.size());
|
||
if (bkey1->strip.size() < b->changeStrip.size())
|
||
bkey1->strip.resize(b->changeStrip.size());
|
||
for (int i = 0; i < b->changeStrip.size(); i++)
|
||
{
|
||
b->changeStrip[i] = Vector(lerp(bkey1->strip[i].x, bkey2->strip[i].x, dt, lerpType), lerp(bkey1->strip[i].y, bkey2->strip[i].y, dt, lerpType));
|
||
}
|
||
b->setGridPoints(b->stripVert, b->changeStrip);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void SkeletalSprite::setFreeze(bool f)
|
||
{
|
||
frozen = f;
|
||
}
|
||
|
||
void SkeletalSprite::updateBones()
|
||
{
|
||
if (!frozen)
|
||
{
|
||
for (int i = 0; i < animLayers.size(); i++)
|
||
{
|
||
animLayers[i].updateBones();
|
||
}
|
||
}
|
||
/*
|
||
for (int i = animLayers.size()-1; i >= 0; i--)
|
||
{
|
||
if (animLayers[i].animating)
|
||
{
|
||
animLayers[i].updateBones();
|
||
return;
|
||
}
|
||
}
|
||
*/
|
||
|
||
}
|
||
|
||
bool SkeletalSprite::isAnimating(int layer)
|
||
{
|
||
return animLayers[layer].animating;
|
||
}
|
||
|
||
void SkeletalSprite::setTimeMultiplier(float t, int layer)
|
||
{
|
||
animLayers[layer].timeMultiplier = t;
|
||
}
|
||
|
||
Bone* SkeletalSprite::getSelectedBone(bool mouseBased)
|
||
{
|
||
if (!loaded) return 0;
|
||
if (mouseBased)
|
||
{
|
||
float closestDist = HUGE_VALF;
|
||
Bone *b = 0;
|
||
Vector p = core->mouse.position;
|
||
for (int i = 0; i < bones.size(); i++)
|
||
{
|
||
if (bones[i]->renderQuad || core->getShiftState())
|
||
{
|
||
bones[i]->color = Vector(1,1,1);
|
||
if (bones[i]->renderQuad && bones[i]->isCoordinateInsideWorld(p))
|
||
{
|
||
float dist = (bones[i]->getWorldPosition() - p).getSquaredLength2D();
|
||
if (dist <= closestDist)
|
||
{
|
||
closestDist = dist;
|
||
b = bones[i];
|
||
selectedBone = i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (b)
|
||
{
|
||
b->color = Vector(1,0,0);
|
||
}
|
||
return b;
|
||
}
|
||
// else
|
||
if (!bones.empty() && selectedBone >= 0 && selectedBone < bones.size())
|
||
return bones[selectedBone];
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
void SkeletalSprite::updateSelectedBoneColor()
|
||
{
|
||
for (int i = 0; i < bones.size(); i++)
|
||
{
|
||
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()
|
||
{
|
||
selectedBone++;
|
||
if (selectedBone >= bones.size())
|
||
selectedBone = 0;
|
||
updateSelectedBoneColor();
|
||
}
|
||
|
||
void SkeletalSprite::selectNextBone()
|
||
{
|
||
selectedBone--;
|
||
if (selectedBone < 0)
|
||
selectedBone = bones.size()-1;
|
||
updateSelectedBoneColor();
|
||
}
|
||
|
||
|