@nice2dev/ui-3d 1.2.0 → 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 +103 -0
- package/dist/cjs/core/editorAsk.js +48 -0
- package/dist/cjs/core/i18n.js +7 -3
- package/dist/cjs/dance/DanceScoreEngine.js +7 -0
- package/dist/cjs/dance/PoseDetector.js +4 -2
- 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 -2
- 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 -4
- package/dist/cjs/model/NiceArmatureEditor.js +6 -4
- package/dist/cjs/model/NiceMorphTargetEditor.js +5 -3
- package/dist/cjs/model/NiceOctree.js +3 -1
- package/dist/cjs/model/NicePhysicsSimulation.js +6 -4
- package/dist/cjs/model/NiceProceduralGeometry.js +5 -3
- package/dist/cjs/model/NiceTerrainEditor.js +8 -6
- package/dist/cjs/model/NiceWeightPainter.js +4 -2
- package/dist/cjs/model/NiceXRPreview.js +4 -2
- package/dist/cjs/model/useModelEditor.js +22 -15
- 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 +9 -3
- package/dist/cjs/rendering/NiceSSAO.js +3 -1
- package/dist/cjs/rendering/NiceSSR.js +3 -1
- package/dist/cjs/rendering/NiceWebGPURenderer.js +4 -2
- 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/dance/DanceScoreEngine.js +7 -0
- package/dist/esm/dance/PoseDetector.js +4 -2
- 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 +3 -2
- 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 +6 -4
- package/dist/esm/model/NiceArmatureEditor.js +6 -4
- package/dist/esm/model/NiceMorphTargetEditor.js +5 -3
- package/dist/esm/model/NiceOctree.js +3 -1
- package/dist/esm/model/NicePhysicsSimulation.js +6 -4
- package/dist/esm/model/NiceProceduralGeometry.js +5 -3
- package/dist/esm/model/NiceTerrainEditor.js +8 -6
- package/dist/esm/model/NiceWeightPainter.js +4 -2
- package/dist/esm/model/NiceXRPreview.js +4 -2
- package/dist/esm/model/useModelEditor.js +22 -15
- 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 +9 -3
- package/dist/esm/rendering/NiceSSAO.js +3 -1
- package/dist/esm/rendering/NiceSSR.js +3 -1
- package/dist/esm/rendering/NiceWebGPURenderer.js +4 -2
- 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/dance/DanceScoreEngine.d.ts +0 -6
- 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,11 +283,12 @@ 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
294
|
color: 'var(--nice-color-white, #fff)',
|
|
@@ -294,7 +296,7 @@ const NicePhysicsSimulation = ({ scene, selectedObject, onSimulationStep, classN
|
|
|
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,14 +352,14 @@ 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',
|
|
@@ -374,19 +376,19 @@ const NiceTerrainEditor = ({ scene, camera, canvas, config: configProp, onTerrai
|
|
|
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,7 +258,7 @@ 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',
|
|
@@ -275,7 +277,7 @@ const NiceWeightPainter = ({ skinnedMesh, activeBoneIndex: initialBoneIndex, sce
|
|
|
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,13 +281,14 @@ 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,
|
|
@@ -311,7 +313,7 @@ const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionSt
|
|
|
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,7 @@ 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');
|
|
26
27
|
var threeCompat = require('../core/threeCompat.js');
|
|
27
28
|
var modelEditorTypes = require('./modelEditorTypes.js');
|
|
28
29
|
var modelEditorUtils = require('./modelEditorUtils.js');
|
|
@@ -53,7 +54,7 @@ var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
|
|
|
53
54
|
*
|
|
54
55
|
* Extracted from the original ModelEditor.tsx god-component.
|
|
55
56
|
*/
|
|
56
|
-
const log = { error: console.error, warn: console.warn
|
|
57
|
+
const log = { error: console.error, warn: console.warn };
|
|
57
58
|
/* helpers */
|
|
58
59
|
let _nid = 0;
|
|
59
60
|
const nid = () => `n${++_nid}`;
|
|
@@ -75,6 +76,7 @@ function findNodeById(nodes, id) {
|
|
|
75
76
|
═══════════════════════════════════════════ */
|
|
76
77
|
function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
77
78
|
var _a;
|
|
79
|
+
const { pendingAsk, ask } = editorAsk.useEditorAsk();
|
|
78
80
|
/* ── refs ── */
|
|
79
81
|
const mountRef = React.useRef(null);
|
|
80
82
|
const rendererRef = React.useRef(null);
|
|
@@ -225,10 +227,9 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
225
227
|
// goes into the scene is its helper.
|
|
226
228
|
const tcHelper = tc.getHelper();
|
|
227
229
|
tcHelper.name = '__transformControls';
|
|
228
|
-
|
|
229
|
-
tc.addEventListener('dragging-changed', ((e) => {
|
|
230
|
+
tc.addEventListener('dragging-changed', (e) => {
|
|
230
231
|
orbit.enabled = !e.value;
|
|
231
|
-
})
|
|
232
|
+
});
|
|
232
233
|
scene.add(tcHelper);
|
|
233
234
|
transformRef.current = tc;
|
|
234
235
|
// Resize handler
|
|
@@ -1247,11 +1248,8 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1247
1248
|
}
|
|
1248
1249
|
});
|
|
1249
1250
|
}, []);
|
|
1250
|
-
const
|
|
1251
|
+
const clearSceneNow = React.useCallback(() => {
|
|
1251
1252
|
var _a;
|
|
1252
|
-
if (!window.confirm('Clear the entire scene? This cannot be undone.')) {
|
|
1253
|
-
return;
|
|
1254
|
-
}
|
|
1255
1253
|
const scene = sceneRef.current;
|
|
1256
1254
|
const toRemove = [];
|
|
1257
1255
|
scene.traverse((obj) => {
|
|
@@ -1283,6 +1281,14 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1283
1281
|
rebuildTree();
|
|
1284
1282
|
setStatusText('Scene cleared');
|
|
1285
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]);
|
|
1286
1292
|
/* ─────────────────────────────────────────
|
|
1287
1293
|
Save to Library
|
|
1288
1294
|
───────────────────────────────────────── */
|
|
@@ -1300,19 +1306,19 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1300
1306
|
const blob = new Blob([result], { type: 'model/gltf-binary' });
|
|
1301
1307
|
const reader = new FileReader();
|
|
1302
1308
|
reader.onload = () => {
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
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
|
+
});
|
|
1309
1315
|
};
|
|
1310
1316
|
reader.readAsDataURL(blob);
|
|
1311
1317
|
}, (err) => {
|
|
1312
1318
|
log.error('Save to library error:', err);
|
|
1313
1319
|
setStatusText(`Save error: ${String(err)}`);
|
|
1314
1320
|
}, { binary: true, animations: allClipsRef.current.map((a) => a.clip) });
|
|
1315
|
-
}, [onSaveToLibrary]);
|
|
1321
|
+
}, [onSaveToLibrary, ask]);
|
|
1316
1322
|
/* ─────────────────────────────────────────
|
|
1317
1323
|
Add Mesh / Light / Camera / Empty
|
|
1318
1324
|
───────────────────────────────────────── */
|
|
@@ -1808,6 +1814,7 @@ function useModelEditor({ onSaveToLibrary, onAIPoseVideo, }) {
|
|
|
1808
1814
|
bgColor,
|
|
1809
1815
|
setBgColor,
|
|
1810
1816
|
statusText,
|
|
1817
|
+
pendingAsk,
|
|
1811
1818
|
dragOver,
|
|
1812
1819
|
polyCount,
|
|
1813
1820
|
meshCount,
|