@babylonjs/inspector 8.45.3-preview → 8.45.4-preview

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.
@@ -3,8 +3,8 @@ import { createContext, useContext, useCallback, useMemo, useEffect, useState, u
3
3
  import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
4
4
  import { Vector3, Quaternion, Matrix, Vector2, Vector4, TmpVectors } from '@babylonjs/core/Maths/math.vector.js';
5
5
  import { Observable } from '@babylonjs/core/Misc/observable.js';
6
- import { makeStyles, Link as Link$1, Body1, ToggleButton as ToggleButton$1, Button as Button$1, tokens, InfoLabel as InfoLabel$1, Body1Strong, Tooltip, Checkbox as Checkbox$1, mergeClasses, Accordion as Accordion$1, AccordionItem, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, createLightTheme, createDarkTheme, FluentProvider, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Portal, RendererProvider, createDOMRenderer, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, SearchBox as SearchBox$1, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, treeItemLevelToken, MenuItemCheckbox, Switch as Switch$1, PresenceBadge, useId, SpinButton as SpinButton$1, Slider, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Spinner, Badge, MessageBar as MessageBar$1, MessageBarBody, MessageBarTitle, useComboboxFilter, Combobox, Textarea as Textarea$1, ToolbarButton, Label, ToolbarDivider, Field } from '@fluentui/react-components';
7
- import { ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, Copy20Regular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, DocumentTextRegular, createFluentIcon, FilterRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, SettingsRegular, ArrowUploadRegular, DataBarHorizontalRegular, WrenchRegular, WeatherSunnyRegular, WeatherMoonRegular, ErrorCircleRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, LinkDismissRegular, LinkEditRegular, SaveRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, CopyRegular, DeleteRegular, EyeOffFilled, EyeFilled, ArrowMoveFilled, StopFilled, PlayFilled, EyeRegular, EyeOffRegular, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ChevronDownRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, PlayRegular, AppGenericRegular, MyLocationRegular, CameraRegular, LightbulbRegular, BorderOutsideRegular, BorderNoneRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
6
+ import { makeStyles, Link as Link$1, Caption1, Body1, ToggleButton as ToggleButton$1, Button as Button$1, Spinner, tokens, InfoLabel as InfoLabel$1, Body1Strong, Tooltip, Checkbox as Checkbox$1, mergeClasses, Accordion as Accordion$1, AccordionItem, AccordionHeader, Subtitle2Stronger, AccordionPanel, Divider, createLightTheme, createDarkTheme, FluentProvider, TeachingPopover, TeachingPopoverSurface, TeachingPopoverHeader, TeachingPopoverBody, Portal, RendererProvider, createDOMRenderer, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, Toolbar as Toolbar$1, ToolbarRadioButton, MenuGroup, MenuGroupHeader, SearchBox as SearchBox$1, FlatTree, FlatTreeItem, TreeItemLayout, MenuDivider, treeItemLevelToken, MenuItemCheckbox, Switch as Switch$1, PresenceBadge, useId, SpinButton as SpinButton$1, Slider, Input, Dropdown as Dropdown$1, Option, Popover as Popover$1, PopoverTrigger, PopoverSurface, ColorPicker, ColorArea, ColorSlider, AlphaSlider, ColorSwatch, MenuItemRadio, Dialog, DialogSurface, DialogBody, DialogTitle, DialogContent, DialogActions, List as List$1, ListItem, Badge, MessageBar as MessageBar$1, MessageBarBody, MessageBarTitle, useComboboxFilter, Combobox, Textarea as Textarea$1, ToolbarButton, Label, ToolbarDivider, Field } from '@fluentui/react-components';
7
+ import { ChevronCircleRight16Regular, ChevronCircleRight20Regular, ChevronCircleDown16Regular, ChevronCircleDown20Regular, Copy16Regular, Copy20Regular, PanelLeftExpandRegular, PanelRightExpandRegular, PanelLeftContractRegular, PanelRightContractRegular, PictureInPictureEnterRegular, MoreHorizontalRegular, LayoutColumnTwoFocusLeftFilled, LayoutColumnTwoSplitLeftFocusTopLeftFilled, LayoutColumnTwoSplitLeftFocusBottomLeftFilled, LayoutColumnTwoFocusRightFilled, LayoutColumnTwoSplitRightFocusTopRightFilled, LayoutColumnTwoSplitRightFocusBottomRightFilled, DocumentTextRegular, createFluentIcon, FilterRegular, GlobeRegular, ArrowExpandAllRegular, ArrowCollapseAllRegular, CubeTreeRegular, BugRegular, SettingsRegular, ArrowUploadRegular, DataBarHorizontalRegular, WrenchRegular, WeatherSunnyRegular, WeatherMoonRegular, ErrorCircleRegular, ArrowRotateClockwiseRegular, ArrowExpandRegular, SelectObjectRegular, CubeRegular, DeleteRegular, PlayRegular, EditRegular, LinkDismissRegular, LinkEditRegular, SaveRegular, ArrowUndoRegular, BracesRegular, BracesDismiss16Regular, CopyRegular, EyeOffFilled, EyeFilled, ArrowMoveFilled, EyeRegular, StopRegular, ArrowDownloadRegular, CloudArrowUpRegular, CloudArrowDownRegular, StopFilled, PlayFilled, EyeOffRegular, LockOpenRegular, LockClosedRegular, ResizeRegular, ChevronUpRegular, ChevronDownRegular, ArrowResetRegular, CircleHalfFillRegular, EyedropperRegular, PaintBucketRegular, InkStrokeRegular, StackRegular, FilmstripRegular, PauseFilled, WeatherSunnyLowFilled, LayerRegular, FrameRegular, AppGenericRegular, MyLocationRegular, CameraRegular, LightbulbRegular, BorderOutsideRegular, BorderNoneRegular, VideoFilled, VideoRegular, FlashlightRegular, FlashlightOffRegular, DropRegular, BlurRegular, PipelineRegular, PersonWalkingRegular, DataLineRegular, PersonSquareRegular, LayerDiagonalPersonRegular, ImageEditRegular, ImageRegular, TargetRegular, PersonFeedbackRegular, BranchRegular, DeleteFilled } from '@fluentui/react-icons';
8
8
  import { Collapse as Collapse$1, Fade } from '@fluentui/react-motion-components-preview';
9
9
  import '@babylonjs/core/Misc/typeStore.js';
10
10
  import { useLocalStorage, useTernaryDarkMode } from 'usehooks-ts';
@@ -59,18 +59,21 @@ import { RectAreaLight } from '@babylonjs/core/Lights/rectAreaLight.js';
59
59
  import { ShadowLight } from '@babylonjs/core/Lights/shadowLight.js';
60
60
  import { SpotLight } from '@babylonjs/core/Lights/spotLight.js';
61
61
  import { CascadedShadowGenerator } from '@babylonjs/core/Lights/Shadows/cascadedShadowGenerator.js';
62
+ import { DirectionalLightFrustumViewer } from '@babylonjs/core/Debug/directionalLightFrustumViewer.js';
62
63
  import { ShadowGenerator } from '@babylonjs/core/Lights/Shadows/shadowGenerator.js';
63
64
  import '@babylonjs/core/Lights/Shadows/shadowGeneratorSceneComponent.js';
64
65
  import { Material } from '@babylonjs/core/Materials/material.js';
65
66
  import { MultiMaterial } from '@babylonjs/core/Materials/multiMaterial.js';
66
- import { PBRBaseMaterial } from '@babylonjs/core/Materials/PBR/pbrBaseMaterial.js';
67
+ import { NodeMaterial } from '@babylonjs/core/Materials/Node/nodeMaterial.js';
67
68
  import { OpenPBRMaterial } from '@babylonjs/core/Materials/PBR/openpbrMaterial.js';
69
+ import { PBRBaseMaterial } from '@babylonjs/core/Materials/PBR/pbrBaseMaterial.js';
68
70
  import { SkyMaterial } from '@babylonjs/materials/sky/skyMaterial.js';
69
71
  import { Constants } from '@babylonjs/core/Engines/constants.js';
70
72
  import { Engine } from '@babylonjs/core/Engines/engine.js';
71
73
  import { ParticleSystem } from '@babylonjs/core/Particles/particleSystem.js';
72
- import { CubeTexture } from '@babylonjs/core/Materials/Textures/cubeTexture.js';
73
74
  import { ReadFile } from '@babylonjs/core/Misc/fileTools.js';
75
+ import { CubeTexture } from '@babylonjs/core/Materials/Textures/cubeTexture.js';
76
+ import { GaussianSplattingMesh } from '@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh.js';
74
77
  import { Mesh } from '@babylonjs/core/Meshes/mesh.js';
75
78
  import { SkeletonViewer } from '@babylonjs/core/Debug/skeletonViewer.js';
76
79
  import { VertexBuffer } from '@babylonjs/core/Meshes/buffer.js';
@@ -79,7 +82,6 @@ import { InstancedMesh } from '@babylonjs/core/Meshes/instancedMesh.js';
79
82
  import { RenderingManager } from '@babylonjs/core/Rendering/renderingManager.js';
80
83
  import '@babylonjs/core/Rendering/edgesRenderer.js';
81
84
  import '@babylonjs/core/Rendering/outlineRenderer.js';
82
- import { GaussianSplattingMesh } from '@babylonjs/core/Meshes/GaussianSplatting/gaussianSplattingMesh.js';
83
85
  import { BoxParticleEmitter } from '@babylonjs/core/Particles/EmitterTypes/boxParticleEmitter.js';
84
86
  import { ConeParticleEmitter } from '@babylonjs/core/Particles/EmitterTypes/coneParticleEmitter.js';
85
87
  import { CylinderParticleEmitter } from '@babylonjs/core/Particles/EmitterTypes/cylinderParticleEmitter.js';
@@ -87,6 +89,7 @@ import { HemisphericParticleEmitter } from '@babylonjs/core/Particles/EmitterTyp
87
89
  import { MeshParticleEmitter } from '@babylonjs/core/Particles/EmitterTypes/meshParticleEmitter.js';
88
90
  import { PointParticleEmitter } from '@babylonjs/core/Particles/EmitterTypes/pointParticleEmitter.js';
89
91
  import { SphereParticleEmitter } from '@babylonjs/core/Particles/EmitterTypes/sphereParticleEmitter.js';
92
+ import { ConvertToNodeParticleSystemSetAsync } from '@babylonjs/core/Particles/Node/nodeParticleSystemSet.helper.js';
90
93
  import { ParticleHelper } from '@babylonjs/core/Particles/particleHelper.js';
91
94
  import { FactorGradient, Color3Gradient, ColorGradient } from '@babylonjs/core/Misc/gradients.js';
92
95
  import { GradientBlockColorStep } from '@babylonjs/core/Materials/Node/Blocks/gradientBlock.js';
@@ -673,8 +676,11 @@ function copyCommandToClipboard(strCommand) {
673
676
  const ToolContext = createContext({ useFluent: false, disableCopy: false, toolName: "", size: undefined });
674
677
 
675
678
  const Link = forwardRef((props, ref) => {
679
+ const { size } = useContext(ToolContext);
676
680
  const { target, url, onLink, ...rest } = props;
677
- return (jsxs(Link$1, { ref: ref, inline: true, target: target === "current" ? "_self" : "_blank", rel: "noopener noreferrer", href: url, onClick: onLink ?? undefined, ...rest, children: [props.children, jsx(Body1, { wrap: false, truncate: true, children: props.value })] }));
681
+ // eslint-disable-next-line @typescript-eslint/naming-convention
682
+ const TextComponent = size === "small" ? Caption1 : Body1;
683
+ return (jsxs(Link$1, { ref: ref, inline: true, target: target === "current" ? "_self" : "_blank", rel: "noopener noreferrer", href: url, onClick: onLink ?? undefined, ...rest, children: [props.children, jsx(TextComponent, { children: props.value })] }));
678
684
  });
679
685
  Link.displayName = "Link";
680
686
 
@@ -714,8 +720,21 @@ const ToggleButton = (props) => {
714
720
  const Button = forwardRef((props, ref) => {
715
721
  const { size } = useContext(ToolContext);
716
722
  // eslint-disable-next-line @typescript-eslint/naming-convention
717
- const { icon: Icon, label, ...buttonProps } = props;
718
- return (jsx(Button$1, { ref: ref, iconPosition: "after", ...buttonProps, size: size, icon: Icon && jsx(Icon, {}), children: label && props.label }));
723
+ const { icon: Icon, label, onClick, disabled, ...buttonProps } = props;
724
+ const [isOnClickBusy, setIsOnClickBusy] = useState(false);
725
+ const handleOnClick = useCallback(async (e) => {
726
+ const result = onClick?.();
727
+ if (result instanceof Promise) {
728
+ setIsOnClickBusy(true);
729
+ try {
730
+ await result;
731
+ }
732
+ finally {
733
+ setIsOnClickBusy(false);
734
+ }
735
+ }
736
+ }, [onClick]);
737
+ return (jsx(Button$1, { ref: ref, iconPosition: "after", ...buttonProps, size: size, icon: isOnClickBusy ? jsx(Spinner, { size: "extra-tiny" }) : Icon && jsx(Icon, {}), onClick: handleOnClick, disabled: disabled || isOnClickBusy, children: label && props.label }));
719
738
  });
720
739
  Button.displayName = "Button";
721
740
 
@@ -788,6 +807,33 @@ function ValidateColorHex(val) {
788
807
  return val != "" && HEX_REGEX.test(val);
789
808
  }
790
809
 
810
+ const useInfoLabelStyles = makeStyles({
811
+ infoPopup: {
812
+ whiteSpace: "normal",
813
+ wordBreak: "break-word",
814
+ },
815
+ labelSlot: {
816
+ display: "flex",
817
+ minWidth: 0,
818
+ },
819
+ labelText: {
820
+ whiteSpace: "nowrap",
821
+ overflow: "hidden",
822
+ textOverflow: "ellipsis",
823
+ },
824
+ });
825
+ /**
826
+ * Renders a label with an optional popup containing more info
827
+ * @param props
828
+ * @returns
829
+ */
830
+ const InfoLabel = (props) => {
831
+ InfoLabel.displayName = "InfoLabel";
832
+ const classes = useInfoLabelStyles();
833
+ const infoContent = props.info ? jsx("div", { className: classes.infoPopup, children: props.info }) : undefined;
834
+ return infoContent ? (jsx(InfoLabel$1, { htmlFor: props.htmlFor, info: infoContent, className: props.className, label: props.flexLabel ? { className: classes.labelSlot } : undefined, children: jsx(Body1Strong, { className: classes.labelText, children: props.label }) })) : (jsx(Body1Strong, { className: props.className, children: props.label }));
835
+ };
836
+
791
837
  const usePropertyLineStyles = makeStyles({
792
838
  baseLine: {
793
839
  display: "flex",
@@ -801,15 +847,6 @@ const usePropertyLineStyles = makeStyles({
801
847
  minWidth: CustomTokens.labelMinWidth,
802
848
  textAlign: "left",
803
849
  },
804
- labelSlot: {
805
- display: "flex",
806
- minWidth: 0,
807
- },
808
- labelText: {
809
- whiteSpace: "nowrap",
810
- overflow: "hidden",
811
- textOverflow: "ellipsis",
812
- },
813
850
  rightContent: {
814
851
  flex: "0 1 auto",
815
852
  minWidth: 0,
@@ -853,7 +890,7 @@ const PropertyLine = forwardRef((props, ref) => {
853
890
  const { label, onCopy, expandedContent, children, nullable, ignoreNullable } = props;
854
891
  const [expanded, setExpanded] = useState("expandByDefault" in props ? props.expandByDefault : false);
855
892
  const cachedVal = useRef(nullable ? props.value : null);
856
- const description = props.docLink ? jsx(Link, { url: props.docLink, value: props.description ?? "Docs" }) : props.description;
893
+ const description = props.docLink ? jsx(Link, { url: props.docLink, value: props.description ?? "Docs" }) : props.description ? jsx(Body1, { children: props.description }) : undefined;
857
894
  // Process children to handle nullable state -- creating component in disabled state with default value in lieu of null value
858
895
  const processedChildren = (nullable || ignoreNullable) && isValidElement(children)
859
896
  ? cloneElement(children, {
@@ -863,7 +900,7 @@ const PropertyLine = forwardRef((props, ref) => {
863
900
  defaultValue: undefined, // Don't pass defaultValue to children as there is no guarantee how this will be used and we can't mix controlled + uncontrolled state
864
901
  })
865
902
  : children;
866
- return (jsxs(LineContainer, { ref: ref, children: [jsxs("div", { className: classes.baseLine, children: [jsx(InfoLabel$1, { size: size, className: classes.infoLabel, label: { className: classes.labelSlot }, info: description ? jsx("div", { className: classes.infoPopup, children: description }) : undefined, title: label, children: jsx(Body1Strong, { className: classes.labelText, children: label }) }), jsxs("div", { className: classes.rightContent, children: [expandedContent && (jsx(ToggleButton, { title: "Expand/Collapse property", appearance: "transparent", checkedIcon: size === "small" ? ChevronCircleDown16Regular : ChevronCircleDown20Regular, uncheckedIcon: size === "small" ? ChevronCircleRight16Regular : ChevronCircleRight20Regular, value: expanded === true, onChange: setExpanded })), nullable && !ignoreNullable && (
903
+ return (jsxs(LineContainer, { ref: ref, children: [jsxs("div", { className: classes.baseLine, children: [jsx(InfoLabel, { className: classes.infoLabel, htmlFor: "property", info: description, label: label, flexLabel: true }), jsxs("div", { className: classes.rightContent, id: "property", children: [expandedContent && (jsx(ToggleButton, { title: "Expand/Collapse property", appearance: "transparent", checkedIcon: size === "small" ? ChevronCircleDown16Regular : ChevronCircleDown20Regular, uncheckedIcon: size === "small" ? ChevronCircleRight16Regular : ChevronCircleRight20Regular, value: expanded === true, onChange: setExpanded })), nullable && !ignoreNullable && (
867
904
  // If this is a nullableProperty and ignoreNullable was not sent, display a checkbox used to toggle null ('checked' means 'non null')
868
905
  jsx(Tooltip, { relationship: "label", content: props.value == null ? "Enable property" : "Disable property (set to null)", children: jsx(Checkbox$1, { className: classes.checkbox, checked: !(props.value == null), onChange: (_, data) => {
869
906
  if (data.checked) {
@@ -3315,7 +3352,7 @@ const DebugPane = (props) => {
3315
3352
  SwitchGrid(scene);
3316
3353
  }
3317
3354
  });
3318
- return (jsxs(ExtensibleAccordion, { ...props, children: [jsxs(AccordionSection, { title: "Helpers", children: [jsx(SwitchPropertyLine, { label: "Grid", description: "Display a ground grid.", value: !!scene.reservedDataStore.gridMesh, onChange: () => SwitchGrid(scene) }), jsx(SwitchPropertyLine, { label: "Physics", description: "Display physic debug info.", value: !!scene.reservedDataStore.physicsViewer, onChange: () => SwitchPhysicsViewers(scene) }), jsx(SwitchPropertyLine, { label: "Names", description: "Display mesh names.", value: !!scene.reservedDataStore.textRenderersHook, onChange: () => void SwitchNameViewerAsync(scene) })] }), jsxs(AccordionSection, { title: "Texture Channels", children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Diffuse", target: StandardMaterial, propertyKey: "DiffuseTextureEnabled" }, "Diffuse"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Ambient", target: StandardMaterial, propertyKey: "AmbientTextureEnabled" }, "Ambient"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Specular", target: StandardMaterial, propertyKey: "SpecularTextureEnabled" }, "Specular"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Emissive", target: StandardMaterial, propertyKey: "EmissiveTextureEnabled" }, "Emissive"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Bump", target: StandardMaterial, propertyKey: "BumpTextureEnabled" }, "Bump"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Opacity", target: StandardMaterial, propertyKey: "OpacityTextureEnabled" }, "Opacity"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Reflection", target: StandardMaterial, propertyKey: "ReflectionTextureEnabled" }, "Reflection"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Color Grading", target: StandardMaterial, propertyKey: "ColorGradingTextureEnabled" }, "ColorGrading"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Lightmap", target: StandardMaterial, propertyKey: "LightmapTextureEnabled" }, "Lightmap"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Fresnel", target: StandardMaterial, propertyKey: "FresnelEnabled" }, "Fresnel"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Detail", target: MaterialFlags, propertyKey: "DetailTextureEnabled" }, "Detail"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Decal", target: MaterialFlags, propertyKey: "DecalMapEnabled" }, "Decal")] }), jsxs(AccordionSection, { title: "Features", children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Animations", target: scene, propertyKey: "animationsEnabled" }, "Animations"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Physics", target: scene, propertyKey: "physicsEnabled" }, "Physics"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Collisions", target: scene, propertyKey: "collisionsEnabled" }, "Collisions"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Fog", target: scene, propertyKey: "fogEnabled" }, "Fog"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Lens Flares", target: scene, propertyKey: "lensFlaresEnabled" }, "Lens flares"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Lights", target: scene, propertyKey: "lightsEnabled" }, "Lights"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Particles", target: scene, propertyKey: "particlesEnabled" }, "Particles"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Post-processes", target: scene, propertyKey: "postProcessesEnabled" }, "Post-processes"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Probes", target: scene, propertyKey: "probesEnabled" }, "Probes"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Textures", target: scene, propertyKey: "texturesEnabled" }, "Textures"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Procedural Textures", target: scene, propertyKey: "proceduralTexturesEnabled" }, "Procedural textures"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render Targets", target: scene, propertyKey: "renderTargetsEnabled" }, "Render targets"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Shadows", target: scene, propertyKey: "shadowsEnabled" }, "Shadows"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Skeletons", target: scene, propertyKey: "skeletonsEnabled" }, "Skeletons"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Sprites", target: scene, propertyKey: "spritesEnabled" }, "Sprites")] })] }));
3355
+ return (jsxs(ExtensibleAccordion, { ...props, children: [jsxs(AccordionSection, { title: "Helpers", children: [jsx(SwitchPropertyLine, { label: "Grid", description: "Display a ground grid.", value: !!scene.reservedDataStore.gridMesh, onChange: () => SwitchGrid(scene) }), jsx(SwitchPropertyLine, { label: "Physics", description: "Display physic debug info.", value: !!scene.reservedDataStore.physicsViewer, onChange: () => SwitchPhysicsViewers(scene) }), jsx(SwitchPropertyLine, { label: "Names", description: "Display mesh names.", value: !!scene.reservedDataStore.textRenderersHook, onChange: () => void SwitchNameViewerAsync(scene) })] }), jsxs(AccordionSection, { title: "Texture Channels", children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Diffuse", target: StandardMaterial, propertyKey: "DiffuseTextureEnabled" }, "Diffuse"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Ambient", target: StandardMaterial, propertyKey: "AmbientTextureEnabled" }, "Ambient"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Specular", target: StandardMaterial, propertyKey: "SpecularTextureEnabled" }, "Specular"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Emissive", target: StandardMaterial, propertyKey: "EmissiveTextureEnabled" }, "Emissive"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Bump", target: StandardMaterial, propertyKey: "BumpTextureEnabled" }, "Bump"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Opacity", target: StandardMaterial, propertyKey: "OpacityTextureEnabled" }, "Opacity"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Reflection", target: StandardMaterial, propertyKey: "ReflectionTextureEnabled" }, "Reflection"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Refraction", target: StandardMaterial, propertyKey: "RefractionTextureEnabled" }, "Refraction"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Color Grading", target: StandardMaterial, propertyKey: "ColorGradingTextureEnabled" }, "ColorGrading"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Lightmap", target: StandardMaterial, propertyKey: "LightmapTextureEnabled" }, "Lightmap"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Fresnel", target: StandardMaterial, propertyKey: "FresnelEnabled" }, "Fresnel"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Detail", target: MaterialFlags, propertyKey: "DetailTextureEnabled" }, "Detail"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Decal", target: MaterialFlags, propertyKey: "DecalMapEnabled" }, "Decal")] }), jsxs(AccordionSection, { title: "Features", children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Animations", target: scene, propertyKey: "animationsEnabled" }, "Animations"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Physics", target: scene, propertyKey: "physicsEnabled" }, "Physics"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Collisions", target: scene, propertyKey: "collisionsEnabled" }, "Collisions"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Fog", target: scene, propertyKey: "fogEnabled" }, "Fog"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Lens Flares", target: scene, propertyKey: "lensFlaresEnabled" }, "Lens flares"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Lights", target: scene, propertyKey: "lightsEnabled" }, "Lights"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Particles", target: scene, propertyKey: "particlesEnabled" }, "Particles"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Post-processes", target: scene, propertyKey: "postProcessesEnabled" }, "Post-processes"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Probes", target: scene, propertyKey: "probesEnabled" }, "Probes"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Textures", target: scene, propertyKey: "texturesEnabled" }, "Textures"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Procedural Textures", target: scene, propertyKey: "proceduralTexturesEnabled" }, "Procedural textures"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Render Targets", target: scene, propertyKey: "renderTargetsEnabled" }, "Render targets"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Shadows", target: scene, propertyKey: "shadowsEnabled" }, "Shadows"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Skeletons", target: scene, propertyKey: "skeletonsEnabled" }, "Skeletons"), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Sprites", target: scene, propertyKey: "spritesEnabled" }, "Sprites")] })] }));
3319
3356
  };
3320
3357
 
3321
3358
  const DebugServiceIdentity = Symbol("DebugService");
@@ -3368,6 +3405,7 @@ const SettingsServiceDefinition = {
3368
3405
  const sectionsCollection = new ObservableCollection();
3369
3406
  const sectionContentCollection = new ObservableCollection();
3370
3407
  let useDegrees = DataStorage.ReadBoolean("Babylon/Settings/UseDegrees", false);
3408
+ let useEuler = DataStorage.ReadBoolean("Babylon/Settings/UseEuler", false);
3371
3409
  let ignoreBackfacesForPicking = DataStorage.ReadBoolean("Babylon/Settings/IgnoreBackfacesForPicking", false);
3372
3410
  let showPropertiesOnEntitySelection = DataStorage.ReadBoolean("Babylon/Settings/ShowPropertiesOnEntitySelection", true);
3373
3411
  const settings = {
@@ -3393,6 +3431,17 @@ const SettingsServiceDefinition = {
3393
3431
  DataStorage.WriteBoolean("Babylon/Settings/IgnoreBackfacesForPicking", ignoreBackfacesForPicking);
3394
3432
  this.settingsChangedObservable.notifyObservers(this);
3395
3433
  },
3434
+ get useEuler() {
3435
+ return useEuler;
3436
+ },
3437
+ set useEuler(value) {
3438
+ if (useEuler === value) {
3439
+ return; // No change, no need to notify
3440
+ }
3441
+ useEuler = value;
3442
+ DataStorage.WriteBoolean("Babylon/Settings/UseEuler", useEuler);
3443
+ this.settingsChangedObservable.notifyObservers(this);
3444
+ },
3396
3445
  get showPropertiesOnEntitySelection() {
3397
3446
  return showPropertiesOnEntitySelection;
3398
3447
  },
@@ -3427,6 +3476,8 @@ const SettingsServiceDefinition = {
3427
3476
  setCompactMode(checked);
3428
3477
  } }), jsx(SwitchPropertyLine, { label: "Use Degrees", description: "Using degrees instead of radians.", value: settings.useDegrees, onChange: (checked) => {
3429
3478
  settings.useDegrees = checked;
3479
+ } }), jsx(SwitchPropertyLine, { label: "Only Show Euler Angles", description: "Only show Euler angles in rotation properties, rather than quaternions.", value: settings.useEuler, onChange: (checked) => {
3480
+ settings.useEuler = checked;
3430
3481
  } }), jsx(SwitchPropertyLine, { label: "Ignore Backfaces for Picking", description: "Ignore backfaces when picking.", value: settings.ignoreBackfacesForPicking, onChange: (checked) => {
3431
3482
  settings.ignoreBackfacesForPicking = checked;
3432
3483
  } }), jsx(SwitchPropertyLine, { label: "Show Properties on Selection", description: "Shows the Properties pane when an entity is selected.", value: settings.showPropertiesOnEntitySelection, onChange: (checked) => {
@@ -3503,7 +3554,7 @@ const FrameStepsStats = ({ context: scene }) => {
3503
3554
  const interFrameTime = useObservableState(() => sceneInstrumentation.interFrameTimeCounter.lastSecAverage, pollingObservable);
3504
3555
  const gpuFrameTime = useObservableState(() => engineInstrumentation.gpuFrameTimeCounter.lastSecAverage * 0.000001, pollingObservable);
3505
3556
  const gpuFrameTimeAverage = useObservableState(() => engineInstrumentation.gpuFrameTimeCounter.average * 0.000001, pollingObservable);
3506
- return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Absolute FPS", value: absoluteFPS }, "AbsoluteFPS"), jsx(StringifiedPropertyLine, { label: "Meshes Selection", value: meshesSelection, precision: 2, units: "ms" }, "MeshesSelection"), jsx(StringifiedPropertyLine, { label: "Render Targets", value: renderTargets, precision: 2, units: "ms" }, "RenderTargets"), jsx(StringifiedPropertyLine, { label: "Particles", value: particles, precision: 2, units: "ms" }, "Particles"), jsx(StringifiedPropertyLine, { label: "Sprites", value: sprites, precision: 2, units: "ms" }, "Sprites"), jsx(StringifiedPropertyLine, { label: "Animations", value: animations, precision: 2, units: "ms" }, "Animations"), jsx(StringifiedPropertyLine, { label: "Physics", value: physics, precision: 2, units: "ms" }, "Physics"), jsx(StringifiedPropertyLine, { label: "Inter-Frame Time", value: interFrameTime, precision: 2, units: "ms" }, "InterFrameTime"), jsx(StringifiedPropertyLine, { label: "GPU Frame Time", value: gpuFrameTime, precision: 2, units: "ms" }, "GPUFrameTime"), jsx(StringifiedPropertyLine, { label: "GPU Frame Time (Average)", value: gpuFrameTimeAverage, precision: 2, units: "ms" }, "GPUFrameTimeAverage")] }));
3557
+ return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Absolute FPS", value: absoluteFPS }, "AbsoluteFPS"), jsx(StringifiedPropertyLine, { label: "Meshes Selection", value: meshesSelection, precision: 2, units: "ms" }, "MeshesSelection"), jsx(StringifiedPropertyLine, { label: "Render Targets", value: renderTargets, precision: 2, units: "ms" }, "RenderTargets"), jsx(StringifiedPropertyLine, { label: "Particles", value: particles, precision: 2, units: "ms" }, "Particles"), jsx(StringifiedPropertyLine, { label: "Sprites", value: sprites, precision: 2, units: "ms" }, "Sprites"), jsx(StringifiedPropertyLine, { label: "Animations", value: animations, precision: 2, units: "ms" }, "Animations"), jsx(StringifiedPropertyLine, { label: "Physics", value: physics, precision: 2, units: "ms" }, "Physics"), jsx(StringifiedPropertyLine, { label: "Render", value: sceneInstrumentation.renderTimeCounter.lastSecAverage, precision: 2, units: "ms" }, "Render"), jsx(StringifiedPropertyLine, { label: "Frame", value: sceneInstrumentation.frameTimeCounter.lastSecAverage, precision: 2, units: "ms" }, "Frame"), jsx(StringifiedPropertyLine, { label: "Inter-Frame Time", value: interFrameTime, precision: 2, units: "ms" }, "InterFrameTime"), jsx(StringifiedPropertyLine, { label: "GPU Frame Time", value: gpuFrameTime, precision: 2, units: "ms" }, "GPUFrameTime"), jsx(StringifiedPropertyLine, { label: "GPU Frame Time (Average)", value: gpuFrameTimeAverage, precision: 2, units: "ms" }, "GPUFrameTimeAverage")] }));
3507
3558
  };
3508
3559
 
3509
3560
  /**
@@ -3837,7 +3888,7 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
3837
3888
  keywords: ["export", "gltf", "glb", "babylon", "exporter", "tools"],
3838
3889
  ...BabylonWebResources,
3839
3890
  author: { name: "Alex Chuber", forumUserName: "alexchuber" },
3840
- getExtensionModuleAsync: async () => await import('./exportService-CnPFMXUA.js'),
3891
+ getExtensionModuleAsync: async () => await import('./exportService-D19rsLCQ.js'),
3841
3892
  },
3842
3893
  {
3843
3894
  name: "Capture Tools",
@@ -3845,7 +3896,7 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
3845
3896
  keywords: ["capture", "screenshot", "gif", "video", "tools"],
3846
3897
  ...BabylonWebResources,
3847
3898
  author: { name: "Alex Chuber", forumUserName: "alexchuber" },
3848
- getExtensionModuleAsync: async () => await import('./captureService-BDZ64ZKE.js'),
3899
+ getExtensionModuleAsync: async () => await import('./captureService-C4KzF-3L.js'),
3849
3900
  },
3850
3901
  {
3851
3902
  name: "Import Tools",
@@ -3853,7 +3904,7 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
3853
3904
  keywords: ["import", "tools"],
3854
3905
  ...BabylonWebResources,
3855
3906
  author: { name: "Alex Chuber", forumUserName: "alexchuber" },
3856
- getExtensionModuleAsync: async () => await import('./importService-e9XEtGfl.js'),
3907
+ getExtensionModuleAsync: async () => await import('./importService-xoACJHME.js'),
3857
3908
  },
3858
3909
  {
3859
3910
  name: "Quick Creation Tools (Preview)",
@@ -3861,20 +3912,10 @@ const DefaultInspectorExtensionFeed = new BuiltInsExtensionFeed("Inspector", [
3861
3912
  keywords: ["creation", "tools"],
3862
3913
  ...BabylonWebResources,
3863
3914
  author: { name: "Babylon.js", forumUserName: "" },
3864
- getExtensionModuleAsync: async () => await import('./quickCreateToolsService-gqXZXpNH.js'),
3915
+ getExtensionModuleAsync: async () => await import('./quickCreateToolsService-ROfMXsQm.js'),
3865
3916
  },
3866
3917
  ]);
3867
3918
 
3868
- /**
3869
- * Renders a label with an optional popup containing more info
3870
- * @param props
3871
- * @returns
3872
- */
3873
- const InfoLabel = (props) => {
3874
- InfoLabel.displayName = "InfoLabel";
3875
- return (jsx(InfoLabel$1, { htmlFor: props.htmlFor, info: props.info, children: jsx(Body1, { children: props.label }) }));
3876
- };
3877
-
3878
3919
  const SpinButton = (props) => {
3879
3920
  SpinButton.displayName = "SpinButton";
3880
3921
  const classes = useInputStyles$1();
@@ -4405,7 +4446,7 @@ const QuaternionPropertyLine = (props) => {
4405
4446
  setQuat(props.value);
4406
4447
  }, [props.value]);
4407
4448
  // Extract only the properties that exist on QuaternionPropertyLineProps
4408
- const { useDegrees, ...restProps } = props;
4449
+ const { useEuler, ...restProps } = props;
4409
4450
  const onQuatChange = (val) => {
4410
4451
  setQuat(val);
4411
4452
  props.onChange(val);
@@ -4414,7 +4455,7 @@ const QuaternionPropertyLine = (props) => {
4414
4455
  const quat = Quaternion.FromEulerAngles(val.x, val.y, val.z);
4415
4456
  onQuatChange(quat);
4416
4457
  };
4417
- return useDegrees ? (jsx(Vector3PropertyLine, { ...restProps, nullable: false, ignoreNullable: false, value: quat.toEulerAngles(), valueConverter: ToDegreesConverter, min: min, max: max, onChange: onEulerChange, unit: "deg" })) : (jsx(QuaternionPropertyLineInternal, { ...props, nullable: false, value: quat, min: min, max: max, onChange: onQuatChange }));
4458
+ return useEuler ? (jsx(Vector3PropertyLine, { ...restProps, nullable: false, ignoreNullable: false, value: quat.toEulerAngles(), valueConverter: ToDegreesConverter, min: min, max: max, onChange: onEulerChange, unit: props.useDegrees ? "deg" : "rad" })) : (jsx(QuaternionPropertyLineInternal, { ...props, nullable: false, value: quat, min: min, max: max, onChange: onQuatChange, unit: props.useDegrees ? "deg" : "rad" }));
4418
4459
  };
4419
4460
  const Vector2PropertyLine = TensorPropertyLine;
4420
4461
  const Vector3PropertyLine = TensorPropertyLine;
@@ -5003,7 +5044,7 @@ function MakeModularTool(options) {
5003
5044
  });
5004
5045
  // Register the extension list service (for browsing/installing extensions) if extension feeds are provided.
5005
5046
  if (extensionFeeds.length > 0) {
5006
- const { ExtensionListServiceDefinition } = await import('./extensionsListService-CN7-7Dna.js');
5047
+ const { ExtensionListServiceDefinition } = await import('./extensionsListService-BPIryMmP.js');
5007
5048
  await serviceContainer.addServiceAsync(ExtensionListServiceDefinition);
5008
5049
  }
5009
5050
  // Register the theme selector service (for selecting the theme) if theming is configured.
@@ -5700,10 +5741,11 @@ const FollowCameraLimitsProperties = (props) => {
5700
5741
  const FreeCameraTransformProperties = (props) => {
5701
5742
  const { camera, settings } = props;
5702
5743
  const useDegrees = useObservableState(() => settings.useDegrees, settings.settingsChangedObservable);
5744
+ const useEuler = useObservableState(() => settings.useEuler, settings.settingsChangedObservable);
5703
5745
  const position = useProperty(camera, "position");
5704
5746
  const rotation = useProperty(camera, "rotation");
5705
5747
  const quatRotation = useProperty(camera, "rotationQuaternion");
5706
- return (jsxs(Fragment, { children: [jsx(Vector3PropertyLine, { label: "Position", value: position, onChange: (value) => (camera.position = value) }), quatRotation ? (jsx(QuaternionPropertyLine, { label: "Rotation (Quat)", value: quatRotation, onChange: (val) => (camera.rotationQuaternion = val), useDegrees: useDegrees }, "QuaternionRotationTransform")) : (jsx(RotationVectorPropertyLine, { label: "Rotation", value: rotation, onChange: (val) => (camera.rotation = val), useDegrees: useDegrees }, "RotationTransform"))] }));
5748
+ return (jsxs(Fragment, { children: [jsx(Vector3PropertyLine, { label: "Position", value: position, onChange: (value) => (camera.position = value) }), quatRotation ? (jsx(QuaternionPropertyLine, { label: "Rotation (Quat)", value: quatRotation, onChange: (val) => (camera.rotationQuaternion = val), useDegrees: useDegrees, useEuler: useEuler }, "QuaternionRotationTransform")) : (jsx(RotationVectorPropertyLine, { label: "Rotation", value: rotation, onChange: (val) => (camera.rotation = val), useDegrees: useDegrees }, "RotationTransform"))] }));
5707
5749
  };
5708
5750
  const FreeCameraControlProperties = (props) => {
5709
5751
  const { camera } = props;
@@ -5713,7 +5755,7 @@ const FreeCameraCollisionProperties = (props) => {
5713
5755
  const { camera } = props;
5714
5756
  const ellipsoid = useProperty(camera, "ellipsoid");
5715
5757
  const ellipsoidOffset = useProperty(camera, "ellipsoidOffset");
5716
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Check Collisions", target: camera, propertyKey: "checkCollisions" }), jsx(Vector3PropertyLine, { label: "Ellipsoid", value: ellipsoid, onChange: (val) => (camera.ellipsoid = val) }), jsx(Vector3PropertyLine, { label: "Ellipsoid Offset", value: ellipsoidOffset, onChange: (val) => (camera.ellipsoidOffset = val) })] }));
5758
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Check Collisions", target: camera, propertyKey: "checkCollisions" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Apply Gravity", target: camera, propertyKey: "applyGravity" }), jsx(Vector3PropertyLine, { label: "Ellipsoid", value: ellipsoid, onChange: (val) => (camera.ellipsoid = val) }), jsx(Vector3PropertyLine, { label: "Ellipsoid Offset", value: ellipsoidOffset, onChange: (val) => (camera.ellipsoidOffset = val) })] }));
5717
5759
  };
5718
5760
 
5719
5761
  const TargetCameraTransformProperties = (props) => {
@@ -5844,7 +5886,7 @@ const CommonGeneralProperties = (props) => {
5844
5886
  };
5845
5887
  const DisposableGeneralProperties = (props) => {
5846
5888
  const { disposableEntity } = props;
5847
- return (jsx(Fragment, { children: jsx(ButtonLine, { label: "Dispose", onClick: () => disposableEntity.dispose() }) }));
5889
+ return (jsx(Fragment, { children: jsx(ButtonLine, { label: "Dispose", icon: DeleteRegular, onClick: () => disposableEntity.dispose() }) }));
5848
5890
  };
5849
5891
 
5850
5892
  const CommonPropertiesServiceDefinition = {
@@ -5914,8 +5956,15 @@ const FrameGraphTaskProperties = (props) => {
5914
5956
  const FrameGraphGeneralProperties = (props) => {
5915
5957
  const { frameGraph } = props;
5916
5958
  const isSceneFrameGraph = useProperty(frameGraph.scene, "frameGraph");
5917
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Optimize Texture Allocation", description: "Whether to optimize texture allocation.", target: frameGraph, propertyKey: "optimizeTextureAllocation" }), isSceneFrameGraph !== frameGraph && jsx(ButtonLine, { onClick: () => (frameGraph.scene.frameGraph = frameGraph), label: "Set as scene's frame graph" }), jsx(ButtonLine, { label: "Edit Graph", onClick: () => {
5918
- void frameGraph.getLinkedNodeRenderGraph().edit({ nodeRenderGraphEditorConfig: { hostScene: frameGraph.scene } });
5959
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Optimize Texture Allocation", description: "Whether to optimize texture allocation.", target: frameGraph, propertyKey: "optimizeTextureAllocation" }), isSceneFrameGraph !== frameGraph && jsx(ButtonLine, { onClick: () => (frameGraph.scene.frameGraph = frameGraph), label: "Make Active", icon: PlayRegular }), jsx(ButtonLine, { label: "Edit Graph", icon: EditRegular, onClick: async () => {
5960
+ const renderGraph = frameGraph.getLinkedNodeRenderGraph();
5961
+ if (renderGraph) {
5962
+ // TODO: Figure out how to get all the various build steps to work with this.
5963
+ // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
5964
+ // const { NodeRenderGraphEditor } = await import("node-render-graph-editor/nodeRenderGraphEditor");
5965
+ // NodeRenderGraphEditor.Show({ nodeRenderGraph: renderGraph, hostScene: frameGraph.scene });
5966
+ await renderGraph.edit({ nodeRenderGraphEditorConfig: { hostScene: frameGraph.scene } });
5967
+ }
5919
5968
  } })] }));
5920
5969
  };
5921
5970
 
@@ -5950,7 +5999,44 @@ const AreaLightSetupProperties = ({ context: areaLight }) => {
5950
5999
  };
5951
6000
 
5952
6001
  const DirectionalLightSetupProperties = ({ context: directionalLight }) => {
5953
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { label: "Position", component: Vector3PropertyLine, target: directionalLight, propertyKey: "position" }), jsx(BoundProperty, { label: "Direction", component: Vector3PropertyLine, target: directionalLight, propertyKey: "direction" }), jsx(BoundProperty, { label: "Diffuse", component: Color3PropertyLine, target: directionalLight, propertyKey: "diffuse" }), jsx(BoundProperty, { label: "Specular", component: Color3PropertyLine, target: directionalLight, propertyKey: "specular" }), jsx(BoundProperty, { label: "Intensity", component: NumberInputPropertyLine, target: directionalLight, propertyKey: "intensity" })] }));
6002
+ const scene = directionalLight.getScene();
6003
+ const camera = scene.activeCamera;
6004
+ // Check if using CascadedShadowGenerator to hide autoCalcShadowZBounds
6005
+ let generator = directionalLight.getShadowGenerator(camera) ?? null;
6006
+ if (generator === null) {
6007
+ const shadowGenerators = directionalLight.getShadowGenerators();
6008
+ if (shadowGenerators && shadowGenerators.size > 0) {
6009
+ generator = shadowGenerators.values().next().value ?? null;
6010
+ }
6011
+ }
6012
+ const hideAutoCalcShadowZBounds = generator instanceof CascadedShadowGenerator;
6013
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { label: "Position", component: Vector3PropertyLine, target: directionalLight, propertyKey: "position" }), jsx(BoundProperty, { label: "Direction", component: Vector3PropertyLine, target: directionalLight, propertyKey: "direction" }), jsx(BoundProperty, { label: "Diffuse", component: Color3PropertyLine, target: directionalLight, propertyKey: "diffuse" }), jsx(BoundProperty, { label: "Specular", component: Color3PropertyLine, target: directionalLight, propertyKey: "specular" }), jsx(BoundProperty, { label: "Intensity", component: NumberInputPropertyLine, target: directionalLight, propertyKey: "intensity" }), jsx(BoundProperty, { label: "Auto Update Extends", description: "Automatically compute the projection matrix to fit the light's shadow frustum to the scene.", component: SwitchPropertyLine, target: directionalLight, propertyKey: "autoUpdateExtends" }), !hideAutoCalcShadowZBounds && (jsx(BoundProperty, { label: "Auto Calc Shadow ZBounds", description: "Automatically compute the shadow min/max z values.", component: SwitchPropertyLine, target: directionalLight, propertyKey: "autoCalcShadowZBounds" })), jsx(BoundProperty, { label: "Ortho Left", component: NumberInputPropertyLine, target: directionalLight, propertyKey: "orthoLeft" }), jsx(BoundProperty, { label: "Ortho Right", component: NumberInputPropertyLine, target: directionalLight, propertyKey: "orthoRight" }), jsx(BoundProperty, { label: "Ortho Bottom", component: NumberInputPropertyLine, target: directionalLight, propertyKey: "orthoBottom" }), jsx(BoundProperty, { label: "Ortho Top", component: NumberInputPropertyLine, target: directionalLight, propertyKey: "orthoTop" })] }));
6014
+ };
6015
+ const FrustumViewerMap = new WeakMap();
6016
+ const DirectionalLightDebugProperties = ({ context: directionalLight }) => {
6017
+ const [displayFrustum, setDisplayFrustum] = useState(FrustumViewerMap.has(directionalLight));
6018
+ const toggleDisplayFrustum = () => {
6019
+ const light = directionalLight;
6020
+ const camera = light.getScene().activeCamera;
6021
+ const existingState = FrustumViewerMap.get(light);
6022
+ if (existingState) {
6023
+ // Clean up existing frustum viewer
6024
+ light.getScene().onAfterRenderObservable.remove(existingState.observer);
6025
+ existingState.viewer.dispose();
6026
+ FrustumViewerMap.delete(light);
6027
+ setDisplayFrustum(false);
6028
+ }
6029
+ else {
6030
+ // Create new frustum viewer
6031
+ const viewer = new DirectionalLightFrustumViewer(light, camera);
6032
+ const observer = light.getScene().onAfterRenderObservable.add(() => {
6033
+ viewer.update();
6034
+ });
6035
+ FrustumViewerMap.set(light, { viewer, observer });
6036
+ setDisplayFrustum(true);
6037
+ }
6038
+ };
6039
+ return (jsx(Fragment, { children: jsx(SwitchPropertyLine, { label: "Display Frustum", value: displayFrustum, onChange: toggleDisplayFrustum }) }));
5954
6040
  };
