intersec.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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 : G *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/intersec.cpp $*
  25. * *
  26. * $Author:: Greg_h $*
  27. * *
  28. * $Modtime:: 2/06/01 5:41p $*
  29. * *
  30. * $Revision:: 3 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "intersec.h"
  36. #include "camera.h"
  37. #include "scene.h"
  38. #include "intersec.inl"
  39. #ifdef _DEBUG
  40. #undef THIS_FILE
  41. static char THIS_FILE[]=__FILE__;
  42. #define new DEBUG_NEW
  43. #endif
  44. //////////////////////////////////////////////////////////////////////
  45. // Construction/Destruction
  46. //////////////////////////////////////////////////////////////////////
  47. // these statics are used for single-threaded use of the IntersectionClass ONLY
  48. Vector3 IntersectionClass::_RayLocation(0,0,0);
  49. Vector3 IntersectionClass::_RayDirection(0,0,0);
  50. Vector3 IntersectionClass::_IntersectionNormal(0,0,0);
  51. bool IntersectionClass::Intersect_Screen_Point_RenderObject(float screen_x, float screen_y, const LayerClass &Layer, RenderObjClass *RObj, IntersectionResultClass *FinalResult)
  52. {
  53. Get_Screen_Ray(screen_x, screen_y, Layer);
  54. return Intersect_RenderObject(RObj, FinalResult);
  55. }
  56. bool IntersectionClass::Intersect_RenderObject(RenderObjClass *RObj, IntersectionResultClass *FinalResult)
  57. {
  58. if(FinalResult == 0)
  59. FinalResult = &Result;
  60. return RObj->Intersect(this, FinalResult);
  61. }
  62. // iterate through the layers of a world, front to back, returning true if/when an intersection
  63. // with an object occurs.
  64. bool IntersectionClass::Intersect_Screen_Point_Layer_Range
  65. (
  66. float screen_x,
  67. float screen_y,
  68. const LayerClass &TopLayer,
  69. const LayerClass &BackLayer
  70. )
  71. {
  72. // intersect from front layer to back layers. An intersection with an object
  73. // in any layer is assumed to be in front of any potential intersections in layers
  74. // below it.
  75. // find the last layer in the list
  76. const LayerClass *Layer = &TopLayer;
  77. // iterate through all layers in list
  78. while(Layer->Is_Valid()) {
  79. if(Intersect_Screen_Point_Layer(screen_x, screen_y, *Layer))
  80. return true;
  81. // if this is the back layer then that is all we need to test
  82. if(Layer == &BackLayer)
  83. return false;
  84. Layer = Layer->Next();
  85. }
  86. return false;
  87. }
  88. bool IntersectionClass::Intersect_Screen_Point_Layer(float screen_x, float screen_y, const LayerClass &Layer)
  89. {
  90. // mark this object as not intersecting yet
  91. Result.Intersects = false;
  92. // first, do a test to make sure the screen coords are within the rendering area for this layer.
  93. const ViewportClass &v = Layer.Camera->Get_Viewport();
  94. if((screen_x < v.Min.X) ||
  95. (screen_x > v.Max.X) ||
  96. (screen_y < v.Min.Y) ||
  97. (screen_y > v.Max.Y))
  98. return false;
  99. Result.Range = Layer.Camera->Get_Depth(); //scene->depth * scene->zstop;
  100. // get the ray for these screen coordinates
  101. Get_Screen_Ray(screen_x, screen_y, Layer);
  102. return Intersect_Layer(Layer, false);
  103. }
  104. bool IntersectionClass::Intersect_Layer(const LayerClass &Layer, bool Test_All)
  105. {
  106. IntersectionResultClass FinalResult;
  107. Result.Intersects = false;
  108. SceneIterator *it = Layer.Scene->Create_Iterator(!Test_All);
  109. // select the first object
  110. it->First();
  111. // loop through all render objects in this layer:
  112. while(!it->Is_Done()) {
  113. // get the render object
  114. RenderObjClass * robj = it->Current_Item();
  115. it->Next();
  116. // only intersect if it was visible or if we must test all in layer
  117. if((Test_All || robj->Is_Really_Visible()) && robj->Intersect(this, &FinalResult)) {
  118. if(FinalResult.Range < Result.Range) {
  119. Copy_Results(&FinalResult);
  120. }
  121. }
  122. }
  123. Layer.Scene->Destroy_Iterator(it);
  124. return Result.Intersects;
  125. }
  126. void IntersectionClass::Append_Object_Array(
  127. int MaxCount,
  128. int &CurrentCount,
  129. RenderObjClass **ObjectArray,
  130. RenderObjClass *Object)
  131. {
  132. if(CurrentCount < MaxCount) {
  133. ObjectArray[CurrentCount] = Object;
  134. CurrentCount++;
  135. return;
  136. }
  137. WWDEBUG_SAY(("IntersectionClass::Append_Object_Array - Too many objects\n"));
  138. }
  139. // determines if specified plane-intersection point (co-planar with polygon) is within the the passed polygon.
  140. // If Interpolated_Normal is specified, it will interpolate the normal for the intersection point
  141. // note: Polygon normal MUST BE CORRECT
  142. // this will return true if the ray intersects the specified box
  143. // sets the point of intersection within the Request->Result.Intersection vector
  144. bool IntersectionClass::Intersect_Box(Vector3 &Box_Min, Vector3 &Box_Max, IntersectionResultClass *FinalResult) {
  145. // Fast Ray-Box Intersection, modified from code written by Andrew Woo from "Graphics Gems", Academic Press, 1990
  146. enum {
  147. RIGHT = 0,
  148. LEFT,
  149. MIDDLE,
  150. PLANE_COUNT
  151. };
  152. bool inside = true;
  153. char quadrant[PLANE_COUNT];
  154. int counter;
  155. float distance[PLANE_COUNT];
  156. float candidate_plane[PLANE_COUNT];
  157. register Vector3 *intersection = &FinalResult->Intersection;
  158. // Find candidate planes and determine if the ray is outside the box
  159. for (counter = 0; counter < PLANE_COUNT; counter++) {
  160. if((*RayLocation)[counter] < Box_Min[counter]) {
  161. quadrant[counter] = LEFT;
  162. candidate_plane[counter] = Box_Min[counter];
  163. inside = false;
  164. } else {
  165. if ((*RayLocation)[counter] > Box_Max[counter]) {
  166. quadrant[counter] = RIGHT;
  167. candidate_plane[counter] = Box_Max[counter];
  168. inside = false;
  169. } else {
  170. quadrant[counter] = MIDDLE;
  171. }
  172. }
  173. }
  174. // check to see if the ray origin is inside bounding box
  175. if(inside) {
  176. *intersection = *RayLocation;
  177. return FinalResult->Intersects = true;
  178. }
  179. // Calculate distances to candidate planes
  180. for (counter = 0; counter < PLANE_COUNT; counter++) {
  181. if ((quadrant[counter] != MIDDLE) && ((*RayDirection)[counter] != 0.0f))
  182. distance[counter] = (candidate_plane[counter] - (*RayLocation)[counter]) / (*RayDirection)[counter];
  183. else
  184. distance[counter] = -1.0f;
  185. }
  186. // get the largest of the distances for final choice of intersection
  187. int nearest_plane = 0;
  188. for (counter = 1; counter < PLANE_COUNT; counter++) {
  189. if (distance[nearest_plane] < distance[counter])
  190. nearest_plane = counter;
  191. }
  192. // Check to make sure the nearest plane is not behind the ray (inside box tested above)
  193. if (distance[nearest_plane] < 0.0f)
  194. return FinalResult->Intersects = false;
  195. for (counter = 0; counter < PLANE_COUNT; counter++) {
  196. if (nearest_plane != counter) {
  197. (*intersection)[counter] = (*RayLocation)[counter] + distance[nearest_plane] *(*RayDirection)[counter];
  198. if ((*intersection)[counter] < Box_Min[counter] || (*intersection)[counter] > Box_Max[counter])
  199. return FinalResult->Intersects = false;
  200. } else {
  201. (*intersection)[counter] = candidate_plane[counter];
  202. }
  203. }
  204. return FinalResult->Intersects = true; // ray hits box
  205. }
  206. // simply returns true if a ray hits the bounding sphere of any node in a hierarchy
  207. // note: Result will only contain range, not the intersection point/normal.
  208. bool IntersectionClass::Intersect_Hierarchy_Sphere_Quick(RenderObjClass *Hierarchy, IntersectionResultClass *FinalResult)
  209. {
  210. int counter = Hierarchy->Get_Num_Sub_Objects();
  211. while(counter--) {
  212. RenderObjClass *obj = Hierarchy->Get_Sub_Object(counter);
  213. obj->Release_Ref(); // you already own a reference to this object indirectly..
  214. if(obj->Intersect_Sphere_Quick(this, FinalResult))
  215. return true;
  216. }
  217. return false;
  218. }
  219. // returns true if a ray hits the bounding sphere of any node in a hierarchy
  220. // note: Result will contain range and the intersection point/normal.
  221. bool IntersectionClass::Intersect_Hierarchy_Sphere(RenderObjClass *Hierarchy, IntersectionResultClass *FinalResult) {
  222. int counter = Hierarchy->Get_Num_Sub_Objects();
  223. while(counter--) {
  224. RenderObjClass *obj = Hierarchy->Get_Sub_Object(counter);
  225. obj->Release_Ref(); // you already own a reference to this object indirectly..
  226. if(obj->Intersect_Sphere(this, FinalResult))
  227. return true;
  228. }
  229. return false;
  230. }
  231. void IntersectionClass::Append_Hierarchy_Objects(
  232. int MaxCount,
  233. int &CurrentCount,
  234. RenderObjClass **ObjectArray,
  235. RenderObjClass *Hierarchy,
  236. bool Test_Bounding_Sphere,
  237. bool Convex)
  238. {
  239. IntersectionResultClass result;
  240. // first check the bounding spheres for hits (if specified)
  241. int counter = Hierarchy->Get_Num_Sub_Objects();
  242. if(Test_Bounding_Sphere) {
  243. while(counter--) {
  244. RenderObjClass *obj = Hierarchy->Get_Sub_Object(counter);
  245. obj->Release_Ref(); // you already own a reference to the object indirectly
  246. if(obj->Intersect_Sphere_Quick(this, &result)) {
  247. Append_Object_Array(MaxCount, CurrentCount, ObjectArray, obj);
  248. // OutputDebugString("o"); // this shows one o per sphere intersection
  249. } else {
  250. // OutputDebugString("."); // this shows one . per sphere miss
  251. }
  252. }
  253. } else {
  254. // simply copy the pointers into the array
  255. while(counter--) {
  256. RenderObjClass *obj = Hierarchy->Get_Sub_Object(counter);
  257. Append_Object_Array(MaxCount, CurrentCount, ObjectArray, obj);
  258. obj->Release_Ref(); // you already own a reference to this object indirectly..
  259. }
  260. }
  261. }
  262. bool IntersectionClass::Intersect_Hierarchy(RenderObjClass *Hierarchy, IntersectionResultClass *FinalResult, bool Test_Bounding_Sphere, bool Convex ) {
  263. // OutputDebugString("\n");
  264. // return FinalResult->Intersects = false;
  265. RenderObjClass *candidate_objects[MAX_HIERARCHY_NODE_COUNT];
  266. int candidate_count = 0;
  267. Append_Hierarchy_Objects(MAX_HIERARCHY_NODE_COUNT, candidate_count, candidate_objects, Hierarchy, Test_Bounding_Sphere, Convex);
  268. // make sure there's at least one sphere hit before continuing to more expensive tests below..
  269. if(candidate_count == 0) {
  270. // OutputDebugString("/"); // no sphere intersections
  271. return FinalResult->Intersects = false;
  272. }
  273. // note: Test_Bounding_Sphere argument is false because the Append_Hierarchy_Objects will have
  274. // already performed that test if indicated.
  275. if(Intersect_Object_Array(candidate_count, candidate_objects, FinalResult, false, Convex)) {
  276. return true;
  277. }
  278. return false;
  279. }
  280. RenderObjClass *IntersectionClass::Intersect_Sub_Object(float screenx, float screeny, LayerClass &layer, RenderObjClass *robj, IntersectionResultClass *result)
  281. {
  282. if (robj->Get_Num_Sub_Objects()) {
  283. for (int lp = 0; lp < robj->Get_Num_Sub_Objects(); lp++) {
  284. RenderObjClass *sub = robj->Get_Sub_Object(lp);
  285. RenderObjClass *retval = Intersect_Sub_Object(screenx, screeny, layer, sub, result);
  286. sub->Release_Ref();
  287. if (retval) return retval;
  288. }
  289. }
  290. if (Intersect_Screen_Point_RenderObject(screenx, screeny, layer, robj, result)) {
  291. return robj;
  292. }
  293. return NULL;
  294. }
  295. // finds the intersection of the nearest object in the array.
  296. // This will usually be the last stage after determining potential intersections
  297. // using Intersect_Sphere_Quick() and adding hits to the array for this
  298. // more accurate test, as done in Intersect_Heirarchy().
  299. bool IntersectionClass::Intersect_Object_Array(
  300. int Object_Count,
  301. RenderObjClass **ObjectArray,
  302. IntersectionResultClass *FinalResult,
  303. bool Test_Bounding_Sphere,
  304. bool Convex
  305. )
  306. {
  307. IntersectionResultClass TemporaryResults[MAX_HIERARCHY_NODE_COUNT];
  308. assert(Object_Count <= MAX_HIERARCHY_NODE_COUNT);
  309. return Intersect_Object_Array(Object_Count, ObjectArray, FinalResult, TemporaryResults, Test_Bounding_Sphere, Convex);
  310. }
  311. bool IntersectionClass::Intersect_Object_Array(
  312. int Object_Count,
  313. RenderObjClass **ObjectArray,
  314. IntersectionResultClass *FinalResult,
  315. IntersectionResultClass *TemporaryResults,
  316. bool Test_Bounding_Sphere,
  317. bool Convex
  318. )
  319. {
  320. // Determine ranges for all intersections
  321. IntersectionClass temp(this);
  322. int counter = Object_Count;
  323. bool hit = false;
  324. // if it's a convex hierarchy (ie a control panel) then find the first hit otherwise use the more expensive exact intersection routine
  325. // for use with potentially concave hierarchies.
  326. int nearest_index = -1;
  327. if(ConvexTest || Convex) {
  328. if(Test_Bounding_Sphere) {
  329. while(counter--) {
  330. if(ObjectArray[counter]->Intersect_Sphere_Quick(this, &TemporaryResults[counter])) {
  331. hit = ObjectArray[counter]->Intersect(this, FinalResult);
  332. }
  333. if(hit) {
  334. nearest_index = counter;
  335. counter = 0;
  336. }
  337. }
  338. } else {
  339. while(counter--) {
  340. hit = ObjectArray[counter]->Intersect(this, FinalResult);
  341. if(hit) {
  342. nearest_index = counter;
  343. counter = 0;
  344. }
  345. }
  346. }
  347. } else {
  348. if(Test_Bounding_Sphere) {
  349. while(counter--) {
  350. if(ObjectArray[counter]->Intersect_Sphere_Quick(this, &TemporaryResults[counter])) {
  351. hit |= ObjectArray[counter]->Intersect(this, &TemporaryResults[counter]);
  352. }
  353. }
  354. } else {
  355. while(counter--) {
  356. hit |= ObjectArray[counter]->Intersect(this, &TemporaryResults[counter]);
  357. }
  358. }
  359. }
  360. // test to see if anything actually hit a mesh
  361. if( ! hit ) {
  362. // OutputDebugString("!"); // no mesh intersections
  363. return FinalResult->Intersects = false;
  364. }
  365. if(! (Convex || ConvexTest)) {
  366. // now find the nearest of the actual hits
  367. float nearest_range = (float) (2<<28);
  368. counter = Object_Count;
  369. while(counter--) {
  370. if(TemporaryResults[counter].Intersects && (nearest_range > TemporaryResults[counter].Range)) {
  371. nearest_index = counter;
  372. nearest_range = TemporaryResults[counter].Range;
  373. }
  374. }
  375. Copy_Results(FinalResult, &TemporaryResults[nearest_index]);
  376. }
  377. // OutputDebugString("+");
  378. // Debug.Print("Mesh ", Object_Array[nearest_index]);
  379. // Intersection_Node = candidate_indices[nearest_index];
  380. return true;
  381. }