Ray.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright 2006 Herre Kuijpers - <herre@xs4all.nl>
  2. //
  3. // This source file(s) may be redistributed, altered and customized
  4. // by any means PROVIDING the authors name and all copyright
  5. // notices remain intact.
  6. // THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  7. // EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
  8. // LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
  9. //-----------------------------------------------------------------------
  10. /*
  11. Copyright (c) 2014, Lars Brubaker
  12. All rights reserved.
  13. Redistribution and use in source and binary forms, with or without
  14. modification, are permitted provided that the following conditions are met:
  15. 1. Redistributions of source code must retain the above copyright notice, this
  16. list of conditions and the following disclaimer.
  17. 2. Redistributions in binary form must reproduce the above copyright notice,
  18. this list of conditions and the following disclaimer in the documentation
  19. and/or other materials provided with the distribution.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  21. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  22. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  24. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  25. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  26. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  27. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. The views and conclusions contained in the software and documentation are those
  31. of the authors and should not be interpreted as representing official policies,
  32. either expressed or implied, of the FreeBSD Project.
  33. */
  34. using System;
  35. namespace MatterHackers.VectorMath
  36. {
  37. [Flags]
  38. public enum IntersectionType { None = 0, FrontFace = 1, BackFace = 2, Both = FrontFace | BackFace };
  39. /// <summary>
  40. /// a virtual ray that is casted from a begin Position in a certain Direction.
  41. /// </summary>
  42. public class Ray
  43. {
  44. public static double sameSurfaceOffset = .00001;
  45. public Vector3 origin;
  46. public Vector3 directionNormal;
  47. public double minDistanceToConsider;
  48. public double maxDistanceToConsider;
  49. public Vector3 oneOverDirection;
  50. public bool isShadowRay;
  51. public IntersectionType intersectionType;
  52. public enum Sign { negative = 1, positive = 0 };
  53. public Sign[] sign = new Sign[3];
  54. public Ray(Vector3 origin, Vector3 directionNormal, double minDistanceToConsider = 0, double maxDistanceToConsider = double.PositiveInfinity, IntersectionType intersectionType = IntersectionType.FrontFace)
  55. {
  56. this.origin = origin;
  57. this.directionNormal = directionNormal;
  58. this.minDistanceToConsider = minDistanceToConsider;
  59. this.maxDistanceToConsider = maxDistanceToConsider;
  60. this.intersectionType = intersectionType;
  61. oneOverDirection = 1 / directionNormal;
  62. sign[0] = (oneOverDirection.X < 0) ? Sign.negative : Sign.positive;
  63. sign[1] = (oneOverDirection.Y < 0) ? Sign.negative : Sign.positive;
  64. sign[2] = (oneOverDirection.Z < 0) ? Sign.negative : Sign.positive;
  65. }
  66. public Ray(Ray rayToCopy)
  67. {
  68. origin = rayToCopy.origin;
  69. directionNormal = rayToCopy.directionNormal;
  70. minDistanceToConsider = rayToCopy.minDistanceToConsider;
  71. maxDistanceToConsider = rayToCopy.maxDistanceToConsider;
  72. oneOverDirection = rayToCopy.oneOverDirection;
  73. isShadowRay = rayToCopy.isShadowRay;
  74. intersectionType = rayToCopy.intersectionType;
  75. sign[0] = rayToCopy.sign[0];
  76. sign[1] = rayToCopy.sign[1];
  77. sign[2] = rayToCopy.sign[2];
  78. }
  79. public static Ray Transform(Ray ray, Matrix4X4 matrix)
  80. {
  81. Vector3 transformedOrigin = Vector3Ex.TransformPosition(ray.origin, matrix);
  82. Vector3 transformedDirecton = Vector3Ex.TransformVector(ray.directionNormal, matrix);
  83. return new Ray(transformedOrigin, transformedDirecton, ray.minDistanceToConsider, ray.maxDistanceToConsider, ray.intersectionType);
  84. }
  85. public bool Intersect(AxisAlignedBoundingBox bounds, out double minDistFound, out double maxDistFound, out int minAxis, out int maxAxis)
  86. {
  87. minAxis = 0;
  88. maxAxis = 0;
  89. // we calculate distance to the intersection with the x planes of the box
  90. minDistFound = (bounds[(int)this.sign[0]].X - this.origin.X) * this.oneOverDirection.X;
  91. maxDistFound = (bounds[1 - (int)this.sign[0]].X - this.origin.X) * this.oneOverDirection.X;
  92. // now find the distance to the y planes of the box
  93. double minDistToY = (bounds[(int)this.sign[1]].Y - this.origin.Y) * this.oneOverDirection.Y;
  94. double maxDistToY = (bounds[1 - (int)this.sign[1]].Y - this.origin.Y) * this.oneOverDirection.Y;
  95. if ((minDistFound > maxDistToY) || (minDistToY > maxDistFound))
  96. {
  97. return false;
  98. }
  99. if (minDistToY > minDistFound)
  100. {
  101. minAxis = 1;
  102. minDistFound = minDistToY;
  103. }
  104. if (maxDistToY < maxDistFound)
  105. {
  106. maxAxis = 1;
  107. maxDistFound = maxDistToY;
  108. }
  109. // and finally the z planes
  110. double minDistToZ = (bounds[(int)this.sign[2]].Z - this.origin.Z) * this.oneOverDirection.Z;
  111. double maxDistToZ = (bounds[1 - (int)this.sign[2]].Z - this.origin.Z) * this.oneOverDirection.Z;
  112. if ((minDistFound > maxDistToZ) || (minDistToZ > maxDistFound))
  113. {
  114. return false;
  115. }
  116. if (minDistToZ > minDistFound)
  117. {
  118. minAxis = 2;
  119. minDistFound = minDistToZ;
  120. }
  121. if (maxDistToZ < maxDistFound)
  122. {
  123. maxAxis = 2;
  124. maxDistFound = maxDistToZ;
  125. }
  126. bool oneHitIsWithinLimits = (minDistFound < this.maxDistanceToConsider && minDistFound > this.minDistanceToConsider)
  127. || (maxDistFound < this.maxDistanceToConsider && maxDistFound > this.minDistanceToConsider);
  128. return oneHitIsWithinLimits;
  129. }
  130. public RayHitInfo GetClosestIntersection(AxisAlignedBoundingBox bounds)
  131. {
  132. RayHitInfo info = new RayHitInfo();
  133. double minDistFound;
  134. double maxDistFound;
  135. int minAxis;
  136. int maxAxis;
  137. if (Intersect(bounds, out minDistFound, out maxDistFound, out minAxis, out maxAxis))
  138. {
  139. if (this.intersectionType == IntersectionType.FrontFace)
  140. {
  141. if (minDistFound > this.minDistanceToConsider && minDistFound < this.maxDistanceToConsider)
  142. {
  143. info.HitType = IntersectionType.FrontFace;
  144. if (this.isShadowRay)
  145. {
  146. return info;
  147. }
  148. info.ClosestHitObject = bounds;
  149. info.HitPosition = this.origin + this.directionNormal * minDistFound;
  150. Vector3 normalAtHit = default(Vector3);
  151. normalAtHit[minAxis] = this.sign[minAxis] == Ray.Sign.negative ? 1 : -1; // you hit the side that is opposite your sign
  152. info.NormalAtHit = normalAtHit;
  153. info.DistanceToHit = minDistFound;
  154. }
  155. }
  156. else // check back faces
  157. {
  158. if (maxDistFound > this.minDistanceToConsider && maxDistFound < this.maxDistanceToConsider)
  159. {
  160. info.HitType = IntersectionType.BackFace;
  161. if (this.isShadowRay)
  162. {
  163. return info;
  164. }
  165. info.ClosestHitObject = bounds;
  166. info.HitPosition = this.origin + this.directionNormal * maxDistFound;
  167. Vector3 normalAtHit = default(Vector3);
  168. normalAtHit[minAxis] = this.sign[minAxis] == Ray.Sign.negative ? 1 : -1; // you hit the side that is opposite your sign
  169. info.NormalAtHit = normalAtHit;
  170. info.DistanceToHit = maxDistFound;
  171. }
  172. }
  173. }
  174. return info;
  175. }
  176. public bool Intersection(AxisAlignedBoundingBox bounds)
  177. {
  178. Ray ray = this;
  179. // we calculate distance to the intersection with the x planes of the box
  180. double minDistFound = (bounds[(int)ray.sign[0]].X - ray.origin.X) * ray.oneOverDirection.X;
  181. double maxDistFound = (bounds[1 - (int)ray.sign[0]].X - ray.origin.X) * ray.oneOverDirection.X;
  182. // now find the distance to the y planes of the box
  183. double minDistToY = (bounds[(int)ray.sign[1]].Y - ray.origin.Y) * ray.oneOverDirection.Y;
  184. double maxDistToY = (bounds[1 - (int)ray.sign[1]].Y - ray.origin.Y) * ray.oneOverDirection.Y;
  185. if ((minDistFound > maxDistToY) || (minDistToY > maxDistFound))
  186. {
  187. return false;
  188. }
  189. if (minDistToY > minDistFound)
  190. {
  191. minDistFound = minDistToY;
  192. }
  193. if (maxDistToY < maxDistFound)
  194. {
  195. maxDistFound = maxDistToY;
  196. }
  197. // and finally the z planes
  198. double minDistToZ = (bounds[(int)ray.sign[2]].Z - ray.origin.Z) * ray.oneOverDirection.Z;
  199. double maxDistToZ = (bounds[1 - (int)ray.sign[2]].Z - ray.origin.Z) * ray.oneOverDirection.Z;
  200. if ((minDistFound > maxDistToZ) || (minDistToZ > maxDistFound))
  201. {
  202. return false;
  203. }
  204. if (minDistToZ > minDistFound)
  205. {
  206. minDistFound = minDistToZ;
  207. }
  208. if (maxDistToZ < maxDistFound)
  209. {
  210. maxDistFound = maxDistToZ;
  211. }
  212. bool withinDistanceToConsider = (minDistFound < ray.maxDistanceToConsider) && (maxDistFound > ray.minDistanceToConsider);
  213. return withinDistanceToConsider;
  214. }
  215. public void PerturbDirection(Random random)
  216. {
  217. directionNormal.X += 1e-5 * random.NextDouble();
  218. directionNormal.Y += 1e-5 * random.NextDouble();
  219. directionNormal.Z += 1e-5 * random.NextDouble();
  220. }
  221. }
  222. }