5955
6041
 
5956
6042
  const HemisphericLightSetupProperties = ({ context: hemisphericLight }) => {
@@ -5973,6 +6059,31 @@ const MapSizeOptions = [
5973
6059
  { label: "512 x 512", value: 512 },
5974
6060
  { label: "256 x 256", value: 256 },
5975
6061
  ];
6062
+ const BlurModeOptions = [
6063
+ { label: "None", value: ShadowGenerator.FILTER_NONE },
6064
+ { label: "PCF", value: ShadowGenerator.FILTER_PCF },
6065
+ { label: "PCSS", value: ShadowGenerator.FILTER_PCSS },
6066
+ { label: "Poisson", value: ShadowGenerator.FILTER_POISSONSAMPLING },
6067
+ { label: "Exponential", value: ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP },
6068
+ { label: "Blurred Exponential", value: ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP },
6069
+ { label: "Close Exponential", value: ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP },
6070
+ { label: "Blurred Close Exponential", value: ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP },
6071
+ ];
6072
+ const CSMBlurModeOptions = [
6073
+ { label: "None", value: ShadowGenerator.FILTER_NONE },
6074
+ { label: "PCF", value: ShadowGenerator.FILTER_PCF },
6075
+ { label: "PCSS", value: ShadowGenerator.FILTER_PCSS },
6076
+ ];
6077
+ const FilteringQualityOptions = [
6078
+ { label: "Low", value: ShadowGenerator.QUALITY_LOW },
6079
+ { label: "Medium", value: ShadowGenerator.QUALITY_MEDIUM },
6080
+ { label: "High", value: ShadowGenerator.QUALITY_HIGH },
6081
+ ];
6082
+ const NumCascadesOptions = [
6083
+ { label: "2", value: 2 },
6084
+ { label: "3", value: 3 },
6085
+ { label: "4", value: 4 },
6086
+ ];
5976
6087
  function GetShadowGenerator(camera, shadowLight) {
5977
6088
  return shadowLight.getShadowGenerator(camera) ?? shadowLight.getShadowGenerators()?.values().next().value ?? null;
5978
6089
  }
@@ -6007,14 +6118,29 @@ const ShadowGeneratorSetupProperties = ({ context: shadowLight }) => {
6007
6118
  useEffect(() => {
6008
6119
  setHasShadowGenerator(!!shadowGenerator);
6009
6120
  }, [shadowGenerator]);
6121
+ const isCascaded = shadowGenerator instanceof CascadedShadowGenerator;
6122
+ const isStandardGenerator = shadowGenerator instanceof ShadowGenerator && !isCascaded;
6123
+ // Use useProperty to track filter changes and trigger re-renders for conditional UI
6124
+ const filter = useProperty(shadowGenerator, "filter") ?? ShadowGenerator.FILTER_NONE;
6125
+ const useKernelBlur = useProperty(shadowGenerator, "useKernelBlur");
6126
+ const blurModeOptions = isCascaded ? CSMBlurModeOptions : BlurModeOptions;
6127
+ const near = camera?.minZ ?? 0;
6128
+ const far = camera?.maxZ ?? 500000;
6129
+ const isPCFOrPCSS = filter === ShadowGenerator.FILTER_PCF || filter === ShadowGenerator.FILTER_PCSS;
6130
+ const isPCSS = filter === ShadowGenerator.FILTER_PCSS;
6131
+ const isBlurExponential = filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
6132
+ const isExponential = filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP;
6010
6133
  return (jsxs(Fragment, { children: [!hasShadowGenerator && (jsxs(Fragment, { children: [jsx(StringDropdownPropertyLine, { label: "Type", options: shadowGeneratorOptions, value: shadowGeneratorSettings.generatorType, onChange: (value) => setShadowGeneratorSettings((prev) => ({ ...prev, generatorType: value })) }), jsx(NumberDropdownPropertyLine, { label: "Map Size", options: MapSizeOptions, value: shadowGeneratorSettings.mapSize, onChange: (value) => setShadowGeneratorSettings((prev) => ({ ...prev, mapSize: value })) }), jsx(ButtonLine, { label: "Create Generator", onClick: () => {
6011
6134
  CreateShadowGenerator(shadowLight, shadowGeneratorSettings);
6012
6135
  setHasShadowGenerator(true);
6013
- } })] })), shadowGenerator && (jsxs(Fragment, { children: ["TODO: Not Implemented", jsx(ButtonLine, { label: "Dispose Generator", onClick: () => {
6136
+ } })] })), shadowGenerator && (jsxs(Fragment, { children: [isCascaded && jsx(CascadedShadowGeneratorProperties, { generator: shadowGenerator, near: near, far: far, isPCSS: isPCSS }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Bias", description: "Bias to apply to the shadow map to avoid shadow acne.", target: shadowGenerator, propertyKey: "bias", step: 0.0001 }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Normal Bias", description: "Normal bias to apply to avoid shadow acne.", target: shadowGenerator, propertyKey: "normalBias" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Darkness", description: "Darkness of the shadow (0 = no shadow, 1 = full shadow).", target: shadowGenerator, propertyKey: "darkness", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Transparent Shadows", description: "Allow transparent objects to cast shadows.", target: shadowGenerator, propertyKey: "transparencyShadow" }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Filter", description: "Shadow filtering mode.", target: shadowGenerator, propertyKey: "filter", options: blurModeOptions }), jsx(Collapse, { visible: isPCFOrPCSS, children: jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Filtering Quality", target: shadowGenerator, propertyKey: "filteringQuality", options: FilteringQualityOptions }) }), jsx(Collapse, { visible: isPCSS, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Penumbra Ratio", description: "Light size UV ratio for PCSS.", target: shadowGenerator, propertyKey: "contactHardeningLightSizeUVRatio", min: 0, max: 0.5, step: 0.001 }) }), isStandardGenerator && (jsxs(Fragment, { children: [jsx(Collapse, { visible: isBlurExponential, children: jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Kernel Blur", description: "Use kernel-based blur instead of box blur.", target: shadowGenerator, propertyKey: "useKernelBlur" }), useKernelBlur ? (jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Blur Kernel", target: shadowGenerator, propertyKey: "blurKernel", min: 1, max: 64, step: 1 })) : (jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Blur Box Offset", target: shadowGenerator, propertyKey: "blurBoxOffset", min: 1, max: 64, step: 1 }))] }) }), jsx(Collapse, { visible: isExponential, children: jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Depth Scale", target: shadowGenerator, propertyKey: "depthScale" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Blur Scale", target: shadowGenerator, propertyKey: "blurScale", min: 1, max: 4, step: 1 })] }) })] })), jsx(ButtonLine, { label: "Dispose Generator", onClick: () => {
6014
6137
  DisposeShadowGenerator(camera, shadowLight);
6015
6138
  setHasShadowGenerator(false);
6016
6139
  } })] }))] }));
