VisPointGenerator.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  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/VisPointGenerator.cpp $Modtime:: $*
  25. * *
  26. * $Revision:: 25 $*
  27. * *
  28. *---------------------------------------------------------------------------------------------*
  29. * Functions: *
  30. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  31. #include "stdafx.h"
  32. #include "vispointgenerator.h"
  33. #include "cameramgr.h"
  34. #include "utils.h"
  35. #include "mesh.h"
  36. #include "meshmdl.h"
  37. #include "vector3.h"
  38. #include "vector3i.h"
  39. #include "collisiongroups.h"
  40. #include "sceneeditor.h"
  41. #include "physcoltest.h"
  42. #include "hittestinfo.h"
  43. #include "node.h"
  44. #include "obbox.h"
  45. #include "vispointnode.h"
  46. #include "nodemgr.h"
  47. #include "staticphys.h"
  48. //////////////////////////////////////////////////////////////////////////
  49. //
  50. // Constants
  51. //
  52. //////////////////////////////////////////////////////////////////////////
  53. const float CEILING_CHECK_HEIGHT = 250.0F;
  54. const float FLOOR_CHECK_HEIGHT = -250.0F;
  55. const float MIN_CEILING_HEIGHT = 2.0F;
  56. const float PERCENT_FROM_COLLISION = 0.75F;
  57. const float DEF_POINT_RAISE_HEIGHT = 6.5F;
  58. const float DEF_INDOOR_POINT_RAISE_HEIGHT = 2.0f;
  59. const float FLAT_FACE_NORMAL_MIN = 0.0F;
  60. const float VIEWPLANE_BOX_HALF_DEPTH = 0.0005F;
  61. const float DEF_VIS_SAMPLE_HEIGHT = 20.0F; // we sample up to 20 meters above the vis sector
  62. const float VERTICAL_GRANULARITY = 5.0F; // granularity of sampling in the Z axis
  63. const float DEF_CAM_SIM_POINT_COUNT = 32.0F;
  64. const float CAMERA_SIM_RADIUS = 10.0F;
  65. const char * VIS_BIAS_STRING = "visbias";
  66. //////////////////////////////////////////////////////////////////////////
  67. //
  68. // VisPointGeneratorClass
  69. //
  70. //////////////////////////////////////////////////////////////////////////
  71. VisPointGeneratorClass::VisPointGeneratorClass (float granularity)
  72. : m_Granularity (granularity),
  73. m_BaseGranularity (granularity),
  74. m_IgnoreBias (false),
  75. m_ViewPlaneExtent (0, 0, 0),
  76. m_WorldToGridTM (1),
  77. m_CurrentNode (NULL),
  78. m_StatWindow (NULL),
  79. m_pCameraSimOffsets (NULL),
  80. m_CurrentPointList (NULL),
  81. m_PolygonsProcessed (0),
  82. m_TotalPoints (0),
  83. m_CameraSimPointCount (DEF_CAM_SIM_POINT_COUNT),
  84. m_CurrentCeilingHeight (DEF_POINT_RAISE_HEIGHT),
  85. m_VisSampleHeight(DEF_VIS_SAMPLE_HEIGHT)
  86. {
  87. Initialize_Camera_Sim ();
  88. double hfov = 0;
  89. double vfov = 0;
  90. float znear = 0;
  91. float zfar = 0;
  92. ::Get_Scene_Editor ()->Get_Vis_Camera_FOV (hfov, vfov);
  93. ::Get_Scene_Editor ()->Get_Vis_Camera_Clip_Planes (znear, zfar);
  94. //
  95. // Calculate what the extents of a box would be if we
  96. // extruded the view plane by one millimeter.
  97. // Note: The extents are half-extents.
  98. //
  99. float y_dim = znear * ::tan (hfov / 2);
  100. float z_dim = znear * ::tan (vfov / 2);
  101. float max_dim = max (y_dim, z_dim);
  102. max_dim += 0.001F;
  103. m_ViewPlaneExtent.Set (max_dim, max_dim, max_dim);
  104. return ;
  105. }
  106. //////////////////////////////////////////////////////////////////////////
  107. //
  108. // ~VisPointGeneratorClass
  109. //
  110. //////////////////////////////////////////////////////////////////////////
  111. VisPointGeneratorClass::~VisPointGeneratorClass (void)
  112. {
  113. SAFE_DELETE_ARRAY (m_pCameraSimOffsets);
  114. //
  115. // Free the memory associated with the per-node point list
  116. //
  117. for (int index = 0; index < m_PointList.Count (); index ++) {
  118. VisPointListClass *sub_point_list = m_PointList[index];
  119. SAFE_DELETE (sub_point_list);
  120. }
  121. m_PointList.Delete_All ();
  122. return ;
  123. }
  124. //////////////////////////////////////////////////////////////////////////
  125. //
  126. // Is_Numerical
  127. //
  128. //////////////////////////////////////////////////////////////////////////
  129. static inline bool
  130. Is_Numerical (char ch)
  131. {
  132. return ((ch >= '0' && ch <= '9') || ch == '-' || ch == '+' || ch == '.');
  133. }
  134. //////////////////////////////////////////////////////////////////////////
  135. //
  136. // Read_Float_Param
  137. //
  138. //////////////////////////////////////////////////////////////////////////
  139. static bool
  140. Read_Float_Param (LPCTSTR text, LPCTSTR key, float *value)
  141. {
  142. bool retval = false;
  143. //
  144. // Attempt to find the 'key' in this text block
  145. //
  146. CString lowercase_text (text);
  147. lowercase_text.MakeLower ();
  148. LPCTSTR key_start = ::strstr (lowercase_text, key);
  149. if (key_start != NULL) {
  150. //
  151. // Move past the key designator
  152. //
  153. LPCTSTR key_value = key_start + ::lstrlen (key);
  154. //
  155. // Skip whitespace
  156. //
  157. while (key_value[0] != 0 && key_value[0] == ' ') {
  158. key_value ++;
  159. }
  160. if (key_value[0] == '=') {
  161. key_value ++;
  162. //
  163. // Skip whitespace
  164. //
  165. while (key_value[0] != 0 && key_value[0] == ' ') {
  166. key_value ++;
  167. }
  168. //
  169. // Determine how much of the string we should convert
  170. // to a number.
  171. //
  172. int index = 0;
  173. while (::Is_Numerical (key_value[index ++])) ;
  174. if (index > 1) {
  175. //
  176. // Convert the string to a float and return the value
  177. // to the caller.
  178. //
  179. char *number_str = new char[index];
  180. ::lstrcpyn (number_str, key_value, index);
  181. (*value) = ::atof (number_str);
  182. delete [] number_str;
  183. retval = true;
  184. }
  185. }
  186. }
  187. return retval;
  188. }
  189. //////////////////////////////////////////////////////////////////////////
  190. //
  191. // Determine_Granularity
  192. //
  193. //////////////////////////////////////////////////////////////////////////
  194. void
  195. VisPointGeneratorClass::Determine_Granularity (MeshClass &mesh)
  196. {
  197. //
  198. // Default to the base granularity
  199. //
  200. m_Granularity = m_BaseGranularity;
  201. //
  202. // Should we use the vis-bias settings from the mesh?
  203. //
  204. if (m_IgnoreBias == false) {
  205. //
  206. // Check to see if this mesh uses a vis-bias parameter
  207. //
  208. float vis_bias = 1.0F;
  209. CString user_text = mesh.Get_User_Text ();
  210. if (::Read_Float_Param (user_text, VIS_BIAS_STRING, &vis_bias)) {
  211. //
  212. // Adjust the granularity based on the value of the
  213. // VisBias setting.
  214. //
  215. m_Granularity = m_Granularity / vis_bias;
  216. //
  217. // Ensure the bias is inside an exceptable range
  218. //
  219. m_Granularity = max (m_Granularity, 0.1F);
  220. m_Granularity = min (m_Granularity, 100.0F);
  221. }
  222. }
  223. return ;
  224. }
  225. //////////////////////////////////////////////////////////////////////////
  226. //
  227. // Submit_Mesh
  228. //
  229. //////////////////////////////////////////////////////////////////////////
  230. void
  231. VisPointGeneratorClass::Submit_Mesh (MeshClass &mesh)
  232. {
  233. DWORD start_ticks = ::GetTickCount ();
  234. //
  235. // Make sure we use the right granularity for this mesh
  236. //
  237. Determine_Granularity (mesh);
  238. //
  239. // Create a grid we can use to effeciently build a list
  240. // of vis points spaced 'granularity' meters apart.
  241. //
  242. AABoxClass box;
  243. mesh.Get_Obj_Space_Bounding_Box (box);
  244. int cells_x = (int)fabs(((box.Extent.X * 2) / m_Granularity));
  245. int cells_y = (int)fabs(((box.Extent.Y * 2) / m_Granularity));
  246. int cells_z = (int)fabs((((box.Extent.Z * 2) + DEF_POINT_RAISE_HEIGHT) / m_Granularity));
  247. m_Grid.Create_Grid (Vector3 (cells_x + 1, cells_y + 1, cells_z + 1), 0);
  248. //
  249. // Create a transformation matrix that can convert
  250. // a vis-point from world space to 'grid' space.
  251. //
  252. Matrix3D object_tm = mesh.Get_Transform ();
  253. object_tm.Get_Orthogonal_Inverse (m_WorldToGridTM);
  254. Vector3 offset ( -(box.Center.X - box.Extent.X),
  255. -(box.Center.Y - box.Extent.Y),
  256. -(box.Center.Z - box.Extent.Z));
  257. Vector3 pos = m_WorldToGridTM.Get_Translation ();
  258. pos += offset;
  259. m_WorldToGridTM.Set_Translation (pos);
  260. //
  261. // Get this mesh's polygon information
  262. //
  263. MeshModelClass *model = mesh.Get_Model ();
  264. if (model != NULL) {
  265. const TriIndex *poly_array = model->Get_Polygon_Array ();
  266. const Vector3 *vertex_array = model->Get_Vertex_Array ();
  267. const Vector4 *plane_array = model->Get_Plane_Array (true);
  268. //
  269. // Loop through all the polygons in the mesh
  270. //
  271. int poly_count = model->Get_Polygon_Count ();
  272. for (int index = 0; index < poly_count; index ++) {
  273. const TriIndex &poly_def = poly_array[index];
  274. // Is this poly mostly flat?
  275. if (plane_array[index].Z > FLAT_FACE_NORMAL_MIN) {
  276. //
  277. // Get the verticies that make up the triangle
  278. //
  279. Vector3 point1 = vertex_array[poly_def.I];
  280. Vector3 point2 = vertex_array[poly_def.J];
  281. Vector3 point3 = vertex_array[poly_def.K];
  282. //
  283. // Convert the points from obj-space to world space.
  284. //
  285. Matrix3D obj_tm = mesh.Get_Transform ();
  286. point1 = obj_tm * point1;
  287. point2 = obj_tm * point2;
  288. point3 = obj_tm * point3;
  289. // Determine what the center of the triangle is.
  290. Vector3 center = (point1 + point2 + point3) / 3;
  291. //
  292. // Try to submit a vis point for vertex #1 of this triangle.
  293. //
  294. Find_Valid_Points (point1 + ((center-point1) * 0.1F), center, Matrix3(1), false);
  295. //
  296. // Try to submit a vis point for vertex #2 of this triangle.
  297. //
  298. Find_Valid_Points (point2 + ((center-point2) * 0.1F), center, Matrix3(1), false);
  299. //
  300. // Try to submit a vis point for vertex #3 of this triangle.
  301. //
  302. Find_Valid_Points (point3 + ((center-point3) * 0.1F), center, Matrix3(1), false);
  303. //
  304. // Recursively subdivide and submit center points
  305. //
  306. Subdivide_And_Submit_Centers (point1, point2, point3);
  307. }
  308. m_PolygonsProcessed ++;
  309. }
  310. }
  311. MEMBER_RELEASE (model);
  312. //DWORD before_collection = ::GetTickCount ();
  313. //
  314. // Add all the vis points from the grid to the global list.
  315. //
  316. int cell_count = m_Grid.Get_Flat_Size ();
  317. for (int cell = 0; cell < cell_count; cell ++) {
  318. //
  319. // Add the vis point from this cell to the list.
  320. //
  321. //Matrix3D *vis_point = m_Grid.Get_At_Flat (cell);
  322. VisPointInfo *vis_point = m_Grid.Get_At_Flat (cell);
  323. if (vis_point != NULL) {
  324. //
  325. // Allocate a new point list for this point
  326. //
  327. m_CurrentPointList = new VisPointListClass;
  328. m_CurrentPointList->transform = vis_point->m_Transform;
  329. m_CurrentPointList->sample_point = vis_point->m_Transform.Get_Translation ();
  330. m_PointList.Add (m_CurrentPointList);
  331. m_TotalPoints ++;
  332. //
  333. // Add 'sub-points' to this point list
  334. //
  335. if (vis_point->m_DoCameraRing) {
  336. //Generate_Camera_Locations (vis_point->m_Transform);
  337. }
  338. //
  339. // Convert the transform from an obj transform to a camera transform
  340. //
  341. Matrix3D world_to_cam_tm (Vector3 (0, -1, 0), Vector3 (0, 0, 1), Vector3 (-1, 0, 0), Vector3 (0, 0, 0));
  342. m_CurrentPointList->transform = m_CurrentPointList->transform * world_to_cam_tm;
  343. m_CurrentPointList->sample_point = m_CurrentPointList->transform.Get_Translation ();
  344. // Free the grid-vis point
  345. delete vis_point;
  346. }
  347. }
  348. /*DWORD finished_ticks = ::GetTickCount ();
  349. if (m_StatWindow != NULL) {
  350. CString stats;
  351. stats.Format ("Grid Size: %d, %d, %d.\nTotal Time: %d, Collection Time %d.",
  352. m_Grid.Get_Cells_X (),
  353. m_Grid.Get_Cells_Y (),
  354. m_Grid.Get_Cells_Z (),
  355. (finished_ticks - start_ticks),
  356. (finished_ticks - before_collection));
  357. SetWindowText (m_StatWindow, stats);
  358. }*/
  359. m_Grid.Free_Grid ();
  360. return ;
  361. }
  362. /////////////////////////////////////////////////////////////////////////
  363. //
  364. // Subdivide_And_Submit_Centers
  365. // submits the center point of the given triangle and recursively
  366. // subdivides the triangle until it is smaller than the granularity
  367. // value.
  368. //
  369. /////////////////////////////////////////////////////////////////////////
  370. void
  371. VisPointGeneratorClass::Subdivide_And_Submit_Centers
  372. (
  373. const Vector3 & point1,
  374. const Vector3 & point2,
  375. const Vector3 & point3
  376. )
  377. {
  378. // submit the center point
  379. Vector3 center = (point1 + point2 + point3) / 3.0f;
  380. Find_Valid_Points (center, center, Matrix3(1), false);
  381. // recurse if the edge lengths are greater than 2*granularity
  382. float e0len = (point2 - point1).Length2();
  383. float e1len = (point3 - point2).Length2();
  384. float e2len = (point1 - point3).Length2();
  385. float elen = m_Granularity * m_Granularity * 2.0f;
  386. if ( (e0len > elen) || (e1len > elen) || (e2len > elen) ) {
  387. // subdivide into four tris
  388. Vector3 p12 = (point1 + point2) * 0.5f;
  389. Vector3 p23 = (point2 + point3) * 0.5f;
  390. Vector3 p31 = (point3 + point1) * 0.5f;
  391. Subdivide_And_Submit_Centers(point1,p12,p31);
  392. Subdivide_And_Submit_Centers(p12,point2,p23);
  393. Subdivide_And_Submit_Centers(p23,point3,p31);
  394. Subdivide_And_Submit_Centers(p12,p23,p31);
  395. }
  396. }
  397. /////////////////////////////////////////////////////////////////////////
  398. //
  399. // Find_Valid_Point
  400. //
  401. /////////////////////////////////////////////////////////////////////////
  402. bool
  403. VisPointGeneratorClass::Find_Valid_Points
  404. (
  405. const Vector3 &start,
  406. const Vector3 &end,
  407. const Matrix3 &orientation,
  408. bool do_camera_ring
  409. )
  410. {
  411. bool retval = false;
  412. //
  413. // Try a few different points along the line from [start, end].
  414. //
  415. const int MAX_ATTEMPTS = 5;
  416. //
  417. // If the start and end point are very close together, only make
  418. // one attempt.
  419. //
  420. int max_attempts = 5;
  421. if ((end - start).Length() < 0.01f) {
  422. max_attempts = 1;
  423. }
  424. for (int attempt = 0; (attempt < max_attempts) && !retval; attempt ++) {
  425. //
  426. // Build a transform to test along the provided line segment
  427. //
  428. float percent = ((float)attempt) / ((float)MAX_ATTEMPTS);
  429. Vector3 position = start + ((end - start) * percent);
  430. #if 0
  431. //
  432. // Check to make sure this vis-point isn't under a hill or mountain.
  433. // If the vis-point's ceiling checks out, then raise the point up
  434. // to the approximate camera height in the game.
  435. //
  436. float ceiling_dist = 0;
  437. if ( Check_Ceiling (position, &ceiling_dist) &&
  438. (ceiling_dist > MIN_CEILING_HEIGHT))
  439. {
  440. bool retval = true;
  441. if (ceiling_dist < HEIGHT_SPREAD_DISTANCE) {
  442. //
  443. // The ceiling is low enough where we should just submit one point
  444. //
  445. position.Z += DEF_INDOOR_POINT_RAISE_HEIGHT;
  446. retval &= Submit_Point (Matrix3D (position), do_camera_ring);
  447. } else {
  448. //
  449. // The ceiling is high enough to try spreading points vertically
  450. //
  451. float delta_z = HEIGHT_SPREAD_DELTA;
  452. while (retval && delta_z <= HEIGHT_SPREAD_DISTANCE) {
  453. Vector3 point_pos = position;
  454. point_pos.Z += delta_z;
  455. retval &= Submit_Point (Matrix3D (point_pos), do_camera_ring);
  456. delta_z += HEIGHT_SPREAD_DELTA;
  457. }
  458. }
  459. }
  460. #else
  461. //
  462. // Search for the proper interval to sample above this sector.
  463. // We search upwards for "floor" and "roof" polygons. We start above
  464. // the highest floor encountered and sample up until we hit the lowest
  465. // "roof" encountered.
  466. // If any floor is encountered that is itself another vis sector, we don't
  467. // generate sample points at this location for this vis sector.
  468. //
  469. bool done = false;
  470. bool ok_to_sample = false;
  471. Vector3 start_point(position.X,position.Y,position.Z + 0.001f);
  472. Vector3 end_point(position.X,position.Y,position.Z + m_VisSampleHeight);
  473. while (!done) {
  474. CastResultStruct res;
  475. LineSegClass ray (start_point, end_point);
  476. PhysRayCollisionTestClass raytest (ray, &res, GAME_COLLISION_GROUP, COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VIS);
  477. raytest.CheckDynamicObjs = false;
  478. ::Get_Scene_Editor ()->Cast_Ray (raytest);
  479. if (res.Fraction == 1.0f) {
  480. done = true;
  481. } else {
  482. Vector3 point;
  483. ray.Compute_Point(res.Fraction,&point);
  484. PhysClass *physobj = raytest.CollidedPhysObj;
  485. bool is_vis_sector = (physobj->As_StaticPhysClass() && physobj->As_StaticPhysClass()->Is_Vis_Sector());
  486. if ((is_vis_sector) || (res.Normal.Z < 0.0f)) {
  487. //
  488. // If we hit a roof or another vis sector, snap the end point down and signal that we're done
  489. //
  490. end_point.Z = point.Z;
  491. ok_to_sample = true;
  492. done = true;
  493. } else {
  494. //
  495. // If we hit a floor (or anything else), "ratchet" up past that point
  496. //
  497. start_point.Z = point.Z + 0.001f;
  498. }
  499. }
  500. }
  501. //
  502. // If we didn't find a roof within the VIS_SAMPLE_HEIGHT (20 meters)
  503. // then we'll just do a sanity check to make sure we're not underneath a backface above
  504. // the 20 meter sampling distance. The purpose of this is to try to avoid
  505. // generating sample points that will have to be rejected. These types of points can
  506. // easily be generated when a vis sector protrudes underneath a hill or something else big.
  507. // If this check passes, then it is ok to sample.
  508. //
  509. if (!ok_to_sample) {
  510. float dist = 0.0f;
  511. ok_to_sample = Check_Ceiling(end_point,&dist);
  512. }
  513. //
  514. // Now generate samples between start_point and end_point
  515. //
  516. if (ok_to_sample) {
  517. retval = true;
  518. Vector3 sample_point(start_point);
  519. if (DEF_INDOOR_POINT_RAISE_HEIGHT < end_point.Z - sample_point.Z) {
  520. sample_point.Z += DEF_INDOOR_POINT_RAISE_HEIGHT;
  521. } else {
  522. sample_point.Z = end_point.Z - 0.1f;
  523. }
  524. while (sample_point.Z < end_point.Z) {
  525. retval &= Submit_Point (Matrix3D(sample_point), do_camera_ring);
  526. sample_point.Z += VERTICAL_GRANULARITY;
  527. }
  528. }
  529. #endif
  530. }
  531. return retval;
  532. }
  533. /////////////////////////////////////////////////////////////////////////
  534. //
  535. // Check_Ceiling
  536. //
  537. // This method casts a ray straight up from the candiate vis-point
  538. // and checks to see if the object it hits (if any) is a valid
  539. // vis 'ceiling'. This makes sure we don't generate points that are
  540. // inside of hills or mountains.
  541. //
  542. /////////////////////////////////////////////////////////////////////////
  543. bool
  544. VisPointGeneratorClass::Check_Ceiling (const Vector3 &position, float *ceiling_dist)
  545. {
  546. bool retval = true;
  547. //
  548. // Build a ray from the given position up 100 meters in the air.
  549. //
  550. Vector3 start_point = position + Vector3 (0, 0, 0.001F);
  551. Vector3 end_point = position + Vector3 (0, 0, CEILING_CHECK_HEIGHT);
  552. LineSegClass ray (start_point, end_point);
  553. //
  554. // Cast the ray into the world and see what it hits.
  555. //
  556. CastResultStruct res;
  557. PhysRayCollisionTestClass raytest (ray, &res, GAME_COLLISION_GROUP, COLLISION_TYPE_PHYSICAL | COLLISION_TYPE_VIS);
  558. ::Get_Scene_Editor ()->Cast_Ray (raytest);
  559. //
  560. // Return how far above us the ceiling is.
  561. //
  562. if (ceiling_dist != NULL) {
  563. (*ceiling_dist) = (res.Fraction * (end_point.Z - start_point.Z));
  564. }
  565. //
  566. // Did we hit anything?
  567. //
  568. if (res.Fraction < 1.0F) {
  569. // Get the physics object we hit
  570. PhysClass *physobj = raytest.CollidedPhysObj;
  571. if (physobj != NULL) {
  572. //
  573. // Check to see if the node we hit is a static tile. Otherwise
  574. // we don't care about it.
  575. //
  576. /*HITTESTINFO *hittest = (HITTESTINFO *)physobj->Peek_Model ()->Get_User_Data ();
  577. if ( (hittest != NULL) &&
  578. (hittest->Type == HITTESTINFO::Node) &&
  579. (hittest->node != NULL) &&
  580. (hittest->node->Is_Static ()))*/
  581. if (physobj->As_StaticPhysClass () != NULL) {
  582. // If this polygon is facing up, then make sure it satisfies
  583. // our 'ceiling' requirements for back-face polygons.
  584. if ((res.Normal.Z > 0) &&
  585. Is_Object_Invalid_Roof (physobj->Peek_Model ()))
  586. {
  587. retval = false;
  588. }
  589. }
  590. }
  591. }
  592. return retval;
  593. }
  594. /////////////////////////////////////////////////////////////////////////
  595. //
  596. // Is_Grid_Cell_Empty
  597. //
  598. /////////////////////////////////////////////////////////////////////////
  599. bool
  600. VisPointGeneratorClass::Is_Grid_Cell_Empty (const Vector3 &position)
  601. {
  602. Vector3 grid_pos = m_WorldToGridTM * position;
  603. grid_pos.X = grid_pos.X / m_Granularity;
  604. grid_pos.Y = grid_pos.Y / m_Granularity;
  605. grid_pos.Z = grid_pos.Z / m_Granularity;
  606. return (m_Grid.Get_At (grid_pos.X, grid_pos.Y, grid_pos.Z) == NULL);
  607. }
  608. /////////////////////////////////////////////////////////////////////////
  609. //
  610. // Submit_Point
  611. //
  612. /////////////////////////////////////////////////////////////////////////
  613. bool
  614. VisPointGeneratorClass::Submit_Point
  615. (
  616. const Matrix3D & vis_transform,
  617. bool do_camera_ring
  618. )
  619. {
  620. bool was_valid = true;
  621. //
  622. // Convert the world-space position of the matrix to the
  623. // grid-space position.
  624. //
  625. Vector3 grid_pos = vis_transform.Get_Translation ();
  626. grid_pos = m_WorldToGridTM * grid_pos;
  627. grid_pos.X = grid_pos.X / m_Granularity;
  628. grid_pos.Y = grid_pos.Y / m_Granularity;
  629. grid_pos.Z = grid_pos.Z / m_Granularity;
  630. grid_pos.X = WWMath::Clamp (grid_pos.X, 0, (float)(m_Grid.Get_Cells_X () - 1));
  631. grid_pos.Y = WWMath::Clamp (grid_pos.Y, 0, (float)(m_Grid.Get_Cells_Y () - 1));
  632. grid_pos.Z = WWMath::Clamp (grid_pos.Z, 0, (float)(m_Grid.Get_Cells_Z () - 1));
  633. /*grid_pos.X = max (0.0F, grid_pos.X);
  634. grid_pos.Y = max (0.0F, grid_pos.Y);
  635. grid_pos.Z = max (0.0F, grid_pos.Z);
  636. grid_pos.X = min (0.0F, grid_pos.X);
  637. grid_pos.Y = min (0.0F, grid_pos.Y);
  638. grid_pos.Z = min (0.0F, grid_pos.Z);*/
  639. float half_gran = m_Granularity / 2;
  640. Vector3 cell_center ((grid_pos.X * m_Granularity) + half_gran,
  641. (grid_pos.Y * m_Granularity) + half_gran,
  642. (grid_pos.Z * m_Granularity) + half_gran);
  643. Matrix3D grid_to_world_tm;
  644. m_WorldToGridTM.Get_Orthogonal_Inverse (grid_to_world_tm);
  645. cell_center = grid_to_world_tm * cell_center;
  646. float new_point_dist = (vis_transform.Get_Translation () - cell_center).Length ();
  647. //
  648. // Only insert the point into the grid if the grid cell is
  649. // empty.
  650. //
  651. VisPointInfo *cell_contents = m_Grid.Get_At (grid_pos.X, grid_pos.Y, grid_pos.Z);
  652. if ((cell_contents == NULL) ||
  653. ((cell_contents->m_Transform.Get_Translation () - cell_center).Length () > new_point_dist)) {
  654. //
  655. // See if the camera would intersect a wall if a vis point
  656. // were positioned here.
  657. //
  658. was_valid = Do_View_Planes_Pass (vis_transform);
  659. if (1 || was_valid) {
  660. //
  661. // Convert the world-space transform to a
  662. // camera space transform.
  663. //
  664. //Matrix3D world_to_cam_tm (Vector3 (0, -1, 0), Vector3 (0, 0, 1), Vector3 (-1, 0, 0), Vector3 (0, 0, 0));
  665. VisPointInfo *vis_info = new VisPointInfo;
  666. vis_info->m_Transform = vis_transform;
  667. vis_info->m_DoCameraRing = do_camera_ring;
  668. // Insert this point into the grid
  669. m_Grid.Set_At (grid_pos.X, grid_pos.Y, grid_pos.Z, vis_info);
  670. SAFE_DELETE (cell_contents);
  671. }
  672. }
  673. return was_valid;
  674. }
  675. /////////////////////////////////////////////////////////////////////////
  676. //
  677. // Do_View_Planes_Pass
  678. //
  679. // This method checks the view-plane in each of the six directions around
  680. // the candidate point and determines if any of them intersect a 'wall'.
  681. //
  682. /////////////////////////////////////////////////////////////////////////
  683. bool
  684. VisPointGeneratorClass::Do_View_Planes_Pass (const Matrix3D &vis_transform)
  685. {
  686. bool retval = true;
  687. Vector3 center = vis_transform.Get_Translation ();
  688. Matrix3 orig_basis (vis_transform);
  689. //
  690. // Loop through and test each of the 6 orienations
  691. // of the view plane to make sure none of them intersect
  692. // a 'wall'.
  693. //
  694. /*CastResultStruct result;
  695. for (int index = 0; (index < VIS_RENDER_DIRECTIONS) && retval; index ++) {
  696. //
  697. // Build the orientation of the view-plane
  698. //
  699. Matrix3 basis = orig_basis;
  700. basis.Rotate_X (VIS_RENDER_TABLE[index].X);
  701. basis.Rotate_Y (VIS_RENDER_TABLE[index].Y);
  702. basis.Rotate_Z (VIS_RENDER_TABLE[index].Z);*/
  703. //
  704. // Create a box representing the view plane
  705. //
  706. //Vector3
  707. AABoxClass box (center, m_ViewPlaneExtent);
  708. //
  709. // Check to see if this viewplane 'box' collides
  710. // with anything.
  711. //
  712. //result.Reset ();
  713. CastResultStruct result;
  714. PhysAABoxCollisionTestClass collision_test (box, Vector3(0,0,0), &result, GAME_COLLISION_GROUP, COLLISION_TYPE_ALL);
  715. SceneEditorClass *scene = ::Get_Scene_Editor ();
  716. scene->Cast_AABox (collision_test);
  717. retval = (result.StartBad == false) && !(result.Fraction < 1.0F);
  718. //}
  719. return retval;
  720. }
  721. /////////////////////////////////////////////////////////////////////////
  722. //
  723. // Test_Camera_Sim_Point
  724. //
  725. /////////////////////////////////////////////////////////////////////////
  726. bool
  727. VisPointGeneratorClass::Test_Camera_Sim_Point
  728. (
  729. const Vector3 & start_point,
  730. const Vector3 & end_point,
  731. Matrix3D * transform_result,
  732. NodeClass ** node_result
  733. )
  734. {
  735. bool retval = false;
  736. AABoxClass box (start_point, m_ViewPlaneExtent);
  737. Vector3 sweep_vector = end_point - start_point;
  738. //
  739. // Check to see if the camera's 'box' collides with anything.
  740. //
  741. CastResultStruct result;
  742. PhysAABoxCollisionTestClass collision_test (box, sweep_vector, &result, GAME_COLLISION_GROUP, COLLISION_TYPE_ALL);
  743. SceneEditorClass *scene = ::Get_Scene_Editor ();
  744. scene->Cast_AABox (collision_test);
  745. //
  746. // If we collided with an object, then move the sample point in a little...
  747. //
  748. Vector3 test_point = end_point;
  749. if (result.Fraction < 1.0F) {
  750. test_point = start_point + (sweep_vector * result.Fraction * PERCENT_FROM_COLLISION);
  751. }
  752. //
  753. // Find the node this point lies over
  754. //
  755. NodeClass *node = Find_Floor_Node (test_point);
  756. //
  757. // If the point doesn't lie over the current node, then
  758. // we've successfully found a new point.
  759. //
  760. if ((node != m_CurrentNode) && (node != NULL)) {
  761. (*node_result) = node;
  762. (*transform_result).Make_Identity ();
  763. (*transform_result).Obj_Look_At (test_point, start_point, 0);
  764. retval = true;
  765. }
  766. return retval;
  767. }
  768. /////////////////////////////////////////////////////////////////////////
  769. //
  770. // Find_Floor_Node
  771. //
  772. /////////////////////////////////////////////////////////////////////////
  773. NodeClass *
  774. VisPointGeneratorClass::Find_Floor_Node (const Vector3 &start_point)
  775. {
  776. NodeClass *node = NULL;
  777. //
  778. // Build a ray from the given position down 1000 meters
  779. //
  780. Vector3 end_point = start_point + Vector3 (0, 0, FLOOR_CHECK_HEIGHT);
  781. LineSegClass ray (start_point, end_point);
  782. //
  783. // Cast the ray into the world and see what it hits.
  784. //
  785. CastResultStruct res;
  786. PhysRayCollisionTestClass raytest (ray, &res, GAME_COLLISION_GROUP, COLLISION_TYPE_ALL);
  787. ::Get_Scene_Editor ()->Cast_Ray (raytest);
  788. //
  789. // Did we hit anything?
  790. //
  791. if (res.Fraction < 1.0F) {
  792. // Get the physics object we hit
  793. PhysClass *physobj = raytest.CollidedPhysObj;
  794. if (physobj != NULL) {
  795. //
  796. // Check to see if the 'node' we hit is different then the node
  797. // we are currently processing.
  798. //
  799. HITTESTINFO *hittest = (HITTESTINFO *)physobj->Peek_Model ()->Get_User_Data ();
  800. if ( (hittest != NULL) &&
  801. (hittest->Type == HITTESTINFO::Node))
  802. {
  803. //
  804. // Pass the node we found back to the caller.
  805. //
  806. node = hittest->node;
  807. }
  808. }
  809. }
  810. return node;
  811. }
  812. /////////////////////////////////////////////////////////////////////////
  813. //
  814. // Is_Object_Invalid_Roof
  815. //
  816. // This method determines if it is valid for a vis-point to be generated
  817. // underneath a given object.
  818. //
  819. // An object is considered 'invalid' for a vis-point's 'roof' if:
  820. //
  821. // - The mesh's polygons are visible.
  822. // - The mesh's polygons are single-sided
  823. // - The mesh's polygons can be physically collideable.
  824. // - The mesh is marked for vis generation itself.
  825. //
  826. /////////////////////////////////////////////////////////////////////////
  827. bool
  828. VisPointGeneratorClass::Is_Object_Invalid_Roof (RenderObjClass *render_obj)
  829. {
  830. bool retval = true;
  831. //
  832. // Loop through all the render object's sub-objects
  833. //
  834. int count = render_obj->Get_Num_Sub_Objects ();
  835. for (int index = 0; (index < count) && retval; index ++) {
  836. //
  837. // Check this subobject
  838. //
  839. RenderObjClass *sub_object = render_obj->Get_Sub_Object (index);
  840. if (sub_object != NULL) {
  841. retval &= Is_Object_Invalid_Roof (sub_object);
  842. MEMBER_RELEASE (sub_object);
  843. }
  844. }
  845. //
  846. // Is this render object a mesh?
  847. //
  848. if (render_obj->Class_ID () == RenderObjClass::CLASSID_MESH) {
  849. MeshModelClass *model = ((MeshClass *)render_obj)->Get_Model ();
  850. if (model != NULL) {
  851. //
  852. // The mesh is invalid if:
  853. //
  854. // a) The mesh's polys are single-sided AND
  855. // b) The mesh is a vis-sector AND
  856. // c) The mesh is physically collideable AND
  857. // d) The mesh visible
  858. //
  859. retval &= (model->Get_Flag (MeshModelClass::TWO_SIDED) != MeshModelClass::TWO_SIDED);
  860. //retval &= ((render_obj->Get_Collision_Type () & COLLISION_TYPE_VIS) == COLLISION_TYPE_VIS);
  861. retval &= ((render_obj->Get_Collision_Type () & COLLISION_TYPE_PHYSICAL) == COLLISION_TYPE_PHYSICAL);
  862. retval &= (render_obj->Is_Not_Hidden_At_All () != 0);
  863. MEMBER_RELEASE (model);
  864. }
  865. }
  866. return retval;
  867. }
  868. /////////////////////////////////////////////////////////////////////////
  869. //
  870. // Initialize_Camera_Sim
  871. //
  872. /////////////////////////////////////////////////////////////////////////
  873. void
  874. VisPointGeneratorClass::Initialize_Camera_Sim (void)
  875. {
  876. SAFE_DELETE_ARRAY (m_pCameraSimOffsets);
  877. m_pCameraSimOffsets = new Vector3[m_CameraSimPointCount];
  878. for (int index = 0; index < m_CameraSimPointCount; index ++) {
  879. //
  880. // Calculate an arbitrary number of points around a circle
  881. // and store these 'offsets' in a vector array.
  882. //
  883. float angle = ((2 * 3.14159265359F) * index) / m_CameraSimPointCount;
  884. float x = CAMERA_SIM_RADIUS * ::cos (angle);
  885. float y = CAMERA_SIM_RADIUS * ::sin (angle);
  886. m_pCameraSimOffsets[index] = Vector3 (y, -x, 0);
  887. }
  888. return ;
  889. }
  890. /////////////////////////////////////////////////////////////////////////
  891. //
  892. // Generate_Camera_Locations
  893. //
  894. // This method generates extra phantom vis-points for a given position to
  895. // simulate areas the camera can be. These points are checked to see if
  896. // they are over a different mesh. If they are over a different mesh,
  897. // then the height information is used by the mesh when generating its
  898. // vis-points;
  899. //
  900. /////////////////////////////////////////////////////////////////////////
  901. void
  902. VisPointGeneratorClass::Generate_Camera_Locations (const Matrix3D &real_vis_point)
  903. {
  904. Vector3 position = real_vis_point.Get_Translation ();
  905. struct CAMERA_SIM_POINTS
  906. {
  907. Matrix3D vis_point;
  908. NodeClass * node;
  909. };
  910. CAMERA_SIM_POINTS *sim_points = new CAMERA_SIM_POINTS[m_CameraSimPointCount];
  911. //
  912. // Loop through each camera simulation point (they lie
  913. // in a circle around the position)
  914. //
  915. for (int index = 0; index < m_CameraSimPointCount; index ++) {
  916. //
  917. // Build a ray from the given position down 1000 meters
  918. //
  919. Vector3 end_point = position + m_pCameraSimOffsets[index];
  920. sim_points[index].vis_point.Make_Identity ();
  921. sim_points[index].node = NULL;
  922. //
  923. // Test the point to see if lies over another mesh
  924. //
  925. Test_Camera_Sim_Point ( position,
  926. end_point,
  927. &sim_points[index].vis_point,
  928. &sim_points[index].node);
  929. }
  930. //
  931. // Now submit the 'middle' point of each node series
  932. //
  933. int start_index = 0;
  934. NodeClass *current_node = NULL;
  935. for (index = 0; index < m_CameraSimPointCount; index ++) {
  936. //
  937. // Has the node changed?
  938. //
  939. if ((sim_points[index].node != current_node) ||
  940. (index == m_CameraSimPointCount - 1)) {
  941. if (index > 0) {
  942. //
  943. // Attempt to find the 'middle' of the series
  944. //
  945. int middle_index = start_index + ((index - start_index) >> 1);
  946. bool found = false;
  947. int max = ((index - start_index) >> 1) + 1;
  948. for (int offset = 0; (offset < max) && !found; offset ++) {
  949. if (((middle_index + offset) < index) &&
  950. (sim_points[middle_index + offset].node != NULL))
  951. {
  952. middle_index += offset;
  953. found = true;
  954. } else if (((middle_index - offset) >= start_index) &&
  955. (sim_points[middle_index - offset].node != NULL))
  956. {
  957. middle_index -= offset;
  958. found = true;
  959. }
  960. }
  961. //
  962. // Add the point to our list
  963. //
  964. if (found) {
  965. //
  966. // Convert the transform from an obj trasform to a camera transform
  967. //
  968. Matrix3D world_to_cam_tm (Vector3 (0, -1, 0), Vector3 (0, 0, 1), Vector3 (-1, 0, 0), Vector3 (0, 0, 0));
  969. Matrix3D camera_tm = sim_points[middle_index].vis_point * world_to_cam_tm;
  970. m_CurrentPointList->Add (camera_tm);
  971. m_TotalPoints ++;
  972. }
  973. }
  974. //
  975. // Start working on points over another node.
  976. //
  977. start_index = index;
  978. current_node = sim_points[index].node;
  979. }
  980. }
  981. // Free our 'sim-points' array
  982. delete [] sim_points;
  983. return ;
  984. }
  985. /////////////////////////////////////////////////////////////////////////
  986. //
  987. // Set_Current_Node
  988. //
  989. /////////////////////////////////////////////////////////////////////////
  990. void
  991. VisPointGeneratorClass::Set_Current_Node (NodeClass *node)
  992. {
  993. m_CurrentNode = node;
  994. return ;
  995. }
  996. /////////////////////////////////////////////////////////////////////////
  997. //
  998. // Post_Process_Nodes
  999. //
  1000. /////////////////////////////////////////////////////////////////////////
  1001. void
  1002. VisPointGeneratorClass::Post_Process_Nodes (void)
  1003. {
  1004. return ;
  1005. }
  1006. /////////////////////////////////////////////////////////////////////////
  1007. //
  1008. // Add_Manual_Nodes
  1009. //
  1010. /////////////////////////////////////////////////////////////////////////
  1011. void
  1012. VisPointGeneratorClass::Add_Manual_Nodes (void)
  1013. {
  1014. /*VisPointNodeClass *vis_point = NULL;
  1015. for ( vis_point = (VisPointNodeClass *)NodeMgrClass::Get_First (NODE_TYPE_VIS_POINT);
  1016. vis_point != NULL;
  1017. vis_point = (VisPointNodeClass *)NodeMgrClass::Get_Next (vis_point, NODE_TYPE_VIS_POINT))
  1018. {
  1019. //
  1020. // Convert the object transform to a camera transform
  1021. //
  1022. Matrix3D transform = vis_point->Get_Transform ();
  1023. Matrix3D cam_transform (Vector3 (0, -1, 0), Vector3 (0, 0, 1), Vector3 (-1, 0, 0), Vector3 (0, 0, 0));
  1024. Matrix3D new_transform = transform * cam_transform;
  1025. //
  1026. // Allocate a new point list for this point
  1027. //
  1028. m_CurrentPointList = new VisPointListClass;
  1029. m_CurrentPointList->transform = transform;
  1030. m_CurrentPointList->sample_point = vis_point->Get_Vis_Tile_Location ();
  1031. m_PointList.Add (m_CurrentPointList);
  1032. m_TotalPoints ++;
  1033. }*/
  1034. return ;
  1035. }