2
0

Split Animations.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. SplitAnimation SplitAnim;
  5. /******************************************************************************/
  6. /******************************************************************************/
  7. void SplitAnimation::Anim::Remove(Anim &anim) {SplitAnim.anims.removeData(&anim, true); SplitAnim.setList();}
  8. SplitAnimation::Anim::Anim()
  9. {
  10. SplitAnim.region+=remove.create().func(Remove, T).focusable(false).desc("Remove this split"); remove.image="Gui/close.img";
  11. SplitAnim.region+=name.create().desc("Name" );
  12. SplitAnim.region+=from.create().desc("Start KeyFrame");
  13. SplitAnim.region+=to .create().desc( "End KeyFrame");
  14. SplitAnim.region+=loop.create().desc("Loop Animation");
  15. }
  16. void SplitAnimation::Anim::pos(flt y)
  17. {
  18. flt h=0.05f, total_width=SplitAnim.region.rect().w()-SplitAnim.region.slidebarSize(), frame_width=h*4;
  19. remove.rect(Rect_LU(Vec2(0, y), h));
  20. name .rect(Rect_LU(remove.rect().ru(), total_width-h-frame_width*2-h, h));
  21. from .rect(Rect_LU(name .rect().ru(), frame_width, h));
  22. to .rect(Rect_LU(from .rect().ru(), frame_width, h));
  23. loop .rect(Rect_LU(to .rect().ru(), h , h));
  24. }
  25. void SplitAnimation::Clipboard(SplitAnimation &sa) {sa.add(ClipGet());}
  26. void SplitAnimation::Split(SplitAnimation &sa) {sa.splitDo();}
  27. void SplitAnimation::New(SplitAnimation &sa) {sa.addNew();}
  28. void SplitAnimation::Clear(SplitAnimation &sa) {sa.clearDo();}
  29. void SplitAnimation::Hide(SplitAnimation &sa) {sa.hide(); if(AnimEdit.preview.visible())AnimEdit.preview.activate();else Proj.list.kbSet();}
  30. void SplitAnimation::splitDo()
  31. {
  32. if(anims.elms())
  33. {
  34. // verify
  35. REPA(anims)
  36. {
  37. C Anim &anim=anims[i];
  38. if(!anim.from().is() || !anim.to().is() || TextInt(anim.from())>TextInt(anim.to()))
  39. {
  40. Gui.msgBox(S, S+"Invalid KeyFrame range for \""+anim.name()+'"');
  41. return;
  42. }
  43. }
  44. // split
  45. if(Elm *base=Proj.findElm(anim_id, ELM_ANIM))if(ElmAnim *base_data=base->animData())
  46. {
  47. Mems<Edit::FileParams> files=Edit::FileParams::Decode(base->srcFile());
  48. if(files.elms()!=1)Gui.msgBox(S, S+"Unknown source file for animation.");else
  49. {
  50. Str src=files[0].name, anim_name; if(C TextParam *name=files[0].findParam("name"))anim_name=name->value;
  51. if(FileInfoSystem(src).type!=FSTD_FILE)Gui.msgBox(S, S+"Can't find file \""+src+'"');else
  52. {
  53. TimeStamp time; time.getUTC();
  54. files[0].params.clear().setNum(3+anim_name.is());
  55. files[0].params[0].name="start_frame";
  56. files[0].params[1].name= "end_frame";
  57. files[0].params[2].name="loop";
  58. if(anim_name.is())files[0].params[3].set( "name", anim_name);
  59. FREPA(anims)
  60. {
  61. C Anim &anim=anims[i];
  62. // set only values, because names are the same for all animations
  63. files[0].params[0].value=TextInt(anim.from());
  64. files[0].params[1].value=TextInt(anim.to ());
  65. files[0].params[2].value= anim.loop() ;
  66. if(!i) // first animation just updates the existing one
  67. {
  68. base->setName(anim.name(), time).setSrcFile(Edit::FileParams::Encode(files), time); Proj.elmReload(base->id, false, false);
  69. Server.renameElm(*base); Server.setElmShort(base->id);
  70. }else // next are created as new anim elements
  71. if(Elm *anim_elm=Proj.newElm(ELM_ANIM, base->parent_id, &anim.name(), false))if(ElmAnim *anim_data=anim_elm->animData())
  72. {
  73. anim_data->newVer();
  74. anim_data->skel_id =base_data->skel_id ; anim_data->skel_time=time;
  75. anim_data->transform=base_data->transform;
  76. anim_data->linear (base_data->linear()); // set linear to the same value as the main animation, this will make reload use that value
  77. anim_data->setSrcFile(Edit::FileParams::Encode(files), time); Proj.elmReload(anim_elm->id, false, false);
  78. // sending to Server was already queued in 'Proj.newElm'
  79. }
  80. }
  81. Proj.refresh(); // names were changed, elements added and importing set
  82. hide();
  83. }
  84. }
  85. }
  86. }
  87. }
  88. void SplitAnimation::clearDo() {anims.clear(); setList();}
  89. void SplitAnimation::addNew() {anims.New (); setList();}
  90. void SplitAnimation::setList()
  91. {
  92. flt y=0; FREPA(anims)
  93. {
  94. Anim &anim=anims[i]; anim.pos(y); y-=anim.remove.size().y;
  95. }
  96. }
  97. void SplitAnimation::create()
  98. {
  99. Gui+=::EE::Window::create(Rect_C (0, 0, 1.3f, 1.15f)); button[2].show().func(Hide, T);
  100. T+=text .create(Rect_C (clientWidth()/2 , -0.07f, clientWidth()-0.04f, 0), "Drag and drop a file with animations list on this window,\nor use the button to detect them from clipboard."); text.auto_line=AUTO_LINE_SPACE_SPLIT;
  101. T+=clear .create(Rect_LU( 0.04f, -0.15f, 0.2f, 0.06f), "Clear" ).func(Clear , T);
  102. T+=clipboard.create(Rect_U (clientWidth()/2 , -0.15f, 0.5f, 0.06f), "Detect from Clipboard").func(Clipboard, T);
  103. T+=add_new .create(Rect_RU(clientWidth()-0.04f, -0.15f, 0.2f, 0.06f), "New" ).func(New , T);
  104. T+=t_name .create(Vec2 ( 0.45f, -0.255f), "Name");
  105. T+=t_start .create(Vec2 (clientWidth()-0.44f, -0.255f), "Start");
  106. T+=t_end .create(Vec2 (clientWidth()-0.24f, -0.255f), "End");
  107. T+=t_loop .create(Vec2 (clientWidth()-0.12f, -0.255f), "Loop");
  108. T+=split .create(Rect_D (clientWidth()/2, -clientHeight()+0.04f, 0.3f, 0.06f), "Split").func(Split, T);
  109. T+=region .create(Rect (clear.rect().min.x, split.rect().max.y+0.04f, add_new.rect().max.x, t_name.rect().min.y-0.02f));
  110. }
  111. void SplitAnimation::activate(C UID &elm_id)
  112. {
  113. if(Elm *elm=Proj.findElm(elm_id, ELM_ANIM))
  114. {
  115. anim_id=elm_id;
  116. ::EE::GuiObj::activate();
  117. ::EE::Window::title=S+"Split Animation \""+elm->name+'"';
  118. anims.clear();
  119. setList();
  120. }
  121. }
  122. bool SplitAnimation::Create(int &data, C Str &key, ptr user) {data=0; return true;}
  123. bool SplitAnimation::IsNumber(C Str &str) {return FlagTest(CharFlag(str[0]), CHARF_DIG);}
  124. void SplitAnimation::add(C Str &text)
  125. {
  126. // auto-detect from following samples: (watch out as names may include numbers)
  127. // frame 6-65:name
  128. // standby,15,55
  129. // Combat Mode C 604-680
  130. // idle 0--50
  131. // stand: 0 - 40
  132. // 1-200 Idle
  133. // split into lines
  134. Memt<Str> lines; ::Split(lines, text, '\n');
  135. // first find what tokens occur the most between 2 numbers
  136. Map<Str, int> separators(CompareCI, Create), left(CompareCI, Create), right(CompareCI, Create);
  137. FREPA(lines)
  138. {
  139. Memc<Str> tokens; Tokenize(tokens, lines[i]); if(tokens.elms())
  140. {
  141. int number_pos=-1; FREPA(tokens)if(IsNumber(tokens[i]))
  142. {
  143. if(number_pos>=0) // if there was a number detected
  144. {
  145. Str separator; for(int j=number_pos+1; j<i; j++)separator.space()+=tokens[j];
  146. if(int *s=separators(separator))(*s)++; // increase occurence of this separator
  147. }
  148. number_pos=i;
  149. }
  150. if(!IsNumber(tokens.first()))if(int *s=left (tokens.first()))(*s)++; // increase occurence of first token
  151. if(!IsNumber(tokens.last ()))if(int *s=right(tokens.last ()))(*s)++; // increase occurence of last token
  152. }
  153. }
  154. // find the separator with most occurences
  155. C Str *separator=null; int occurences=0;
  156. REPA(separators)if(separators[i]>occurences)
  157. {
  158. occurences= separators [i];
  159. separator =&separators.key(i);
  160. }
  161. // find which side repeats more
  162. int left_repeat=0, right_repeat=0;
  163. REPA( left)MAX( left_repeat, left[i]);
  164. REPA(right)MAX(right_repeat, right[i]);
  165. // tokenize the separator
  166. Memt<Str> sep_tokens; if(separator)Tokenize(sep_tokens, *separator);
  167. // process all lines and look for "number, sep_tokens, number"
  168. FREPA(lines)
  169. {
  170. Memc<Str> tokens; Tokenize(tokens, lines[i]);
  171. FREPA(tokens)if(IsNumber(tokens[i]) && InRange(i+1+sep_tokens.elms(), tokens) && IsNumber(tokens[i+1+sep_tokens.elms()]))
  172. {
  173. for(int j=0; j<sep_tokens.elms(); j++)if(!InRange(i+1+j, tokens) || tokens[i+1+j]!=sep_tokens[j])goto different;
  174. {
  175. // set name ranges (inclusive)
  176. VecI2 name_range_left (0 , i-1),
  177. name_range_right(i+1+sep_tokens.elms()+1, tokens.elms()-1);
  178. // remove symbol token next to frame ranges
  179. if(InRange(name_range_left .y, tokens) && CharType(tokens[name_range_left .y][0])==CHART_SIGN)name_range_left .y--;
  180. if(InRange(name_range_right.x, tokens) && CharType(tokens[name_range_right.x][0])==CHART_SIGN)name_range_right.x++;
  181. bool left=(left_repeat<right_repeat);
  182. if(name_range_left .y<name_range_left .x)left=false;else // there are no name tokens on the left side
  183. if(name_range_right.y<name_range_right.x)left=true ; // there are no name tokens on the right side
  184. VecI2 name_range=(left ? name_range_left : name_range_right);
  185. Str name; for(int i=name_range.x; i<=name_range.y; i++)if(InRange(i, tokens))name.space()+=tokens[i];
  186. Anim &anim=anims.New();
  187. anim.from.set(S+TextInt(tokens[i]));
  188. anim.to .set(S+TextInt(tokens[i+1+sep_tokens.elms()]));
  189. anim.name.set(name.replace('_', ' '));
  190. anim.loop.set(Contains(name, "loop", false, true) || Contains(name, "looped", false, true));
  191. break; // stop looking
  192. }
  193. different:; // keep looking
  194. }
  195. }
  196. setList();
  197. }
  198. void SplitAnimation::drop(Memc<Str> &names, GuiObj *obj, C Vec2 &screen_pos)
  199. {
  200. if(contains(obj))
  201. {
  202. FREPA(names)
  203. {
  204. C Str &name=names[i], ext=GetExt(name);
  205. if(ext=="txt")
  206. {
  207. FileText f; if(f.read(name)){add(f.getAll()); break;}
  208. }else
  209. if(ext=="meta") // unity metafile
  210. {
  211. bool added=false;
  212. TextData td;
  213. if(td.loadYAML(name))
  214. if(C TextNode *ModelImporter=td.findNode("ModelImporter"))
  215. if(C TextNode *animations=ModelImporter->findNode("animations"))
  216. if(C TextNode *clipAnimations=animations->findNode("clipAnimations"))
  217. FREPA(clipAnimations->nodes)
  218. {
  219. C TextNode &clip=clipAnimations->nodes[i];
  220. Str name, first, last;
  221. if(C TextNode *n=clip.findNode( "name"))name =n->asText();
  222. if(C TextNode *n=clip.findNode("firstFrame"))first=n->asText();
  223. if(C TextNode *n=clip.findNode( "lastFrame"))last =n->asText();
  224. if(name.is() || first.is() || last.is())
  225. {
  226. Anim &anim=anims.New();
  227. anim.from.set(first);
  228. anim.to .set(last );
  229. anim.name.set(name );
  230. if(C TextNode *n=clip.findNode("loop"))anim.loop.set(n->asBool());
  231. added=true;
  232. }
  233. }
  234. if(added)setList();
  235. }
  236. }
  237. names.clear();
  238. }
  239. }
  240. SplitAnimation::SplitAnimation() : anim_id(UIDZero) {}
  241. /******************************************************************************/