6017
6140
  };
6141
+ const CascadedShadowGeneratorProperties = ({ generator, near, far, isPCSS, }) => {
6142
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Num Cascades", description: "Number of cascades for the cascaded shadow map.", target: generator, propertyKey: "numCascades", options: NumCascadesOptions }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Debug Mode", description: "Colorize cascades for debugging.", target: generator, propertyKey: "debug" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Stabilize Cascades", description: "Stabilize the cascade splits to avoid shimmering.", target: generator, propertyKey: "stabilizeCascades" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Lambda", description: "Balance between logarithmic and uniform cascade splits.", target: generator, propertyKey: "lambda", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Cascade Blend", description: "Percentage of blending between cascades.", target: generator, propertyKey: "cascadeBlendPercentage", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Depth Clamp", target: generator, propertyKey: "depthClamp" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Auto-Calc Depth Bounds", target: generator, propertyKey: "autoCalcDepthBounds" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Shadow MaxZ", target: generator, propertyKey: "shadowMaxZ", min: near, max: far, step: 0.5 }), jsx(Collapse, { visible: isPCSS, children: jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Penumbra Darkness", description: "Darkness of the penumbra for PCSS in CSM.", target: generator, propertyKey: "penumbraDarkness", min: 0, max: 1, step: 0.01 }) })] }));
6143
+ };
6018
6144
 
6019
6145
  const ShadowsSetupProperties = ({ context: shadowLight }) => {
6020
6146
  const shadowsEnabled = useProperty(shadowLight, "shadowEnabled");
@@ -6039,6 +6165,10 @@ const LightPropertiesServiceDefinition = {
6039
6165
  section: "Setup",
6040
6166
  component: DirectionalLightSetupProperties,
6041
6167
  },
6168
+ {
6169
+ section: "Debug",
6170
+ component: DirectionalLightDebugProperties,
6171
+ },
6042
6172
  ],
6043
6173
  });
