-
Notifications
You must be signed in to change notification settings - Fork 294
Add Gaussian Splatting support (.ply / .splat / .spz) #892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
83844f2
4c79b07
028b7c5
5751bad
f1832b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,8 @@ import { Scene, ShadowGenerator, CascadedShadowGenerator, RenderTargetTexture } | |
|
|
||
| import { Editor } from "../../../editor/main"; | ||
|
|
||
| import { isGaussianSplattingMesh } from "../../../tools/guards/nodes"; | ||
|
|
||
| import { ISceneLoaderPluginOptions } from "../scene"; | ||
|
|
||
| export async function loadShadowGenerators(editor: Editor, shadowGeneratorFiles: string[], scene: Scene, options: ISceneLoaderPluginOptions) { | ||
|
|
@@ -33,6 +35,12 @@ export async function loadShadowGenerators(editor: Editor, shadowGeneratorFiles: | |
| const shadowMap = shadowGenerator.getShadowMap(); | ||
| if (shadowMap) { | ||
| shadowMap.refreshRate = data.refreshRate ?? RenderTargetTexture.REFRESHRATE_RENDER_ONEVERYFRAME; | ||
|
|
||
| // Gaussian splatting meshes can't be rendered into shadow maps (their thin-instance splat | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found this example that uses splats as shadow casters: https://playground.babylonjs.com/?inspectorv2=true#OE54M5#15. Can you double-check? |
||
| // layout breaks the depth pass), so drop any that an older project persisted in the render list. | ||
| if (shadowMap.renderList) { | ||
| shadowMap.renderList = shadowMap.renderList.filter((mesh) => !isGaussianSplattingMesh(mesh)); | ||
| } | ||
| } | ||
| } catch (e) { | ||
| editor.layout.console.error(`Failed to load shadow generator file "${file}": ${e.message}`); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ import { isSpriteManagerNode, isSpriteMapNode } from "../../tools/guards/sprites | |
| import { serializePhysicsAggregate } from "../../tools/physics/serialization/aggregate"; | ||
| import { isAnimationGroupFromSceneLink, isFromSceneLink } from "../../tools/scene/scene-link"; | ||
| import { isGPUParticleSystem, isNodeParticleSystemSetMesh, isParticleSystem } from "../../tools/guards/particles"; | ||
| import { isAnyTransformNode, isClusteredLightContainer, isCollisionMesh, isEditorCamera, isMesh, isTransformNode } from "../../tools/guards/nodes"; | ||
| import { isAnyTransformNode, isClusteredLightContainer, isCollisionMesh, isEditorCamera, isGaussianSplattingMesh, isMesh, isTransformNode } from "../../tools/guards/nodes"; | ||
|
|
||
| import { taaPipelineCameraConfigurations } from "../../editor/rendering/taa"; | ||
| import { vlsPostProcessCameraConfigurations } from "../../editor/rendering/vls"; | ||
|
|
@@ -73,7 +73,7 @@ export async function saveScene(editor: Editor, projectPath: string, scenePath: | |
|
|
||
| const scene = editor.layout.preview.scene; | ||
| const meshesToSave = scene.meshes.filter((mesh) => { | ||
| if ((!isMesh(mesh) && !isCollisionMesh(mesh)) || mesh._masterMesh || isFromSceneLink(mesh) || !isNodeVisibleInGraph(mesh)) { | ||
| if ((!isMesh(mesh) && !isCollisionMesh(mesh) && !isGaussianSplattingMesh(mesh)) || mesh._masterMesh || isFromSceneLink(mesh) || !isNodeVisibleInGraph(mesh)) { | ||
| return false; | ||
| } | ||
|
|
||
|
|
@@ -99,7 +99,7 @@ export async function saveScene(editor: Editor, projectPath: string, scenePath: | |
| // Write geometries and meshes | ||
| await Promise.all( | ||
| meshesToSave.map(async (mesh) => { | ||
| if ((!isMesh(mesh) && !isCollisionMesh(mesh)) || mesh._masterMesh || isFromSceneLink(mesh) || !isNodeVisibleInGraph(mesh)) { | ||
| if ((!isMesh(mesh) && !isCollisionMesh(mesh) && !isGaussianSplattingMesh(mesh)) || mesh._masterMesh || isFromSceneLink(mesh) || !isNodeVisibleInGraph(mesh)) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -131,6 +131,21 @@ export async function saveScene(editor: Editor, projectPath: string, scenePath: | |
| data.metadata = meshToSerialize.metadata; | ||
| data.basePoseMatrix = meshToSerialize.getPoseMatrix().asArray(); | ||
|
|
||
| // Gaussian splatting meshes embed their splat data inline in the serialized JSON and recreate | ||
| // their own material and quad geometry when parsed (the loader skips importing geometry for | ||
| // them). So the serialized material and geometry references are unused: dropping them avoids | ||
| // writing/delay-loading a useless `.babylonbinarymeshdata` file for the splatting quad. | ||
| if (isGaussianSplattingMesh(meshToSerialize)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In declas.ts I get this error: TypeError: Cannot read properties of null (reading 'scripts') |
||
| delete data.materials; | ||
| delete data.geometries; | ||
|
|
||
| data.meshes?.forEach((m) => { | ||
| delete m.geometryId; | ||
| delete m.geometryUniqueId; | ||
| delete m.delayLoadingFile; | ||
| }); | ||
| } | ||
|
|
||
| // Handle case where the mesh is a collision mesh | ||
| if (isCollisionMesh(meshToSerialize)) { | ||
| data.isCollisionMesh = true; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| export const assetsImageExtensions = [".png", ".webp", ".jpg", ".bmp", ".jpeg"]; | ||
| export const assetsAudioExtensions = [".mp3", ".wav", ".wave", ".ogg"]; | ||
| export const assetsVideoExtensions = [".mp4", ".webm", ".ogg"]; | ||
| export const assetsModelExtensions = [".gltf", ".glb", ".obj", ".babylon", ".stl", ".3ds", ".fbx"]; | ||
| export const assetsGaussianSplattingExtensions = [".ply", ".splat", ".spz"]; | ||
| export const assetsModelExtensions = [".gltf", ".glb", ".obj", ".babylon", ".stl", ".3ds", ".fbx", ...assetsGaussianSplattingExtensions]; | ||
|
|
||
| export const assetsAllSupportedExtensions = [...assetsImageExtensions, ...assetsAudioExtensions, ...assetsVideoExtensions, ...assetsModelExtensions]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,6 +66,13 @@ export function setNodeSerializable(node: Node, value: boolean): void { | |
| * @param node defines the reference to the node to check. | ||
| */ | ||
| export function isNodeVisibleInGraph(node: Node): boolean { | ||
| // Internal nodes flagged hidden through Babylon's reservedDataStore convention (e.g. the per-camera | ||
| // proxy meshes a GaussianSplattingMesh creates at render time) are implementation details and must | ||
| // never show in the graph nor be saved. | ||
| if (node.reservedDataStore?.hidden) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is reservedDataStore?.hidden used especially by splat meshes? I did not need this until now |
||
| return false; | ||
| } | ||
|
|
||
| const value = ensureNodeMetadata(node).notVisibleInGraph; | ||
| return value === undefined ? true : !value; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| // Registers the GaussianSplattingMesh parser (Mesh._GaussianSplattingMeshParser) so the .babylon scene | ||
| // loader can reconstruct Gaussian splatting meshes serialized inline by the editor. | ||
| import "@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This must be performed by the devloper in his project so all projects don't include gaussian splatting in their builds |
||
|
|
||
| import { Scene } from "@babylonjs/core/scene"; | ||
| import { Vector3 } from "@babylonjs/core/Maths/math.vector"; | ||
| import { Constants } from "@babylonjs/core/Engines/constants"; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like .sog is supported too as proved in this example: https://playground.babylonjs.com/?inspectorv2=true#QA2662#12