@nice2dev/ui-3d 1.3.14 → 1.3.16

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/collaborative/collaborativeScene.js +160 -206
  3. package/dist/cjs/core/editorAsk.js +51 -41
  4. package/dist/cjs/core/i18n.js +13 -14
  5. package/dist/cjs/core/threeCompat.js +29 -42
  6. package/dist/cjs/dance/DanceBridge.js +148 -177
  7. package/dist/cjs/dance/DanceScoreEngine.js +209 -221
  8. package/dist/cjs/dance/PoseDetector.js +302 -275
  9. package/dist/cjs/index.js +110 -113
  10. package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
  11. package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
  12. package/dist/cjs/material/materialEditorTypes.js +35 -49
  13. package/dist/cjs/material/materialEditorUtils.js +684 -747
  14. package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
  15. package/dist/cjs/model/ModelEditor.js +90 -40
  16. package/dist/cjs/model/ModelEditor.module.css.js +257 -5
  17. package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
  18. package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
  19. package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
  20. package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
  21. package/dist/cjs/model/ModelEditorTimeline.js +232 -34
  22. package/dist/cjs/model/ModelEditorToolbar.js +170 -14
  23. package/dist/cjs/model/ModelEditorViewport.js +231 -39
  24. package/dist/cjs/model/ModelViewer.js +388 -63
  25. package/dist/cjs/model/ModelViewer.module.css.js +77 -5
  26. package/dist/cjs/model/NiceArmatureEditor.js +473 -274
  27. package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
  28. package/dist/cjs/model/NiceOctree.js +372 -320
  29. package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
  30. package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
  31. package/dist/cjs/model/NiceTerrainEditor.js +595 -385
  32. package/dist/cjs/model/NiceWeightPainter.js +384 -261
  33. package/dist/cjs/model/NiceXRPreview.js +385 -308
  34. package/dist/cjs/model/cadModeUtils.js +103 -106
  35. package/dist/cjs/model/editorShortcuts.js +171 -194
  36. package/dist/cjs/model/modelEditorTypes.js +100 -118
  37. package/dist/cjs/model/modelEditorUtils.js +1061 -1146
  38. package/dist/cjs/model/simsModeUtils.js +1023 -1053
  39. package/dist/cjs/model/useModelEditor.js +1977 -1933
  40. package/dist/cjs/model/useModelViewer.js +633 -665
  41. package/dist/cjs/nice2dev-ui-3d.css +1 -1
  42. package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
  43. package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
  44. package/dist/cjs/particle/particleEditorTypes.js +62 -70
  45. package/dist/cjs/particle/particleEditorUtils.js +998 -1013
  46. package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
  47. package/dist/cjs/rendering/NiceRenderExport.js +424 -337
  48. package/dist/cjs/rendering/NiceSSAO.js +454 -337
  49. package/dist/cjs/rendering/NiceSSR.js +387 -261
  50. package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
  51. package/dist/cjs/uv/NiceUVEditor.js +787 -550
  52. package/dist/cjs/uv/UVEditor.module.css.js +53 -5
  53. package/dist/cjs/uv/uvEditorTypes.js +59 -76
  54. package/dist/cjs/uv/uvEditorUtils.js +501 -575
  55. package/dist/esm/collaborative/collaborativeScene.js +163 -205
  56. package/dist/esm/core/editorAsk.js +53 -40
  57. package/dist/esm/core/i18n.js +15 -13
  58. package/dist/esm/core/threeCompat.js +18 -26
  59. package/dist/esm/dance/DanceBridge.js +151 -176
  60. package/dist/esm/dance/DanceScoreEngine.js +211 -220
  61. package/dist/esm/dance/PoseDetector.js +304 -274
  62. package/dist/esm/index.js +258 -36
  63. package/dist/esm/material/MaterialEditor.module.css.js +242 -3
  64. package/dist/esm/material/NiceMaterialEditor.js +1215 -756
  65. package/dist/esm/material/materialEditorTypes.js +45 -48
  66. package/dist/esm/material/materialEditorUtils.js +713 -746
  67. package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
  68. package/dist/esm/model/ModelEditor.js +91 -39
  69. package/dist/esm/model/ModelEditor.module.css.js +257 -3
  70. package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
  71. package/dist/esm/model/ModelEditorMenuBar.js +359 -30
  72. package/dist/esm/model/ModelEditorRightPanel.js +634 -114
  73. package/dist/esm/model/ModelEditorSubComponents.js +265 -94
  74. package/dist/esm/model/ModelEditorTimeline.js +232 -32
  75. package/dist/esm/model/ModelEditorToolbar.js +170 -12
  76. package/dist/esm/model/ModelEditorViewport.js +231 -37
  77. package/dist/esm/model/ModelViewer.js +389 -62
  78. package/dist/esm/model/ModelViewer.module.css.js +77 -3
  79. package/dist/esm/model/NiceArmatureEditor.js +462 -258
  80. package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
  81. package/dist/esm/model/NiceOctree.js +361 -304
  82. package/dist/esm/model/NicePhysicsSimulation.js +517 -282
  83. package/dist/esm/model/NiceProceduralGeometry.js +667 -284
  84. package/dist/esm/model/NiceTerrainEditor.js +584 -369
  85. package/dist/esm/model/NiceWeightPainter.js +373 -245
  86. package/dist/esm/model/NiceXRPreview.js +374 -292
  87. package/dist/esm/model/cadModeUtils.js +95 -88
  88. package/dist/esm/model/editorShortcuts.js +172 -193
  89. package/dist/esm/model/modelEditorTypes.js +90 -100
  90. package/dist/esm/model/modelEditorUtils.js +1056 -1128
  91. package/dist/esm/model/simsModeUtils.js +1021 -1035
  92. package/dist/esm/model/useModelEditor.js +1946 -1920
  93. package/dist/esm/model/useModelViewer.js +619 -647
  94. package/dist/esm/nice2dev-ui-3d.css +1 -1
  95. package/dist/esm/particle/NiceParticleEditor.js +1188 -570
  96. package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
  97. package/dist/esm/particle/particleEditorTypes.js +69 -69
  98. package/dist/esm/particle/particleEditorUtils.js +1027 -1012
  99. package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
  100. package/dist/esm/rendering/NiceRenderExport.js +413 -321
  101. package/dist/esm/rendering/NiceSSAO.js +450 -328
  102. package/dist/esm/rendering/NiceSSR.js +383 -252
  103. package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
  104. package/dist/esm/uv/NiceUVEditor.js +788 -549
  105. package/dist/esm/uv/UVEditor.module.css.js +53 -3
  106. package/dist/esm/uv/uvEditorTypes.js +68 -75
  107. package/dist/esm/uv/uvEditorUtils.js +515 -557
  108. package/package.json +7 -12
