VisSectorSampler.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  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/VisSectorSampler.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Patrick $*
  29. * *
  30. * $Modtime:: 11/27/01 12:21p $*
  31. * *
  32. * $Revision:: 6 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  37. #include "stdafx.h"
  38. #include "VisSectorSampler.h"
  39. #include "SceneEditor.h"
  40. #include "VisGenProgress.h"
  41. #include "rendobj.h"
  42. #include "meshbuild.h"
  43. #include "vector.h"
  44. #include "pscene.h"
  45. #include "mesh.h"
  46. #include "meshmdl.h"
  47. #include "vector3.h"
  48. #include "vector4.h"
  49. #include "vector3i.h"
  50. #include "physcoltest.h"
  51. #include "phys.h"
  52. /*
  53. ** Compile time options
  54. */
  55. #define USE_EDGE_SKIPPING 0
  56. /*
  57. ** Constants
  58. */
  59. const float SHRINKAGE_DISTANCE = 0.3f; // amount to move in from each edge
  60. const float FLOOR_SAMPLE_HEIGHT = 2.0f; // hieght off the floor for first sample
  61. const float CEILING_CHECK_HEIGHT = 250.0F; // how high to look for a ceiling
  62. /**
  63. ** SectorEdgeClass
  64. ** This is a description of a single edge in the sector mesh
  65. */
  66. class SectorEdgeClass
  67. {
  68. public:
  69. SectorEdgeClass(void);
  70. SectorEdgeClass(const Vector3 & p0,const Vector3 & p1,int p0index,int p1index);
  71. SectorEdgeClass(const SectorEdgeClass & that);
  72. SectorEdgeClass & operator = (const SectorEdgeClass & that);
  73. bool operator == (const SectorEdgeClass & that) const;
  74. bool operator != (const SectorEdgeClass & that) const;
  75. const Vector3 & Get_P0(void) const { return P0; }
  76. const Vector3 & Get_P1(void) const { return P1; }
  77. const int Get_P0_Index(void) const { return P0Index; }
  78. const int Get_P1_Index(void) const { return P1Index; }
  79. void Set_P0(const Vector3 & p,int index) { P0 = p; P0Index = index; }
  80. void Set_P1(const Vector3 & p,int index) { P1 = p; P1Index = index; }
  81. void Increment_Instance_Count(void) { Counter++; }
  82. int Get_Instance_Count(void) { return Counter; }
  83. protected:
  84. Vector3 P0;
  85. Vector3 P1;
  86. int P0Index;
  87. int P1Index;
  88. int Counter;
  89. };
  90. /**
  91. ** SectorEdgeTableClass
  92. ** This class is used to build the table of "outer" edges for a vis sector. Basically the
  93. ** goal is to have a list of all of the edges that apear in a mesh only once.
  94. */
  95. class SectorEdgeTableClass : public DynamicVectorClass<SectorEdgeClass>
  96. {
  97. public:
  98. SectorEdgeTableClass(int count) : DynamicVectorClass<SectorEdgeClass> (count) { }
  99. ~SectorEdgeTableClass(void) { }
  100. void Add_Edge_Unique(const SectorEdgeClass & edge);
  101. };
  102. /************************************************************************************************
  103. **
  104. ** SectorEdgeClass Implementation
  105. **
  106. ************************************************************************************************/
  107. SectorEdgeClass::SectorEdgeClass(void) :
  108. P0(0,0,0),
  109. P1(0,0,0),
  110. P0Index(0),
  111. P1Index(0),
  112. Counter(1)
  113. {
  114. }
  115. SectorEdgeClass::SectorEdgeClass(const Vector3 & p0,const Vector3 & p1,int p0index,int p1index) :
  116. P0(p0),
  117. P1(p1),
  118. P0Index(p0index),
  119. P1Index(p1index),
  120. Counter(1)
  121. {
  122. }
  123. SectorEdgeClass::SectorEdgeClass(const SectorEdgeClass & that)
  124. {
  125. *this = that;
  126. }
  127. SectorEdgeClass & SectorEdgeClass::operator = (const SectorEdgeClass & that)
  128. {
  129. if (this != &that) {
  130. P0 = that.P0;
  131. P1 = that.P1;
  132. P0Index = that.P0Index;
  133. P1Index = that.P1Index;
  134. Counter = 1;
  135. }
  136. return *this;
  137. }
  138. bool SectorEdgeClass::operator == (const SectorEdgeClass & that) const
  139. {
  140. return ( ((P0Index == that.P0Index) && (P1Index == that.P1Index)) ||
  141. ((P0Index == that.P1Index) && (P1Index == that.P0Index)) );
  142. }
  143. bool SectorEdgeClass::operator != (const SectorEdgeClass & that) const
  144. {
  145. return !(*this == that);
  146. }
  147. /************************************************************************************************
  148. **
  149. ** SectorEdgeTableClass Implementation
  150. **
  151. ************************************************************************************************/
  152. void SectorEdgeTableClass::Add_Edge_Unique(const SectorEdgeClass & edge)
  153. {
  154. /*
  155. ** See if we already have this edge in the table.
  156. */
  157. for (int i=0; i<Count(); i++) {
  158. if (edge == (*this)[i]) {
  159. (*this)[i].Increment_Instance_Count();
  160. return;
  161. }
  162. }
  163. /*
  164. ** If we fall through to here, add the edge to our array
  165. */
  166. Add(edge);
  167. }
  168. /************************************************************************************************
  169. **
  170. ** VisSectorSamplerClass Implementation
  171. **
  172. ************************************************************************************************/
  173. VisSectorSamplerClass::VisSectorSamplerClass
  174. (
  175. SceneEditorClass * scene,
  176. VisGenProgressClass * stats,
  177. float min_sample_distance,
  178. int collision_group
  179. ) :
  180. Scene(NULL),
  181. MeshBuilder(NULL),
  182. Stats(stats),
  183. MinSampleDistance(min_sample_distance),
  184. CollisionGroup(collision_group),
  185. EdgeSkipAccum(0.0f)
  186. {
  187. REF_PTR_SET(Scene,scene);
  188. MeshBuilder = new MeshBuilderClass;
  189. }
  190. VisSectorSamplerClass::~VisSectorSamplerClass(void)
  191. {
  192. if (MeshBuilder != NULL) {
  193. delete MeshBuilder;
  194. MeshBuilder = NULL;
  195. }
  196. REF_PTR_RELEASE(Scene);
  197. }
  198. void VisSectorSamplerClass::Process(RenderObjClass * model)
  199. {
  200. Reset(model->Get_Num_Polys());
  201. int count = Collect_Polygons(model);
  202. if (count > 0) {
  203. Sample_Edges();
  204. } else {
  205. WWDEBUG_SAY(("Vis-sector %s had no up-facing polygons!\r\n",model->Get_Name()));
  206. }
  207. Stats->Increment_Processed_Node_Count();
  208. }
  209. void VisSectorSamplerClass::Reset(int poly_count)
  210. {
  211. MeshBuilder->Reset(1,poly_count,0.5f*poly_count + 1);
  212. }
  213. int VisSectorSamplerClass::Collect_Polygons(RenderObjClass * renderobj)
  214. {
  215. WWASSERT(renderobj != NULL);
  216. int poly_count = 0;
  217. /*
  218. ** If this render object is a mesh and is marked as a VIS sector, then
  219. ** collect upward-facing polygons and submit them into the mesh builder
  220. */
  221. if ( (renderobj->Get_Collision_Type () & COLLISION_TYPE_VIS) &&
  222. (renderobj->Class_ID() == RenderObjClass::CLASSID_MESH) )
  223. {
  224. MeshModelClass * model = ((MeshClass *)renderobj)->Get_Model();
  225. if (model != NULL) {
  226. MeshBuilderClass::FaceClass face;
  227. /*
  228. ** Grab the relevent data from the mesh.
  229. */
  230. const TriIndex *poly_array = model->Get_Polygon_Array ();
  231. const Vector3 *vertex_array = model->Get_Vertex_Array ();
  232. const Vector4 *plane_array = model->Get_Plane_Array (true);
  233. /*
  234. ** Add each up-facing polygon into our builder
  235. */
  236. for (int pi = 0; pi < model->Get_Polygon_Count(); pi++) {
  237. if (plane_array[pi].Z > 0.3f) {
  238. /*
  239. ** Copy this face into the builder
  240. */
  241. for (int vi=0; vi<3; vi++) {
  242. face.Verts[vi].Position = vertex_array[poly_array[pi][vi]];
  243. }
  244. MeshBuilder->Add_Face(face);
  245. poly_count++;
  246. }
  247. }
  248. }
  249. }
  250. /*
  251. ** Recurse into all sub-meshes
  252. */
  253. int count = renderobj->Get_Num_Sub_Objects();
  254. for (int index = 0; index < count; index ++) {
  255. RenderObjClass *sub_object = renderobj->Get_Sub_Object(index);
  256. if (sub_object != NULL) {
  257. poly_count += Collect_Polygons(sub_object);
  258. REF_PTR_RELEASE(sub_object);
  259. }
  260. }
  261. return poly_count;
  262. }
  263. void VisSectorSamplerClass::Sample_Edges(void)
  264. {
  265. /*
  266. ** Tell the mesh builder to process its input.
  267. */
  268. MeshBuilder->Build_Mesh(false);
  269. /*
  270. ** Build an array of all of the edges, marking duplicates for removal
  271. */
  272. SectorEdgeTableClass edgetable(3 * MeshBuilder->Get_Face_Count());
  273. for (int fi=0; fi<MeshBuilder->Get_Face_Count(); fi++) {
  274. const MeshBuilderClass::FaceClass & face = MeshBuilder->Get_Face(fi);
  275. for (int vi=0; vi<3; vi++) {
  276. int v0 = vi;
  277. int v1 = (vi + 1) % 3;
  278. const MeshBuilderClass::VertClass & vertex0 = MeshBuilder->Get_Vertex(face.VertIdx[v0]);
  279. const MeshBuilderClass::VertClass & vertex1 = MeshBuilder->Get_Vertex(face.VertIdx[v1]);
  280. edgetable.Add_Edge_Unique(SectorEdgeClass(vertex0.Position,vertex1.Position,face.VertIdx[v0],face.VertIdx[v1]));
  281. }
  282. }
  283. /*
  284. ** For each mesh that has an instance count of 1, recursively sample vis along it
  285. */
  286. for (int ei=0; ei<edgetable.Count(); ei++) {
  287. if (edgetable[ei].Get_Instance_Count() == 1) {
  288. Vector3 edge_dir = edgetable[ei].Get_P1() - edgetable[ei].Get_P0();
  289. edge_dir.Z = 0.0f;
  290. float edge_len = edge_dir.Length();
  291. edge_dir /= edge_len;
  292. if (edge_len > 0.0f) {
  293. /*
  294. ** Skip the edge if it is short
  295. */
  296. #if (USE_EDGE_SKIPPING)
  297. if (edge_len + EdgeSkipAccum < MinSampleDistance) {
  298. EdgeSkipAccum += edge_len;
  299. } else {
  300. #endif
  301. EdgeSkipAccum = 0.0f;
  302. /*
  303. ** Move "inward" and up in Z to hopefully generate valid points which don't
  304. ** cause the camera to intersect walls.
  305. */
  306. Vector3 offset(-edge_dir.Y,edge_dir.X,0.0f);
  307. offset *= SHRINKAGE_DISTANCE;
  308. offset.Z += FLOOR_SAMPLE_HEIGHT;
  309. Vector3 p0 = edgetable[ei].Get_P0() + offset;
  310. Vector3 p1 = edgetable[ei].Get_P1() + offset;
  311. Sample_Edge(p0,p1);
  312. Stats->Increment_Edge_Count();
  313. #if (USE_EDGE_SKIPPING)
  314. }
  315. #endif
  316. }
  317. }
  318. if (Stats->Is_Cancel_Requested()) {
  319. return;
  320. }
  321. }
  322. }
  323. void VisSectorSamplerClass::Sample_Edge(const Vector3 & p0,const Vector3 & p1)
  324. {
  325. /*
  326. ** Perform a vis sample at the center of the edge both near the ground
  327. ** and at the maximum height.
  328. */
  329. int bits_changed = 0;
  330. Vector3 sample_point = 0.5f * (p0 + p1);
  331. bits_changed = Sample_Point(sample_point);
  332. /*
  333. ** Should we subdivide this edge and keep sampling?
  334. */
  335. if ( (bits_changed > 0) &&
  336. ((p1-p0).Quick_Length() > 2.0f*MinSampleDistance) )
  337. {
  338. Sample_Edge(p0,sample_point);
  339. Sample_Edge(sample_point,p1);
  340. }
  341. }
  342. int VisSectorSamplerClass::Sample_Point(const Vector3 & point)
  343. {
  344. int bits_changed = 0;
  345. float ceiling_distance = 0.0f;
  346. /*
  347. ** Check the ceiling height
  348. */
  349. if ((Check_Ceiling(point,&ceiling_distance) == true) && (ceiling_distance > 1.0f)) {
  350. if (ceiling_distance > 20.0f) {
  351. ceiling_distance = 20.0f;
  352. }
  353. Vector3 ceiling_point = point;
  354. ceiling_point.Z += ceiling_distance - 0.3f;
  355. /*
  356. ** Now, sample the bottom and top of the vertical segment, recording
  357. ** the amount of changes made to the vis vector
  358. */
  359. int bits0 = 0,bits1 = 0;
  360. bits0 = Update_Vis(point);
  361. if (bits0 > 0) {
  362. bits1 = Update_Vis(ceiling_point);
  363. }
  364. bits_changed += bits0 + bits1;
  365. /*
  366. ** Recursively continue to sample vertically until we are making no
  367. ** more changes or the points get too close together
  368. */
  369. if (bits1 > 0) {
  370. bits_changed += Sample_Vertical_Segment(point,ceiling_point);
  371. }
  372. }
  373. return bits_changed;
  374. }
  375. int VisSectorSamplerClass::Sample_Vertical_Segment(const Vector3 & p0,const Vector3 & p1)
  376. {
  377. Vector3 sample_point = 0.5f * (p0 + p1);
  378. int bits_changed = Update_Vis(sample_point);
  379. if ( (bits_changed > 0) &&
  380. (p1.Z-p0.Z > 2.0f*MinSampleDistance) )
  381. {
  382. bits_changed += Sample_Vertical_Segment(p0,sample_point);
  383. bits_changed += Sample_Vertical_Segment(sample_point,p1);
  384. }
  385. return bits_changed;
  386. }
  387. int VisSectorSamplerClass::Update_Vis(const Vector3 & point)
  388. {
  389. /*
  390. ** Perform the vis sample
  391. */
  392. Matrix3D transform(Matrix3(1),point);
  393. VisSampleClass sample = Scene->Update_Vis(point,transform);
  394. Stats->Increment_Sample_Count();
  395. /*
  396. ** Log the results with the scene editor
  397. */
  398. VisLogClass &vis_log = Scene->Get_Vis_Log();
  399. vis_log.Log_Sample(sample);
  400. Scene->Create_Vis_Point(transform);
  401. /*
  402. ** Return the number of bits changed by this sample
  403. */
  404. if (sample.Sample_Rejected()) {
  405. return 0;
  406. } else {
  407. return sample.Get_Bits_Changed();
  408. }
  409. }
  410. /////////////////////////////////////////////////////////////////////////
  411. //
  412. // Check_Ceiling
  413. //
  414. // This method casts a ray straight up from the candiate vis-point
  415. // and checks to see if the object it hits (if any) is a valid
  416. // vis 'ceiling'. This makes sure we don't generate points that are
  417. // inside of hills or mountains.
  418. //
  419. /////////////////////////////////////////////////////////////////////////
  420. bool VisSectorSamplerClass::Check_Ceiling (const Vector3 &position, float *ceiling_dist)
  421. {
  422. //
  423. // This method casts a ray straight up from the candiate vis-point
  424. // and checks to see if the object it hits (if any) is a valid
  425. // vis 'ceiling'. This makes sure we don't generate points that are
  426. // inside of hills or mountains.
  427. //
  428. bool retval = true;
  429. //
  430. // Build a ray from the given position up 100 meters in the air.
  431. //
  432. Vector3 start_point = position + Vector3 (0, 0, 0.001F);
  433. Vector3 end_point = position + Vector3 (0, 0, CEILING_CHECK_HEIGHT);
  434. LineSegClass ray (start_point, end_point);
  435. //
  436. // Cast the ray into the world and see what it hits.
  437. //
  438. CastResultStruct res;
  439. PhysRayCollisionTestClass raytest (ray, &res, CollisionGroup, COLLISION_TYPE_PHYSICAL);
  440. Scene->Cast_Ray (raytest);
  441. //
  442. // Return how far above us the ceiling is.
  443. //
  444. if (ceiling_dist != NULL) {
  445. (*ceiling_dist) = (res.Fraction * (end_point.Z - start_point.Z));
  446. }
  447. //
  448. // Did we hit anything?
  449. //
  450. if (res.Fraction < 1.0F) {
  451. // Get the physics object we hit
  452. PhysClass *physobj = raytest.CollidedPhysObj;
  453. if ((physobj != NULL) && (physobj->As_StaticPhysClass() != NULL)) {
  454. // If this polygon is facing up, then make sure it satisfies
  455. // our 'ceiling' requirements for back-face polygons.
  456. if ((res.Normal.Z > 0) &&
  457. Is_Object_Invalid_Roof (physobj->Peek_Model ()))
  458. {
  459. retval = false;
  460. }
  461. }
  462. }
  463. return retval;
  464. }
  465. /////////////////////////////////////////////////////////////////////////
  466. //
  467. // Is_Object_Invalid_Roof
  468. //
  469. // This method determines if it is valid for a vis-point to be generated
  470. // underneath a given object.
  471. //
  472. // An object is considered 'invalid' for a vis-point's 'roof' if:
  473. //
  474. // - The mesh's polygons are visible.
  475. // - The mesh's polygons are single-sided
  476. // - The mesh's polygons can be physically collideable.
  477. // - The mesh is marked for vis generation itself.
  478. //
  479. /////////////////////////////////////////////////////////////////////////
  480. bool VisSectorSamplerClass::Is_Object_Invalid_Roof(RenderObjClass *render_obj)
  481. {
  482. bool retval = true;
  483. //
  484. // Recursively check all sub-objects
  485. //
  486. int count = render_obj->Get_Num_Sub_Objects ();
  487. for (int index = 0; (index < count) && retval; index ++) {
  488. //
  489. // Check this subobject
  490. //
  491. RenderObjClass *sub_object = render_obj->Get_Sub_Object (index);
  492. if (sub_object != NULL) {
  493. retval &= Is_Object_Invalid_Roof (sub_object);
  494. REF_PTR_RELEASE(sub_object);
  495. }
  496. }
  497. //
  498. // Is this render object a mesh?
  499. //
  500. if (render_obj->Class_ID () == RenderObjClass::CLASSID_MESH) {
  501. MeshModelClass *model = ((MeshClass *)render_obj)->Get_Model ();
  502. if (model != NULL) {
  503. //
  504. // The mesh is invalid if:
  505. //
  506. // a) The mesh's polys are single-sided AND
  507. // b) The mesh is a vis-sector AND
  508. // c) The mesh is physically collideable AND
  509. // d) The mesh visible
  510. //
  511. retval &= (model->Get_Flag (MeshModelClass::TWO_SIDED) != MeshModelClass::TWO_SIDED);
  512. //retval &= ((render_obj->Get_Collision_Type () & COLLISION_TYPE_VIS) == COLLISION_TYPE_VIS);
  513. retval &= ((render_obj->Get_Collision_Type () & COLLISION_TYPE_PHYSICAL) == COLLISION_TYPE_PHYSICAL);
  514. retval &= (render_obj->Is_Not_Hidden_At_All () != 0);
  515. REF_PTR_RELEASE(model);
  516. }
  517. }
  518. return retval;
  519. }
  520. /////////////////////////////////////////////////////////////////////////
  521. //
  522. // Do_View_Planes_Pass
  523. //
  524. // This method checks the view-plane in each of the six directions around
  525. // the candidate point and determines if any of them intersect a 'wall'.
  526. //
  527. /////////////////////////////////////////////////////////////////////////
  528. bool VisSectorSamplerClass::Do_View_Planes_Pass (const Matrix3D &vis_transform)
  529. {
  530. bool retval = true;
  531. #if 0 // TODO!
  532. Vector3 center = vis_transform.Get_Translation ();
  533. Matrix3 orig_basis (vis_transform);
  534. //
  535. // Loop through and test each of the 6 orienations
  536. // of the view plane to make sure none of them intersect
  537. // a 'wall'.
  538. //
  539. /*CastResultStruct result;
  540. for (int index = 0; (index < VIS_RENDER_DIRECTIONS) && retval; index ++) {
  541. //
  542. // Build the orientation of the view-plane
  543. //
  544. Matrix3 basis = orig_basis;
  545. basis.Rotate_X (VIS_RENDER_TABLE[index].X);
  546. basis.Rotate_Y (VIS_RENDER_TABLE[index].Y);
  547. basis.Rotate_Z (VIS_RENDER_TABLE[index].Z);*/
  548. //
  549. // Create a box representing the view plane
  550. //
  551. //Vector3
  552. AABoxClass box (center, m_ViewPlaneExtent);
  553. //
  554. // Check to see if this viewplane 'box' collides
  555. // with anything.
  556. //
  557. //result.Reset ();
  558. CastResultStruct result;
  559. PhysAABoxCollisionTestClass collision_test (box, Vector3(0,0,0), &result, CollisionGroup, COLLISION_TYPE_PHYSICAL);
  560. Scene->Cast_AABox (collision_test);
  561. retval = (result.StartBad == false) && !(result.Fraction < 1.0F);
  562. //}
  563. #endif
  564. return retval;
  565. }