OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
pose.cpp
Go to the documentation of this file.
1#include "pose.h"
2
3#include <Tempest/Log>
4
5#include "utils/string_frm.h"
6#include "world/objects/npc.h"
7#include "world/world.h"
8#include "game/serialize.h"
9#include "skeleton.h"
10#include "animmath.h"
11
12#include <cmath>
13
14using namespace Tempest;
15
17 lay.reserve(4);
18 }
19
20uint8_t Pose::calcAniComb(const Vec3& dpos, float rotation) {
21 float l = std::sqrt(dpos.x*dpos.x+dpos.z*dpos.z);
22
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);
26
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);
29
30 // sides angle: +/- 30 height angle: +/- 45
31 return uint8_t(1u+cy*3u+cx);
32 }
33
34uint8_t Pose::calcAniCombVert(const Vec3& dpos) {
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);
38
39 if(dpos.y<-50)
40 cy = 0;
41 if(dpos.y>50)
42 cy = 2;
43
44 // height angle: +/- 25
45 return uint8_t(cy+1u);
46 }
47
48void Pose::save(Serialize &fout) {
49 uint8_t sz=uint8_t(lay.size());
50 fout.write(sz);
51 for(auto& i:lay) {
52 fout.write(i.seq->name,i.sAnim,i.bs,i.sBlend);
53 }
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);
59
60 for(auto& i:hasSamples)
61 fout.write(uint8_t(i));
62 for(auto& i:base)
63 fout.write(i);
64 for(auto& i:prev)
65 fout.write(i);
66 for(auto& i:tr)
67 fout.write(i);
68 }
69
70void Pose::load(Serialize &fin, const AnimationSolver& solver) {
71 std::string name;
72 uint8_t sz = uint8_t(lay.size());
73
74 fin.read(sz);
75 lay.resize(sz);
76 for(auto& i:lay) {
77 fin.read(name,i.sAnim,i.bs);
78 if(fin.version()>43)
79 fin.read(i.sBlend);
80 i.seq = solver.solveFrm(name);
81 }
82 fin.read(lastUpdate);
83 fin.read(combo.bits);
84 removeIf(lay,[](const Layer& l){
85 return l.seq==nullptr;
86 });
87 fin.read(name);
88 for(auto& i:lay) {
89 if(i.seq->name==name)
90 rotation = i.seq;
91 }
92 fin.read(itemUseSt,itemUseDestSt);
93 hasEvents = 0;
94 isFlyCombined = 0;
95 hasTransitions = 0;
96 for(auto& i:lay)
97 onAddLayer(i);
98 fin.read(headRotX,headRotY);
99 needToUpdate = true;
100
101 numBones = skeleton==nullptr ? 0 : skeleton->nodes.size();
102 for(auto& i:hasSamples)
103 fin.read(reinterpret_cast<uint8_t&>(i));
104 for(auto& i:base)
105 fin.read(i);
106 for(auto& i:prev)
107 fin.read(i);
108 for(auto& i:tr)
109 fin.read(i);
110 }
111
113 flag = f;
114 needToUpdate = true;
115 }
116
118 uint32_t b = BS_NONE;
119 for(auto& i:lay)
120 b = std::max(b,uint32_t(i.bs&(BS_MAX | BS_FLAG_MASK)));
121 return BodyState(b);
122 }
123
125 for(auto& i:lay)
126 if(i.bs==s)
127 return true;
128 return false;
129 }
130
132 for(auto& i:lay)
133 if((i.bs & (BS_FLAG_MASK | BS_MOD_MASK))==f)
134 return true;
135 return false;
136 }
137
139 if(skeleton==sk)
140 return;
141 skeleton = sk;
142 for(auto& i:tr)
143 i.identity();
144 if(skeleton!=nullptr) {
145 numBones = skeleton->tr.size();
146 } else {
147 numBones = 0;
148 }
149 for(auto& i:hasSamples)
150 i = S_None;
151 trY = skeleton->rootTr.y;
152 needToUpdate = true;
153 if(lay.size()>0) //TODO
154 Log::d("WARNING: ",__func__," animation adjustment is not implemented");
155 lay.clear();
156
157 if(skeleton!=nullptr)
158 mkSkeleton(Matrix4x4::mkIdentity());
159 }
160
161bool Pose::startAnim(const AnimationSolver& solver, const Animation::Sequence *sq, uint8_t comb, BodyState bs,
162 StartHint hint, uint64_t tickCount) {
163 if(sq==nullptr)
164 return false;
165 // NOTE: zero stands for no-comb, other numbers are comb-index+1
166 comb = std::min<uint8_t>(comb, uint8_t(sq->comb.size()));
167
168 const bool force = (hint&Force);
169
170 for(auto& i:lay)
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) {
176 i.bs = bs;
177 return true;
178 }
179 if(!interrupt && !finished)
180 return false;
181 if(i.bs==BS_ITEMINTERACT) {
182 stopItemStateAnim(solver,tickCount);
183 return false;
184 }
185 const Animation::Sequence* tr=nullptr;
186 if(i.seq->shortName!=nullptr && sq->shortName!=nullptr) {
187 string_frm tansition("T_",i.seq->shortName,"_2_",sq->shortName);
188 tr = solver.solveFrm(tansition);
189 }
190 if(tr==nullptr && sq->shortName!=nullptr && i.bs==BS_STAND) {
191 string_frm tansition("T_STAND_2_",sq->shortName);
192 tr = solver.solveFrm(tansition);
193 }
194 if(tr==nullptr && i.seq->shortName!=nullptr && bs==BS_STAND) {
195 string_frm tansition("T_",i.seq->shortName,"_2_STAND");
196 tr = solver.solveFrm(tansition);
197 bs = tr ? i.bs : bs;
198 }
199 onRemoveLayer(i);
200 i.seq = tr ? tr : sq;
201 i.sAnim = tickCount;
202 i.sBlend = 0;
203 i.comb = comb;
204 i.bs = bs;
205 onAddLayer(i);
206 return true;
207 }
208 addLayer(sq,bs,comb,tickCount);
209 return true;
210 }
211
212bool Pose::stopAnim(std::string_view name) {
213 bool done=false;
214 size_t ret=0;
215 for(size_t i=0;i<lay.size();++i) {
216 bool rm = (name.empty() || lay[i].seq->name==name);
217 if(itemUseSt!=0 && lay[i].bs==BS_ITEMINTERACT)
218 rm = false;
219
220 if(!rm) {
221 if(ret!=i)
222 lay[ret] = lay[i];
223 ret++;
224 } else {
225 onRemoveLayer(lay[i]);
226 done=true;
227 }
228 }
229 lay.resize(ret);
230 return done;
231 }
232
234 bool done=false;
235 size_t ret=0;
236 for(size_t i=0;i<lay.size();++i) {
237 if(lay[i].bs!=BS_RUN && lay[i].bs!=BS_SPRINT && lay[i].bs!=BS_SNEAK && lay[i].bs!=BS_WALK) {
238 if(ret!=i)
239 lay[ret] = lay[i];
240 ret++;
241 } else {
242 onRemoveLayer(lay[i]);
243 done=true;
244 }
245 }
246 lay.resize(ret);
247 return done;
248 }
249
251 size_t ret=0;
252 for(size_t i=0;i<lay.size();++i) {
253 if(BS_FLAG_INTERRUPTABLE & lay[i].bs) {
254 onRemoveLayer(lay[i]);
255 } else {
256 if(ret!=i)
257 lay[ret] = lay[i];
258 ret++;
259 }
260 }
261 lay.resize(ret);
262 }
263
265 for(auto& i:lay)
266 onRemoveLayer(i);
267 lay.clear();
268 }
269
270void Pose::processLayers(AnimationSolver& solver, uint64_t tickCount) {
271 if(hasTransitions==0)
272 return;
273
274 size_t ret = 0;
275 bool doSort = false;
276 for(size_t i=0; i<lay.size(); ++i) {
277 auto& l = lay[i];
278 if(l.seq->animCls==Animation::Transition && l.seq->isFinished(tickCount,l.sAnim,combo.len())) {
279 auto next = solveNext(solver,l);
280 if(next!=l.seq) {
281 needToUpdate = true;
282 onRemoveLayer(l);
283
284 if(next!=nullptr) {
285 if(l.seq==rotation)
286 rotation = next;
287 doSort = l.seq->layer!=next->layer;
288 auto bs = (l.bs & BS_MAX);
289 // WA for:
290 // 1. for swampshark, "T_STUMBLEB" has next animationn: "S_FISTRUN"
291 // 2. for some of jump-back animnations, also 'next' is not empty
292 if(bs==BS_STUMBLE || bs==BS_PARADE)
293 l.bs = BS_NONE;
294 l.seq = next;
295 l.sAnim = tickCount;
296 onAddLayer(l);
297 ret++;
298 }
299 continue;
300 }
301 }
302 if(ret!=i)
303 lay[ret] = lay[i];
304 ret++;
305 }
306 lay.resize(ret);
307
308 if(doSort) {
309 std::sort(lay.begin(),lay.end(),[](const Layer& a,const Layer& b){
310 return a.seq->layer<b.seq->layer;
311 });
312 }
313 }
314
315bool Pose::update(uint64_t tickCount, bool force) {
316 if(lay.size()==0) {
317 const bool ret = needToUpdate;
318 if(needToUpdate || lastUpdate==0)
319 mkSkeleton(pos);
320 needToUpdate = false;
321 lastUpdate = tickCount;
322 return ret;
323 }
324
325 bool needMkSkeleton = false;
326 if(lastUpdate!=tickCount || force) {
327 for(auto& i:lay) {
328 const Animation::Sequence* seq = i.seq;
329 if(0<i.comb && i.comb<=i.seq->comb.size()) {
330 if(auto sx = i.seq->comb[size_t(i.comb-1)])
331 seq = sx;
332 }
333
334 auto& d = *seq->data;
335 const size_t numFrames = d.numFrames;
336 if(numFrames==1 && !needToUpdate)
337 continue; //mobsi
338
339 needMkSkeleton |= updateFrame(*seq,i.bs,i.sBlend,lastUpdate,i.sAnim,tickCount);
340 }
341 lastUpdate = tickCount;
342 needToUpdate = needMkSkeleton;
343 }
344
345 if(needMkSkeleton || force) {
346 mkSkeleton(pos);
347 return true;
348 }
349 return false;
350 }
351
352bool Pose::updateFrame(const Animation::Sequence &s, BodyState bs, uint64_t sBlend,
353 uint64_t barrier, uint64_t sTime, uint64_t now) {
354 auto& d = *s.data;
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)
358 return false; // error
359
360 (void)barrier;
361 now = now-sTime;
362
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; //next
367
368 float a = float(frame%1000)/1000.f;
369
370 if(s.animCls==Animation::Loop) {
371 frameA%=d.numFrames;
372 frameB%=d.numFrames;
373 } else {
374 frameA = std::min<uint64_t>(frameA,d.numFrames-1);
375 frameB = std::min<uint64_t>(frameB,d.numFrames-1);
376 }
377
378 if(s.reverse) {
379 frameA = d.numFrames-1-frameA;
380 frameB = d.numFrames-1-frameB;
381 }
382
383 auto* sampleA = &d.samples[size_t(frameA*idSize)];
384 auto* sampleB = &d.samples[size_t(frameB*idSize)];
385
386 const uint64_t blendMax = std::max(s.blendOut,s.blendIn);
387 const uint64_t blend = std::max<uint64_t>(0, now-sBlend);
388
389 for(size_t i=0; i<idSize; ++i) {
390 size_t idx = d.nodeIndex[i];
391 if(idx>=numBones)
392 continue;
393 auto smp = mix(sampleA[i],sampleB[i],a);
394 if(i==0) {
395 if(bs==BS_CLIMB)
396 smp.position.y = trY;
397 else if(s.isFly())
398 smp.position.y = d.translate.y;
399 }
400
401 switch(hasSamples[idx]) {
402 case S_None:
403 hasSamples[idx] = S_Old;
404 base [idx] = smp;
405 break;
406 case S_Old:
407 hasSamples[idx] = S_Valid;
408 prev [idx] = base[idx];
409 [[fallthrough]];
410 case S_Valid:
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);
415 } else {
416 prev[idx] = smp;
417 base[idx] = smp;
418 }
419 break;
420 }
421 }
422 return true;
423 }
424
425void Pose::mkSkeleton(const Tempest::Matrix4x4& mt) {
426 if(skeleton==nullptr)
427 return;
428 Matrix4x4 m = mt;
429 m.translate(mkBaseTranslation());
430 if(skeleton->ordered)
431 implMkSkeleton(m); else
432 implMkSkeleton(m,size_t(-1));
433 }
434
435void Pose::implMkSkeleton(const Matrix4x4 &mt) {
436 if(skeleton==nullptr)
437 return;
438 auto& nodes = skeleton->nodes;
439 auto BIP01_HEAD = skeleton->BIP01_HEAD;
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;
443
445 tr[i] = tr[parent]*mat; else
446 tr[i] = mt*mat;
447
448 if(i==BIP01_HEAD && (headRotX!=0 || headRotY!=0)) {
449 Matrix4x4& m = tr[i];
450 m.rotateOY(headRotY);
451 m.rotateOX(headRotX);
452 }
453 }
454 }
455
456void Pose::implMkSkeleton(const Tempest::Matrix4x4 &mt, size_t parent) {
457 if(skeleton==nullptr)
458 return;
459 auto& nodes = skeleton->nodes;
460 for(size_t i=0;i<nodes.size();++i){
461 if(nodes[i].parent!=parent)
462 continue;
463 auto mat = hasSamples[i] ? mkMatrix(base[i]) : nodes[i].tr;
464 tr[i] = mt*mat;
465 implMkSkeleton(tr[i],i);
466 }
467 }
468
469const Animation::Sequence* Pose::solveNext(const AnimationSolver &solver, const Layer& lay) {
470 auto sq = lay.seq;
471
472 if((lay.bs & BS_ITEMINTERACT)==BS_ITEMINTERACT && itemUseSt!=itemUseDestSt) {
473 int sA = itemUseSt, sB = itemUseSt, nextState = 0;
474 if(itemUseSt<itemUseDestSt) {
475 sB++;
476 nextState = itemUseSt+1;
477 } else {
478 sB--;
479 nextState = itemUseSt-1;
480 }
481 char scheme[64]={};
482 sq->schemeName(scheme);
483
484 const Animation::Sequence* ret = nullptr;
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);
488 }
489
490 if(ret==nullptr) {
491 string_frm T_ID_Sa_2_Sb("T_",scheme,"_S",sA,"_2_S",sB);
492 ret = solver.solveFrm(T_ID_Sa_2_Sb);
493 }
494
495 if(ret==nullptr && itemUseDestSt>=0)
496 return sq;
497 itemUseSt = nextState;
498 return ret;
499 }
500
501 if(sq==rotation)
502 return sq;
503
504 return solver.solveNext(*sq);
505 }
506
507void Pose::addLayer(const Animation::Sequence *seq, BodyState bs, uint8_t comb, uint64_t tickCount) {
508 if(seq==nullptr)
509 return;
510 Layer l;
511 l.seq = seq;
512 l.sAnim = tickCount;
513 l.bs = bs;
514 l.comb = comb;
515 lay.push_back(l);
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;
519 });
520 }
521
522void Pose::onAddLayer(const Pose::Layer& l) {
523 if(hasLayerEvents(l))
524 hasEvents++;
525 if(l.seq->isFly())
526 isFlyCombined++;
527 if(l.seq->animCls==Animation::Transition)
528 hasTransitions++;
529 needToUpdate = true;
530 }
531
532void Pose::onRemoveLayer(const Pose::Layer &l) {
533 if(l.seq==rotation)
534 rotation=nullptr;
535 if(hasLayerEvents(l))
536 hasEvents--;
537 if(l.seq->animCls==Animation::Transition)
538 hasTransitions--;
539 if(l.seq->isFly())
540 isFlyCombined--;
541
542 for(auto id:l.seq->data->nodeIndex)
543 if(hasSamples[id]==S_Valid)
544 hasSamples[id] = S_Old;
545
546 for(auto& lx : lay) {
547 if(&lx==&l)
548 break;
549 lx.sBlend = lastUpdate-lx.sAnim;
550 }
551 }
552
553bool Pose::hasLayerEvents(const Pose::Layer& l) {
554 if(l.seq==nullptr)
555 return false;
556 return
557 l.seq->data->events.size()>0 ||
558 l.seq->data->mmStartAni.size()>0 ||
559 l.seq->data->gfx.size()>0;
560 }
561
562void Pose::processSfx(Npc &npc, uint64_t tickCount) {
563 for(auto& i:lay)
564 i.seq->processSfx(lastUpdate,i.sAnim,tickCount,&npc,nullptr);
565 }
566
567void Pose::processSfx(Interactive& mob, uint64_t tickCount) {
568 for(auto& i:lay)
569 i.seq->processSfx(lastUpdate,i.sAnim,tickCount,nullptr,&mob);
570 }
571
572void Pose::processPfx(MdlVisual& visual, World& world, uint64_t tickCount) {
573 for(auto& i:lay)
574 i.seq->processPfx(lastUpdate,i.sAnim,tickCount,visual,world);
575 }
576
577bool Pose::processEvents(uint64_t &barrier, uint64_t now, Animation::EvCount &ev) const {
578 if(hasEvents==0) {
579 barrier=now;
580 return false;
581 }
582
583 for(auto& i:lay)
584 i.seq->processEvents(barrier,i.sAnim,now,ev);
585 barrier=now;
586 return hasEvents>0;
587 }
588
589void Pose::setObjectMatrix(const Tempest::Matrix4x4& obj, bool sync) {
590 if(pos==obj)
591 return;
592 pos = obj;
593 if(sync)
594 mkSkeleton(pos); else
595 needToUpdate = true;
596 }
597
598Tempest::Vec3 Pose::animMoveSpeed(uint64_t tickCount, uint64_t dt) const {
599 for(size_t i=lay.size(); i>0; ) {
600 --i;
601 auto& lx = lay[i];
602 if(!lx.seq->data->hasMoveTr)
603 continue;
604 return lx.seq->speed(tickCount-lx.sAnim,dt);
605 }
606 return Tempest::Vec3();
607 }
608
609bool Pose::isDefParWindow(uint64_t tickCount) const {
610 for(auto& i:lay)
611 if(i.seq->isDefParWindow(tickCount-i.sAnim))
612 return true;
613 return false;
614 }
615
616bool Pose::isDefWindow(uint64_t tickCount) const {
617 for(auto& i:lay)
618 if(i.seq->isDefWindow(tickCount-i.sAnim))
619 return true;
620 return false;
621 }
622
623bool Pose::isDefence(uint64_t tickCount) const {
624 for(auto& i:lay) {
625 if(i.bs==BS_PARADE && i.seq->isDefWindow(tickCount-i.sAnim))
626 return true;
627 }
628 return false;
629 }
630
631bool Pose::isJumpBack(uint64_t tickCount) const {
632 for(auto& i:lay) {
633 if(i.bs==BS_PARADE && i.seq->data->defWindow.empty())
634 return true;
635 if(i.bs==BS_PARADE && i.seq->isDefWindow(tickCount-i.sAnim))
636 return true;
637 }
638 return false;
639 }
640
641bool Pose::isJumpAnim() const {
642 for(auto& i:lay) {
643 if(i.bs!=BS_JUMP || i.seq->animCls!=Animation::Transition)
644 continue;
645 return true;
646 }
647 return false;
648 }
649
650bool Pose::isFlyAnim() const {
651 return isFlyCombined>0;
652 }
653
654bool Pose::isStanding() const {
655 if(lay.size()!=1 || lay[0].seq->animCls==Animation::Transition)
656 return false;
657 auto& s = *lay[0].seq;
658 if(s.isIdle())
659 return true;
660 // check common idle animations
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";
668 }
669
670bool Pose::isPrehit(uint64_t now) const {
671 for(auto& i:lay)
672 if(i.seq->isPrehit(i.sAnim,now))
673 return true;
674 return false;
675 }
676
677bool Pose::isAttackAnim() const {
678 for(auto& i:lay)
679 if(i.seq->isAttackAnim())
680 return true;
681 return false;
682 }
683
684bool Pose::isIdle() const {
685 for(auto& i:lay)
686 if(!i.seq->isIdle())
687 return false;
688 return true;
689 }
690
691bool Pose::isInAnim(std::string_view sq) const {
692 for(auto& i:lay)
693 if(i.seq->name==sq)
694 return true;
695 return false;
696 }
697
698bool Pose::isInAnim(const Animation::Sequence *sq) const {
699 for(auto& i:lay)
700 if(i.seq==sq)
701 return true;
702 return false;
703 }
704
705bool Pose::hasAnim() const {
706 return lay.size()>0;
707 }
708
709uint64_t Pose::animationTotalTime() const {
710 uint64_t ret=0;
711 for(auto& i:lay)
712 ret = std::max(ret,uint64_t(i.seq->totalTime()));
713 return ret;
714 }
715
716uint64_t Pose::atkTotalTime() const {
717 uint16_t comboLen = combo.len();
718 uint64_t ret=0;
719 for(auto& i:lay)
720 ret = std::max(ret,uint64_t(i.seq->atkTotalTime(comboLen)));
721 return ret;
722 }
723
725 BodyState bs, uint64_t tickCount) {
726 if(sq==nullptr)
727 return nullptr;
728
729 Layer* prev = nullptr;
730 for(auto& i:lay)
731 if(i.seq->layer==sq->layer) {
732 prev = &i;
733 break;
734 }
735
736 if(prev==nullptr) {
737 combo = ComboState();
738 return nullptr;
739 }
740
741 auto& d = *prev->seq->data;
742 uint64_t t = tickCount-prev->sAnim;
743 size_t id = combo.len()*2;
744
745 if(0==d.defWindow.size() || 0==d.defHitEnd.size()) {
746 if(!startAnim(solver,sq,prev->comb,bs,Pose::NoHint,tickCount))
747 return nullptr;
748 combo = ComboState();
749 return sq;
750 }
751
752 if(sq->data->defHitEnd.size()==0) {
753 // hit -> block
754 startAnim(solver,sq,prev->comb,bs,Pose::Force,tickCount);
755 combo = ComboState();
756 return sq;
757 }
758
759 if(id+1>=d.defWindow.size()) {
760 combo.setBreak();
761 return nullptr;
762 }
763
764 if(!(d.defWindow[id+0]<t && t<=d.defWindow[id+1])) {
765 if(prev->seq->name==sq->name && sq->data->defHitEnd.size()>0)
766 combo.setBreak();
767 return nullptr;
768 }
769
770 if(combo.isBreak())
771 return nullptr;
772
773 if(prev->seq->name!=sq->name) {
774 startAnim(solver,sq,prev->comb,bs,Pose::Force,tickCount);
775 combo = ComboState();
776 return sq;
777 }
778
779 if(combo.len()<d.defHitEnd.size())
780 prev->sAnim = tickCount - d.defHitEnd[combo.len()];
781 combo.incLen();
782 return prev->seq;
783 }
784
785uint16_t Pose::comboLength() const {
786 return combo.len();
787 }
788
789const Tempest::Matrix4x4 Pose::rootNode() const {
790 size_t id = 0;
791 if(skeleton->rootNodes.size())
792 id = skeleton->rootNodes[0];
793 auto& nodes = skeleton->nodes;
794 return hasSamples[id] ? mkMatrix(base[id]) : nodes[id].tr;
795 }
796
797const Matrix4x4 Pose::rootBone() const {
798 size_t id = 0;
799 if(skeleton->rootNodes.size())
800 id = skeleton->rootNodes[0];
801 return tr[id];
802 }
803
804const Tempest::Matrix4x4& Pose::bone(size_t id) const {
805 return tr[id];
806 }
807
808size_t Pose::boneCount() const {
809 return numBones;
810 }
811
812size_t Pose::findNode(std::string_view b) const {
813 if(skeleton!=nullptr)
814 return skeleton->findNode(b);
815 return size_t(-1);
816 }
817
818void Pose::setHeadRotation(float dx, float dz) {
819 headRotX = dx;
820 headRotY = dz;
821 }
822
823Vec2 Pose::headRotation() const {
824 return Vec2(headRotX,headRotY);
825 }
826
827void Pose::setAnimRotate(const AnimationSolver &solver, Npc &npc, WeaponState fightMode, AnimationSolver::TurnType turn, int dir) {
828 const Animation::Sequence *sq = nullptr;
829 if(dir==0) {
830 if(rotation!=nullptr) {
831 if(stopAnim(rotation->name))
832 rotation = nullptr;
833 }
834 return;
835 }
836 if(bodyState()!=BS_STAND)
837 return;
838
839 enum AnimationSolver::Anim ani;
842 } else {
844 }
845
846 sq = solver.solveAnim(ani,fightMode,npc.walkMode(),*this);
847 if(rotation!=nullptr) {
848 if(sq!=nullptr && rotation->name==sq->name)
849 return;
850 if(!stopAnim(rotation->name))
851 return;
852 }
853 if(sq==nullptr)
854 return;
855 if(startAnim(solver,sq,0,BS_FLAG_FREEHANDS,Pose::NoHint,npc.world().tickCount())) {
856 rotation = sq;
857 return;
858 }
859 }
860
861const Animation::Sequence* Pose::setAnimItem(const AnimationSolver &solver, Npc &npc, std::string_view scheme, int state) {
862 string_frm T_ID_STAND_2_S0("T_",scheme,"_STAND_2_S0");
863 const Animation::Sequence *sq = solver.solveFrm(T_ID_STAND_2_S0);
864 if(startAnim(solver,sq,0,BS_ITEMINTERACT,Pose::NoHint,npc.world().tickCount())) {
865 itemUseSt = 0;
866 itemUseDestSt = state;
867 return sq;
868 }
869 return nullptr;
870 }
871
872bool Pose::stopItemStateAnim(const AnimationSolver& solver, uint64_t tickCount) {
873 if(itemUseSt<0)
874 return true;
875 itemUseDestSt = -1;
876 for(auto& i:lay)
877 if(i.bs==BS_ITEMINTERACT) {
878 auto next = solveNext(solver,i);
879 if(next==nullptr)
880 continue;
881 onRemoveLayer(i);
882 i.seq = next;
883 i.sAnim = tickCount;
884 onAddLayer(i);
885 }
886 return true;
887 }
888
889const Matrix4x4* Pose::transform() const {
890 return tr;
891 }
892
893Vec3 Pose::mkBaseTranslation() {
894 if(numBones==0)
895 return Vec3();
896
897 auto b0 = rootNode();
898
899 float dx = b0.at(3,0);
900 //float dy = b0.at(3,1) - translateY();
901 float dy = 0;
902 float dz = b0.at(3,2);
903
904 if((flag&NoTranslation)==NoTranslation)
905 dy = b0.at(3,1);
906
907 return Vec3(-dx,-dy,-dz);
908 }
909
910template<class T, class F>
911void Pose::removeIf(T &t, F f) {
912 size_t ret=0;
913 for(size_t i=0;i<t.size();++i) {
914 if( !f(t[i]) ) {
915 if(ret!=i)
916 t[ret] = t[i];
917 ret++;
918 }
919 }
920 t.resize(ret);
921 }
static Tempest::Matrix4x4 mkMatrix(float x, float y, float z, float w, float px, float py, float pz)
Definition animmath.cpp:55
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
Definition npc.h:25
auto walkMode() const
Definition npc.h:113
auto world() -> World &
Definition npc.cpp:624
bool update(uint64_t tickCount, bool force)
Definition pose.cpp:315
void stopAllAnim()
Definition pose.cpp:264
BodyState bodyState() const
Definition pose.cpp:117
const Tempest::Matrix4x4 * transform() const
Definition pose.cpp:889
StartHint
Definition pose.h:25
@ Force
Definition pose.h:27
@ NoHint
Definition pose.h:26
void setObjectMatrix(const Tempest::Matrix4x4 &obj, bool sync)
Definition pose.cpp:589
auto rootNode() const -> const Tempest::Matrix4x4
Definition pose.cpp:789
bool stopAnim(std::string_view name)
Definition pose.cpp:212
bool stopWalkAnim()
Definition pose.cpp:233
void setAnimRotate(const AnimationSolver &solver, Npc &npc, WeaponState fightMode, AnimationSolver::TurnType turn, int dir)
Definition pose.cpp:827
bool isFlyAnim() const
Definition pose.cpp:650
bool isJumpBack(uint64_t tickCount) const
Definition pose.cpp:631
void load(Serialize &fin, const AnimationSolver &solver)
Definition pose.cpp:70
static uint8_t calcAniComb(const Tempest::Vec3 &dpos, float rotation)
Definition pose.cpp:20
void setHeadRotation(float dx, float dz)
Definition pose.cpp:818
Flags
Definition pose.h:20
@ NoTranslation
Definition pose.h:22
bool startAnim(const AnimationSolver &solver, const Animation::Sequence *sq, uint8_t comb, BodyState bs, StartHint hint, uint64_t tickCount)
Definition pose.cpp:161
bool isDefence(uint64_t tickCount) const
Definition pose.cpp:623
bool hasAnim() const
Definition pose.cpp:705
bool isStanding() const
Definition pose.cpp:654
bool isAttackAnim() const
Definition pose.cpp:677
bool isInAnim(std::string_view sq) const
Definition pose.cpp:691
auto setAnimItem(const AnimationSolver &solver, Npc &npc, std::string_view scheme, int state) -> const Animation::Sequence *
Definition pose.cpp:861
size_t boneCount() const
Definition pose.cpp:808
Pose()
Definition pose.cpp:16
bool isJumpAnim() const
Definition pose.cpp:641
bool hasState(BodyState s) const
Definition pose.cpp:124
uint64_t animationTotalTime() const
Definition pose.cpp:709
bool stopItemStateAnim(const AnimationSolver &solver, uint64_t tickCount)
Definition pose.cpp:872
void processLayers(AnimationSolver &solver, uint64_t tickCount)
Definition pose.cpp:270
auto rootBone() const -> const Tempest::Matrix4x4
Definition pose.cpp:797
auto bone(size_t id) const -> const Tempest::Matrix4x4 &
Definition pose.cpp:804
void processPfx(MdlVisual &visual, World &world, uint64_t tickCount)
Definition pose.cpp:572
Tempest::Vec3 animMoveSpeed(uint64_t tickCount, uint64_t dt) const
Definition pose.cpp:598
bool isDefWindow(uint64_t tickCount) const
Definition pose.cpp:616
bool isPrehit(uint64_t now) const
Definition pose.cpp:670
bool processEvents(uint64_t &barrier, uint64_t now, Animation::EvCount &ev) const
Definition pose.cpp:577
Tempest::Vec2 headRotation() const
Definition pose.cpp:823
static uint8_t calcAniCombVert(const Tempest::Vec3 &dpos)
Definition pose.cpp:34
bool isDefParWindow(uint64_t tickCount) const
Definition pose.cpp:609
void processSfx(Npc &npc, uint64_t tickCount)
Definition pose.cpp:562
void setSkeleton(const Skeleton *sk)
Definition pose.cpp:138
auto continueCombo(const AnimationSolver &solver, const Animation::Sequence *sq, BodyState bs, uint64_t tickCount) -> const Animation::Sequence *
Definition pose.cpp:724
bool isIdle() const
Definition pose.cpp:684
void interrupt()
Definition pose.cpp:250
uint64_t atkTotalTime() const
Definition pose.cpp:716
uint16_t comboLength() const
Definition pose.cpp:785
void save(Serialize &fout)
Definition pose.cpp:48
bool hasStateFlag(BodyState f) const
Definition pose.cpp:131
void setFlags(Flags f)
Definition pose.cpp:112
size_t findNode(std::string_view b) const
Definition pose.cpp:812
static const size_t MAX_NUM_SKELETAL_NODES
Definition resources.h:52
void write(const Arg &... a)
Definition serialize.h:76
uint16_t version() const
Definition serialize.h:51
void read(Arg &... a)
Definition serialize.h:81
std::vector< size_t > rootNodes
Definition skeleton.h:23
bool ordered
Definition skeleton.h:21
size_t findNode(std::string_view name, size_t def=size_t(-1)) const
Definition skeleton.cpp:52
std::vector< Node > nodes
Definition skeleton.h:22
Tempest::Vec3 rootTr
Definition skeleton.h:25
size_t BIP01_HEAD
Definition skeleton.h:27
std::vector< Tempest::Matrix4x4 > tr
Definition skeleton.h:24
Definition world.h:31
BodyState
Definition constants.h:140
@ BS_SNEAK
Definition constants.h:156
@ BS_MOD_MASK
Definition constants.h:152
@ BS_FLAG_INTERRUPTABLE
Definition constants.h:149
@ BS_NONE
Definition constants.h:141
@ BS_PARADE
Definition constants.h:181
@ BS_CLIMB
Definition constants.h:163
@ BS_RUN
Definition constants.h:157
@ BS_FLAG_MASK
Definition constants.h:153
@ BS_JUMP
Definition constants.h:162
@ BS_FLAG_FREEHANDS
Definition constants.h:150
@ BS_MAX
Definition constants.h:185
@ BS_STUMBLE
Definition constants.h:175
@ BS_SPRINT
Definition constants.h:158
@ BS_ITEMINTERACT
Definition constants.h:168
@ BS_WALK
Definition constants.h:155
@ BS_STAND
Definition constants.h:186
WeaponState
Definition constants.h:191
static float mix(float x, float y, float a)
const char * shortName
Definition animation.h:104
bool isFly() const
Definition animation.h:82
std::string name
Definition animation.h:103
bool isIdle() const
Definition animation.h:83
std::vector< const Sequence * > comb
Definition animation.h:116
std::shared_ptr< AnimData > data
Definition animation.h:117