SphereSizePropPage.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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. // SphereSizePropPage.cpp : implementation file
  19. //
  20. #include "stdafx.h"
  21. #include "w3dview.h"
  22. #include "spheresizeproppage.h"
  23. #include "colorutils.h"
  24. #include "utils.h"
  25. #include "scaledialog.h"
  26. #ifdef _DEBUG
  27. #define new DEBUG_NEW
  28. #undef THIS_FILE
  29. static char THIS_FILE[] = __FILE__;
  30. #endif
  31. IMPLEMENT_DYNCREATE(SphereSizePropPageClass, CPropertyPage)
  32. /////////////////////////////////////////////////////////////
  33. // Local prototypes
  34. /////////////////////////////////////////////////////////////
  35. static bool Is_LERP (float last_value, float last_time, float curr_value, float curr_time, float next_value, float next_time);
  36. /////////////////////////////////////////////////////////////
  37. //
  38. // SphereSizePropPageClass
  39. //
  40. /////////////////////////////////////////////////////////////
  41. SphereSizePropPageClass::SphereSizePropPageClass (SphereRenderObjClass *sphere)
  42. : m_RenderObj (sphere),
  43. m_bValid (true),
  44. m_ScaleXBar (NULL),
  45. m_ScaleYBar (NULL),
  46. m_ScaleZBar (NULL),
  47. m_Size (0.5F, 0.5F, 0.5F),
  48. CPropertyPage(SphereSizePropPageClass::IDD)
  49. {
  50. //{{AFX_DATA_INIT(SphereSizePropPageClass)
  51. // NOTE: the ClassWizard will add member initialization here
  52. //}}AFX_DATA_INIT
  53. Initialize ();
  54. return ;
  55. }
  56. /////////////////////////////////////////////////////////////
  57. //
  58. // ~SphereSizePropPageClass
  59. //
  60. /////////////////////////////////////////////////////////////
  61. SphereSizePropPageClass::~SphereSizePropPageClass (void)
  62. {
  63. return ;
  64. }
  65. /////////////////////////////////////////////////////////////
  66. //
  67. // DoDataExchange
  68. //
  69. /////////////////////////////////////////////////////////////
  70. void
  71. SphereSizePropPageClass::DoDataExchange (CDataExchange* pDX)
  72. {
  73. CPropertyPage::DoDataExchange(pDX);
  74. //{{AFX_DATA_MAP(SphereSizePropPageClass)
  75. DDX_Control(pDX, IDC_SIZE_Z_SPIN, m_SizeZSpin);
  76. DDX_Control(pDX, IDC_SIZE_Y_SPIN, m_SizeYSpin);
  77. DDX_Control(pDX, IDC_SIZE_X_SPIN, m_SizeXSpin);
  78. //}}AFX_DATA_MAP
  79. return ;
  80. }
  81. BEGIN_MESSAGE_MAP(SphereSizePropPageClass, CPropertyPage)
  82. //{{AFX_MSG_MAP(SphereSizePropPageClass)
  83. ON_WM_DESTROY()
  84. //}}AFX_MSG_MAP
  85. END_MESSAGE_MAP()
  86. /////////////////////////////////////////////////////////////
  87. //
  88. // Initialize
  89. //
  90. /////////////////////////////////////////////////////////////
  91. void
  92. SphereSizePropPageClass::Initialize (void)
  93. {
  94. m_ScaleChannel.Reset ();
  95. m_OrigScaleChannel.Reset ();
  96. if (m_RenderObj != NULL) {
  97. m_Size = m_RenderObj->Get_Box ().Extent;
  98. m_ScaleChannel = m_RenderObj->Get_Scale_Channel ();
  99. m_OrigScaleChannel = m_RenderObj->Get_Scale_Channel ();
  100. if (m_OrigScaleChannel.Get_Key_Count () == 0) {
  101. m_ScaleChannel.Add_Key (m_RenderObj->Get_Scale (), 0);
  102. m_OrigScaleChannel.Add_Key (m_RenderObj->Get_Scale (), 0);
  103. }
  104. }
  105. return ;
  106. }
  107. /////////////////////////////////////////////////////////////
  108. //
  109. // OnInitDialog
  110. //
  111. /////////////////////////////////////////////////////////////
  112. BOOL
  113. SphereSizePropPageClass::OnInitDialog (void)
  114. {
  115. // Allow the base class to process this message
  116. CPropertyPage::OnInitDialog ();
  117. m_ScaleXBar = ColorBarClass::Get_Color_Bar (::GetDlgItem (m_hWnd, IDC_SCALE_BAR_X));
  118. m_ScaleYBar = ColorBarClass::Get_Color_Bar (::GetDlgItem (m_hWnd, IDC_SCALE_BAR_Y));
  119. m_ScaleZBar = ColorBarClass::Get_Color_Bar (::GetDlgItem (m_hWnd, IDC_SCALE_BAR_Z));
  120. //
  121. // Setup the spinners
  122. //
  123. ::Initialize_Spinner (m_SizeXSpin, m_Size.X, 0, 10000);
  124. ::Initialize_Spinner (m_SizeYSpin, m_Size.Y, 0, 10000);
  125. ::Initialize_Spinner (m_SizeZSpin, m_Size.Z, 0, 10000);
  126. //
  127. // Setup the timelines
  128. //
  129. m_ScaleXBar->Set_Range (0, 1);
  130. m_ScaleYBar->Set_Range (0, 1);
  131. m_ScaleZBar->Set_Range (0, 1);
  132. //
  133. // Insert the starting points
  134. //
  135. m_ScaleXBar->Modify_Point (0, 0, 0, 0, 0);
  136. m_ScaleYBar->Modify_Point (0, 0, 0, 0, 0);
  137. m_ScaleZBar->Modify_Point (0, 0, 0, 0, 0);
  138. m_ScaleXBar->Set_Graph_Percent (0, m_OrigScaleChannel[0].Get_Value ().X);
  139. m_ScaleYBar->Set_Graph_Percent (0, m_OrigScaleChannel[0].Get_Value ().Y);
  140. m_ScaleZBar->Set_Graph_Percent (0, m_OrigScaleChannel[0].Get_Value ().Z);
  141. //
  142. // Set-up the timelines
  143. //
  144. int x_index = 1;
  145. int y_index = 1;
  146. int z_index = 1;
  147. for (int index = 1; index < m_OrigScaleChannel.Get_Key_Count (); index ++) {
  148. const LERPAnimationChannelClass<Vector3>::KeyClass &prev_value = m_OrigScaleChannel.Get_Key (index - 1);
  149. const LERPAnimationChannelClass<Vector3>::KeyClass &curr_value = m_OrigScaleChannel.Get_Key (index);
  150. //
  151. // Find out which channels are unique (we toss the others)
  152. //
  153. bool unique_x = false;
  154. bool unique_y = false;
  155. bool unique_z = false;
  156. if (index == (m_OrigScaleChannel.Get_Key_Count () - 1)) {
  157. unique_x = (curr_value.Get_Value ().X != prev_value.Get_Value ().X);
  158. unique_y = (curr_value.Get_Value ().Y != prev_value.Get_Value ().Y);
  159. unique_z = (curr_value.Get_Value ().Z != prev_value.Get_Value ().Z);
  160. } else {
  161. const LERPAnimationChannelClass<Vector3>::KeyClass &next_value = m_OrigScaleChannel[index + 1];
  162. //
  163. // Check to ensure the X-value isn't just a LERP of the 2 adjacent keys.
  164. //
  165. unique_x = (::Is_LERP ( prev_value.Get_Value ().X,
  166. prev_value.Get_Time (),
  167. curr_value.Get_Value ().X,
  168. curr_value.Get_Time (),
  169. next_value.Get_Value ().X,
  170. next_value.Get_Time ()) == false);
  171. //
  172. // Check to ensure the Y-value isn't just a LERP of the 2 adjacent keys.
  173. //
  174. unique_y = (::Is_LERP ( prev_value.Get_Value ().Y,
  175. prev_value.Get_Time (),
  176. curr_value.Get_Value ().Y,
  177. curr_value.Get_Time (),
  178. next_value.Get_Value ().Y,
  179. next_value.Get_Time ()) == false);
  180. //
  181. // Check to ensure the Z-value isn't just a LERP of the 2 adjacent keys.
  182. //
  183. unique_z = (::Is_LERP ( prev_value.Get_Value ().Z,
  184. prev_value.Get_Time (),
  185. curr_value.Get_Value ().Z,
  186. curr_value.Get_Time (),
  187. next_value.Get_Value ().Z,
  188. next_value.Get_Time ()) == false);
  189. }
  190. //
  191. // Insert a key for each unique axis
  192. //
  193. if (unique_x) {
  194. m_ScaleXBar->Modify_Point (x_index, curr_value.Get_Time (), 0, 0, 0);
  195. m_ScaleXBar->Set_Graph_Percent (x_index, curr_value.Get_Value ().X);
  196. x_index ++;
  197. }
  198. if (unique_y) {
  199. m_ScaleYBar->Modify_Point (y_index, curr_value.Get_Time (), 0, 0, 0);
  200. m_ScaleYBar->Set_Graph_Percent (y_index, curr_value.Get_Value ().Y);
  201. y_index ++;
  202. }
  203. if (unique_z) {
  204. m_ScaleZBar->Modify_Point (z_index, curr_value.Get_Time (), 0, 0, 0);
  205. m_ScaleZBar->Set_Graph_Percent (z_index, curr_value.Get_Value ().Z);
  206. z_index ++;
  207. }
  208. // One of the keys MUST be unique...
  209. ASSERT (unique_x || unique_y || unique_z);
  210. }
  211. //
  212. // Ensure our initial 'current' values are up-to-date
  213. //
  214. Update_Scale_Array ();
  215. return TRUE;
  216. }
  217. /////////////////////////////////////////////////////////////
  218. //
  219. // OnApply
  220. //
  221. /////////////////////////////////////////////////////////////
  222. BOOL
  223. SphereSizePropPageClass::OnApply (void)
  224. {
  225. // Allow the base class to process this message
  226. return CPropertyPage::OnApply ();
  227. }
  228. /////////////////////////////////////////////////////////////
  229. //
  230. // OnDestroy
  231. //
  232. /////////////////////////////////////////////////////////////
  233. void
  234. SphereSizePropPageClass::OnDestroy (void)
  235. {
  236. CPropertyPage::OnDestroy();
  237. return ;
  238. }
  239. /////////////////////////////////////////////////////////////
  240. //
  241. // OnNotify
  242. //
  243. /////////////////////////////////////////////////////////////
  244. BOOL
  245. SphereSizePropPageClass::OnNotify
  246. (
  247. WPARAM wParam,
  248. LPARAM lParam,
  249. LRESULT *pResult
  250. )
  251. {
  252. CBR_NMHDR *color_bar_hdr = (CBR_NMHDR *)lParam;
  253. //
  254. // Which control sent the notification?
  255. //
  256. switch (color_bar_hdr->hdr.idFrom)
  257. {
  258. case IDC_SCALE_BAR_X:
  259. case IDC_SCALE_BAR_Y:
  260. case IDC_SCALE_BAR_Z:
  261. {
  262. //
  263. // Determine the timeline bar which sent the notification
  264. //
  265. ColorBarClass *timeline = NULL;
  266. if (color_bar_hdr->hdr.idFrom == IDC_SCALE_BAR_X) {
  267. timeline = m_ScaleXBar;
  268. } else if (color_bar_hdr->hdr.idFrom == IDC_SCALE_BAR_Y) {
  269. timeline = m_ScaleYBar;
  270. } else {
  271. timeline = m_ScaleZBar;
  272. }
  273. bool update = (color_bar_hdr->hdr.code == CBRN_MOVING_POINT) ||
  274. (color_bar_hdr->hdr.code == CBRN_DELETED_POINT);
  275. if (color_bar_hdr->hdr.code == CBRN_DBLCLK_POINT) {
  276. //
  277. // Allow the user to edit the keyframe
  278. //
  279. float scale = timeline->Get_Graph_Percent (color_bar_hdr->key_index);
  280. ScaleDialogClass dialog (scale, this);
  281. if (dialog.DoModal () == IDOK) {
  282. //
  283. // Update the timeline
  284. //
  285. timeline->Set_Graph_Percent (color_bar_hdr->key_index, dialog.Get_Scale ());
  286. update = true;
  287. }
  288. }
  289. //
  290. // Update the object
  291. //
  292. if (update) {
  293. Update_Scale_Array ();
  294. SetModified ();
  295. }
  296. }
  297. break;
  298. case IDC_SIZE_X_SPIN:
  299. case IDC_SIZE_Y_SPIN:
  300. case IDC_SIZE_Z_SPIN:
  301. {
  302. // Update the object
  303. m_Size.X = ::GetDlgItemFloat (m_hWnd, IDC_SIZE_X_EDIT);
  304. m_Size.Y = ::GetDlgItemFloat (m_hWnd, IDC_SIZE_Y_EDIT);
  305. m_Size.Z = ::GetDlgItemFloat (m_hWnd, IDC_SIZE_Z_EDIT);
  306. m_RenderObj->Set_Extent (m_Size);
  307. SetModified ();
  308. }
  309. break;
  310. }
  311. return CPropertyPage::OnNotify (wParam, lParam, pResult);
  312. }
  313. /////////////////////////////////////////////////////////////
  314. //
  315. // OnCommand
  316. //
  317. /////////////////////////////////////////////////////////////
  318. BOOL
  319. SphereSizePropPageClass::OnCommand
  320. (
  321. WPARAM wParam,
  322. LPARAM lParam
  323. )
  324. {
  325. switch (LOWORD (wParam))
  326. {
  327. case IDC_SIZE_X_EDIT:
  328. case IDC_SIZE_Y_EDIT:
  329. case IDC_SIZE_Z_EDIT:
  330. {
  331. // Update the object
  332. if ((HIWORD (wParam) == EN_KILLFOCUS) &&
  333. SendDlgItemMessage (LOWORD (wParam), EM_GETMODIFY))
  334. {
  335. SendDlgItemMessage (LOWORD (wParam), EM_SETMODIFY, (WPARAM)0);
  336. // Update the object
  337. m_Size.X = ::GetDlgItemFloat (m_hWnd, IDC_SIZE_X_EDIT);
  338. m_Size.Y = ::GetDlgItemFloat (m_hWnd, IDC_SIZE_Y_EDIT);
  339. m_Size.Z = ::GetDlgItemFloat (m_hWnd, IDC_SIZE_Z_EDIT);
  340. m_RenderObj->Set_Extent (m_Size);
  341. SetModified ();
  342. } else if (HIWORD (wParam) == EN_CHANGE) {
  343. SetModified ();
  344. }
  345. }
  346. break;
  347. }
  348. return CPropertyPage::OnCommand (wParam, lParam);
  349. }
  350. /////////////////////////////////////////////////////////////
  351. //
  352. // OnCancel
  353. //
  354. /////////////////////////////////////////////////////////////
  355. void
  356. SphereSizePropPageClass::OnCancel (void)
  357. {
  358. //
  359. // Reset the object to its original state
  360. //
  361. m_RenderObj->Set_Scale_Channel (m_ScaleChannel);
  362. CPropertyPage::OnCancel ();
  363. return ;
  364. }
  365. /////////////////////////////////////////////////////////////
  366. //
  367. // Update_Scale_Array
  368. //
  369. /////////////////////////////////////////////////////////////
  370. void
  371. SphereSizePropPageClass::Update_Scale_Array (void)
  372. {
  373. m_ScaleChannel.Reset ();
  374. float position = 0;
  375. float red = 0;
  376. float green = 0;
  377. float blue = 0;
  378. //
  379. // Allocate arrays we can store the 3 separate timelines in
  380. //
  381. int max_x = m_ScaleXBar->Get_Point_Count ();
  382. int max_y = m_ScaleYBar->Get_Point_Count ();
  383. int max_z = m_ScaleZBar->Get_Point_Count ();
  384. LERPAnimationChannelClass<float> x_values;
  385. LERPAnimationChannelClass<float> y_values;
  386. LERPAnimationChannelClass<float> z_values;
  387. //
  388. // Build the X-axis timline
  389. //
  390. for (int index = 0; index < max_x; index++) {
  391. m_ScaleXBar->Get_Point (index, &position, &red, &green, &blue);
  392. x_values.Add_Key (m_ScaleXBar->Get_Graph_Percent (index), position);
  393. }
  394. //
  395. // Build the Y-axis timline
  396. //
  397. for (index = 0; index < max_y; index++) {
  398. m_ScaleYBar->Get_Point (index, &position, &red, &green, &blue);
  399. y_values.Add_Key (m_ScaleYBar->Get_Graph_Percent (index), position);
  400. }
  401. //
  402. // Build the Z-axis timline
  403. //
  404. for (index = 0; index < max_z; index++) {
  405. m_ScaleZBar->Get_Point (index, &position, &red, &green, &blue);
  406. z_values.Add_Key (m_ScaleZBar->Get_Graph_Percent (index), position);
  407. }
  408. //
  409. // Combine the 3 separate time lines into one time line
  410. //
  411. int x_index = 0;
  412. int y_index = 0;
  413. int z_index = 0;
  414. int list_index = 0;
  415. float x_val = x_values[0].Get_Value ();
  416. float y_val = y_values[0].Get_Value ();
  417. float z_val = z_values[0].Get_Value ();
  418. while ( x_index < max_x ||
  419. y_index < max_y ||
  420. z_index < max_z)
  421. {
  422. //
  423. // Find the smallest time
  424. //
  425. float time = 2.0F;
  426. float x_time = time;
  427. float y_time = time;
  428. float z_time = time;
  429. if (x_index < max_x) {
  430. x_time = x_values[x_index].Get_Time ();
  431. }
  432. if (y_index < max_y) {
  433. y_time = y_values[y_index].Get_Time ();
  434. }
  435. if (z_index < max_z) {
  436. z_time = z_values[z_index].Get_Time ();
  437. }
  438. time = min (x_time, time);
  439. time = min (y_time, time);
  440. time = min (z_time, time);
  441. if (x_time == time) {
  442. x_index ++;
  443. }
  444. if (y_time == time) {
  445. y_index ++;
  446. }
  447. if (z_time == time) {
  448. z_index ++;
  449. }
  450. //
  451. // Evaluate X
  452. //
  453. x_val = x_values.Evaluate (time);
  454. y_val = y_values.Evaluate (time);
  455. z_val = z_values.Evaluate (time);
  456. //
  457. // Add this scalar to the list
  458. //
  459. m_ScaleChannel.Add_Key (Vector3 (x_val, y_val, z_val), time);
  460. }
  461. //
  462. // Update the object
  463. //
  464. m_RenderObj->Set_Scale_Channel (m_ScaleChannel);
  465. m_RenderObj->Restart_Animation ();
  466. return ;
  467. }
  468. /////////////////////////////////////////////////////////////
  469. //
  470. // Is_LERP
  471. //
  472. /////////////////////////////////////////////////////////////
  473. bool
  474. Is_LERP
  475. (
  476. float last_value,
  477. float last_time,
  478. float curr_value,
  479. float curr_time,
  480. float next_value,
  481. float next_time
  482. )
  483. {
  484. float percent = (curr_time - last_time) / (next_time - last_time);
  485. float interpolated_value = last_value + ((next_value-last_value) * percent);
  486. return bool(WWMath::Fabs (interpolated_value - curr_value) < WWMATH_EPSILON);
  487. }