@nice2dev/ui-3d 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/i18n.js +3 -3
- package/dist/cjs/dance/DanceBridge.js +13 -13
- package/dist/cjs/dance/DanceScoreEngine.js +8 -8
- package/dist/cjs/dance/PoseDetector.js +20 -20
- package/dist/cjs/material/NiceMaterialEditor.js +19 -19
- package/dist/cjs/model/ModelEditorLeftPanel.js +3 -3
- package/dist/cjs/model/ModelEditorMenuBar.js +2 -2
- package/dist/cjs/model/ModelEditorRightPanel.js +2 -2
- package/dist/cjs/model/ModelEditorSubComponents.js +3 -3
- package/dist/cjs/model/ModelEditorTimeline.js +3 -3
- package/dist/cjs/model/ModelEditorToolbar.js +2 -2
- package/dist/cjs/model/ModelEditorViewport.js +2 -2
- package/dist/cjs/model/ModelViewer.js +8 -8
- package/dist/cjs/model/NiceArmatureEditor.js +18 -18
- package/dist/cjs/model/NiceMorphTargetEditor.js +18 -18
- package/dist/cjs/model/NiceOctree.js +16 -16
- package/dist/cjs/model/NicePhysicsSimulation.js +24 -24
- package/dist/cjs/model/NiceProceduralGeometry.js +8 -8
- package/dist/cjs/model/NiceTerrainEditor.js +19 -19
- package/dist/cjs/model/NiceWeightPainter.js +14 -14
- package/dist/cjs/model/NiceXRPreview.js +18 -18
- package/dist/cjs/model/useModelEditor.js +127 -127
- package/dist/cjs/model/useModelViewer.js +61 -61
- package/dist/cjs/particle/NiceParticleEditor.js +11 -11
- package/dist/cjs/rendering/NiceCascadedShadows.js +12 -12
- package/dist/cjs/rendering/NiceRenderExport.js +5 -5
- package/dist/cjs/rendering/NiceSSAO.js +18 -18
- package/dist/cjs/rendering/NiceSSR.js +14 -14
- package/dist/cjs/rendering/NiceWebGPURenderer.js +21 -21
- package/dist/cjs/ui/dist/index.js +32608 -24033
- package/dist/cjs/ui/dist/index.js.map +1 -1
- package/dist/cjs/uv/NiceUVEditor.js +24 -24
- package/dist/esm/model/ModelEditorLeftPanel.js +5 -5
- package/dist/esm/model/ModelEditorMenuBar.js +5 -5
- package/dist/esm/model/ModelEditorRightPanel.js +17 -17
- package/dist/esm/model/ModelEditorSubComponents.js +6 -6
- package/dist/esm/model/ModelEditorTimeline.js +5 -5
- package/dist/esm/model/ModelEditorToolbar.js +4 -4
- package/dist/esm/model/ModelEditorViewport.js +2 -2
- package/dist/esm/model/ModelViewer.js +5 -5
- package/dist/esm/ui/dist/index.js +31739 -23166
- package/dist/esm/ui/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var Ue = require('react');
|
|
4
4
|
var THREE = require('three');
|
|
5
5
|
var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
|
|
6
6
|
var TransformControls_js = require('three/examples/jsm/controls/TransformControls.js');
|
|
@@ -72,82 +72,82 @@ function findNodeById(nodes, id) {
|
|
|
72
72
|
═══════════════════════════════════════════ */
|
|
73
73
|
function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
74
74
|
/* ── refs ── */
|
|
75
|
-
const mountRef =
|
|
76
|
-
const rendererRef =
|
|
77
|
-
const sceneRef =
|
|
78
|
-
const cameraRef =
|
|
79
|
-
const orbitRef =
|
|
80
|
-
const transformRef =
|
|
81
|
-
const clockRef =
|
|
82
|
-
const mixerRef =
|
|
83
|
-
const rafRef =
|
|
84
|
-
const fileInputRef =
|
|
85
|
-
const mergeInputRef =
|
|
86
|
-
const videoInputRef =
|
|
87
|
-
const raycasterRef =
|
|
88
|
-
const skeletonHelperRef =
|
|
75
|
+
const mountRef = Ue.useRef(null);
|
|
76
|
+
const rendererRef = Ue.useRef(null);
|
|
77
|
+
const sceneRef = Ue.useRef(new THREE__namespace.Scene());
|
|
78
|
+
const cameraRef = Ue.useRef(new THREE__namespace.PerspectiveCamera(50, 1, 0.01, 10000));
|
|
79
|
+
const orbitRef = Ue.useRef(null);
|
|
80
|
+
const transformRef = Ue.useRef(null);
|
|
81
|
+
const clockRef = Ue.useRef(new THREE__namespace.Clock());
|
|
82
|
+
const mixerRef = Ue.useRef(null);
|
|
83
|
+
const rafRef = Ue.useRef(0);
|
|
84
|
+
const fileInputRef = Ue.useRef(null);
|
|
85
|
+
const mergeInputRef = Ue.useRef(null);
|
|
86
|
+
const videoInputRef = Ue.useRef(null);
|
|
87
|
+
const raycasterRef = Ue.useRef(new THREE__namespace.Raycaster());
|
|
88
|
+
const skeletonHelperRef = Ue.useRef(null);
|
|
89
89
|
/* ── state ── */
|
|
90
|
-
const [sceneTree, setSceneTree] =
|
|
91
|
-
const [selectedId, setSelectedId] =
|
|
92
|
-
const [transformMode, setTransformMode] =
|
|
93
|
-
const [animations, setAnimations] =
|
|
94
|
-
const [activeAnimIdx, setActiveAnimIdx] =
|
|
95
|
-
const [isPlaying, setIsPlaying] =
|
|
96
|
-
const [animTime, setAnimTime] =
|
|
97
|
-
const [animDuration, setAnimDuration] =
|
|
98
|
-
const [animSpeed, setAnimSpeed] =
|
|
99
|
-
const [loopAnim, setLoopAnim] =
|
|
100
|
-
const [bottomCollapsed, setBottomCollapsed] =
|
|
101
|
-
const [showGrid, setShowGrid] =
|
|
102
|
-
const [showAxes, setShowAxes] =
|
|
103
|
-
const [wireframe, setWireframe] =
|
|
104
|
-
const [bgColor, setBgColor] =
|
|
105
|
-
const [statusText, setStatusText] =
|
|
106
|
-
const [dragOver, setDragOver] =
|
|
107
|
-
const [polyCount, setPolyCount] =
|
|
108
|
-
const [meshCount, setMeshCount] =
|
|
109
|
-
const [boneCount, setBoneCount] =
|
|
90
|
+
const [sceneTree, setSceneTree] = Ue.useState([]);
|
|
91
|
+
const [selectedId, setSelectedId] = Ue.useState(null);
|
|
92
|
+
const [transformMode, setTransformMode] = Ue.useState("translate");
|
|
93
|
+
const [animations, setAnimations] = Ue.useState([]);
|
|
94
|
+
const [activeAnimIdx, setActiveAnimIdx] = Ue.useState(-1);
|
|
95
|
+
const [isPlaying, setIsPlaying] = Ue.useState(false);
|
|
96
|
+
const [animTime, setAnimTime] = Ue.useState(0);
|
|
97
|
+
const [animDuration, setAnimDuration] = Ue.useState(0);
|
|
98
|
+
const [animSpeed, setAnimSpeed] = Ue.useState(1);
|
|
99
|
+
const [loopAnim, setLoopAnim] = Ue.useState(true);
|
|
100
|
+
const [bottomCollapsed, setBottomCollapsed] = Ue.useState(false);
|
|
101
|
+
const [showGrid, setShowGrid] = Ue.useState(true);
|
|
102
|
+
const [showAxes, setShowAxes] = Ue.useState(true);
|
|
103
|
+
const [wireframe, setWireframe] = Ue.useState(false);
|
|
104
|
+
const [bgColor, setBgColor] = Ue.useState("#111122");
|
|
105
|
+
const [statusText, setStatusText] = Ue.useState("Ready");
|
|
106
|
+
const [dragOver, setDragOver] = Ue.useState(false);
|
|
107
|
+
const [polyCount, setPolyCount] = Ue.useState(0);
|
|
108
|
+
const [meshCount, setMeshCount] = Ue.useState(0);
|
|
109
|
+
const [boneCount, setBoneCount] = Ue.useState(0);
|
|
110
110
|
// Material editor
|
|
111
|
-
const [selMaterialIdx, setSelMaterialIdx] =
|
|
112
|
-
const [matRefresh, setMatRefresh] =
|
|
111
|
+
const [selMaterialIdx, setSelMaterialIdx] = Ue.useState(0);
|
|
112
|
+
const [matRefresh, setMatRefresh] = Ue.useState(0);
|
|
113
113
|
// AI panel
|
|
114
|
-
const [aiStatus, setAiStatus] =
|
|
115
|
-
const [aiBusy, setAiBusy] =
|
|
114
|
+
const [aiStatus, setAiStatus] = Ue.useState("");
|
|
115
|
+
const [aiBusy, setAiBusy] = Ue.useState(false);
|
|
116
116
|
// Blender 2.0 — viewport & editor
|
|
117
|
-
const [shadingMode, setShadingMode] =
|
|
118
|
-
const [editorMode, setEditorMode] =
|
|
119
|
-
const [contextMenu, setContextMenu] =
|
|
120
|
-
const [outlinerSearch, setOutlinerSearch] =
|
|
121
|
-
const [snapEnabled, setSnapEnabled] =
|
|
122
|
-
const [snapGrid, setSnapGrid] =
|
|
123
|
-
const [showSkeleton, setShowSkeleton] =
|
|
124
|
-
const [addMenuOpen, setAddMenuOpen] =
|
|
125
|
-
const [gizmoSpace, setGizmoSpace] =
|
|
126
|
-
const [propTab, setPropTab] =
|
|
127
|
-
const [fogEnabled, setFogEnabled] =
|
|
128
|
-
const [fogColor, setFogColor] =
|
|
129
|
-
const [fogNear, setFogNear] =
|
|
130
|
-
const [fogFar, setFogFar] =
|
|
131
|
-
const [showLightHelpers, setShowLightHelpers] =
|
|
117
|
+
const [shadingMode, setShadingMode] = Ue.useState("solid");
|
|
118
|
+
const [editorMode, setEditorMode] = Ue.useState("object");
|
|
119
|
+
const [contextMenu, setContextMenu] = Ue.useState(null);
|
|
120
|
+
const [outlinerSearch, setOutlinerSearch] = Ue.useState("");
|
|
121
|
+
const [snapEnabled, setSnapEnabled] = Ue.useState(false);
|
|
122
|
+
const [snapGrid, setSnapGrid] = Ue.useState(1);
|
|
123
|
+
const [showSkeleton, setShowSkeleton] = Ue.useState(true);
|
|
124
|
+
const [addMenuOpen, setAddMenuOpen] = Ue.useState(null);
|
|
125
|
+
const [gizmoSpace, setGizmoSpace] = Ue.useState("world");
|
|
126
|
+
const [propTab, setPropTab] = Ue.useState("object");
|
|
127
|
+
const [fogEnabled, setFogEnabled] = Ue.useState(false);
|
|
128
|
+
const [fogColor, setFogColor] = Ue.useState("#111122");
|
|
129
|
+
const [fogNear, setFogNear] = Ue.useState(10);
|
|
130
|
+
const [fogFar, setFogFar] = Ue.useState(100);
|
|
131
|
+
const [showLightHelpers, setShowLightHelpers] = Ue.useState(true);
|
|
132
132
|
// The root object that was loaded (mesh/character)
|
|
133
|
-
const rootObjectRef =
|
|
133
|
+
const rootObjectRef = Ue.useRef(null);
|
|
134
134
|
// Active animation action
|
|
135
|
-
const activeActionRef =
|
|
135
|
+
const activeActionRef = Ue.useRef(null);
|
|
136
136
|
// All loaded animation clips (for the current model)
|
|
137
|
-
const allClipsRef =
|
|
137
|
+
const allClipsRef = Ue.useRef([]);
|
|
138
138
|
/* keep refs current */
|
|
139
|
-
const animTimeRef =
|
|
139
|
+
const animTimeRef = Ue.useRef(animTime);
|
|
140
140
|
animTimeRef.current = animTime;
|
|
141
|
-
const isPlayingRef =
|
|
141
|
+
const isPlayingRef = Ue.useRef(isPlaying);
|
|
142
142
|
isPlayingRef.current = isPlaying;
|
|
143
|
-
const animSpeedRef =
|
|
143
|
+
const animSpeedRef = Ue.useRef(animSpeed);
|
|
144
144
|
animSpeedRef.current = animSpeed;
|
|
145
|
-
const loopAnimRef =
|
|
145
|
+
const loopAnimRef = Ue.useRef(loopAnim);
|
|
146
146
|
loopAnimRef.current = loopAnim;
|
|
147
147
|
/* ─────────────────────────────────────────
|
|
148
148
|
Scene setup
|
|
149
149
|
───────────────────────────────────────── */
|
|
150
|
-
|
|
150
|
+
Ue.useEffect(() => {
|
|
151
151
|
const container = mountRef.current;
|
|
152
152
|
if (!container)
|
|
153
153
|
return;
|
|
@@ -280,7 +280,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
280
280
|
/* ─────────────────────────────────────────
|
|
281
281
|
Rebuild scene tree from Three.js scene
|
|
282
282
|
───────────────────────────────────────── */
|
|
283
|
-
const rebuildTree =
|
|
283
|
+
const rebuildTree = Ue.useCallback(() => {
|
|
284
284
|
const scene = sceneRef.current;
|
|
285
285
|
let polys = 0;
|
|
286
286
|
let meshes = 0;
|
|
@@ -347,9 +347,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
347
347
|
/* ─────────────────────────────────────────
|
|
348
348
|
File loading
|
|
349
349
|
───────────────────────────────────────── */
|
|
350
|
-
const playClipRef =
|
|
351
|
-
const focusOnObjectRef =
|
|
352
|
-
const addObjectToScene =
|
|
350
|
+
const playClipRef = Ue.useRef(() => { });
|
|
351
|
+
const focusOnObjectRef = Ue.useRef(() => { });
|
|
352
|
+
const addObjectToScene = Ue.useCallback((obj, clips, filename) => {
|
|
353
353
|
const scene = sceneRef.current;
|
|
354
354
|
obj.traverse((child) => {
|
|
355
355
|
if (child instanceof THREE__namespace.Mesh) {
|
|
@@ -396,7 +396,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
396
396
|
rebuildTree();
|
|
397
397
|
setStatusText(`Loaded: ${filename} (${clips.length} animation(s))`);
|
|
398
398
|
}, [rebuildTree]);
|
|
399
|
-
const loadFile =
|
|
399
|
+
const loadFile = Ue.useCallback(async (file) => {
|
|
400
400
|
var _a, _b, _c, _d, _e, _f;
|
|
401
401
|
const name = file.name.toLowerCase();
|
|
402
402
|
const ext = "." + name.split(".").pop();
|
|
@@ -515,7 +515,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
515
515
|
URL.revokeObjectURL(url);
|
|
516
516
|
}
|
|
517
517
|
}, [addObjectToScene]);
|
|
518
|
-
const mergeAnimationFBX =
|
|
518
|
+
const mergeAnimationFBX = Ue.useCallback(async (file) => {
|
|
519
519
|
var _a;
|
|
520
520
|
const url = URL.createObjectURL(file);
|
|
521
521
|
setStatusText(`Merging animations from ${file.name}...`);
|
|
@@ -556,7 +556,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
556
556
|
/* ─────────────────────────────────────────
|
|
557
557
|
Animation controls
|
|
558
558
|
───────────────────────────────────────── */
|
|
559
|
-
const playClip =
|
|
559
|
+
const playClip = Ue.useCallback((index) => {
|
|
560
560
|
const mixer = mixerRef.current;
|
|
561
561
|
if (!mixer || index < 0 || index >= allClipsRef.current.length)
|
|
562
562
|
return;
|
|
@@ -576,7 +576,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
576
576
|
setIsPlaying(true);
|
|
577
577
|
}, []);
|
|
578
578
|
playClipRef.current = playClip;
|
|
579
|
-
const togglePlay =
|
|
579
|
+
const togglePlay = Ue.useCallback(() => {
|
|
580
580
|
if (activeAnimIdx < 0 && allClipsRef.current.length > 0) {
|
|
581
581
|
playClip(0);
|
|
582
582
|
return;
|
|
@@ -593,7 +593,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
593
593
|
setIsPlaying(true);
|
|
594
594
|
}
|
|
595
595
|
}, [activeAnimIdx, playClip]);
|
|
596
|
-
const stopAnim =
|
|
596
|
+
const stopAnim = Ue.useCallback(() => {
|
|
597
597
|
const action = activeActionRef.current;
|
|
598
598
|
if (action) {
|
|
599
599
|
action.stop();
|
|
@@ -602,7 +602,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
602
602
|
setIsPlaying(false);
|
|
603
603
|
setAnimTime(0);
|
|
604
604
|
}, []);
|
|
605
|
-
const seekAnim =
|
|
605
|
+
const seekAnim = Ue.useCallback((t) => {
|
|
606
606
|
var _a;
|
|
607
607
|
const action = activeActionRef.current;
|
|
608
608
|
if (!action)
|
|
@@ -616,7 +616,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
616
616
|
/* ─────────────────────────────────────────
|
|
617
617
|
Camera helpers
|
|
618
618
|
───────────────────────────────────────── */
|
|
619
|
-
const focusOnObject =
|
|
619
|
+
const focusOnObject = Ue.useCallback((obj) => {
|
|
620
620
|
const box = new THREE__namespace.Box3().setFromObject(obj);
|
|
621
621
|
const center = box.getCenter(new THREE__namespace.Vector3());
|
|
622
622
|
const size = box.getSize(new THREE__namespace.Vector3());
|
|
@@ -630,7 +630,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
630
630
|
}
|
|
631
631
|
}, []);
|
|
632
632
|
focusOnObjectRef.current = focusOnObject;
|
|
633
|
-
const setCameraPreset =
|
|
633
|
+
const setCameraPreset = Ue.useCallback((preset) => {
|
|
634
634
|
const orbit = orbitRef.current;
|
|
635
635
|
if (!orbit)
|
|
636
636
|
return;
|
|
@@ -664,7 +664,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
664
664
|
/* ─────────────────────────────────────────
|
|
665
665
|
Selection & transform
|
|
666
666
|
───────────────────────────────────────── */
|
|
667
|
-
const selectObject =
|
|
667
|
+
const selectObject = Ue.useCallback((node) => {
|
|
668
668
|
var _a;
|
|
669
669
|
setSelectedId((_a = node === null || node === void 0 ? void 0 : node.id) !== null && _a !== void 0 ? _a : null);
|
|
670
670
|
const tc = transformRef.current;
|
|
@@ -678,29 +678,29 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
678
678
|
tc.detach();
|
|
679
679
|
}
|
|
680
680
|
}, [transformMode]);
|
|
681
|
-
|
|
681
|
+
Ue.useEffect(() => {
|
|
682
682
|
var _a;
|
|
683
683
|
(_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.setMode(transformMode);
|
|
684
684
|
}, [transformMode]);
|
|
685
685
|
/* ─────────────────────────────────────────
|
|
686
686
|
Toggle helpers
|
|
687
687
|
───────────────────────────────────────── */
|
|
688
|
-
|
|
688
|
+
Ue.useEffect(() => {
|
|
689
689
|
const scene = sceneRef.current;
|
|
690
690
|
const grid = scene.getObjectByName("__grid");
|
|
691
691
|
if (grid)
|
|
692
692
|
grid.visible = showGrid;
|
|
693
693
|
}, [showGrid]);
|
|
694
|
-
|
|
694
|
+
Ue.useEffect(() => {
|
|
695
695
|
const scene = sceneRef.current;
|
|
696
696
|
const axes = scene.getObjectByName("__axes");
|
|
697
697
|
if (axes)
|
|
698
698
|
axes.visible = showAxes;
|
|
699
699
|
}, [showAxes]);
|
|
700
|
-
|
|
700
|
+
Ue.useEffect(() => {
|
|
701
701
|
sceneRef.current.background = new THREE__namespace.Color(bgColor);
|
|
702
702
|
}, [bgColor]);
|
|
703
|
-
|
|
703
|
+
Ue.useEffect(() => {
|
|
704
704
|
const root = rootObjectRef.current;
|
|
705
705
|
if (!root)
|
|
706
706
|
return;
|
|
@@ -719,7 +719,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
719
719
|
/* ─────────────────────────────────────────
|
|
720
720
|
Viewport shading mode
|
|
721
721
|
───────────────────────────────────────── */
|
|
722
|
-
|
|
722
|
+
Ue.useEffect(() => {
|
|
723
723
|
const renderer = rendererRef.current;
|
|
724
724
|
if (!renderer)
|
|
725
725
|
return;
|
|
@@ -772,12 +772,12 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
772
772
|
}
|
|
773
773
|
}, [shadingMode]);
|
|
774
774
|
/* Gizmo space */
|
|
775
|
-
|
|
775
|
+
Ue.useEffect(() => {
|
|
776
776
|
var _a;
|
|
777
777
|
(_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.setSpace(gizmoSpace);
|
|
778
778
|
}, [gizmoSpace]);
|
|
779
779
|
/* Snap settings */
|
|
780
|
-
|
|
780
|
+
Ue.useEffect(() => {
|
|
781
781
|
const tc = transformRef.current;
|
|
782
782
|
if (!tc)
|
|
783
783
|
return;
|
|
@@ -793,7 +793,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
793
793
|
}
|
|
794
794
|
}, [snapEnabled, snapGrid]);
|
|
795
795
|
/* Skeleton helper */
|
|
796
|
-
|
|
796
|
+
Ue.useEffect(() => {
|
|
797
797
|
const scene = sceneRef.current;
|
|
798
798
|
if (skeletonHelperRef.current) {
|
|
799
799
|
scene.remove(skeletonHelperRef.current);
|
|
@@ -814,7 +814,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
814
814
|
}
|
|
815
815
|
}, [showSkeleton, sceneTree]);
|
|
816
816
|
/* Fog */
|
|
817
|
-
|
|
817
|
+
Ue.useEffect(() => {
|
|
818
818
|
if (fogEnabled) {
|
|
819
819
|
sceneRef.current.fog = new THREE__namespace.Fog(fogColor, fogNear, fogFar);
|
|
820
820
|
}
|
|
@@ -823,7 +823,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
823
823
|
}
|
|
824
824
|
}, [fogEnabled, fogColor, fogNear, fogFar]);
|
|
825
825
|
/* Light helpers visibility */
|
|
826
|
-
|
|
826
|
+
Ue.useEffect(() => {
|
|
827
827
|
sceneRef.current.traverse((obj) => {
|
|
828
828
|
if (obj.name.startsWith("__helper_")) {
|
|
829
829
|
obj.visible = showLightHelpers;
|
|
@@ -833,16 +833,16 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
833
833
|
/* ─────────────────────────────────────────
|
|
834
834
|
Drag-and-drop
|
|
835
835
|
───────────────────────────────────────── */
|
|
836
|
-
const handleDragOver =
|
|
836
|
+
const handleDragOver = Ue.useCallback((e) => {
|
|
837
837
|
e.preventDefault();
|
|
838
838
|
e.stopPropagation();
|
|
839
839
|
setDragOver(true);
|
|
840
840
|
}, []);
|
|
841
|
-
const handleDragLeave =
|
|
841
|
+
const handleDragLeave = Ue.useCallback((e) => {
|
|
842
842
|
e.preventDefault();
|
|
843
843
|
setDragOver(false);
|
|
844
844
|
}, []);
|
|
845
|
-
const handleDrop =
|
|
845
|
+
const handleDrop = Ue.useCallback((e) => {
|
|
846
846
|
e.preventDefault();
|
|
847
847
|
e.stopPropagation();
|
|
848
848
|
setDragOver(false);
|
|
@@ -857,14 +857,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
857
857
|
/* ─────────────────────────────────────────
|
|
858
858
|
Helpers
|
|
859
859
|
───────────────────────────────────────── */
|
|
860
|
-
const selectedNode =
|
|
861
|
-
const selectedMaterials =
|
|
860
|
+
const selectedNode = Ue.useMemo(() => (selectedId ? findNodeById(sceneTree, selectedId) : null), [selectedId, sceneTree]);
|
|
861
|
+
const selectedMaterials = Ue.useMemo(() => {
|
|
862
862
|
if (!selectedNode || !(selectedNode.object instanceof THREE__namespace.Mesh))
|
|
863
863
|
return [];
|
|
864
864
|
const m = selectedNode.object.material;
|
|
865
865
|
return Array.isArray(m) ? m : [m];
|
|
866
866
|
}, [selectedNode, matRefresh]);
|
|
867
|
-
const deleteSelected =
|
|
867
|
+
const deleteSelected = Ue.useCallback(() => {
|
|
868
868
|
var _a;
|
|
869
869
|
if (!selectedNode)
|
|
870
870
|
return;
|
|
@@ -873,7 +873,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
873
873
|
setSelectedId(null);
|
|
874
874
|
rebuildTree();
|
|
875
875
|
}, [selectedNode, rebuildTree]);
|
|
876
|
-
const duplicateSelected =
|
|
876
|
+
const duplicateSelected = Ue.useCallback(() => {
|
|
877
877
|
if (!selectedNode)
|
|
878
878
|
return;
|
|
879
879
|
const clone = selectedNode.object.clone(true);
|
|
@@ -886,7 +886,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
886
886
|
/* ─────────────────────────────────────────
|
|
887
887
|
Keyboard shortcuts
|
|
888
888
|
───────────────────────────────────────── */
|
|
889
|
-
|
|
889
|
+
Ue.useEffect(() => {
|
|
890
890
|
const handleKey = (e) => {
|
|
891
891
|
var _a, _b, _c;
|
|
892
892
|
const tag = (_a = e.target) === null || _a === void 0 ? void 0 : _a.tagName;
|
|
@@ -994,14 +994,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
994
994
|
/* ─────────────────────────────────────────
|
|
995
995
|
File input handlers
|
|
996
996
|
───────────────────────────────────────── */
|
|
997
|
-
const handleFileInput =
|
|
997
|
+
const handleFileInput = Ue.useCallback((e) => {
|
|
998
998
|
const files = e.target.files;
|
|
999
999
|
if (!files)
|
|
1000
1000
|
return;
|
|
1001
1001
|
Array.from(files).forEach(loadFile);
|
|
1002
1002
|
e.target.value = "";
|
|
1003
1003
|
}, [loadFile]);
|
|
1004
|
-
const handleMergeInput =
|
|
1004
|
+
const handleMergeInput = Ue.useCallback((e) => {
|
|
1005
1005
|
const files = e.target.files;
|
|
1006
1006
|
if (!files)
|
|
1007
1007
|
return;
|
|
@@ -1011,7 +1011,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1011
1011
|
/* ─────────────────────────────────────────
|
|
1012
1012
|
Export functions
|
|
1013
1013
|
───────────────────────────────────────── */
|
|
1014
|
-
const exportGLTF =
|
|
1014
|
+
const exportGLTF = Ue.useCallback((binary) => {
|
|
1015
1015
|
const root = rootObjectRef.current;
|
|
1016
1016
|
if (!root) {
|
|
1017
1017
|
setStatusText("Nothing to export");
|
|
@@ -1038,7 +1038,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1038
1038
|
setStatusText(`Export error: ${String(err)}`);
|
|
1039
1039
|
}, options);
|
|
1040
1040
|
}, []);
|
|
1041
|
-
const exportOBJ =
|
|
1041
|
+
const exportOBJ = Ue.useCallback(() => {
|
|
1042
1042
|
const root = rootObjectRef.current;
|
|
1043
1043
|
if (!root) {
|
|
1044
1044
|
setStatusText("Nothing to export");
|
|
@@ -1054,7 +1054,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1054
1054
|
URL.revokeObjectURL(a.href);
|
|
1055
1055
|
setStatusText("Exported OBJ successfully");
|
|
1056
1056
|
}, []);
|
|
1057
|
-
const exportSTL =
|
|
1057
|
+
const exportSTL = Ue.useCallback((binary) => {
|
|
1058
1058
|
const root = rootObjectRef.current;
|
|
1059
1059
|
if (!root) {
|
|
1060
1060
|
setStatusText("Nothing to export");
|
|
@@ -1076,7 +1076,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1076
1076
|
URL.revokeObjectURL(a.href);
|
|
1077
1077
|
setStatusText(`Exported STL${binary ? " (binary)" : ""} successfully`);
|
|
1078
1078
|
}, []);
|
|
1079
|
-
const exportPLY =
|
|
1079
|
+
const exportPLY = Ue.useCallback((binary) => {
|
|
1080
1080
|
const root = rootObjectRef.current;
|
|
1081
1081
|
if (!root) {
|
|
1082
1082
|
setStatusText("Nothing to export");
|
|
@@ -1100,7 +1100,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1100
1100
|
setStatusText(`Exported PLY${binary ? " (binary)" : ""} successfully`);
|
|
1101
1101
|
}, { binary });
|
|
1102
1102
|
}, []);
|
|
1103
|
-
const exportUSDZ =
|
|
1103
|
+
const exportUSDZ = Ue.useCallback(async () => {
|
|
1104
1104
|
const root = rootObjectRef.current;
|
|
1105
1105
|
if (!root) {
|
|
1106
1106
|
setStatusText("Nothing to export");
|
|
@@ -1128,7 +1128,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1128
1128
|
/* ─────────────────────────────────────────
|
|
1129
1129
|
Dispose & clear
|
|
1130
1130
|
───────────────────────────────────────── */
|
|
1131
|
-
const disposeObject =
|
|
1131
|
+
const disposeObject = Ue.useCallback((obj) => {
|
|
1132
1132
|
obj.traverse((child) => {
|
|
1133
1133
|
if (child.geometry) {
|
|
1134
1134
|
child.geometry.dispose();
|
|
@@ -1148,7 +1148,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1148
1148
|
}
|
|
1149
1149
|
});
|
|
1150
1150
|
}, []);
|
|
1151
|
-
const clearScene =
|
|
1151
|
+
const clearScene = Ue.useCallback(() => {
|
|
1152
1152
|
var _a;
|
|
1153
1153
|
if (!window.confirm("Clear the entire scene? This cannot be undone."))
|
|
1154
1154
|
return;
|
|
@@ -1186,7 +1186,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1186
1186
|
/* ─────────────────────────────────────────
|
|
1187
1187
|
Save to Library
|
|
1188
1188
|
───────────────────────────────────────── */
|
|
1189
|
-
const saveToLibrary =
|
|
1189
|
+
const saveToLibrary = Ue.useCallback(() => {
|
|
1190
1190
|
if (!onSaveToLibrary)
|
|
1191
1191
|
return;
|
|
1192
1192
|
const root = rootObjectRef.current;
|
|
@@ -1214,7 +1214,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1214
1214
|
/* ─────────────────────────────────────────
|
|
1215
1215
|
Add Mesh / Light / Camera / Empty
|
|
1216
1216
|
───────────────────────────────────────── */
|
|
1217
|
-
const addPrimitive =
|
|
1217
|
+
const addPrimitive = Ue.useCallback((type) => {
|
|
1218
1218
|
let geo;
|
|
1219
1219
|
switch (type) {
|
|
1220
1220
|
case "cube":
|
|
@@ -1275,7 +1275,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1275
1275
|
setStatusText(`Added ${mesh.name}`);
|
|
1276
1276
|
setAddMenuOpen(null);
|
|
1277
1277
|
}, [rebuildTree]);
|
|
1278
|
-
const addSceneLight =
|
|
1278
|
+
const addSceneLight = Ue.useCallback((type) => {
|
|
1279
1279
|
let light;
|
|
1280
1280
|
switch (type) {
|
|
1281
1281
|
case "point": {
|
|
@@ -1328,7 +1328,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1328
1328
|
setStatusText(`Added ${light.name}`);
|
|
1329
1329
|
setAddMenuOpen(null);
|
|
1330
1330
|
}, [rebuildTree]);
|
|
1331
|
-
const addCameraObject =
|
|
1331
|
+
const addCameraObject = Ue.useCallback(() => {
|
|
1332
1332
|
const cam = new THREE__namespace.PerspectiveCamera(50, 16 / 9, 0.1, 1000);
|
|
1333
1333
|
cam.name = "Camera";
|
|
1334
1334
|
cam.position.set(5, 3, 5);
|
|
@@ -1341,7 +1341,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1341
1341
|
setStatusText("Added Camera");
|
|
1342
1342
|
setAddMenuOpen(null);
|
|
1343
1343
|
}, [rebuildTree]);
|
|
1344
|
-
const addEmpty =
|
|
1344
|
+
const addEmpty = Ue.useCallback((type) => {
|
|
1345
1345
|
const obj = new THREE__namespace.Object3D();
|
|
1346
1346
|
obj.name = type === "arrows" ? "Empty_Arrows" : type === "cube_empty" ? "Empty_Cube" : "Empty";
|
|
1347
1347
|
sceneRef.current.add(obj);
|
|
@@ -1357,7 +1357,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1357
1357
|
/* ─────────────────────────────────────────
|
|
1358
1358
|
Raycaster click-to-select
|
|
1359
1359
|
───────────────────────────────────────── */
|
|
1360
|
-
const handleViewportClick =
|
|
1360
|
+
const handleViewportClick = Ue.useCallback((e) => {
|
|
1361
1361
|
if (editorMode !== "object")
|
|
1362
1362
|
return;
|
|
1363
1363
|
if (!mountRef.current || !rendererRef.current || !cameraRef.current)
|
|
@@ -1393,12 +1393,12 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1393
1393
|
/* ─────────────────────────────────────────
|
|
1394
1394
|
Context menu
|
|
1395
1395
|
───────────────────────────────────────── */
|
|
1396
|
-
const handleContextMenu =
|
|
1396
|
+
const handleContextMenu = Ue.useCallback((e) => {
|
|
1397
1397
|
e.preventDefault();
|
|
1398
1398
|
setContextMenu({ x: e.clientX, y: e.clientY });
|
|
1399
1399
|
}, []);
|
|
1400
|
-
const closeContextMenu =
|
|
1401
|
-
|
|
1400
|
+
const closeContextMenu = Ue.useCallback(() => setContextMenu(null), []);
|
|
1401
|
+
Ue.useEffect(() => {
|
|
1402
1402
|
if (!contextMenu)
|
|
1403
1403
|
return;
|
|
1404
1404
|
const close = () => setContextMenu(null);
|
|
@@ -1408,7 +1408,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1408
1408
|
/* ─────────────────────────────────────────
|
|
1409
1409
|
AI animation from video
|
|
1410
1410
|
───────────────────────────────────────── */
|
|
1411
|
-
const handleAIVideo =
|
|
1411
|
+
const handleAIVideo = Ue.useCallback(async (e) => {
|
|
1412
1412
|
var _a;
|
|
1413
1413
|
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
1414
1414
|
if (!file)
|
|
@@ -1454,24 +1454,24 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1454
1454
|
/* ─────────────────────────────────────────
|
|
1455
1455
|
Node helpers
|
|
1456
1456
|
───────────────────────────────────────── */
|
|
1457
|
-
const toggleNodeVisibility =
|
|
1457
|
+
const toggleNodeVisibility = Ue.useCallback((node) => {
|
|
1458
1458
|
node.object.visible = !node.object.visible;
|
|
1459
1459
|
rebuildTree();
|
|
1460
1460
|
}, [rebuildTree]);
|
|
1461
|
-
const toggleNodeExpanded =
|
|
1461
|
+
const toggleNodeExpanded = Ue.useCallback((node) => {
|
|
1462
1462
|
node.expanded = !node.expanded;
|
|
1463
1463
|
setSceneTree((prev) => [...prev]);
|
|
1464
1464
|
}, []);
|
|
1465
1465
|
/* ─────────────────────────────────────────
|
|
1466
1466
|
Undo / Redo
|
|
1467
1467
|
───────────────────────────────────────── */
|
|
1468
|
-
const undoRedoRef =
|
|
1469
|
-
const [undoRedoVer, setUndoRedoVer] =
|
|
1470
|
-
const pushUndo =
|
|
1468
|
+
const undoRedoRef = Ue.useRef(new modelEditorUtils.UndoRedoStack());
|
|
1469
|
+
const [undoRedoVer, setUndoRedoVer] = Ue.useState(0);
|
|
1470
|
+
const pushUndo = Ue.useCallback((action) => {
|
|
1471
1471
|
undoRedoRef.current.push(action);
|
|
1472
1472
|
setUndoRedoVer((v) => v + 1);
|
|
1473
1473
|
}, []);
|
|
1474
|
-
const undo =
|
|
1474
|
+
const undo = Ue.useCallback(() => {
|
|
1475
1475
|
const label = undoRedoRef.current.undo();
|
|
1476
1476
|
if (label) {
|
|
1477
1477
|
setStatusText(`Undo: ${label}`);
|
|
@@ -1479,7 +1479,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1479
1479
|
}
|
|
1480
1480
|
setUndoRedoVer((v) => v + 1);
|
|
1481
1481
|
}, [rebuildTree]);
|
|
1482
|
-
const redo =
|
|
1482
|
+
const redo = Ue.useCallback(() => {
|
|
1483
1483
|
const label = undoRedoRef.current.redo();
|
|
1484
1484
|
if (label) {
|
|
1485
1485
|
setStatusText(`Redo: ${label}`);
|
|
@@ -1492,7 +1492,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1492
1492
|
const undoLabel = undoRedoRef.current.undoLabel;
|
|
1493
1493
|
const redoLabel = undoRedoRef.current.redoLabel;
|
|
1494
1494
|
// Keyboard shortcuts for undo/redo
|
|
1495
|
-
|
|
1495
|
+
Ue.useEffect(() => {
|
|
1496
1496
|
const handleKeyDown = (e) => {
|
|
1497
1497
|
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
|
|
1498
1498
|
e.preventDefault();
|
|
@@ -1509,7 +1509,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1509
1509
|
/* ─────────────────────────────────────────
|
|
1510
1510
|
Batch Import
|
|
1511
1511
|
───────────────────────────────────────── */
|
|
1512
|
-
const loadFiles =
|
|
1512
|
+
const loadFiles = Ue.useCallback(async (files) => {
|
|
1513
1513
|
const fileArray = Array.from(files).filter((f) => {
|
|
1514
1514
|
const ext = f.name.substring(f.name.lastIndexOf(".")).toLowerCase();
|
|
1515
1515
|
return modelEditorTypes.SUPPORTED_EXTENSIONS.includes(ext);
|
|
@@ -1535,7 +1535,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1535
1535
|
/* ─────────────────────────────────────────
|
|
1536
1536
|
PBR Presets
|
|
1537
1537
|
───────────────────────────────────────── */
|
|
1538
|
-
const applyPreset =
|
|
1538
|
+
const applyPreset = Ue.useCallback((preset) => {
|
|
1539
1539
|
const node = selectedNode;
|
|
1540
1540
|
if (!node || !(node.object instanceof THREE__namespace.Mesh)) {
|
|
1541
1541
|
setStatusText("Select a mesh to apply material preset");
|
|
@@ -1589,7 +1589,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1589
1589
|
/* ─────────────────────────────────────────
|
|
1590
1590
|
LOD Generation
|
|
1591
1591
|
───────────────────────────────────────── */
|
|
1592
|
-
const generateSelectedLOD =
|
|
1592
|
+
const generateSelectedLOD = Ue.useCallback(() => {
|
|
1593
1593
|
const node = selectedNode;
|
|
1594
1594
|
if (!node || !(node.object instanceof THREE__namespace.Mesh)) {
|
|
1595
1595
|
setStatusText("Select a mesh to generate LOD");
|
|
@@ -1611,7 +1611,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1611
1611
|
/* ─────────────────────────────────────────
|
|
1612
1612
|
Instancing
|
|
1613
1613
|
───────────────────────────────────────── */
|
|
1614
|
-
const convertSelectedToInstanced =
|
|
1614
|
+
const convertSelectedToInstanced = Ue.useCallback(() => {
|
|
1615
1615
|
const root = rootObjectRef.current || sceneRef.current;
|
|
1616
1616
|
const result = modelEditorUtils.convertToInstanced(root);
|
|
1617
1617
|
if (result.created === 0) {
|
|
@@ -1622,7 +1622,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1622
1622
|
setStatusText(`Created ${result.created} instanced meshes, removed ${result.removed} duplicates`);
|
|
1623
1623
|
}, [rebuildTree]);
|
|
1624
1624
|
/* ── HDRI Environment ── */
|
|
1625
|
-
const applyHDRI =
|
|
1625
|
+
const applyHDRI = Ue.useCallback((preset) => {
|
|
1626
1626
|
const renderer = rendererRef.current;
|
|
1627
1627
|
if (!renderer)
|
|
1628
1628
|
return;
|
|
@@ -1640,7 +1640,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1640
1640
|
setStatusText(`Environment: ${preset.name}`);
|
|
1641
1641
|
}, []);
|
|
1642
1642
|
/* ── Snap ── */
|
|
1643
|
-
const snapPoint =
|
|
1643
|
+
const snapPoint = Ue.useCallback((point) => {
|
|
1644
1644
|
const root = rootObjectRef.current || sceneRef.current;
|
|
1645
1645
|
const modes = snapEnabled ? ["vertex", "edge", "grid"] : ["grid"];
|
|
1646
1646
|
return modelEditorUtils.snapMulti(point, root, snapGrid, modes, 0.5);
|