소스 검색

Add a node material mode property

Popov72 5 년 전
부모
커밋
d431bdbe36

+ 101 - 68
nodeEditor/src/components/preview/previewManager.ts

@@ -18,11 +18,15 @@ import { DirectionalLight } from 'babylonjs/Lights/directionalLight';
 import { LogEntry } from '../log/logComponent';
 import { PointerEventTypes } from 'babylonjs/Events/pointerEvents';
 import { Color3 } from 'babylonjs/Maths/math.color';
+import { PostProcess } from 'babylonjs/PostProcesses/postProcess';
+import { Constants } from 'babylonjs/Engines/constants';
+import { CurrentScreenBlock } from 'babylonjs/Materials/Node/Blocks/Dual/currentScreenBlock';
+import { NodeMaterialModes } from 'babylonjs/Materials/Node/Enums/nodeMaterialModes';
 
 export class PreviewManager {
     private _nodeMaterial: NodeMaterial;
     private _onBuildObserver: Nullable<Observer<NodeMaterial>>;    
-    private _onPreviewCommandActivatedObserver: Nullable<Observer<void>>;
+    private _onPreviewCommandActivatedObserver: Nullable<Observer<boolean>>;
     private _onAnimationCommandActivatedObserver: Nullable<Observer<void>>;
     private _onUpdateRequiredObserver: Nullable<Observer<void>>;
     private _onPreviewBackgroundChangedObserver: Nullable<Observer<void>>;
@@ -37,6 +41,7 @@ export class PreviewManager {
     private _globalState: GlobalState;   
     private _currentType: number; 
     private _lightParent: TransformNode;
+    private _postprocess: Nullable<PostProcess>;
 
     public constructor(targetCanvas: HTMLCanvasElement, globalState: GlobalState) {
         this._nodeMaterial = globalState.nodeMaterial;
@@ -47,7 +52,10 @@ export class PreviewManager {
             this._updatePreview(serializationObject);
         });
 
-        this._onPreviewCommandActivatedObserver = globalState.onPreviewCommandActivated.add(() => {
+        this._onPreviewCommandActivatedObserver = globalState.onPreviewCommandActivated.add((forceRefresh: boolean) => {
+            if (forceRefresh) {
+                this._currentType = -1;
+            }
             this._refreshPreviewMesh();
         });
 
@@ -186,33 +194,35 @@ export class PreviewManager {
     }
 
     private _prepareMeshes() {
-        this._prepareLights();
-
-        // Framing
-        this._camera.useFramingBehavior = true;
-
-        var framingBehavior = this._camera.getBehaviorByName("Framing") as FramingBehavior;
-
-        setTimeout(() => { // Let the behavior activate first
-            framingBehavior.framingTime = 0;
-            framingBehavior.elevationReturnTime = -1;
-    
-            if (this._scene.meshes.length) {
-                var worldExtends = this._scene.getWorldExtends();
-                this._camera.lowerRadiusLimit = null;
-                this._camera.upperRadiusLimit = null;
-                framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
-            }
-    
-            this._camera.pinchPrecision = 200 / this._camera.radius;
-            this._camera.upperRadiusLimit = 5 * this._camera.radius;    
-        });
+        if (this._globalState.mode !== NodeMaterialModes.PostProcess) {
+            this._prepareLights();
+
+            // Framing
+            this._camera.useFramingBehavior = true;
+
+            var framingBehavior = this._camera.getBehaviorByName("Framing") as FramingBehavior;
+
+            setTimeout(() => { // Let the behavior activate first
+                framingBehavior.framingTime = 0;
+                framingBehavior.elevationReturnTime = -1;
+        
+                if (this._scene.meshes.length) {
+                    var worldExtends = this._scene.getWorldExtends();
+                    this._camera.lowerRadiusLimit = null;
+                    this._camera.upperRadiusLimit = null;
+                    framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
+                }
+        
+                this._camera.pinchPrecision = 200 / this._camera.radius;
+                this._camera.upperRadiusLimit = 5 * this._camera.radius;    
+            });
 
-        this._camera.wheelDeltaPercentage = 0.01;
-        this._camera.pinchDeltaPercentage = 0.01;
+            this._camera.wheelDeltaPercentage = 0.01;
+            this._camera.pinchDeltaPercentage = 0.01;
 
-        // Animations
-        this._handleAnimations();
+            // Animations
+            this._handleAnimations();
+        }
 
         // Material        
         let serializationObject = this._nodeMaterial.serialize();
@@ -222,7 +232,6 @@ export class PreviewManager {
     private _refreshPreviewMesh() {    
 
         if (this._currentType !== this._globalState.previewMeshType || this._currentType === PreviewMeshType.Custom) {
-
             this._currentType = this._globalState.previewMeshType;
             if (this._meshes && this._meshes.length) {
                 for (var mesh of this._meshes) {
@@ -241,46 +250,48 @@ export class PreviewManager {
             SceneLoader.ShowLoadingScreen = false;
 
             this._globalState.onIsLoadingChanged.notifyObservers(true);
-        
-            switch (this._globalState.previewMeshType) {
-                case PreviewMeshType.Box:
-                    SceneLoader.AppendAsync("https://models.babylonjs.com/", "roundedCube.glb", this._scene).then(() => {     
-                        this._meshes.push(...this._scene.meshes);
-                        this._prepareMeshes();
-                    });     
-                    break;
-                case PreviewMeshType.Sphere:
-                    this._meshes.push(Mesh.CreateSphere("dummy-sphere", 32, 2, this._scene));
-                    break;
-                case PreviewMeshType.Torus:
-                    this._meshes.push(Mesh.CreateTorus("dummy-torus", 2, 0.5, 32, this._scene));
-                    break;
-                case PreviewMeshType.Cylinder:
-                    SceneLoader.AppendAsync("https://models.babylonjs.com/", "roundedCylinder.glb", this._scene).then(() => {     
-                        this._meshes.push(...this._scene.meshes);
-                        this._prepareMeshes();
-                    });                    
-                    return;                   
-                case PreviewMeshType.Plane:
-                    let plane = Mesh.CreateGround("dummy-plane", 2, 2, 128, this._scene);
-                    plane.scaling.y = -1;
-                    plane.rotation.x = Math.PI;
-                    this._meshes.push(plane);
-                    break;    
-                case PreviewMeshType.ShaderBall:
-                    SceneLoader.AppendAsync("https://models.babylonjs.com/", "shaderBall.glb", this._scene).then(() => {     
-                        this._meshes.push(...this._scene.meshes);
-                        this._prepareMeshes();
-                    });
-                    return;                             
-                case PreviewMeshType.Custom:
-                    SceneLoader.AppendAsync("file:", this._globalState.previewMeshFile, this._scene).then(() => {     
-                        this._meshes.push(...this._scene.meshes);
-                        this._prepareMeshes();
-                    });
-                    return;     
+
+            if (this._globalState.mode !== NodeMaterialModes.PostProcess) {
+                switch (this._globalState.previewMeshType) {
+                    case PreviewMeshType.Box:
+                        SceneLoader.AppendAsync("https://models.babylonjs.com/", "roundedCube.glb", this._scene).then(() => {     
+                            this._meshes.push(...this._scene.meshes);
+                            this._prepareMeshes();
+                        });     
+                        return;
+                    case PreviewMeshType.Sphere:
+                        this._meshes.push(Mesh.CreateSphere("dummy-sphere", 32, 2, this._scene));
+                        break;
+                    case PreviewMeshType.Torus:
+                        this._meshes.push(Mesh.CreateTorus("dummy-torus", 2, 0.5, 32, this._scene));
+                        break;
+                    case PreviewMeshType.Cylinder:
+                        SceneLoader.AppendAsync("https://models.babylonjs.com/", "roundedCylinder.glb", this._scene).then(() => {     
+                            this._meshes.push(...this._scene.meshes);
+                            this._prepareMeshes();
+                        });                    
+                        return;                   
+                    case PreviewMeshType.Plane:
+                        let plane = Mesh.CreateGround("dummy-plane", 2, 2, 128, this._scene);
+                        plane.scaling.y = -1;
+                        plane.rotation.x = Math.PI;
+                        this._meshes.push(plane);
+                        break;    
+                    case PreviewMeshType.ShaderBall:
+                        SceneLoader.AppendAsync("https://models.babylonjs.com/", "shaderBall.glb", this._scene).then(() => {     
+                            this._meshes.push(...this._scene.meshes);
+                            this._prepareMeshes();
+                        });
+                        return;                             
+                    case PreviewMeshType.Custom:
+                        SceneLoader.AppendAsync("file:", this._globalState.previewMeshFile, this._scene).then(() => {     
+                            this._meshes.push(...this._scene.meshes);
+                            this._prepareMeshes();
+                        });
+                        return;     
+                }
             }
-            
+
             this._prepareMeshes();
         }
     }
@@ -296,7 +307,28 @@ export class PreviewManager {
             tempMaterial.backFaceCulling = this._globalState.backFaceCulling;
             tempMaterial.needDepthPrePass = this._globalState.depthPrePass;
 
-            if (this._meshes.length) {
+            if (this._postprocess) {
+                this._postprocess.dispose(this._camera);
+                this._postprocess = null;
+            }
+
+            if (this._globalState.mode === NodeMaterialModes.PostProcess) {
+                this._globalState.onIsLoadingChanged.notifyObservers(false);
+
+                this._postprocess = tempMaterial.createPostProcess(this._camera, 1.0, Constants.TEXTURE_NEAREST_SAMPLINGMODE, this._engine);
+
+                const currentScreen = tempMaterial.getBlockByPredicate((block) => block instanceof CurrentScreenBlock);
+                if (currentScreen) {
+                    this._postprocess!.onApplyObservable.add((effect) => {
+                        effect.setTexture("textureSampler", (currentScreen as CurrentScreenBlock).texture);
+                    });
+                }
+
+                if (this._material) {
+                    this._material.dispose();
+                }
+                this._material = tempMaterial;
+            } else if (this._meshes.length) {
                 let tasks = this._meshes.map(m => this._forceCompilationAsync(tempMaterial, m));
 
                 Promise.all(tasks).then(() => {
@@ -319,6 +351,7 @@ export class PreviewManager {
             }
         } catch(err) {
             // Ignore the error
+            this._globalState.onIsLoadingChanged.notifyObservers(false);
         }
     }
 

+ 2 - 2
nodeEditor/src/components/preview/previewMeshControlComponent.tsx

@@ -32,7 +32,7 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
         }
 
         this.props.globalState.previewMeshType = newOne;
-        this.props.globalState.onPreviewCommandActivated.notifyObservers();
+        this.props.globalState.onPreviewCommandActivated.notifyObservers(false);
 
         DataStorage.WriteNumber("PreviewMeshType", newOne);
 
@@ -46,7 +46,7 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
 
             this.props.globalState.previewMeshFile = file;
             this.props.globalState.previewMeshType = PreviewMeshType.Custom;
-            this.props.globalState.onPreviewCommandActivated.notifyObservers();
+            this.props.globalState.onPreviewCommandActivated.notifyObservers(false);
             this.props.globalState.listOfCustomPreviewMeshFiles = [file];       
             this.forceUpdate();
         }

+ 40 - 0
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -31,6 +31,8 @@ import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
 import { FrameNodePort } from '../../diagram/frameNodePort';
 import { NodePort } from '../../diagram/nodePort';
 import { isFramePortData } from '../../diagram/graphCanvas';
+import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
+import { NodeMaterialModes } from 'babylonjs/Materials/Node/Enums/nodeMaterialModes';
 require("./propertyTab.scss");
 
 interface IPropertyTabComponentProps {
@@ -46,11 +48,14 @@ interface IPropertyTabComponentState {
 
 export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, IPropertyTabComponentState> {
     private _onBuiltObserver: Nullable<Observer<void>>;
+    private _modeSelect: React.RefObject<OptionsLineComponent>;
 
     constructor(props: IPropertyTabComponentProps) {
         super(props);
 
         this.state = { currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: null };
+
+        this._modeSelect = React.createRef();
     }
 
     componentDidMount() {
@@ -150,6 +155,8 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
             let decoder = new TextDecoder("utf-8");
             SerializationTools.Deserialize(JSON.parse(decoder.decode(data)), this.props.globalState);
 
+            this.changeMode(this.props.globalState.nodeMaterial!.mode, true, false);
+            this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
         }, undefined, true);
     }
 
@@ -241,6 +248,36 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
         });
     }
 
+    changeMode(value: any, force = false, loadDefault = true) {
+        if (this.props.globalState.mode === value) {
+            return;
+        }
+
+        if (!force && !confirm('Are your sure? You will loose your current changes (if any) if they are not saved!')) {
+            this._modeSelect.current?.setValue(this.props.globalState.mode);
+            return;
+        }
+
+        if (force) {
+            this._modeSelect.current?.setValue(value);
+        }
+
+        this.props.globalState.mode = value as NodeMaterialModes;
+
+        if (loadDefault) {
+            switch (value) {
+                case NodeMaterialModes.Material:
+                    this.props.globalState.nodeMaterial!.setToDefault();
+                    this.props.globalState.onResetRequiredObservable.notifyObservers();
+                    break;
+                case NodeMaterialModes.PostProcess:
+                    this.props.globalState.nodeMaterial!.setToDefaultPostProcess();
+                    this.props.globalState.onResetRequiredObservable.notifyObservers();
+                    break;
+            }
+        }
+    }
+
     render() {
         if (this.state.currentNode) {
             return (
@@ -285,6 +322,9 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                     </div>
                 </div>
                 <div>
+                    <LineContainerComponent title="MODE">
+                        <OptionsLineComponent ref={this._modeSelect} label="Mode" target={this} getSelection={(target) => this.props.globalState.mode} options={[{ label: "Material", value: NodeMaterialModes.Material }, { label: "Post Process", value: NodeMaterialModes.PostProcess }]} onSelect={(value) => this.changeMode(value)} />
+                    </LineContainerComponent>
                     <LineContainerComponent title="GENERAL">
                         <TextLineComponent label="Version" value={Engine.Version}/>
                         <TextLineComponent label="Help" value="doc.babylonjs.com" underline={true} onLink={() => window.open('https://doc.babylonjs.com/how_to/node_material', '_blank')}/>

+ 15 - 1
nodeEditor/src/globalState.ts

@@ -13,6 +13,7 @@ import { NodeLink } from './diagram/nodeLink';
 import { GraphFrame } from './diagram/graphFrame';
 import { FrameNodePort } from './diagram/frameNodePort';
 import { FramePortData } from './diagram/graphCanvas';
+import { NodeMaterialModes } from 'babylonjs/Materials/Node/Enums/nodeMaterialModes';
 
 export class GlobalState {
     nodeMaterial: NodeMaterial;
@@ -29,7 +30,7 @@ export class GlobalState {
     onLogRequiredObservable = new Observable<LogEntry>();
     onErrorMessageDialogRequiredObservable = new Observable<string>();
     onIsLoadingChanged = new Observable<boolean>();
-    onPreviewCommandActivated = new Observable<void>();
+    onPreviewCommandActivated = new Observable<boolean>();
     onLightUpdated = new Observable<void>();
     onPreviewBackgroundChanged = new Observable<void>();
     onBackFaceCullingChanged = new Observable<void>();
@@ -56,6 +57,18 @@ export class GlobalState {
     directionalLight1: boolean;
     controlCamera: boolean;
     storeEditorData:(serializationObject: any) => void;
+    _mode: NodeMaterialModes;
+
+    /** Gets the mode */
+    public get mode(): NodeMaterialModes {
+        return this._mode;
+    }
+
+    /** Sets the mode */
+    public set mode(m: NodeMaterialModes) {
+        this._mode = m;
+        this.onPreviewCommandActivated.notifyObservers(true);
+    }
 
     customSave?: {label: string, action: (data: string) => Promise<void>};
 
@@ -67,6 +80,7 @@ export class GlobalState {
         this.directionalLight0 = DataStorage.ReadBoolean("DirectionalLight0", false);
         this.directionalLight1 = DataStorage.ReadBoolean("DirectionalLight1", false);
         this.controlCamera = DataStorage.ReadBoolean("ControlCamera", true);
+        this._mode = NodeMaterialModes.Material;
 
         let r = DataStorage.ReadNumber("BackgroundColorR", 0.12549019607843137);
         let g = DataStorage.ReadNumber("BackgroundColorG", 0.09803921568627451);

+ 4 - 3
nodeEditor/src/serializationTools.ts

@@ -38,10 +38,11 @@ export class SerializationTools {
         return JSON.stringify(serializationObject, undefined, 2);
     }
 
-    public static Deserialize(serializationObject:any, globalState: GlobalState) {       
-        globalState.onIsLoadingChanged.notifyObservers(true); 
+    public static Deserialize(serializationObject: any, globalState: GlobalState) {
+        globalState.onIsLoadingChanged.notifyObservers(true);
         globalState.nodeMaterial!.loadFromSerialization(serializationObject, "");
-        
+        globalState.mode = globalState.nodeMaterial!.mode;
+
         globalState.onResetRequiredObservable.notifyObservers();
     }
 }

+ 2 - 1
src/Materials/Node/Enums/index.ts

@@ -1,4 +1,5 @@
 export * from "./nodeMaterialBlockTargets";
 export * from "./nodeMaterialBlockConnectionPointTypes";
 export * from "./nodeMaterialBlockConnectionPointMode";
-export * from "./nodeMaterialSystemValues";
+export * from "./nodeMaterialSystemValues";
+export * from "./nodeMaterialModes";

+ 9 - 0
src/Materials/Node/Enums/nodeMaterialModes.ts

@@ -0,0 +1,9 @@
+/**
+ * Enum used to define the material modes
+ */
+export enum NodeMaterialModes {
+    /** Regular material */
+    Material = 0,
+    /** For post process */
+    PostProcess = 1,
+}

+ 22 - 0
src/Materials/Node/nodeMaterial.ts

@@ -31,6 +31,7 @@ import { RefractionBlock } from './Blocks/PBR/refractionBlock';
 import { EffectFallbacks } from '../effectFallbacks';
 import { WebRequest } from '../../Misc/webRequest';
 import { Effect } from '../effect';
+import { NodeMaterialModes } from './Enums/nodeMaterialModes';
 
 const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable<SubMesh> };
 
@@ -228,6 +229,20 @@ export class NodeMaterial extends PushMaterial {
     public attachedBlocks = new Array<NodeMaterialBlock>();
 
     /**
+     * Specifies the mode of the node material
+     * @hidden
+     */
+    @serialize("mode")
+    public _mode: NodeMaterialModes = NodeMaterialModes.Material;
+
+    /**
+     * Gets the mode property
+     */
+    public get mode(): NodeMaterialModes {
+        return this._mode;
+    }
+
+    /**
      * Create a new node based material
      * @param name defines the material name
      * @param scene defines the hosting scene
@@ -1083,6 +1098,11 @@ export class NodeMaterial extends PushMaterial {
         // Add to nodes
         this.addOutputNode(vertexOutput);
         this.addOutputNode(fragmentOutput);
+
+        this._mode = NodeMaterialModes.Material;
+    }
+
+    /**
     }
 
     /**
@@ -1316,6 +1336,8 @@ export class NodeMaterial extends PushMaterial {
 
             this.editorData.map = blockMap;
         }
+
+        this._mode = source.mode ?? NodeMaterialModes.Material;
     }
 
     /**