segline.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. ** Command & Conquer Generals(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 : G *
  23. * *
  24. * $Archive:: /VSS_Sync/ww3d2/segline.cpp $*
  25. * *
  26. * $Author:: Vss_sync $*
  27. * *
  28. * $Modtime:: 8/29/01 7:29p $*
  29. * *
  30. * $Revision:: 23 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "segline.h"
  36. #include "ww3d.h"
  37. #include "rinfo.h"
  38. #include "predlod.h"
  39. #include "v3_rnd.h"
  40. #include "texture.h"
  41. #include "coltest.h"
  42. #include "w3d_file.h"
  43. #include "texture.h"
  44. #include "dx8wrapper.h"
  45. #include "vp.h"
  46. #include "vector3i.h"
  47. #include "sortingrenderer.h"
  48. static SegLineRendererClass _LineRenderer;
  49. /*
  50. ** SegmentedLineClass implementation:
  51. */
  52. SegmentedLineClass::SegmentedLineClass(void) :
  53. MaxSubdivisionLevels(0),
  54. NormalizedScreenArea(0.0f)
  55. {
  56. }
  57. SegmentedLineClass::SegmentedLineClass(const SegmentedLineClass & src) :
  58. MaxSubdivisionLevels(src.MaxSubdivisionLevels),
  59. NormalizedScreenArea(src.NormalizedScreenArea),
  60. PointLocations(src.PointLocations),
  61. LineRenderer(src.LineRenderer)
  62. {
  63. }
  64. SegmentedLineClass & SegmentedLineClass::operator = (const SegmentedLineClass &that)
  65. {
  66. RenderObjClass::operator = (that);
  67. if (this != &that) {
  68. MaxSubdivisionLevels = that.MaxSubdivisionLevels;
  69. NormalizedScreenArea = that.NormalizedScreenArea;
  70. PointLocations = that.PointLocations;
  71. LineRenderer = that.LineRenderer;
  72. }
  73. return * this;
  74. }
  75. SegmentedLineClass::~SegmentedLineClass(void)
  76. {
  77. }
  78. void SegmentedLineClass::Reset_Line(void)
  79. {
  80. LineRenderer.Reset_Line();
  81. }
  82. // These are segment points, and include the start and end point of the
  83. // entire line. Therefore there must be at least two.
  84. void SegmentedLineClass::Set_Points(unsigned int num_points, Vector3 *locs)
  85. {
  86. if (num_points < 2 || !locs) {
  87. WWASSERT(0);
  88. return;
  89. }
  90. PointLocations.Delete_All();
  91. for (unsigned int i=0; i<num_points; i++) {
  92. PointLocations.Add(locs[i],num_points);
  93. }
  94. Invalidate_Cached_Bounding_Volumes();
  95. }
  96. // These are segment points, and include the start and end point of the
  97. // entire line. Therefore there must be at least two.
  98. int SegmentedLineClass::Get_Num_Points(void)
  99. {
  100. return PointLocations.Count();
  101. }
  102. // Set object-space location for a given point.
  103. // NOTE: If given position beyond end of point list, do nothing.
  104. void SegmentedLineClass::Set_Point_Location(unsigned int point_idx, const Vector3 &location)
  105. {
  106. if (point_idx < (unsigned int)PointLocations.Count()) {
  107. PointLocations[point_idx] = location;
  108. }
  109. Invalidate_Cached_Bounding_Volumes();
  110. }
  111. // Get object-space location of a given point (if position beyond end of
  112. // point list, will return 0,0,0).
  113. void SegmentedLineClass::Get_Point_Location(unsigned int point_idx, Vector3 &loc)
  114. {
  115. if (point_idx < (unsigned int)PointLocations.Count()) {
  116. loc.Set(PointLocations[point_idx]);
  117. } else {
  118. loc.Set(0, 0, 0);
  119. }
  120. }
  121. void SegmentedLineClass::Add_Point(const Vector3 & location)
  122. {
  123. PointLocations.Add(location);
  124. }
  125. void SegmentedLineClass::Delete_Point(unsigned int point_idx)
  126. {
  127. if (point_idx < (unsigned int)PointLocations.Count()) {
  128. PointLocations.Delete(point_idx);
  129. }
  130. }
  131. TextureClass * SegmentedLineClass::Get_Texture(void)
  132. {
  133. return LineRenderer.Get_Texture();
  134. }
  135. ShaderClass SegmentedLineClass::Get_Shader(void)
  136. {
  137. return LineRenderer.Get_Shader();
  138. }
  139. void SegmentedLineClass::Get_Color(Vector3 &color)
  140. {
  141. color.Set(LineRenderer.Get_Color());
  142. }
  143. float SegmentedLineClass::Get_Opacity(void)
  144. {
  145. return LineRenderer.Get_Opacity();
  146. }
  147. float SegmentedLineClass::Get_Noise_Amplitude(void)
  148. {
  149. return LineRenderer.Get_Noise_Amplitude();
  150. }
  151. float SegmentedLineClass::Get_Merge_Abort_Factor(void)
  152. {
  153. return LineRenderer.Get_Merge_Abort_Factor();
  154. }
  155. unsigned int SegmentedLineClass::Get_Subdivision_Levels(void)
  156. {
  157. return MaxSubdivisionLevels;
  158. }
  159. SegLineRendererClass::TextureMapMode SegmentedLineClass::Get_Texture_Mapping_Mode(void)
  160. {
  161. return LineRenderer.Get_Texture_Mapping_Mode();
  162. }
  163. float SegmentedLineClass::Get_Texture_Tile_Factor(void)
  164. {
  165. return LineRenderer.Get_Texture_Tile_Factor();
  166. }
  167. Vector2 SegmentedLineClass::Get_UV_Offset_Rate(void)
  168. {
  169. return LineRenderer.Get_UV_Offset_Rate();
  170. }
  171. int SegmentedLineClass::Is_Merge_Intersections(void)
  172. {
  173. return LineRenderer.Is_Merge_Intersections();
  174. }
  175. int SegmentedLineClass::Is_Freeze_Random(void)
  176. {
  177. return LineRenderer.Is_Freeze_Random();
  178. }
  179. int SegmentedLineClass::Is_Sorting_Disabled(void)
  180. {
  181. return LineRenderer.Is_Sorting_Disabled();
  182. }
  183. int SegmentedLineClass::Are_End_Caps_Enabled(void)
  184. {
  185. return LineRenderer.Are_End_Caps_Enabled();
  186. }
  187. void SegmentedLineClass::Set_Texture(TextureClass *texture)
  188. {
  189. LineRenderer.Set_Texture(texture);
  190. }
  191. void SegmentedLineClass::Set_Shader(ShaderClass shader)
  192. {
  193. LineRenderer.Set_Shader(shader);
  194. }
  195. float SegmentedLineClass::Get_Width(void)
  196. {
  197. return LineRenderer.Get_Width();
  198. }
  199. void SegmentedLineClass::Set_Width(float width)
  200. {
  201. // Widths need to be clamped because they are not automatically clamped later (like colors and
  202. // alphas are).
  203. LineRenderer.Set_Width(MAX(width, 0.0f));
  204. Invalidate_Cached_Bounding_Volumes();
  205. }
  206. void SegmentedLineClass::Set_Color(const Vector3 &color)
  207. {
  208. LineRenderer.Set_Color(color);
  209. }
  210. void SegmentedLineClass::Set_Opacity(float opacity)
  211. {
  212. LineRenderer.Set_Opacity(opacity);
  213. }
  214. void SegmentedLineClass::Set_Noise_Amplitude(float amplitude)
  215. {
  216. LineRenderer.Set_Noise_Amplitude(WWMath::Fabs(amplitude));
  217. Invalidate_Cached_Bounding_Volumes();
  218. }
  219. void SegmentedLineClass::Set_Merge_Abort_Factor(float factor)
  220. {
  221. LineRenderer.Set_Merge_Abort_Factor(factor);
  222. }
  223. void SegmentedLineClass::Set_Subdivision_Levels(unsigned int levels)
  224. {
  225. MaxSubdivisionLevels = MIN(levels, MAX_SEGLINE_SUBDIV_LEVELS);
  226. Invalidate_Cached_Bounding_Volumes();
  227. }
  228. void SegmentedLineClass::Set_Texture_Mapping_Mode(SegLineRendererClass::TextureMapMode mode)
  229. {
  230. LineRenderer.Set_Texture_Mapping_Mode(mode);
  231. }
  232. void SegmentedLineClass::Set_Texture_Tile_Factor(float factor)
  233. {
  234. LineRenderer.Set_Texture_Tile_Factor(factor);
  235. }
  236. void SegmentedLineClass::Set_UV_Offset_Rate(const Vector2 &rate)
  237. {
  238. LineRenderer.Set_UV_Offset_Rate(rate);
  239. }
  240. void SegmentedLineClass::Set_Merge_Intersections(int onoff)
  241. {
  242. LineRenderer.Set_Merge_Intersections(onoff);
  243. }
  244. void SegmentedLineClass::Set_Freeze_Random(int onoff)
  245. {
  246. LineRenderer.Set_Freeze_Random(onoff);
  247. }
  248. void SegmentedLineClass::Set_Disable_Sorting(int onoff)
  249. {
  250. LineRenderer.Set_Disable_Sorting(onoff);
  251. }
  252. void SegmentedLineClass::Set_End_Caps(int onoff)
  253. {
  254. LineRenderer.Set_End_Caps(onoff);
  255. }
  256. /*
  257. ** RenderObjClass interface:
  258. */
  259. RenderObjClass * SegmentedLineClass::Clone(void) const
  260. {
  261. return NEW_REF( SegmentedLineClass, (*this));
  262. }
  263. int SegmentedLineClass::Get_Num_Polys(void) const
  264. {
  265. int subdivision_factor = 1 << LineRenderer.Get_Current_Subdivision_Level();
  266. return 2 * (PointLocations.Count() - 1) * subdivision_factor;
  267. }
  268. void SegmentedLineClass::Render(RenderInfoClass & rinfo)
  269. {
  270. if (Is_Not_Hidden_At_All() == false) {
  271. return ;
  272. }
  273. // Process texture reductions:
  274. // if (LineRenderer.Peek_Texture()) LineRenderer.Peek_Texture()->Process_Reduction();
  275. unsigned int sort_level = SORT_LEVEL_NONE;
  276. if (!WW3D::Is_Sorting_Enabled())
  277. sort_level=Get_Shader().Guess_Sort_Level();
  278. if (WW3D::Are_Static_Sort_Lists_Enabled() && sort_level!=SORT_LEVEL_NONE) {
  279. WW3D::Add_To_Static_Sort_List(this, sort_level);
  280. } else
  281. Render_Seg_Line(rinfo);
  282. }
  283. void SegmentedLineClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
  284. {
  285. // Get object-space bounding box and create bounding sphere from it
  286. AABoxClass box;
  287. Get_Obj_Space_Bounding_Box(box);
  288. // Create object-space bounding sphere from the bounding box:
  289. sphere.Center = box.Center;
  290. sphere.Radius = box.Extent.Length();
  291. }
  292. void SegmentedLineClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
  293. {
  294. unsigned int num_points = PointLocations.Count();
  295. // Line must have at least two points to be valid
  296. if (num_points >= 2) {
  297. // Find object-space axis-aligned bounding box
  298. Vector3 max_coords;
  299. Vector3 min_coords;
  300. unsigned int i;
  301. // We create two bounding boxes; one from the points, and if we have random noise
  302. // subdivision we create another one from the midpoints and factor the noise amplitude
  303. // into the second box, and then combine the two.
  304. // First bounding box:
  305. max_coords = PointLocations[0];
  306. min_coords = PointLocations[0];
  307. for (i = 1; i < num_points; i++) {
  308. max_coords.Update_Max(PointLocations[i]);
  309. min_coords.Update_Min(PointLocations[i]);
  310. }
  311. // Enlarge bounding box by half the width
  312. float enlarge_factor = LineRenderer.Get_Width() * 0.5f;
  313. Vector3 enlarge_offset;
  314. enlarge_offset.Set(enlarge_factor, enlarge_factor, enlarge_factor);
  315. max_coords += enlarge_offset;
  316. min_coords -= enlarge_offset;
  317. if (MaxSubdivisionLevels > 0) {
  318. // Second bounding box:
  319. Vector3 max_coords2;
  320. Vector3 min_coords2;
  321. Vector3 midpoint = (PointLocations[0] + PointLocations[1]) * 0.5f;
  322. max_coords2 = midpoint;
  323. min_coords2 = midpoint;
  324. for (i = 1; i < num_points - 1; i++) {
  325. midpoint = (PointLocations[i] + PointLocations[i + 1]) * 0.5f;
  326. max_coords2.Update_Max(midpoint);
  327. min_coords2.Update_Min(midpoint);
  328. }
  329. // We ignore the actual number of subdivision levels: we multiply the random noise
  330. // amplitude by 2, which is the limit as the number of subdivision levels goes to
  331. // infinity.
  332. enlarge_factor += (2 * LineRenderer.Get_Noise_Amplitude());
  333. enlarge_offset.Set(enlarge_factor, enlarge_factor, enlarge_factor);
  334. max_coords2 += enlarge_offset;
  335. min_coords2 -= enlarge_offset;
  336. // Combine the two:
  337. max_coords.Update_Max(max_coords2);
  338. min_coords.Update_Min(min_coords2);
  339. }
  340. box.Init_Min_Max(min_coords, max_coords);
  341. } else {
  342. // Invalid line - return something
  343. box.Init(Vector3(0,0,0),Vector3(1,1,1));
  344. }
  345. }
  346. void SegmentedLineClass::Prepare_LOD(CameraClass &camera)
  347. {
  348. // Find the maximum screen dimension of the object in pixels
  349. NormalizedScreenArea = Get_Screen_Size(camera);
  350. // // Find and set texture reduction factor
  351. // Set_Texture_Reduction_Factor(Calculate_Texture_Reduction_Factor(NormalizedScreenArea));
  352. // Ensure subdivision level is legal
  353. unsigned int lvl = LineRenderer.Get_Current_Subdivision_Level();
  354. lvl = MIN(lvl, MaxSubdivisionLevels);
  355. LineRenderer.Set_Current_Subdivision_Level(lvl);
  356. // Prepare LOD processing if the line has subdivision enabled:
  357. if (MaxSubdivisionLevels > 0) {
  358. // Add myself to the LOD optimizer:
  359. PredictiveLODOptimizerClass::Add_Object(this);
  360. } else {
  361. // Not added to optimizer, need to add cost
  362. PredictiveLODOptimizerClass::Add_Cost(Get_Cost());
  363. }
  364. }
  365. void SegmentedLineClass::Increment_LOD(void)
  366. {
  367. unsigned int lvl = LineRenderer.Get_Current_Subdivision_Level();
  368. lvl = MIN(lvl+1,MaxSubdivisionLevels);
  369. LineRenderer.Set_Current_Subdivision_Level(lvl);
  370. }
  371. void SegmentedLineClass::Decrement_LOD(void)
  372. {
  373. int lvl = LineRenderer.Get_Current_Subdivision_Level();
  374. if (lvl == 0) return;
  375. LineRenderer.Set_Current_Subdivision_Level(lvl-1);
  376. }
  377. float SegmentedLineClass::Get_Cost(void) const
  378. {
  379. return Get_Num_Polys();
  380. }
  381. float SegmentedLineClass::Get_Value(void) const
  382. {
  383. // If we are at the minimum LOD, we must return AT_MIN_LOD.
  384. if (LineRenderer.Get_Current_Subdivision_Level() == 0) {
  385. return AT_MIN_LOD;
  386. } else {
  387. float polycount = (float)Get_Num_Polys();
  388. float benefit_factor = 1.0f - (0.5f / (polycount * polycount));
  389. return (benefit_factor * NormalizedScreenArea) / Get_Cost();
  390. }
  391. }
  392. float SegmentedLineClass::Get_Post_Increment_Value(void) const
  393. {
  394. // If we are at the maximum LOD, we must return AT_MIN_LOD.
  395. if (LineRenderer.Get_Current_Subdivision_Level() == MaxSubdivisionLevels) {
  396. return AT_MAX_LOD;
  397. } else {
  398. // Assumption: each subdivision level doubles polycount
  399. float polycount = 2.0f * (float)Get_Num_Polys();
  400. float benefit_factor = 1.0f - (0.5f / (polycount * polycount));
  401. // Assumption: Cost() == polycount
  402. return (benefit_factor * NormalizedScreenArea) / polycount;
  403. }
  404. }
  405. void SegmentedLineClass::Set_LOD_Level(int lod)
  406. {
  407. lod = MAX(0, lod);
  408. lod = MIN(lod, (int)MaxSubdivisionLevels);
  409. LineRenderer.Set_Current_Subdivision_Level((unsigned int)lod);
  410. }
  411. int SegmentedLineClass::Get_LOD_Level(void) const
  412. {
  413. return (int) LineRenderer.Get_Current_Subdivision_Level();
  414. }
  415. int SegmentedLineClass::Get_LOD_Count(void) const
  416. {
  417. return (int)MaxSubdivisionLevels;
  418. }
  419. /*
  420. void SegmentedLineClass::Set_Texture_Reduction_Factor(float trf)
  421. {
  422. if (LineRenderer.Peek_Texture()) LineRenderer.Peek_Texture()->Set_Reduction_Factor(trf);
  423. }*/
  424. void SegmentedLineClass::Render_Seg_Line(RenderInfoClass & rinfo)
  425. {
  426. // Line must have at least two points to be valid
  427. if (PointLocations.Count() < 2) return;
  428. SphereClass bounding_sphere;
  429. Get_Obj_Space_Bounding_Sphere(bounding_sphere);
  430. LineRenderer.Render(
  431. rinfo,
  432. Transform,
  433. PointLocations.Count(),
  434. &(PointLocations[0]),
  435. bounding_sphere
  436. );
  437. }
  438. bool SegmentedLineClass::Cast_Ray(RayCollisionTestClass & raytest)
  439. {
  440. if ((Get_Collision_Type() & raytest.CollisionType) == 0) return false;
  441. bool retval = false;
  442. //
  443. // Check each line segment against the ray
  444. //
  445. float fraction = 1.0F;
  446. for (uint32 index = 1; index < (unsigned int)PointLocations.Count(); index ++)
  447. {
  448. #ifdef ALLOW_TEMPORARIES
  449. Vector3 curr_start = Transform * PointLocations[index-1];
  450. Vector3 curr_end = Transform * PointLocations[index];
  451. LineSegClass line_seg (curr_start, curr_end);
  452. #else
  453. Vector3 curr[2];
  454. Transform.mulVector3Array(&PointLocations[index-1], curr, 2);
  455. LineSegClass line_seg(curr[0], curr[1]);
  456. #endif
  457. Vector3 p0;
  458. Vector3 p1;
  459. if (raytest.Ray.Find_Intersection (line_seg, &p0, &fraction, &p1, NULL)) {
  460. //
  461. // Determine if the ray was close enough to this line to be
  462. // considered intersecting
  463. //
  464. float dist = (p0 - p1).Length ();
  465. if (dist <= LineRenderer.Get_Width() && fraction >= 0 && fraction < raytest.Result->Fraction) {
  466. //if (dist <= Width && fraction < raytest.Result->Fraction) {
  467. retval = true;
  468. break;
  469. }
  470. }
  471. }
  472. //
  473. // Fill in the raytest structure if we were successfull
  474. //
  475. if (retval) {
  476. raytest.Result->Fraction = fraction;
  477. raytest.Result->SurfaceType = SURFACE_TYPE_DEFAULT;
  478. raytest.CollidedRenderObj = this;
  479. }
  480. return retval;
  481. }