@babylonjs/inspector 9.1.0 → 9.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/inspector-bridge.mjs +4734 -0
- package/bin/inspector-cli.mjs +4931 -0
- package/lib/cli/protocol.d.ts +180 -0
- package/lib/components/debug/debugPane.d.ts +1 -1
- package/lib/components/properties/materials/openpbrMaterialProperties.d.ts +3 -0
- package/lib/components/properties/propertiesPane.d.ts +1 -1
- package/lib/components/properties/textures/texturePreview.d.ts +38 -0
- package/lib/components/scene/sceneExplorer.d.ts +5 -0
- package/lib/components/stats/statsPane.d.ts +1 -1
- package/lib/components/tools/toolsPane.d.ts +1 -1
- package/lib/extensibility/defaultInspectorExtensionFeed.d.ts +1 -1
- package/lib/extensions/quickCreate/quickCreateToolsService.d.ts +2 -2
- package/lib/{extensionsListService-BmiNjZiw.js → extensionsListService-CBQwBhYh.js} +3 -3
- package/lib/extensionsListService-CBQwBhYh.js.map +1 -0
- package/lib/hooks/settingsHooks.d.ts +0 -8
- package/lib/{index-BCbXjPTn.js → index-DmfAhsIm.js} +1451 -433
- package/lib/index-DmfAhsIm.js.map +1 -0
- package/lib/index.d.ts +28 -21
- package/lib/index.js +2 -2
- package/lib/inspectable.d.ts +67 -0
- package/lib/inspector.d.ts +1 -1
- package/lib/legacy/inspectableCustomPropertiesService.d.ts +1 -1
- package/lib/misc/defaultPerfStrategies.d.ts +16 -0
- package/lib/misc/textureTools.d.ts +2 -1
- package/lib/{quickCreateToolsService-BoqrJqoM.js → quickCreateToolsService-C38aK2nP.js} +3 -3
- package/lib/{quickCreateToolsService-BoqrJqoM.js.map → quickCreateToolsService-C38aK2nP.js.map} +1 -1
- package/lib/{reflectorService-CFTva2GG.js → reflectorService-Bs9E2OMh.js} +3 -3
- package/lib/reflectorService-Bs9E2OMh.js.map +1 -0
- package/lib/services/cli/cliConnectionStatus.d.ts +19 -0
- package/lib/services/cli/entityQueryService.d.ts +8 -0
- package/lib/services/cli/inspectableBridgeService.d.ts +22 -0
- package/lib/services/cli/inspectableCommandRegistry.d.ts +58 -0
- package/lib/services/cli/perfTraceCommandService.d.ts +9 -0
- package/lib/services/cli/screenshotCommandService.d.ts +8 -0
- package/lib/services/cli/shaderCommandService.d.ts +7 -0
- package/lib/services/cli/statsCommandService.d.ts +9 -0
- package/lib/services/cliConnectionStatusService.d.ts +4 -0
- package/lib/services/defaultToolbarMetadata.d.ts +2 -1
- package/lib/services/gizmoService.d.ts +1 -1
- package/lib/services/gizmoToolbarService.d.ts +2 -2
- package/lib/services/globalSettings.d.ts +2 -3
- package/lib/services/highlightService.d.ts +3 -3
- package/lib/services/inspectorSettingsService.d.ts +3 -0
- package/lib/services/miniStatsService.d.ts +2 -2
- package/lib/services/panes/debugService.d.ts +3 -3
- package/lib/services/panes/properties/animationGroupPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/animationPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/atmospherePropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/audioPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/cameraPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/commonPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/effectLayerPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/frameGraphPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/lightPropertiesServices.d.ts +1 -1
- package/lib/services/panes/properties/materialPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/metadataPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/nodePropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/particleSystemPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/physicsPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/postProcessPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/propertiesService.d.ts +3 -3
- package/lib/services/panes/properties/renderingPipelinePropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/scenePropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/skeletonPropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/spritePropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/texturePropertiesService.d.ts +1 -1
- package/lib/services/panes/properties/transformPropertiesService.d.ts +1 -1
- package/lib/services/panes/scene/animationGroupExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/atmosphereExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/disposableCommandService.d.ts +1 -1
- package/lib/services/panes/scene/effectLayersExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/frameGraphExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/guiExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/materialExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/nodeExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/particleSystemExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/postProcessExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/renderingPipelinesExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/sceneExplorerService.d.ts +2 -2
- package/lib/services/panes/scene/skeletonExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/soundExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/spriteManagerExplorerService.d.ts +1 -1
- package/lib/services/panes/scene/texturesExplorerService.d.ts +1 -1
- package/lib/services/panes/statsService.d.ts +3 -3
- package/lib/services/panes/tools/captureService.d.ts +1 -1
- package/lib/services/panes/tools/exportService.d.ts +1 -1
- package/lib/services/panes/tools/import/gltfAnimationImportService.d.ts +1 -1
- package/lib/services/panes/tools/import/gltfLoaderOptionsService.d.ts +1 -1
- package/lib/services/panes/tools/import/gltfValidationService.d.ts +1 -1
- package/lib/services/panes/tools/reflectorService.d.ts +1 -1
- package/lib/services/panes/toolsService.d.ts +3 -3
- package/lib/services/pickingService.d.ts +3 -3
- package/lib/services/sceneContext.d.ts +1 -1
- package/lib/services/selectionService.d.ts +4 -4
- package/lib/services/textureEditor/textureEditorService.d.ts +1 -1
- package/lib/services/userFeedbackService.d.ts +2 -2
- package/lib/services/watcherService.d.ts +5 -5
- package/package.json +11 -4
- package/readme.md +27 -0
- package/lib/components/errorBoundary.d.ts +0 -31
- package/lib/components/extensibleAccordion.d.ts +0 -67
- package/lib/components/pane.d.ts +0 -4
- package/lib/components/teachingMoment.d.ts +0 -20
- package/lib/components/theme.d.ts +0 -10
- package/lib/components/uxContextProvider.d.ts +0 -2
- package/lib/contexts/extensionManagerContext.d.ts +0 -6
- package/lib/contexts/settingsContext.d.ts +0 -3
- package/lib/extensibility/builtInsExtensionFeed.d.ts +0 -21
- package/lib/extensibility/extensionFeed.d.ts +0 -113
- package/lib/extensibility/extensionManager.d.ts +0 -111
- package/lib/extensionsListService-BmiNjZiw.js.map +0 -1
- package/lib/hooks/observableHooks.d.ts +0 -35
- package/lib/hooks/resourceHooks.d.ts +0 -20
- package/lib/hooks/teachingMomentHooks.d.ts +0 -34
- package/lib/hooks/themeHooks.d.ts +0 -17
- package/lib/hooks/useResizeHandle.d.ts +0 -35
- package/lib/index-BCbXjPTn.js.map +0 -1
- package/lib/misc/assert.d.ts +0 -5
- package/lib/misc/graphUtils.d.ts +0 -44
- package/lib/misc/observableCollection.d.ts +0 -23
- package/lib/modularTool.d.ts +0 -37
- package/lib/modularity/serviceContainer.d.ts +0 -41
- package/lib/modularity/serviceDefinition.d.ts +0 -64
- package/lib/reflectorService-CFTva2GG.js.map +0 -1
- package/lib/services/extensionsListService.d.ts +0 -3
- package/lib/services/panes/settingsService.d.ts +0 -25
- package/lib/services/reactContextService.d.ts +0 -18
- package/lib/services/settingsStore.d.ts +0 -55
- package/lib/services/shellService.d.ts +0 -256
- package/lib/services/shellSettingsService.d.ts +0 -3
- package/lib/services/themeSelectorService.d.ts +0 -3
- package/lib/services/themeService.d.ts +0 -60
- package/lib/themes/babylonTheme.d.ts +0 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { createContext, forwardRef, useContext, useState, useCallback, Component, useMemo, useEffect, useRef, useReducer, Children, isValidElement, useLayoutEffect,
|
|
2
|
+
import { createContext, forwardRef, useContext, useState, useCallback, Component, useMemo, useEffect, useRef, useReducer, Children, isValidElement, useLayoutEffect, useImperativeHandle, cloneElement, createElement, Suspense, memo, Fragment as Fragment$1, lazy } from 'react';
|
|
3
3
|
import { tokens, makeStyles, Tooltip as Tooltip$1, Button as Button$1, Spinner, Link as Link$1, Caption1, Body1, useFluent, Accordion as Accordion$1, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, MessageBar as MessageBar$1, MessageBarBody, AccordionItem, SearchBox as SearchBox$1, Portal, ToggleButton as ToggleButton$1, InfoLabel as InfoLabel$1, Body1Strong, mergeClasses, useId, useToastController, Toast, ToastTitle, FluentProvider, Toaster, Checkbox as Checkbox$1, createLightTheme, createDarkTheme, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, createDOMRenderer, RendererProvider, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, Switch as Switch$1, treeItemLevelToken, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, MenuItemCheckbox, useMergedRefs, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, PresenceBadge, Slider as Slider$1, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, Label, MessageBarTitle, useComboboxFilter, Combobox, Subtitle2, Textarea as Textarea$1, ToolbarButton, ToolbarDivider, Field } from '@fluentui/react-components';
|
|
4
|
-
import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, BubbleMultipleRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
|
|
4
|
+
import { ErrorCircleRegular, EyeFilled, EyeOffRegular, CheckmarkFilled, EditRegular, FilterRegular, PinFilled, PinRegular, ArrowCircleUpRegular, ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, CopyRegular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, SettingsRegular, DocumentTextRegular, createFluentIcon, TextSortAscendingRegular, GlobeRegular, WarningRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, ArrowUploadRegular, ArrowBidirectionalUpDownFilled, ArrowDownloadRegular, StopRegular, RecordRegular, DataBarHorizontalRegular, WrenchRegular, ArrowClockwiseRegular, WeatherSunnyRegular, WeatherMoonRegular, PlugConnectedRegular, PlugDisconnectedRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, CameraRegular, AddRegular, DeleteRegular, FullScreenMaximizeRegular, ChevronDownRegular, ChevronRightRegular, CircleSmallFilled, SaveRegular, PreviousRegular, ArrowPreviousRegular, TriangleLeftRegular, RecordStopRegular, PlayRegular, ArrowNextRegular, NextRegular, PauseRegular, LinkDismissRegular, LinkEditRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, EyeRegular, CloudArrowUpRegular, CloudArrowDownRegular, EyeOffFilled, ArrowMoveFilled, StopFilled, PlayFilled, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, RectangleLandscapeRegular, BorderOutsideRegular, BorderNoneRegular, MyLocationRegular, BubbleMultipleRegular, LightbulbRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, SoundWaveCircleRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
|
|
5
5
|
import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
|
|
6
6
|
import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
|
|
7
7
|
import { Observable } from '@babylonjs/core/Misc/observable.js';
|
|
@@ -45,6 +45,7 @@ import { Light } from '@babylonjs/core/Lights/light.js';
|
|
|
45
45
|
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh.js';
|
|
46
46
|
import { Node as Node$1 } from '@babylonjs/core/node.js';
|
|
47
47
|
import { createRoot } from 'react-dom/client';
|
|
48
|
+
import { CreateScreenshotUsingRenderTargetAsync } from '@babylonjs/core/Misc/screenshotTools.js';
|
|
48
49
|
import { SelectionOutlineLayer } from '@babylonjs/core/Layers/selectionOutlineLayer.js';
|
|
49
50
|
import { GaussianSplattingMesh } from '@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh.js';
|
|
50
51
|
import { AnimationGroup, TargetedAnimation } from '@babylonjs/core/Animations/animationGroup.js';
|
|
@@ -138,7 +139,6 @@ import '@babylonjs/core/Sprites/spriteSceneComponent.js';
|
|
|
138
139
|
import { DynamicTexture } from '@babylonjs/core/Materials/Textures/dynamicTexture.js';
|
|
139
140
|
import { captureEquirectangularFromScene } from '@babylonjs/core/Misc/equirectangularCapture.js';
|
|
140
141
|
import { SceneRecorder } from '@babylonjs/core/Misc/sceneRecorder.js';
|
|
141
|
-
import { CreateScreenshotUsingRenderTargetAsync } from '@babylonjs/core/Misc/screenshotTools.js';
|
|
142
142
|
import { VideoRecorder } from '@babylonjs/core/Misc/videoRecorder.js';
|
|
143
143
|
import { SceneSerializer } from '@babylonjs/core/Misc/sceneSerializer.js';
|
|
144
144
|
import { EnvironmentTextureTools } from '@babylonjs/core/Misc/environmentTextureTools.js';
|
|
@@ -1743,13 +1743,18 @@ const InfoLabel = (props) => {
|
|
|
1743
1743
|
};
|
|
1744
1744
|
|
|
1745
1745
|
const ToastContext = createContext({ showToast: () => { } });
|
|
1746
|
-
|
|
1746
|
+
/**
|
|
1747
|
+
* Provides toast notification functionality to child components via context and an optional imperative ref.
|
|
1748
|
+
* @returns The toast provider component tree.
|
|
1749
|
+
*/
|
|
1750
|
+
const ToastProvider = ({ children, imperativeRef }) => {
|
|
1747
1751
|
const toasterId = useId("toaster");
|
|
1748
1752
|
const { dispatchToast } = useToastController(toasterId);
|
|
1749
1753
|
const { targetDocument } = useFluent();
|
|
1750
|
-
const showToast = useCallback((message) => {
|
|
1751
|
-
dispatchToast(jsx(Toast, { children: jsx(ToastTitle, { children: message }) }), { intent: "info", timeout: 2000 });
|
|
1754
|
+
const showToast = useCallback((message, options) => {
|
|
1755
|
+
dispatchToast(jsx(Toast, { children: jsx(ToastTitle, { children: message }) }), { intent: options?.intent ?? "info", timeout: 2000 });
|
|
1752
1756
|
}, [dispatchToast]);
|
|
1757
|
+
useImperativeHandle(imperativeRef, () => ({ showToast }), [showToast]);
|
|
1753
1758
|
return (jsxs(ToastContext.Provider, { value: { showToast }, children: [children, jsx(FluentProvider, { applyStylesToPortals: true, targetDocument: targetDocument, children: jsx(Toaster, { toasterId: toasterId, position: "bottom" }) })] }));
|
|
1754
1759
|
};
|
|
1755
1760
|
/**
|
|
@@ -1931,25 +1936,6 @@ function useSettingsStore() {
|
|
|
1931
1936
|
return useContext(SettingsStoreContext);
|
|
1932
1937
|
}
|
|
1933
1938
|
|
|
1934
|
-
// These are all "global" settings that aren't produced/owned by a specific service,
|
|
1935
|
-
// so we just add them by default directly in the SettingsService.
|
|
1936
|
-
const CompactModeSettingDescriptor = {
|
|
1937
|
-
key: "CompactMode",
|
|
1938
|
-
defaultValue: !matchMedia("(pointer: coarse)").matches,
|
|
1939
|
-
};
|
|
1940
|
-
const UseDegreesSettingDescriptor = {
|
|
1941
|
-
key: "UseDegrees",
|
|
1942
|
-
defaultValue: true,
|
|
1943
|
-
};
|
|
1944
|
-
const UseEulerSettingDescriptor = {
|
|
1945
|
-
key: "UseEuler",
|
|
1946
|
-
defaultValue: true,
|
|
1947
|
-
};
|
|
1948
|
-
const DisableCopySettingDescriptor = {
|
|
1949
|
-
key: "DisableCopy",
|
|
1950
|
-
defaultValue: false,
|
|
1951
|
-
};
|
|
1952
|
-
|
|
1953
1939
|
/**
|
|
1954
1940
|
* Hook that reads and writes a setting from the settings store.
|
|
1955
1941
|
* @param descriptor The setting descriptor that identifies the setting and its default value.
|
|
@@ -1985,35 +1971,15 @@ function useSetting(descriptor) {
|
|
|
1985
1971
|
}
|
|
1986
1972
|
return [value, setValue, resetValue];
|
|
1987
1973
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
* Gets functions used to convert to/from display values for angles based on the current settings.
|
|
1998
|
-
* @returns A tuple containing the functions to convert to and from display values.
|
|
1999
|
-
*/
|
|
2000
|
-
function useAngleConverters() {
|
|
2001
|
-
const [useDegrees] = useSetting(UseDegreesSettingDescriptor);
|
|
2002
|
-
const toDisplayValue = useCallback((angle, wrap = false) => {
|
|
2003
|
-
if (wrap) {
|
|
2004
|
-
angle = WrapAngle(angle);
|
|
2005
|
-
}
|
|
2006
|
-
return useDegrees ? angle * RadiansToDegrees : angle;
|
|
2007
|
-
}, [useDegrees]);
|
|
2008
|
-
const fromDisplayValue = useCallback((angle, wrap = false) => {
|
|
2009
|
-
angle = useDegrees ? angle / RadiansToDegrees : angle;
|
|
2010
|
-
if (wrap) {
|
|
2011
|
-
angle = WrapAngle(angle);
|
|
2012
|
-
}
|
|
2013
|
-
return angle;
|
|
2014
|
-
}, [useDegrees]);
|
|
2015
|
-
return [toDisplayValue, fromDisplayValue, useDegrees];
|
|
2016
|
-
}
|
|
1974
|
+
|
|
1975
|
+
const CompactModeSettingDescriptor = {
|
|
1976
|
+
key: "CompactMode",
|
|
1977
|
+
defaultValue: !matchMedia("(pointer: coarse)").matches,
|
|
1978
|
+
};
|
|
1979
|
+
const DisableCopySettingDescriptor = {
|
|
1980
|
+
key: "DisableCopy",
|
|
1981
|
+
defaultValue: false,
|
|
1982
|
+
};
|
|
2017
1983
|
|
|
2018
1984
|
const UXContextProvider = (props) => {
|
|
2019
1985
|
const [compactMode] = useSetting(CompactModeSettingDescriptor);
|
|
@@ -2535,6 +2501,47 @@ function usePollingObservable(delay) {
|
|
|
2535
2501
|
return observable;
|
|
2536
2502
|
}
|
|
2537
2503
|
|
|
2504
|
+
// These are all "global" settings that aren't produced/owned by a specific service,
|
|
2505
|
+
// so we just add them by default directly in the SettingsService.
|
|
2506
|
+
const UseDegreesSettingDescriptor = {
|
|
2507
|
+
key: "UseDegrees",
|
|
2508
|
+
defaultValue: true,
|
|
2509
|
+
};
|
|
2510
|
+
const UseEulerSettingDescriptor = {
|
|
2511
|
+
key: "UseEuler",
|
|
2512
|
+
defaultValue: true,
|
|
2513
|
+
};
|
|
2514
|
+
|
|
2515
|
+
const RadiansToDegrees = 180 / Math.PI;
|
|
2516
|
+
function WrapAngle(angle) {
|
|
2517
|
+
angle %= Math.PI * 2;
|
|
2518
|
+
if (angle < 0) {
|
|
2519
|
+
angle += Math.PI * 2;
|
|
2520
|
+
}
|
|
2521
|
+
return angle;
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Gets functions used to convert to/from display values for angles based on the current settings.
|
|
2525
|
+
* @returns A tuple containing the functions to convert to and from display values.
|
|
2526
|
+
*/
|
|
2527
|
+
function useAngleConverters() {
|
|
2528
|
+
const [useDegrees] = useSetting(UseDegreesSettingDescriptor);
|
|
2529
|
+
const toDisplayValue = useCallback((angle, wrap = false) => {
|
|
2530
|
+
if (wrap) {
|
|
2531
|
+
angle = WrapAngle(angle);
|
|
2532
|
+
}
|
|
2533
|
+
return useDegrees ? angle * RadiansToDegrees : angle;
|
|
2534
|
+
}, [useDegrees]);
|
|
2535
|
+
const fromDisplayValue = useCallback((angle, wrap = false) => {
|
|
2536
|
+
angle = useDegrees ? angle / RadiansToDegrees : angle;
|
|
2537
|
+
if (wrap) {
|
|
2538
|
+
angle = WrapAngle(angle);
|
|
2539
|
+
}
|
|
2540
|
+
return angle;
|
|
2541
|
+
}, [useDegrees]);
|
|
2542
|
+
return [toDisplayValue, fromDisplayValue, useDegrees];
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2538
2545
|
const SequencerLock = new AsyncLock();
|
|
2539
2546
|
/**
|
|
2540
2547
|
* Creates a hook for managing teaching moment state.
|
|
@@ -2671,11 +2678,6 @@ function ConstructorFactory(constructor) {
|
|
|
2671
2678
|
return (...args) => new constructor(...args);
|
|
2672
2679
|
}
|
|
2673
2680
|
|
|
2674
|
-
/**
|
|
2675
|
-
* The unique identity symbol for the scene context service.
|
|
2676
|
-
*/
|
|
2677
|
-
const SceneContextIdentity = Symbol("SceneContext");
|
|
2678
|
-
|
|
2679
2681
|
function ToFeaturesString(options) {
|
|
2680
2682
|
const { defaultWidth, defaultHeight, defaultLeft, defaultTop } = options;
|
|
2681
2683
|
const features = [];
|
|
@@ -3612,6 +3614,46 @@ function MakeShellServiceDefinition({ leftPaneDefaultWidth = 350, leftPaneMinWid
|
|
|
3612
3614
|
};
|
|
3613
3615
|
}
|
|
3614
3616
|
|
|
3617
|
+
/**
|
|
3618
|
+
* The unique identity symbol for the settings service.
|
|
3619
|
+
*/
|
|
3620
|
+
const SettingsServiceIdentity = Symbol("SettingsService");
|
|
3621
|
+
const SettingsServiceDefinition = {
|
|
3622
|
+
friendlyName: "Settings",
|
|
3623
|
+
consumes: [ShellServiceIdentity],
|
|
3624
|
+
produces: [SettingsServiceIdentity],
|
|
3625
|
+
factory: (shellService) => {
|
|
3626
|
+
const sectionsCollection = new ObservableCollection();
|
|
3627
|
+
const sectionContentCollection = new ObservableCollection();
|
|
3628
|
+
const registration = shellService.addSidePane({
|
|
3629
|
+
key: "Settings",
|
|
3630
|
+
title: "Settings",
|
|
3631
|
+
icon: SettingsRegular,
|
|
3632
|
+
horizontalLocation: "right",
|
|
3633
|
+
verticalLocation: "top",
|
|
3634
|
+
order: 500,
|
|
3635
|
+
teachingMoment: false,
|
|
3636
|
+
content: () => {
|
|
3637
|
+
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
3638
|
+
const sectionContent = useObservableCollection(sectionContentCollection);
|
|
3639
|
+
return jsx(ExtensibleAccordion, { sections: sections, sectionContent: sectionContent, context: true });
|
|
3640
|
+
},
|
|
3641
|
+
});
|
|
3642
|
+
return {
|
|
3643
|
+
addSection: (section) => sectionsCollection.add(section),
|
|
3644
|
+
addSectionContent: (content) => sectionContentCollection.add(content),
|
|
3645
|
+
dispose: () => {
|
|
3646
|
+
registration.dispose();
|
|
3647
|
+
},
|
|
3648
|
+
};
|
|
3649
|
+
},
|
|
3650
|
+
};
|
|
3651
|
+
|
|
3652
|
+
/**
|
|
3653
|
+
* The unique identity symbol for the scene context service.
|
|
3654
|
+
*/
|
|
3655
|
+
const SceneContextIdentity = Symbol("SceneContext");
|
|
3656
|
+
|
|
3615
3657
|
const useSwitchStyles = makeStyles({
|
|
3616
3658
|
switch: {
|
|
3617
3659
|
marginLeft: "auto",
|
|
@@ -3659,54 +3701,6 @@ const SwitchPropertyLine = (props) => {
|
|
|
3659
3701
|
return (jsx(PropertyLine, { ...props, children: jsx(Switch, { ...switchProps }) }));
|
|
3660
3702
|
};
|
|
3661
3703
|
|
|
3662
|
-
/**
|
|
3663
|
-
* The unique identity symbol for the settings service.
|
|
3664
|
-
*/
|
|
3665
|
-
const SettingsServiceIdentity = Symbol("SettingsService");
|
|
3666
|
-
const SettingsServiceDefinition = {
|
|
3667
|
-
friendlyName: "Settings",
|
|
3668
|
-
consumes: [ShellServiceIdentity, SceneContextIdentity],
|
|
3669
|
-
produces: [SettingsServiceIdentity],
|
|
3670
|
-
factory: (shellService, sceneContext) => {
|
|
3671
|
-
const sectionsCollection = new ObservableCollection();
|
|
3672
|
-
const sectionContentCollection = new ObservableCollection();
|
|
3673
|
-
const registration = shellService.addSidePane({
|
|
3674
|
-
key: "Settings",
|
|
3675
|
-
title: "Settings",
|
|
3676
|
-
icon: SettingsRegular,
|
|
3677
|
-
horizontalLocation: "right",
|
|
3678
|
-
verticalLocation: "top",
|
|
3679
|
-
order: 500,
|
|
3680
|
-
teachingMoment: false,
|
|
3681
|
-
content: () => {
|
|
3682
|
-
const sections = useOrderedObservableCollection(sectionsCollection);
|
|
3683
|
-
const sectionContent = useObservableCollection(sectionContentCollection);
|
|
3684
|
-
const scene = useObservableState(() => sceneContext.currentScene, sceneContext.currentSceneObservable);
|
|
3685
|
-
const [compactMode, setCompactMode] = useSetting(CompactModeSettingDescriptor);
|
|
3686
|
-
const [useDegrees, setUseDegrees] = useSetting(UseDegreesSettingDescriptor);
|
|
3687
|
-
const [useEuler, setUseEuler] = useSetting(UseEulerSettingDescriptor);
|
|
3688
|
-
const [disableCopy, setDisableCopy] = useSetting(DisableCopySettingDescriptor);
|
|
3689
|
-
return (jsx(Fragment, { children: scene && (jsx(ExtensibleAccordion, { sections: sections, sectionContent: sectionContent, context: scene, children: jsxs(AccordionSection, { title: "UI", children: [jsx(SwitchPropertyLine, { label: "Compact Mode", description: "Use a more compact UI with less spacing.", value: compactMode, onChange: (checked) => {
|
|
3690
|
-
setCompactMode(checked);
|
|
3691
|
-
} }), jsx(SwitchPropertyLine, { label: "Use Degrees", description: "Using degrees instead of radians.", value: useDegrees, onChange: (checked) => {
|
|
3692
|
-
setUseDegrees(checked);
|
|
3693
|
-
} }), jsx(SwitchPropertyLine, { label: "Only Show Euler Angles", description: "Only show Euler angles in rotation properties, rather than quaternions.", value: useEuler, onChange: (checked) => {
|
|
3694
|
-
setUseEuler(checked);
|
|
3695
|
-
} }), jsx(SwitchPropertyLine, { label: "Disable Copy Button", description: "Disables the copy to clipboard button on property lines. You can still Ctrl+Click on the label to copy.", value: disableCopy, onChange: (checked) => {
|
|
3696
|
-
setDisableCopy(checked);
|
|
3697
|
-
} })] }) })) }));
|
|
3698
|
-
},
|
|
3699
|
-
});
|
|
3700
|
-
return {
|
|
3701
|
-
addSection: (section) => sectionsCollection.add(section),
|
|
3702
|
-
addSectionContent: (content) => sectionContentCollection.add(content),
|
|
3703
|
-
dispose: () => {
|
|
3704
|
-
registration.dispose();
|
|
3705
|
-
},
|
|
3706
|
-
};
|
|
3707
|
-
},
|
|
3708
|
-
};
|
|
3709
|
-
|
|
3710
3704
|
/**
|
|
3711
3705
|
* The unique identity symbol for the selection service.
|
|
3712
3706
|
*/
|
|
@@ -4147,14 +4141,14 @@ class GraphUtils {
|
|
|
4147
4141
|
}
|
|
4148
4142
|
}
|
|
4149
4143
|
|
|
4150
|
-
const SyntheticUniqueIds = new WeakMap();
|
|
4151
|
-
function GetEntityId(entity) {
|
|
4144
|
+
const SyntheticUniqueIds$1 = new WeakMap();
|
|
4145
|
+
function GetEntityId$1(entity) {
|
|
4152
4146
|
if ("uniqueId" in entity && typeof entity.uniqueId === "number") {
|
|
4153
4147
|
return entity.uniqueId;
|
|
4154
4148
|
}
|
|
4155
|
-
let id = SyntheticUniqueIds.get(entity);
|
|
4149
|
+
let id = SyntheticUniqueIds$1.get(entity);
|
|
4156
4150
|
if (!id) {
|
|
4157
|
-
SyntheticUniqueIds.set(entity, (id = UniqueIdGenerator.UniqueId));
|
|
4151
|
+
SyntheticUniqueIds$1.set(entity, (id = UniqueIdGenerator.UniqueId));
|
|
4158
4152
|
}
|
|
4159
4153
|
return id;
|
|
4160
4154
|
}
|
|
@@ -4174,7 +4168,7 @@ function GetEntitySection(entityItem) {
|
|
|
4174
4168
|
}
|
|
4175
4169
|
function ExpandOrCollapseAll(treeItem, open, openItems) {
|
|
4176
4170
|
const addOrRemove = open ? openItems.add.bind(openItems) : openItems.delete.bind(openItems);
|
|
4177
|
-
TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? GetEntityId(treeItem.entity) : treeItem.sectionName));
|
|
4171
|
+
TraverseGraph([treeItem], (treeItem) => treeItem.children, (treeItem) => addOrRemove(treeItem.type === "entity" ? GetEntityId$1(treeItem.entity) : treeItem.sectionName));
|
|
4178
4172
|
}
|
|
4179
4173
|
function GetCommandHotKeyDescription(command) {
|
|
4180
4174
|
if (!command.hotKey) {
|
|
@@ -4470,16 +4464,16 @@ const EntityTreeItem = (props) => {
|
|
|
4470
4464
|
}
|
|
4471
4465
|
}
|
|
4472
4466
|
}, [commands]);
|
|
4473
|
-
return (jsxs(Menu, { openOnContext: true, checkedValues: checkedContextMenuItems, onCheckedValueChange: onContextMenuCheckedValueChange, children: [jsx(MenuTrigger, { disableButtonEnhancement: true, children: jsx(FlatTreeItem, { className: mergeClasses(classes.treeItem, isDragging && classes.treeItemDragging, isDropTarget && classes.treeItemDropTarget), value: GetEntityId(entityItem.entity),
|
|
4467
|
+
return (jsxs(Menu, { openOnContext: true, checkedValues: checkedContextMenuItems, onCheckedValueChange: onContextMenuCheckedValueChange, children: [jsx(MenuTrigger, { disableButtonEnhancement: true, children: jsx(FlatTreeItem, { className: mergeClasses(classes.treeItem, isDragging && classes.treeItemDragging, isDropTarget && classes.treeItemDropTarget), value: GetEntityId$1(entityItem.entity),
|
|
4474
4468
|
// Disable manual expand/collapse when a filter is active.
|
|
4475
|
-
itemType: !isFiltering && hasChildren ? "branch" : "leaf", parentValue: entityItem.parent.type === "section" ? entityItem.parent.sectionName : GetEntityId(entityItem.parent.entity), "aria-level": entityItem.depth, "aria-setsize": 1, "aria-posinset": 1, onClick: select, onKeyDown: onKeyDown, style: { [treeItemLevelToken]: entityItem.depth }, ...dragProps, children: jsx(TreeItemLayout, { iconBefore: entityItem.icon ? jsx(entityItem.icon, { entity: entityItem.entity }) : null, className: mergeClasses(hasChildren ? classes.treeItemLayoutBranch : classes.treeItemLayoutLeaf, compactMode ? classes.treeItemLayoutCompact : undefined, isDropTarget && classes.treeItemDropTarget), style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
|
|
4469
|
+
itemType: !isFiltering && hasChildren ? "branch" : "leaf", parentValue: entityItem.parent.type === "section" ? entityItem.parent.sectionName : GetEntityId$1(entityItem.parent.entity), "aria-level": entityItem.depth, "aria-setsize": 1, "aria-posinset": 1, onClick: select, onKeyDown: onKeyDown, style: { [treeItemLevelToken]: entityItem.depth }, ...dragProps, children: jsx(TreeItemLayout, { iconBefore: displayInfo.validationError ? (jsx(Tooltip$1, { content: displayInfo.validationError, relationship: "description", children: jsx(WarningRegular, {}) })) : entityItem.icon ? (jsx(entityItem.icon, { entity: entityItem.entity })) : null, className: mergeClasses(hasChildren ? classes.treeItemLayoutBranch : classes.treeItemLayoutLeaf, compactMode ? classes.treeItemLayoutCompact : undefined, isDropTarget && classes.treeItemDropTarget), style: isSelected ? { backgroundColor: tokens.colorNeutralBackground1Selected } : undefined, actions: actions, aside: {
|
|
4476
4470
|
// Match the gap and padding of the actions.
|
|
4477
4471
|
className: classes.treeItemLayoutAside,
|
|
4478
4472
|
children: aside,
|
|
4479
4473
|
}, main: {
|
|
4480
4474
|
// Prevent the "main" content (the Body1 below) from growing too large and pushing the actions/aside out of view.
|
|
4481
4475
|
className: classes.treeItemLayoutMain,
|
|
4482
|
-
}, children: jsx(Tooltip$1, { content: name, relationship: "description", children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }) }, GetEntityId(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
|
|
4476
|
+
}, children: jsx(Tooltip$1, { content: name, relationship: "description", children: jsx(Body1, { wrap: false, truncate: true, children: name }) }) }) }, GetEntityId$1(entityItem.entity)) }), jsx(MenuPopover, { hidden: !hasChildren && contextMenuCommands.length === 0, children: jsxs(MenuList, { children: [hasChildren && (jsxs(Fragment, { children: [jsx(MenuItem, { icon: jsx(ArrowExpandAllRegular, {}), onClick: expandAll, children: jsx(Body1, { children: "Expand All" }) }), jsx(MenuItem, { icon: jsx(ArrowCollapseAllRegular, {}), onClick: collapseAll, children: jsx(Body1, { children: "Collapse All" }) })] })), hasChildren && contextMenuCommands.length > 0 && jsx(MenuDivider, {}), contextMenuItems] }) })] }));
|
|
4483
4477
|
};
|
|
4484
4478
|
const SceneExplorer = (props) => {
|
|
4485
4479
|
const classes = useStyles$Q();
|
|
@@ -4502,7 +4496,7 @@ const SceneExplorer = (props) => {
|
|
|
4502
4496
|
if (targetEntity) {
|
|
4503
4497
|
setOpenItems((prev) => {
|
|
4504
4498
|
const next = new Set(prev);
|
|
4505
|
-
next.add(GetEntityId(targetEntity));
|
|
4499
|
+
next.add(GetEntityId$1(targetEntity));
|
|
4506
4500
|
return next;
|
|
4507
4501
|
});
|
|
4508
4502
|
}
|
|
@@ -4519,7 +4513,7 @@ const SceneExplorer = (props) => {
|
|
|
4519
4513
|
};
|
|
4520
4514
|
const onSceneItemRemoved = (item) => {
|
|
4521
4515
|
setSceneVersion((version) => version + 1);
|
|
4522
|
-
if (openItems.delete(GetEntityId(item))) {
|
|
4516
|
+
if (openItems.delete(GetEntityId$1(item))) {
|
|
4523
4517
|
setOpenItems(new Set(openItems));
|
|
4524
4518
|
}
|
|
4525
4519
|
if (item === selectedEntity) {
|
|
@@ -4575,7 +4569,7 @@ const SceneExplorer = (props) => {
|
|
|
4575
4569
|
parent.children = [];
|
|
4576
4570
|
}
|
|
4577
4571
|
parent.children.push(treeItemData);
|
|
4578
|
-
allTreeItems.set(GetEntityId(entity), treeItemData);
|
|
4572
|
+
allTreeItems.set(GetEntityId$1(entity), treeItemData);
|
|
4579
4573
|
return treeItemData;
|
|
4580
4574
|
};
|
|
4581
4575
|
const rootEntityTreeItems = rootEntities.map((entity) => createEntityTreeItemData(entity, sectionTreeItem));
|
|
@@ -4623,7 +4617,7 @@ const SceneExplorer = (props) => {
|
|
|
4623
4617
|
TraverseGraph(children,
|
|
4624
4618
|
// Get children
|
|
4625
4619
|
(treeItem) => {
|
|
4626
|
-
if (filter || openItems.has(GetEntityId(treeItem.entity))) {
|
|
4620
|
+
if (filter || openItems.has(GetEntityId$1(treeItem.entity))) {
|
|
4627
4621
|
if (!treeItem.children) {
|
|
4628
4622
|
return null;
|
|
4629
4623
|
}
|
|
@@ -4671,8 +4665,8 @@ const SceneExplorer = (props) => {
|
|
|
4671
4665
|
}, [sceneTreeItem, sectionTreeItems, allTreeItems, openItems, itemsFilter, isSorted]);
|
|
4672
4666
|
const getParentStack = useCallback((entity) => {
|
|
4673
4667
|
const parentStack = [];
|
|
4674
|
-
for (let treeItem = allTreeItems.get(GetEntityId(entity)); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
|
|
4675
|
-
parentStack.push(treeItem.type === "entity" ? GetEntityId(treeItem.entity) : treeItem.sectionName);
|
|
4668
|
+
for (let treeItem = allTreeItems.get(GetEntityId$1(entity)); treeItem; treeItem = treeItem?.type === "entity" ? treeItem.parent : undefined) {
|
|
4669
|
+
parentStack.push(treeItem.type === "entity" ? GetEntityId$1(treeItem.entity) : treeItem.sectionName);
|
|
4676
4670
|
}
|
|
4677
4671
|
// The first item will be the entity itself, so just remove it.
|
|
4678
4672
|
parentStack.shift();
|
|
@@ -4680,7 +4674,7 @@ const SceneExplorer = (props) => {
|
|
|
4680
4674
|
}, [allTreeItems]);
|
|
4681
4675
|
const selectEntity = useCallback((selectedEntity) => {
|
|
4682
4676
|
const entity = selectedEntity;
|
|
4683
|
-
if (entity && GetEntityId(entity) != undefined) {
|
|
4677
|
+
if (entity && GetEntityId$1(entity) != undefined) {
|
|
4684
4678
|
const parentStack = getParentStack(entity);
|
|
4685
4679
|
if (parentStack.length > 0) {
|
|
4686
4680
|
const newOpenItems = new Set(openItems);
|
|
@@ -4752,7 +4746,7 @@ const SceneExplorer = (props) => {
|
|
|
4752
4746
|
return name;
|
|
4753
4747
|
};
|
|
4754
4748
|
const dragProps = createDragProps(item.entity, getName, section.dragDropConfig);
|
|
4755
|
-
return (jsx(EntityTreeItem, { scene: scene, entityItem: item, isSelected: selectedEntity === item.entity, select: () => setSelectedEntity?.(item.entity), isFiltering: !!itemsFilter, commandProviders: entityCommandProviders, expandAll: () => expandAll(item), collapseAll: () => collapseAll(item), isDragging: draggedEntity === item.entity, isDropTarget: dropTarget === item.entity, ...dragProps }, GetEntityId(item.entity)));
|
|
4749
|
+
return (jsx(EntityTreeItem, { scene: scene, entityItem: item, isSelected: selectedEntity === item.entity, select: () => setSelectedEntity?.(item.entity), isFiltering: !!itemsFilter, commandProviders: entityCommandProviders, expandAll: () => expandAll(item), collapseAll: () => collapseAll(item), isDragging: draggedEntity === item.entity, isDropTarget: dropTarget === item.entity, ...dragProps }, GetEntityId$1(item.entity)));
|
|
4756
4750
|
}
|
|
4757
4751
|
} }) })] }));
|
|
4758
4752
|
};
|
|
@@ -5032,7 +5026,8 @@ const FrameStepsStats = ({ context: scene }) => {
|
|
|
5032
5026
|
*/
|
|
5033
5027
|
const ButtonLine = (props) => {
|
|
5034
5028
|
ButtonLine.displayName = "ButtonLine";
|
|
5035
|
-
|
|
5029
|
+
const { uniqueId, ...buttonProps } = props;
|
|
5030
|
+
return (jsx(LineContainer, { uniqueId: uniqueId ?? props.label, children: jsx(Button, { ...buttonProps }) }));
|
|
5036
5031
|
};
|
|
5037
5032
|
|
|
5038
5033
|
/**
|
|
@@ -6343,7 +6338,10 @@ const SpinButton = forwardRef((props, ref) => {
|
|
|
6343
6338
|
// Update edit text to reflect the new value so the user sees the change
|
|
6344
6339
|
setEditText(formatValue(newValue));
|
|
6345
6340
|
}
|
|
6346
|
-
|
|
6341
|
+
// Ignore modifier keys so the useKeyState calls above can still observe them.
|
|
6342
|
+
if (event.key !== "Alt" && event.key !== "Shift") {
|
|
6343
|
+
HandleKeyDown(event);
|
|
6344
|
+
}
|
|
6347
6345
|
}, [value, step, constrainValue, tryCommitValue, commitEditText, formatValue]);
|
|
6348
6346
|
const id = useId("spin-button2");
|
|
6349
6347
|
// Real-time validation: when editing, validate the expression; otherwise validate the committed value.
|
|
@@ -6904,35 +6902,18 @@ const PerformanceViewer = (props) => {
|
|
|
6904
6902
|
return (jsxs("div", { className: classes.container, children: [jsx(Button, { className: classes.returnButton, onClick: onReturnToPlayheadClick, label: "Return", title: "Return to Playhead" }), jsx("div", { className: classes.sidebar, children: jsx(PerformanceSidebar, { collector: performanceCollector, onVisibleRangeChangedObservable: onVisibleRangeChangedObservable }) }), jsx("div", { className: classes.graph, children: jsx(CanvasGraph, { returnToPlayheadObservable: returnToLiveObservable, layoutObservable: layoutObservable, scene: scene, collector: performanceCollector, onVisibleRangeChangedObservable: onVisibleRangeChangedObservable, initialGraphSize: initialGraphSize }) })] }));
|
|
6905
6903
|
};
|
|
6906
6904
|
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
// Do not enable for now as the Pressure API does not
|
|
6911
|
-
// report factors at the moment.
|
|
6912
|
-
// perfCollector.addCollectionStrategies({
|
|
6913
|
-
// strategyCallback: PerfCollectionStrategy.ThermalStrategy(),
|
|
6914
|
-
// category: IPerfMetadataCategory.FrameSteps,
|
|
6915
|
-
// hidden: true,
|
|
6916
|
-
// });
|
|
6917
|
-
// perfCollector.addCollectionStrategies({
|
|
6918
|
-
// strategyCallback: PerfCollectionStrategy.PowerSupplyStrategy(),
|
|
6919
|
-
// category: IPerfMetadataCategory.FrameSteps,
|
|
6920
|
-
// hidden: true,
|
|
6921
|
-
// });
|
|
6922
|
-
perfCollector.addCollectionStrategies({
|
|
6923
|
-
strategyCallback: PerfCollectionStrategy.PressureStrategy(),
|
|
6924
|
-
category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */,
|
|
6925
|
-
hidden: true,
|
|
6926
|
-
});
|
|
6927
|
-
}
|
|
6928
|
-
}
|
|
6905
|
+
/**
|
|
6906
|
+
* Performance metadata categories for grouping strategies.
|
|
6907
|
+
*/
|
|
6929
6908
|
var PerfMetadataCategory;
|
|
6930
6909
|
(function (PerfMetadataCategory) {
|
|
6931
6910
|
PerfMetadataCategory["Count"] = "Count";
|
|
6932
6911
|
PerfMetadataCategory["FrameSteps"] = "Frame Steps Duration";
|
|
6933
6912
|
})(PerfMetadataCategory || (PerfMetadataCategory = {}));
|
|
6934
|
-
|
|
6935
|
-
|
|
6913
|
+
/**
|
|
6914
|
+
* Default list of performance collection strategies used by the performance viewer and CLI perf trace.
|
|
6915
|
+
*/
|
|
6916
|
+
const DefaultPerfStrategies = [
|
|
6936
6917
|
{ strategyCallback: PerfCollectionStrategy.FpsStrategy() },
|
|
6937
6918
|
{ strategyCallback: PerfCollectionStrategy.TotalMeshesStrategy(), category: "Count" /* PerfMetadataCategory.Count */, hidden: true },
|
|
6938
6919
|
{ strategyCallback: PerfCollectionStrategy.ActiveMeshesStrategy(), category: "Count" /* PerfMetadataCategory.Count */, hidden: true },
|
|
@@ -6956,6 +6937,21 @@ const DefaultStrategiesList = [
|
|
|
6956
6937
|
{ strategyCallback: PerfCollectionStrategy.InterFrameStrategy(), category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */, hidden: true },
|
|
6957
6938
|
{ strategyCallback: PerfCollectionStrategy.GpuFrameTimeStrategy(), category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */, hidden: true },
|
|
6958
6939
|
];
|
|
6940
|
+
|
|
6941
|
+
/**
|
|
6942
|
+
* Adds default and platform-specific performance collection strategies to the collector.
|
|
6943
|
+
* @param perfCollector - The performance viewer collector to add strategies to.
|
|
6944
|
+
*/
|
|
6945
|
+
function AddStrategies(perfCollector) {
|
|
6946
|
+
perfCollector.addCollectionStrategies(...DefaultPerfStrategies);
|
|
6947
|
+
if (PressureObserverWrapper.IsAvailable) {
|
|
6948
|
+
perfCollector.addCollectionStrategies({
|
|
6949
|
+
strategyCallback: PerfCollectionStrategy.PressureStrategy(),
|
|
6950
|
+
category: "Frame Steps Duration" /* PerfMetadataCategory.FrameSteps */,
|
|
6951
|
+
hidden: true,
|
|
6952
|
+
});
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6959
6955
|
// arbitrary window size
|
|
6960
6956
|
const InitialWindowSize = { width: 1024, height: 512 };
|
|
6961
6957
|
const InitialGraphSize = new Vector2(724, 512);
|
|
@@ -8143,12 +8139,20 @@ function SortServiceDefinitions(serviceDefinitions) {
|
|
|
8143
8139
|
* passing dependencies through to services, and disposing of services when the container is disposed.
|
|
8144
8140
|
*/
|
|
8145
8141
|
class ServiceContainer {
|
|
8146
|
-
|
|
8142
|
+
/**
|
|
8143
|
+
* Creates a new ServiceContainer.
|
|
8144
|
+
* @param _friendlyName A human-readable name for debugging.
|
|
8145
|
+
* @param _parent An optional parent container. Dependencies not found locally will be resolved from the parent.
|
|
8146
|
+
*/
|
|
8147
|
+
constructor(_friendlyName, _parent) {
|
|
8147
8148
|
this._friendlyName = _friendlyName;
|
|
8149
|
+
this._parent = _parent;
|
|
8148
8150
|
this._isDisposed = false;
|
|
8149
8151
|
this._serviceDefinitions = new Map();
|
|
8150
8152
|
this._serviceDependents = new Map();
|
|
8151
8153
|
this._serviceInstances = new Map();
|
|
8154
|
+
this._children = new Set();
|
|
8155
|
+
_parent?._children.add(this);
|
|
8152
8156
|
}
|
|
8153
8157
|
/**
|
|
8154
8158
|
* Adds a set of service definitions in the service container.
|
|
@@ -8209,30 +8213,61 @@ class ServiceContainer {
|
|
|
8209
8213
|
service.produces?.forEach((contract) => {
|
|
8210
8214
|
this._serviceDefinitions.set(contract, service);
|
|
8211
8215
|
});
|
|
8212
|
-
const dependencies = service.consumes?.map((
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8216
|
+
const dependencies = service.consumes?.map((contract) => this._resolveDependency(contract, service)) ?? [];
|
|
8217
|
+
this._serviceInstances.set(service, await service.factory(...dependencies, abortSignal));
|
|
8218
|
+
}
|
|
8219
|
+
/**
|
|
8220
|
+
* Resolves a dependency by contract identity for a consuming service.
|
|
8221
|
+
* Checks local services first, then walks up the parent chain.
|
|
8222
|
+
* Registers the consumer as a dependent in whichever container owns the dependency.
|
|
8223
|
+
* @param contract The contract identity to resolve.
|
|
8224
|
+
* @param consumer The service definition that consumes this dependency.
|
|
8225
|
+
* @returns The resolved service instance.
|
|
8226
|
+
*/
|
|
8227
|
+
_resolveDependency(contract, consumer) {
|
|
8228
|
+
const definition = this._serviceDefinitions.get(contract);
|
|
8229
|
+
if (definition) {
|
|
8230
|
+
let dependentDefinitions = this._serviceDependents.get(definition);
|
|
8218
8231
|
if (!dependentDefinitions) {
|
|
8219
|
-
this._serviceDependents.set(
|
|
8232
|
+
this._serviceDependents.set(definition, (dependentDefinitions = new Set()));
|
|
8220
8233
|
}
|
|
8221
|
-
dependentDefinitions.add(
|
|
8222
|
-
const
|
|
8223
|
-
if (!
|
|
8224
|
-
throw new Error(`Service '${
|
|
8234
|
+
dependentDefinitions.add(consumer);
|
|
8235
|
+
const instance = this._serviceInstances.get(definition);
|
|
8236
|
+
if (!instance) {
|
|
8237
|
+
throw new Error(`Service '${contract.toString()}' has not been instantiated in the '${this._friendlyName}' container.`);
|
|
8225
8238
|
}
|
|
8226
|
-
return
|
|
8227
|
-
}
|
|
8228
|
-
this.
|
|
8239
|
+
return instance;
|
|
8240
|
+
}
|
|
8241
|
+
if (this._parent) {
|
|
8242
|
+
return this._parent._resolveDependency(contract, consumer);
|
|
8243
|
+
}
|
|
8244
|
+
throw new Error(`Service '${contract.toString()}' has not been registered in the '${this._friendlyName}' container.`);
|
|
8245
|
+
}
|
|
8246
|
+
/**
|
|
8247
|
+
* Removes a consumer from the dependent set for a given contract, checking locally first then the parent chain.
|
|
8248
|
+
* @param contract The contract identity.
|
|
8249
|
+
* @param consumer The service definition to remove as a dependent.
|
|
8250
|
+
*/
|
|
8251
|
+
_removeDependentFromChain(contract, consumer) {
|
|
8252
|
+
const definition = this._serviceDefinitions.get(contract);
|
|
8253
|
+
if (definition) {
|
|
8254
|
+
const dependentDefinitions = this._serviceDependents.get(definition);
|
|
8255
|
+
if (dependentDefinitions) {
|
|
8256
|
+
dependentDefinitions.delete(consumer);
|
|
8257
|
+
if (dependentDefinitions.size === 0) {
|
|
8258
|
+
this._serviceDependents.delete(definition);
|
|
8259
|
+
}
|
|
8260
|
+
}
|
|
8261
|
+
return;
|
|
8262
|
+
}
|
|
8263
|
+
this._parent?._removeDependentFromChain(contract, consumer);
|
|
8229
8264
|
}
|
|
8230
8265
|
_removeService(service) {
|
|
8231
8266
|
if (this._isDisposed) {
|
|
8232
8267
|
throw new Error(`'${this._friendlyName}' container is disposed.`);
|
|
8233
8268
|
}
|
|
8234
8269
|
const serviceDependents = this._serviceDependents.get(service);
|
|
8235
|
-
if (serviceDependents) {
|
|
8270
|
+
if (serviceDependents && serviceDependents.size > 0) {
|
|
8236
8271
|
throw new Error(`Service '${service.friendlyName}' has dependents: ${Array.from(serviceDependents)
|
|
8237
8272
|
.map((dependent) => dependent.friendlyName)
|
|
8238
8273
|
.join(", ")}`);
|
|
@@ -8244,27 +8279,24 @@ class ServiceContainer {
|
|
|
8244
8279
|
service.produces?.forEach((contract) => {
|
|
8245
8280
|
this._serviceDefinitions.delete(contract);
|
|
8246
8281
|
});
|
|
8247
|
-
service
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
const dependentDefinitions = this._serviceDependents.get(dependencyDefinition);
|
|
8251
|
-
if (dependentDefinitions) {
|
|
8252
|
-
dependentDefinitions.delete(service);
|
|
8253
|
-
if (dependentDefinitions.size === 0) {
|
|
8254
|
-
this._serviceDependents.delete(dependencyDefinition);
|
|
8255
|
-
}
|
|
8256
|
-
}
|
|
8257
|
-
}
|
|
8282
|
+
// Remove this service as a dependent from each of its consumed dependencies (local or in parent chain).
|
|
8283
|
+
service.consumes?.forEach((contract) => {
|
|
8284
|
+
this._removeDependentFromChain(contract, service);
|
|
8258
8285
|
});
|
|
8259
8286
|
}
|
|
8260
8287
|
/**
|
|
8261
8288
|
* Disposes the service container and all contained services.
|
|
8289
|
+
* Throws if this container is still a parent of any live child containers.
|
|
8262
8290
|
*/
|
|
8263
8291
|
dispose() {
|
|
8292
|
+
if (this._children.size > 0) {
|
|
8293
|
+
throw new Error(`'${this._friendlyName}' container cannot be disposed because it has ${this._children.size} active child container(s).`);
|
|
8294
|
+
}
|
|
8264
8295
|
Array.from(this._serviceInstances.keys()).reverse().forEach(this._removeService.bind(this));
|
|
8265
8296
|
this._serviceInstances.clear();
|
|
8266
8297
|
this._serviceDependents.clear();
|
|
8267
8298
|
this._serviceDefinitions.clear();
|
|
8299
|
+
this._parent?._children.delete(this);
|
|
8268
8300
|
this._isDisposed = true;
|
|
8269
8301
|
}
|
|
8270
8302
|
}
|
|
@@ -8345,7 +8377,7 @@ const ReactContextsWrapper = ({ contexts, children }) => {
|
|
|
8345
8377
|
* @returns A token that can be used to dispose of the tool.
|
|
8346
8378
|
*/
|
|
8347
8379
|
function MakeModularTool(options) {
|
|
8348
|
-
const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [] } = options;
|
|
8380
|
+
const { namespace, containerElement, serviceDefinitions, themeMode, showThemeSelector = true, extensionFeeds = [], parentContainer } = options;
|
|
8349
8381
|
// Create the settings store immediately as it will be exposed to services and through React context.
|
|
8350
8382
|
const settingsStore = new SettingsStore(namespace);
|
|
8351
8383
|
// If a theme mode is provided, just write the setting so it is the active theme.
|
|
@@ -8372,7 +8404,7 @@ function MakeModularTool(options) {
|
|
|
8372
8404
|
// This is the main async initialization.
|
|
8373
8405
|
useEffect(() => {
|
|
8374
8406
|
const initializeExtensionManagerAsync = async () => {
|
|
8375
|
-
const serviceContainer = new ServiceContainer("ModularToolContainer");
|
|
8407
|
+
const serviceContainer = new ServiceContainer("ModularToolContainer", parentContainer);
|
|
8376
8408
|
// Expose the settings store as a service so other services can read/write settings.
|
|
8377
8409
|
await serviceContainer.addServiceAsync({
|
|
8378
8410
|
friendlyName: "Settings Store",
|
|
@@ -8420,7 +8452,7 @@ function MakeModularTool(options) {
|
|
|
8420
8452
|
}
|
|
8421
8453
|
// Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
|
|
8422
8454
|
if (extensionFeeds.length > 0) {
|
|
8423
|
-
const { ExtensionListServiceDefinition } = await import('./extensionsListService-
|
|
8455
|
+
const { ExtensionListServiceDefinition } = await import('./extensionsListService-CBQwBhYh.js');
|
|
8424
8456
|
await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);
|
|
8425
8457
|
}
|
|
8426
8458
|
// Register all external services (that make up a unique tool).
|
|
@@ -8514,38 +8546,955 @@ function MakeModularTool(options) {
|
|
|
8514
8546
|
},
|
|
8515
8547
|
};
|
|
8516
8548
|
}
|
|
8517
|
-
|
|
8518
|
-
// Created by running the following command from the packages/dev/inspector-v2 directory:
|
|
8519
|
-
// npm run makeAvatar https://raw.githubusercontent.com/BabylonJS/Brand-Toolkit/master/babylon_logo/fullColor/babylon_logo_color.png 0.8
|
|
8520
|
-
const BabylonLogoBase64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsSAAALEgHS3X78AAAFXElEQVR4nO2WbWwTdRzHu+EbYmLgpeILwcQXvvCFLwwGxthG1/Z6d33Ag22FDXAE98wixsCbSiS+4IUhxoQYoyiSGKcogsjaa9c9dw9sbdfrbevW611nYTDYliCRsbv7mrt2sPiGUTZf8Uua9P7/u9/383v4//MzGJ5bjgaDIQ8Ms66FYdYZ/m+D250PgyF/GUy+trb2wshEnfmPvKnG3fukJoZ+vI912vqapRsGg+481cy8I9RSbTONNkw32pXJatMvCcvmN7Q9t5ad1QSBJuzOpHvmo7JXkrX01zfq6Yd3mhxIHrHIQuUOee5EJR4MtN0D8Bnm5jbo37nd+c/UH1jmINXcvF6scxxL1VJ3Zo86kKol5UTVTlmsIXH3p68g35uXASgAoCqyCKBKy4Lup0UL4Bn6Q2piaKmWjM0etSNdTyvCoV2LwsES9daXbixMCZomoKpQFFkFsKg9aUtzv//QM7ln6zYdIlu6lUVuyLw8e7zsLbGGunqrXq8zhMMmOVFZqKQ/+QD3w8GMsB6xAmi/rN0PB+X0qQZ5psGGG/W0LNXT5/9x731tRSDubKquWJlN3H7H7N1GO1K1hDy5v1BJNe/BvPcilIUHj4TVxUWosqJnYMlmfz0H4X0jhANF8l915OJ8oxORCnv0vHHfi0+EcGcBBpwVW9hSaqGdIBSu0qrOXvgCyvzsUsgZ4WVR6zDKYxD59g3cPvspwuVmpZ2g4DPR0khNzcYVA3QwzGY/QT/wFpnBMuVqShKwsCSQDVZVVaQ9LG519z7qgyXAhwCmxnh4TJTSZiThM1NioKpqw4oBWKdzi89CLbDFZrSVudRIfzti0UFMpyXIioJ5fhSDH36Ma9uL0LrTiPDJU/hbSkFWZMxMpyGK4xjv64KPsCmBUit8ZlrqqqjYmBvAXpcaG+pFbGQA0egAOF8rfCZSF/dZaC06/Ll1B9qZciT5CIQED0mawMRg7+oBcEM9OgA/FsbQby1gjQS8JgreUiu8pSQ8Riv8tt2YHO6HKMbXEiCE0KWf4Skh0ElT4A+Q4CpJBAgrWGo3JkMDawzADyPcehlhFwnx0C5IdRRSdSSE/TsRqi5HIhqCmBxfA4DBbh0gFulHjLuO8bZLSJyqh3jIiGS1CeKZE0gNd0FMxiFOaj0Qx8RAT+4AHQyzmTUvAVSo2gngR0PgQkEdguND4LghxC9+A9F7EWI6iaQ0qYsnJ2JITUtIhAe1JtUB2Kc9hl3Oii1+M7XgNRIKW2pVe44fx0iXD6MTI/pxjA0HwUX6wcejSE4JEBM8knFOr3/qpoR4dzt6m4/BW2JW/CYSfgstjVQ8xUV0hWE2BQjb3SBh0zpe9hQUKz7Kif4zn4MbDoKfiIAL92E00p8RFsYwdVOCwIVw/fRp/WheKyjR4OVB0ok2whb17FvBVbz8hU6GeZMl6MudFhu6CJsWjezZXqwEXJUYuvA9eH4IY+MRSGlBb7zId+cQeK8MrduKFO8ui9xttaPDQi+yhO3bPlf1q8t9P5X5bU7Cb6EjQasdAQuteIpMi55Co9pZ14ARz1XwrX+g8/ARtBYUq57iUrmDsKk9hF2re0e7nXk3Z2FoA4khM5D0NjPrfVZ7k99E3Q6SDu0mVDwFJTJrpuE1EmgtKJHbzJTSp+2ZSYGlHS4Aejm1oWaptDlZiz6SZU+Hy/UyS9jOBsz0Qo/VDi3VPiMh95IO+M3UPdbqONnnanhpKYBVm5RhMOS5CwtfcGdH8Q7Hnre9FortJvQ6K6yF+tG/t/J1bU+LVofOJe1PMs3p8kHTRzvKtB7JPuatmfB/TU8vHgutarpz7Y/nZsjR/gUhEcwHmuaM9wAAAABJRU5ErkJggg==";
|
|
8521
|
-
const BabylonWebResources = {
|
|
8522
|
-
homepage: "https://www.babylonjs.com",
|
|
8523
|
-
repository: "https://github.com/BabylonJS/Babylon.js",
|
|
8524
|
-
bugs: "https://github.com/BabylonJS/Babylon.js/issues",
|
|
8525
|
-
author: {
|
|
8526
|
-
name: "Babylon.js",
|
|
8527
|
-
avatar: BabylonLogoBase64,
|
|
8528
|
-
},
|
|
8529
|
-
};
|
|
8549
|
+
|
|
8550
|
+
// Created by running the following command from the packages/dev/inspector-v2 directory:
|
|
8551
|
+
// npm run makeAvatar https://raw.githubusercontent.com/BabylonJS/Brand-Toolkit/master/babylon_logo/fullColor/babylon_logo_color.png 0.8
|
|
8552
|
+
const BabylonLogoBase64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsSAAALEgHS3X78AAAFXElEQVR4nO2WbWwTdRzHu+EbYmLgpeILwcQXvvCFLwwGxthG1/Z6d33Ag22FDXAE98wixsCbSiS+4IUhxoQYoyiSGKcogsjaa9c9dw9sbdfrbevW611nYTDYliCRsbv7mrt2sPiGUTZf8Uua9P7/u9/383v4//MzGJ5bjgaDIQ8Ms66FYdYZ/m+D250PgyF/GUy+trb2wshEnfmPvKnG3fukJoZ+vI912vqapRsGg+481cy8I9RSbTONNkw32pXJatMvCcvmN7Q9t5ad1QSBJuzOpHvmo7JXkrX01zfq6Yd3mhxIHrHIQuUOee5EJR4MtN0D8Bnm5jbo37nd+c/UH1jmINXcvF6scxxL1VJ3Zo86kKol5UTVTlmsIXH3p68g35uXASgAoCqyCKBKy4Lup0UL4Bn6Q2piaKmWjM0etSNdTyvCoV2LwsES9daXbixMCZomoKpQFFkFsKg9aUtzv//QM7ln6zYdIlu6lUVuyLw8e7zsLbGGunqrXq8zhMMmOVFZqKQ/+QD3w8GMsB6xAmi/rN0PB+X0qQZ5psGGG/W0LNXT5/9x731tRSDubKquWJlN3H7H7N1GO1K1hDy5v1BJNe/BvPcilIUHj4TVxUWosqJnYMlmfz0H4X0jhANF8l915OJ8oxORCnv0vHHfi0+EcGcBBpwVW9hSaqGdIBSu0qrOXvgCyvzsUsgZ4WVR6zDKYxD59g3cPvspwuVmpZ2g4DPR0khNzcYVA3QwzGY/QT/wFpnBMuVqShKwsCSQDVZVVaQ9LG519z7qgyXAhwCmxnh4TJTSZiThM1NioKpqw4oBWKdzi89CLbDFZrSVudRIfzti0UFMpyXIioJ5fhSDH36Ma9uL0LrTiPDJU/hbSkFWZMxMpyGK4xjv64KPsCmBUit8ZlrqqqjYmBvAXpcaG+pFbGQA0egAOF8rfCZSF/dZaC06/Ll1B9qZciT5CIQED0mawMRg7+oBcEM9OgA/FsbQby1gjQS8JgreUiu8pSQ8Riv8tt2YHO6HKMbXEiCE0KWf4Skh0ElT4A+Q4CpJBAgrWGo3JkMDawzADyPcehlhFwnx0C5IdRRSdSSE/TsRqi5HIhqCmBxfA4DBbh0gFulHjLuO8bZLSJyqh3jIiGS1CeKZE0gNd0FMxiFOaj0Qx8RAT+4AHQyzmTUvAVSo2gngR0PgQkEdguND4LghxC9+A9F7EWI6iaQ0qYsnJ2JITUtIhAe1JtUB2Kc9hl3Oii1+M7XgNRIKW2pVe44fx0iXD6MTI/pxjA0HwUX6wcejSE4JEBM8knFOr3/qpoR4dzt6m4/BW2JW/CYSfgstjVQ8xUV0hWE2BQjb3SBh0zpe9hQUKz7Kif4zn4MbDoKfiIAL92E00p8RFsYwdVOCwIVw/fRp/WheKyjR4OVB0ok2whb17FvBVbz8hU6GeZMl6MudFhu6CJsWjezZXqwEXJUYuvA9eH4IY+MRSGlBb7zId+cQeK8MrduKFO8ui9xttaPDQi+yhO3bPlf1q8t9P5X5bU7Cb6EjQasdAQuteIpMi55Co9pZ14ARz1XwrX+g8/ARtBYUq57iUrmDsKk9hF2re0e7nXk3Z2FoA4khM5D0NjPrfVZ7k99E3Q6SDu0mVDwFJTJrpuE1EmgtKJHbzJTSp+2ZSYGlHS4Aejm1oWaptDlZiz6SZU+Hy/UyS9jOBsz0Qo/VDi3VPiMh95IO+M3UPdbqONnnanhpKYBVm5RhMOS5CwtfcGdH8Q7Hnre9FortJvQ6K6yF+tG/t/J1bU+LVofOJe1PMs3p8kHTRzvKtB7JPuatmfB/TU8vHgutarpz7Y/nZsjR/gUhEcwHmuaM9wAAAABJRU5ErkJggg==";
|
|
8553
|
+
const BabylonWebResources = {
|
|
8554
|
+
homepage: "https://www.babylonjs.com",
|
|
8555
|
+
repository: "https://github.com/BabylonJS/Babylon.js",
|
|
8556
|
+
bugs: "https://github.com/BabylonJS/Babylon.js/issues",
|
|
8557
|
+
author: {
|
|
8558
|
+
name: "Babylon.js",
|
|
8559
|
+
avatar: BabylonLogoBase64,
|
|
8560
|
+
},
|
|
8561
|
+
};
|
|
8562
|
+
/**
|
|
8563
|
+
* Well-known default built in extensions for the Inspector.
|
|
8564
|
+
*/
|
|
8565
|
+
const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
|
|
8566
|
+
{
|
|
8567
|
+
name: "Quick Creation Tools (Preview)",
|
|
8568
|
+
description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
|
|
8569
|
+
keywords: ["creation", "tools"],
|
|
8570
|
+
...BabylonWebResources,
|
|
8571
|
+
getExtensionModuleAsync: async () => await import('./quickCreateToolsService-C38aK2nP.js'),
|
|
8572
|
+
},
|
|
8573
|
+
{
|
|
8574
|
+
name: "Reflector",
|
|
8575
|
+
description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
|
|
8576
|
+
keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
|
|
8577
|
+
...BabylonWebResources,
|
|
8578
|
+
getExtensionModuleAsync: async () => await import('./reflectorService-Bs9E2OMh.js'),
|
|
8579
|
+
},
|
|
8580
|
+
]);
|
|
8581
|
+
|
|
8582
|
+
/**
|
|
8583
|
+
* The service identity for the inspectable command registry.
|
|
8584
|
+
*/
|
|
8585
|
+
const InspectableCommandRegistryIdentity = Symbol("InspectableCommandRegistry");
|
|
8586
|
+
|
|
8587
|
+
const UniqueIdArg = {
|
|
8588
|
+
name: "uniqueId",
|
|
8589
|
+
description: "The uniqueId of the entity to query. Omit to list all entities of this type.",
|
|
8590
|
+
required: false,
|
|
8591
|
+
};
|
|
8592
|
+
const SyntheticUniqueIds = new WeakMap();
|
|
8593
|
+
function GetEntityId(entity) {
|
|
8594
|
+
if ("uniqueId" in entity && typeof entity.uniqueId === "number") {
|
|
8595
|
+
return entity.uniqueId;
|
|
8596
|
+
}
|
|
8597
|
+
let id = SyntheticUniqueIds.get(entity);
|
|
8598
|
+
if (!id) {
|
|
8599
|
+
SyntheticUniqueIds.set(entity, (id = UniqueIdGenerator.UniqueId));
|
|
8600
|
+
}
|
|
8601
|
+
return id;
|
|
8602
|
+
}
|
|
8603
|
+
function NodeSummary(entity) {
|
|
8604
|
+
return {
|
|
8605
|
+
uniqueId: entity.uniqueId,
|
|
8606
|
+
name: entity.name,
|
|
8607
|
+
className: entity.getClassName(),
|
|
8608
|
+
parentId: entity.parent?.uniqueId,
|
|
8609
|
+
};
|
|
8610
|
+
}
|
|
8611
|
+
function NamedSummary(entity) {
|
|
8612
|
+
return {
|
|
8613
|
+
uniqueId: entity.uniqueId,
|
|
8614
|
+
name: entity.name,
|
|
8615
|
+
className: entity.getClassName(),
|
|
8616
|
+
};
|
|
8617
|
+
}
|
|
8618
|
+
function MinimalSummary(entity) {
|
|
8619
|
+
return {
|
|
8620
|
+
uniqueId: entity.uniqueId,
|
|
8621
|
+
name: entity.name,
|
|
8622
|
+
};
|
|
8623
|
+
}
|
|
8624
|
+
function MakeQueryCommand(collection, sceneContext) {
|
|
8625
|
+
return {
|
|
8626
|
+
id: collection.id,
|
|
8627
|
+
description: collection.description,
|
|
8628
|
+
args: [UniqueIdArg],
|
|
8629
|
+
executeAsync: async (args) => {
|
|
8630
|
+
const scene = sceneContext.currentScene;
|
|
8631
|
+
if (!scene) {
|
|
8632
|
+
throw new Error("No active scene.");
|
|
8633
|
+
}
|
|
8634
|
+
const entities = collection.getEntities(scene);
|
|
8635
|
+
if (!entities) {
|
|
8636
|
+
return JSON.stringify([], null, 2);
|
|
8637
|
+
}
|
|
8638
|
+
if (!args.uniqueId) {
|
|
8639
|
+
return JSON.stringify(entities.map((e) => collection.getSummary(e)), null, 2);
|
|
8640
|
+
}
|
|
8641
|
+
const id = parseInt(args.uniqueId, 10);
|
|
8642
|
+
if (isNaN(id)) {
|
|
8643
|
+
throw new Error("uniqueId must be a number.");
|
|
8644
|
+
}
|
|
8645
|
+
const entity = entities.find((e) => collection.getUniqueId(e) === id);
|
|
8646
|
+
if (!entity) {
|
|
8647
|
+
throw new Error(`No ${collection.id.replace("query-", "")} found with uniqueId ${id}.`);
|
|
8648
|
+
}
|
|
8649
|
+
return JSON.stringify(collection.serialize ? collection.serialize(entity) : collection.getSummary(entity), null, 2);
|
|
8650
|
+
},
|
|
8651
|
+
};
|
|
8652
|
+
}
|
|
8653
|
+
/**
|
|
8654
|
+
* Service that registers CLI commands for querying scene entities by uniqueId.
|
|
8655
|
+
* When uniqueId is omitted, returns a summary list of all entities of that type.
|
|
8656
|
+
*/
|
|
8657
|
+
const EntityQueryServiceDefinition = {
|
|
8658
|
+
friendlyName: "Entity Query Service",
|
|
8659
|
+
consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
|
|
8660
|
+
factory: (commandRegistry, sceneContext) => {
|
|
8661
|
+
const collections = [
|
|
8662
|
+
{
|
|
8663
|
+
id: "query-mesh",
|
|
8664
|
+
description: "List meshes, or query a specific mesh by uniqueId.",
|
|
8665
|
+
getEntities: (scene) => scene.meshes,
|
|
8666
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8667
|
+
getSummary: NodeSummary,
|
|
8668
|
+
serialize: (e) => e.serialize(),
|
|
8669
|
+
},
|
|
8670
|
+
{
|
|
8671
|
+
id: "query-light",
|
|
8672
|
+
description: "List lights, or query a specific light by uniqueId.",
|
|
8673
|
+
getEntities: (scene) => scene.lights,
|
|
8674
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8675
|
+
getSummary: NodeSummary,
|
|
8676
|
+
serialize: (e) => e.serialize(),
|
|
8677
|
+
},
|
|
8678
|
+
{
|
|
8679
|
+
id: "query-camera",
|
|
8680
|
+
description: "List cameras, or query a specific camera by uniqueId.",
|
|
8681
|
+
getEntities: (scene) => scene.cameras,
|
|
8682
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8683
|
+
getSummary: NodeSummary,
|
|
8684
|
+
serialize: (e) => e.serialize(),
|
|
8685
|
+
},
|
|
8686
|
+
{
|
|
8687
|
+
id: "query-transformNode",
|
|
8688
|
+
description: "List transform nodes, or query a specific transform node by uniqueId.",
|
|
8689
|
+
getEntities: (scene) => scene.transformNodes,
|
|
8690
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8691
|
+
getSummary: NodeSummary,
|
|
8692
|
+
serialize: (e) => e.serialize(),
|
|
8693
|
+
},
|
|
8694
|
+
{
|
|
8695
|
+
id: "query-material",
|
|
8696
|
+
description: "List materials, or query a specific material by uniqueId.",
|
|
8697
|
+
getEntities: (scene) => scene.materials,
|
|
8698
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8699
|
+
getSummary: NamedSummary,
|
|
8700
|
+
serialize: (e) => e.serialize(),
|
|
8701
|
+
},
|
|
8702
|
+
{
|
|
8703
|
+
id: "query-texture",
|
|
8704
|
+
description: "List textures, or query a specific texture by uniqueId.",
|
|
8705
|
+
getEntities: (scene) => scene.textures,
|
|
8706
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8707
|
+
getSummary: NamedSummary,
|
|
8708
|
+
serialize: (e) => e.serialize(),
|
|
8709
|
+
},
|
|
8710
|
+
{
|
|
8711
|
+
id: "query-skeleton",
|
|
8712
|
+
description: "List skeletons, or query a specific skeleton by uniqueId.",
|
|
8713
|
+
getEntities: (scene) => scene.skeletons,
|
|
8714
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8715
|
+
getSummary: NamedSummary,
|
|
8716
|
+
serialize: (e) => e.serialize(),
|
|
8717
|
+
},
|
|
8718
|
+
{
|
|
8719
|
+
id: "query-geometry",
|
|
8720
|
+
description: "List geometries, or query a specific geometry by uniqueId.",
|
|
8721
|
+
getEntities: (scene) => scene.geometries,
|
|
8722
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8723
|
+
getSummary: MinimalSummary,
|
|
8724
|
+
serialize: (e) => e.serialize(),
|
|
8725
|
+
},
|
|
8726
|
+
{
|
|
8727
|
+
id: "query-animation",
|
|
8728
|
+
description: "List animations, or query a specific animation by uniqueId.",
|
|
8729
|
+
getEntities: (scene) => scene.animations,
|
|
8730
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8731
|
+
getSummary: MinimalSummary,
|
|
8732
|
+
serialize: (e) => e.serialize(),
|
|
8733
|
+
},
|
|
8734
|
+
{
|
|
8735
|
+
id: "query-animationGroup",
|
|
8736
|
+
description: "List animation groups, or query a specific animation group by uniqueId.",
|
|
8737
|
+
getEntities: (scene) => scene.animationGroups,
|
|
8738
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8739
|
+
getSummary: NamedSummary,
|
|
8740
|
+
serialize: (e) => e.serialize(),
|
|
8741
|
+
},
|
|
8742
|
+
{
|
|
8743
|
+
id: "query-particleSystem",
|
|
8744
|
+
description: "List particle systems, or query a specific particle system by uniqueId.",
|
|
8745
|
+
getEntities: (scene) => scene.particleSystems,
|
|
8746
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8747
|
+
getSummary: NamedSummary,
|
|
8748
|
+
serialize: (e) => e.serialize(false),
|
|
8749
|
+
},
|
|
8750
|
+
{
|
|
8751
|
+
id: "query-morphTargetManager",
|
|
8752
|
+
description: "List morph target managers, or query a specific morph target manager by uniqueId.",
|
|
8753
|
+
getEntities: (scene) => scene.morphTargetManagers,
|
|
8754
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8755
|
+
getSummary: MinimalSummary,
|
|
8756
|
+
serialize: (e) => e.serialize(),
|
|
8757
|
+
},
|
|
8758
|
+
{
|
|
8759
|
+
id: "query-multiMaterial",
|
|
8760
|
+
description: "List multi-materials, or query a specific multi-material by uniqueId.",
|
|
8761
|
+
getEntities: (scene) => scene.multiMaterials,
|
|
8762
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8763
|
+
getSummary: NamedSummary,
|
|
8764
|
+
serialize: (e) => e.serialize(),
|
|
8765
|
+
},
|
|
8766
|
+
{
|
|
8767
|
+
id: "query-postProcess",
|
|
8768
|
+
description: "List post-processes, or query a specific post-process by uniqueId.",
|
|
8769
|
+
getEntities: (scene) => scene.postProcesses,
|
|
8770
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8771
|
+
getSummary: NamedSummary,
|
|
8772
|
+
serialize: (e) => e.serialize(),
|
|
8773
|
+
},
|
|
8774
|
+
{
|
|
8775
|
+
id: "query-frameGraph",
|
|
8776
|
+
description: "List frame graphs, or query a specific frame graph by uniqueId.",
|
|
8777
|
+
getEntities: (scene) => scene.frameGraphs,
|
|
8778
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8779
|
+
getSummary: NamedSummary,
|
|
8780
|
+
},
|
|
8781
|
+
{
|
|
8782
|
+
id: "query-effectLayer",
|
|
8783
|
+
description: "List effect layers, or query a specific effect layer by uniqueId.",
|
|
8784
|
+
getEntities: (scene) => scene.effectLayers,
|
|
8785
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8786
|
+
getSummary: NamedSummary,
|
|
8787
|
+
serialize: (e) => e.serialize?.(),
|
|
8788
|
+
},
|
|
8789
|
+
{
|
|
8790
|
+
id: "query-spriteManager",
|
|
8791
|
+
description: "List sprite managers, or query a specific sprite manager by uniqueId.",
|
|
8792
|
+
getEntities: (scene) => scene.spriteManagers,
|
|
8793
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8794
|
+
getSummary: MinimalSummary,
|
|
8795
|
+
serialize: (e) => e.serialize(false),
|
|
8796
|
+
},
|
|
8797
|
+
{
|
|
8798
|
+
id: "query-sound",
|
|
8799
|
+
description: "List sounds in the main sound track, or query a specific sound by uniqueId.",
|
|
8800
|
+
getEntities: (scene) => scene.mainSoundTrack?.soundCollection ?? [],
|
|
8801
|
+
getUniqueId: (e) => GetEntityId(e),
|
|
8802
|
+
getSummary: (e) => ({ uniqueId: GetEntityId(e), name: e.name, className: e.getClassName() }),
|
|
8803
|
+
serialize: (e) => e.serialize(),
|
|
8804
|
+
},
|
|
8805
|
+
{
|
|
8806
|
+
id: "query-renderingPipeline",
|
|
8807
|
+
description: "List rendering pipelines, or query a specific rendering pipeline by uniqueId.",
|
|
8808
|
+
getEntities: (scene) => scene.postProcessRenderPipelineManager?.supportedPipelines ?? [],
|
|
8809
|
+
getUniqueId: (e) => e.uniqueId,
|
|
8810
|
+
getSummary: NamedSummary,
|
|
8811
|
+
},
|
|
8812
|
+
];
|
|
8813
|
+
const registrations = collections.map((col) => commandRegistry.addCommand(MakeQueryCommand(col, sceneContext)));
|
|
8814
|
+
return {
|
|
8815
|
+
dispose: () => {
|
|
8816
|
+
for (const reg of registrations) {
|
|
8817
|
+
reg.dispose();
|
|
8818
|
+
}
|
|
8819
|
+
},
|
|
8820
|
+
};
|
|
8821
|
+
},
|
|
8822
|
+
};
|
|
8823
|
+
|
|
8824
|
+
/**
|
|
8825
|
+
* The service identity for the CLI connection status.
|
|
8826
|
+
*/
|
|
8827
|
+
const CliConnectionStatusIdentity = Symbol("CliConnectionStatus");
|
|
8828
|
+
|
|
8829
|
+
/**
|
|
8830
|
+
* Creates the service definition for the InspectableBridgeService.
|
|
8831
|
+
* @param options The options for connecting to the bridge.
|
|
8832
|
+
* @returns A service definition that produces an IInspectableCommandRegistry.
|
|
8833
|
+
*/
|
|
8834
|
+
function MakeInspectableBridgeServiceDefinition(options) {
|
|
8835
|
+
return {
|
|
8836
|
+
friendlyName: "Inspectable Bridge Service",
|
|
8837
|
+
produces: [InspectableCommandRegistryIdentity, CliConnectionStatusIdentity],
|
|
8838
|
+
factory: () => {
|
|
8839
|
+
const commands = new Map();
|
|
8840
|
+
let ws = null;
|
|
8841
|
+
let reconnectTimer = null;
|
|
8842
|
+
let disposed = false;
|
|
8843
|
+
let connected = false;
|
|
8844
|
+
const onConnectionStatusChanged = new Observable();
|
|
8845
|
+
function setConnected(value) {
|
|
8846
|
+
if (connected !== value) {
|
|
8847
|
+
connected = value;
|
|
8848
|
+
onConnectionStatusChanged.notifyObservers(value);
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
function sendToBridge(message) {
|
|
8852
|
+
ws?.send(JSON.stringify(message));
|
|
8853
|
+
}
|
|
8854
|
+
function connect() {
|
|
8855
|
+
if (disposed) {
|
|
8856
|
+
return;
|
|
8857
|
+
}
|
|
8858
|
+
try {
|
|
8859
|
+
ws = new WebSocket(`ws://127.0.0.1:${options.port}`);
|
|
8860
|
+
}
|
|
8861
|
+
catch {
|
|
8862
|
+
scheduleReconnect();
|
|
8863
|
+
return;
|
|
8864
|
+
}
|
|
8865
|
+
ws.onopen = () => {
|
|
8866
|
+
setConnected(true);
|
|
8867
|
+
sendToBridge({ type: "register", name: options.name });
|
|
8868
|
+
};
|
|
8869
|
+
ws.onmessage = (event) => {
|
|
8870
|
+
try {
|
|
8871
|
+
const message = JSON.parse(event.data);
|
|
8872
|
+
void handleMessage(message);
|
|
8873
|
+
}
|
|
8874
|
+
catch {
|
|
8875
|
+
Logger.Warn("InspectableBridgeService: Failed to parse message from bridge.");
|
|
8876
|
+
}
|
|
8877
|
+
};
|
|
8878
|
+
ws.onclose = () => {
|
|
8879
|
+
ws = null;
|
|
8880
|
+
setConnected(false);
|
|
8881
|
+
scheduleReconnect();
|
|
8882
|
+
};
|
|
8883
|
+
ws.onerror = () => {
|
|
8884
|
+
// onclose will fire after onerror, which handles reconnection.
|
|
8885
|
+
};
|
|
8886
|
+
}
|
|
8887
|
+
function scheduleReconnect() {
|
|
8888
|
+
if (disposed || reconnectTimer !== null) {
|
|
8889
|
+
return;
|
|
8890
|
+
}
|
|
8891
|
+
reconnectTimer = setTimeout(() => {
|
|
8892
|
+
reconnectTimer = null;
|
|
8893
|
+
connect();
|
|
8894
|
+
}, 3000);
|
|
8895
|
+
}
|
|
8896
|
+
async function handleMessage(message) {
|
|
8897
|
+
switch (message.type) {
|
|
8898
|
+
case "listCommands": {
|
|
8899
|
+
const commandList = Array.from(commands.values()).map((cmd) => ({
|
|
8900
|
+
id: cmd.id,
|
|
8901
|
+
description: cmd.description,
|
|
8902
|
+
args: cmd.args,
|
|
8903
|
+
}));
|
|
8904
|
+
sendToBridge({
|
|
8905
|
+
type: "commandListResponse",
|
|
8906
|
+
requestId: message.requestId,
|
|
8907
|
+
commands: commandList,
|
|
8908
|
+
});
|
|
8909
|
+
break;
|
|
8910
|
+
}
|
|
8911
|
+
case "execCommand": {
|
|
8912
|
+
const command = commands.get(message.commandId);
|
|
8913
|
+
if (!command) {
|
|
8914
|
+
sendToBridge({
|
|
8915
|
+
type: "commandResponse",
|
|
8916
|
+
requestId: message.requestId,
|
|
8917
|
+
error: `Unknown command: ${message.commandId}`,
|
|
8918
|
+
});
|
|
8919
|
+
break;
|
|
8920
|
+
}
|
|
8921
|
+
try {
|
|
8922
|
+
const result = await command.executeAsync(message.args);
|
|
8923
|
+
sendToBridge({
|
|
8924
|
+
type: "commandResponse",
|
|
8925
|
+
requestId: message.requestId,
|
|
8926
|
+
result,
|
|
8927
|
+
});
|
|
8928
|
+
}
|
|
8929
|
+
catch (error) {
|
|
8930
|
+
sendToBridge({
|
|
8931
|
+
type: "commandResponse",
|
|
8932
|
+
requestId: message.requestId,
|
|
8933
|
+
error: String(error),
|
|
8934
|
+
});
|
|
8935
|
+
}
|
|
8936
|
+
break;
|
|
8937
|
+
}
|
|
8938
|
+
}
|
|
8939
|
+
}
|
|
8940
|
+
// Initiate connection.
|
|
8941
|
+
connect();
|
|
8942
|
+
const registry = {
|
|
8943
|
+
addCommand(descriptor) {
|
|
8944
|
+
if (commands.has(descriptor.id)) {
|
|
8945
|
+
throw new Error(`Command '${descriptor.id}' is already registered.`);
|
|
8946
|
+
}
|
|
8947
|
+
commands.set(descriptor.id, descriptor);
|
|
8948
|
+
return {
|
|
8949
|
+
dispose: () => {
|
|
8950
|
+
commands.delete(descriptor.id);
|
|
8951
|
+
},
|
|
8952
|
+
};
|
|
8953
|
+
},
|
|
8954
|
+
get isConnected() {
|
|
8955
|
+
return connected;
|
|
8956
|
+
},
|
|
8957
|
+
onConnectionStatusChanged,
|
|
8958
|
+
dispose: () => {
|
|
8959
|
+
disposed = true;
|
|
8960
|
+
if (reconnectTimer !== null) {
|
|
8961
|
+
clearTimeout(reconnectTimer);
|
|
8962
|
+
reconnectTimer = null;
|
|
8963
|
+
}
|
|
8964
|
+
commands.clear();
|
|
8965
|
+
setConnected(false);
|
|
8966
|
+
onConnectionStatusChanged.clear();
|
|
8967
|
+
if (ws) {
|
|
8968
|
+
ws.onclose = null;
|
|
8969
|
+
ws.close();
|
|
8970
|
+
ws = null;
|
|
8971
|
+
}
|
|
8972
|
+
},
|
|
8973
|
+
};
|
|
8974
|
+
return registry;
|
|
8975
|
+
},
|
|
8976
|
+
};
|
|
8977
|
+
}
|
|
8978
|
+
|
|
8979
|
+
/**
|
|
8980
|
+
* Service that registers CLI commands for performance tracing using the PerformanceViewerCollector.
|
|
8981
|
+
* start-perf-trace begins collecting data, stop-perf-trace stops and returns the collected data as JSON.
|
|
8982
|
+
*/
|
|
8983
|
+
const PerfTraceCommandServiceDefinition = {
|
|
8984
|
+
friendlyName: "Perf Trace Command Service",
|
|
8985
|
+
consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
|
|
8986
|
+
factory: (commandRegistry, sceneContext) => {
|
|
8987
|
+
let perfCollector;
|
|
8988
|
+
const startReg = commandRegistry.addCommand({
|
|
8989
|
+
id: "start-perf-trace",
|
|
8990
|
+
description: "Start collecting performance trace data.",
|
|
8991
|
+
executeAsync: async () => {
|
|
8992
|
+
const scene = sceneContext.currentScene;
|
|
8993
|
+
if (!scene) {
|
|
8994
|
+
throw new Error("No active scene.");
|
|
8995
|
+
}
|
|
8996
|
+
if (perfCollector?.isStarted) {
|
|
8997
|
+
return "Performance trace is already running.";
|
|
8998
|
+
}
|
|
8999
|
+
perfCollector = scene.getPerfCollector();
|
|
9000
|
+
perfCollector.stop();
|
|
9001
|
+
perfCollector.clear(false);
|
|
9002
|
+
perfCollector.addCollectionStrategies(...DefaultPerfStrategies);
|
|
9003
|
+
perfCollector.start(true);
|
|
9004
|
+
return "Performance trace started.";
|
|
9005
|
+
},
|
|
9006
|
+
});
|
|
9007
|
+
const stopReg = commandRegistry.addCommand({
|
|
9008
|
+
id: "stop-perf-trace",
|
|
9009
|
+
description: "Stop collecting performance trace data and return the results as JSON.",
|
|
9010
|
+
executeAsync: async () => {
|
|
9011
|
+
if (!perfCollector || !perfCollector.isStarted) {
|
|
9012
|
+
throw new Error("Performance trace is not running. Run start-perf-trace first.");
|
|
9013
|
+
}
|
|
9014
|
+
perfCollector.stop();
|
|
9015
|
+
const datasets = perfCollector.datasets;
|
|
9016
|
+
const ids = datasets.ids;
|
|
9017
|
+
const rawData = datasets.data.subarray(0, datasets.data.itemLength);
|
|
9018
|
+
const sliceSize = ids.length + PerformanceViewerCollector.SliceDataOffset;
|
|
9019
|
+
const samples = [];
|
|
9020
|
+
for (let i = 0; i < rawData.length; i += sliceSize) {
|
|
9021
|
+
const timestamp = rawData[i];
|
|
9022
|
+
const sample = { timestamp };
|
|
9023
|
+
for (let j = 0; j < ids.length; j++) {
|
|
9024
|
+
sample[ids[j]] = rawData[i + PerformanceViewerCollector.SliceDataOffset + j];
|
|
9025
|
+
}
|
|
9026
|
+
samples.push(sample);
|
|
9027
|
+
}
|
|
9028
|
+
perfCollector.clear(false);
|
|
9029
|
+
perfCollector = undefined;
|
|
9030
|
+
return JSON.stringify({ strategies: ids, sampleCount: samples.length, samples }, null, 2);
|
|
9031
|
+
},
|
|
9032
|
+
});
|
|
9033
|
+
return {
|
|
9034
|
+
dispose: () => {
|
|
9035
|
+
startReg.dispose();
|
|
9036
|
+
stopReg.dispose();
|
|
9037
|
+
if (perfCollector?.isStarted) {
|
|
9038
|
+
perfCollector.stop();
|
|
9039
|
+
}
|
|
9040
|
+
perfCollector = undefined;
|
|
9041
|
+
},
|
|
9042
|
+
};
|
|
9043
|
+
},
|
|
9044
|
+
};
|
|
9045
|
+
|
|
9046
|
+
/**
|
|
9047
|
+
* Service that registers a CLI command for capturing a screenshot of the scene.
|
|
9048
|
+
* Returns the image as a base64 data string, suitable for consumption by AI agents.
|
|
9049
|
+
*/
|
|
9050
|
+
const ScreenshotCommandServiceDefinition = {
|
|
9051
|
+
friendlyName: "Screenshot Command Service",
|
|
9052
|
+
consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
|
|
9053
|
+
factory: (commandRegistry, sceneContext) => {
|
|
9054
|
+
const registration = commandRegistry.addCommand({
|
|
9055
|
+
id: "take-screenshot",
|
|
9056
|
+
description: "Capture a screenshot of the scene. Returns base64-encoded PNG data.",
|
|
9057
|
+
args: [
|
|
9058
|
+
{
|
|
9059
|
+
name: "cameraUniqueId",
|
|
9060
|
+
description: "The uniqueId of the camera to use. Defaults to the active camera.",
|
|
9061
|
+
required: false,
|
|
9062
|
+
},
|
|
9063
|
+
{
|
|
9064
|
+
name: "width",
|
|
9065
|
+
description: "Screenshot width in pixels. When set, uses custom size mode.",
|
|
9066
|
+
required: false,
|
|
9067
|
+
},
|
|
9068
|
+
{
|
|
9069
|
+
name: "height",
|
|
9070
|
+
description: "Screenshot height in pixels. When set, uses custom size mode.",
|
|
9071
|
+
required: false,
|
|
9072
|
+
},
|
|
9073
|
+
{
|
|
9074
|
+
name: "precision",
|
|
9075
|
+
description: "Resolution multiplier (e.g. 2 for double resolution). Defaults to 1.",
|
|
9076
|
+
required: false,
|
|
9077
|
+
},
|
|
9078
|
+
],
|
|
9079
|
+
executeAsync: async (args) => {
|
|
9080
|
+
const scene = sceneContext.currentScene;
|
|
9081
|
+
if (!scene) {
|
|
9082
|
+
throw new Error("No active scene.");
|
|
9083
|
+
}
|
|
9084
|
+
const engine = scene.getEngine();
|
|
9085
|
+
// Resolve camera: explicit uniqueId, or active/frame-graph camera.
|
|
9086
|
+
let camera;
|
|
9087
|
+
if (args.cameraUniqueId) {
|
|
9088
|
+
const cameraId = parseInt(args.cameraUniqueId, 10);
|
|
9089
|
+
if (isNaN(cameraId)) {
|
|
9090
|
+
throw new Error("cameraUniqueId must be a number.");
|
|
9091
|
+
}
|
|
9092
|
+
camera = scene.cameras.find((c) => c.uniqueId === cameraId);
|
|
9093
|
+
if (!camera) {
|
|
9094
|
+
throw new Error(`No camera found with uniqueId ${cameraId}.`);
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
9097
|
+
else {
|
|
9098
|
+
camera = scene.frameGraph ? FrameGraphUtils.FindMainCamera(scene.frameGraph) : scene.activeCamera;
|
|
9099
|
+
}
|
|
9100
|
+
if (!camera) {
|
|
9101
|
+
throw new Error("No camera available for screenshot.");
|
|
9102
|
+
}
|
|
9103
|
+
const precision = args.precision !== undefined ? Number(args.precision) : 1;
|
|
9104
|
+
if (!Number.isFinite(precision) || precision <= 0) {
|
|
9105
|
+
throw new Error("precision must be a finite number greater than 0.");
|
|
9106
|
+
}
|
|
9107
|
+
let width;
|
|
9108
|
+
if (args.width !== undefined) {
|
|
9109
|
+
width = Number(args.width);
|
|
9110
|
+
if (!Number.isFinite(width) || width <= 0 || !Number.isInteger(width)) {
|
|
9111
|
+
throw new Error("width must be a finite positive integer.");
|
|
9112
|
+
}
|
|
9113
|
+
}
|
|
9114
|
+
let height;
|
|
9115
|
+
if (args.height !== undefined) {
|
|
9116
|
+
height = Number(args.height);
|
|
9117
|
+
if (!Number.isFinite(height) || height <= 0 || !Number.isInteger(height)) {
|
|
9118
|
+
throw new Error("height must be a finite positive integer.");
|
|
9119
|
+
}
|
|
9120
|
+
}
|
|
9121
|
+
const screenshotSize = width !== undefined && height !== undefined ? { width, height, precision } : { precision };
|
|
9122
|
+
// Omit fileName to get data URL back without triggering a download.
|
|
9123
|
+
const dataUrl = await CreateScreenshotUsingRenderTargetAsync(engine, camera, screenshotSize, "image/png");
|
|
9124
|
+
// Strip the data URI prefix to return raw base64, which is what AI agent APIs expect.
|
|
9125
|
+
const commaIndex = dataUrl.indexOf(",");
|
|
9126
|
+
return commaIndex !== -1 ? dataUrl.substring(commaIndex + 1) : dataUrl;
|
|
9127
|
+
},
|
|
9128
|
+
});
|
|
9129
|
+
return {
|
|
9130
|
+
dispose: () => {
|
|
9131
|
+
registration.dispose();
|
|
9132
|
+
},
|
|
9133
|
+
};
|
|
9134
|
+
},
|
|
9135
|
+
};
|
|
9136
|
+
|
|
9137
|
+
/**
|
|
9138
|
+
* Service that registers a CLI command for retrieving compiled shader code from a material.
|
|
9139
|
+
*/
|
|
9140
|
+
const ShaderCommandServiceDefinition = {
|
|
9141
|
+
friendlyName: "Shader Command Service",
|
|
9142
|
+
consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
|
|
9143
|
+
factory: (commandRegistry, sceneContext) => {
|
|
9144
|
+
const registration = commandRegistry.addCommand({
|
|
9145
|
+
id: "get-shader-code",
|
|
9146
|
+
description: "Get the shader code for a material by uniqueId.",
|
|
9147
|
+
args: [
|
|
9148
|
+
{
|
|
9149
|
+
name: "uniqueId",
|
|
9150
|
+
description: "The uniqueId of the material.",
|
|
9151
|
+
required: true,
|
|
9152
|
+
},
|
|
9153
|
+
{
|
|
9154
|
+
name: "variant",
|
|
9155
|
+
description: "Which shader variant to return: compiled (default), raw, or beforeMigration.",
|
|
9156
|
+
required: false,
|
|
9157
|
+
},
|
|
9158
|
+
],
|
|
9159
|
+
executeAsync: async (args) => {
|
|
9160
|
+
const scene = sceneContext.currentScene;
|
|
9161
|
+
if (!scene) {
|
|
9162
|
+
throw new Error("No active scene.");
|
|
9163
|
+
}
|
|
9164
|
+
const id = parseInt(args.uniqueId, 10);
|
|
9165
|
+
if (isNaN(id)) {
|
|
9166
|
+
throw new Error("uniqueId must be a number.");
|
|
9167
|
+
}
|
|
9168
|
+
const material = scene.materials.find((m) => m.uniqueId === id);
|
|
9169
|
+
if (!material) {
|
|
9170
|
+
throw new Error(`No material found with uniqueId ${id}.`);
|
|
9171
|
+
}
|
|
9172
|
+
const effect = material.getEffect();
|
|
9173
|
+
if (!effect) {
|
|
9174
|
+
throw new Error(`Material "${material.name}" has no effect. It may not have been rendered yet.`);
|
|
9175
|
+
}
|
|
9176
|
+
if (!effect.isReady()) {
|
|
9177
|
+
throw new Error(`Material "${material.name}" effect is not ready. Wait for it to be rendered.`);
|
|
9178
|
+
}
|
|
9179
|
+
const variant = args.variant ?? "compiled";
|
|
9180
|
+
let vertexShader;
|
|
9181
|
+
let fragmentShader;
|
|
9182
|
+
switch (variant) {
|
|
9183
|
+
case "compiled":
|
|
9184
|
+
vertexShader = effect.vertexSourceCode;
|
|
9185
|
+
fragmentShader = effect.fragmentSourceCode;
|
|
9186
|
+
break;
|
|
9187
|
+
case "raw":
|
|
9188
|
+
vertexShader = effect.rawVertexSourceCode;
|
|
9189
|
+
fragmentShader = effect.rawFragmentSourceCode;
|
|
9190
|
+
break;
|
|
9191
|
+
case "beforeMigration":
|
|
9192
|
+
vertexShader = effect.vertexSourceCodeBeforeMigration;
|
|
9193
|
+
fragmentShader = effect.fragmentSourceCodeBeforeMigration;
|
|
9194
|
+
break;
|
|
9195
|
+
default:
|
|
9196
|
+
throw new Error(`Unknown variant "${variant}". Use: compiled, raw, or beforeMigration.`);
|
|
9197
|
+
}
|
|
9198
|
+
return JSON.stringify({ vertexShader, fragmentShader }, null, 2);
|
|
9199
|
+
},
|
|
9200
|
+
});
|
|
9201
|
+
return {
|
|
9202
|
+
dispose: () => {
|
|
9203
|
+
registration.dispose();
|
|
9204
|
+
},
|
|
9205
|
+
};
|
|
9206
|
+
},
|
|
9207
|
+
};
|
|
9208
|
+
|
|
9209
|
+
/**
|
|
9210
|
+
* Service that registers CLI commands for querying scene and engine statistics.
|
|
9211
|
+
*/
|
|
9212
|
+
const StatsCommandServiceDefinition = {
|
|
9213
|
+
friendlyName: "Stats Command Service",
|
|
9214
|
+
consumes: [InspectableCommandRegistryIdentity, SceneContextIdentity],
|
|
9215
|
+
factory: (commandRegistry, sceneContext) => {
|
|
9216
|
+
let sceneInstrumentation;
|
|
9217
|
+
let engineInstrumentation;
|
|
9218
|
+
function disposeInstrumentation() {
|
|
9219
|
+
sceneInstrumentation?.dispose();
|
|
9220
|
+
sceneInstrumentation = undefined;
|
|
9221
|
+
engineInstrumentation?.dispose();
|
|
9222
|
+
engineInstrumentation = undefined;
|
|
9223
|
+
}
|
|
9224
|
+
const startPerfReg = commandRegistry.addCommand({
|
|
9225
|
+
id: "start-perf-instrumentation",
|
|
9226
|
+
description: "Start scene and engine performance instrumentation for frame stats collection.",
|
|
9227
|
+
executeAsync: async () => {
|
|
9228
|
+
const scene = sceneContext.currentScene;
|
|
9229
|
+
if (!scene) {
|
|
9230
|
+
throw new Error("No active scene.");
|
|
9231
|
+
}
|
|
9232
|
+
// Dispose any stale instrumentation (e.g. scene changed).
|
|
9233
|
+
if (sceneInstrumentation && sceneInstrumentation.scene !== scene) {
|
|
9234
|
+
disposeInstrumentation();
|
|
9235
|
+
}
|
|
9236
|
+
if (sceneInstrumentation) {
|
|
9237
|
+
return "Performance instrumentation is already running.";
|
|
9238
|
+
}
|
|
9239
|
+
sceneInstrumentation = new SceneInstrumentation(scene);
|
|
9240
|
+
sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
|
|
9241
|
+
sceneInstrumentation.captureRenderTargetsRenderTime = true;
|
|
9242
|
+
sceneInstrumentation.captureFrameTime = true;
|
|
9243
|
+
sceneInstrumentation.captureRenderTime = true;
|
|
9244
|
+
sceneInstrumentation.captureInterFrameTime = true;
|
|
9245
|
+
sceneInstrumentation.captureParticlesRenderTime = true;
|
|
9246
|
+
sceneInstrumentation.captureSpritesRenderTime = true;
|
|
9247
|
+
sceneInstrumentation.capturePhysicsTime = true;
|
|
9248
|
+
sceneInstrumentation.captureAnimationsTime = true;
|
|
9249
|
+
engineInstrumentation = new EngineInstrumentation(scene.getEngine());
|
|
9250
|
+
engineInstrumentation.captureGPUFrameTime = true;
|
|
9251
|
+
return "Performance instrumentation started.";
|
|
9252
|
+
},
|
|
9253
|
+
});
|
|
9254
|
+
const stopPerfReg = commandRegistry.addCommand({
|
|
9255
|
+
id: "stop-perf-instrumentation",
|
|
9256
|
+
description: "Stop scene and engine performance instrumentation.",
|
|
9257
|
+
executeAsync: async () => {
|
|
9258
|
+
if (!sceneInstrumentation && !engineInstrumentation) {
|
|
9259
|
+
return "Performance instrumentation is not running.";
|
|
9260
|
+
}
|
|
9261
|
+
disposeInstrumentation();
|
|
9262
|
+
return "Performance instrumentation stopped.";
|
|
9263
|
+
},
|
|
9264
|
+
});
|
|
9265
|
+
const countStatsReg = commandRegistry.addCommand({
|
|
9266
|
+
id: "get-count-stats",
|
|
9267
|
+
description: "Get scene entity counts (meshes, lights, vertices, draw calls, etc.).",
|
|
9268
|
+
executeAsync: async () => {
|
|
9269
|
+
const scene = sceneContext.currentScene;
|
|
9270
|
+
if (!scene) {
|
|
9271
|
+
throw new Error("No active scene.");
|
|
9272
|
+
}
|
|
9273
|
+
let activeMeshesCount = scene.getActiveMeshes().length;
|
|
9274
|
+
for (const objectRenderer of scene.objectRenderers) {
|
|
9275
|
+
activeMeshesCount += objectRenderer.getActiveMeshes().length;
|
|
9276
|
+
}
|
|
9277
|
+
const activeIndices = scene.getActiveIndices();
|
|
9278
|
+
return JSON.stringify({
|
|
9279
|
+
totalMeshes: scene.meshes.length,
|
|
9280
|
+
activeMeshes: activeMeshesCount,
|
|
9281
|
+
activeIndices,
|
|
9282
|
+
activeFaces: Math.floor(activeIndices / 3),
|
|
9283
|
+
activeBones: scene.getActiveBones(),
|
|
9284
|
+
activeParticles: scene.getActiveParticles(),
|
|
9285
|
+
drawCalls: scene.getEngine()._drawCalls.current,
|
|
9286
|
+
totalLights: scene.lights.length,
|
|
9287
|
+
totalVertices: scene.getTotalVertices(),
|
|
9288
|
+
totalMaterials: scene.materials.length,
|
|
9289
|
+
totalTextures: scene.textures.length,
|
|
9290
|
+
}, null, 2);
|
|
9291
|
+
},
|
|
9292
|
+
});
|
|
9293
|
+
const frameStatsReg = commandRegistry.addCommand({
|
|
9294
|
+
id: "get-frame-stats",
|
|
9295
|
+
description: "Get frame timing statistics. Requires start-perf-instrumentation to be run first.",
|
|
9296
|
+
executeAsync: async () => {
|
|
9297
|
+
if (!sceneInstrumentation || !engineInstrumentation) {
|
|
9298
|
+
throw new Error("Performance instrumentation is not running. Run start-perf-instrumentation first.");
|
|
9299
|
+
}
|
|
9300
|
+
const si = sceneInstrumentation;
|
|
9301
|
+
const ei = engineInstrumentation;
|
|
9302
|
+
const round = (v) => Math.round(v * 100) / 100;
|
|
9303
|
+
return JSON.stringify({
|
|
9304
|
+
absoluteFPS: Math.floor(1000.0 / si.frameTimeCounter.lastSecAverage),
|
|
9305
|
+
meshesSelectionMs: round(si.activeMeshesEvaluationTimeCounter.lastSecAverage),
|
|
9306
|
+
renderTargetsMs: round(si.renderTargetsRenderTimeCounter.lastSecAverage),
|
|
9307
|
+
particlesMs: round(si.particlesRenderTimeCounter.lastSecAverage),
|
|
9308
|
+
spritesMs: round(si.spritesRenderTimeCounter.lastSecAverage),
|
|
9309
|
+
animationsMs: round(si.animationsTimeCounter.lastSecAverage),
|
|
9310
|
+
physicsMs: round(si.physicsTimeCounter.lastSecAverage),
|
|
9311
|
+
renderMs: round(si.renderTimeCounter.lastSecAverage),
|
|
9312
|
+
frameMs: round(si.frameTimeCounter.lastSecAverage),
|
|
9313
|
+
interFrameMs: round(si.interFrameTimeCounter.lastSecAverage),
|
|
9314
|
+
gpuFrameMs: round(ei.gpuFrameTimeCounter.lastSecAverage * 0.000001),
|
|
9315
|
+
gpuFrameAverageMs: round(ei.gpuFrameTimeCounter.average * 0.000001),
|
|
9316
|
+
}, null, 2);
|
|
9317
|
+
},
|
|
9318
|
+
});
|
|
9319
|
+
const systemStatsReg = commandRegistry.addCommand({
|
|
9320
|
+
id: "get-system-stats",
|
|
9321
|
+
description: "Get engine capabilities and system information.",
|
|
9322
|
+
executeAsync: async () => {
|
|
9323
|
+
const scene = sceneContext.currentScene;
|
|
9324
|
+
if (!scene) {
|
|
9325
|
+
throw new Error("No active scene.");
|
|
9326
|
+
}
|
|
9327
|
+
const engine = scene.getEngine();
|
|
9328
|
+
const caps = engine.getCaps();
|
|
9329
|
+
return JSON.stringify({
|
|
9330
|
+
resolution: `${engine.getRenderWidth()} x ${engine.getRenderHeight()}`,
|
|
9331
|
+
hardwareScalingLevel: engine.getHardwareScalingLevel(),
|
|
9332
|
+
engine: engine.description,
|
|
9333
|
+
driver: engine.extractDriverInfo(),
|
|
9334
|
+
capabilities: {
|
|
9335
|
+
stdDerivatives: caps.standardDerivatives,
|
|
9336
|
+
compressedTextures: caps.s3tc !== undefined,
|
|
9337
|
+
hardwareInstances: caps.instancedArrays,
|
|
9338
|
+
textureFloat: caps.textureFloat,
|
|
9339
|
+
textureHalfFloat: caps.textureHalfFloat,
|
|
9340
|
+
renderToTextureFloat: caps.textureFloatRender,
|
|
9341
|
+
renderToTextureHalfFloat: caps.textureHalfFloatRender,
|
|
9342
|
+
indices32Bit: caps.uintIndices,
|
|
9343
|
+
fragmentDepth: caps.fragmentDepthSupported,
|
|
9344
|
+
highPrecisionShaders: caps.highPrecisionShaderSupported,
|
|
9345
|
+
drawBuffers: caps.drawBuffersExtension,
|
|
9346
|
+
vertexArrayObject: caps.vertexArrayObject,
|
|
9347
|
+
timerQuery: caps.timerQuery !== undefined,
|
|
9348
|
+
stencil: engine.isStencilEnable,
|
|
9349
|
+
parallelShaderCompilation: caps.parallelShaderCompile != null,
|
|
9350
|
+
maxTexturesUnits: caps.maxTexturesImageUnits,
|
|
9351
|
+
maxTexturesSize: caps.maxTextureSize,
|
|
9352
|
+
maxAnisotropy: caps.maxAnisotropy,
|
|
9353
|
+
},
|
|
9354
|
+
}, null, 2);
|
|
9355
|
+
},
|
|
9356
|
+
});
|
|
9357
|
+
return {
|
|
9358
|
+
dispose: () => {
|
|
9359
|
+
startPerfReg.dispose();
|
|
9360
|
+
stopPerfReg.dispose();
|
|
9361
|
+
countStatsReg.dispose();
|
|
9362
|
+
frameStatsReg.dispose();
|
|
9363
|
+
systemStatsReg.dispose();
|
|
9364
|
+
disposeInstrumentation();
|
|
9365
|
+
},
|
|
9366
|
+
};
|
|
9367
|
+
},
|
|
9368
|
+
};
|
|
9369
|
+
|
|
9370
|
+
const DefaultPort = 4400;
|
|
9371
|
+
const InspectableStates = new Map();
|
|
9372
|
+
/**
|
|
9373
|
+
* @internal
|
|
9374
|
+
* Internal implementation that returns an {@link InternalInspectableToken} with access
|
|
9375
|
+
* to the underlying ServiceContainer. Used by ShowInspector to set up a parent container relationship.
|
|
9376
|
+
*/
|
|
9377
|
+
function _StartInspectable(scene, options) {
|
|
9378
|
+
let state = InspectableStates.get(scene);
|
|
9379
|
+
if (!state) {
|
|
9380
|
+
const port = options?.port ?? DefaultPort;
|
|
9381
|
+
const name = options?.name ?? (typeof document !== "undefined" ? document.title : "Babylon.js Scene");
|
|
9382
|
+
const serviceContainer = new ServiceContainer("InspectableContainer");
|
|
9383
|
+
const fullyDispose = () => {
|
|
9384
|
+
InspectableStates.delete(scene);
|
|
9385
|
+
serviceContainer.dispose();
|
|
9386
|
+
sceneDisposeObserver.remove();
|
|
9387
|
+
};
|
|
9388
|
+
// Initialize the service container asynchronously.
|
|
9389
|
+
const sceneContextServiceDefinition = {
|
|
9390
|
+
friendlyName: "Inspectable Scene Context",
|
|
9391
|
+
produces: [SceneContextIdentity],
|
|
9392
|
+
factory: () => ({
|
|
9393
|
+
currentScene: scene,
|
|
9394
|
+
currentSceneObservable: new Observable(),
|
|
9395
|
+
}),
|
|
9396
|
+
};
|
|
9397
|
+
const readyPromise = (async () => {
|
|
9398
|
+
await serviceContainer.addServicesAsync(sceneContextServiceDefinition, MakeInspectableBridgeServiceDefinition({
|
|
9399
|
+
port,
|
|
9400
|
+
name,
|
|
9401
|
+
}), EntityQueryServiceDefinition, ScreenshotCommandServiceDefinition, ShaderCommandServiceDefinition, StatsCommandServiceDefinition, PerfTraceCommandServiceDefinition);
|
|
9402
|
+
})();
|
|
9403
|
+
state = {
|
|
9404
|
+
refCount: 0,
|
|
9405
|
+
serviceContainer,
|
|
9406
|
+
sceneDisposeObserver: { remove: () => { } },
|
|
9407
|
+
fullyDispose,
|
|
9408
|
+
readyPromise,
|
|
9409
|
+
};
|
|
9410
|
+
const capturedState = state;
|
|
9411
|
+
InspectableStates.set(scene, state);
|
|
9412
|
+
// Auto-dispose when the scene is disposed.
|
|
9413
|
+
const sceneDisposeObserver = scene.onDisposeObservable.addOnce(() => {
|
|
9414
|
+
capturedState.refCount = 0;
|
|
9415
|
+
capturedState.fullyDispose();
|
|
9416
|
+
});
|
|
9417
|
+
state.sceneDisposeObserver = sceneDisposeObserver;
|
|
9418
|
+
// Handle initialization failure (guard against already-disposed state).
|
|
9419
|
+
void (async () => {
|
|
9420
|
+
try {
|
|
9421
|
+
await readyPromise;
|
|
9422
|
+
}
|
|
9423
|
+
catch (error) {
|
|
9424
|
+
if (InspectableStates.has(scene)) {
|
|
9425
|
+
Logger.Error(`Failed to initialize Inspectable: ${error}`);
|
|
9426
|
+
capturedState.refCount = 0;
|
|
9427
|
+
capturedState.fullyDispose();
|
|
9428
|
+
}
|
|
9429
|
+
}
|
|
9430
|
+
})();
|
|
9431
|
+
}
|
|
9432
|
+
state.refCount++;
|
|
9433
|
+
const { serviceContainer } = state;
|
|
9434
|
+
const owningState = state;
|
|
9435
|
+
// If additional service definitions were provided, add them in a separate call
|
|
9436
|
+
// so they can be independently removed when this token is disposed.
|
|
9437
|
+
let extraServicesDisposable;
|
|
9438
|
+
const extraAbortController = new AbortController();
|
|
9439
|
+
const extraServiceDefinitions = options?.serviceDefinitions;
|
|
9440
|
+
if (extraServiceDefinitions && extraServiceDefinitions.length > 0) {
|
|
9441
|
+
// Wait for the built-in services to be ready, then add the extra ones.
|
|
9442
|
+
void (async () => {
|
|
9443
|
+
try {
|
|
9444
|
+
await owningState.readyPromise;
|
|
9445
|
+
extraServicesDisposable = await serviceContainer.addServicesAsync(...extraServiceDefinitions, extraAbortController.signal);
|
|
9446
|
+
}
|
|
9447
|
+
catch (error) {
|
|
9448
|
+
if (!extraAbortController.signal.aborted) {
|
|
9449
|
+
Logger.Error(`Failed to add extra inspectable services: ${error}`);
|
|
9450
|
+
}
|
|
9451
|
+
}
|
|
9452
|
+
})();
|
|
9453
|
+
}
|
|
9454
|
+
let disposed = false;
|
|
9455
|
+
const token = {
|
|
9456
|
+
get isDisposed() {
|
|
9457
|
+
return disposed;
|
|
9458
|
+
},
|
|
9459
|
+
get serviceContainer() {
|
|
9460
|
+
return serviceContainer;
|
|
9461
|
+
},
|
|
9462
|
+
dispose() {
|
|
9463
|
+
if (disposed) {
|
|
9464
|
+
return;
|
|
9465
|
+
}
|
|
9466
|
+
disposed = true;
|
|
9467
|
+
// Abort any in-flight extra service initialization and remove already-added extra services.
|
|
9468
|
+
extraAbortController.abort();
|
|
9469
|
+
extraServicesDisposable?.dispose();
|
|
9470
|
+
owningState.refCount--;
|
|
9471
|
+
if (owningState.refCount <= 0) {
|
|
9472
|
+
owningState.fullyDispose();
|
|
9473
|
+
}
|
|
9474
|
+
},
|
|
9475
|
+
};
|
|
9476
|
+
return token;
|
|
9477
|
+
}
|
|
8530
9478
|
/**
|
|
8531
|
-
*
|
|
9479
|
+
* Makes a scene inspectable by connecting it to the Inspector CLI bridge.
|
|
9480
|
+
* This creates a headless {@link ServiceContainer} (no UI) and registers the
|
|
9481
|
+
* {@link InspectableBridgeService} which opens a WebSocket to the bridge and
|
|
9482
|
+
* exposes a command registry for CLI-invocable commands.
|
|
9483
|
+
*
|
|
9484
|
+
* Multiple callers may call this for the same scene. Each returned token is
|
|
9485
|
+
* ref-counted — the underlying connection is only torn down when all tokens
|
|
9486
|
+
* have been disposed. Additional {@link InspectableOptions.serviceDefinitions}
|
|
9487
|
+
* passed by each caller are added to the shared container and removed when
|
|
9488
|
+
* that caller's token is disposed.
|
|
9489
|
+
*
|
|
9490
|
+
* @param scene The scene to make inspectable.
|
|
9491
|
+
* @param options Optional configuration.
|
|
9492
|
+
* @returns An {@link InspectableToken} that can be disposed to disconnect.
|
|
9493
|
+
* @experimental
|
|
8532
9494
|
*/
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
description: "Adds a new panel for easy creation of various Babylon assets. This is a WIP extension...expect changes!",
|
|
8537
|
-
keywords: ["creation", "tools"],
|
|
8538
|
-
...BabylonWebResources,
|
|
8539
|
-
getExtensionModuleAsync: async () => await import('./quickCreateToolsService-BoqrJqoM.js'),
|
|
8540
|
-
},
|
|
8541
|
-
{
|
|
8542
|
-
name: "Reflector",
|
|
8543
|
-
description: "Connects to the Reflector Bridge for real-time scene synchronization with the Babylon.js Sandbox.",
|
|
8544
|
-
keywords: ["reflector", "bridge", "sync", "sandbox", "tools"],
|
|
8545
|
-
...BabylonWebResources,
|
|
8546
|
-
getExtensionModuleAsync: async () => await import('./reflectorService-CFTva2GG.js'),
|
|
8547
|
-
},
|
|
8548
|
-
]);
|
|
9495
|
+
function StartInspectable(scene, options) {
|
|
9496
|
+
return _StartInspectable(scene, options);
|
|
9497
|
+
}
|
|
8549
9498
|
|
|
8550
9499
|
/**
|
|
8551
9500
|
* Reusable component which renders a color property line containing a label, colorPicker popout, and expandable RGBA values
|
|
@@ -8735,6 +9684,39 @@ const LegacyInspectableObjectPropertiesServiceDefinition = {
|
|
|
8735
9684
|
},
|
|
8736
9685
|
};
|
|
8737
9686
|
|
|
9687
|
+
const CliConnectionStatusServiceDefinition = {
|
|
9688
|
+
friendlyName: "CLI Connection Status",
|
|
9689
|
+
consumes: [ShellServiceIdentity, CliConnectionStatusIdentity],
|
|
9690
|
+
factory: (shellService, cliConnectionStatus) => {
|
|
9691
|
+
shellService.addToolbarItem({
|
|
9692
|
+
key: "CLI Connection Status",
|
|
9693
|
+
verticalLocation: "bottom",
|
|
9694
|
+
horizontalLocation: "right",
|
|
9695
|
+
teachingMoment: false,
|
|
9696
|
+
order: 0 /* DefaultToolbarItemOrder.CliStatus */,
|
|
9697
|
+
component: () => {
|
|
9698
|
+
const isConnected = useObservableState(() => cliConnectionStatus.isConnected, cliConnectionStatus.onConnectionStatusChanged);
|
|
9699
|
+
const { showToast } = useToast();
|
|
9700
|
+
const isFirstRender = useRef(true);
|
|
9701
|
+
useEffect(() => {
|
|
9702
|
+
if (isFirstRender.current) {
|
|
9703
|
+
isFirstRender.current = false;
|
|
9704
|
+
return;
|
|
9705
|
+
}
|
|
9706
|
+
if (isConnected) {
|
|
9707
|
+
showToast("Inspector bridge connected.", { intent: "success" });
|
|
9708
|
+
}
|
|
9709
|
+
else {
|
|
9710
|
+
showToast("Inspector bridge disconnected.", { intent: "warning" });
|
|
9711
|
+
}
|
|
9712
|
+
}, [isConnected, showToast]);
|
|
9713
|
+
// Using raw Fluent Button to pass color directly to the icon.
|
|
9714
|
+
return (jsx(Tooltip, { content: isConnected ? "Connected to Inspector CLI Bridge" : "Disconnected from Inspector CLI Bridge", children: jsx(Button$1, { appearance: "subtle", icon: isConnected ? (jsx(PlugConnectedRegular, { color: tokens.colorPaletteGreenForeground2 })) : (jsx(PlugDisconnectedRegular, { color: tokens.colorPaletteRedForeground2 })), onClick: () => window.open("https://www.npmjs.com/package/@babylonjs/inspector", "_blank") }) }));
|
|
9715
|
+
},
|
|
9716
|
+
});
|
|
9717
|
+
},
|
|
9718
|
+
};
|
|
9719
|
+
|
|
8738
9720
|
const MeshIcon = createFluentIcon("Mesh", "1em", '<g transform="scale(1.25)"><path d="M14.03,3.54l-5.11-2.07c-.61-.25-1.27-.25-1.88,0L1.93,3.54c-.57.23-.94.78-.94,1.39v6.15c0,.61.37,1.16.94,1.39l5.11,2.07c.3.12.62.18.94.18s.64-.06.94-.18l5.12-2.07c.57-.23.94-.78.94-1.39v-6.15c0-.61-.37-1.16-.94-1.39ZM13.97,7.71l-2.11.86v-2.71l2.11-.86v2.71ZM1.99,5l2.11.86v2.71l-2.11-.86v-2.71ZM11.35,4.98l-2.04-.83,1.78-.72,2.04.83-1.78.72ZM10.02,5.52l-2.04.83-2.04-.83,2.04-.83,2.04.83ZM4.6,4.98l-1.78-.72,2.04-.83,1.78.72-2.04.83ZM5.1,6.26l2.38.96v2.71l-2.38-.96v-2.71ZM8.48,7.22l2.38-.96v2.71l-2.38.96v-2.71ZM7.41,2.39c.18-.07.37-.11.56-.11s.38.04.56.11l1.22.49-1.78.72-1.79-.72,1.22-.49ZM1.99,11.07v-2.29l2.11.86v2.62l-1.8-.73c-.19-.08-.31-.26-.31-.46ZM5.1,12.67v-2.62l2.38.96v2.61s-.04,0-.06-.01l-2.31-.94ZM8.54,13.61s-.04,0-.06.01v-2.61l2.38-.96v2.62l-2.31.94ZM13.66,11.54l-1.8.73v-2.62l2.11-.86v2.29c0,.2-.12.39-.31.46Z"/></g>');
|
|
8739
9721
|
const TranslateIcon = createFluentIcon("Translate", "1em", '<g transform="scale(0.833)"><path d="M20.16,12.98l-2.75-2.75c-.29-.29-.77-.29-1.06,0-.29.29-.29.77,0,1.06l1.47,1.47h-6.69v-6.69l1.47,1.47c.29.29.77.29,1.06,0,.29-.29.29-.77,0-1.06l-2.75-2.75c-.14-.14-.33-.22-.53-.22s-.39.08-.53.22l-2.75,2.75c-.29.29-.29.77,0,1.06.29.29.77.29,1.06,0l1.47-1.47v7.13l-3.52,3.52v-2.08c0-.41-.34-.75-.75-.75s-.75.34-.75.75v3.89c0,.2.08.39.22.53.14.14.33.22.53.22h3.89c.41,0,.75-.34.75-.75s-.34-.75-.75-.75h-2.08s3.52-3.52,3.52-3.52h7.13l-1.47,1.47c-.29.29-.29.77,0,1.06s.77.29,1.06,0l2.75-2.75c.14-.14.22-.33.22-.53s-.08-.39-.22-.53Z" /></g>');
|
|
8740
9722
|
const MaterialIcon = createFluentIcon("Material", "1em", '<g transform="scale(1.25)"><path d="M14.74,6.3c-.09-.36-.38-.64-.75-.72-.04-.09-.08-.18-.12-.27.1-.15.16-.32.16-.51,0-.18-.05-.34-.13-.48-1.23-1.97-3.41-3.28-5.9-3.28C4.16,1.04,1.04,4.16,1.04,7.99c0,.39.23.72.57.88.02.12.03.25.06.37-.18.18-.3.42-.3.7,0,.11.02.21.06.31.94,2.74,3.53,4.71,6.58,4.71,3.84,0,6.96-3.12,6.96-6.96,0-.59-.08-1.16-.22-1.7ZM2.07,8.58c-.02-.19-.03-.39-.03-.58,0-3.29,2.67-5.96,5.96-5.96,2.23,0,4.17,1.23,5.2,3.05.05.18-.07.45-.3.75-.57-.73-1.45-1.21-2.45-1.21-1.72,0-3.12,1.4-3.12,3.11,0,.33.07.65.16.95-3.05.82-5.17.52-5.42-.11ZM12.56,7.75c0,1.17-.95,2.11-2.11,2.11s-2.12-.95-2.12-2.11.95-2.11,2.12-2.11,2.11.95,2.11,2.11ZM8,13.96c-2.6,0-4.81-1.68-5.62-4.01.5.16,1.11.24,1.79.24,1.15,0,2.49-.22,3.79-.59.57.76,1.47,1.26,2.49,1.26,1.72,0,3.11-1.4,3.11-3.11,0-.34-.07-.65-.17-.96.13-.13.24-.26.34-.39.14.51.22,1.04.22,1.6,0,3.29-2.67,5.96-5.96,5.96Z"/></g>');
|
|
@@ -14547,14 +15529,42 @@ const NormalMapProperties = (props) => {
|
|
|
14547
15529
|
return (jsx(Fragment, { children: IsMaterialWithPublicNormalMaps(material) ? (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert X Axis", target: material, propertyKey: "invertNormalMapX" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert Y Axis", target: material, propertyKey: "invertNormalMapY" })] })) : (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert X Axis", target: material, propertyKey: "_invertNormalMapX" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Invert Y Axis", target: material, propertyKey: "_invertNormalMapY" })] })) }));
|
|
14548
15530
|
};
|
|
14549
15531
|
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
15532
|
+
const useCheckboxStyles = makeStyles({
|
|
15533
|
+
indicator: {
|
|
15534
|
+
margin: 0,
|
|
15535
|
+
},
|
|
15536
|
+
});
|
|
15537
|
+
/**
|
|
15538
|
+
* This is a primitive fluent checkbox that can both read and write checked state
|
|
15539
|
+
* @param props
|
|
15540
|
+
* @returns Checkbox component
|
|
15541
|
+
*/
|
|
15542
|
+
const Checkbox = (props) => {
|
|
15543
|
+
Checkbox.displayName = "Checkbox";
|
|
15544
|
+
const [checked, setChecked] = useState(() => props.value ?? false);
|
|
15545
|
+
const classes = useCheckboxStyles();
|
|
15546
|
+
useEffect(() => {
|
|
15547
|
+
if (props.value != undefined) {
|
|
15548
|
+
setChecked(props.value); // Update local state when props.checked changes
|
|
15549
|
+
}
|
|
15550
|
+
}, [props.value]);
|
|
15551
|
+
const onChange = (ev, _) => {
|
|
15552
|
+
props.onChange(ev.target.checked);
|
|
15553
|
+
setChecked(ev.target.checked);
|
|
15554
|
+
};
|
|
15555
|
+
return jsx(Checkbox$1, { checked: checked, onChange: onChange, indicator: { className: classes.indicator } });
|
|
15556
|
+
};
|
|
15557
|
+
|
|
15558
|
+
/**
|
|
15559
|
+
* Wraps a checkbox in a property line
|
|
15560
|
+
* @param props - PropertyLineProps and CheckboxProps
|
|
15561
|
+
* @returns property-line wrapped checkbox
|
|
15562
|
+
*/
|
|
15563
|
+
const CheckboxPropertyLine = (props) => {
|
|
15564
|
+
CheckboxPropertyLine.displayName = "CheckboxPropertyLine";
|
|
15565
|
+
return (jsx(PropertyLine, { ...props, children: jsx(Checkbox, { ...props }) }));
|
|
14557
15566
|
};
|
|
15567
|
+
|
|
14558
15568
|
/**
|
|
14559
15569
|
* Displays the base layer properties of an OpenPBR material.
|
|
14560
15570
|
* @param props - The required properties
|
|
@@ -14562,23 +15572,7 @@ const UpdateTexture = (file, material, textureSetter) => {
|
|
|
14562
15572
|
*/
|
|
14563
15573
|
const OpenPBRMaterialBaseProperties = (props) => {
|
|
14564
15574
|
const { material } = props;
|
|
14565
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeight", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14566
|
-
if (files.length > 0) {
|
|
14567
|
-
UpdateTexture(files[0], material, (texture) => (material.baseWeightTexture = texture));
|
|
14568
|
-
}
|
|
14569
|
-
} }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Base Color", target: material, propertyKey: "baseColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Base Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14570
|
-
if (files.length > 0) {
|
|
14571
|
-
UpdateTexture(files[0], material, (texture) => (material.baseColorTexture = texture));
|
|
14572
|
-
}
|
|
14573
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Metalness", target: material, propertyKey: "baseMetalness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Metalness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14574
|
-
if (files.length > 0) {
|
|
14575
|
-
UpdateTexture(files[0], material, (texture) => (material.baseMetalnessTexture = texture));
|
|
14576
|
-
}
|
|
14577
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Diffuse Roughness", target: material, propertyKey: "baseDiffuseRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Diffuse Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14578
|
-
if (files.length > 0) {
|
|
14579
|
-
UpdateTexture(files[0], material, (texture) => (material.baseDiffuseRoughnessTexture = texture));
|
|
14580
|
-
}
|
|
14581
|
-
} })] }));
|
|
15575
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeight", min: 0, max: 1, step: 0.01, description: "Controls how strong or visible the base aspect appears.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Base Color", target: material, propertyKey: "baseColor", isLinearMode: true, description: "Sets the primary surface color of the material.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Color", target: material, propertyKey: "baseColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Metalness", target: material, propertyKey: "baseMetalness", min: 0, max: 1, step: 0.01, description: "Controls whether the material behaves as metal or non-metal. The parameter supersedes transmission_weight and subsurface_weight.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Metalness", target: material, propertyKey: "baseMetalnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Diffuse Roughness", target: material, propertyKey: "baseDiffuseRoughness", min: 0, max: 1, step: 0.01, description: "Softens the surface's base appearance. Higher values create matte or porous looks. Lower values are smoother.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Base Diffuse Roughness", target: material, propertyKey: "baseDiffuseRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Ambient Occlusion", target: material, propertyKey: "ambientOcclusionTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
|
|
14582
15576
|
};
|
|
14583
15577
|
/**
|
|
14584
15578
|
* Displays the specular layer properties of an OpenPBR material.
|
|
@@ -14587,47 +15581,15 @@ const OpenPBRMaterialBaseProperties = (props) => {
|
|
|
14587
15581
|
*/
|
|
14588
15582
|
const OpenPBRMaterialSpecularProperties = (props) => {
|
|
14589
15583
|
const { material } = props;
|
|
14590
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14591
|
-
if (files.length > 0) {
|
|
14592
|
-
UpdateTexture(files[0], material, (texture) => (material.specularWeightTexture = texture));
|
|
14593
|
-
}
|
|
14594
|
-
} }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Specular Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14595
|
-
if (files.length > 0) {
|
|
14596
|
-
UpdateTexture(files[0], material, (texture) => (material.specularColorTexture = texture));
|
|
14597
|
-
}
|
|
14598
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14599
|
-
if (files.length > 0) {
|
|
14600
|
-
UpdateTexture(files[0], material, (texture) => (material.specularRoughnessTexture = texture));
|
|
14601
|
-
}
|
|
14602
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropy", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Roughness Anisotropy", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14603
|
-
if (files.length > 0) {
|
|
14604
|
-
UpdateTexture(files[0], material, (texture) => (material.specularRoughnessAnisotropyTexture = texture));
|
|
14605
|
-
}
|
|
14606
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01 })] }));
|
|
15584
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01, description: "Controls how strong the reflections appear.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true, description: "Tints the color of reflections.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Color", target: material, propertyKey: "specularColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughness", min: 0, max: 1, step: 0.01, description: "Controls how sharp or blurry reflections are.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness", target: material, propertyKey: "specularRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropy", min: 0, max: 1, step: 0.01, description: "Stretches reflections in one direction for brushed or streaked looks. Requires specular_roughness > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/microfacetmodel" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Specular Roughness Anisotropy", target: material, propertyKey: "specularRoughnessAnisotropyTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01, description: "Index of refraction is a physical value controlling the reflective intensity and refraction. The parameter has no effect on metals.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate" })] }));
|
|
14607
15585
|
};
|
|
14608
15586
|
const OpenPBRMaterialTransmissionProperties = (props) => {
|
|
14609
15587
|
const { material } = props;
|
|
14610
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Weight", target: material, propertyKey: "transmissionWeight", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14611
|
-
|
|
14612
|
-
|
|
14613
|
-
|
|
14614
|
-
|
|
14615
|
-
if (files.length > 0) {
|
|
14616
|
-
UpdateTexture(files[0], material, (texture) => (material.transmissionColorTexture = texture));
|
|
14617
|
-
}
|
|
14618
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Depth", target: material, propertyKey: "transmissionDepth", min: 0, step: 0.01 }), jsx(FileUploadLine, { label: "Transmission Depth", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14619
|
-
if (files.length > 0) {
|
|
14620
|
-
UpdateTexture(files[0], material, (texture) => (material.transmissionDepthTexture = texture));
|
|
14621
|
-
}
|
|
14622
|
-
} }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Scatter", target: material, propertyKey: "transmissionScatter", isLinearMode: true }), jsx(FileUploadLine, { label: "Transmission Scatter", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14623
|
-
if (files.length > 0) {
|
|
14624
|
-
UpdateTexture(files[0], material, (texture) => (material.transmissionScatterTexture = texture));
|
|
14625
|
-
}
|
|
14626
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Scatter Anisotropy", target: material, propertyKey: "transmissionScatterAnisotropy", min: -1, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Abbe Number", target: material, propertyKey: "transmissionDispersionAbbeNumber", min: 1, max: 100, step: 1 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Scale", target: material, propertyKey: "transmissionDispersionScale", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Transmission Dispersion Scale", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14627
|
-
if (files.length > 0) {
|
|
14628
|
-
UpdateTexture(files[0], material, (texture) => (material.transmissionDispersionScaleTexture = texture));
|
|
14629
|
-
}
|
|
14630
|
-
} })] }));
|
|
15588
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Weight", target: material, propertyKey: "transmissionWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the transparency effect. The parameter is superseded by base_metalness.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Weight", target: material, propertyKey: "transmissionWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Color", target: material, propertyKey: "transmissionColor", isLinearMode: true, description: "Tints light passing through the material. Works with transmission_depth for realistic thickness-based coloring.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Color", target: material, propertyKey: "transmissionColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Depth (cm)", target: material, propertyKey: "transmissionDepth", min: 0, max: 5, step: 0.001, convertTo: (value) => value * 100, convertFrom: (value) => value / 100, description: "Controls how quickly light is absorbed with thickness. Distance is in scene units.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Depth", target: material, propertyKey: "transmissionDepthTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Transmission Scatter", target: material, propertyKey: "transmissionScatter", isLinearMode: true, description: "Adds internal cloudiness to create materials like juice, honey, etc. Requires transmission_depth > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Scatter", target: material, propertyKey: "transmissionScatterTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Scatter Anisotropy", target: material, propertyKey: "transmissionScatterAnisotropy", min: -1, max: 1, step: 0.01, description: "Shifts scattering forward/backward for clearer or hazier appearance depending on viewing angle.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Abbe Number", target: material, propertyKey: "transmissionDispersionAbbeNumber", min: 1, max: 100, step: 1, description: "Physical value for the rainbow color separation in refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Transmission Dispersion Scale", target: material, propertyKey: "transmissionDispersionScale", min: 0, max: 1, step: 0.01, description: "Strength of rainbow color separation in refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/translucentbase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Transmission Dispersion Scale", target: material, propertyKey: "transmissionDispersionScaleTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
|
|
15589
|
+
};
|
|
15590
|
+
const OpenPBRMaterialSubsurfaceProperties = (props) => {
|
|
15591
|
+
const { material } = props;
|
|
15592
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Subsurface Weight", target: material, propertyKey: "subsurfaceWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the subsurface effect. The parameter is superseded by base_metalness and transmission_weight.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Subsurface Weight", target: material, propertyKey: "subsurfaceWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Subsurface Color", target: material, propertyKey: "subsurfaceColor", isLinearMode: true, description: "Colors the light that scatters under the surface.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Subsurface Color", target: material, propertyKey: "subsurfaceColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Subsurface Radius (cm)", target: material, propertyKey: "subsurfaceRadius", min: 0, max: 4, step: 0.001, convertTo: (value) => value * 100, convertFrom: (value) => value / 100, description: "Controls how soft and spread-out the subsurface look appears.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Subsurface Radius Scale", target: material, propertyKey: "subsurfaceRadiusScale", isLinearMode: true, description: "Tints thin areas with light shining through, like warm glow on ears or leaves.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Subsurface Radius Scale", target: material, propertyKey: "subsurfaceRadiusScaleTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Subsurface Scatter Anisotropy", target: material, propertyKey: "subsurfaceScatterAnisotropy", min: -1, max: 1, step: 0.01, description: "Shifts scattering forward/backward for a softer glow or a sharper one.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/basesubstrate/subsurface" })] }));
|
|
14631
15593
|
};
|
|
14632
15594
|
/**
|
|
14633
15595
|
* Displays the coat layer properties of an OpenPBR material.
|
|
@@ -14636,27 +15598,7 @@ const OpenPBRMaterialTransmissionProperties = (props) => {
|
|
|
14636
15598
|
*/
|
|
14637
15599
|
const OpenPBRMaterialCoatProperties = (props) => {
|
|
14638
15600
|
const { material } = props;
|
|
14639
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeight", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14640
|
-
if (files.length > 0) {
|
|
14641
|
-
UpdateTexture(files[0], material, (texture) => (material.coatWeightTexture = texture));
|
|
14642
|
-
}
|
|
14643
|
-
} }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Coat Color", target: material, propertyKey: "coatColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Coat Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14644
|
-
if (files.length > 0) {
|
|
14645
|
-
UpdateTexture(files[0], material, (texture) => (material.coatColorTexture = texture));
|
|
14646
|
-
}
|
|
14647
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness", target: material, propertyKey: "coatRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14648
|
-
if (files.length > 0) {
|
|
14649
|
-
UpdateTexture(files[0], material, (texture) => (material.coatRoughnessTexture = texture));
|
|
14650
|
-
}
|
|
14651
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness Anisotropy", target: material, propertyKey: "coatRoughnessAnisotropy", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Roughness Anisotropy", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14652
|
-
if (files.length > 0) {
|
|
14653
|
-
UpdateTexture(files[0], material, (texture) => (material.coatRoughnessAnisotropyTexture = texture));
|
|
14654
|
-
}
|
|
14655
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat IOR", target: material, propertyKey: "coatIor", min: 1, max: 3, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Darkening", target: material, propertyKey: "coatDarkening", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Darkening", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14656
|
-
if (files.length > 0) {
|
|
14657
|
-
UpdateTexture(files[0], material, (texture) => (material.coatDarkeningTexture = texture));
|
|
14658
|
-
}
|
|
14659
|
-
} })] }));
|
|
15601
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the coat.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Coat Color", target: material, propertyKey: "coatColor", isLinearMode: true, description: "Tints the coat, for tinted varnish or paint.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Color", target: material, propertyKey: "coatColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness", target: material, propertyKey: "coatRoughness", min: 0, max: 1, step: 0.01, description: "Controls how sharp or blurry the coat reflections appear.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat/roughening" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Roughness", target: material, propertyKey: "coatRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Roughness Anisotropy", target: material, propertyKey: "coatRoughnessAnisotropy", min: 0, max: 1, step: 0.01, description: "Stretches coat reflections in one direction for brushed or streaked looks. Requires coat_roughness > 0.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Roughness Anisotropy", target: material, propertyKey: "coatRoughnessAnisotropyTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat IOR", target: material, propertyKey: "coatIor", min: 1, max: 3, step: 0.01, description: "Index of refraction is a physical value controlling the reflective intensity of the coat.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Darkening", target: material, propertyKey: "coatDarkening", min: 0, max: 1, step: 0.01, description: "Darkens the base under the coat, similar to how real varnish deepens color.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/coat/darkening" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Coat Darkening", target: material, propertyKey: "coatDarkeningTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
|
|
14660
15602
|
};
|
|
14661
15603
|
/**
|
|
14662
15604
|
* Displays the fuzz layer properties of an OpenPBR material.
|
|
@@ -14665,19 +15607,7 @@ const OpenPBRMaterialCoatProperties = (props) => {
|
|
|
14665
15607
|
*/
|
|
14666
15608
|
const OpenPBRMaterialFuzzProperties = (props) => {
|
|
14667
15609
|
const { material } = props;
|
|
14668
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeight", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14669
|
-
if (files.length > 0) {
|
|
14670
|
-
UpdateTexture(files[0], material, (texture) => (material.fuzzWeightTexture = texture));
|
|
14671
|
-
}
|
|
14672
|
-
} }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Fuzz Color", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14673
|
-
if (files.length > 0) {
|
|
14674
|
-
UpdateTexture(files[0], material, (texture) => (material.fuzzColorTexture = texture));
|
|
14675
|
-
}
|
|
14676
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Roughness", target: material, propertyKey: "fuzzRoughness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Fuzz Roughness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14677
|
-
if (files.length > 0) {
|
|
14678
|
-
UpdateTexture(files[0], material, (texture) => (material.fuzzRoughnessTexture = texture));
|
|
14679
|
-
}
|
|
14680
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Number of Samples", target: material, propertyKey: "fuzzSampleNumber", min: 4, max: 64, step: 1 })] }));
|
|
15610
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the fuzz.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/fuzz" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColor", isLinearMode: true, description: "Controls the color of the fuzz.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/fuzz" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Roughness", target: material, propertyKey: "fuzzRoughness", min: 0, max: 1, step: 0.01, description: "Controls the roughness of the fuzz.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/fuzz" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Fuzz Roughness", target: material, propertyKey: "fuzzRoughnessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Number of Samples", target: material, propertyKey: "fuzzSampleNumber", min: 4, max: 64, step: 1 })] }));
|
|
14681
15611
|
};
|
|
14682
15612
|
/**
|
|
14683
15613
|
* Displays the emission properties of an OpenPBR material.
|
|
@@ -14686,11 +15616,7 @@ const OpenPBRMaterialFuzzProperties = (props) => {
|
|
|
14686
15616
|
*/
|
|
14687
15617
|
const OpenPBRMaterialEmissionProperties = (props) => {
|
|
14688
15618
|
const { material } = props;
|
|
14689
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color3PropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColor", isLinearMode: true }), jsx(
|
|
14690
|
-
if (files.length > 0) {
|
|
14691
|
-
UpdateTexture(files[0], material, (texture) => (material.emissionColorTexture = texture));
|
|
14692
|
-
}
|
|
14693
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Emission Luminance", target: material, propertyKey: "emissionLuminance", min: 0, max: 10, step: 0.01 })] }));
|
|
15619
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color3PropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColor", isLinearMode: true, description: "Controls the color of the glow.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/emission" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColorTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Emission Luminance", target: material, propertyKey: "emissionLuminance", min: 0, max: 10, step: 0.01, description: "Controls how bright the glow is.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/emission" })] }));
|
|
14694
15620
|
};
|
|
14695
15621
|
/**
|
|
14696
15622
|
* Displays the thin film properties of an OpenPBR material.
|
|
@@ -14699,15 +15625,7 @@ const OpenPBRMaterialEmissionProperties = (props) => {
|
|
|
14699
15625
|
*/
|
|
14700
15626
|
const OpenPBRMaterialThinFilmProperties = (props) => {
|
|
14701
15627
|
const { material } = props;
|
|
14702
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeight", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14703
|
-
if (files.length > 0) {
|
|
14704
|
-
UpdateTexture(files[0], material, (texture) => (material.thinFilmWeightTexture = texture));
|
|
14705
|
-
}
|
|
14706
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Thickness", target: material, propertyKey: "thinFilmThickness", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Thin Film Thickness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14707
|
-
if (files.length > 0) {
|
|
14708
|
-
UpdateTexture(files[0], material, (texture) => (material.thinFilmThicknessTexture = texture));
|
|
14709
|
-
}
|
|
14710
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film IOR", target: material, propertyKey: "thinFilmIor", min: 1, max: 3, step: 0.01 })] }));
|
|
15628
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeight", min: 0, max: 1, step: 0.01, description: "Controls the presence of the thin-film.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-filmiridescence" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeightTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Thickness", target: material, propertyKey: "thinFilmThickness", min: 0, max: 1, step: 0.01, description: "Changes the color pattern of the iridescence.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-filmiridescence" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Thin Film Thickness", target: material, propertyKey: "thinFilmThicknessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film IOR", target: material, propertyKey: "thinFilmIor", min: 1, max: 3, step: 0.01, description: "Alters the strength and contrast of the color shift based in the index of refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-filmiridescence" })] }));
|
|
14711
15629
|
};
|
|
14712
15630
|
/**
|
|
14713
15631
|
* Displays the geometry properties of an OpenPBR material.
|
|
@@ -14716,31 +15634,7 @@ const OpenPBRMaterialThinFilmProperties = (props) => {
|
|
|
14716
15634
|
*/
|
|
14717
15635
|
const OpenPBRMaterialGeometryProperties = (props) => {
|
|
14718
15636
|
const { material } = props;
|
|
14719
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Opacity", target: material, propertyKey: "geometryOpacity", min: 0, max: 1, step: 0.01 }), jsx(
|
|
14720
|
-
if (files.length > 0) {
|
|
14721
|
-
UpdateTexture(files[0], material, (texture) => (material.geometryOpacityTexture = texture));
|
|
14722
|
-
}
|
|
14723
|
-
} }), jsx(FileUploadLine, { label: "Geometry Normal", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14724
|
-
if (files.length > 0) {
|
|
14725
|
-
UpdateTexture(files[0], material, (texture) => (material.geometryNormalTexture = texture));
|
|
14726
|
-
}
|
|
14727
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Tangent Angle", target: material, propertyKey: "geometryTangentAngle", min: 0, max: Math.PI, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Tangent", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14728
|
-
if (files.length > 0) {
|
|
14729
|
-
UpdateTexture(files[0], material, (texture) => (material.geometryTangentTexture = texture));
|
|
14730
|
-
}
|
|
14731
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Tangent Angle", target: material, propertyKey: "geometryCoatTangentAngle", min: 0, max: Math.PI, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Coat Normal", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14732
|
-
if (files.length > 0) {
|
|
14733
|
-
UpdateTexture(files[0], material, (texture) => (material.geometryCoatNormalTexture = texture));
|
|
14734
|
-
}
|
|
14735
|
-
} }), jsx(FileUploadLine, { label: "Geometry Coat Tangent", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14736
|
-
if (files.length > 0) {
|
|
14737
|
-
UpdateTexture(files[0], material, (texture) => (material.geometryCoatTangentTexture = texture));
|
|
14738
|
-
}
|
|
14739
|
-
} }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Geometry Thickness", target: material, propertyKey: "geometryThickness", min: 0, step: 0.1 }), jsx(FileUploadLine, { label: "Geometry Thickness", accept: ".jpg, .png, .webp, .tga, .dds, .env, .exr", onClick: (files) => {
|
|
14740
|
-
if (files.length > 0) {
|
|
14741
|
-
UpdateTexture(files[0], material, (texture) => (material.geometryThicknessTexture = texture));
|
|
14742
|
-
}
|
|
14743
|
-
} })] }));
|
|
15637
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Geometry Opacity", target: material, propertyKey: "geometryOpacity", min: 0, max: 1, step: 0.01, description: "Controls material presence and transparency cutout.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/opacity/transparency" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Opacity", target: material, propertyKey: "geometryOpacityTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: CheckboxPropertyLine, label: "Thin-Walled", target: material, propertyKey: "geometryThinWalled", description: "When enabled, treats material as a thin shell (like leaves, paper sheets or windows). Disables ray bending in refraction.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thin-walledcase" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Normal", target: material, propertyKey: "geometryNormalTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Tangent Angle", target: material, propertyKey: "geometryTangentAngle", min: 0, max: Math.PI, step: 0.01, description: "Tangent vector controlling anisotropic reflection direction for the base (metal and non-metal). Works with specular_roughness_anisotropy.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/geometry/tangent" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Tangent", target: material, propertyKey: "geometryTangentTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Tangent Angle", target: material, propertyKey: "geometryCoatTangentAngle", min: 0, max: Math.PI, step: 0.01, description: "Tangent vector controlling anisotropic reflection direction for the coat. Works with coat_roughness_anisotropy.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/geometry/coat-tangent" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Coat Normal", target: material, propertyKey: "geometryCoatNormalTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Coat Tangent", target: material, propertyKey: "geometryCoatTangentTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Geometry Thickness", target: material, propertyKey: "geometryThickness", min: 0, step: 0.001, description: "Controls the thickness of the geometry for volume approximations.", docLink: "https://academysoftwarefoundation.github.io/OpenPBR/index.html#model/thickness" }), jsx(BoundProperty, { component: TextureSelectorPropertyLine, label: "Geometry Thickness", target: material, propertyKey: "geometryThicknessTexture", scene: material.getScene(), defaultValue: null, onLink: (texture) => void 0 })] }));
|
|
14744
15638
|
};
|
|
14745
15639
|
|
|
14746
15640
|
const LightFalloffOptions = [
|
|
@@ -15101,6 +15995,10 @@ const MaterialPropertiesServiceDefinition = {
|
|
|
15101
15995
|
section: "OpenPBR",
|
|
15102
15996
|
component: ({ context }) => jsx(OpenPBRMaterialTransmissionProperties, { material: context }),
|
|
15103
15997
|
},
|
|
15998
|
+
{
|
|
15999
|
+
section: "OpenPBR",
|
|
16000
|
+
component: ({ context }) => jsx(OpenPBRMaterialSubsurfaceProperties, { material: context }),
|
|
16001
|
+
},
|
|
15104
16002
|
{
|
|
15105
16003
|
section: "OpenPBR",
|
|
15106
16004
|
component: ({ context }) => jsx(OpenPBRMaterialCoatProperties, { material: context }),
|
|
@@ -16142,6 +17040,8 @@ const ParticleSystemEmitterProperties = (props) => {
|
|
|
16142
17040
|
useProperty(emitterVector, "_y");
|
|
16143
17041
|
useProperty(emitterVector, "_z");
|
|
16144
17042
|
const particleEmitterType = useProperty(system, "particleEmitterType");
|
|
17043
|
+
const meshEmitter = particleEmitterType instanceof MeshParticleEmitter ? particleEmitterType : undefined;
|
|
17044
|
+
const useMeshNormalsForDirection = useProperty(meshEmitter, "useMeshNormalsForDirection");
|
|
16145
17045
|
// Derive the current dropdown value from the current instance to stay in sync with external changes.
|
|
16146
17046
|
const derivedEmitterTypeKey = (() => {
|
|
16147
17047
|
if (particleEmitterType instanceof SphereParticleEmitter) {
|
|
@@ -16245,21 +17145,21 @@ const ParticleSystemEmitterProperties = (props) => {
|
|
|
16245
17145
|
break;
|
|
16246
17146
|
}
|
|
16247
17147
|
}
|
|
16248
|
-
} }), particleEmitterType instanceof MeshParticleEmitter && (
|
|
16249
|
-
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
16259
|
-
|
|
16260
|
-
|
|
16261
|
-
|
|
16262
|
-
|
|
17148
|
+
} }), particleEmitterType instanceof MeshParticleEmitter && (jsxs(Fragment, { children: [scene && scene.meshes.length > 0 ? (jsx(Property, { component: StringDropdownPropertyLine, propertyPath: "source", label: "Source", value: particleEmitterType.mesh ? `mesh:${particleEmitterType.mesh.uniqueId}` : `mesh:${scene.meshes[0].uniqueId}`, options: scene.meshes.map((mesh) => {
|
|
17149
|
+
const uniqueId = mesh.uniqueId;
|
|
17150
|
+
const name = mesh.name ?? "(unnamed)";
|
|
17151
|
+
const label = `${name} (#${uniqueId})`;
|
|
17152
|
+
return {
|
|
17153
|
+
label,
|
|
17154
|
+
value: `mesh:${uniqueId}`,
|
|
17155
|
+
};
|
|
17156
|
+
}), onChange: (value) => {
|
|
17157
|
+
const next = String(value);
|
|
17158
|
+
const uniqueIdText = next.replace("mesh:", "");
|
|
17159
|
+
const uniqueId = Number(uniqueIdText);
|
|
17160
|
+
const mesh = scene.meshes.find((candidate) => candidate.uniqueId === uniqueId) ?? null;
|
|
17161
|
+
particleEmitterType.mesh = mesh;
|
|
17162
|
+
} })) : (jsx(Property, { component: TextPropertyLine, propertyPath: "source", label: "Source", value: "No meshes in scene." })), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use normals for direction", target: particleEmitterType, propertyKey: "useMeshNormalsForDirection" }), !useMeshNormalsForDirection && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction1", target: particleEmitterType, propertyKey: "direction1" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction2", target: particleEmitterType, propertyKey: "direction2" })] }))] })), particleEmitterType instanceof BoxParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction1", target: particleEmitterType, propertyKey: "direction1" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction2", target: particleEmitterType, propertyKey: "direction2" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Min emit box", target: particleEmitterType, propertyKey: "minEmitBox" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Max emit box", target: particleEmitterType, propertyKey: "maxEmitBox" })] })), particleEmitterType instanceof ConeParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Height range", target: particleEmitterType, propertyKey: "heightRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Emit from spawn point only", target: particleEmitterType, propertyKey: "emitFromSpawnPointOnly" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof SphereParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", target: particleEmitterType, propertyKey: "radius", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof CylinderParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", target: particleEmitterType, propertyKey: "radius", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Height", target: particleEmitterType, propertyKey: "height", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof HemisphericParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius", target: particleEmitterType, propertyKey: "radius", min: 0, step: 0.1 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Radius range", target: particleEmitterType, propertyKey: "radiusRange", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Direction randomizer", target: particleEmitterType, propertyKey: "directionRandomizer", min: 0, max: 1, step: 0.01 })] })), particleEmitterType instanceof PointParticleEmitter && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction1", target: particleEmitterType, propertyKey: "direction1" }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Direction2", target: particleEmitterType, propertyKey: "direction2" })] }))] })), !scene && jsx(TextPropertyLine, { label: "Emitter", value: "No scene available." })] }));
|
|
16263
17163
|
};
|
|
16264
17164
|
|
|
16265
17165
|
const useStyles$h = makeStyles({
|
|
@@ -17172,42 +18072,6 @@ const PhysicsPropertiesServiceDefinition = {
|
|
|
17172
18072
|
},
|
|
17173
18073
|
};
|
|
17174
18074
|
|
|
17175
|
-
const useCheckboxStyles = makeStyles({
|
|
17176
|
-
indicator: {
|
|
17177
|
-
margin: 0,
|
|
17178
|
-
},
|
|
17179
|
-
});
|
|
17180
|
-
/**
|
|
17181
|
-
* This is a primitive fluent checkbox that can both read and write checked state
|
|
17182
|
-
* @param props
|
|
17183
|
-
* @returns Checkbox component
|
|
17184
|
-
*/
|
|
17185
|
-
const Checkbox = (props) => {
|
|
17186
|
-
Checkbox.displayName = "Checkbox";
|
|
17187
|
-
const [checked, setChecked] = useState(() => props.value ?? false);
|
|
17188
|
-
const classes = useCheckboxStyles();
|
|
17189
|
-
useEffect(() => {
|
|
17190
|
-
if (props.value != undefined) {
|
|
17191
|
-
setChecked(props.value); // Update local state when props.checked changes
|
|
17192
|
-
}
|
|
17193
|
-
}, [props.value]);
|
|
17194
|
-
const onChange = (ev, _) => {
|
|
17195
|
-
props.onChange(ev.target.checked);
|
|
17196
|
-
setChecked(ev.target.checked);
|
|
17197
|
-
};
|
|
17198
|
-
return jsx(Checkbox$1, { checked: checked, onChange: onChange, indicator: { className: classes.indicator } });
|
|
17199
|
-
};
|
|
17200
|
-
|
|
17201
|
-
/**
|
|
17202
|
-
* Wraps a checkbox in a property line
|
|
17203
|
-
* @param props - PropertyLineProps and CheckboxProps
|
|
17204
|
-
* @returns property-line wrapped checkbox
|
|
17205
|
-
*/
|
|
17206
|
-
const CheckboxPropertyLine = (props) => {
|
|
17207
|
-
CheckboxPropertyLine.displayName = "CheckboxPropertyLine";
|
|
17208
|
-
return (jsx(PropertyLine, { ...props, children: jsx(Checkbox, { ...props }) }));
|
|
17209
|
-
};
|
|
17210
|
-
|
|
17211
18075
|
/**
|
|
17212
18076
|
* The properties component for a post process.
|
|
17213
18077
|
* @param props - The properties component props containing the post process.
|
|
@@ -17366,7 +18230,7 @@ const IblShadowsRenderPipelineScreenspaceProperties = (props) => {
|
|
|
17366
18230
|
const IblShadowsRenderPipelineDebugProperties = (props) => {
|
|
17367
18231
|
const { pipeline } = props;
|
|
17368
18232
|
const allowDebugPasses = useProperty(pipeline, "allowDebugPasses");
|
|
17369
|
-
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Allow Debug Passes", target: pipeline, propertyKey: "allowDebugPasses" }), allowDebugPasses && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "
|
|
18233
|
+
return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Allow Debug Passes", target: pipeline, propertyKey: "allowDebugPasses" }), allowDebugPasses && (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "CDF Debug Enabled", target: pipeline, propertyKey: "cdfDebugEnabled" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Voxel Tracing Debug Enabled", target: pipeline, propertyKey: "voxelTracingDebugEnabled" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Spatial Blur Debug Enabled", target: pipeline, propertyKey: "spatialBlurPassDebugEnabled" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Accumulation Pass Debug Enabled", target: pipeline, propertyKey: "accumulationPassDebugEnabled" })] }))] }));
|
|
17370
18234
|
};
|
|
17371
18235
|
|
|
17372
18236
|
const RenderingPipelinePropertiesServiceDefinition = {
|
|
@@ -17899,12 +18763,42 @@ const SpriteManagerActionsProperties = (props) => {
|
|
|
17899
18763
|
* @param faceOrLayer if the texture has multiple faces, the face index to use for the source. For 2D array textures, this is the layer index.
|
|
17900
18764
|
* @param channels a filter for which of the RGBA channels to return in the result
|
|
17901
18765
|
* @param lod if the texture has multiple LODs, the lod index to use for the source
|
|
18766
|
+
* @param slice if the texture is 3D, the depth slice index to use for the source
|
|
17902
18767
|
* @returns the 8-bit texture data
|
|
17903
18768
|
*/
|
|
17904
|
-
async function ApplyChannelsToTextureDataAsync(texture, width, height, faceOrLayer, channels, lod = 0) {
|
|
18769
|
+
async function ApplyChannelsToTextureDataAsync(texture, width, height, faceOrLayer, channels, lod = 0, slice = 0) {
|
|
18770
|
+
const internalTexture = texture.getInternalTexture();
|
|
18771
|
+
const is3DTexture = texture.is3D || !!internalTexture?.is3D;
|
|
18772
|
+
const textureFormat = internalTexture?.format ?? texture.textureFormat;
|
|
17905
18773
|
// For cube maps, force RTT path to ensure correct face orientation and gamma correction
|
|
17906
18774
|
// For 2D array textures, face is reinterpreted as the layer index for direct pixel readback
|
|
17907
|
-
const data = await GetTextureDataAsync(texture, width, height, faceOrLayer, lod, texture.isCube);
|
|
18775
|
+
const data = await GetTextureDataAsync(texture, width, height, faceOrLayer, lod, texture.isCube || is3DTexture, slice);
|
|
18776
|
+
const forceOpaqueAlpha = is3DTexture || _TextureFormatHasNoAlpha(textureFormat);
|
|
18777
|
+
if (forceOpaqueAlpha) {
|
|
18778
|
+
for (let i = 3; i < width * height * 4; i += 4) {
|
|
18779
|
+
data[i] = 255;
|
|
18780
|
+
}
|
|
18781
|
+
}
|
|
18782
|
+
else if (texture.getScene()?.getEngine().isWebGPU) {
|
|
18783
|
+
let alphaAllZero = true;
|
|
18784
|
+
let hasNonZeroColor = false;
|
|
18785
|
+
for (let i = 0; i < width * height * 4; i += 4) {
|
|
18786
|
+
if (data[i] !== 0 || data[i + 1] !== 0 || data[i + 2] !== 0) {
|
|
18787
|
+
hasNonZeroColor = true;
|
|
18788
|
+
}
|
|
18789
|
+
if (data[i + 3] !== 0) {
|
|
18790
|
+
alphaAllZero = false;
|
|
18791
|
+
break;
|
|
18792
|
+
}
|
|
18793
|
+
}
|
|
18794
|
+
// Some WebGPU RTT readback paths can return zeroed alpha for textures that
|
|
18795
|
+
// are effectively opaque in source data. In that case, force opaque preview.
|
|
18796
|
+
if (alphaAllZero && hasNonZeroColor) {
|
|
18797
|
+
for (let i = 3; i < width * height * 4; i += 4) {
|
|
18798
|
+
data[i] = 255;
|
|
18799
|
+
}
|
|
18800
|
+
}
|
|
18801
|
+
}
|
|
17908
18802
|
if (!channels.R || !channels.G || !channels.B || !channels.A) {
|
|
17909
18803
|
for (let i = 0; i < width * height * 4; i += 4) {
|
|
17910
18804
|
// If alpha is the only channel, just display alpha across all channels
|
|
@@ -17977,6 +18871,40 @@ async function ApplyChannelsToTextureDataAsync(texture, width, height, faceOrLay
|
|
|
17977
18871
|
}
|
|
17978
18872
|
return data;
|
|
17979
18873
|
}
|
|
18874
|
+
function _TextureFormatHasNoAlpha(format) {
|
|
18875
|
+
switch (format) {
|
|
18876
|
+
case Constants.TEXTUREFORMAT_LUMINANCE:
|
|
18877
|
+
case Constants.TEXTUREFORMAT_R:
|
|
18878
|
+
case Constants.TEXTUREFORMAT_R16_UNORM:
|
|
18879
|
+
case Constants.TEXTUREFORMAT_R16_SNORM:
|
|
18880
|
+
case Constants.TEXTUREFORMAT_RG:
|
|
18881
|
+
case Constants.TEXTUREFORMAT_RG16_UNORM:
|
|
18882
|
+
case Constants.TEXTUREFORMAT_RG16_SNORM:
|
|
18883
|
+
case Constants.TEXTUREFORMAT_RGB:
|
|
18884
|
+
case Constants.TEXTUREFORMAT_RGB16_UNORM:
|
|
18885
|
+
case Constants.TEXTUREFORMAT_RGB16_SNORM:
|
|
18886
|
+
case Constants.TEXTUREFORMAT_DEPTH16:
|
|
18887
|
+
case Constants.TEXTUREFORMAT_DEPTH24:
|
|
18888
|
+
case Constants.TEXTUREFORMAT_DEPTH24_STENCIL8:
|
|
18889
|
+
case Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8:
|
|
18890
|
+
case Constants.TEXTUREFORMAT_DEPTH32_FLOAT:
|
|
18891
|
+
case Constants.TEXTUREFORMAT_DEPTH32FLOAT_STENCIL8:
|
|
18892
|
+
case Constants.TEXTUREFORMAT_STENCIL8:
|
|
18893
|
+
case Constants.TEXTUREFORMAT_RED_INTEGER:
|
|
18894
|
+
case Constants.TEXTUREFORMAT_RG_INTEGER:
|
|
18895
|
+
case Constants.TEXTUREFORMAT_RGB_INTEGER:
|
|
18896
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_RGB_S3TC_DXT1:
|
|
18897
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_SRGB_S3TC_DXT1_EXT:
|
|
18898
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_RGB_ETC1_WEBGL:
|
|
18899
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_RGB8_ETC2:
|
|
18900
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_SRGB8_ETC2:
|
|
18901
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
|
|
18902
|
+
case Constants.TEXTUREFORMAT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
|
|
18903
|
+
return true;
|
|
18904
|
+
default:
|
|
18905
|
+
return false;
|
|
18906
|
+
}
|
|
18907
|
+
}
|
|
17980
18908
|
|
|
17981
18909
|
const useStyles$9 = makeStyles({
|
|
17982
18910
|
root: {
|
|
@@ -17988,6 +18916,10 @@ const useStyles$9 = makeStyles({
|
|
|
17988
18916
|
gap: tokens.spacingHorizontalXS,
|
|
17989
18917
|
padding: 0,
|
|
17990
18918
|
},
|
|
18919
|
+
sliderContainer: {
|
|
18920
|
+
marginTop: tokens.spacingVerticalXS,
|
|
18921
|
+
marginBottom: tokens.spacingVerticalXS,
|
|
18922
|
+
},
|
|
17991
18923
|
controlButton: {
|
|
17992
18924
|
minWidth: "auto",
|
|
17993
18925
|
flex: "1 1 0", // Equal flex grow/shrink with 0 basis
|
|
@@ -18019,15 +18951,28 @@ const TextureChannelStates = {
|
|
|
18019
18951
|
A: { R: false, G: false, B: false, A: true },
|
|
18020
18952
|
ALL: { R: true, G: true, B: true, A: true },
|
|
18021
18953
|
};
|
|
18954
|
+
/**
|
|
18955
|
+
* Displays a 2D preview for a texture, including channels/cube face controls.
|
|
18956
|
+
* @param props The component properties.
|
|
18957
|
+
* @returns The rendered component.
|
|
18958
|
+
*/
|
|
18022
18959
|
const TexturePreview = (props) => {
|
|
18023
18960
|
const { texture, disableToolbar = false, maxWidth = "100%", maxHeight = "384px", offsetX = 0, offsetY = 0, width, height, imperativeRef } = props;
|
|
18024
18961
|
const classes = useStyles$9();
|
|
18025
18962
|
const canvasRef = useRef(null);
|
|
18026
18963
|
const [channels, setChannels] = useState(TextureChannelStates.ALL);
|
|
18027
18964
|
const [face, setFace] = useState(0);
|
|
18965
|
+
const [slice, setSlice] = useState(0);
|
|
18966
|
+
const [lod, setLod] = useState(0);
|
|
18028
18967
|
const [layer, setLayer] = useState(0);
|
|
18029
18968
|
const [canvasStyle, setCanvasStyle] = useState();
|
|
18030
18969
|
const internalTexture = useProperty(texture, "_texture");
|
|
18970
|
+
const is3DTexture = !!internalTexture?.is3D;
|
|
18971
|
+
const baseDepth = Math.max(internalTexture?.depth ?? 1, 1);
|
|
18972
|
+
const lodDepth = Math.max(1, Math.floor(baseDepth / Math.pow(2, lod)));
|
|
18973
|
+
const maxSlice = lodDepth - 1;
|
|
18974
|
+
const lodBaseSize = Math.max(1, Math.min(internalTexture?.width ?? 1, internalTexture?.height ?? 1, is3DTexture ? (internalTexture?.depth ?? 1) : Number.MAX_SAFE_INTEGER));
|
|
18975
|
+
const maxLod = internalTexture?.generateMipMaps ? Math.max(0, Math.floor(Math.log2(lodBaseSize))) : 0;
|
|
18031
18976
|
const showLayerDropdown = texture.is2DArray;
|
|
18032
18977
|
const layerCount = texture.is2DArray && internalTexture ? internalTexture.depth : 0;
|
|
18033
18978
|
useEffect(() => {
|
|
@@ -18044,7 +18989,14 @@ const TexturePreview = (props) => {
|
|
|
18044
18989
|
}
|
|
18045
18990
|
try {
|
|
18046
18991
|
await WhenTextureReadyAsync(texture); // Ensure texture is loaded before grabbing size
|
|
18047
|
-
const
|
|
18992
|
+
const size = texture.getSize();
|
|
18993
|
+
let textureWidth = size.width || internalTexture?.width || 1;
|
|
18994
|
+
let textureHeight = size.height || internalTexture?.height || textureWidth;
|
|
18995
|
+
if (is3DTexture) {
|
|
18996
|
+
const squareSize = Math.max(1, Math.min(textureWidth, textureHeight));
|
|
18997
|
+
textureWidth = squareSize;
|
|
18998
|
+
textureHeight = squareSize;
|
|
18999
|
+
}
|
|
18048
19000
|
// Calculate canvas dimensions
|
|
18049
19001
|
const canvasWidth = width ?? textureWidth;
|
|
18050
19002
|
const canvasHeight = height ?? textureHeight;
|
|
@@ -18054,7 +19006,7 @@ const TexturePreview = (props) => {
|
|
|
18054
19006
|
const imageWidth = `min(${maxWidth}, calc(${maxHeight} * ${aspectRatio}))`;
|
|
18055
19007
|
setCanvasStyle({ width: imageWidth });
|
|
18056
19008
|
// Fetch texture data BEFORE clearing the canvas to avoid flicker
|
|
18057
|
-
const data = await ApplyChannelsToTextureDataAsync(texture, textureWidth, textureHeight, texture.is2DArray ? layer : face, channels);
|
|
19009
|
+
const data = await ApplyChannelsToTextureDataAsync(texture, textureWidth, textureHeight, texture.is2DArray ? layer : face, channels, lod, slice);
|
|
18058
19010
|
// Now set canvas dimensions (this clears the canvas) and draw immediately
|
|
18059
19011
|
canvas.width = canvasWidth;
|
|
18060
19012
|
canvas.height = canvasHeight;
|
|
@@ -18069,7 +19021,19 @@ const TexturePreview = (props) => {
|
|
|
18069
19021
|
catch {
|
|
18070
19022
|
// If we fail, leave the canvas empty
|
|
18071
19023
|
}
|
|
18072
|
-
}, [texture, face, channels, offsetX, offsetY, width, height, internalTexture, layer]);
|
|
19024
|
+
}, [texture, face, channels, lod, slice, offsetX, offsetY, width, height, internalTexture, layer]);
|
|
19025
|
+
useEffect(() => {
|
|
19026
|
+
if (!is3DTexture || slice <= maxSlice) {
|
|
19027
|
+
return;
|
|
19028
|
+
}
|
|
19029
|
+
setSlice(maxSlice);
|
|
19030
|
+
}, [is3DTexture, maxSlice, slice]);
|
|
19031
|
+
useEffect(() => {
|
|
19032
|
+
if (lod <= maxLod) {
|
|
19033
|
+
return;
|
|
19034
|
+
}
|
|
19035
|
+
setLod(maxLod);
|
|
19036
|
+
}, [lod, maxLod]);
|
|
18073
19037
|
useImperativeHandle(imperativeRef, () => ({ refresh: updatePreviewAsync }), [updatePreviewAsync]);
|
|
18074
19038
|
useEffect(() => {
|
|
18075
19039
|
void updatePreviewAsync();
|
|
@@ -18078,7 +19042,7 @@ const TexturePreview = (props) => {
|
|
|
18078
19042
|
useEffect(() => {
|
|
18079
19043
|
void updatePreviewAsync();
|
|
18080
19044
|
}, [isPinned]);
|
|
18081
|
-
return (jsx(LineContainer, { uniqueId: "TexturePreview", children: jsxs("div", { className: classes.root, children: [disableToolbar ? null : texture.isCube ? (jsx(Toolbar$1, { className: classes.controls, size: size, "aria-label": "Cube Faces", children: ["+X", "-X", "+Y", "-Y", "+Z", "-Z"].map((label, idx) => (jsx(ToolbarButton, { className: classes.controlButton, appearance: face === idx ? "primary" : "subtle", onClick: () => setFace(idx), children: label }, label))) })) : (jsx(Toolbar$1, { className: classes.controls, size: size, "aria-label": "Channels", children: ["R", "G", "B", "A", "ALL"].map((ch) => (jsx(ToolbarButton, { className: classes.controlButton, appearance: channels === TextureChannelStates[ch] ? "primary" : "subtle", onClick: () => setChannels(TextureChannelStates[ch]), children: ch }, ch))) })), jsx("div", { className: classes.previewContainer, children: jsx("canvas", { ref: canvasRef, className: classes.preview, style: canvasStyle }) }), !disableToolbar && showLayerDropdown && layerCount > 0 && (jsx(SyncedSliderPropertyLine, { label: "Layer", value: layer, onChange: setLayer, min: 0, max: layerCount - 1, step: 1 })), texture.isRenderTarget && (jsx(Button$1, { appearance: "outline", onClick: () => {
|
|
19045
|
+
return (jsx(LineContainer, { uniqueId: "TexturePreview", children: jsxs("div", { className: classes.root, children: [disableToolbar ? null : texture.isCube ? (jsx(Toolbar$1, { className: classes.controls, size: size, "aria-label": "Cube Faces", children: ["+X", "-X", "+Y", "-Y", "+Z", "-Z"].map((label, idx) => (jsx(ToolbarButton, { className: classes.controlButton, appearance: face === idx ? "primary" : "subtle", onClick: () => setFace(idx), children: label }, label))) })) : (jsx(Toolbar$1, { className: classes.controls, size: size, "aria-label": "Channels", children: ["R", "G", "B", "A", "ALL"].map((ch) => (jsx(ToolbarButton, { className: classes.controlButton, appearance: channels === TextureChannelStates[ch] ? "primary" : "subtle", onClick: () => setChannels(TextureChannelStates[ch]), children: ch }, ch))) })), is3DTexture && (jsxs("div", { className: classes.sliderContainer, children: [jsxs(Label, { children: ["Slice: ", slice, " / ", maxSlice] }), jsx(Slider$1, { min: 0, max: maxSlice, step: 1, value: slice, onChange: (_, data) => setSlice(data.value) })] })), maxLod > 0 && (jsxs("div", { className: classes.sliderContainer, children: [jsxs(Label, { children: ["LOD: ", lod] }), jsx(Slider$1, { min: 0, max: maxLod, step: 1, value: lod, onChange: (_, data) => setLod(data.value) })] })), jsx("div", { className: classes.previewContainer, children: jsx("canvas", { ref: canvasRef, className: classes.preview, style: canvasStyle }) }), !disableToolbar && showLayerDropdown && layerCount > 0 && (jsx(SyncedSliderPropertyLine, { label: "Layer", value: layer, onChange: setLayer, min: 0, max: layerCount - 1, step: 1 })), texture.isRenderTarget && (jsx(Button$1, { appearance: "outline", onClick: () => {
|
|
18082
19046
|
void updatePreviewAsync();
|
|
18083
19047
|
}, children: "Refresh" }))] }) }));
|
|
18084
19048
|
};
|
|
@@ -19846,7 +20810,7 @@ const AdvancedDynamicTextureGeneralProperties = MakeLazyComponent(async () => {
|
|
|
19846
20810
|
const AdvancedDynamicTexturePreviewProperties = (props) => {
|
|
19847
20811
|
const { texture } = props;
|
|
19848
20812
|
return (jsx(Fragment, { children: jsx(ButtonLine, { label: "Edit GUI", icon: EditRegular, onClick: async () => {
|
|
19849
|
-
const { GUIEditor } = await import('@babylonjs/gui-editor
|
|
20813
|
+
const { GUIEditor } = await import('@babylonjs/gui-editor');
|
|
19850
20814
|
await GUIEditor.Show({ liveGuiTexture: texture });
|
|
19851
20815
|
} }) }));
|
|
19852
20816
|
};
|
|
@@ -20551,7 +21515,7 @@ const GuiExplorerServiceDefinition = {
|
|
|
20551
21515
|
icon: () => jsx(EditRegular, {}),
|
|
20552
21516
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
20553
21517
|
execute: async () => {
|
|
20554
|
-
const { GUIEditor } = await import('@babylonjs/gui-editor
|
|
21518
|
+
const { GUIEditor } = await import('@babylonjs/gui-editor');
|
|
20555
21519
|
await GUIEditor.Show({ liveGuiTexture: texture });
|
|
20556
21520
|
},
|
|
20557
21521
|
};
|
|
@@ -20671,6 +21635,9 @@ const MaterialExplorerServiceDefinition = {
|
|
|
20671
21635
|
function IsCameraFrameGraphTask(task) {
|
|
20672
21636
|
return task.camera instanceof Camera;
|
|
20673
21637
|
}
|
|
21638
|
+
function IsNodesSectionType(node) {
|
|
21639
|
+
return node instanceof TransformNode || node instanceof Camera || node instanceof Light;
|
|
21640
|
+
}
|
|
20674
21641
|
const NodeExplorerServiceDefinition = {
|
|
20675
21642
|
friendlyName: "Node Explorer",
|
|
20676
21643
|
consumes: [SceneExplorerServiceIdentity, SceneContextIdentity, GizmoServiceIdentity, WatcherServiceIdentity],
|
|
@@ -20680,21 +21647,36 @@ const NodeExplorerServiceDefinition = {
|
|
|
20680
21647
|
return undefined;
|
|
20681
21648
|
}
|
|
20682
21649
|
const nodeMovedObservable = new Observable();
|
|
21650
|
+
// Set of all nodes known to be in the scene, rebuilt each time getRootEntities
|
|
21651
|
+
// is called. Used by getEntityDisplayInfo to detect orphaned ancestor nodes.
|
|
21652
|
+
const knownSceneNodes = new Set();
|
|
20683
21653
|
const sectionRegistration = sceneExplorerService.addSection({
|
|
20684
21654
|
displayName: "Nodes",
|
|
20685
21655
|
order: 100 /* DefaultSectionsOrder.Nodes */,
|
|
20686
21656
|
getRootEntities: () => {
|
|
20687
|
-
const rootNodes =
|
|
20688
|
-
|
|
20689
|
-
//
|
|
20690
|
-
//
|
|
21657
|
+
const rootNodes = new Set(scene.rootNodes);
|
|
21658
|
+
knownSceneNodes.clear();
|
|
21659
|
+
// Ensure all nodes in the scene are reachable in the explorer, even if their
|
|
21660
|
+
// parent was removed from the scene or is not a type shown in the Nodes section.
|
|
20691
21661
|
for (const node of [...scene.meshes, ...scene.transformNodes, ...scene.cameras, ...scene.lights]) {
|
|
20692
|
-
|
|
20693
|
-
|
|
20694
|
-
|
|
20695
|
-
|
|
20696
|
-
|
|
20697
|
-
|
|
21662
|
+
knownSceneNodes.add(node);
|
|
21663
|
+
if (!node.parent) {
|
|
21664
|
+
continue;
|
|
21665
|
+
}
|
|
21666
|
+
if (!IsNodesSectionType(node.parent)) {
|
|
21667
|
+
// Parent is not a type shown in the Nodes section (e.g. a Bone).
|
|
21668
|
+
// Treat this node as a root so it still appears in the explorer.
|
|
21669
|
+
rootNodes.add(node);
|
|
21670
|
+
}
|
|
21671
|
+
else {
|
|
21672
|
+
// Walk up through Nodes-section-type parents to find the topmost ancestor.
|
|
21673
|
+
// If that ancestor was removed from the scene (not in rootNodes), add it
|
|
21674
|
+
// so the entire subtree remains visible in the explorer.
|
|
21675
|
+
let ancestor = node.parent;
|
|
21676
|
+
while (ancestor.parent && IsNodesSectionType(ancestor.parent)) {
|
|
21677
|
+
ancestor = ancestor.parent;
|
|
21678
|
+
}
|
|
21679
|
+
rootNodes.add(ancestor);
|
|
20698
21680
|
}
|
|
20699
21681
|
}
|
|
20700
21682
|
// Lights within a clustered light container are not included in Scene.lights or Scene.rootNodes.
|
|
@@ -20702,23 +21684,32 @@ const NodeExplorerServiceDefinition = {
|
|
|
20702
21684
|
for (const light of scene.lights) {
|
|
20703
21685
|
if (light instanceof ClusteredLightContainer) {
|
|
20704
21686
|
for (const childLight of light.lights) {
|
|
20705
|
-
|
|
20706
|
-
|
|
21687
|
+
knownSceneNodes.add(childLight);
|
|
21688
|
+
if (!childLight.parent) {
|
|
21689
|
+
rootNodes.add(childLight);
|
|
20707
21690
|
}
|
|
20708
21691
|
}
|
|
20709
21692
|
}
|
|
20710
21693
|
}
|
|
20711
|
-
return rootNodes;
|
|
21694
|
+
return [...rootNodes];
|
|
20712
21695
|
},
|
|
20713
21696
|
getEntityChildren: (node) => node.getChildren(),
|
|
20714
21697
|
getEntityDisplayInfo: (node) => {
|
|
20715
21698
|
const onChangeObservable = new Observable();
|
|
20716
21699
|
const nameHookToken = watcherService.watchProperty(node, "name", () => onChangeObservable.notifyObservers());
|
|
20717
21700
|
const parentHookToken = watcherService.watchProperty(node, "parent", () => nodeMovedObservable.notifyObservers(node));
|
|
21701
|
+
// A node is "not in the scene" if it is a Nodes-section type but is not
|
|
21702
|
+
// a known scene node. This handles nodes that were removed from the scene
|
|
21703
|
+
// but still appear because a descendant is in the scene. Nodes from the
|
|
21704
|
+
// !IsNodesSectionType(parent) branch are unaffected because they always
|
|
21705
|
+
// come from the scene's tracking lists. Clustered light children are also
|
|
21706
|
+
// unaffected because they are added to knownSceneNodes explicitly.
|
|
21707
|
+
const validationError = IsNodesSectionType(node) && !knownSceneNodes.has(node) ? "This entity is not in the scene but is shown because a descendant is still in the scene." : undefined;
|
|
20718
21708
|
return {
|
|
20719
21709
|
get name() {
|
|
20720
21710
|
return node.name || `Unnamed ${node.getClassName()}`;
|
|
20721
21711
|
},
|
|
21712
|
+
validationError,
|
|
20722
21713
|
onChange: onChangeObservable,
|
|
20723
21714
|
dispose: () => {
|
|
20724
21715
|
nameHookToken.dispose();
|
|
@@ -21736,7 +22727,7 @@ const CoordinateSystemModeOptions = [
|
|
|
21736
22727
|
{ label: "Right Handed", value: GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED },
|
|
21737
22728
|
];
|
|
21738
22729
|
const GLTFLoaderOptionsTool = ({ loaderOptions }) => {
|
|
21739
|
-
return (jsx(PropertyLine, { label: "Loader Options", expandByDefault: false, expandedContent: jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Always
|
|
22730
|
+
return (jsx(PropertyLine, { label: "Loader Options", expandByDefault: false, expandedContent: jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Always Compute Bounding Box", target: loaderOptions, propertyKey: "alwaysComputeBoundingBox", nullable: true, defaultValue: LoaderOptionDefaults.alwaysComputeBoundingBox }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Always Compute Skeleton Root Node", target: loaderOptions, propertyKey: "alwaysComputeSkeletonRootNode", nullable: true, defaultValue: LoaderOptionDefaults.alwaysComputeSkeletonRootNode }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Animation Start Mode", options: AnimationStartModeOptions, target: loaderOptions, propertyKey: "animationStartMode", nullable: true, defaultValue: LoaderOptionDefaults.animationStartMode }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Capture Performance Counters", target: loaderOptions, propertyKey: "capturePerformanceCounters", nullable: true, defaultValue: LoaderOptionDefaults.capturePerformanceCounters }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Compile Materials", target: loaderOptions, propertyKey: "compileMaterials", nullable: true, defaultValue: LoaderOptionDefaults.compileMaterials }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Compile Shadow Generators", target: loaderOptions, propertyKey: "compileShadowGenerators", nullable: true, defaultValue: LoaderOptionDefaults.compileShadowGenerators }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Coordinate System", options: CoordinateSystemModeOptions, target: loaderOptions, propertyKey: "coordinateSystemMode", nullable: true, defaultValue: LoaderOptionDefaults.coordinateSystemMode }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Create Instances", target: loaderOptions, propertyKey: "createInstances", nullable: true, defaultValue: LoaderOptionDefaults.createInstances }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Don't Use Transmission Helper", target: loaderOptions, propertyKey: "dontUseTransmissionHelper", nullable: true, defaultValue: LoaderOptionDefaults.dontUseTransmissionHelper }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Enable Logging", target: loaderOptions, propertyKey: "loggingEnabled", nullable: true, defaultValue: LoaderOptionDefaults.loggingEnabled }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Load All Materials", target: loaderOptions, propertyKey: "loadAllMaterials", nullable: true, defaultValue: LoaderOptionDefaults.loadAllMaterials }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Load Morph Targets", target: loaderOptions, propertyKey: "loadMorphTargets", nullable: true, defaultValue: LoaderOptionDefaults.loadMorphTargets }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Load Node Animations", target: loaderOptions, propertyKey: "loadNodeAnimations", nullable: true, defaultValue: LoaderOptionDefaults.loadNodeAnimations }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Load Only Materials", target: loaderOptions, propertyKey: "loadOnlyMaterials", nullable: true, defaultValue: LoaderOptionDefaults.loadOnlyMaterials }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Load Skins", target: loaderOptions, propertyKey: "loadSkins", nullable: true, defaultValue: LoaderOptionDefaults.loadSkins }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Skip Materials", target: loaderOptions, propertyKey: "skipMaterials", nullable: true, defaultValue: LoaderOptionDefaults.skipMaterials }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Target FPS", target: loaderOptions, propertyKey: "targetFps", min: 1, max: 120, step: 1, nullable: true, defaultValue: LoaderOptionDefaults.targetFps }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Transparency As Coverage", target: loaderOptions, propertyKey: "transparencyAsCoverage", nullable: true, defaultValue: LoaderOptionDefaults.transparencyAsCoverage }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Clip Plane", target: loaderOptions, propertyKey: "useClipPlane", nullable: true, defaultValue: LoaderOptionDefaults.useClipPlane }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use glTF Texture Names", target: loaderOptions, propertyKey: "useGltfTextureNames", nullable: true, defaultValue: LoaderOptionDefaults.useGltfTextureNames }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use OpenPBR", target: loaderOptions, propertyKey: "useOpenPBR", nullable: true, defaultValue: LoaderOptionDefaults.useOpenPBR }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Range Requests", target: loaderOptions, propertyKey: "useRangeRequests", nullable: true, defaultValue: LoaderOptionDefaults.useRangeRequests }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use sRGB Buffers", target: loaderOptions, propertyKey: "useSRGBBuffers", nullable: true, defaultValue: LoaderOptionDefaults.useSRGBBuffers })] }) }));
|
|
21740
22731
|
};
|
|
21741
22732
|
const GLTFExtensionOptionsTool = ({ extensionOptions }) => {
|
|
21742
22733
|
return (jsx(PropertyLine, { label: "Extension Options", expandByDefault: false, expandedContent: jsx(Fragment, { children: Object.entries(extensionOptions)
|
|
@@ -21989,6 +22980,37 @@ const ShellSettingsServiceDefinition = {
|
|
|
21989
22980
|
},
|
|
21990
22981
|
};
|
|
21991
22982
|
|
|
22983
|
+
const InspectorSettingsServiceDefinition = {
|
|
22984
|
+
friendlyName: "Inspector Settings",
|
|
22985
|
+
consumes: [SettingsServiceIdentity],
|
|
22986
|
+
factory: (settingsService) => {
|
|
22987
|
+
const settingRegistration = settingsService.addSectionContent({
|
|
22988
|
+
key: "Inspector Settings",
|
|
22989
|
+
section: "UI",
|
|
22990
|
+
component: () => {
|
|
22991
|
+
const [compactMode, setCompactMode] = useSetting(CompactModeSettingDescriptor);
|
|
22992
|
+
const [useDegrees, setUseDegrees] = useSetting(UseDegreesSettingDescriptor);
|
|
22993
|
+
const [useEuler, setUseEuler] = useSetting(UseEulerSettingDescriptor);
|
|
22994
|
+
const [disableCopy, setDisableCopy] = useSetting(DisableCopySettingDescriptor);
|
|
22995
|
+
return (jsxs(Fragment, { children: [jsx(SwitchPropertyLine, { label: "Compact Mode", description: "Use a more compact UI with less spacing.", value: compactMode, onChange: (checked) => {
|
|
22996
|
+
setCompactMode(checked);
|
|
22997
|
+
} }), jsx(SwitchPropertyLine, { label: "Use Degrees", description: "Using degrees instead of radians.", value: useDegrees, onChange: (checked) => {
|
|
22998
|
+
setUseDegrees(checked);
|
|
22999
|
+
} }), jsx(SwitchPropertyLine, { label: "Only Show Euler Angles", description: "Only show Euler angles in rotation properties, rather than quaternions.", value: useEuler, onChange: (checked) => {
|
|
23000
|
+
setUseEuler(checked);
|
|
23001
|
+
} }), jsx(SwitchPropertyLine, { label: "Disable Copy Button", description: "Disables the copy to clipboard button on property lines. You can still Ctrl+Click on the label to copy.", value: disableCopy, onChange: (checked) => {
|
|
23002
|
+
setDisableCopy(checked);
|
|
23003
|
+
} })] }));
|
|
23004
|
+
},
|
|
23005
|
+
});
|
|
23006
|
+
return {
|
|
23007
|
+
dispose: () => {
|
|
23008
|
+
settingRegistration.dispose();
|
|
23009
|
+
},
|
|
23010
|
+
};
|
|
23011
|
+
},
|
|
23012
|
+
};
|
|
23013
|
+
|
|
21992
23014
|
const UserFeedbackServiceDefinition = {
|
|
21993
23015
|
friendlyName: "User Feedback",
|
|
21994
23016
|
consumes: [ShellServiceIdentity],
|
|
@@ -22117,6 +23139,11 @@ function ShowInspector(scene, options = {}) {
|
|
|
22117
23139
|
});
|
|
22118
23140
|
// This array will contain all the default Inspector service definitions.
|
|
22119
23141
|
const serviceDefinitions = [];
|
|
23142
|
+
// Ensure the inspectable bridge is running for this scene. The inspector's
|
|
23143
|
+
// ServiceContainer will use the inspectable container as a parent, inheriting
|
|
23144
|
+
// services like ISceneContext and IInspectableCommandRegistry.
|
|
23145
|
+
const inspectableToken = _StartInspectable(scene);
|
|
23146
|
+
disposeActions.push(() => inspectableToken.dispose());
|
|
22120
23147
|
// Create a container element for the inspector UI.
|
|
22121
23148
|
// This element will become the root React node, so it must be a new empty node
|
|
22122
23149
|
// since React will completely take over its contents.
|
|
@@ -22166,18 +23193,6 @@ function ShowInspector(scene, options = {}) {
|
|
|
22166
23193
|
disposeActions.push(() => {
|
|
22167
23194
|
parentElement.removeChild(containerElement);
|
|
22168
23195
|
});
|
|
22169
|
-
// This service exposes the scene that was passed into Inspector through ISceneContext, which is used by other services that may be used in other contexts outside of Inspector.
|
|
22170
|
-
const sceneContextServiceDefinition = {
|
|
22171
|
-
friendlyName: "Inspector Scene Context",
|
|
22172
|
-
produces: [SceneContextIdentity],
|
|
22173
|
-
factory: () => {
|
|
22174
|
-
return {
|
|
22175
|
-
currentScene: scene,
|
|
22176
|
-
currentSceneObservable: new Observable(),
|
|
22177
|
-
};
|
|
22178
|
-
},
|
|
22179
|
-
};
|
|
22180
|
-
serviceDefinitions.push(sceneContextServiceDefinition);
|
|
22181
23196
|
if (options.autoResizeEngine) {
|
|
22182
23197
|
const observer = scene.onBeforeRenderObservable.add(() => scene.getEngine().resize());
|
|
22183
23198
|
disposeActions.push(() => observer.remove());
|
|
@@ -22200,7 +23215,7 @@ function ShowInspector(scene, options = {}) {
|
|
|
22200
23215
|
// Tools pane tab and related services.
|
|
22201
23216
|
ToolsServiceDefinition, ExportServiceDefinition, GLTFAnimationImportServiceDefinition, GLTFLoaderOptionsServiceDefinition, GLTFValidationServiceDefinition, CaptureToolsDefinition,
|
|
22202
23217
|
// Settings pane tab and related services.
|
|
22203
|
-
SettingsServiceDefinition, WatcherSettingsServiceDefinition, ShellSettingsServiceDefinition,
|
|
23218
|
+
SettingsServiceDefinition, InspectorSettingsServiceDefinition, WatcherSettingsServiceDefinition, ShellSettingsServiceDefinition,
|
|
22204
23219
|
// Adds a button to refresh all properties manually (when watcher is in "manual" mode).
|
|
22205
23220
|
WatcherRefreshToolbarServiceDefinition,
|
|
22206
23221
|
// Tracks entity selection state (e.g. which Mesh or Material or other entity is currently selected in scene explorer and bound to the properties pane, etc.).
|
|
@@ -22213,6 +23228,8 @@ function ShowInspector(scene, options = {}) {
|
|
|
22213
23228
|
HighlightServiceDefinition,
|
|
22214
23229
|
// Adds entry points for user feedback on Inspector v2 (probably eventually will be removed).
|
|
22215
23230
|
UserFeedbackServiceDefinition,
|
|
23231
|
+
// Shows CLI bridge connection status in the toolbar.
|
|
23232
|
+
CliConnectionStatusServiceDefinition,
|
|
22216
23233
|
// Adds always present "mini stats" (like fps) to the toolbar, etc.
|
|
22217
23234
|
MiniStatsServiceDefinition,
|
|
22218
23235
|
// Legacy service to support custom inspectable properties on objects.
|
|
@@ -22220,6 +23237,7 @@ function ShowInspector(scene, options = {}) {
|
|
|
22220
23237
|
const modularTool = MakeModularTool({
|
|
22221
23238
|
namespace: "Inspector",
|
|
22222
23239
|
containerElement,
|
|
23240
|
+
parentContainer: inspectableToken.serviceContainer,
|
|
22223
23241
|
serviceDefinitions: [
|
|
22224
23242
|
// Default Inspector services.
|
|
22225
23243
|
...serviceDefinitions,
|
|
@@ -23054,5 +24072,5 @@ const TextAreaPropertyLine = (props) => {
|
|
|
23054
24072
|
// Attach Inspector v2 to Scene.debugLayer as a side effect for back compat.
|
|
23055
24073
|
AttachDebugLayer();
|
|
23056
24074
|
|
|
23057
|
-
export { GizmoServiceIdentity as $, Accordion as A, Button as B, CheckboxPropertyLine as C, ColorPickerPopup as D, ColorStepGradientComponent as E, ComboBox as F, ComboBoxPropertyLine as G, ConstructorFactory as H, ConvertOptions as I, DebugServiceIdentity as J, DetachDebugLayer as K, Link as L, MessageBar as M, NumberInputPropertyLine as N, DraggableLine as O, Popover as P, Dropdown as Q, EntitySelector as R, ShellServiceIdentity as S, TextInputPropertyLine as T, ErrorBoundary as U, Vector3PropertyLine as V, ExtensibleAccordion as W, FactorGradientComponent as X, FactorGradientList as Y, FileUploadLine as Z, GetPropertyDescriptor as _, useToast as a,
|
|
23058
|
-
//# sourceMappingURL=index-
|
|
24075
|
+
export { GizmoServiceIdentity as $, Accordion as A, Button as B, CheckboxPropertyLine as C, ColorPickerPopup as D, ColorStepGradientComponent as E, ComboBox as F, ComboBoxPropertyLine as G, ConstructorFactory as H, ConvertOptions as I, DebugServiceIdentity as J, DetachDebugLayer as K, Link as L, MessageBar as M, NumberInputPropertyLine as N, DraggableLine as O, Popover as P, Dropdown as Q, EntitySelector as R, ShellServiceIdentity as S, TextInputPropertyLine as T, ErrorBoundary as U, Vector3PropertyLine as V, ExtensibleAccordion as W, FactorGradientComponent as X, FactorGradientList as Y, FileUploadLine as Z, GetPropertyDescriptor as _, useToast as a, ToastProvider as a$, HexPropertyLine as a0, InfoLabel as a1, InputHexField as a2, InputHsvField as a3, InspectableCommandRegistryIdentity as a4, Inspector as a5, InterceptFunction as a6, InterceptProperty as a7, IsPropertyReadonly as a8, LineContainer as a9, SelectionServiceDefinition as aA, SettingsServiceIdentity as aB, SettingsStore as aC, SettingsStoreIdentity as aD, ShowInspector as aE, SidePaneContainer as aF, SkeletonSelector as aG, Slider as aH, SpinButton as aI, StartInspectable as aJ, StatsServiceIdentity as aK, StringDropdown as aL, StringDropdownPropertyLine as aM, StringifiedPropertyLine as aN, Switch as aO, SwitchPropertyLine as aP, SyncedSliderInput as aQ, SyncedSliderPropertyLine as aR, TeachingMoment as aS, TextAreaPropertyLine as aT, TextInput as aU, TextPropertyLine as aV, Textarea as aW, TextureSelector as aX, TextureUpload as aY, Theme as aZ, ThemeServiceIdentity as a_, LinkPropertyLine as aa, LinkToEntityPropertyLine as ab, List as ac, MakeDialogTeachingMoment as ad, MakeLazyComponent as ae, MakeModularTool as af, MakePopoverTeachingMoment as ag, MakePropertyHook as ah, MakeTeachingMoment as ai, MaterialSelector as aj, NodeSelector as ak, NumberDropdown as al, NumberDropdownPropertyLine as am, ObservableCollection as an, Pane as ao, PlaceholderPropertyLine as ap, PositionedPopover as aq, PropertiesServiceIdentity as ar, Property as as, PropertyContext as at, PropertyLine as au, QuaternionPropertyLine as av, RotationVectorPropertyLine as aw, SceneExplorerServiceIdentity as ax, SearchBar as ay, SearchBox as az, useInterceptObservable as b, ToggleButton as b0, Tooltip as b1, UploadButton as b2, Vector2PropertyLine as b3, Vector4PropertyLine as b4, WatcherServiceIdentity as b5, useAngleConverters as b6, useAsyncResource as b7, useColor3Property as b8, useColor4Property as b9, useEventListener as ba, useEventfulState as bb, useKeyListener as bc, useKeyState as bd, useObservableCollection as be, useOrderedObservableCollection as bf, usePollingObservable as bg, usePropertyChangedNotifier as bh, useQuaternionProperty as bi, useResource as bj, useTheme as bk, useThemeMode as bl, useVector3Property as bm, LinkToEntity as c, SpinButtonPropertyLine as d, useProperty as e, SceneContextIdentity as f, SelectionServiceIdentity as g, useObservableState as h, AccordionSection as i, ButtonLine as j, ToolsServiceIdentity as k, AccordionSectionItem as l, AttachDebugLayer as m, BooleanBadgePropertyLine as n, BoundProperty as o, BuiltInsExtensionFeed as p, Checkbox as q, ChildWindow as r, Collapse as s, Color3GradientComponent as t, useExtensionManager as u, Color3GradientList as v, Color3PropertyLine as w, Color4GradientComponent as x, Color4GradientList as y, Color4PropertyLine as z };
|
|
24076
|
+
//# sourceMappingURL=index-DmfAhsIm.js.map
|