Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
937606e
feat: adding support of gaussian splatting meshes
julien-moreau Jul 24, 2026
55895a6
fix: clone of gaussian splatting
julien-moreau Jul 24, 2026
846bf36
feat: add support of gaussian splatting in babylonjs-editor-tools
julien-moreau Jul 24, 2026
44e88c6
feat: add support of gaussian splatting in babylonjs-editor-cli
julien-moreau Jul 24, 2026
5a8fc23
fix: re-apply old active render loops when stoping game in preview to…
julien-moreau Jul 28, 2026
b344b94
fix: keep parenting and isEnabled properties when saving Gaussian Spl…
julien-moreau Jul 28, 2026
d291fcc
feat: adding support of Gaussian Splatting parts
julien-moreau Jul 29, 2026
07c350b
fix: create gaussian splatting mesh receiver only when gaussian splat…
julien-moreau Aug 4, 2026
26a9f98
fix: remove and restore gaussian splatting proxy meshes
Aug 6, 2026
c576065
fix: compute parentId only when parent exist for gaussian splatting m…
Aug 6, 2026
87b0641
feat: add support of sh data for gaussian splatting meshes
Aug 6, 2026
733d6a8
fix: minor fixes for gaussian splatting support
Aug 6, 2026
a33d18b
docs: starting documentation for Gaussian Splatting support
Aug 6, 2026
1beaa66
chore: bump electron to v42.8.0
Aug 6, 2026
74ceb70
fix: add .ply to gaussian splatting supported formats
Aug 6, 2026
ce33fb1
fix: set navmesh editor a vertical tool
Aug 6, 2026
83f235f
fix: fixed preview of gaussian splatting meshes in assets browser
Aug 7, 2026
b249946
feat: make graph optionally updated automatically when playing inline…
Aug 7, 2026
841da6a
feat: add support of "debug only" for attachable scripts
Aug 7, 2026
aee1766
chore: update yarn.lock
julien-moreau Aug 7, 2026
bf885b4
v5.4.3-alpha.5
julien-moreau Aug 7, 2026
6371e8a
chore: bump electron-updater to v6.8.9 and postcss to v8.5.26
julien-moreau Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "babylonjs-editor-cli",
"version": "5.4.3-alpha.4",
"version": "5.4.3-alpha.5",
"description": "Babylon.js Editor CLI is a command line interface to help you package your scenes made using the Babylon.js Editor",
"productName": "Babylon.js Editor CLI",
"scripts": {
Expand Down
42 changes: 41 additions & 1 deletion cli/src/pack/scene.mts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,36 @@ export async function createBabylonScene(options: ICreateBabylonSceneOptions) {
const meshesResult = await Promise.all(
options.directories.meshesFiles.map(async (file) => {
const data = await fs.readJSON(join(options.sceneFile, "meshes", file));

if (data.type === "GaussianSplattingMesh") {
data.splatDataPath = join(options.sceneName, basename(data.splatDataPath));

data.shDataPaths?.forEach((shDataPath: string, index: number) => {
data.shDataPaths[index] = join(options.sceneName, basename(shDataPath));
});

const splatDataDestination = join(options.publicDir, data.splatDataPath);
const shDataDestinations = data.shDataPaths?.map((shDataPath: string) => join(options.publicDir, shDataPath));

const promises = [fs.copyFile(join(options.sceneFile, "splats", basename(data.splatDataPath)), splatDataDestination)];
shDataDestinations?.forEach((shDataDestination: string, index: number) => {
promises.push(fs.copyFile(join(options.sceneFile, "splats", basename(data.shDataPaths[index])), shDataDestination));
});

await Promise.all(promises);

options.exportedAssets.push(splatDataDestination);
shDataDestinations?.forEach((shDataDestination: string) => {
options.exportedAssets.push(shDataDestination);
});

return {
mesh: data,
lodMeshes: [],
effectiveMaterials: [],
};
}

const mesh = data.meshes[0];

if (mesh.metadata?.doNotSerialize) {
Expand Down Expand Up @@ -650,10 +680,15 @@ export async function createBabylonScene(options: ICreateBabylonSceneOptions) {
reflectionProbes: [],
} as any;

// Resolve parenting for mesh instances.
// Remove debug-only scripts for scene
if (scene.metadata?.scripts?.length) {
scene.metadata.scripts = scene.metadata.scripts.filter((script) => !script.debugOnly);
}

const allNodes = [...scene.meshes, ...scene.cameras, ...scene.lights, ...scene.transformNodes, ...scene.meshes.map((m) => m.instances ?? []).flat()];

allNodes.forEach((node) => {
// Resolve parenting for mesh instances.
if (node.parentId !== undefined && node.parentInstanceIndex !== undefined) {
const effectiveMesh = scene.meshes.find((mesh) => {
return mesh.instances?.find((instance) => instance.uniqueId === node.parentId);
Expand All @@ -663,6 +698,11 @@ export async function createBabylonScene(options: ICreateBabylonSceneOptions) {
node.parentId = effectiveMesh.uniqueId;
}
}

// Remove debug-only scripts
if (node.metadata?.scripts?.length) {
node.metadata.scripts = node.metadata.scripts.filter((script) => !script.debugOnly);
}
});

// Configue ennviornment texture
Expand Down
4 changes: 4 additions & 0 deletions cli/src/pack/scripts.mts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ export async function createScriptsFile(projectDir: string): Promise<void> {
const promises: Promise<void>[] = [];
availableMetadata.forEach((configuration) => {
configuration.metadata.scripts?.forEach((script) => {
if (!script.enabled || script.debugOnly) {
return;
}

promises.push(
new Promise<void>(async (resolve) => {
const path = join(projectDir, "src", script.key);
Expand Down
4 changes: 4 additions & 0 deletions cli/src/tools/scene.mts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export async function ensureSceneDirectories(scenePath: string) {
fs.ensureDir(join(scenePath, "sprite-maps")),
fs.ensureDir(join(scenePath, "sprite-managers")),
fs.ensureDir(join(scenePath, "nodeParticleSystemSets")),
fs.ensureDir(join(scenePath, "splats")),
]);
}

Expand All @@ -48,6 +49,7 @@ export async function readSceneDirectories(scenePath: string) {
spriteManagerFiles,
geometryFiles,
nodeParticleSystemSetFiles,
gaussianSplattingFiles,
] = await Promise.all([
readdir(join(scenePath, "nodes")),
readdir(join(scenePath, "meshes")),
Expand All @@ -68,6 +70,7 @@ export async function readSceneDirectories(scenePath: string) {
readdir(join(scenePath, "sprite-managers")),
readdir(join(scenePath, "geometries")),
readdir(join(scenePath, "nodeParticleSystemSets")),
readdir(join(scenePath, "splats")),
]);

return {
Expand All @@ -90,5 +93,6 @@ export async function readSceneDirectories(scenePath: string) {
spriteManagerFiles,
geometryFiles,
nodeParticleSystemSetFiles,
gaussianSplattingFiles,
};
}
10 changes: 6 additions & 4 deletions editor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "babylonjs-editor",
"version": "5.4.3-alpha.4",
"version": "5.4.3-alpha.5",
"description": "Babylon.js Editor is a Web Application helping artists to work with Babylon.js",
"productName": "Babylon.js Editor",
"main": "build/src/index.js",
Expand Down Expand Up @@ -31,7 +31,7 @@
"@types/react-dom": "18.2.18",
"@vitest/coverage-v8": "4.0.17",
"concurrently": "9.2.0",
"electron": "39.8.5",
"electron": "42.8.0",
"electron-builder": "26.0.12",
"electron-reloader": "1.2.3",
"postcss-import": "16.1.0",
Expand All @@ -40,6 +40,7 @@
"vitest": "4.1.0"
},
"dependencies": {
"@adobe/spz": "0.2.2",
"@babylonjs/addons": "9.12.1",
"@babylonjs/core": "9.12.1",
"@babylonjs/havok": "1.3.12",
Expand Down Expand Up @@ -98,8 +99,9 @@
"decompress-targz": "^4.1.1",
"dotenv": "17.2.3",
"dunder-proto": "1.0.1",
"electron-updater": "6.6.2",
"electron-updater": "6.8.9",
"esbuild": "0.28.1",
"fflate": "0.8.3",
"filenamify": "4.3.0",
"flexlayout-react": "0.7.15",
"fluent-ffmpeg": "^2.1.3",
Expand All @@ -111,7 +113,7 @@
"md5": "2.3.0",
"motion": "12.23.24",
"next-themes": "^0.3.0",
"node-pty": "1.2.0-beta.12",
"node-pty": "1.2.0-beta.15",
"node-stream-zip": "1.15.0",
"pngjs": "7.0.0",
"react": "18.2.0",
Expand Down
7 changes: 5 additions & 2 deletions editor/src/editor/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { platform } from "os";

import { Component, ReactNode } from "react";
import { Actions, IJsonModel, Layout, Model, TabNode, TabSetNode } from "flexlayout-react";
import { Actions, ICloseType, IJsonModel, Layout, Model, TabNode, TabSetNode } from "flexlayout-react";

import { Observable, Tools } from "babylonjs";
import { ipcRenderer } from "electron";
Expand Down Expand Up @@ -204,7 +204,7 @@ export class EditorLayout extends Component<IEditorLayoutProps> {
* @param component defines the reference to the React component to draw in.
* @param options defines the options of the tab such as the title etc.
*/
public addLayoutTab(component: ReactNode, options: IEditorLayoutTabOptions): void {
public addLayoutTab(component: ReactNode, options: IEditorLayoutTabOptions): string {
options.id ??= Tools.RandomId();

const activeTabId = this._layoutRef?.props.model.getActiveTabset()?.getSelectedNode()?.getId();
Expand Down Expand Up @@ -238,11 +238,14 @@ export class EditorLayout extends Component<IEditorLayoutProps> {
type: "tab",
component: options.id,
enableClose: options.enableClose,
closeType: ICloseType.Visible,
});

if (activeTabId && !options.setAsActiveTab) {
this._layoutRef?.props.model.doAction(Actions.selectTab(activeTabId));
}

return options.id;
}

/**
Expand Down
13 changes: 11 additions & 2 deletions editor/src/editor/layout/assets-browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -841,13 +841,14 @@ export class EditorAssetsBrowser extends Component<IEditorAssetsBrowserProps, IE
<ContextMenuSeparator />
<ContextMenuItem onClick={() => this._handleAddNodeParticleSystem()}>Node Particle System</ContextMenuItem>

<ContextMenuSeparator />
<ContextMenuItem onClick={() => this._handleAddNavmesh()}>Navmesh</ContextMenuItem>

{this.props.editor.state.enableExperimentalFeatures && (
<>
<ContextMenuSeparator />
<ContextMenuItem onClick={() => this._handleAddCinematic()}>Cinematic</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem onClick={() => this._handleAddNavmesh()}>Navmesh</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem onClick={() => this._handleAddRagdoll()}>Ragdoll</ContextMenuItem>
</>
)}
Expand Down Expand Up @@ -895,6 +896,10 @@ export class EditorAssetsBrowser extends Component<IEditorAssetsBrowserProps, IE
case ".lwo":
case ".gltf":
case ".ms3d":
case ".ply":
case ".sog":
case ".spz":
case ".splat":
case ".babylon":
return <MeshSelectable {...props} />;

Expand Down Expand Up @@ -1474,6 +1479,10 @@ export class EditorAssetsBrowser extends Component<IEditorAssetsBrowserProps, IE
case ".gltf":
case ".babylon":
case ".fbx":
case ".ply":
case ".sog":
case ".spz":
case ".splat":
return openModelViewer(this.props.editor, item.props.absolutePath);

case ".env":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class AssetBrowserMeshItem extends AssetsBrowserItem {
this.props.onRefresh();

const scene = new Scene(this.props.editor.layout.preview.engine);
await loadImportedSceneFile(scene, file);
await loadImportedSceneFile(scene, file, this.props.editor);

const data = await SceneSerializer.SerializeAsync(scene);
await writeJSON(`${file}.babylon`, data, "utf-8");
Expand Down
38 changes: 35 additions & 3 deletions editor/src/editor/layout/assets-browser/items/navmesh-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import { NavMeshEditor } from "../../navmesh/editor";

import { AssetsBrowserItem } from "./item";

interface _IEditedNavMeshConfiguration {
tabId: string;
absolutePath: string;
}

const editedNavMeshConfigurations: _IEditedNavMeshConfiguration[] = [];

export class AssetBrowserNavmeshItem extends AssetsBrowserItem {
/**
* @override
Expand All @@ -24,11 +31,36 @@ export class AssetBrowserNavmeshItem extends AssetsBrowserItem {
* @override
*/
protected async onDoubleClick(): Promise<void> {
const existingConfiguration = editedNavMeshConfigurations.find((c) => c.absolutePath === this.props.absolutePath);
if (existingConfiguration) {
this.props.editor.layout.selectTab(existingConfiguration.tabId);
return;
}

const data = await readJSON(join(this.props.absolutePath, "config.json"));

this.props.editor.layout.addLayoutTab(<NavMeshEditor editor={this.props.editor} configuration={data} absolutePath={this.props.absolutePath} />, {
setAsActiveTab: true,
title: "NavMesh Editor",
const tabId = this.props.editor.layout.addLayoutTab(
<NavMeshEditor
editor={this.props.editor}
configuration={data}
absolutePath={this.props.absolutePath}
onClose={() => {
const index = editedNavMeshConfigurations.findIndex((c) => c.tabId === tabId);
if (index !== -1) {
editedNavMeshConfigurations.splice(index, 1);
}
}}
/>,
{
setAsActiveTab: true,
title: "NavMesh Editor",
neighborId: "inspector",
}
);

editedNavMeshConfigurations.push({
tabId,
absolutePath: this.props.absolutePath,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { showAlert } from "../../../../ui/dialog";
import { Progress } from "../../../../ui/shadcn/ui/progress";

import { isMesh } from "../../../../tools/guards/nodes";
import { getLoaderPluginOptions } from "../../../../tools/assets/loader";

import { projectConfiguration } from "../../../../project/configuration";

Expand Down Expand Up @@ -73,6 +74,7 @@ function AssetBrowserModelViewer(props: IAssetBrowserModelViewerProps) {
await AppendSceneAsync(source, scene, {
rootUrl,
onProgress: (ev) => setProgress((ev.loaded / ev.total) * 100),
pluginOptions: getLoaderPluginOptions(props.editor.path ?? ""),
});

if (props.options?.overrideMaterialAbsolutePath) {
Expand Down
Loading