浏览代码

More features for particle editor

David Catuhe 5 年之前
父节点
当前提交
9dd5184ea3

+ 10 - 0
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -89,6 +89,8 @@ import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
 import { NodeMaterialPropertyGridComponent } from './propertyGrids/materials/nodeMaterialPropertyGridComponent';
 import { MultiMaterial } from 'babylonjs/Materials/multiMaterial';
 import { MultiMaterialPropertyGridComponent } from './propertyGrids/materials/multiMaterialPropertyGridComponent';
+import { IParticleSystem } from 'babylonjs';
+import { ParticleSystemPropertyGridComponent } from './propertyGrids/particleSystems/particleSystemPropertyGridComponent';
 
 export class PropertyGridTabComponent extends PaneComponent {
     private _timerIntervalId: number;
@@ -149,6 +151,14 @@ export class PropertyGridTabComponent extends PaneComponent {
                 }
             }
 
+            if (className.indexOf("ParticleSystem") !== -1) {
+                const particleSystem = entity as IParticleSystem;
+                return (<ParticleSystemPropertyGridComponent globalState={this.props.globalState} system={particleSystem}
+                    lockObject={this._lockObject}
+                    onSelectionChangedObservable={this.props.onSelectionChangedObservable}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
             if (className.indexOf("FreeCamera") !== -1 || className.indexOf("UniversalCamera") !== -1
             || className.indexOf("WebXRCamera") !== -1  || className.indexOf("DeviceOrientationCamera") !== -1) {
                 const freeCamera = entity as FreeCamera;

+ 2 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -44,6 +44,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
 
         const texture = this.props.texture;
 
+        this.textureLineRef = React.createRef();
+
         if (!texture || !(texture as any).rootContainer) {
             return;
         }
@@ -53,8 +55,6 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
         this._adtInstrumentation!.captureRenderTime = true;
         this._adtInstrumentation!.captureLayoutTime = true;
-
-        this.textureLineRef = React.createRef();
     }
 
     componentWillUnmount() {

+ 137 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx

@@ -9,11 +9,27 @@ import { LockObject } from "../lockObject";
 import { GlobalState } from '../../../../globalState';
 import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
 import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
+import { FloatLineComponent } from '../../../lines/floatLineComponent';
+import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
+import { TextureLinkLineComponent } from '../../../lines/textureLinkLineComponent';
+import { OptionsLineComponent } from '../../../lines/optionsLineComponent';
+import { ParticleSystem } from 'babylonjs/Particles/particleSystem';
+import { Color4LineComponent } from '../../../lines/color4LineComponent';
+import { Vector3LineComponent } from '../../../lines/vector3LineComponent';
+import { CheckBoxLineComponent } from '../../../lines/checkBoxLineComponent';
+import { SliderLineComponent } from '../../../lines/sliderLineComponent';
+import { BoxParticleEmitter } from 'babylonjs/Particles/EmitterTypes/boxParticleEmitter';
+import { ConeParticleEmitter } from 'babylonjs/Particles/EmitterTypes/coneParticleEmitter';
+import { CylinderParticleEmitter } from 'babylonjs/Particles/EmitterTypes/cylinderParticleEmitter';
+import { HemisphericParticleEmitter } from 'babylonjs/Particles/EmitterTypes/hemisphericParticleEmitter';
+import { PointParticleEmitter } from 'babylonjs/Particles/EmitterTypes/pointParticleEmitter';
+import { SphereParticleEmitter } from 'babylonjs/Particles/EmitterTypes/sphereParticleEmitter';
 
 interface IParticleSystemPropertyGridComponentProps {
     globalState: GlobalState;
     system: IParticleSystem,
     lockObject: LockObject,
+    onSelectionChangedObservable?: Observable<any>,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -25,6 +41,23 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
     render() {
         const system = this.props.system;
 
+        var blendModeOptions = [
+            { label: "Add", value: ParticleSystem.BLENDMODE_ADD },
+            { label: "Multiply", value: ParticleSystem.BLENDMODE_MULTIPLY },
+            { label: "Multiply Add", value: ParticleSystem.BLENDMODE_MULTIPLYADD },
+            { label: "OneOne", value: ParticleSystem.BLENDMODE_ONEONE },
+            { label: "Standard", value: ParticleSystem.BLENDMODE_STANDARD },
+        ];   
+        
+        var particleEmitterTypeOptions = [
+            { label: "Box", value: 0 },
+            { label: "Cone", value: 1 },
+            { label: "Cylinder", value: 2 },
+            { label: "Hemispheric", value: 3 },
+            { label: "Point", value: 4 },
+            { label: "Sphere", value: 5 },
+        ];
+
         return (
             <div className="pane">
                 <CustomPropertyGridComponent globalState={this.props.globalState} target={system}
@@ -32,8 +65,110 @@ export class ParticleSystemPropertyGridComponent extends React.Component<IPartic
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
                     <TextLineComponent label="ID" value={system.id} />
-                    <TextLineComponent label="Class" value={system.getClassName()} />                                      
-                </LineContainerComponent>                
+                    <TextLineComponent label="Class" value={system.getClassName()} />  
+                    <TextureLinkLineComponent label="Texture" texture={system.particleTexture} onSelectionChangedObservable={this.props.onSelectionChangedObservable}/>
+                    <OptionsLineComponent label="Blend mode" options={blendModeOptions} target={system} propertyName="blendMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Vector3LineComponent label="Gravity" target={system} propertyName="gravity"
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Is billboard" target={system} propertyName="isBillboardBased" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Is local" target={system} propertyName="isLocal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Update speed" target={system} propertyName="updateSpeed" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>                      
+                <LineContainerComponent globalState={this.props.globalState} title="OPTIONS">
+                    <ButtonLineComponent label={system.isStarted() ? "Stop" : "Start"} onClick={() => {
+                        if (system.isStarted()) {
+                            system.stop();
+                        } else {
+                            system.start();
+                        }
+                    }} />
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="Emitter">
+                    <OptionsLineComponent 
+                        label="Type" 
+                        options={particleEmitterTypeOptions} 
+                        target={system}
+                        propertyName="particleEmitterType"
+                        noDirectUpdate={true}
+                        onSelect={(value: number) => {
+                            switch(value) {
+                                case 0:
+                                    system.particleEmitterType = new BoxParticleEmitter();
+                                    break;
+                                    
+                                case 1:
+                                    system.particleEmitterType = new ConeParticleEmitter();
+                                    break;
+                                    
+                                case 2:
+                                    system.particleEmitterType = new CylinderParticleEmitter();
+                                    break;
+                                    
+                                case 3:
+                                    system.particleEmitterType = new HemisphericParticleEmitter();
+                                    break;
+                                    
+                                case 4:
+                                    system.particleEmitterType = new PointParticleEmitter();
+                                    break;   
+
+                                case 5:
+                                    system.particleEmitterType = new SphereParticleEmitter();
+                                    break;
+                            }
+                        }}
+                        extractValue={() => {
+                            switch(system.particleEmitterType?.getClassName()) {
+                                case "BoxParticleEmitter":
+                                    return 0;
+                                case "ConeParticleEmitter":
+                                    return 1;
+                                case "CylinderParticleEmitter":
+                                    return 2;        
+                                case "HemisphericParticleEmitter":
+                                    return 3;
+                                case "PointParticleEmitter":
+                                    return 4;
+                                case "SphereParticleEmitter":
+                                    return 5;                                                                                                          
+                            }
+
+                            return 0;
+                        }}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="SIZE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min size" target={system} propertyName="minSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max size" target={system} propertyName="maxSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min scale X" target={system} propertyName="minScaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max scale X" target={system} propertyName="maxScaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min scale Y" target={system} propertyName="minScaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max scale Y" target={system} propertyName="maxScaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>          
+                <LineContainerComponent globalState={this.props.globalState} title="LIFETIME">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min lifetime" target={system} propertyName="minLifeTime" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max lifetime" target={system} propertyName="maxLifeTime" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>           
+                <LineContainerComponent globalState={this.props.globalState} title="EMISSION">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Rate" target={system} propertyName="emitRate" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min emit power" target={system} propertyName="minEmitPower" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max emit power" target={system} propertyName="maxEmitPower" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>  
+                <LineContainerComponent globalState={this.props.globalState} title="COLORS">
+                    <Color4LineComponent label="Color 1" target={system} propertyName="color1" 
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color4LineComponent label="Color 2" target={system} propertyName="color2" 
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color4LineComponent label="Color dead" target={system} propertyName="colorDead" 
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>                     
+                <LineContainerComponent globalState={this.props.globalState} title="ROTATION">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min angular speed" target={system} propertyName="minAngularSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max angular speed" target={system} propertyName="maxAngularSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Min initial rotation" target={system} propertyName="minInitialRotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Max initial rotation" target={system} propertyName="maxInitialRotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>  
             </div>
         );
     }

+ 5 - 5
inspector/src/components/embedHost/embedHostComponent.tsx

@@ -30,7 +30,11 @@ export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps
     private bottomPartRef: React.RefObject<HTMLDivElement>;
 
     constructor(props: IEmbedHostComponentProps) {
-        super(props);
+        super(props);        
+
+        this.splitRef = React.createRef();
+        this.topPartRef = React.createRef();
+        this.bottomPartRef = React.createRef();
     }
 
     componentDidMount() {
@@ -45,10 +49,6 @@ export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps
             minSize: [200, 200],
             gutterSize: 4
         });
-
-        this.splitRef = React.createRef();
-        this.topPartRef = React.createRef();
-        this.bottomPartRef = React.createRef();
     }
 
     renderContent() {

+ 30 - 0
inspector/src/components/sceneExplorer/entities/particleSystemTreeItemComponent.tsx

@@ -0,0 +1,30 @@
+import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
+
+import { faBraille } from '@fortawesome/free-solid-svg-icons';
+import { TreeItemLabelComponent } from "../treeItemLabelComponent";
+import { ExtensionsComponent } from "../extensionsComponent";
+import * as React from 'react';
+import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
+
+interface IParticleSystemTreeItemComponentProps {
+    system: IParticleSystem,
+    extensibilityGroups?: IExplorerExtensibilityGroup[],
+    onClick: () => void
+}
+
+export class ParticleSystemTreeItemComponent extends React.Component<IParticleSystemTreeItemComponentProps> {
+    constructor(props: IParticleSystemTreeItemComponentProps) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <div className="particleSystemTools">
+                <TreeItemLabelComponent label={this.props.system.name || "Particle system"} onClick={() => this.props.onClick()} icon={faBraille} color="crimson" />
+                {
+                    <ExtensionsComponent target={this.props.system} extensibilityGroups={this.props.extensibilityGroups} />
+                }
+            </div>
+        )
+    }
+}

+ 12 - 0
inspector/src/components/sceneExplorer/sceneExplorer.scss

@@ -429,6 +429,18 @@
             }
         }
 
