animcontrol.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  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. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/animcontrol.cpp $*
  25. * *
  26. * $Author:: Byon_g $*
  27. * *
  28. * $Modtime:: 1/04/02 10:26a $*
  29. * *
  30. * $Revision:: 53 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "animcontrol.h"
  36. #include "assets.h"
  37. #include "debug.h"
  38. #include "rendobj.h"
  39. #include "chunkio.h"
  40. #include "saveload.h"
  41. #include "htree.h"
  42. /*
  43. **
  44. */
  45. enum {
  46. XXXCHUNKID_LEGS = 910991512,
  47. XXXCHUNKID_TORSO,
  48. CHUNKID_CHANNEL,
  49. CHUNKID_VARIABLES,
  50. CHUNKID_OLD,
  51. CHUNKID_NEW,
  52. CHUNKID_PARENT,
  53. CHUNKID_CHANNEL1,
  54. CHUNKID_CHANNEL2,
  55. MICROCHUNKID_BLEND_TIMER = 1,
  56. MICROCHUNKID_BLEND_TOTAL,
  57. MICROCHUNKID_FRAME,
  58. XXXMICROCHUNKID_WEIGHT,
  59. MICROCHUNKID_MODE,
  60. MICROCHUNKID_ANIMATION_NAME,
  61. MICROCHUNKID_MODEL_PTR,
  62. MICROCHUNKID_CHANNEL2_RATIO,
  63. MICROCHUNKID_TARGET_FRAME,
  64. MICROCHUNKID_SKELETON,
  65. };
  66. /*
  67. ** AnimChannelClass
  68. */
  69. AnimChannelClass::AnimChannelClass( void ) :
  70. Animation( NULL ),
  71. Frame( 0.0f ),
  72. NumFrames( 1 ),
  73. TargetFrame( 0.0f ),
  74. Mode( ANIM_MODE_ONCE )
  75. {
  76. }
  77. AnimChannelClass::~AnimChannelClass( void )
  78. {
  79. if ( Animation ) {
  80. Animation->Release_Ref();
  81. Animation = NULL;
  82. }
  83. }
  84. AnimChannelClass & AnimChannelClass::operator = (const AnimChannelClass & src)
  85. {
  86. if (Animation != NULL) {
  87. Animation->Release_Ref();
  88. }
  89. Animation = src.Animation;
  90. if (Animation != NULL) {
  91. Animation->Add_Ref();
  92. }
  93. Frame = src.Frame;
  94. NumFrames = src.NumFrames;
  95. Mode = src.Mode;
  96. TargetFrame = src.TargetFrame;
  97. return *this;
  98. }
  99. bool AnimChannelClass::Save( ChunkSaveClass & csave )
  100. {
  101. csave.Begin_Chunk( CHUNKID_VARIABLES );
  102. // save the anim first, because the load will stomp the frame & weight
  103. if ( Animation ) {
  104. csave.Begin_Micro_Chunk( MICROCHUNKID_ANIMATION_NAME );
  105. const char * anim_name = Animation->Get_Name();
  106. csave.Write( anim_name, strlen( anim_name ) + 1);
  107. csave.End_Micro_Chunk();
  108. }
  109. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FRAME, Frame );
  110. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MODE, Mode );
  111. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TARGET_FRAME, TargetFrame );
  112. csave.End_Chunk();
  113. return true;
  114. }
  115. bool AnimChannelClass::Load( ChunkLoadClass &cload )
  116. {
  117. while (cload.Open_Chunk()) {
  118. switch(cload.Cur_Chunk_ID()) {
  119. case CHUNKID_VARIABLES:
  120. while (cload.Open_Micro_Chunk()) {
  121. switch(cload.Cur_Micro_Chunk_ID()) {
  122. READ_MICRO_CHUNK( cload, MICROCHUNKID_FRAME, Frame );
  123. READ_MICRO_CHUNK( cload, MICROCHUNKID_MODE, Mode );
  124. READ_MICRO_CHUNK( cload, MICROCHUNKID_TARGET_FRAME, TargetFrame );
  125. case MICROCHUNKID_ANIMATION_NAME:
  126. {
  127. char anim_name[80];
  128. cload.Read( anim_name, cload.Cur_Micro_Chunk_Length() );
  129. Set_Animation( anim_name );
  130. break;
  131. }
  132. default:
  133. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  134. break;
  135. }
  136. cload.Close_Micro_Chunk();
  137. }
  138. break;
  139. default:
  140. Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  141. break;
  142. }
  143. cload.Close_Chunk();
  144. }
  145. return true;
  146. }
  147. void AnimChannelClass::Set_Animation( const char *name )
  148. {
  149. // If this is our current anim, bail
  150. if ( ( Animation != NULL ) && ( name != NULL ) ) {
  151. if ( stricmp( Animation->Get_Name(), name ) == 0 ) {
  152. return;
  153. }
  154. }
  155. if ( ( Animation == NULL ) && ( name == NULL ) ) {
  156. return;
  157. }
  158. // Release the old anim
  159. if ( Animation ) {
  160. Animation->Release_Ref();
  161. Animation = NULL;
  162. }
  163. // we need to switch anims
  164. if (( name != NULL ) && ( name[0] != 0 ) ) {
  165. Animation = WW3DAssetManager::Get_Instance()->Get_HAnim( name );
  166. }
  167. if ( Animation ) {
  168. SET_REF_OWNER( Animation );
  169. NumFrames = Animation->Get_Num_Frames();
  170. Mode = ANIM_MODE_ONCE;
  171. Frame = 0;
  172. TargetFrame = 0;
  173. }
  174. }
  175. void AnimChannelClass::Set_Animation( const HAnimClass *anim )
  176. {
  177. // If this is our current anim, bail
  178. if ( Animation == anim ) {
  179. return;
  180. }
  181. // Release the old anim
  182. if ( Animation ) {
  183. Animation->Release_Ref();
  184. Animation = NULL;
  185. }
  186. // we need to switch anims
  187. Animation = (HAnimClass *)anim;
  188. if ( Animation ) {
  189. Animation->Add_Ref();
  190. NumFrames = Animation->Get_Num_Frames();
  191. Mode = ANIM_MODE_ONCE;
  192. Frame = 0;
  193. TargetFrame = 0;
  194. }
  195. }
  196. void AnimChannelClass::Set_Mode( AnimMode mode, float frame )
  197. {
  198. Mode = mode;
  199. if ( frame >= 0 ) {
  200. Frame = frame;
  201. }
  202. }
  203. void AnimChannelClass::Update( float dtime )
  204. {
  205. if ( Mode == ANIM_MODE_STOP ) {
  206. return;
  207. }
  208. if ( Animation != NULL ) {
  209. switch ( Mode )
  210. {
  211. case ANIM_MODE_LOOP:
  212. //
  213. // Increment the frame based on the current timeslice
  214. //
  215. Frame += dtime * Animation->Get_Frame_Rate();
  216. //
  217. // Handle wrapping
  218. //
  219. if ( Frame >= NumFrames-1 ) {
  220. Frame -= NumFrames-1;
  221. }
  222. if ( Frame >= NumFrames ) {
  223. Frame = 0;
  224. }
  225. break;
  226. case ANIM_MODE_TARGET:
  227. //
  228. // Which direction are we animating?
  229. //
  230. if ( Frame < TargetFrame ) {
  231. Frame += dtime * Animation->Get_Frame_Rate();
  232. //
  233. // If we overshoot targetframe, snap to targetframe
  234. //
  235. if (Frame >= TargetFrame) {
  236. Frame = TargetFrame;
  237. }
  238. } else if ( Frame > TargetFrame ) {
  239. Frame -= dtime * Animation->Get_Frame_Rate();
  240. //
  241. // If we overshoot targetframe, snap to targetframe
  242. //
  243. if ( Frame <= TargetFrame ) {
  244. Frame = TargetFrame;
  245. }
  246. }
  247. break;
  248. case ANIM_MODE_ONCE:
  249. //
  250. // Increment the frame based on the current timeslice
  251. //
  252. Frame += dtime * Animation->Get_Frame_Rate();
  253. //
  254. // Make sure we don't go past the end
  255. //
  256. if ( Frame > NumFrames-1 ) {
  257. Frame = NumFrames-1;
  258. }
  259. break;
  260. }
  261. #if 0
  262. if ( dtime != 0 ) {
  263. Debug_Say(( "Anim %s frame %1.3f\n", Animation->Get_Name(), Frame ));
  264. }
  265. #endif
  266. }
  267. }
  268. void AnimChannelClass::Get_Animation_Data( AnimationDataList & list, float weight )
  269. {
  270. if ( Animation != NULL && weight > 0 ) {
  271. AnimationDataRecord * record = list.Uninitialized_Add();
  272. WWASSERT( record != NULL );
  273. record->Animation = Animation;
  274. record->Frame = Frame;
  275. record->Weight = weight;
  276. }
  277. }
  278. void AnimChannelClass::Update_Model( RenderObjClass *anim_model )
  279. {
  280. if ( Animation ) {
  281. anim_model->Set_Animation( Animation, Frame );
  282. } else {
  283. anim_model->Set_Animation();
  284. }
  285. }
  286. /*
  287. ** BlendableAnimChannelClass
  288. */
  289. BlendableAnimChannelClass::BlendableAnimChannelClass( void ) :
  290. BlendTimer( 0 ),
  291. BlendTotal( 0 )
  292. {
  293. }
  294. bool BlendableAnimChannelClass::Save( ChunkSaveClass & csave )
  295. {
  296. csave.Begin_Chunk( CHUNKID_NEW );
  297. NewChannel.Save( csave );
  298. csave.End_Chunk();
  299. csave.Begin_Chunk( CHUNKID_OLD );
  300. OldChannel.Save( csave );
  301. csave.End_Chunk();
  302. csave.Begin_Chunk( CHUNKID_VARIABLES );
  303. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BLEND_TIMER, BlendTimer );
  304. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_BLEND_TOTAL, BlendTotal );
  305. csave.End_Chunk();
  306. return true;
  307. }
  308. bool BlendableAnimChannelClass::Load( ChunkLoadClass &cload )
  309. {
  310. while (cload.Open_Chunk()) {
  311. switch(cload.Cur_Chunk_ID()) {
  312. case CHUNKID_NEW:
  313. NewChannel.Load( cload );
  314. break;
  315. case CHUNKID_OLD:
  316. OldChannel.Load( cload );
  317. break;
  318. case CHUNKID_VARIABLES:
  319. while (cload.Open_Micro_Chunk()) {
  320. switch(cload.Cur_Micro_Chunk_ID()) {
  321. READ_MICRO_CHUNK( cload, MICROCHUNKID_BLEND_TIMER, BlendTimer );
  322. READ_MICRO_CHUNK( cload, MICROCHUNKID_BLEND_TOTAL, BlendTotal );
  323. default:
  324. Debug_Say(( "Unrecognized BlendableAnimChannel Variable chunkID\n" ));
  325. break;
  326. }
  327. cload.Close_Micro_Chunk();
  328. }
  329. break;
  330. default:
  331. Debug_Say(( "Unrecognized BlendableAnimChannel chunkID\n" ));
  332. break;
  333. }
  334. cload.Close_Chunk();
  335. }
  336. return true;
  337. }
  338. void BlendableAnimChannelClass::Set_Animation( const char *name, float blendtime, float start_frame )
  339. {
  340. // if setting to our current anim, bail
  341. if ( ( NewChannel.Peek_Animation() == NULL ) && ( name == NULL ) ) {
  342. return;
  343. }
  344. if ( ( NewChannel.Peek_Animation() != NULL ) && ( name != NULL ) ) {
  345. if ( stricmp( NewChannel.Peek_Animation()->Get_Name(), name ) == 0 ) {
  346. return;
  347. }
  348. }
  349. // if no current channel, or no blend, or no new name, don't blend
  350. if ( (NewChannel.Peek_Animation() == NULL) || (blendtime == 0) || (name == NULL) ) {
  351. BlendTotal = 0.0f;
  352. BlendTimer = 0.0f;
  353. } else if ( BlendTotal == 0.0 ) { //if not currently blending
  354. OldChannel = NewChannel;
  355. BlendTimer = 0.0f;
  356. BlendTotal = blendtime;
  357. #if 0
  358. } else if ( OldChannel.Peek_Animation() == new_anim.Peek_Animation() ) { // if old anim is the new one
  359. OldChannel.Copy_From( NewChannel );
  360. BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
  361. BlendTotal = blendtime;
  362. #endif
  363. } else if ( (BlendTimer / BlendTotal) > 0.5 ) { // if more than halfway through the old blend
  364. OldChannel = NewChannel;
  365. BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
  366. BlendTotal = blendtime;
  367. } else {
  368. BlendTimer = (BlendTimer / BlendTotal) * blendtime;
  369. BlendTotal = blendtime;
  370. }
  371. NewChannel.Set_Animation( name );
  372. if ( NewChannel.Peek_Animation() != NULL ) {
  373. NewChannel.Set_Frame( start_frame );
  374. }
  375. if ( name == NULL ) {
  376. OldChannel.Set_Animation( (const char *)NULL );
  377. }
  378. }
  379. void BlendableAnimChannelClass::Set_Animation( const HAnimClass * anim, float blendtime, float start_frame )
  380. {
  381. // if setting to our current anim, bail
  382. if ( ( NewChannel.Peek_Animation() == NULL ) && ( anim == NULL ) ) {
  383. return;
  384. }
  385. if ( ( NewChannel.Peek_Animation() != NULL ) && ( anim != NULL ) ) {
  386. if ( NewChannel.Peek_Animation() == anim ) {
  387. return;
  388. }
  389. }
  390. // if no current channel, or no blend, or no new name, don't blend
  391. if ( (NewChannel.Peek_Animation() == NULL) || (blendtime == 0) || (anim == NULL) ) {
  392. BlendTotal = 0.0f;
  393. BlendTimer = 0.0f;
  394. } else if ( BlendTotal == 0.0 ) { //if not currently blending
  395. OldChannel = NewChannel;
  396. BlendTimer = 0.0f;
  397. BlendTotal = blendtime;
  398. #if 0
  399. } else if ( OldChannel.Peek_Animation() == new_anim.Peek_Animation() ) { // if old anim is the new one
  400. OldChannel.Copy_From( NewChannel );
  401. BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
  402. BlendTotal = blendtime;
  403. #endif
  404. } else if ( (BlendTimer / BlendTotal) > 0.5 ) { // if more than halfway through the old blend
  405. OldChannel = NewChannel;
  406. BlendTimer = (1.0f - (BlendTimer / BlendTotal)) * blendtime;
  407. BlendTotal = blendtime;
  408. } else {
  409. BlendTimer = (BlendTimer / BlendTotal) * blendtime;
  410. BlendTotal = blendtime;
  411. }
  412. NewChannel.Set_Animation( anim );
  413. if ( NewChannel.Peek_Animation() != NULL ) {
  414. NewChannel.Set_Frame( start_frame );
  415. }
  416. if ( anim == NULL ) {
  417. OldChannel.Set_Animation( (const HAnimClass *)NULL );
  418. }
  419. }
  420. void BlendableAnimChannelClass::Update( float dtime )
  421. {
  422. if ( BlendTotal != 0.0f ) { // if blending between two animations
  423. BlendTimer += dtime; // Bump blend timer forward
  424. if ( BlendTimer >= BlendTotal ) // blend complete, remove oldanim
  425. {
  426. BlendTotal = 0.0f;
  427. BlendTimer = 0.0f;
  428. OldChannel.Set_Animation( (const char *)NULL );
  429. }
  430. }
  431. // Calculate which frame we are on in each of the animations
  432. NewChannel.Update( dtime );
  433. OldChannel.Update( dtime );
  434. }
  435. void BlendableAnimChannelClass::Get_Animation_Data( AnimationDataList & list, float weight )
  436. {
  437. float blend_ratio = 1.0f; // assume no blending
  438. if ( BlendTotal != 0.0f ) { // if blending between two animations
  439. // Calculate the blend percentage between the two animations.
  440. // This starts at 0.0 (all OldAnimation) and proceeds to 1.0 (all Animation)
  441. blend_ratio = WWMath::Clamp( BlendTimer / BlendTotal, 0, 1 );
  442. }
  443. NewChannel.Get_Animation_Data( list, weight * blend_ratio );
  444. OldChannel.Get_Animation_Data( list, weight * ( 1 - blend_ratio ) );
  445. }
  446. void BlendableAnimChannelClass::Update_Model( RenderObjClass *anim_model )
  447. {
  448. float blend_ratio = 1.0f; // assume no blending
  449. if ( BlendTotal != 0.0f ) { // if blending between two animations
  450. // Calculate the blend percentage between the two animations.
  451. // This starts at 0.0 (all OldAnimation) and proceeds to 1.0 (all Animation)
  452. blend_ratio = WWMath::Clamp( BlendTimer / BlendTotal, 0, 1 );
  453. }
  454. if ( OldChannel.Peek_Animation() ) {
  455. anim_model->Set_Animation( OldChannel.Peek_Animation(), OldChannel.Get_Frame(),
  456. NewChannel.Peek_Animation(), NewChannel.Get_Frame(),
  457. blend_ratio );
  458. } else if ( NewChannel.Peek_Animation() ) {
  459. anim_model->Set_Animation( NewChannel.Peek_Animation(), NewChannel.Get_Frame() );
  460. } else {
  461. anim_model->Set_Animation();
  462. }
  463. }
  464. /*
  465. ** AnimControlClass
  466. */
  467. AnimControlClass::AnimControlClass( void ) :
  468. Model( NULL )
  469. {
  470. }
  471. AnimControlClass::~AnimControlClass( void )
  472. {
  473. REF_PTR_RELEASE( Model );
  474. }
  475. bool AnimControlClass::Save( ChunkSaveClass & csave )
  476. {
  477. csave.Begin_Chunk( CHUNKID_VARIABLES );
  478. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_MODEL_PTR, Model );
  479. csave.End_Chunk();
  480. return true;
  481. }
  482. bool AnimControlClass::Load( ChunkLoadClass &cload )
  483. {
  484. while (cload.Open_Chunk()) {
  485. switch(cload.Cur_Chunk_ID()) {
  486. case CHUNKID_VARIABLES:
  487. WWASSERT( Model == NULL );
  488. while (cload.Open_Micro_Chunk()) {
  489. switch(cload.Cur_Micro_Chunk_ID()) {
  490. READ_MICRO_CHUNK( cload, MICROCHUNKID_MODEL_PTR, Model );
  491. default:
  492. Debug_Say(( "Unrecognized AnimControl Variable chunkID\n" ));
  493. break;
  494. }
  495. cload.Close_Micro_Chunk();
  496. }
  497. if ( Model != NULL ) {
  498. REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&Model );
  499. } else {
  500. Debug_Say(( "Loaded NULL model\n" ));
  501. }
  502. break;
  503. default:
  504. Debug_Say(( "Unrecognized AnimControl chunkID\n" ));
  505. break;
  506. }
  507. cload.Close_Chunk();
  508. }
  509. return true;
  510. }
  511. void AnimControlClass::Set_Model( RenderObjClass *anim_model )
  512. {
  513. REF_PTR_SET( Model, anim_model );
  514. }
  515. /*
  516. ** SimpleAnimControlClass
  517. */
  518. SimpleAnimControlClass::SimpleAnimControlClass( void )
  519. {
  520. }
  521. SimpleAnimControlClass::~SimpleAnimControlClass( void )
  522. {
  523. }
  524. bool SimpleAnimControlClass::Save( ChunkSaveClass & csave )
  525. {
  526. csave.Begin_Chunk( CHUNKID_PARENT );
  527. AnimControlClass::Save( csave );
  528. csave.End_Chunk();
  529. csave.Begin_Chunk( CHUNKID_CHANNEL );
  530. Channel.Save( csave );
  531. csave.End_Chunk();
  532. return true;
  533. }
  534. bool SimpleAnimControlClass::Load( ChunkLoadClass &cload )
  535. {
  536. while (cload.Open_Chunk()) {
  537. switch(cload.Cur_Chunk_ID()) {
  538. case CHUNKID_PARENT:
  539. AnimControlClass::Load( cload );
  540. break;
  541. case CHUNKID_CHANNEL:
  542. Channel.Load( cload );
  543. break;
  544. default:
  545. Debug_Say(( "Unrecognized HumanAnimControl chunkID\n" ));
  546. break;
  547. }
  548. cload.Close_Chunk();
  549. }
  550. return true;
  551. }
  552. void SimpleAnimControlClass::Set_Animation( const char *name, float blendtime, float start_frame )
  553. {
  554. Channel.Set_Animation( name, blendtime, start_frame );
  555. }
  556. void SimpleAnimControlClass::Set_Animation( const HAnimClass * anim, float blendtime, float start_frame )
  557. {
  558. Channel.Set_Animation( anim, blendtime, start_frame );
  559. }
  560. void SimpleAnimControlClass::Update( float dtime )
  561. {
  562. Channel.Update( dtime );
  563. // Setup the model for the current frame(s)
  564. assert( Model != NULL );
  565. Channel.Update_Model( Model );
  566. }
  567. /*
  568. ** HumanAnimControlClass
  569. */
  570. HumanAnimControlClass::HumanAnimControlClass( void ) :
  571. AnimCombo( 2 ),
  572. Channel2Ratio( 0 ),
  573. Skeleton( 'A' ),
  574. AnimSpeedScale( 1 )
  575. {
  576. }
  577. HumanAnimControlClass::~HumanAnimControlClass( void )
  578. {
  579. }
  580. bool HumanAnimControlClass::Save( ChunkSaveClass & csave )
  581. {
  582. csave.Begin_Chunk( CHUNKID_PARENT );
  583. AnimControlClass::Save( csave );
  584. csave.End_Chunk();
  585. csave.Begin_Chunk( CHUNKID_VARIABLES );
  586. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_CHANNEL2_RATIO, Channel2Ratio );
  587. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SKELETON, Skeleton );
  588. csave.End_Chunk();
  589. csave.Begin_Chunk( CHUNKID_CHANNEL1 );
  590. Channel1.Save( csave );
  591. csave.End_Chunk();
  592. csave.Begin_Chunk( CHUNKID_CHANNEL2 );
  593. Channel2.Save( csave );
  594. csave.End_Chunk();
  595. // Don't need to save the animcombo
  596. return true;
  597. }
  598. bool HumanAnimControlClass::Load( ChunkLoadClass &cload )
  599. {
  600. while (cload.Open_Chunk()) {
  601. switch(cload.Cur_Chunk_ID()) {
  602. case CHUNKID_PARENT:
  603. AnimControlClass::Load( cload );
  604. break;
  605. case CHUNKID_VARIABLES:
  606. while (cload.Open_Micro_Chunk()) {
  607. switch(cload.Cur_Micro_Chunk_ID()) {
  608. READ_MICRO_CHUNK( cload, MICROCHUNKID_CHANNEL2_RATIO, Channel2Ratio );
  609. READ_MICRO_CHUNK( cload, MICROCHUNKID_SKELETON, Skeleton );
  610. default:
  611. Debug_Say(( "Unrecognized HumanAnimControl Variable chunkID\n" ));
  612. break;
  613. }
  614. cload.Close_Micro_Chunk();
  615. }
  616. break;
  617. case CHUNKID_CHANNEL1:
  618. Channel1.Load( cload );
  619. break;
  620. case CHUNKID_CHANNEL2:
  621. Channel2.Load( cload );
  622. break;
  623. default:
  624. Debug_Say(( "Unrecognized HumanAnimControl chunkID\n" ));
  625. break;
  626. }
  627. cload.Close_Chunk();
  628. }
  629. return true;
  630. }
  631. void HumanAnimControlClass::Build_Skeleton_Anim_Name( StringClass& new_name, const char * name )
  632. {
  633. if (name == NULL) return;
  634. if (*name == NULL) {
  635. Debug_Say(( "No name in Build_Skeleton_Anim_Name\n" ));
  636. return;
  637. }
  638. new_name = name;
  639. if ( Skeleton == 'V' ) { // Special case for visceroids
  640. return;
  641. }
  642. // If the anim doesn't start with "S_A_HUMAN.", add it
  643. if ( ::strnicmp( name, "S_", 2 ) != 0 ) {
  644. new_name.Format( "S_%c_HUMAN.%s", Skeleton, name );
  645. }
  646. // If the anim name is "S_A_HUMAN.H_A_*", and the Skeleton is not 'A', use
  647. // the other skeleton anim, if found
  648. if ( new_name.Get_Length() > 14 && Skeleton != 'A' && ::strnicmp( new_name, "S_A_HUMAN.H_A_", 14 ) == 0 ) {
  649. StringClass mod_name(new_name,true);
  650. mod_name[2] = Skeleton;
  651. mod_name[12] = Skeleton;
  652. // can we find the anim name?
  653. HAnimClass * anim = WW3DAssetManager::Get_Instance()->Get_HAnim( mod_name );
  654. if ( anim != NULL ) {
  655. anim->Release_Ref();
  656. new_name=mod_name;
  657. }
  658. }
  659. }
  660. void HumanAnimControlClass::Set_Animation( const char *name, float blendtime, float start_frame )
  661. {
  662. StringClass new_name(0,true);
  663. Build_Skeleton_Anim_Name( new_name, name );
  664. Channel1.Set_Animation( new_name, blendtime, start_frame );
  665. Channel2.Set_Animation( (const char *)NULL );
  666. Channel2Ratio = 0;
  667. }
  668. void HumanAnimControlClass::Set_Animation( const HAnimClass * anim, float blendtime, float start_frame )
  669. {
  670. if ( anim != NULL ) {
  671. Set_Animation( anim->Get_Name(), blendtime, start_frame );
  672. } else {
  673. Set_Animation( (const char *)NULL, blendtime, start_frame );
  674. }
  675. }
  676. void HumanAnimControlClass::Set_Animation( const char *name1, const char * name2, float ratio, float blendtime )
  677. {
  678. StringClass new_name1(0,true);
  679. Build_Skeleton_Anim_Name( new_name1, name1 );
  680. StringClass new_name2(0,true);
  681. Build_Skeleton_Anim_Name( new_name2, name2 );
  682. if ( ratio == 0 ) {
  683. Set_Animation( new_name1, blendtime );
  684. return;
  685. }
  686. if ( Channel2Ratio == 0 ) {
  687. Channel2 = Channel1;
  688. }
  689. Channel1.Set_Animation( new_name1, blendtime );
  690. Channel2.Set_Animation( new_name2, blendtime );
  691. Channel2Ratio = ratio;
  692. }
  693. void HumanAnimControlClass::Set_Mode( AnimMode mode, float frame )
  694. {
  695. Channel1.Set_Mode( mode, frame );
  696. Channel2.Set_Mode( mode, frame );
  697. }
  698. void HumanAnimControlClass::Set_Model( RenderObjClass *anim_model )
  699. {
  700. AnimControlClass::Set_Model( anim_model );
  701. // Update the skeleton
  702. if( anim_model != NULL ) {
  703. const HTreeClass * tree = anim_model->Get_HTree();
  704. if ( tree != NULL ) {
  705. const char * name = tree->Get_Name();
  706. Skeleton = name[2];
  707. }
  708. }
  709. }
  710. /*
  711. **
  712. */
  713. StringClass _MonitorAnimDescription;
  714. HumanAnimControlClass * _Monitor = NULL;
  715. void HumanAnimControlClass::Update( float dtime )
  716. {
  717. // Update channels
  718. Channel1.Update( dtime * AnimSpeedScale );
  719. Channel2.Update( dtime * AnimSpeedScale );
  720. if ( Model != NULL ) {
  721. // Get Animation data
  722. DataList.Reset_Active();
  723. Channel1.Get_Animation_Data( DataList, (1 - Channel2Ratio) );
  724. Channel2.Get_Animation_Data( DataList, Channel2Ratio );
  725. // Use the cheapest anim method possible
  726. int total_animations = DataList.Count();
  727. if ( total_animations == 0 ) {
  728. // Debug_Say(( "No animations to display!\n" ));
  729. Model->Set_Animation();
  730. } else if ( total_animations == 1 ) {
  731. // Debug_Say(( "1 animation to display!\n" ));
  732. Model->Set_Animation( DataList[0].Animation, DataList[0].Frame );
  733. } else if ( total_animations == 2 ) {
  734. // Debug_Say(( "2 animation to display!\n" ));
  735. float percent = DataList[1].Weight / (DataList[0].Weight + DataList[1].Weight);
  736. Model->Set_Animation( DataList[0].Animation, DataList[0].Frame,
  737. DataList[1].Animation, DataList[1].Frame, percent );
  738. } else {
  739. // Debug_Say(( ">2 animation to display!\n" ));
  740. // set up anim combo
  741. AnimCombo.Reset();
  742. for ( int i = 0; i < total_animations; i++ ) {
  743. HAnimComboDataClass * anim_data = new HAnimComboDataClass();
  744. anim_data->Set_HAnim( DataList[i].Animation );
  745. anim_data->Set_Frame( DataList[i].Frame );
  746. anim_data->Set_Weight( DataList[i].Weight );
  747. AnimCombo.Append_Anim_Combo_Data( anim_data );
  748. }
  749. // Setup the model for the current frame(s)
  750. Model->Set_Animation( &AnimCombo );
  751. }
  752. if ( _Monitor == this ) {
  753. for ( int i = 0; i < total_animations; i++ ) {
  754. if ( i == 0 ) {
  755. _MonitorAnimDescription = "";
  756. }
  757. StringClass a(0,true);
  758. a.Format( "%s %1.0f%% %1.0f\n", DataList[i].Animation->Get_Name()+10, DataList[i].Weight*100, DataList[i].Frame );
  759. _MonitorAnimDescription += a;
  760. }
  761. }
  762. }
  763. }
  764. /*
  765. **
  766. */
  767. void HumanAnimControlClass::Get_Information( StringClass & string )
  768. {
  769. _Monitor = this;
  770. string += _MonitorAnimDescription;
  771. }