14using namespace Tempest;
21 float l = std::sqrt(dpos.x*dpos.x+dpos.z*dpos.z);
23 float dir = 90+180.f*std::atan2(dpos.z,dpos.x)/float(M_PI);
24 float aXZ = (rotation-dir);
25 float aY = -std::atan2(dpos.y,l)*180.f/float(M_PI);
27 uint8_t cx = (aXZ<-30.f) ? 0 : (aXZ<=30.f ? 1 : 2);
28 uint8_t cy = (aY <-45.f) ? 0 : (aY <=45.f ? 1 : 2);
31 return uint8_t(1u+cy*3u+cx);
35 float l = std::sqrt(dpos.x*dpos.x+dpos.z*dpos.z);
36 float aY = 180.f*std::atan2(dpos.y,l)/float(M_PI);
37 uint8_t cy = (aY <-25.f) ? 0 : (aY <=25.f ? 1 : 2);
45 return uint8_t(cy+1u);
49 uint8_t sz=uint8_t(lay.size());
52 fout.
write(i.seq->name,i.sAnim,i.bs,i.sBlend);
54 fout.
write(lastUpdate);
55 fout.
write(combo.bits);
56 fout.
write(rotation ? rotation->
name :
"");
57 fout.
write(itemUseSt,itemUseDestSt);
58 fout.
write(headRotX,headRotY);
60 for(
auto& i:hasSamples)
61 fout.
write(uint8_t(i));
72 uint8_t sz = uint8_t(lay.size());
77 fin.
read(name,i.sAnim,i.bs);
84 removeIf(lay,[](
const Layer& l){
85 return l.seq==
nullptr;
92 fin.
read(itemUseSt,itemUseDestSt);
98 fin.
read(headRotX,headRotY);
101 numBones = skeleton==
nullptr ? 0 : skeleton->
nodes.size();
102 for(
auto& i:hasSamples)
103 fin.
read(
reinterpret_cast<uint8_t&
>(i));
144 if(skeleton!=
nullptr) {
145 numBones = skeleton->
tr.size();
149 for(
auto& i:hasSamples)
154 Log::d(
"WARNING: ",__func__,
" animation adjustment is not implemented");
157 if(skeleton!=
nullptr)
158 mkSkeleton(Matrix4x4::mkIdentity());
166 comb = std::min<uint8_t>(comb, uint8_t(sq->
comb.size()));
168 const bool force = (hint&
Force);
171 if(i.seq->layer==sq->
layer) {
172 const bool hasNext = !i.seq->next.empty();
173 const bool finished = i.seq->isFinished(tickCount,i.sAnim,combo.len()) && !hasNext && (i.seq->animCls!=
Animation::Loop);
174 const bool interrupt = force || i.seq->canInterrupt(tickCount,i.sAnim,combo.len());
175 if(i.seq==sq && i.
comb==comb && !finished) {
186 if(i.seq->shortName!=
nullptr && sq->
shortName!=
nullptr) {
195 string_frm tansition(
"T_",i.seq->shortName,
"_2_STAND");
200 i.seq = tr ? tr : sq;
208 addLayer(sq,bs,comb,tickCount);
215 for(
size_t i=0;i<lay.size();++i) {
216 bool rm = (name.empty() || lay[i].seq->name==name);
225 onRemoveLayer(lay[i]);
236 for(
size_t i=0;i<lay.size();++i) {
242 onRemoveLayer(lay[i]);
252 for(
size_t i=0;i<lay.size();++i) {
254 onRemoveLayer(lay[i]);
271 if(hasTransitions==0)
276 for(
size_t i=0; i<lay.size(); ++i) {
279 auto next = solveNext(solver,l);
287 doSort = l.seq->
layer!=next->layer;
288 auto bs = (l.bs &
BS_MAX);
309 std::sort(lay.begin(),lay.end(),[](
const Layer& a,
const Layer& b){
310 return a.seq->layer<b.seq->layer;
317 const bool ret = needToUpdate;
318 if(needToUpdate || lastUpdate==0)
320 needToUpdate =
false;
321 lastUpdate = tickCount;
325 bool needMkSkeleton =
false;
326 if(lastUpdate!=tickCount || force) {
329 if(0<i.comb && i.comb<=i.seq->comb.size()) {
330 if(
auto sx = i.seq->comb[
size_t(i.comb-1)])
334 auto& d = *seq->
data;
335 const size_t numFrames = d.numFrames;
336 if(numFrames==1 && !needToUpdate)
339 needMkSkeleton |= updateFrame(*seq,i.bs,i.sBlend,lastUpdate,i.sAnim,tickCount);
341 lastUpdate = tickCount;
342 needToUpdate = needMkSkeleton;
345 if(needMkSkeleton || force) {
353 uint64_t barrier, uint64_t sTime, uint64_t now) {
355 const size_t idSize = d.nodeIndex.size();
356 const size_t numFrames = d.numFrames;
357 if(numFrames==0 || idSize==0 || d.samples.size()%idSize!=0)
363 float fpsRate = d.fpsRate;
364 uint64_t frame = uint64_t(
float(now)*fpsRate);
365 uint64_t frameA = frame/1000;
366 uint64_t frameB = frame/1000+1;
368 float a = float(frame%1000)/1000.f;
374 frameA = std::min<uint64_t>(frameA,d.numFrames-1);
375 frameB = std::min<uint64_t>(frameB,d.numFrames-1);
379 frameA = d.numFrames-1-frameA;
380 frameB = d.numFrames-1-frameB;
383 auto* sampleA = &d.samples[size_t(frameA*idSize)];
384 auto* sampleB = &d.samples[size_t(frameB*idSize)];
387 const uint64_t blend = std::max<uint64_t>(0, now-sBlend);
389 for(
size_t i=0; i<idSize; ++i) {
390 size_t idx = d.nodeIndex[i];
393 auto smp =
mix(sampleA[i],sampleB[i],a);
396 smp.position.y = trY;
398 smp.position.y = d.translate.y;
401 switch(hasSamples[idx]) {
403 hasSamples[idx] = S_Old;
407 hasSamples[idx] = S_Valid;
408 prev [idx] = base[idx];
411 if(blend < blendMax) {
412 float a2 = float(blend)/float(blendMax);
413 assert(0.f<=a2 && a2<=1.f);
414 base[idx] =
mix(prev[idx],smp,a2);
425void Pose::mkSkeleton(
const Tempest::Matrix4x4& mt) {
426 if(skeleton==
nullptr)
429 m.translate(mkBaseTranslation());
431 implMkSkeleton(m);
else
432 implMkSkeleton(m,
size_t(-1));
435void Pose::implMkSkeleton(
const Matrix4x4 &mt) {
436 if(skeleton==
nullptr)
438 auto& nodes = skeleton->
nodes;
440 for(
size_t i=0; i<nodes.size(); ++i) {
441 size_t parent = nodes[i].parent;
442 auto mat = hasSamples[i] ?
mkMatrix(base[i]) : nodes[i].tr;
445 tr[i] = tr[parent]*mat;
else
448 if(i==BIP01_HEAD && (headRotX!=0 || headRotY!=0)) {
449 Matrix4x4& m = tr[i];
450 m.rotateOY(headRotY);
451 m.rotateOX(headRotX);
456void Pose::implMkSkeleton(
const Tempest::Matrix4x4 &mt,
size_t parent) {
457 if(skeleton==
nullptr)
459 auto& nodes = skeleton->
nodes;
460 for(
size_t i=0;i<nodes.size();++i){
461 if(nodes[i].parent!=parent)
463 auto mat = hasSamples[i] ?
mkMatrix(base[i]) : nodes[i].tr;
465 implMkSkeleton(tr[i],i);
473 int sA = itemUseSt, sB = itemUseSt, nextState = 0;
474 if(itemUseSt<itemUseDestSt) {
476 nextState = itemUseSt+1;
479 nextState = itemUseSt-1;
482 sq->schemeName(scheme);
485 if(itemUseSt>itemUseDestSt) {
486 string_frm T_ID_SX_2_STAND(
"T_",scheme,
"_S",itemUseSt,
"_2_STAND");
487 ret = solver.
solveFrm(T_ID_SX_2_STAND);
491 string_frm T_ID_Sa_2_Sb(
"T_",scheme,
"_S",sA,
"_2_S",sB);
492 ret = solver.
solveFrm(T_ID_Sa_2_Sb);
495 if(ret==
nullptr && itemUseDestSt>=0)
497 itemUseSt = nextState;
516 onAddLayer(lay.back());
517 std::sort(lay.begin(),lay.end(),[](
const Layer& a,
const Layer& b){
518 return a.seq->layer<b.seq->layer;
522void Pose::onAddLayer(
const Pose::Layer& l) {
523 if(hasLayerEvents(l))
532void Pose::onRemoveLayer(
const Pose::Layer &l) {
535 if(hasLayerEvents(l))
542 for(
auto id:l.seq->data->nodeIndex)
543 if(hasSamples[id]==S_Valid)
544 hasSamples[id] = S_Old;
546 for(
auto& lx : lay) {
549 lx.sBlend = lastUpdate-lx.sAnim;
553bool Pose::hasLayerEvents(
const Pose::Layer& l) {
557 l.seq->data->events.size()>0 ||
558 l.seq->data->mmStartAni.size()>0 ||
559 l.seq->data->gfx.size()>0;
564 i.seq->processSfx(lastUpdate,i.sAnim,tickCount,&npc,
nullptr);
569 i.seq->processSfx(lastUpdate,i.sAnim,tickCount,
nullptr,&mob);
574 i.seq->processPfx(lastUpdate,i.sAnim,tickCount,visual,world);
584 i.seq->processEvents(barrier,i.sAnim,now,ev);
594 mkSkeleton(pos);
else
599 for(
size_t i=lay.size(); i>0; ) {
602 if(!lx.seq->data->hasMoveTr)
604 return lx.seq->speed(tickCount-lx.sAnim,dt);
606 return Tempest::Vec3();
611 if(i.seq->isDefParWindow(tickCount-i.sAnim))
618 if(i.seq->isDefWindow(tickCount-i.sAnim))
625 if(i.bs==
BS_PARADE && i.seq->isDefWindow(tickCount-i.sAnim))
633 if(i.bs==
BS_PARADE && i.seq->data->defWindow.empty())
635 if(i.bs==
BS_PARADE && i.seq->isDefWindow(tickCount-i.sAnim))
651 return isFlyCombined>0;
657 auto& s = *lay[0].seq;
661 return s.
name==
"S_FISTRUN" || s.
name==
"S_MAGRUN" ||
662 s.
name==
"S_1HRUN" || s.
name==
"S_BOWRUN" ||
663 s.
name==
"S_2HRUN" || s.
name==
"S_CBOWRUN" ||
664 s.
name==
"S_RUN" || s.
name==
"S_WALK" ||
665 s.
name==
"S_FISTWALK" || s.
name==
"S_MAGWALK" ||
666 s.
name==
"S_1HWALK" || s.
name==
"S_BOWWALK" ||
667 s.
name==
"S_2HWALK" || s.
name==
"S_CBOWWALK";
672 if(i.seq->isPrehit(i.sAnim,now))
679 if(i.seq->isAttackAnim())
712 ret = std::max(ret,uint64_t(i.seq->totalTime()));
717 uint16_t comboLen = combo.len();
720 ret = std::max(ret,uint64_t(i.seq->atkTotalTime(comboLen)));
729 Layer* prev =
nullptr;
731 if(i.seq->layer==sq->
layer) {
737 combo = ComboState();
741 auto& d = *prev->seq->data;
742 uint64_t t = tickCount-prev->sAnim;
743 size_t id = combo.len()*2;
745 if(0==d.defWindow.size() || 0==d.defHitEnd.size()) {
748 combo = ComboState();
752 if(sq->
data->defHitEnd.size()==0) {
755 combo = ComboState();
759 if(
id+1>=d.defWindow.size()) {
764 if(!(d.defWindow[
id+0]<t && t<=d.defWindow[
id+1])) {
765 if(prev->seq->name==sq->
name && sq->
data->defHitEnd.size()>0)
773 if(prev->seq->name!=sq->
name) {
775 combo = ComboState();
779 if(combo.len()<d.defHitEnd.size())
780 prev->sAnim = tickCount - d.defHitEnd[combo.len()];
793 auto& nodes = skeleton->
nodes;
794 return hasSamples[id] ?
mkMatrix(base[
id]) : nodes[id].tr;
813 if(skeleton!=
nullptr)
824 return Vec2(headRotX,headRotY);
830 if(rotation!=
nullptr) {
847 if(rotation!=
nullptr) {
848 if(sq!=
nullptr && rotation->
name==sq->
name)
862 string_frm T_ID_STAND_2_S0(
"T_",scheme,
"_STAND_2_S0");
866 itemUseDestSt = state;
878 auto next = solveNext(solver,i);
893Vec3 Pose::mkBaseTranslation() {
899 float dx = b0.at(3,0);
902 float dz = b0.at(3,2);
907 return Vec3(-dx,-dy,-dz);
910template<
class T,
class F>
911void Pose::removeIf(T &t, F f) {
913 for(
size_t i=0;i<t.size();++i) {
static Tempest::Matrix4x4 mkMatrix(float x, float y, float z, float w, float px, float py, float pz)
const Animation::Sequence * solveNext(const Animation::Sequence &sq) const
const Animation::Sequence * solveFrm(std::string_view format) const
const Animation::Sequence * solveAnim(Anim a, WeaponState st, WalkBit wlk, const Pose &pose) const
bool update(uint64_t tickCount, bool force)
BodyState bodyState() const
const Tempest::Matrix4x4 * transform() const
void setObjectMatrix(const Tempest::Matrix4x4 &obj, bool sync)
auto rootNode() const -> const Tempest::Matrix4x4
bool stopAnim(std::string_view name)
void setAnimRotate(const AnimationSolver &solver, Npc &npc, WeaponState fightMode, AnimationSolver::TurnType turn, int dir)
bool isJumpBack(uint64_t tickCount) const
void load(Serialize &fin, const AnimationSolver &solver)
static uint8_t calcAniComb(const Tempest::Vec3 &dpos, float rotation)
void setHeadRotation(float dx, float dz)
bool startAnim(const AnimationSolver &solver, const Animation::Sequence *sq, uint8_t comb, BodyState bs, StartHint hint, uint64_t tickCount)
bool isDefence(uint64_t tickCount) const
bool isAttackAnim() const
bool isInAnim(std::string_view sq) const
auto setAnimItem(const AnimationSolver &solver, Npc &npc, std::string_view scheme, int state) -> const Animation::Sequence *
bool hasState(BodyState s) const
uint64_t animationTotalTime() const
bool stopItemStateAnim(const AnimationSolver &solver, uint64_t tickCount)
void processLayers(AnimationSolver &solver, uint64_t tickCount)
auto rootBone() const -> const Tempest::Matrix4x4
auto bone(size_t id) const -> const Tempest::Matrix4x4 &
void processPfx(MdlVisual &visual, World &world, uint64_t tickCount)
Tempest::Vec3 animMoveSpeed(uint64_t tickCount, uint64_t dt) const
bool isDefWindow(uint64_t tickCount) const
bool isPrehit(uint64_t now) const
bool processEvents(uint64_t &barrier, uint64_t now, Animation::EvCount &ev) const
Tempest::Vec2 headRotation() const
static uint8_t calcAniCombVert(const Tempest::Vec3 &dpos)
bool isDefParWindow(uint64_t tickCount) const
void processSfx(Npc &npc, uint64_t tickCount)
void setSkeleton(const Skeleton *sk)
auto continueCombo(const AnimationSolver &solver, const Animation::Sequence *sq, BodyState bs, uint64_t tickCount) -> const Animation::Sequence *
uint64_t atkTotalTime() const
uint16_t comboLength() const
void save(Serialize &fout)
bool hasStateFlag(BodyState f) const
size_t findNode(std::string_view b) const
static const size_t MAX_NUM_SKELETAL_NODES
void write(const Arg &... a)
std::vector< size_t > rootNodes
size_t findNode(std::string_view name, size_t def=size_t(-1)) const
std::vector< Node > nodes
std::vector< Tempest::Matrix4x4 > tr
static float mix(float x, float y, float a)
std::vector< const Sequence * > comb
std::shared_ptr< AnimData > data