@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
215 lines • 8.83 kB
JavaScript
import { WebXRFeaturesManager, WebXRFeatureName } from "../webXRFeaturesManager.js";
import { Observable } from "../../Misc/observable.js";
import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js";
import { Matrix } from "../../Maths/math.vector.js";
import { Tools } from "../../Misc/tools.js";
/**
* Enum that describes the state of the image trackability score status for this session.
*/
var ImageTrackingScoreStatus;
(function (ImageTrackingScoreStatus) {
// AR Session has not yet assessed image trackability scores.
ImageTrackingScoreStatus[ImageTrackingScoreStatus["NotReceived"] = 0] = "NotReceived";
// A request to retrieve trackability scores has been sent, but no response has been received.
ImageTrackingScoreStatus[ImageTrackingScoreStatus["Waiting"] = 1] = "Waiting";
// Image trackability scores have been received for this session
ImageTrackingScoreStatus[ImageTrackingScoreStatus["Received"] = 2] = "Received";
})(ImageTrackingScoreStatus || (ImageTrackingScoreStatus = {}));
/**
* Image tracking for immersive AR sessions.
* Providing a list of images and their estimated widths will enable tracking those images in the real world.
*/
export class WebXRImageTracking extends WebXRAbstractFeature {
/**
* constructs the image tracking feature
* @param _xrSessionManager the session manager for this module
* @param options read-only options to be used in this module
*/
constructor(_xrSessionManager,
/**
* read-only options to be used in this module
*/
options) {
super(_xrSessionManager);
this.options = options;
/**
* This will be triggered if the underlying system deems an image untrackable.
* The index is the index of the image from the array used to initialize the feature.
*/
this.onUntrackableImageFoundObservable = new Observable();
/**
* An image was deemed trackable, and the system will start tracking it.
*/
this.onTrackableImageFoundObservable = new Observable();
/**
* The image was found and its state was updated.
*/
this.onTrackedImageUpdatedObservable = new Observable();
this._trackableScoreStatus = ImageTrackingScoreStatus.NotReceived;
this._trackedImages = [];
this.xrNativeFeatureName = "image-tracking";
}
/**
* attach this feature
* Will usually be called by the features manager
*
* @returns true if successful.
*/
attach() {
return super.attach();
}
/**
* detach this feature.
* Will usually be called by the features manager
*
* @returns true if successful.
*/
detach() {
return super.detach();
}
/**
* Get a tracked image by its ID.
*
* @param id the id of the image to load (position in the init array)
* @returns a trackable image, if exists in this location
*/
getTrackedImageById(id) {
return this._trackedImages[id] || null;
}
/**
* Dispose this feature and all of the resources attached
*/
dispose() {
super.dispose();
for (const trackedImage of this._trackedImages) {
trackedImage.originalBitmap.close();
}
this._trackedImages.length = 0;
this.onTrackableImageFoundObservable.clear();
this.onUntrackableImageFoundObservable.clear();
this.onTrackedImageUpdatedObservable.clear();
}
/**
* Extends the session init object if needed
* @returns augmentation object fo the xr session init object.
*/
async getXRSessionInitExtension() {
if (!this.options.images || !this.options.images.length) {
return {};
}
const promises = this.options.images.map(async (image) => {
if (typeof image.src === "string") {
return await this._xrSessionManager.scene.getEngine()._createImageBitmapFromSource(image.src);
}
else {
return image.src;
}
});
try {
const images = await Promise.all(promises);
this._originalTrackingRequest = images.map((image, idx) => {
return {
image,
widthInMeters: this.options.images[idx].estimatedRealWorldWidth,
};
});
return {
trackedImages: this._originalTrackingRequest,
};
}
catch (ex) {
Tools.Error("Error loading images for tracking, WebXRImageTracking disabled for this session.");
return {};
}
}
_onXRFrame(_xrFrame) {
if (!_xrFrame.getImageTrackingResults || this._trackableScoreStatus === ImageTrackingScoreStatus.Waiting) {
return;
}
// Image tracking scores may be generated a few frames after the XR Session initializes.
// If we haven't received scores yet, then kick off the task to check scores and return immediately.
if (this._trackableScoreStatus === ImageTrackingScoreStatus.NotReceived) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._checkScoresAsync();
return;
}
const imageTrackedResults = _xrFrame.getImageTrackingResults();
for (const result of imageTrackedResults) {
let changed = false;
const imageIndex = result.index;
const imageObject = this._trackedImages[imageIndex];
if (!imageObject) {
// something went wrong!
continue;
}
imageObject.xrTrackingResult = result;
if (imageObject.realWorldWidth !== result.measuredWidthInMeters) {
imageObject.realWorldWidth = result.measuredWidthInMeters;
changed = true;
}
// Get the pose of the image relative to a reference space.
const pose = _xrFrame.getPose(result.imageSpace, this._xrSessionManager.referenceSpace);
if (pose) {
const mat = imageObject.transformationMatrix;
Matrix.FromArrayToRef(pose.transform.matrix, 0, mat);
if (!this._xrSessionManager.scene.useRightHandedSystem) {
mat.toggleModelMatrixHandInPlace();
}
changed = true;
}
const state = result.trackingState;
const emulated = state === "emulated";
if (imageObject.emulated !== emulated) {
imageObject.emulated = emulated;
changed = true;
}
if (changed) {
this.onTrackedImageUpdatedObservable.notifyObservers(imageObject);
}
}
}
async _checkScoresAsync() {
if (!this._xrSessionManager.session.getTrackedImageScores || this._trackableScoreStatus !== ImageTrackingScoreStatus.NotReceived) {
return;
}
this._trackableScoreStatus = ImageTrackingScoreStatus.Waiting;
const imageScores = await this._xrSessionManager.session.getTrackedImageScores();
if (!imageScores || imageScores.length === 0) {
this._trackableScoreStatus = ImageTrackingScoreStatus.NotReceived;
return;
}
// check the scores for all
for (let idx = 0; idx < imageScores.length; ++idx) {
if (imageScores[idx] == "untrackable") {
this.onUntrackableImageFoundObservable.notifyObservers(idx);
}
else {
const originalBitmap = this._originalTrackingRequest[idx].image;
const imageObject = {
id: idx,
originalBitmap,
transformationMatrix: new Matrix(),
ratio: originalBitmap.width / originalBitmap.height,
};
this._trackedImages[idx] = imageObject;
this.onTrackableImageFoundObservable.notifyObservers(imageObject);
}
}
this._trackableScoreStatus = imageScores.length > 0 ? ImageTrackingScoreStatus.Received : ImageTrackingScoreStatus.NotReceived;
}
}
/**
* The module's name
*/
WebXRImageTracking.Name = WebXRFeatureName.IMAGE_TRACKING;
/**
* The (Babylon) version of this module.
* This is an integer representing the implementation version.
* This number does not correspond to the WebXR specs version
*/
WebXRImageTracking.Version = 1;
//register the plugin
WebXRFeaturesManager.AddWebXRFeature(WebXRImageTracking.Name, (xrSessionManager, options) => {
return () => new WebXRImageTracking(xrSessionManager, options);
}, WebXRImageTracking.Version, false);
//# sourceMappingURL=WebXRImageTracking.js.map