heightfieldeditor.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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 : leveledit *
  23. * *
  24. * $Archive:: /Commando/Code/Tools/LevelEdit/heightfieldeditor.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 3/05/02 3:21p $*
  29. * *
  30. * $Revision:: 3 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "stdafx.h"
  36. #include "heightfieldeditor.h"
  37. #include "sceneeditor.h"
  38. #include "mover.h"
  39. #include "rendobj.h"
  40. #include "phys.h"
  41. #include "editableheightfield.h"
  42. #include "mousemgr.h"
  43. #include "texture.h"
  44. #include "surface.h"
  45. #include "terrainmaterial.h"
  46. #include "heightfieldpage.h"
  47. #include "textureloader.h"
  48. #include "d3d8types.h"
  49. #include "dx8wrapper.h"
  50. #include "bitmaphandler.h"
  51. //////////////////////////////////////////////////////////////////////
  52. // Forward declarations
  53. //////////////////////////////////////////////////////////////////////
  54. float HeightfieldEditorClass::BrushInnerRadius = 10.0F;
  55. float HeightfieldEditorClass::BrushOutterRadius = 20.0F;
  56. float HeightfieldEditorClass::BrushAmount = 1.0F;
  57. int HeightfieldEditorClass::CurrentTextureIndex = 0;
  58. EditableHeightfieldClass * HeightfieldEditorClass::CurrentHeightfield = NULL;
  59. HeightfieldEditorClass::EDITING_MODE HeightfieldEditorClass::Mode = HeightfieldEditorClass::MODE_DEFORM;
  60. DynamicVectorClass<TerrainMaterialClass *> HeightfieldEditorClass::MaterialList;
  61. //////////////////////////////////////////////////////////////////////
  62. // Local constants
  63. //////////////////////////////////////////////////////////////////////
  64. static const int MAX_HEIGHTFIELD_MATERIALS = 10;
  65. //////////////////////////////////////////////////////////////////////
  66. //
  67. // Initialize
  68. //
  69. //////////////////////////////////////////////////////////////////////
  70. void
  71. HeightfieldEditorClass::Initialize (void)
  72. {
  73. return ;
  74. }
  75. //////////////////////////////////////////////////////////////////////
  76. //
  77. // Shutdown
  78. //
  79. //////////////////////////////////////////////////////////////////////
  80. void
  81. HeightfieldEditorClass::Shutdown (void)
  82. {
  83. //
  84. // Free the list of materials
  85. //
  86. for (int index = 0; index < MaterialList.Count (); index ++) {
  87. REF_PTR_RELEASE (MaterialList[index]);
  88. }
  89. MaterialList.Delete_All ();
  90. return ;
  91. }
  92. //////////////////////////////////////////////////////////////////////
  93. //
  94. // Set_Brush_Radii
  95. //
  96. //////////////////////////////////////////////////////////////////////
  97. void
  98. HeightfieldEditorClass::Set_Brush_Radii (float inner_radius, float outter_radius)
  99. {
  100. BrushInnerRadius = inner_radius;
  101. BrushOutterRadius = outter_radius;
  102. return ;
  103. }
  104. //////////////////////////////////////////////////////////////////////
  105. //
  106. // Get_Brush_Radii
  107. //
  108. //////////////////////////////////////////////////////////////////////
  109. void
  110. HeightfieldEditorClass::Get_Brush_Radii (float &inner_radius, float &outter_radius)
  111. {
  112. inner_radius = BrushInnerRadius;
  113. outter_radius = BrushOutterRadius;
  114. return ;
  115. }
  116. //////////////////////////////////////////////////////////////////////
  117. //
  118. // Set_Mode
  119. //
  120. //////////////////////////////////////////////////////////////////////
  121. void
  122. HeightfieldEditorClass::Set_Mode (EDITING_MODE mode)
  123. {
  124. Mode = mode;
  125. return ;
  126. }
  127. //////////////////////////////////////////////////////////////////////
  128. //
  129. // On_Frame_Update
  130. //
  131. //////////////////////////////////////////////////////////////////////
  132. void
  133. HeightfieldEditorClass::On_Frame_Update (void)
  134. {
  135. //
  136. // Don't do any processing if we're not in heightfield editing mode
  137. //
  138. if (::Get_Mouse_Mgr ()->Get_Mouse_Mode () != MouseMgrClass::MODE_HEIGHTFIELD_EDIT) {
  139. return ;
  140. }
  141. //
  142. // Get a ray from the current view through the mouse cursor position
  143. //
  144. Vector3 v_start;
  145. Vector3 v_end;
  146. MoverClass::Get_Mouse_Ray (1000.0F, v_start, v_end);
  147. //
  148. // Cast a ray into the world and see what we've hit
  149. //
  150. CastResultStruct res;
  151. res.ComputeContactPoint = true;
  152. PhysClass *phys_obj = MoverClass::Cast_Ray (res, v_start, v_end);
  153. if (phys_obj != NULL) {
  154. //
  155. // For right now, add a debug box to show where the operation will occur
  156. //
  157. PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox (AABoxClass (res.ContactPoint + Vector3 (0, 0, 2.0F), Vector3 (1.0F, 1.0F, 1.0F)), Vector3 (1, 0, 0));
  158. //
  159. // Check to see if this is a heightfield
  160. //
  161. RenderObjClass *model = phys_obj->Peek_Model ();
  162. if (model != NULL && model->Class_ID () == RenderObjClass::CLASSID_RENEGADE_TERRAIN && CurrentHeightfield != NULL) {
  163. EditableHeightfieldClass *heightfield = CurrentHeightfield;
  164. //
  165. // Is the left button or right button down?
  166. //
  167. if (::Get_Mouse_Mgr ()->Is_LButton_Down ()) {
  168. //
  169. // Determine what operation to perform
  170. //
  171. if (Mode == MODE_DEFORM) {
  172. heightfield->Deform_Heightfield (res.ContactPoint, BrushAmount, BrushInnerRadius, BrushOutterRadius);
  173. } else if (Mode == MODE_DEFORM_SMOOTH) {
  174. heightfield->Smooth_Heightfield (res.ContactPoint, BrushAmount, BrushInnerRadius, BrushOutterRadius);
  175. } else if (Mode == MODE_DEFORM_SMOOTH_FOUNDATION) {
  176. heightfield->Smooth_Foundation_Heightfield (res.ContactPoint, BrushAmount, BrushInnerRadius, BrushOutterRadius);
  177. } else if (Mode == MODE_QUAD_CUTOUT) {
  178. heightfield->Cutout_Heightfield (res.ContactPoint, BrushOutterRadius, true);
  179. } else if (Mode == MODE_TEXTURING) {
  180. heightfield->Paint_Heightfield (res.ContactPoint, CurrentTextureIndex, BrushAmount, BrushInnerRadius, BrushOutterRadius);
  181. }
  182. } else if (::Get_Mouse_Mgr ()->Is_RButton_Down ()) {
  183. //
  184. // Determine what operation to perform
  185. //
  186. if (Mode == MODE_DEFORM) {
  187. heightfield->Deform_Heightfield (res.ContactPoint, -BrushAmount, BrushInnerRadius, BrushOutterRadius);
  188. } else if (Mode == MODE_QUAD_CUTOUT) {
  189. heightfield->Cutout_Heightfield (res.ContactPoint, BrushOutterRadius, false);
  190. }
  191. }
  192. }
  193. }
  194. return ;
  195. }
  196. //////////////////////////////////////////////////////////////////////
  197. //
  198. // Render
  199. //
  200. //////////////////////////////////////////////////////////////////////
  201. void
  202. HeightfieldEditorClass::Render (void)
  203. {
  204. return ;
  205. }
  206. //////////////////////////////////////////////////////////////////////
  207. //
  208. // Create_Texture_Thumbnail
  209. //
  210. //////////////////////////////////////////////////////////////////////
  211. HBITMAP
  212. HeightfieldEditorClass::Create_Texture_Thumbnail (TextureClass *texture, int width, int height)
  213. {
  214. if (texture == NULL) {
  215. return NULL;
  216. }
  217. //
  218. // Create the thumbnail
  219. //
  220. StringClass filename = texture->Get_Full_Path ();
  221. HBITMAP thumbnail = Create_Texture_Thumbnail (filename, width, height);
  222. //
  223. // Release our hold on the texture
  224. //
  225. REF_PTR_RELEASE (texture);
  226. return thumbnail;
  227. }
  228. //////////////////////////////////////////////////////////////////////
  229. //
  230. // Create_Texture_Thumbnail
  231. //
  232. //////////////////////////////////////////////////////////////////////
  233. HBITMAP
  234. HeightfieldEditorClass::Create_Texture_Thumbnail (const char *filename, int width, int height)
  235. {
  236. if (filename == NULL) {
  237. return NULL;
  238. }
  239. //
  240. // Load the d3d surface for this texture
  241. //
  242. IDirect3DSurface8 *d3d_surface = TextureLoader::Load_Surface_Immediate (filename, WW3D_FORMAT_R8G8B8, false);
  243. //
  244. // Get information about the texture
  245. //
  246. D3DSURFACE_DESC sd;
  247. ::ZeroMemory (&sd, sizeof(sd));
  248. DX8_ErrorCode (d3d_surface->GetDesc (&sd));
  249. //
  250. // Get the texture's bits so we can copy tthem
  251. //
  252. D3DLOCKED_RECT lock_rect;
  253. ::ZeroMemory (&lock_rect, sizeof (D3DLOCKED_RECT));
  254. DX8_ErrorCode (d3d_surface->LockRect (&lock_rect, 0, 0));
  255. int src_pitch = lock_rect.Pitch;
  256. uint8 *src_bits = (unsigned char *)lock_rect.pBits;
  257. //
  258. // Set-up the fields of the BITMAPINFOHEADER
  259. // Note: Top-down DIBs use negative height in Win32.
  260. //
  261. BITMAPINFOHEADER bitmap_info = { 0 };
  262. bitmap_info.biSize = sizeof (BITMAPINFOHEADER);
  263. bitmap_info.biWidth = width;
  264. bitmap_info.biHeight = -height;
  265. bitmap_info.biPlanes = 1;
  266. bitmap_info.biBitCount = 24;
  267. bitmap_info.biCompression = BI_RGB;
  268. bitmap_info.biSizeImage = ((width * height) * 3);
  269. bitmap_info.biXPelsPerMeter = 0;
  270. bitmap_info.biYPelsPerMeter = 0;
  271. bitmap_info.biClrUsed = 0;
  272. bitmap_info.biClrImportant = 0;
  273. //
  274. // Create a bitmap that we can access the bits directly of
  275. //
  276. uint8 *dest_bits = NULL;
  277. HDC screen_dc = ::GetDC (NULL);
  278. HBITMAP bitmap = ::CreateDIBSection (screen_dc, (const BITMAPINFO *)&bitmap_info,
  279. DIB_RGB_COLORS, (void **)&dest_bits, NULL, 0L);
  280. int dest_pitch = (((width * 3) + 3) & ~3);
  281. //
  282. // Scale the texture to the size requested
  283. //
  284. BitmapHandlerClass::Copy_Image (dest_bits, width, height, dest_pitch, WW3D_FORMAT_R8G8B8,
  285. src_bits, sd.Width, sd.Height, src_pitch, WW3D_FORMAT_R8G8B8,
  286. NULL, 0, false);
  287. //
  288. // Free the surface
  289. //
  290. DX8_ErrorCode (d3d_surface->UnlockRect ());
  291. d3d_surface->Release ();
  292. d3d_surface = NULL;
  293. return bitmap;
  294. }
  295. //////////////////////////////////////////////////////////////////////
  296. //
  297. // Set_Material
  298. //
  299. //////////////////////////////////////////////////////////////////////
  300. void
  301. HeightfieldEditorClass::Set_Material (int index, TerrainMaterialClass *material)
  302. {
  303. //
  304. // Add enough entries to the array until we've got a slot for "index"
  305. //
  306. while (index >= MaterialList.Count ()) {
  307. MaterialList.Add (NULL);
  308. }
  309. //
  310. // Add this material to its slot
  311. //
  312. REF_PTR_SET (MaterialList[index], material);
  313. return ;
  314. }
  315. //////////////////////////////////////////////////////////////////////
  316. //
  317. // Peek_Material
  318. //
  319. //////////////////////////////////////////////////////////////////////
  320. TerrainMaterialClass *
  321. HeightfieldEditorClass::Peek_Material (int index)
  322. {
  323. TerrainMaterialClass *retval = NULL;
  324. //
  325. // Simply return the material in this slot (if it exists)
  326. //
  327. if (index >= 0 && index < MaterialList.Count ()) {
  328. retval = MaterialList[index];
  329. }
  330. return retval;
  331. }
  332. //////////////////////////////////////////////////////////////////////
  333. //
  334. // Get_Material
  335. //
  336. //////////////////////////////////////////////////////////////////////
  337. TerrainMaterialClass *
  338. HeightfieldEditorClass::Get_Material (int index)
  339. {
  340. //
  341. // Simply add a reference to the material and return its pointer
  342. //
  343. TerrainMaterialClass *retval = Peek_Material (index);
  344. if (retval != NULL) {
  345. retval->Add_Ref ();
  346. }
  347. return retval;
  348. }
  349. //////////////////////////////////////////////////////////////////////
  350. //
  351. // Load_Materials
  352. //
  353. //////////////////////////////////////////////////////////////////////
  354. void
  355. HeightfieldEditorClass::Load_Materials (EditableHeightfieldClass *heightfield)
  356. {
  357. if (heightfield == NULL) {
  358. return ;
  359. }
  360. //
  361. // Simply loop over and store all the materials
  362. //
  363. for (int index = 0; index < MAX_HEIGHTFIELD_MATERIALS; index ++) {
  364. TerrainMaterialClass *material = heightfield->Peek_Material (index);
  365. Set_Material (index, material);
  366. //
  367. // Update the UI
  368. //
  369. HeightfieldPageClass::Get_Instance ()->Update_Material_Button (index);
  370. }
  371. CurrentHeightfield = heightfield;
  372. return ;
  373. }
  374. //////////////////////////////////////////////////////////////////////
  375. //
  376. // Set_Current_Heightfield
  377. //
  378. //////////////////////////////////////////////////////////////////////
  379. void
  380. HeightfieldEditorClass::Set_Current_Heightfield (EditableHeightfieldClass *heightfield)
  381. {
  382. CurrentHeightfield = heightfield;
  383. Load_Materials (CurrentHeightfield);
  384. return ;
  385. }
  386. //////////////////////////////////////////////////////////////////////
  387. //
  388. // On_Material_Changed
  389. //
  390. //////////////////////////////////////////////////////////////////////
  391. void
  392. HeightfieldEditorClass::On_Material_Changed (int material_index)
  393. {
  394. //
  395. // Update the material panel UI
  396. //
  397. HeightfieldPageClass::Get_Instance ()->Update_Material_Button (material_index);
  398. //
  399. // Apply these changes to the heightfield itself
  400. //
  401. if (CurrentHeightfield != NULL) {
  402. CurrentHeightfield->Set_Material (material_index, MaterialList[material_index]);
  403. CurrentHeightfield->On_Material_Changed (material_index);
  404. }
  405. return ;
  406. }