How to make plugins for BabylonJS Editor?

Hello!
I’ve been looking into BabylonJS for a while now and wanted to make a new cool feature, but then I found out the editor exists. Without spoiling the purpose of my feature, I found that the Editor plugin system could suit my needs. But there’s a problem. I’ve been doing some research and sadly, the newest plugin I could find was GitHub - julien-moreau/babylonjs-editor-quixel-plugin: Quixel provides a software named "Bridge" that allows to export assets directly to third-party softwares. The Babylon.JS Editor provides a plugin to directly import assets from Quixel using Bridge. which (even after installing all libraries and building without errors) doesn’t import in the Editor, with no errors or any logs at all.

Can someone give me a brief explanation of how to make a plugin work?

A lot of thanks in advance,
Bjarnos

cc: @julien-moreau
If the plugin does work for you, here are my logs:

~/Downloads/babylonjs-editor-quixel-plugin-master 12s
❯ ~/Downloads/BabylonJS-editor.AppImage
[16481:1205/220303.013802:ERROR:net/socket/socket_posix.cc:147] bind() failed: Address already in use (98)
[16481:1205/220303.014083:ERROR:content/browser/devtools/devtools_http_handler.cc:309] Cannot start http server for devtools.
Checking for update
[16519:1205/220306.014525:ERROR:ui/gl/gl_surface_presentation_helper.cc:260] GetVSyncParametersIfAvailable() failed for 1 times!
Update for version 5.2.3 is not available (latest version: 5.2.3, downgrade is disallowed).
checkForUpdatesAndNotify called, downloadPromise is null
[16519:1205/220315.958142:ERROR:ui/gl/gl_surface_presentation_helper.cc:260] GetVSyncParametersIfAvailable() failed for 2 times!
[16519:1205/220317.448293:ERROR:ui/gl/gl_surface_presentation_helper.cc:260] GetVSyncParametersIfAvailable() failed for 3 times!
[16481:1205/220406.439186:ERROR:CONSOLE:1] "Request Autofill.enable failed. {"code":-32601,"message":"'Autofill.enable' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
[16481:1205/220406.439376:ERROR:CONSOLE:1] "Request Autofill.setAddresses failed. {"code":-32601,"message":"'Autofill.setAddresses' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)

1 Like

I forgot to mention, console has nothing notable too.

1 Like

cc @julien-moreau the editor daddy

2 Likes

Hey @Bjarnos !!
That’s so funny that the support plugins system in the editor popped multiple times this week. I’m releasing a new version tomorrow. After it’s released, I’ll actively finish the support for plugins so you’ll be able to plug everywhere in the editor and you’ll be able to use UI components.

Following the link you shared for the existing Quixel plugin, it has been moved to the official Babylon.js Editor repository here: Editor/quixel at master · BabylonJS/Editor · GitHub which is now compatible with the version 5. You can browse the sources for instance in order to prepare your plugin until I released an official documentation. The entry point is here: Editor/quixel/src/index.ts at master · BabylonJS/Editor · GitHub (main and close functions are called by the editor, the rest deserves the plugin)

I also plan to support Fab.com as a plugin to directly import assets from Fab libraries.

And I’m so curious!! What do you plan to do? :grin:

1 Like

My plan is called “babyLua” (definitely need to work on that name), Lua language support for one of the biggest engines natively in browser environment JavaScript. I already have it working with a decent translation layer, but it would be fun to add as a plugin because I could make extra features to it (e.g. precompiling code within export).

Example code that already works (Editor format with classes or functions isn’t supported yet):

local scene = Scene.new(engine) -- the engine is initialized in TS and injected as global

local ground = Mesh.CreateGround("ground", 100, 100, 2, scene)
local groundMat = StandardMaterial.new("groundMat", scene)
groundMat.diffuseTexture = Texture.new("https://assets.babylonjs.com/environments/valleygrass.png", scene)
groundMat.specularColor = Color3.new(0, 0, 0)
ground.material = groundMat

local body = MeshBuilder.CreateCapsule("player", { height = 2, radius = 0.5 }, scene)
body.position.y = 1
local bodyMat = StandardMaterial.new("bodyMat", scene)
bodyMat.diffuseColor = Color3.new(0.2, 0.2, 0.9)
body.material = bodyMat

local cam = FollowCamera.new("cam", Vector3.new(0, 6, -12), scene)
cam.lockedTarget = body
cam.radius = 12
cam.heightOffset = 3
cam.rotationOffset = 180
cam.cameraAcceleration = 0.05
cam.maxCameraSpeed = 10
cam.lowerRadiusLimit = 5
cam.upperRadiusLimit = 20
cam.attachControl()

local light = HemisphericLight.new("light", Vector3.new(0, 1, 0), scene)
light.intensity = 0.9
scene.clearColor = Color4.new(0.7, 0.82, 0.95)

local input = {}
scene.actionManager = ActionManager.new(scene)
scene.actionManager.registerAction(ExecuteCodeAction.new(ActionManager.OnKeyDownTrigger, function(e) input[e.sourceEvent.key:lower()] = true end))
scene.actionManager.registerAction(ExecuteCodeAction.new(ActionManager.OnKeyUpTrigger, function(e) input[e.sourceEvent.key:lower()] = false end))
scene.actionManager.registerAction(ExecuteCodeAction.new(ActionManager.OnKeyDownTrigger, function(e)
    if (e.sourceEvent.key == "ArrowUp") then input["w"] = true end
    if (e.sourceEvent.key == "ArrowDown") then input["s"] = true end
    if (e.sourceEvent.key == "ArrowLeft") then input["a"] = true end
    if (e.sourceEvent.key == "ArrowRight") then input["d"] = true end
    if (e.sourceEvent.key == " ") then input["space"] = true end
end))
scene.actionManager.registerAction(ExecuteCodeAction.new(ActionManager.OnKeyUpTrigger, function(e)
    if (e.sourceEvent.key == "ArrowUp") then input["w"] = false end
    if (e.sourceEvent.key == "ArrowDown") then input["s"] = false end
    if (e.sourceEvent.key == "ArrowLeft") then input["a"] = false end
    if (e.sourceEvent.key == "ArrowRight") then input["d"] = false end
    if (e.sourceEvent.key == " ") then input["space"] = false end
end))

