|
@@ -1,56 +1,150 @@
|
|
|
import { WebXRSessionManager } from './webXRSessionManager';
|
|
|
import { IDisposable } from '../../scene';
|
|
|
|
|
|
+/**
|
|
|
+ * Defining the interface required for a (webxr) feature
|
|
|
+ */
|
|
|
export interface WebXRFeature extends IDisposable {
|
|
|
+ /**
|
|
|
+ * Attach the feature to the session
|
|
|
+ * Will usually be called by the features manager
|
|
|
+ */
|
|
|
attach(): boolean;
|
|
|
+ /**
|
|
|
+ * Detach the feature from the session
|
|
|
+ * Will usually be called by the features manager
|
|
|
+ */
|
|
|
detach(): boolean;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Defining the constructor of a feature. Used to register the modules.
|
|
|
+ */
|
|
|
export type WebXRFeatureConstructor = (xrSessionManager: WebXRSessionManager, options?: any) => (() => WebXRFeature);
|
|
|
|
|
|
+/**
|
|
|
+ * The WebXR features manager is responsible of enabling or disabling features required for the current XR session.
|
|
|
+ * It is mainly used in AR sessions.
|
|
|
+ *
|
|
|
+ * A feature can have a version that is defined by Babylon (and does not correspond with the webxr version).
|
|
|
+ */
|
|
|
export class WebXRFeaturesManager implements IDisposable {
|
|
|
|
|
|
- private static readonly _AvailableFeatures: { [name: string]: WebXRFeatureConstructor } = {};
|
|
|
+ private static readonly _AvailableFeatures: {
|
|
|
+ [name: string]: {
|
|
|
+ stable: number;
|
|
|
+ latest: number;
|
|
|
+ [version: number]: WebXRFeatureConstructor;
|
|
|
+ }
|
|
|
+ } = {};
|
|
|
|
|
|
- public static AddWebXRFeature(featureName: string, constructorFunction: WebXRFeatureConstructor, version: number = 1) {
|
|
|
- this._AvailableFeatures[`${featureName}-${version}`] = constructorFunction;
|
|
|
+ /**
|
|
|
+ * Used to register a module. After calling this function a developer can use this feature in the scene.
|
|
|
+ * Mainly used internally.
|
|
|
+ *
|
|
|
+ * @param featureName the name of the feature to register
|
|
|
+ * @param constructorFunction the function used to construct the module
|
|
|
+ * @param version the (babylon) version of the module
|
|
|
+ * @param stable is that a stable version of this module
|
|
|
+ */
|
|
|
+ public static AddWebXRFeature(featureName: string, constructorFunction: WebXRFeatureConstructor, version: number = 1, stable: boolean = false) {
|
|
|
+ this._AvailableFeatures[featureName] = this._AvailableFeatures[featureName] || { latest: version };
|
|
|
+ if (version > this._AvailableFeatures[featureName].latest) {
|
|
|
+ this._AvailableFeatures[featureName].latest = version;
|
|
|
+ }
|
|
|
+ if (stable) {
|
|
|
+ this._AvailableFeatures[featureName].stable = version;
|
|
|
+ }
|
|
|
+ this._AvailableFeatures[featureName][version] = constructorFunction;
|
|
|
}
|
|
|
|
|
|
- public static ConstructFeature(featureName: string, xrSessionManager: WebXRSessionManager, options?: any) {
|
|
|
- const constructorFunction = this._AvailableFeatures[featureName];
|
|
|
+ /**
|
|
|
+ * Returns a constructor of a specific feature.
|
|
|
+ *
|
|
|
+ * @param featureName the name of the feature to construct
|
|
|
+ * @param version the version of the feature to load
|
|
|
+ * @param xrSessionManager the xrSessionManager. Used to construct the module
|
|
|
+ * @param options optional options provided to the module.
|
|
|
+ * @returns a function that, when called, will return a new instance of this feature
|
|
|
+ */
|
|
|
+ public static ConstructFeature(featureName: string, version: number = 1, xrSessionManager: WebXRSessionManager, options?: any): (() => WebXRFeature) {
|
|
|
+ const constructorFunction = this._AvailableFeatures[featureName][version];
|
|
|
if (!constructorFunction) {
|
|
|
// throw an error? return nothing?
|
|
|
- return;
|
|
|
+ throw new Error('feature not found');
|
|
|
}
|
|
|
|
|
|
return constructorFunction(xrSessionManager, options);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Return the latest unstable version of this feature
|
|
|
+ * @param featureName the name of the feature to search
|
|
|
+ * @returns the version number. if not found will return -1
|
|
|
+ * @static
|
|
|
+ */
|
|
|
+ public static GetLatestVersionOfFeature(featureName: string): number {
|
|
|
+ return (this._AvailableFeatures[featureName] && this._AvailableFeatures[featureName].latest) || -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Return the latest stable version of this feature
|
|
|
+ * @param featureName the name of the feature to search
|
|
|
+ * @returns the version number. if not found will return -1
|
|
|
+ * @static
|
|
|
+ */
|
|
|
+ public static GetStableVersionOfFeature(featureName: string): number {
|
|
|
+ return (this._AvailableFeatures[featureName] && this._AvailableFeatures[featureName].stable) || -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Can be used to return the list of features currently registered
|
|
|
+ *
|
|
|
+ * @static
|
|
|
+ * @returns an Array of available features
|
|
|
+ */
|
|
|
public static GetAvailableFeatures() {
|
|
|
return Object.keys(this._AvailableFeatures);
|
|
|
}
|
|
|
|
|
|
- private features: {
|
|
|
+ /**
|
|
|
+ * Gets the versions available for a specific feature
|
|
|
+ * @param featureName the name of the feature
|
|
|
+ * @returns an array with the available versions
|
|
|
+ */
|
|
|
+ public static GetAvailableVersions(featureName: string) {
|
|
|
+ return Object.keys(this._AvailableFeatures[featureName]);
|
|
|
+ }
|
|
|
+
|
|
|
+ private _features: {
|
|
|
[name: string]: {
|
|
|
featureImplementation: WebXRFeature,
|
|
|
+ version: number,
|
|
|
enabled: boolean,
|
|
|
attached: boolean
|
|
|
}
|
|
|
} = {};
|
|
|
|
|
|
+ /**
|
|
|
+ * constructs a new features manages.
|
|
|
+ *
|
|
|
+ * @param xrSessionManager an instance of WebXRSessionManager
|
|
|
+ */
|
|
|
constructor(private xrSessionManager: WebXRSessionManager) {
|
|
|
+ // when session starts / initialized - attach
|
|
|
this.xrSessionManager.onXRSessionInit.add(() => {
|
|
|
this.getEnabledFeatures().forEach((featureName) => {
|
|
|
- const feature = this.features[featureName];
|
|
|
+ const feature = this._features[featureName];
|
|
|
if (feature.enabled && !feature.attached) {
|
|
|
this.attachFeature(featureName);
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
+ // when session ends - detach
|
|
|
this.xrSessionManager.onXRSessionEnded.add(() => {
|
|
|
this.getEnabledFeatures().forEach((featureName) => {
|
|
|
- const feature = this.features[featureName];
|
|
|
+ const feature = this._features[featureName];
|
|
|
if (feature.enabled && feature.attached) {
|
|
|
// detach, but don't disable!
|
|
|
this.detachFeature(featureName);
|
|
@@ -59,20 +153,47 @@ export class WebXRFeaturesManager implements IDisposable {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public enableFeature(featureName: string | { Name: string }, version: number = 1, moduleOptions: any = {}, attachIfPossible: boolean = true): WebXRFeature {
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param featureName the name of the feature to load or the class of the feature
|
|
|
+ * @param version optional version to load. if not provided the latest version will be enabled
|
|
|
+ * @param moduleOptions options provided to the module. Ses the module documentation / constructor
|
|
|
+ * @param attachIfPossible if set to true (default) the feature will be automatically attached, if it is currently possible
|
|
|
+ * @returns a new constructed feature or throws an error if feature not found.
|
|
|
+ */
|
|
|
+ public enableFeature(featureName: string | { Name: string }, version: number | string = 'latest', moduleOptions: any = {}, attachIfPossible: boolean = true): WebXRFeature {
|
|
|
const name = typeof featureName === 'string' ? featureName : featureName.Name;
|
|
|
- const feature = this.features[name];
|
|
|
- if (!feature || !feature.featureImplementation) {
|
|
|
- const constructFunction = WebXRFeaturesManager.ConstructFeature(name, this.xrSessionManager, moduleOptions);
|
|
|
+ let versionToLoad = 0;
|
|
|
+ if (typeof version === 'string') {
|
|
|
+ if (version === 'stable') {
|
|
|
+ version = WebXRFeaturesManager.GetStableVersionOfFeature(name);
|
|
|
+ } else if (version === 'latest') {
|
|
|
+ version = WebXRFeaturesManager.GetLatestVersionOfFeature(name);
|
|
|
+ }
|
|
|
+ if (version === -1) {
|
|
|
+ throw new Error(`feature not found - ${name} (${version})`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ versionToLoad = version;
|
|
|
+ }
|
|
|
+ // check if already initialized
|
|
|
+ const feature = this._features[name];
|
|
|
+ if (!feature || !feature.featureImplementation || feature.version !== versionToLoad) {
|
|
|
+ const constructFunction = WebXRFeaturesManager.ConstructFeature(name, versionToLoad, this.xrSessionManager, moduleOptions);
|
|
|
if (!constructFunction) {
|
|
|
// report error?
|
|
|
throw new Error(`feature not found - ${name}`);
|
|
|
}
|
|
|
|
|
|
- this.features[name] = {
|
|
|
+ if (feature) {
|
|
|
+ this.disableFeature(name);
|
|
|
+ }
|
|
|
+
|
|
|
+ this._features[name] = {
|
|
|
featureImplementation: constructFunction(),
|
|
|
attached: false,
|
|
|
- enabled: true
|
|
|
+ enabled: true,
|
|
|
+ version: versionToLoad
|
|
|
};
|
|
|
} else {
|
|
|
// make sure it is enabled now:
|
|
@@ -85,44 +206,70 @@ export class WebXRFeaturesManager implements IDisposable {
|
|
|
this.attachFeature(name);
|
|
|
}
|
|
|
|
|
|
- return this.features[name].featureImplementation;
|
|
|
+ return this._features[name].featureImplementation;
|
|
|
}
|
|
|
|
|
|
- public disableFeature(featureName: string | { Name: string }) {
|
|
|
+ /**
|
|
|
+ * Used to disable an already-enabled feature
|
|
|
+ * @param featureName the feature to disable
|
|
|
+ */
|
|
|
+ public disableFeature(featureName: string | { Name: string }): boolean {
|
|
|
const name = typeof featureName === 'string' ? featureName : featureName.Name;
|
|
|
- const feature = this.features[name];
|
|
|
+ const feature = this._features[name];
|
|
|
if (feature && feature.enabled) {
|
|
|
feature.enabled = false;
|
|
|
this.detachFeature(name);
|
|
|
+ return true;
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Attach a feature to the current session. Mainly used when session started to start the feature effect.
|
|
|
+ * Can be used during a session to start a feature
|
|
|
+ * @param featureName the name of feature to attach
|
|
|
+ */
|
|
|
public attachFeature(featureName: string) {
|
|
|
- const feature = this.features[featureName];
|
|
|
+ const feature = this._features[featureName];
|
|
|
if (feature && feature.enabled && !feature.attached) {
|
|
|
feature.featureImplementation.attach();
|
|
|
feature.attached = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Can be used inside a session or when the session ends to detach a specific feature
|
|
|
+ * @param featureName the name of the feature to detach
|
|
|
+ */
|
|
|
public detachFeature(featureName: string) {
|
|
|
- const feature = this.features[featureName];
|
|
|
+ const feature = this._features[featureName];
|
|
|
if (feature && feature.attached) {
|
|
|
feature.featureImplementation.detach();
|
|
|
feature.attached = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Get the list of enabled features
|
|
|
+ * @returns an array of enabled features
|
|
|
+ */
|
|
|
public getEnabledFeatures() {
|
|
|
- return Object.keys(this.features);
|
|
|
+ return Object.keys(this._features);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * get the implementation of an enabled feature.
|
|
|
+ * @param featureName the name of the feature to load
|
|
|
+ */
|
|
|
public getEnabledFeature(featureName: string) {
|
|
|
- return this.features[featureName] && this.features[featureName].featureImplementation;
|
|
|
+ return this._features[featureName] && this._features[featureName].featureImplementation;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * dispose this features manager
|
|
|
+ */
|
|
|
dispose(): void {
|
|
|
- this.getEnabledFeatures().forEach((feature) => this.features[feature].featureImplementation.dispose());
|
|
|
+ this.getEnabledFeatures().forEach((feature) => this._features[feature].featureImplementation.dispose());
|
|
|
}
|
|
|
|
|
|
}
|