Pārlūkot izejas kodu

Added sprite preview

David Catuhe 5 gadi atpakaļ
vecāks
revīzija
5bc8df56c5

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

@@ -1 +1 @@
-{"thinEngineOnly":115886,"engineOnly":152289,"sceneOnly":511469,"minGridMaterial":644774,"minStandardMaterial":788500}
+{"thinEngineOnly":115859,"engineOnly":152262,"sceneOnly":511442,"minGridMaterial":644719,"minStandardMaterial":788495}

+ 14 - 134
inspector/src/components/actionTabs/lines/textureLineComponent.tsx

@@ -1,14 +1,10 @@
 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";
 import { ButtonLineComponent } from './buttonLineComponent';
+import { TextureHelper, TextureChannelToDisplay } from '../../../textureHelper';
 
 interface ITextureLineComponentProps {
     texture: BaseTexture;
@@ -18,29 +14,22 @@ interface ITextureLineComponentProps {
     hideChannelSelect?: boolean;
 }
 
-enum ChannelToDisplay {
-    R,
-    G,
-    B,
-    A,
-    All
-}
 
-export class TextureLineComponent extends React.Component<ITextureLineComponentProps, { channel: ChannelToDisplay, face: number }> {
+export class TextureLineComponent extends React.Component<ITextureLineComponentProps, { channel: TextureChannelToDisplay, face: number }> {
     private canvasRef: React.RefObject<HTMLCanvasElement>;
 
     constructor(props: ITextureLineComponentProps) {
         super(props);
 
         this.state = {
-            channel: ChannelToDisplay.All,
+            channel: TextureChannelToDisplay.All,
             face: 0
         };
 
         this.canvasRef = React.createRef();
     }
 
-    shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: { channel: ChannelToDisplay, face: number }): boolean {
+    shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: { channel: TextureChannelToDisplay, face: number }): boolean {
         return (nextProps.texture !== this.props.texture || nextState.channel !== this.state.channel || nextState.face !== this.state.face);
     }
 
@@ -54,111 +43,13 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
 
     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.canvasRef.current 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.channel != ChannelToDisplay.All) {
-                    for (var i = 0; i < width * height * 4; i += 4) {
-
-                        switch (this.state.channel) {
-                            case ChannelToDisplay.R:
-                                data[i + 1] = data[i];
-                                data[i + 2] = data[i];
-                                data[i + 3] = 255;
-                                break;
-                            case ChannelToDisplay.G:
-                                data[i] = data[i + 1];
-                                data[i + 2] = data[i];
-                                data[i + 3] = 255;
-                                break;
-                            case ChannelToDisplay.B:
-                                data[i] = data[i + 2];
-                                data[i + 1] = data[i + 2];
-                                data[i + 3] = 255;
-                                break;
-                            case ChannelToDisplay.A:
-                                data[i] = data[i + 3];
-                                data[i + 1] = data[i + 3];
-                                data[i + 2] = data[i + 3];
-                                data[i + 3] = 255;
-                                break;
-                        }
-                    }
-                }
-            }
-
-            //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;
-                    }
-                }
-            }
+        var height = (width / ratio) | 1;            
 
