WebXRImageTracking.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { WebXRFeaturesManager, WebXRFeatureName } from "../webXRFeaturesManager.js";
  2. import { Observable } from "../../Misc/observable.js";
  3. import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js";
  4. import { Matrix } from "../../Maths/math.vector.js";
  5. import { Tools } from "../../Misc/tools.js";
  6. /**
  7. * Enum that describes the state of the image trackability score status for this session.
  8. */
  9. var ImageTrackingScoreStatus;
  10. (function (ImageTrackingScoreStatus) {
  11. // AR Session has not yet assessed image trackability scores.
  12. ImageTrackingScoreStatus[ImageTrackingScoreStatus["NotReceived"] = 0] = "NotReceived";
  13. // A request to retrieve trackability scores has been sent, but no response has been received.
  14. ImageTrackingScoreStatus[ImageTrackingScoreStatus["Waiting"] = 1] = "Waiting";
  15. // Image trackability scores have been received for this session
  16. ImageTrackingScoreStatus[ImageTrackingScoreStatus["Received"] = 2] = "Received";
  17. })(ImageTrackingScoreStatus || (ImageTrackingScoreStatus = {}));
  18. /**
  19. * Image tracking for immersive AR sessions.
  20. * Providing a list of images and their estimated widths will enable tracking those images in the real world.
  21. */
  22. export class WebXRImageTracking extends WebXRAbstractFeature {
  23. /**
  24. * constructs the image tracking feature
  25. * @param _xrSessionManager the session manager for this module
  26. * @param options read-only options to be used in this module
  27. */
  28. constructor(_xrSessionManager,
  29. /**
  30. * read-only options to be used in this module
  31. */
  32. options) {
  33. super(_xrSessionManager);
  34. this.options = options;
  35. /**
  36. * This will be triggered if the underlying system deems an image untrackable.
  37. * The index is the index of the image from the array used to initialize the feature.
  38. */
  39. this.onUntrackableImageFoundObservable = new Observable();
  40. /**
  41. * An image was deemed trackable, and the system will start tracking it.
  42. */
  43. this.onTrackableImageFoundObservable = new Observable();
  44. /**
  45. * The image was found and its state was updated.
  46. */
  47. this.onTrackedImageUpdatedObservable = new Observable();
  48. this._trackableScoreStatus = ImageTrackingScoreStatus.NotReceived;
  49. this._trackedImages = [];
  50. this.xrNativeFeatureName = "image-tracking";
  51. }
  52. /**
  53. * attach this feature
  54. * Will usually be called by the features manager
  55. *
  56. * @returns true if successful.
  57. */
  58. attach() {
  59. return super.attach();
  60. }
  61. /**
  62. * detach this feature.
  63. * Will usually be called by the features manager
  64. *
  65. * @returns true if successful.
  66. */
  67. detach() {
  68. return super.detach();
  69. }
  70. /**
  71. * Get a tracked image by its ID.
  72. *
  73. * @param id the id of the image to load (position in the init array)
  74. * @returns a trackable image, if exists in this location
  75. */
  76. getTrackedImageById(id) {
  77. return this._trackedImages[id] || null;
  78. }
  79. /**
  80. * Dispose this feature and all of the resources attached
  81. */
  82. dispose() {
  83. super.dispose();
  84. this._trackedImages.forEach((trackedImage) => {
  85. trackedImage.originalBitmap.close();
  86. });
  87. this._trackedImages.length = 0;
  88. this.onTrackableImageFoundObservable.clear();
  89. this.onUntrackableImageFoundObservable.clear();
  90. this.onTrackedImageUpdatedObservable.clear();
  91. }
  92. /**
  93. * Extends the session init object if needed
  94. * @returns augmentation object fo the xr session init object.
  95. */
  96. async getXRSessionInitExtension() {
  97. if (!this.options.images || !this.options.images.length) {
  98. return {};
  99. }
  100. const promises = this.options.images.map((image) => {
  101. if (typeof image.src === "string") {
  102. return this._xrSessionManager.scene.getEngine()._createImageBitmapFromSource(image.src);
  103. }
  104. else {
  105. return Promise.resolve(image.src); // resolve is probably unneeded
  106. }
  107. });
  108. try {
  109. const images = await Promise.all(promises);
  110. this._originalTrackingRequest = images.map((image, idx) => {
  111. return {
  112. image,
  113. widthInMeters: this.options.images[idx].estimatedRealWorldWidth,
  114. };
  115. });
  116. return {
  117. trackedImages: this._originalTrackingRequest,
  118. };
  119. }
  120. catch (ex) {
  121. Tools.Error("Error loading images for tracking, WebXRImageTracking disabled for this session.");
  122. return {};
  123. }
  124. }
  125. _onXRFrame(_xrFrame) {
  126. if (!_xrFrame.getImageTrackingResults || this._trackableScoreStatus === ImageTrackingScoreStatus.Waiting) {
  127. return;
  128. }
  129. // Image tracking scores may be generated a few frames after the XR Session initializes.
  130. // If we haven't received scores yet, then kick off the task to check scores and return immediately.
  131. if (this._trackableScoreStatus === ImageTrackingScoreStatus.NotReceived) {
  132. this._checkScoresAsync();
  133. return;
  134. }
  135. const imageTrackedResults = _xrFrame.getImageTrackingResults();
  136. for (const result of imageTrackedResults) {
  137. let changed = false;
  138. const imageIndex = result.index;
  139. const imageObject = this._trackedImages[imageIndex];
  140. if (!imageObject) {
  141. // something went wrong!
  142. continue;
  143. }
  144. imageObject.xrTrackingResult = result;
  145. if (imageObject.realWorldWidth !== result.measuredWidthInMeters) {
  146. imageObject.realWorldWidth = result.measuredWidthInMeters;
  147. changed = true;
  148. }
  149. // Get the pose of the image relative to a reference space.
  150. const pose = _xrFrame.getPose(result.imageSpace, this._xrSessionManager.referenceSpace);
  151. if (pose) {
  152. const mat = imageObject.transformationMatrix;
  153. Matrix.FromArrayToRef(pose.transform.matrix, 0, mat);
  154. if (!this._xrSessionManager.scene.useRightHandedSystem) {
  155. mat.toggleModelMatrixHandInPlace();
  156. }
  157. changed = true;
  158. }
  159. const state = result.trackingState;
  160. const emulated = state === "emulated";
  161. if (imageObject.emulated !== emulated) {
  162. imageObject.emulated = emulated;
  163. changed = true;
  164. }
  165. if (changed) {
  166. this.onTrackedImageUpdatedObservable.notifyObservers(imageObject);
  167. }
  168. }
  169. }
  170. async _checkScoresAsync() {
  171. if (!this._xrSessionManager.session.getTrackedImageScores || this._trackableScoreStatus !== ImageTrackingScoreStatus.NotReceived) {
  172. return;
  173. }
  174. this._trackableScoreStatus = ImageTrackingScoreStatus.Waiting;
  175. const imageScores = await this._xrSessionManager.session.getTrackedImageScores();
  176. if (!imageScores || imageScores.length === 0) {
  177. this._trackableScoreStatus = ImageTrackingScoreStatus.NotReceived;
  178. return;
  179. }
  180. // check the scores for all
  181. for (let idx = 0; idx < imageScores.length; ++idx) {
  182. if (imageScores[idx] == "untrackable") {
  183. this.onUntrackableImageFoundObservable.notifyObservers(idx);
  184. }
  185. else {
  186. const originalBitmap = this._originalTrackingRequest[idx].image;
  187. const imageObject = {
  188. id: idx,
  189. originalBitmap,
  190. transformationMatrix: new Matrix(),
  191. ratio: originalBitmap.width / originalBitmap.height,
  192. };
  193. this._trackedImages[idx] = imageObject;
  194. this.onTrackableImageFoundObservable.notifyObservers(imageObject);
  195. }
  196. }
  197. this._trackableScoreStatus = imageScores.length > 0 ? ImageTrackingScoreStatus.Received : ImageTrackingScoreStatus.NotReceived;
  198. }
  199. }
  200. /**
  201. * The module's name
  202. */
  203. WebXRImageTracking.Name = WebXRFeatureName.IMAGE_TRACKING;
  204. /**
  205. * The (Babylon) version of this module.
  206. * This is an integer representing the implementation version.
  207. * This number does not correspond to the WebXR specs version
  208. */
  209. WebXRImageTracking.Version = 1;
  210. //register the plugin
  211. WebXRFeaturesManager.AddWebXRFeature(WebXRImageTracking.Name, (xrSessionManager, options) => {
  212. return () => new WebXRImageTracking(xrSessionManager, options);
  213. }, WebXRImageTracking.Version, false);
  214. //# sourceMappingURL=WebXRImageTracking.js.map