Ver código fonte

no more build errors

Trevor Baron 6 anos atrás
pai
commit
23ee06a07d

Diferenças do arquivo suprimidas por serem muito extensas
+ 22 - 22
dist/preview release/babylon.max.js


+ 4 - 4
nodeEditor/src/components/customDiragramNodes/generic/genericNodeWidget.tsx

@@ -2,10 +2,10 @@ import * as React from "react";
 import { PortWidget } from "storm-react-diagrams";
 import { GenericNodeModel } from './genericNodeModel';
 import { GenericPortModel } from './genericPortModel';
-import {TextureLineComponent} from "../../../../../inspector/src/components/actionTabs/lines/textureLineComponent"
-import {FileButtonLineComponent} from "../../../../../inspector/src/components/actionTabs/lines/fileButtonLineComponent"
-import { Vector2LineComponent } from '../../../../../inspector/src/components/actionTabs/lines/vector2LineComponent';
-import { Vector3LineComponent } from '../../../../../inspector/src/components/actionTabs/lines/vector3LineComponent';
+import {TextureLineComponent} from "../../../sharedComponents/textureLineComponent"
+import {FileButtonLineComponent} from "../../../sharedComponents/fileButtonLineComponent"
+import { Vector2LineComponent } from '../../../sharedComponents/vector2LineComponent';
+import { Vector3LineComponent } from '../../../sharedComponents/vector3LineComponent';
 import { Nullable } from 'babylonjs/types';
 import { Texture } from 'babylonjs/Materials/Textures/texture';
 import { Engine } from 'babylonjs/Engines/engine';

+ 3 - 3
nodeEditor/src/components/graphEditor.tsx

@@ -13,8 +13,8 @@ import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/
 import { GenericNodeModel } from './customDiragramNodes/generic/genericNodeModel';
 import { GenericPortModel } from './customDiragramNodes/generic/genericPortModel';
 import { Engine } from 'babylonjs/Engines/engine';
-import { LineContainerComponent } from "../../../inspector/src/components/actionTabs/lineContainerComponent"
-import { ButtonLineComponent } from '../../../inspector/src/components/actionTabs/lines/buttonLineComponent';
+import { LineContainerComponent } from "../sharedComponents/lineContainerComponent"
+import { ButtonLineComponent } from '../sharedComponents/buttonLineComponent';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
 import { Texture } from 'babylonjs/Materials/Textures/texture';