6044
6174
  const pointLightContentRegistration = propertiesService.addSectionContent({
@@ -6203,7 +6333,7 @@ const MaterialGeneralProperties = (props) => {
6203
6333
  const pointsCloud = useProperty(material, "pointsCloud");
6204
6334
  const faceCulling = useProperty(material, "backFaceCulling");
6205
6335
  const isWebGPU = material.getScene().getEngine().isWebGPU;
6206
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Face Culling", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/using/materials_introduction#back-face-culling", target: material, propertyKey: "backFaceCulling" }), jsx(Collapse, { visible: faceCulling, children: jsx(BoundProperty, { component: SwitchPropertyLine, label: "Cull Back Faces", description: "Culls back faces. If false, front faces are culled.", target: material, propertyKey: "cullBackFaces" }) }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Orientation", description: "The front face side. Overrides mesh's orientation.", options: OrientationOptions, target: material, propertyKey: "sideOrientation", nullable: true, defaultValue: material.getScene().useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Disable Color Write", target: material, propertyKey: "disableColorWrite" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Disable Depth Write", target: material, propertyKey: "disableDepthWrite" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Force Depth Write", target: material, propertyKey: "forceDepthWrite" }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Depth Function", options: DepthFunctionOptions, target: material, propertyKey: "depthFunction" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Need Depth Pre-pass", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/transparent_rendering/#depth-pre-pass-meshes", target: material, propertyKey: "needDepthPrePass" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Z-offset Factor", target: material, propertyKey: "zOffset", min: -10, max: 10, step: 0.1 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Z-offset Units", target: material, propertyKey: "zOffsetUnits", min: -10, max: 10, step: 0.1 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Wireframe", target: material, propertyKey: "wireframe" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Point Cloud", target: material, propertyKey: "pointsCloud" }), pointsCloud && jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Point Size", target: material, propertyKey: "pointSize", min: 0, max: 100, step: 0.1 }), isWebGPU && jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Vertex Pulling", target: material, propertyKey: "useVertexPulling" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Support Fog", target: material, propertyKey: "fogEnabled", description: "Indicates whether the material supports fog (however, fog must be enabled at the scene level to be effective)." }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Logarithmic Depth", target: material, propertyKey: "useLogarithmicDepth", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/logarithmicDepthBuffer" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Set Vertex Output Invariant", target: material, propertyKey: "isVertexOutputInvariant", description: "Setting this property to true will force the shader compiler to disable some optimization to make sure the vertex output is always calculated the same way across different compilation units." })] }));
6336
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Face Culling", description: "Enabling this will enable culling, default is to cull backfaces. To enable front face culling instead, you can disable 'cullBackfaces' using the below option", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/using/materials_introduction#back-face-culling", target: material, propertyKey: "backFaceCulling" }), jsx(Collapse, { visible: faceCulling, children: jsx(BoundProperty, { component: SwitchPropertyLine, label: "Cull Back Faces", description: "Culls back faces. If false, front faces are culled.", target: material, propertyKey: "cullBackFaces" }) }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Orientation", description: "The front face side. Overrides mesh's orientation.", options: OrientationOptions, target: material, propertyKey: "sideOrientation", nullable: true, defaultValue: material.getScene().useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Disable Color Write", target: material, propertyKey: "disableColorWrite" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Disable Depth Write", target: material, propertyKey: "disableDepthWrite" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Force Depth Write", target: material, propertyKey: "forceDepthWrite" }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Depth Function", options: DepthFunctionOptions, target: material, propertyKey: "depthFunction" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Need Depth Pre-pass", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/transparent_rendering/#depth-pre-pass-meshes", target: material, propertyKey: "needDepthPrePass" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Z-offset Factor", target: material, propertyKey: "zOffset", min: -10, max: 10, step: 0.1 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Z-offset Units", target: material, propertyKey: "zOffsetUnits", min: -10, max: 10, step: 0.1 }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Wireframe", target: material, propertyKey: "wireframe" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Point Cloud", target: material, propertyKey: "pointsCloud" }), pointsCloud && jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Point Size", target: material, propertyKey: "pointSize", min: 0, max: 100, step: 0.1 }), isWebGPU && jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Vertex Pulling", target: material, propertyKey: "useVertexPulling" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Support Fog", target: material, propertyKey: "fogEnabled", description: "Indicates whether the material supports fog (however, fog must be enabled at the scene level to be effective)." }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Use Logarithmic Depth", target: material, propertyKey: "useLogarithmicDepth", docLink: "https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/logarithmicDepthBuffer" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Set Vertex Output Invariant", target: material, propertyKey: "isVertexOutputInvariant", description: "Setting this property to true will force the shader compiler to disable some optimization to make sure the vertex output is always calculated the same way across different compilation units." })] }));
6207
6337
  };
