webXRFeaturesManager.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { WebXRSessionManager } from './webXRSessionManager';
  2. import { IDisposable } from '../../scene';
  3. export interface WebXRFeature extends IDisposable {
  4. attach(): boolean;
  5. detach(): boolean;
  6. }
  7. export type WebXRFeatureConstructor = (xrSessionManager: WebXRSessionManager, options?: any) => (() => WebXRFeature);
  8. export class WebXRFeaturesManager implements IDisposable {
  9. private static readonly _AvailableFeatures: { [name: string]: WebXRFeatureConstructor } = {};
  10. public static AddWebXRFeature(featureName: string, constructorFunction: WebXRFeatureConstructor) {
  11. this._AvailableFeatures[featureName] = constructorFunction;
  12. }
  13. public static ConstructFeature(featureName: string, xrSessionManager: WebXRSessionManager, options?: any) {
  14. const constructorFunction = this._AvailableFeatures[featureName];
  15. if (!constructorFunction) {
  16. // throw an error? return nothing?
  17. return;
  18. }
  19. return constructorFunction(xrSessionManager, options);
  20. }
  21. public static GetAvailableFeatures() {
  22. return Object.keys(this._AvailableFeatures);
  23. }
  24. private features: {
  25. [name: string]: {
  26. featureImplementation: WebXRFeature,
  27. enabled: boolean,
  28. attached: boolean
  29. }
  30. } = {};
  31. constructor(private xrSessionManager: WebXRSessionManager) {
  32. this.xrSessionManager.onXRSessionInit.add(() => {
  33. this.getEnabledFeatures().forEach((featureName) => {
  34. const feature = this.features[featureName];
  35. if (feature.enabled && !feature.attached) {
  36. this.attachFeature(featureName);
  37. }
  38. });
  39. });
  40. this.xrSessionManager.onXRSessionEnded.add(() => {
  41. this.getEnabledFeatures().forEach((featureName) => {
  42. const feature = this.features[featureName];
  43. if (feature.enabled && feature.attached) {
  44. // detach, but don't disable!
  45. this.detachFeature(featureName);
  46. }
  47. });
  48. });
  49. }
  50. public enableFeature(featureName: string | { Name: string }, options: any = {}, attachIfPossible: boolean = true): WebXRFeature {
  51. const name = typeof featureName === 'string' ? featureName : featureName.Name;
  52. const feature = this.features[name];
  53. if (!feature || !feature.featureImplementation) {
  54. const constructFunction = WebXRFeaturesManager.ConstructFeature(name, this.xrSessionManager, options);
  55. if (!constructFunction) {
  56. // report error?
  57. throw new Error(`feature not found - ${name}`);
  58. }
  59. this.features[name] = {
  60. featureImplementation: constructFunction(),
  61. attached: false,
  62. enabled: true
  63. };
  64. } else {
  65. // make sure it is enabled now:
  66. feature.enabled = true;
  67. }
  68. // if session started already, request and enable
  69. if (this.xrSessionManager.session && !feature.attached && attachIfPossible) {
  70. // enable feature
  71. this.attachFeature(name);
  72. }
  73. return this.features[name].featureImplementation;
  74. }
  75. public disableFeature(featureName: string | { Name: string }) {
  76. const name = typeof featureName === 'string' ? featureName : featureName.Name;
  77. const feature = this.features[name];
  78. if (feature && feature.enabled) {
  79. feature.enabled = false;
  80. this.detachFeature(name);
  81. }
  82. }
  83. public attachFeature(featureName: string) {
  84. const feature = this.features[featureName];
  85. if (feature && feature.enabled && !feature.attached) {
  86. feature.featureImplementation.attach();
  87. feature.attached = true;
  88. }
  89. }
  90. public detachFeature(featureName: string) {
  91. const feature = this.features[featureName];
  92. if (feature && feature.attached) {
  93. feature.featureImplementation.detach();
  94. feature.attached = false;
  95. }
  96. }
  97. public getEnabledFeatures() {
  98. return Object.keys(this.features);
  99. }
  100. public getEnabledFeature(featureName: string) {
  101. return this.features[featureName] && this.features[featureName].featureImplementation;
  102. }
  103. dispose(): void {
  104. this.getEnabledFeatures().forEach((feature) => this.features[feature].featureImplementation.dispose());
  105. }
  106. }