@@ -37,7 +37,7 @@ import { MultiplyBlock } from 'babylonjs/Materials/Node/Blocks/multiplyBlock';
 import { Vector2TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector2TransformBlock';
 import { Vector3TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector3TransformBlock';
 import { Vector4TransformBlock } from 'babylonjs/Materials/Node/Blocks/vector4TransformBlock';
-require("../../../inspector/src/components/actionTabs/actionTabs.scss");
+//require("../../../inspector/src/components/actionTabs/actionTabs.scss");
 require("storm-react-diagrams/dist/style.min.css");
 /*
 Data vs View

+ 4 - 21
nodeEditor/src/nodeEditor.ts

@@ -3,7 +3,7 @@ import * as ReactDOM from "react-dom";
 import { GlobalState } from './globalState';
 import { GraphEditor } from './components/graphEditor';
 import {NodeMaterial} from "babylonjs/Materials/Node/nodeMaterial"
-import {Inspector} from "../../inspector/src"
+import {Popup} from "../src/sharedComponents/popup"
 /**
  * Interface used to specify creation options for the node editor
  */
@@ -25,24 +25,7 @@ export class NodeEditor {
      */
     public static Show(options: INodeEditorOptions) {
         if(!options.hostElement){
-            
-            // var divElement = document.createElement("div");
-            // document.body.prepend(divElement)
-            // divElement.id = "node-editor";
-            // divElement.style.background = "#474646"
-            // divElement.style.width = "100%"
-            // divElement.style.height = "300px"
-            // divElement.style.display = "flex"
-            // options.hostElement = divElement
-            // debugger;
-
-            // var canvas = EngineStore.LastCreatedEngine!.getRenderingCanvas();
-            // let parentControl = (canvas!.parentElement) as HTMLDivElement;
-            // Inspector._CreateCanvasContainer(parentControl)
-            // options.hostElement = parentControl!;//Inspector._CreatePopup("SCENE EXPLORER", "node-editor")!;
-
-            options.hostElement = Inspector._CreatePopup("SCENE EXPLORER", "node-editor", 1000, 800)!;
-            
+            options.hostElement = Popup.CreatePopup("SCENE EXPLORER", "node-editor", 1000, 800)!;
         }
         let globalState = new GlobalState();
         globalState.nodeMaterial = options.nodeMaterial
@@ -55,7 +38,7 @@ export class NodeEditor {
         ReactDOM.render(graphEditor, options.hostElement);
 
         // Close the popup window when the page is refreshed or scene is disposed
-        var popupWindow = (Inspector as any)["node-editor"];
+        var popupWindow = (Popup as any)["node-editor"];
         if(globalState.nodeMaterial && popupWindow){
             globalState.nodeMaterial.getScene().onDisposeObservable.addOnce(()=>{
                 if(popupWindow){
@@ -63,7 +46,7 @@ export class NodeEditor {
                 }
             })
             window.onbeforeunload = function(event) {
-                var popupWindow = (Inspector as any)["node-editor"];
+                var popupWindow = (Popup as any)["node-editor"];
                 if(popupWindow){
                     popupWindow.close();
                 }

+ 21 - 0
nodeEditor/src/sharedComponents/buttonLineComponent.tsx

@@ -0,0 +1,21 @@
+import * as React from "react";
+
+export interface IButtonLineComponentProps {
+    label: string;
+    onClick: () => void;
+}
+
+export class ButtonLineComponent extends React.Component<IButtonLineComponentProps> {
+    constructor(props: IButtonLineComponentProps) {
+        super(props);
+    }
+
+    render() {
+
+        return (
+            <div className="buttonLine">
+                <button onClick={() => this.props.onClick()}>{this.props.label}</button>
+            </div>
+        );
+    }
+}

+ 33 - 0
nodeEditor/src/sharedComponents/fileButtonLineComponent.tsx

@@ -0,0 +1,33 @@
+import * as React from "react";
+
+interface IFileButtonLineComponentProps {
+    label: string;
+    onClick: (file: File) => void;
+    accept: string;
+}
+
+export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+    constructor(props: IFileButtonLineComponentProps) {
+        super(props);
+    }
+
+    onChange(evt: any) {
+        var files: File[] = evt.target.files;
+        if (files && files.length) {
+            this.props.onClick(files[0]);
+        }
+
+        evt.target.value = "";
+    }
+
+    render() {
+        return (
+            <div className="buttonLine">
+                <label htmlFor="file-upload" className="file-upload">
+                    {this.props.label}
+                </label>
+                <input ref="upload" id="file-upload" type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
+            </div>
+        );
+    }
+}

+ 120 - 0
nodeEditor/src/sharedComponents/lineContainerComponent.tsx

@@ -0,0 +1,120 @@
+import * as React from "react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
+
+
+interface ILineContainerComponentProps {
+    globalState?: any; //
+    title: string;
+    children: any[] | any;
+    closed?: boolean;
+}
+
+export class LineContainerComponent extends React.Component<ILineContainerComponentProps, { isExpanded: boolean, isHighlighted: boolean }> {
+    private static _InMemoryStorage: { [key: string]: boolean };
+
+    constructor(props: ILineContainerComponentProps) {
+        super(props);
+
+        let initialState: boolean;
+
+        try {
+            if (LineContainerComponent._InMemoryStorage && LineContainerComponent._InMemoryStorage[this.props.title] !== undefined) {
+                initialState = LineContainerComponent._InMemoryStorage[this.props.title];
+            } else if (typeof (Storage) !== "undefined" && localStorage.getItem(this.props.title) !== null) {
+                initialState = localStorage.getItem(this.props.title) === "true";
+            } else {
+                initialState = !this.props.closed;
+            }
+        }
+        catch (e) {
+            LineContainerComponent._InMemoryStorage = {};
+            LineContainerComponent._InMemoryStorage[this.props.title] = !this.props.closed
+            initialState = !this.props.closed;
+        }
+
+        this.state = { isExpanded: initialState, isHighlighted: false };
+    }
+
+    switchExpandedState(): void {
+        const newState = !this.state.isExpanded;
+
+        try {
+            if (LineContainerComponent._InMemoryStorage) {
+                LineContainerComponent._InMemoryStorage[this.props.title] = newState;
+            } else if (typeof (Storage) !== "undefined") {
+                localStorage.setItem(this.props.title, newState ? "true" : "false");
+            }
+        }
+        catch (e) {
+            LineContainerComponent._InMemoryStorage = {};
+            LineContainerComponent._InMemoryStorage[this.props.title] = newState;
+        }
+
+        this.setState({ isExpanded: newState });
+    }
+
+    componentDidMount() {
+        if (this.props.globalState && !this.props.globalState.selectedLineContainerTitle) {
+            return;
+        }
+
+        if (this.props.globalState && this.props.globalState.selectedLineContainerTitle === this.props.title) {
+            setTimeout(() => {
+                this.props.globalState!.selectedLineContainerTitle = "";
+            });
+
+            this.setState({ isExpanded: true, isHighlighted: true });
+
+            window.setTimeout(() => {
+                this.setState({ isHighlighted: false });
+            }, 5000);
+        } else {
+            this.setState({isExpanded: false});
+        }        
+    }
+
+    renderHeader() {
+        const className = this.state.isExpanded ? "collapse" : "collapse closed";
+
+        return (
+            <div className="header" onClick={() => this.switchExpandedState()}>
+                <div className="title">
+                    {this.props.title}
+                </div>
+                <div className={className}>
+                    <FontAwesomeIcon icon={faChevronDown} />
+                </div>
+            </div>
+        );
+    }
+
+    render() {
+        if (!this.state.isExpanded) {
+            return (
+                <div className="paneContainer">
+                    <div className="paneContainer-content">
+                        {
+                            this.renderHeader()
+                        }
+                    </div>
+                </div>
+            );
+        }
+
+        return (
+            <div className="paneContainer">
+                <div className="paneContainer-content">
+                    {
+                        this.renderHeader()
+                    }
+                    <div className="paneList">
+                        {this.props.children}
+                    </div >
+                </div>
+                <div className={"paneContainer-highlight-border" + (!this.state.isHighlighted ? " transparent" : "")}>
+                </div>
+            </div>
+        );
+    }
+}

+ 69 - 0
nodeEditor/src/sharedComponents/numericInputComponent.tsx

@@ -0,0 +1,69 @@
+import * as React from "react";
+
+interface INumericInputComponentProps {
+    label: string;
+    value: number;
+    step?: number;
+    onChange: (value: number) => void;
+}
+
+export class NumericInputComponent extends React.Component<INumericInputComponentProps, { value: string }> {
+
+    static defaultProps = {
+        step: 1,
+    };
+
+    private _localChange = false;
+    constructor(props: INumericInputComponentProps) {
+        super(props);
+
+        this.state = { value: this.props.value.toFixed(3) }
+    }
+
+    shouldComponentUpdate(nextProps: INumericInputComponentProps, nextState: { value: string }) {
+        if (this._localChange) {
+            this._localChange = false;
+            return true;
+        }
+
+        if (nextProps.value.toString() !== nextState.value) {
+            nextState.value = nextProps.value.toFixed(3);
+            return true;
+        }
+        return false;
+    }
+
+    updateValue(evt: any) {
+        let value = evt.target.value;
+
+        if (/[^0-9\.\-]/g.test(value)) {
+            return;
+        }
+
+        let valueAsNumber = parseFloat(value);
+
+        this._localChange = true;
+        this.setState({ value: value });
+
+        if (isNaN(valueAsNumber)) {
+            return;
+        }
+
+        this.props.onChange(valueAsNumber);
+    }
+
+
+    render() {
+        return (
+            <div className="numeric">
+                {
+                    this.props.label &&
+                    <div className="numeric-label">
+                        {`${this.props.label}: `}
+                    </div>
+                }
+                <input type="number" step={this.props.step} className="numeric-input" value={this.state.value} onChange={evt => this.updateValue(evt)} />
+            </div>
+        )
+    }
+}

+ 70 - 0
nodeEditor/src/sharedComponents/popup.ts

@@ -0,0 +1,70 @@
+export class Popup {
+    public static CreatePopup(title: string, windowVariableName: string, width = 300, height = 800) {
+        const windowCreationOptionsList = {
+            width: width,
+            height: height,
+            top: (window.innerHeight - width) / 2 + window.screenY,
+            left: (window.innerWidth - height) / 2 + window.screenX
+        };
+    
+        var windowCreationOptions = Object.keys(windowCreationOptionsList)
+            .map(
+                (key) => key + '=' + (windowCreationOptionsList as any)[key]
+            )
+            .join(',');
+    
+        const popupWindow = window.open("", title, windowCreationOptions);
+        if (!popupWindow) {
+            return null;
+        }
+    
+        const parentDocument = popupWindow.document;
+    
+        parentDocument.title = title;
+        parentDocument.body.style.width = "100%";
+        parentDocument.body.style.height = "100%";
+        parentDocument.body.style.margin = "0";
+        parentDocument.body.style.padding = "0";
+    
+        let parentControl = parentDocument.createElement("div");
+        parentControl.style.width = "100%";
+        parentControl.style.height = "100%";
+        parentControl.style.margin = "0";
+        parentControl.style.padding = "0";
+    
+        popupWindow.document.body.appendChild(parentControl);
+    
+        this._CopyStyles(window.document, parentDocument);
+    
+        (this as any)[windowVariableName] = popupWindow;
+    
+        return parentControl;
+    }
+
+    private static _CopyStyles(sourceDoc: HTMLDocument, targetDoc: HTMLDocument) {
+        for (var index = 0; index < sourceDoc.styleSheets.length; index++) {
+            var styleSheet: any = sourceDoc.styleSheets[index];
+            try{
+                if (styleSheet.cssRules) { // for <style> elements
+                    const newStyleEl = sourceDoc.createElement('style');
+    
+                    for (var cssRule of styleSheet.cssRules) {
+                        // write the text of each rule into the body of the style element
+                        newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
+                    }
+    
+                    targetDoc.head!.appendChild(newStyleEl);
+                } else if (styleSheet.href) { // for <link> elements loading CSS from a URL
+                    const newLinkEl = sourceDoc.createElement('link');
+    
+                    newLinkEl.rel = 'stylesheet';
+                    newLinkEl.href = styleSheet.href;
+                    targetDoc.head!.appendChild(newLinkEl);
+                }
+            }catch(e){
+                console.log(e)
+            }
+            
+        }
+    }
+}

+ 6 - 0
nodeEditor/src/sharedComponents/propertyChangedEvent.ts

@@ -0,0 +1,6 @@
+export class PropertyChangedEvent {
+    public object: any;
+    public property: string;
+    public value: any;
+    public initialValue: any;
+}

+ 204 - 0
nodeEditor/src/sharedComponents/textureLineComponent.tsx

@@ -0,0 +1,204 @@
+import * as React from "react";
+
+import { Constants } from "babylonjs/Engines/constants";
+import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
+import { Texture } from "babylonjs/Materials/Textures/texture";
+import { RenderTargetTexture } from "babylonjs/Materials/Textures/renderTargetTexture";
+import { PostProcess } from "babylonjs/PostProcesses/postProcess";
+import { PassPostProcess, PassCubePostProcess } from "babylonjs/PostProcesses/passPostProcess";
+
+//import { GlobalState } from "../../../components/globalState";
+
+interface ITextureLineComponentProps {
+    texture: BaseTexture;
+    width: number;
+    height: number;
+    globalState?: any; //
+    hideChannelSelect?:boolean;
+}
+
+export class TextureLineComponent extends React.Component<ITextureLineComponentProps, { displayRed: boolean, displayGreen: boolean, displayBlue: boolean, displayAlpha: boolean, face: number }> {
+    constructor(props: ITextureLineComponentProps) {
+        super(props);
+
+        this.state = {
+            displayRed: true,
+            displayGreen: true,
+            displayBlue: true,
+            displayAlpha: true,
+            face: 0
+        };
+    }
+
+    shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: { displayRed: boolean, displayGreen: boolean, displayBlue: boolean, displayAlpha: boolean, face: number }): boolean {
+        return (nextProps.texture !== this.props.texture || nextState.displayRed !== this.state.displayRed || nextState.displayGreen !== this.state.displayGreen || nextState.displayBlue !== this.state.displayBlue || nextState.displayAlpha !== this.state.displayAlpha || nextState.face !== this.state.face);
+    }
+
+    componentDidMount() {
+        this.updatePreview();
+    }
+
+    componentDidUpdate() {
+        this.updatePreview();
+    }
+
+    updatePreview() {
+        var texture = this.props.texture;
+        if(!texture.isReady() && texture._texture){
+            texture._texture.onLoadedObservable.addOnce(()=>{
+                this.updatePreview();
+            })
+        }
+        var scene = texture.getScene()!;
+        var engine = scene.getEngine();
+        var size = texture.getSize();
+        var ratio = size.width / size.height;
+        var width = this.props.width;
+        var height = (width / ratio) | 1;
+
+        let passPostProcess: PostProcess;
+
+        if (!texture.isCube) {
+            passPostProcess = new PassPostProcess("pass", 1, null, Texture.NEAREST_SAMPLINGMODE, engine, false, Constants.TEXTURETYPE_UNSIGNED_INT);
+        } else {
+            var passCubePostProcess = new PassCubePostProcess("pass", 1, null, Texture.NEAREST_SAMPLINGMODE, engine, false, Constants.TEXTURETYPE_UNSIGNED_INT);
+            passCubePostProcess.face = this.state.face;
+
+            passPostProcess = passCubePostProcess;
+        }
+
+        if (!passPostProcess.getEffect().isReady()) {
+            // Try again later
+            passPostProcess.dispose();
+
+            setTimeout(() => this.updatePreview(), 250);
+
+            return;
+        }
+
+        const previewCanvas = this.refs.canvas as HTMLCanvasElement;
+
+        if(this.props.globalState){
+            this.props.globalState.blockMutationUpdates = true;
+        }
+        
+        let rtt = new RenderTargetTexture(
+            "temp",
+            { width: width, height: height },
+            scene, false);
+
+        passPostProcess.onApply = function(effect) {
+            effect.setTexture("textureSampler", texture);
+        };
+
+        let internalTexture = rtt.getInternalTexture();
+
+        if (internalTexture) {
+            scene.postProcessManager.directRender([passPostProcess], internalTexture);
+
+            // Read the contents of the framebuffer
+            var numberOfChannelsByLine = width * 4;
+            var halfHeight = height / 2;
+
+            //Reading datas from WebGL
+            var data = engine.readPixels(0, 0, width, height);
+
+            if (!texture.isCube) {
+                if (!this.state.displayRed || !this.state.displayGreen || !this.state.displayBlue) {
+                    for (var i = 0; i < width * height * 4; i += 4) {
+
+                        if (!this.state.displayRed) {
+                            data[i] = 0;
+                        }
+
+                        if (!this.state.displayGreen) {
+                            data[i + 1] = 0;
+                        }
+
+                        if (!this.state.displayBlue) {
+                            data[i + 2] = 0;
+                        }
+
+                        if (this.state.displayAlpha) {
+                            var alpha = data[i + 2];
+                            data[i] = alpha;
+                            data[i + 1] = alpha;
+                            data[i + 2] = alpha;
+                            data[i + 2] = 0;
+                        }
+                    }
+                }
+            }
+
+            //To flip image on Y axis.
+            if ((texture as Texture).invertY || texture.isCube) {
+                for (var i = 0; i < halfHeight; i++) {
+                    for (var j = 0; j < numberOfChannelsByLine; j++) {
+                        var currentCell = j + i * numberOfChannelsByLine;
+                        var targetLine = height - i - 1;
+                        var targetCell = j + targetLine * numberOfChannelsByLine;
+
+                        var temp = data[currentCell];
+                        data[currentCell] = data[targetCell];
+                        data[targetCell] = temp;
+                    }
+                }
+            }
+
+            previewCanvas.width = width;
+            previewCanvas.height = height;
+            var context = previewCanvas.getContext('2d');
+
+            if (context) {
+                // Copy the pixels to the preview canvas
+                var imageData = context.createImageData(width, height);
+                var castData = imageData.data;
+                castData.set(data);
+                context.putImageData(imageData, 0, 0);
+            }
+
+            // Unbind
+            engine.unBindFramebuffer(internalTexture);
+        }
+
+        rtt.dispose();
+        passPostProcess.dispose();
+
+        previewCanvas.style.height = height + "px";
+        if(this.props.globalState){
+            this.props.globalState.blockMutationUpdates = false;
+        }
+        
+    }
+
+    render() {
+        var texture = this.props.texture;
+
+        return (
+            <div className="textureLine">
+                {
+                    !this.props.hideChannelSelect && texture.isCube &&
+                    <div className="control3D">
+                        <button className={this.state.face === 0 ? "px command selected" : "px command"} onClick={() => this.setState({ face: 0 })}>PX</button>
+                        <button className={this.state.face === 1 ? "nx command selected" : "nx command"} onClick={() => this.setState({ face: 1 })}>NX</button>
+                        <button className={this.state.face === 2 ? "py command selected" : "py command"} onClick={() => this.setState({ face: 2 })}>PY</button>
+                        <button className={this.state.face === 3 ? "ny command selected" : "ny command"} onClick={() => this.setState({ face: 3 })}>NY</button>
+                        <button className={this.state.face === 4 ? "pz command selected" : "pz command"} onClick={() => this.setState({ face: 4 })}>PZ</button>
+                        <button className={this.state.face === 5 ? "nz command selected" : "nz command"} onClick={() => this.setState({ face: 5 })}>NZ</button>
+                    </div>
+                }
+                {
+                    !this.props.hideChannelSelect && !texture.isCube &&
+                    <div className="control">
+                        <button className={this.state.displayRed && !this.state.displayGreen ? "red command selected" : "red command"} onClick={() => this.setState({ displayRed: true, displayGreen: false, displayBlue: false, displayAlpha: false })}>R</button>
+                        <button className={this.state.displayGreen && !this.state.displayBlue ? "green command selected" : "green command"} onClick={() => this.setState({ displayRed: false, displayGreen: true, displayBlue: false, displayAlpha: false })}>G</button>
+                        <button className={this.state.displayBlue && !this.state.displayAlpha ? "blue command selected" : "blue command"} onClick={() => this.setState({ displayRed: false, displayGreen: false, displayBlue: true, displayAlpha: false })}>B</button>
+                        <button className={this.state.displayAlpha && !this.state.displayRed ? "alpha command selected" : "alpha command"} onClick={() => this.setState({ displayRed: false, displayGreen: false, displayBlue: false, displayAlpha: true })}>A</button>
+                        <button className={this.state.displayRed && this.state.displayGreen ? "all command selected" : "all command"} onClick={() => this.setState({ displayRed: true, displayGreen: true, displayBlue: true, displayAlpha: true })}>ALL</button>
+                    </div>
+                }
+                <canvas ref="canvas" className="preview" />
+            </div>
+        );
+    }
+}

+ 114 - 0
nodeEditor/src/sharedComponents/vector2LineComponent.tsx

@@ -0,0 +1,114 @@
+import * as React from "react";
+import { Vector2 } from "babylonjs/Maths/math";
+import { Observable } from "babylonjs/Misc/observable";
+
+import { NumericInputComponent } from "./numericInputComponent";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
+import { PropertyChangedEvent } from "./propertyChangedEvent";
+
+interface IVector2LineComponentProps {
+    label: string;
+    target: any;
+    propertyName: string;
+    step?: number;
+    onChange?: (newvalue: Vector2) => void;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+}
+
+export class Vector2LineComponent extends React.Component<IVector2LineComponentProps, { isExpanded: boolean, value: Vector2 }> {
+
+    static defaultProps = {
+        step: 0.001, // cm
+    };
+
+    private _localChange = false;
+
+    constructor(props: IVector2LineComponentProps) {
+        super(props);
+
+        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName].clone() }
+    }
+
+    shouldComponentUpdate(nextProps: IVector2LineComponentProps, nextState: { isExpanded: boolean, value: Vector2 }) {
+        const nextPropsValue = nextProps.target[nextProps.propertyName];
+
+        if (!nextPropsValue.equals(nextState.value) || this._localChange) {
+            nextState.value = nextPropsValue.clone();
+            this._localChange = false;
+            return true;
+        }
+        return false;
+    }
+
+    switchExpandState() {
+        this._localChange = true;
+        this.setState({ isExpanded: !this.state.isExpanded });
+    }
+
+    raiseOnPropertyChanged(previousValue: Vector2) {
+        if (this.props.onChange) {
+            this.props.onChange(this.state.value);
+        }
+
+        if (!this.props.onPropertyChangedObservable) {
+            return;
+        }
+        this.props.onPropertyChangedObservable.notifyObservers({
+            object: this.props.target,
+            property: this.props.propertyName,
+            value: this.state.value,
+            initialValue: previousValue
+        });
+    }
+
+    updateStateX(value: number) {
+        this._localChange = true;
+
+        const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].x = value;
+        this.state.value.x = value;
+        this.setState({ value: this.state.value });
+
+        this.raiseOnPropertyChanged(store);
+    }
+
+    updateStateY(value: number) {
+        this._localChange = true;
+
+        const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].y = value;
+        this.state.value.y = value;
+        this.setState({ value: this.state.value });
+
+        this.raiseOnPropertyChanged(store);
+    }
+
+    render() {
+        const chevron = this.state.isExpanded ? <FontAwesomeIcon icon={faMinus} /> : <FontAwesomeIcon icon={faPlus} />
+
+        return (
+            <div className="vector3Line">
+                <div className="firstLine">
+                    <div className="label">
+                        {this.props.label}
+                    </div>
+                    <div className="vector">
+                        {`X: ${this.state.value.x.toFixed(2)}, Y: ${this.state.value.y.toFixed(2)}`}
+
+                    </div>
+                    <div className="expand hoverIcon" onClick={() => this.switchExpandState()} title="Expand">
+                        {chevron}
+                    </div>
+                </div>
+                {
+                    this.state.isExpanded &&
+                    <div className="secondLine">
+                        <NumericInputComponent label="x" step={this.props.step} value={this.state.value.x} onChange={value => this.updateStateX(value)} />
+                        <NumericInputComponent label="y" step={this.props.step} value={this.state.value.y} onChange={value => this.updateStateY(value)} />
+                    </div>
+                }
+            </div>
+        );
+    }
+}

+ 124 - 0
nodeEditor/src/sharedComponents/vector3LineComponent.tsx

@@ -0,0 +1,124 @@
+import * as React from "react";
+import { Vector3 } from "babylonjs/Maths/math";
+import { Observable } from "babylonjs/Misc/observable";
+
+import { NumericInputComponent } from "./numericInputComponent";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
+import { PropertyChangedEvent } from "./propertyChangedEvent";
+
+interface IVector3LineComponentProps {
+    label: string;
+    target: any;
+    propertyName: string;
+    step?: number;
+    onChange?: (newvalue: Vector3) => void;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+}
+
+export class Vector3LineComponent extends React.Component<IVector3LineComponentProps, { isExpanded: boolean, value: Vector3 }> {
+
+    static defaultProps = {
+        step: 0.001, // cm
+    };
+
+    private _localChange = false;
+
+    constructor(props: IVector3LineComponentProps) {
+        super(props);
+
+        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName].clone() }
+    }
+
+    shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: { isExpanded: boolean, value: Vector3 }) {
+        const nextPropsValue = nextProps.target[nextProps.propertyName];
+
+        if (!nextPropsValue.equals(nextState.value) || this._localChange) {
+            nextState.value = nextPropsValue.clone();
+            this._localChange = false;
+            return true;
+        }
+        return false;
+    }
+
+    switchExpandState() {
+        this._localChange = true;
+        this.setState({ isExpanded: !this.state.isExpanded });
+    }
+
+    raiseOnPropertyChanged(previousValue: Vector3) {
+        if (this.props.onChange) {
+            this.props.onChange(this.state.value);
+        }
+
+        if (!this.props.onPropertyChangedObservable) {
+            return;
+        }
+        this.props.onPropertyChangedObservable.notifyObservers({
+            object: this.props.target,
+            property: this.props.propertyName,
+            value: this.state.value,
+            initialValue: previousValue
+        });
+    }
+
+    updateVector3() {
+        const store = this.props.target[this.props.propertyName].clone();
+        this.props.target[this.props.propertyName] = this.state.value;
+
+        this.setState({ value: store });
+
+        this.raiseOnPropertyChanged(store);
+    }
+
+
+    updateStateX(value: number) {
+        this._localChange = true;
+
+        this.state.value.x = value;
+        this.updateVector3();
+    }
+
+    updateStateY(value: number) {
+        this._localChange = true;
+
+        this.state.value.y = value;
+        this.updateVector3();
+    }
+
+    updateStateZ(value: number) {
+        this._localChange = true;
+
+        this.state.value.z = value;
+        this.updateVector3();
+    }
+
+    render() {
+        const chevron = this.state.isExpanded ? <FontAwesomeIcon icon={faMinus} /> : <FontAwesomeIcon icon={faPlus} />
+
+        return (
+            <div className="vector3Line">
+                <div className="firstLine">
+                    <div className="label">
+                        {this.props.label}
+                    </div>
+                    <div className="vector">
+                        {`X: ${this.state.value.x.toFixed(2)}, Y: ${this.state.value.y.toFixed(2)}, Z: ${this.state.value.z.toFixed(2)}`}
+
+                    </div>
+                    <div className="expand hoverIcon" onClick={() => this.switchExpandState()} title="Expand">
+                        {chevron}
+                    </div>
+                </div>
+                {
+                    this.state.isExpanded &&
+                    <div className="secondLine">
+                        <NumericInputComponent label="x" step={this.props.step} value={this.state.value.x} onChange={value => this.updateStateX(value)} />
+                        <NumericInputComponent label="y" step={this.props.step} value={this.state.value.y} onChange={value => this.updateStateY(value)} />
+                        <NumericInputComponent label="z" step={this.props.step} value={this.state.value.z} onChange={value => this.updateStateZ(value)} />
+                    </div>
+                }
+            </div>
+        );
+    }
+}

+ 17 - 2
nodeEditor/tsconfig.json

@@ -5,9 +5,24 @@
         "baseUrl": "./src/",
         "rootDir": "./src/",
         "paths": {
-            "babylonjs": [
+            "babylonjs-gui/*": [
+                "../../dist/preview release/gui/babylon.gui.module.d.ts"
+            ],
+            "babylonjs-gltf2interface/*": [
+                "../../dist/preview release/glTF2Interface/babylon.glTF2Interface.d.ts"
+            ],
+            "babylonjs-loaders/*": [
+                "../../dist/preview release/loaders/babylonjs.loaders.module.d.ts"
+            ],
+            "babylonjs-serializers/*": [
+                "../../dist/preview release/serializers/babylonjs.serializers.module.d.ts"
+            ],
+            "babylonjs/*": [
                 "../../dist/preview release/babylon.module.d.ts"
             ]
         }
-    }
+    },
+    "exclude": [
+        "../../inspector"
+    ]
 }