local moveSpeed = 3
local jumpVelocity = 0

engine.runRenderLoop(function()
    local dt = engine.getDeltaTime() / 1000

    local grounded = body.position.y <= 1
    if (not grounded) then jumpVelocity = jumpVelocity - 9.8 * dt end

    if (input["space"] and grounded) then jumpVelocity = 5 end

    local forward = cam.getFrontPosition(1).subtract(cam.position).normalize()
    local right = Vector3.Cross(forward, Axis.Y).normalize().negate()

    if (input["w"]) then body.position.addInPlace(forward.scale(moveSpeed * dt)) end
    if (input["s"]) then body.position.addInPlace(forward.scale(-moveSpeed * dt)) end
    if (input["a"]) then body.position.addInPlace(right.scale(-moveSpeed * dt)) end
    if (input["d"]) then body.position.addInPlace(right.scale(moveSpeed * dt)) end

    body.position.y = body.position.y + jumpVelocity * dt
    if (body.position.y < 1) then
        body.position.y = 1
        jumpVelocity = 0
    end
    
    scene.render()
end)
1 Like

Hey Julien,

I’ve cloned the quixel plugin and successfully built it using yarn. I can now begin on my own plugin but there’s a small annoying problem, every time I try to remove my plugin and add it again the built-in cache of require activates. Editor’s source code:

	private _handleAddPluginFromLocalDisk(): void {
		const directory = openSingleFolderDialog("Select plugin's directory.");

		try {
			require(join(directory, "package.json"));
			const result = require(directory);
			result.main(this.props.editor);

			this.props.editor.setState({
				plugins: [...this.props.editor.state.plugins, directory],
			});
		} catch (e) {
			this.props.editor.layout.console.error("Invalid plugin.");
			if (e.message) {
				this.props.editor.layout.console.error(e.message);
			}
		}

		this.forceUpdate();
	}

I’ve tried to remove this cache using various method, including erasing it entirely (Object.keys(require.cache).forEach(function(key) { delete require.cache[key] })), but the entire cache keeps on coming back. I suspect there’s some library or bundler handling it but I’m not sure. Any insights are appreciated :slight_smile:

Edit 2: I’ve managed live reload by watching the plugin’s directory. Still, native support (e.g. a reload button or file watch for live reload) would be cool;

	const path = require("path");
	const pluginRoot = path.resolve("/home/bjarnos/Downloads/Editor-master/babyLua");
	const buildFile = path.join(pluginRoot, "build", "build-id.js");

	fs.watch(buildFile, (eventType: any) => {
		console.log("New version detected in build");

		setTimeout(() => {
			for (const key of Object.keys(require.cache)) {
				if (key.startsWith(pluginRoot)) {
					delete require.cache[key];
				}
			}

			try {
				require(join(pluginRoot, "package.json"));
				const result = require(pluginRoot);
				console.log("Loaded new module!");
				result.main(editor);
				close();
			} catch (err) {
				console.error("Error reloading plugin:", err);
			}
		}, 1000);
	});
1 Like

That’s so awesome !!! I started development when I discovered lua on PSP many years ago, you’ll have the best plugins support ever ahah :slight_smile:

I created and started an issue that you can track in case you’de like to follow the progress: Plugins system · Issue #748 · BabylonJS/Editor · GitHub
I plan to finish the plugins system for the next release. Let’s say something like 2 weeks?

2 Likes

Oh wow, you must’ve read my mind because I was just about to ask you when you’d think the plugins system would be done. Thanks for the positive vibes and you’ll see me around with my project :blush:

2 Likes

Can’t wait !!!
Are you the creator of it ? https://rumblerush.io/

Oh wow, looks like somebody visited my website. :laughing:
No, I am not the creator of Rumble Rush, but I was the old maintainer of the wiki until the game went in a half-dead state, because of too few updates. I have made many projects (including games) in multiple languages before but I removed them all from my website since I’m not for hire anymore and all my projects are personal now :slight_smile:

1 Like

I actually took a stab at making one…I basically load a mini overlay and then export things to a database that the main engine uses (also babylon).

My setup is I have a base scene with assets loaded and then add scripts to items to decorate extra metadata, then have buttons in a popout window to save/load levels.

One thing I’m bumping into is that my game assumes all units are meters (it’s a webxr game) and I can’t figure out how to do the math to get things in the correct units. I may play with “zoom level” in the preview window.

Anyone else using the editor for VR/Webxr scenes? I know, by definition, the units are arbitrary, but when you’re trying to view/play in a headset “units matter”…

Thoughts?

1 Like

space-game/bjsEditorPlugin at main - space-game - Gitea: Git with a cup of tea if anyone wants to take a look.

Game is at https://www.flatearthdefense.com/

If anyone wants to actually use it, I can walk you through the setup. Very Alpha mode right now, but if you want examples of how to hook into the editor and save “somewhere else” this should give you an idea on how to handle it.

2 Likes