Просмотр исходного кода

Merge pull request #5031 from sebavan/master

Outline Renderer as a Component
David Catuhe 7 лет назад
Родитель
Сommit
ebd096b0aa

+ 0 - 10
src/Mesh/babylon.abstractMesh.ts

@@ -341,21 +341,11 @@
             this._markSubMeshesAsLightDirty();
         }
 
-        /**
-         * Gets or sets a boolean indicating if the outline must be rendered as well
-         * @see https://www.babylonjs-playground.com/#10WJ5S#3
-         */
-        public renderOutline = false;
         /** Defines color to use when rendering outline */
         public outlineColor = Color3.Red();
         /** Define width to use when rendering outline */
         public outlineWidth = 0.02;
 
-        /**
-         * Gets or sets a boolean indicating if the overlay must be rendered as well
-         * @see https://www.babylonjs-playground.com/#10WJ5S#2
-         */        
-        public renderOverlay = false;
         /** Defines color to use when rendering overlay */
         public overlayColor = Color3.Red();
         /** Defines alpha to use when rendering overlay */

+ 4 - 20
src/Mesh/babylon.mesh.ts

@@ -1415,12 +1415,8 @@
                 engine.setAlphaMode(this._effectiveMaterial.alphaMode);
             }
 
-            // Outline - step 1
-            var savedDepthWrite = engine.getDepthWrite();
-            if (this.renderOutline) {
-                engine.setDepthWrite(false);
-                scene.getOutlineRenderer().render(subMesh, batch);
-                engine.setDepthWrite(savedDepthWrite);
+            for (let step of scene._beforeRenderingMeshStage) {
+                step.action(this, subMesh, batch);
             }
 
             var effect: Nullable<Effect>;
@@ -1475,20 +1471,8 @@
             // Unbind
             this._effectiveMaterial.unbind();
 
-            // Outline - step 2
-            if (this.renderOutline && savedDepthWrite) {
-                engine.setDepthWrite(true);
-                engine.setColorWrite(false);
-                scene.getOutlineRenderer().render(subMesh, batch);
-                engine.setColorWrite(true);
-            }
-
-            // Overlay
-            if (this.renderOverlay) {
-                var currentMode = engine.getAlphaMode();
-                engine.setAlphaMode(Engine.ALPHA_COMBINE);
-                scene.getOutlineRenderer().render(subMesh, batch, true);
-                engine.setAlphaMode(currentMode);
+            for (let step of scene._afterRenderingMeshStage) {
+                step.action(this, subMesh, batch);
             }
 
             if (this._onAfterRenderObservable) {

+ 166 - 8
src/Rendering/babylon.outlineRenderer.ts

@@ -1,18 +1,141 @@
 module BABYLON {
-    export class OutlineRenderer {
-        private _scene: Scene;
-        private _effect: Effect;
-        private _cachedDefines: string;
+    export interface Scene {
+        /** @hidden */
+        _outlineRenderer: OutlineRenderer;
 
+        /** 
+         * Gets the outline renderer associated with the scene
+         * @returns a OutlineRenderer
+         */
+        getOutlineRenderer(): OutlineRenderer;
+    }
+
+    /** 
+     * Gets the outline renderer associated with the scene
+     * @returns a OutlineRenderer
+     */
+    Scene.prototype.getOutlineRenderer = function(): OutlineRenderer {
+        if (!this._outlineRenderer) {
+            this._outlineRenderer = new OutlineRenderer(this);
+        }
+        return this._outlineRenderer;
+    }
+
+    export interface AbstractMesh {
+        /** @hidden (Backing field) */
+        _renderOutline: boolean;
+        /**
+         * Gets or sets a boolean indicating if the outline must be rendered as well
+         * @see https://www.babylonjs-playground.com/#10WJ5S#3
+         */
+        renderOutline: boolean;
+
+        /** @hidden (Backing field) */
+        _renderOverlay: boolean;
+        /**
+         * Gets or sets a boolean indicating if the overlay must be rendered as well
+         * @see https://www.babylonjs-playground.com/#10WJ5S#2
+         */
+        renderOverlay: boolean;
+    }
+
+    Object.defineProperty(AbstractMesh.prototype, "renderOutline", {
+        get: function (this:AbstractMesh) {
+            return this._renderOutline;
+        },
+        set: function (this:AbstractMesh, value: boolean) {
+            if (value) {
+                // Lazy Load the component.
+                this.getScene().getOutlineRenderer();
+            }
+            this._renderOutline = value;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    Object.defineProperty(AbstractMesh.prototype, "renderOverlay", {
+        get: function (this:AbstractMesh) {
+            return this._renderOverlay;
+        },
+        set: function (this:AbstractMesh, value: boolean) {
+            if (value) {
+                // Lazy Load the component.
+                this.getScene().getOutlineRenderer();
+            }
+            this._renderOverlay = value;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    /**
+     * This class is responsible to draw bothe outline/overlay of meshes.
+     * It should not be used directly but through the available method on mesh.
+     */
+    export class OutlineRenderer implements ISceneComponent {
+        /**
+         * The name of the component. Each component must have a unique name.
+         */
+        public name = SceneComponentConstants.NAME_OUTLINERENDERER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /**
+         * Defines a zOffset to prevent zFighting between the overlay and the mesh.
+         */
         public zOffset = 1;
 
+        private _engine: Engine;
+        private _effect: Effect;
+        private _cachedDefines: string;
+        private _savedDepthWrite: boolean;
+
+        /**
+         * Instantiates a new outline renderer. (There could be only one per scene).
+         * @param scene Defines the scene it belongs to
+         */
         constructor(scene: Scene) {
-            this._scene = scene;
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this.scene._addComponent(this);
+        }
+
+        /**
+         * Register the component to one instance of a scene.
+         */
+        public register(): void {
+            this.scene._beforeRenderingMeshStage.registerStep(SceneComponentConstants.STEP_BEFORERENDERINGMESH_OUTLINE, this, this._beforeRenderingMesh);
+            this.scene._afterRenderingMeshStage.registerStep(SceneComponentConstants.STEP_AFTERRENDERINGMESH_OUTLINE, this, this._afterRenderingMesh);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            // Nothing to do here.
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            // Nothing to do here.
         }
 
+        /**
+         * Renders the outline in the canvas.
+         * @param subMesh Defines the sumesh to render
+         * @param batch Defines the batch of meshes in case of instances
+         * @param useOverlay Defines if the rendering is for the overlay or the outline
+         */
         public render(subMesh: SubMesh, batch: _InstancesBatch, useOverlay: boolean = false): void {
-            var scene = this._scene;
-            var engine = this._scene.getEngine();
+            var scene = this.scene;
+            var engine = scene.getEngine();
 
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
 
@@ -63,6 +186,13 @@
             engine.setZOffset(0);
         }
 
+        /**
+         * Returns whether or not the outline renderer is ready for a given submesh.
+         * All the dependencies e.g. submeshes, texture, effect... mus be ready
+         * @param subMesh Defines the submesh to check readyness for
+         * @param useInstances Defines wheter wee are trying to render instances or not
+         * @returns true if ready otherwise false
+         */
         public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
             var defines = [];
             var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
@@ -117,7 +247,7 @@
             var join = defines.join("\n");
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
-                this._effect = this._scene.getEngine().createEffect("outline",
+                this._effect = this.scene.getEngine().createEffect("outline",
                     attribs,
                     ["world", "mBones", "viewProjection", "diffuseMatrix", "offset", "color", "logarithmicDepthConstant"],
                     ["diffuseSampler"], join);
@@ -125,5 +255,33 @@
 
             return this._effect.isReady();
         }
+
+        private _beforeRenderingMesh(mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch): void {
+            // Outline - step 1
+            this._savedDepthWrite = this._engine.getDepthWrite();
+            if (mesh.renderOutline) {
+                this._engine.setDepthWrite(false);
+                this.render(subMesh, batch);
+                this._engine.setDepthWrite(this._savedDepthWrite);
+            }
+        }
+
+        private _afterRenderingMesh(mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch): void {
+            // Outline - step 2
+            if (mesh.renderOutline && this._savedDepthWrite) {
+                this._engine.setDepthWrite(true);
+                this._engine.setColorWrite(false);
+                this.render(subMesh, batch);
+                this._engine.setColorWrite(true);
+            }
+
+            // Overlay
+            if (mesh.renderOverlay) {
+                var currentMode = this._engine.getAlphaMode();
+                this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
+                this.render(subMesh, batch, true);
+                this._engine.setAlphaMode(currentMode);
+            }
+        }
     }
 } 

+ 13 - 13
src/babylon.scene.ts

@@ -1000,7 +1000,6 @@
         private _alternateSceneUbo: UniformBuffer;
 
         private _pickWithRayInverseMatrix: Matrix;
-        private _outlineRenderer: OutlineRenderer;
 
         private _viewMatrix: Matrix;
         private _projectionMatrix: Matrix;
@@ -1146,11 +1145,22 @@
          */
         public _beforeCameraDrawStage = Stage.Create<CameraStageAction>();
         /**
+         * @hidden
          * Defines the actions happening just before a rendering group is drawing.
          */
         public _beforeRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
         /**
          * @hidden
+         * Defines the actions happening just before a mesh is drawing.
+         */
+        public _beforeRenderingMeshStage = Stage.Create<RenderingMeshStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening just after a mesh has been drawn.
+         */
+        public _afterRenderingMeshStage = Stage.Create<RenderingMeshStageAction>();
+        /**
+         * @hidden
          * Defines the actions happening just after a rendering group has been drawn.
          */
         public _afterRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
@@ -1197,10 +1207,6 @@
                 this.postProcessManager = new PostProcessManager(this);
             }
 
-            if (OutlineRenderer) {
-                this._outlineRenderer = new OutlineRenderer(this);
-            }
-
             if (Tools.IsWindowObjectExist()) {
                 this.attachControl();
             }
@@ -1318,14 +1324,6 @@
         }
 
         /** 
-         * Gets the outline renderer associated with the scene
-         * @returns a OutlineRenderer
-         */
-        public getOutlineRenderer(): OutlineRenderer {
-            return this._outlineRenderer;
-        }
-
-        /** 
          * Gets the engine associated with the scene
          * @returns an Engine
          */
@@ -4806,6 +4804,8 @@
             this._cameraDrawRenderTargetStage.clear();
             this._beforeCameraDrawStage.clear();
             this._beforeRenderingGroupDrawStage.clear();
+            this._beforeRenderingMeshStage.clear();
+            this._afterRenderingMeshStage.clear();
             this._afterRenderingGroupDrawStage.clear();
             this._afterCameraDrawStage.clear();
             this._beforeCameraUpdateStage.clear();

+ 10 - 0
src/babylon.sceneComponent.ts

@@ -15,6 +15,7 @@
         public static readonly NAME_DEPTHRENDERER = "DepthRenderer";
         public static readonly NAME_POSTPROCESSRENDERPIPELINEMANAGER = "PostProcessRenderPipelineManager";
         public static readonly NAME_SPRITE = "Sprite";
+        public static readonly NAME_OUTLINERENDERER = "Outline";
 
         public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;
 
@@ -29,6 +30,10 @@
         public static readonly STEP_BEFORECAMERADRAW_EFFECTLAYER = 0;
         public static readonly STEP_BEFORECAMERADRAW_LAYER = 1;
 
+        public static readonly STEP_BEFORERENDERINGMESH_OUTLINE = 0;
+
+        public static readonly STEP_AFTERRENDERINGMESH_OUTLINE = 0;
+
         public static readonly STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW = 0;
 
         public static readonly STEP_BEFORECAMERAUPDATE_SIMPLIFICATIONQUEUE = 0;
@@ -136,6 +141,11 @@
     export type RenderingGroupStageAction = (renderingGroupId: number) => void;
 
     /** 
+     * Strong typing of a Mesh Render related stage step action 
+     */
+    export type RenderingMeshStageAction = (mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch) => void;
+
+    /** 
      * Strong typing of a simple stage step action 
      */
     export type SimpleStageAction = () => void;

BIN
tests/validation/ReferenceImages/outline.png


+ 8 - 3
tests/validation/config.json

@@ -1,16 +1,21 @@
 {
   "root": "https://rawgit.com/BabylonJS/Website/master",
-  "tests": [  
+  "tests": [
+    {
+      "title": "Outline",
+      "playgroundId": "#10WJ5S#6",
+      "referenceImage": "outline.png"
+    },
     {
       "title": "Clip planes",
       "playgroundId": "#Y6W087#0",
       "referenceImage": "clipplanes.png"
-    },         
+    },
     {
       "title": "ShadowOnlyMaterial",
       "playgroundId": "#1KF7V1#18",
       "referenceImage": "shadowOnlyMaterial.png"
-    },     
+    },
     {
       "title": "Gizmos",
       "playgroundId": "#8GY6J8#48",