@nice2dev/ui-3d 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/collaborative/collaborativeScene.js +16 -8
- package/dist/cjs/collaborative/collaborativeScene.js.map +1 -1
- package/dist/cjs/core/i18n.js +3 -3
- package/dist/cjs/dance/DanceBridge.js +65 -30
- package/dist/cjs/dance/DanceBridge.js.map +1 -1
- package/dist/cjs/dance/DanceScoreEngine.js +37 -23
- package/dist/cjs/dance/DanceScoreEngine.js.map +1 -1
- package/dist/cjs/dance/PoseDetector.js +92 -49
- package/dist/cjs/dance/PoseDetector.js.map +1 -1
- package/dist/cjs/material/NiceMaterialEditor.js +134 -91
- package/dist/cjs/material/NiceMaterialEditor.js.map +1 -1
- package/dist/cjs/material/materialEditorUtils.js +86 -55
- package/dist/cjs/material/materialEditorUtils.js.map +1 -1
- package/dist/cjs/model/ModelEditor.js +16 -14
- package/dist/cjs/model/ModelEditor.js.map +1 -1
- package/dist/cjs/model/ModelEditorLeftPanel.js +21 -15
- package/dist/cjs/model/ModelEditorLeftPanel.js.map +1 -1
- package/dist/cjs/model/ModelEditorMenuBar.js +23 -10
- package/dist/cjs/model/ModelEditorMenuBar.js.map +1 -1
- package/dist/cjs/model/ModelEditorRightPanel.js +32 -27
- package/dist/cjs/model/ModelEditorRightPanel.js.map +1 -1
- package/dist/cjs/model/ModelEditorSubComponents.js +20 -20
- package/dist/cjs/model/ModelEditorSubComponents.js.map +1 -1
- package/dist/cjs/model/ModelEditorTimeline.js +12 -11
- package/dist/cjs/model/ModelEditorTimeline.js.map +1 -1
- package/dist/cjs/model/ModelEditorToolbar.js +5 -5
- package/dist/cjs/model/ModelEditorToolbar.js.map +1 -1
- package/dist/cjs/model/ModelEditorViewport.js +12 -8
- package/dist/cjs/model/ModelEditorViewport.js.map +1 -1
- package/dist/cjs/model/ModelViewer.js +16 -15
- package/dist/cjs/model/ModelViewer.js.map +1 -1
- package/dist/cjs/model/NiceArmatureEditor.js +53 -30
- package/dist/cjs/model/NiceArmatureEditor.js.map +1 -1
- package/dist/cjs/model/NiceMorphTargetEditor.js +51 -30
- package/dist/cjs/model/NiceMorphTargetEditor.js.map +1 -1
- package/dist/cjs/model/NiceOctree.js +62 -37
- package/dist/cjs/model/NiceOctree.js.map +1 -1
- package/dist/cjs/model/NicePhysicsSimulation.js +74 -36
- package/dist/cjs/model/NicePhysicsSimulation.js.map +1 -1
- package/dist/cjs/model/NiceProceduralGeometry.js +101 -31
- package/dist/cjs/model/NiceProceduralGeometry.js.map +1 -1
- package/dist/cjs/model/NiceTerrainEditor.js +88 -38
- package/dist/cjs/model/NiceTerrainEditor.js.map +1 -1
- package/dist/cjs/model/NiceWeightPainter.js +46 -24
- package/dist/cjs/model/NiceWeightPainter.js.map +1 -1
- package/dist/cjs/model/NiceXRPreview.js +89 -43
- package/dist/cjs/model/NiceXRPreview.js.map +1 -1
- package/dist/cjs/model/cadModeUtils.js +2 -2
- package/dist/cjs/model/cadModeUtils.js.map +1 -1
- package/dist/cjs/model/editorShortcuts.js +69 -35
- package/dist/cjs/model/editorShortcuts.js.map +1 -1
- package/dist/cjs/model/modelEditorTypes.js +56 -52
- package/dist/cjs/model/modelEditorTypes.js.map +1 -1
- package/dist/cjs/model/modelEditorUtils.js +356 -131
- package/dist/cjs/model/modelEditorUtils.js.map +1 -1
- package/dist/cjs/model/simsModeUtils.js +819 -74
- package/dist/cjs/model/simsModeUtils.js.map +1 -1
- package/dist/cjs/model/useModelEditor.js +501 -394
- package/dist/cjs/model/useModelEditor.js.map +1 -1
- package/dist/cjs/model/useModelViewer.js +117 -86
- package/dist/cjs/model/useModelViewer.js.map +1 -1
- package/dist/cjs/particle/NiceParticleEditor.js +126 -69
- package/dist/cjs/particle/NiceParticleEditor.js.map +1 -1
- package/dist/cjs/particle/particleEditorUtils.js +64 -27
- package/dist/cjs/particle/particleEditorUtils.js.map +1 -1
- package/dist/cjs/rendering/NiceCascadedShadows.js +35 -23
- package/dist/cjs/rendering/NiceCascadedShadows.js.map +1 -1
- package/dist/cjs/rendering/NiceRenderExport.js +26 -12
- package/dist/cjs/rendering/NiceRenderExport.js.map +1 -1
- package/dist/cjs/rendering/NiceSSAO.js +28 -23
- package/dist/cjs/rendering/NiceSSAO.js.map +1 -1
- package/dist/cjs/rendering/NiceSSR.js +22 -18
- package/dist/cjs/rendering/NiceSSR.js.map +1 -1
- package/dist/cjs/rendering/NiceWebGPURenderer.js +60 -37
- package/dist/cjs/rendering/NiceWebGPURenderer.js.map +1 -1
- package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js +572 -0
- package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
- package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js +4979 -0
- package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
- package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js +98 -0
- package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
- package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5244 -0
- package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
- package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js +174 -0
- package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
- package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js +10459 -0
- package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
- package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6313 -0
- package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
- package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js +1639 -0
- package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
- package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js +807 -0
- package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
- package/dist/cjs/ui/dist/charts-BAS21jkC.js +3833 -0
- package/dist/cjs/ui/dist/charts-BAS21jkC.js.map +1 -0
- package/dist/cjs/ui/dist/core-Da43q-pO.js +11683 -0
- package/dist/cjs/ui/dist/core-Da43q-pO.js.map +1 -0
- package/dist/cjs/ui/dist/index-pAuGTbuB.js +28876 -0
- package/dist/cjs/ui/dist/index-pAuGTbuB.js.map +1 -0
- package/dist/cjs/uv/NiceUVEditor.js +115 -86
- package/dist/cjs/uv/NiceUVEditor.js.map +1 -1
- package/dist/cjs/uv/uvEditorUtils.js +41 -23
- package/dist/cjs/uv/uvEditorUtils.js.map +1 -1
- package/dist/esm/collaborative/collaborativeScene.js +16 -8
- package/dist/esm/collaborative/collaborativeScene.js.map +1 -1
- package/dist/esm/dance/DanceBridge.js +55 -20
- package/dist/esm/dance/DanceBridge.js.map +1 -1
- package/dist/esm/dance/DanceScoreEngine.js +29 -15
- package/dist/esm/dance/DanceScoreEngine.js.map +1 -1
- package/dist/esm/dance/PoseDetector.js +73 -30
- package/dist/esm/dance/PoseDetector.js.map +1 -1
- package/dist/esm/material/NiceMaterialEditor.js +115 -72
- package/dist/esm/material/NiceMaterialEditor.js.map +1 -1
- package/dist/esm/material/materialEditorUtils.js +86 -55
- package/dist/esm/material/materialEditorUtils.js.map +1 -1
- package/dist/esm/model/ModelEditor.js +16 -14
- package/dist/esm/model/ModelEditor.js.map +1 -1
- package/dist/esm/model/ModelEditorLeftPanel.js +21 -15
- package/dist/esm/model/ModelEditorLeftPanel.js.map +1 -1
- package/dist/esm/model/ModelEditorMenuBar.js +23 -10
- package/dist/esm/model/ModelEditorMenuBar.js.map +1 -1
- package/dist/esm/model/ModelEditorRightPanel.js +32 -27
- package/dist/esm/model/ModelEditorRightPanel.js.map +1 -1
- package/dist/esm/model/ModelEditorSubComponents.js +17 -17
- package/dist/esm/model/ModelEditorSubComponents.js.map +1 -1
- package/dist/esm/model/ModelEditorTimeline.js +12 -11
- package/dist/esm/model/ModelEditorTimeline.js.map +1 -1
- package/dist/esm/model/ModelEditorToolbar.js +5 -5
- package/dist/esm/model/ModelEditorToolbar.js.map +1 -1
- package/dist/esm/model/ModelEditorViewport.js +12 -8
- package/dist/esm/model/ModelEditorViewport.js.map +1 -1
- package/dist/esm/model/ModelViewer.js +8 -7
- package/dist/esm/model/ModelViewer.js.map +1 -1
- package/dist/esm/model/NiceArmatureEditor.js +36 -13
- package/dist/esm/model/NiceArmatureEditor.js.map +1 -1
- package/dist/esm/model/NiceMorphTargetEditor.js +34 -13
- package/dist/esm/model/NiceMorphTargetEditor.js.map +1 -1
- package/dist/esm/model/NiceOctree.js +47 -22
- package/dist/esm/model/NiceOctree.js.map +1 -1
- package/dist/esm/model/NicePhysicsSimulation.js +51 -13
- package/dist/esm/model/NicePhysicsSimulation.js.map +1 -1
- package/dist/esm/model/NiceProceduralGeometry.js +94 -24
- package/dist/esm/model/NiceProceduralGeometry.js.map +1 -1
- package/dist/esm/model/NiceTerrainEditor.js +70 -20
- package/dist/esm/model/NiceTerrainEditor.js.map +1 -1
- package/dist/esm/model/NiceWeightPainter.js +33 -11
- package/dist/esm/model/NiceWeightPainter.js.map +1 -1
- package/dist/esm/model/NiceXRPreview.js +72 -26
- package/dist/esm/model/NiceXRPreview.js.map +1 -1
- package/dist/esm/model/cadModeUtils.js +2 -2
- package/dist/esm/model/cadModeUtils.js.map +1 -1
- package/dist/esm/model/editorShortcuts.js +69 -35
- package/dist/esm/model/editorShortcuts.js.map +1 -1
- package/dist/esm/model/modelEditorTypes.js +56 -52
- package/dist/esm/model/modelEditorTypes.js.map +1 -1
- package/dist/esm/model/modelEditorUtils.js +356 -131
- package/dist/esm/model/modelEditorUtils.js.map +1 -1
- package/dist/esm/model/simsModeUtils.js +819 -74
- package/dist/esm/model/simsModeUtils.js.map +1 -1
- package/dist/esm/model/useModelEditor.js +384 -277
- package/dist/esm/model/useModelEditor.js.map +1 -1
- package/dist/esm/model/useModelViewer.js +56 -25
- package/dist/esm/model/useModelViewer.js.map +1 -1
- package/dist/esm/particle/NiceParticleEditor.js +116 -59
- package/dist/esm/particle/NiceParticleEditor.js.map +1 -1
- package/dist/esm/particle/particleEditorUtils.js +64 -27
- package/dist/esm/particle/particleEditorUtils.js.map +1 -1
- package/dist/esm/rendering/NiceCascadedShadows.js +24 -12
- package/dist/esm/rendering/NiceCascadedShadows.js.map +1 -1
- package/dist/esm/rendering/NiceRenderExport.js +22 -8
- package/dist/esm/rendering/NiceRenderExport.js.map +1 -1
- package/dist/esm/rendering/NiceSSAO.js +11 -6
- package/dist/esm/rendering/NiceSSAO.js.map +1 -1
- package/dist/esm/rendering/NiceSSR.js +9 -5
- package/dist/esm/rendering/NiceSSR.js.map +1 -1
- package/dist/esm/rendering/NiceWebGPURenderer.js +40 -17
- package/dist/esm/rendering/NiceWebGPURenderer.js.map +1 -1
- package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js +563 -0
- package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
- package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js +4960 -0
- package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
- package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js +96 -0
- package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
- package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5201 -0
- package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
- package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js +172 -0
- package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
- package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js +10408 -0
- package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
- package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6296 -0
- package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
- package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js +1630 -0
- package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
- package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js +796 -0
- package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
- package/dist/esm/ui/dist/charts-BAS21jkC.js +3809 -0
- package/dist/esm/ui/dist/charts-BAS21jkC.js.map +1 -0
- package/dist/esm/ui/dist/core-Da43q-pO.js +11502 -0
- package/dist/esm/ui/dist/core-Da43q-pO.js.map +1 -0
- package/dist/esm/ui/dist/index-pAuGTbuB.js +28792 -0
- package/dist/esm/ui/dist/index-pAuGTbuB.js.map +1 -0
- package/dist/esm/uv/NiceUVEditor.js +91 -62
- package/dist/esm/uv/NiceUVEditor.js.map +1 -1
- package/dist/esm/uv/uvEditorUtils.js +41 -23
- package/dist/esm/uv/uvEditorUtils.js.map +1 -1
- package/package.json +2 -2
|
@@ -25,16 +25,18 @@ class UndoRedoStack {
|
|
|
25
25
|
}
|
|
26
26
|
undo() {
|
|
27
27
|
const action = this.undoStack.pop();
|
|
28
|
-
if (!action)
|
|
28
|
+
if (!action) {
|
|
29
29
|
return null;
|
|
30
|
+
}
|
|
30
31
|
action.undo();
|
|
31
32
|
this.redoStack.push(action);
|
|
32
33
|
return action.label;
|
|
33
34
|
}
|
|
34
35
|
redo() {
|
|
35
36
|
const action = this.redoStack.pop();
|
|
36
|
-
if (!action)
|
|
37
|
+
if (!action) {
|
|
37
38
|
return null;
|
|
39
|
+
}
|
|
38
40
|
action.redo();
|
|
39
41
|
this.undoStack.push(action);
|
|
40
42
|
return action.label;
|
|
@@ -67,11 +69,13 @@ const DEFAULT_LOD_LEVELS = [
|
|
|
67
69
|
* Returns a new geometry with reduced vertex count.
|
|
68
70
|
*/
|
|
69
71
|
function decimateGeometry(geometry, ratio) {
|
|
70
|
-
if (ratio >= 1.0)
|
|
72
|
+
if (ratio >= 1.0) {
|
|
71
73
|
return geometry.clone();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
}
|
|
75
|
+
const posAttr = geometry.getAttribute('position');
|
|
76
|
+
if (!posAttr) {
|
|
74
77
|
return geometry.clone();
|
|
78
|
+
}
|
|
75
79
|
const positions = Array.from(posAttr.array);
|
|
76
80
|
const vertexCount = posAttr.count;
|
|
77
81
|
const targetCount = Math.max(3, Math.floor(vertexCount * ratio));
|
|
@@ -130,7 +134,7 @@ function decimateGeometry(geometry, ratio) {
|
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
const result = new THREE.BufferGeometry();
|
|
133
|
-
result.setAttribute(
|
|
137
|
+
result.setAttribute('position', new THREE.BufferAttribute(newPositions, 3));
|
|
134
138
|
result.setIndex(newIndices);
|
|
135
139
|
result.computeVertexNormals();
|
|
136
140
|
return result;
|
|
@@ -140,7 +144,7 @@ function decimateGeometry(geometry, ratio) {
|
|
|
140
144
|
*/
|
|
141
145
|
function generateLOD(mesh, levels = DEFAULT_LOD_LEVELS) {
|
|
142
146
|
const lod = new THREE.LOD();
|
|
143
|
-
lod.name = mesh.name +
|
|
147
|
+
lod.name = mesh.name + '_LOD';
|
|
144
148
|
lod.position.copy(mesh.position);
|
|
145
149
|
lod.rotation.copy(mesh.rotation);
|
|
146
150
|
lod.scale.copy(mesh.scale);
|
|
@@ -158,44 +162,233 @@ function generateLOD(mesh, levels = DEFAULT_LOD_LEVELS) {
|
|
|
158
162
|
}
|
|
159
163
|
const PBR_PRESETS = [
|
|
160
164
|
// Metals
|
|
161
|
-
{ name:
|
|
162
|
-
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
{
|
|
168
|
-
|
|
165
|
+
{ name: 'Gold', category: 'Metal', params: { color: 0xffd700, metalness: 1.0, roughness: 0.15 } },
|
|
166
|
+
{
|
|
167
|
+
name: 'Silver',
|
|
168
|
+
category: 'Metal',
|
|
169
|
+
params: { color: 0xc0c0c0, metalness: 1.0, roughness: 0.1 },
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'Copper',
|
|
173
|
+
category: 'Metal',
|
|
174
|
+
params: { color: 0xb87333, metalness: 1.0, roughness: 0.25 },
|
|
175
|
+
},
|
|
176
|
+
{ name: 'Iron', category: 'Metal', params: { color: 0x808080, metalness: 0.95, roughness: 0.6 } },
|
|
177
|
+
{
|
|
178
|
+
name: 'Brushed Steel',
|
|
179
|
+
category: 'Metal',
|
|
180
|
+
params: { color: 0xa0a0a0, metalness: 0.95, roughness: 0.4 },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'Chrome',
|
|
184
|
+
category: 'Metal',
|
|
185
|
+
params: { color: 0xdddddd, metalness: 1.0, roughness: 0.02 },
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'Bronze',
|
|
189
|
+
category: 'Metal',
|
|
190
|
+
params: { color: 0xcd7f32, metalness: 0.9, roughness: 0.35 },
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: 'Aluminum',
|
|
194
|
+
category: 'Metal',
|
|
195
|
+
params: { color: 0xd6d6d6, metalness: 0.85, roughness: 0.2 },
|
|
196
|
+
},
|
|
169
197
|
// Non-metals
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
|
|
198
|
+
{
|
|
199
|
+
name: 'Plastic (White)',
|
|
200
|
+
category: 'Plastic',
|
|
201
|
+
params: { color: 0xf0f0f0, metalness: 0.0, roughness: 0.3 },
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'Plastic (Red)',
|
|
205
|
+
category: 'Plastic',
|
|
206
|
+
params: { color: 0xcc2222, metalness: 0.0, roughness: 0.35 },
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'Rubber',
|
|
210
|
+
category: 'Plastic',
|
|
211
|
+
params: { color: 0x333333, metalness: 0.0, roughness: 0.9 },
|
|
212
|
+
},
|
|
173
213
|
// Glass
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
214
|
+
{
|
|
215
|
+
name: 'Clear Glass',
|
|
216
|
+
category: 'Glass',
|
|
217
|
+
params: {
|
|
218
|
+
color: 0xffffff,
|
|
219
|
+
metalness: 0.0,
|
|
220
|
+
roughness: 0.0,
|
|
221
|
+
opacity: 0.15,
|
|
222
|
+
transparent: true,
|
|
223
|
+
ior: 1.5,
|
|
224
|
+
transmission: 0.95,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'Frosted Glass',
|
|
229
|
+
category: 'Glass',
|
|
230
|
+
params: {
|
|
231
|
+
color: 0xeeeeff,
|
|
232
|
+
metalness: 0.0,
|
|
233
|
+
roughness: 0.5,
|
|
234
|
+
opacity: 0.3,
|
|
235
|
+
transparent: true,
|
|
236
|
+
ior: 1.5,
|
|
237
|
+
transmission: 0.7,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'Tinted Glass',
|
|
242
|
+
category: 'Glass',
|
|
243
|
+
params: {
|
|
244
|
+
color: 0x224488,
|
|
245
|
+
metalness: 0.0,
|
|
246
|
+
roughness: 0.05,
|
|
247
|
+
opacity: 0.3,
|
|
248
|
+
transparent: true,
|
|
249
|
+
ior: 1.5,
|
|
250
|
+
transmission: 0.8,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
177
253
|
// Natural
|
|
178
|
-
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{
|
|
254
|
+
{
|
|
255
|
+
name: 'Wood (Light)',
|
|
256
|
+
category: 'Natural',
|
|
257
|
+
params: { color: 0xc19a6b, metalness: 0.0, roughness: 0.7 },
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: 'Wood (Dark)',
|
|
261
|
+
category: 'Natural',
|
|
262
|
+
params: { color: 0x5c3317, metalness: 0.0, roughness: 0.65 },
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'Stone',
|
|
266
|
+
category: 'Natural',
|
|
267
|
+
params: { color: 0x888888, metalness: 0.0, roughness: 0.85 },
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'Marble',
|
|
271
|
+
category: 'Natural',
|
|
272
|
+
params: { color: 0xf0ece0, metalness: 0.0, roughness: 0.15 },
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'Clay',
|
|
276
|
+
category: 'Natural',
|
|
277
|
+
params: { color: 0xb5651d, metalness: 0.0, roughness: 0.8 },
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: 'Concrete',
|
|
281
|
+
category: 'Natural',
|
|
282
|
+
params: { color: 0x999999, metalness: 0.0, roughness: 0.95 },
|
|
283
|
+
},
|
|
184
284
|
// Fabric
|
|
185
|
-
{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
285
|
+
{
|
|
286
|
+
name: 'Cotton (White)',
|
|
287
|
+
category: 'Fabric',
|
|
288
|
+
params: {
|
|
289
|
+
color: 0xf5f5dc,
|
|
290
|
+
metalness: 0.0,
|
|
291
|
+
roughness: 0.9,
|
|
292
|
+
sheen: 0.5,
|
|
293
|
+
sheenRoughness: 0.8,
|
|
294
|
+
sheenColor: 0xffffff,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: 'Silk',
|
|
299
|
+
category: 'Fabric',
|
|
300
|
+
params: {
|
|
301
|
+
color: 0xe3dac9,
|
|
302
|
+
metalness: 0.0,
|
|
303
|
+
roughness: 0.3,
|
|
304
|
+
sheen: 1.0,
|
|
305
|
+
sheenRoughness: 0.3,
|
|
306
|
+
sheenColor: 0xffeedd,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: 'Velvet',
|
|
311
|
+
category: 'Fabric',
|
|
312
|
+
params: {
|
|
313
|
+
color: 0x800020,
|
|
314
|
+
metalness: 0.0,
|
|
315
|
+
roughness: 0.95,
|
|
316
|
+
sheen: 0.9,
|
|
317
|
+
sheenRoughness: 0.9,
|
|
318
|
+
sheenColor: 0xff4060,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: 'Leather',
|
|
323
|
+
category: 'Fabric',
|
|
324
|
+
params: { color: 0x553322, metalness: 0.0, roughness: 0.6 },
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: 'Denim',
|
|
328
|
+
category: 'Fabric',
|
|
329
|
+
params: { color: 0x3b5998, metalness: 0.0, roughness: 0.85 },
|
|
330
|
+
},
|
|
190
331
|
// Skin
|
|
191
|
-
{
|
|
192
|
-
|
|
193
|
-
|
|
332
|
+
{
|
|
333
|
+
name: 'Skin (Light)',
|
|
334
|
+
category: 'Skin',
|
|
335
|
+
params: { color: 0xffdbac, metalness: 0.0, roughness: 0.55 },
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: 'Skin (Medium)',
|
|
339
|
+
category: 'Skin',
|
|
340
|
+
params: { color: 0xc68642, metalness: 0.0, roughness: 0.5 },
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'Skin (Dark)',
|
|
344
|
+
category: 'Skin',
|
|
345
|
+
params: { color: 0x8d5524, metalness: 0.0, roughness: 0.5 },
|
|
346
|
+
},
|
|
194
347
|
// Special
|
|
195
|
-
{
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
348
|
+
{
|
|
349
|
+
name: 'Car Paint',
|
|
350
|
+
category: 'Special',
|
|
351
|
+
params: {
|
|
352
|
+
color: 0xcc0000,
|
|
353
|
+
metalness: 0.5,
|
|
354
|
+
roughness: 0.15,
|
|
355
|
+
clearcoat: 1.0,
|
|
356
|
+
clearcoatRoughness: 0.05,
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: 'Emissive (Blue)',
|
|
361
|
+
category: 'Special',
|
|
362
|
+
params: {
|
|
363
|
+
color: 0x111111,
|
|
364
|
+
metalness: 0.0,
|
|
365
|
+
roughness: 0.5,
|
|
366
|
+
emissive: 0x0088ff,
|
|
367
|
+
emissiveIntensity: 2.0,
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: 'Emissive (Orange)',
|
|
372
|
+
category: 'Special',
|
|
373
|
+
params: {
|
|
374
|
+
color: 0x111111,
|
|
375
|
+
metalness: 0.0,
|
|
376
|
+
roughness: 0.5,
|
|
377
|
+
emissive: 0xff6600,
|
|
378
|
+
emissiveIntensity: 2.0,
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: 'Neon',
|
|
383
|
+
category: 'Special',
|
|
384
|
+
params: {
|
|
385
|
+
color: 0x000000,
|
|
386
|
+
metalness: 0.0,
|
|
387
|
+
roughness: 0.1,
|
|
388
|
+
emissive: 0x00ff88,
|
|
389
|
+
emissiveIntensity: 5.0,
|
|
390
|
+
},
|
|
391
|
+
},
|
|
199
392
|
];
|
|
200
393
|
/**
|
|
201
394
|
* Apply a PBR preset to a MeshStandardMaterial or MeshPhysicalMaterial.
|
|
@@ -209,39 +402,52 @@ function applyPBRPreset(material, preset) {
|
|
|
209
402
|
material.color.setHex(p.color);
|
|
210
403
|
material.metalness = p.metalness;
|
|
211
404
|
material.roughness = p.roughness;
|
|
212
|
-
if (p.emissive != null)
|
|
405
|
+
if (p.emissive != null) {
|
|
213
406
|
material.emissive.setHex(p.emissive);
|
|
214
|
-
|
|
407
|
+
}
|
|
408
|
+
if (p.emissiveIntensity != null) {
|
|
215
409
|
material.emissiveIntensity = p.emissiveIntensity;
|
|
216
|
-
|
|
410
|
+
}
|
|
411
|
+
if (p.opacity != null) {
|
|
217
412
|
material.opacity = p.opacity;
|
|
413
|
+
}
|
|
218
414
|
material.transparent = (_a = p.transparent) !== null && _a !== void 0 ? _a : false;
|
|
219
|
-
if (p.clearcoat != null)
|
|
415
|
+
if (p.clearcoat != null) {
|
|
220
416
|
material.clearcoat = p.clearcoat;
|
|
221
|
-
|
|
417
|
+
}
|
|
418
|
+
if (p.clearcoatRoughness != null) {
|
|
222
419
|
material.clearcoatRoughness = p.clearcoatRoughness;
|
|
223
|
-
|
|
420
|
+
}
|
|
421
|
+
if (p.ior != null) {
|
|
224
422
|
material.ior = p.ior;
|
|
225
|
-
|
|
423
|
+
}
|
|
424
|
+
if (p.transmission != null) {
|
|
226
425
|
material.transmission = p.transmission;
|
|
227
|
-
|
|
426
|
+
}
|
|
427
|
+
if (p.sheen != null) {
|
|
228
428
|
material.sheen = p.sheen;
|
|
229
|
-
|
|
429
|
+
}
|
|
430
|
+
if (p.sheenRoughness != null) {
|
|
230
431
|
material.sheenRoughness = p.sheenRoughness;
|
|
231
|
-
|
|
432
|
+
}
|
|
433
|
+
if (p.sheenColor != null) {
|
|
232
434
|
material.sheenColor.setHex(p.sheenColor);
|
|
435
|
+
}
|
|
233
436
|
material.needsUpdate = true;
|
|
234
437
|
}
|
|
235
438
|
else if (material instanceof THREE.MeshStandardMaterial && !needsPhysical) {
|
|
236
439
|
material.color.setHex(p.color);
|
|
237
440
|
material.metalness = p.metalness;
|
|
238
441
|
material.roughness = p.roughness;
|
|
239
|
-
if (p.emissive != null)
|
|
442
|
+
if (p.emissive != null) {
|
|
240
443
|
material.emissive.setHex(p.emissive);
|
|
241
|
-
|
|
444
|
+
}
|
|
445
|
+
if (p.emissiveIntensity != null) {
|
|
242
446
|
material.emissiveIntensity = p.emissiveIntensity;
|
|
243
|
-
|
|
447
|
+
}
|
|
448
|
+
if (p.opacity != null) {
|
|
244
449
|
material.opacity = p.opacity;
|
|
450
|
+
}
|
|
245
451
|
material.transparent = (_b = p.transparent) !== null && _b !== void 0 ? _b : false;
|
|
246
452
|
material.needsUpdate = true;
|
|
247
453
|
}
|
|
@@ -278,10 +484,10 @@ function convertToInstanced(parent) {
|
|
|
278
484
|
var _a;
|
|
279
485
|
const groups = new Map();
|
|
280
486
|
parent.traverse((child) => {
|
|
281
|
-
if (child instanceof THREE.Mesh && !child.name.startsWith(
|
|
487
|
+
if (child instanceof THREE.Mesh && !child.name.startsWith('__')) {
|
|
282
488
|
const geoId = child.geometry.uuid;
|
|
283
489
|
const matId = Array.isArray(child.material)
|
|
284
|
-
? child.material.map((m) => m.uuid).join(
|
|
490
|
+
? child.material.map((m) => m.uuid).join(',')
|
|
285
491
|
: child.material.uuid;
|
|
286
492
|
const key = `${geoId}|${matId}`;
|
|
287
493
|
let list = groups.get(key);
|
|
@@ -295,11 +501,12 @@ function convertToInstanced(parent) {
|
|
|
295
501
|
let created = 0;
|
|
296
502
|
let removed = 0;
|
|
297
503
|
for (const [, meshes] of groups) {
|
|
298
|
-
if (meshes.length < 2)
|
|
504
|
+
if (meshes.length < 2) {
|
|
299
505
|
continue;
|
|
506
|
+
}
|
|
300
507
|
const source = meshes[0];
|
|
301
508
|
const instancedMesh = new THREE.InstancedMesh(source.geometry, source.material, meshes.length);
|
|
302
|
-
instancedMesh.name = source.name +
|
|
509
|
+
instancedMesh.name = source.name + '_Instanced';
|
|
303
510
|
instancedMesh.castShadow = source.castShadow;
|
|
304
511
|
instancedMesh.receiveShadow = source.receiveShadow;
|
|
305
512
|
const matrix = new THREE.Matrix4();
|
|
@@ -331,9 +538,10 @@ function countTriangles(object) {
|
|
|
331
538
|
count += geo.index.count / 3;
|
|
332
539
|
}
|
|
333
540
|
else {
|
|
334
|
-
const pos = geo.getAttribute(
|
|
335
|
-
if (pos)
|
|
541
|
+
const pos = geo.getAttribute('position');
|
|
542
|
+
if (pos) {
|
|
336
543
|
count += pos.count / 3;
|
|
544
|
+
}
|
|
337
545
|
}
|
|
338
546
|
}
|
|
339
547
|
});
|
|
@@ -367,8 +575,8 @@ function measureAngle(a, b, c) {
|
|
|
367
575
|
const HDRI_PRESETS = [
|
|
368
576
|
// Studio
|
|
369
577
|
{
|
|
370
|
-
name:
|
|
371
|
-
category:
|
|
578
|
+
name: 'Studio Soft',
|
|
579
|
+
category: 'Studio',
|
|
372
580
|
bgColor: 0x1a1a2e,
|
|
373
581
|
envIntensity: 1.0,
|
|
374
582
|
ambientColor: 0xffffff,
|
|
@@ -379,8 +587,8 @@ const HDRI_PRESETS = [
|
|
|
379
587
|
exposure: 1.0,
|
|
380
588
|
},
|
|
381
589
|
{
|
|
382
|
-
name:
|
|
383
|
-
category:
|
|
590
|
+
name: 'Studio High Key',
|
|
591
|
+
category: 'Studio',
|
|
384
592
|
bgColor: 0xf0f0f0,
|
|
385
593
|
envIntensity: 1.2,
|
|
386
594
|
ambientColor: 0xffffff,
|
|
@@ -391,8 +599,8 @@ const HDRI_PRESETS = [
|
|
|
391
599
|
exposure: 1.2,
|
|
392
600
|
},
|
|
393
601
|
{
|
|
394
|
-
name:
|
|
395
|
-
category:
|
|
602
|
+
name: 'Studio Low Key',
|
|
603
|
+
category: 'Studio',
|
|
396
604
|
bgColor: 0x0a0a0a,
|
|
397
605
|
envIntensity: 0.5,
|
|
398
606
|
ambientColor: 0x222244,
|
|
@@ -403,8 +611,8 @@ const HDRI_PRESETS = [
|
|
|
403
611
|
exposure: 0.8,
|
|
404
612
|
},
|
|
405
613
|
{
|
|
406
|
-
name:
|
|
407
|
-
category:
|
|
614
|
+
name: 'Studio Rim',
|
|
615
|
+
category: 'Studio',
|
|
408
616
|
bgColor: 0x111111,
|
|
409
617
|
envIntensity: 0.3,
|
|
410
618
|
ambientColor: 0x111122,
|
|
@@ -416,8 +624,8 @@ const HDRI_PRESETS = [
|
|
|
416
624
|
},
|
|
417
625
|
// Outdoor
|
|
418
626
|
{
|
|
419
|
-
name:
|
|
420
|
-
category:
|
|
627
|
+
name: 'Sunny Day',
|
|
628
|
+
category: 'Outdoor',
|
|
421
629
|
bgColor: 0x87ceeb,
|
|
422
630
|
envIntensity: 1.0,
|
|
423
631
|
ambientColor: 0x8ec8f0,
|
|
@@ -428,8 +636,8 @@ const HDRI_PRESETS = [
|
|
|
428
636
|
exposure: 1.0,
|
|
429
637
|
},
|
|
430
638
|
{
|
|
431
|
-
name:
|
|
432
|
-
category:
|
|
639
|
+
name: 'Overcast',
|
|
640
|
+
category: 'Outdoor',
|
|
433
641
|
bgColor: 0xb0b0b8,
|
|
434
642
|
envIntensity: 0.8,
|
|
435
643
|
ambientColor: 0xccccdd,
|
|
@@ -441,8 +649,8 @@ const HDRI_PRESETS = [
|
|
|
441
649
|
exposure: 1.0,
|
|
442
650
|
},
|
|
443
651
|
{
|
|
444
|
-
name:
|
|
445
|
-
category:
|
|
652
|
+
name: 'Golden Hour',
|
|
653
|
+
category: 'Outdoor',
|
|
446
654
|
bgColor: 0xff8844,
|
|
447
655
|
envIntensity: 1.0,
|
|
448
656
|
ambientColor: 0xffaa55,
|
|
@@ -453,8 +661,8 @@ const HDRI_PRESETS = [
|
|
|
453
661
|
exposure: 1.1,
|
|
454
662
|
},
|
|
455
663
|
{
|
|
456
|
-
name:
|
|
457
|
-
category:
|
|
664
|
+
name: 'Blue Hour',
|
|
665
|
+
category: 'Outdoor',
|
|
458
666
|
bgColor: 0x1a2a4a,
|
|
459
667
|
envIntensity: 0.6,
|
|
460
668
|
ambientColor: 0x334466,
|
|
@@ -465,8 +673,8 @@ const HDRI_PRESETS = [
|
|
|
465
673
|
exposure: 0.8,
|
|
466
674
|
},
|
|
467
675
|
{
|
|
468
|
-
name:
|
|
469
|
-
category:
|
|
676
|
+
name: 'Night',
|
|
677
|
+
category: 'Outdoor',
|
|
470
678
|
bgColor: 0x050510,
|
|
471
679
|
envIntensity: 0.1,
|
|
472
680
|
ambientColor: 0x111133,
|
|
@@ -478,8 +686,8 @@ const HDRI_PRESETS = [
|
|
|
478
686
|
},
|
|
479
687
|
// Interior
|
|
480
688
|
{
|
|
481
|
-
name:
|
|
482
|
-
category:
|
|
689
|
+
name: 'Warm Interior',
|
|
690
|
+
category: 'Interior',
|
|
483
691
|
bgColor: 0x2a1a10,
|
|
484
692
|
envIntensity: 0.7,
|
|
485
693
|
ambientColor: 0xffddaa,
|
|
@@ -490,8 +698,8 @@ const HDRI_PRESETS = [
|
|
|
490
698
|
exposure: 1.0,
|
|
491
699
|
},
|
|
492
700
|
{
|
|
493
|
-
name:
|
|
494
|
-
category:
|
|
701
|
+
name: 'Cool Office',
|
|
702
|
+
category: 'Interior',
|
|
495
703
|
bgColor: 0x1a2230,
|
|
496
704
|
envIntensity: 0.8,
|
|
497
705
|
ambientColor: 0xddeeff,
|
|
@@ -502,8 +710,8 @@ const HDRI_PRESETS = [
|
|
|
502
710
|
exposure: 1.0,
|
|
503
711
|
},
|
|
504
712
|
{
|
|
505
|
-
name:
|
|
506
|
-
category:
|
|
713
|
+
name: 'Gallery',
|
|
714
|
+
category: 'Interior',
|
|
507
715
|
bgColor: 0xfafafa,
|
|
508
716
|
envIntensity: 1.0,
|
|
509
717
|
ambientColor: 0xffffff,
|
|
@@ -515,8 +723,8 @@ const HDRI_PRESETS = [
|
|
|
515
723
|
},
|
|
516
724
|
// Dramatic
|
|
517
725
|
{
|
|
518
|
-
name:
|
|
519
|
-
category:
|
|
726
|
+
name: 'Neon City',
|
|
727
|
+
category: 'Dramatic',
|
|
520
728
|
bgColor: 0x0a0015,
|
|
521
729
|
envIntensity: 0.4,
|
|
522
730
|
ambientColor: 0xff00ff,
|
|
@@ -527,8 +735,8 @@ const HDRI_PRESETS = [
|
|
|
527
735
|
exposure: 1.0,
|
|
528
736
|
},
|
|
529
737
|
{
|
|
530
|
-
name:
|
|
531
|
-
category:
|
|
738
|
+
name: 'Cinematic',
|
|
739
|
+
category: 'Dramatic',
|
|
532
740
|
bgColor: 0x0f0f1a,
|
|
533
741
|
envIntensity: 0.5,
|
|
534
742
|
ambientColor: 0x223344,
|
|
@@ -539,8 +747,8 @@ const HDRI_PRESETS = [
|
|
|
539
747
|
exposure: 0.9,
|
|
540
748
|
},
|
|
541
749
|
{
|
|
542
|
-
name:
|
|
543
|
-
category:
|
|
750
|
+
name: 'Sunrise',
|
|
751
|
+
category: 'Dramatic',
|
|
544
752
|
bgColor: 0xff6633,
|
|
545
753
|
envIntensity: 0.8,
|
|
546
754
|
ambientColor: 0xffaa66,
|
|
@@ -551,8 +759,8 @@ const HDRI_PRESETS = [
|
|
|
551
759
|
exposure: 1.2,
|
|
552
760
|
},
|
|
553
761
|
{
|
|
554
|
-
name:
|
|
555
|
-
category:
|
|
762
|
+
name: 'Desert',
|
|
763
|
+
category: 'Dramatic',
|
|
556
764
|
bgColor: 0xe8c888,
|
|
557
765
|
envIntensity: 1.0,
|
|
558
766
|
ambientColor: 0xddcc99,
|
|
@@ -575,10 +783,12 @@ function applyHDRIPreset(scene, renderer, preset) {
|
|
|
575
783
|
let ambient;
|
|
576
784
|
let directional;
|
|
577
785
|
scene.traverse((child) => {
|
|
578
|
-
if (child instanceof THREE.AmbientLight && !ambient)
|
|
786
|
+
if (child instanceof THREE.AmbientLight && !ambient) {
|
|
579
787
|
ambient = child;
|
|
580
|
-
|
|
788
|
+
}
|
|
789
|
+
if (child instanceof THREE.DirectionalLight && !directional) {
|
|
581
790
|
directional = child;
|
|
791
|
+
}
|
|
582
792
|
});
|
|
583
793
|
if (ambient != null) {
|
|
584
794
|
ambient.color.setHex(preset.ambientColor);
|
|
@@ -607,11 +817,13 @@ function snapToVertex(point, scene, maxDistance = Infinity) {
|
|
|
607
817
|
let minDist = maxDistance;
|
|
608
818
|
const tmpV = new THREE.Vector3();
|
|
609
819
|
scene.traverse((child) => {
|
|
610
|
-
if (!(child instanceof THREE.Mesh))
|
|
820
|
+
if (!(child instanceof THREE.Mesh)) {
|
|
611
821
|
return;
|
|
612
|
-
|
|
613
|
-
|
|
822
|
+
}
|
|
823
|
+
const posAttr = child.geometry.getAttribute('position');
|
|
824
|
+
if (!posAttr) {
|
|
614
825
|
return;
|
|
826
|
+
}
|
|
615
827
|
child.updateWorldMatrix(true, false);
|
|
616
828
|
for (let i = 0; i < posAttr.count; i++) {
|
|
617
829
|
tmpV.fromBufferAttribute(posAttr, i);
|
|
@@ -619,7 +831,7 @@ function snapToVertex(point, scene, maxDistance = Infinity) {
|
|
|
619
831
|
const d = point.distanceTo(tmpV);
|
|
620
832
|
if (d < minDist) {
|
|
621
833
|
minDist = d;
|
|
622
|
-
closest = { point: tmpV.clone(), target:
|
|
834
|
+
closest = { point: tmpV.clone(), target: 'vertex', object: child };
|
|
623
835
|
}
|
|
624
836
|
}
|
|
625
837
|
});
|
|
@@ -632,11 +844,13 @@ function snapToEdge(point, scene, maxDistance = Infinity) {
|
|
|
632
844
|
const a = new THREE.Vector3();
|
|
633
845
|
const b = new THREE.Vector3();
|
|
634
846
|
scene.traverse((child) => {
|
|
635
|
-
if (!(child instanceof THREE.Mesh))
|
|
847
|
+
if (!(child instanceof THREE.Mesh)) {
|
|
636
848
|
return;
|
|
637
|
-
|
|
638
|
-
|
|
849
|
+
}
|
|
850
|
+
const posAttr = child.geometry.getAttribute('position');
|
|
851
|
+
if (!posAttr) {
|
|
639
852
|
return;
|
|
853
|
+
}
|
|
640
854
|
const index = child.geometry.index;
|
|
641
855
|
child.updateWorldMatrix(true, false);
|
|
642
856
|
const processEdge = (i0, i1) => {
|
|
@@ -646,7 +860,7 @@ function snapToEdge(point, scene, maxDistance = Infinity) {
|
|
|
646
860
|
const d = point.distanceTo(closestOnEdge);
|
|
647
861
|
if (d < minDist) {
|
|
648
862
|
minDist = d;
|
|
649
|
-
closest = { point: closestOnEdge.clone(), target:
|
|
863
|
+
closest = { point: closestOnEdge.clone(), target: 'edge', object: child };
|
|
650
864
|
}
|
|
651
865
|
};
|
|
652
866
|
if (index) {
|
|
@@ -678,23 +892,25 @@ function closestPointOnSegment(p, a, b) {
|
|
|
678
892
|
* Multi-mode snap: tries vertex/edge/grid in priority order.
|
|
679
893
|
* Returns the best snap within maxDistance, or grid snap as fallback.
|
|
680
894
|
*/
|
|
681
|
-
function snapMulti(point, scene, gridSize, modes = [
|
|
895
|
+
function snapMulti(point, scene, gridSize, modes = ['vertex', 'edge', 'grid'], maxDistance = 0.5) {
|
|
682
896
|
for (const mode of modes) {
|
|
683
|
-
if (mode ===
|
|
897
|
+
if (mode === 'vertex') {
|
|
684
898
|
const result = snapToVertex(point, scene, maxDistance);
|
|
685
|
-
if (result)
|
|
899
|
+
if (result) {
|
|
686
900
|
return result;
|
|
901
|
+
}
|
|
687
902
|
}
|
|
688
|
-
else if (mode ===
|
|
903
|
+
else if (mode === 'edge') {
|
|
689
904
|
const result = snapToEdge(point, scene, maxDistance);
|
|
690
|
-
if (result)
|
|
905
|
+
if (result) {
|
|
691
906
|
return result;
|
|
907
|
+
}
|
|
692
908
|
}
|
|
693
|
-
else if (mode ===
|
|
694
|
-
return { point: snapToGrid(point, gridSize), target:
|
|
909
|
+
else if (mode === 'grid') {
|
|
910
|
+
return { point: snapToGrid(point, gridSize), target: 'grid' };
|
|
695
911
|
}
|
|
696
912
|
}
|
|
697
|
-
return { point: snapToGrid(point, gridSize), target:
|
|
913
|
+
return { point: snapToGrid(point, gridSize), target: 'grid' };
|
|
698
914
|
}
|
|
699
915
|
/** Evaluate a cubic bezier segment at parameter t (0-1). */
|
|
700
916
|
function cubicBezier(p0, p1, p2, p3, t) {
|
|
@@ -709,15 +925,19 @@ function cubicBezier(p0, p1, p2, p3, t) {
|
|
|
709
925
|
function evaluateTrack(track, time) {
|
|
710
926
|
var _a, _b;
|
|
711
927
|
const { keyframes } = track;
|
|
712
|
-
if (keyframes.length === 0)
|
|
928
|
+
if (keyframes.length === 0) {
|
|
713
929
|
return 0;
|
|
714
|
-
|
|
930
|
+
}
|
|
931
|
+
if (keyframes.length === 1) {
|
|
715
932
|
return keyframes[0].value;
|
|
933
|
+
}
|
|
716
934
|
// Clamp to range
|
|
717
|
-
if (time <= keyframes[0].time)
|
|
935
|
+
if (time <= keyframes[0].time) {
|
|
718
936
|
return keyframes[0].value;
|
|
719
|
-
|
|
937
|
+
}
|
|
938
|
+
if (time >= keyframes[keyframes.length - 1].time) {
|
|
720
939
|
return keyframes[keyframes.length - 1].value;
|
|
940
|
+
}
|
|
721
941
|
// Find surrounding keyframes
|
|
722
942
|
let left = 0;
|
|
723
943
|
for (let i = 1; i < keyframes.length; i++) {
|
|
@@ -730,21 +950,22 @@ function evaluateTrack(track, time) {
|
|
|
730
950
|
const kfL = keyframes[left];
|
|
731
951
|
const kfR = keyframes[right];
|
|
732
952
|
const dt = kfR.time - kfL.time;
|
|
733
|
-
if (dt === 0)
|
|
953
|
+
if (dt === 0) {
|
|
734
954
|
return kfL.value;
|
|
955
|
+
}
|
|
735
956
|
const t = (time - kfL.time) / dt;
|
|
736
957
|
switch (kfL.interpolation) {
|
|
737
|
-
case
|
|
958
|
+
case 'constant':
|
|
738
959
|
return kfL.value;
|
|
739
|
-
case
|
|
960
|
+
case 'linear':
|
|
740
961
|
return kfL.value + (kfR.value - kfL.value) * t;
|
|
741
|
-
case
|
|
962
|
+
case 'ease-in':
|
|
742
963
|
return kfL.value + (kfR.value - kfL.value) * (t * t);
|
|
743
|
-
case
|
|
964
|
+
case 'ease-out':
|
|
744
965
|
return kfL.value + (kfR.value - kfL.value) * (1 - (1 - t) * (1 - t));
|
|
745
|
-
case
|
|
746
|
-
return kfL.value + (kfR.value - kfL.value) * (t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t));
|
|
747
|
-
case
|
|
966
|
+
case 'ease-in-out':
|
|
967
|
+
return (kfL.value + (kfR.value - kfL.value) * (t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t)));
|
|
968
|
+
case 'bezier': {
|
|
748
969
|
const handleR = (_a = kfL.handleRight) !== null && _a !== void 0 ? _a : { y: 0 };
|
|
749
970
|
const handleL = (_b = kfR.handleLeft) !== null && _b !== void 0 ? _b : { y: 0 };
|
|
750
971
|
return cubicBezier(kfL.value, kfL.value + handleR.y, kfR.value + handleL.y, kfR.value, t);
|
|
@@ -754,7 +975,7 @@ function evaluateTrack(track, time) {
|
|
|
754
975
|
}
|
|
755
976
|
}
|
|
756
977
|
/** Insert or update a keyframe at a given time. */
|
|
757
|
-
function setKeyframe(track, time, value, interpolation =
|
|
978
|
+
function setKeyframe(track, time, value, interpolation = 'bezier') {
|
|
758
979
|
// Find existing keyframe at same time (within epsilon)
|
|
759
980
|
const epsilon = 0.001;
|
|
760
981
|
const existingIdx = track.keyframes.findIndex((kf) => Math.abs(kf.time - time) < epsilon);
|
|
@@ -804,7 +1025,7 @@ function createAnimationClip(name, tracks, sampleRate = 30) {
|
|
|
804
1025
|
/** Trigger a file download in the browser. */
|
|
805
1026
|
function downloadBlob(blob, filename) {
|
|
806
1027
|
const url = URL.createObjectURL(blob);
|
|
807
|
-
const a = document.createElement(
|
|
1028
|
+
const a = document.createElement('a');
|
|
808
1029
|
a.href = url;
|
|
809
1030
|
a.download = filename;
|
|
810
1031
|
document.body.appendChild(a);
|
|
@@ -829,9 +1050,10 @@ async function exportSceneUSDZ(scene) {
|
|
|
829
1050
|
const { USDZExporter } = await import('three/examples/jsm/exporters/USDZExporter.js');
|
|
830
1051
|
const exporter = new USDZExporter();
|
|
831
1052
|
const arraybuffer = await new Promise((resolve, reject) => {
|
|
1053
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- USDZExporter.parse callback signature varies between three.js minor versions
|
|
832
1054
|
exporter.parse(scene, resolve, reject);
|
|
833
1055
|
});
|
|
834
|
-
return new Blob([arraybuffer], { type:
|
|
1056
|
+
return new Blob([arraybuffer], { type: 'model/vnd.usdz+zip' });
|
|
835
1057
|
}
|
|
836
1058
|
/**
|
|
837
1059
|
* Setup crossfade between two animation actions.
|
|
@@ -862,8 +1084,9 @@ function makeAdditiveAction(mixer, clip, referenceClip) {
|
|
|
862
1084
|
*/
|
|
863
1085
|
function blendAnimations(actions) {
|
|
864
1086
|
const totalWeight = actions.reduce((sum, a) => sum + a.weight, 0);
|
|
865
|
-
if (totalWeight === 0)
|
|
1087
|
+
if (totalWeight === 0) {
|
|
866
1088
|
return;
|
|
1089
|
+
}
|
|
867
1090
|
for (const { action, weight } of actions) {
|
|
868
1091
|
const normalizedWeight = weight / totalWeight;
|
|
869
1092
|
action.setEffectiveWeight(normalizedWeight);
|
|
@@ -887,12 +1110,14 @@ function solveIKChain(skeleton, chain) {
|
|
|
887
1110
|
const bones = [];
|
|
888
1111
|
for (const name of boneNames) {
|
|
889
1112
|
const bone = skeleton.bones.find((b) => b.name === name);
|
|
890
|
-
if (!bone)
|
|
1113
|
+
if (!bone) {
|
|
891
1114
|
return false;
|
|
1115
|
+
}
|
|
892
1116
|
bones.push(bone);
|
|
893
1117
|
}
|
|
894
|
-
if (bones.length < 2)
|
|
1118
|
+
if (bones.length < 2) {
|
|
895
1119
|
return false;
|
|
1120
|
+
}
|
|
896
1121
|
const endEffector = bones[bones.length - 1];
|
|
897
1122
|
const endPos = new THREE.Vector3();
|
|
898
1123
|
const toEnd = new THREE.Vector3();
|
|
@@ -942,10 +1167,10 @@ function solveIKChain(skeleton, chain) {
|
|
|
942
1167
|
}
|
|
943
1168
|
/** Standard quad viewport layout. */
|
|
944
1169
|
const QUAD_VIEWPORT = [
|
|
945
|
-
{ preset:
|
|
946
|
-
{ preset:
|
|
947
|
-
{ preset:
|
|
948
|
-
{ preset:
|
|
1170
|
+
{ preset: 'perspective', x: 0.5, y: 0.5, width: 0.5, height: 0.5 },
|
|
1171
|
+
{ preset: 'top', x: 0, y: 0.5, width: 0.5, height: 0.5 },
|
|
1172
|
+
{ preset: 'front', x: 0, y: 0, width: 0.5, height: 0.5 },
|
|
1173
|
+
{ preset: 'right', x: 0.5, y: 0, width: 0.5, height: 0.5 },
|
|
949
1174
|
];
|
|
950
1175
|
/** Camera position/rotation for orthographic viewport presets. */
|
|
951
1176
|
const VIEWPORT_CAMERAS = {
|
|
@@ -963,7 +1188,7 @@ const VIEWPORT_CAMERAS = {
|
|
|
963
1188
|
function createViewportCamera(preset, aspect = 1, frustumSize = 10) {
|
|
964
1189
|
const config = VIEWPORT_CAMERAS[preset];
|
|
965
1190
|
if (config.ortho) {
|
|
966
|
-
const camera = new THREE.OrthographicCamera(-frustumSize * aspect / 2, frustumSize * aspect / 2, frustumSize / 2, -frustumSize / 2, 0.01, 10000);
|
|
1191
|
+
const camera = new THREE.OrthographicCamera((-frustumSize * aspect) / 2, (frustumSize * aspect) / 2, frustumSize / 2, -frustumSize / 2, 0.01, 10000);
|
|
967
1192
|
camera.position.set(...config.position);
|
|
968
1193
|
camera.up.set(...config.up);
|
|
969
1194
|
camera.lookAt(0, 0, 0);
|