Animation.cpp 104 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. AnimEditor AnimEdit;
  5. /******************************************************************************/
  6. /******************************************************************************/
  7. cchar8 *AnimEditor::event_op_t[]=
  8. {
  9. "Move",
  10. "Rename",
  11. "Insert",
  12. "Delete",
  13. };
  14. cchar8 *AnimEditor::transform_obj_dialog_id="animateObj";
  15. /******************************************************************************/
  16. flt AnimEditor::TimeEps()
  17. {
  18. flt w=AnimEdit.track.rect().w(); w=(w ? 1/w : 0);
  19. if(AnimEdit.anim)w*=AnimEdit.anim->length();
  20. return w*0.025f;
  21. }
  22. void AnimEditor::Change::create(ptr user)
  23. {
  24. if(AnimEdit.anim)anim=*AnimEdit.anim;
  25. if(AnimEdit.skel)skel=*AnimEdit.skel;
  26. if(ElmAnim *anim_data=AnimEdit.data())T.anim_data=*anim_data;
  27. AnimEdit.undoVis();
  28. }
  29. void AnimEditor::Change::apply(ptr user)
  30. {
  31. if(AnimEdit.anim)if(ElmAnim *anim_data=AnimEdit.data())
  32. {
  33. CacheLock cl(Animations);
  34. Pose pose=anim_data->transform; // preserve pose, keep what we already have
  35. if(Elm *skel=Proj.findElm(T.anim_data.skel_id))if(ElmSkel *skel_data=skel->skelData())pose=skel_data->transform; // if the undo used a different skeleton, then we have to set animation pose to match it, because we will use it now, so always when the animation is linked to a skeleton then use its pose
  36. *AnimEdit.anim=anim;
  37. AnimEdit.anim->transform(GetTransform(T.anim_data.transform(), pose()), skel); // transform from undo matrix to new matrix
  38. anim_data->undo(T.anim_data); anim_data->undoSrcFile(T.anim_data); // call 'undoSrcFile' separately because it may not be called in 'undo', however we need it in case speed adjustment was performed
  39. anim_data->transform=pose;
  40. AnimEdit.setChanged(false);
  41. AnimEdit.toGui();
  42. AnimEdit.undoVis();
  43. AnimEdit.prepMeshSkel();
  44. AnimEdit.setOrnTarget();
  45. Proj.refresh(false, false); // refresh any invalid skeleton link references
  46. }
  47. }
  48. flt AnimEditor::Track::screenToTime(flt x)C
  49. {
  50. if(C Animation *anim=AnimEdit.anim)
  51. {
  52. Rect r=screenRect();
  53. return Frac(LerpR(r.min.x, r.max.x, x))*anim->length();
  54. }
  55. return 0;
  56. }
  57. ::AnimEditor::Track& AnimEditor::Track::create(bool events)
  58. {
  59. ::EE::GuiCustom::create();
  60. T.events=events;
  61. return T;
  62. }
  63. flt AnimEditor::Track::ElmPosX(C Rect &rect, flt time)
  64. {
  65. return rect.lerpX(AnimEdit.timeToFrac(time));
  66. }
  67. Rect AnimEditor::Track::ElmRect(C Rect &rect, flt time)
  68. {
  69. return Rect_C(rect.lerp(AnimEdit.timeToFrac(time), 0.5f), 0.01f, rect.h());
  70. }
  71. flt AnimEditor::Track::Slide(flt &time, flt dt, flt length)
  72. {
  73. return time=Frac(time+dt, length);
  74. }
  75. void AnimEditor::Track::update(C GuiPC &gpc)
  76. {
  77. AnimEdit.preview.event_lit=-1;
  78. if(gpc.visible && visible() && Gui.ms()==this)if(Animation *anim=AnimEdit.anim)
  79. {
  80. Rect r=rect()+gpc.offset;
  81. flt time=Frac(LerpR(r.min.x, r.max.x, Ms.pos().x))*anim->length();
  82. if(events)
  83. {
  84. // get lit
  85. flt dist=0;
  86. if(AnimEdit.preview.event_op()>=0 && AnimEdit.preview.event_op()!=EVENT_NEW)REPA(anim->events)
  87. {
  88. flt d=Abs(Ms.pos().x - ElmPosX(r, anim->events[i].time));
  89. if(AnimEdit.preview.event_lit<0 || d<dist){AnimEdit.preview.event_lit=i; dist=d;}
  90. }
  91. if(dist>0.05f)AnimEdit.preview.event_lit=-1;
  92. if(AnimEdit.preview.event_op()==EVENT_RENAME && Ms.bp(0))RenameEvent.activate(AnimEdit.preview.event_lit);
  93. if(AnimEdit.preview.event_op()==EVENT_DEL && Ms.bp(0))AnimEdit .delEvent(AnimEdit.preview.event_lit);
  94. if(AnimEdit.preview.event_op()==EVENT_NEW && Ms.bp(0))AnimEdit .newEvent(time);
  95. if(AnimEdit.preview.event_op()==EVENT_MOVE && Ms.b (0))
  96. {
  97. if(Ms.bp(0))AnimEdit.preview.event_sel=AnimEdit.preview.event_lit; AnimEdit.moveEvent(AnimEdit.preview.event_sel, time);
  98. AnimEdit.preview.event_lit=AnimEdit.preview.event_sel;
  99. }
  100. // set time
  101. if(AnimEdit.preview.event_op()<0 && Ms.b(0))AnimEdit.animTime(time);
  102. }else
  103. {
  104. // set time
  105. if(Ms.b(0))AnimEdit.animTime(time);
  106. // move KeyFrame
  107. if(Ms.b(1))if(AnimKeys *keys=AnimEdit.findKeys(AnimEdit.sel_bone))
  108. {
  109. AnimKeys *keys_parent=AnimEdit.findKeysParent(AnimEdit.sel_bone, false); // skip sliding for root
  110. AnimEdit.undos.set("slide");
  111. flt dt=Ms.dc().x*anim->length()/rect().w();
  112. if(Kb.ctrlCmd()) // all
  113. {
  114. if(AnimEdit.sel_bone<0) // entire animation
  115. {
  116. if(AnimEdit.anim->keys.is())dt=(Ms.bp(1) ? -AnimEdit.animTime() : 0); // if have any root keys, then call only one time at the start
  117. AnimEdit.anim->slideTime(dt);
  118. }else
  119. {
  120. REPA(keys->orns )Slide(keys->orns [i].time, dt, anim->length());
  121. REPA(keys->poss )Slide(keys->poss [i].time, dt, anim->length());
  122. REPA(keys->scales)Slide(keys->scales[i].time, dt, anim->length());
  123. if(AnimEdit.op()==OP_ORN2 && keys_parent)
  124. {
  125. REPA(keys_parent->orns )Slide(keys_parent->orns [i].time, dt, anim->length());
  126. REPA(keys_parent->poss )Slide(keys_parent->poss [i].time, dt, anim->length());
  127. REPA(keys_parent->scales)Slide(keys_parent->scales[i].time, dt, anim->length());
  128. }
  129. }
  130. AnimEdit.key_time=AnimEdit.animTime();
  131. }else
  132. {
  133. if(Ms.bp(1))AnimEdit.key_time=AnimEdit.animTime();
  134. switch(AnimEdit.op())
  135. {
  136. case OP_ORN : if(AnimKeys::Orn *key=FindOrn (*keys, AnimEdit.key_time))AnimEdit.key_time=Slide(key->time, dt, anim->length());else AnimEdit.key_time=AnimEdit.animTime(); break;
  137. case OP_POS : if(AnimKeys::Pos *key=FindPos (*keys, AnimEdit.key_time))AnimEdit.key_time=Slide(key->time, dt, anim->length());else AnimEdit.key_time=AnimEdit.animTime(); break;
  138. case OP_SCALE: if(AnimKeys::Scale *key=FindScale(*keys, AnimEdit.key_time))AnimEdit.key_time=Slide(key->time, dt, anim->length());else AnimEdit.key_time=AnimEdit.animTime(); break;
  139. case OP_ORN2 :
  140. {
  141. if(AnimKeys::Orn *key=FindOrn(*keys, AnimEdit.key_time))
  142. {
  143. if(keys_parent)if(AnimKeys::Orn *key=FindOrn(*keys_parent, AnimEdit.key_time))Slide(key->time, dt, anim->length()); // !! first process the parent without modifying the 'key_time' !!
  144. AnimEdit.key_time =Slide(key->time, dt, anim->length());
  145. }else AnimEdit.key_time=AnimEdit.animTime();
  146. }break;
  147. }
  148. }
  149. keys->sortFrames().setTangents(anim->loop(), anim->length());
  150. anim->setRootMatrix();
  151. flt t=AnimEdit.animTime(); AnimEdit.animTime(Slide(t, dt, anim->length()));
  152. AnimEdit.setChanged();
  153. }
  154. }
  155. }
  156. }
  157. void AnimEditor::Track::DrawKey(flt time, C Rect &rect, flt y, flt r)
  158. {
  159. VI.dot(Vec2(ElmPosX(rect, time), y), r);
  160. }
  161. void AnimEditor::Track::DrawKeys(AnimKeys &keys, C Rect &rect, C Color &color, flt y, flt r, int row, bool lit)
  162. {
  163. VI.color(color);
  164. switch(row)
  165. {
  166. case TRACK_ORN : REPA(keys.orns )DrawKey(keys.orns [i].time, rect, y, r); break;
  167. case TRACK_POS : REPA(keys.poss )DrawKey(keys.poss [i].time, rect, y, r); break;
  168. case TRACK_SCALE: REPA(keys.scales)DrawKey(keys.scales[i].time, rect, y, r); break;
  169. }
  170. VI.end();
  171. if(lit)switch(row)
  172. {
  173. case TRACK_ORN : if(AnimEdit.op()==OP_ORN || AnimEdit.op()==OP_ORN2)if(AnimKeys::Orn *key=FindOrn (keys, AnimEdit.animTime())){VI.color(WHITE); DrawKey(key->time, rect, y, r); VI.end();} break;
  174. case TRACK_POS : if(AnimEdit.op()==OP_POS )if(AnimKeys::Pos *key=FindPos (keys, AnimEdit.animTime())){VI.color(WHITE); DrawKey(key->time, rect, y, r); VI.end();} break;
  175. case TRACK_SCALE: if(AnimEdit.op()==OP_SCALE )if(AnimKeys::Scale *key=FindScale(keys, AnimEdit.animTime())){VI.color(WHITE); DrawKey(key->time, rect, y, r); VI.end();} break;
  176. }
  177. }
  178. void AnimEditor::Track::draw(C GuiPC &gpc)
  179. {
  180. if(gpc.visible && visible())
  181. {
  182. D.clip(gpc.clip);
  183. Rect r=rect()+gpc.offset;
  184. r.draw(GREY);
  185. if(C Animation *anim=AnimEdit.getVisAnim())
  186. {
  187. ElmRect(r, AnimEdit.animTime()).draw(BLACK); // draw time position
  188. if(events)
  189. {
  190. TextStyleParams ts; ts.size=0.05f; ts.align.set(0, 1); ts.color=ColorAlpha(0.6f);
  191. FREPA(anim->events) // draw events
  192. {
  193. C AnimEvent &event=anim->events[i];
  194. Rect e=ElmRect(r, event.time);
  195. e.draw((AnimEdit.preview.event_lit==i) ? LitColor : LitSelColor); if(AnimEdit.preview.event_op()>=0 && AnimEdit.preview.event_lit!=i)D.text(ts, e.up(), event.name);
  196. }
  197. if(AnimEdit.preview.event_op()>=0 && InRange(AnimEdit.preview.event_lit, anim->events)) // draw highlighted event last to be on top of others
  198. {
  199. C AnimEvent &event=anim->events[AnimEdit.preview.event_lit];
  200. ts.resetColors(false); ts.size*=1.3f; D.text(ts, ElmRect(r, event.time).up(), event.name);
  201. }
  202. }else
  203. {
  204. r.draw(Color(0, 64), false);
  205. AnimKeys *sel_keys=AnimEdit.findVisKeys(AnimEdit.sel_bone),
  206. *lit_keys=AnimEdit.findVisKeys(AnimEdit.lit_bone, false); // don't display root keys for highlighted
  207. const flt s=r.h()/(TRACK_NUM+1)/2;
  208. FREP(TRACK_NUM)
  209. {
  210. bool sel=false;
  211. switch(AnimEdit.op())
  212. {
  213. case OP_ORN :
  214. case OP_ORN2 : sel=(i==TRACK_ORN ); break;
  215. case OP_POS : sel=(i==TRACK_POS ); break;
  216. case OP_SCALE: sel=(i==TRACK_SCALE); break;
  217. }
  218. flt y=r.lerpY((TRACK_NUM-i)/flt(TRACK_NUM+1));
  219. D.lineX(ColorAlpha(WHITE, sel ? 0.5f : 0.19f), y, r.min.x, r.max.x);
  220. Color color=ColorI(i);
  221. if(lit_keys)DrawKeys(*lit_keys, r, ColorAlpha(Lerp(color, WHITE, 0.6f), 0.5f), y, s*1.1f, i);
  222. if(sel_keys)DrawKeys(*sel_keys, r, color , y, s*0.5f, i, true);
  223. }
  224. D.clip();
  225. Vec2 p=AnimEdit.screenPos()+Vec2(0.01f, -0.25f);
  226. int bone=AnimEdit.lit_bone;
  227. Skeleton *skel=AnimEdit.skel;
  228. D.text(ObjEdit.ts, p, S+"Time: "+AnimEdit.animTime()+'/'+anim->length()+"s ("+Round(AnimEdit.timeToFrac(AnimEdit.animTime())*100)+"%)"); p.y-=ObjEdit.ts.size.y;
  229. if(skel && InRange(bone, skel->bones))D.text(ObjEdit.ts, p, S+"Bone \""+skel->bones[bone].name+"\", Parent: "+(InRange(skel->bones[bone].parent, skel->bones) ? S+'"'+skel->bones[skel->bones[bone].parent].name+'"' : S+"none"));
  230. if(Gui.ms()==this)
  231. {
  232. flt frac=Frac(LerpR(r.min.x, r.max.x, Ms.pos().x)), time=frac*anim->length();
  233. TextStyleParams ts; ts.align.set(0, 1); ts.size=0.052f;
  234. Str t=S+time+"s ("+Round(frac*100)+"%)";
  235. flt w_2=ts.textWidth(t)/2, x=Ms.pos().x; Clamp(x, r.min.x+w_2, r.max.x-w_2);
  236. D.text(ts, x, r.max.y, t);
  237. }
  238. }
  239. }
  240. }
  241. }
  242. void AnimEditor::Preview::Play( Preview &editor, C Str &t) {AnimEdit.play.set(TextBool(t));}
  243. Str AnimEditor::Preview::Play(C Preview &editor ) {return AnimEdit.play();}
  244. void AnimEditor::Preview::Loop( Preview &editor, C Str &t) {AnimEdit.setLoop(TextBool(t));}
  245. Str AnimEditor::Preview::Loop(C Preview &editor ) {if(ElmAnim *d=AnimEdit.data())return d->loop(); return false;}
  246. void AnimEditor::Preview::Linear( Preview &editor, C Str &t) {AnimEdit.setLinear(TextBool(t));}
  247. Str AnimEditor::Preview::Linear(C Preview &editor ) {if(ElmAnim *d=AnimEdit.data())return d->linear(); return false;}
  248. void AnimEditor::Preview::Target( Preview &editor, C Str &t) {AnimEdit.setTarget(t);}
  249. Str AnimEditor::Preview::Target(C Preview &editor ) {return Proj.elmFullName(Proj.animToObj(AnimEdit.elm));}
  250. void AnimEditor::Preview::Split( Preview &editor ) {SplitAnim.activate(AnimEdit.elm_id);}
  251. void AnimEditor::Preview::Speed( Preview &editor ) {AnimEdit.applySpeed();}
  252. void AnimEditor::Preview::Render()
  253. {
  254. switch(Renderer())
  255. {
  256. case RM_PREPARE:
  257. {
  258. if(AnimEdit.mesh)AnimEdit.mesh->draw(AnimEdit.anim_skel);
  259. LightDir(!(ActiveCam.matrix.z*2+ActiveCam.matrix.x-ActiveCam.matrix.y), 1-D.ambientColor()).add(false);
  260. }break;
  261. }
  262. }
  263. void AnimEditor::Preview::Draw(Viewport &viewport) {AnimEdit.preview.draw();}
  264. void AnimEditor::Preview::draw()
  265. {
  266. AnimEdit.prepMeshSkel();
  267. if(C Skeleton *skel=AnimEdit.skel)
  268. {
  269. C MeshPtr &mesh =AnimEdit.mesh;
  270. C AnimatedSkeleton &anim_skel=AnimEdit.anim_skel;
  271. // remember settings
  272. CurrentEnvironment().set();
  273. AMBIENT_MODE ambient =D. ambientMode(); D. ambientMode(AMBIENT_FLAT);
  274. DOF_MODE dof =D. dofMode(); D. dofMode( DOF_NONE);
  275. bool eye_adapt=D.eyeAdaptation(); D.eyeAdaptation( false);
  276. bool astros =AstrosDraw ; AstrosDraw =false;
  277. bool ocean =Water.draw ; Water.draw =false;
  278. flt fov=D.viewFov(), from=D.viewFrom(), range=D.viewRange();
  279. // render
  280. Box box(0); if(mesh){if(mesh->is())box=mesh->ext;else if(skel)box=*skel;}
  281. flt dist=Max(0.1f, GetDist(box));
  282. D.viewFrom(dist*0.01f).viewRange(dist*24);
  283. SetCam(cam, box, cam_yaw, cam_pitch, cam_zoom);
  284. Renderer(Preview::Render);
  285. Renderer.setDepthForDebugDrawing();
  286. bool line_smooth=D.lineSmooth(true); // this can be very slow, so don't use it everywhere
  287. SetMatrix();
  288. if(draw_plane){bool line_smooth=D.lineSmooth(false); Plane(VecZero, Vec(0, 1, 0)).drawInfiniteByResolution(Color(255, 128), 192); D.lineSmooth(line_smooth);} // disable line smoothing because it can be very slow for lots of full-screen lines
  289. if(draw_axis ){D.depthLock(false); MatrixIdentity.draw(); D.depthUnlock();}
  290. if(draw_bones || draw_slots)
  291. {
  292. D.depthLock(false);
  293. anim_skel.draw(draw_bones ? ColorAlpha(CYAN, 0.75f) : TRANSPARENT, draw_slots ? ORANGE : TRANSPARENT);
  294. D.depthUnlock();
  295. }
  296. D.lineSmooth(line_smooth);
  297. // restore settings
  298. D.viewFov(fov).viewFrom(from).viewRange(range);
  299. D. dofMode(dof );
  300. D. ambientMode(ambient );
  301. D.eyeAdaptation(eye_adapt);
  302. AstrosDraw =astros;
  303. Water.draw =ocean;
  304. }
  305. }
  306. Animation* AnimEditor::Preview::anim()C {return AnimEdit.anim;}
  307. void AnimEditor::Preview::create()
  308. {
  309. Property *play, *obj;
  310. length=&add(); // length
  311. add("Loop" , MemberDesc(DATA_BOOL).setFunc(Loop , Loop )).desc("If animation is looped");
  312. add("Linear" , MemberDesc(DATA_BOOL).setFunc(Linear, Linear)).desc("If KeyFrame interpolation should be Linear, if disabled then Cubic is used.\nLinear is recommended for animations with high framerate.\nCubic is recommended for animations with low framerate.\n\nLinear interpolation is calculated faster than Cubic.\nAnimations with Linear interpolation can be optimized for smaller size than Cubic.\nIf the animation has low framerate, then Cubic will give smoother results than Linear.");
  313. obj=&add("Target Object", MemberDesc(DATA_STR ).setFunc(Target, Target)).desc("Drag and drop an object here to set it").elmType(ELM_OBJ);
  314. add();
  315. add("Display:");
  316. play=&add("Play" , MemberDesc(DATA_BOOL).setFunc(Play, Play)).desc(S+"Keyboard Shortcut: "+Kb.ctrlCmdName()+"+P, "+Kb.ctrlCmdName()+"+D, Space");
  317. add("Anim Speed" , MEMBER(AnimEditor, anim_speed)).range(-100, 100).desc("Animation speed");
  318. add("Bones" , MEMBER(AnimEditor, preview.draw_bones)).desc("Keyboard Shortcut: Alt+B");
  319. add("Slots" , MEMBER(AnimEditor, preview.draw_slots));
  320. add("Axis" , MEMBER(AnimEditor, preview.draw_axis )).desc("Keyboard Shortcut: Alt+A");
  321. add("Plane" , MEMBER(AnimEditor, preview.draw_plane)).desc("Display ground plane\nKeyboard Shortcut: Alt+P");
  322. event=&add("Events:");
  323. autoData(&AnimEdit);
  324. flt h=0.043f;
  325. Rect r=::PropWin::create("Animation Editor", Vec2(0.02f, -0.02f), 0.036f, h, 0.35f); button[1].show(); button[2].func(HideProjAct, SCAST(GuiObj, T)).show(); flag|=WIN_RESIZABLE;
  326. obj->textline.resize(Vec2(h, 0));
  327. prop_max_x=r.max.x;
  328. T+=split.create(Rect_RU(r.max.x, r.max.y, 0.15f, 0.055f), "Split").func(Split, T).desc(S+"Split Animation\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+S");
  329. T+=apply_speed.create(Rect_R(r.max.x, play->name.rect().centerY(), 0.23f, h), "Apply Speed").func(Speed, T).desc("Animation length/speed will be adjusted according to current \"Anim Speed\".");
  330. T+=edit.create(Rect_L(r.max.x/2, r.min.y, 0.17f, 0.055f), "Edit").func(Fullscreen, AnimEdit).focusable(false).desc(S+"Edit Animation KeyFrames\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+E");
  331. T+=locate.create(Rect_R(r.max.x/2-0.01f, r.min.y, 0.17f, 0.055f), "Locate").func(Locate, AnimEdit).focusable(false).desc("Locate this element in the Project");
  332. T+=viewport.create(Draw); viewport.fov=PreviewFOV;
  333. T+=event_op.create(Rect_LU(0.02f, 0, 0.45f, 0.110f), 0, event_op_t, Elms(event_op_t));
  334. event_op.tab(EVENT_MOVE ).rect(Rect(event_op.rect().lerp(0 , 0.5f), event_op.rect().lerp(0.5f, 1 ))).desc("Move event\nKeyboard Shortcut: F1");
  335. event_op.tab(EVENT_RENAME).rect(Rect(event_op.rect().lerp(0.5f, 0.5f), event_op.rect().lerp(1 , 1 ))).desc("Rename event\nKeyboard Shortcut: F2");
  336. event_op.tab(EVENT_NEW ).rect(Rect(event_op.rect().lerp(0 , 0 ), event_op.rect().lerp(0.5f, 0.5f))).desc("Create new event\nHold Ctrl to don't show Rename Window\nKeyboard Shortcut: F3");
  337. event_op.tab(EVENT_DEL ).rect(Rect(event_op.rect().lerp(0.5f, 0 ), event_op.rect().lerp(1 , 0.5f))).desc("Delete event\nKeyboard Shortcut: F4");
  338. T+=track.create(true);
  339. rect(Rect_C(0, 0, Min(1.7f, D.w()*2), Min(1.07f, D.h()*2)));
  340. }
  341. void AnimEditor::Preview::toGui()
  342. {
  343. ::PropWin::toGui();
  344. if(length)length->name.set(S+"Length: "+(anim() ? S+TextReal(anim()->length(), -2)+"s" : S));
  345. }
  346. void AnimEditor::Preview::removedEvent(int index)
  347. {
  348. if(event_lit==index)event_lit=-1;else if(event_lit>index)event_lit--;
  349. if(event_sel==index)event_sel=-1;else if(event_sel>index)event_sel--;
  350. }
  351. ::AnimEditor::Preview& AnimEditor::Preview::hide( ) {if(!AnimEdit.fullscreen)AnimEdit.set(null); ::PropWin::hide(); return T;}
  352. Rect AnimEditor::Preview::sizeLimit( )C {Rect r=::EE::Window::sizeLimit(); r.min.set(1.0f, 0.9f); return r;}
  353. ::AnimEditor::Preview& AnimEditor::Preview::rect(C Rect &rect)
  354. {
  355. ::EE::Window::rect(rect);
  356. track .rect(Rect(prop_max_x, -clientHeight(), clientWidth(), -clientHeight()+0.09f).extend(-0.02f));
  357. viewport.rect(Rect(prop_max_x, track.rect().max.y, clientWidth(), 0).extend(-0.02f));
  358. event_op.move(Vec2(0, track.rect().min.y-event_op.rect().min.y));
  359. if(event)event->name.pos(event_op.rect().lu()+Vec2(0, 0.025f));
  360. return T;
  361. }
  362. void AnimEditor::Preview::update(C GuiPC &gpc)
  363. {
  364. flt old_time=AnimEdit.animTime();
  365. if(visible() && gpc.visible)
  366. {
  367. if(contains(Gui.kb()))
  368. {
  369. KbSc edit(KB_E, KBSC_CTRL_CMD), split(KB_S, KBSC_CTRL_CMD), play(KB_P, KBSC_CTRL_CMD), play2(KB_D, KBSC_CTRL_CMD), bones(KB_B, KBSC_ALT), axis(KB_A, KBSC_ALT), plane(KB_P, KBSC_ALT);
  370. if(Kb.bp(KB_F1))event_op.toggle(0);else
  371. if(Kb.bp(KB_F2))event_op.toggle(1);else
  372. if(Kb.bp(KB_F3))event_op.toggle(2);else
  373. if(Kb.bp(KB_F4))event_op.toggle(3);
  374. if(edit .pd()){edit .eat(); AnimEdit.toggleFullscreen();}else
  375. if(split.pd()){split.eat(); Split(T);}else
  376. if(bones.pd()){bones.eat(); draw_bones^=true; toGui();}else
  377. if(axis .pd()){axis .eat(); draw_axis ^=true; toGui();}else
  378. if(plane.pd()){plane.eat(); draw_plane^=true; toGui();}else
  379. if(play .pd() || play2.pd()){play.eat(); play2.eat(); AnimEdit.playToggle();}
  380. if(Gui.kb()->type()!=GO_TEXTLINE && Gui.kb()->type()!=GO_CHECKBOX && Kb.bp(KB_SPACE)){Kb.eat(KB_SPACE); AnimEdit.playToggle();}
  381. }
  382. if(Gui.ms ()==&viewport)if(Ms.b(0) || Ms.b(4)){cam_yaw-=Ms.d().x; cam_pitch+=Ms.d().y; Ms.freeze();}
  383. if(Gui.wheel()==&viewport)Clamp(cam_zoom*=ScaleFactor(Ms.wheel()*-0.2f), 0.1f, 32);
  384. REPA(Touches)if(Touches[i].guiObj()==&viewport && Touches[i].on()){cam_yaw-=Touches[i].ad().x*2.0f; cam_pitch+=Touches[i].ad().y*2.0f;}
  385. AnimEdit.playUpdate(time_speed);
  386. }
  387. ::EE::ClosableWindow::update(gpc); // process after adjusting 'animTime' so clicking on anim track will not be changed afterwards
  388. flt delta_t=Time.ad()*time_speed; // use default delta
  389. if(AnimEdit.anim) // if we have animation
  390. if(flt length=AnimEdit.anim->length()) // if it has time
  391. {delta_t=Frac(AnimEdit.animTime()-old_time, length); if(delta_t>=length*0.5f)delta_t-=length;} // use delta based on anim time delta
  392. }
  393. void AnimEditor::OptimizeAnim::Changed(C Property &prop) {AnimEdit.optimize_anim.refresh();}
  394. void AnimEditor::OptimizeAnim::Optimize(OptimizeAnim &oa)
  395. {
  396. if(AnimEdit.anim)
  397. {
  398. AnimEdit.undos.set("optimize");
  399. oa.optimizeDo(*AnimEdit.anim);
  400. AnimEdit.setChanged();
  401. }
  402. oa.hide();
  403. }
  404. void AnimEditor::OptimizeAnim::optimizeDo(Animation &anim)
  405. {
  406. anim.optimize(angle_eps, pos_eps, scale_eps);
  407. }
  408. Animation* AnimEditor::OptimizeAnim::getAnim()
  409. {
  410. Animation *src=AnimEdit.anim;
  411. if(refresh_needed)
  412. {
  413. refresh_needed=false;
  414. if(src)anim=*src;else anim.del();
  415. File f;
  416. if( file_size){anim.save(f.writeMem()); file_size->name.set(S+ "File Size: "+FileSizeKB(f.size()));}
  417. optimizeDo(anim);
  418. if(optimized_size){anim.save(f.reset ()); optimized_size->name.set(S+"Optimized Size: "+FileSizeKB(f.size()));}
  419. }
  420. return preview ? &anim : src;
  421. }
  422. void AnimEditor::OptimizeAnim::refresh() {refresh_needed=true;}
  423. ::AnimEditor::OptimizeAnim& AnimEditor::OptimizeAnim::create()
  424. {
  425. add("Preview" , MEMBER(OptimizeAnim, preview));
  426. add("Angle Tolerance" , MEMBER(OptimizeAnim, angle_eps)).changed(Changed).mouseEditSpeed(0.001f ).range(0, PI);
  427. add("Position Tolerance", MEMBER(OptimizeAnim, pos_eps)).changed(Changed).mouseEditSpeed(0.0001f).min(0);
  428. add("Scale Tolerance" , MEMBER(OptimizeAnim, scale_eps)).changed(Changed).mouseEditSpeed(0.0001f).min(0);
  429. file_size=&add();
  430. optimized_size=&add();
  431. Rect r=::PropWin::create("Reduce Keyframes", Vec2(0.02f, -0.02f), 0.040f, 0.046f); button[2].show();
  432. autoData(this);
  433. resize(Vec2(0, 0.1f));
  434. T+=optimize.create(Rect_D(clientWidth()/2, -clientHeight()+0.03f, 0.2f, 0.06f), "Optimize").func(Optimize, T);
  435. hide();
  436. return T;
  437. }
  438. void AnimEditor::TimeRangeSpeed::Apply(TimeRangeSpeed &tsr)
  439. {
  440. if(AnimEdit.anim && tsr.speed)
  441. {
  442. AnimEdit.undos.set("trs");
  443. AnimEdit.anim->scaleTime(tsr.start, tsr.end, 1/tsr.speed);
  444. AnimEdit.setChanged();
  445. }
  446. tsr.hide();
  447. }
  448. ::AnimEditor::TimeRangeSpeed& AnimEditor::TimeRangeSpeed::create()
  449. {
  450. st=&add("Start Time", MEMBER(TimeRangeSpeed, start));
  451. e =&add("End Time" , MEMBER(TimeRangeSpeed, end ));
  452. sp=&add("Speed" , MEMBER(TimeRangeSpeed, speed));
  453. Rect r=::PropWin::create("Time Range Speed", Vec2(0.02f, -0.02f), 0.040f, 0.046f); button[2].show();
  454. autoData(this);
  455. resize(Vec2(0, 0.1f));
  456. T+=apply.create(Rect_D(clientWidth()/2, -clientHeight()+0.03f, 0.2f, 0.06f), "Apply").func(Apply, T);
  457. hide();
  458. return T;
  459. }
  460. void AnimEditor::TimeRangeSpeed::display()
  461. {
  462. if(AnimEdit.anim)
  463. {
  464. st->range(0, AnimEdit.anim->length()).set(AnimEdit.animTime());
  465. e ->range(0, AnimEdit.anim->length()).set(AnimEdit.track.screenToTime(Ms.pos().x));
  466. sp->set(AnimEdit.anim_speed);
  467. activate();
  468. }
  469. }
  470. void AnimEditor::Fullscreen(AnimEditor &editor) {editor.toggleFullscreen();}
  471. void AnimEditor::Render()
  472. {
  473. switch(Renderer())
  474. {
  475. case RM_PREPARE:
  476. {
  477. if(AnimEdit.draw_mesh() && AnimEdit.mesh)AnimEdit.mesh->draw(AnimEdit.anim_skel);
  478. LightDir(!(ActiveCam.matrix.z*2+ActiveCam.matrix.x-ActiveCam.matrix.y), 1-D.ambientColor()).add(false);
  479. }break;
  480. }
  481. }
  482. void AnimEditor::Draw(Viewport &viewport) {if(Edit::Viewport4::View *view=AnimEdit.v4.getView(&viewport))AnimEdit.draw(*view);}
  483. void AnimEditor::draw(Edit::Viewport4::View &view)
  484. {
  485. prepMeshSkel();
  486. view.camera.set();
  487. CurrentEnvironment().set();
  488. bool astros=AstrosDraw; AstrosDraw=false;
  489. bool ocean =Water.draw; Water.draw=false;
  490. Renderer.wire=wire();
  491. Renderer(AnimEditor::Render);
  492. Renderer.wire=false;
  493. AstrosDraw=astros;
  494. Water.draw=ocean;
  495. // helpers using depth buffer
  496. bool line_smooth=D.lineSmooth(true); // this can be very slow, so don't use it everywhere
  497. Renderer.setDepthForDebugDrawing();
  498. if( axis ()){SetMatrix(); MatrixIdentity.draw();}
  499. if(show_grid ()){SetMatrix(); bool line_smooth=D.lineSmooth(false); int size=8; Plane(VecZero, Vec(0, 1, 0)).drawLocal(Color(255, 128), size, false, size*(3*2)); D.lineSmooth(line_smooth);} // disable line smoothing because it can be very slow for lots of full-screen lines
  500. if(draw_bones())
  501. {
  502. SetMatrix();
  503. D.depthLock(false);
  504. int sel_bone2=((op()==OP_ORN2) ? boneParent(sel_bone) : -1);
  505. REPA(anim_skel.bones)
  506. {
  507. SkelBone bone=transformedBone(i);
  508. bool has_keys=false; if(AnimKeys *keys=findVisKeys(i))switch(op())
  509. {
  510. case OP_ORN :
  511. case OP_ORN2 : has_keys=(keys->orns .elms()>0); break;
  512. case OP_POS : has_keys=(keys->poss .elms()>0); break;
  513. case OP_SCALE: has_keys=(keys->scales.elms()>0); break;
  514. }
  515. Color col=(has_keys ? PURPLE : CYAN);
  516. bool lit=(lit_bone==i), sel=(sel_bone==i || sel_bone2==i);
  517. if(lit && sel)col=LitSelColor;else
  518. if( sel)col= SelColor;else
  519. if(lit )col= col ;else
  520. col.a=140;
  521. bone.draw(col);
  522. }
  523. //if(skel && InRange(sel_bone, skel.bones)) don't check this so we can draw just for root too
  524. {
  525. if(op()==OP_ORN || op()==OP_ORN2 )orn_target.draw(RED, 0.005f);
  526. if(op()==OP_POS || op()==OP_SCALE
  527. || (op()==OP_ORN || op()==OP_ORN2 || (anim && anim->keys.is()) || settings()==0) && sel_bone<0 // always draw axis for root
  528. )DrawMatrix(transformedBoneAxis(sel_bone), bone_axis);
  529. }
  530. if(sel_bone>=0 && settings()==0)DrawMatrix(transformedBoneAxis(-1), -1); // always draw axis for root when settings are visible
  531. D.depthUnlock();
  532. }
  533. D.lineSmooth(line_smooth);
  534. }
  535. void AnimEditor::undoVis() {SetUndo(undos, undo, redo);}
  536. void AnimEditor::PrevAnim(AnimEditor &editor) {Proj.elmNext(editor.elm_id, -1);}
  537. void AnimEditor::NextAnim(AnimEditor &editor) {Proj.elmNext(editor.elm_id);}
  538. void AnimEditor::Mode1(AnimEditor &editor) {editor.op.toggle(0);}
  539. void AnimEditor::Mode2(AnimEditor &editor) {editor.op.toggle(1);}
  540. void AnimEditor::Mode3(AnimEditor &editor) {editor.op.toggle(2);}
  541. void AnimEditor::Mode4(AnimEditor &editor) {editor.op.toggle(3);}
  542. void AnimEditor::Play(AnimEditor &editor) {editor.playToggle();}
  543. void AnimEditor::Identity(AnimEditor &editor) {editor.axis .push();}
  544. void AnimEditor::Settings(AnimEditor &editor) {editor.settings.toggle(0);}
  545. void AnimEditor::Start(AnimEditor &editor) { editor.animTime(0);}
  546. void AnimEditor::End(AnimEditor &editor) {if(editor.anim)editor.animTime(editor.anim->length());}
  547. void AnimEditor::PrevFrame(AnimEditor &editor) {editor.frame(-1);}
  548. void AnimEditor::NextFrame(AnimEditor &editor) {editor.frame(+1);}
  549. void AnimEditor::DelFrame(AnimEditor &editor) {editor.delFrame ();}
  550. void AnimEditor::DelFrames(AnimEditor &editor) {editor.delFrames();}
  551. void AnimEditor::DelFramesAtEnd(AnimEditor &editor) {editor.delFramesAtEnd();}
  552. void AnimEditor::Optimize(AnimEditor &editor) {editor.optimize_anim.activate();}
  553. void AnimEditor::TimeRangeSp(AnimEditor &editor) {editor.time_range_speed.display();}
  554. void AnimEditor::ReverseFrames(AnimEditor &editor) {editor.reverseFrames();}
  555. void AnimEditor::RemMovement(AnimEditor &editor) {editor.removeMovement();}
  556. void AnimEditor::FreezeBone(AnimEditor &editor) {editor.freezeBone();}
  557. void AnimEditor::Mirror(AnimEditor &editor) {if(editor.anim){editor.undos.set("mirror", true); Skeleton temp; editor.anim->mirror ( editor.skel ? *editor.skel : temp); editor.setOrnTarget(); editor.setChanged();}}
  558. void AnimEditor::RotX(AnimEditor &editor) {if(editor.anim){editor.undos.set("rot" , true); Skeleton temp; editor.anim->transform(Matrix3().setRotateX(PI_2), editor.skel ? *editor.skel : temp); editor.setOrnTarget(); editor.setChanged();}}
  559. void AnimEditor::RotY(AnimEditor &editor) {if(editor.anim){editor.undos.set("rot" , true); Skeleton temp; editor.anim->transform(Matrix3().setRotateY(PI_2), editor.skel ? *editor.skel : temp); editor.setOrnTarget(); editor.setChanged();}}
  560. void AnimEditor::RotZ(AnimEditor &editor) {if(editor.anim){editor.undos.set("rot" , true); Skeleton temp; editor.anim->transform(Matrix3().setRotateZ(PI_2), editor.skel ? *editor.skel : temp); editor.setOrnTarget(); editor.setChanged();}}
  561. void AnimEditor::DrawBones(AnimEditor &editor) {editor.draw_bones.push();}
  562. void AnimEditor::DrawMesh(AnimEditor &editor) {editor.draw_mesh .push();}
  563. void AnimEditor::Grid(AnimEditor &editor) {editor.show_grid .push();}
  564. void AnimEditor::TransformObj(AnimEditor &editor)
  565. {
  566. Dialog &dialog=Gui.getMsgBox(transform_obj_dialog_id);
  567. dialog.set("Transform Object", "Warning: this option will open the original object in the Object Editor, clear undo levels (if any) and transform it, including its Mesh and Skeleton according to current animation. This cannot be undone. Are you sure you want to do this?", Memt<Str>().add("Yes").add("Cancel"));
  568. dialog.buttons[0].func(TransformObjDo, editor);
  569. dialog.buttons[1].func(Hide , SCAST(GuiObj, dialog));
  570. dialog.activate();
  571. }
  572. void AnimEditor::TransformObjDo(AnimEditor &editor)
  573. {
  574. Gui.closeMsgBox(transform_obj_dialog_id);
  575. if(Elm *obj=Proj.animToObjElm(editor.elm))
  576. {
  577. ObjEdit.activate(obj);
  578. ObjEdit.animate(editor.anim_skel);
  579. }else Gui.msgBox(S, "There's no Object associated with this Animation.");
  580. }
  581. void AnimEditor::Undo(AnimEditor &editor) {editor.undos.undo();}
  582. void AnimEditor::Redo(AnimEditor &editor) {editor.undos.redo();}
  583. void AnimEditor::Locate(AnimEditor &editor) {Proj.elmLocate(editor.elm_id);}
  584. void AnimEditor::Reload(AnimEditor &editor) {Proj.elmReload(editor.elm_id);}
  585. void AnimEditor::Loop(AnimEditor &editor) {editor.setLoop (editor.loop ());}
  586. void AnimEditor::Linear(AnimEditor &editor) {editor.setLinear(editor.linear());}
  587. void AnimEditor::RootDelPos(AnimEditor &editor)
  588. {
  589. if(ElmAnim *d=editor.data())
  590. {
  591. editor.undos.set("rootDelPos");
  592. bool on=!FlagTest(d->flag, ElmAnim::ROOT_DEL_POS);
  593. editor.root_del_pos_x.set(on, QUIET);
  594. editor.root_del_pos_y.set(on, QUIET);
  595. editor.root_del_pos_z.set(on, QUIET);
  596. FlagSet(d->flag, ElmAnim::ROOT_DEL_POS, on); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(on){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_POSITION); editor.setOrnTarget(); editor.toGui();} editor.setChanged();
  597. }
  598. }
  599. void AnimEditor::RootDelRot(AnimEditor &editor)
  600. {
  601. if(ElmAnim *d=editor.data())
  602. {
  603. editor.undos.set("rootDelRot");
  604. bool on=!FlagTest(d->flag, ElmAnim::ROOT_DEL_ROT);
  605. editor.root_del_rot_x.set(on, QUIET);
  606. editor.root_del_rot_y.set(on, QUIET);
  607. editor.root_del_rot_z.set(on, QUIET);
  608. FlagSet(d->flag, ElmAnim::ROOT_DEL_ROT, on); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(on){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_ROTATION); editor.setOrnTarget(); editor.toGui();} editor.setChanged();
  609. }
  610. }
  611. void AnimEditor::RootDel(AnimEditor &editor)
  612. {
  613. if(ElmAnim *d=editor.data())if(editor.skel)
  614. {
  615. editor.undos.set("rootDel");
  616. uint flag=d->flag;
  617. FlagEnable(d->flag, ElmAnim::ROOT_DEL_POS|ElmAnim::ROOT_DEL_ROT);
  618. if(d->flag!=flag)
  619. {
  620. editor.anim->adjustForSameTransformWithDifferentSkeleton(*editor.skel, *editor.skel, -1, null, ROOT_DEL_POSITION|ROOT_DEL_ROTATION); editor.setOrnTarget(); editor.toGui(); editor.setChanged();
  621. }
  622. }
  623. }
  624. void AnimEditor::RootDelPosX(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("rootDelPos" ); FlagToggle(d->flag, ElmAnim::ROOT_DEL_POS_X); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_DEL_POS_X){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_POSITION_X); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  625. void AnimEditor::RootDelPosY(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("rootDelPos" ); FlagToggle(d->flag, ElmAnim::ROOT_DEL_POS_Y); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_DEL_POS_Y){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_POSITION_Y); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  626. void AnimEditor::RootDelPosZ(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("rootDelPos" ); FlagToggle(d->flag, ElmAnim::ROOT_DEL_POS_Z); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_DEL_POS_Z){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_POSITION_Z); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  627. void AnimEditor::RootDelRotX(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("rootDelRot" ); FlagToggle(d->flag, ElmAnim::ROOT_DEL_ROT_X); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_DEL_ROT_X){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_ROTATION_X); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  628. void AnimEditor::RootDelRotY(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("rootDelRot" ); FlagToggle(d->flag, ElmAnim::ROOT_DEL_ROT_Y); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_DEL_ROT_Y){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_ROTATION_Y); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  629. void AnimEditor::RootDelRotZ(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("rootDelRot" ); FlagToggle(d->flag, ElmAnim::ROOT_DEL_ROT_Z); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_DEL_ROT_Z){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_DEL_ROTATION_Z); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  630. void AnimEditor::Root2Keys(AnimEditor &editor) {if(ElmAnim *d=editor.data()){editor.undos.set("root2Keys" ); FlagToggle(d->flag, ElmAnim::ROOT_2_KEYS ); /*d.file_time.getUTC(); already changed in 'setChanged' */ if(d->flag&ElmAnim::ROOT_2_KEYS ){Skeleton temp, &skel=editor.skel ? *editor.skel : temp; editor.anim->adjustForSameTransformWithDifferentSkeleton(skel, skel, -1, null, ROOT_2_KEYS ); editor.setOrnTarget(); editor.toGui();} editor.setChanged();}}
  631. void AnimEditor::RootFromBody(AnimEditor &editor)
  632. {
  633. if(ElmAnim *d=editor.data())
  634. {
  635. editor.undos.set("rootFromBody"); FlagToggle(d->flag, ElmAnim::ROOT_FROM_BODY); /*d.file_time.getUTC(); already changed in 'setChanged' */
  636. if(d->flag&ElmAnim::ROOT_FROM_BODY)if(editor.skel)
  637. {
  638. editor.anim->adjustForSameTransformWithDifferentSkeleton(*editor.skel, *editor.skel, Max(0, editor.skel->findBoneI(BONE_SPINE)), null, d->rootFlags()); editor.setOrnTarget(); editor.toGui();
  639. }
  640. editor.setChanged();
  641. }
  642. }
  643. void AnimEditor::RootFromBodyX(AnimEditor &editor)
  644. {
  645. if(ElmAnim *d=editor.data())if(editor.skel)
  646. {
  647. editor.undos.set("rootFromBody");
  648. uint flag=d->flag;
  649. FlagDisable(d->flag, ElmAnim::ROOT_DEL_POS_X);
  650. FlagEnable (d->flag, ElmAnim::ROOT_DEL_POS_Y|ElmAnim::ROOT_DEL_POS_Z|ElmAnim::ROOT_DEL_ROT|ElmAnim::ROOT_FROM_BODY);
  651. if(d->flag!=flag)
  652. {
  653. editor.anim->adjustForSameTransformWithDifferentSkeleton(*editor.skel, *editor.skel, Max(0, editor.skel->findBoneI(BONE_SPINE)), null, d->rootFlags()); editor.setOrnTarget(); editor.toGui(); editor.setChanged();
  654. }
  655. }
  656. }
  657. void AnimEditor::RootFromBodyZ(AnimEditor &editor)
  658. {
  659. if(ElmAnim *d=editor.data())if(editor.skel)
  660. {
  661. editor.undos.set("rootFromBody");
  662. uint flag=d->flag;
  663. FlagDisable(d->flag, ElmAnim::ROOT_DEL_POS_Z);
  664. FlagEnable (d->flag, ElmAnim::ROOT_DEL_POS_X|ElmAnim::ROOT_DEL_POS_Y|ElmAnim::ROOT_DEL_ROT|ElmAnim::ROOT_FROM_BODY);
  665. if(d->flag!=flag)
  666. {
  667. editor.anim->adjustForSameTransformWithDifferentSkeleton(*editor.skel, *editor.skel, Max(0, editor.skel->findBoneI(BONE_SPINE)), null, d->rootFlags()); editor.setOrnTarget(); editor.toGui(); editor.setChanged();
  668. }
  669. }
  670. }
  671. void AnimEditor::RootSetMove(AnimEditor &editor)
  672. {
  673. if(ElmAnim *d=editor.data())
  674. {
  675. editor.undos.set("rootMove");
  676. if(editor.root_set_move())
  677. {
  678. d->rootMove(editor.anim->rootTransform().pos/d->transform.scale);
  679. d->setRoot(*editor.anim);
  680. editor.setOrnTarget();
  681. }else d->rootMoveZero();
  682. /*d.file_time.getUTC(); already changed in 'setChanged' */
  683. editor.setChanged();
  684. }
  685. }
  686. void AnimEditor::RootSetRot(AnimEditor &editor)
  687. {
  688. if(ElmAnim *d=editor.data())
  689. {
  690. editor.undos.set("rootRot");
  691. if(editor.root_set_rot())
  692. {
  693. d->rootRot(editor.anim->rootTransform().axisAngle());
  694. d->setRoot(*editor.anim);
  695. editor.setOrnTarget();
  696. }else d->rootRotZero();
  697. /*d.file_time.getUTC(); already changed in 'setChanged' */
  698. editor.setChanged();
  699. }
  700. }
  701. Str AnimEditor::RootMoveX(C AnimEditor &editor) {if(ElmAnim *d=editor.data())if(d->rootMove())return d->root_move.x*d->transform.scale; if(editor.anim)return editor.anim->rootTransform().pos.x; return S;}
  702. Str AnimEditor::RootMoveY(C AnimEditor &editor) {if(ElmAnim *d=editor.data())if(d->rootMove())return d->root_move.y*d->transform.scale; if(editor.anim)return editor.anim->rootTransform().pos.y; return S;}
  703. Str AnimEditor::RootMoveZ(C AnimEditor &editor) {if(ElmAnim *d=editor.data())if(d->rootMove())return d->root_move.z*d->transform.scale; if(editor.anim)return editor.anim->rootTransform().pos.z; return S;}
  704. void AnimEditor::RootMoveX( AnimEditor &editor, C Str &t) {if(ElmAnim *d=editor.data()){editor.undos.set("rootMove"); editor.root_set_move.set(true, QUIET); if(!d->rootMove())d->root_move=editor.anim->rootTransform().pos; d->root_move.x=TextFlt(t)/d->transform.scale; d->rootMove(d->root_move); /*d.file_time.getUTC(); already changed in 'setChanged' */ d->setRoot(*editor.anim); editor.setOrnTarget(); editor.setChanged();}}
  705. void AnimEditor::RootMoveY( AnimEditor &editor, C Str &t) {if(ElmAnim *d=editor.data()){editor.undos.set("rootMove"); editor.root_set_move.set(true, QUIET); if(!d->rootMove())d->root_move=editor.anim->rootTransform().pos; d->root_move.y=TextFlt(t)/d->transform.scale; d->rootMove(d->root_move); /*d.file_time.getUTC(); already changed in 'setChanged' */ d->setRoot(*editor.anim); editor.setOrnTarget(); editor.setChanged();}}
  706. void AnimEditor::RootMoveZ( AnimEditor &editor, C Str &t) {if(ElmAnim *d=editor.data()){editor.undos.set("rootMove"); editor.root_set_move.set(true, QUIET); if(!d->rootMove())d->root_move=editor.anim->rootTransform().pos; d->root_move.z=TextFlt(t)/d->transform.scale; d->rootMove(d->root_move); /*d.file_time.getUTC(); already changed in 'setChanged' */ d->setRoot(*editor.anim); editor.setOrnTarget(); editor.setChanged();}}
  707. Str AnimEditor::RootRotX(C AnimEditor &editor) {if(ElmAnim *d=editor.data())if(d->rootRot())return TextReal(RadToDeg(d->root_rot.x), 1); if(editor.anim)return TextReal(RadToDeg(editor.anim->rootTransform().axisAngle().x), 1); return S;}
  708. Str AnimEditor::RootRotY(C AnimEditor &editor) {if(ElmAnim *d=editor.data())if(d->rootRot())return TextReal(RadToDeg(d->root_rot.y), 1); if(editor.anim)return TextReal(RadToDeg(editor.anim->rootTransform().axisAngle().y), 1); return S;}
  709. Str AnimEditor::RootRotZ(C AnimEditor &editor) {if(ElmAnim *d=editor.data())if(d->rootRot())return TextReal(RadToDeg(d->root_rot.z), 1); if(editor.anim)return TextReal(RadToDeg(editor.anim->rootTransform().axisAngle().z), 1); return S;}
  710. void AnimEditor::RootRotX( AnimEditor &editor, C Str &t) {if(ElmAnim *d=editor.data()){editor.undos.set("rootRot"); editor.root_set_rot.set(true, QUIET); if(!d->rootRot())d->root_rot=editor.anim->rootTransform().axisAngle(); d->root_rot.x=DegToRad(TextFlt(t)); d->rootRot(d->root_rot); /*d.file_time.getUTC(); already changed in 'setChanged' */ d->setRoot(*editor.anim); editor.setOrnTarget(); editor.setChanged();}}
  711. void AnimEditor::RootRotY( AnimEditor &editor, C Str &t) {if(ElmAnim *d=editor.data()){editor.undos.set("rootRot"); editor.root_set_rot.set(true, QUIET); if(!d->rootRot())d->root_rot=editor.anim->rootTransform().axisAngle(); d->root_rot.y=DegToRad(TextFlt(t)); d->rootRot(d->root_rot); /*d.file_time.getUTC(); already changed in 'setChanged' */ d->setRoot(*editor.anim); editor.setOrnTarget(); editor.setChanged();}}
  712. void AnimEditor::RootRotZ( AnimEditor &editor, C Str &t) {if(ElmAnim *d=editor.data()){editor.undos.set("rootRot"); editor.root_set_rot.set(true, QUIET); if(!d->rootRot())d->root_rot=editor.anim->rootTransform().axisAngle(); d->root_rot.z=DegToRad(TextFlt(t)); d->rootRot(d->root_rot); /*d.file_time.getUTC(); already changed in 'setChanged' */ d->setRoot(*editor.anim); editor.setOrnTarget(); editor.setChanged();}}
  713. void AnimEditor::SetSelMirror(AnimEditor &editor) {editor.setSelMirror(false);}
  714. void AnimEditor::SetMirrorSel(AnimEditor &editor) {editor.setSelMirror(true );}
  715. void AnimEditor::setSelMirror(bool set_other)
  716. {
  717. if(skel)
  718. {
  719. undos.set("boneMirror", true);
  720. int bone_i=(sel_bone>=0 ? sel_bone : lit_bone);
  721. if(SkelBone *bone=skel->bones.addr(bone_i))
  722. {
  723. Str bone_name=BoneNeutralName(bone->name);
  724. REPA(skel->bones)if(i!=bone_i && bone_name==BoneNeutralName(skel->bones[i].name))
  725. {
  726. if(set_other)Swap(i, bone_i);
  727. AnimKeys *src=findKeys(i, false), *dest=(src ? getKeys(bone_i, false) : findKeys(bone_i, false));
  728. if(dest)
  729. {
  730. if(src)
  731. {
  732. *dest=*src;
  733. dest->mirrorX();
  734. }else anim->bones.remove(bone_i, true);
  735. }
  736. prepMeshSkel();
  737. setOrnTarget();
  738. setChanged();
  739. break;
  740. }
  741. }
  742. }
  743. }
  744. void AnimEditor::SkelBonePosCopy(AnimEditor &editor) {editor.skelBonePosCopy(false);}
  745. void AnimEditor::SkelBonePosCopyR(AnimEditor &editor) {editor.skelBonePosCopy(true );}
  746. void AnimEditor::skelBonePosCopy(bool relative)
  747. {
  748. prepMeshSkel();
  749. //copied_bone_pos=anim_skel.boneRoot(sel_bone).pos;
  750. copied_bone_pos= transformedBone(sel_bone).pos;
  751. if(relative && sel_bone>=0)copied_bone_pos/=(Matrix)transformedBone(-1); // set as relative to root
  752. copied_bone_pos_relative=relative;
  753. }
  754. void AnimEditor::SkelBonePosPaste(AnimEditor &editor) {editor.skelBonePosPaste(Vec(1, 1, 1));}
  755. void AnimEditor::SkelBonePosPasteX(AnimEditor &editor) {editor.skelBonePosPaste(Vec(1, 0, 0));}
  756. void AnimEditor::SkelBonePosPasteZ(AnimEditor &editor) {editor.skelBonePosPaste(Vec(0, 0, 1));}
  757. void AnimEditor::skelBonePosPaste(C Vec &mask)
  758. {
  759. if(skel && anim)
  760. {
  761. undos.set("bonePosPaste");
  762. prepMeshSkel();
  763. //Vec pos=anim_skel.boneRoot(sel_bone).pos;
  764. Vec pos= transformedBone(sel_bone).pos;
  765. if(copied_bone_pos_relative && sel_bone>=0)pos/=(Matrix)transformedBone(-1);
  766. Vec delta=(copied_bone_pos-pos)*mask; if(delta.any())
  767. {
  768. anim->offsetRootBones(*skel, delta);
  769. prepMeshSkel();
  770. setOrnTarget();
  771. setChanged();
  772. }
  773. }
  774. }
  775. bool AnimEditor::selected()C {return Mode()==MODE_ANIM;}
  776. void AnimEditor::selectedChanged()
  777. {
  778. setMenu();
  779. flush();
  780. }
  781. ElmAnim* AnimEditor::data()C {return elm ? elm->animData() : null;}
  782. Animation* AnimEditor::getVisAnim() {return optimize_anim.visibleFull() ? optimize_anim.getAnim() : anim;}
  783. flt AnimEditor::timeToFrac(flt time)C {return (anim && anim->length()) ? time/anim->length() : 0;}
  784. flt AnimEditor::animTime( )C {return _anim_time;}
  785. void AnimEditor::animTime(flt time)
  786. {
  787. if(_anim_time!=time)
  788. {
  789. _anim_time=time;
  790. prepMeshSkel();
  791. setOrnTarget();
  792. }
  793. }
  794. void AnimEditor::setLoop(bool loop)
  795. {
  796. if(ElmAnim *d=data())if(d->loop()!=loop)
  797. {
  798. undos.set("loop"); d->loop(loop); d->loop_time.getUTC(); if(anim)anim->loop(loop); toGui(); setOrnTarget(); setChanged(false); // set 'file' to false because the Editor will recreate the animation file upon receiving new data from server
  799. }
  800. }
  801. void AnimEditor::setLinear(bool linear)
  802. {
  803. if(ElmAnim *d=data())if(d->linear()!=linear)
  804. {
  805. undos.set("linear"); d->linear(linear); d->linear_time.getUTC(); if(anim)anim->linear(linear); toGui(); setOrnTarget(); setChanged(false); // set 'file' to false because the Editor will recreate the animation file upon receiving new data from server
  806. }
  807. }
  808. void AnimEditor::selBone(int bone)
  809. {
  810. sel_bone=bone;
  811. setOrnTarget();
  812. }
  813. void AnimEditor::setOrnTarget()
  814. {
  815. SkelBone bone =transformedBone( sel_bone ), bone_parent;
  816. if(sel_bone>=0)bone_parent=transformedBone(boneParent(sel_bone)); // set 'bone_parent' only if we have a bone selected, otherwise (when root is selected) then keep it as identity
  817. orn_target=bone.to();
  818. orn_perp=bone.pos-NearestPointOnStr(bone.pos, bone_parent.pos, !(orn_target-bone_parent.pos));
  819. if(!orn_perp.normalize())orn_perp.set(0, 0, 1);
  820. }
  821. void AnimEditor::setMenu()
  822. {
  823. ::Viewport4Region::setMenu(selected());
  824. cmd.menu.enabled(selected());
  825. }
  826. void AnimEditor::setMenu(Node<MenuElm> &menu, C Str &prefix)
  827. {
  828. ::Viewport4Region::setMenu(menu, prefix);
  829. FREPA(menu.children)if(menu.children[i].name==prefix+"View")
  830. {
  831. Node<MenuElm> &v=menu.children[i];
  832. v.New().create("Mode 1" , Mode1 , T, true).kbsc(KbSc(KB_F1));
  833. v.New().create("Mode 2" , Mode2 , T, true).kbsc(KbSc(KB_F2));
  834. v.New().create("Mode 3" , Mode3 , T, true).kbsc(KbSc(KB_F3));
  835. v.New().create("Mode 4" , Mode4 , T, true).kbsc(KbSc(KB_F4));
  836. v.New().create("Edit" , Fullscreen, T).kbsc(KbSc(KB_E , KBSC_CTRL_CMD)).flag(MENU_HIDDEN);
  837. v.New().create("Play" , Play , T).kbsc(KbSc(KB_P , KBSC_CTRL_CMD)).kbsc2(KbSc(KB_D, KBSC_CTRL_CMD)).flag(MENU_HIDDEN);
  838. v.New().create("Bones" , DrawBones , T).kbsc(KbSc(KB_B , KBSC_ALT )).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  839. v.New().create("Mesh" , DrawMesh , T).kbsc(KbSc(KB_M , KBSC_ALT )).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  840. v.New().create("Grid" , Grid , T).kbsc(KbSc(KB_G , KBSC_ALT )).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  841. v.New().create("Axis" , Identity , T).kbsc(KbSc(KB_A , KBSC_ALT )).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  842. v.New().create("Settings" , Settings , T).kbsc(KbSc(KB_S , KBSC_ALT )).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  843. v.New().create("Start" , Start , T).kbsc(KbSc(KB_HOME , KBSC_CTRL_CMD)).kbsc2(KbSc(KB_LBR , KBSC_CTRL_CMD)).flag(MENU_HIDDEN);
  844. v.New().create("End" , End , T).kbsc(KbSc(KB_END , KBSC_CTRL_CMD)).kbsc2(KbSc(KB_RBR , KBSC_CTRL_CMD)).flag(MENU_HIDDEN);
  845. v.New().create("Prev Frame" , PrevFrame , T).kbsc(KbSc(KB_LEFT , KBSC_CTRL_CMD|KBSC_REPEAT)).kbsc2(KbSc(KB_COMMA, KBSC_CTRL_CMD|KBSC_REPEAT)).flag(MENU_HIDDEN);
  846. v.New().create("Next Frame" , NextFrame , T).kbsc(KbSc(KB_RIGHT, KBSC_CTRL_CMD|KBSC_REPEAT)).kbsc2(KbSc(KB_DOT , KBSC_CTRL_CMD|KBSC_REPEAT)).flag(MENU_HIDDEN);
  847. v.New().create("Prev Frames", PrevFrame , T).kbsc(KbSc(KB_LEFT , KBSC_CTRL_CMD|KBSC_SHIFT|KBSC_REPEAT)).kbsc2(KbSc(KB_COMMA, KBSC_CTRL_CMD|KBSC_SHIFT|KBSC_REPEAT)).flag(MENU_HIDDEN);
  848. v.New().create("Next Frames", NextFrame , T).kbsc(KbSc(KB_RIGHT, KBSC_CTRL_CMD|KBSC_SHIFT|KBSC_REPEAT)).kbsc2(KbSc(KB_DOT , KBSC_CTRL_CMD|KBSC_SHIFT|KBSC_REPEAT)).flag(MENU_HIDDEN);
  849. v.New().create("Previous Animation", PrevAnim, T).kbsc(KbSc(KB_PGUP, KBSC_CTRL_CMD|KBSC_REPEAT)).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  850. v.New().create("Next Animation" , NextAnim, T).kbsc(KbSc(KB_PGDN, KBSC_CTRL_CMD|KBSC_REPEAT)).flag(MENU_HIDDEN|MENU_TOGGLABLE);
  851. break;
  852. }
  853. }
  854. AnimEditor& AnimEditor::create()
  855. {
  856. ::Viewport4Region::create(Draw, false, 0, PI, 1, 0.01f, 1000); v4.toggleHorizontal();
  857. flt h=ctrls.rect().h();
  858. T+=axis .create(Rect_LU(ctrls .rect().ld(), h) ).focusable(false).desc(S+"Display identity matrix axes, where:\nRed = right (x vector)\nGreen = up (y vector)\nBlue = forward (z vector)\nLength of each vector is 1 unit\nPlease note that the camera in Object Editor is by default faced from forward to backward.\n\nKeyboard Shortcut: Alt+A"); axis.mode=BUTTON_TOGGLE; axis.set(true); axis.image="Gui/Misc/axis.img";
  859. T+=draw_bones.create(Rect_LU(axis .rect().ru(), h), "B").focusable(false).desc(S+"Display skeleton bones\nKeyboard Shortcut: Alt+B"); draw_bones.mode=BUTTON_TOGGLE; draw_bones.set(true);
  860. T+=draw_mesh .create(Rect_LU(draw_bones.rect().ru(), h), "M").focusable(false).desc(S+"Display mesh\nKeyboard Shortcut: Alt+M" ); draw_mesh .mode=BUTTON_TOGGLE; draw_mesh .set(true);
  861. wire .pos ( draw_mesh .rect().ru());
  862. T+=show_grid .create(Rect_LU(axis .rect().ld(), h)).focusable(false).desc("Draw grid\nKeyboard Shortcut: Alt+G"); show_grid.mode=BUTTON_TOGGLE; show_grid.set(false); show_grid.image="Gui/Misc/grid.img";
  863. cam_spherical.hide(); cam_lock.pos(cam_spherical.pos());
  864. T+=undo .create(Rect_LU(ctrls.rect().ru()+Vec2(h, 0), 0.05f, 0.05f) ).func(Undo , T).focusable(false).desc("Undo"); undo.image="Gui/Misc/undo.img";
  865. T+=redo .create(Rect_LU( undo.rect().ru() , 0.05f, 0.05f) ).func(Redo , T).focusable(false).desc("Redo"); redo.image="Gui/Misc/redo.img";
  866. T+=locate.create(Rect_LU( redo.rect().ru() , 0.05f, 0.05f), "L").func(Locate, T).focusable(false).desc("Locate this element in the Project");
  867. T+=play.create(Rect_LU(locate.rect().ru()+Vec2(h, 0), h), true).desc(S+"Play Animation\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+P, "+Kb.ctrlCmdName()+"+D");
  868. props.New().create("Play Speed", MEMBER(AnimEditor, anim_speed)).precision(2);
  869. Rect r=AddProperties(props, T, play.rect().ru()+Vec2(0.01f, 0), play.rect().h(), 0.15f, &ObjEdit.ts); REPAO(props).autoData(this);
  870. T+=op.create(Rect_LU(r.ru()+Vec2(h, 0), h*OP_NUM, h), 0, (cchar**)null, OP_NUM);
  871. op.tab(OP_ORN ).setImage("Gui/Misc/target.img" ).desc(S+"Set Target Orientation KeyFrames\n\nSelect with LeftClick\nTransform with RightClick\nRotate with Alt+RightClick\nHold Shift for more precision\nHold "+Kb.ctrlCmdName()+" to transform all KeyFrames\n\nKeyboard Shortcut: F1");
  872. op.tab(OP_ORN2 ).setImage("Gui/Misc/target2.img").desc(S+"Set Target Orientation KeyFrames for Bone and its Parent\n\nSelect with LeftClick\nTransform with RightClick\nRotate with Alt+RightClick\nHold Shift for more precision\n\nKeyboard Shortcut: F2");
  873. op.tab(OP_POS ).setImage("Gui/Misc/move.img" ).desc(S+"Set Position Offset KeyFrames\n\nSelect with LeftClick\nTransform with RightClick\nHold Shift for more precision\nHold "+Kb.ctrlCmdName()+" to transform all KeyFrames\nHold Alt to use World Matrix alignment\n\nKeyboard Shortcut: F3");
  874. op.tab(OP_SCALE).setImage("Gui/Misc/scale.img" ).desc(S+"Set Scale KeyFrames\n\nSelect with LeftClick\nTransform with RightClick\nHold Shift for more precision\nHold "+Kb.ctrlCmdName()+" to transform all KeyFrames\n\nKeyboard Shortcut: F4");
  875. Node<MenuElm> n;
  876. n.New().create("Delete KeyFrame" , DelFrame , T).kbsc(KbSc(KB_DEL, KBSC_CTRL_CMD)).desc("This will delete a single keyframe for selected bone");
  877. n.New().create("Delete KeyFrames" , DelFrames , T).kbsc(KbSc(KB_DEL, KBSC_CTRL_CMD|KBSC_SHIFT)).desc("This will delete all keyframes for selected bone");
  878. n.New().create("Delete All Bone KeyFrames at End", DelFramesAtEnd, T).kbsc(KbSc(KB_DEL, KBSC_CTRL_CMD|KBSC_WIN_CTRL)).desc("This will delete keyframes located at the end of the animation, for all bones (except root motion).");
  879. n++;
  880. n.New().create("Reduce KeyFrames", Optimize, T).kbsc(KbSc(KB_O, KBSC_CTRL_CMD));
  881. n++;
  882. n.New().create("Reverse KeyFrames", ReverseFrames, T).kbsc(KbSc(KB_R, KBSC_CTRL_CMD|KBSC_SHIFT)); // avoid Ctrl+R collision with reload project element
  883. n++;
  884. n.New().create("Change Speed for Time Range", TimeRangeSp, T).kbsc(KbSc(KB_S, KBSC_CTRL_CMD));
  885. n++;
  886. //n.New().create("Remove Movement", RemMovement, T).desc("This option can be used for animations that include actual movement - ending position is not the same as the starting position.\nThis option will adjust the animation so that the ending position is the same as starting position.").kbsc(KbSc(KB_M, KBSC_CTRL_CMD|KBSC_ALT));
  887. n.New().create("Freeze Bone" , FreezeBone , T).desc("This option will adjust position offset to the root bone, so that currently selected bone will appear without movement.").kbsc(KbSc(KB_F, KBSC_CTRL_CMD|KBSC_ALT));
  888. n++;
  889. n.New().create("Set Mirrored from Selection", SetMirrorSel, T).kbsc(KbSc(KB_M, KBSC_CTRL_CMD )).desc("This option will set bone animation from the other side as mirrored version of the selected bone");
  890. n.New().create("Set Selection from Mirrored", SetSelMirror, T).kbsc(KbSc(KB_M, KBSC_CTRL_CMD|KBSC_SHIFT)).desc("This option will set selected bone animation as mirrored version of the bone from the other side");
  891. n++;
  892. n.New().create( "Copy Bone Position" , SkelBonePosCopy , T).kbsc(KbSc(KB_C, KBSC_CTRL_CMD));
  893. n.New().create( "Copy Bone Position Relative", SkelBonePosCopyR , T).kbsc(KbSc(KB_C, KBSC_CTRL_CMD|KBSC_SHIFT)).desc("Copy selected bone position relative to root");
  894. n.New().create("Paste Bone Position" , SkelBonePosPaste , T).kbsc(KbSc(KB_V, KBSC_CTRL_CMD));
  895. n.New().create("Paste Bone Position Z" , SkelBonePosPasteZ, T).kbsc(KbSc(KB_V, KBSC_CTRL_CMD|KBSC_SHIFT));
  896. n.New().create("Paste Bone Position X" , SkelBonePosPasteX, T).kbsc(KbSc(KB_V, KBSC_CTRL_CMD|KBSC_WIN_CTRL));
  897. n++;
  898. n.New().create("Set Root From Body Z" , RootFromBodyZ, T).kbsc(KbSc(KB_B, KBSC_CTRL_CMD));
  899. n.New().create("Set Root From Body X" , RootFromBodyX, T).kbsc(KbSc(KB_B, KBSC_CTRL_CMD|KBSC_SHIFT));
  900. n.New().create("Del Root Position+Rotation", RootDel , T).kbsc(KbSc(KB_R, KBSC_CTRL_CMD|KBSC_ALT));
  901. n++;
  902. n.New().create("Mirror" , Mirror, T).desc("Mirror entire animation along X axis");
  903. n.New().create("Rotate X", RotX , T).kbsc(KbSc(KB_X, KBSC_CTRL_CMD|KBSC_ALT|KBSC_REPEAT)).desc("Rotate entire animation along X axis");
  904. n.New().create("Rotate Y", RotY , T).kbsc(KbSc(KB_Y, KBSC_CTRL_CMD|KBSC_ALT|KBSC_REPEAT)).desc("Rotate entire animation along Y axis");
  905. n.New().create("Rotate Z", RotZ , T).kbsc(KbSc(KB_Z, KBSC_CTRL_CMD|KBSC_ALT|KBSC_REPEAT)).desc("Rotate entire animation along Z axis");
  906. n++;
  907. n.New().create("Transform Original Object", TransformObj, T);
  908. T+=cmd.create(Rect_LU(op.rect().ru(), h), n).focusable(false); cmd.flag|=COMBOBOX_CONST_TEXT;
  909. T+=edit.create(Rect_LU(cmd.rect().ru()+Vec2(h, 0), 0.12f, h), "Edit").func(Fullscreen, T).focusable(false).desc(S+"Close Editing\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+E");
  910. cchar8 *settings_t[]={"Settings"};
  911. T+=settings.create(Rect_LU(edit.rect().ru()+Vec2(h, 0), 0.17f, h), 0, settings_t, Elms(settings_t));
  912. settings.tab(0).desc("Keyboard Shortcut: Alt+S")+=settings_region.create(Rect_LU(settings.rect().ru(), 0.333f+h*3, 0.5f)).skin(&TransparentSkin, false); settings_region.kb_lit=false;
  913. {
  914. flt x=0.01f, y=-0.005f, l=h*1.1f, ll=l*1.1f, w=0.25f, ph=h*0.95f;
  915. ts.reset().size=0.041f;
  916. settings_region+=t_settings.create(Vec2(settings_region.clientWidth()/2, y-h/2), "Settings also affect reloading", &ts); y-=ll;
  917. settings_region+=loop .create(Rect_U(settings_region.clientWidth()*(1.0f/3.5f), y, 0.15f, h), "Loop" ).func(Loop , T); loop .mode=BUTTON_TOGGLE;
  918. settings_region+=linear.create(Rect_U(settings_region.clientWidth()*(2.5f/3.5f), y, 0.15f, h), "Linear").func(Linear, T); linear.mode=BUTTON_TOGGLE;
  919. y-=ll;
  920. settings_region+=t_settings_root.create(Vec2(x, y-h/2), "Root:", &ObjEdit.ts); y-=l;
  921. settings_region+=root_del_pos .create(Rect_LU(x, y, w, h), "Del Position").func(RootDelPos, T);
  922. settings_region+=root_del_pos_x.create(Rect_LU(root_del_pos .rect().ru()+Vec2(0.01f, 0), h, h), "X").func(RootDelPosX, T).subType(BUTTON_TYPE_TAB_LEFT ); root_del_pos_x.mode=BUTTON_TOGGLE;
  923. settings_region+=root_del_pos_y.create(Rect_LU(root_del_pos_x.rect().ru() , h, h), "Y").func(RootDelPosY, T).subType(BUTTON_TYPE_TAB_HORIZONTAL); root_del_pos_y.mode=BUTTON_TOGGLE;
  924. settings_region+=root_del_pos_z.create(Rect_LU(root_del_pos_y.rect().ru() , h, h), "Z").func(RootDelPosZ, T).subType(BUTTON_TYPE_TAB_RIGHT ); root_del_pos_z.mode=BUTTON_TOGGLE;
  925. y-=l;
  926. settings_region+=root_del_rot .create(Rect_LU(x, y, w, h), "Del Rotation").func(RootDelRot, T);
  927. settings_region+=root_del_rot_x.create(Rect_LU(root_del_rot .rect().ru()+Vec2(0.01f, 0), h, h), "X").func(RootDelRotX, T).subType(BUTTON_TYPE_TAB_LEFT ); root_del_rot_x.mode=BUTTON_TOGGLE;
  928. settings_region+=root_del_rot_y.create(Rect_LU(root_del_rot_x.rect().ru() , h, h), "Y").func(RootDelRotY, T).subType(BUTTON_TYPE_TAB_HORIZONTAL); root_del_rot_y.mode=BUTTON_TOGGLE;
  929. settings_region+=root_del_rot_z.create(Rect_LU(root_del_rot_y.rect().ru() , h, h), "Z").func(RootDelRotZ, T).subType(BUTTON_TYPE_TAB_RIGHT ); root_del_rot_z.mode=BUTTON_TOGGLE;
  930. y-=l;
  931. //settings_region+=root_del_scale.create(Rect_LU(x, y, w, h), "Del Scale" ).func(RootDelScale, T); root_del_scale.mode=BUTTON_TOGGLE; y-=l;
  932. settings_region+=root_from_body.create(Rect_LU(x, y, w, h), "From Body" ).func(RootFromBody, T); root_from_body.mode=BUTTON_TOGGLE; y-=l;
  933. settings_region+=root_2_keys .create(Rect_LU(x, y, w, h), "2 Keys Only" ).func(Root2Keys , T); root_2_keys .mode=BUTTON_TOGGLE; y-=l; root_2_keys.desc("Limit number of keyframes to 2 keys only: Start+End");
  934. settings_region+=root_set_move .create(Rect_LU(x, y, w, h), "Set Movement").func(RootSetMove , T); root_set_move .mode=BUTTON_TOGGLE; root_set_move.desc("Override Animation's root movement with a custom value");
  935. settings_region+=root_set_rot .create(Rect_LU(x, y-3*ph, w, h), "Set Rotation").func(RootSetRot , T); root_set_rot .mode=BUTTON_TOGGLE; root_set_rot.desc("Override Animation's root rotation with a custom value");
  936. //settings_region+=reload .create(Rect_U (settings_region.clientWidth()/2, y-6*ph-0.01, 0.18, h+0.005), "Reload").func(Reload, T);
  937. settings_region+=reload .create(Rect_LD(x, y-6*ph, 0.18f, h+0.005f), "Reload").func(Reload, T);
  938. root_props.New().create("X", MemberDesc(DATA_REAL).setFunc(RootMoveX, RootMoveX));
  939. root_props.New().create("Y", MemberDesc(DATA_REAL).setFunc(RootMoveY, RootMoveY));
  940. root_props.New().create("Z", MemberDesc(DATA_REAL).setFunc(RootMoveZ, RootMoveZ));
  941. root_props.New().create("X", MemberDesc(DATA_REAL).setFunc(RootRotX , RootRotX )).mouseEditSpeed(15).precision(1);
  942. root_props.New().create("Y", MemberDesc(DATA_REAL).setFunc(RootRotY , RootRotY )).mouseEditSpeed(15).precision(1);
  943. root_props.New().create("Z", MemberDesc(DATA_REAL).setFunc(RootRotZ , RootRotZ )).mouseEditSpeed(15).precision(1);
  944. Rect r=AddProperties(root_props, settings_region, Vec2(x+w+0.01f, y), ph, 0.17f, &ObjEdit.ts); REPAO(root_props).autoData(this); y=Min(r.min.y, reload.rect().min.y);
  945. settings_region.size(Vec2(settings_region.rect().w(), -y+0.02f));
  946. }
  947. T+=start .create("[").focusable(false).func(Start , T).desc(S+"Go to animation start\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+Home, "+Kb.ctrlCmdName()+"+[");
  948. T+=prev_frame.create("<").focusable(false).func(PrevFrame, T).desc(S+"Go to previous KeyFrame\nHold Shift to iterate all KeyFrame types\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+LeftArrow, "+Kb.ctrlCmdName()+"+<");
  949. T+=force_play.create( ).focusable(false) .desc(S+"Play as long as this button is pressed\nHold Shift to play backwards\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+W"); force_play.image=Proj.icon_play; force_play.mode=BUTTON_CONTINUOUS;
  950. T+=next_frame.create(">").focusable(false).func(NextFrame, T).desc(S+"Go to next KeyFrame\nHold Shift to iterate all KeyFrame types\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+RightArrow, "+Kb.ctrlCmdName()+"+>");
  951. T+=end .create("]").focusable(false).func(End , T).desc(S+"Go to animation end\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+End, "+Kb.ctrlCmdName()+"+]");
  952. T+=track .create(false);
  953. T+=optimize_anim .create();
  954. T+=time_range_speed.create();
  955. preview.create();
  956. return T;
  957. }
  958. SkelBone AnimEditor::transformedBone(int i)C
  959. {
  960. SkelBone bone;
  961. if(skel && InRange(i, skel->bones))bone=skel->bones[i];//else bone is already set to identity in constructor
  962. bone*=anim_skel.boneRoot(i).matrix();
  963. return bone;
  964. }
  965. Matrix AnimEditor::transformedBoneAxis(int i)C
  966. {
  967. Matrix m=transformedBone(i);
  968. if(op()==OP_POS && Kb.alt())m.orn().identity();
  969. m.scaleOrn((i>=0) ? SkelSlotSize : 1.0f/3);
  970. return m;
  971. }
  972. int AnimEditor::getBone(GuiObj *go, C Vec2 &screen_pos)
  973. {
  974. int index=-1; if(Edit::Viewport4::View *view=v4.getView(go))
  975. {
  976. view->setViewportCamera();
  977. flt dist=0;
  978. REPA(anim_skel.bones)
  979. {
  980. SkelBone bone=transformedBone(i);
  981. flt d; if(Distance2D(screen_pos, Edge(bone.pos, bone.to()), d, 0))if(index<0 || d<dist){index=i; dist=d;}
  982. }
  983. if(dist>0.03f)index=-1;
  984. }
  985. return index;
  986. }
  987. int AnimEditor::boneParent(int bone)C
  988. {
  989. return skel ? skel->boneParent(bone) : -1;
  990. }
  991. AnimKeys* AnimEditor::findVisKeys(int sbon_index, bool root)
  992. {
  993. if(Animation *anim=getVisAnim())
  994. {
  995. if(skel)if(C SkelBone *sbon=skel->bones.addr(sbon_index))return anim->findBone(sbon->name, sbon->type, sbon->type_index, sbon->type_sub); // use types in case animation was from another skeleton and we haven't adjusted types
  996. if(root)return &anim->keys;
  997. }
  998. return null;
  999. }
  1000. AnimKeys* AnimEditor::findKeys(int sbon_index, bool root)
  1001. {
  1002. if(anim)
  1003. {
  1004. if(skel)if(C SkelBone *sbon=skel->bones.addr(sbon_index))return anim->findBone(sbon->name, sbon->type, sbon->type_index, sbon->type_sub); // use types in case animation was from another skeleton and we haven't adjusted types
  1005. if(root)return &anim->keys;
  1006. }
  1007. return null;
  1008. }
  1009. AnimKeys* AnimEditor::getKeys(int sbon_index, bool root)
  1010. {
  1011. if(anim)
  1012. {
  1013. if(skel)if(C SkelBone *sbon=skel->bones.addr(sbon_index))return &anim->getBone(sbon->name, sbon->type, sbon->type_index, sbon->type_sub); // use types in case animation was from another skeleton and we haven't adjusted types
  1014. if(root)return &anim->keys;
  1015. }
  1016. return null;
  1017. }
  1018. AnimKeys* AnimEditor::findKeysParent(int sbon_index, bool root) {return sbon_index<0 ? null : findKeys(boneParent(sbon_index), root);}
  1019. int AnimEditor::CompareKey(C AnimKeys::Orn &key, C flt &time) {return Compare(key.time, time);}
  1020. int AnimEditor::CompareKey(C AnimKeys::Pos &key, C flt &time) {return Compare(key.time, time);}
  1021. int AnimEditor::CompareKey(C AnimKeys::Scale &key, C flt &time) {return Compare(key.time, time);}
  1022. AnimKeys::Orn * AnimEditor::FindOrn(AnimKeys &keys, flt time) {AnimKeys::Orn *key=null; flt dist; REPA(keys.orns ){flt d=Abs(keys.orns [i].time-time); if(!key || d<dist){key=&keys.orns [i]; dist=d;}} return (!key || dist>TimeEps()) ? null : key;}
  1023. AnimKeys::Pos * AnimEditor::FindPos(AnimKeys &keys, flt time) {AnimKeys::Pos *key=null; flt dist; REPA(keys.poss ){flt d=Abs(keys.poss [i].time-time); if(!key || d<dist){key=&keys.poss [i]; dist=d;}} return (!key || dist>TimeEps()) ? null : key;}
  1024. AnimKeys::Scale* AnimEditor::FindScale(AnimKeys &keys, flt time) {AnimKeys::Scale *key=null; flt dist; REPA(keys.scales){flt d=Abs(keys.scales[i].time-time); if(!key || d<dist){key=&keys.scales[i]; dist=d;}} return (!key || dist>TimeEps()) ? null : key;}
  1025. AnimKeys::Orn & AnimEditor::GetOrn(AnimKeys &keys, flt time, C Orient &Default) {if(AnimKeys::Orn *key=FindOrn (keys, time))return *key; int i; if(keys.orns .binarySearch(time, i, CompareKey))return keys.orns [i]; AnimKeys::Orn &key=keys.orns .NewAt(i); key.time=time; key.orn =Default; return key;}
  1026. AnimKeys::Pos & AnimEditor::GetPos(AnimKeys &keys, flt time, C Vec &Default) {if(AnimKeys::Pos *key=FindPos (keys, time))return *key; int i; if(keys.poss .binarySearch(time, i, CompareKey))return keys.poss [i]; AnimKeys::Pos &key=keys.poss .NewAt(i); key.time=time; key.pos =Default; return key;}
  1027. AnimKeys::Scale& AnimEditor::GetScale(AnimKeys &keys, flt time, C Vec &Default) {if(AnimKeys::Scale *key=FindScale(keys, time))return *key; int i; if(keys.scales.binarySearch(time, i, CompareKey))return keys.scales[i]; AnimKeys::Scale &key=keys.scales.NewAt(i); key.time=time; key.scale=Default; return key;}
  1028. flt AnimEditor::getBlend(C AnimKeys::Key &key )C {return getBlend(key.time);}
  1029. flt AnimEditor::getBlend( flt time)C
  1030. {
  1031. flt delta=time-animTime();
  1032. if(anim && anim->loop())
  1033. {
  1034. delta=Frac(delta, anim->length());
  1035. if(delta>anim->length()/2)delta-=anim->length();
  1036. }
  1037. return BlendSmoothCube(delta/blend_range);
  1038. }
  1039. void AnimEditor::update(C GuiPC &gpc)
  1040. {
  1041. lit_bone=-1;
  1042. if(visible() && gpc.visible)
  1043. {
  1044. optimize_anim.refresh(); // refresh all the time because animation can be changed all the time (since we're accessing it directly from 'Animations' cache)
  1045. playUpdate();
  1046. prepMeshSkel();
  1047. if(draw_bones())
  1048. {
  1049. // get lit
  1050. lit_bone=getBone(Gui.ms(), Ms.pos());
  1051. // get 'bone_axis'
  1052. if((op()==OP_POS || op()==OP_SCALE)
  1053. // && skel && InRange(sel_bone, skel.bones) don't check this so we can process just root too
  1054. )MatrixAxis(v4, transformedBoneAxis(sel_bone), bone_axis);else bone_axis=-1;
  1055. // select / edit
  1056. if(Edit::Viewport4::View *view=v4.getView(Gui.ms()))
  1057. {
  1058. if(Ms.bp(0))selBone(lit_bone);else
  1059. if(Ms.b (1) && op()>=0
  1060. //&& skel && InRange(sel_bone, skel.bones) don't check this so we can process just root too
  1061. )if(AnimKeys *keys=getKeys(sel_bone))
  1062. {
  1063. view->setViewportCamera();
  1064. C SkelBone * sbon=(skel ? skel->bones.addr(sel_bone) : null);
  1065. SkelBone bone= transformedBone (sel_bone), bone_parent; if(sel_bone>=0)bone_parent=transformedBone(boneParent(sel_bone)); // set 'bone_parent' only if we have a bone selected, otherwise (when root is selected) then keep it as identity
  1066. C AnimSkelBone &asbon= anim_skel.boneRoot (sel_bone);
  1067. Vec2 mul =(Kb.shift() ? 0.1f : 1.0f)*0.5f;
  1068. bool all =Kb.ctrlCmd(), rotate=Kb.alt(), use_blend=(blend_range>0);
  1069. switch(op())
  1070. {
  1071. case OP_ORN:
  1072. {
  1073. undos.set("orn");
  1074. op_orn:
  1075. if(!rotate)
  1076. {
  1077. mul*=CamMoveScale(v4.perspective())*MoveScale(*view);
  1078. Vec d=ActiveCam.matrix.x*Ms.d().x*mul.x
  1079. +ActiveCam.matrix.y*Ms.d().y*mul.y;
  1080. orn_target+=d;
  1081. bone.setFromTo(bone.pos, orn_target);
  1082. }
  1083. Orient pose=GetAnimOrient(bone, &bone_parent); pose.fix();
  1084. AnimKeys::Orn *orn=((all && keys->orns.elms()) ? null : &GetOrn(*keys, animTime(), pose)); // if there are no keys then create
  1085. if(rotate)
  1086. {
  1087. flt d=(Ms.d()*mul).sum();
  1088. if(all)
  1089. {
  1090. if(use_blend)REPA (keys->orns)keys->orns[i].orn.rotateDir(getBlend(keys->orns[i])*d);
  1091. else REPAO(keys->orns).orn.rotateDir(d);
  1092. }else // single
  1093. {
  1094. orn->orn.rotateDir(d);
  1095. }
  1096. }else
  1097. {
  1098. if(all)
  1099. {
  1100. // always calculate current orientation, because 'asbon.orn' can be zero
  1101. Vec p=orn_target-bone.pos; p/=Matrix3(bone_parent); p.normalize();
  1102. Orient orn=asbon.orn; if(!orn.fix()){if(sbon /*&& skel - no need to check because 'sbon' already guarantees 'skel'*/)orn=GetAnimOrient(*sbon, skel->bones.addr(sbon->parent));else orn.identity();}
  1103. Matrix3 transform;
  1104. if(use_blend)REPA(keys->orns)
  1105. {
  1106. Orient next=orn; next.rotateToDir(p, getBlend(keys->orns[i])); next.fixPerp();
  1107. GetTransform(transform, orn, next);
  1108. keys->orns[i].orn.mul(transform, true).fix();
  1109. }else
  1110. {
  1111. Orient next=orn; next.rotateToDir(p); next.fixPerp();
  1112. GetTransform(transform, orn, next);
  1113. REPAO(keys->orns).orn.mul(transform, true).fix();
  1114. }
  1115. }else // single
  1116. {
  1117. orn->orn=pose;
  1118. }
  1119. }
  1120. keys->setTangents(anim->loop(), anim->length());
  1121. anim->setRootMatrix();
  1122. setChanged();
  1123. }break;
  1124. case OP_ORN2:
  1125. {
  1126. undos.set("orn");
  1127. if(!sbon /*|| !skel - no need to check because 'sbon' already guarantees 'skel'*/)goto op_orn;
  1128. C SkelBone *sbon_parent=skel->bones.addr(sbon->parent);
  1129. AnimKeys *keys_parent= getKeys(sbon->parent, false);
  1130. if(!sbon_parent || !keys_parent)goto op_orn;
  1131. if(!rotate)
  1132. {
  1133. Vec perp=bone.pos-NearestPointOnStr(bone.pos, bone_parent.pos, !(orn_target-bone_parent.pos));
  1134. if( perp.normalize()<=EPS)switch(sbon->type)
  1135. {
  1136. case BONE_FINGER:
  1137. {
  1138. int hand=skel->findParent(sel_bone, BONE_HAND);
  1139. if( hand>=0)perp=transformedBone(hand).perp;else perp.set(0, 1, 0);
  1140. }break;
  1141. case BONE_HEAD :
  1142. case BONE_FOOT :
  1143. case BONE_SPINE :
  1144. case BONE_NECK : perp.set(0, 0, bone.pos.z-orn_target.z); break;
  1145. case BONE_TOE : perp.set(0, bone.pos.y-orn_target.y, 0); break;
  1146. case BONE_UPPER_ARM:
  1147. case BONE_FOREARM : perp.set(0, 0, -1); break;
  1148. case BONE_LOWER_LEG: perp.set(0, 0, 1); break;
  1149. default : perp=orn_perp; break;
  1150. }
  1151. mul*=CamMoveScale(v4.perspective())*MoveScale(*view);
  1152. Vec d=ActiveCam.matrix.x*Ms.d().x*mul.x
  1153. +ActiveCam.matrix.y*Ms.d().y*mul.y;
  1154. orn_target+=d;
  1155. Vec desired_dir=orn_target-bone_parent.pos;
  1156. flt a_length=Dist(bone_parent.pos, bone.pos), b_length=bone.length,
  1157. cur_length=Dist(bone_parent.pos, bone.to()),
  1158. desired_length=Min (desired_dir.length(), a_length+b_length);
  1159. // first rotate the parent to match the new direction (from parent->child.to to parent->orn_target)
  1160. Matrix3 m;
  1161. m.setRotation(!(bone.to()-bone_parent.pos), !desired_dir);
  1162. SCAST(Orient, bone_parent)*=m;
  1163. SCAST(Orient, bone )*=m; // this should include position, but we don't actually need it
  1164. perp *=m;
  1165. // rotate the parent and bone
  1166. Vec rotate_axis=CrossN(desired_dir, perp);
  1167. flt cur_angle=TriABAngle( cur_length, a_length, b_length),
  1168. desired_angle=TriABAngle(desired_length, a_length, b_length);
  1169. flt delta=desired_angle-cur_angle;
  1170. SCAST(Orient, bone_parent)*=Matrix3().setRotate(rotate_axis, delta);
  1171. //SCAST(Orient, bone )*=Matrix3().setRotate(rotate_axis, delta); child should be transformed too, but instead we're transforming child by 'delta' below
  1172. cur_angle=TriABAngle(a_length, b_length, cur_length);
  1173. desired_angle=TriABAngle(a_length, b_length, desired_length);
  1174. SCAST(Orient, bone )*=Matrix3().setRotate(rotate_axis, delta + desired_angle-cur_angle); // add delta because child is transformed by parent
  1175. Orient pose=GetAnimOrient(bone_parent, &transformedBone(sbon_parent->parent)); pose.fix();
  1176. AnimKeys::Orn *orn=&GetOrn(*keys_parent, animTime(), pose);
  1177. orn->orn=pose;
  1178. pose=GetAnimOrient(bone, &bone_parent); pose.fix();
  1179. orn=&GetOrn(*keys, animTime(), pose);
  1180. orn->orn=pose;
  1181. }else
  1182. {
  1183. flt d=(Ms.d()*mul).sum();
  1184. Matrix3 m;
  1185. m.setRotate(!(bone.to()-bone_parent.pos), d);
  1186. SCAST(Orient, bone_parent)*=m;
  1187. SCAST(Orient, bone )*=m; // this should include position, but we don't actually need it
  1188. Orient pose=GetAnimOrient(bone_parent, &transformedBone(sbon_parent->parent)); pose.fix();
  1189. AnimKeys::Orn *orn=&GetOrn(*keys_parent, animTime(), pose);
  1190. orn->orn=pose;
  1191. pose=GetAnimOrient(bone, &bone_parent); pose.fix();
  1192. orn=&GetOrn(*keys, animTime(), pose);
  1193. orn->orn=pose;
  1194. }
  1195. keys_parent->setTangents(anim->loop(), anim->length());
  1196. keys ->setTangents(anim->loop(), anim->length());
  1197. anim->setRootMatrix();
  1198. setChanged();
  1199. }break;
  1200. case OP_POS:
  1201. {
  1202. undos.set("pos");
  1203. AnimKeys::Pos *pos=((all && keys->poss.elms()) ? null : &GetPos(*keys, animTime(), asbon.pos)); // if there are no keys then create
  1204. mul*=CamMoveScale(v4.perspective())*MoveScale(*view);
  1205. Matrix m=transformedBoneAxis(sel_bone);
  1206. Vec d; switch(bone_axis)
  1207. {
  1208. case 0: d=AlignDirToCam(m.x, Ms.d() *mul ); break;
  1209. case 1: d=AlignDirToCam(m.y, Ms.d() *mul ); break;
  1210. case 2: d=AlignDirToCam(m.z, Ms.d() *mul ); break;
  1211. default: d=ActiveCam.matrix.x*Ms.d().x*mul.x
  1212. +ActiveCam.matrix.y*Ms.d().y*mul.y; break;
  1213. }
  1214. orn_target+=d;
  1215. d/=Matrix3(bone_parent);
  1216. if(all)
  1217. {
  1218. if(use_blend)REPA (keys->poss)keys->poss[i].pos+=d*getBlend(keys->poss[i]);
  1219. else REPAO(keys->poss).pos+=d;
  1220. }else // single
  1221. {
  1222. pos->pos+=d;
  1223. }
  1224. keys->setTangents(anim->loop(), anim->length());
  1225. anim->setRootMatrix();
  1226. setChanged();
  1227. }break;
  1228. case OP_SCALE:
  1229. {
  1230. undos.set("scale");
  1231. AnimKeys::Scale *scale=((all && keys->scales.elms()) ? null : &GetScale(*keys, animTime(), asbon.scale)); // if there are no keys then create
  1232. Vec d=0;
  1233. switch(bone_axis)
  1234. {
  1235. case 0: d.x+=AlignDirToCamEx(bone.cross(), Ms.d()*mul) ; break;
  1236. case 1: d.y+=AlignDirToCamEx(bone.perp , Ms.d()*mul) ; break;
  1237. case 2: d.z+=AlignDirToCamEx(bone.dir , Ms.d()*mul) ; break;
  1238. default: d += (Ms.d()*mul).sum(); break;
  1239. }
  1240. if(Kb.ctrlCmd()) // all
  1241. {
  1242. if(use_blend)REPA (keys->scales)keys->scales[i].scale+=d*getBlend(keys->scales[i]);
  1243. else REPAO(keys->scales).scale+=d;
  1244. }else // single
  1245. {
  1246. scale->scale+=d;
  1247. }
  1248. keys->setTangents(anim->loop(), anim->length());
  1249. anim->setRootMatrix();
  1250. setChanged();
  1251. setOrnTarget();
  1252. }break;
  1253. }
  1254. }
  1255. }
  1256. }
  1257. if(Ms.bp(2)) // close on middle click
  1258. {
  1259. if(settings_region.contains(Gui.ms()))settings.set(-1);
  1260. }
  1261. }
  1262. ::Viewport4Region::update(gpc); // process after adjusting 'animTime' so clicking on anim track will not be changed afterwards
  1263. }
  1264. bool AnimEditor::selectionZoom(flt &dist)
  1265. {
  1266. flt size=(mesh ? mesh->ext.size().avg() : 0);
  1267. if( size>0)
  1268. {
  1269. dist=size/Tan(v4.perspFov()/2);
  1270. return true;
  1271. }
  1272. return false;
  1273. }
  1274. bool AnimEditor::getHit(GuiObj *go, C Vec2 &screen_pos, Vec &hit_pos)
  1275. {
  1276. int hit_part=-1;
  1277. if(mesh)if(Edit::Viewport4::View *view=v4.getView(go))
  1278. {
  1279. C MeshLod &lod=mesh->getDrawLod(anim_skel.matrix());
  1280. MeshPart part;
  1281. view->setViewportCamera();
  1282. Vec pos, dir; ScreenToPosDir(screen_pos, pos, dir);
  1283. pos+=(D.viewFrom ()/Dot(dir, ActiveCam.matrix.z))*dir;
  1284. dir*= D.viewRange();
  1285. flt frac, f; Vec hp;
  1286. REPA(lod)
  1287. if(!(lod.parts[i].part_flag&MSHP_HIDDEN))
  1288. {
  1289. part.create(lod.parts[i]).setBase().base.animate(anim_skel);
  1290. if(Sweep(pos, dir, part, null, &f, &hp))if(hit_part<0 || f<frac){hit_part=i; frac=f; hit_pos=hp;}
  1291. }
  1292. }
  1293. return hit_part>=0;
  1294. }
  1295. void AnimEditor::camCenter(bool zoom)
  1296. {
  1297. Vec hit_pos; bool hit=getHit(Gui.ms(), Ms.pos(), hit_pos); flt dist;
  1298. v4.moveTo(hit ? hit_pos :
  1299. (skel && InRange(lit_bone, skel->bones)) ? transformedBone(lit_bone).center() :
  1300. (skel && InRange(sel_bone, skel->bones)) ? transformedBone(sel_bone).center() :
  1301. mesh ? mesh->ext.center :
  1302. VecZero);
  1303. if(zoom && selectionZoom(dist))v4.dist(dist);
  1304. }
  1305. void AnimEditor::resize()
  1306. {
  1307. ::Viewport4Region::resize();
  1308. track.rect(Rect(Proj.visible() ? 0 : Misc.rect().w(), -clientHeight(), v4.rect().max.x, 0.09f-clientHeight()));
  1309. end .rect(Rect_RD(track .rect().ru(), 0.07f, 0.07f));
  1310. next_frame.rect(Rect_RD(end .rect().ld(), 0.08f, 0.07f));
  1311. force_play.rect(Rect_RD(next_frame.rect().ld(), 0.08f, 0.07f));
  1312. prev_frame.rect(Rect_RD(force_play.rect().ld(), 0.08f, 0.07f));
  1313. start .rect(Rect_RD(prev_frame.rect().ld(), 0.07f, 0.07f));
  1314. v4.rect(Rect(v4.rect().min.x, Proj.visible() ? track.rect().max.y : Misc.rect().h()-clientHeight(), v4.rect().max.x, v4.rect().max.y));
  1315. optimize_anim.move(Vec2(rect().w(), rect().h()/-2)-optimize_anim.rect().right());
  1316. }
  1317. void AnimEditor::frame(int d)
  1318. {
  1319. if(d)if(AnimKeys *keys=findKeys(sel_bone))switch(Kb.shift() ? -1 : op())
  1320. {
  1321. case OP_ORN :
  1322. case OP_ORN2: if(keys->orns.elms())
  1323. {
  1324. int i=0; for(; i<keys->orns.elms(); i++){flt t=keys->orns[i].time; if(Equal(animTime(), t))break; if(t>=animTime()){if(d>0)d--; break;}}
  1325. i+=d; i=Mod(i, keys->orns.elms()); animTime(keys->orns[i].time);
  1326. }break;
  1327. case OP_POS: if(keys->poss.elms())
  1328. {
  1329. int i=0; for(; i<keys->poss.elms(); i++){flt t=keys->poss[i].time; if(Equal(animTime(), t))break; if(t>=animTime()){if(d>0)d--; break;}}
  1330. i+=d; i=Mod(i, keys->poss.elms()); animTime(keys->poss[i].time);
  1331. }break;
  1332. case OP_SCALE: if(keys->scales.elms())
  1333. {
  1334. int i=0; for(; i<keys->scales.elms(); i++){flt t=keys->scales[i].time; if(Equal(animTime(), t))break; if(t>=animTime()){if(d>0)d--; break;}}
  1335. i+=d; i=Mod(i, keys->scales.elms()); animTime(keys->scales[i].time);
  1336. }break;
  1337. default:
  1338. {
  1339. Memt<flt, 16384> times; keys->includeTimes(times, times, times);
  1340. if(times.elms())
  1341. {
  1342. int i=0; for(; i<times.elms(); i++){flt t=times[i]; if(Equal(animTime(), t))break; if(t>=animTime()){if(d>0)d--; break;}}
  1343. i+=d; i=Mod(i, times.elms()); animTime(times[i]);
  1344. }
  1345. }break;
  1346. }
  1347. }
  1348. bool AnimEditor::delFrameOrn(int bone)
  1349. {
  1350. if(AnimKeys *keys=findKeys(bone))if(AnimKeys::Orn *key=FindOrn(*keys, animTime()))
  1351. {
  1352. undos.set("del"); keys->orns.removeData(key, true);
  1353. if(keys->is())keys->setTangents(anim->loop(), anim->length());else anim->bones.removeData(static_cast<AnimBone*>(keys), true);
  1354. return true;
  1355. }
  1356. return false;
  1357. }
  1358. bool AnimEditor::delFramePos(int bone)
  1359. {
  1360. if(AnimKeys *keys=findKeys(bone))if(AnimKeys::Pos *key=FindPos(*keys, animTime()))
  1361. {
  1362. undos.set("del"); keys->poss.removeData(key, true);
  1363. if(keys->is())keys->setTangents(anim->loop(), anim->length());else anim->bones.removeData(static_cast<AnimBone*>(keys), true);
  1364. return true;
  1365. }
  1366. return false;
  1367. }
  1368. bool AnimEditor::delFrameScale(int bone)
  1369. {
  1370. if(AnimKeys *keys=findKeys(bone))if(AnimKeys::Scale *key=FindScale(*keys, animTime()))
  1371. {
  1372. undos.set("del"); keys->scales.removeData(key, true);
  1373. if(keys->is())keys->setTangents(anim->loop(), anim->length());else anim->bones.removeData(static_cast<AnimBone*>(keys), true);
  1374. return true;
  1375. }
  1376. return false;
  1377. }
  1378. bool AnimEditor::delFramesOrn(int bone)
  1379. {
  1380. if(AnimKeys *keys=findKeys(bone))if(keys->orns.elms()){undos.set("delAll"); keys->orns.del(); if(!keys->is())anim->bones.removeData(static_cast<AnimBone*>(keys), true); return true;}
  1381. return false;
  1382. }
  1383. bool AnimEditor::delFramesPos(int bone)
  1384. {
  1385. if(AnimKeys *keys=findKeys(bone))if(keys->poss.elms()){undos.set("delAll"); keys->poss.del(); if(!keys->is())anim->bones.removeData(static_cast<AnimBone*>(keys), true); return true;}
  1386. return false;
  1387. }
  1388. bool AnimEditor::delFramesScale(int bone)
  1389. {
  1390. if(AnimKeys *keys=findKeys(bone))if(keys->scales.elms()){undos.set("delAll"); keys->scales.del(); if(!keys->is())anim->bones.removeData(static_cast<AnimBone*>(keys), true); return true;}
  1391. return false;
  1392. }
  1393. void AnimEditor::delFrame()
  1394. {
  1395. bool changed=false;
  1396. if(op()==OP_ORN || op()<0)changed|=delFrameOrn (sel_bone);
  1397. if(op()==OP_ORN2 )changed|=delFrameOrn (sel_bone)|delFrameOrn(boneParent(sel_bone));
  1398. if(op()==OP_POS || op()<0)changed|=delFramePos (sel_bone);
  1399. if(op()==OP_SCALE || op()<0)changed|=delFrameScale(sel_bone);
  1400. if(changed){prepMeshSkel(); setOrnTarget(); anim->setRootMatrix(); setChanged();}
  1401. }
  1402. void AnimEditor::delFrames()
  1403. {
  1404. bool changed=false;
  1405. if(op()==OP_ORN || op()<0)changed|=delFramesOrn (sel_bone);
  1406. if(op()==OP_ORN2 )changed|=delFramesOrn (sel_bone)|delFramesOrn(boneParent(sel_bone));
  1407. if(op()==OP_POS || op()<0)changed|=delFramesPos (sel_bone);
  1408. if(op()==OP_SCALE || op()<0)changed|=delFramesScale(sel_bone);
  1409. if(changed){prepMeshSkel(); setOrnTarget(); anim->setRootMatrix(); setChanged();}
  1410. }
  1411. void AnimEditor::delFramesAtEnd()
  1412. {
  1413. if(anim)
  1414. {
  1415. bool changed=false;
  1416. undos.set("delAtEnd");
  1417. flt time=anim->length()-EPS;
  1418. REP(anim->bones.elms())
  1419. {
  1420. bool bone_changed=false;
  1421. AnimBone &bone=anim->bones[i];
  1422. if(bone.poss .elms()>=2 && bone.poss .last().time>=time && bone.poss .first().time<=EPS){bone.poss .removeLast(); bone_changed=true;}
  1423. if(bone.orns .elms()>=2 && bone.orns .last().time>=time && bone.orns .first().time<=EPS){bone.orns .removeLast(); bone_changed=true;}
  1424. if(bone.scales.elms()>=2 && bone.scales.last().time>=time && bone.scales.first().time<=EPS){bone.scales.removeLast(); bone_changed=true;}
  1425. if(bone_changed){bone.setTangents(anim->loop(), anim->length()); changed=true;}
  1426. }
  1427. if(changed){prepMeshSkel(); setOrnTarget(); /*anim.setRootMatrix(); we don't change root, only bones*/ setChanged();}
  1428. }
  1429. }
  1430. void AnimEditor::reverseFrames()
  1431. {
  1432. if(anim)
  1433. {
  1434. undos.set("reverse");
  1435. if(sel_bone<0 )anim->reverse();else // reverse entire animation when no bone is selected (instead of just root)
  1436. if(AnimKeys *keys=findKeys(sel_bone)){keys->reverse(anim->length()); anim->setRootMatrix();}
  1437. prepMeshSkel(); setOrnTarget(); setChanged();
  1438. }
  1439. }
  1440. void AnimEditor::removeMovement()
  1441. {
  1442. if(skel && anim)
  1443. {
  1444. undos.set("remMove");
  1445. bool changed=false;
  1446. REPA(skel->bones)if(skel->bones[i].parent==0xFF)if(AnimKeys *keys=findKeys(i))if(keys->poss.elms()>=2)
  1447. {
  1448. Vec pos_delta=keys->poss[0].pos - keys->poss.last().pos ;
  1449. flt start_time =keys->poss[0].time, end_time=keys->poss.last().time;
  1450. if(pos_delta.length()>EPS)
  1451. {
  1452. changed=true;
  1453. for(int i=1; i<keys->poss.elms(); i++)
  1454. {
  1455. AnimKeys::Pos &pos=keys->poss[i];
  1456. flt f=LerpR(start_time, end_time, pos.time);
  1457. pos.pos+=pos_delta*f;
  1458. }
  1459. keys->setTangents(anim->loop(), anim->length());
  1460. }
  1461. }
  1462. if(changed){prepMeshSkel(); setOrnTarget(); anim->setRootMatrix(); setChanged();}
  1463. }
  1464. }
  1465. void AnimEditor::freezeBone()
  1466. {
  1467. if(skel && anim)
  1468. {
  1469. if(!InRange(sel_bone, skel->bones)){Gui.msgBox(S, "No bone selected"); return;}
  1470. undos.set("freezeBone");
  1471. anim->freezeBone(*skel, sel_bone);
  1472. prepMeshSkel(); setOrnTarget(); setChanged();
  1473. }
  1474. }
  1475. void AnimEditor::playToggle()
  1476. {
  1477. play.toggle();
  1478. preview.toGui();
  1479. }
  1480. void AnimEditor::playUpdate(flt multiplier)
  1481. {
  1482. bool play=(Kb.b(KB_W) && Kb.ctrlCmd() && !Kb.alt()); if(play)T.force_play.push();
  1483. play|=T.force_play(); if(play && Kb.shift())CHS(multiplier);
  1484. play|=T.play();
  1485. if(anim)
  1486. {
  1487. if(flt length=anim->length())
  1488. {
  1489. if(play && !Ms.b(1))_anim_time+=Time.ad()*anim_speed*multiplier;
  1490. if(_anim_time>=length && _anim_time<length+EPS)_anim_time=length;else _anim_time=Frac(_anim_time, length); // allow being at the end
  1491. }else _anim_time=0;
  1492. }
  1493. if(play){prepMeshSkel(); setOrnTarget();}
  1494. }
  1495. void AnimEditor::prepMeshSkel()
  1496. {
  1497. if(Animation *anim=getVisAnim())if(ElmAnim *anim_data=data())if(Elm *skel_elm=Proj.findElm(anim_data->skel_id))if(ElmSkel *skel_data=skel_elm->skelData())if(skel_data->mesh_id.valid()) // get mesh from anim->skel->mesh
  1498. {
  1499. mesh=Proj.gamePath(skel_data->mesh_id);
  1500. skel=mesh->skeleton();
  1501. if(skel)
  1502. {
  1503. if(anim_skel.skeleton()!=skel || anim_skel.bones.elms()!=skel->bones.elms() || anim_skel.slots.elms()!=skel->slots.elms())anim_skel.create(skel);
  1504. skel_anim.create(*skel, *anim);
  1505. flt time=animTime(); if(anim->loop() && Equal(time, anim->length()))time=Max(0, anim->length()-EPS); // if animation is looped and the time is at the end, then normally it will be wrapped to the beginning due to Frac, however since we're editing/previewing, then we may want to see the last keyframe without wrapping
  1506. anim_skel.clear().animate(skel_anim, time-Time.ad()).animateRoot(*anim, time-Time.ad()).updateMatrix().updateVelocities(false, false); // always set previous frame to setup correct motion blur velocities when manually changing anim time
  1507. anim_skel.clear().animate(skel_anim, time ).animateRoot(*anim, time ).updateMatrix().updateVelocities(false, false);
  1508. }else
  1509. {
  1510. anim_skel.del().updateMatrix().updateVelocities(false, false);
  1511. }
  1512. return;
  1513. }
  1514. mesh=null;
  1515. skel=null;
  1516. anim_skel.del();
  1517. }
  1518. void AnimEditor::toGui()
  1519. {
  1520. REPAO( props).toGui();
  1521. REPAO(root_props).toGui();
  1522. preview.toGui();
  1523. ElmAnim *data=T.data();
  1524. loop .set(data && data->loop (), QUIET);
  1525. linear .set(data && data->linear(), QUIET);
  1526. root_from_body.set(data && FlagTest(data->flag, ElmAnim::ROOT_FROM_BODY), QUIET);
  1527. root_del_pos_x.set(data && FlagTest(data->flag, ElmAnim::ROOT_DEL_POS_X), QUIET);
  1528. root_del_pos_y.set(data && FlagTest(data->flag, ElmAnim::ROOT_DEL_POS_Y), QUIET);
  1529. root_del_pos_z.set(data && FlagTest(data->flag, ElmAnim::ROOT_DEL_POS_Z), QUIET);
  1530. root_del_rot_x.set(data && FlagTest(data->flag, ElmAnim::ROOT_DEL_ROT_X), QUIET);
  1531. root_del_rot_y.set(data && FlagTest(data->flag, ElmAnim::ROOT_DEL_ROT_Y), QUIET);
  1532. root_del_rot_z.set(data && FlagTest(data->flag, ElmAnim::ROOT_DEL_ROT_Z), QUIET);
  1533. root_2_keys .set(data && FlagTest(data->flag, ElmAnim::ROOT_2_KEYS ), QUIET);
  1534. root_set_move .set(data && data->rootMove(), QUIET);
  1535. root_set_rot .set(data && data->rootRot (), QUIET);
  1536. }
  1537. void AnimEditor::applySpeed()
  1538. {
  1539. if(anim && !Equal(anim_speed, 1) && !Equal(anim_speed, 0))if(ElmAnim *data=T.data())
  1540. {
  1541. undos.set("speed");
  1542. // adjust speed file param
  1543. Mems<Edit::FileParams> file_params=Edit::FileParams::Decode(data->src_file);
  1544. if(file_params.elms()==1)
  1545. {
  1546. TextParam &speed=file_params[0].getParam("speed");
  1547. flt set_speed=anim_speed; if(flt cur_speed=speed.asFlt())set_speed*=cur_speed;
  1548. speed.setValue(set_speed);
  1549. data->setSrcFile(Edit::FileParams::Encode(file_params));
  1550. }
  1551. anim->length(anim->length()/anim_speed, true);
  1552. setChanged();
  1553. anim_speed=1;
  1554. toGui();
  1555. }
  1556. }
  1557. void AnimEditor::moveEvent(int event, flt time)
  1558. {
  1559. if(anim && InRange(event, anim->events))
  1560. {
  1561. undos.set("eventMove");
  1562. anim->events[event].time=time;
  1563. setChanged();
  1564. }
  1565. }
  1566. void AnimEditor::newEvent(flt time)
  1567. {
  1568. if(anim)
  1569. {
  1570. undos.set("event"); // keep the same as 'renameEvent' because they're linked
  1571. anim->events.New().set(RenameEvent.textline().is() ? RenameEvent.textline() : S+"Event", time); // reuse last name if available
  1572. setChanged();
  1573. if(!Kb.ctrlCmd())RenameEvent.activate(anim->events.elms()-1); // activate window for renaming created event
  1574. }
  1575. }
  1576. void AnimEditor::delEvent(int index)
  1577. {
  1578. if(anim && InRange(index, anim->events))
  1579. {
  1580. undos.set("eventDel");
  1581. anim->events.remove(index, true);
  1582. setChanged();
  1583. preview.removedEvent(index);
  1584. }
  1585. }
  1586. void AnimEditor::renameEvent(int index, C Str &old_name, C Str &new_name)
  1587. {
  1588. if(anim)FREPA(anim->events)if(old_name==anim->events[i].name)if(!index--)
  1589. {
  1590. undos.set("event"); // keep the same as 'newEvent' because they're linked
  1591. Set(anim->events[i].name, new_name);
  1592. setChanged();
  1593. break;
  1594. }
  1595. }
  1596. void AnimEditor::flush()
  1597. {
  1598. if(elm && changed)
  1599. {
  1600. if(ElmAnim *data=elm->animData()){data->newVer(); if(anim)data->from(*anim);} // modify just before saving/sending in case we've received data from server after edit
  1601. if(anim){Save(*anim, Proj.gamePath(elm->id)); Proj.savedGame(*elm);}
  1602. Server.setElmLong(elm->id);
  1603. }
  1604. changed=false;
  1605. }
  1606. void AnimEditor::setChanged(bool file)
  1607. {
  1608. if(elm)
  1609. {
  1610. changed=true;
  1611. if(ElmAnim *data=elm->animData())
  1612. {
  1613. data->newVer();
  1614. if(anim)data->from(*anim);
  1615. if(file)data->file_time.getUTC();
  1616. optimize_anim.refresh();
  1617. }
  1618. }
  1619. }
  1620. void AnimEditor::validateFullscreen()
  1621. {
  1622. Mode .tabAvailable(MODE_ANIM, fullscreen && elm);
  1623. preview.visible ( !fullscreen && elm);
  1624. }
  1625. void AnimEditor::toggleFullscreen()
  1626. {
  1627. fullscreen^=1;
  1628. validateFullscreen();
  1629. activate(elm);
  1630. preview.kbSet();
  1631. toGui();
  1632. }
  1633. void AnimEditor::set(Elm *elm)
  1634. {
  1635. if(elm && elm->type!=ELM_ANIM)elm=null;
  1636. if(T.elm!=elm)
  1637. {
  1638. flush();
  1639. undos.del(); undoVis();
  1640. if(skel)
  1641. {
  1642. sel_bone_name=(InRange(sel_bone, skel->bones) ? skel->bones[sel_bone].name : null);
  1643. }
  1644. T.elm =elm;
  1645. T.elm_id=(elm ? elm->id : UIDZero);
  1646. if(elm)anim=Animations(Proj.gamePath(elm->id));else anim=null;
  1647. toGui();
  1648. Proj.refresh(false, false);
  1649. validateFullscreen();
  1650. RenameEvent.hide();
  1651. optimize_anim.refresh();
  1652. prepMeshSkel();
  1653. selBone(skel ? skel->findBoneI(sel_bone_name) : -1);
  1654. setOrnTarget();
  1655. optimize_anim.hide();
  1656. time_range_speed.hide();
  1657. Gui.closeMsgBox(transform_obj_dialog_id);
  1658. preview.moveToTop();
  1659. }
  1660. }
  1661. void AnimEditor::focus()
  1662. {
  1663. if(T.elm){if(fullscreen){Mode.set(MODE_ANIM); HideBig();}else preview.activate();}
  1664. }
  1665. void AnimEditor::activate(Elm *elm)
  1666. {
  1667. set(elm); focus();
  1668. }
  1669. void AnimEditor::toggle(Elm *elm)
  1670. {
  1671. if(elm==T.elm && (fullscreen ? selected() : true))elm=null;
  1672. if(fullscreen || preview.maximized())activate(elm);else set(elm); // if editor is in fullscreen mode, or the window is maximized then activate
  1673. }
  1674. void AnimEditor::elmChanging(C UID &elm_id)
  1675. {
  1676. if(elm && elm->id==elm_id)
  1677. {
  1678. undos.set(null, true);
  1679. }
  1680. }
  1681. void AnimEditor::elmChanged(C UID &elm_id)
  1682. {
  1683. if(elm && elm->id==elm_id)
  1684. {
  1685. prepMeshSkel();
  1686. setOrnTarget();
  1687. toGui();
  1688. }
  1689. }
  1690. void AnimEditor::erasing(C UID &elm_id) {if(elm && elm->id==elm_id)set(null);}
  1691. void AnimEditor::setTarget(C Str &obj_id)
  1692. {
  1693. if(anim)if(ElmAnim *anim_data=data())
  1694. {
  1695. if(obj_id.is())
  1696. {
  1697. if(Elm *obj=Proj.findElm(obj_id))if(ElmObj *obj_data=obj->objData())
  1698. {
  1699. bool has_skel=false;
  1700. if(Elm *mesh=Proj.findElm( obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())
  1701. if(Elm *skel=Proj.findElm(mesh_data->skel_id))if(ElmSkel *skel_data=skel->skelData())
  1702. {
  1703. has_skel=true;
  1704. if(skel->id!=anim_data->skel_id)
  1705. {
  1706. undos.set("skel");
  1707. /*if(0) // don't transform, because for example if original mesh was super small, then it had to be scaled up, making the transform very big, and when transforming to new transform the scale could get big, so just accept the current transform, assume that user has scaled the orignal mesh/skel/anim to match the new mesh/skel/anim
  1708. {
  1709. Elm *old_skel_elm=Proj.findElm(anim_data.skel_id, ELM_SKEL);
  1710. C Skeleton *old_skel=(old_skel_elm ? Skeletons(Proj.gamePath(*old_skel_elm)) : null),
  1711. *new_skel= Skeletons(Proj.gamePath( skel.id ));
  1712. anim.transform(GetTransform(anim_data.transform(), skel_data.transform()), old_skel ? *old_skel : *new_skel); // transform from current to target, if 'old_skel' is not available then try using 'new_skel'
  1713. }*/
  1714. if(RenameAnimBonesOnSkelChange)if(Skeleton *new_skel=Skeletons(Proj.gamePath(skel->id)))
  1715. {
  1716. if(ObjEdit.skel_elm && ObjEdit.skel_elm->id==skel->id)ObjEdit.flushMeshSkel(); // !! if this skeleton is currently edited, then we have to flush it, for example, it could have been empty at the start and we've just added bones, so 'new_skel' is empty and bones are only in 'ObjEdit.mesh_skel_temp' memory, and before changing 'anim_data.skel_id' !!
  1717. anim->setBoneNameTypeIndexesFromSkeleton(*new_skel);
  1718. }
  1719. anim_data->skel_id =skel->id; // !! set after 'flushMeshSkel' !!
  1720. anim_data->skel_time.getUTC();
  1721. anim_data->transform=skel_data->transform;
  1722. setChanged();
  1723. toGui(); Proj.refresh(false, false);
  1724. }
  1725. }
  1726. if(!has_skel)Gui.msgBox(S, "Can't set target object because it doesn't have a mesh skeleton.");
  1727. }
  1728. }else
  1729. {
  1730. undos.set("skel");
  1731. anim_data->skel_id.zero();
  1732. anim_data->skel_time.getUTC();
  1733. setChanged();
  1734. toGui(); Proj.refresh(false, false);
  1735. }
  1736. }
  1737. }
  1738. void AnimEditor::drag(Memc<UID> &elms, GuiObj *obj, C Vec2 &screen_pos)
  1739. {
  1740. if(contains(obj) || preview.contains(obj))
  1741. REPA(elms)
  1742. if(Elm *obj=Proj.findElm(elms[i], ELM_OBJ))
  1743. {
  1744. setTarget(obj->id.asHex());
  1745. elms.remove(i, true);
  1746. break;
  1747. }
  1748. }
  1749. AnimEditor::AnimEditor() : elm_id(UIDZero), elm(null), changed(false), fullscreen(false), copied_bone_pos_relative(false), blend_range(-1), anim(null), skel(null), anim_speed(1), key_time(0), lit_bone(-1), sel_bone(-1), bone_axis(-1), orn_target(0), orn_perp(0), copied_bone_pos(0), undos(true), _anim_time(0) {}
  1750. AnimEditor::Preview::Preview() : draw_bones(false), draw_slots(false), draw_axis(false), draw_plane(false), event_lit(-1), event_sel(-1), time_speed(1), prop_max_x(0), cam_yaw(PI), cam_pitch(0), cam_zoom(1), length(null), event(null) {}
  1751. AnimEditor::OptimizeAnim::OptimizeAnim() : refresh_needed(true), preview(true), angle_eps(EPS_ANIM_ANGLE), pos_eps(EPS_ANIM_POS), scale_eps(EPS_ANIM_SCALE), file_size(null), optimized_size(null) {}
  1752. /******************************************************************************/