소스 검색

Merge pull request #7294 from BabylonJS/master

4.1.0-beta.10
David Catuhe 5 년 전
부모
커밋
a348db24ce
51개의 변경된 파일6370개의 추가작업 그리고 408개의 파일을 삭제
  1. 621 9
      dist/preview release/babylon.d.ts
  2. 2 2
      dist/preview release/babylon.js
  3. 1187 84
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 1216 10
      dist/preview release/babylon.module.d.ts
  6. 621 9
      dist/preview release/documentation.d.ts
  7. 1 1
      dist/preview release/glTF2Interface/package.json
  8. 2 2
      dist/preview release/gui/package.json
  9. 7 7
      dist/preview release/inspector/package.json
  10. 3 3
      dist/preview release/loaders/package.json
  11. 2 2
      dist/preview release/materialsLibrary/package.json
  12. 5 2
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  13. 6 6
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  14. 33 10
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  15. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  16. 10 4
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  17. 2 2
      dist/preview release/nodeEditor/package.json
  18. 1 1
      dist/preview release/package.json
  19. 1 1
      dist/preview release/packagesSizeBaseLine.json
  20. 2 2
      dist/preview release/postProcessesLibrary/package.json
  21. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  22. 3 3
      dist/preview release/serializers/package.json
  23. 1216 10
      dist/preview release/viewer/babylon.module.d.ts
  24. 93 69
      dist/preview release/viewer/babylon.viewer.js
  25. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  26. 2 0
      dist/preview release/what's new.md
  27. 3 1
      nodeEditor/src/diagram/graphCanvas.scss
  28. 5 2
      nodeEditor/src/diagram/graphCanvas.tsx
  29. 27 7
      nodeEditor/src/diagram/graphFrame.ts
  30. 3 3
      nodeEditor/src/diagram/graphNode.ts
  31. 1 1
      package.json
  32. 271 0
      src/Cameras/XR/features/WebXRAnchorSystem.ts
  33. 136 0
      src/Cameras/XR/features/WebXRBackgroundRemover.ts
  34. 215 0
      src/Cameras/XR/features/WebXRHitTestLegacy.ts
  35. 213 0
      src/Cameras/XR/features/WebXRPlaneDetector.ts
  36. 4 0
      src/Cameras/XR/features/index.ts
  37. 3 1
      src/Cameras/XR/index.ts
  38. 5 0
      src/Cameras/XR/webXRExperienceHelper.ts
  39. 279 0
      src/Cameras/XR/webXRFeaturesManager.ts
  40. 7 5
      src/Cameras/XR/webXRSessionManager.ts
  41. 38 1
      src/Engines/Native/nativeShaderProcessor.ts
  42. 5 0
      src/Engines/WebGL/webGLPipelineContext.ts
  43. 8 0
      src/Engines/nativeEngine.ts
  44. 18 8
      src/Engines/thinEngine.ts
  45. 58 8
      src/LibDeclarations/webxr.d.ts
  46. 2 0
      src/Materials/material.ts
  47. 9 9
      src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx
  48. 7 6
      src/Shaders/spriteMap.fragment.fx
  49. 0 78
      src/Shaders/spriteMap.fragment.ts
  50. 0 33
      src/Shaders/spriteMap.vertex.ts
  51. 12 1
      src/Sprites/spriteMap.ts

+ 621 - 9
dist/preview release/babylon.d.ts