6208
6338
  const MaterialTransparencyProperties = (props) => {
6209
6339
  const { material } = props;
@@ -6222,6 +6352,17 @@ const MultiMaterialChildrenProperties = (props) => {
6222
6352
  .map((material, index) => (jsx(LinkToEntityPropertyLine, { label: `Material #${index + 1}`, entity: material, selectionService: selectionService }, material.uniqueId))) }));
6223
6353
  };
6224
6354
 
6355
+ const NodeMaterialGeneralProperties = (props) => {
6356
+ const { material } = props;
6357
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SwitchPropertyLine, label: "Ignore Alpha", target: material, propertyKey: "ignoreAlpha" }), jsx(ButtonLine, { label: "Edit", icon: EditRegular, onClick: async () => {
6358
+ // TODO: Figure out how to get all the various build steps to work with this.
6359
+ // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
6360
+ // const { NodeEditor } = await import("node-editor/nodeEditor");
6361
+ // NodeEditor.Show({ nodeMaterial: material });
6362
+ await material.edit();
6363
+ } })] }));
6364
+ };
6365
+
6225
6366
  function IsMaterialWithPublicNormalMaps(mat) {
6226
6367
  return mat.invertNormalMapX !== undefined;
6227
6368
  }
@@ -6235,6 +6376,174 @@ const NormalMapProperties = (props) => {
6235
6376
  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" })] })) }));
6236
6377
  };
6237
6378
 
