normalcone.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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:: /G/WWMath/normalcone.h $*
  25. * *
  26. * $Author:: Eric_c $*
  27. * *
  28. * $Modtime:: 11/01/99 2:44p $*
  29. * *
  30. * $Revision:: 8 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. /*
  36. ** The NormalCone is a class used to represent a cone of unit length. It can be used to
  37. ** loosely represent a collection of other normals allowing backface culling to be
  38. ** performed at a heirarchial level rather than at a triangle level.
  39. **
  40. ** The term 'NormalCone' is a bit of a misnomer; it is really a circular portion of a sphere.
  41. ** -ehc
  42. */
  43. #ifndef NORMALCONE_H
  44. #define NORMALCONE_H
  45. #include "vector3.h"
  46. #include "matrix3.h"
  47. class NormalCone : public Vector3
  48. {
  49. public:
  50. NormalCone() {}
  51. NormalCone(const Vector3 & normal, float angle = 1.0f)
  52. : Vector3(normal),
  53. Angle(angle)
  54. {}
  55. void Set(const Vector3 & normal, float angle = 1.0f)
  56. {
  57. Vector3::Set(normal);
  58. Angle = angle;
  59. }
  60. void Set(const NormalCone & src)
  61. {
  62. Set(src, src.Angle);
  63. }
  64. // return true if this object has degenerated into a sphere.
  65. inline bool Complete_Sphere()
  66. {
  67. return (Angle - WWMATH_EPSILON) <= -1.0f;
  68. }
  69. // find the two vectors on the edge of the cone residing on the same plane as the input vector.
  70. // Note: these two Get_Coplanar functions return floats in an attempt to reduce float/int CPU state changes...
  71. inline float Get_Coplanar_Normals(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2) const;
  72. // find the two vectors on the edge of the cone residing on the same plane as the input vector and their dot products with the input.
  73. inline float Get_Coplanar_Normals_And_Dots(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2, float & dot1, float & dot2) const;
  74. // evaluate the input vector, expanding the angle of the cone and recalculating the
  75. // new center vector as needed.
  76. inline void Merge(const Vector3 & Input);
  77. // merge the input normal cone's coplanar normals with this object.
  78. inline void Merge(const NormalCone & Input);
  79. // this function returns the dot product between the input vector and the nearest coplanar normal
  80. // contained by the cone.
  81. // If the input vector is also contained by the cone, the result is always 1.0f.
  82. // Note that in the case of a complete sphere, the nearest coplanar normal will be pointing in
  83. // the opposite direction of the input vector.
  84. inline float Smallest_Dot_Product(const Vector3 & Input);
  85. // this value is the dot product of the edge of the cone and the center of the cone.
  86. // A value of 1.0f indicates that it is a degenerate cone which is basically a cone with radius zero.
  87. // A value of zero indicates the cone is actually hemisphere.
  88. // A value of -1.0f indicates that it is a complete sphere.
  89. float Angle;
  90. };
  91. // find the two vectors on the edge of the cone residing on the same plane as the input vector.
  92. inline float NormalCone::Get_Coplanar_Normals(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2) const
  93. {
  94. // get the cross product of the existing normal and the new one
  95. Vector3 cross;
  96. Vector3::Cross_Product(Input, *this, & cross);
  97. float length = cross.Length2();
  98. if(length < WWMATH_EPSILON)
  99. return 0.0f;
  100. length = sqrt(length);
  101. cross /= length;
  102. // Make a matrix3 which uses it as an axis of rotation and
  103. // rotate this about that axis twice, once +Angle, once -Angle.
  104. float radians = (1.0f - Angle) * WWMATH_PI * 0.5f;
  105. Matrix3 m1(cross, radians);
  106. Matrix3 m2(cross, -radians);
  107. Matrix3::Rotate_Vector(m1, *this, & Output1);
  108. Matrix3::Rotate_Vector(m2, *this, & Output2);
  109. return length;
  110. }
  111. inline float NormalCone::Get_Coplanar_Normals_And_Dots(const Vector3 & Input, Vector3 & Output1, Vector3 & Output2, float & Dot1, float & Dot2) const
  112. {
  113. float length = Get_Coplanar_Normals(Input, Output1, Output2);
  114. if(length < WWMATH_EPSILON)
  115. return 0.0f;
  116. // get the dot products of the new normal with the two coplanar normals and the current average
  117. Dot1 = Vector3::Dot_Product(Input, Output1);
  118. Dot2 = Vector3::Dot_Product(Input, Output2);
  119. return length;
  120. }
  121. // evaluate the input vector, expanding the angle of the cone and recalculating the
  122. // new center vector as needed.
  123. inline void NormalCone::Merge(const Vector3 & Input)
  124. {
  125. // early exit if this normal cone has already turned into a complete sphere.
  126. if(Complete_Sphere())
  127. return;
  128. // get the dot of the new vector with the current center vector
  129. float dot0 = Vector3::Dot_Product(Input, * this) + WWMATH_EPSILON;
  130. // if the dot value is greater than the existing cone angle, then the new vector fits
  131. // within the cone, so return.
  132. if(dot0 >= Angle)
  133. return;
  134. // get the two normals found in the cone which are coplanar to the one passed to this function.
  135. Vector3 normal1, normal2;
  136. float dot1, dot2;
  137. if(Get_Coplanar_Normals_And_Dots(Input, normal1, normal2, dot1, dot2) <= WWMATH_EPSILON)
  138. return;
  139. // test the case where the current average has a lower dot than either of the coplanar normals.
  140. // If true, this means that the object now represents a complete sphere with normals facing every
  141. // direction.
  142. if((dot0 < dot1) && (dot0 < dot2))
  143. {
  144. Angle = -1;
  145. return;
  146. }
  147. // the smaller of the dot values we have is going to indicate which of the two coplanar normals to use
  148. // for averaging into the new center normal.
  149. if(dot1 < dot2)
  150. Set(Input + normal1, dot1);
  151. else
  152. Set(Input + normal2, dot2);
  153. // if the angle is < 0, reverse the direction of the averaged normal since we have constructed
  154. // something more like a sphere with a cone shape taken out of it (a negative cone).
  155. if(Angle < WWMATH_EPSILON)
  156. *this *= -1;
  157. Normalize();
  158. }
  159. // merge the input normal cone's coplanar normals with this object.
  160. inline void NormalCone::Merge(const NormalCone & Input)
  161. {
  162. Vector3 n1, n2;
  163. if(Input.Get_Coplanar_Normals(*this, n1,n2) >= WWMATH_EPSILON)
  164. {
  165. Merge(n1);
  166. Merge(n2);
  167. }
  168. }
  169. // this function returns the dot product between the input vector and the nearest coplanar normal
  170. // contained by the cone.
  171. // If the input vector is also contained by the cone, the result is always 1.0f.
  172. // Note that in the case of a complete sphere, the nearest coplanar normal will be pointing in
  173. // the opposite direction of the input vector.
  174. inline float NormalCone::Smallest_Dot_Product(const Vector3 & Input)
  175. {
  176. if(Complete_Sphere())
  177. return -1.0f;
  178. // get the dot of the new vector with the current center vector
  179. float dot0 = Vector3::Dot_Product(Input, * this);
  180. // if the negative dot value is greater than the existing cone angle, then the new vector is
  181. // parallel to one of the vectors contained in the cone but in negative
  182. // direction, so return -1.0f
  183. if(-dot0 + WWMATH_EPSILON >= Angle)
  184. return -1.0f;
  185. // if the dot value is greater than the existing cone angle, then the new vector is
  186. // parallel to one of the vectors contained in the cone, so return 1.0f
  187. if(dot0 + WWMATH_EPSILON >= Angle)
  188. return 1.0f;
  189. // get the two normals found in the cone which are coplanar to the one passed to this function.
  190. Vector3 normal1, normal2;
  191. float dot1, dot2;
  192. Get_Coplanar_Normals_And_Dots(Input, normal1, normal2, dot1, dot2);
  193. // return the smaller of the two dot products
  194. if(dot1 < dot2)
  195. return dot1;
  196. return dot2;
  197. }
  198. #endif