+        .particleSystemTools {
+            grid-column: 2;
+            display: grid;
+            grid-template-columns: 1fr auto 5px;
+            align-items: center;
+
+            .extensions {
+                width: 20px;
+                grid-column: 2;
+            }
+        }
+
         .postProcessTools {
             grid-column: 2;
             display: grid;

+ 1 - 0
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -324,6 +324,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             label: "Add new particle system",
             action: () => {
                 let newSystem = ParticleHelper.CreateDefault(Vector3.Zero(), 1000, scene);
+                newSystem.start();
                 this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
             }
         });

+ 6 - 0
inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx

@@ -30,6 +30,8 @@ import { SkeletonTreeItemComponent } from './entities/skeletonTreeItemComponent'
 import { Skeleton } from 'babylonjs/Bones/skeleton';
 import { BoneTreeItemComponent } from './entities/boneTreeItemComponent';
 import { Bone } from 'babylonjs/Bones/bone';
+import { ParticleSystemTreeItemComponent } from './entities/particleSystemTreeItemComponent';
+import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
 
 
 interface ITreeItemSpecializedComponentProps {
@@ -92,6 +94,10 @@ export class TreeItemSpecializedComponent extends React.Component<ITreeItemSpeci
                 return (<MaterialTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} material={entity as Material} onClick={() => this.onClick()} />);
             }
 
+            if (className.indexOf("ParticleSystem") !== -1) {
+                return (<ParticleSystemTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} system={entity as IParticleSystem} onClick={() => this.onClick()} />);
+            }
+
             if (className === "AdvancedDynamicTexture") {
                 return (<AdvancedDynamicTextureTreeItemComponent onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} extensibilityGroups={this.props.extensibilityGroups} texture={entity as AdvancedDynamicTexture} onClick={() => this.onClick()} />);
             }

+ 1 - 1
src/Particles/particleSystem.ts

@@ -344,7 +344,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
                     });
                 }
 
-                if (this.isLocal) {
+                if (this.isLocal && particle._localPosition) {
                     particle._localPosition!.addInPlace(this._scaledDirection);
                     Vector3.TransformCoordinatesToRef(particle._localPosition!, this._emitterWorldMatrix, particle.position);
                 } else {