6379
+ // TODO: ryamtrem / gehalper This function is temporal until there is a line control to handle texture links (similar to the old TextureLinkLineComponent)
6380
+ const UpdateTexture = (file, material, textureSetter) => {
6381
+ ReadFile(file, (data) => {
6382
+ const blob = new Blob([data], { type: "octet/stream" });
6383
+ const url = URL.createObjectURL(blob);
6384
+ textureSetter(new Texture(url, material.getScene(), false, false));
6385
+ }, undefined, true);
6386
+ };
6387
+ /**
6388
+ * Displays the base layer properties of an OpenPBR material.
6389
+ * @param props - The required properties
6390
+ * @returns A JSX element representing the base layer properties.
6391
+ */
6392
+ const OpenPBRMaterialBaseProperties = (props) => {
6393
+ const { material } = props;
6394
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6395
+ if (files.length > 0) {
6396
+ UpdateTexture(files[0], material, (texture) => (material.baseWeightTexture = texture));
6397
+ }
6398
+ } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Base Color", target: material, propertyKey: "baseColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Base Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6399
+ if (files.length > 0) {
6400
+ UpdateTexture(files[0], material, (texture) => (material.baseColorTexture = texture));
6401
+ }
6402
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6403
+ if (files.length > 0) {
6404
+ UpdateTexture(files[0], material, (texture) => (material.baseMetalnessTexture = texture));
6405
+ }
6406
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6407
+ if (files.length > 0) {
6408
+ UpdateTexture(files[0], material, (texture) => (material.baseDiffuseRoughnessTexture = texture));
6409
+ }
6410
+ } })] }));
6411
+ };
6412
+ /**
6413
+ * Displays the specular layer properties of an OpenPBR material.
6414
+ * @param props - The required properties
6415
+ * @returns A JSX element representing the specular layer properties.
6416
+ */
6417
+ const OpenPBRMaterialSpecularProperties = (props) => {
6418
+ const { material } = props;
6419
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6420
+ if (files.length > 0) {
6421
+ UpdateTexture(files[0], material, (texture) => (material.specularWeightTexture = texture));
6422
+ }
6423
+ } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Specular Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6424
+ if (files.length > 0) {
6425
+ UpdateTexture(files[0], material, (texture) => (material.specularColorTexture = texture));
6426
+ }
6427
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6428
+ if (files.length > 0) {
6429
+ UpdateTexture(files[0], material, (texture) => (material.specularRoughnessTexture = texture));
6430
+ }
6431
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6432
+ if (files.length > 0) {
6433
+ UpdateTexture(files[0], material, (texture) => (material.specularRoughnessAnisotropyTexture = texture));
6434
+ }
6435
+ } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01 })] }));
6436
+ };
6437
+ /**
6438
+ * Displays the coat layer properties of an OpenPBR material.
6439
+ * @param props - The required properties
6440
+ * @returns A JSX element representing the coat layer properties.
6441
+ */
6442
+ const OpenPBRMaterialCoatProperties = (props) => {
6443
+ const { material } = props;
6444
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6445
+ if (files.length > 0) {
6446
+ UpdateTexture(files[0], material, (texture) => (material.coatWeightTexture = texture));
6447
+ }
6448
+ } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Coat Color", target: material, propertyKey: "coatColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Coat Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6449
+ if (files.length > 0) {
6450
+ UpdateTexture(files[0], material, (texture) => (material.coatColorTexture = texture));
6451
+ }
6452
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6453
+ if (files.length > 0) {
6454
+ UpdateTexture(files[0], material, (texture) => (material.coatRoughnessTexture = texture));
6455
+ }
6456
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6457
+ if (files.length > 0) {
6458
+ UpdateTexture(files[0], material, (texture) => (material.coatRoughnessAnisotropyTexture = texture));
6459
+ }
6460
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6461
+ if (files.length > 0) {
6462
+ UpdateTexture(files[0], material, (texture) => (material.coatDarkeningTexture = texture));
6463
+ }
6464
+ } })] }));
6465
+ };
6466
+ /**
6467
+ * Displays the fuzz layer properties of an OpenPBR material.
6468
+ * @param props - The required properties
6469
+ * @returns A JSX element representing the fuzz layer properties.
6470
+ */
6471
+ const OpenPBRMaterialFuzzProperties = (props) => {
6472
+ const { material } = props;
6473
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Fuzz Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6474
+ if (files.length > 0) {
6475
+ UpdateTexture(files[0], material, (texture) => (material.fuzzWeightTexture = texture));
6476
+ }
6477
+ } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Fuzz Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6478
+ if (files.length > 0) {
6479
+ UpdateTexture(files[0], material, (texture) => (material.fuzzColorTexture = texture));
6480
+ }
6481
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6482
+ if (files.length > 0) {
6483
+ UpdateTexture(files[0], material, (texture) => (material.fuzzRoughnessTexture = texture));
6484
+ }
6485
+ } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Number of Samples", target: material, propertyKey: "fuzzSampleNumber", min: 4, max: 64, step: 1 })] }));
6486
+ };
6487
+ /**
6488
+ * Displays the emission properties of an OpenPBR material.
6489
+ * @param props - The required properties
6490
+ * @returns A JSX element representing the emission properties.
6491
+ */
6492
+ const OpenPBRMaterialEmissionProperties = (props) => {
6493
+ const { material } = props;
6494
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color3PropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Emission Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6495
+ if (files.length > 0) {
6496
+ UpdateTexture(files[0], material, (texture) => (material.emissionColorTexture = texture));
6497
+ }
6498
+ } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Emission Luminance", target: material, propertyKey: "emissionLuminance", min: 0, max: 10, step: 0.01 })] }));
6499
+ };
6500
+ /**
6501
+ * Displays the thin film properties of an OpenPBR material.
6502
+ * @param props - The required properties
6503
+ * @returns A JSX element representing the thin film properties.
6504
+ */
6505
+ const OpenPBRMaterialThinFilmProperties = (props) => {
6506
+ const { material } = props;
6507
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Thin Film Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6508
+ if (files.length > 0) {
6509
+ UpdateTexture(files[0], material, (texture) => (material.thinFilmWeightTexture = texture));
6510
+ }
6511
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6512
+ if (files.length > 0) {
6513
+ UpdateTexture(files[0], material, (texture) => (material.thinFilmThicknessTexture = texture));
6514
+ }
6515
+ } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film IOR", target: material, propertyKey: "thinFilmIor", min: 1, max: 3, step: 0.01 })] }));
6516
+ };
6517
+ /**
6518
+ * Displays the geometry properties of an OpenPBR material.
6519
+ * @param props - The required properties
6520
+ * @returns A JSX element representing the geometry properties.
6521
+ */
6522
+ const OpenPBRMaterialGeometryProperties = (props) => {
6523
+ const { material } = props;
6524
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Opacity", target: material, propertyKey: "geometryOpacity", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Opacity", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6525
+ if (files.length > 0) {
6526
+ UpdateTexture(files[0], material, (texture) => (material.geometryOpacityTexture = texture));
6527
+ }
6528
+ } }), jsx(FileUploadLine, { label: "Geometry Normal", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6529
+ if (files.length > 0) {
6530
+ UpdateTexture(files[0], material, (texture) => (material.geometryNormalTexture = texture));
6531
+ }
6532
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6533
+ if (files.length > 0) {
6534
+ UpdateTexture(files[0], material, (texture) => (material.geometryTangentTexture = texture));
6535
+ }
6536
+ } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6537
+ if (files.length > 0) {
6538
+ UpdateTexture(files[0], material, (texture) => (material.geometryCoatNormalTexture = texture));
6539
+ }
6540
+ } }), jsx(FileUploadLine, { label: "Geometry Coat Tangent", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6541
+ if (files.length > 0) {
6542
+ UpdateTexture(files[0], material, (texture) => (material.geometryCoatTangentTexture = texture));
6543
+ }
6544
+ } })] }));
6545
+ };
6546
+
6238
6547
  const useStyles$d = makeStyles({
6239
6548
  root: {
6240
6549
  display: "grid",
@@ -6319,7 +6628,7 @@ function EntitySelector(props) {
6319
6628
  return getEntities()
6320
6629
  .filter((e) => e.uniqueId !== undefined && (!filter || filter(e)))
6321
6630
  .map((entity) => ({
6322
- label: getName(entity),
6631
+ label: getName(entity).toString(),
6323
6632
  value: entity.uniqueId.toString(),
6324
6633
  }))
6325
6634
  .sort((a, b) => a.label.localeCompare(b.label));
@@ -6648,174 +6957,6 @@ const PBRBaseMaterialDebugProperties = (props) => {
6648
6957
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Debug Mode", target: material, propertyKey: "debugMode", options: DebugMode }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Split Position", target: material, propertyKey: "debugLimit", min: -1, max: 1, step: 0.01 }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Output Factor", target: material, propertyKey: "debugFactor", min: 0, max: 5, step: 0.01 })] }));
6649
6958
  };
6650
6959
 
6651
- // TODO: ryamtrem / gehalper This function is temporal until there is a line control to handle texture links (similar to the old TextureLinkLineComponent)
6652
- const UpdateTexture = (file, material, textureSetter) => {
6653
- ReadFile(file, (data) => {
6654
- const blob = new Blob([data], { type: "octet/stream" });
6655
- const url = URL.createObjectURL(blob);
6656
- textureSetter(new Texture(url, material.getScene(), false, false));
6657
- }, undefined, true);
6658
- };
6659
- /**
6660
- * Displays the base layer properties of an OpenPBR material.
6661
- * @param props - The required properties
6662
- * @returns A JSX element representing the base layer properties.
6663
- */
6664
- const OpenPBRMaterialBaseProperties = (props) => {
6665
- const { material } = props;
6666
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Base Weight", target: material, propertyKey: "baseWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Base Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6667
- if (files.length > 0) {
6668
- UpdateTexture(files[0], material, (texture) => (material.baseWeightTexture = texture));
6669
- }
6670
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Base Color", target: material, propertyKey: "baseColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Base Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6671
- if (files.length > 0) {
6672
- UpdateTexture(files[0], material, (texture) => (material.baseColorTexture = texture));
6673
- }
6674
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6675
- if (files.length > 0) {
6676
- UpdateTexture(files[0], material, (texture) => (material.baseMetalnessTexture = texture));
6677
- }
6678
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6679
- if (files.length > 0) {
6680
- UpdateTexture(files[0], material, (texture) => (material.baseDiffuseRoughnessTexture = texture));
6681
- }
6682
- } })] }));
6683
- };
6684
- /**
6685
- * Displays the specular layer properties of an OpenPBR material.
6686
- * @param props - The required properties
6687
- * @returns A JSX element representing the specular layer properties.
6688
- */
6689
- const OpenPBRMaterialSpecularProperties = (props) => {
6690
- const { material } = props;
6691
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular Weight", target: material, propertyKey: "specularWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Specular Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6692
- if (files.length > 0) {
6693
- UpdateTexture(files[0], material, (texture) => (material.specularWeightTexture = texture));
6694
- }
6695
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Specular Color", target: material, propertyKey: "specularColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Specular Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6696
- if (files.length > 0) {
6697
- UpdateTexture(files[0], material, (texture) => (material.specularColorTexture = texture));
6698
- }
6699
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6700
- if (files.length > 0) {
6701
- UpdateTexture(files[0], material, (texture) => (material.specularRoughnessTexture = texture));
6702
- }
6703
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6704
- if (files.length > 0) {
6705
- UpdateTexture(files[0], material, (texture) => (material.specularRoughnessAnisotropyTexture = texture));
6706
- }
6707
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Specular IOR", target: material, propertyKey: "specularIor", min: 1, max: 3, step: 0.01 })] }));
6708
- };
6709
- /**
6710
- * Displays the coat layer properties of an OpenPBR material.
6711
- * @param props - The required properties
6712
- * @returns A JSX element representing the coat layer properties.
6713
- */
6714
- const OpenPBRMaterialCoatProperties = (props) => {
6715
- const { material } = props;
6716
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Coat Weight", target: material, propertyKey: "coatWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Coat Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6717
- if (files.length > 0) {
6718
- UpdateTexture(files[0], material, (texture) => (material.coatWeightTexture = texture));
6719
- }
6720
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Coat Color", target: material, propertyKey: "coatColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Coat Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6721
- if (files.length > 0) {
6722
- UpdateTexture(files[0], material, (texture) => (material.coatColorTexture = texture));
6723
- }
6724
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6725
- if (files.length > 0) {
6726
- UpdateTexture(files[0], material, (texture) => (material.coatRoughnessTexture = texture));
6727
- }
6728
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6729
- if (files.length > 0) {
6730
- UpdateTexture(files[0], material, (texture) => (material.coatRoughnessAnisotropyTexture = texture));
6731
- }
6732
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6733
- if (files.length > 0) {
6734
- UpdateTexture(files[0], material, (texture) => (material.coatDarkeningTexture = texture));
6735
- }
6736
- } })] }));
6737
- };
6738
- /**
6739
- * Displays the fuzz layer properties of an OpenPBR material.
6740
- * @param props - The required properties
6741
- * @returns A JSX element representing the fuzz layer properties.
6742
- */
6743
- const OpenPBRMaterialFuzzProperties = (props) => {
6744
- const { material } = props;
6745
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Fuzz Weight", target: material, propertyKey: "fuzzWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Fuzz Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6746
- if (files.length > 0) {
6747
- UpdateTexture(files[0], material, (texture) => (material.fuzzWeightTexture = texture));
6748
- }
6749
- } }), jsx(BoundProperty, { component: Color3PropertyLine, label: "Fuzz Color", target: material, propertyKey: "fuzzColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Fuzz Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6750
- if (files.length > 0) {
6751
- UpdateTexture(files[0], material, (texture) => (material.fuzzColorTexture = texture));
6752
- }
6753
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6754
- if (files.length > 0) {
6755
- UpdateTexture(files[0], material, (texture) => (material.fuzzRoughnessTexture = texture));
6756
- }
6757
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Number of Samples", target: material, propertyKey: "fuzzSampleNumber", min: 4, max: 64, step: 1 })] }));
6758
- };
6759
- /**
6760
- * Displays the emission properties of an OpenPBR material.
6761
- * @param props - The required properties
6762
- * @returns A JSX element representing the emission properties.
6763
- */
6764
- const OpenPBRMaterialEmissionProperties = (props) => {
6765
- const { material } = props;
6766
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Color3PropertyLine, label: "Emission Color", target: material, propertyKey: "emissionColor", isLinearMode: true }), jsx(FileUploadLine, { label: "Emission Color", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6767
- if (files.length > 0) {
6768
- UpdateTexture(files[0], material, (texture) => (material.emissionColorTexture = texture));
6769
- }
6770
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Emission Luminance", target: material, propertyKey: "emissionLuminance", min: 0, max: 10, step: 0.01 })] }));
6771
- };
6772
- /**
6773
- * Displays the thin film properties of an OpenPBR material.
6774
- * @param props - The required properties
6775
- * @returns A JSX element representing the thin film properties.
6776
- */
6777
- const OpenPBRMaterialThinFilmProperties = (props) => {
6778
- const { material } = props;
6779
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film Weight", target: material, propertyKey: "thinFilmWeight", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Thin Film Weight", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6780
- if (files.length > 0) {
6781
- UpdateTexture(files[0], material, (texture) => (material.thinFilmWeightTexture = texture));
6782
- }
6783
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6784
- if (files.length > 0) {
6785
- UpdateTexture(files[0], material, (texture) => (material.thinFilmThicknessTexture = texture));
6786
- }
6787
- } }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Thin Film IOR", target: material, propertyKey: "thinFilmIor", min: 1, max: 3, step: 0.01 })] }));
6788
- };
6789
- /**
6790
- * Displays the geometry properties of an OpenPBR material.
6791
- * @param props - The required properties
6792
- * @returns A JSX element representing the geometry properties.
6793
- */
6794
- const OpenPBRMaterialGeometryProperties = (props) => {
6795
- const { material } = props;
6796
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Opacity", target: material, propertyKey: "geometryOpacity", min: 0, max: 1, step: 0.01 }), jsx(FileUploadLine, { label: "Geometry Opacity", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6797
- if (files.length > 0) {
6798
- UpdateTexture(files[0], material, (texture) => (material.geometryOpacityTexture = texture));
6799
- }
6800
- } }), jsx(FileUploadLine, { label: "Geometry Normal", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6801
- if (files.length > 0) {
6802
- UpdateTexture(files[0], material, (texture) => (material.geometryNormalTexture = texture));
6803
- }
6804
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6805
- if (files.length > 0) {
6806
- UpdateTexture(files[0], material, (texture) => (material.geometryTangentTexture = texture));
6807
- }
6808
- } }), 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, .tga, .dds, .env, .exr", onClick: (files) => {
6809
- if (files.length > 0) {
6810
- UpdateTexture(files[0], material, (texture) => (material.geometryCoatNormalTexture = texture));
6811
- }
6812
- } }), jsx(FileUploadLine, { label: "Geometry Coat Tangent", accept: ".jpg, .png, .tga, .dds, .env, .exr", onClick: (files) => {
6813
- if (files.length > 0) {
6814
- UpdateTexture(files[0], material, (texture) => (material.geometryCoatTangentTexture = texture));
6815
- }
6816
- } })] }));
6817
- };
6818
-
6819
6960
  const SkyMaterialProperties = (props) => {
6820
6961
  const { material, settings } = props;
6821
6962
  const [toDisplayAngle, fromDisplayAngle, useDegrees] = useAngleConverters(settings);
@@ -7038,6 +7179,16 @@ const MaterialPropertiesServiceDefinition = {
7038
7179
  },
7039
7180
  ],