+        TextureHelper.GetTextureDataAsync(texture, width, height, this.state.face, this.state.channel, this.props.globalState).then(data => {
+            const previewCanvas = this.canvasRef.current as HTMLCanvasElement;
             previewCanvas.width = width;
             previewCanvas.height = height;
             var context = previewCanvas.getContext('2d');
@@ -170,19 +61,8 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
                 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;
-        }
-
+            previewCanvas.style.height = height + "px";
+        });
     }
 
     render() {
@@ -205,11 +85,11 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
                     {
                         !this.props.hideChannelSelect && !texture.isCube &&
                         <div className="control">
-                            <button className={this.state.channel === ChannelToDisplay.R ? "red command selected" : "red command"} onClick={() => this.setState({ channel: ChannelToDisplay.R })}>R</button>
-                            <button className={this.state.channel === ChannelToDisplay.G ? "green command selected" : "green command"} onClick={() => this.setState({ channel: ChannelToDisplay.G })}>G</button>
-                            <button className={this.state.channel === ChannelToDisplay.B ? "blue command selected" : "blue command"} onClick={() => this.setState({ channel: ChannelToDisplay.B })}>B</button>
-                            <button className={this.state.channel === ChannelToDisplay.A ? "alpha command selected" : "alpha command"} onClick={() => this.setState({ channel: ChannelToDisplay.A })}>A</button>
-                            <button className={this.state.channel === ChannelToDisplay.All ? "all command selected" : "all command"} onClick={() => this.setState({ channel: ChannelToDisplay.All })}>ALL</button>
+                            <button className={this.state.channel === TextureChannelToDisplay.R ? "red command selected" : "red command"} onClick={() => this.setState({ channel: TextureChannelToDisplay.R })}>R</button>
+                            <button className={this.state.channel === TextureChannelToDisplay.G ? "green command selected" : "green command"} onClick={() => this.setState({ channel: TextureChannelToDisplay.G })}>G</button>
+                            <button className={this.state.channel === TextureChannelToDisplay.B ? "blue command selected" : "blue command"} onClick={() => this.setState({ channel: TextureChannelToDisplay.B })}>B</button>
+                            <button className={this.state.channel === TextureChannelToDisplay.A ? "alpha command selected" : "alpha command"} onClick={() => this.setState({ channel: TextureChannelToDisplay.A })}>A</button>
+                            <button className={this.state.channel === TextureChannelToDisplay.All ? "all command selected" : "all command"} onClick={() => this.setState({ channel: TextureChannelToDisplay.All })}>ALL</button>
                         </div>
                     }
                     <canvas ref={this.canvasRef} className="preview" />

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent.tsx

@@ -202,7 +202,7 @@ export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteM
                         onPropertyChangedObservable={this.props.onPropertyChangedObservable} 
                         onSelect={(value) => this.setState({ blendMode: value })} />                                       
                 </LineContainerComponent>
-                <LineContainerComponent globalState={this.props.globalState} title="DEFAULT SIZE">
+                <LineContainerComponent globalState={this.props.globalState} title="CELLS">
                     <FloatLineComponent label="Cell width" isInteger={true} target={spriteManager} propertyName="cellWidth" min={0} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                     <FloatLineComponent label="Cell height" isInteger={true} target={spriteManager} propertyName="cellHeight" min={0} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
                 </LineContainerComponent>

+ 76 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent.tsx

@@ -15,6 +15,7 @@ import { Color4LineComponent } from '../../../lines/color4LineComponent';
 import { FloatLineComponent } from '../../../lines/floatLineComponent';
 import { SliderLineComponent } from '../../../lines/sliderLineComponent';
 import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
+import { TextureHelper, TextureChannelToDisplay } from '../../../../../textureHelper';
 
 interface ISpritePropertyGridComponentProps {
     globalState: GlobalState;
@@ -25,8 +26,14 @@ interface ISpritePropertyGridComponentProps {
 }
 
 export class SpritePropertyGridComponent extends React.Component<ISpritePropertyGridComponentProps> {
+    private canvasRef: React.RefObject<HTMLCanvasElement>;
+    private imageData: Uint8Array;
+    private cachedCellIndex = -1;
+
     constructor(props: ISpritePropertyGridComponentProps) {
         super(props);
+        
+        this.canvasRef = React.createRef();
     }
 
     onManagerLink() {
@@ -63,6 +70,65 @@ export class SpritePropertyGridComponent extends React.Component<ISpriteProperty
         this.props.onSelectionChangedObservable?.notifyObservers(null);
     }
 
+    componentDidMount() {
+        this.updatePreview();
+    }
+
+    componentDidUpdate() {
+        this.updatePreview();
+    }
+
+    updatePreview() {        
+        const sprite = this.props.sprite;        
+        const manager = sprite.manager;
+        var texture = manager.texture;
+        var size = texture.getSize();
+
+        if (!this.imageData) {
+            TextureHelper.GetTextureDataAsync(texture, size.width, size.height, 0, TextureChannelToDisplay.All, this.props.globalState).then(data => {
+                this.imageData = data;
+                this.forceUpdate();
+            });
+
+            return;
+        }
+
+        if (this.cachedCellIndex === sprite.cellIndex) {
+            return;
+        }
+
+        this.cachedCellIndex = sprite.cellIndex;
+
+        const previewCanvas = this.canvasRef.current as HTMLCanvasElement;
+        previewCanvas.width = manager.cellWidth;
+        previewCanvas.height = manager.cellHeight;
+        var context = previewCanvas.getContext('2d');
+
+        if (context) {
+            // Copy the pixels to the preview canvas
+            var imageData = context.createImageData(manager.cellWidth, manager.cellHeight);
+            var castData = imageData.data;
+
+            let rowLength = size.width / manager.cellWidth | 0;
+            let offsetY = sprite.cellIndex / rowLength | 0;
+            let offsetX = sprite.cellIndex - offsetY * rowLength;
+            let offset = (offsetX + offsetY * size.width) * 4 * manager.cellWidth ;
+
+            for (var x = 0; x < manager.cellWidth; x++) {
+                for (var y = 0; y < manager.cellHeight; y++) {
+                    let targetCoord = (x + y * manager.cellWidth) * 4;
+                    let sourceCoord = (x + y * size.width) * 4
+                    castData[targetCoord] = this.imageData[offset + sourceCoord];
+                    castData[targetCoord + 1] = this.imageData[offset + sourceCoord + 1];
+                    castData[targetCoord + 2] = this.imageData[offset + sourceCoord + 2];
+                    castData[targetCoord + 3] = this.imageData[offset + sourceCoord + 3];
+                }
+            }
+
+            context.putImageData(imageData, 0, 0);
+        }
+    }
+
     render() {
         const sprite = this.props.sprite;
         const manager = sprite.manager;
@@ -92,6 +158,16 @@ export class SpritePropertyGridComponent extends React.Component<ISpriteProperty
                     <SliderLineComponent useEuler={this.props.globalState.onlyUseEulers} label="Angle" target={sprite} propertyName="angle" minimum={0} maximum={2 * Math.PI} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="CELL">
+                    <canvas ref={this.canvasRef} className="preview" style={{
+                        margin: "auto",
+                        marginTop: "4px",
+                        marginBottom: "4px",
+                        display: "grid",
+                        height: "108px"
+                    }}/>
+                    <SliderLineComponent label="Cell index" decimalCount={0} target={sprite} propertyName="cellIndex" minimum={0} maximum={maxCellCount} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} 
+                        onChange={() => this.forceUpdate()}
+                        />
                     <CheckBoxLineComponent label="Invert U axis" target={sprite} propertyName="invertU" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Invert V axis" target={sprite} propertyName="invertV" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>

+ 145 - 0
inspector/src/textureHelper.ts

@@ -0,0 +1,145 @@
+import { PostProcess } from 'babylonjs/PostProcesses/postProcess';
+import { Texture } from 'babylonjs/Materials/Textures/texture';
+import { PassPostProcess, PassCubePostProcess } from 'babylonjs/PostProcesses/passPostProcess';
+import { Constants } from 'babylonjs/Engines/constants';
+import { GlobalState } from './components/globalState';
+import { RenderTargetTexture } from 'babylonjs/Materials/Textures/renderTargetTexture';
+import { BaseTexture } from 'babylonjs/Materials/Textures/index';
+import { Nullable } from 'babylonjs/types';
+
+export enum TextureChannelToDisplay {
+    R,
+    G,
+    B,
+    A,
+    All
+}
+
+export class TextureHelper {
+
+    private static _ProcessAsync(texture: BaseTexture, width: number, height: number, face: number, channel: TextureChannelToDisplay, globalState: Nullable<GlobalState>, resolve: (result: Uint8Array) => void, reject: () => void) {
+        var scene = texture.getScene()!;
+        var engine = scene.getEngine();
+
+        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 = face;
+
+            passPostProcess = passCubePostProcess;
+        }
+
+        if (!passPostProcess.getEffect().isReady()) {
+            // Try again later
+            passPostProcess.dispose();
+
+            setTimeout(() => {
+                this._ProcessAsync(texture, width, height, face, channel, globalState, resolve, reject);
+            }, 250);
+
+            return;
+        }
+
+        if (globalState) {
+            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 (channel != TextureChannelToDisplay.All) {
+                    for (var i = 0; i < width * height * 4; i += 4) {
+
+                        switch (channel) {
+                            case TextureChannelToDisplay.R:
+                                data[i + 1] = data[i];
+                                data[i + 2] = data[i];
+                                data[i + 3] = 255;
+                                break;
+                            case TextureChannelToDisplay.G:
+                                data[i] = data[i + 1];
+                                data[i + 2] = data[i];
+                                data[i + 3] = 255;
+                                break;
+                            case TextureChannelToDisplay.B:
+                                data[i] = data[i + 2];
+                                data[i + 1] = data[i + 2];
+                                data[i + 3] = 255;
+                                break;
+                            case TextureChannelToDisplay.A:
+                                data[i] = data[i + 3];
+                                data[i + 1] = data[i + 3];
+                                data[i + 2] = data[i + 3];
+                                data[i + 3] = 255;
+                                break;
+                        }
+                    }
+                }
+            }
+
+            //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;
+                    }
+                }
+            }
+            
+            resolve(data);
+
+            // Unbind
+            engine.unBindFramebuffer(internalTexture);
+        } else {
+            reject();
+        }
+
+        rtt.dispose();
+        passPostProcess.dispose();
+        
+        if (globalState) {
+            globalState.blockMutationUpdates = false;
+        }
+    }
+
+    public static GetTextureDataAsync(texture: BaseTexture, width: number, height: number, face: number, channel: TextureChannelToDisplay, globalState?: GlobalState): Promise<Uint8Array> {
+        return new Promise((resolve, reject) => {
+            if (!texture.isReady() && texture._texture) {
+                texture._texture.onLoadedObservable.addOnce(() => {
+                    this._ProcessAsync(texture, width, height, face, channel, globalState || null, resolve, reject);
+                });
+                return;
+            }        
+
+            this._ProcessAsync(texture, width, height, face, channel, globalState || null, resolve, reject);
+        });
+    }
+}