dynamicspeechanim.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/dynamicspeechanim.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/16/02 7:15p $*
  29. * *
  30. * $Revision:: 7 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "dynamicspeechanim.h"
  36. #include "assetmgr.h"
  37. #include "crandom.h"
  38. ////////////////////////////////////////////////////////////////
  39. // Static member initialization
  40. ////////////////////////////////////////////////////////////////
  41. VisemeManager DynamicSpeechAnimClass::VisemeLookupMgr;
  42. ////////////////////////////////////////////////////////////////
  43. // Constants
  44. ////////////////////////////////////////////////////////////////
  45. enum
  46. {
  47. CHANNEL0 = 0,
  48. CHANNEL1,
  49. CHANNEL_MAX
  50. };
  51. static const int EYEBROW_POSES[] =
  52. {
  53. 1, 3, 4, 5, 6, 7
  54. };
  55. static const int EYEBROW_POSE_COUNT = sizeof (EYEBROW_POSES) / sizeof (int);
  56. typedef enum
  57. {
  58. NORMAL = 0,
  59. NORMAL_EYELIDS_HALF,
  60. NORMAL_EYELIDS_DOWN,
  61. EYEBROWS_DOWN,
  62. SPOCK_EYEBROW_L,
  63. SPOCK_EYEBROW_R,
  64. EYEBROWS_UP,
  65. EYEBROWS_DOWN_EYELIDS_HALF,
  66. EYEBROWS_DOWN_EYELIDS_DOWN
  67. } EYEBROW_POSE_INDICES;
  68. ////////////////////////////////////////////////////////////////
  69. //
  70. // DynamicSpeechAnimClass
  71. //
  72. ////////////////////////////////////////////////////////////////
  73. DynamicSpeechAnimClass::DynamicSpeechAnimClass (const char *skeleton_name)
  74. {
  75. StringClass anim0_name;
  76. StringClass anim1_name;
  77. //
  78. // Determine which animations to load
  79. //
  80. StringClass upper_skel_name = skeleton_name;
  81. ::strupr (upper_skel_name.Peek_Buffer ());
  82. if (::strstr (upper_skel_name, "S_A_") != NULL) {
  83. anim0_name.Format ("%s.S_A_MOUTH", skeleton_name);
  84. anim1_name.Format ("%s.S_A_EXPRESSION", skeleton_name);
  85. } else {
  86. anim0_name.Format ("%s.S_B_MOUTH", skeleton_name);
  87. anim1_name.Format ("%s.S_B_EXPRESSION", skeleton_name);
  88. }
  89. //
  90. // Load the animations for each channel
  91. //
  92. HAnimClass *animations[CHANNEL_MAX] = { 0 };
  93. animations[CHANNEL0] = WW3DAssetManager::Get_Instance ()->Get_HAnim (anim0_name);
  94. animations[CHANNEL1] = WW3DAssetManager::Get_Instance ()->Get_HAnim (anim1_name);
  95. //
  96. // Did we successfully load all the animations?
  97. //
  98. bool is_valid = true;
  99. for (int index = 0; index < CHANNEL_MAX; index ++) {
  100. if (animations[index] == NULL) {
  101. is_valid = false;
  102. break;
  103. }
  104. }
  105. //
  106. // If valid, create the morph animation from the channel information
  107. //
  108. if (is_valid) {
  109. Create_New_Morph (CHANNEL_MAX, animations);
  110. } else {
  111. //
  112. // Release our hold on the animations
  113. //
  114. for (int index = 0; index < CHANNEL_MAX; index ++) {
  115. REF_PTR_RELEASE (animations[index]);
  116. }
  117. }
  118. return ;
  119. }
  120. ////////////////////////////////////////////////////////////////
  121. //
  122. // ~DynamicSpeechAnimClass
  123. //
  124. ////////////////////////////////////////////////////////////////
  125. DynamicSpeechAnimClass::~DynamicSpeechAnimClass (void)
  126. {
  127. Free_Morph ();
  128. return ;
  129. }
  130. ////////////////////////////////////////////////////////////////
  131. //
  132. // Generate_Animation
  133. //
  134. ////////////////////////////////////////////////////////////////
  135. bool
  136. DynamicSpeechAnimClass::Generate_Animation (const char *text, float duration)
  137. {
  138. if (ChannelCount <= 0) {
  139. return false;
  140. }
  141. //
  142. // Start fresh
  143. //
  144. Release_Keys ();
  145. //
  146. // Get the list of mouth poses based on the phrase through the viseme manager
  147. //
  148. bool retval = false;
  149. int pose_table[256] = { 0 };
  150. int pose_count = VisemeLookupMgr.Get_Visemes (text, pose_table);
  151. if (pose_count > 0) {
  152. retval = true;
  153. //
  154. // Determine how much time each pose should take
  155. //
  156. float time_per_pose = duration / float(pose_count + 1);
  157. float frame_inc = time_per_pose * Get_Frame_Rate ();
  158. float curr_frame = 0;
  159. //
  160. // Always start the animation with the closed mouth
  161. //
  162. Insert_Morph_Key (CHANNEL0, 0, 0);
  163. curr_frame += frame_inc;
  164. //
  165. // Now add all the poses to the animation
  166. //
  167. for (int index = 0; index < pose_count; index ++) {
  168. Insert_Morph_Key (CHANNEL0, uint32(curr_frame + 0.5F), pose_table[index] + 1);
  169. curr_frame += frame_inc;
  170. }
  171. //
  172. // Always end the mouth sentence with a closed mouth
  173. //
  174. Insert_Morph_Key (CHANNEL0, uint32(curr_frame + (frame_inc * 2) + 0.5F), 0);
  175. //
  176. // Add in some eyebrow animation
  177. //
  178. Generate_Eyebrows (duration);
  179. }
  180. return retval;
  181. }
  182. ////////////////////////////////////////////////////////////////
  183. //
  184. // Generate_Eyebrows
  185. //
  186. ////////////////////////////////////////////////////////////////
  187. void
  188. DynamicSpeechAnimClass::Generate_Eyebrows (float duration, float frequency)
  189. {
  190. if (ChannelCount <= 0) {
  191. return ;
  192. }
  193. //
  194. // Start with the eyebrows in the "normal" position
  195. //
  196. Insert_Morph_Key (CHANNEL1, 0, 0);
  197. //
  198. // Calculate how many frames are in the animation
  199. //
  200. int max_frames = int(duration * Get_Frame_Rate ());
  201. int min_blink_delay = int(0.5F * Get_Frame_Rate ());
  202. int max_blink_delay = int(3.0F * Get_Frame_Rate ());
  203. int current_frame = 0;
  204. int current_pose = NORMAL;
  205. while (current_frame < max_frames) {
  206. //
  207. // Wait a random amount of time before we blink
  208. //
  209. current_frame += FreeRandom.Get_Int (min_blink_delay, max_blink_delay);
  210. //
  211. // Blink
  212. //
  213. if (current_frame < max_frames) {
  214. current_frame = Insert_Blink (current_frame, EYEBROW_POSES[current_pose]);
  215. }
  216. //
  217. // Randomly change eyebrow position
  218. //
  219. if (current_frame < max_frames && FreeRandom.Get_Int (6) == 2) {
  220. //
  221. // Lock the last pose at this frame
  222. //
  223. Insert_Morph_Key (CHANNEL1, current_frame, EYEBROW_POSES[current_pose]);
  224. //
  225. // Wait a small random amount of time
  226. //
  227. float delay = WWMath::Random_Float (0.2F, 0.4F);
  228. current_frame += int(delay * Get_Frame_Rate ());
  229. //
  230. // Choose a random eyebrow pose
  231. //
  232. current_pose = FreeRandom.Get_Int (EYEBROW_POSE_COUNT);
  233. Insert_Morph_Key (CHANNEL1, current_frame, EYEBROW_POSES[current_pose]);
  234. }
  235. }
  236. //
  237. // End with the eyebrows in the "normal" position
  238. //
  239. Insert_Morph_Key (CHANNEL1, max_frames, 0);
  240. return ;
  241. }
  242. ////////////////////////////////////////////////////////////////
  243. //
  244. // Generate_Idle_Animation
  245. //
  246. ////////////////////////////////////////////////////////////////
  247. void
  248. DynamicSpeechAnimClass::Generate_Idle_Animation (float duration, float frequency)
  249. {
  250. if (ChannelCount <= 0) {
  251. return ;
  252. }
  253. //
  254. // Start fresh
  255. //
  256. Release_Keys ();
  257. //
  258. // Make the mouth closed
  259. //
  260. Insert_Morph_Key (CHANNEL0, 0, 0);
  261. //
  262. // Generate 20 seconds worth of animation data for the eyebrows
  263. //
  264. Generate_Eyebrows (duration, frequency);
  265. return ;
  266. }
  267. ////////////////////////////////////////////////////////////////
  268. //
  269. // Insert_Blink
  270. //
  271. ////////////////////////////////////////////////////////////////
  272. int
  273. DynamicSpeechAnimClass::Insert_Blink (int current_frame, int current_pose)
  274. {
  275. //
  276. // Insert a placeholder key so we don't "slow-mo" blink
  277. //
  278. Insert_Morph_Key (CHANNEL1, current_frame, current_pose);
  279. //
  280. // Wait a small amount of time
  281. //
  282. current_frame += int(0.05F * Get_Frame_Rate ());
  283. //
  284. // Determine which pose to use for "eyes-closed"
  285. //
  286. int closed_pose = EYEBROWS_DOWN_EYELIDS_DOWN;
  287. if ( current_pose == NORMAL ||
  288. current_pose == NORMAL_EYELIDS_HALF ||
  289. current_pose == EYEBROWS_UP)
  290. {
  291. closed_pose = NORMAL_EYELIDS_DOWN;
  292. }
  293. //
  294. // Close the eyes
  295. //
  296. Insert_Morph_Key (CHANNEL1, current_frame, closed_pose);
  297. //
  298. // Wait a small random amount of time
  299. //
  300. float blink_delay = WWMath::Random_Float (0.1F, 0.3F);
  301. current_frame += int(blink_delay * Get_Frame_Rate ());
  302. //
  303. // Restore the last pose (open-eyed)
  304. //
  305. Insert_Morph_Key (CHANNEL1, current_frame, current_pose);
  306. return current_frame;
  307. }