Skip to content

Commit 5018d8b

Browse files
deltakoshDavid Catuhe
andauthored
Add support for DebugPane and Settings pane for Inspector v2 (#16792)
Co-authored-by: David Catuhe <[email protected]>
1 parent 67b2133 commit 5018d8b

File tree

12 files changed

+402
-28
lines changed

12 files changed

+402
-28
lines changed

packages/dev/inspector-v2/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"devDependencies": {
1919
"@dev/core": "1.0.0",
2020
"@dev/loaders": "1.0.0",
21+
"@dev/addons": "^1.0.0",
22+
"@dev/materials": "^1.0.0",
2123
"@fluentui/react-components": "^9.62.0",
2224
"@fluentui/react-icons": "^2.0.271",
2325
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.0",
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// eslint-disable-next-line import/no-internal-modules
2+
import { AccordionPane } from "../accordionPane";
3+
import { SwitchPropertyLine } from "shared-ui-components/fluent/hoc/switchPropertyLine";
4+
import { FontAsset } from "addons/msdfText/fontAsset";
5+
import { TextRenderer } from "addons/msdfText/textRenderer";
6+
import { Matrix } from "core/Maths/math.vector";
7+
import { PhysicsViewer } from "core/Debug/physicsViewer";
8+
import type { Mesh } from "core/Meshes/mesh";
9+
import type { AbstractMesh } from "core/Meshes/abstractMesh";
10+
import { UtilityLayerRenderer } from "core/Rendering/utilityLayerRenderer";
11+
import { CreateGround } from "core/Meshes/Builders/groundBuilder";
12+
import { GridMaterial } from "materials/grid/gridMaterial";
13+
import { Tools } from "core/Misc/tools";
14+
import { Color3 } from "core/Maths/math.color";
15+
import { Texture } from "core/Materials/Textures/texture";
16+
import { StandardMaterial } from "core/Materials/standardMaterial";
17+
import type { Scene } from "core/scene";
18+
import { MaterialFlags } from "core/Materials/materialFlags";
19+
import { BoundPropertyLine } from "../properties/boundPropertyLine";
20+
import { Accordion, AccordionSection } from "shared-ui-components/fluent/primitives/accordion";
21+
22+
const SwitchGrid = function (renderScene: Scene) {
23+
const scene = UtilityLayerRenderer.DefaultKeepDepthUtilityLayer.utilityLayerScene;
24+
25+
if (!renderScene.reservedDataStore.gridMesh) {
26+
const extend = renderScene.getWorldExtends();
27+
const width = (extend.max.x - extend.min.x) * 5.0;
28+
const depth = (extend.max.z - extend.min.z) * 5.0;
29+
30+
renderScene.reservedDataStore.gridMesh = CreateGround("grid", { width: 1.0, height: 1.0, subdivisions: 1 }, scene);
31+
const gridMesh = renderScene.reservedDataStore.gridMesh as AbstractMesh;
32+
if (!gridMesh.reservedDataStore) {
33+
gridMesh.reservedDataStore = {};
34+
}
35+
gridMesh.scaling.x = Math.max(width, depth);
36+
gridMesh.scaling.z = gridMesh.scaling.x;
37+
gridMesh.reservedDataStore.isInspectorGrid = true;
38+
gridMesh.isPickable = false;
39+
40+
const groundMaterial = new GridMaterial("GridMaterial", scene);
41+
groundMaterial.majorUnitFrequency = 10;
42+
groundMaterial.minorUnitVisibility = 0.3;
43+
groundMaterial.gridRatio = 0.01;
44+
groundMaterial.backFaceCulling = false;
45+
groundMaterial.mainColor = new Color3(1, 1, 1);
46+
groundMaterial.lineColor = new Color3(1.0, 1.0, 1.0);
47+
groundMaterial.opacity = 0.8;
48+
groundMaterial.zOffset = 1.0;
49+
const textureUrl = Tools.GetAssetUrl("https://assets.babylonjs.com/core/environments/backgroundGround.png");
50+
groundMaterial.opacityTexture = new Texture(textureUrl, scene);
51+
52+
gridMesh.material = groundMaterial;
53+
return;
54+
}
55+
const gridMesh = renderScene.reservedDataStore.gridMesh as AbstractMesh;
56+
gridMesh.dispose(true, true);
57+
renderScene.reservedDataStore.gridMesh = null;
58+
};
59+
60+
const SwitchPhysicsViewers = function (scene: Scene) {
61+
if (!scene.reservedDataStore.physicsViewer) {
62+
const physicsViewer = new PhysicsViewer(scene);
63+
scene.reservedDataStore.physicsViewer = physicsViewer;
64+
65+
for (const mesh of scene.meshes) {
66+
if (mesh.physicsImpostor) {
67+
const debugMesh = physicsViewer.showImpostor(mesh.physicsImpostor, mesh as Mesh);
68+
69+
if (debugMesh) {
70+
debugMesh.reservedDataStore = { hidden: true };
71+
debugMesh.material!.reservedDataStore = { hidden: true };
72+
}
73+
} else if (mesh.physicsBody) {
74+
const debugMesh = physicsViewer.showBody(mesh.physicsBody);
75+
76+
if (debugMesh) {
77+
debugMesh.reservedDataStore = { hidden: true };
78+
debugMesh.material!.reservedDataStore = { hidden: true };
79+
}
80+
}
81+
}
82+
83+
for (const transformNode of scene.transformNodes) {
84+
if (transformNode.physicsBody) {
85+
const debugMesh = physicsViewer.showBody(transformNode.physicsBody);
86+
87+
if (debugMesh) {
88+
debugMesh.reservedDataStore = { hidden: true };
89+
debugMesh.material!.reservedDataStore = { hidden: true };
90+
}
91+
}
92+
}
93+
} else {
94+
scene.reservedDataStore.physicsViewer.dispose();
95+
scene.reservedDataStore.physicsViewer = null;
96+
}
97+
};
98+
99+
const SwitchNameViewerAsync = async function (scene: Scene) {
100+
if (!scene.reservedDataStore.textRenderersHook) {
101+
scene.reservedDataStore.textRenderers = [];
102+
if (!scene.reservedDataStore.fontAsset) {
103+
const sdfFontDefinition = await (await fetch("https://assets.babylonjs.com/fonts/roboto-regular.json")).text();
104+
// eslint-disable-next-line require-atomic-updates
105+
scene.reservedDataStore.fontAsset = new FontAsset(sdfFontDefinition, "https://assets.babylonjs.com/fonts/roboto-regular.png");
106+
}
107+
108+
const textRendererPromises = scene.meshes.map(async (mesh) => {
109+
const textRenderer = await TextRenderer.CreateTextRendererAsync(scene.reservedDataStore.fontAsset!, scene.getEngine());
110+
111+
textRenderer.addParagraph(mesh.name);
112+
textRenderer.isBillboard = true;
113+
textRenderer.isBillboardScreenProjected = true;
114+
textRenderer.parent = mesh;
115+
textRenderer.ignoreDepthBuffer = true;
116+
textRenderer.transformMatrix = Matrix.Scaling(0.02, 0.02, 0.02);
117+
118+
scene.reservedDataStore.textRenderers.push(textRenderer);
119+
});
120+
121+
await Promise.all(textRendererPromises);
122+
123+
scene.reservedDataStore.textRenderersHook = scene.onAfterRenderObservable.add(() => {
124+
for (const textRenderer of scene.reservedDataStore.textRenderers) {
125+
if (!textRenderer.parent.isVisible || !textRenderer.parent.isEnabled()) {
126+
continue;
127+
}
128+
textRenderer.render(scene.getViewMatrix(), scene.getProjectionMatrix());
129+
}
130+
});
131+
} else {
132+
scene.onAfterRenderObservable.remove(scene.reservedDataStore.textRenderersHook);
133+
for (const textRenderer of scene.reservedDataStore.textRenderers) {
134+
textRenderer.dispose();
135+
}
136+
scene.reservedDataStore.textRenderersHook = null;
137+
scene.reservedDataStore.textRenderers = null;
138+
}
139+
};
140+
141+
export const DebugPane: typeof AccordionPane<Scene> = (props) => {
142+
const scene = props.context;
143+
144+
if (!scene.reservedDataStore) {
145+
scene.reservedDataStore = {};
146+
}
147+
148+
// Making sure we clean up when the scene is disposed
149+
scene.onDisposeObservable.addOnce(() => {
150+
if (scene.reservedDataStore.physicsViewer) {
151+
SwitchPhysicsViewers(scene);
152+
}
153+
154+
if (scene.reservedDataStore.textRenderersHook) {
155+
void SwitchNameViewerAsync(scene);
156+
scene.reservedDataStore.fontAsset?.dispose();
157+
}
158+
159+
if (scene.reservedDataStore.gridMesh) {
160+
SwitchGrid(scene);
161+
}
162+
});
163+
164+
return (
165+
<>
166+
<Accordion>
167+
<AccordionSection title="Helpers">
168+
<SwitchPropertyLine label="Grid" description="Display a ground grid." value={!!scene.reservedDataStore.gridMesh} onChange={() => SwitchGrid(scene)} />
169+
<SwitchPropertyLine
170+
label="Physics"
171+
description="Display physic debug info."
172+
value={!!scene.reservedDataStore.physicsViewer}
173+
onChange={() => SwitchPhysicsViewers(scene)}
174+
/>
175+
<SwitchPropertyLine
176+
label="Names"
177+
description="Display mesh names."
178+
value={!!scene.reservedDataStore.textRenderersHook}
179+
onChange={() => void SwitchNameViewerAsync(scene)}
180+
/>
181+
</AccordionSection>
182+
<AccordionSection title="Core texture channels">
183+
<BoundPropertyLine component={SwitchPropertyLine} key="Diffuse" label="Diffuse" target={StandardMaterial} propertyKey="DiffuseTextureEnabled" />
184+
<BoundPropertyLine component={SwitchPropertyLine} key="Ambient" label="Ambient" target={StandardMaterial} propertyKey="AmbientTextureEnabled" />
185+
<BoundPropertyLine component={SwitchPropertyLine} key="Specular" label="Specular" target={StandardMaterial} propertyKey="SpecularTextureEnabled" />
186+
<BoundPropertyLine component={SwitchPropertyLine} key="Emissive" label="Emissive" target={StandardMaterial} propertyKey="EmissiveTextureEnabled" />
187+
<BoundPropertyLine component={SwitchPropertyLine} key="Bump" label="Bump" target={StandardMaterial} propertyKey="BumpTextureEnabled" />
188+
<BoundPropertyLine component={SwitchPropertyLine} key="Opacity" label="Opacity" target={StandardMaterial} propertyKey="OpacityTextureEnabled" />
189+
<BoundPropertyLine component={SwitchPropertyLine} key="Reflection" label="Reflection" target={StandardMaterial} propertyKey="ReflectionTextureEnabled" />
190+
<BoundPropertyLine component={SwitchPropertyLine} key="ColorGrading" label="Color Grading" target={StandardMaterial} propertyKey="ColorGradingTextureEnabled" />
191+
<BoundPropertyLine component={SwitchPropertyLine} key="Lightmap" label="Lightmap" target={StandardMaterial} propertyKey="LightmapTextureEnabled" />
192+
<BoundPropertyLine component={SwitchPropertyLine} key="Fresnel" label="Fresnel" target={StandardMaterial} propertyKey="FresnelEnabled" />
193+
<BoundPropertyLine component={SwitchPropertyLine} key="Detail" label="Detail" target={MaterialFlags} propertyKey="DetailTextureEnabled" />
194+
<BoundPropertyLine component={SwitchPropertyLine} key="Decal" label="Decal" target={MaterialFlags} propertyKey="DecalMapEnabled" />
195+
</AccordionSection>
196+
<AccordionSection title="Features">
197+
<BoundPropertyLine component={SwitchPropertyLine} key="Animations" label="Animations" target={scene} propertyKey="animationsEnabled" />
198+
<BoundPropertyLine component={SwitchPropertyLine} key="Physics" label="Physics" target={scene} propertyKey="physicsEnabled" />
199+
<BoundPropertyLine component={SwitchPropertyLine} key="Collisions" label="Collisions" target={scene} propertyKey="collisionsEnabled" />
200+
<BoundPropertyLine component={SwitchPropertyLine} key="Fog" label="Fog" target={scene} propertyKey="fogEnabled" />
201+
<BoundPropertyLine component={SwitchPropertyLine} key="Lens flares" label="Lens flares" target={scene} propertyKey="lensFlaresEnabled" />
202+
<BoundPropertyLine component={SwitchPropertyLine} key="Lights" label="Lights" target={scene} propertyKey="lightsEnabled" />
203+
<BoundPropertyLine component={SwitchPropertyLine} key="Particles" label="Particles" target={scene} propertyKey="particlesEnabled" />
204+
<BoundPropertyLine component={SwitchPropertyLine} key="Post-processes" label="Post-processes" target={scene} propertyKey="postProcessesEnabled" />
205+
<BoundPropertyLine component={SwitchPropertyLine} key="Probes" label="Probes" target={scene} propertyKey="probesEnabled" />
206+
<BoundPropertyLine component={SwitchPropertyLine} key="Textures" label="Textures" target={scene} propertyKey="texturesEnabled" />
207+
<BoundPropertyLine
208+
component={SwitchPropertyLine}
209+
key="Procedural textures"
210+
label="Procedural textures"
211+
target={scene}
212+
propertyKey="proceduralTexturesEnabled"
213+
/>
214+
<BoundPropertyLine component={SwitchPropertyLine} key="Render targets" label="Render targets" target={scene} propertyKey="renderTargetsEnabled" />
215+
<BoundPropertyLine component={SwitchPropertyLine} key="Shadows" label="Shadows" target={scene} propertyKey="shadowsEnabled" />
216+
<BoundPropertyLine component={SwitchPropertyLine} key="Skeletons" label="Skeletons" target={scene} propertyKey="skeletonsEnabled" />
217+
<BoundPropertyLine component={SwitchPropertyLine} key="Sprites" label="Sprites" target={scene} propertyKey="spritesEnabled" />
218+
</AccordionSection>
219+
</Accordion>
220+
<AccordionPane {...props} />
221+
</>
222+
);
223+
};

packages/dev/inspector-v2/src/components/properties/transformNodeTransformProperties.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Vector3PropertyLine } from "shared-ui-components/fluent/hoc/vectorPrope
77

88
import { useInterceptObservable } from "../../hooks/instrumentationHooks";
99
import { useObservableState } from "../../hooks/observableHooks";
10+
import type { ISettingsContext } from "../../services/settingsContext";
1011

1112
type Vector3Keys<T> = { [P in keyof T]: T[P] extends Vector3 ? P : never }[keyof T];
1213

@@ -21,17 +22,19 @@ function useVector3Property<T extends object, K extends Vector3Keys<T>>(target:
2122
return position;
2223
}
2324

24-
export const TransformNodeTransformProperties: FunctionComponent<{ node: TransformNode }> = (props) => {
25-
const { node } = props;
25+
export const TransformNodeTransformProperties: FunctionComponent<{ node: TransformNode; settings: ISettingsContext }> = (props) => {
26+
const { node, settings } = props;
2627

2728
const position = useVector3Property(node, "position");
2829
const rotation = useVector3Property(node, "rotation");
2930
const scaling = useVector3Property(node, "scaling");
3031

32+
const useDegrees = useObservableState(() => settings.useDegrees, settings.settingsChangedObservable);
33+
3134
return (
3235
<>
3336
<Vector3PropertyLine key="PositionTransform" label="Position" value={position} onChange={(val) => (node.position = val)} />
34-
<Vector3PropertyLine key="RotationTransform" label="Rotation" value={rotation} onChange={(val) => (node.scaling = val)} />
37+
<Vector3PropertyLine key="RotationTransform" label="Rotation" value={rotation} onChange={(val) => (node.rotation = val)} useDegrees={useDegrees} />
3538
<Vector3PropertyLine key="ScalingTransform" label="Scaling" value={scaling} onChange={(val) => (node.scaling = val)} />
3639
</>
3740
);

packages/dev/inspector-v2/src/services/panes/debugService.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,34 @@ import type { IShellService } from "../shellService";
44
import { BugRegular } from "@fluentui/react-icons";
55

66
import { ShellServiceIdentity } from "../shellService";
7+
import { DebugPane } from "../../components/debug/debugPane";
8+
import { useObservableCollection, useObservableState, useOrderedObservableCollection } from "../../hooks/observableHooks";
9+
import { SceneContextIdentity, type ISceneContext } from "../sceneContext";
10+
import { ObservableCollection } from "../../misc/observableCollection";
11+
import { type AccordionSection, type AccordionSectionContent } from "../../components/accordionPane";
12+
import type { Scene } from "core/scene";
713

8-
export const DebugServiceDefinition: ServiceDefinition<[], [IShellService]> = {
14+
export const HelpersServiceIdentity = Symbol("Helpers");
15+
export const CoreTextureSectionIdentity = Symbol("CoreTextureChannels");
16+
17+
export const DebugServiceDefinition: ServiceDefinition<[], [IShellService, ISceneContext]> = {
918
friendlyName: "Debug",
10-
consumes: [ShellServiceIdentity],
11-
factory: (shellService) => {
19+
consumes: [ShellServiceIdentity, SceneContextIdentity],
20+
factory: (shellService, sceneContext) => {
21+
const sectionsCollection = new ObservableCollection<AccordionSection>();
22+
const sectionContentCollection = new ObservableCollection<AccordionSectionContent<Scene>>();
23+
1224
const registration = shellService.addSidePane({
1325
key: "Debug",
1426
title: "Debug",
1527
icon: BugRegular,
1628
horizontalLocation: "right",
1729
suppressTeachingMoment: true,
1830
content: () => {
19-
return <>Not yet implemented.</>;
31+
const sections = useOrderedObservableCollection(sectionsCollection);
32+
const sectionContent = useObservableCollection(sectionContentCollection);
33+
const scene = useObservableState(() => sceneContext.currentScene, sceneContext.currentSceneObservable);
34+
return <>{scene && <DebugPane sections={sections} sectionContent={sectionContent} context={scene} />}</>;
2035
},
2136
});
2237

packages/dev/inspector-v2/src/services/panes/properties/transformNodePropertiesService.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import { TransformNode } from "core/Meshes/transformNode";
55

66
import { PropertiesServiceIdentity } from "./propertiesService";
77
import { TransformNodeTransformProperties } from "../../../components/properties/transformNodeTransformProperties";
8+
import { SettingsContextIdentity, type ISettingsContext } from "../../../services/settingsContext";
89

910
export const TransformsPropertiesSectionIdentity = Symbol("Transforms");
1011

11-
export const TransformNodePropertiesServiceDefinition: ServiceDefinition<[], [IPropertiesService]> = {
12+
export const TransformNodePropertiesServiceDefinition: ServiceDefinition<[], [IPropertiesService, ISettingsContext]> = {
1213
friendlyName: "Transform Node Properties",
13-
consumes: [PropertiesServiceIdentity],
14-
factory: (propertiesService) => {
14+
consumes: [PropertiesServiceIdentity, SettingsContextIdentity],
15+
factory: (propertiesService, settingsContent) => {
1516
const transformsSectionRegistration = propertiesService.addSection({
1617
order: 1,
1718
identity: TransformsPropertiesSectionIdentity,
@@ -25,7 +26,7 @@ export const TransformNodePropertiesServiceDefinition: ServiceDefinition<[], [IP
2526
{
2627
section: TransformsPropertiesSectionIdentity,
2728
order: 0,
28-
component: ({ context }) => <TransformNodeTransformProperties node={context} />,
29+
component: ({ context }) => <TransformNodeTransformProperties node={context} settings={settingsContent} />,
2930
},
3031
],
3132
});

0 commit comments

Comments
 (0)