7040
7181
  });
7182
+ const nodeMaterialContentRegistration = propertiesService.addSectionContent({
7183
+ key: "Node Material Properties",
7184
+ predicate: (entity) => entity instanceof NodeMaterial,
7185
+ content: [
7186
+ {
7187
+ section: "General",
7188
+ component: ({ context }) => jsx(NodeMaterialGeneralProperties, { material: context }),
7189
+ },
7190
+ ],
7191
+ });
7041
7192
  return {
7042
7193
  dispose: () => {
7043
7194
  pbrMaterialPropertyChangedObserver.remove();
@@ -7047,6 +7198,7 @@ const MaterialPropertiesServiceDefinition = {
7047
7198
  openPBRMaterialPropertiesRegistration.dispose();
7048
7199
  skyMaterialRegistration.dispose();
7049
7200
  multiMaterialContentRegistration.dispose();
7201
+ nodeMaterialContentRegistration.dispose();
7050
7202
  },
7051
7203
  };
7052
7204
  },
@@ -7263,7 +7415,7 @@ const AbstractMeshGeneralProperties = (props) => {
7263
7415
  };
7264
7416
  const AbstractMeshDisplayProperties = (props) => {
7265
7417
  const { mesh } = props;
7266
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Alpha Index", target: mesh, propertyKey: "alphaIndex" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Receive Shadows", target: mesh, propertyKey: "receiveShadows" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rendering Group Id", target: mesh, propertyKey: "renderingGroupId", min: RenderingManager.MIN_RENDERINGGROUPS, max: RenderingManager.MAX_RENDERINGGROUPS - 1, step: 1 }), jsx(BoundProperty, { component: HexPropertyLine, label: "Layer Mask", target: mesh, propertyKey: "layerMask" })] }));
7418
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Alpha Index", target: mesh, propertyKey: "alphaIndex" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Receive Shadows", target: mesh, propertyKey: "receiveShadows" }), mesh.isVerticesDataPresent(VertexBuffer.ColorKind) && (jsxs(Fragment, { children: [jsx(BoundProperty, { label: "Use Vertex Colors", component: SwitchPropertyLine, target: mesh, propertyKey: "useVertexColors" }), jsx(BoundProperty, { label: "Has Vertex Alpha", component: SwitchPropertyLine, target: mesh, propertyKey: "hasVertexAlpha" })] })), mesh.getScene().fogMode !== Constants.FOGMODE_NONE && jsx(BoundProperty, { label: "Apply Fog", component: SwitchPropertyLine, target: mesh, propertyKey: "applyFog" }), !mesh.parent && jsx(BoundProperty, { component: SwitchPropertyLine, label: "Infinite distance", target: mesh, propertyKey: "infiniteDistance" }), jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Rendering Group Id", target: mesh, propertyKey: "renderingGroupId", min: RenderingManager.MIN_RENDERINGGROUPS, max: RenderingManager.MAX_RENDERINGGROUPS - 1, step: 1 }), jsx(BoundProperty, { component: HexPropertyLine, label: "Layer Mask", target: mesh, propertyKey: "layerMask" })] }));
7267
7419
  };
7268
7420
  const AbstractMeshAdvancedProperties = (props) => {
7269
7421
  const { mesh } = props;
@@ -7525,6 +7677,22 @@ const AbstractMeshDebugProperties = (props) => {
7525
7677
  } }), jsx(SyncedSliderPropertyLine, { label: "Target Bone", value: displayBoneIndex, min: 0, max: targetBoneOptions.length - 1, step: 1, onChange: (value) => onBoneDisplayIndexChangeHandler(value) })] }) }), skeleton && jsx(SwitchPropertyLine, { label: "Display Skeleton Map", value: displaySkeletonMap, onChange: () => displaySkeletonMapHandler() })] }));
7526
7678
  };
7527
7679
 
7680
+ const GaussianSplattingDisplayProperties = (props) => {
7681
+ const { mesh } = props;
7682
+ return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Splat Count", value: mesh.splatCount ?? 0 }), jsx(StringifiedPropertyLine, { label: "SH Parameter Count", value: (mesh.shDegree + 1) * (mesh.shDegree + 1) - 1 }), jsx(BooleanBadgePropertyLine, { label: "Has Compensation", value: mesh.compensation }), jsx(StringifiedPropertyLine, { label: "Kernel Size", value: mesh.kernelSize })] }));
7683
+ };
7684
+
7685
+ const MeshGeneralProperties = (props) => {
7686
+ const { mesh } = props;
7687
+ const nodeGeometry = mesh._internalMetadata?.nodeGeometry;
7688
+ return (jsx(Fragment, { children: nodeGeometry && (jsx(ButtonLine, { label: "Edit", icon: EditRegular, onClick: async () => {
7689
+ // TODO: Figure out how to get all the various build steps to work with this.
7690
+ // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
7691
+ // const { NodeGeometryEditor } = await import("node-geometry-editor/nodeGeometryEditor");
7692
+ // NodeGeometryEditor.Show({ nodeGeometry: nodeGeometry, hostScene: mesh.getScene() });
7693
+ await nodeGeometry.edit({ nodeGeometryEditorConfig: { hostScene: mesh.getScene() } });
7694
+ } })) }));
7695
+ };
7528
7696
  const MeshDisplayProperties = (props) => {
7529
7697
  const { mesh } = props;
7530
7698
  return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: SyncedSliderPropertyLine, label: "Visibility", description: "Controls the visibility of the mesh. 0 is invisible, 1 is fully visible.", target: mesh, propertyKey: "visibility", min: 0, max: 1, step: 0.01 }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Orientation", description: "Controls the side orientation or winding order of the mesh.", target: mesh, propertyKey: "sideOrientation", options: [
@@ -7533,11 +7701,6 @@ const MeshDisplayProperties = (props) => {
7533
7701
  ] })] }));
7534
7702
  };
7535
7703
 
