hanim.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  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. /* $Header: /Commando/Code/ww3d2/hanim.cpp 3 12/13/01 7:01p Patrick $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Commando / G 3D Library *
  24. * *
  25. * $Archive:: /Commando/Code/ww3d2/hanim.cpp $*
  26. * *
  27. * Author:: Greg_h *
  28. * *
  29. * $Modtime:: 12/13/01 6:54p $*
  30. * *
  31. * $Revision:: 3 $*
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  36. #include "hanim.h"
  37. #include "assetmgr.h"
  38. #include "htree.h"
  39. #include "motchan.h"
  40. #include "chunkio.h"
  41. #include "w3d_file.h"
  42. #include "wwdebug.h"
  43. #include <string.h>
  44. #include <nstrdup.h>
  45. /*
  46. **
  47. ** HAnimComboClass
  48. **
  49. **
  50. */
  51. NamedPivotMapClass::~NamedPivotMapClass(void)
  52. {
  53. }
  54. NamedPivotMapClass::WeightInfoStruct & NamedPivotMapClass::WeightInfoStruct::operator = (WeightInfoStruct const &that)
  55. {
  56. if(Name) delete [] Name;
  57. assert(that.Name != 0);
  58. Name = nstrdup(that.Name);
  59. Weight = that.Weight;
  60. return *this;
  61. }
  62. // add a name & weight to the arrays
  63. void NamedPivotMapClass::Add(const char *Name, float Weight)
  64. {
  65. WeightInfoStruct info;
  66. info.Name = (char *) Name;
  67. info.Weight = Weight;
  68. WeightInfo.Add(info);
  69. info.Name = 0;
  70. }
  71. // configure the base pivot map using the specified tree
  72. void NamedPivotMapClass::Update_Pivot_Map(const HTreeClass *Tree)
  73. {
  74. // first, resize the base pivot map to fit the tree
  75. int numPivots = Tree->Num_Pivots();
  76. Resize(numPivots);
  77. ActiveCount = numPivots;
  78. // first, reset all weights to 1 (the default)
  79. while(numPivots--) {
  80. (*this)[numPivots] = 1.0f;
  81. }
  82. // for each named pivot, find the correct index and set the weight if the indicated bone is present
  83. // note: there is no check for redundant names
  84. int count = WeightInfo.Count();
  85. while(count--) {
  86. int actualPivot = Tree->Get_Bone_Index(WeightInfo[count].Name);
  87. if(actualPivot != -1) {
  88. (*this)[actualPivot] = WeightInfo[count].Weight;
  89. }
  90. }
  91. }
  92. /*
  93. **
  94. */
  95. DEFINE_AUTO_POOL(HAnimComboDataClass,256);
  96. HAnimComboDataClass::HAnimComboDataClass(bool shared)
  97. : Shared(shared), HAnim(0), PivotMap(0), Frame(0), PrevFrame(0), Weight(1)
  98. {}
  99. HAnimComboDataClass::HAnimComboDataClass(const HAnimComboDataClass &src)
  100. : PivotMap(src.Get_Pivot_Map()),
  101. HAnim(src.Get_HAnim())
  102. {
  103. Shared = src.Is_Shared();
  104. Frame = src.Get_Frame();
  105. PrevFrame = src.Get_Prev_Frame();
  106. Weight = src.Get_Weight();
  107. }
  108. void HAnimComboDataClass::Copy(const HAnimComboDataClass *src)
  109. {
  110. if(src) {
  111. HAnim = src->Get_HAnim();
  112. PivotMap = src->Get_Pivot_Map();
  113. Frame = src->Get_Frame();
  114. PrevFrame = src->Get_Prev_Frame();
  115. Weight = src->Get_Weight();
  116. } else {
  117. HAnim = 0;
  118. PivotMap = 0;
  119. Frame = 0;
  120. PrevFrame = 0;
  121. Weight = 1;
  122. }
  123. }
  124. HAnimComboDataClass::~HAnimComboDataClass(void)
  125. {
  126. if(HAnim)
  127. HAnim->Release_Ref();
  128. if(PivotMap)
  129. PivotMap->Release_Ref();
  130. }
  131. void HAnimComboDataClass::Clear(void)
  132. {
  133. if ( HAnim != NULL ) {
  134. HAnim->Release_Ref();
  135. HAnim = NULL;
  136. }
  137. // not sure if the pivot map should be deleted or just have everything set to one.
  138. // removing it effectively sets it to one, so that's what I'm doing for now.
  139. if(PivotMap) {
  140. PivotMap->Release_Ref();
  141. PivotMap = NULL;
  142. }
  143. Frame = 0.0f;
  144. PrevFrame = 0.0f;
  145. Weight = 1.0;
  146. PivotMap = NULL;
  147. }
  148. void HAnimComboDataClass::Set_HAnim(HAnimClass *motion)
  149. {
  150. if ( motion != NULL ) {
  151. motion->Add_Ref();
  152. }
  153. if ( HAnim != NULL ) {
  154. HAnim->Release_Ref();
  155. }
  156. HAnim = motion;
  157. }
  158. void HAnimComboDataClass::Set_Pivot_Map(PivotMapClass *map)
  159. {
  160. if ( map != NULL ) {
  161. map->Add_Ref();
  162. }
  163. if ( PivotMap != NULL ) {
  164. PivotMap->Release_Ref();
  165. }
  166. PivotMap = map;
  167. }
  168. /*
  169. ** This function will replace the current pivot map (if any) with another pivot map that is
  170. ** set to 1 for only those pivot indices that actually have data.
  171. */
  172. void HAnimComboDataClass::Build_Active_Pivot_Map(void)
  173. {
  174. if ( PivotMap != NULL ) {
  175. PivotMap->Release_Ref();
  176. }
  177. if(HAnim == NULL) {
  178. PivotMap = 0;
  179. return;
  180. }
  181. int numpivots = HAnim->Get_Num_Pivots();
  182. PivotMap = NEW_REF( PivotMapClass, ());
  183. PivotMap->Resize(numpivots);
  184. int count = 0;
  185. while(count < numpivots) {
  186. if(HAnim->Is_Node_Motion_Present(count)) {
  187. PivotMap->Add(1);
  188. } else {
  189. PivotMap->Add(0);
  190. }
  191. count++;
  192. }
  193. }
  194. /*
  195. ** HAnimComboClass
  196. **
  197. **
  198. */
  199. HAnimComboClass::HAnimComboClass(void)
  200. {}
  201. HAnimComboClass::HAnimComboClass( int num_animations )
  202. {
  203. HAnimComboData.Resize(num_animations);
  204. while(num_animations--) {
  205. HAnimComboData.Add(new HAnimComboDataClass());
  206. }
  207. }
  208. HAnimComboClass::~HAnimComboClass(void)
  209. {
  210. Reset();
  211. }
  212. void HAnimComboClass::Clear( void )
  213. {
  214. int numAnimations = HAnimComboData.Count();
  215. while ( numAnimations-- ) {
  216. HAnimComboDataClass *data = HAnimComboData[numAnimations];
  217. if(data && (! data->Is_Shared()))
  218. data->Clear();
  219. }
  220. }
  221. void HAnimComboClass::Reset( void )
  222. {
  223. int numAnimations = HAnimComboData.Count();
  224. while ( numAnimations-- ) {
  225. HAnimComboDataClass *data = HAnimComboData[numAnimations];
  226. if(data && (! data->Is_Shared())) {
  227. delete data;
  228. }
  229. }
  230. HAnimComboData.Reset_Active();
  231. }
  232. bool HAnimComboClass::Normalize_Weights(void)
  233. {
  234. // NOTE: This can only work if either no anims have pivot weight maps (in which case we will
  235. // adjust the anim weights to ensure normalization), or else if all do (in which case we will
  236. // adjust the pivot maps). Otherwise we do nothing and return false.
  237. int anim_count = Get_Num_Anims();
  238. if (!anim_count) return true; // Trivially succeeded
  239. // Loop over all anims. Check if all or none have pivot maps, and also calculate the minimum
  240. // number of pivots.
  241. int anim_idx = 0;
  242. bool all_pivot_maps = true;
  243. bool none_pivot_maps = true;
  244. int num_anim_pivots = 100000;
  245. for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
  246. num_anim_pivots = MIN(num_anim_pivots, Peek_Motion(anim_idx)->Get_Num_Pivots());
  247. bool has_pivot_map = Peek_Pivot_Weight_Map(anim_idx) != NULL;
  248. all_pivot_maps &= has_pivot_map;
  249. none_pivot_maps &= !has_pivot_map;
  250. }
  251. if ( num_anim_pivots == 100000 ) {
  252. num_anim_pivots = 0;
  253. }
  254. if (none_pivot_maps) {
  255. // Calculate total weight of all active anims, ensure it is very close to 1.
  256. float weight_total = 0.0f;
  257. for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
  258. if (Peek_Motion(anim_idx) != NULL ) {
  259. float weight = Get_Weight(anim_idx);
  260. weight_total += weight;
  261. }
  262. }
  263. // weight_total should be very close to 1. If not, normalize this pivot's weights
  264. if (weight_total != 0.0 && WWMath::Fabs( weight_total - 1.0 ) > WWMATH_EPSILON) {
  265. float oo_total = 1.0f / weight_total;
  266. for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
  267. if (Peek_Motion(anim_idx) != NULL ) {
  268. Set_Weight(anim_idx, Get_Weight(anim_idx) * oo_total);
  269. }
  270. }
  271. }
  272. } else {
  273. if (all_pivot_maps) {
  274. // For each pivot, calculate total weight of all active anims, ensure close to 1.
  275. for (int piv_idx = 1; piv_idx < num_anim_pivots; piv_idx++) {
  276. float weight_total = 0.0f;
  277. for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
  278. if (Peek_Motion(anim_idx) != NULL ) {
  279. float weight = Get_Weight(anim_idx) * (*Peek_Pivot_Weight_Map(anim_idx))[piv_idx];
  280. weight_total += weight;
  281. }
  282. }
  283. // weight_total should be very close to 1. If not, normalize this pivot's weights
  284. if (weight_total != 0.0 && WWMath::Fabs( weight_total - 1.0 ) > WWMATH_EPSILON) {
  285. float oo_total = 1.0f / weight_total;
  286. for (anim_idx = 0; anim_idx < anim_count; anim_idx++ ) {
  287. if (Peek_Motion(anim_idx) != NULL ) {
  288. PivotMapClass *pivot_map = Get_Pivot_Weight_Map(anim_idx);
  289. float new_weight = (*pivot_map)[piv_idx] * oo_total;
  290. (*pivot_map)[piv_idx] = new_weight;
  291. pivot_map->Release_Ref();
  292. }
  293. }
  294. }
  295. }
  296. } else {
  297. return false;
  298. }
  299. }
  300. return true;
  301. }
  302. void HAnimComboClass::Set_Motion( int index, HAnimClass *motion )
  303. {
  304. HAnimComboDataClass *data = HAnimComboData[index];
  305. WWASSERT(data);
  306. data->Set_HAnim(motion);
  307. }
  308. HAnimClass *HAnimComboClass::Get_Motion( int index )
  309. {
  310. HAnimComboDataClass *data = HAnimComboData[index];
  311. WWASSERT(data);
  312. HAnimClass *anim = data->Peek_HAnim();
  313. if ( anim != NULL ) {
  314. anim->Add_Ref();
  315. }
  316. return anim;
  317. }
  318. HAnimClass *HAnimComboClass::Peek_Motion( int index )
  319. {
  320. HAnimComboDataClass *data = HAnimComboData[index];
  321. WWASSERT(data);
  322. HAnimClass *anim = data->Peek_HAnim();
  323. return anim;
  324. }
  325. void HAnimComboClass::Set_Frame( int index, float frame )
  326. {
  327. HAnimComboDataClass *data = HAnimComboData[index];
  328. WWASSERT(data);
  329. data->Set_Frame(frame);
  330. }
  331. float HAnimComboClass::Get_Frame( int index )
  332. {
  333. HAnimComboDataClass *data = HAnimComboData[index];
  334. WWASSERT(data);
  335. return data->Get_Frame();
  336. }
  337. void HAnimComboClass::Set_Prev_Frame( int index, float frame )
  338. {
  339. HAnimComboDataClass *data = HAnimComboData[index];
  340. WWASSERT(data);
  341. data->Set_Prev_Frame(frame);
  342. }
  343. float HAnimComboClass::Get_Prev_Frame( int index )
  344. {
  345. HAnimComboDataClass *data = HAnimComboData[index];
  346. WWASSERT(data);
  347. return data->Get_Prev_Frame();
  348. }
  349. void HAnimComboClass::Set_Weight( int index, float weight )
  350. {
  351. HAnimComboDataClass *data = HAnimComboData[index];
  352. WWASSERT(data);
  353. data->Set_Weight(weight);
  354. }
  355. float HAnimComboClass::Get_Weight( int index )
  356. {
  357. HAnimComboDataClass *data = HAnimComboData[index];
  358. WWASSERT(data);
  359. return data->Get_Weight();
  360. }
  361. void HAnimComboClass::Set_Pivot_Weight_Map( int index, PivotMapClass *map )
  362. {
  363. HAnimComboDataClass *data = HAnimComboData[index];
  364. WWASSERT(data);
  365. data->Set_Pivot_Map(map);
  366. }
  367. PivotMapClass *HAnimComboClass::Get_Pivot_Weight_Map( int index )
  368. {
  369. HAnimComboDataClass *data = HAnimComboData[index];
  370. WWASSERT(data);
  371. return data->Get_Pivot_Map();
  372. }
  373. PivotMapClass *HAnimComboClass::Peek_Pivot_Weight_Map( int index )
  374. {
  375. HAnimComboDataClass *data = HAnimComboData[index];
  376. WWASSERT(data);
  377. return data->Peek_Pivot_Map();
  378. }
  379. void HAnimComboClass::Append_Anim_Combo_Data(HAnimComboDataClass * Data)
  380. {
  381. HAnimComboData.Add(Data);
  382. }
  383. void HAnimComboClass::Remove_Anim_Combo_Data(HAnimComboDataClass * Data)
  384. {
  385. HAnimComboData.Delete(Data);
  386. }