@@ -7120,6 +7120,10 @@ declare module BABYLON {
         isParallelCompiled: boolean;
         onCompiled?: () => void;
         transformFeedback?: WebGLTransformFeedback | null;
+        vertexCompilationError: Nullable<string>;
+        fragmentCompilationError: Nullable<string>;
+        programLinkError: Nullable<string>;
+        programValidationError: Nullable<string>;
         readonly isAsync: boolean;
         readonly isReady: boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
@@ -31473,7 +31477,7 @@ declare module BABYLON {
          * Gets host document
          * @returns the host document object
          */
-        getHostDocument(): Document;
+        getHostDocument(): Nullable<Document>;
     }
 }
 declare module BABYLON {
@@ -41884,6 +41888,8 @@ declare module BABYLON {
          * Current XR frame
          */
         currentFrame: Nullable<XRFrame>;
+        /** WebXR timestamp updated every frame */
+        currentTimestamp: number;
         private _xrNavigator;
         private baseLayer;
         private _rttProvider;
@@ -41998,6 +42004,138 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Defining the interface required for a (webxr) feature
+     */
+    export interface IWebXRFeature extends IDisposable {
+        /**
+         * Attach the feature to the session
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * Detach the feature from the session
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+    }
+    /**
+     * Defining the constructor of a feature. Used to register the modules.
+     */
+    export type WebXRFeatureConstructor = (xrSessionManager: WebXRSessionManager, options?: any) => (() => IWebXRFeature);
+    /**
+     * 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 _xrSessionManager;
+        private static readonly _AvailableFeatures;
+        /**
+         * 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
+         */
+        static AddWebXRFeature(featureName: string, constructorFunction: WebXRFeatureConstructor, version?: number, stable?: boolean): void;
+        /**
+         * 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
+         */
+        static ConstructFeature(featureName: string, version: number | undefined, xrSessionManager: WebXRSessionManager, options?: any): (() => IWebXRFeature);
+        /**
+         * 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 GetLatestVersionOfFeature(featureName: string): number;
+        /**
+         * 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 GetStableVersionOfFeature(featureName: string): number;
+        /**
+         * Can be used to return the list of features currently registered
+         *
+         * @returns an Array of available features
+         */
+        static GetAvailableFeatures(): string[];
+        /**
+         * Gets the versions available for a specific feature
+         * @param featureName the name of the feature
+         * @returns an array with the available versions
+         */
+        static GetAvailableVersions(featureName: string): string[];
+        private _features;
+        /**
+         * constructs a new features manages.
+         *
+         * @param _xrSessionManager an instance of WebXRSessionManager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Enable a feature using its name and a version. This will enable it in the scene, and will be responsible to attach it when the session starts.
+         *
+         * @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.
+         */
+        enableFeature(featureName: string | {
+            Name: string;
+        }, version?: number | string, moduleOptions?: any, attachIfPossible?: boolean): IWebXRFeature;
+        /**
+         * Used to disable an already-enabled feature
+         * @param featureName the feature to disable
+         * @returns true if disable was successful
+         */
+        disableFeature(featureName: string | {
+            Name: string;
+        }): boolean;
+        /**
+         * 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
+         */
+        attachFeature(featureName: string): void;
+        /**
+         * 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
+         */
+        detachFeature(featureName: string): void;
+        /**
+         * Get the list of enabled features
+         * @returns an array of enabled features
+         */
+        getEnabledFeatures(): string[];
+        /**
+         * get the implementation of an enabled feature.
+         * @param featureName the name of the feature to load
+         * @returns the feature class, if found
+         */
+        getEnabledFeature(featureName: string): IWebXRFeature;
+        /**
+         * dispose this features manager
+         */
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Base set of functionality needed to create an XR experince (WebXRSessionManager, Camera, StateManagement, etc.)
      * @see https://doc.babylonjs.com/how_to/webxr
      */
@@ -42023,6 +42161,8 @@ declare module BABYLON {
         onStateChangedObservable: Observable<WebXRState>;
         /** Session manager used to keep track of xr session */
         sessionManager: WebXRSessionManager;
+        /** A features manager for this xr session */
+        featuresManager: WebXRFeaturesManager;
         private _nonVRCamera;
         private _originalSceneAutoClear;
         private _supported;
@@ -43560,6 +43700,423 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Options used for hit testing
+     */
+    export interface IWebXRHitTestOptions {
+        /**
+         * Only test when user interacted with the scene. Default - hit test every frame
+         */
+        testOnPointerDownOnly?: boolean;
+        /**
+         * The node to use to transform the local results to world coordinates
+         */
+        worldParentNode?: TransformNode;
+    }
+    /**
+     * Interface defining the babylon result of raycasting/hit-test
+     */
+    export interface IWebXRHitResult {
+        /**
+         * The native hit test result
+         */
+        xrHitResult: XRHitResult;
+        /**
+         * Transformation matrix that can be applied to a node that will put it in the hit point location
+         */
+        transformationMatrix: Matrix;
+    }
+    /**
+     * The currently-working hit-test module.
+     * Hit test (or raycasting) is used to interact with the real world.
+     * For further information read here - https://github.com/immersive-web/hit-test
+     */
+    export class WebXRHitTestLegacy implements IWebXRFeature {
+        private _xrSessionManager;
+        /**
+         * options to use when constructing this feature
+         */
+        readonly options: IWebXRHitTestOptions;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * Execute a hit test on the current running session using a select event returned from a transient input (such as touch)
+         * @param event the (select) event to use to select with
+         * @param referenceSpace the reference space to use for this hit test
+         * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
+         */
+        static XRHitTestWithSelectEvent(event: XRInputSourceEvent, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]>;
+        /**
+         * execute a hit test with an XR Ray
+         *
+         * @param xrSession a native xrSession that will execute this hit test
+         * @param xrRay the ray (position and direction) to use for raycasting
+         * @param referenceSpace native XR reference space to use for the hit-test
+         * @param filter filter function that will filter the results
+         * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
+         */
+        static XRHitTestWithRay(xrSession: XRSession, xrRay: XRRay, referenceSpace: XRReferenceSpace, filter?: (result: XRHitResult) => boolean): Promise<XRHitResult[]>;
+        /**
+         * Triggered when new babylon (transformed) hit test results are available
+         */
+        onHitTestResultObservable: Observable<IWebXRHitResult[]>;
+        /**
+         * Creates a new instance of the (legacy version) hit test feature
+         * @param _xrSessionManager an instance of WebXRSessionManager
+         * @param options options to use when constructing this feature
+         */
+        constructor(_xrSessionManager: WebXRSessionManager, 
+        /**
+         * options to use when constructing this feature
+         */
+        options?: IWebXRHitTestOptions);
+        private _onSelectEnabled;
+        private _xrFrameObserver;
+        private _attached;
+        /**
+         * Populated with the last native XR Hit Results
+         */
+        lastNativeXRHitResults: XRHitResult[];
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        private _onHitTestResults;
+        private _onSelect;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
+     * Options used in the plane detector module
+     */
+    export interface IWebXRPlaneDetectorOptions {
+        /**
+         * The node to use to transform the local results to world coordinates
+         */
+        worldParentNode?: TransformNode;
+    }
+    /**
+     * A babylon interface for a webxr plane.
+     * A Plane is actually a polygon, built from N points in space
+     */
+    export interface IWebXRPlane {
+        /**
+         * a babylon-assigned ID for this polygon
+         */
+        id: number;
+        /**
+         * the native xr-plane object
+         */
+        xrPlane: XRPlane;
+        /**
+         * an array of vector3 points in babylon space. right/left hand system is taken into account.
+         */
+        polygonDefinition: Array<Vector3>;
+        /**
+         * A transformation matrix to apply on the mesh that will be built using the polygonDefinition
+         * Local vs. World are decided if worldParentNode was provided or not in the options when constructing the module
+         */
+        transformationMatrix: Matrix;
+    }
+    /**
+     * The plane detector is used to detect planes in the real world when in AR
+     * For more information see https://github.com/immersive-web/real-world-geometry/
+     */
+    export class WebXRPlaneDetector implements IWebXRFeature {
+        private _xrSessionManager;
+        private _options;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * Observers registered here will be executed when a new plane was added to the session
+         */
+        onPlaneAddedObservable: Observable<IWebXRPlane>;
+        /**
+         * Observers registered here will be executed when a plane is no longer detected in the session
+         */
+        onPlaneRemovedObservable: Observable<IWebXRPlane>;
+        /**
+         * Observers registered here will be executed when an existing plane updates (for example - expanded)
+         * This can execute N times every frame
+         */
+        onPlaneUpdatedObservable: Observable<IWebXRPlane>;
+        private _enabled;
+        private _attached;
+        private _detectedPlanes;
+        private _lastFrameDetected;
+        private _observerTracked;
+        /**
+         * construct a new Plane Detector
+         * @param _xrSessionManager an instance of xr Session manager
+         * @param _options configuration to use when constructing this feature
+         */
+        constructor(_xrSessionManager: WebXRSessionManager, _options?: IWebXRPlaneDetectorOptions);
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        private _updatePlaneWithXRPlane;
+        /**
+         * avoiding using Array.find for global support.
+         * @param xrPlane the plane to find in the array
+         */
+        private findIndexInPlaneArray;
+    }
+}
+declare module BABYLON {
+    /**
+     * Configuration options of the anchor system
+     */
+    export interface IWebXRAnchorSystemOptions {
+        /**
+         * a node that will be used to convert local to world coordinates
+         */
+        worldParentNode?: TransformNode;
+        /**
+         * should the anchor system use plane detection.
+         * If set to true, the plane-detection feature should be set using setPlaneDetector
+         */
+        usePlaneDetection?: boolean;
+        /**
+         * Should a new anchor be added every time a select event is triggered
+         */
+        addAnchorOnSelect?: boolean;
+    }
+    /**
+     * A babylon container for an XR Anchor
+     */
+    export interface IWebXRAnchor {
+        /**
+         * A babylon-assigned ID for this anchor
+         */
+        id: number;
+        /**
+         * The native anchor object
+         */
+        xrAnchor: XRAnchor;
+        /**
+         * Transformation matrix to apply to an object attached to this anchor
+         */
+        transformationMatrix: Matrix;
+    }
+    /**
+     * An implementation of the anchor system of WebXR.
+     * Note that the current documented implementation is not available in any browser. Future implementations
+     * will use the frame to create an anchor and not the session or a detected plane
+     * For further information see https://github.com/immersive-web/anchors/
+     */
+    export class WebXRAnchorSystem implements IWebXRFeature {
+        private _xrSessionManager;
+        private _options;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * Observers registered here will be executed when a new anchor was added to the session
+         */
+        onAnchorAddedObservable: Observable<IWebXRAnchor>;
+        /**
+         * Observers registered here will be executed when an existing anchor updates
+         * This can execute N times every frame
+         */
+        onAnchorUpdatedObservable: Observable<IWebXRAnchor>;
+        /**
+         * Observers registered here will be executed when an anchor was removed from the session
+         */
+        onAnchorRemovedObservable: Observable<IWebXRAnchor>;
+        private _planeDetector;
+        private _hitTestModule;
+        private _enabled;
+        private _attached;
+        private _trackedAnchors;
+        private _lastFrameDetected;
+        private _observerTracked;
+        /**
+         * constructs a new anchor system
+         * @param _xrSessionManager an instance of WebXRSessionManager
+         * @param _options configuration object for this feature
+         */
+        constructor(_xrSessionManager: WebXRSessionManager, _options?: IWebXRAnchorSystemOptions);
+        /**
+         * set the plane detector to use in order to create anchors from frames
+         * @param planeDetector the plane-detector module to use
+         * @param enable enable plane-anchors. default is true
+         */
+        setPlaneDetector(planeDetector: WebXRPlaneDetector, enable?: boolean): void;
+        /**
+         * If set, it will improve performance by using the current hit-test results instead of executing a new hit-test
+         * @param hitTestModule the hit-test module to use.
+         */
+        setHitTestModule(hitTestModule: WebXRHitTestLegacy): void;
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        private _onSelect;
+        /**
+         * Add anchor at a specific XR point.
+         *
+         * @param xrRigidTransformation xr-coordinates where a new anchor should be added
+         * @param anchorCreator the object o use to create an anchor with. either a session or a plane
+         * @returns a promise the fulfills when the anchor was created
+         */
+        addAnchorAtRigidTransformation(xrRigidTransformation: XRRigidTransform, anchorCreator?: XRAnchorCreator): Promise<XRAnchor>;
+        private _updateAnchorWithXRFrame;
+        /**
+         * avoiding using Array.find for global support.
+         * @param xrAnchor the plane to find in the array
+         */
+        private _findIndexInAnchorArray;
+    }
+}
+declare module BABYLON {
+    /**
+     * Options interface for the background remover plugin
+     */
+    export interface IWebXRBackgroundRemoverOptions {
+        /**
+         * don't disable the environment helper
+         */
+        ignoreEnvironmentHelper?: boolean;
+        /**
+         * flags to configure the removal of the environment helper.
+         * If not set, the entire background will be removed. If set, flags should be set as well.
+         */
+        environmentHelperRemovalFlags?: {
+            /**
+             * Should the skybox be removed (default false)
+             */
+            skyBox?: boolean;
+            /**
+             * Should the ground be removed (default false)
+             */
+            ground?: boolean;
+        };
+        /**
+         * Further background meshes to disable when entering AR
+         */
+        backgroundMeshes?: AbstractMesh[];
+    }
+    /**
+     * A module that will automatically disable background meshes when entering AR and will enable them when leaving AR.
+     */
+    export class WebXRBackgroundRemover implements IWebXRFeature {
+        private _xrSessionManager;
+        /**
+         * read-only options to be used in this module
+         */
+        readonly options: IWebXRBackgroundRemoverOptions;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * registered observers will be triggered when the background state changes
+         */
+        onBackgroundStateChangedObservable: Observable<boolean>;
+        /**
+         * constructs a new background remover module
+         * @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
+         */
+        options?: IWebXRBackgroundRemoverOptions);
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        private _setBackgroundState;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Contains an array of blocks representing the octree
      */
     export interface IOctreeContainer<T> {
@@ -45858,6 +46415,11 @@ declare module BABYLON {
          * @param stencil
          */
         _bindUnboundFramebuffer(framebuffer: Nullable<WebGLFramebuffer>): void;
+        /**
+         * Gets host document
+         * @returns the host document object
+         */
+        getHostDocument(): Nullable<Document>;
         clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil?: boolean): void;
         createIndexBuffer(indices: IndicesArray): NativeDataBuffer;
         createVertexBuffer(data: DataArray): NativeDataBuffer;
@@ -66241,8 +66803,9 @@ interface XRInputSource {
     profiles: Array<string>;
 }
 
-interface XRSession {
+interface XRSession extends XRAnchorCreator {
     addEventListener: Function;
+    removeEventListener: Function;
     requestReferenceSpace(type: XRReferenceSpaceType): Promise<XRReferenceSpace>;
     updateRenderState(XRRenderStateInit: XRRenderState): Promise<void>;
     requestAnimationFrame: Function;
@@ -66250,6 +66813,12 @@ interface XRSession {
     renderState: XRRenderState;
     inputSources: Array<XRInputSource>;
 
+    // AR hit test
+    requestHitTest(ray: XRRay, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]>;
+
+    updateWorldTrackingState(options: {
+        planeDetectionState?: { enabled: boolean; }
+    }): void;
 }
 
 interface XRReferenceSpace extends XRSpace {
@@ -66257,10 +66826,20 @@ interface XRReferenceSpace extends XRSpace {
     onreset: any;
 }
 
+type XRPlaneSet = Set<XRPlane>;
+type XRAnchorSet = Set<XRAnchor>;
+
 interface XRFrame {
     session: XRSession;
     getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | undefined;
     getPose(space: XRSpace, baseSpace: XRSpace): XRPose | undefined;
+
+    // Anchors
+    trackedAnchors?: XRAnchorSet;
+    // Planes
+    worldInformation: {
+        detectedPlanes?: XRPlaneSet;
+    };
 }
 
 interface XRViewerPose extends XRPose {
@@ -66273,12 +66852,12 @@ interface XRPose {
 }
 
 interface XRWebGLLayerOptions {
-    antialias ?: boolean;
-    depth ?: boolean;
-    stencil ?: boolean;
-    alpha ?: boolean;
-    multiview ?: boolean;
-    framebufferScaleFactor ?: number;
+    antialias?: boolean;
+    depth?: boolean;
+    stencil?: boolean;
+    alpha?: boolean;
+    multiview?: boolean;
+    framebufferScaleFactor?: number;
 }
 
 declare var XRWebGLLayer: {
@@ -66292,7 +66871,8 @@ interface XRWebGLLayer {
     getViewport: Function;
 }
 
-interface XRRigidTransform {
+declare class XRRigidTransform {
+    constructor(matrix: Float32Array);
     position: DOMPointReadOnly;
     orientation: DOMPointReadOnly;
     matrix: Float32Array;
@@ -66314,4 +66894,36 @@ interface XRInputSourceChangeEvent {
 interface XRInputSourceEvent extends Event {
     readonly frame: XRFrame;
     readonly inputSource: XRInputSource;
+}
+
+// Experimental(er) features
+declare class XRRay {
+    constructor(transformOrOrigin: XRRigidTransform | DOMPointReadOnly, direction?: DOMPointReadOnly);
+    origin: DOMPointReadOnly;
+    direction: DOMPointReadOnly;
+    matrix: Float32Array;
+}
+
+interface XRHitResult {
+    hitMatrix: Float32Array;
+}
+
+interface XRAnchor {
+    // remove?
+    id?: string;
+    anchorSpace: XRSpace;
+    lastChangedTime: number;
+    detach(): void;
+}
+
+interface XRPlane extends XRAnchorCreator {
+    orientation: "Horizontal" | "Vertical";
+    planeSpace: XRSpace;
+    polygon: Array<DOMPointReadOnly>;
+    lastChangedTime: number;
+}
+
+interface XRAnchorCreator {
+    // AR Anchors
+    createAnchor(pose: XRPose | XRRigidTransform, referenceSpace: XRReferenceSpace): Promise<XRAnchor>;
 }

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 2
dist/preview release/babylon.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1187 - 84
dist/preview release/babylon.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/babylon.max.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1216 - 10
dist/preview release/babylon.module.d.ts


+ 621 - 9
dist/preview release/documentation.d.ts

@@ -7120,6 +7120,10 @@ declare module BABYLON {
         isParallelCompiled: boolean;
         onCompiled?: () => void;
         transformFeedback?: WebGLTransformFeedback | null;
+        vertexCompilationError: Nullable<string>;
+        fragmentCompilationError: Nullable<string>;
+        programLinkError: Nullable<string>;
+        programValidationError: Nullable<string>;
         readonly isAsync: boolean;
         readonly isReady: boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
@@ -31473,7 +31477,7 @@ declare module BABYLON {
          * Gets host document
          * @returns the host document object
          */
-        getHostDocument(): Document;
+        getHostDocument(): Nullable<Document>;
     }
 }
 declare module BABYLON {
@@ -41884,6 +41888,8 @@ declare module BABYLON {
          * Current XR frame
          */
         currentFrame: Nullable<XRFrame>;
+        /** WebXR timestamp updated every frame */
+        currentTimestamp: number;
         private _xrNavigator;
         private baseLayer;
         private _rttProvider;
@@ -41998,6 +42004,138 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Defining the interface required for a (webxr) feature
+     */
+    export interface IWebXRFeature extends IDisposable {
+        /**
+         * Attach the feature to the session
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * Detach the feature from the session
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+    }
+    /**
+     * Defining the constructor of a feature. Used to register the modules.
+     */
+    export type WebXRFeatureConstructor = (xrSessionManager: WebXRSessionManager, options?: any) => (() => IWebXRFeature);
+    /**
+     * 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 _xrSessionManager;
+        private static readonly _AvailableFeatures;
+        /**
+         * 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
+         */
+        static AddWebXRFeature(featureName: string, constructorFunction: WebXRFeatureConstructor, version?: number, stable?: boolean): void;
+        /**
+         * 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
+         */
+        static ConstructFeature(featureName: string, version: number | undefined, xrSessionManager: WebXRSessionManager, options?: any): (() => IWebXRFeature);
+        /**
+         * 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 GetLatestVersionOfFeature(featureName: string): number;
+        /**
+         * 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 GetStableVersionOfFeature(featureName: string): number;
+        /**
+         * Can be used to return the list of features currently registered
+         *
+         * @returns an Array of available features
+         */
+        static GetAvailableFeatures(): string[];
+        /**
+         * Gets the versions available for a specific feature
+         * @param featureName the name of the feature
+         * @returns an array with the available versions
+         */
+        static GetAvailableVersions(featureName: string): string[];
+        private _features;
+        /**
+         * constructs a new features manages.
+         *
+         * @param _xrSessionManager an instance of WebXRSessionManager
+         */
+        constructor(_xrSessionManager: WebXRSessionManager);
+        /**
+         * Enable a feature using its name and a version. This will enable it in the scene, and will be responsible to attach it when the session starts.
+         *
+         * @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.
+         */
+        enableFeature(featureName: string | {
+            Name: string;
+        }, version?: number | string, moduleOptions?: any, attachIfPossible?: boolean): IWebXRFeature;
+        /**
+         * Used to disable an already-enabled feature
+         * @param featureName the feature to disable
+         * @returns true if disable was successful
+         */
+        disableFeature(featureName: string | {
+            Name: string;
+        }): boolean;
+        /**
+         * 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
+         */
+        attachFeature(featureName: string): void;
+        /**
+         * 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
+         */
+        detachFeature(featureName: string): void;
+        /**
+         * Get the list of enabled features
+         * @returns an array of enabled features
+         */
+        getEnabledFeatures(): string[];
+        /**
+         * get the implementation of an enabled feature.
+         * @param featureName the name of the feature to load
+         * @returns the feature class, if found
+         */
+        getEnabledFeature(featureName: string): IWebXRFeature;
+        /**
+         * dispose this features manager
+         */
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Base set of functionality needed to create an XR experince (WebXRSessionManager, Camera, StateManagement, etc.)
      * @see https://doc.babylonjs.com/how_to/webxr
      */
@@ -42023,6 +42161,8 @@ declare module BABYLON {
         onStateChangedObservable: Observable<WebXRState>;
         /** Session manager used to keep track of xr session */
         sessionManager: WebXRSessionManager;
+        /** A features manager for this xr session */
+        featuresManager: WebXRFeaturesManager;
         private _nonVRCamera;
         private _originalSceneAutoClear;
         private _supported;
@@ -43560,6 +43700,423 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Options used for hit testing
+     */
+    export interface IWebXRHitTestOptions {
+        /**
+         * Only test when user interacted with the scene. Default - hit test every frame
+         */
+        testOnPointerDownOnly?: boolean;
+        /**
+         * The node to use to transform the local results to world coordinates
+         */
+        worldParentNode?: TransformNode;
+    }
+    /**
+     * Interface defining the babylon result of raycasting/hit-test
+     */
+    export interface IWebXRHitResult {
+        /**
+         * The native hit test result
+         */
+        xrHitResult: XRHitResult;
+        /**
+         * Transformation matrix that can be applied to a node that will put it in the hit point location
+         */
+        transformationMatrix: Matrix;
+    }
+    /**
+     * The currently-working hit-test module.
+     * Hit test (or raycasting) is used to interact with the real world.
+     * For further information read here - https://github.com/immersive-web/hit-test
+     */
+    export class WebXRHitTestLegacy implements IWebXRFeature {
+        private _xrSessionManager;
+        /**
+         * options to use when constructing this feature
+         */
+        readonly options: IWebXRHitTestOptions;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * Execute a hit test on the current running session using a select event returned from a transient input (such as touch)
+         * @param event the (select) event to use to select with
+         * @param referenceSpace the reference space to use for this hit test
+         * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
+         */
+        static XRHitTestWithSelectEvent(event: XRInputSourceEvent, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]>;
+        /**
+         * execute a hit test with an XR Ray
+         *
+         * @param xrSession a native xrSession that will execute this hit test
+         * @param xrRay the ray (position and direction) to use for raycasting
+         * @param referenceSpace native XR reference space to use for the hit-test
+         * @param filter filter function that will filter the results
+         * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
+         */
+        static XRHitTestWithRay(xrSession: XRSession, xrRay: XRRay, referenceSpace: XRReferenceSpace, filter?: (result: XRHitResult) => boolean): Promise<XRHitResult[]>;
+        /**
+         * Triggered when new babylon (transformed) hit test results are available
+         */
+        onHitTestResultObservable: Observable<IWebXRHitResult[]>;
+        /**
+         * Creates a new instance of the (legacy version) hit test feature
+         * @param _xrSessionManager an instance of WebXRSessionManager
+         * @param options options to use when constructing this feature
+         */
+        constructor(_xrSessionManager: WebXRSessionManager, 
+        /**
+         * options to use when constructing this feature
+         */
+        options?: IWebXRHitTestOptions);
+        private _onSelectEnabled;
+        private _xrFrameObserver;
+        private _attached;
+        /**
+         * Populated with the last native XR Hit Results
+         */
+        lastNativeXRHitResults: XRHitResult[];
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        private _onHitTestResults;
+        private _onSelect;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
+     * Options used in the plane detector module
+     */
+    export interface IWebXRPlaneDetectorOptions {
+        /**
+         * The node to use to transform the local results to world coordinates
+         */
+        worldParentNode?: TransformNode;
+    }
+    /**
+     * A babylon interface for a webxr plane.
+     * A Plane is actually a polygon, built from N points in space
+     */
+    export interface IWebXRPlane {
+        /**
+         * a babylon-assigned ID for this polygon
+         */
+        id: number;
+        /**
+         * the native xr-plane object
+         */
+        xrPlane: XRPlane;
+        /**
+         * an array of vector3 points in babylon space. right/left hand system is taken into account.
+         */
+        polygonDefinition: Array<Vector3>;
+        /**
+         * A transformation matrix to apply on the mesh that will be built using the polygonDefinition
+         * Local vs. World are decided if worldParentNode was provided or not in the options when constructing the module
+         */
+        transformationMatrix: Matrix;
+    }
+    /**
+     * The plane detector is used to detect planes in the real world when in AR
+     * For more information see https://github.com/immersive-web/real-world-geometry/
+     */
+    export class WebXRPlaneDetector implements IWebXRFeature {
+        private _xrSessionManager;
+        private _options;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * Observers registered here will be executed when a new plane was added to the session
+         */
+        onPlaneAddedObservable: Observable<IWebXRPlane>;
+        /**
+         * Observers registered here will be executed when a plane is no longer detected in the session
+         */
+        onPlaneRemovedObservable: Observable<IWebXRPlane>;
+        /**
+         * Observers registered here will be executed when an existing plane updates (for example - expanded)
+         * This can execute N times every frame
+         */
+        onPlaneUpdatedObservable: Observable<IWebXRPlane>;
+        private _enabled;
+        private _attached;
+        private _detectedPlanes;
+        private _lastFrameDetected;
+        private _observerTracked;
+        /**
+         * construct a new Plane Detector
+         * @param _xrSessionManager an instance of xr Session manager
+         * @param _options configuration to use when constructing this feature
+         */
+        constructor(_xrSessionManager: WebXRSessionManager, _options?: IWebXRPlaneDetectorOptions);
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        private _updatePlaneWithXRPlane;
+        /**
+         * avoiding using Array.find for global support.
+         * @param xrPlane the plane to find in the array
+         */
+        private findIndexInPlaneArray;
+    }
+}
+declare module BABYLON {
+    /**
+     * Configuration options of the anchor system
+     */
+    export interface IWebXRAnchorSystemOptions {
+        /**
+         * a node that will be used to convert local to world coordinates
+         */
+        worldParentNode?: TransformNode;
+        /**
+         * should the anchor system use plane detection.
+         * If set to true, the plane-detection feature should be set using setPlaneDetector
+         */
+        usePlaneDetection?: boolean;
+        /**
+         * Should a new anchor be added every time a select event is triggered
+         */
+        addAnchorOnSelect?: boolean;
+    }
+    /**
+     * A babylon container for an XR Anchor
+     */
+    export interface IWebXRAnchor {
+        /**
+         * A babylon-assigned ID for this anchor
+         */
+        id: number;
+        /**
+         * The native anchor object
+         */
+        xrAnchor: XRAnchor;
+        /**
+         * Transformation matrix to apply to an object attached to this anchor
+         */
+        transformationMatrix: Matrix;
+    }
+    /**
+     * An implementation of the anchor system of WebXR.
+     * Note that the current documented implementation is not available in any browser. Future implementations
+     * will use the frame to create an anchor and not the session or a detected plane
+     * For further information see https://github.com/immersive-web/anchors/
+     */
+    export class WebXRAnchorSystem implements IWebXRFeature {
+        private _xrSessionManager;
+        private _options;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * Observers registered here will be executed when a new anchor was added to the session
+         */
+        onAnchorAddedObservable: Observable<IWebXRAnchor>;
+        /**
+         * Observers registered here will be executed when an existing anchor updates
+         * This can execute N times every frame
+         */
+        onAnchorUpdatedObservable: Observable<IWebXRAnchor>;
+        /**
+         * Observers registered here will be executed when an anchor was removed from the session
+         */
+        onAnchorRemovedObservable: Observable<IWebXRAnchor>;
+        private _planeDetector;
+        private _hitTestModule;
+        private _enabled;
+        private _attached;
+        private _trackedAnchors;
+        private _lastFrameDetected;
+        private _observerTracked;
+        /**
+         * constructs a new anchor system
+         * @param _xrSessionManager an instance of WebXRSessionManager
+         * @param _options configuration object for this feature
+         */
+        constructor(_xrSessionManager: WebXRSessionManager, _options?: IWebXRAnchorSystemOptions);
+        /**
+         * set the plane detector to use in order to create anchors from frames
+         * @param planeDetector the plane-detector module to use
+         * @param enable enable plane-anchors. default is true
+         */
+        setPlaneDetector(planeDetector: WebXRPlaneDetector, enable?: boolean): void;
+        /**
+         * If set, it will improve performance by using the current hit-test results instead of executing a new hit-test
+         * @param hitTestModule the hit-test module to use.
+         */
+        setHitTestModule(hitTestModule: WebXRHitTestLegacy): void;
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+        private _onSelect;
+        /**
+         * Add anchor at a specific XR point.
+         *
+         * @param xrRigidTransformation xr-coordinates where a new anchor should be added
+         * @param anchorCreator the object o use to create an anchor with. either a session or a plane
+         * @returns a promise the fulfills when the anchor was created
+         */
+        addAnchorAtRigidTransformation(xrRigidTransformation: XRRigidTransform, anchorCreator?: XRAnchorCreator): Promise<XRAnchor>;
+        private _updateAnchorWithXRFrame;
+        /**
+         * avoiding using Array.find for global support.
+         * @param xrAnchor the plane to find in the array
+         */
+        private _findIndexInAnchorArray;
+    }
+}
+declare module BABYLON {
+    /**
+     * Options interface for the background remover plugin
+     */
+    export interface IWebXRBackgroundRemoverOptions {
+        /**
+         * don't disable the environment helper
+         */
+        ignoreEnvironmentHelper?: boolean;
+        /**
+         * flags to configure the removal of the environment helper.
+         * If not set, the entire background will be removed. If set, flags should be set as well.
+         */
+        environmentHelperRemovalFlags?: {
+            /**
+             * Should the skybox be removed (default false)
+             */
+            skyBox?: boolean;
+            /**
+             * Should the ground be removed (default false)
+             */
+            ground?: boolean;
+        };
+        /**
+         * Further background meshes to disable when entering AR
+         */
+        backgroundMeshes?: AbstractMesh[];
+    }
+    /**
+     * A module that will automatically disable background meshes when entering AR and will enable them when leaving AR.
+     */
+    export class WebXRBackgroundRemover implements IWebXRFeature {
+        private _xrSessionManager;
+        /**
+         * read-only options to be used in this module
+         */
+        readonly options: IWebXRBackgroundRemoverOptions;
+        /**
+         * The module's name
+         */
+        static readonly Name: string;
+        /**
+         * The (Babylon) version of this module.
+         * This is an integer representing the implementation version.
+         * This number does not correspond to the webxr specs version
+         */
+        static readonly Version: number;
+        /**
+         * registered observers will be triggered when the background state changes
+         */
+        onBackgroundStateChangedObservable: Observable<boolean>;
+        /**
+         * constructs a new background remover module
+         * @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
+         */
+        options?: IWebXRBackgroundRemoverOptions);
+        /**
+         * attach this feature
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        attach(): boolean;
+        /**
+         * detach this feature.
+         * Will usually be called by the features manager
+         *
+         * @returns true if successful.
+         */
+        detach(): boolean;
+        private _setBackgroundState;
+        /**
+         * Dispose this feature and all of the resources attached
+         */
+        dispose(): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Contains an array of blocks representing the octree
      */
     export interface IOctreeContainer<T> {
@@ -45858,6 +46415,11 @@ declare module BABYLON {
          * @param stencil
          */
         _bindUnboundFramebuffer(framebuffer: Nullable<WebGLFramebuffer>): void;
+        /**
+         * Gets host document
+         * @returns the host document object
+         */
+        getHostDocument(): Nullable<Document>;
         clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil?: boolean): void;
         createIndexBuffer(indices: IndicesArray): NativeDataBuffer;
         createVertexBuffer(data: DataArray): NativeDataBuffer;
@@ -66241,8 +66803,9 @@ interface XRInputSource {
     profiles: Array<string>;
 }
 
-interface XRSession {
+interface XRSession extends XRAnchorCreator {
     addEventListener: Function;
+    removeEventListener: Function;
     requestReferenceSpace(type: XRReferenceSpaceType): Promise<XRReferenceSpace>;
     updateRenderState(XRRenderStateInit: XRRenderState): Promise<void>;
     requestAnimationFrame: Function;
@@ -66250,6 +66813,12 @@ interface XRSession {
     renderState: XRRenderState;
     inputSources: Array<XRInputSource>;
 
+    // AR hit test
+    requestHitTest(ray: XRRay, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]>;
+
+    updateWorldTrackingState(options: {
+        planeDetectionState?: { enabled: boolean; }
+    }): void;
 }
 
 interface XRReferenceSpace extends XRSpace {
@@ -66257,10 +66826,20 @@ interface XRReferenceSpace extends XRSpace {
     onreset: any;
 }
 
+type XRPlaneSet = Set<XRPlane>;
+type XRAnchorSet = Set<XRAnchor>;
+
 interface XRFrame {
     session: XRSession;
     getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | undefined;
     getPose(space: XRSpace, baseSpace: XRSpace): XRPose | undefined;
+
+    // Anchors
+    trackedAnchors?: XRAnchorSet;
+    // Planes
+    worldInformation: {
+        detectedPlanes?: XRPlaneSet;
+    };
 }
 
 interface XRViewerPose extends XRPose {
@@ -66273,12 +66852,12 @@ interface XRPose {
 }
 
 interface XRWebGLLayerOptions {
-    antialias ?: boolean;
-    depth ?: boolean;
-    stencil ?: boolean;
-    alpha ?: boolean;
-    multiview ?: boolean;
-    framebufferScaleFactor ?: number;
+    antialias?: boolean;
+    depth?: boolean;
+    stencil?: boolean;
+    alpha?: boolean;
+    multiview?: boolean;
+    framebufferScaleFactor?: number;
 }
 
 declare var XRWebGLLayer: {
@@ -66292,7 +66871,8 @@ interface XRWebGLLayer {
     getViewport: Function;
 }
 
-interface XRRigidTransform {
+declare class XRRigidTransform {
+    constructor(matrix: Float32Array);
     position: DOMPointReadOnly;
     orientation: DOMPointReadOnly;
     matrix: Float32Array;
@@ -66314,6 +66894,38 @@ interface XRInputSourceChangeEvent {
 interface XRInputSourceEvent extends Event {
     readonly frame: XRFrame;
     readonly inputSource: XRInputSource;
+}
+
+// Experimental(er) features
+declare class XRRay {
+    constructor(transformOrOrigin: XRRigidTransform | DOMPointReadOnly, direction?: DOMPointReadOnly);
+    origin: DOMPointReadOnly;
+    direction: DOMPointReadOnly;
+    matrix: Float32Array;
+}
+
+interface XRHitResult {
+    hitMatrix: Float32Array;
+}
+
+interface XRAnchor {
+    // remove?
+    id?: string;
+    anchorSpace: XRSpace;
+    lastChangedTime: number;
+    detach(): void;
+}
+
+interface XRPlane extends XRAnchorCreator {
+    orientation: "Horizontal" | "Vertical";
+    planeSpace: XRSpace;
+    polygon: Array<DOMPointReadOnly>;
+    lastChangedTime: number;
+}
+
+interface XRAnchorCreator {
+    // AR Anchors
+    createAnchor(pose: XRPose | XRRigidTransform, referenceSpace: XRReferenceSpace): Promise<XRAnchor>;
 }
 
 /**

+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 2 - 2
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10"
     },
     "engines": {
         "node": "*"

+ 7 - 7
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8",
-        "babylonjs-gui": "4.1.0-beta.8",
-        "babylonjs-loaders": "4.1.0-beta.8",
-        "babylonjs-materials": "4.1.0-beta.8",
-        "babylonjs-serializers": "4.1.0-beta.8",
-        "babylonjs-gltf2interface": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10",
+        "babylonjs-gui": "4.1.0-beta.10",
+        "babylonjs-loaders": "4.1.0-beta.10",
+        "babylonjs-materials": "4.1.0-beta.10",
+        "babylonjs-serializers": "4.1.0-beta.10",
+        "babylonjs-gltf2interface": "4.1.0-beta.10"
     },
     "devDependencies": {
         "@types/react": "~16.7.3",

+ 3 - 3
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.1.0-beta.8",
-        "babylonjs": "4.1.0-beta.8"
+        "babylonjs-gltf2interface": "4.1.0-beta.10",
+        "babylonjs": "4.1.0-beta.10"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10"
     },
     "engines": {
         "node": "*"

+ 5 - 2
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts

@@ -119,6 +119,9 @@ declare module NODEEDITOR {
         private _isCollapsed;
         private _ports;
         private _controlledPorts;
+        private readonly CloseSVG;
+        private readonly ExpandSVG;
+        private readonly CollapseSVG;
         isCollapsed: boolean;
         private _createInputPort;
         readonly nodes: GraphNode[];
@@ -219,7 +222,7 @@ declare module NODEEDITOR {
         readonly selectionContainer: HTMLDivElement;
         readonly frameContainer: HTMLDivElement;
         constructor(props: IGraphCanvasComponentProps);
-        getGridPosition(position: number): number;
+        getGridPosition(position: number, useCeil?: boolean): number;
         getGridPositionCeil(position: number): number;
         updateTransform(): void;
         onKeyUp(): void;
@@ -954,7 +957,7 @@ declare module NODEEDITOR {
         private _refreshLinks;
         refresh(): void;
         private _onDown;
-        cleanAccumulation(): void;
+        cleanAccumulation(useCeil?: boolean): void;
         private _onUp;
         private _onMove;
         renderProperties(): BABYLON.Nullable<JSX.Element>;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 6 - 6
dist/preview release/nodeEditor/babylon.nodeEditor.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 33 - 10
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map


+ 10 - 4
dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts

@@ -194,6 +194,9 @@ declare module "babylonjs-node-editor/diagram/graphFrame" {
         private _isCollapsed;
         private _ports;
         private _controlledPorts;
+        private readonly CloseSVG;
+        private readonly ExpandSVG;
+        private readonly CollapseSVG;
         isCollapsed: boolean;
         private _createInputPort;
         readonly nodes: GraphNode[];
@@ -307,7 +310,7 @@ declare module "babylonjs-node-editor/diagram/graphCanvas" {
         readonly selectionContainer: HTMLDivElement;
         readonly frameContainer: HTMLDivElement;
         constructor(props: IGraphCanvasComponentProps);
-        getGridPosition(position: number): number;
+        getGridPosition(position: number, useCeil?: boolean): number;
         getGridPositionCeil(position: number): number;
         updateTransform(): void;
         onKeyUp(): void;
@@ -1161,7 +1164,7 @@ declare module "babylonjs-node-editor/diagram/graphNode" {
         private _refreshLinks;
         refresh(): void;
         private _onDown;
-        cleanAccumulation(): void;
+        cleanAccumulation(useCeil?: boolean): void;
         private _onUp;
         private _onMove;
         renderProperties(): Nullable<JSX.Element>;
@@ -1574,6 +1577,9 @@ declare module NODEEDITOR {
         private _isCollapsed;
         private _ports;
         private _controlledPorts;
+        private readonly CloseSVG;
+        private readonly ExpandSVG;
+        private readonly CollapseSVG;
         isCollapsed: boolean;
         private _createInputPort;
         readonly nodes: GraphNode[];
@@ -1674,7 +1680,7 @@ declare module NODEEDITOR {
         readonly selectionContainer: HTMLDivElement;
         readonly frameContainer: HTMLDivElement;
         constructor(props: IGraphCanvasComponentProps);
-        getGridPosition(position: number): number;
+        getGridPosition(position: number, useCeil?: boolean): number;
         getGridPositionCeil(position: number): number;
         updateTransform(): void;
         onKeyUp(): void;
@@ -2409,7 +2415,7 @@ declare module NODEEDITOR {
         private _refreshLinks;
         refresh(): void;
         private _onDown;
-        cleanAccumulation(): void;
+        cleanAccumulation(useCeil?: boolean): void;
         private _onUp;
         private _onMove;
         renderProperties(): BABYLON.Nullable<JSX.Element>;

+ 2 - 2
dist/preview release/nodeEditor/package.json

@@ -4,14 +4,14 @@
     },
     "name": "babylonjs-node-editor",
     "description": "The Babylon.js node material editor.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10"
     },
     "files": [
         "babylon.nodeEditor.max.js.map",

+ 1 - 1
dist/preview release/package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/packagesSizeBaseLine.json

@@ -1 +1 @@
-{"thinEngineOnly":118814,"engineOnly":155623,"sceneOnly":501304,"minGridMaterial":631304,"minStandardMaterial":755202}
+{"thinEngineOnly":119146,"engineOnly":155955,"sceneOnly":501692,"minGridMaterial":631692,"minStandardMaterial":755590}

+ 2 - 2
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10"
     },
     "engines": {
         "node": "*"

+ 3 - 3
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.1.0-beta.8",
-        "babylonjs-gltf2interface": "4.1.0-beta.8"
+        "babylonjs": "4.1.0-beta.10",
+        "babylonjs-gltf2interface": "4.1.0-beta.10"
     },
     "engines": {
         "node": "*"

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1216 - 10
dist/preview release/viewer/babylon.module.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 93 - 69
dist/preview release/viewer/babylon.viewer.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 2 - 0
dist/preview release/what's new.md

@@ -176,6 +176,8 @@
 - Pointer-Event simulation for screen target ray mode ([RaananW](https://github.com/RaananW/))
 - New observable that triggers when a session was initialized ([RaananW](https://github.com/RaananW/))
 - WebXR teleportation can now be disabled after initialized ([RaananW](https://github.com/RaananW/))
+- New Features Manager for WebXR features ([RaananW](https://github.com/RaananW/))
+- New features - Plane detection, Hit Test, Background remover ([RaananW](https://github.com/RaananW/))
 
 ### Ray
 

+ 3 - 1
nodeEditor/src/diagram/graphCanvas.scss

@@ -136,7 +136,7 @@
                 text-align: center;
                 display: grid;
                 grid-template-rows: 100%;  
-                grid-template-columns: calc(100% - 40px) 20px 20px;  
+                grid-template-columns: calc(100% - 60px) 30px 30px;  
                 align-content: center;
                 overflow: hidden;
                 margin: -4px;
@@ -148,6 +148,8 @@
                     align-self: center;
                     transform-origin: 50% 50%;
                     transform: scale(1);
+                    stroke: white;     
+                    display: grid;               
 
                     &.down {
                         transform: scale(0.90);

+ 5 - 2
nodeEditor/src/diagram/graphCanvas.tsx

@@ -206,11 +206,14 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         }
     }
 
-    public getGridPosition(position: number) {
+    public getGridPosition(position: number, useCeil = false) {
         let gridSize = this.gridSize;
 		if (gridSize === 0) {
 			return position;
-		}
+        }
+        if (useCeil) {
+            return gridSize * Math.ceil(position / gridSize);    
+        }
 		return gridSize * Math.floor(position / gridSize);
     }
     

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 27 - 7
nodeEditor/src/diagram/graphFrame.ts


+ 3 - 3
nodeEditor/src/diagram/graphNode.ts

@@ -291,9 +291,9 @@ export class GraphNode {
         this._visual.setPointerCapture(evt.pointerId);
     }
 
-    public cleanAccumulation() {
-        this.x = this._ownerCanvas.getGridPosition(this.x);
-        this.y = this._ownerCanvas.getGridPosition(this.y);
+    public cleanAccumulation(useCeil = false) {
+        this.x = this._ownerCanvas.getGridPosition(this.x, useCeil);
+        this.y = this._ownerCanvas.getGridPosition(this.y, useCeil);
     }
 
     private _onUp(evt: PointerEvent) {

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.1.0-beta.8",
+    "version": "4.1.0-beta.10",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 271 - 0
src/Cameras/XR/features/WebXRAnchorSystem.ts

@@ -0,0 +1,271 @@
+import { IWebXRFeature, WebXRFeaturesManager } from '../webXRFeaturesManager';
+import { WebXRSessionManager } from '../webXRSessionManager';
+import { Observable, Observer } from '../../../Misc/observable';
+import { Matrix } from '../../../Maths/math.vector';
+import { TransformNode } from '../../../Meshes/transformNode';
+import { WebXRPlaneDetector } from './WebXRPlaneDetector';
+import { Nullable } from '../../../types';
+import { WebXRHitTestLegacy } from './WebXRHitTestLegacy';
+
+const Name = "xr-anchor-system";
+
+/**
+ * Configuration options of the anchor system
+ */
+export interface IWebXRAnchorSystemOptions {
+    /**
+     * a node that will be used to convert local to world coordinates
+     */
+    worldParentNode?: TransformNode;
+    /**
+     * should the anchor system use plane detection.
+     * If set to true, the plane-detection feature should be set using setPlaneDetector
+     */
+    usePlaneDetection?: boolean;
+    /**
+     * Should a new anchor be added every time a select event is triggered
+     */
+    addAnchorOnSelect?: boolean;
+}
+
+/**
+ * A babylon container for an XR Anchor
+ */
+export interface IWebXRAnchor {
+    /**
+     * A babylon-assigned ID for this anchor
+     */
+    id: number;
+    /**
+     * The native anchor object
+     */
+    xrAnchor: XRAnchor;
+    /**
+     * Transformation matrix to apply to an object attached to this anchor
+     */
+    transformationMatrix: Matrix;
+}
+
+let anchorIdProvider = 0;
+
+/**
+ * An implementation of the anchor system of WebXR.
+ * Note that the current documented implementation is not available in any browser. Future implementations
+ * will use the frame to create an anchor and not the session or a detected plane
+ * For further information see https://github.com/immersive-web/anchors/
+ */
+export class WebXRAnchorSystem implements IWebXRFeature {
+
+    /**
+     * The module's name
+     */
+    public static readonly Name = Name;
+    /**
+     * 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;
+
+    /**
+     * Observers registered here will be executed when a new anchor was added to the session
+     */
+    public onAnchorAddedObservable: Observable<IWebXRAnchor> = new Observable();
+    /**
+     * Observers registered here will be executed when an existing anchor updates
+     * This can execute N times every frame
+     */
+    public onAnchorUpdatedObservable: Observable<IWebXRAnchor> = new Observable();
+    /**
+     * Observers registered here will be executed when an anchor was removed from the session
+     */
+    public onAnchorRemovedObservable: Observable<IWebXRAnchor> = new Observable();
+
+    private _planeDetector: WebXRPlaneDetector;
+    private _hitTestModule: WebXRHitTestLegacy;
+
+    private _enabled: boolean = false;
+    private _attached: boolean = false;
+    private _trackedAnchors: Array<IWebXRAnchor> = [];
+    private _lastFrameDetected: XRAnchorSet = new Set();
+    private _observerTracked: Nullable<Observer<XRFrame>>;
+
+    /**
+     * constructs a new anchor system
+     * @param _xrSessionManager an instance of WebXRSessionManager
+     * @param _options configuration object for this feature
+     */
+    constructor(private _xrSessionManager: WebXRSessionManager, private _options: IWebXRAnchorSystemOptions = {}) {
+    }
+
+    /**
+     * set the plane detector to use in order to create anchors from frames
+     * @param planeDetector the plane-detector module to use
+     * @param enable enable plane-anchors. default is true
+     */
+    public setPlaneDetector(planeDetector: WebXRPlaneDetector, enable: boolean = true) {
+        this._planeDetector = planeDetector;
+        this._options.usePlaneDetection = enable;
+    }
+
+    /**
+     * If set, it will improve performance by using the current hit-test results instead of executing a new hit-test
+     * @param hitTestModule the hit-test module to use.
+     */
+    public setHitTestModule(hitTestModule: WebXRHitTestLegacy) {
+        this._hitTestModule = hitTestModule;
+    }
+
+    /**
+     * attach this feature
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    attach(): boolean {
+        this._observerTracked = this._xrSessionManager.onXRFrameObservable.add(() => {
+            const frame = this._xrSessionManager.currentFrame;
+            if (!this._attached || !this._enabled || !frame) { return; }
+            // const timestamp = this.xrSessionManager.currentTimestamp;
+
+            const trackedAnchors = frame.trackedAnchors;
+            if (trackedAnchors && trackedAnchors.size) {
+                this._trackedAnchors.filter((anchor) => !trackedAnchors.has(anchor.xrAnchor)).map((anchor) => {
+                    const index = this._trackedAnchors.indexOf(anchor);
+                    this._trackedAnchors.splice(index, 1);
+                    this.onAnchorRemovedObservable.notifyObservers(anchor);
+                });
+                // now check for new ones
+                trackedAnchors.forEach((xrAnchor) => {
+                    if (!this._lastFrameDetected.has(xrAnchor)) {
+                        const newAnchor: Partial<IWebXRAnchor> = {
+                            id: anchorIdProvider++,
+                            xrAnchor: xrAnchor
+                        };
+                        const plane = this._updateAnchorWithXRFrame(xrAnchor, newAnchor, frame);
+                        this._trackedAnchors.push(plane);
+                        this.onAnchorAddedObservable.notifyObservers(plane);
+                    } else {
+                        // updated?
+                        if (xrAnchor.lastChangedTime === this._xrSessionManager.currentTimestamp) {
+                            let index = this._findIndexInAnchorArray(xrAnchor);
+                            const anchor = this._trackedAnchors[index];
+                            this._updateAnchorWithXRFrame(xrAnchor, anchor, frame);
+                            this.onAnchorUpdatedObservable.notifyObservers(anchor);
+                        }
+                    }
+                });
+                this._lastFrameDetected = trackedAnchors;
+            }
+        });
+
+        if (this._options.addAnchorOnSelect) {
+            this._xrSessionManager.session.addEventListener('select', this._onSelect, false);
+        }
+
+        this._attached = true;
+        return true;
+    }
+
+    /**
+     * detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    detach(): boolean {
+        this._attached = false;
+
+        this._xrSessionManager.session.removeEventListener('select', this._onSelect);
+
+        if (this._observerTracked) {
+            this._xrSessionManager.onXRFrameObservable.remove(this._observerTracked);
+        }
+
+        return true;
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    dispose(): void {
+        this.detach();
+        this.onAnchorAddedObservable.clear();
+        this.onAnchorRemovedObservable.clear();
+        this.onAnchorUpdatedObservable.clear();
+    }
+
+    private _onSelect = (event: XRInputSourceEvent) => {
+        if (!this._options.addAnchorOnSelect) {
+            return;
+        }
+        const onResults = (results: XRHitResult[]) => {
+            if (results.length) {
+                const hitResult = results[0];
+                const transform = new XRRigidTransform(hitResult.hitMatrix);
+                // find the plane on which to add.
+                this.addAnchorAtRigidTransformation(transform);
+            }
+        };
+
+        // avoid the hit-test, if the hit-test module is defined
+        if (this._hitTestModule && !this._hitTestModule.options.testOnPointerDownOnly) {
+            onResults(this._hitTestModule.lastNativeXRHitResults);
+        }
+        WebXRHitTestLegacy.XRHitTestWithSelectEvent(event, this._xrSessionManager.referenceSpace).then(onResults);
+
+        // API will soon change, will need to use the plane
+        this._planeDetector;
+    }
+
+    /**
+     * Add anchor at a specific XR point.
+     *
+     * @param xrRigidTransformation xr-coordinates where a new anchor should be added
+     * @param anchorCreator the object o use to create an anchor with. either a session or a plane
+     * @returns a promise the fulfills when the anchor was created
+     */
+    public addAnchorAtRigidTransformation(xrRigidTransformation: XRRigidTransform, anchorCreator?: XRAnchorCreator): Promise<XRAnchor> {
+        const creator = anchorCreator || this._xrSessionManager.session;
+        return creator.createAnchor(xrRigidTransformation, this._xrSessionManager.referenceSpace);
+    }
+
+    private _updateAnchorWithXRFrame(xrAnchor: XRAnchor, anchor: Partial<IWebXRAnchor>, xrFrame: XRFrame): IWebXRAnchor {
+        // matrix
+        const pose = xrFrame.getPose(xrAnchor.anchorSpace, this._xrSessionManager.referenceSpace);
+        if (pose) {
+            const mat = anchor.transformationMatrix || new Matrix();
+            Matrix.FromArrayToRef(pose.transform.matrix, 0, mat);
+            if (!this._xrSessionManager.scene.useRightHandedSystem) {
+                mat.toggleModelMatrixHandInPlace();
+            }
+            anchor.transformationMatrix = mat;
+            if (!this._options.worldParentNode) {
+                // Logger.Warn("Please provide a world parent node to apply world transformation");
+            } else {
+                mat.multiplyToRef(this._options.worldParentNode.getWorldMatrix(), mat);
+            }
+        }
+
+        return <IWebXRAnchor>anchor;
+    }
+
+    /**
+     * avoiding using Array.find for global support.
+     * @param xrAnchor the plane to find in the array
+     */
+    private _findIndexInAnchorArray(xrAnchor: XRAnchor) {
+        for (let i = 0; i < this._trackedAnchors.length; ++i) {
+            if (this._trackedAnchors[i].xrAnchor === xrAnchor) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+}
+
+//register the plugin
+WebXRFeaturesManager.AddWebXRFeature(WebXRAnchorSystem.Name, (xrSessionManager, options) => {
+    return () => new WebXRAnchorSystem(xrSessionManager, options);
+}, WebXRAnchorSystem.Version);

+ 136 - 0
src/Cameras/XR/features/WebXRBackgroundRemover.ts

@@ -0,0 +1,136 @@
+import { WebXRFeaturesManager, IWebXRFeature } from "../webXRFeaturesManager";
+import { WebXRSessionManager } from '../webXRSessionManager';
+import { AbstractMesh } from '../../../Meshes/abstractMesh';
+import { Observable } from '../../../Misc/observable';
+
+const Name = "xr-background-remover";
+
+/**
+ * Options interface for the background remover plugin
+ */
+export interface IWebXRBackgroundRemoverOptions {
+    /**
+     * don't disable the environment helper
+     */
+    ignoreEnvironmentHelper?: boolean;
+    /**
+     * flags to configure the removal of the environment helper.
+     * If not set, the entire background will be removed. If set, flags should be set as well.
+     */
+    environmentHelperRemovalFlags?: {
+        /**
+         * Should the skybox be removed (default false)
+         */
+        skyBox?: boolean;
+        /**
+         * Should the ground be removed (default false)
+         */
+        ground?: boolean;
+    };
+    /**
+     * Further background meshes to disable when entering AR
+     */
+    backgroundMeshes?: AbstractMesh[];
+}
+
+/**
+ * A module that will automatically disable background meshes when entering AR and will enable them when leaving AR.
+ */
+export class WebXRBackgroundRemover implements IWebXRFeature {
+
+    /**
+     * The module's name
+     */
+    public static readonly Name = Name;
+    /**
+     * 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;
+
+    /**
+     * registered observers will be triggered when the background state changes
+     */
+    public onBackgroundStateChangedObservable: Observable<boolean> = new Observable();
+
+    /**
+     * constructs a new background remover module
+     * @param _xrSessionManager the session manager for this module
+     * @param options read-only options to be used in this module
+     */
+    constructor(private _xrSessionManager: WebXRSessionManager,
+        /**
+         * read-only options to be used in this module
+         */
+        public readonly options: IWebXRBackgroundRemoverOptions = {}) {
+
+    }
+
+    /**
+     * attach this feature
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    attach(): boolean {
+        this._setBackgroundState(false);
+
+        return true;
+    }
+
+    /**
+     * detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    detach(): boolean {
+        this._setBackgroundState(true);
+
+        return true;
+    }
+
+    private _setBackgroundState(newState: boolean) {
+        const scene = this._xrSessionManager.scene;
+        if (!this.options.ignoreEnvironmentHelper) {
+            if (this.options.environmentHelperRemovalFlags) {
+                if (this.options.environmentHelperRemovalFlags.skyBox) {
+                    const backgroundSkybox = scene.getMeshByName("BackgroundSkybox");
+                    if (backgroundSkybox) {
+                        backgroundSkybox.setEnabled(newState);
+                    }
+                }
+                if (this.options.environmentHelperRemovalFlags.ground) {
+                    const backgroundPlane = scene.getMeshByName("BackgroundPlane");
+                    if (backgroundPlane) {
+                        backgroundPlane.setEnabled(newState);
+                    }
+                }
+            } else {
+                const backgroundHelper = scene.getMeshByName("BackgroundHelper");
+                if (backgroundHelper) {
+                    backgroundHelper.setEnabled(newState);
+                }
+            }
+        }
+
+        if (this.options.backgroundMeshes) {
+            this.options.backgroundMeshes.forEach((mesh) => mesh.setEnabled(newState));
+        }
+
+        this.onBackgroundStateChangedObservable.notifyObservers(newState);
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    dispose(): void {
+        this.onBackgroundStateChangedObservable.clear();
+    }
+}
+
+//register the plugin
+WebXRFeaturesManager.AddWebXRFeature(WebXRBackgroundRemover.Name, (xrSessionManager, options) => {
+    return () => new WebXRBackgroundRemover(xrSessionManager, options);
+}, WebXRBackgroundRemover.Version, true);

+ 215 - 0
src/Cameras/XR/features/WebXRHitTestLegacy.ts

@@ -0,0 +1,215 @@
+import { IWebXRFeature, WebXRFeaturesManager } from '../webXRFeaturesManager';
+import { WebXRSessionManager } from '../webXRSessionManager';
+import { Observable, Observer } from '../../../Misc/observable';
+import { Vector3, Matrix } from '../../../Maths/math.vector';
+import { TransformNode } from '../../../Meshes/transformNode';
+import { Nullable } from '../../../types';
+
+/**
+ * name of module (can be reused with other versions)
+ */
+const WebXRHitTestModuleName = "xr-hit-test";
+
+// the plugin is registered at the end of the file
+
+/**
+ * Options used for hit testing
+ */
+export interface IWebXRHitTestOptions {
+    /**
+     * Only test when user interacted with the scene. Default - hit test every frame
+     */
+    testOnPointerDownOnly?: boolean;
+    /**
+     * The node to use to transform the local results to world coordinates
+     */
+    worldParentNode?: TransformNode;
+}
+
+/**
+ * Interface defining the babylon result of raycasting/hit-test
+ */
+export interface IWebXRHitResult {
+    /**
+     * The native hit test result
+     */
+    xrHitResult: XRHitResult;
+    /**
+     * Transformation matrix that can be applied to a node that will put it in the hit point location
+     */
+    transformationMatrix: Matrix;
+}
+
+/**
+ * The currently-working hit-test module.
+ * Hit test (or raycasting) is used to interact with the real world.
+ * For further information read here - https://github.com/immersive-web/hit-test
+ */
+export class WebXRHitTestLegacy implements IWebXRFeature {
+
+    /**
+     * The module's name
+     */
+    public static readonly Name = WebXRHitTestModuleName;
+    /**
+     * 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;
+
+    /**
+     * Execute a hit test on the current running session using a select event returned from a transient input (such as touch)
+     * @param event the (select) event to use to select with
+     * @param referenceSpace the reference space to use for this hit test
+     * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
+     */
+    public static XRHitTestWithSelectEvent(event: XRInputSourceEvent, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]> {
+        let targetRayPose = event.frame.getPose(event.inputSource.targetRaySpace, referenceSpace);
+        if (!targetRayPose) {
+            return Promise.resolve([]);
+        }
+        let targetRay = new XRRay(targetRayPose.transform);
+
+        return this.XRHitTestWithRay(event.frame.session, targetRay, referenceSpace);
+    }
+
+    /**
+     * execute a hit test with an XR Ray
+     *
+     * @param xrSession a native xrSession that will execute this hit test
+     * @param xrRay the ray (position and direction) to use for raycasting
+     * @param referenceSpace native XR reference space to use for the hit-test
+     * @param filter filter function that will filter the results
+     * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
+     */
+    public static XRHitTestWithRay(xrSession: XRSession, xrRay: XRRay, referenceSpace: XRReferenceSpace, filter?: (result: XRHitResult) => boolean): Promise<XRHitResult[]> {
+        return xrSession.requestHitTest(xrRay, referenceSpace).then((results) => {
+            const filterFunction = filter || ((result) => !!result.hitMatrix);
+            return results.filter(filterFunction);
+        });
+    }
+
+    /**
+     * Triggered when new babylon (transformed) hit test results are available
+     */
+    public onHitTestResultObservable: Observable<IWebXRHitResult[]> = new Observable();
+
+    /**
+     * Creates a new instance of the (legacy version) hit test feature
+     * @param _xrSessionManager an instance of WebXRSessionManager
+     * @param options options to use when constructing this feature
+     */
+    constructor(private _xrSessionManager: WebXRSessionManager,
+        /**
+         * options to use when constructing this feature
+         */
+        public readonly options: IWebXRHitTestOptions = {}) { }
+
+    private _onSelectEnabled = false;
+    private _xrFrameObserver: Nullable<Observer<XRFrame>>;
+    private _attached: boolean = false;
+
+    /**
+     * Populated with the last native XR Hit Results
+     */
+    public lastNativeXRHitResults: XRHitResult[] = [];
+
+    /**
+     * attach this feature
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    attach(): boolean {
+        if (this.options.testOnPointerDownOnly) {
+            this._xrSessionManager.session.addEventListener('select', this._onSelect, false);
+        } else {
+            // we are in XR space!
+            const origin = new Vector3(0, 0, 0);
+            // in XR space z-forward is negative
+            const direction = new Vector3(0, 0, -1);
+            const mat = new Matrix();
+            this._xrFrameObserver = this._xrSessionManager.onXRFrameObservable.add((frame) => {
+                // make sure we do nothing if (async) not attached
+                if (!this._attached) {
+                    return;
+                }
+                let pose = frame.getViewerPose(this._xrSessionManager.referenceSpace);
+                if (!pose) {
+                    return;
+                }
+                Matrix.FromArrayToRef(pose.transform.matrix, 0, mat);
+                Vector3.TransformCoordinatesFromFloatsToRef(0, 0, 0, mat, origin);
+                Vector3.TransformCoordinatesFromFloatsToRef(0, 0, -1, mat, direction);
+                direction.subtractInPlace(origin);
+                direction.normalize();
+                let ray = new XRRay((<DOMPointReadOnly>{ x: origin.x, y: origin.y, z: origin.z, w: 0 }),
+                    (<DOMPointReadOnly>{ x: direction.x, y: direction.y, z: direction.z, w: 0 }));
+                WebXRHitTestLegacy.XRHitTestWithRay(this._xrSessionManager.session, ray, this._xrSessionManager.referenceSpace).then(this._onHitTestResults);
+            });
+        }
+        this._attached = true;
+
+        return true;
+    }
+
+    /**
+     * detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    detach(): boolean {
+        // disable select
+        this._onSelectEnabled = false;
+        this._xrSessionManager.session.removeEventListener('select', this._onSelect);
+        if (this._xrFrameObserver) {
+            this._xrSessionManager.onXRFrameObservable.remove(this._xrFrameObserver);
+            this._xrFrameObserver = null;
+        }
+        this._attached = false;
+        return true;
+    }
+
+    private _onHitTestResults = (xrResults: XRHitResult[]) => {
+        const mats = xrResults.map((result) => {
+            let mat = Matrix.FromArray(result.hitMatrix);
+            if (!this._xrSessionManager.scene.useRightHandedSystem) {
+                mat.toggleModelMatrixHandInPlace();
+            }
+            // if (this.options.coordinatesSpace === Space.WORLD) {
+            if (this.options.worldParentNode) {
+                mat.multiplyToRef(this.options.worldParentNode.getWorldMatrix(), mat);
+            }
+            return {
+                xrHitResult: result,
+                transformationMatrix: mat
+            };
+        });
+
+        this.lastNativeXRHitResults = xrResults;
+        this.onHitTestResultObservable.notifyObservers(mats);
+    }
+
+    // can be done using pointerdown event, and xrSessionManager.currentFrame
+    private _onSelect = (event: XRInputSourceEvent) => {
+        if (!this._onSelectEnabled) {
+            return;
+        }
+        WebXRHitTestLegacy.XRHitTestWithSelectEvent(event, this._xrSessionManager.referenceSpace);
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    dispose(): void {
+        this.detach();
+        this.onHitTestResultObservable.clear();
+    }
+}
+
+//register the plugin versions
+WebXRFeaturesManager.AddWebXRFeature(WebXRHitTestLegacy.Name, (xrSessionManager, options) => {
+    return () => new WebXRHitTestLegacy(xrSessionManager, options);
+}, WebXRHitTestLegacy.Version, true);

+ 213 - 0
src/Cameras/XR/features/WebXRPlaneDetector.ts

@@ -0,0 +1,213 @@
+import { WebXRFeaturesManager, IWebXRFeature } from '../webXRFeaturesManager';
+import { TransformNode } from '../../../Meshes/transformNode';
+import { WebXRSessionManager } from '../webXRSessionManager';
+import { Observable, Observer } from '../../../Misc/observable';
+import { Vector3, Matrix } from '../../../Maths/math.vector';
+import { Nullable } from '../../../types';
+
+const Name = "xr-plane-detector";
+
+/**
+ * Options used in the plane detector module
+ */
+export interface IWebXRPlaneDetectorOptions {
+    /**
+     * The node to use to transform the local results to world coordinates
+     */
+    worldParentNode?: TransformNode;
+}
+
+/**
+ * A babylon interface for a webxr plane.
+ * A Plane is actually a polygon, built from N points in space
+ */
+export interface IWebXRPlane {
+    /**
+     * a babylon-assigned ID for this polygon
+     */
+    id: number;
+    /**
+     * the native xr-plane object
+     */
+    xrPlane: XRPlane;
+    /**
+     * an array of vector3 points in babylon space. right/left hand system is taken into account.
+     */
+    polygonDefinition: Array<Vector3>;
+    /**
+     * A transformation matrix to apply on the mesh that will be built using the polygonDefinition
+     * Local vs. World are decided if worldParentNode was provided or not in the options when constructing the module
+     */
+    transformationMatrix: Matrix;
+}
+
+let planeIdProvider = 0;
+
+/**
+ * The plane detector is used to detect planes in the real world when in AR
+ * For more information see https://github.com/immersive-web/real-world-geometry/
+ */
+export class WebXRPlaneDetector implements IWebXRFeature {
+
+    /**
+     * The module's name
+     */
+    public static readonly Name = Name;
+    /**
+     * 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;
+
+    /**
+     * Observers registered here will be executed when a new plane was added to the session
+     */
+    public onPlaneAddedObservable: Observable<IWebXRPlane> = new Observable();
+    /**
+     * Observers registered here will be executed when a plane is no longer detected in the session
+     */
+    public onPlaneRemovedObservable: Observable<IWebXRPlane> = new Observable();
+    /**
+     * Observers registered here will be executed when an existing plane updates (for example - expanded)
+     * This can execute N times every frame
+     */
+    public onPlaneUpdatedObservable: Observable<IWebXRPlane> = new Observable();
+
+    private _enabled: boolean = false;
+    private _attached: boolean = false;
+    private _detectedPlanes: Array<IWebXRPlane> = [];
+    private _lastFrameDetected: XRPlaneSet = new Set();
+    private _observerTracked: Nullable<Observer<XRFrame>>;
+
+    /**
+     * construct a new Plane Detector
+     * @param _xrSessionManager an instance of xr Session manager
+     * @param _options configuration to use when constructing this feature
+     */
+    constructor(private _xrSessionManager: WebXRSessionManager, private _options: IWebXRPlaneDetectorOptions = {}) {
+        if (this._xrSessionManager.session) {
+            this._xrSessionManager.session.updateWorldTrackingState({ planeDetectionState: { enabled: true } });
+            this._enabled = true;
+        } else {
+            this._xrSessionManager.onXRSessionInit.addOnce(() => {
+                this._xrSessionManager.session.updateWorldTrackingState({ planeDetectionState: { enabled: true } });
+                this._enabled = true;
+            });
+        }
+    }
+
+    /**
+     * attach this feature
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    attach(): boolean {
+
+        this._observerTracked = this._xrSessionManager.onXRFrameObservable.add(() => {
+            const frame = this._xrSessionManager.currentFrame;
+            if (!this._attached || !this._enabled || !frame) { return; }
+            // const timestamp = this.xrSessionManager.currentTimestamp;
+
+            const detectedPlanes = frame.worldInformation.detectedPlanes;
+            if (detectedPlanes && detectedPlanes.size) {
+                this._detectedPlanes.filter((plane) => !detectedPlanes.has(plane.xrPlane)).map((plane) => {
+                    const index = this._detectedPlanes.indexOf(plane);
+                    this._detectedPlanes.splice(index, 1);
+                    this.onPlaneRemovedObservable.notifyObservers(plane);
+                });
+                // now check for new ones
+                detectedPlanes.forEach((xrPlane) => {
+                    if (!this._lastFrameDetected.has(xrPlane)) {
+                        const newPlane: Partial<IWebXRPlane> = {
+                            id: planeIdProvider++,
+                            xrPlane: xrPlane,
+                            polygonDefinition: []
+                        };
+                        const plane = this._updatePlaneWithXRPlane(xrPlane, newPlane, frame);
+                        this._detectedPlanes.push(plane);
+                        this.onPlaneAddedObservable.notifyObservers(plane);
+                    } else {
+                        // updated?
+                        if (xrPlane.lastChangedTime === this._xrSessionManager.currentTimestamp) {
+                            let index = this.findIndexInPlaneArray(xrPlane);
+                            const plane = this._detectedPlanes[index];
+                            this._updatePlaneWithXRPlane(xrPlane, plane, frame);
+                            this.onPlaneUpdatedObservable.notifyObservers(plane);
+                        }
+                    }
+                });
+                this._lastFrameDetected = detectedPlanes;
+            }
+        });
+
+        this._attached = true;
+        return true;
+    }
+
+    /**
+     * detach this feature.
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    detach(): boolean {
+        this._attached = false;
+
+        if (this._observerTracked) {
+            this._xrSessionManager.onXRFrameObservable.remove(this._observerTracked);
+        }
+
+        return true;
+    }
+
+    /**
+     * Dispose this feature and all of the resources attached
+     */
+    dispose(): void {
+        this.detach();
+        this.onPlaneAddedObservable.clear();
+        this.onPlaneRemovedObservable.clear();
+        this.onPlaneUpdatedObservable.clear();
+    }
+
+    private _updatePlaneWithXRPlane(xrPlane: XRPlane, plane: Partial<IWebXRPlane>, xrFrame: XRFrame): IWebXRPlane {
+        plane.polygonDefinition = xrPlane.polygon.map((xrPoint) => {
+            const rightHandedSystem = this._xrSessionManager.scene.useRightHandedSystem ? 1 : -1;
+            return new Vector3(xrPoint.x, xrPoint.y, xrPoint.z * rightHandedSystem);
+        });
+        // matrix
+        const pose = xrFrame.getPose(xrPlane.planeSpace, this._xrSessionManager.referenceSpace);
+        if (pose) {
+            const mat = plane.transformationMatrix || new Matrix();
+            Matrix.FromArrayToRef(pose.transform.matrix, 0, mat);
+            if (!this._xrSessionManager.scene.useRightHandedSystem) {
+                mat.toggleModelMatrixHandInPlace();
+            }
+            plane.transformationMatrix = mat;
+            if (this._options.worldParentNode) {
+                mat.multiplyToRef(this._options.worldParentNode.getWorldMatrix(), mat);
+            }
+        }
+        return <IWebXRPlane>plane;
+    }
+
+    /**
+     * avoiding using Array.find for global support.
+     * @param xrPlane the plane to find in the array
+     */
+    private findIndexInPlaneArray(xrPlane: XRPlane) {
+        for (let i = 0; i < this._detectedPlanes.length; ++i) {
+            if (this._detectedPlanes[i].xrPlane === xrPlane) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}
+
+//register the plugin
+WebXRFeaturesManager.AddWebXRFeature(WebXRPlaneDetector.Name, (xrSessionManager, options) => {
+    return () => new WebXRPlaneDetector(xrSessionManager, options);
+}, WebXRPlaneDetector.Version);

+ 4 - 0
src/Cameras/XR/features/index.ts

@@ -0,0 +1,4 @@
+export * from "./WebXRHitTestLegacy";
+export * from "./WebXRAnchorSystem";
+export * from "./WebXRPlaneDetector";
+export * from "./WebXRBackgroundRemover";

+ 3 - 1
src/Cameras/XR/index.ts

@@ -9,4 +9,6 @@ export * from "./webXRController";
 export * from "./webXRManagedOutputCanvas";
 export * from "./webXRTypes";
 export * from "./webXRSessionManager";
-export * from "./webXRDefaultExperience";
+export * from "./webXRDefaultExperience";
+export * from "./webXRFeaturesManager";
+export * from "./features/index";

+ 5 - 0
src/Cameras/XR/webXRExperienceHelper.ts

@@ -7,6 +7,7 @@ import { Camera } from "../../Cameras/camera";
 import { WebXRSessionManager } from "./webXRSessionManager";
 import { WebXRCamera } from "./webXRCamera";
 import { WebXRState, WebXRRenderTarget } from './webXRTypes';
+import { WebXRFeaturesManager } from './webXRFeaturesManager';
 
 /**
  * Base set of functionality needed to create an XR experince (WebXRSessionManager, Camera, StateManagement, etc.)
@@ -42,6 +43,9 @@ export class WebXRExperienceHelper implements IDisposable {
     /** Session manager used to keep track of xr session */
     public sessionManager: WebXRSessionManager;
 
+    /** A features manager for this xr session */
+    public featuresManager: WebXRFeaturesManager;
+
     private _nonVRCamera: Nullable<Camera> = null;
     private _originalSceneAutoClear = true;
 
@@ -69,6 +73,7 @@ export class WebXRExperienceHelper implements IDisposable {
     private constructor(private scene: Scene) {
         this.camera = new WebXRCamera("", scene);
         this.sessionManager = new WebXRSessionManager(scene);
+        this.featuresManager = new WebXRFeaturesManager(this.sessionManager);
         this.container = new AbstractMesh("WebXR Container", scene);
         this.camera.parent = this.container;
 

+ 279 - 0
src/Cameras/XR/webXRFeaturesManager.ts

@@ -0,0 +1,279 @@
+import { WebXRSessionManager } from './webXRSessionManager';
+import { IDisposable } from '../../scene';
+
+/**
+ * Defining the interface required for a (webxr) feature
+ */
+export interface IWebXRFeature extends IDisposable {
+    /**
+     * Attach the feature to the session
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    attach(): boolean;
+    /**
+     * Detach the feature from the session
+     * Will usually be called by the features manager
+     *
+     * @returns true if successful.
+     */
+    detach(): boolean;
+}
+
+/**
+ * Defining the constructor of a feature. Used to register the modules.
+ */
+export type WebXRFeatureConstructor = (xrSessionManager: WebXRSessionManager, options?: any) => (() => IWebXRFeature);
+
+/**
+ * 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]: {
+            stable: number;
+            latest: number;
+            [version: number]: WebXRFeatureConstructor;
+        }
+    } = {};
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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): (() => IWebXRFeature) {
+        const constructorFunction = this._AvailableFeatures[featureName][version];
+        if (!constructorFunction) {
+            // throw an error? return nothing?
+            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
+     */
+    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
+     */
+    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
+     *
+     * @returns an Array of available features
+     */
+    public static GetAvailableFeatures() {
+        return Object.keys(this._AvailableFeatures);
+    }
+
+    /**
+     * 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: IWebXRFeature,
+            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];
+                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];
+                if (feature.enabled && feature.attached) {
+                    // detach, but don't disable!
+                    this.detachFeature(featureName);
+                }
+            });
+        });
+    }
+
+    /**
+     * Enable a feature using its name and a version. This will enable it in the scene, and will be responsible to attach it when the session starts.
+     *
+     * @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): IWebXRFeature {
+        const name = typeof featureName === 'string' ? featureName : featureName.Name;
+        let versionToLoad = 0;
+        if (typeof version === 'string') {
+            if (version === 'stable') {
+                versionToLoad = WebXRFeaturesManager.GetStableVersionOfFeature(name);
+            } else if (version === 'latest') {
+                versionToLoad = WebXRFeaturesManager.GetLatestVersionOfFeature(name);
+            }
+            if (versionToLoad === -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}`);
+            }
+
+            if (feature) {
+                this.disableFeature(name);
+            }
+
+            this._features[name] = {
+                featureImplementation: constructFunction(),
+                attached: false,
+                enabled: true,
+                version: versionToLoad
+            };
+        } else {
+            // make sure it is enabled now:
+            feature.enabled = true;
+        }
+
+        // if session started already, request and enable
+        if (this._xrSessionManager.session && !feature.attached && attachIfPossible) {
+            // enable feature
+            this.attachFeature(name);
+        }
+
+        return this._features[name].featureImplementation;
+    }
+
+    /**
+     * Used to disable an already-enabled feature
+     * @param featureName the feature to disable
+     * @returns true if disable was successful
+     */
+    public disableFeature(featureName: string | { Name: string }): boolean {
+        const name = typeof featureName === 'string' ? featureName : featureName.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];
+        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];
+        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);
+    }
+
+    /**
+     * get the implementation of an enabled feature.
+     * @param featureName the name of the feature to load
+     * @returns the feature class, if found
+     */
+    public getEnabledFeature(featureName: string): IWebXRFeature {
+        return this._features[featureName] && this._features[featureName].featureImplementation;
+    }
+
+    /**
+     * dispose this features manager
+     */
+    dispose(): void {
+        this.getEnabledFeatures().forEach((feature) => this._features[feature].featureImplementation.dispose());
+    }
+
+}

+ 7 - 5
src/Cameras/XR/webXRSessionManager.ts

@@ -56,6 +56,9 @@ export class WebXRSessionManager implements IDisposable {
      */
     public currentFrame: Nullable<XRFrame>;
 
+    /** WebXR timestamp updated every frame */
+    public currentTimestamp: number = -1;
+
     private _xrNavigator: any;
     private baseLayer: Nullable<XRWebGLLayer> = null;
     private _rttProvider: Nullable<IRenderTargetProvider>;
@@ -166,6 +169,7 @@ export class WebXRSessionManager implements IDisposable {
                 }
                 // Store the XR frame in the manager to be consumed by the XR camera to update pose
                 this.currentFrame = xrFrame;
+                this.currentTimestamp = timestamp;
                 if (xrFrame) {
                     this.onXRFrameObservable.notifyObservers(xrFrame);
                 }
@@ -202,12 +206,10 @@ export class WebXRSessionManager implements IDisposable {
      * @returns Promise which resolves after it exits XR
      */
     public exitXRAsync() {
-        if (this.session) {
-            try {
-                return this.session.end();
-            } catch (e) {
+        if (this.session && !this._sessionEnded) {
+            return this.session.end().catch((e) => {
                 Logger.Warn("could not end XR session. It has ended already.");
-            }
+            });
         }
         return Promise.resolve();
     }

+ 38 - 1
src/Engines/Native/nativeShaderProcessor.ts

@@ -6,10 +6,27 @@ const attributeLocations: { [kind: string]: number } = {
     [VertexBuffer.PositionKind]: 0,
     [VertexBuffer.NormalKind]: 1,
     [VertexBuffer.TangentKind]: 2,
+    [VertexBuffer.UVKind]: 10,
+    [VertexBuffer.UV2Kind]: 11,
+    [VertexBuffer.UV3Kind]: 12,
+    [VertexBuffer.UV4Kind]: 13,
     [VertexBuffer.ColorKind]: 4,
     [VertexBuffer.MatricesIndicesKind]: 8,
     [VertexBuffer.MatricesWeightsKind]: 9,
 };
+// Remap BJS names to bgfx names
+const attributeBGFXName: { [kind: string]: string } = {
+    [VertexBuffer.PositionKind]: "a_position",
+    [VertexBuffer.NormalKind]: "a_normal",
+    [VertexBuffer.TangentKind]: "a_tangent",
+    [VertexBuffer.UVKind]: "a_texcoord0",
+    [VertexBuffer.UV2Kind]: "a_texcoord1",
+    [VertexBuffer.UV3Kind]: "a_texcoord2",
+    [VertexBuffer.UV4Kind]: "a_texcoord3",
+    [VertexBuffer.ColorKind]: "a_color0",
+    [VertexBuffer.MatricesIndicesKind]: "a_indices",
+    [VertexBuffer.MatricesWeightsKind]: "a_weight",
+};
 
 // Must match bgfx::Attrib::TexCoord0
 const firstGenericAttributeLocation = 10;
@@ -45,7 +62,12 @@ export class NativeShaderProcessor extends WebGL2ShaderProcessor {
                 throw new Error("Exceeded maximum custom attributes");
             }
         }
-
+        let newName = attributeBGFXName[name];
+        if (newName === undefined) {
+            throw new Error("Can't find bgfx name mapping");
+        }
+        attribute = attribute.replace(name, newName);
+        this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${newName}` });
         return `layout(location=${location}) ${super.attributeProcessor(attribute)}`;
     }
 
@@ -76,6 +98,21 @@ export class NativeShaderProcessor extends WebGL2ShaderProcessor {
                 this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `sampler${suffix}(${name}Texture, ${name})` });
                 return `layout(binding=${binding}) uniform texture${suffix} ${name}Texture;\nlayout(binding=${binding}) uniform sampler ${name};`;
             }
+            case "float": {
+                this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${name}.x` });
+                uniform = `uniform vec4 ${name};`;
+                break;
+            }
+            case "vec2": {
+                this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${name}.xy` });
+                uniform = `uniform vec4 ${name};`;
+                break;
+            }
+            case "vec3": {
+                this._replacements.push({ searchValue: new RegExp(`\\b${name}\\b`), replaceValue: `${name}.xyz` });
+                uniform = `uniform vec4 ${name};`;
+                break;
+            }
         }
 
         this._uniforms.push(uniform);

+ 5 - 0
src/Engines/WebGL/webGLPipelineContext.ts

@@ -13,6 +13,11 @@ export class WebGLPipelineContext implements IPipelineContext {
     public onCompiled?: () => void;
     public transformFeedback?: WebGLTransformFeedback | null;
 
+    public vertexCompilationError: Nullable<string> = null;
+    public fragmentCompilationError: Nullable<string> = null;
+    public programLinkError: Nullable<string> = null;
+    public programValidationError: Nullable<string> = null;
+
     public get isAsync() {
         return this.isParallelCompiled;
     }

+ 8 - 0
src/Engines/nativeEngine.ts

@@ -293,6 +293,14 @@ export class NativeEngine extends Engine {
         }
     }
 
+    /**
+     * Gets host document
+     * @returns the host document object
+     */
+    public getHostDocument(): Nullable<Document> {
+        return null;
+    }
+
     public clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
         var mode = 0;
         if (backBuffer && color) {

+ 18 - 8
src/Engines/thinEngine.ts

@@ -131,14 +131,14 @@ export class ThinEngine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.1.0-beta.8";
+        return "babylonjs@4.1.0-beta.10";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.1.0-beta.8";
+        return "4.1.0-beta.10";
     }
 
     /**
@@ -2168,27 +2168,28 @@ export class ThinEngine {
         const program = pipelineContext.program!;
 
         var linked = context.getProgramParameter(program, context.LINK_STATUS);
-
         if (!linked) { // Get more info
-
             // Vertex
             if (!this._gl.getShaderParameter(vertexShader, this._gl.COMPILE_STATUS)) {
-                let log = this._gl.getShaderInfoLog(vertexShader);
+                const log = this._gl.getShaderInfoLog(vertexShader);
                 if (log) {
+                    pipelineContext.vertexCompilationError = log;
                     throw new Error("VERTEX SHADER " + log);
                 }
             }
 
             // Fragment
             if (!this._gl.getShaderParameter(fragmentShader, this._gl.COMPILE_STATUS)) {
-                let log = this._gl.getShaderInfoLog(fragmentShader);
+                const log = this._gl.getShaderInfoLog(fragmentShader);
                 if (log) {
+                    pipelineContext.fragmentCompilationError = log;
                     throw new Error("FRAGMENT SHADER " + log);
                 }
             }
 
             var error = context.getProgramInfoLog(program);
             if (error) {
+                pipelineContext.programLinkError = error;
                 throw new Error(error);
             }
         }
@@ -2200,6 +2201,7 @@ export class ThinEngine {
             if (!validated) {
                 var error = context.getProgramInfoLog(program);
                 if (error) {
+                    pipelineContext.programValidationError = error;
                     throw new Error(error);
                 }
             }
@@ -2622,13 +2624,18 @@ export class ThinEngine {
         this._viewportCached.w = 0;
 
         if (bruteForce) {
-            this.resetTextureCache();
             this._currentProgram = null;
+            this.resetTextureCache();
 
             this._stencilState.reset();
+
             this._depthCullingState.reset();
             this._depthCullingState.depthFunc = this._gl.LEQUAL;
+
             this._alphaState.reset();
+            this._alphaMode = Constants.ALPHA_ADD;
+            this._alphaEquation = Constants.ALPHA_DISABLE;
+
             this._colorWrite = true;
             this._colorWriteChanged = true;
 
@@ -2636,6 +2643,9 @@ export class ThinEngine {
 
             this._gl.pixelStorei(this._gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, this._gl.NONE);
             this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
+
+            this._mustWipeVertexAttributes = true;
+            this.unbindAllAttributes();
         }
 
         this._resetVertexBufferBinding();
@@ -4302,7 +4312,7 @@ export class ThinEngine {
      * Gets host document
      * @returns the host document object
      */
-    public getHostDocument(): Document {
+    public getHostDocument(): Nullable<Document> {
         if (this._renderingCanvas && this._renderingCanvas.ownerDocument) {
             return this._renderingCanvas.ownerDocument;
         }

+ 58 - 8
src/LibDeclarations/webxr.d.ts

@@ -55,8 +55,9 @@ interface XRInputSource {
     profiles: Array<string>;
 }
 
-interface XRSession {
+interface XRSession extends XRAnchorCreator {
     addEventListener: Function;
+    removeEventListener: Function;
     requestReferenceSpace(type: XRReferenceSpaceType): Promise<XRReferenceSpace>;
     updateRenderState(XRRenderStateInit: XRRenderState): Promise<void>;
     requestAnimationFrame: Function;
@@ -64,6 +65,12 @@ interface XRSession {
     renderState: XRRenderState;
     inputSources: Array<XRInputSource>;
 
+    // AR hit test
+    requestHitTest(ray: XRRay, referenceSpace: XRReferenceSpace): Promise<XRHitResult[]>;
+
+    updateWorldTrackingState(options: {
+        planeDetectionState?: { enabled: boolean; }
+    }): void;
 }
 
 interface XRReferenceSpace extends XRSpace {
@@ -71,10 +78,20 @@ interface XRReferenceSpace extends XRSpace {
     onreset: any;
 }
 
+type XRPlaneSet = Set<XRPlane>;
+type XRAnchorSet = Set<XRAnchor>;
+
 interface XRFrame {
     session: XRSession;
     getViewerPose(referenceSpace: XRReferenceSpace): XRViewerPose | undefined;
     getPose(space: XRSpace, baseSpace: XRSpace): XRPose | undefined;
+
+    // Anchors
+    trackedAnchors?: XRAnchorSet;
+    // Planes
+    worldInformation: {
+        detectedPlanes?: XRPlaneSet;
+    };
 }
 
 interface XRViewerPose extends XRPose {
@@ -87,12 +104,12 @@ interface XRPose {
 }
 
 interface XRWebGLLayerOptions {
-    antialias ?: boolean;
-    depth ?: boolean;
-    stencil ?: boolean;
-    alpha ?: boolean;
-    multiview ?: boolean;
-    framebufferScaleFactor ?: number;
+    antialias?: boolean;
+    depth?: boolean;
+    stencil?: boolean;
+    alpha?: boolean;
+    multiview?: boolean;
+    framebufferScaleFactor?: number;
 }
 
 declare var XRWebGLLayer: {
@@ -106,7 +123,8 @@ interface XRWebGLLayer {
     getViewport: Function;
 }
 
-interface XRRigidTransform {
+declare class XRRigidTransform {
+    constructor(matrix: Float32Array);
     position: DOMPointReadOnly;
     orientation: DOMPointReadOnly;
     matrix: Float32Array;
@@ -128,4 +146,36 @@ interface XRInputSourceChangeEvent {
 interface XRInputSourceEvent extends Event {
     readonly frame: XRFrame;
     readonly inputSource: XRInputSource;
+}
+
+// Experimental(er) features
+declare class XRRay {
+    constructor(transformOrOrigin: XRRigidTransform | DOMPointReadOnly, direction?: DOMPointReadOnly);
+    origin: DOMPointReadOnly;
+    direction: DOMPointReadOnly;
+    matrix: Float32Array;
+}
+
+interface XRHitResult {
+    hitMatrix: Float32Array;
+}
+
+interface XRAnchor {
+    // remove?
+    id?: string;
+    anchorSpace: XRSpace;
+    lastChangedTime: number;
+    detach(): void;
+}
+
+interface XRPlane extends XRAnchorCreator {
+    orientation: "Horizontal" | "Vertical";
+    planeSpace: XRSpace;
+    polygon: Array<DOMPointReadOnly>;
+    lastChangedTime: number;
+}
+
+interface XRAnchorCreator {
+    // AR Anchors
+    createAnchor(pose: XRPose | XRRigidTransform, referenceSpace: XRReferenceSpace): Promise<XRAnchor>;
 }

+ 2 - 0
src/Materials/material.ts

@@ -624,6 +624,7 @@ export class Material implements IAnimatable {
      * Locks updates for the material
      */
     public freeze(): void {
+        this._wasPreviouslyReady = false;
         this.checkReadyOnlyOnce = true;
     }
 
@@ -631,6 +632,7 @@ export class Material implements IAnimatable {
      * Unlocks updates for the material
      */
     public unfreeze(): void {
+        this._wasPreviouslyReady = false;
         this.checkReadyOnlyOnce = false;
     }
 

+ 9 - 9
src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx

@@ -72,12 +72,12 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m
         float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
 
         #ifdef BRDF_V_HEIGHT_CORRELATED
-            float visibility = smithVisibility_GGXCorrelated(info.NdotL, info.NdotV, alphaG);
+            float smithVisibility = smithVisibility_GGXCorrelated(info.NdotL, info.NdotV, alphaG);
         #else
-            float visibility = smithVisibility_TrowbridgeReitzGGXFast(info.NdotL, info.NdotV, alphaG);
+            float smithVisibility = smithVisibility_TrowbridgeReitzGGXFast(info.NdotL, info.NdotV, alphaG);
         #endif
 
-        vec3 specTerm = fresnel * distribution * visibility;
+        vec3 specTerm = fresnel * distribution * smithVisibility;
         return specTerm * info.attenuation * info.NdotL * lightColor;
     }
 #endif
@@ -97,9 +97,9 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m
 
         vec3 fresnel = fresnelSchlickGGX(info.VdotH, reflectance0, reflectance90);
         float distribution = normalDistributionFunction_BurleyGGX_Anisotropic(NdotH, TdotH, BdotH, alphaTB);
-        float visibility = smithVisibility_GGXCorrelated_Anisotropic(info.NdotL, info.NdotV, TdotV, BdotV, TdotL, BdotL, alphaTB);
+        float smithVisibility = smithVisibility_GGXCorrelated_Anisotropic(info.NdotL, info.NdotV, TdotV, BdotV, TdotL, BdotL, alphaTB);
 
-        vec3 specTerm = fresnel * distribution * visibility;
+        vec3 specTerm = fresnel * distribution * smithVisibility;
         return specTerm * info.attenuation * info.NdotL * lightColor;
     }
 #endif
@@ -114,9 +114,9 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m
         float fresnel = fresnelSchlickGGX(info.VdotH, vClearCoatRefractionParams.x, CLEARCOATREFLECTANCE90);
         fresnel *= clearCoatIntensity;
         float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NccdotH, alphaG);
-        float visibility = visibility_Kelemen(info.VdotH);
+        float kelemenVisibility = visibility_Kelemen(info.VdotH);
 
-        float clearCoatTerm = fresnel * distribution * visibility;
+        float clearCoatTerm = fresnel * distribution * kelemenVisibility;
 
         return vec4(
             clearCoatTerm * info.attenuation * NccdotL * lightColor,
@@ -143,9 +143,9 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m
         // vec3 fresnel = fresnelSchlickGGX(info.VdotH, reflectance0, reflectance90);
         vec3 fresnel = reflectance0;
         float distribution = normalDistributionFunction_CharlieSheen(NdotH, alphaG);
-        float visibility = visibility_Ashikhmin(info.NdotL, info.NdotV);
+        float ashikhminvisibility = visibility_Ashikhmin(info.NdotL, info.NdotV);
 
-        vec3 sheenTerm = fresnel * distribution * visibility;
+        vec3 sheenTerm = fresnel * distribution * ashikhminvisibility;
         return sheenTerm * info.attenuation * info.NdotL * lightColor;
     }
 #endif

+ 7 - 6
src/Shaders/spriteMap.fragment.fx

@@ -37,7 +37,6 @@ mat4 getFrameData(float frameID){
 void main(){
     vec4 color = vec4(0.);    
     vec2 tileUV = fract(tUV);
-
     #ifdef FLIPU
         tileUV.y = 1.0 - tileUV.y;
     #endif
@@ -45,12 +44,14 @@ void main(){
     vec2 tileID = floor(tUV);	
     vec2 sheetUnits = 1. / spriteMapSize;
     float spriteUnits = 1. / spriteCount;
-    vec2 stageUnits = 1. / stageSize;	
-
-    for(int i = 0; i < LAYERS; i++){	
+    vec2 stageUnits = 1. / stageSize;        
     
-        float frameID = texture(tileMaps[i], (tileID + 0.5) / stageSize, 0.).x;		
-
+    for(int i = 0; i < LAYERS; i++){
+        float frameID;
+            switch(i){
+                #define LAYER_ID_SWITCH      
+            }        
+        
         vec4 animationData = texture(animationMap, vec2((frameID + 0.5) / spriteCount, 0.), 0.); 
         
         if(animationData.y > 0.){

+ 0 - 78
src/Shaders/spriteMap.fragment.ts

@@ -1,78 +0,0 @@
-import { Effect } from "../Materials/effect";
-
-let name = 'spriteMapPixelShader';
-let shader = `precision highp float;
-varying vec3 vPosition;
-varying vec2 vUV;
-varying vec2 tUV;
-uniform float time;
-uniform float spriteCount;
-uniform sampler2D spriteSheet;
-uniform vec2 spriteMapSize;
-uniform vec2 outputSize;
-uniform vec2 stageSize;
-uniform float maxAnimationFrames;
-uniform sampler2D frameMap;
-uniform sampler2D tileMaps[LAYERS];
-uniform sampler2D animationMap;
-uniform vec3 colorMul;
-float mt;
-float fdStep=1./4.;
-mat4 getFrameData(float frameID){
-float fX=frameID/spriteCount;
-return mat4(
-texture(frameMap,vec2(fX,0.),0.),
-texture(frameMap,vec2(fX,fdStep*1.),0.),
-texture(frameMap,vec2(fX,fdStep*2.),0.),
-vec4(0.)
-);
-}
-void main(){
-vec4 color=vec4(0.);
-vec2 tileUV=fract(tUV);
-#ifdef FLIPU
-tileUV.y=1.0-tileUV.y;
-#endif
-vec2 tileID=floor(tUV);
-vec2 sheetUnits=1./spriteMapSize;
-float spriteUnits=1./spriteCount;
-vec2 stageUnits=1./stageSize;
-for(int i=0; i<LAYERS; i++){
-float frameID=texture(tileMaps[i],(tileID+0.5)/stageSize,0.).x;
-vec4 animationData=texture(animationMap,vec2((frameID+0.5)/spriteCount,0.),0.);
-if(animationData.y>0.){
-mt=mod(time*animationData.z,1.0);
-float aFrameSteps=1./maxAnimationFrames;
-for(float f=0.; f<maxAnimationFrames; f++){
-if(animationData.y>mt){
-frameID=animationData.x;
-break;
-}
-animationData=texture(animationMap,vec2((frameID+0.5)/spriteCount,aFrameSteps*f),0.);
-}
-}
-
-mat4 frameData=getFrameData(frameID+0.5);
-vec2 frameSize=(frameData[0].wz)/spriteMapSize;
-vec2 offset=frameData[0].xy*sheetUnits;
-vec2 ratio=frameData[2].xy/frameData[0].wz;
-
-if(frameData[2].z == 1.){
-tileUV.xy=tileUV.yx;
-}
-if(i == 0){
-color=texture(spriteSheet,tileUV*frameSize+offset);
-} else {
-vec4 nc=texture(spriteSheet,tileUV*frameSize+offset);
-float alpha=min(color.a+nc.a,1.0);
-vec3 mixed=mix(color.xyz,nc.xyz,nc.a);
-color=vec4(mixed,alpha);
-}
-}
-color.xyz*=colorMul;
-gl_FragColor=color;
-}`;
-
-Effect.ShadersStore[name] = shader;
-/** @hidden */
-export var spriteMapPixelShader = { name, shader };

+ 0 - 33
src/Shaders/spriteMap.vertex.ts

@@ -1,33 +0,0 @@
-import { Effect } from "../Materials/effect";
-
-let name = 'spriteMapVertexShader';
-let shader = `precision highp float;
-
-attribute vec3 position;
-attribute vec3 normal;
-attribute vec2 uv;
-
-varying vec3 vPosition;
-varying vec2 vUV;
-varying vec2 tUV;
-varying vec2 stageUnits;
-varying vec2 levelUnits;
-varying vec2 tileID;
-
-uniform float time;
-uniform mat4 worldViewProjection;
-uniform vec2 outputSize;
-uniform vec2 stageSize;
-uniform vec2 spriteMapSize;
-uniform float stageScale;
-void main() {
-vec4 p=vec4( position,1. );
-vPosition=p.xyz;
-vUV=uv;
-tUV=uv*stageSize;
-gl_Position=worldViewProjection*p;
-}`;
-
-Effect.ShadersStore[name] = shader;
-/** @hidden */
-export var spriteMapVertexShader = { name, shader };

+ 12 - 1
src/Sprites/spriteMap.ts

@@ -8,6 +8,7 @@ import { ShaderMaterial } from "../Materials/shaderMaterial";
 import { Mesh } from "../Meshes/mesh";
 import { PickingInfo } from "../Collisions/pickingInfo";
 import { ISpriteJSONSprite, ISpriteJSONAtlas } from "./ISprites";
+import { Effect } from "../Materials/effect";
 
 import "../Meshes/Builders/planeBuilder";
 import "../Shaders/spriteMap.fragment";
@@ -220,9 +221,19 @@ export class SpriteMap implements ISpriteMap {
         defines.push("#define FLIPU");
     }
 
+    let shaderString: string = Effect.ShadersStore['spriteMapPixelShader'];
+    let layerSampleString: string = '';
+
+    for (let i = 0; i < this.options.layerCount; i++) {
+        layerSampleString += 'case ' + i + ' : frameID = texture(tileMaps[' + i + '], (tileID + 0.5) / stageSize, 0.).x;';
+        layerSampleString += 'break;';
+    }
+
+    Effect.ShadersStore['spriteMap' + this.name + 'PixelShader'] = shaderString.replace('#define LAYER_ID_SWITCH',  layerSampleString);
+
     this._material = new ShaderMaterial("spriteMap:" + this.name, this._scene, {
         vertex: "spriteMap",
-        fragment: "spriteMap",
+        fragment: "spriteMap" + this.name,
     }, {
         defines,
         attributes: ["position", "normal", "uv"],