mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
621 lines
15 KiB
C#
621 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace THREE
|
|
{
|
|
/// <summary>
|
|
/// @author bhouston / http://clara.io
|
|
/// </summary>
|
|
public class Ray
|
|
{
|
|
function Ray(origin, direction )
|
|
{
|
|
|
|
this.origin = (origin !== undefined) ? origin : new Vector3();
|
|
this.direction = (direction !== undefined) ? direction : new Vector3();
|
|
|
|
}
|
|
|
|
set: function(origin, direction )
|
|
{
|
|
|
|
this.origin.copy(origin);
|
|
this.direction.copy(direction);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
clone: function()
|
|
{
|
|
|
|
return new this.constructor().copy(this);
|
|
|
|
},
|
|
|
|
copy: function(ray )
|
|
{
|
|
|
|
this.origin.copy(ray.origin);
|
|
this.direction.copy(ray.direction);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
at: function(t, target )
|
|
{
|
|
|
|
if (target === undefined)
|
|
{
|
|
|
|
console.warn('THREE.Ray: .at() target is now required');
|
|
target = new Vector3();
|
|
|
|
}
|
|
|
|
return target.copy(this.direction).multiplyScalar(t).add(this.origin);
|
|
|
|
},
|
|
|
|
lookAt: function(v )
|
|
{
|
|
|
|
this.direction.copy(v).sub(this.origin).normalize();
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
recast: function()
|
|
{
|
|
|
|
var v1 = new Vector3();
|
|
|
|
return function recast(t) {
|
|
|
|
this.origin.copy(this.at(t, v1));
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
}
|
|
(),
|
|
|
|
closestPointToPoint: function(point, target )
|
|
{
|
|
|
|
if (target === undefined)
|
|
{
|
|
|
|
console.warn('THREE.Ray: .closestPointToPoint() target is now required');
|
|
target = new Vector3();
|
|
|
|
}
|
|
|
|
target.subVectors(point, this.origin);
|
|
|
|
var directionDistance = target.dot(this.direction);
|
|
|
|
if (directionDistance < 0)
|
|
{
|
|
|
|
return target.copy(this.origin);
|
|
|
|
}
|
|
|
|
return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);
|
|
|
|
},
|
|
|
|
distanceToPoint: function(point )
|
|
{
|
|
|
|
return Math.sqrt(this.distanceSqToPoint(point));
|
|
|
|
},
|
|
|
|
distanceSqToPoint: function()
|
|
{
|
|
|
|
var v1 = new Vector3();
|
|
|
|
return function distanceSqToPoint(point) {
|
|
|
|
var directionDistance = v1.subVectors(point, this.origin).dot(this.direction);
|
|
|
|
// point behind the ray
|
|
|
|
if (directionDistance < 0)
|
|
{
|
|
|
|
return this.origin.distanceToSquared(point);
|
|
|
|
}
|
|
|
|
v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);
|
|
|
|
return v1.distanceToSquared(point);
|
|
|
|
};
|
|
|
|
}
|
|
(),
|
|
|
|
distanceSqToSegment: function()
|
|
{
|
|
|
|
var segCenter = new Vector3();
|
|
var segDir = new Vector3();
|
|
var diff = new Vector3();
|
|
|
|
return function distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) {
|
|
|
|
// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h
|
|
// It returns the min distance between the ray and the segment
|
|
// defined by v0 and v1
|
|
// It can also set two optional targets :
|
|
// - The closest point on the ray
|
|
// - The closest point on the segment
|
|
|
|
segCenter.copy(v0).add(v1).multiplyScalar(0.5);
|
|
segDir.copy(v1).sub(v0).normalize();
|
|
diff.copy(this.origin).sub(segCenter);
|
|
|
|
var segExtent = v0.distanceTo(v1) * 0.5;
|
|
var a01 = -this.direction.dot(segDir);
|
|
var b0 = diff.dot(this.direction);
|
|
var b1 = -diff.dot(segDir);
|
|
var c = diff.lengthSq();
|
|
var det = Math.abs(1 - a01 * a01);
|
|
var s0, s1, sqrDist, extDet;
|
|
|
|
if (det > 0)
|
|
{
|
|
|
|
// The ray and segment are not parallel.
|
|
|
|
s0 = a01 * b1 - b0;
|
|
s1 = a01 * b0 - b1;
|
|
extDet = segExtent * det;
|
|
|
|
if (s0 >= 0)
|
|
{
|
|
|
|
if (s1 >= -extDet)
|
|
{
|
|
|
|
if (s1 <= extDet)
|
|
{
|
|
|
|
// region 0
|
|
// Minimum at interior points of ray and segment.
|
|
|
|
var invDet = 1 / det;
|
|
s0 *= invDet;
|
|
s1 *= invDet;
|
|
sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// region 1
|
|
|
|
s1 = segExtent;
|
|
s0 = Math.max(0, -(a01 * s1 + b0));
|
|
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// region 5
|
|
|
|
s1 = -segExtent;
|
|
s0 = Math.max(0, -(a01 * s1 + b0));
|
|
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
if (s1 <= -extDet)
|
|
{
|
|
|
|
// region 4
|
|
|
|
s0 = Math.max(0, -(-a01 * segExtent + b0));
|
|
s1 = (s0 > 0) ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent);
|
|
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
|
|
|
|
}
|
|
else if (s1 <= extDet)
|
|
{
|
|
|
|
// region 3
|
|
|
|
s0 = 0;
|
|
s1 = Math.min(Math.max(-segExtent, -b1), segExtent);
|
|
sqrDist = s1 * (s1 + 2 * b1) + c;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// region 2
|
|
|
|
s0 = Math.max(0, -(a01 * segExtent + b0));
|
|
s1 = (s0 > 0) ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent);
|
|
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// Ray and segment are parallel.
|
|
|
|
s1 = (a01 > 0) ? -segExtent : segExtent;
|
|
s0 = Math.max(0, -(a01 * s1 + b0));
|
|
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
|
|
|
|
}
|
|
|
|
if (optionalPointOnRay)
|
|
{
|
|
|
|
optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin);
|
|
|
|
}
|
|
|
|
if (optionalPointOnSegment)
|
|
{
|
|
|
|
optionalPointOnSegment.copy(segDir).multiplyScalar(s1).add(segCenter);
|
|
|
|
}
|
|
|
|
return sqrDist;
|
|
|
|
};
|
|
|
|
}
|
|
(),
|
|
|
|
intersectSphere: function()
|
|
{
|
|
|
|
var v1 = new Vector3();
|
|
|
|
return function intersectSphere(sphere, target) {
|
|
|
|
v1.subVectors(sphere.center, this.origin);
|
|
var tca = v1.dot(this.direction);
|
|
var d2 = v1.dot(v1) - tca * tca;
|
|
var radius2 = sphere.radius * sphere.radius;
|
|
|
|
if (d2 > radius2) return null;
|
|
|
|
var thc = Math.sqrt(radius2 - d2);
|
|
|
|
// t0 = first intersect point - entrance on front of sphere
|
|
var t0 = tca - thc;
|
|
|
|
// t1 = second intersect point - exit point on back of sphere
|
|
var t1 = tca + thc;
|
|
|
|
// test to see if both t0 and t1 are behind the ray - if so, return null
|
|
if (t0 < 0 && t1 < 0) return null;
|
|
|
|
// test to see if t0 is behind the ray:
|
|
// if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
|
|
// in order to always return an intersect point that is in front of the ray.
|
|
if (t0 < 0) return this.at(t1, target);
|
|
|
|
// else t0 is in front of the ray, so return the first collision point scaled by t0
|
|
return this.at(t0, target);
|
|
|
|
};
|
|
|
|
}
|
|
(),
|
|
|
|
intersectsSphere: function(sphere )
|
|
{
|
|
|
|
return this.distanceSqToPoint(sphere.center) <= (sphere.radius * sphere.radius);
|
|
|
|
},
|
|
|
|
distanceToPlane: function(plane )
|
|
{
|
|
|
|
var denominator = plane.normal.dot(this.direction);
|
|
|
|
if (denominator === 0)
|
|
{
|
|
|
|
// line is coplanar, return origin
|
|
if (plane.distanceToPoint(this.origin) === 0)
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// Null is preferable to undefined since undefined means.... it is undefined
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var t = -(this.origin.dot(plane.normal) + plane.constant) / denominator;
|
|
|
|
// Return if the ray never intersects the plane
|
|
|
|
return t >= 0 ? t : null;
|
|
|
|
},
|
|
|
|
intersectPlane: function(plane, target )
|
|
{
|
|
|
|
var t = this.distanceToPlane(plane);
|
|
|
|
if (t === null)
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.at(t, target);
|
|
|
|
},
|
|
|
|
intersectsPlane: function(plane )
|
|
{
|
|
|
|
// check if the ray lies on the plane first
|
|
|
|
var distToPoint = plane.distanceToPoint(this.origin);
|
|
|
|
if (distToPoint === 0)
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
var denominator = plane.normal.dot(this.direction);
|
|
|
|
if (denominator * distToPoint < 0)
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// ray origin is behind the plane (and is pointing behind it)
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
intersectBox: function(box, target )
|
|
{
|
|
|
|
var tmin, tmax, tymin, tymax, tzmin, tzmax;
|
|
|
|
var invdirx = 1 / this.direction.x,
|
|
invdiry = 1 / this.direction.y,
|
|
invdirz = 1 / this.direction.z;
|
|
|
|
var origin = this.origin;
|
|
|
|
if (invdirx >= 0)
|
|
{
|
|
|
|
tmin = (box.min.x - origin.x) * invdirx;
|
|
tmax = (box.max.x - origin.x) * invdirx;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
tmin = (box.max.x - origin.x) * invdirx;
|
|
tmax = (box.min.x - origin.x) * invdirx;
|
|
|
|
}
|
|
|
|
if (invdiry >= 0)
|
|
{
|
|
|
|
tymin = (box.min.y - origin.y) * invdiry;
|
|
tymax = (box.max.y - origin.y) * invdiry;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
tymin = (box.max.y - origin.y) * invdiry;
|
|
tymax = (box.min.y - origin.y) * invdiry;
|
|
|
|
}
|
|
|
|
if ((tmin > tymax) || (tymin > tmax)) return null;
|
|
|
|
// These lines also handle the case where tmin or tmax is NaN
|
|
// (result of 0 * Infinity). x !== x returns true if x is NaN
|
|
|
|
if (tymin > tmin || tmin !== tmin) tmin = tymin;
|
|
|
|
if (tymax < tmax || tmax !== tmax) tmax = tymax;
|
|
|
|
if (invdirz >= 0)
|
|
{
|
|
|
|
tzmin = (box.min.z - origin.z) * invdirz;
|
|
tzmax = (box.max.z - origin.z) * invdirz;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
tzmin = (box.max.z - origin.z) * invdirz;
|
|
tzmax = (box.min.z - origin.z) * invdirz;
|
|
|
|
}
|
|
|
|
if ((tmin > tzmax) || (tzmin > tmax)) return null;
|
|
|
|
if (tzmin > tmin || tmin !== tmin) tmin = tzmin;
|
|
|
|
if (tzmax < tmax || tmax !== tmax) tmax = tzmax;
|
|
|
|
//return point closest to the ray (positive side)
|
|
|
|
if (tmax < 0) return null;
|
|
|
|
return this.at(tmin >= 0 ? tmin : tmax, target);
|
|
|
|
},
|
|
|
|
intersectsBox: (function () {
|
|
|
|
var v = new Vector3();
|
|
|
|
return function intersectsBox(box )
|
|
{
|
|
|
|
return this.intersectBox(box, v) !== null;
|
|
|
|
};
|
|
|
|
} )(),
|
|
|
|
intersectTriangle: function()
|
|
{
|
|
|
|
// Compute the offset origin, edges, and normal.
|
|
var diff = new Vector3();
|
|
var edge1 = new Vector3();
|
|
var edge2 = new Vector3();
|
|
var normal = new Vector3();
|
|
|
|
return function intersectTriangle(a, b, c, backfaceCulling, target) {
|
|
|
|
// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
|
|
|
|
edge1.subVectors(b, a);
|
|
edge2.subVectors(c, a);
|
|
normal.crossVectors(edge1, edge2);
|
|
|
|
// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
|
|
// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
|
|
// |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
|
|
// |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
|
|
// |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
|
|
var DdN = this.direction.dot(normal);
|
|
var sign;
|
|
|
|
if (DdN > 0)
|
|
{
|
|
|
|
if (backfaceCulling) return null;
|
|
sign = 1;
|
|
|
|
}
|
|
else if (DdN < 0)
|
|
{
|
|
|
|
sign = -1;
|
|
DdN = -DdN;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
diff.subVectors(this.origin, a);
|
|
var DdQxE2 = sign * this.direction.dot(edge2.crossVectors(diff, edge2));
|
|
|
|
// b1 < 0, no intersection
|
|
if (DdQxE2 < 0)
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var DdE1xQ = sign * this.direction.dot(edge1.cross(diff));
|
|
|
|
// b2 < 0, no intersection
|
|
if (DdE1xQ < 0)
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
// b1+b2 > 1, no intersection
|
|
if (DdQxE2 + DdE1xQ > DdN)
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
// Line intersects triangle, check if ray does.
|
|
var QdN = -sign * diff.dot(normal);
|
|
|
|
// t < 0, no intersection
|
|
if (QdN < 0)
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
// Ray intersects triangle.
|
|
return this.at(QdN / DdN, target);
|
|
|
|
};
|
|
|
|
}
|
|
(),
|
|
|
|
applyMatrix4: function(matrix4 )
|
|
{
|
|
|
|
this.origin.applyMatrix4(matrix4);
|
|
this.direction.transformDirection(matrix4);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
equals: function(ray )
|
|
{
|
|
|
|
return ray.origin.equals(this.origin) && ray.direction.equals(this.direction);
|
|
|
|
}
|
|
}
|
|
}
|