7536
- const GaussianSplattingDisplayProperties = (props) => {
7537
- const { mesh } = props;
7538
- return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Splat Count", value: mesh.splatCount ?? 0 }), jsx(StringifiedPropertyLine, { label: "SH Parameter Count", value: (mesh.shDegree + 1) * (mesh.shDegree + 1) - 1 }), jsx(BooleanBadgePropertyLine, { label: "Has Compensation", value: mesh.compensation }), jsx(StringifiedPropertyLine, { label: "Kernel Size", value: mesh.kernelSize })] }));
7539
- };
7540
-
7541
7704
  const NodeGeneralProperties = (props) => {
7542
7705
  const { node, selectionService } = props;
7543
7706
  const isEnabled = useObservableState(() => node.isEnabled(false), node.onEnabledStateChangedObservable);
@@ -7597,6 +7760,10 @@ const NodePropertiesServiceDefinition = {
7597
7760
  key: "Mesh Properties",
7598
7761
  predicate: (entity) => entity instanceof Mesh && entity.getTotalVertices() > 0,
7599
7762
  content: [
7763
+ {
7764
+ section: "General",
7765
+ component: ({ context }) => jsx(MeshGeneralProperties, { mesh: context }),
7766
+ },
7600
7767
  {
7601
7768
  section: "Display",
7602
7769
  component: ({ context }) => jsx(MeshDisplayProperties, { mesh: context }),
@@ -7919,7 +8086,7 @@ const AttractorList = (props) => {
7919
8086
  gizmoManager.attachToMesh(attached);
7920
8087
  setControlledImpostor(attached);
7921
8088
  };
7922
- return (jsxs(Fragment, { children: [items.length > 0 && (jsxs(Fragment, { children: [jsx(Color3PropertyLine, { label: "Attractor debug color", value: impostorColor, onChange: setImpostorColor }), jsx(SyncedSliderPropertyLine, { label: "Attractor debug size", value: impostorScale, onChange: setImpostorScale, min: 0, max: 10, step: 0.1 })] })), jsx(List, { addButtonLabel: `Add new attractor`, items: items, onDelete: (item, _index) => system.removeAttractor(item.data), onAdd: (item) => system.addAttractor(item?.data ?? new Attractor()), renderItem: (item) => {
8089
+ return (jsxs(Fragment, { children: [items.length > 0 && (jsxs(Fragment, { children: [jsx(Color3PropertyLine, { label: "Attractor Debug Color", value: impostorColor, onChange: setImpostorColor }), jsx(SyncedSliderPropertyLine, { label: "Attractor Debug Size", value: impostorScale, onChange: setImpostorScale, min: 0, max: 10, step: 0.1 })] })), jsx(List, { addButtonLabel: `Add New Attractor`, items: items, onDelete: (item, _index) => system.removeAttractor(item.data), onAdd: (item) => system.addAttractor(item?.data ?? new Attractor()), renderItem: (item) => {
7923
8090
  return (jsx(AttractorComponent, { attractor: item.data, id: item.id, scene: scene, impostorColor: impostorColor, impostorScale: impostorScale, impostorMaterial: impostorMaterial, isControlled: (impostor) => impostor === controlledImpostor, onControl: onControlImpostor }));
7924
8091
  } })] }));
7925
8092
  };
@@ -8039,7 +8206,8 @@ const ParticleSystemGeneralProperties = (props) => {
8039
8206
  request.open("GET", ParticleHelper.SnippetUrl + "/" + trimmed.replace(/#/g, "/"), true);
8040
8207
  request.send();
8041
8208
  }, [applyParticleSystemJsonToSystem, scene, system]);
8042
- const saveToSnippetServer = useCallback(() => {
8209
+ const saveToSnippetServer = useCallback(async () => {
8210
+ const deferred = new Deferred();
8043
8211
  // Serialize once and post as snippet payload.
8044
8212
  const content = JSON.stringify(system.serialize(true));
8045
8213
  const xmlHttp = new XMLHttpRequest();
@@ -8048,6 +8216,7 @@ const ParticleSystemGeneralProperties = (props) => {
8048
8216
  return;
8049
8217
  }
8050
8218
  if (xmlHttp.status !== 200) {
8219
+ deferred.reject();
8051
8220
  alert("Unable to save your particle system");
8052
8221
  return;
8053
8222
  }
@@ -8062,9 +8231,11 @@ const ParticleSystemGeneralProperties = (props) => {
8062
8231
  void navigator.clipboard.writeText(system.snippetId);
8063
8232
  }
8064
8233
  PersistSnippetId(system.snippetId);
8234
+ deferred.resolve();
8065
8235
  alert("Particle system saved with ID: " + system.snippetId + " (the id was also saved to your clipboard)");
8066
8236
  }
8067
8237
  catch (e) {
8238
+ deferred.reject(e);
8068
8239
  alert("Unable to save your particle system: " + e);
8069
8240
  }
8070
8241
  };
@@ -8079,16 +8250,28 @@ const ParticleSystemGeneralProperties = (props) => {
8079
8250
  tags: "",
8080
8251
  };
8081
8252
  xmlHttp.send(JSON.stringify(dataToSend));
8253
+ await deferred.promise;
8082
8254
  }, [system]);
8083
- return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Capacity", description: "Maximum number of particles in the system.", value: capacity }), jsx(StringifiedPropertyLine, { label: "Active Particles", description: "Current number of active particles.", value: activeCount }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Blend Mode", target: system, propertyKey: "blendMode", options: BlendModeOptions }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "World Offset", target: system, propertyKey: "worldOffset" }), !system.isNodeGenerated && jsx(BoundProperty, { component: Vector3PropertyLine, label: "Gravity", target: system, propertyKey: "gravity" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Is Billboard", target: system, propertyKey: "isBillboardBased" }), isBillboardBased && (jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Billboard Mode", target: system, propertyKey: "billboardMode", options: ParticleBillboardModeOptions })), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Is Local", target: system, propertyKey: "isLocal" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Force Depth Write", target: system, propertyKey: "forceDepthWrite" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Update Speed", target: system, propertyKey: "updateSpeed", min: 0, step: 0.01 }), jsx(ButtonLine, { label: system.isNodeGenerated ? "Edit in Node Particle Editor (coming soon)" : "View in Node Particle Editor (coming soon)", disabled: true, onClick: () => {
8084
- // Hook up once Node Particle Editor UX is wired.
8085
- } }), isStopping ? (jsx(TextPropertyLine, { label: "System is stopping...", value: "" })) : isAlive ? (jsx(ButtonLine, { label: "Stop", onClick: () => {
8255
+ return (jsxs(Fragment, { children: [jsx(StringifiedPropertyLine, { label: "Capacity", description: "Maximum number of particles in the system.", value: capacity }), jsx(StringifiedPropertyLine, { label: "Active Particles", description: "Current number of active particles.", value: activeCount }), jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Blend Mode", target: system, propertyKey: "blendMode", options: BlendModeOptions }), jsx(BoundProperty, { component: Vector3PropertyLine, label: "World Offset", target: system, propertyKey: "worldOffset" }), !system.isNodeGenerated && jsx(BoundProperty, { component: Vector3PropertyLine, label: "Gravity", target: system, propertyKey: "gravity" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Is Billboard", target: system, propertyKey: "isBillboardBased" }), isBillboardBased && (jsx(BoundProperty, { component: NumberDropdownPropertyLine, label: "Billboard Mode", target: system, propertyKey: "billboardMode", options: ParticleBillboardModeOptions })), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Is Local", target: system, propertyKey: "isLocal" }), jsx(BoundProperty, { component: SwitchPropertyLine, label: "Force Depth Write", target: system, propertyKey: "forceDepthWrite" }), jsx(BoundProperty, { component: NumberInputPropertyLine, label: "Update Speed", target: system, propertyKey: "updateSpeed", min: 0, step: 0.01 }), jsx(ButtonLine, { label: system.isNodeGenerated ? "Edit" : "View", icon: system.isNodeGenerated ? EditRegular : EyeRegular, onClick: async () => {
8256
+ const scene = system.getScene();
8257
+ if (!scene) {
8258
+ return;
8259
+ }
8260
+ const systemSet = system.source ? system.source : await ConvertToNodeParticleSystemSetAsync("source", [system]);
8261
+ if (systemSet) {
8262
+ // TODO: Figure out how to get all the various build steps to work with this.
8263
+ // See the initial attempt here: https://github.com/BabylonJS/Babylon.js/pull/17646
8264
+ // const { NodeParticleEditor } = await import("node-particle-editor/nodeParticleEditor");
8265
+ // NodeParticleEditor.Show({ nodeParticleSet: systemSet, hostScene: scene, backgroundColor: scene.clearColor });
8266
+ await systemSet.editAsync({ nodeEditorConfig: { backgroundColor: scene.clearColor } });
8267
+ }
8268
+ } }), isStopping ? (jsx(TextPropertyLine, { label: "System is stopping...", value: "" })) : isAlive ? (jsx(ButtonLine, { label: "Stop", icon: StopRegular, onClick: () => {
8086
8269
  setStopRequested(true);
8087
8270
  system.stop();
8088
- } })) : (jsx(ButtonLine, { label: "Start", onClick: () => {
8271
+ } })) : (jsx(ButtonLine, { label: "Start", icon: PlayRegular, onClick: () => {
8089
8272
  setStopRequested(false);
8090
8273
  system.start();
8091
- } })), !system.isNodeGenerated && (jsxs(Fragment, { children: [jsx(FileUploadLine, { label: "Load from file", accept: ".json", onClick: (files) => {
8274
+ } })), !system.isNodeGenerated && (jsxs(Fragment, { children: [jsx(FileUploadLine, { label: "Load from File", accept: ".json", onClick: (files) => {
8092
8275
  if (files.length === 0) {
8093
8276
  return;
8094
8277
  }
@@ -8101,13 +8284,13 @@ const ParticleSystemGeneralProperties = (props) => {
8101
8284
  }
8102
8285
  applyParticleSystemJsonToSystem(jsonObject);
8103
8286
  }, undefined, true);
8104
- } }), jsx(ButtonLine, { label: "Save to file", onClick: () => {
8287
+ } }), jsx(ButtonLine, { label: "Save to File", icon: ArrowDownloadRegular, onClick: () => {
8105
8288
  // Download serialization as a JSON file.
8106
8289
  const data = JSON.stringify(system.serialize(true), null, 2);
8107
8290
  const blob = new Blob([data], { type: "application/json" });
8108
8291
  const name = (system.name && system.name.trim().length > 0 ? system.name.trim() : "particleSystem") + ".json";
8109
8292
  Tools.Download(blob, name);
8110
- } }), snippetId && jsx(TextPropertyLine, { label: "Snippet ID", value: snippetId }), jsx(ButtonLine, { label: "Load from snippet server", onClick: loadFromSnippetServer }), jsx(ButtonLine, { label: "Save to snippet server", onClick: saveToSnippetServer })] }))] }));
8293
+ } }), snippetId && jsx(TextPropertyLine, { label: "Snippet ID", value: snippetId }), jsx(ButtonLine, { label: "Load from Snippet Server", onClick: loadFromSnippetServer, icon: CloudArrowUpRegular }), jsx(ButtonLine, { label: "Save to Snippet Server", onClick: saveToSnippetServer, icon: CloudArrowDownRegular })] }))] }));
8111
8294
  };
8112
8295
  /**
8113
8296
  * Display attractor-related properties for a particle system.
@@ -11354,7 +11537,8 @@ const TransformProperties = (props) => {
11354
11537
  const { transform, settings } = props;
11355
11538
  const quatRotation = useQuaternionProperty(transform, "rotationQuaternion");
11356
11539
  const useDegrees = useObservableState(() => settings.useDegrees, settings.settingsChangedObservable);
11357
- return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: transform, propertyKey: "position" }), quatRotation ? (jsx(QuaternionPropertyLine, { label: "Rotation (Quat)", value: quatRotation, onChange: (val) => (transform.rotationQuaternion = val), useDegrees: useDegrees }, "QuaternionRotationTransform")) : (jsx(BoundProperty, { component: RotationVectorPropertyLine, label: "Rotation", target: transform, propertyKey: "rotation", useDegrees: useDegrees })), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Scaling", target: transform, propertyKey: "scaling" })] }));
11540
+ const useEuler = useObservableState(() => settings.useEuler, settings.settingsChangedObservable);
11541
+ return (jsxs(Fragment, { children: [jsx(BoundProperty, { component: Vector3PropertyLine, label: "Position", target: transform, propertyKey: "position" }), quatRotation ? (jsx(QuaternionPropertyLine, { label: "Rotation (Quat)", value: quatRotation, onChange: (val) => (transform.rotationQuaternion = val), useDegrees: useDegrees, useEuler: useEuler }, "QuaternionRotationTransform")) : (jsx(BoundProperty, { component: RotationVectorPropertyLine, label: "Rotation", target: transform, propertyKey: "rotation", useDegrees: useDegrees })), jsx(BoundProperty, { component: Vector3PropertyLine, label: "Scaling", target: transform, propertyKey: "scaling" })] }));
11358
11542
  };
11359
11543
 
11360
11544
  const TransformPropertiesServiceDefinition = {
@@ -13352,4 +13536,4 @@ const TextAreaPropertyLine = (props) => {
13352
13536
  AttachDebugLayer();
13353
13537
 
13354
13538
  export { useSidePaneDockOverrides as $, Accordion as A, ButtonLine as B, Collapse as C, DebugServiceIdentity as D, ExtensibleAccordion as E, FileUploadLine as F, useVector3Property as G, useColor3Property as H, Inspector as I, useColor4Property as J, useQuaternionProperty as K, Link as L, MakeLazyComponent as M, NumberDropdownPropertyLine as N, MakePropertyHook as O, Popover as P, useInterceptObservable as Q, useEventfulState as R, SwitchPropertyLine as S, ToolsServiceIdentity as T, useObservableCollection as U, Vector3PropertyLine as V, useOrderedObservableCollection as W, usePollingObservable as X, useResource as Y, useAsyncResource as Z, useCompactMode as _, SyncedSliderPropertyLine as a, useAngleConverters as a0, MakeTeachingMoment as a1, MakeDialogTeachingMoment as a2, InterceptFunction as a3, GetPropertyDescriptor as a4, IsPropertyReadonly as a5, InterceptProperty as a6, ObservableCollection as a7, ConstructorFactory as a8, SelectionServiceIdentity as a9, TextInput as aA, ToggleButton as aB, ChildWindow as aC, FactorGradientList as aD, Color3GradientList as aE, Color4GradientList as aF, Pane as aG, BooleanBadgePropertyLine as aH, Color3PropertyLine as aI, Color4PropertyLine as aJ, HexPropertyLine as aK, NumberInputPropertyLine as aL, LinkPropertyLine as aM, PropertyLine as aN, LineContainer as aO, PlaceholderPropertyLine as aP, StringifiedPropertyLine as aQ, TextAreaPropertyLine as aR, TextPropertyLine as aS, RotationVectorPropertyLine as aT, QuaternionPropertyLine as aU, Vector2PropertyLine as aV, Vector4PropertyLine as aW, SelectionServiceDefinition as aa, SettingsContextIdentity as ab, ShowInspector as ac, Checkbox as ad, ColorPickerPopup as ae, InputHexField as af, InputHsvField as ag, ComboBox as ah, DraggableLine as ai, Dropdown as aj, NumberDropdown as ak, StringDropdown as al, FactorGradientComponent as am, Color3GradientComponent as an, Color4GradientComponent as ao, ColorStepGradientComponent as ap, InfoLabel as aq, List as ar, MessageBar as as, PositionedPopover as at, SearchBar as au, SearchBox as av, SpinButton as aw, Switch as ax, SyncedSliderInput as ay, Textarea as az, Button as b, TextInputPropertyLine as c, SpinButtonPropertyLine as d, CheckboxPropertyLine as e, ShellServiceIdentity as f, SceneContextIdentity as g, AccordionSection as h, useExtensionManager as i, MakePopoverTeachingMoment as j, TeachingMoment as k, SidePaneContainer as l, PropertiesServiceIdentity as m, SceneExplorerServiceIdentity as n, SettingsServiceIdentity as o, StatsServiceIdentity as p, ConvertOptions as q, AttachDebugLayer as r, DetachDebugLayer as s, StringDropdownPropertyLine as t, useObservableState as u, BoundProperty as v, LinkToEntityPropertyLine as w, Theme as x, BuiltInsExtensionFeed as y, useProperty as z };
13355
- //# sourceMappingURL=index-D450kD0o.js.map
13539
+ //# sourceMappingURL=index-upm3WBf8.js.map