@nice2dev/ui-3d 1.1.10 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +173 -0
- package/README.md +11 -15
- package/dist/cjs/core/editorAsk.js +48 -0
- package/dist/cjs/core/i18n.js +7 -3
- package/dist/cjs/core/threeCompat.js +52 -0
- package/dist/cjs/dance/DanceScoreEngine.js +7 -0
- package/dist/cjs/dance/PoseDetector.js +116 -64
- package/dist/cjs/index.js +0 -3
- package/dist/cjs/material/NiceMaterialEditor.js +7 -3
- package/dist/cjs/material/materialEditorTypes.js +1 -1
- package/dist/cjs/material/materialNodeDefinitions.js +14 -11
- package/dist/cjs/model/ModelEditor.js +3 -5
- package/dist/cjs/model/ModelEditorLeftPanel.js +10 -4
- package/dist/cjs/model/ModelEditorMenuBar.js +7 -5
- package/dist/cjs/model/ModelEditorRightPanel.js +18 -16
- package/dist/cjs/model/ModelEditorSubComponents.js +8 -6
- package/dist/cjs/model/ModelEditorTimeline.js +6 -4
- package/dist/cjs/model/ModelEditorToolbar.js +6 -4
- package/dist/cjs/model/ModelEditorViewport.js +16 -6
- package/dist/cjs/model/ModelViewer.js +6 -7
- package/dist/cjs/model/NiceArmatureEditor.js +8 -6
- package/dist/cjs/model/NiceMorphTargetEditor.js +5 -3
- package/dist/cjs/model/NiceOctree.js +3 -1
- package/dist/cjs/model/NicePhysicsSimulation.js +7 -5
- package/dist/cjs/model/NiceProceduralGeometry.js +5 -3
- package/dist/cjs/model/NiceTerrainEditor.js +10 -8
- package/dist/cjs/model/NiceWeightPainter.js +6 -4
- package/dist/cjs/model/NiceXRPreview.js +7 -5
- package/dist/cjs/model/useModelEditor.js +30 -18
- package/dist/cjs/model/useModelViewer.js +13 -9
- package/dist/cjs/nice2dev-ui-3d.css +1 -1
- package/dist/cjs/particle/NiceParticleEditor.js +14 -9
- package/dist/cjs/rendering/NiceCascadedShadows.js +3 -1
- package/dist/cjs/rendering/NiceRenderExport.js +11 -5
- package/dist/cjs/rendering/NiceSSAO.js +3 -1
- package/dist/cjs/rendering/NiceSSR.js +3 -1
- package/dist/cjs/rendering/NiceWebGPURenderer.js +7 -4
- package/dist/cjs/uv/NiceUVEditor.js +16 -12
- package/dist/cjs/uv/uvEditorTypes.js +1 -0
- package/dist/esm/core/editorAsk.js +45 -0
- package/dist/esm/core/i18n.js +8 -4
- package/dist/esm/core/threeCompat.js +30 -0
- package/dist/esm/dance/DanceScoreEngine.js +7 -0
- package/dist/esm/dance/PoseDetector.js +118 -65
- package/dist/esm/index.js +3 -3
- package/dist/esm/material/NiceMaterialEditor.js +7 -3
- package/dist/esm/material/materialEditorTypes.js +1 -1
- package/dist/esm/material/materialNodeDefinitions.js +14 -11
- package/dist/esm/model/ModelEditor.js +4 -5
- package/dist/esm/model/ModelEditorLeftPanel.js +10 -4
- package/dist/esm/model/ModelEditorMenuBar.js +7 -5
- package/dist/esm/model/ModelEditorRightPanel.js +18 -16
- package/dist/esm/model/ModelEditorSubComponents.js +8 -6
- package/dist/esm/model/ModelEditorTimeline.js +6 -4
- package/dist/esm/model/ModelEditorToolbar.js +6 -4
- package/dist/esm/model/ModelEditorViewport.js +16 -6
- package/dist/esm/model/ModelViewer.js +7 -7
- package/dist/esm/model/NiceArmatureEditor.js +9 -7
- package/dist/esm/model/NiceMorphTargetEditor.js +5 -3
- package/dist/esm/model/NiceOctree.js +3 -1
- package/dist/esm/model/NicePhysicsSimulation.js +7 -5
- package/dist/esm/model/NiceProceduralGeometry.js +5 -3
- package/dist/esm/model/NiceTerrainEditor.js +10 -8
- package/dist/esm/model/NiceWeightPainter.js +6 -4
- package/dist/esm/model/NiceXRPreview.js +7 -5
- package/dist/esm/model/useModelEditor.js +30 -18
- package/dist/esm/model/useModelViewer.js +13 -9
- package/dist/esm/nice2dev-ui-3d.css +1 -1
- package/dist/esm/particle/NiceParticleEditor.js +14 -9
- package/dist/esm/rendering/NiceCascadedShadows.js +3 -1
- package/dist/esm/rendering/NiceRenderExport.js +11 -5
- package/dist/esm/rendering/NiceSSAO.js +3 -1
- package/dist/esm/rendering/NiceSSR.js +3 -1
- package/dist/esm/rendering/NiceWebGPURenderer.js +7 -4
- package/dist/esm/uv/NiceUVEditor.js +16 -12
- package/dist/esm/uv/uvEditorTypes.js +1 -0
- package/dist/types/core/editorAsk.d.ts +26 -0
- package/dist/types/core/i18n.d.ts +1 -17
- package/dist/types/core/threeCompat.d.ts +14 -0
- package/dist/types/dance/DanceScoreEngine.d.ts +0 -6
- package/dist/types/dance/PoseDetector.d.ts +14 -13
- package/dist/types/index.d.ts +4 -4
- package/dist/types/model/ModelEditor.d.ts +0 -4
- package/dist/types/model/ModelViewer.d.ts +0 -4
- package/dist/types/model/useModelEditor.d.ts +3 -0
- package/package.json +3 -3
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -211,6 +212,7 @@ function useMorphTargetEditor(mesh, onInfluenceChange) {
|
|
|
211
212
|
}
|
|
212
213
|
/* ── Component ── */
|
|
213
214
|
const NiceMorphTargetEditor = ({ mesh, onInfluenceChange, className = '', tutorial, }) => {
|
|
215
|
+
const { t } = i18n.useNiceTranslation();
|
|
214
216
|
const { targets, setInfluence, createMorphTarget, deleteMorphTarget, resetAllInfluences, isAnimating, startAnimation, stopAnimation, animations, addAnimation: _addAnimation, removeAnimation, } = useMorphTargetEditor(mesh, onInfluenceChange);
|
|
215
217
|
const [newName, setNewName] = React.useState('');
|
|
216
218
|
const handleCreate = () => {
|
|
@@ -221,17 +223,17 @@ const NiceMorphTargetEditor = ({ mesh, onInfluenceChange, className = '', tutori
|
|
|
221
223
|
createMorphTarget(name);
|
|
222
224
|
setNewName('');
|
|
223
225
|
};
|
|
224
|
-
return (jsxRuntime.jsxs("div", { className: `nice-morph-target-editor ${className}`, style: { position: 'relative', display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 0, right: 0, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, MORPH_TARGET_EDITOR_TOUR) }) }), jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsx("strong", { children:
|
|
226
|
+
return (jsxRuntime.jsxs("div", { className: `nice-morph-target-editor ${className}`, style: { position: 'relative', display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 0, right: 0, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, MORPH_TARGET_EDITOR_TOUR) }) }), jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsx("strong", { children: t('morphTargetEditor.morphTargets', 'Morph Targets') }), jsxRuntime.jsx("button", { type: "button", onClick: resetAllInfluences, style: { fontSize: 11 }, children: t('morphTargetEditor.resetAll', 'Reset All') })] }), targets.length === 0 && (jsxRuntime.jsx("div", { style: { opacity: 0.6, fontStyle: 'italic' }, children: t('morphTargetEditor.noMorphTargetsFound', 'No morph targets found.') })), targets.map((target) => (jsxRuntime.jsxs("div", { style: {
|
|
225
227
|
display: 'grid',
|
|
226
228
|
gridTemplateColumns: '1fr 50px auto',
|
|
227
229
|
alignItems: 'center',
|
|
228
230
|
gap: 6,
|
|
229
|
-
}, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { style: { display: 'block', fontSize: 11, marginBottom: 2 }, children: target.name }), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: target.influence, onChange: (e) => setInfluence(target.index, parseFloat(e.target.value)), style: { width: '100%' } })] }), jsxRuntime.jsx("span", { style: { fontSize: 11, textAlign: 'right' }, children: target.influence.toFixed(2) }), jsxRuntime.jsx("button", { type: "button", onClick: () => deleteMorphTarget(target.index), title:
|
|
231
|
+
}, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { style: { display: 'block', fontSize: 11, marginBottom: 2 }, children: target.name }), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: target.influence, onChange: (e) => setInfluence(target.index, parseFloat(e.target.value)), style: { width: '100%' } })] }), jsxRuntime.jsx("span", { style: { fontSize: 11, textAlign: 'right' }, children: target.influence.toFixed(2) }), jsxRuntime.jsx("button", { type: "button", onClick: () => deleteMorphTarget(target.index), title: t('morphTargetEditor.delete', 'Delete'), "aria-label": t('morphTargetEditor.delete', 'Delete'), style: { background: 'none', border: 'none', cursor: 'pointer' }, children: jsxRuntime.jsx(ui.NiceIcon, { name: "trash", size: 14 }) })] }, target.index))), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, marginTop: 4 }, children: [jsxRuntime.jsx("input", { type: "text", value: newName, onChange: (e) => setNewName(e.target.value), placeholder: t('morphTargetEditor.newMorphTargetName', 'New morph target name…'), onKeyDown: (e) => e.key === 'Enter' && handleCreate(), style: {
|
|
230
232
|
flex: 1,
|
|
231
233
|
padding: '4px 8px',
|
|
232
234
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
233
235
|
borderRadius: 4,
|
|
234
|
-
} }), jsxRuntime.jsx("button", { type: "button", onClick: handleCreate, children: "+ Add" })] }), animations.length > 0 && (jsxRuntime.jsxs("div", { style: { marginTop: 8 }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12 }, children:
|
|
236
|
+
} }), jsxRuntime.jsx("button", { type: "button", onClick: handleCreate, children: "+ Add" })] }), animations.length > 0 && (jsxRuntime.jsxs("div", { style: { marginTop: 8 }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12 }, children: t('morphTargetEditor.animations', 'Animations') }), animations.map((anim) => (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginTop: 4 }, children: [jsxRuntime.jsx("span", { style: { flex: 1 }, children: anim.name }), jsxRuntime.jsx("button", { type: "button", onClick: () => (isAnimating ? stopAnimation() : startAnimation(anim)), style: { fontSize: 11 }, "aria-label": isAnimating ? 'Stop' : 'Play', children: jsxRuntime.jsx(ui.NiceIcon, { name: isAnimating ? 'stop' : 'play', size: 14 }) }), jsxRuntime.jsx("button", { type: "button", onClick: () => removeAnimation(anim.name), style: { fontSize: 11 }, "aria-label": t('morphTargetEditor.removeAnimation', 'Remove animation'), children: jsxRuntime.jsx(ui.NiceIcon, { name: "trash", size: 14 }) })] }, anim.name)))] }))] }));
|
|
235
237
|
};
|
|
236
238
|
|
|
237
239
|
exports.NiceMorphTargetEditor = NiceMorphTargetEditor;
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -345,6 +346,7 @@ function useOctree(scene, configOverride) {
|
|
|
345
346
|
}
|
|
346
347
|
/* ── Component ── */
|
|
347
348
|
const NiceOctree = ({ scene, config, showDebug: initialShowDebug, className = '', }) => {
|
|
349
|
+
const { t } = i18n.useNiceTranslation();
|
|
348
350
|
const octree = useOctree(scene, config);
|
|
349
351
|
React.useEffect(() => {
|
|
350
352
|
if (initialShowDebug !== undefined) {
|
|
@@ -358,7 +360,7 @@ const NiceOctree = ({ scene, config, showDebug: initialShowDebug, className = ''
|
|
|
358
360
|
borderRadius: 4,
|
|
359
361
|
padding: '6px 12px',
|
|
360
362
|
cursor: 'pointer',
|
|
361
|
-
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }), " Build Octree
|
|
363
|
+
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }), " ", t('octree.buildOctree', 'Build Octree')] }), jsxRuntime.jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: ["Nodes: ", octree.nodeCount, " | Objects: ", octree.objectCount] }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: octree.showDebug, onChange: (e) => octree.setShowDebug(e.target.checked) }), t('octree.showDebugWireframes', 'Show debug wireframes')] })] }));
|
|
362
364
|
};
|
|
363
365
|
|
|
364
366
|
exports.NiceOctree = NiceOctree;
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -282,19 +283,20 @@ function usePhysicsSimulation(scene, onSimulationStep) {
|
|
|
282
283
|
}
|
|
283
284
|
/* ── Component ── */
|
|
284
285
|
const NicePhysicsSimulation = ({ scene, selectedObject, onSimulationStep, className = '', }) => {
|
|
286
|
+
const { t } = i18n.useNiceTranslation();
|
|
285
287
|
const { worldConfig, setWorldConfig, bodies, addBody, removeBody, updateBody, getBodyForObject, isRunning, play, pause, step, reset, debugDraw, setDebugDraw, } = usePhysicsSimulation(scene, onSimulationStep);
|
|
286
288
|
const selectedBody = selectedObject
|
|
287
289
|
? getBodyForObject(selectedObject.name || selectedObject.uuid)
|
|
288
290
|
: undefined;
|
|
289
|
-
return (jsxRuntime.jsxs("div", { className: `nice-physics-simulation ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }), " Physics
|
|
291
|
+
return (jsxRuntime.jsxs("div", { className: `nice-physics-simulation ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }), " ", t('physicsSimulation.physics', 'Physics')] }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 11 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debugDraw, onChange: (e) => setDebugDraw(e.target.checked) }), t('physicsSimulation.debug', 'Debug')] })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsxRuntime.jsx("button", { type: "button", onClick: isRunning ? pause : play, style: {
|
|
290
292
|
flex: 1,
|
|
291
293
|
background: isRunning ? 'var(--nice-danger, #ef4444)' : 'var(--nice-success, #22c55e)',
|
|
292
|
-
color: 'var(--nice-
|
|
294
|
+
color: 'var(--nice-color-white, #fff)',
|
|
293
295
|
border: 'none',
|
|
294
296
|
borderRadius: 4,
|
|
295
297
|
padding: '5px 8px',
|
|
296
298
|
cursor: 'pointer',
|
|
297
|
-
}, children: isRunning ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "pause", size: 14 }), " Pause
|
|
299
|
+
}, children: isRunning ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "pause", size: 14 }), " ", t('physicsSimulation.pause', 'Pause')] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "play", size: 14 }), " ", t('physicsSimulation.play', 'Play')] })) }), jsxRuntime.jsxs("button", { type: "button", onClick: step, disabled: isRunning, style: { padding: '5px 8px', cursor: 'pointer' }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "skip-forward", size: 14 }), " ", t('physicsSimulation.step', 'Step')] }), jsxRuntime.jsxs("button", { type: "button", onClick: reset, style: { padding: '5px 8px', cursor: 'pointer' }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "refresh", size: 14 }), " ", t('physicsSimulation.reset', 'Reset')] })] }), jsxRuntime.jsxs("label", { children: ["Gravity Y: ", worldConfig.gravity.y.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: -20, max: 0, step: 0.1, value: worldConfig.gravity.y, onChange: (e) => setWorldConfig({ gravity: new THREE__namespace.Vector3(0, parseFloat(e.target.value), 0) }), style: { width: '100%' } })] }), selectedObject && (jsxRuntime.jsxs("fieldset", { style: {
|
|
298
300
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
299
301
|
borderRadius: 6,
|
|
300
302
|
padding: 6,
|
|
@@ -307,7 +309,7 @@ const NicePhysicsSimulation = ({ scene, selectedObject, onSimulationStep, classN
|
|
|
307
309
|
borderRadius: 4,
|
|
308
310
|
padding: '5px 8px',
|
|
309
311
|
cursor: 'pointer',
|
|
310
|
-
}, children: "+ Add Rigid Body" })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("label", { children: ["Type:", jsxRuntime.jsxs("select", { value: selectedBody.bodyType, onChange: (e) => updateBody(selectedBody.id, { bodyType: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsxRuntime.jsx("option", { value: "dynamic", children:
|
|
312
|
+
}, children: "+ Add Rigid Body" })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("label", { children: ["Type:", jsxRuntime.jsxs("select", { value: selectedBody.bodyType, onChange: (e) => updateBody(selectedBody.id, { bodyType: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsxRuntime.jsx("option", { value: "dynamic", children: t('physicsSimulation.dynamic', 'Dynamic') }), jsxRuntime.jsx("option", { value: "static", children: t('physicsSimulation.static', 'Static') }), jsxRuntime.jsx("option", { value: "kinematic", children: t('physicsSimulation.kinematic', 'Kinematic') })] })] }), jsxRuntime.jsxs("label", { children: ["Shape:", jsxRuntime.jsxs("select", { value: selectedBody.colliderShape, onChange: (e) => updateBody(selectedBody.id, { colliderShape: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsxRuntime.jsx("option", { value: "box", children: "Box" }), jsxRuntime.jsx("option", { value: "sphere", children: t('physicsSimulation.sphere', 'Sphere') }), jsxRuntime.jsx("option", { value: "capsule", children: t('physicsSimulation.capsule', 'Capsule') }), jsxRuntime.jsx("option", { value: "convexHull", children: t('physicsSimulation.convexHull', 'Convex Hull') }), jsxRuntime.jsx("option", { value: "trimesh", children: t('physicsSimulation.trimesh', 'Trimesh') })] })] }), jsxRuntime.jsxs("label", { children: ["Mass: ", selectedBody.mass.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: 0.1, max: 100, step: 0.1, value: selectedBody.mass, onChange: (e) => updateBody(selectedBody.id, { mass: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Restitution: ", selectedBody.restitution.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: selectedBody.restitution, onChange: (e) => updateBody(selectedBody.id, { restitution: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Friction: ", selectedBody.friction.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: selectedBody.friction, onChange: (e) => updateBody(selectedBody.id, { friction: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: selectedBody.isSensor, onChange: (e) => updateBody(selectedBody.id, { isSensor: e.target.checked }) }), "Sensor (trigger only)"] }), jsxRuntime.jsx("button", { type: "button", onClick: () => removeBody(selectedBody.id), style: {
|
|
311
313
|
background: 'var(--nice-danger, #ef4444)',
|
|
312
314
|
color: 'var(--nice-text-on-primary, #fff)',
|
|
313
315
|
border: 'none',
|
|
@@ -315,7 +317,7 @@ const NicePhysicsSimulation = ({ scene, selectedObject, onSimulationStep, classN
|
|
|
315
317
|
padding: '4px 8px',
|
|
316
318
|
cursor: 'pointer',
|
|
317
319
|
fontSize: 11,
|
|
318
|
-
}, children:
|
|
320
|
+
}, children: t('physicsSimulation.removeRigidBody', 'Remove Rigid Body') })] }))] })), jsxRuntime.jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: ["Total bodies: ", bodies.length] })] }));
|
|
319
321
|
};
|
|
320
322
|
|
|
321
323
|
exports.NicePhysicsSimulation = NicePhysicsSimulation;
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -313,21 +314,22 @@ function useProceduralGeometry(scene, onGenerated) {
|
|
|
313
314
|
}
|
|
314
315
|
/* ── Component ── */
|
|
315
316
|
const NiceProceduralGeometry = ({ scene, onGenerated, className = '', }) => {
|
|
317
|
+
const { t } = i18n.useNiceTranslation();
|
|
316
318
|
const { type, setType, treeParams, setTreeParams, rockParams, setRockParams, buildingParams, setBuildingParams, gearParams, setGearParams, generate, } = useProceduralGeometry(scene, onGenerated);
|
|
317
|
-
return (jsxRuntime.jsxs("div", { className: `nice-procedural-geometry ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("strong", { children:
|
|
319
|
+
return (jsxRuntime.jsxs("div", { className: `nice-procedural-geometry ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("strong", { children: t('proceduralGeometry.proceduralGeometry', 'Procedural Geometry') }), jsxRuntime.jsxs("select", { value: type, onChange: (e) => setType(e.target.value), style: { width: '100%', padding: '4px 8px' }, children: [jsxRuntime.jsx("option", { value: "tree", children: t('proceduralGeometry.tree', 'Tree') }), jsxRuntime.jsx("option", { value: "rock", children: t('proceduralGeometry.rock', 'Rock') }), jsxRuntime.jsx("option", { value: "building", children: t('proceduralGeometry.building', 'Building') }), jsxRuntime.jsx("option", { value: "pipe", children: t('proceduralGeometry.pipe', 'Pipe') }), jsxRuntime.jsx("option", { value: "gear", children: t('proceduralGeometry.gear', 'Gear') })] }), type === 'tree' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Height: ", treeParams.trunkHeight.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: 1, max: 15, step: 0.5, value: treeParams.trunkHeight, onChange: (e) => setTreeParams({ ...treeParams, trunkHeight: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Branches: ", treeParams.branches, jsxRuntime.jsx("input", { type: "range", min: 1, max: 6, value: treeParams.branches, onChange: (e) => setTreeParams({ ...treeParams, branches: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Depth: ", treeParams.depth, jsxRuntime.jsx("input", { type: "range", min: 1, max: 5, value: treeParams.depth, onChange: (e) => setTreeParams({ ...treeParams, depth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Angle: ", treeParams.branchAngle, "\u00B0", jsxRuntime.jsx("input", { type: "range", min: 10, max: 80, value: treeParams.branchAngle, onChange: (e) => setTreeParams({ ...treeParams, branchAngle: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Seed: ", treeParams.seed, jsxRuntime.jsx("input", { type: "range", min: 1, max: 999, value: treeParams.seed, onChange: (e) => setTreeParams({ ...treeParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'rock' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Radius: ", rockParams.radius.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: 0.2, max: 5, step: 0.1, value: rockParams.radius, onChange: (e) => setRockParams({ ...rockParams, radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Detail: ", rockParams.detail, jsxRuntime.jsx("input", { type: "range", min: 1, max: 5, value: rockParams.detail, onChange: (e) => setRockParams({ ...rockParams, detail: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Noise: ", rockParams.noiseAmount.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0, max: 0.5, step: 0.01, value: rockParams.noiseAmount, onChange: (e) => setRockParams({ ...rockParams, noiseAmount: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Seed: ", rockParams.seed, jsxRuntime.jsx("input", { type: "range", min: 1, max: 999, value: rockParams.seed, onChange: (e) => setRockParams({ ...rockParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'building' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Width: ", buildingParams.width, jsxRuntime.jsx("input", { type: "range", min: 4, max: 20, step: 1, value: buildingParams.width, onChange: (e) => setBuildingParams({ ...buildingParams, width: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Floors: ", buildingParams.floors, jsxRuntime.jsx("input", { type: "range", min: 1, max: 20, value: buildingParams.floors, onChange: (e) => setBuildingParams({ ...buildingParams, floors: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Windows/Floor: ", buildingParams.windowsPerFloor, jsxRuntime.jsx("input", { type: "range", min: 1, max: 10, value: buildingParams.windowsPerFloor, onChange: (e) => setBuildingParams({
|
|
318
320
|
...buildingParams,
|
|
319
321
|
windowsPerFloor: parseInt(e.target.value, 10),
|
|
320
322
|
}), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Roof:", jsxRuntime.jsxs("select", { value: buildingParams.roofStyle, onChange: (e) => setBuildingParams({
|
|
321
323
|
...buildingParams,
|
|
322
324
|
roofStyle: e.target.value,
|
|
323
|
-
}), style: { marginLeft: 8 }, children: [jsxRuntime.jsx("option", { value: "flat", children:
|
|
325
|
+
}), style: { marginLeft: 8 }, children: [jsxRuntime.jsx("option", { value: "flat", children: t('proceduralGeometry.flat', 'Flat') }), jsxRuntime.jsx("option", { value: "gable", children: t('proceduralGeometry.gable', 'Gable') }), jsxRuntime.jsx("option", { value: "hip", children: "Hip" })] })] })] })), type === 'gear' && (jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Outer Radius: ", gearParams.outerRadius.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: 0.5, max: 5, step: 0.1, value: gearParams.outerRadius, onChange: (e) => setGearParams({ ...gearParams, outerRadius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Teeth: ", gearParams.teeth, jsxRuntime.jsx("input", { type: "range", min: 4, max: 30, value: gearParams.teeth, onChange: (e) => setGearParams({ ...gearParams, teeth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Tooth Depth: ", gearParams.toothDepth.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0.05, max: 1, step: 0.05, value: gearParams.toothDepth, onChange: (e) => setGearParams({ ...gearParams, toothDepth: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Thickness: ", gearParams.thickness.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0.1, max: 2, step: 0.1, value: gearParams.thickness, onChange: (e) => setGearParams({ ...gearParams, thickness: parseFloat(e.target.value) }), style: { width: '100%' } })] })] })), jsxRuntime.jsxs("button", { type: "button", onClick: generate, style: {
|
|
324
326
|
background: 'var(--nice-color-accent, #6366f1)',
|
|
325
327
|
color: 'var(--nice-text-on-primary, #fff)',
|
|
326
328
|
border: 'none',
|
|
327
329
|
borderRadius: 4,
|
|
328
330
|
padding: '6px 12px',
|
|
329
331
|
cursor: 'pointer',
|
|
330
|
-
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "wand", size: 14 }), " Generate
|
|
332
|
+
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "wand", size: 14 }), " ", t('proceduralGeometry.generate', 'Generate')] })] }));
|
|
331
333
|
};
|
|
332
334
|
|
|
333
335
|
exports.NiceProceduralGeometry = NiceProceduralGeometry;
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -340,6 +341,7 @@ function useTerrainEditor(scene, camera, canvas, config = { width: 100, depth: 1
|
|
|
340
341
|
}
|
|
341
342
|
/* ── Component ── */
|
|
342
343
|
const NiceTerrainEditor = ({ scene, camera, canvas, config: configProp, onTerrainChange, className = '', tutorial, }) => {
|
|
344
|
+
const { t } = i18n.useNiceTranslation();
|
|
343
345
|
const config = {
|
|
344
346
|
width: 100,
|
|
345
347
|
depth: 100,
|
|
@@ -350,43 +352,43 @@ const NiceTerrainEditor = ({ scene, camera, canvas, config: configProp, onTerrai
|
|
|
350
352
|
const { brush, setBrush, isPainting, startPainting, stopPainting, generateFlat, generateNoise, exportHeightmap, textureLayers, setTextureLayer, activeTextureLayer, setActiveTextureLayer, } = useTerrainEditor(scene, camera, canvas, config, onTerrainChange);
|
|
351
353
|
const [noiseOctaves, setNoiseOctaves] = React.useState(6);
|
|
352
354
|
const [noisePersistence, setNoisePersistence] = React.useState(0.5);
|
|
353
|
-
return (jsxRuntime.jsxs("div", { className: `nice-terrain-editor ${className}`, style: { position: 'relative', display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 0, right: 0, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, TERRAIN_EDITOR_TOUR) }) }), jsxRuntime.jsx("strong", { children:
|
|
355
|
+
return (jsxRuntime.jsxs("div", { className: `nice-terrain-editor ${className}`, style: { position: 'relative', display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 0, right: 0, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, TERRAIN_EDITOR_TOUR) }) }), jsxRuntime.jsx("strong", { children: t('terrainEditor.terrainEditor', 'Terrain Editor') }), jsxRuntime.jsxs("fieldset", { style: {
|
|
354
356
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
355
357
|
borderRadius: 6,
|
|
356
358
|
padding: 4,
|
|
357
359
|
display: 'flex',
|
|
358
360
|
flexWrap: 'wrap',
|
|
359
361
|
gap: 2,
|
|
360
|
-
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children:
|
|
362
|
+
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children: t('terrainEditor.brush', 'Brush') }), ['raise', 'lower', 'flatten', 'smooth', 'noise', 'paint'].map((m) => (jsxRuntime.jsx("button", { type: "button", onClick: () => setBrush({ mode: m }), style: {
|
|
361
363
|
padding: '3px 6px',
|
|
362
364
|
fontSize: 11,
|
|
363
365
|
cursor: 'pointer',
|
|
364
366
|
borderRadius: 4,
|
|
365
367
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
366
368
|
background: brush.mode === m ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
|
|
367
|
-
color: brush.mode === m ? 'var(--nice-
|
|
369
|
+
color: brush.mode === m ? 'var(--nice-color-white, #fff)' : 'inherit',
|
|
368
370
|
}, children: m }, m)))] }), jsxRuntime.jsxs("label", { children: ["Radius: ", brush.radius.toFixed(1), jsxRuntime.jsx("input", { type: "range", min: 0.5, max: 20, step: 0.5, value: brush.radius, onChange: (e) => setBrush({ radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Strength: ", brush.strength.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0.01, max: 1, step: 0.01, value: brush.strength, onChange: (e) => setBrush({ strength: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxRuntime.jsx("button", { type: "button", onClick: isPainting ? stopPainting : startPainting, style: {
|
|
369
371
|
background: isPainting
|
|
370
372
|
? 'var(--nice-danger, #ef4444)'
|
|
371
373
|
: 'var(--nice-color-accent, #6366f1)',
|
|
372
|
-
color: 'var(--nice-
|
|
374
|
+
color: 'var(--nice-color-white, #fff)',
|
|
373
375
|
border: 'none',
|
|
374
376
|
borderRadius: 4,
|
|
375
377
|
padding: '6px 12px',
|
|
376
378
|
cursor: 'pointer',
|
|
377
|
-
}, children: isPainting ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }), " Stop Painting
|
|
379
|
+
}, children: isPainting ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }), " ", t('terrainEditor.stopPainting', 'Stop Painting')] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "brush", size: 14 }), " ", t('terrainEditor.startPainting', 'Start Painting')] })) }), jsxRuntime.jsxs("fieldset", { style: {
|
|
378
380
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
379
381
|
borderRadius: 6,
|
|
380
382
|
padding: 6,
|
|
381
383
|
display: 'grid',
|
|
382
384
|
gap: 4,
|
|
383
|
-
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children:
|
|
385
|
+
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children: t('terrainEditor.procedural', 'Procedural') }), jsxRuntime.jsxs("label", { children: ["Octaves: ", noiseOctaves, jsxRuntime.jsx("input", { type: "range", min: 1, max: 10, value: noiseOctaves, onChange: (e) => setNoiseOctaves(parseInt(e.target.value, 10)), style: { width: '100%' } })] }), jsxRuntime.jsxs("label", { children: ["Persistence: ", noisePersistence.toFixed(2), jsxRuntime.jsx("input", { type: "range", min: 0.1, max: 1, step: 0.05, value: noisePersistence, onChange: (e) => setNoisePersistence(parseFloat(e.target.value)), style: { width: '100%' } })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsxRuntime.jsxs("button", { type: "button", onClick: () => generateNoise(noiseOctaves, noisePersistence), style: { flex: 1 }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "mountain", size: 14 }), ' ', t('terrainEditor.generateNoise', 'Generate Noise')] }), jsxRuntime.jsxs("button", { type: "button", onClick: generateFlat, style: { flex: 1 }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "square", size: 14 }), " ", t('terrainEditor.flatten', 'Flatten')] })] })] }), brush.mode === 'paint' && (jsxRuntime.jsxs("fieldset", { style: {
|
|
384
386
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
385
387
|
borderRadius: 6,
|
|
386
388
|
padding: 6,
|
|
387
389
|
display: 'grid',
|
|
388
390
|
gap: 4,
|
|
389
|
-
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children:
|
|
391
|
+
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children: t('terrainEditor.textureLayers', 'Texture Layers') }), textureLayers.map((layer, i) => (jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "radio", name: "texLayer", checked: activeTextureLayer === i, onChange: () => setActiveTextureLayer(i) }), jsxRuntime.jsx("input", { type: "text", value: layer.name, onChange: (e) => setTextureLayer(i, { name: e.target.value }), style: {
|
|
390
392
|
flex: 1,
|
|
391
393
|
padding: '2px 6px',
|
|
392
394
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
@@ -399,7 +401,7 @@ const NiceTerrainEditor = ({ scene, camera, canvas, config: configProp, onTerrai
|
|
|
399
401
|
link.href = c.toDataURL('image/png');
|
|
400
402
|
link.click();
|
|
401
403
|
}
|
|
402
|
-
}, style: { fontSize: 11 }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "download", size: 14 }),
|
|
404
|
+
}, style: { fontSize: 11 }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "download", size: 14 }), ' ', t('terrainEditor.exportHeightmapPng', 'Export Heightmap PNG')] })] }));
|
|
403
405
|
};
|
|
404
406
|
|
|
405
407
|
exports.NiceTerrainEditor = NiceTerrainEditor;
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -246,6 +247,7 @@ function updateHeatMapColors(mesh, boneIndex, colors) {
|
|
|
246
247
|
}
|
|
247
248
|
/* ── Component ── */
|
|
248
249
|
const NiceWeightPainter = ({ skinnedMesh, activeBoneIndex: initialBoneIndex, scene, camera, canvas, onWeightsChange, className = '', }) => {
|
|
250
|
+
const { t } = i18n.useNiceTranslation();
|
|
249
251
|
const { brush, setBrush, isPainting, activeBoneIndex, setActiveBoneIndex, boneNames, startPainting, stopPainting, normalizeWeights, showHeatMap, setShowHeatMap, } = useWeightPainter(skinnedMesh, scene, camera, canvas, onWeightsChange);
|
|
250
252
|
React.useEffect(() => {
|
|
251
253
|
setActiveBoneIndex(initialBoneIndex);
|
|
@@ -256,11 +258,11 @@ const NiceWeightPainter = ({ skinnedMesh, activeBoneIndex: initialBoneIndex, sce
|
|
|
256
258
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
257
259
|
borderRadius: 6,
|
|
258
260
|
padding: 4,
|
|
259
|
-
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children:
|
|
261
|
+
}, children: [jsxRuntime.jsx("legend", { style: { fontSize: 11 }, children: t('weightPainter.mode', 'Mode') }), ['add', 'subtract', 'smooth', 'blur'].map((mode) => (jsxRuntime.jsx("button", { type: "button", onClick: () => setBrush({ mode }), style: {
|
|
260
262
|
flex: 1,
|
|
261
263
|
padding: '4px 6px',
|
|
262
264
|
background: brush.mode === mode ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
|
|
263
|
-
color: brush.mode === mode ? 'var(--nice-
|
|
265
|
+
color: brush.mode === mode ? 'var(--nice-color-white, #fff)' : 'inherit',
|
|
264
266
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
265
267
|
borderRadius: 4,
|
|
266
268
|
cursor: 'pointer',
|
|
@@ -270,12 +272,12 @@ const NiceWeightPainter = ({ skinnedMesh, activeBoneIndex: initialBoneIndex, sce
|
|
|
270
272
|
background: isPainting
|
|
271
273
|
? 'var(--nice-danger, #ef4444)'
|
|
272
274
|
: 'var(--nice-color-accent, #6366f1)',
|
|
273
|
-
color: 'var(--nice-
|
|
275
|
+
color: 'var(--nice-color-white, #fff)',
|
|
274
276
|
border: 'none',
|
|
275
277
|
borderRadius: 4,
|
|
276
278
|
padding: '6px 12px',
|
|
277
279
|
cursor: 'pointer',
|
|
278
|
-
}, children: isPainting ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }), " Stop Painting
|
|
280
|
+
}, children: isPainting ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }), " ", t('weightPainter.stopPainting', 'Stop Painting')] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "brush", size: 14 }), ' ', t('weightPainter.startPainting', 'Start Painting')] })) }), jsxRuntime.jsxs("button", { type: "button", onClick: normalizeWeights, title: t('weightPainter.normalizeAllWeights', 'Normalize all weights'), children: [jsxRuntime.jsx(ui.NiceIcon, { name: "scale", size: 14 }), " ", t('weightPainter.normalize', 'Normalize')] })] }), jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showHeatMap, onChange: (e) => setShowHeatMap(e.target.checked) }), t('weightPainter.showWeightHeatMap', 'Show weight heat map')] })] }));
|
|
279
281
|
};
|
|
280
282
|
|
|
281
283
|
exports.NiceWeightPainter = NiceWeightPainter;
|
|
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var ui = require('@nice2dev/ui');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var THREE = require('three');
|
|
7
|
+
var i18n = require('../core/i18n.js');
|
|
7
8
|
|
|
8
9
|
function _interopNamespaceDefault(e) {
|
|
9
10
|
var n = Object.create(null);
|
|
@@ -280,19 +281,20 @@ function useXRPreview(renderer, scene, camera, onSessionStart, onSessionEnd) {
|
|
|
280
281
|
}
|
|
281
282
|
/* ── Component ── */
|
|
282
283
|
const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionStart, onSessionEnd, className = '', }) => {
|
|
284
|
+
const { t } = i18n.useNiceTranslation();
|
|
283
285
|
const { isSupported, isSessionActive, mode, setMode, startSession, endSession, controllers, teleportEnabled, setTeleportEnabled, hitTestResult, } = useXRPreview(renderer, scene, camera, onSessionStart, onSessionEnd);
|
|
284
286
|
React.useEffect(() => {
|
|
285
287
|
if (initialMode) {
|
|
286
288
|
setMode(initialMode);
|
|
287
289
|
}
|
|
288
290
|
}, [initialMode, setMode]);
|
|
289
|
-
return (jsxRuntime.jsxs("div", { className: `nice-xr-preview ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "eye", size: 14 }), " XR Preview
|
|
291
|
+
return (jsxRuntime.jsxs("div", { className: `nice-xr-preview ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "eye", size: 14 }), " ", t('xrPreview.xrPreview', 'XR Preview')] }), !isSupported && (jsxRuntime.jsx("div", { style: { color: 'var(--nice-danger, #ef4444)', fontSize: 11 }, children: t('xrPreview.webxrIsNotSupportedIn', 'WebXR is not supported in this browser.') })), isSupported && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsxRuntime.jsxs("button", { type: "button", onClick: () => setMode('vr'), style: {
|
|
290
292
|
flex: 1,
|
|
291
293
|
padding: '5px 8px',
|
|
292
294
|
borderRadius: 4,
|
|
293
295
|
cursor: 'pointer',
|
|
294
296
|
background: mode === 'vr' ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
|
|
295
|
-
color: mode === 'vr' ? 'var(--nice-
|
|
297
|
+
color: mode === 'vr' ? 'var(--nice-color-white, #fff)' : 'inherit',
|
|
296
298
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
297
299
|
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "eye", size: 14 }), " VR"] }), jsxRuntime.jsxs("button", { type: "button", onClick: () => setMode('ar'), style: {
|
|
298
300
|
flex: 1,
|
|
@@ -300,18 +302,18 @@ const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionSt
|
|
|
300
302
|
borderRadius: 4,
|
|
301
303
|
cursor: 'pointer',
|
|
302
304
|
background: mode === 'ar' ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
|
|
303
|
-
color: mode === 'ar' ? 'var(--nice-
|
|
305
|
+
color: mode === 'ar' ? 'var(--nice-color-white, #fff)' : 'inherit',
|
|
304
306
|
border: '1px solid var(--nice-border, #e2e8f0)',
|
|
305
307
|
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "window", size: 14 }), " AR"] })] }), jsxRuntime.jsx("button", { type: "button", onClick: isSessionActive ? endSession : startSession, style: {
|
|
306
308
|
background: isSessionActive
|
|
307
309
|
? 'var(--nice-danger, #ef4444)'
|
|
308
310
|
: 'var(--nice-success, #22c55e)',
|
|
309
|
-
color: 'var(--nice-
|
|
311
|
+
color: 'var(--nice-color-white, #fff)',
|
|
310
312
|
border: 'none',
|
|
311
313
|
borderRadius: 4,
|
|
312
314
|
padding: '6px 12px',
|
|
313
315
|
cursor: 'pointer',
|
|
314
|
-
}, children: isSessionActive ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }), " Exit XR
|
|
316
|
+
}, children: isSessionActive ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }), " ", t('xrPreview.exitXr', 'Exit XR')] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ui.NiceIcon, { name: "play", size: 14 }), " ", t('xrPreview.enterXr', 'Enter XR')] })) }), mode === 'vr' && (jsxRuntime.jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsxRuntime.jsx("input", { type: "checkbox", checked: teleportEnabled, onChange: (e) => setTeleportEnabled(e.target.checked) }), t('xrPreview.teleportation', 'Teleportation')] })), isSessionActive && (jsxRuntime.jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: ["Controllers: ", controllers.length, jsxRuntime.jsx("br", {}), mode === 'ar' && hitTestResult && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["Hit: (", hitTestResult.x.toFixed(2), ", ", hitTestResult.y.toFixed(2), ",", ' ', hitTestResult.z.toFixed(2), ")"] }))] }))] }))] }));
|
|
315
317
|
};
|
|
316
318
|
|
|
317
319
|
exports.NiceXRPreview = NiceXRPreview;
|
|
@@ -23,6 +23,8 @@ var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
|
|
|
23
23
|
var SVGLoader_js = require('three/examples/jsm/loaders/SVGLoader.js');
|
|
24
24
|
var TDSLoader_js = require('three/examples/jsm/loaders/TDSLoader.js');
|
|
25
25
|
var VTKLoader_js = require('three/examples/jsm/loaders/VTKLoader.js');
|
|
26
|
+
var editorAsk = require('../core/editorAsk.js');
|
|
27
|
+
var threeCompat = require('../core/threeCompat.js');
|
|
26
28
|
var modelEditorTypes = require('./modelEditorTypes.js');
|
|
27
29
|
var modelEditorUtils = require('./modelEditorUtils.js');
|
|
28
30
|
|
|
@@ -52,7 +54,7 @@ var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
|
|
|
52
54
|
*
|
|
53
55
|
* Extracted from the original ModelEditor.tsx god-component.
|
|
54
56
|
*/
|
|
55
|
-
const log = { error: console.error, warn: console.warn
|
|
57
|
+
const log = { error: console.error, warn: console.warn };
|
|
56
58
|
/* helpers */
|
|
57
59
|
let _nid = 0;
|
|
58
60
|
const nid = () => `n${++_nid}`;
|
|
@@ -73,6 +75,8 @@ function findNodeById(nodes, id) {
|
|
|
73
75
|
Hook
|
|
74
76
|
═══════════════════════════════════════════ */
|
|
75
77
|
function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
78
|
+
var _a;
|
|
79
|
+
const { pendingAsk, ask } = editorAsk.useEditorAsk();
|
|
76
80
|
/* ── refs ── */
|
|
77
81
|
const mountRef = React.useRef(null);
|
|
78
82
|
const rendererRef = React.useRef(null);
|
|
@@ -80,7 +84,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
80
84
|
const cameraRef = React.useRef(new THREE__namespace.PerspectiveCamera(50, 1, 0.01, 10000));
|
|
81
85
|
const orbitRef = React.useRef(null);
|
|
82
86
|
const transformRef = React.useRef(null);
|
|
83
|
-
|
|
87
|
+
// Created once: useRef's argument runs on every render, and three r183 warns per Clock.
|
|
88
|
+
const clockRef = React.useRef(null);
|
|
89
|
+
(_a = clockRef.current) !== null && _a !== void 0 ? _a : (clockRef.current = threeCompat.createFrameClock());
|
|
84
90
|
const mixerRef = React.useRef(null);
|
|
85
91
|
const rafRef = React.useRef(0);
|
|
86
92
|
const fileInputRef = React.useRef(null);
|
|
@@ -160,7 +166,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
160
166
|
const renderer = new THREE__namespace.WebGLRenderer({ antialias: true, alpha: false });
|
|
161
167
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
162
168
|
renderer.shadowMap.enabled = true;
|
|
163
|
-
renderer.shadowMap.type =
|
|
169
|
+
renderer.shadowMap.type = threeCompat.SOFT_SHADOW_MAP;
|
|
164
170
|
renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping;
|
|
165
171
|
renderer.toneMappingExposure = 1.2;
|
|
166
172
|
renderer.outputColorSpace = THREE__namespace.SRGBColorSpace;
|
|
@@ -221,10 +227,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
221
227
|
// goes into the scene is its helper.
|
|
222
228
|
const tcHelper = tc.getHelper();
|
|
223
229
|
tcHelper.name = '__transformControls';
|
|
224
|
-
|
|
225
|
-
tc.addEventListener('dragging-changed', ((e) => {
|
|
230
|
+
tc.addEventListener('dragging-changed', (e) => {
|
|
226
231
|
orbit.enabled = !e.value;
|
|
227
|
-
})
|
|
232
|
+
});
|
|
228
233
|
scene.add(tcHelper);
|
|
229
234
|
transformRef.current = tc;
|
|
230
235
|
// Resize handler
|
|
@@ -243,8 +248,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
243
248
|
onResize();
|
|
244
249
|
// Animation loop
|
|
245
250
|
const animate = () => {
|
|
251
|
+
var _a, _b;
|
|
246
252
|
rafRef.current = requestAnimationFrame(animate);
|
|
247
|
-
const dt = clockRef.current.getDelta();
|
|
253
|
+
const dt = (_b = (_a = clockRef.current) === null || _a === void 0 ? void 0 : _a.getDelta()) !== null && _b !== void 0 ? _b : 0;
|
|
248
254
|
orbit.update();
|
|
249
255
|
// Update mixer
|
|
250
256
|
if (mixerRef.current && isPlayingRef.current) {
|
|
@@ -1242,11 +1248,8 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1242
1248
|
}
|
|
1243
1249
|
});
|
|
1244
1250
|
}, []);
|
|
1245
|
-
const
|
|
1251
|
+
const clearSceneNow = React.useCallback(() => {
|
|
1246
1252
|
var _a;
|
|
1247
|
-
if (!window.confirm('Clear the entire scene? This cannot be undone.')) {
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
1253
|
const scene = sceneRef.current;
|
|
1251
1254
|
const toRemove = [];
|
|
1252
1255
|
scene.traverse((obj) => {
|
|
@@ -1278,6 +1281,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1278
1281
|
rebuildTree();
|
|
1279
1282
|
setStatusText('Scene cleared');
|
|
1280
1283
|
}, [rebuildTree, disposeObject]);
|
|
1284
|
+
// asks first: clearing cannot be undone
|
|
1285
|
+
const clearScene = React.useCallback(() => {
|
|
1286
|
+
void ask('confirm', 'Clear the entire scene? This cannot be undone.').then((answer) => {
|
|
1287
|
+
if (answer !== null) {
|
|
1288
|
+
clearSceneNow();
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
}, [ask, clearSceneNow]);
|
|
1281
1292
|
/* ─────────────────────────────────────────
|
|
1282
1293
|
Save to Library
|
|
1283
1294
|
───────────────────────────────────────── */
|
|
@@ -1295,19 +1306,19 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1295
1306
|
const blob = new Blob([result], { type: 'model/gltf-binary' });
|
|
1296
1307
|
const reader = new FileReader();
|
|
1297
1308
|
reader.onload = () => {
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1309
|
+
const dataUrl = reader.result;
|
|
1310
|
+
void ask('prompt', 'Save to library as:', 'model.glb').then((name) => {
|
|
1311
|
+
if (name && name.trim()) {
|
|
1312
|
+
onSaveToLibrary(dataUrl, name.trim(), 'model/gltf-binary');
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1304
1315
|
};
|
|
1305
1316
|
reader.readAsDataURL(blob);
|
|
1306
1317
|
}, (err) => {
|
|
1307
1318
|
log.error('Save to library error:', err);
|
|
1308
1319
|
setStatusText(`Save error: ${String(err)}`);
|
|
1309
1320
|
}, { binary: true, animations: allClipsRef.current.map((a) => a.clip) });
|
|
1310
|
-
}, [onSaveToLibrary]);
|
|
1321
|
+
}, [onSaveToLibrary, ask]);
|
|
1311
1322
|
/* ─────────────────────────────────────────
|
|
1312
1323
|
Add Mesh / Light / Camera / Empty
|
|
1313
1324
|
───────────────────────────────────────── */
|
|
@@ -1803,6 +1814,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1803
1814
|
bgColor,
|
|
1804
1815
|
setBgColor,
|
|
1805
1816
|
statusText,
|
|
1817
|
+
pendingAsk,
|
|
1806
1818
|
dragOver,
|
|
1807
1819
|
polyCount,
|
|
1808
1820
|
meshCount,
|
|
@@ -10,6 +10,7 @@ var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
|
10
10
|
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
11
11
|
var PLYLoader_js = require('three/examples/jsm/loaders/PLYLoader.js');
|
|
12
12
|
var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
|
|
13
|
+
var threeCompat = require('../core/threeCompat.js');
|
|
13
14
|
|
|
14
15
|
function _interopNamespaceDefault(e) {
|
|
15
16
|
var n = Object.create(null);
|
|
@@ -60,6 +61,7 @@ const TONE_MAP = {
|
|
|
60
61
|
Hook
|
|
61
62
|
═══════════════════════════════════════════ */
|
|
62
63
|
function useModelViewer(props = {}) {
|
|
64
|
+
var _a;
|
|
63
65
|
const { src, format, navigationMode: initNavMode = 'orbit', quality = 'high', autoRotateSpeed: initAutoRotate = 0, backgroundColor = 'var(--nice-bg, #1a1a2e)', showGrid: initShowGrid = true, shadows = true, gamepad = false, cameraPresets: _cameraPresets = [], annotations: _annotations = [], hotspots: _hotspots = [], floorPlans: _floorPlans = [], onHotspotClick: _onHotspotClick, onAnnotationClick: _onAnnotationClick, onLoadProgress, onLoad, onError, clippingPlanes = [], measureMode: initMeasureMode = false, measurements: _measurements = [], onMeasure: _onMeasure, minDistance = 0.1, maxDistance = 1000, fov = 50, vr: _vr = false, walkSpeed = 5, ambientIntensity = 0.5, directionalIntensity = 0.8, antiAlias = true, toneMapping = 'aces', exposure = 1, } = props;
|
|
64
66
|
/* ── refs ── */
|
|
65
67
|
const mountRef = React.useRef(null);
|
|
@@ -67,7 +69,9 @@ function useModelViewer(props = {}) {
|
|
|
67
69
|
const sceneRef = React.useRef(new THREE__namespace.Scene());
|
|
68
70
|
const cameraRef = React.useRef(new THREE__namespace.PerspectiveCamera(fov, 1, 0.01, 10000));
|
|
69
71
|
const orbitRef = React.useRef(null);
|
|
70
|
-
|
|
72
|
+
// Created once: useRef's argument runs on every render, and three r183 warns per Clock.
|
|
73
|
+
const clockRef = React.useRef(null);
|
|
74
|
+
(_a = clockRef.current) !== null && _a !== void 0 ? _a : (clockRef.current = threeCompat.createFrameClock());
|
|
71
75
|
const mixerRef = React.useRef(null);
|
|
72
76
|
const rafRef = React.useRef(0);
|
|
73
77
|
const rootRef = React.useRef(null);
|
|
@@ -123,7 +127,7 @@ function useModelViewer(props = {}) {
|
|
|
123
127
|
renderer.outputColorSpace = THREE__namespace.SRGBColorSpace;
|
|
124
128
|
if (shadows) {
|
|
125
129
|
renderer.shadowMap.enabled = true;
|
|
126
|
-
renderer.shadowMap.type =
|
|
130
|
+
renderer.shadowMap.type = threeCompat.SOFT_SHADOW_MAP;
|
|
127
131
|
}
|
|
128
132
|
mount.appendChild(renderer.domElement);
|
|
129
133
|
rendererRef.current = renderer;
|
|
@@ -177,20 +181,20 @@ function useModelViewer(props = {}) {
|
|
|
177
181
|
ro.observe(mount);
|
|
178
182
|
// Animation loop
|
|
179
183
|
const animate = () => {
|
|
180
|
-
var _a, _b, _c, _d, _e;
|
|
184
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
181
185
|
if (disposeRef.current) {
|
|
182
186
|
return;
|
|
183
187
|
}
|
|
184
188
|
rafRef.current = requestAnimationFrame(animate);
|
|
185
|
-
const dt = clockRef.current.getDelta();
|
|
189
|
+
const dt = (_b = (_a = clockRef.current) === null || _a === void 0 ? void 0 : _a.getDelta()) !== null && _b !== void 0 ? _b : 0;
|
|
186
190
|
// Gamepad FPS-style nav
|
|
187
191
|
if (gamepad && navMode === 'fps') {
|
|
188
|
-
const pads = (
|
|
189
|
-
const gp = (
|
|
192
|
+
const pads = (_c = navigator.getGamepads) === null || _c === void 0 ? void 0 : _c.call(navigator);
|
|
193
|
+
const gp = (_d = pads === null || pads === void 0 ? void 0 : pads[0]) !== null && _d !== void 0 ? _d : null;
|
|
190
194
|
if (gp) {
|
|
191
|
-
const lx = (
|
|
192
|
-
const ly = (
|
|
193
|
-
const rx = (
|
|
195
|
+
const lx = (_e = gp.axes[0]) !== null && _e !== void 0 ? _e : 0;
|
|
196
|
+
const ly = (_f = gp.axes[1]) !== null && _f !== void 0 ? _f : 0;
|
|
197
|
+
const rx = (_g = gp.axes[2]) !== null && _g !== void 0 ? _g : 0;
|
|
194
198
|
const fwd = new THREE__namespace.Vector3();
|
|
195
199
|
camera.getWorldDirection(fwd);
|
|
196
200
|
fwd.y = 0;
|