ray.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. import { ArrayTools } from "../Misc/arrayTools.js";
  2. import { Matrix, Vector3, TmpVectors } from "../Maths/math.vector.js";
  3. import { PickingInfo } from "../Collisions/pickingInfo.js";
  4. import { IntersectionInfo } from "../Collisions/intersectionInfo.js";
  5. import { Scene } from "../scene.js";
  6. import { Camera } from "../Cameras/camera.js";
  7. import { EngineStore } from "../Engines/engineStore.js";
  8. import { Epsilon } from "../Maths/math.constants.js";
  9. /**
  10. * Class representing a ray with position and direction
  11. */
  12. export class Ray {
  13. /**
  14. * Creates a new ray
  15. * @param origin origin point
  16. * @param direction direction
  17. * @param length length of the ray
  18. * @param epsilon The epsilon value to use when calculating the ray/triangle intersection (default: 0)
  19. */
  20. constructor(
  21. /** origin point */
  22. origin,
  23. /** direction */
  24. direction,
  25. /** length of the ray */
  26. length = Number.MAX_VALUE,
  27. /** The epsilon value to use when calculating the ray/triangle intersection (default: Epsilon from math constants) */
  28. epsilon = Epsilon) {
  29. this.origin = origin;
  30. this.direction = direction;
  31. this.length = length;
  32. this.epsilon = epsilon;
  33. }
  34. // Methods
  35. /**
  36. * Clone the current ray
  37. * @returns a new ray
  38. */
  39. clone() {
  40. return new Ray(this.origin.clone(), this.direction.clone(), this.length);
  41. }
  42. /**
  43. * Checks if the ray intersects a box
  44. * This does not account for the ray length by design to improve perfs.
  45. * @param minimum bound of the box
  46. * @param maximum bound of the box
  47. * @param intersectionTreshold extra extend to be added to the box in all direction
  48. * @returns if the box was hit
  49. */
  50. intersectsBoxMinMax(minimum, maximum, intersectionTreshold = 0) {
  51. const newMinimum = Ray._TmpVector3[0].copyFromFloats(minimum.x - intersectionTreshold, minimum.y - intersectionTreshold, minimum.z - intersectionTreshold);
  52. const newMaximum = Ray._TmpVector3[1].copyFromFloats(maximum.x + intersectionTreshold, maximum.y + intersectionTreshold, maximum.z + intersectionTreshold);
  53. let d = 0.0;
  54. let maxValue = Number.MAX_VALUE;
  55. let inv;
  56. let min;
  57. let max;
  58. let temp;
  59. if (Math.abs(this.direction.x) < 0.0000001) {
  60. if (this.origin.x < newMinimum.x || this.origin.x > newMaximum.x) {
  61. return false;
  62. }
  63. }
  64. else {
  65. inv = 1.0 / this.direction.x;
  66. min = (newMinimum.x - this.origin.x) * inv;
  67. max = (newMaximum.x - this.origin.x) * inv;
  68. if (max === -Infinity) {
  69. max = Infinity;
  70. }
  71. if (min > max) {
  72. temp = min;
  73. min = max;
  74. max = temp;
  75. }
  76. d = Math.max(min, d);
  77. maxValue = Math.min(max, maxValue);
  78. if (d > maxValue) {
  79. return false;
  80. }
  81. }
  82. if (Math.abs(this.direction.y) < 0.0000001) {
  83. if (this.origin.y < newMinimum.y || this.origin.y > newMaximum.y) {
  84. return false;
  85. }
  86. }
  87. else {
  88. inv = 1.0 / this.direction.y;
  89. min = (newMinimum.y - this.origin.y) * inv;
  90. max = (newMaximum.y - this.origin.y) * inv;
  91. if (max === -Infinity) {
  92. max = Infinity;
  93. }
  94. if (min > max) {
  95. temp = min;
  96. min = max;
  97. max = temp;
  98. }
  99. d = Math.max(min, d);
  100. maxValue = Math.min(max, maxValue);
  101. if (d > maxValue) {
  102. return false;
  103. }
  104. }
  105. if (Math.abs(this.direction.z) < 0.0000001) {
  106. if (this.origin.z < newMinimum.z || this.origin.z > newMaximum.z) {
  107. return false;
  108. }
  109. }
  110. else {
  111. inv = 1.0 / this.direction.z;
  112. min = (newMinimum.z - this.origin.z) * inv;
  113. max = (newMaximum.z - this.origin.z) * inv;
  114. if (max === -Infinity) {
  115. max = Infinity;
  116. }
  117. if (min > max) {
  118. temp = min;
  119. min = max;
  120. max = temp;
  121. }
  122. d = Math.max(min, d);
  123. maxValue = Math.min(max, maxValue);
  124. if (d > maxValue) {
  125. return false;
  126. }
  127. }
  128. return true;
  129. }
  130. /**
  131. * Checks if the ray intersects a box
  132. * This does not account for the ray lenght by design to improve perfs.
  133. * @param box the bounding box to check
  134. * @param intersectionTreshold extra extend to be added to the BoundingBox in all direction
  135. * @returns if the box was hit
  136. */
  137. intersectsBox(box, intersectionTreshold = 0) {
  138. return this.intersectsBoxMinMax(box.minimum, box.maximum, intersectionTreshold);
  139. }
  140. /**
  141. * If the ray hits a sphere
  142. * @param sphere the bounding sphere to check
  143. * @param intersectionTreshold extra extend to be added to the BoundingSphere in all direction
  144. * @returns true if it hits the sphere
  145. */
  146. intersectsSphere(sphere, intersectionTreshold = 0) {
  147. const x = sphere.center.x - this.origin.x;
  148. const y = sphere.center.y - this.origin.y;
  149. const z = sphere.center.z - this.origin.z;
  150. const pyth = x * x + y * y + z * z;
  151. const radius = sphere.radius + intersectionTreshold;
  152. const rr = radius * radius;
  153. if (pyth <= rr) {
  154. return true;
  155. }
  156. const dot = x * this.direction.x + y * this.direction.y + z * this.direction.z;
  157. if (dot < 0.0) {
  158. return false;
  159. }
  160. const temp = pyth - dot * dot;
  161. return temp <= rr;
  162. }
  163. /**
  164. * If the ray hits a triange
  165. * @param vertex0 triangle vertex
  166. * @param vertex1 triangle vertex
  167. * @param vertex2 triangle vertex
  168. * @returns intersection information if hit
  169. */
  170. intersectsTriangle(vertex0, vertex1, vertex2) {
  171. const edge1 = Ray._TmpVector3[0];
  172. const edge2 = Ray._TmpVector3[1];
  173. const pvec = Ray._TmpVector3[2];
  174. const tvec = Ray._TmpVector3[3];
  175. const qvec = Ray._TmpVector3[4];
  176. vertex1.subtractToRef(vertex0, edge1);
  177. vertex2.subtractToRef(vertex0, edge2);
  178. Vector3.CrossToRef(this.direction, edge2, pvec);
  179. const det = Vector3.Dot(edge1, pvec);
  180. if (det === 0) {
  181. return null;
  182. }
  183. const invdet = 1 / det;
  184. this.origin.subtractToRef(vertex0, tvec);
  185. const bv = Vector3.Dot(tvec, pvec) * invdet;
  186. if (bv < -this.epsilon || bv > 1.0 + this.epsilon) {
  187. return null;
  188. }
  189. Vector3.CrossToRef(tvec, edge1, qvec);
  190. const bw = Vector3.Dot(this.direction, qvec) * invdet;
  191. if (bw < -this.epsilon || bv + bw > 1.0 + this.epsilon) {
  192. return null;
  193. }
  194. //check if the distance is longer than the predefined length.
  195. const distance = Vector3.Dot(edge2, qvec) * invdet;
  196. if (distance > this.length) {
  197. return null;
  198. }
  199. return new IntersectionInfo(1 - bv - bw, bv, distance);
  200. }
  201. /**
  202. * Checks if ray intersects a plane
  203. * @param plane the plane to check
  204. * @returns the distance away it was hit
  205. */
  206. intersectsPlane(plane) {
  207. let distance;
  208. const result1 = Vector3.Dot(plane.normal, this.direction);
  209. if (Math.abs(result1) < 9.99999997475243e-7) {
  210. return null;
  211. }
  212. else {
  213. const result2 = Vector3.Dot(plane.normal, this.origin);
  214. distance = (-plane.d - result2) / result1;
  215. if (distance < 0.0) {
  216. if (distance < -9.99999997475243e-7) {
  217. return null;
  218. }
  219. else {
  220. return 0;
  221. }
  222. }
  223. return distance;
  224. }
  225. }
  226. /**
  227. * Calculate the intercept of a ray on a given axis
  228. * @param axis to check 'x' | 'y' | 'z'
  229. * @param offset from axis interception (i.e. an offset of 1y is intercepted above ground)
  230. * @returns a vector containing the coordinates where 'axis' is equal to zero (else offset), or null if there is no intercept.
  231. */
  232. intersectsAxis(axis, offset = 0) {
  233. switch (axis) {
  234. case "y": {
  235. const t = (this.origin.y - offset) / this.direction.y;
  236. if (t > 0) {
  237. return null;
  238. }
  239. return new Vector3(this.origin.x + this.direction.x * -t, offset, this.origin.z + this.direction.z * -t);
  240. }
  241. case "x": {
  242. const t = (this.origin.x - offset) / this.direction.x;
  243. if (t > 0) {
  244. return null;
  245. }
  246. return new Vector3(offset, this.origin.y + this.direction.y * -t, this.origin.z + this.direction.z * -t);
  247. }
  248. case "z": {
  249. const t = (this.origin.z - offset) / this.direction.z;
  250. if (t > 0) {
  251. return null;
  252. }
  253. return new Vector3(this.origin.x + this.direction.x * -t, this.origin.y + this.direction.y * -t, offset);
  254. }
  255. default:
  256. return null;
  257. }
  258. }
  259. /**
  260. * Checks if ray intersects a mesh. The ray is defined in WORLD space. A mesh triangle can be picked both from its front and back sides,
  261. * irrespective of orientation.
  262. * @param mesh the mesh to check
  263. * @param fastCheck defines if the first intersection will be used (and not the closest)
  264. * @param trianglePredicate defines an optional predicate used to select faces when a mesh intersection is detected
  265. * @param onlyBoundingInfo defines a boolean indicating if picking should only happen using bounding info (false by default)
  266. * @param worldToUse defines the world matrix to use to get the world coordinate of the intersection point
  267. * @param skipBoundingInfo a boolean indicating if we should skip the bounding info check
  268. * @returns picking info of the intersection
  269. */
  270. intersectsMesh(mesh, fastCheck, trianglePredicate, onlyBoundingInfo = false, worldToUse, skipBoundingInfo = false) {
  271. const tm = TmpVectors.Matrix[0];
  272. mesh.getWorldMatrix().invertToRef(tm);
  273. if (this._tmpRay) {
  274. Ray.TransformToRef(this, tm, this._tmpRay);
  275. }
  276. else {
  277. this._tmpRay = Ray.Transform(this, tm);
  278. }
  279. return mesh.intersects(this._tmpRay, fastCheck, trianglePredicate, onlyBoundingInfo, worldToUse, skipBoundingInfo);
  280. }
  281. /**
  282. * Checks if ray intersects a mesh
  283. * @param meshes the meshes to check
  284. * @param fastCheck defines if the first intersection will be used (and not the closest)
  285. * @param results array to store result in
  286. * @returns Array of picking infos
  287. */
  288. intersectsMeshes(meshes, fastCheck, results) {
  289. if (results) {
  290. results.length = 0;
  291. }
  292. else {
  293. results = [];
  294. }
  295. for (let i = 0; i < meshes.length; i++) {
  296. const pickInfo = this.intersectsMesh(meshes[i], fastCheck);
  297. if (pickInfo.hit) {
  298. results.push(pickInfo);
  299. }
  300. }
  301. results.sort(this._comparePickingInfo);
  302. return results;
  303. }
  304. _comparePickingInfo(pickingInfoA, pickingInfoB) {
  305. if (pickingInfoA.distance < pickingInfoB.distance) {
  306. return -1;
  307. }
  308. else if (pickingInfoA.distance > pickingInfoB.distance) {
  309. return 1;
  310. }
  311. else {
  312. return 0;
  313. }
  314. }
  315. /**
  316. * Intersection test between the ray and a given segment within a given tolerance (threshold)
  317. * @param sega the first point of the segment to test the intersection against
  318. * @param segb the second point of the segment to test the intersection against
  319. * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
  320. * @returns the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
  321. */
  322. intersectionSegment(sega, segb, threshold) {
  323. const o = this.origin;
  324. const u = TmpVectors.Vector3[0];
  325. const rsegb = TmpVectors.Vector3[1];
  326. const v = TmpVectors.Vector3[2];
  327. const w = TmpVectors.Vector3[3];
  328. segb.subtractToRef(sega, u);
  329. this.direction.scaleToRef(Ray._Rayl, v);
  330. o.addToRef(v, rsegb);
  331. sega.subtractToRef(o, w);
  332. const a = Vector3.Dot(u, u); // always >= 0
  333. const b = Vector3.Dot(u, v);
  334. const c = Vector3.Dot(v, v); // always >= 0
  335. const d = Vector3.Dot(u, w);
  336. const e = Vector3.Dot(v, w);
  337. const D = a * c - b * b; // always >= 0
  338. let sN, sD = D; // sc = sN / sD, default sD = D >= 0
  339. let tN, tD = D; // tc = tN / tD, default tD = D >= 0
  340. // compute the line parameters of the two closest points
  341. if (D < Ray._Smallnum) {
  342. // the lines are almost parallel
  343. sN = 0.0; // force using point P0 on segment S1
  344. sD = 1.0; // to prevent possible division by 0.0 later
  345. tN = e;
  346. tD = c;
  347. }
  348. else {
  349. // get the closest points on the infinite lines
  350. sN = b * e - c * d;
  351. tN = a * e - b * d;
  352. if (sN < 0.0) {
  353. // sc < 0 => the s=0 edge is visible
  354. sN = 0.0;
  355. tN = e;
  356. tD = c;
  357. }
  358. else if (sN > sD) {
  359. // sc > 1 => the s=1 edge is visible
  360. sN = sD;
  361. tN = e + b;
  362. tD = c;
  363. }
  364. }
  365. if (tN < 0.0) {
  366. // tc < 0 => the t=0 edge is visible
  367. tN = 0.0;
  368. // recompute sc for this edge
  369. if (-d < 0.0) {
  370. sN = 0.0;
  371. }
  372. else if (-d > a) {
  373. sN = sD;
  374. }
  375. else {
  376. sN = -d;
  377. sD = a;
  378. }
  379. }
  380. else if (tN > tD) {
  381. // tc > 1 => the t=1 edge is visible
  382. tN = tD;
  383. // recompute sc for this edge
  384. if (-d + b < 0.0) {
  385. sN = 0;
  386. }
  387. else if (-d + b > a) {
  388. sN = sD;
  389. }
  390. else {
  391. sN = -d + b;
  392. sD = a;
  393. }
  394. }
  395. // finally do the division to get sc and tc
  396. const sc = Math.abs(sN) < Ray._Smallnum ? 0.0 : sN / sD;
  397. const tc = Math.abs(tN) < Ray._Smallnum ? 0.0 : tN / tD;
  398. // get the difference of the two closest points
  399. const qtc = TmpVectors.Vector3[4];
  400. v.scaleToRef(tc, qtc);
  401. const qsc = TmpVectors.Vector3[5];
  402. u.scaleToRef(sc, qsc);
  403. qsc.addInPlace(w);
  404. const dP = TmpVectors.Vector3[6];
  405. qsc.subtractToRef(qtc, dP); // = S1(sc) - S2(tc)
  406. const isIntersected = tc > 0 && tc <= this.length && dP.lengthSquared() < threshold * threshold; // return intersection result
  407. if (isIntersected) {
  408. return qsc.length();
  409. }
  410. return -1;
  411. }
  412. /**
  413. * Update the ray from viewport position
  414. * @param x position
  415. * @param y y position
  416. * @param viewportWidth viewport width
  417. * @param viewportHeight viewport height
  418. * @param world world matrix
  419. * @param view view matrix
  420. * @param projection projection matrix
  421. * @param enableDistantPicking defines if picking should handle large values for mesh position/scaling (false by default)
  422. * @returns this ray updated
  423. */
  424. update(x, y, viewportWidth, viewportHeight, world, view, projection, enableDistantPicking = false) {
  425. if (enableDistantPicking) {
  426. // With world matrices having great values (like 8000000000 on 1 or more scaling or position axis),
  427. // multiplying view/projection/world and doing invert will result in loss of float precision in the matrix.
  428. // One way to fix it is to compute the ray with world at identity then transform the ray in object space.
  429. // This is slower (2 matrix inverts instead of 1) but precision is preserved.
  430. // This is hidden behind `EnableDistantPicking` flag (default is false)
  431. if (!Ray._RayDistant) {
  432. Ray._RayDistant = Ray.Zero();
  433. }
  434. Ray._RayDistant.unprojectRayToRef(x, y, viewportWidth, viewportHeight, Matrix.IdentityReadOnly, view, projection);
  435. const tm = TmpVectors.Matrix[0];
  436. world.invertToRef(tm);
  437. Ray.TransformToRef(Ray._RayDistant, tm, this);
  438. }
  439. else {
  440. this.unprojectRayToRef(x, y, viewportWidth, viewportHeight, world, view, projection);
  441. }
  442. return this;
  443. }
  444. // Statics
  445. /**
  446. * Creates a ray with origin and direction of 0,0,0
  447. * @returns the new ray
  448. */
  449. static Zero() {
  450. return new Ray(Vector3.Zero(), Vector3.Zero());
  451. }
  452. /**
  453. * Creates a new ray from screen space and viewport
  454. * @param x position
  455. * @param y y position
  456. * @param viewportWidth viewport width
  457. * @param viewportHeight viewport height
  458. * @param world world matrix
  459. * @param view view matrix
  460. * @param projection projection matrix
  461. * @returns new ray
  462. */
  463. static CreateNew(x, y, viewportWidth, viewportHeight, world, view, projection) {
  464. const result = Ray.Zero();
  465. return result.update(x, y, viewportWidth, viewportHeight, world, view, projection);
  466. }
  467. /**
  468. * 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
  469. * transformed to the given world matrix.
  470. * @param origin The origin point
  471. * @param end The end point
  472. * @param world a matrix to transform the ray to. Default is the identity matrix.
  473. * @returns the new ray
  474. */
  475. static CreateNewFromTo(origin, end, world = Matrix.IdentityReadOnly) {
  476. const result = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
  477. return Ray.CreateFromToToRef(origin, end, result, world);
  478. }
  479. /**
  480. * Function will update a transformed ray starting from origin and ending at the end point. Ray's length will be set, and ray will be
  481. * transformed to the given world matrix.
  482. * @param origin The origin point
  483. * @param end The end point
  484. * @param result the object to store the result
  485. * @param world a matrix to transform the ray to. Default is the identity matrix.
  486. * @returns the ref ray
  487. */
  488. static CreateFromToToRef(origin, end, result, world = Matrix.IdentityReadOnly) {
  489. result.origin.copyFrom(origin);
  490. const direction = end.subtractToRef(origin, result.direction);
  491. const length = Math.sqrt(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z);
  492. result.length = length;
  493. result.direction.normalize();
  494. return Ray.TransformToRef(result, world, result);
  495. }
  496. /**
  497. * Transforms a ray by a matrix
  498. * @param ray ray to transform
  499. * @param matrix matrix to apply
  500. * @returns the resulting new ray
  501. */
  502. static Transform(ray, matrix) {
  503. const result = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
  504. Ray.TransformToRef(ray, matrix, result);
  505. return result;
  506. }
  507. /**
  508. * Transforms a ray by a matrix
  509. * @param ray ray to transform
  510. * @param matrix matrix to apply
  511. * @param result ray to store result in
  512. * @returns the updated result ray
  513. */
  514. static TransformToRef(ray, matrix, result) {
  515. Vector3.TransformCoordinatesToRef(ray.origin, matrix, result.origin);
  516. Vector3.TransformNormalToRef(ray.direction, matrix, result.direction);
  517. result.length = ray.length;
  518. result.epsilon = ray.epsilon;
  519. const dir = result.direction;
  520. const len = dir.length();
  521. if (!(len === 0 || len === 1)) {
  522. const num = 1.0 / len;
  523. dir.x *= num;
  524. dir.y *= num;
  525. dir.z *= num;
  526. result.length *= len;
  527. }
  528. return result;
  529. }
  530. /**
  531. * Unproject a ray from screen space to object space
  532. * @param sourceX defines the screen space x coordinate to use
  533. * @param sourceY defines the screen space y coordinate to use
  534. * @param viewportWidth defines the current width of the viewport
  535. * @param viewportHeight defines the current height of the viewport
  536. * @param world defines the world matrix to use (can be set to Identity to go to world space)
  537. * @param view defines the view matrix to use
  538. * @param projection defines the projection matrix to use
  539. */
  540. unprojectRayToRef(sourceX, sourceY, viewportWidth, viewportHeight, world, view, projection) {
  541. const matrix = TmpVectors.Matrix[0];
  542. world.multiplyToRef(view, matrix);
  543. matrix.multiplyToRef(projection, matrix);
  544. matrix.invert();
  545. const engine = EngineStore.LastCreatedEngine;
  546. const nearScreenSource = TmpVectors.Vector3[0];
  547. nearScreenSource.x = (sourceX / viewportWidth) * 2 - 1;
  548. nearScreenSource.y = -((sourceY / viewportHeight) * 2 - 1);
  549. nearScreenSource.z = engine?.useReverseDepthBuffer ? 1 : engine?.isNDCHalfZRange ? 0 : -1;
  550. // far Z need to be close but < to 1 or camera projection matrix with maxZ = 0 will NaN
  551. const farScreenSource = TmpVectors.Vector3[1].copyFromFloats(nearScreenSource.x, nearScreenSource.y, 1.0 - 1e-8);
  552. const nearVec3 = TmpVectors.Vector3[2];
  553. const farVec3 = TmpVectors.Vector3[3];
  554. Vector3._UnprojectFromInvertedMatrixToRef(nearScreenSource, matrix, nearVec3);
  555. Vector3._UnprojectFromInvertedMatrixToRef(farScreenSource, matrix, farVec3);
  556. this.origin.copyFrom(nearVec3);
  557. farVec3.subtractToRef(nearVec3, this.direction);
  558. this.direction.normalize();
  559. }
  560. }
  561. Ray._TmpVector3 = ArrayTools.BuildArray(6, Vector3.Zero);
  562. Ray._RayDistant = Ray.Zero();
  563. Ray._Smallnum = 0.00000001;
  564. Ray._Rayl = 10e8;
  565. Scene.prototype.createPickingRay = function (x, y, world, camera, cameraViewSpace = false) {
  566. const result = Ray.Zero();
  567. this.createPickingRayToRef(x, y, world, result, camera, cameraViewSpace);
  568. return result;
  569. };
  570. Scene.prototype.createPickingRayToRef = function (x, y, world, result, camera, cameraViewSpace = false, enableDistantPicking = false) {
  571. const engine = this.getEngine();
  572. if (!camera && !(camera = this.activeCamera)) {
  573. return this;
  574. }
  575. const cameraViewport = camera.viewport;
  576. const renderHeight = engine.getRenderHeight();
  577. const { x: vx, y: vy, width, height } = cameraViewport.toGlobal(engine.getRenderWidth(), renderHeight);
  578. // Moving coordinates to local viewport world
  579. const levelInv = 1 / engine.getHardwareScalingLevel();
  580. x = x * levelInv - vx;
  581. y = y * levelInv - (renderHeight - vy - height);
  582. result.update(x, y, width, height, world ? world : Matrix.IdentityReadOnly, cameraViewSpace ? Matrix.IdentityReadOnly : camera.getViewMatrix(), camera.getProjectionMatrix(), enableDistantPicking);
  583. return this;
  584. };
  585. Scene.prototype.createPickingRayInCameraSpace = function (x, y, camera) {
  586. const result = Ray.Zero();
  587. this.createPickingRayInCameraSpaceToRef(x, y, result, camera);
  588. return result;
  589. };
  590. Scene.prototype.createPickingRayInCameraSpaceToRef = function (x, y, result, camera) {
  591. if (!PickingInfo) {
  592. return this;
  593. }
  594. const engine = this.getEngine();
  595. if (!camera && !(camera = this.activeCamera)) {
  596. throw new Error("Active camera not set");
  597. }
  598. const cameraViewport = camera.viewport;
  599. const renderHeight = engine.getRenderHeight();
  600. const { x: vx, y: vy, width, height } = cameraViewport.toGlobal(engine.getRenderWidth(), renderHeight);
  601. const identity = Matrix.Identity();
  602. // Moving coordinates to local viewport world
  603. const levelInv = 1 / engine.getHardwareScalingLevel();
  604. x = x * levelInv - vx;
  605. y = y * levelInv - (renderHeight - vy - height);
  606. result.update(x, y, width, height, identity, identity, camera.getProjectionMatrix());
  607. return this;
  608. };
  609. Scene.prototype._internalPickForMesh = function (pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate, skipBoundingInfo) {
  610. const ray = rayFunction(world, mesh.enableDistantPicking);
  611. const result = mesh.intersects(ray, fastCheck, trianglePredicate, onlyBoundingInfo, world, skipBoundingInfo);
  612. if (!result || !result.hit) {
  613. return null;
  614. }
  615. if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance) {
  616. return null;
  617. }
  618. return result;
  619. };
  620. Scene.prototype._internalPick = function (rayFunction, predicate, fastCheck, onlyBoundingInfo, trianglePredicate) {
  621. let pickingInfo = null;
  622. const computeWorldMatrixForCamera = !!(this.activeCameras && this.activeCameras.length > 1 && this.cameraToUseForPointers !== this.activeCamera);
  623. const currentCamera = this.cameraToUseForPointers || this.activeCamera;
  624. for (let meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
  625. const mesh = this.meshes[meshIndex];
  626. if (predicate) {
  627. if (!predicate(mesh)) {
  628. continue;
  629. }
  630. }
  631. else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
  632. continue;
  633. }
  634. const forceCompute = computeWorldMatrixForCamera && mesh.isWorldMatrixCameraDependent();
  635. const world = mesh.computeWorldMatrix(forceCompute, currentCamera);
  636. if (mesh.hasThinInstances && mesh.thinInstanceEnablePicking) {
  637. // first check if the ray intersects the whole bounding box/sphere of the mesh
  638. const result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, true, true, trianglePredicate);
  639. if (result) {
  640. if (onlyBoundingInfo) {
  641. // the user only asked for a bounding info check so we can return
  642. return result;
  643. }
  644. const tmpMatrix = TmpVectors.Matrix[1];
  645. const thinMatrices = mesh.thinInstanceGetWorldMatrices();
  646. for (let index = 0; index < thinMatrices.length; index++) {
  647. const thinMatrix = thinMatrices[index];
  648. thinMatrix.multiplyToRef(world, tmpMatrix);
  649. const result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, tmpMatrix, fastCheck, onlyBoundingInfo, trianglePredicate, true);
  650. if (result) {
  651. pickingInfo = result;
  652. pickingInfo.thinInstanceIndex = index;
  653. if (fastCheck) {
  654. return pickingInfo;
  655. }
  656. }
  657. }
  658. }
  659. }
  660. else {
  661. const result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
  662. if (result) {
  663. pickingInfo = result;
  664. if (fastCheck) {
  665. return pickingInfo;
  666. }
  667. }
  668. }
  669. }
  670. return pickingInfo || new PickingInfo();
  671. };
  672. Scene.prototype._internalMultiPick = function (rayFunction, predicate, trianglePredicate) {
  673. if (!PickingInfo) {
  674. return null;
  675. }
  676. const pickingInfos = [];
  677. const computeWorldMatrixForCamera = !!(this.activeCameras && this.activeCameras.length > 1 && this.cameraToUseForPointers !== this.activeCamera);
  678. const currentCamera = this.cameraToUseForPointers || this.activeCamera;
  679. for (let meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
  680. const mesh = this.meshes[meshIndex];
  681. if (predicate) {
  682. if (!predicate(mesh)) {
  683. continue;
  684. }
  685. }
  686. else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
  687. continue;
  688. }
  689. const forceCompute = computeWorldMatrixForCamera && mesh.isWorldMatrixCameraDependent();
  690. const world = mesh.computeWorldMatrix(forceCompute, currentCamera);
  691. if (mesh.hasThinInstances && mesh.thinInstanceEnablePicking) {
  692. const result = this._internalPickForMesh(null, rayFunction, mesh, world, true, true, trianglePredicate);
  693. if (result) {
  694. const tmpMatrix = TmpVectors.Matrix[1];
  695. const thinMatrices = mesh.thinInstanceGetWorldMatrices();
  696. for (let index = 0; index < thinMatrices.length; index++) {
  697. const thinMatrix = thinMatrices[index];
  698. thinMatrix.multiplyToRef(world, tmpMatrix);
  699. const result = this._internalPickForMesh(null, rayFunction, mesh, tmpMatrix, false, false, trianglePredicate, true);
  700. if (result) {
  701. result.thinInstanceIndex = index;
  702. pickingInfos.push(result);
  703. }
  704. }
  705. }
  706. }
  707. else {
  708. const result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
  709. if (result) {
  710. pickingInfos.push(result);
  711. }
  712. }
  713. }
  714. return pickingInfos;
  715. };
  716. Scene.prototype.pickWithBoundingInfo = function (x, y, predicate, fastCheck, camera) {
  717. if (!PickingInfo) {
  718. return null;
  719. }
  720. const result = this._internalPick((world) => {
  721. if (!this._tempPickingRay) {
  722. this._tempPickingRay = Ray.Zero();
  723. }
  724. this.createPickingRayToRef(x, y, world, this._tempPickingRay, camera || null);
  725. return this._tempPickingRay;
  726. }, predicate, fastCheck, true);
  727. if (result) {
  728. result.ray = this.createPickingRay(x, y, Matrix.Identity(), camera || null);
  729. }
  730. return result;
  731. };
  732. Object.defineProperty(Scene.prototype, "_pickingAvailable", {
  733. get: () => true,
  734. enumerable: false,
  735. configurable: false,
  736. });
  737. Scene.prototype.pick = function (x, y, predicate, fastCheck, camera, trianglePredicate, _enableDistantPicking = false) {
  738. const result = this._internalPick((world, enableDistantPicking) => {
  739. if (!this._tempPickingRay) {
  740. this._tempPickingRay = Ray.Zero();
  741. }
  742. this.createPickingRayToRef(x, y, world, this._tempPickingRay, camera || null, false, enableDistantPicking);
  743. return this._tempPickingRay;
  744. }, predicate, fastCheck, false, trianglePredicate);
  745. if (result) {
  746. result.ray = this.createPickingRay(x, y, Matrix.Identity(), camera || null);
  747. }
  748. return result;
  749. };
  750. Scene.prototype.pickWithRay = function (ray, predicate, fastCheck, trianglePredicate) {
  751. const result = this._internalPick((world) => {
  752. if (!this._pickWithRayInverseMatrix) {
  753. this._pickWithRayInverseMatrix = Matrix.Identity();
  754. }
  755. world.invertToRef(this._pickWithRayInverseMatrix);
  756. if (!this._cachedRayForTransform) {
  757. this._cachedRayForTransform = Ray.Zero();
  758. }
  759. Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
  760. return this._cachedRayForTransform;
  761. }, predicate, fastCheck, false, trianglePredicate);
  762. if (result) {
  763. result.ray = ray;
  764. }
  765. return result;
  766. };
  767. Scene.prototype.multiPick = function (x, y, predicate, camera, trianglePredicate) {
  768. return this._internalMultiPick((world) => this.createPickingRay(x, y, world, camera || null), predicate, trianglePredicate);
  769. };
  770. Scene.prototype.multiPickWithRay = function (ray, predicate, trianglePredicate) {
  771. return this._internalMultiPick((world) => {
  772. if (!this._pickWithRayInverseMatrix) {
  773. this._pickWithRayInverseMatrix = Matrix.Identity();
  774. }
  775. world.invertToRef(this._pickWithRayInverseMatrix);
  776. if (!this._cachedRayForTransform) {
  777. this._cachedRayForTransform = Ray.Zero();
  778. }
  779. Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
  780. return this._cachedRayForTransform;
  781. }, predicate, trianglePredicate);
  782. };
  783. Camera.prototype.getForwardRay = function (length = 100, transform, origin) {
  784. return this.getForwardRayToRef(new Ray(Vector3.Zero(), Vector3.Zero(), length), length, transform, origin);
  785. };
  786. Camera.prototype.getForwardRayToRef = function (refRay, length = 100, transform, origin) {
  787. if (!transform) {
  788. transform = this.getWorldMatrix();
  789. }
  790. refRay.length = length;
  791. if (origin) {
  792. refRay.origin.copyFrom(origin);
  793. }
  794. else {
  795. refRay.origin.copyFrom(this.position);
  796. }
  797. const forward = TmpVectors.Vector3[2];
  798. forward.set(0, 0, this._scene.useRightHandedSystem ? -1 : 1);
  799. const worldForward = TmpVectors.Vector3[3];
  800. Vector3.TransformNormalToRef(forward, transform, worldForward);
  801. Vector3.NormalizeToRef(worldForward, refRay.direction);
  802. return refRay;
  803. };
  804. //# sourceMappingURL=ray.js.map