@@ -1,116 +1,636 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { NiceButton, NiceIcon, NiceTextInput, NiceColorPicker, NiceSlider, NiceCheckbox, NiceNumberInput } from '@nice2dev/ui';
3
- import React from 'react';
4
- import * as THREE from 'three';
5
- import { useNiceTranslation } from '../core/i18n.js';
6
- import styles from './ModelEditor.module.css.js';
7
- import { Vec3Row, Vec3RowDeg, MaterialCard } from './ModelEditorSubComponents.js';
8
- import { DEG } from './modelEditorTypes.js';
9
-
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { NiceButton, NiceIcon, NiceTextInput, NiceColorPicker, NiceSlider, NiceCheckbox, NiceNumberInput } from "@nice2dev/ui";
3
+ import React from "react";
4
+ import * as THREE from "three";
5
+ import { useNiceTranslation } from "../core/i18n.js";
6
+ import styles from "./ModelEditor.module.css.js";
7
+ import { Vec3Row, Vec3RowDeg, MaterialCard } from "./ModelEditorSubComponents.js";
8
+ import { DEG } from "./modelEditorTypes.js";
10
9
  const ModelEditorRightPanel = /* @__PURE__ */ Object.assign(
11
- /* @__PURE__ */ React.memo(({ api }) => {
10
+ /* @__PURE__ */ React.memo(({ api }) => {
12
11
  const { t } = useNiceTranslation();
13
- const { propTab, setPropTab, selectedNode, selectedMaterials, selMaterialIdx, setSelMaterialIdx, setMatRefresh, rebuildTree, aiBusy, aiStatus, rootObjectRef, videoInputRef, bgColor, setBgColor, fogEnabled, setFogEnabled, fogColor, setFogColor, fogNear, setFogNear, fogFar, setFogFar, showLightHelpers, setShowLightHelpers, showGrid, setShowGrid, showAxes, setShowAxes, showSkeleton, setShowSkeleton, snapEnabled, setSnapEnabled, snapGrid, setSnapGrid, setStatusText, } = api;
14
- return (jsxs("div", { className: styles.rightPanel, children: [jsx("div", { className: styles.propTabs, children: ['object', 'material', 'world', 'modifiers', 'physics'].map((t) => (jsx(NiceButton, { className: styles.propTabBtn, variant: propTab === t ? 'primary' : 'ghost', size: "sm", onClick: () => setPropTab(t), title: t.charAt(0).toUpperCase() + t.slice(1), "aria-label": t.charAt(0).toUpperCase() + t.slice(1), children: jsx(NiceIcon, { name: t === 'object'
15
- ? 'settings'
16
- : t === 'material'
17
- ? 'palette'
18
- : t === 'world'
19
- ? 'globe'
20
- : t === 'modifiers'
21
- ? 'settings'
22
- : 'zap', size: 16 }) }, t))) }), propTab === 'object' && (jsxs(Fragment, { children: [jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "settings", size: 14 }), ' ', t('modelEditorRightPanel.transform', 'Transform')] }), jsx("div", { className: styles.panelContent, children: selectedNode ? (jsxs(Fragment, { children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.name', 'Name') }), jsx(NiceTextInput, { className: styles.propInput, value: selectedNode.name, onChange: (val) => {
23
- selectedNode.object.name = val;
24
- rebuildTree();
25
- } })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.type', 'Type') }), jsx("span", { style: { fontSize: 10, color: 'var(--nice-text-secondary, #888)' }, children: selectedNode.type })] }), jsx(Vec3Row, { label: t('modelEditorRightPanel.position', 'Position'), value: selectedNode.object.position, onChange: () => setMatRefresh((n) => n + 1) }), jsx(Vec3RowDeg, { label: t('modelEditorRightPanel.rotation', 'Rotation'), value: selectedNode.object.rotation, onChange: () => setMatRefresh((n) => n + 1) }), jsx(Vec3Row, { label: t('modelEditorRightPanel.scale', 'Scale'), value: selectedNode.object.scale, onChange: () => setMatRefresh((n) => n + 1) })] })) : (jsx("div", { style: { color: 'var(--nice-text-secondary, #666)', fontSize: 10 }, children: t('modelEditorRightPanel.selectAnObject', 'Select an object') })) })] }), selectedNode && selectedNode.object instanceof THREE.Light && (jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "lightbulb", size: 14 }), ' ', t('modelEditorRightPanel.light', 'Light')] }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.color', 'Color') }), jsx(NiceColorPicker, { value: `#${selectedNode.object.color.getHexString()}`, onChange: (c) => {
26
- selectedNode.object.color.set(c);
27
- setMatRefresh((n) => n + 1);
28
- } })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.intensity', 'Intensity') }), jsx(NiceSlider, { min: 0, max: 10, step: 0.1, value: selectedNode.object.intensity, onChange: (val) => {
29
- selectedNode.object.intensity = val;
30
- setMatRefresh((n) => n + 1);
31
- }, style: { flex: 1 } }), jsx("span", { style: {
32
- fontSize: 9,
33
- color: 'var(--nice-text-secondary, #888)',
34
- width: 28,
35
- textAlign: 'right',
36
- }, children: selectedNode.object.intensity.toFixed(1) })] }), 'castShadow' in selectedNode.object && (jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.shadow', 'Shadow') }), jsx(NiceCheckbox, { checked: selectedNode.object.castShadow, onChange: (checked) => {
37
- selectedNode.object.castShadow = checked;
38
- setMatRefresh((n) => n + 1);
39
- } })] })), selectedNode.object instanceof THREE.SpotLight && (jsxs(Fragment, { children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.angle', 'Angle') }), jsx(NiceSlider, { min: 0, max: 90, step: 1, value: selectedNode.object.angle * DEG, onChange: (val) => {
40
- selectedNode.object.angle = val / DEG;
41
- setMatRefresh((n) => n + 1);
42
- }, style: { flex: 1 } }), jsxs("span", { style: {
43
- fontSize: 9,
44
- color: 'var(--nice-text-secondary, #888)',
45
- width: 28,
46
- textAlign: 'right',
47
- }, children: [(selectedNode.object.angle * DEG).toFixed(0), "\u00B0"] })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.penumbra', 'Penumbra') }), jsx(NiceSlider, { min: 0, max: 1, step: 0.01, value: selectedNode.object.penumbra, onChange: (val) => {
48
- selectedNode.object.penumbra = val;
49
- setMatRefresh((n) => n + 1);
50
- }, style: { flex: 1 } })] })] }))] })] })), selectedNode && selectedNode.object instanceof THREE.PerspectiveCamera && (jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "camera", size: 14 }), " ", t('modelEditorRightPanel.camera', 'Camera')] }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.fov', 'FOV') }), jsx(NiceSlider, { min: 10, max: 120, step: 1, value: selectedNode.object.fov, onChange: (val) => {
51
- const cam = selectedNode.object;
52
- cam.fov = val;
53
- cam.updateProjectionMatrix();
54
- setMatRefresh((n) => n + 1);
55
- }, style: { flex: 1 } }), jsxs("span", { style: {
56
- fontSize: 9,
57
- color: 'var(--nice-text-secondary, #888)',
58
- width: 28,
59
- textAlign: 'right',
60
- }, children: [selectedNode.object.fov.toFixed(0), "\u00B0"] })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.near', 'Near') }), jsx(NiceNumberInput, { className: styles.propInput, step: 0.01, value: selectedNode.object.near, onChange: (val) => {
61
- const cam = selectedNode.object;
62
- cam.near = val !== null && val !== void 0 ? val : 0.1;
63
- cam.updateProjectionMatrix();
64
- setMatRefresh((n) => n + 1);
65
- }, style: { width: 60 } })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.far', 'Far') }), jsx(NiceNumberInput, { className: styles.propInput, step: 1, value: selectedNode.object.far, onChange: (val) => {
66
- const cam = selectedNode.object;
67
- cam.far = val !== null && val !== void 0 ? val : 1000;
68
- cam.updateProjectionMatrix();
69
- setMatRefresh((n) => n + 1);
70
- }, style: { width: 80 } })] })] })] })), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "wand", size: 14 }), ' ', t('modelEditorRightPanel.aiAnimation', 'AI Animation')] }), jsx("div", { className: styles.panelContent, children: jsxs("div", { className: styles.aiPanel, children: [jsxs("div", { className: styles.aiHeader, children: [jsx(NiceIcon, { name: "wand", size: 14 }), ' ', t('modelEditorRightPanel.generateAnimationFromVideo', 'Generate animation from video')] }), jsx("p", { style: {
71
- fontSize: 10,
72
- color: 'var(--nice-text-secondary, #888)',
73
- margin: '0 0 6px',
74
- }, children: t('modelEditorRightPanel.uploadAVideoAndAi', 'Upload a video and AI will extract 3D pose data to create an animation clip for the current model.') }), jsx("div", { className: styles.aiBtnRow, children: jsx(NiceButton, { className: styles.aiBtn, variant: "ghost", size: "sm", disabled: aiBusy || !rootObjectRef.current, onClick: () => { var _a; return (_a = videoInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: aiBusy
75
- ? t('modelEditorRightPanel.processing', 'Processing...')
76
- : t('modelEditorRightPanel.uploadVideo', 'Upload Video') }) }), aiStatus && jsx("div", { className: styles.aiStatus, children: aiStatus })] }) })] })] })), propTab === 'material' && (jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "palette", size: 14 }), ' ', t('modelEditorRightPanel.materials', 'Materials')] }), jsx("div", { className: styles.panelContent, children: selectedMaterials.length > 0 ? (selectedMaterials.map((mat, i) => (jsx(MaterialCard, { material: mat, active: i === selMaterialIdx, onClick: () => setSelMaterialIdx(i), onUpdate: () => setMatRefresh((n) => n + 1) }, i)))) : (jsx("div", { style: { color: 'var(--nice-text-secondary, #666)', fontSize: 10 }, children: selectedNode
77
- ? t('modelEditorRightPanel.noMaterialsOnThisObject', 'No materials on this object')
78
- : t('modelEditorRightPanel.selectAMeshToInspect', 'Select a mesh to inspect') })) })] })), propTab === 'world' && (jsxs(Fragment, { children: [jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "globe", size: 14 }), ' ', t('modelEditorRightPanel.environment', 'Environment')] }), jsx("div", { className: styles.panelContent, children: jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.background', 'Background') }), jsx(NiceColorPicker, { value: bgColor, onChange: (c) => setBgColor(c) }), jsx("span", { style: { fontSize: 9, color: 'var(--nice-text-secondary, #888)' }, children: bgColor })] }) })] }), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "cloud", size: 14 }), " ", t('modelEditorRightPanel.fog', 'Fog')] }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.enable', 'Enable') }), jsx(NiceCheckbox, { checked: fogEnabled, onChange: () => setFogEnabled((v) => !v) })] }), fogEnabled && (jsxs(Fragment, { children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.color', 'Color') }), jsx(NiceColorPicker, { value: fogColor, onChange: (c) => setFogColor(c) })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.near', 'Near') }), jsx(NiceNumberInput, { className: styles.propInput, value: fogNear, onChange: (val) => setFogNear(val !== null && val !== void 0 ? val : 1), style: { width: 60 } })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.far', 'Far') }), jsx(NiceNumberInput, { className: styles.propInput, value: fogFar, onChange: (val) => setFogFar(val !== null && val !== void 0 ? val : 100), style: { width: 60 } })] })] }))] })] }), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "lightbulb", size: 14 }), ' ', t('modelEditorRightPanel.sceneLights', 'Scene Lights')] }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.showHelpers', 'Show Helpers') }), jsx(NiceCheckbox, { checked: showLightHelpers, onChange: () => setShowLightHelpers((v) => !v) })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.grid', 'Grid') }), jsx(NiceCheckbox, { checked: showGrid, onChange: () => setShowGrid((v) => !v) })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.axes', 'Axes') }), jsx(NiceCheckbox, { checked: showAxes, onChange: () => setShowAxes((v) => !v) })] }), jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.skeleton', 'Skeleton') }), jsx(NiceCheckbox, { checked: showSkeleton, onChange: () => setShowSkeleton((v) => !v) })] })] })] }), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "grid", size: 14 }), " ", t('modelEditorRightPanel.snapping', 'Snapping')] }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.snap', 'Snap') }), jsx(NiceCheckbox, { checked: snapEnabled, onChange: () => setSnapEnabled((v) => !v) })] }), snapEnabled && (jsxs("div", { className: styles.propRow, children: [jsx("span", { className: styles.propLabel, children: t('modelEditorRightPanel.gridSize', 'Grid Size') }), jsx(NiceNumberInput, { className: styles.propInput, step: 0.25, min: 0.1, value: snapGrid, onChange: (val) => setSnapGrid(val !== null && val !== void 0 ? val : 1), style: { width: 60 } })] }))] })] })] })), propTab === 'modifiers' && (jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "settings", size: 14 }), ' ', t('modelEditorRightPanel.modifiers', 'Modifiers')] }), jsx("div", { className: styles.panelContent, children: selectedNode && selectedNode.object instanceof THREE.Mesh ? (jsxs(Fragment, { children: [jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", size: "sm", style: { width: '100%', marginBottom: 4 }, onClick: () => {
79
- const mesh = selectedNode.object;
80
- const geo = mesh.geometry;
81
- const pos = geo.attributes.position;
82
- if (pos) {
83
- const wireGeo = new THREE.WireframeGeometry(geo);
84
- mesh.geometry = wireGeo;
85
- setStatusText(t('modelEditorRightPanel.appliedWireframeModifier', 'Applied Wireframe modifier'));
86
- rebuildTree();
87
- }
88
- }, children: t('modelEditorRightPanel.wireframeModifier', '+ Wireframe Modifier') }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", size: "sm", style: { width: '100%', marginBottom: 4 }, onClick: () => {
89
- const mesh = selectedNode.object;
90
- mesh.geometry.computeVertexNormals();
91
- setStatusText(t('modelEditorRightPanel.recomputedNormals', 'Recomputed normals'));
92
- }, children: t('modelEditorRightPanel.recomputeNormals', 'Recompute Normals') }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", size: "sm", style: { width: '100%', marginBottom: 4 }, onClick: () => {
93
- const mesh = selectedNode.object;
94
- const geo = mesh.geometry;
95
- geo.center();
96
- setStatusText(t('modelEditorRightPanel.centeredGeometry', 'Centered geometry'));
97
- setMatRefresh((n) => n + 1);
98
- }, children: t('modelEditorRightPanel.centerGeometry', 'Center Geometry') }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", size: "sm", style: { width: '100%', marginBottom: 4 }, onClick: () => {
99
- const mesh = selectedNode.object;
100
- const geo = mesh.geometry;
101
- const idx = geo.index;
102
- if (idx) {
103
- const arr = idx.array;
104
- for (let i = 0; i < arr.length; i += 3) {
105
- const tmp = arr[i];
106
- arr[i] = arr[i + 2];
107
- arr[i + 2] = tmp;
108
- }
109
- idx.needsUpdate = true;
110
- geo.computeVertexNormals();
111
- setStatusText(t('modelEditorRightPanel.flippedNormals', 'Flipped normals'));
112
- }
113
- }, children: t('modelEditorRightPanel.flipNormals', 'Flip Normals') })] })) : (jsx("div", { style: { color: 'var(--nice-text-secondary, #666)', fontSize: 10 }, children: t('modelEditorRightPanel.selectAMeshToApply', 'Select a mesh to apply modifiers') })) })] })), propTab === 'physics' && (jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx(NiceIcon, { name: "zap", size: 14 }), ' ', t('modelEditorRightPanel.physicsInfo', 'Physics (Info)')] }), jsx("div", { className: styles.panelContent, children: jsxs("div", { style: { color: 'var(--nice-text-secondary, #666)', fontSize: 10, lineHeight: 1.5 }, children: [t('modelEditorRightPanel.physicsSimulationPreviewIsNot', 'Physics simulation preview is not yet available.'), jsx("br", {}), jsx("br", {}), t('modelEditorRightPanel.plannedFeatures', 'Planned features:'), jsx("br", {}), t('modelEditorRightPanel.rigidBodySimulation', '• Rigid body simulation'), jsx("br", {}), t('modelEditorRightPanel.softBodyDynamics', '• Soft body dynamics'), jsx("br", {}), t('modelEditorRightPanel.clothSimulation', '• Cloth simulation'), jsx("br", {}), t('modelEditorRightPanel.particleSystems', '• Particle systems')] }) })] }))] }));
114
- }), { displayName: 'ModelEditorRightPanel' });
115
-
116
- export { ModelEditorRightPanel as default };
12
+ const {
13
+ propTab,
14
+ setPropTab,
15
+ selectedNode,
16
+ selectedMaterials,
17
+ selMaterialIdx,
18
+ setSelMaterialIdx,
19
+ setMatRefresh,
20
+ rebuildTree,
21
+ aiBusy,
22
+ aiStatus,
23
+ rootObjectRef,
24
+ videoInputRef,
25
+ bgColor,
26
+ setBgColor,
27
+ fogEnabled,
28
+ setFogEnabled,
29
+ fogColor,
30
+ setFogColor,
31
+ fogNear,
32
+ setFogNear,
33
+ fogFar,
34
+ setFogFar,
35
+ showLightHelpers,
36
+ setShowLightHelpers,
37
+ showGrid,
38
+ setShowGrid,
39
+ showAxes,
40
+ setShowAxes,
41
+ showSkeleton,
42
+ setShowSkeleton,
43
+ snapEnabled,
44
+ setSnapEnabled,
45
+ snapGrid,
46
+ setSnapGrid,
47
+ setStatusText
48
+ } = api;
49
+ return /* @__PURE__ */ jsxs("div", { className: styles.rightPanel, children: [
50
+ /* @__PURE__ */ jsx("div", { className: styles.propTabs, children: ["object", "material", "world", "modifiers", "physics"].map((t2) => /* @__PURE__ */ jsx(
51
+ NiceButton,
52
+ {
53
+ className: styles.propTabBtn,
54
+ variant: propTab === t2 ? "primary" : "ghost",
55
+ size: "sm",
56
+ onClick: () => setPropTab(t2),
57
+ title: t2.charAt(0).toUpperCase() + t2.slice(1),
58
+ "aria-label": t2.charAt(0).toUpperCase() + t2.slice(1),
59
+ children: /* @__PURE__ */ jsx(
60
+ NiceIcon,
61
+ {
62
+ name: t2 === "object" ? "settings" : t2 === "material" ? "palette" : t2 === "world" ? "globe" : t2 === "modifiers" ? "settings" : "zap",
63
+ size: 16
64
+ }
65
+ )
66
+ },
67
+ t2
68
+ )) }),
69
+ propTab === "object" && /* @__PURE__ */ jsxs(Fragment, { children: [
70
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
71
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
72
+ /* @__PURE__ */ jsx(NiceIcon, { name: "settings", size: 14 }),
73
+ " ",
74
+ t("modelEditorRightPanel.transform", "Transform")
75
+ ] }),
76
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: selectedNode ? /* @__PURE__ */ jsxs(Fragment, { children: [
77
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
78
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.name", "Name") }),
79
+ /* @__PURE__ */ jsx(
80
+ NiceTextInput,
81
+ {
82
+ className: styles.propInput,
83
+ value: selectedNode.name,
84
+ onChange: (val) => {
85
+ selectedNode.object.name = val;
86
+ rebuildTree();
87
+ }
88
+ }
89
+ )
90
+ ] }),
91
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
92
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.type", "Type") }),
93
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: "var(--nice-text-secondary, #888)" }, children: selectedNode.type })
94
+ ] }),
95
+ /* @__PURE__ */ jsx(
96
+ Vec3Row,
97
+ {
98
+ label: t("modelEditorRightPanel.position", "Position"),
99
+ value: selectedNode.object.position,
100
+ onChange: () => setMatRefresh((n) => n + 1)
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx(
104
+ Vec3RowDeg,
105
+ {
106
+ label: t("modelEditorRightPanel.rotation", "Rotation"),
107
+ value: selectedNode.object.rotation,
108
+ onChange: () => setMatRefresh((n) => n + 1)
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsx(
112
+ Vec3Row,
113
+ {
114
+ label: t("modelEditorRightPanel.scale", "Scale"),
115
+ value: selectedNode.object.scale,
116
+ onChange: () => setMatRefresh((n) => n + 1)
117
+ }
118
+ )
119
+ ] }) : /* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #666)", fontSize: 10 }, children: t("modelEditorRightPanel.selectAnObject", "Select an object") }) })
120
+ ] }),
121
+ selectedNode && selectedNode.object instanceof THREE.Light && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
122
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
123
+ /* @__PURE__ */ jsx(NiceIcon, { name: "lightbulb", size: 14 }),
124
+ " ",
125
+ t("modelEditorRightPanel.light", "Light")
126
+ ] }),
127
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
128
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
129
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.color", "Color") }),
130
+ /* @__PURE__ */ jsx(
131
+ NiceColorPicker,
132
+ {
133
+ value: `#${selectedNode.object.color.getHexString()}`,
134
+ onChange: (c) => {
135
+ selectedNode.object.color.set(c);
136
+ setMatRefresh((n) => n + 1);
137
+ }
138
+ }
139
+ )
140
+ ] }),
141
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
142
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.intensity", "Intensity") }),
143
+ /* @__PURE__ */ jsx(
144
+ NiceSlider,
145
+ {
146
+ min: 0,
147
+ max: 10,
148
+ step: 0.1,
149
+ value: selectedNode.object.intensity,
150
+ onChange: (val) => {
151
+ selectedNode.object.intensity = val;
152
+ setMatRefresh((n) => n + 1);
153
+ },
154
+ style: { flex: 1 }
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsx(
158
+ "span",
159
+ {
160
+ style: {
161
+ fontSize: 9,
162
+ color: "var(--nice-text-secondary, #888)",
163
+ width: 28,
164
+ textAlign: "right"
165
+ },
166
+ children: selectedNode.object.intensity.toFixed(1)
167
+ }
168
+ )
169
+ ] }),
170
+ "castShadow" in selectedNode.object && /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
171
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.shadow", "Shadow") }),
172
+ /* @__PURE__ */ jsx(
173
+ NiceCheckbox,
174
+ {
175
+ checked: selectedNode.object.castShadow,
176
+ onChange: (checked) => {
177
+ selectedNode.object.castShadow = checked;
178
+ setMatRefresh((n) => n + 1);
179
+ }
180
+ }
181
+ )
182
+ ] }),
183
+ selectedNode.object instanceof THREE.SpotLight && /* @__PURE__ */ jsxs(Fragment, { children: [
184
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
185
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.angle", "Angle") }),
186
+ /* @__PURE__ */ jsx(
187
+ NiceSlider,
188
+ {
189
+ min: 0,
190
+ max: 90,
191
+ step: 1,
192
+ value: selectedNode.object.angle * DEG,
193
+ onChange: (val) => {
194
+ selectedNode.object.angle = val / DEG;
195
+ setMatRefresh((n) => n + 1);
196
+ },
197
+ style: { flex: 1 }
198
+ }
199
+ ),
200
+ /* @__PURE__ */ jsxs(
201
+ "span",
202
+ {
203
+ style: {
204
+ fontSize: 9,
205
+ color: "var(--nice-text-secondary, #888)",
206
+ width: 28,
207
+ textAlign: "right"
208
+ },
209
+ children: [
210
+ (selectedNode.object.angle * DEG).toFixed(0),
211
+ "°"
212
+ ]
213
+ }
214
+ )
215
+ ] }),
216
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
217
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.penumbra", "Penumbra") }),
218
+ /* @__PURE__ */ jsx(
219
+ NiceSlider,
220
+ {
221
+ min: 0,
222
+ max: 1,
223
+ step: 0.01,
224
+ value: selectedNode.object.penumbra,
225
+ onChange: (val) => {
226
+ selectedNode.object.penumbra = val;
227
+ setMatRefresh((n) => n + 1);
228
+ },
229
+ style: { flex: 1 }
230
+ }
231
+ )
232
+ ] })
233
+ ] })
234
+ ] })
235
+ ] }),
236
+ selectedNode && selectedNode.object instanceof THREE.PerspectiveCamera && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
237
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
238
+ /* @__PURE__ */ jsx(NiceIcon, { name: "camera", size: 14 }),
239
+ " ",
240
+ t("modelEditorRightPanel.camera", "Camera")
241
+ ] }),
242
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
243
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
244
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.fov", "FOV") }),
245
+ /* @__PURE__ */ jsx(
246
+ NiceSlider,
247
+ {
248
+ min: 10,
249
+ max: 120,
250
+ step: 1,
251
+ value: selectedNode.object.fov,
252
+ onChange: (val) => {
253
+ const cam = selectedNode.object;
254
+ cam.fov = val;
255
+ cam.updateProjectionMatrix();
256
+ setMatRefresh((n) => n + 1);
257
+ },
258
+ style: { flex: 1 }
259
+ }
260
+ ),
261
+ /* @__PURE__ */ jsxs(
262
+ "span",
263
+ {
264
+ style: {
265
+ fontSize: 9,
266
+ color: "var(--nice-text-secondary, #888)",
267
+ width: 28,
268
+ textAlign: "right"
269
+ },
270
+ children: [
271
+ selectedNode.object.fov.toFixed(0),
272
+ "°"
273
+ ]
274
+ }
275
+ )
276
+ ] }),
277
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
278
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.near", "Near") }),
279
+ /* @__PURE__ */ jsx(
280
+ NiceNumberInput,
281
+ {
282
+ className: styles.propInput,
283
+ step: 0.01,
284
+ value: selectedNode.object.near,
285
+ onChange: (val) => {
286
+ const cam = selectedNode.object;
287
+ cam.near = val ?? 0.1;
288
+ cam.updateProjectionMatrix();
289
+ setMatRefresh((n) => n + 1);
290
+ },
291
+ style: { width: 60 }
292
+ }
293
+ )
294
+ ] }),
295
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
296
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.far", "Far") }),
297
+ /* @__PURE__ */ jsx(
298
+ NiceNumberInput,
299
+ {
300
+ className: styles.propInput,
301
+ step: 1,
302
+ value: selectedNode.object.far,
303
+ onChange: (val) => {
304
+ const cam = selectedNode.object;
305
+ cam.far = val ?? 1e3;
306
+ cam.updateProjectionMatrix();
307
+ setMatRefresh((n) => n + 1);
308
+ },
309
+ style: { width: 80 }
310
+ }
311
+ )
312
+ ] })
313
+ ] })
314
+ ] }),
315
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
316
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
317
+ /* @__PURE__ */ jsx(NiceIcon, { name: "wand", size: 14 }),
318
+ " ",
319
+ t("modelEditorRightPanel.aiAnimation", "AI Animation")
320
+ ] }),
321
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: /* @__PURE__ */ jsxs("div", { className: styles.aiPanel, children: [
322
+ /* @__PURE__ */ jsxs("div", { className: styles.aiHeader, children: [
323
+ /* @__PURE__ */ jsx(NiceIcon, { name: "wand", size: 14 }),
324
+ " ",
325
+ t(
326
+ "modelEditorRightPanel.generateAnimationFromVideo",
327
+ "Generate animation from video"
328
+ )
329
+ ] }),
330
+ /* @__PURE__ */ jsx(
331
+ "p",
332
+ {
333
+ style: {
334
+ fontSize: 10,
335
+ color: "var(--nice-text-secondary, #888)",
336
+ margin: "0 0 6px"
337
+ },
338
+ children: t(
339
+ "modelEditorRightPanel.uploadAVideoAndAi",
340
+ "Upload a video and AI will extract 3D pose data to create an animation clip for the current model."
341
+ )
342
+ }
343
+ ),
344
+ /* @__PURE__ */ jsx("div", { className: styles.aiBtnRow, children: /* @__PURE__ */ jsx(
345
+ NiceButton,
346
+ {
347
+ className: styles.aiBtn,
348
+ variant: "ghost",
349
+ size: "sm",
350
+ disabled: aiBusy || !rootObjectRef.current,
351
+ onClick: () => videoInputRef.current?.click(),
352
+ children: aiBusy ? t("modelEditorRightPanel.processing", "Processing...") : t("modelEditorRightPanel.uploadVideo", "Upload Video")
353
+ }
354
+ ) }),
355
+ aiStatus && /* @__PURE__ */ jsx("div", { className: styles.aiStatus, children: aiStatus })
356
+ ] }) })
357
+ ] })
358
+ ] }),
359
+ propTab === "material" && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
360
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
361
+ /* @__PURE__ */ jsx(NiceIcon, { name: "palette", size: 14 }),
362
+ " ",
363
+ t("modelEditorRightPanel.materials", "Materials")
364
+ ] }),
365
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: selectedMaterials.length > 0 ? selectedMaterials.map((mat, i) => /* @__PURE__ */ jsx(
366
+ MaterialCard,
367
+ {
368
+ material: mat,
369
+ active: i === selMaterialIdx,
370
+ onClick: () => setSelMaterialIdx(i),
371
+ onUpdate: () => setMatRefresh((n) => n + 1)
372
+ },
373
+ i
374
+ )) : /* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #666)", fontSize: 10 }, children: selectedNode ? t(
375
+ "modelEditorRightPanel.noMaterialsOnThisObject",
376
+ "No materials on this object"
377
+ ) : t("modelEditorRightPanel.selectAMeshToInspect", "Select a mesh to inspect") }) })
378
+ ] }),
379
+ propTab === "world" && /* @__PURE__ */ jsxs(Fragment, { children: [
380
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
381
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
382
+ /* @__PURE__ */ jsx(NiceIcon, { name: "globe", size: 14 }),
383
+ " ",
384
+ t("modelEditorRightPanel.environment", "Environment")
385
+ ] }),
386
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
387
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.background", "Background") }),
388
+ /* @__PURE__ */ jsx(NiceColorPicker, { value: bgColor, onChange: (c) => setBgColor(c) }),
389
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 9, color: "var(--nice-text-secondary, #888)" }, children: bgColor })
390
+ ] }) })
391
+ ] }),
392
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
393
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
394
+ /* @__PURE__ */ jsx(NiceIcon, { name: "cloud", size: 14 }),
395
+ " ",
396
+ t("modelEditorRightPanel.fog", "Fog")
397
+ ] }),
398
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
399
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
400
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.enable", "Enable") }),
401
+ /* @__PURE__ */ jsx(NiceCheckbox, { checked: fogEnabled, onChange: () => setFogEnabled((v) => !v) })
402
+ ] }),
403
+ fogEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
404
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
405
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.color", "Color") }),
406
+ /* @__PURE__ */ jsx(NiceColorPicker, { value: fogColor, onChange: (c) => setFogColor(c) })
407
+ ] }),
408
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
409
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.near", "Near") }),
410
+ /* @__PURE__ */ jsx(
411
+ NiceNumberInput,
412
+ {
413
+ className: styles.propInput,
414
+ value: fogNear,
415
+ onChange: (val) => setFogNear(val ?? 1),
416
+ style: { width: 60 }
417
+ }
418
+ )
419
+ ] }),
420
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
421
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.far", "Far") }),
422
+ /* @__PURE__ */ jsx(
423
+ NiceNumberInput,
424
+ {
425
+ className: styles.propInput,
426
+ value: fogFar,
427
+ onChange: (val) => setFogFar(val ?? 100),
428
+ style: { width: 60 }
429
+ }
430
+ )
431
+ ] })
432
+ ] })
433
+ ] })
434
+ ] }),
435
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
436
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
437
+ /* @__PURE__ */ jsx(NiceIcon, { name: "lightbulb", size: 14 }),
438
+ " ",
439
+ t("modelEditorRightPanel.sceneLights", "Scene Lights")
440
+ ] }),
441
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
442
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
443
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.showHelpers", "Show Helpers") }),
444
+ /* @__PURE__ */ jsx(
445
+ NiceCheckbox,
446
+ {
447
+ checked: showLightHelpers,
448
+ onChange: () => setShowLightHelpers((v) => !v)
449
+ }
450
+ )
451
+ ] }),
452
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
453
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.grid", "Grid") }),
454
+ /* @__PURE__ */ jsx(NiceCheckbox, { checked: showGrid, onChange: () => setShowGrid((v) => !v) })
455
+ ] }),
456
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
457
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.axes", "Axes") }),
458
+ /* @__PURE__ */ jsx(NiceCheckbox, { checked: showAxes, onChange: () => setShowAxes((v) => !v) })
459
+ ] }),
460
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
461
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.skeleton", "Skeleton") }),
462
+ /* @__PURE__ */ jsx(
463
+ NiceCheckbox,
464
+ {
465
+ checked: showSkeleton,
466
+ onChange: () => setShowSkeleton((v) => !v)
467
+ }
468
+ )
469
+ ] })
470
+ ] })
471
+ ] }),
472
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
473
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
474
+ /* @__PURE__ */ jsx(NiceIcon, { name: "grid", size: 14 }),
475
+ " ",
476
+ t("modelEditorRightPanel.snapping", "Snapping")
477
+ ] }),
478
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
479
+ /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
480
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.snap", "Snap") }),
481
+ /* @__PURE__ */ jsx(NiceCheckbox, { checked: snapEnabled, onChange: () => setSnapEnabled((v) => !v) })
482
+ ] }),
483
+ snapEnabled && /* @__PURE__ */ jsxs("div", { className: styles.propRow, children: [
484
+ /* @__PURE__ */ jsx("span", { className: styles.propLabel, children: t("modelEditorRightPanel.gridSize", "Grid Size") }),
485
+ /* @__PURE__ */ jsx(
486
+ NiceNumberInput,
487
+ {
488
+ className: styles.propInput,
489
+ step: 0.25,
490
+ min: 0.1,
491
+ value: snapGrid,
492
+ onChange: (val) => setSnapGrid(val ?? 1),
493
+ style: { width: 60 }
494
+ }
495
+ )
496
+ ] })
497
+ ] })
498
+ ] })
499
+ ] }),
500
+ propTab === "modifiers" && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
501
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
502
+ /* @__PURE__ */ jsx(NiceIcon, { name: "settings", size: 14 }),
503
+ " ",
504
+ t("modelEditorRightPanel.modifiers", "Modifiers")
505
+ ] }),
506
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: selectedNode && selectedNode.object instanceof THREE.Mesh ? /* @__PURE__ */ jsxs(Fragment, { children: [
507
+ /* @__PURE__ */ jsx(
508
+ NiceButton,
509
+ {
510
+ className: styles.btnSecondary,
511
+ variant: "ghost",
512
+ size: "sm",
513
+ style: { width: "100%", marginBottom: 4 },
514
+ onClick: () => {
515
+ const mesh = selectedNode.object;
516
+ const geo = mesh.geometry;
517
+ const pos = geo.attributes.position;
518
+ if (pos) {
519
+ const wireGeo = new THREE.WireframeGeometry(geo);
520
+ mesh.geometry = wireGeo;
521
+ setStatusText(
522
+ t(
523
+ "modelEditorRightPanel.appliedWireframeModifier",
524
+ "Applied Wireframe modifier"
525
+ )
526
+ );
527
+ rebuildTree();
528
+ }
529
+ },
530
+ children: t("modelEditorRightPanel.wireframeModifier", "+ Wireframe Modifier")
531
+ }
532
+ ),
533
+ /* @__PURE__ */ jsx(
534
+ NiceButton,
535
+ {
536
+ className: styles.btnSecondary,
537
+ variant: "ghost",
538
+ size: "sm",
539
+ style: { width: "100%", marginBottom: 4 },
540
+ onClick: () => {
541
+ const mesh = selectedNode.object;
542
+ mesh.geometry.computeVertexNormals();
543
+ setStatusText(
544
+ t("modelEditorRightPanel.recomputedNormals", "Recomputed normals")
545
+ );
546
+ },
547
+ children: t("modelEditorRightPanel.recomputeNormals", "Recompute Normals")
548
+ }
549
+ ),
550
+ /* @__PURE__ */ jsx(
551
+ NiceButton,
552
+ {
553
+ className: styles.btnSecondary,
554
+ variant: "ghost",
555
+ size: "sm",
556
+ style: { width: "100%", marginBottom: 4 },
557
+ onClick: () => {
558
+ const mesh = selectedNode.object;
559
+ const geo = mesh.geometry;
560
+ geo.center();
561
+ setStatusText(
562
+ t("modelEditorRightPanel.centeredGeometry", "Centered geometry")
563
+ );
564
+ setMatRefresh((n) => n + 1);
565
+ },
566
+ children: t("modelEditorRightPanel.centerGeometry", "Center Geometry")
567
+ }
568
+ ),
569
+ /* @__PURE__ */ jsx(
570
+ NiceButton,
571
+ {
572
+ className: styles.btnSecondary,
573
+ variant: "ghost",
574
+ size: "sm",
575
+ style: { width: "100%", marginBottom: 4 },
576
+ onClick: () => {
577
+ const mesh = selectedNode.object;
578
+ const geo = mesh.geometry;
579
+ const idx = geo.index;
580
+ if (idx) {
581
+ const arr = idx.array;
582
+ for (let i = 0; i < arr.length; i += 3) {
583
+ const tmp = arr[i];
584
+ arr[i] = arr[i + 2];
585
+ arr[i + 2] = tmp;
586
+ }
587
+ idx.needsUpdate = true;
588
+ geo.computeVertexNormals();
589
+ setStatusText(t("modelEditorRightPanel.flippedNormals", "Flipped normals"));
590
+ }
591
+ },
592
+ children: t("modelEditorRightPanel.flipNormals", "Flip Normals")
593
+ }
594
+ )
595
+ ] }) : /* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #666)", fontSize: 10 }, children: t(
596
+ "modelEditorRightPanel.selectAMeshToApply",
597
+ "Select a mesh to apply modifiers"
598
+ ) }) })
599
+ ] }),
600
+ propTab === "physics" && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
601
+ /* @__PURE__ */ jsxs("div", { className: styles.panelTitle, children: [
602
+ /* @__PURE__ */ jsx(NiceIcon, { name: "zap", size: 14 }),
603
+ " ",
604
+ t("modelEditorRightPanel.physicsInfo", "Physics (Info)")
605
+ ] }),
606
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: /* @__PURE__ */ jsxs(
607
+ "div",
608
+ {
609
+ style: { color: "var(--nice-text-secondary, #666)", fontSize: 10, lineHeight: 1.5 },
610
+ children: [
611
+ t(
612
+ "modelEditorRightPanel.physicsSimulationPreviewIsNot",
613
+ "Physics simulation preview is not yet available."
614
+ ),
615
+ /* @__PURE__ */ jsx("br", {}),
616
+ /* @__PURE__ */ jsx("br", {}),
617
+ t("modelEditorRightPanel.plannedFeatures", "Planned features:"),
618
+ /* @__PURE__ */ jsx("br", {}),
619
+ t("modelEditorRightPanel.rigidBodySimulation", "• Rigid body simulation"),
620
+ /* @__PURE__ */ jsx("br", {}),
621
+ t("modelEditorRightPanel.softBodyDynamics", "• Soft body dynamics"),
622
+ /* @__PURE__ */ jsx("br", {}),
623
+ t("modelEditorRightPanel.clothSimulation", "• Cloth simulation"),
624
+ /* @__PURE__ */ jsx("br", {}),
625
+ t("modelEditorRightPanel.particleSystems", "• Particle systems")
626
+ ]
627
+ }
628
+ ) })
629
+ ] })
630
+ ] });
631
+ }),
632
+ { displayName: "ModelEditorRightPanel" }
633
+ );
634
+ export {
635
+ ModelEditorRightPanel as default
636
+ };