import { vec2 , vec3 , vec4 } from "gl-matrix" ; import { AABB } from "../AABB" ; import { AbstractCamera } from "../Camera" ; export enum TRIANGLE_FACING { BACK , FRONT } export interface IRayTriangleIntersection { facing : TRIANGLE_FACING ; intersection : vec3 ; normal : vec3 ; t : number ; } export interface IAABBIntersection { t : number ; } export interface IRayOptions { camera : AbstractCamera ; origin ? : vec3 ; direction ? : vec3 ; } export const RAY_DEFAULT_OPTIONS = { camera : null , origin : null , direction : null , } ; export class Ray { private _origin : vec3 = null ; private _direction : vec3 = null ; private _camera : AbstractCamera = null ; param options public constructor ( options ) { options = Object . assign ( { } , RAY_DEFAULT_OPTIONS , options ) ; this . _origin = options . origin ? options . origin : vec3 . create ( ) ; this . _direction = options . direction ? options . direction : vec3 . create ( ) ; this . _camera = options . camera ; } /**
     * The ray origin
     */ public getOrigin ( ) { return this . _origin ; } /**
     * The ray direction
     */ public getDirection ( ) { return this . _direction ; } param x param y public fromMousePosition ( x , y ) { const camera = this . _camera ; const deviceCoords = this . _getDeviceCoords ( x , y ) ; const clipCoords = this . _getClipCoords ( deviceCoords ) ; const cameraCoords = this . _getCameraCoords ( clipCoords ) ; const direction = this . _getRayCoords ( cameraCoords ) ; const origin = camera . getTranslation ( ) ; this . _origin = vec3 . copy ( vec3 . create ( ) , origin ) ; this . _direction = direction ; return this ; } param x param y public fromCameraCenter ( ) { const camera = this . _camera ; return this . fromMousePosition ( camera . getWidth ( ) * 0.5 , camera . getHeight ( ) * 0.5 ) ; } param x param y private _getDeviceCoords ( x , y ) { const out = vec2 . create ( ) ; const camera = this . _camera ; const rx = ( ( 2.0 * x ) / camera . getWidth ( ) ) - 1.0 ; const ry = ( ( 2.0 * y ) / camera . getHeight ( ) ) - 1.0 ; vec2 . set ( out , rx , ry ) ; return out ; } param pt private _getClipCoords ( pt ) { const out = vec4 . create ( ) ; vec4 . set ( out , pt [ 0 ] , - pt [ 1 ] , - 1.0 , 1.0 ) ; return out ; } param pt private _getCameraCoords ( pt ) { const out = vec4 . create ( ) ; const camera = this . _camera ; const eyeCoords = vec4 . create ( ) ; vec4 . transformMat4 ( eyeCoords , pt , camera . getProjectionInverseMatrix ( ) ) ; vec4 . set ( out , eyeCoords [ 0 ] , eyeCoords [ 1 ] , - 1.0 , 0.0 ) ; return out ; } param pt private _getRayCoords ( pt ) { const out = vec3 . create ( ) ; const camera = this . _camera ; const worldCoords = vec4 . create ( ) ; vec4 . transformMat4 ( worldCoords , pt , camera . getViewInverseMatrix ( ) ) ; vec3 . set ( out , worldCoords [ 0 ] , worldCoords [ 1 ] , worldCoords [ 2 ] ) ; vec3 . normalize ( out , out ) ; return out ; } param v0 param v1 param v2 public intersectsTriangle ( v0 , v1 , v2 ) { const origin = this . getOrigin ( ) ; const direction = this . getDirection ( ) ; const e1 = vec3 . sub ( vec3 . create ( ) , v1 , v0 ) ; const e2 = vec3 . sub ( vec3 . create ( ) , v2 , v0 ) ; const r = vec3 . cross ( vec3 . create ( ) , direction , e2 ) ; const s = vec3 . sub ( vec3 . create ( ) , origin , v0 ) ; const a = vec3 . dot ( e1 , r ) ; const q = vec3 . cross ( vec3 . create ( ) , s , e1 ) ; const u = vec3 . dot ( s , r ) ; const v = vec3 . dot ( direction , q ) ; if ( a > 0.000001 ) { if ( u < 0.0 || u > a ) return null ; if ( v < 0.0 || u + v > a ) return null ; } else if ( a < - 0.000001 ) { if ( u > 0.0 || u < a ) return null ; if ( v > 0.0 || u + v < a ) return null ; } else { return null ; } const intersection = vec3 . create ( ) ; vec3 . scaleAndAdd ( intersection , origin , direction , vec3 . dot ( e2 , q ) / a ) ; const isFrontFacing = a > 0.000001 ; const facing = ( isFrontFacing ? TRIANGLE_FACING . FRONT : TRIANGLE_FACING . BACK ) ; const dist = vec3 . distance ( origin , intersection ) ; return { facing , intersection , t : dist , normal : null } ; } param aabb public intersectsAABB ( aabb ) { const min = aabb . getMin ( ) ; const max = aabb . getMax ( ) ; const origin = this . getOrigin ( ) ; const direction = this . getDirection ( ) ; const t1 = ( min [ 0 ] - origin [ 0 ] ) / direction [ 0 ] ; const t2 = ( max [ 0 ] - origin [ 0 ] ) / direction [ 0 ] ; const t3 = ( min [ 1 ] - origin [ 1 ] ) / direction [ 1 ] ; const t4 = ( max [ 1 ] - origin [ 1 ] ) / direction [ 1 ] ; const t5 = ( min [ 2 ] - origin [ 2 ] ) / direction [ 2 ] ; const t6 = ( max [ 2 ] - origin [ 2 ] ) / direction [ 2 ] ; const t7 = Math . max ( Math . max ( Math . min ( t1 , t2 ) , Math . min ( t3 , t4 ) ) , Math . min ( t5 , t6 ) ) ; const t8 = Math . min ( Math . min ( Math . max ( t1 , t2 ) , Math . max ( t3 , t4 ) ) , Math . max ( t5 , t6 ) ) ; if ( t8 < 0 || t7 > t8 ) return null ; return { t : t7 } ; } }