Ray.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /**
  2. * @author bhouston / http://exocortex.com
  3. */
  4. THREE.Ray = function ( origin, direction ) {
  5. this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
  6. this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
  7. };
  8. THREE.Ray.prototype = {
  9. constructor: THREE.Ray,
  10. set: function ( origin, direction ) {
  11. this.origin.copy( origin );
  12. this.direction.copy( direction );
  13. return this;
  14. },
  15. copy: function ( ray ) {
  16. this.origin.copy( ray.origin );
  17. this.direction.copy( ray.direction );
  18. return this;
  19. },
  20. at: function ( t, optionalTarget ) {
  21. var result = optionalTarget || new THREE.Vector3();
  22. return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
  23. },
  24. recast: function () {
  25. var v1 = new THREE.Vector3();
  26. return function ( t ) {
  27. this.origin.copy( this.at( t, v1 ) );
  28. return this;
  29. };
  30. }(),
  31. closestPointToPoint: function ( point, optionalTarget ) {
  32. var result = optionalTarget || new THREE.Vector3();
  33. result.subVectors( point, this.origin );
  34. var directionDistance = result.dot( this.direction );
  35. if ( directionDistance < 0 ) {
  36. return result.copy( this.origin );
  37. }
  38. return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  39. },
  40. distanceToPoint: function () {
  41. var v1 = new THREE.Vector3();
  42. return function ( point ) {
  43. var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
  44. // point behind the ray
  45. if ( directionDistance < 0 ) {
  46. return this.origin.distanceTo( point );
  47. }
  48. v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
  49. return v1.distanceTo( point );
  50. };
  51. }(),
  52. distanceSqToSegment: function( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
  53. // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
  54. // It returns the min distance between the ray and the segment
  55. // defined by v0 and v1
  56. // It can also set two optional targets :
  57. // - The closest point on the ray
  58. // - The closest point on the segment
  59. var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 );
  60. var segDir = v1.clone().sub( v0 ).normalize();
  61. var segExtent = v0.distanceTo( v1 ) * 0.5;
  62. var diff = this.origin.clone().sub( segCenter );
  63. var a01 = - this.direction.dot( segDir );
  64. var b0 = diff.dot( this.direction );
  65. var b1 = - diff.dot( segDir );
  66. var c = diff.lengthSq();
  67. var det = Math.abs( 1 - a01 * a01 );
  68. var s0, s1, sqrDist, extDet;
  69. if ( det >= 0 ) {
  70. // The ray and segment are not parallel.
  71. s0 = a01 * b1 - b0;
  72. s1 = a01 * b0 - b1;
  73. extDet = segExtent * det;
  74. if ( s0 >= 0 ) {
  75. if ( s1 >= - extDet ) {
  76. if ( s1 <= extDet ) {
  77. // region 0
  78. // Minimum at interior points of ray and segment.
  79. var invDet = 1 / det;
  80. s0 *= invDet;
  81. s1 *= invDet;
  82. sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
  83. } else {
  84. // region 1
  85. s1 = segExtent;
  86. s0 = Math.max( 0, - ( a01 * s1 + b0) );
  87. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  88. }
  89. } else {
  90. // region 5
  91. s1 = - segExtent;
  92. s0 = Math.max( 0, - ( a01 * s1 + b0) );
  93. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  94. }
  95. } else {
  96. if ( s1 <= - extDet) {
  97. // region 4
  98. s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
  99. s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
  100. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  101. } else if ( s1 <= extDet ) {
  102. // region 3
  103. s0 = 0;
  104. s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
  105. sqrDist = s1 * ( s1 + 2 * b1 ) + c;
  106. } else {
  107. // region 2
  108. s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
  109. s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
  110. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  111. }
  112. }
  113. } else {
  114. // Ray and segment are parallel.
  115. s1 = ( a01 > 0 ) ? - segExtent : segExtent;
  116. s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
  117. sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
  118. }
  119. if ( optionalPointOnRay ) {
  120. optionalPointOnRay.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) );
  121. }
  122. if ( optionalPointOnSegment ) {
  123. optionalPointOnSegment.copy( segDir.clone().multiplyScalar( s1 ).add( segCenter ) );
  124. }
  125. return sqrDist;
  126. },
  127. isIntersectionSphere: function ( sphere ) {
  128. return this.distanceToPoint( sphere.center ) <= sphere.radius;
  129. },
  130. intersectSphere: function ( sphere, optionalTarget ) {
  131. // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/
  132. var L = new THREE.Vector3();
  133. var radius = sphere.radius;
  134. var radius2 = radius * radius;
  135. L.subVectors( sphere.center, this.origin );
  136. var tca = L.dot( this.direction );
  137. if ( tca < 0 ) {
  138. return null;
  139. }
  140. var d2 = L.dot( L ) - tca * tca;
  141. if ( d2 > radius2 ) {
  142. return null;
  143. }
  144. var thc = Math.sqrt( radius2 - d2 );
  145. // t0 = first collision point entrance on front of sphere
  146. var t0 = tca - thc;
  147. // t1 = exit point on back of sphere.  Rarely needed, so it is commented out
  148. // var t1 = tca + thc;
  149. // Now return the THREE.Vector3() location (collision point) of this Ray,
  150. //   scaled by amount t0 along Ray.direction 
  151. // This collision point will always be located somewhere on the sphere
  152. return this.at( t0, optionalTarget );
  153. },
  154. isIntersectionPlane: function ( plane ) {
  155. // check if the ray lies on the plane first
  156. var distToPoint = plane.distanceToPoint( this.origin );
  157. if ( distToPoint === 0 ) {
  158. return true;
  159. }
  160. var denominator = plane.normal.dot( this.direction );
  161. if ( denominator * distToPoint < 0 ) {
  162. return true;
  163. }
  164. // ray origin is behind the plane (and is pointing behind it)
  165. return false;
  166. },
  167. distanceToPlane: function ( plane ) {
  168. var denominator = plane.normal.dot( this.direction );
  169. if ( denominator == 0 ) {
  170. // line is coplanar, return origin
  171. if( plane.distanceToPoint( this.origin ) == 0 ) {
  172. return 0;
  173. }
  174. // Null is preferable to undefined since undefined means.... it is undefined
  175. return null;
  176. }
  177. var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
  178. // Return if the ray never intersects the plane
  179. return t >= 0 ? t : null;
  180. },
  181. intersectPlane: function ( plane, optionalTarget ) {
  182. var t = this.distanceToPlane( plane );
  183. if ( t === null ) {
  184. return null;
  185. }
  186. return this.at( t, optionalTarget );
  187. },
  188. isIntersectionBox: function () {
  189. var v = new THREE.Vector3();
  190. return function ( box ) {
  191. return this.intersectBox( box, v ) !== null;
  192. };
  193. }(),
  194. intersectBox: function ( box , optionalTarget ) {
  195. // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
  196. var tmin,tmax,tymin,tymax,tzmin,tzmax;
  197. var invdirx = 1/this.direction.x,
  198. invdiry = 1/this.direction.y,
  199. invdirz = 1/this.direction.z;
  200. var origin = this.origin;
  201. if (invdirx >= 0) {
  202. tmin = (box.min.x - origin.x) * invdirx;
  203. tmax = (box.max.x - origin.x) * invdirx;
  204. } else {
  205. tmin = (box.max.x - origin.x) * invdirx;
  206. tmax = (box.min.x - origin.x) * invdirx;
  207. }
  208. if (invdiry >= 0) {
  209. tymin = (box.min.y - origin.y) * invdiry;
  210. tymax = (box.max.y - origin.y) * invdiry;
  211. } else {
  212. tymin = (box.max.y - origin.y) * invdiry;
  213. tymax = (box.min.y - origin.y) * invdiry;
  214. }
  215. if ((tmin > tymax) || (tymin > tmax)) return null;
  216. // These lines also handle the case where tmin or tmax is NaN
  217. // (result of 0 * Infinity). x !== x returns true if x is NaN
  218. if (tymin > tmin || tmin !== tmin ) tmin = tymin;
  219. if (tymax < tmax || tmax !== tmax ) tmax = tymax;
  220. if (invdirz >= 0) {
  221. tzmin = (box.min.z - origin.z) * invdirz;
  222. tzmax = (box.max.z - origin.z) * invdirz;
  223. } else {
  224. tzmin = (box.max.z - origin.z) * invdirz;
  225. tzmax = (box.min.z - origin.z) * invdirz;
  226. }
  227. if ((tmin > tzmax) || (tzmin > tmax)) return null;
  228. if (tzmin > tmin || tmin !== tmin ) tmin = tzmin;
  229. if (tzmax < tmax || tmax !== tmax ) tmax = tzmax;
  230. //return point closest to the ray (positive side)
  231. if ( tmax < 0 ) return null;
  232. return this.at( tmin >= 0 ? tmin : tmax, optionalTarget );
  233. },
  234. intersectTriangle: function() {
  235. // Compute the offset origin, edges, and normal.
  236. var diff = new THREE.Vector3();
  237. var edge1 = new THREE.Vector3();
  238. var edge2 = new THREE.Vector3();
  239. var normal = new THREE.Vector3();
  240. return function ( a, b, c, backfaceCulling, optionalTarget ) {
  241. // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
  242. edge1.subVectors( b, a );
  243. edge2.subVectors( c, a );
  244. normal.crossVectors( edge1, edge2 );
  245. // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
  246. // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
  247. // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
  248. // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
  249. // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
  250. var DdN = this.direction.dot( normal );
  251. var sign;
  252. if ( DdN > 0 ) {
  253. if ( backfaceCulling ) return null;
  254. sign = 1;
  255. } else if ( DdN < 0 ) {
  256. sign = - 1;
  257. DdN = - DdN;
  258. } else {
  259. return null;
  260. }
  261. diff.subVectors( this.origin, a );
  262. var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );
  263. // b1 < 0, no intersection
  264. if ( DdQxE2 < 0 ) {
  265. return null;
  266. }
  267. var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );
  268. // b2 < 0, no intersection
  269. if ( DdE1xQ < 0 ) {
  270. return null;
  271. }
  272. // b1+b2 > 1, no intersection
  273. if ( DdQxE2 + DdE1xQ > DdN ) {
  274. return null;
  275. }
  276. // Line intersects triangle, check if ray does.
  277. var QdN = - sign * diff.dot( normal );
  278. // t < 0, no intersection
  279. if ( QdN < 0 ) {
  280. return null;
  281. }
  282. // Ray intersects triangle.
  283. return this.at( QdN / DdN, optionalTarget );
  284. };
  285. }(),
  286. applyMatrix4: function ( matrix4 ) {
  287. this.direction.add( this.origin ).applyMatrix4( matrix4 );
  288. this.origin.applyMatrix4( matrix4 );
  289. this.direction.sub( this.origin );
  290. this.direction.normalize();
  291. return this;
  292. },
  293. equals: function ( ray ) {
  294. return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
  295. },
  296. clone: function () {
  297. return new THREE.Ray().copy( this );
  298. }
  299. };