Blender2.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "blender2.h"
  19. #include <istdplug.h>
  20. #include "utilapi.h"
  21. #include "appdata.h"
  22. //----------------------------------------------------------------------------
  23. // Blender_Class
  24. //----------------------------------------------------------------------------
  25. class Blender_Class : public UtilityObj
  26. {
  27. public:
  28. Blender_Class();
  29. void BeginEditParams(Interface *ip,IUtil *iu);
  30. void EndEditParams(Interface *ip,IUtil *iu);
  31. void SelectionSetChanged ( Interface * ip, IUtil * ui );
  32. void DeleteThis() {}
  33. void Init(HWND hWnd);
  34. void Destroy(HWND hWnd);
  35. void Close () { iu->CloseUtility (); }
  36. void Get_Active_Time_Range ();
  37. void Blend_Keys ();
  38. void Loop_Controllers ();
  39. void OutputObject(INode *node,TCHAR *fname);
  40. private:
  41. BOOL Is_Root ( INode * node );
  42. float Heading_Delta_From_Quat ( Quat q );
  43. void Set_Data_Chunk ( INode * node, const Blender_Data_Chunk & new_data );
  44. void Remove_Data_Chunk ( INode * node );
  45. int first_frame;
  46. int first_match;
  47. int last_frame;
  48. int last_match;
  49. ISpinnerControl * first_frame_spin;
  50. ISpinnerControl * first_match_spin;
  51. ISpinnerControl * last_frame_spin;
  52. ISpinnerControl * last_match_spin;
  53. IUtil * iu;
  54. Interface *ip;
  55. HWND hPanel;
  56. };
  57. //----------------------------------------------------------------------------
  58. // the_blender
  59. //----------------------------------------------------------------------------
  60. static Blender_Class the_blender;
  61. //----------------------------------------------------------------------------
  62. // Blender_Desc_Class
  63. //----------------------------------------------------------------------------
  64. class Blender_Desc_Class:public ClassDesc
  65. {
  66. public:
  67. int IsPublic() {return 1;}
  68. void * Create(BOOL) {return &the_blender;}
  69. const TCHAR * ClassName() {return _T("Key Blender");}
  70. SClass_ID SuperClassID() {return UTILITY_CLASS_ID;}
  71. Class_ID ClassID() {return Blender_Class_ID;}
  72. const TCHAR* Category() {return _T("Westwood Tools");}
  73. };
  74. //----------------------------------------------------------------------------
  75. // blender_desc
  76. //----------------------------------------------------------------------------
  77. static Blender_Desc_Class blender_desc;
  78. //----------------------------------------------------------------------------
  79. // BlenderDesc
  80. //----------------------------------------------------------------------------
  81. ClassDesc* BlenderDesc() {return &blender_desc;}
  82. //----------------------------------------------------------------------------
  83. // BlenderDlgProc
  84. //----------------------------------------------------------------------------
  85. static BOOL CALLBACK BlenderDlgProc
  86. (
  87. HWND hWnd,
  88. UINT msg,
  89. WPARAM wParam,
  90. LPARAM lParam
  91. )
  92. {
  93. switch (msg)
  94. {
  95. case WM_INITDIALOG:
  96. the_blender.Init(hWnd);
  97. break;
  98. case WM_DESTROY:
  99. the_blender.Destroy(hWnd);
  100. break;
  101. case WM_COMMAND:
  102. switch (LOWORD(wParam))
  103. {
  104. case ID_CLOSE:
  105. the_blender.Close ();
  106. break;
  107. case ID_GET_ACTIVE_TIME_RANGE:
  108. the_blender.Get_Active_Time_Range ();
  109. break;
  110. case ID_APPLY:
  111. the_blender.Blend_Keys ();
  112. break;
  113. }
  114. break;
  115. default:
  116. return FALSE;
  117. }
  118. return TRUE;
  119. }
  120. //----------------------------------------------------------------------------
  121. // Blender_Class::Blender_Class
  122. //----------------------------------------------------------------------------
  123. Blender_Class::Blender_Class()
  124. {
  125. iu = NULL;
  126. ip = NULL;
  127. hPanel = NULL;
  128. first_frame = 0;
  129. first_match = 0;
  130. last_frame = 0;
  131. last_match = 0;
  132. }
  133. //----------------------------------------------------------------------------
  134. // Blender_Class::BeginEditParams
  135. //----------------------------------------------------------------------------
  136. void Blender_Class::BeginEditParams(Interface *ip,IUtil *iu)
  137. {
  138. this->iu = iu;
  139. this->ip = ip;
  140. hPanel = ip->AddRollupPage
  141. (
  142. hInstance,
  143. MAKEINTRESOURCE(IDD_ASCIIOUT_PANEL),
  144. BlenderDlgProc,
  145. _T("Key Blender"),
  146. 0
  147. );
  148. }
  149. //----------------------------------------------------------------------------
  150. // Blender_Class::EndEditParams
  151. //----------------------------------------------------------------------------
  152. void Blender_Class::EndEditParams ( Interface *ip, IUtil *iu )
  153. {
  154. this->iu = NULL;
  155. this->ip = NULL;
  156. ip->DeleteRollupPage(hPanel);
  157. hPanel = NULL;
  158. }
  159. //----------------------------------------------------------------------------
  160. // Blender_Class::SelectionSetChanged
  161. //----------------------------------------------------------------------------
  162. void Blender_Class::SelectionSetChanged ( Interface * ip, IUtil * iu )
  163. {
  164. if ( ip->GetSelNodeCount () == 0 )
  165. {
  166. EnableWindow ( GetDlgItem ( hPanel, ID_APPLY ), FALSE );
  167. }
  168. else
  169. {
  170. EnableWindow ( GetDlgItem ( hPanel, ID_APPLY ), TRUE );
  171. }
  172. }
  173. //----------------------------------------------------------------------------
  174. // Blender_Class::Init
  175. //----------------------------------------------------------------------------
  176. void Blender_Class::Init ( HWND hWnd )
  177. {
  178. if ( ip->GetSelNodeCount () == 0 )
  179. {
  180. EnableWindow ( GetDlgItem ( hWnd, ID_APPLY ), FALSE );
  181. }
  182. else
  183. {
  184. EnableWindow ( GetDlgItem ( hWnd, ID_APPLY ), TRUE );
  185. }
  186. int ticks_per_frame = GetTicksPerFrame ();
  187. int start_frame = ip->GetAnimRange ().Start () / ticks_per_frame;
  188. int end_frame = ip->GetAnimRange ().End () / ticks_per_frame;
  189. first_frame = start_frame;
  190. first_match = start_frame;
  191. last_frame = end_frame;
  192. last_match = start_frame;
  193. first_frame_spin = SetupIntSpinner
  194. (
  195. hWnd,
  196. IDC_FIRST_SPIN,
  197. IDC_FIRST_EDIT,
  198. -65536,
  199. 65535,
  200. first_frame
  201. );
  202. first_frame_spin->SetResetValue ( first_frame );
  203. first_match_spin = SetupIntSpinner
  204. (
  205. hWnd,
  206. IDC_FIRST_MATCH_SPIN,
  207. IDC_FIRST_MATCH_EDIT,
  208. -65536,
  209. 65535,
  210. first_match
  211. );
  212. first_match_spin->SetResetValue ( first_match );
  213. last_frame_spin = SetupIntSpinner
  214. (
  215. hWnd,
  216. IDC_LAST_SPIN,
  217. IDC_LAST_EDIT,
  218. -65536,
  219. 65535,
  220. last_frame
  221. );
  222. last_frame_spin->SetResetValue ( last_frame );
  223. last_match_spin = SetupIntSpinner
  224. (
  225. hWnd,
  226. IDC_LAST_MATCH_SPIN,
  227. IDC_LAST_MATCH_EDIT,
  228. -65536,
  229. 65535,
  230. last_match
  231. );
  232. last_match_spin->SetResetValue ( last_match );
  233. }
  234. //----------------------------------------------------------------------------
  235. // Blender_Class::Destroy
  236. //----------------------------------------------------------------------------
  237. void Blender_Class::Destroy(HWND hWnd)
  238. {
  239. }
  240. //----------------------------------------------------------------------------
  241. // Blender_Class::Get_Active_Time_Range
  242. //----------------------------------------------------------------------------
  243. void Blender_Class::Get_Active_Time_Range ()
  244. {
  245. int ticks_per_frame = GetTicksPerFrame ();
  246. int start_frame = ip->GetAnimRange ().Start () / ticks_per_frame;
  247. int end_frame = ip->GetAnimRange ().End () / ticks_per_frame;
  248. first_frame_spin->SetValue ( start_frame, FALSE );
  249. last_frame_spin->SetValue ( end_frame, FALSE );
  250. first_match_spin->SetValue ( start_frame, FALSE );
  251. last_match_spin->SetValue ( start_frame, FALSE );
  252. }
  253. //----------------------------------------------------------------------------
  254. // Blender_Class::Blend_Keys
  255. //----------------------------------------------------------------------------
  256. static const Quat zero (0.0,0.0,0.0,1.0);
  257. void Blender_Class::Blend_Keys ()
  258. {
  259. Interval interval; // Used for returned validity intervals (which are not used).
  260. int ticks_per_frame = GetTicksPerFrame ();
  261. TimeValue start_time = first_frame_spin->GetIVal () * ticks_per_frame;
  262. TimeValue end_time = last_frame_spin->GetIVal () * ticks_per_frame;
  263. TimeValue start_match = first_match_spin->GetIVal () * ticks_per_frame;
  264. TimeValue end_match = last_match_spin->GetIVal () * ticks_per_frame;
  265. float t_scale = 1.0f / (float) (end_time - start_time);
  266. BOOL bad_controller_found = FALSE;
  267. theHold.Begin ();
  268. SetCursor ( LoadCursor (NULL, IDC_WAIT) );
  269. SuspendAnimate ();
  270. AnimateOn ();
  271. int number_of_nodes = ip->GetSelNodeCount ();
  272. for ( int i = 0; i < number_of_nodes; ++ i )
  273. {
  274. // Get the inode.
  275. INode * inode = ip->GetSelNode ( i );
  276. Control * tm_controller = inode->GetTMController ();
  277. if ( tm_controller == NULL )
  278. continue;
  279. Quat rot_start_delta = zero;
  280. Quat rot_end_delta = zero;
  281. Blender_Data_Chunk data;
  282. data.position_delta = Point3 (0.0f, 0.0f, 0.0f);
  283. data.heading_delta = 0.0f;
  284. Control * c;
  285. //--------------------------------------------------------------------
  286. // Get the rotation controller and change its keys.
  287. c = tm_controller->GetRotationController ();
  288. if ( c != NULL )
  289. {
  290. if ( c->ClassID () != Class_ID (TCBINTERP_ROTATION_CLASS_ID, 0) )
  291. {
  292. #if 0
  293. char m [ 256 ];
  294. sprintf ( m, "Node %s has rotation controller ID %u.",
  295. inode->GetName (), c->ClassID ().PartA () );
  296. MessageBox ( GetActiveWindow (), m, "Debug", MB_OK );
  297. #endif
  298. bad_controller_found = TRUE;
  299. }
  300. else
  301. {
  302. Quat actual_start_orientation;
  303. Quat desired_start_orientation;
  304. Quat actual_end_orientation;
  305. Quat desired_end_orientation;
  306. c->GetValue ( start_time, & actual_start_orientation, interval );
  307. c->GetValue ( start_match, & desired_start_orientation, interval );
  308. c->GetValue ( end_time, & actual_end_orientation, interval );
  309. c->GetValue ( end_match, & desired_end_orientation, interval );
  310. // Ensure there are keys at the beginning and end of the blend interval.
  311. c->SetValue ( start_time, & actual_start_orientation );
  312. c->SetValue ( end_time, & actual_end_orientation );
  313. actual_end_orientation.MakeClosest ( actual_start_orientation );
  314. desired_start_orientation.MakeClosest ( actual_start_orientation );
  315. desired_end_orientation.MakeClosest ( actual_end_orientation );
  316. rot_start_delta = desired_start_orientation / actual_start_orientation;
  317. rot_end_delta = desired_end_orientation / actual_end_orientation;
  318. data.heading_delta = Heading_Delta_From_Quat
  319. ( actual_end_orientation / actual_start_orientation );
  320. #if 1
  321. int number_of_keys = c->NumKeys ();
  322. for ( int j = 0; j < number_of_keys; ++ j )
  323. {
  324. TimeValue key_time = c->GetKeyTime ( j );
  325. if ( key_time >= start_time && key_time <= end_time )
  326. {
  327. Quat orientation;
  328. c->GetValue ( key_time, & orientation, interval );
  329. float t = (float) (key_time - start_time) * t_scale;
  330. Quat delta = Slerp ( rot_start_delta, zero, t ) *
  331. Slerp ( zero, rot_end_delta, t );
  332. orientation = orientation * delta;
  333. c->SetValue ( key_time, & orientation );
  334. }
  335. }
  336. #else
  337. IKeyControl * keys = GetKeyControlInterface ( c );
  338. if ( keys != NULL )
  339. {
  340. int number_of_keys = keys->GetNumKeys ();
  341. Quat prev_key_absolute (0.0,0.0,0.0,1.0);
  342. Quat new_prev_key_absolute (0.0,0.0,0.0,1.0);
  343. for ( int j = 0; j < number_of_keys; ++ j )
  344. {
  345. ITCBRotKey key;
  346. keys->GetKey ( j, & key );
  347. Quat this_key_absolute = prev_key_absolute * (Quat) key.val;
  348. Quat new_key_absolute = this_key_absolute;
  349. if ( key.time >= start_time && key.time <= end_time )
  350. {
  351. float t = (float) (key.time - start_time) * t_scale;
  352. Quat delta = Slerp ( rot_start_delta, zero, t ) *
  353. Slerp ( zero, rot_end_delta, t );
  354. new_key_absolute = this_key_absolute * delta;
  355. key.val = new_key_absolute / new_prev_key_absolute;
  356. keys->SetKey ( j, & key );
  357. }
  358. prev_key_absolute = this_key_absolute;
  359. new_prev_key_absolute = new_key_absolute;
  360. }
  361. }
  362. #endif
  363. }
  364. }
  365. //--------------------------------------------------------------------
  366. // Get the position controller and change its keys.
  367. c = tm_controller->GetPositionController ();
  368. if ( c != NULL )
  369. {
  370. if ( c->ClassID () != Class_ID (TCBINTERP_POSITION_CLASS_ID, 0) )
  371. {
  372. bad_controller_found = TRUE;
  373. }
  374. else
  375. {
  376. Point3 actual_start_position;
  377. Point3 desired_start_position;
  378. Point3 actual_end_position;
  379. Point3 desired_end_position;
  380. c->GetValue ( start_time, & actual_start_position, interval );
  381. c->GetValue ( start_match, & desired_start_position, interval );
  382. c->GetValue ( end_time, & actual_end_position, interval );
  383. c->GetValue ( end_match, & desired_end_position, interval );
  384. // Ensure there are keys at the beginning and end of the blend interval.
  385. c->SetValue ( start_time, & actual_start_position );
  386. c->SetValue ( end_time, & actual_end_position );
  387. data.position_delta = actual_end_position - actual_start_position;
  388. #if 1
  389. int number_of_keys = c->NumKeys ();
  390. for ( int j = 0; j < number_of_keys; ++ j )
  391. {
  392. TimeValue key_time = c->GetKeyTime ( j );
  393. if ( key_time >= start_time && key_time <= end_time )
  394. {
  395. Point3 position;
  396. c->GetValue ( key_time, & position, interval );
  397. float t = (float) (key_time - start_time) * t_scale;
  398. // Calculate the position the node would be in if it
  399. // traveled in a straight line.
  400. Point3 actual_position = actual_start_position * (1.0f - t) +
  401. actual_end_position * t;
  402. // Find the delta between the straight-line position and
  403. // the real position.
  404. Point3 delta = position - actual_position;
  405. // Rotate the delta by the change in orientation
  406. // at this time.
  407. Quat rot_delta = Slerp ( rot_start_delta, zero, t ) *
  408. Slerp ( zero, rot_end_delta, t );
  409. Matrix3 rot_matrix;
  410. rot_delta.MakeMatrix ( rot_matrix );
  411. delta = delta * rot_matrix;
  412. // Add the delta to the straight-line position on the
  413. // desired line.
  414. Point3 desired_position = desired_start_position * (1.0f - t) +
  415. desired_end_position * t;
  416. position = desired_position + delta;
  417. // Store the new key value.
  418. c->SetValue ( key_time, & position );
  419. }
  420. }
  421. #else
  422. IKeyControl * keys = GetKeyControlInterface ( c );
  423. if ( keys != NULL )
  424. {
  425. int number_of_keys = keys->GetNumKeys ();
  426. for ( int j = 0; j < number_of_keys; ++ j )
  427. {
  428. ITCBPoint3Key key;
  429. keys->GetKey ( j, & key );
  430. if ( key.time >= start_time && key.time <= end_time )
  431. {
  432. float t = (float) (key.time - start_time) * t_scale;
  433. // Calculate the position the node would be in if it
  434. // traveled in a straight line.
  435. Point3 actual_position = actual_start_position * (1.0f - t) +
  436. actual_end_position * t;
  437. // Find the delta between the straight-line position and
  438. // the real position.
  439. Point3 delta = key.val - actual_position;
  440. // Rotate the delta by the change in orientation
  441. // at this time.
  442. Quat rot_delta = Slerp ( rot_start_delta, zero, t ) *
  443. Slerp ( zero, rot_end_delta, t );
  444. Matrix3 rot_matrix;
  445. rot_delta.MakeMatrix ( rot_matrix );
  446. delta = delta * rot_matrix;
  447. // Add the delta to the straight-line position on the
  448. // desired line.
  449. Point3 desired_position = desired_start_position * (1.0f - t) +
  450. desired_end_position * t;
  451. key.val = desired_position + delta;
  452. // Store the new key value.
  453. keys->SetKey ( j, & key );
  454. }
  455. }
  456. }
  457. #endif
  458. }
  459. }
  460. // If this node is a root node (its parent is not selected), attach
  461. // a data chunk that describes its motion during the blended section.
  462. if ( Is_Root ( inode ) )
  463. Set_Data_Chunk ( inode, data );
  464. else
  465. Remove_Data_Chunk ( inode );
  466. inode->InvalidateTM ();
  467. }
  468. ResumeAnimate ();
  469. TSTR undostr;
  470. undostr.printf ( "Blend Keys" );
  471. theHold.Accept ( undostr );
  472. SetCursor ( LoadCursor (NULL, IDC_ARROW) );
  473. ip->RedrawViews ( ip->GetTime () );
  474. if ( bad_controller_found )
  475. {
  476. MessageBox ( GetActiveWindow (),
  477. "Warning: Some controllers were not blended\n"
  478. "because they were not TCB controllers.", "Warning", MB_OK );
  479. }
  480. }
  481. //----------------------------------------------------------------------------
  482. // Blender_Class::Is_Root
  483. //----------------------------------------------------------------------------
  484. BOOL Blender_Class::Is_Root ( INode * node )
  485. {
  486. node = node->GetParentNode ();
  487. int number_of_nodes = ip->GetSelNodeCount ();
  488. for ( int i = 0; i < number_of_nodes; ++ i )
  489. {
  490. if ( ip->GetSelNode ( i ) == node )
  491. return FALSE;
  492. }
  493. return TRUE;
  494. }
  495. //----------------------------------------------------------------------------
  496. // Blender_Class::Heading_Delta_From_Quat
  497. //----------------------------------------------------------------------------
  498. float Blender_Class::Heading_Delta_From_Quat ( Quat q )
  499. {
  500. Matrix3 rot_matrix;
  501. q.MakeMatrix ( rot_matrix );
  502. Point3 row = rot_matrix.GetRow ( 0 );
  503. return (float) atan2 ( row.y, row.x );
  504. }
  505. //----------------------------------------------------------------------------
  506. // Blender_Class::Set_Data_Chunk
  507. //----------------------------------------------------------------------------
  508. void Blender_Class::Set_Data_Chunk
  509. (
  510. INode * node,
  511. const Blender_Data_Chunk & new_data
  512. )
  513. {
  514. AppDataChunk * chunk = node->GetAppDataChunk
  515. (
  516. Blender_Class_ID,
  517. UTILITY_CLASS_ID,
  518. 1
  519. );
  520. Blender_Data_Chunk * data;
  521. if ( chunk != NULL )
  522. {
  523. data = (Blender_Data_Chunk *) chunk->data;
  524. }
  525. else
  526. {
  527. data = (Blender_Data_Chunk *) malloc ( sizeof (Blender_Data_Chunk) );
  528. node->AddAppDataChunk
  529. (
  530. Blender_Class_ID,
  531. UTILITY_CLASS_ID,
  532. 1,
  533. sizeof (Blender_Data_Chunk),
  534. data
  535. );
  536. }
  537. *data = new_data;
  538. #if 0
  539. char m [ 256 ];
  540. sprintf ( m, "Setting data chunk for %s:\n"
  541. "Position delta = (%.1f, %.1f, %.1f)\n"
  542. "Heading delta = %.1f",
  543. node->GetName (),
  544. data->position_delta.x,
  545. data->position_delta.y,
  546. data->position_delta.z,
  547. data->heading_delta * 180 / PI );
  548. MessageBox ( GetActiveWindow (), m, "Debug", MB_OK );
  549. #endif
  550. }
  551. //----------------------------------------------------------------------------
  552. // Blender_Class::Remove_Data_Chunk
  553. //----------------------------------------------------------------------------
  554. void Blender_Class::Remove_Data_Chunk ( INode * node )
  555. {
  556. node->RemoveAppDataChunk
  557. (
  558. Blender_Class_ID,
  559. UTILITY_CLASS_ID,
  560. 1
  561. );
  562. }
  563. //----------------------------------------------------------------------------
  564. // Blender_Class::Loop_Controllers
  565. //----------------------------------------------------------------------------
  566. void Blender_Class::Loop_Controllers ()
  567. {
  568. int number_of_nodes = ip->GetSelNodeCount ();
  569. for ( int i = 0; i < number_of_nodes; ++ i )
  570. {
  571. // Get the inode.
  572. INode * inode = ip->GetSelNode ( i );
  573. Control * tm_controller = inode->GetTMController ();
  574. if ( tm_controller == NULL )
  575. continue;
  576. Control * c;
  577. c = tm_controller->GetRotationController ();
  578. if ( c )
  579. {
  580. c->SetORT ( ORT_LOOP, ORT_BEFORE );
  581. c->SetORT ( ORT_LOOP, ORT_AFTER );
  582. }
  583. c = tm_controller->GetPositionController ();
  584. if ( c )
  585. {
  586. c->SetORT ( ORT_LOOP, ORT_BEFORE );
  587. c->SetORT ( ORT_LOOP, ORT_AFTER );
  588. }
  589. c = tm_controller->GetScaleController ();
  590. if ( c )
  591. {
  592. c->SetORT ( ORT_LOOP, ORT_BEFORE );
  593. c->SetORT ( ORT_LOOP, ORT_AFTER );
  594. }
  595. }
  596. }