BabylonJS / Editor

Community managed visual editor for Babylon.js
http://editor.babylonjs.com/
813 stars 232 forks source link

Add Graph Editor #190

Open julien-moreau opened 3 years ago

julien-moreau commented 3 years ago

Add a graph editor (blueprint style) to the Editor. It should generate a TypeScript file.

julien-moreau commented 3 years ago

Missing nodes:

MegsTan commented 3 years ago

unable to right-click on graph editor using Windows 10, If I'm using the online editor when you left-click or right-click the graph editor does not respond

julien-moreau commented 3 years ago

The Graph Editor in the version 3 of the Editor was more an experiment than a finished feature. For the v4, the Graph Editor development has been started and now generates real typescript code that will be compiled with the project directly. That allows tree shaking etc. This is still in beta version so bugs can appear.

If you don't build the Editor yourself, you can download the latest released beta here: https://github.com/BabylonJS/Editor/wiki/BabylonJS-Editor-v4.0.0-beta And following the documentation which is still WIP: https://github.com/BabylonJS/Editor/blob/release/4.0.0/doc/00%20-%20welcome/doc.md

The Graph Editor has no documentation at the moment.

MegsTan commented 3 years ago

Hi @julien-moreau , I downloaded the standalone editor and it is one of the wonderful editors I've seen, very straight forward, just curious how do you attach the graph onto an object or how can I use it? I noticed it creates a TS script inside the graph folder but if I select script on any object it does not show up.

julien-moreau commented 3 years ago

Hi @MegsTan , really happy that you like the Editor!! =D

You are right for the graphs. At the moment they are processed globally (means it is not attached to a node but on the overall scene).

To get a reference to an object in the scene and use it with other nodes, simply add a new node like "Mesh": image

Then, in the properties of the node, select the mesh to get. This list is refreshed and shows ALL the available meshes in the current scene running in the Editor: image

And then use the output of the node (here the ground mesh in my scene) to connect to other nodes. Like: image

This will rotate the "ground" mesh each frame on the Y axis (the vector3 is equal to (0,1,0)).

You can get references from lights, cameras and meshes in the scene. The "Var name" property defines the name of the property in the TypeScript code. If already exists, an underscore with an index will be crated for you.

Don't forget to rebuild or watch your project using webpack to be sure the project will execute your graph!

MegsTan commented 3 years ago

Hi @julien-moreau ,

Thank you for that fast response, our company has chosen to use this as this is the best option instead of Unity or Unreal for web game development and the editor is really organized and very well made. Hope you keep the standalone feature as this is what most game dev wants to use instead of the browser-based editor. We really love your editor. Sometimes the auto watch from the preference does not work, I usually use the terminal to watch for any changes using "npm run watch".

AA

julien-moreau commented 3 years ago

Hi @MegsTan, I'm really happy to read you!! In that case, don't hesitate to report any bug and feature requests on the Babylon.JS forum: https://forum.babylonjs.com/ I'm 100% focused on that Editor :)

I saw that the auto watch in the Editor works, but the console is buggy and doesn't shows messages of webpack. I'm fixing ASAP :)

MegsTan commented 3 years ago

Hi @julien-moreau , Thank you, sure will create a ticket for this one and also for requested features :)

julien-moreau commented 3 years ago

You rox :) For the request features I suggest that you use the BabylonJS forum instead. It'll be the occasion to share your ideas with others :)

jkeys-ecg-nmsu commented 3 years ago

@julien-moreau we are currently porting Sumerian state machines to Babylon/JS code rather than use the visual editor to generate code, because it currently lacks many of the nodes that we need.

Would you mind contributing a guide for developing and contributing visual scripting nodes? I'm sure there's an interface somewhere that I need to extend but I would appreciate a little guidance before trying to add the nodes we are missing (and re-start our visual scripting adventure in Babylon).

julien-moreau commented 3 years ago

Hey @jkeys-ecg-nmsu ! I would be very happy! You are right there is an interface ahah and I can create a guide for you. I'm more focusing on the overall Editor's stability right now but, until I create the mini guide:

That's the only thing you have to do :)

I'm creating the mini guide in the doc!

julien-moreau commented 3 years ago

Hey @jkeys-ecg-nmsu I create the first pass of the documentation here: https://github.com/BabylonJS/Editor/blob/release/4.0.0/doc/dev%2001-%20create-graph-node/doc.md

jkeys-ecg-nmsu commented 3 years ago

@julien-moreau can you take a look at this and tell me if I'm on the right track? This node should work if there exists a TransformNode in the scene with a node.metadata.host property defined (which is the case for our scenes).

https://github.com/electronic-caregiver/Editor/blob/feature/custom-nodes/src/renderer/editor/graph/custom/emote.ts

julien-moreau commented 3 years ago

Hey @jkeys-ecg-nmsu !

You are! I'm adding the support of TransformNode ASAP, and the code would more look like:

import { TransformNode } from "babylonjs";
import { LiteGraph } from "litegraph.js";

import { GraphNode, ICodeGenerationOutput, CodeGenerationOutputType } from "../node";

export class Emote extends GraphNode<{ emote_name: string; }> {
    /**
     * Constructor.
     */
    public constructor() {
        super("Emote");

        this.addInput("", LiteGraph.EVENT as any);
        this.addInput("node *", "TransformNode");

        this.addProperty("emote_name", "vannaMid", "string", (v) => this.properties.emote_name = v);

        this.addWidget("text", "emote_name", this.properties.emote_name, (v) => this.properties.emote_name = v);

        this.addOutput("", LiteGraph.EVENT as any);
    }

    /**
     * Called on the node is being executed.
     */
    public execute(): void {
        const node = this.getInputData<TransformNode>(1);
        if (!node?.metadata?.host) { return; }

        node.metadata.host.GestureFeature.playGesture("Emote", this.properties.emote_name);

        this.triggerSlot(0, null);
    }

    /**
     * Generates the code of the graph.
     */
    public generateCode(node: ICodeGenerationOutput): ICodeGenerationOutput {
        return {
            type: CodeGenerationOutputType.Function,
            code: `${node.code}.metadata.host.GestureFeature.playGesture("Emote", "${this.properties.emote_name}")`
        };
    }
}
    /**
     * Called on the node is being executed.
     */
    public execute(): void {
        const node = this.getInputData<TransformNode>(1);
        if (!node?.metadata?.host) { return; }

        node.metadata.host.GestureFeature.playGesture("Emote", this.properties.emote_name);

        this.triggerSlot(0, null);
    }

    /**
     * Generates the code of the graph.
     */
    public generateCode(node: ICodeGenerationOutput): ICodeGenerationOutput {
        return {
            type: CodeGenerationOutputType.Function,
            code: `${node.code}.metadata.host.GestureFeature.playGesture("Emote", "${this.properties.emote_name}")`
        };
    }
}
julien-moreau commented 3 years ago

Just added the support of Transform Node so you can pull!

So the graph would look like: image

And generates that code:

...
/**
 * Defines the generated class of the graph.
 */
export default class GraphClass {
    public emoteNode = this._scene.getTransformNodeByName("transform");

    /**
     * Constructor.
     * @param scene defines the scene where the graph is running.
     */
    public constructor(private _scene: Scene) {

    }

    /**
     * Called on the scene starts.
     */
    public onStart(): void {

        this._scene.onPointerObservable.add((ev) => {
            if (ev.type !== PointerEventTypes.POINTERTAP) {
                return;
            }

            this.emoteNode.metadata.host.GestureFeature.playGesture("Emote", "vannaMid");
        });

    }
...