ray.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. import { DeepImmutable, Nullable, float } from "../types";
  2. import { ArrayTools } from "../Misc/arrayTools";
  3. import { Matrix, Vector3, Plane, Tmp } from "../Maths/math";
  4. import { AbstractMesh } from "../Meshes/abstractMesh";
  5. import { PickingInfo } from "../Collisions/pickingInfo";
  6. import { IntersectionInfo } from "../Collisions/intersectionInfo";
  7. import { BoundingBox } from "./boundingBox";
  8. import { BoundingSphere } from "./boundingSphere";
  9. import { Scene } from '../scene';
  10. import { Camera } from '../Cameras/camera';
  11. /**
  12. * Class representing a ray with position and direction
  13. */
  14. export class Ray {
  15. private static readonly TmpVector3 = ArrayTools.BuildArray(6, Vector3.Zero);
  16. private _tmpRay: Ray;
  17. /**
  18. * Creates a new ray
  19. * @param origin origin point
  20. * @param direction direction
  21. * @param length length of the ray
  22. */
  23. constructor(
  24. /** origin point */
  25. public origin: Vector3,
  26. /** direction */
  27. public direction: Vector3,
  28. /** length of the ray */
  29. public length: number = Number.MAX_VALUE) {
  30. }
  31. // Methods
  32. /**
  33. * Checks if the ray intersects a box
  34. * @param minimum bound of the box
  35. * @param maximum bound of the box
  36. * @param intersectionTreshold extra extend to be added to the box in all direction
  37. * @returns if the box was hit
  38. */
  39. public intersectsBoxMinMax(minimum: DeepImmutable<Vector3>, maximum: DeepImmutable<Vector3>, intersectionTreshold: number = 0): boolean {
  40. const newMinimum = Ray.TmpVector3[0].copyFromFloats(minimum.x - intersectionTreshold, minimum.y - intersectionTreshold, minimum.z - intersectionTreshold);
  41. const newMaximum = Ray.TmpVector3[1].copyFromFloats(maximum.x + intersectionTreshold, maximum.y + intersectionTreshold, maximum.z + intersectionTreshold);
  42. var d = 0.0;
  43. var maxValue = Number.MAX_VALUE;
  44. var inv: number;
  45. var min: number;
  46. var max: number;
  47. var temp: number;
  48. if (Math.abs(this.direction.x) < 0.0000001) {
  49. if (this.origin.x < newMinimum.x || this.origin.x > newMaximum.x) {
  50. return false;
  51. }
  52. }
  53. else {
  54. inv = 1.0 / this.direction.x;
  55. min = (newMinimum.x - this.origin.x) * inv;
  56. max = (newMaximum.x - this.origin.x) * inv;
  57. if (max === -Infinity) {
  58. max = Infinity;
  59. }
  60. if (min > max) {
  61. temp = min;
  62. min = max;
  63. max = temp;
  64. }
  65. d = Math.max(min, d);
  66. maxValue = Math.min(max, maxValue);
  67. if (d > maxValue) {
  68. return false;
  69. }
  70. }
  71. if (Math.abs(this.direction.y) < 0.0000001) {
  72. if (this.origin.y < newMinimum.y || this.origin.y > newMaximum.y) {
  73. return false;
  74. }
  75. }
  76. else {
  77. inv = 1.0 / this.direction.y;
  78. min = (newMinimum.y - this.origin.y) * inv;
  79. max = (newMaximum.y - this.origin.y) * inv;
  80. if (max === -Infinity) {
  81. max = Infinity;
  82. }
  83. if (min > max) {
  84. temp = min;
  85. min = max;
  86. max = temp;
  87. }
  88. d = Math.max(min, d);
  89. maxValue = Math.min(max, maxValue);
  90. if (d > maxValue) {
  91. return false;
  92. }
  93. }
  94. if (Math.abs(this.direction.z) < 0.0000001) {
  95. if (this.origin.z < newMinimum.z || this.origin.z > newMaximum.z) {
  96. return false;
  97. }
  98. }
  99. else {
  100. inv = 1.0 / this.direction.z;
  101. min = (newMinimum.z - this.origin.z) * inv;
  102. max = (newMaximum.z - this.origin.z) * inv;
  103. if (max === -Infinity) {
  104. max = Infinity;
  105. }
  106. if (min > max) {
  107. temp = min;
  108. min = max;
  109. max = temp;
  110. }
  111. d = Math.max(min, d);
  112. maxValue = Math.min(max, maxValue);
  113. if (d > maxValue) {
  114. return false;
  115. }
  116. }
  117. return true;
  118. }
  119. /**
  120. * Checks if the ray intersects a box
  121. * @param box the bounding box to check
  122. * @param intersectionTreshold extra extend to be added to the BoundingBox in all direction
  123. * @returns if the box was hit
  124. */
  125. public intersectsBox(box: DeepImmutable<BoundingBox>, intersectionTreshold: number = 0): boolean {
  126. return this.intersectsBoxMinMax(box.minimum, box.maximum, intersectionTreshold);
  127. }
  128. /**
  129. * If the ray hits a sphere
  130. * @param sphere the bounding sphere to check
  131. * @param intersectionTreshold extra extend to be added to the BoundingSphere in all direction
  132. * @returns true if it hits the sphere
  133. */
  134. public intersectsSphere(sphere: DeepImmutable<BoundingSphere>, intersectionTreshold: number = 0): boolean {
  135. var x = sphere.center.x - this.origin.x;
  136. var y = sphere.center.y - this.origin.y;
  137. var z = sphere.center.z - this.origin.z;
  138. var pyth = (x * x) + (y * y) + (z * z);
  139. const radius = sphere.radius + intersectionTreshold;
  140. var rr = radius * radius;
  141. if (pyth <= rr) {
  142. return true;
  143. }
  144. var dot = (x * this.direction.x) + (y * this.direction.y) + (z * this.direction.z);
  145. if (dot < 0.0) {
  146. return false;
  147. }
  148. var temp = pyth - (dot * dot);
  149. return temp <= rr;
  150. }
  151. /**
  152. * If the ray hits a triange
  153. * @param vertex0 triangle vertex
  154. * @param vertex1 triangle vertex
  155. * @param vertex2 triangle vertex
  156. * @returns intersection information if hit
  157. */
  158. public intersectsTriangle(vertex0: DeepImmutable<Vector3>, vertex1: DeepImmutable<Vector3>, vertex2: DeepImmutable<Vector3>): Nullable<IntersectionInfo> {
  159. const edge1 = Ray.TmpVector3[0];
  160. const edge2 = Ray.TmpVector3[1];
  161. const pvec = Ray.TmpVector3[2];
  162. const tvec = Ray.TmpVector3[3];
  163. const qvec = Ray.TmpVector3[4];
  164. vertex1.subtractToRef(vertex0, edge1);
  165. vertex2.subtractToRef(vertex0, edge2);
  166. Vector3.CrossToRef(this.direction, edge2, pvec);
  167. var det = Vector3.Dot(edge1, pvec);
  168. if (det === 0) {
  169. return null;
  170. }
  171. var invdet = 1 / det;
  172. this.origin.subtractToRef(vertex0, tvec);
  173. var bu = Vector3.Dot(tvec, pvec) * invdet;
  174. if (bu < 0 || bu > 1.0) {
  175. return null;
  176. }
  177. Vector3.CrossToRef(tvec, edge1, qvec);
  178. var bv = Vector3.Dot(this.direction, qvec) * invdet;
  179. if (bv < 0 || bu + bv > 1.0) {
  180. return null;
  181. }
  182. //check if the distance is longer than the predefined length.
  183. var distance = Vector3.Dot(edge2, qvec) * invdet;
  184. if (distance > this.length) {
  185. return null;
  186. }
  187. return new IntersectionInfo(bu, bv, distance);
  188. }
  189. /**
  190. * Checks if ray intersects a plane
  191. * @param plane the plane to check
  192. * @returns the distance away it was hit
  193. */
  194. public intersectsPlane(plane: DeepImmutable<Plane>): Nullable<number> {
  195. var distance: number;
  196. var result1 = Vector3.Dot(plane.normal, this.direction);
  197. if (Math.abs(result1) < 9.99999997475243E-07) {
  198. return null;
  199. }
  200. else {
  201. var result2 = Vector3.Dot(plane.normal, this.origin);
  202. distance = (-plane.d - result2) / result1;
  203. if (distance < 0.0) {
  204. if (distance < -9.99999997475243E-07) {
  205. return null;
  206. } else {
  207. return 0;
  208. }
  209. }
  210. return distance;
  211. }
  212. }
  213. /**
  214. * Checks if ray intersects a mesh
  215. * @param mesh the mesh to check
  216. * @param fastCheck if only the bounding box should checked
  217. * @returns picking info of the intersecton
  218. */
  219. public intersectsMesh(mesh: DeepImmutable<AbstractMesh>, fastCheck?: boolean): PickingInfo {
  220. var tm = Tmp.Matrix[0];
  221. mesh.getWorldMatrix().invertToRef(tm);
  222. if (this._tmpRay) {
  223. Ray.TransformToRef(this, tm, this._tmpRay);
  224. } else {
  225. this._tmpRay = Ray.Transform(this, tm);
  226. }
  227. return mesh.intersects(this._tmpRay, fastCheck);
  228. }
  229. /**
  230. * Checks if ray intersects a mesh
  231. * @param meshes the meshes to check
  232. * @param fastCheck if only the bounding box should checked
  233. * @param results array to store result in
  234. * @returns Array of picking infos
  235. */
  236. public intersectsMeshes(meshes: Array<DeepImmutable<AbstractMesh>>, fastCheck?: boolean, results?: Array<PickingInfo>): Array<PickingInfo> {
  237. if (results) {
  238. results.length = 0;
  239. } else {
  240. results = [];
  241. }
  242. for (var i = 0; i < meshes.length; i++) {
  243. var pickInfo = this.intersectsMesh(meshes[i], fastCheck);
  244. if (pickInfo.hit) {
  245. results.push(pickInfo);
  246. }
  247. }
  248. results.sort(this._comparePickingInfo);
  249. return results;
  250. }
  251. private _comparePickingInfo(pickingInfoA: DeepImmutable<PickingInfo>, pickingInfoB: DeepImmutable<PickingInfo>): number {
  252. if (pickingInfoA.distance < pickingInfoB.distance) {
  253. return -1;
  254. } else if (pickingInfoA.distance > pickingInfoB.distance) {
  255. return 1;
  256. } else {
  257. return 0;
  258. }
  259. }
  260. private static smallnum = 0.00000001;
  261. private static rayl = 10e8;
  262. /**
  263. * Intersection test between the ray and a given segment whithin a given tolerance (threshold)
  264. * @param sega the first point of the segment to test the intersection against
  265. * @param segb the second point of the segment to test the intersection against
  266. * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
  267. * @return the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
  268. */
  269. intersectionSegment(sega: DeepImmutable<Vector3>, segb: DeepImmutable<Vector3>, threshold: number): number {
  270. const o = this.origin;
  271. const u = Tmp.Vector3[0];
  272. const rsegb = Tmp.Vector3[1];
  273. const v = Tmp.Vector3[2];
  274. const w = Tmp.Vector3[3];
  275. segb.subtractToRef(sega, u);
  276. this.direction.scaleToRef(Ray.rayl, v);
  277. o.addToRef(v, rsegb);
  278. sega.subtractToRef(o, w);
  279. var a = Vector3.Dot(u, u); // always >= 0
  280. var b = Vector3.Dot(u, v);
  281. var c = Vector3.Dot(v, v); // always >= 0
  282. var d = Vector3.Dot(u, w);
  283. var e = Vector3.Dot(v, w);
  284. var D = a * c - b * b; // always >= 0
  285. var sc: number, sN: number, sD = D; // sc = sN / sD, default sD = D >= 0
  286. var tc: number, tN: number, tD = D; // tc = tN / tD, default tD = D >= 0
  287. // compute the line parameters of the two closest points
  288. if (D < Ray.smallnum) { // the lines are almost parallel
  289. sN = 0.0; // force using point P0 on segment S1
  290. sD = 1.0; // to prevent possible division by 0.0 later
  291. tN = e;
  292. tD = c;
  293. }
  294. else { // get the closest points on the infinite lines
  295. sN = (b * e - c * d);
  296. tN = (a * e - b * d);
  297. if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
  298. sN = 0.0;
  299. tN = e;
  300. tD = c;
  301. } else if (sN > sD) { // sc > 1 => the s=1 edge is visible
  302. sN = sD;
  303. tN = e + b;
  304. tD = c;
  305. }
  306. }
  307. if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
  308. tN = 0.0;
  309. // recompute sc for this edge
  310. if (-d < 0.0) {
  311. sN = 0.0;
  312. } else if (-d > a) {
  313. sN = sD;
  314. }
  315. else {
  316. sN = -d;
  317. sD = a;
  318. }
  319. } else if (tN > tD) { // tc > 1 => the t=1 edge is visible
  320. tN = tD;
  321. // recompute sc for this edge
  322. if ((-d + b) < 0.0) {
  323. sN = 0;
  324. } else if ((-d + b) > a) {
  325. sN = sD;
  326. } else {
  327. sN = (-d + b);
  328. sD = a;
  329. }
  330. }
  331. // finally do the division to get sc and tc
  332. sc = (Math.abs(sN) < Ray.smallnum ? 0.0 : sN / sD);
  333. tc = (Math.abs(tN) < Ray.smallnum ? 0.0 : tN / tD);
  334. // get the difference of the two closest points
  335. const qtc = Tmp.Vector3[4];
  336. v.scaleToRef(tc, qtc);
  337. const qsc = Tmp.Vector3[5];
  338. u.scaleToRef(sc, qsc);
  339. qsc.addInPlace(w);
  340. const dP = Tmp.Vector3[6];
  341. qsc.subtractToRef(qtc, dP); // = S1(sc) - S2(tc)
  342. var isIntersected = (tc > 0) && (tc <= this.length) && (dP.lengthSquared() < (threshold * threshold)); // return intersection result
  343. if (isIntersected) {
  344. return qsc.length();
  345. }
  346. return -1;
  347. }
  348. /**
  349. * Update the ray from viewport position
  350. * @param x position
  351. * @param y y position
  352. * @param viewportWidth viewport width
  353. * @param viewportHeight viewport height
  354. * @param world world matrix
  355. * @param view view matrix
  356. * @param projection projection matrix
  357. * @returns this ray updated
  358. */
  359. public update(x: number, y: number, viewportWidth: number, viewportHeight: number, world: DeepImmutable<Matrix>, view: DeepImmutable<Matrix>, projection: DeepImmutable<Matrix>): Ray {
  360. this.unprojectRayToRef(x, y, viewportWidth, viewportHeight, world, view, projection);
  361. return this;
  362. }
  363. // Statics
  364. /**
  365. * Creates a ray with origin and direction of 0,0,0
  366. * @returns the new ray
  367. */
  368. public static Zero(): Ray {
  369. return new Ray(Vector3.Zero(), Vector3.Zero());
  370. }
  371. /**
  372. * Creates a new ray from screen space and viewport
  373. * @param x position
  374. * @param y y position
  375. * @param viewportWidth viewport width
  376. * @param viewportHeight viewport height
  377. * @param world world matrix
  378. * @param view view matrix
  379. * @param projection projection matrix
  380. * @returns new ray
  381. */
  382. public static CreateNew(x: number, y: number, viewportWidth: number, viewportHeight: number, world: DeepImmutable<Matrix>, view: DeepImmutable<Matrix>, projection: DeepImmutable<Matrix>): Ray {
  383. let result = Ray.Zero();
  384. return result.update(x, y, viewportWidth, viewportHeight, world, view, projection);
  385. }
  386. /**
  387. * Function will create a new transformed ray starting from origin and ending at the end point. Ray's length will be set, and ray will be
  388. * transformed to the given world matrix.
  389. * @param origin The origin point
  390. * @param end The end point
  391. * @param world a matrix to transform the ray to. Default is the identity matrix.
  392. * @returns the new ray
  393. */
  394. public static CreateNewFromTo(origin: DeepImmutable<Vector3>, end: DeepImmutable<Vector3>, world: DeepImmutable<Matrix> = Matrix.IdentityReadOnly): Ray {
  395. var direction = end.subtract(origin);
  396. var length = Math.sqrt((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z));
  397. direction.normalize();
  398. return Ray.Transform(new Ray(origin, direction, length), world);
  399. }
  400. /**
  401. * Transforms a ray by a matrix
  402. * @param ray ray to transform
  403. * @param matrix matrix to apply
  404. * @returns the resulting new ray
  405. */
  406. public static Transform(ray: DeepImmutable<Ray>, matrix: DeepImmutable<Matrix>): Ray {
  407. var result = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
  408. Ray.TransformToRef(ray, matrix, result);
  409. return result;
  410. }
  411. /**
  412. * Transforms a ray by a matrix
  413. * @param ray ray to transform
  414. * @param matrix matrix to apply
  415. * @param result ray to store result in
  416. */
  417. public static TransformToRef(ray: DeepImmutable<Ray>, matrix: DeepImmutable<Matrix>, result: Ray): void {
  418. Vector3.TransformCoordinatesToRef(ray.origin, matrix, result.origin);
  419. Vector3.TransformNormalToRef(ray.direction, matrix, result.direction);
  420. result.length = ray.length;
  421. var dir = result.direction;
  422. var len = dir.length();
  423. if (!(len === 0 || len === 1)) {
  424. var num = 1.0 / len;
  425. dir.x *= num;
  426. dir.y *= num;
  427. dir.z *= num;
  428. result.length *= len;
  429. }
  430. }
  431. /**
  432. * Unproject a ray from screen space to object space
  433. * @param sourceX defines the screen space x coordinate to use
  434. * @param sourceY defines the screen space y coordinate to use
  435. * @param viewportWidth defines the current width of the viewport
  436. * @param viewportHeight defines the current height of the viewport
  437. * @param world defines the world matrix to use (can be set to Identity to go to world space)
  438. * @param view defines the view matrix to use
  439. * @param projection defines the projection matrix to use
  440. */
  441. public unprojectRayToRef(sourceX: float, sourceY: float, viewportWidth: number, viewportHeight: number, world: DeepImmutable<Matrix>, view: DeepImmutable<Matrix>, projection: DeepImmutable<Matrix>): void {
  442. var matrix = Tmp.Matrix[0];
  443. world.multiplyToRef(view, matrix);
  444. matrix.multiplyToRef(projection, matrix);
  445. matrix.invert();
  446. var nearScreenSource = Tmp.Vector3[0];
  447. nearScreenSource.x = sourceX / viewportWidth * 2 - 1;
  448. nearScreenSource.y = -(sourceY / viewportHeight * 2 - 1);
  449. nearScreenSource.z = -1.0;
  450. var farScreenSource = Tmp.Vector3[1].copyFromFloats(nearScreenSource.x, nearScreenSource.y, 1.0);
  451. const nearVec3 = Tmp.Vector3[2];
  452. const farVec3 = Tmp.Vector3[3];
  453. Vector3._UnprojectFromInvertedMatrixToRef(nearScreenSource, matrix, nearVec3);
  454. Vector3._UnprojectFromInvertedMatrixToRef(farScreenSource, matrix, farVec3);
  455. this.origin.copyFrom(nearVec3);
  456. farVec3.subtractToRef(nearVec3, this.direction);
  457. this.direction.normalize();
  458. }
  459. }
  460. // Picking
  461. declare module "../scene" {
  462. export interface Scene {
  463. /** @hidden */
  464. _tempPickingRay: Nullable<Ray>;
  465. /** @hidden */
  466. _cachedRayForTransform: Ray;
  467. /** @hidden */
  468. _pickWithRayInverseMatrix: Matrix;
  469. /** @hidden */
  470. _internalPick(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
  471. /** @hidden */
  472. _internalMultiPick(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean): Nullable<PickingInfo[]>;
  473. }
  474. }
  475. Scene.prototype.createPickingRay = function(x: number, y: number, world: Matrix, camera: Nullable<Camera>, cameraViewSpace = false): Ray {
  476. let result = Ray.Zero();
  477. this.createPickingRayToRef(x, y, world, result, camera, cameraViewSpace);
  478. return result;
  479. };
  480. Scene.prototype.createPickingRayToRef = function(x: number, y: number, world: Matrix, result: Ray, camera: Nullable<Camera>, cameraViewSpace = false): Scene {
  481. var engine = this.getEngine();
  482. if (!camera) {
  483. if (!this.activeCamera) {
  484. throw new Error("Active camera not set");
  485. }
  486. camera = this.activeCamera;
  487. }
  488. var cameraViewport = camera.viewport;
  489. var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
  490. // Moving coordinates to local viewport world
  491. x = x / engine.getHardwareScalingLevel() - viewport.x;
  492. y = y / engine.getHardwareScalingLevel() - (engine.getRenderHeight() - viewport.y - viewport.height);
  493. result.update(x, y, viewport.width, viewport.height, world ? world : Matrix.IdentityReadOnly, cameraViewSpace ? Matrix.IdentityReadOnly : camera.getViewMatrix(), camera.getProjectionMatrix());
  494. return this;
  495. };
  496. Scene.prototype.createPickingRayInCameraSpace = function(x: number, y: number, camera?: Camera): Ray {
  497. let result = Ray.Zero();
  498. this.createPickingRayInCameraSpaceToRef(x, y, result, camera);
  499. return result;
  500. };
  501. Scene.prototype.createPickingRayInCameraSpaceToRef = function(x: number, y: number, result: Ray, camera?: Camera): Scene {
  502. if (!PickingInfo) {
  503. return this;
  504. }
  505. var engine = this.getEngine();
  506. if (!camera) {
  507. if (!this.activeCamera) {
  508. throw new Error("Active camera not set");
  509. }
  510. camera = this.activeCamera;
  511. }
  512. var cameraViewport = camera.viewport;
  513. var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
  514. var identity = Matrix.Identity();
  515. // Moving coordinates to local viewport world
  516. x = x / engine.getHardwareScalingLevel() - viewport.x;
  517. y = y / engine.getHardwareScalingLevel() - (engine.getRenderHeight() - viewport.y - viewport.height);
  518. result.update(x, y, viewport.width, viewport.height, identity, identity, camera.getProjectionMatrix());
  519. return this;
  520. };
  521. Scene.prototype._internalPick = function(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean): Nullable<PickingInfo> {
  522. if (!PickingInfo) {
  523. return null;
  524. }
  525. var pickingInfo = null;
  526. for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
  527. var mesh = this.meshes[meshIndex];
  528. if (predicate) {
  529. if (!predicate(mesh)) {
  530. continue;
  531. }
  532. } else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
  533. continue;
  534. }
  535. var world = mesh.getWorldMatrix();
  536. var ray = rayFunction(world);
  537. var result = mesh.intersects(ray, fastCheck);
  538. if (!result || !result.hit) {
  539. continue;
  540. }
  541. if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance) {
  542. continue;
  543. }
  544. pickingInfo = result;
  545. if (fastCheck) {
  546. break;
  547. }
  548. }
  549. return pickingInfo || new PickingInfo();
  550. };
  551. Scene.prototype._internalMultiPick = function(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean): Nullable<PickingInfo[]> {
  552. if (!PickingInfo) {
  553. return null;
  554. }
  555. var pickingInfos = new Array<PickingInfo>();
  556. for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
  557. var mesh = this.meshes[meshIndex];
  558. if (predicate) {
  559. if (!predicate(mesh)) {
  560. continue;
  561. }
  562. } else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
  563. continue;
  564. }
  565. var world = mesh.getWorldMatrix();
  566. var ray = rayFunction(world);
  567. var result = mesh.intersects(ray, false);
  568. if (!result || !result.hit) {
  569. continue;
  570. }
  571. pickingInfos.push(result);
  572. }
  573. return pickingInfos;
  574. };
  575. Scene.prototype.pick = function(x: number, y: number, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean, camera?: Nullable<Camera>): Nullable<PickingInfo> {
  576. if (!PickingInfo) {
  577. return null;
  578. }
  579. var result = this._internalPick((world) => {
  580. if (!this._tempPickingRay) {
  581. this._tempPickingRay = Ray.Zero();
  582. }
  583. this.createPickingRayToRef(x, y, world, this._tempPickingRay, camera || null);
  584. return this._tempPickingRay;
  585. }, predicate, fastCheck);
  586. if (result) {
  587. result.ray = this.createPickingRay(x, y, Matrix.Identity(), camera || null);
  588. }
  589. return result;
  590. };
  591. Scene.prototype.pickWithRay = function(ray: Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean): Nullable<PickingInfo> {
  592. var result = this._internalPick((world) => {
  593. if (!this._pickWithRayInverseMatrix) {
  594. this._pickWithRayInverseMatrix = Matrix.Identity();
  595. }
  596. world.invertToRef(this._pickWithRayInverseMatrix);
  597. if (!this._cachedRayForTransform) {
  598. this._cachedRayForTransform = Ray.Zero();
  599. }
  600. Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
  601. return this._cachedRayForTransform;
  602. }, predicate, fastCheck);
  603. if (result) {
  604. result.ray = ray;
  605. }
  606. return result;
  607. };
  608. Scene.prototype.multiPick = function(x: number, y: number, predicate?: (mesh: AbstractMesh) => boolean, camera?: Camera): Nullable<PickingInfo[]> {
  609. return this._internalMultiPick((world) => this.createPickingRay(x, y, world, camera || null), predicate);
  610. };
  611. Scene.prototype.multiPickWithRay = function(ray: Ray, predicate: (mesh: AbstractMesh) => boolean): Nullable<PickingInfo[]> {
  612. return this._internalMultiPick((world) => {
  613. if (!this._pickWithRayInverseMatrix) {
  614. this._pickWithRayInverseMatrix = Matrix.Identity();
  615. }
  616. world.invertToRef(this._pickWithRayInverseMatrix);
  617. if (!this._cachedRayForTransform) {
  618. this._cachedRayForTransform = Ray.Zero();
  619. }
  620. Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
  621. return this._cachedRayForTransform;
  622. }, predicate);
  623. };
  624. Camera.prototype.getForwardRay = function(length = 100, transform?: Matrix, origin?: Vector3): Ray {
  625. if (!transform) {
  626. transform = this.getWorldMatrix();
  627. }
  628. if (!origin) {
  629. origin = this.position;
  630. }
  631. var forward = this._scene.useRightHandedSystem ? new Vector3(0, 0, -1) : new Vector3(0, 0, 1);
  632. var forwardWorld = Vector3.TransformNormal(forward, transform);
  633. var direction = Vector3.Normalize(forwardWorld);
  634. return new Ray(origin, direction, length);
  635. };