123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- import { WebXRFeaturesManager, WebXRFeatureName } from "../webXRFeaturesManager";
- import { WebXRSessionManager } from "../webXRSessionManager";
- import { Observable } from "../../Misc/observable";
- import { WebXRAbstractFeature } from "./WebXRAbstractFeature";
- import { Matrix } from "../../Maths/math.vector";
- import { Tools } from "../../Misc/tools";
- import { Nullable } from "../../types";
- declare const XRImageTrackingResult: XRImageTrackingResult;
- /**
- * Options interface for the background remover plugin
- */
- export interface IWebXRImageTrackingOptions {
- /**
- * A required array with images to track
- */
- images: {
- /**
- * The source of the image. can be a URL or an image bitmap
- */
- src: string | ImageBitmap;
- /**
- * The estimated width in the real world (in meters)
- */
- estimatedRealWorldWidth: number; // In meters!
- }[];
- }
- /**
- * An object representing an image tracked by the system
- */
- export interface IWebXRTrackedImage {
- /**
- * The ID of this image (which is the same as the position in the array that was used to initialize the feature)
- */
- id: number;
- /**
- * Is the transformation provided emulated. If it is, the system "guesses" its real position. Otherwise it can be considered as exact position.
- */
- emulated?: boolean;
- /**
- * Just in case it is needed - the image bitmap that is being tracked
- */
- originalBitmap: ImageBitmap;
- /**
- * The native XR result image tracking result, untouched
- */
- xrTrackingResult?: XRImageTrackingResult;
- /**
- * Width in real world (meters)
- */
- realWorldWidth?: number;
- /**
- * A transformation matrix of this current image in the current reference space.
- */
- transformationMatrix: Matrix;
- /**
- * The width/height ratio of this image. can be used to calculate the size of the detected object/image
- */
- ratio?: number;
- }
- /**
- * 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 {
- /**
- * The module's name
- */
- public static readonly 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
- */
- public static readonly Version = 1;
- /**
- * 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.
- */
- public onUntrackableImageFoundObservable: Observable<number> = new Observable();
- /**
- * An image was deemed trackable, and the system will start tracking it.
- */
- public onTrackableImageFoundObservable: Observable<IWebXRTrackedImage> = new Observable();
- /**
- * The image was found and its state was updated.
- */
- public onTrackedImageUpdatedObservable: Observable<IWebXRTrackedImage> = new Observable();
- private _trackedImages: IWebXRTrackedImage[] = [];
- private _originalTrackingRequest: XRTrackedImageInit[];
- /**
- * 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: WebXRSessionManager,
- /**
- * read-only options to be used in this module
- */
- public readonly options: IWebXRImageTrackingOptions
- ) {
- super(_xrSessionManager);
- this.xrNativeFeatureName = "image-tracking";
- if (this.options.images.length === 0) {
- // no images provided?... return.
- return;
- }
- if (this._xrSessionManager.session) {
- this._init();
- } else {
- this._xrSessionManager.onXRSessionInit.addOnce(() => {
- this._init();
- });
- }
- }
- /**
- * attach this feature
- * Will usually be called by the features manager
- *
- * @returns true if successful.
- */
- public attach(): boolean {
- return super.attach();
- }
- /**
- * detach this feature.
- * Will usually be called by the features manager
- *
- * @returns true if successful.
- */
- public detach(): boolean {
- return super.detach();
- }
- /**
- * Check if the needed objects are defined.
- * This does not mean that the feature is enabled, but that the objects needed are well defined.
- */
- public isCompatible(): boolean {
- return typeof XRImageTrackingResult !== "undefined";
- }
- /**
- * 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
- */
- public getTrackedImageById(id: number): Nullable<IWebXRTrackedImage> {
- return this._trackedImages[id] || null;
- }
- /**
- * Dispose this feature and all of the resources attached
- */
- public dispose(): void {
- super.dispose();
- this._trackedImages.forEach((trackedImage) => {
- 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.
- */
- public async getXRSessionInitExtension(): Promise<Partial<XRSessionInit>> {
- if (!this.options.images || !this.options.images.length) {
- return {};
- }
- const promises = this.options.images.map((image) => {
- if (typeof image.src === "string") {
- const p = new Promise<ImageBitmap>((resolve, reject) => {
- if (typeof image.src === "string") {
- const img = new Image();
- img.src = image.src;
- img.onload = () => {
- img.decode().then(() => {
- createImageBitmap(img).then((imageBitmap) => {
- resolve(imageBitmap);
- });
- });
- };
- img.onerror = () => {
- Tools.Error(`Error loading image ${image.src}`);
- reject(`Error loading image ${image.src}`);
- };
- }
- });
- return p;
- } else {
- return Promise.resolve(image.src); // resolve is probably unneeded
- }
- });
- const images = await Promise.all(promises);
- this._originalTrackingRequest = images.map((image, idx) => {
- return {
- image,
- widthInMeters: this.options.images[idx].estimatedRealWorldWidth,
- };
- });
- return {
- trackedImages: this._originalTrackingRequest,
- };
- }
- protected _onXRFrame(_xrFrame: XRFrame) {
- if (!_xrFrame.getImageTrackingResults) {
- 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);
- }
- }
- }
- private async _init() {
- if (!this._xrSessionManager.session.getTrackedImageScores) {
- return;
- }
- //
- const imageScores = await this._xrSessionManager.session.getTrackedImageScores();
- // 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: IWebXRTrackedImage = {
- id: idx,
- originalBitmap,
- transformationMatrix: new Matrix(),
- ratio: originalBitmap.width / originalBitmap.height,
- };
- this._trackedImages[idx] = imageObject;
- this.onTrackableImageFoundObservable.notifyObservers(imageObject);
- }
- }
- }
- }
- //register the plugin
- WebXRFeaturesManager.AddWebXRFeature(
- WebXRImageTracking.Name,
- (xrSessionManager, options) => {
- return () => new WebXRImageTracking(xrSessionManager, options);
- },
- WebXRImageTracking.Version,
- false
- );
|