Skip to content

Commit 1e15a97

Browse files
authored
Inspector v2: Start using new Fluent components from shared-ui-components (#16771)
- Minor changes to webpack and tsconfig for inspector v2 to consume shared-ui-components - Use new Fluent common components in the property pane - Make transform related properties applicable to TransformNode (not specific to Mesh) - Don't show mesh properties for meshes without vertices (same as inspector v1) - Explicitly set the description prop on the individual sliders to undefined so we don't pass through the top level description (otherwise, the top level description gets shown for each x/y/z line) ![image](https://github.com/user-attachments/assets/11771390-6e64-4e8e-a1c9-053dc4a2cf06)
1 parent 2f085d9 commit 1e15a97

File tree

13 files changed

+158
-56
lines changed

13 files changed

+158
-56
lines changed
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
import type { FunctionComponent } from "react";
22

3+
import { TextPropertyLine } from "shared-ui-components/fluent/hoc/textPropertyLine";
4+
35
type CommonEntity = {
46
id?: number;
57
name?: string;
68
uniqueId?: number;
79
getClassName?: () => string;
810
};
911

10-
export const CommonGeneralProperties: FunctionComponent<{ context: CommonEntity }> = ({ context: commonEntity }) => {
12+
export const CommonGeneralProperties: FunctionComponent<{ commonEntity: CommonEntity }> = (props) => {
13+
const { commonEntity } = props;
14+
1115
return (
12-
// TODO: Use the new Fluent property line shared components.
1316
<>
14-
{commonEntity.id !== undefined && <div key="EntityId">ID: {commonEntity.id}</div>}
15-
{commonEntity.name !== undefined && <div key="EntityName">Name: {commonEntity.name}</div>}
16-
{commonEntity.uniqueId !== undefined && <div key="EntityUniqueId">Unique ID: {commonEntity.uniqueId}</div>}
17-
{commonEntity.getClassName !== undefined && <div key="EntityClassName">Class: {commonEntity.getClassName()}</div>}
17+
{commonEntity.id !== undefined && <TextPropertyLine key="EntityId" label="ID" description="The id of the node." value={commonEntity.id.toString()} />}
18+
{commonEntity.name !== undefined && <TextPropertyLine key="EntityName" label="Name" description="The name of the node." value={commonEntity.name} />}
19+
{commonEntity.uniqueId !== undefined && (
20+
<TextPropertyLine key="EntityUniqueId" label="Unique ID" description="The unique id of the node." value={commonEntity.uniqueId.toString()} />
21+
)}
22+
{commonEntity.getClassName !== undefined && (
23+
<TextPropertyLine key="EntityClassName" label="Class" description="The class of the node." value={commonEntity.getClassName()} />
24+
)}
1825
</>
1926
);
2027
};

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,34 @@ import type { AbstractMesh } from "core/index";
33

44
import type { FunctionComponent } from "react";
55

6+
import { SwitchPropertyLine } from "shared-ui-components/fluent/hoc/switchPropertyLine";
7+
68
import { useInterceptObservable } from "../../hooks/instrumentationHooks";
79
import { useObservableState } from "../../hooks/observableHooks";
810

9-
export const MeshAdvancedProperties: FunctionComponent<{ context: AbstractMesh }> = ({ context: mesh }) => {
11+
export const MeshAdvancedProperties: FunctionComponent<{ mesh: AbstractMesh }> = (props) => {
12+
const { mesh } = props;
13+
1014
// There is no observable for computeBonesUsingShaders, so we use an interceptor to listen for changes.
1115
const computeBonesUsingShaders = useObservableState(() => mesh.computeBonesUsingShaders, useInterceptObservable("property", mesh, "computeBonesUsingShaders"));
16+
const checkCollisions = useObservableState(() => mesh.checkCollisions, useInterceptObservable("property", mesh, "checkCollisions"));
1217

1318
return (
14-
// TODO: Use the new Fluent property line shared components.
1519
<>
16-
<div key="ComputeBonesUsingShaders">Compute bones using shaders: {computeBonesUsingShaders.toString()}</div>
20+
{mesh.useBones && (
21+
<SwitchPropertyLine
22+
label="Compute bones using shaders"
23+
description="Whether to compute bones using shaders."
24+
checked={computeBonesUsingShaders}
25+
onChange={(_, data) => (mesh.computeBonesUsingShaders = data.checked)}
26+
/>
27+
)}
28+
<SwitchPropertyLine
29+
label="Check collisions"
30+
description="Whether to check for collisions."
31+
checked={checkCollisions}
32+
onChange={(_, data) => (mesh.checkCollisions = data.checked)}
33+
/>
1734
</>
1835
);
1936
};

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,39 @@ import type { AbstractMesh } from "core/index";
33

44
import type { FunctionComponent } from "react";
55

6+
import type { ISelectionService } from "../../services/selectionService";
7+
8+
import { SwitchPropertyLine } from "shared-ui-components/fluent/hoc/switchPropertyLine";
9+
import { LinkPropertyLine } from "shared-ui-components/fluent/hoc/linkPropertyLine";
10+
611
import { useObservableState } from "../../hooks/observableHooks";
712

8-
export const MeshGeneralProperties: FunctionComponent<{ context: AbstractMesh }> = ({ context: mesh }) => {
13+
export const MeshGeneralProperties: FunctionComponent<{ mesh: AbstractMesh; selectionService: ISelectionService }> = (props) => {
14+
const { mesh, selectionService } = props;
15+
16+
const isEnabled = useObservableState(() => mesh.isEnabled(false), mesh.onEnabledStateChangedObservable);
17+
918
// Use the observable to keep keep state up-to-date and re-render the component when it changes.
1019
const material = useObservableState(() => mesh.material, mesh.onMaterialChangedObservable);
1120

1221
return (
13-
// TODO: Use the new Fluent property line shared components.
1422
<>
15-
<div key="MeshIsEnabled">Is enabled: {mesh.isEnabled(false).toString()}</div>
16-
{material && (!material.reservedDataStore || !material.reservedDataStore.hidden) && <div key="Material">Material: {material.name}</div>}
23+
<SwitchPropertyLine
24+
key="MeshIsEnabled"
25+
label="Is enabled"
26+
description="Whether the mesh is enabled or not."
27+
checked={isEnabled}
28+
onChange={(_, data) => mesh.setEnabled(data.checked)}
29+
/>
30+
{material && (!material.reservedDataStore || !material.reservedDataStore.hidden) && (
31+
<LinkPropertyLine
32+
key="Material"
33+
label="Material"
34+
description={`The material used by the mesh.`}
35+
value={material.name}
36+
onLink={() => (selectionService.selectedEntity = material)}
37+
/>
38+
)}
1739
</>
1840
);
1941
};

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

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// eslint-disable-next-line import/no-internal-modules
2+
import type { TransformNode, Vector3 } from "core/index";
3+
4+
import type { FunctionComponent } from "react";
5+
6+
import { VectorPropertyLine } from "shared-ui-components/fluent/hoc/vectorPropertyLine";
7+
8+
import { useInterceptObservable } from "../../hooks/instrumentationHooks";
9+
import { useObservableState } from "../../hooks/observableHooks";
10+
11+
type Vector3Keys<T> = { [P in keyof T]: T[P] extends Vector3 ? P : never }[keyof T];
12+
13+
// This helper hook gets the value of a Vector3 property from a target object and causes the component
14+
// to re-render when the property changes or when the x/y/z components of the Vector3 change.
15+
// eslint-disable-next-line @typescript-eslint/naming-convention
16+
function useVector3Property<T extends object, K extends Vector3Keys<T>>(target: T, propertyKey: K): Vector3 {
17+
const position = useObservableState(() => target[propertyKey] as Vector3, useInterceptObservable("property", target, propertyKey));
18+
useObservableState(() => position.x, useInterceptObservable("property", position, "x"));
19+
useObservableState(() => position.y, useInterceptObservable("property", position, "y"));
20+
useObservableState(() => position.z, useInterceptObservable("property", position, "z"));
21+
return position;
22+
}
23+
24+
export const TransformNodeTransformProperties: FunctionComponent<{ node: TransformNode }> = (props) => {
25+
const { node: transformNode } = props;
26+
27+
const position = useVector3Property(transformNode, "position");
28+
const rotation = useVector3Property(transformNode, "rotation");
29+
const scaling = useVector3Property(transformNode, "scaling");
30+
31+
return (
32+
<>
33+
<VectorPropertyLine key="PositionTransform" label="Position" description="The position of the transform node." vector={position} />
34+
<VectorPropertyLine key="RotationTransform" label="Rotation" description="The rotation of the transform node." vector={rotation} />
35+
<VectorPropertyLine key="ScalingTransform" label="Scaling" description="The scaling of the transform node." vector={scaling} />
36+
</>
37+
);
38+
};

packages/dev/inspector-v2/src/inspector.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { SceneExplorerServiceDefinition } from "./services/panes/scene/sceneExpl
1818
import { TextureHierarchyServiceDefinition } from "./services/panes/scene/texturesExplorerService";
1919
import { CommonPropertiesServiceDefinition } from "./services/panes/properties/commonPropertiesService";
2020
import { MeshPropertiesServiceDefinition } from "./services/panes/properties/meshPropertiesService";
21+
import { TransformNodePropertiesServiceDefinition } from "./services/panes/properties/transformNodePropertiesService";
2122
import { PropertiesServiceDefinition } from "./services/panes/properties/propertiesService";
2223
import { SettingsServiceDefinition } from "./services/panes/settingsService";
2324
import { StatsServiceDefinition } from "./services/panes/statsService";
@@ -173,6 +174,7 @@ function _ShowInspector(scene: Nullable<Scene>, options: Partial<IInspectorOptio
173174
PropertiesServiceDefinition,
174175
CommonPropertiesServiceDefinition,
175176
MeshPropertiesServiceDefinition,
177+
TransformNodePropertiesServiceDefinition,
176178

177179
// Debug pane tab and related services.
178180
DebugServiceDefinition,

packages/dev/inspector-v2/src/services/panes/properties/commonPropertiesService.ts renamed to packages/dev/inspector-v2/src/services/panes/properties/commonPropertiesService.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const CommonPropertiesServiceDefinition: ServiceDefinition<[], [IProperti
3333
{
3434
section: GeneralPropertiesSectionIdentity,
3535
order: 0,
36-
component: CommonGeneralProperties,
36+
component: ({ context }) => <CommonGeneralProperties commonEntity={context} />,
3737
},
3838
],
3939
});

packages/dev/inspector-v2/src/services/panes/properties/meshPropertiesService.ts renamed to packages/dev/inspector-v2/src/services/panes/properties/meshPropertiesService.tsx

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,50 @@
11
import type { ServiceDefinition } from "../../../modularity/serviceDefinition";
22
import type { IPropertiesService } from "./propertiesService";
3+
import type { ISelectionService } from "../../selectionService";
34

45
import { AbstractMesh } from "core/Meshes/abstractMesh";
56

67
import { GeneralPropertiesSectionIdentity } from "./commonPropertiesService";
78
import { PropertiesServiceIdentity } from "./propertiesService";
9+
import { SelectionServiceIdentity } from "../../selectionService";
810
import { MeshAdvancedProperties } from "../../../components/properties/meshAdvancedProperties";
911
import { MeshGeneralProperties } from "../../../components/properties/meshGeneralProperties";
10-
import { MeshTransformProperties } from "../../../components/properties/meshTransformProperties";
1112

12-
export const TransformsPropertiesSectionIdentity = Symbol("Transforms");
1313
export const AdvancedPropertiesSectionIdentity = Symbol("Advanced");
1414

15-
export const MeshPropertiesServiceDefinition: ServiceDefinition<[], [IPropertiesService]> = {
15+
export const MeshPropertiesServiceDefinition: ServiceDefinition<[], [IPropertiesService, ISelectionService]> = {
1616
friendlyName: "Mesh Properties",
17-
consumes: [PropertiesServiceIdentity],
18-
factory: (propertiesService) => {
19-
const transformsSectionRegistration = propertiesService.addSection({
20-
order: 1,
21-
identity: TransformsPropertiesSectionIdentity,
22-
});
23-
17+
consumes: [PropertiesServiceIdentity, SelectionServiceIdentity],
18+
factory: (propertiesService, selectionService) => {
2419
const advancedSectionRegistration = propertiesService.addSection({
2520
order: 2,
2621
identity: AdvancedPropertiesSectionIdentity,
2722
});
2823

2924
const contentRegistration = propertiesService.addSectionContent({
3025
key: "Mesh Properties",
31-
predicate: (entity: unknown) => entity instanceof AbstractMesh,
26+
// Meshes without vertices are effectively TransformNodes, so don't add mesh properties for them.
27+
predicate: (entity: unknown): entity is AbstractMesh => entity instanceof AbstractMesh && entity.getTotalVertices() > 0,
3228
content: [
3329
// "GENERAL" section.
3430
{
3531
section: GeneralPropertiesSectionIdentity,
3632
order: 1,
37-
component: MeshGeneralProperties,
38-
},
39-
40-
// "TRANSFORMS" section.
41-
{
42-
section: TransformsPropertiesSectionIdentity,
43-
order: 0,
44-
component: MeshTransformProperties,
33+
component: ({ context }) => <MeshGeneralProperties mesh={context} selectionService={selectionService} />,
4534
},
4635

4736
// "ADVANCED" section.
4837
{
4938
section: AdvancedPropertiesSectionIdentity,
5039
order: 0,
51-
component: MeshAdvancedProperties,
40+
component: ({ context }) => <MeshAdvancedProperties mesh={context} />,
5241
},
5342
],
5443
});
5544

5645
return {
5746
dispose: () => {
5847
contentRegistration.dispose();
59-
transformsSectionRegistration.dispose();
6048
advancedSectionRegistration.dispose();
6149
},
6250
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { ServiceDefinition } from "../../../modularity/serviceDefinition";
2+
import type { IPropertiesService } from "./propertiesService";
3+
4+
import { TransformNode } from "core/Meshes/transformNode";
5+
6+
import { PropertiesServiceIdentity } from "./propertiesService";
7+
import { TransformNodeTransformProperties } from "../../../components/properties/transformNodeTransformProperties";
8+
9+
export const TransformsPropertiesSectionIdentity = Symbol("Transforms");
10+
11+
export const TransformNodePropertiesServiceDefinition: ServiceDefinition<[], [IPropertiesService]> = {
12+
friendlyName: "Transform Node Properties",
13+
consumes: [PropertiesServiceIdentity],
14+
factory: (propertiesService) => {
15+
const transformsSectionRegistration = propertiesService.addSection({
16+
order: 1,
17+
identity: TransformsPropertiesSectionIdentity,
18+
});
19+
20+
const contentRegistration = propertiesService.addSectionContent({
21+
key: "Transform Node Properties",
22+
predicate: (entity: unknown) => entity instanceof TransformNode,
23+
content: [
24+
// "TRANSFORMS" section.
25+
{
26+
section: TransformsPropertiesSectionIdentity,
27+
order: 0,
28+
component: ({ context }) => <TransformNodeTransformProperties node={context} />,
29+
},
30+
],
31+
});
32+
33+
return {
34+
dispose: () => {
35+
contentRegistration.dispose();
36+
transformsSectionRegistration.dispose();
37+
},
38+
};
39+
},
40+
};

packages/dev/inspector-v2/test/app/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const engine = new Engine(canvas, true, {
2121
});
2222

2323
const scene = new Scene(engine);
24+
(globalThis as any).scene = scene; // For debugging purposes
2425

2526
let camera: Nullable<ArcRotateCamera> = null;
2627

0 commit comments

Comments
 (0)