1 | /**
|
---|
2 | * @author bhouston / http://exocortex.com
|
---|
3 | */
|
---|
4 |
|
---|
5 | THREE.Ray = function ( origin, direction ) {
|
---|
6 |
|
---|
7 | this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
|
---|
8 | this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
|
---|
9 |
|
---|
10 | };
|
---|
11 |
|
---|
12 | THREE.Ray.prototype = {
|
---|
13 |
|
---|
14 | constructor: THREE.Ray,
|
---|
15 |
|
---|
16 | set: function ( origin, direction ) {
|
---|
17 |
|
---|
18 | this.origin.copy( origin );
|
---|
19 | this.direction.copy( direction );
|
---|
20 |
|
---|
21 | return this;
|
---|
22 |
|
---|
23 | },
|
---|
24 |
|
---|
25 | copy: function ( ray ) {
|
---|
26 |
|
---|
27 | this.origin.copy( ray.origin );
|
---|
28 | this.direction.copy( ray.direction );
|
---|
29 |
|
---|
30 | return this;
|
---|
31 |
|
---|
32 | },
|
---|
33 |
|
---|
34 | at: function ( t, optionalTarget ) {
|
---|
35 |
|
---|
36 | var result = optionalTarget || new THREE.Vector3();
|
---|
37 |
|
---|
38 | return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
|
---|
39 |
|
---|
40 | },
|
---|
41 |
|
---|
42 | recast: function () {
|
---|
43 |
|
---|
44 | var v1 = new THREE.Vector3();
|
---|
45 |
|
---|
46 | return function ( t ) {
|
---|
47 |
|
---|
48 | this.origin.copy( this.at( t, v1 ) );
|
---|
49 |
|
---|
50 | return this;
|
---|
51 |
|
---|
52 | };
|
---|
53 |
|
---|
54 | }(),
|
---|
55 |
|
---|
56 | closestPointToPoint: function ( point, optionalTarget ) {
|
---|
57 |
|
---|
58 | var result = optionalTarget || new THREE.Vector3();
|
---|
59 | result.subVectors( point, this.origin );
|
---|
60 | var directionDistance = result.dot( this.direction );
|
---|
61 |
|
---|
62 | if ( directionDistance < 0 ) {
|
---|
63 |
|
---|
64 | return result.copy( this.origin );
|
---|
65 |
|
---|
66 | }
|
---|
67 |
|
---|
68 | return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
|
---|
69 |
|
---|
70 | },
|
---|
71 |
|
---|
72 | distanceToPoint: function () {
|
---|
73 |
|
---|
74 | var v1 = new THREE.Vector3();
|
---|
75 |
|
---|
76 | return function ( point ) {
|
---|
77 |
|
---|
78 | var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
|
---|
79 |
|
---|
80 | // point behind the ray
|
---|
81 |
|
---|
82 | if ( directionDistance < 0 ) {
|
---|
83 |
|
---|
84 | return this.origin.distanceTo( point );
|
---|
85 |
|
---|
86 | }
|
---|
87 |
|
---|
88 | v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
|
---|
89 |
|
---|
90 | return v1.distanceTo( point );
|
---|
91 |
|
---|
92 | };
|
---|
93 |
|
---|
94 | }(),
|
---|
95 |
|
---|
96 | distanceSqToSegment: function( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
|
---|
97 |
|
---|
98 | // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
|
---|
99 | // It returns the min distance between the ray and the segment
|
---|
100 | // defined by v0 and v1
|
---|
101 | // It can also set two optional targets :
|
---|
102 | // - The closest point on the ray
|
---|
103 | // - The closest point on the segment
|
---|
104 |
|
---|
105 | var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 );
|
---|
106 | var segDir = v1.clone().sub( v0 ).normalize();
|
---|
107 | var segExtent = v0.distanceTo( v1 ) * 0.5;
|
---|
108 | var diff = this.origin.clone().sub( segCenter );
|
---|
109 | var a01 = - this.direction.dot( segDir );
|
---|
110 | var b0 = diff.dot( this.direction );
|
---|
111 | var b1 = - diff.dot( segDir );
|
---|
112 | var c = diff.lengthSq();
|
---|
113 | var det = Math.abs( 1 - a01 * a01 );
|
---|
114 | var s0, s1, sqrDist, extDet;
|
---|
115 |
|
---|
116 | if ( det >= 0 ) {
|
---|
117 |
|
---|
118 | // The ray and segment are not parallel.
|
---|
119 |
|
---|
120 | s0 = a01 * b1 - b0;
|
---|
121 | s1 = a01 * b0 - b1;
|
---|
122 | extDet = segExtent * det;
|
---|
123 |
|
---|
124 | if ( s0 >= 0 ) {
|
---|
125 |
|
---|
126 | if ( s1 >= - extDet ) {
|
---|
127 |
|
---|
128 | if ( s1 <= extDet ) {
|
---|
129 |
|
---|
130 | // region 0
|
---|
131 | // Minimum at interior points of ray and segment.
|
---|
132 |
|
---|
133 | var invDet = 1 / det;
|
---|
134 | s0 *= invDet;
|
---|
135 | s1 *= invDet;
|
---|
136 | sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
|
---|
137 |
|
---|
138 | } else {
|
---|
139 |
|
---|
140 | // region 1
|
---|
141 |
|
---|
142 | s1 = segExtent;
|
---|
143 | s0 = Math.max( 0, - ( a01 * s1 + b0) );
|
---|
144 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
---|
145 |
|
---|
146 | }
|
---|
147 |
|
---|
148 | } else {
|
---|
149 |
|
---|
150 | // region 5
|
---|
151 |
|
---|
152 | s1 = - segExtent;
|
---|
153 | s0 = Math.max( 0, - ( a01 * s1 + b0) );
|
---|
154 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
---|
155 |
|
---|
156 | }
|
---|
157 |
|
---|
158 | } else {
|
---|
159 |
|
---|
160 | if ( s1 <= - extDet) {
|
---|
161 |
|
---|
162 | // region 4
|
---|
163 |
|
---|
164 | s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
|
---|
165 | s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
|
---|
166 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
---|
167 |
|
---|
168 | } else if ( s1 <= extDet ) {
|
---|
169 |
|
---|
170 | // region 3
|
---|
171 |
|
---|
172 | s0 = 0;
|
---|
173 | s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
|
---|
174 | sqrDist = s1 * ( s1 + 2 * b1 ) + c;
|
---|
175 |
|
---|
176 | } else {
|
---|
177 |
|
---|
178 | // region 2
|
---|
179 |
|
---|
180 | s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
|
---|
181 | s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
|
---|
182 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
---|
183 |
|
---|
184 | }
|
---|
185 |
|
---|
186 | }
|
---|
187 |
|
---|
188 | } else {
|
---|
189 |
|
---|
190 | // Ray and segment are parallel.
|
---|
191 |
|
---|
192 | s1 = ( a01 > 0 ) ? - segExtent : segExtent;
|
---|
193 | s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
|
---|
194 | sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
|
---|
195 |
|
---|
196 | }
|
---|
197 |
|
---|
198 | if ( optionalPointOnRay ) {
|
---|
199 |
|
---|
200 | optionalPointOnRay.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) );
|
---|
201 |
|
---|
202 | }
|
---|
203 |
|
---|
204 | if ( optionalPointOnSegment ) {
|
---|
205 |
|
---|
206 | optionalPointOnSegment.copy( segDir.clone().multiplyScalar( s1 ).add( segCenter ) );
|
---|
207 |
|
---|
208 | }
|
---|
209 |
|
---|
210 | return sqrDist;
|
---|
211 |
|
---|
212 | },
|
---|
213 |
|
---|
214 | isIntersectionSphere: function ( sphere ) {
|
---|
215 |
|
---|
216 | return this.distanceToPoint( sphere.center ) <= sphere.radius;
|
---|
217 |
|
---|
218 | },
|
---|
219 |
|
---|
220 | isIntersectionPlane: function ( plane ) {
|
---|
221 |
|
---|
222 | // check if the ray lies on the plane first
|
---|
223 |
|
---|
224 | var distToPoint = plane.distanceToPoint( this.origin );
|
---|
225 |
|
---|
226 | if ( distToPoint === 0 ) {
|
---|
227 |
|
---|
228 | return true;
|
---|
229 |
|
---|
230 | }
|
---|
231 |
|
---|
232 | var denominator = plane.normal.dot( this.direction );
|
---|
233 |
|
---|
234 | if ( denominator * distToPoint < 0 ) {
|
---|
235 |
|
---|
236 | return true
|
---|
237 |
|
---|
238 | }
|
---|
239 |
|
---|
240 | // ray origin is behind the plane (and is pointing behind it)
|
---|
241 |
|
---|
242 | return false;
|
---|
243 |
|
---|
244 | },
|
---|
245 |
|
---|
246 | distanceToPlane: function ( plane ) {
|
---|
247 |
|
---|
248 | var denominator = plane.normal.dot( this.direction );
|
---|
249 | if ( denominator == 0 ) {
|
---|
250 |
|
---|
251 | // line is coplanar, return origin
|
---|
252 | if( plane.distanceToPoint( this.origin ) == 0 ) {
|
---|
253 |
|
---|
254 | return 0;
|
---|
255 |
|
---|
256 | }
|
---|
257 |
|
---|
258 | // Null is preferable to undefined since undefined means.... it is undefined
|
---|
259 |
|
---|
260 | return null;
|
---|
261 |
|
---|
262 | }
|
---|
263 |
|
---|
264 | var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
|
---|
265 |
|
---|
266 | // Return if the ray never intersects the plane
|
---|
267 |
|
---|
268 | return t >= 0 ? t : null;
|
---|
269 |
|
---|
270 | },
|
---|
271 |
|
---|
272 | intersectPlane: function ( plane, optionalTarget ) {
|
---|
273 |
|
---|
274 | var t = this.distanceToPlane( plane );
|
---|
275 |
|
---|
276 | if ( t === null ) {
|
---|
277 |
|
---|
278 | return null;
|
---|
279 | }
|
---|
280 |
|
---|
281 | return this.at( t, optionalTarget );
|
---|
282 |
|
---|
283 | },
|
---|
284 |
|
---|
285 | isIntersectionBox: function () {
|
---|
286 |
|
---|
287 | var v = new THREE.Vector3();
|
---|
288 |
|
---|
289 | return function ( box ) {
|
---|
290 |
|
---|
291 | return this.intersectBox( box, v ) !== null;
|
---|
292 |
|
---|
293 | }
|
---|
294 |
|
---|
295 | }(),
|
---|
296 |
|
---|
297 | intersectBox: function ( box , optionalTarget ) {
|
---|
298 |
|
---|
299 | // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/
|
---|
300 |
|
---|
301 | var tmin,tmax,tymin,tymax,tzmin,tzmax;
|
---|
302 |
|
---|
303 | var invdirx = 1/this.direction.x,
|
---|
304 | invdiry = 1/this.direction.y,
|
---|
305 | invdirz = 1/this.direction.z;
|
---|
306 |
|
---|
307 | var origin = this.origin;
|
---|
308 |
|
---|
309 | if (invdirx >= 0) {
|
---|
310 |
|
---|
311 | tmin = (box.min.x - origin.x) * invdirx;
|
---|
312 | tmax = (box.max.x - origin.x) * invdirx;
|
---|
313 |
|
---|
314 | } else {
|
---|
315 |
|
---|
316 | tmin = (box.max.x - origin.x) * invdirx;
|
---|
317 | tmax = (box.min.x - origin.x) * invdirx;
|
---|
318 | }
|
---|
319 |
|
---|
320 | if (invdiry >= 0) {
|
---|
321 |
|
---|
322 | tymin = (box.min.y - origin.y) * invdiry;
|
---|
323 | tymax = (box.max.y - origin.y) * invdiry;
|
---|
324 |
|
---|
325 | } else {
|
---|
326 |
|
---|
327 | tymin = (box.max.y - origin.y) * invdiry;
|
---|
328 | tymax = (box.min.y - origin.y) * invdiry;
|
---|
329 | }
|
---|
330 |
|
---|
331 | if ((tmin > tymax) || (tymin > tmax)) return null;
|
---|
332 |
|
---|
333 | // These lines also handle the case where tmin or tmax is NaN
|
---|
334 | // (result of 0 * Infinity). x !== x returns true if x is NaN
|
---|
335 |
|
---|
336 | if (tymin > tmin || tmin !== tmin ) tmin = tymin;
|
---|
337 |
|
---|
338 | if (tymax < tmax || tmax !== tmax ) tmax = tymax;
|
---|
339 |
|
---|
340 | if (invdirz >= 0) {
|
---|
341 |
|
---|
342 | tzmin = (box.min.z - origin.z) * invdirz;
|
---|
343 | tzmax = (box.max.z - origin.z) * invdirz;
|
---|
344 |
|
---|
345 | } else {
|
---|
346 |
|
---|
347 | tzmin = (box.max.z - origin.z) * invdirz;
|
---|
348 | tzmax = (box.min.z - origin.z) * invdirz;
|
---|
349 | }
|
---|
350 |
|
---|
351 | if ((tmin > tzmax) || (tzmin > tmax)) return null;
|
---|
352 |
|
---|
353 | if (tzmin > tmin || tmin !== tmin ) tmin = tzmin;
|
---|
354 |
|
---|
355 | if (tzmax < tmax || tmax !== tmax ) tmax = tzmax;
|
---|
356 |
|
---|
357 | //return point closest to the ray (positive side)
|
---|
358 |
|
---|
359 | if ( tmax < 0 ) return null;
|
---|
360 |
|
---|
361 | return this.at( tmin >= 0 ? tmin : tmax, optionalTarget );
|
---|
362 |
|
---|
363 | },
|
---|
364 |
|
---|
365 | intersectTriangle: function() {
|
---|
366 |
|
---|
367 | // Compute the offset origin, edges, and normal.
|
---|
368 | var diff = new THREE.Vector3();
|
---|
369 | var edge1 = new THREE.Vector3();
|
---|
370 | var edge2 = new THREE.Vector3();
|
---|
371 | var normal = new THREE.Vector3();
|
---|
372 |
|
---|
373 | return function ( a, b, c, backfaceCulling, optionalTarget ) {
|
---|
374 |
|
---|
375 | // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp
|
---|
376 |
|
---|
377 | edge1.subVectors( b, a );
|
---|
378 | edge2.subVectors( c, a );
|
---|
379 | normal.crossVectors( edge1, edge2 );
|
---|
380 |
|
---|
381 | // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
|
---|
382 | // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
|
---|
383 | // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
|
---|
384 | // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
|
---|
385 | // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
|
---|
386 | var DdN = this.direction.dot( normal );
|
---|
387 | var sign;
|
---|
388 |
|
---|
389 | if ( DdN > 0 ) {
|
---|
390 |
|
---|
391 | if ( backfaceCulling ) return null;
|
---|
392 | sign = 1;
|
---|
393 |
|
---|
394 | } else if ( DdN < 0 ) {
|
---|
395 |
|
---|
396 | sign = - 1;
|
---|
397 | DdN = - DdN;
|
---|
398 |
|
---|
399 | } else {
|
---|
400 |
|
---|
401 | return null;
|
---|
402 |
|
---|
403 | }
|
---|
404 |
|
---|
405 | diff.subVectors( this.origin, a );
|
---|
406 | var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );
|
---|
407 |
|
---|
408 | // b1 < 0, no intersection
|
---|
409 | if ( DdQxE2 < 0 ) {
|
---|
410 |
|
---|
411 | return null;
|
---|
412 |
|
---|
413 | }
|
---|
414 |
|
---|
415 | var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );
|
---|
416 |
|
---|
417 | // b2 < 0, no intersection
|
---|
418 | if ( DdE1xQ < 0 ) {
|
---|
419 |
|
---|
420 | return null;
|
---|
421 |
|
---|
422 | }
|
---|
423 |
|
---|
424 | // b1+b2 > 1, no intersection
|
---|
425 | if ( DdQxE2 + DdE1xQ > DdN ) {
|
---|
426 |
|
---|
427 | return null;
|
---|
428 |
|
---|
429 | }
|
---|
430 |
|
---|
431 | // Line intersects triangle, check if ray does.
|
---|
432 | var QdN = - sign * diff.dot( normal );
|
---|
433 |
|
---|
434 | // t < 0, no intersection
|
---|
435 | if ( QdN < 0 ) {
|
---|
436 |
|
---|
437 | return null;
|
---|
438 |
|
---|
439 | }
|
---|
440 |
|
---|
441 | // Ray intersects triangle.
|
---|
442 | return this.at( QdN / DdN, optionalTarget );
|
---|
443 |
|
---|
444 | }
|
---|
445 |
|
---|
446 | }(),
|
---|
447 |
|
---|
448 | applyMatrix4: function ( matrix4 ) {
|
---|
449 |
|
---|
450 | this.direction.add( this.origin ).applyMatrix4( matrix4 );
|
---|
451 | this.origin.applyMatrix4( matrix4 );
|
---|
452 | this.direction.sub( this.origin );
|
---|
453 | this.direction.normalize();
|
---|
454 |
|
---|
455 | return this;
|
---|
456 | },
|
---|
457 |
|
---|
458 | equals: function ( ray ) {
|
---|
459 |
|
---|
460 | return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
|
---|
461 |
|
---|
462 | },
|
---|
463 |
|
---|
464 | clone: function () {
|
---|
465 |
|
---|
466 | return new THREE.Ray().copy( this );
|
---|
467 |
|
---|
468 | }
|
---|
469 |
|
---|
470 | };
|
---|