@nice2dev/ui-3d 1.3.14 → 1.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/collaborative/collaborativeScene.js +160 -206
  3. package/dist/cjs/core/editorAsk.js +51 -41
  4. package/dist/cjs/core/i18n.js +13 -14
  5. package/dist/cjs/core/threeCompat.js +29 -42
  6. package/dist/cjs/dance/DanceBridge.js +148 -177
  7. package/dist/cjs/dance/DanceScoreEngine.js +209 -221
  8. package/dist/cjs/dance/PoseDetector.js +302 -275
  9. package/dist/cjs/index.js +110 -113
  10. package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
  11. package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
  12. package/dist/cjs/material/materialEditorTypes.js +35 -49
  13. package/dist/cjs/material/materialEditorUtils.js +684 -747
  14. package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
  15. package/dist/cjs/model/ModelEditor.js +90 -40
  16. package/dist/cjs/model/ModelEditor.module.css.js +257 -5
  17. package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
  18. package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
  19. package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
  20. package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
  21. package/dist/cjs/model/ModelEditorTimeline.js +232 -34
  22. package/dist/cjs/model/ModelEditorToolbar.js +170 -14
  23. package/dist/cjs/model/ModelEditorViewport.js +231 -39
  24. package/dist/cjs/model/ModelViewer.js +388 -63
  25. package/dist/cjs/model/ModelViewer.module.css.js +77 -5
  26. package/dist/cjs/model/NiceArmatureEditor.js +473 -274
  27. package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
  28. package/dist/cjs/model/NiceOctree.js +372 -320
  29. package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
  30. package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
  31. package/dist/cjs/model/NiceTerrainEditor.js +595 -385
  32. package/dist/cjs/model/NiceWeightPainter.js +384 -261
  33. package/dist/cjs/model/NiceXRPreview.js +385 -308
  34. package/dist/cjs/model/cadModeUtils.js +103 -106
  35. package/dist/cjs/model/editorShortcuts.js +171 -194
  36. package/dist/cjs/model/modelEditorTypes.js +100 -118
  37. package/dist/cjs/model/modelEditorUtils.js +1061 -1146
  38. package/dist/cjs/model/simsModeUtils.js +1023 -1053
  39. package/dist/cjs/model/useModelEditor.js +1977 -1933
  40. package/dist/cjs/model/useModelViewer.js +633 -665
  41. package/dist/cjs/nice2dev-ui-3d.css +1 -1
  42. package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
  43. package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
  44. package/dist/cjs/particle/particleEditorTypes.js +62 -70
  45. package/dist/cjs/particle/particleEditorUtils.js +998 -1013
  46. package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
  47. package/dist/cjs/rendering/NiceRenderExport.js +424 -337
  48. package/dist/cjs/rendering/NiceSSAO.js +454 -337
  49. package/dist/cjs/rendering/NiceSSR.js +387 -261
  50. package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
  51. package/dist/cjs/uv/NiceUVEditor.js +787 -550
  52. package/dist/cjs/uv/UVEditor.module.css.js +53 -5
  53. package/dist/cjs/uv/uvEditorTypes.js +59 -76
  54. package/dist/cjs/uv/uvEditorUtils.js +501 -575
  55. package/dist/esm/collaborative/collaborativeScene.js +163 -205
  56. package/dist/esm/core/editorAsk.js +53 -40
  57. package/dist/esm/core/i18n.js +15 -13
  58. package/dist/esm/core/threeCompat.js +18 -26
  59. package/dist/esm/dance/DanceBridge.js +151 -176
  60. package/dist/esm/dance/DanceScoreEngine.js +211 -220
  61. package/dist/esm/dance/PoseDetector.js +304 -274
  62. package/dist/esm/index.js +258 -36
  63. package/dist/esm/material/MaterialEditor.module.css.js +242 -3
  64. package/dist/esm/material/NiceMaterialEditor.js +1215 -756
  65. package/dist/esm/material/materialEditorTypes.js +45 -48
  66. package/dist/esm/material/materialEditorUtils.js +713 -746
  67. package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
  68. package/dist/esm/model/ModelEditor.js +91 -39
  69. package/dist/esm/model/ModelEditor.module.css.js +257 -3
  70. package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
  71. package/dist/esm/model/ModelEditorMenuBar.js +359 -30
  72. package/dist/esm/model/ModelEditorRightPanel.js +634 -114
  73. package/dist/esm/model/ModelEditorSubComponents.js +265 -94
  74. package/dist/esm/model/ModelEditorTimeline.js +232 -32
  75. package/dist/esm/model/ModelEditorToolbar.js +170 -12
  76. package/dist/esm/model/ModelEditorViewport.js +231 -37
  77. package/dist/esm/model/ModelViewer.js +389 -62
  78. package/dist/esm/model/ModelViewer.module.css.js +77 -3
  79. package/dist/esm/model/NiceArmatureEditor.js +462 -258
  80. package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
  81. package/dist/esm/model/NiceOctree.js +361 -304
  82. package/dist/esm/model/NicePhysicsSimulation.js +517 -282
  83. package/dist/esm/model/NiceProceduralGeometry.js +667 -284
  84. package/dist/esm/model/NiceTerrainEditor.js +584 -369
  85. package/dist/esm/model/NiceWeightPainter.js +373 -245
  86. package/dist/esm/model/NiceXRPreview.js +374 -292
  87. package/dist/esm/model/cadModeUtils.js +95 -88
  88. package/dist/esm/model/editorShortcuts.js +172 -193
  89. package/dist/esm/model/modelEditorTypes.js +90 -100
  90. package/dist/esm/model/modelEditorUtils.js +1056 -1128
  91. package/dist/esm/model/simsModeUtils.js +1021 -1035
  92. package/dist/esm/model/useModelEditor.js +1946 -1920
  93. package/dist/esm/model/useModelViewer.js +619 -647
  94. package/dist/esm/nice2dev-ui-3d.css +1 -1
  95. package/dist/esm/particle/NiceParticleEditor.js +1188 -570
  96. package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
  97. package/dist/esm/particle/particleEditorTypes.js +69 -69
  98. package/dist/esm/particle/particleEditorUtils.js +1027 -1012
  99. package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
  100. package/dist/esm/rendering/NiceRenderExport.js +413 -321
  101. package/dist/esm/rendering/NiceSSAO.js +450 -328
  102. package/dist/esm/rendering/NiceSSR.js +383 -252
  103. package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
  104. package/dist/esm/uv/NiceUVEditor.js +788 -549
  105. package/dist/esm/uv/UVEditor.module.css.js +53 -3
  106. package/dist/esm/uv/uvEditorTypes.js +68 -75
  107. package/dist/esm/uv/uvEditorUtils.js +515 -557
  108. package/package.json +7 -12
@@ -1,323 +1,400 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var ui = require('@nice2dev/ui');
5
- var React = require('react');
6
- var THREE = require('three');
7
- var i18n = require('../core/i18n.js');
8
-
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const ui = require("@nice2dev/ui");
5
+ const React = require("react");
6
+ const THREE = require("three");
7
+ const i18n = require("../core/i18n.js");
9
8
  function _interopNamespaceDefault(e) {
10
- var n = Object.create(null);
11
- if (e) {
12
- Object.keys(e).forEach(function (k) {
13
- if (k !== 'default') {
14
- var d = Object.getOwnPropertyDescriptor(e, k);
15
- Object.defineProperty(n, k, d.get ? d : {
16
- enumerable: true,
17
- get: function () { return e[k]; }
18
- });
19
- }
9
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
10
+ if (e) {
11
+ for (const k in e) {
12
+ if (k !== "default") {
13
+ const d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: () => e[k]
20
17
  });
18
+ }
21
19
  }
22
- n.default = e;
23
- return Object.freeze(n);
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
24
23
  }
25
-
26
- var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
27
-
28
- /* ── Hook ── */
24
+ const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
29
25
  function useXRPreview(renderer, scene, camera, onSessionStart, onSessionEnd) {
30
- const [isSupported, setIsSupported] = React.useState(false);
31
- const [isSessionActive, setIsSessionActive] = React.useState(false);
32
- const [mode, setMode] = React.useState('vr');
33
- const [controllers, setControllers] = React.useState([]);
34
- const [teleportEnabled, setTeleportEnabled] = React.useState(true);
35
- const [hitTestResult, setHitTestResult] = React.useState(null);
36
- const controllerGripsRef = React.useRef([]);
37
- const controllerRaysRef = React.useRef([]);
38
- const teleportMarkerRef = React.useRef(null);
39
- const hitTestSourceRef = React.useRef(null);
40
- const cameraGroupRef = React.useRef(new THREE__namespace.Group());
41
- // Check WebXR support
42
- React.useEffect(() => {
43
- if ('xr' in navigator) {
44
- const xr = navigator.xr;
45
- xr.isSessionSupported('immersive-vr')
46
- .then((vr) => {
47
- if (vr) {
48
- setIsSupported(true);
49
- }
50
- else {
51
- xr.isSessionSupported('immersive-ar').then((ar) => {
52
- setIsSupported(ar);
53
- });
54
- }
55
- })
56
- .catch(() => setIsSupported(false));
26
+ const [isSupported, setIsSupported] = React.useState(false);
27
+ const [isSessionActive, setIsSessionActive] = React.useState(false);
28
+ const [mode, setMode] = React.useState("vr");
29
+ const [controllers, setControllers] = React.useState([]);
30
+ const [teleportEnabled, setTeleportEnabled] = React.useState(true);
31
+ const [hitTestResult, setHitTestResult] = React.useState(null);
32
+ const controllerGripsRef = React.useRef([]);
33
+ const controllerRaysRef = React.useRef([]);
34
+ const teleportMarkerRef = React.useRef(null);
35
+ const hitTestSourceRef = React.useRef(null);
36
+ const cameraGroupRef = React.useRef(new THREE__namespace.Group());
37
+ React.useEffect(() => {
38
+ if ("xr" in navigator) {
39
+ const xr = navigator.xr;
40
+ xr.isSessionSupported("immersive-vr").then((vr) => {
41
+ if (vr) {
42
+ setIsSupported(true);
43
+ } else {
44
+ xr.isSessionSupported("immersive-ar").then((ar) => {
45
+ setIsSupported(ar);
46
+ });
57
47
  }
58
- }, []);
59
- // Setup camera rig
60
- React.useEffect(() => {
61
- if (!scene || !camera) {
62
- return;
63
- }
64
- const rig = cameraGroupRef.current;
65
- rig.name = '__xr_camera_rig__';
66
- rig.add(camera);
67
- scene.add(rig);
68
- return () => {
69
- rig.remove(camera);
70
- scene.remove(rig);
71
- };
72
- }, [scene, camera]);
73
- const setupControllers = React.useCallback(() => {
74
- if (!renderer || !scene) {
75
- return;
48
+ }).catch(() => setIsSupported(false));
49
+ }
50
+ }, []);
51
+ React.useEffect(() => {
52
+ if (!scene || !camera) {
53
+ return;
54
+ }
55
+ const rig = cameraGroupRef.current;
56
+ rig.name = "__xr_camera_rig__";
57
+ rig.add(camera);
58
+ scene.add(rig);
59
+ return () => {
60
+ rig.remove(camera);
61
+ scene.remove(rig);
62
+ };
63
+ }, [scene, camera]);
64
+ const setupControllers = React.useCallback(() => {
65
+ if (!renderer || !scene) {
66
+ return;
67
+ }
68
+ for (let i = 0; i < 2; i++) {
69
+ const controller = renderer.xr.getController(i);
70
+ const grip = renderer.xr.getControllerGrip(i);
71
+ const lineGeo = new THREE__namespace.BufferGeometry().setFromPoints([
72
+ new THREE__namespace.Vector3(0, 0, 0),
73
+ new THREE__namespace.Vector3(0, 0, -3)
74
+ ]);
75
+ const ray = new THREE__namespace.Line(lineGeo, new THREE__namespace.LineBasicMaterial({ color: 65535 }));
76
+ controller.add(ray);
77
+ controllerRaysRef.current[i] = ray;
78
+ const gripGeo = new THREE__namespace.CylinderGeometry(0.015, 0.015, 0.15, 8);
79
+ gripGeo.rotateX(-Math.PI / 2);
80
+ grip.add(new THREE__namespace.Mesh(gripGeo, new THREE__namespace.MeshStandardMaterial({ color: 4473924 })));
81
+ cameraGroupRef.current.add(controller);
82
+ cameraGroupRef.current.add(grip);
83
+ controllerGripsRef.current[i] = grip;
84
+ controller.addEventListener("selectstart", () => {
85
+ setControllers((prev) => {
86
+ const copy = [...prev];
87
+ if (copy[i]) {
88
+ copy[i] = { ...copy[i], isSelecting: true };
89
+ }
90
+ return copy;
91
+ });
92
+ });
93
+ controller.addEventListener("selectend", () => {
94
+ setControllers((prev) => {
95
+ const copy = [...prev];
96
+ if (copy[i]) {
97
+ copy[i] = { ...copy[i], isSelecting: false };
98
+ }
99
+ return copy;
100
+ });
101
+ if (teleportEnabled && teleportMarkerRef.current?.visible) {
102
+ cameraGroupRef.current.position.copy(teleportMarkerRef.current.position);
76
103
  }
77
- for (let i = 0; i < 2; i++) {
104
+ });
105
+ controller.addEventListener("squeezestart", () => {
106
+ setControllers((prev) => {
107
+ const copy = [...prev];
108
+ if (copy[i]) {
109
+ copy[i] = { ...copy[i], isSqueezing: true };
110
+ }
111
+ return copy;
112
+ });
113
+ });
114
+ controller.addEventListener("squeezeend", () => {
115
+ setControllers((prev) => {
116
+ const copy = [...prev];
117
+ if (copy[i]) {
118
+ copy[i] = { ...copy[i], isSqueezing: false };
119
+ }
120
+ return copy;
121
+ });
122
+ });
123
+ }
124
+ const marker = new THREE__namespace.Mesh(
125
+ new THREE__namespace.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2),
126
+ new THREE__namespace.MeshBasicMaterial({ color: 65280, side: THREE__namespace.DoubleSide })
127
+ );
128
+ marker.visible = false;
129
+ scene.add(marker);
130
+ teleportMarkerRef.current = marker;
131
+ setControllers([
132
+ {
133
+ hand: "left",
134
+ position: new THREE__namespace.Vector3(),
135
+ rotation: new THREE__namespace.Quaternion(),
136
+ isSelecting: false,
137
+ isSqueezing: false,
138
+ gamepad: null
139
+ },
140
+ {
141
+ hand: "right",
142
+ position: new THREE__namespace.Vector3(),
143
+ rotation: new THREE__namespace.Quaternion(),
144
+ isSelecting: false,
145
+ isSqueezing: false,
146
+ gamepad: null
147
+ }
148
+ ]);
149
+ }, [renderer, scene, teleportEnabled]);
150
+ const startSession = React.useCallback(async () => {
151
+ if (!renderer || !isSupported) {
152
+ return;
153
+ }
154
+ const xr = navigator.xr;
155
+ const sessionType = mode === "vr" ? "immersive-vr" : "immersive-ar";
156
+ const features = mode === "vr" ? ["local-floor", "bounded-floor"] : ["hit-test", "dom-overlay", "local-floor"];
157
+ try {
158
+ const session = await xr.requestSession(sessionType, {
159
+ optionalFeatures: features
160
+ });
161
+ renderer.xr.enabled = true;
162
+ await renderer.xr.setSession(session);
163
+ setupControllers();
164
+ setIsSessionActive(true);
165
+ onSessionStart?.();
166
+ if (mode === "ar") {
167
+ const refSpace = await session.requestReferenceSpace("viewer");
168
+ const hitTestSource = await session.requestHitTestSource({ space: refSpace });
169
+ hitTestSourceRef.current = hitTestSource ?? null;
170
+ }
171
+ session.addEventListener("end", () => {
172
+ renderer.xr.enabled = false;
173
+ setIsSessionActive(false);
174
+ hitTestSourceRef.current = null;
175
+ onSessionEnd?.();
176
+ });
177
+ renderer.xr.addEventListener("sessionstart", () => {
178
+ renderer.setAnimationLoop((_time, frame) => {
179
+ for (let i = 0; i < 2; i++) {
78
180
  const controller = renderer.xr.getController(i);
79
- const grip = renderer.xr.getControllerGrip(i);
80
- // Controller ray
81
- const lineGeo = new THREE__namespace.BufferGeometry().setFromPoints([
82
- new THREE__namespace.Vector3(0, 0, 0),
83
- new THREE__namespace.Vector3(0, 0, -3),
84
- ]);
85
- const ray = new THREE__namespace.Line(lineGeo, new THREE__namespace.LineBasicMaterial({ color: 0x00ffff }));
86
- controller.add(ray);
87
- controllerRaysRef.current[i] = ray;
88
- // Grip model (simple cylinder)
89
- const gripGeo = new THREE__namespace.CylinderGeometry(0.015, 0.015, 0.15, 8);
90
- gripGeo.rotateX(-Math.PI / 2);
91
- grip.add(new THREE__namespace.Mesh(gripGeo, new THREE__namespace.MeshStandardMaterial({ color: 0x444444 })));
92
- cameraGroupRef.current.add(controller);
93
- cameraGroupRef.current.add(grip);
94
- controllerGripsRef.current[i] = grip;
95
- // Events
96
- controller.addEventListener('selectstart', () => {
97
- setControllers((prev) => {
98
- const copy = [...prev];
99
- if (copy[i]) {
100
- copy[i] = { ...copy[i], isSelecting: true };
101
- }
102
- return copy;
103
- });
181
+ setControllers((prev) => {
182
+ const copy = [...prev];
183
+ if (copy[i]) {
184
+ copy[i] = {
185
+ ...copy[i],
186
+ position: controller.position.clone(),
187
+ rotation: controller.quaternion.clone(),
188
+ gamepad: session.inputSources[i]?.gamepad ?? null
189
+ };
190
+ }
191
+ return copy;
104
192
  });
105
- controller.addEventListener('selectend', () => {
106
- var _a;
107
- setControllers((prev) => {
108
- const copy = [...prev];
109
- if (copy[i]) {
110
- copy[i] = { ...copy[i], isSelecting: false };
111
- }
112
- return copy;
113
- });
114
- // Teleport on select release
115
- if (teleportEnabled && ((_a = teleportMarkerRef.current) === null || _a === void 0 ? void 0 : _a.visible)) {
116
- cameraGroupRef.current.position.copy(teleportMarkerRef.current.position);
193
+ if (teleportEnabled && controllerRaysRef.current[i]) {
194
+ const raycaster = new THREE__namespace.Raycaster();
195
+ const tempMatrix = new THREE__namespace.Matrix4().identity().extractRotation(controller.matrixWorld);
196
+ raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
197
+ raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
198
+ if (scene) {
199
+ const floorMeshes = scene.children.filter(
200
+ (c) => c instanceof THREE__namespace.Mesh && c.name !== "__xr_camera_rig__"
201
+ );
202
+ const intersects = raycaster.intersectObjects(floorMeshes);
203
+ if (intersects.length > 0 && teleportMarkerRef.current) {
204
+ teleportMarkerRef.current.position.copy(intersects[0].point);
205
+ teleportMarkerRef.current.visible = true;
206
+ } else if (teleportMarkerRef.current) {
207
+ teleportMarkerRef.current.visible = false;
117
208
  }
118
- });
119
- controller.addEventListener('squeezestart', () => {
120
- setControllers((prev) => {
121
- const copy = [...prev];
122
- if (copy[i]) {
123
- copy[i] = { ...copy[i], isSqueezing: true };
124
- }
125
- return copy;
126
- });
127
- });
128
- controller.addEventListener('squeezeend', () => {
129
- setControllers((prev) => {
130
- const copy = [...prev];
131
- if (copy[i]) {
132
- copy[i] = { ...copy[i], isSqueezing: false };
133
- }
134
- return copy;
135
- });
136
- });
137
- }
138
- // Teleport marker
139
- const marker = new THREE__namespace.Mesh(new THREE__namespace.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2), new THREE__namespace.MeshBasicMaterial({ color: 0x00ff00, side: THREE__namespace.DoubleSide }));
140
- marker.visible = false;
141
- scene.add(marker);
142
- teleportMarkerRef.current = marker;
143
- // Initial controller states
144
- setControllers([
145
- {
146
- hand: 'left',
147
- position: new THREE__namespace.Vector3(),
148
- rotation: new THREE__namespace.Quaternion(),
149
- isSelecting: false,
150
- isSqueezing: false,
151
- gamepad: null,
152
- },
153
- {
154
- hand: 'right',
155
- position: new THREE__namespace.Vector3(),
156
- rotation: new THREE__namespace.Quaternion(),
157
- isSelecting: false,
158
- isSqueezing: false,
159
- gamepad: null,
160
- },
161
- ]);
162
- }, [renderer, scene, teleportEnabled]);
163
- const startSession = React.useCallback(async () => {
164
- if (!renderer || !isSupported) {
165
- return;
166
- }
167
- const xr = navigator.xr;
168
- const sessionType = mode === 'vr' ? 'immersive-vr' : 'immersive-ar';
169
- const features = mode === 'vr' ? ['local-floor', 'bounded-floor'] : ['hit-test', 'dom-overlay', 'local-floor'];
170
- try {
171
- const session = await xr.requestSession(sessionType, {
172
- optionalFeatures: features,
173
- });
174
- renderer.xr.enabled = true;
175
- await renderer.xr.setSession(session);
176
- setupControllers();
177
- setIsSessionActive(true);
178
- onSessionStart === null || onSessionStart === void 0 ? void 0 : onSessionStart();
179
- // AR hit-test setup
180
- if (mode === 'ar') {
181
- const refSpace = await session.requestReferenceSpace('viewer');
182
- const hitTestSource = await session.requestHitTestSource({ space: refSpace });
183
- hitTestSourceRef.current = hitTestSource !== null && hitTestSource !== void 0 ? hitTestSource : null;
209
+ }
184
210
  }
185
- session.addEventListener('end', () => {
186
- renderer.xr.enabled = false;
187
- setIsSessionActive(false);
188
- hitTestSourceRef.current = null;
189
- onSessionEnd === null || onSessionEnd === void 0 ? void 0 : onSessionEnd();
190
- });
191
- // XR render loop for controller updates
192
- renderer.xr.addEventListener('sessionstart', () => {
193
- renderer.setAnimationLoop((_time, frame) => {
194
- // Update controller positions
195
- for (let i = 0; i < 2; i++) {
196
- const controller = renderer.xr.getController(i);
197
- setControllers((prev) => {
198
- var _a, _b;
199
- const copy = [...prev];
200
- if (copy[i]) {
201
- copy[i] = {
202
- ...copy[i],
203
- position: controller.position.clone(),
204
- rotation: controller.quaternion.clone(),
205
- gamepad: (_b = (_a = session.inputSources[i]) === null || _a === void 0 ? void 0 : _a.gamepad) !== null && _b !== void 0 ? _b : null,
206
- };
207
- }
208
- return copy;
209
- });
210
- // Teleport aiming
211
- if (teleportEnabled && controllerRaysRef.current[i]) {
212
- const raycaster = new THREE__namespace.Raycaster();
213
- const tempMatrix = new THREE__namespace.Matrix4()
214
- .identity()
215
- .extractRotation(controller.matrixWorld);
216
- raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
217
- raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
218
- if (scene) {
219
- const floorMeshes = scene.children.filter((c) => c instanceof THREE__namespace.Mesh && c.name !== '__xr_camera_rig__');
220
- const intersects = raycaster.intersectObjects(floorMeshes);
221
- if (intersects.length > 0 && teleportMarkerRef.current) {
222
- teleportMarkerRef.current.position.copy(intersects[0].point);
223
- teleportMarkerRef.current.visible = true;
224
- }
225
- else if (teleportMarkerRef.current) {
226
- teleportMarkerRef.current.visible = false;
227
- }
228
- }
229
- }
230
- }
231
- // AR hit-test
232
- if (mode === 'ar' && frame && hitTestSourceRef.current) {
233
- const results = frame.getHitTestResults(hitTestSourceRef.current);
234
- if (results.length > 0) {
235
- const refSpace = renderer.xr.getReferenceSpace();
236
- if (refSpace) {
237
- const pose = results[0].getPose(refSpace);
238
- if (pose) {
239
- setHitTestResult(new THREE__namespace.Vector3(pose.transform.position.x, pose.transform.position.y, pose.transform.position.z));
240
- }
241
- }
242
- }
243
- }
244
- renderer.render(scene, camera);
245
- });
246
- });
247
- }
248
- catch (err) {
249
- console.warn('Failed to start XR session:', err);
250
- }
251
- }, [
252
- renderer,
253
- isSupported,
254
- mode,
255
- setupControllers,
256
- onSessionStart,
257
- onSessionEnd,
258
- scene,
259
- camera,
260
- teleportEnabled,
261
- ]);
262
- const endSession = React.useCallback(() => {
263
- if (!renderer) {
264
- return;
265
- }
266
- const session = renderer.xr.getSession();
267
- session === null || session === void 0 ? void 0 : session.end();
268
- }, [renderer]);
269
- return {
270
- isSupported,
271
- isSessionActive,
272
- mode,
273
- setMode,
274
- startSession,
275
- endSession,
276
- controllers,
277
- teleportEnabled,
278
- setTeleportEnabled,
279
- hitTestResult,
280
- };
211
+ }
212
+ if (mode === "ar" && frame && hitTestSourceRef.current) {
213
+ const results = frame.getHitTestResults(hitTestSourceRef.current);
214
+ if (results.length > 0) {
215
+ const refSpace = renderer.xr.getReferenceSpace();
216
+ if (refSpace) {
217
+ const pose = results[0].getPose(refSpace);
218
+ if (pose) {
219
+ setHitTestResult(
220
+ new THREE__namespace.Vector3(
221
+ pose.transform.position.x,
222
+ pose.transform.position.y,
223
+ pose.transform.position.z
224
+ )
225
+ );
226
+ }
227
+ }
228
+ }
229
+ }
230
+ renderer.render(scene, camera);
231
+ });
232
+ });
233
+ } catch (err) {
234
+ console.warn("Failed to start XR session:", err);
235
+ }
236
+ }, [
237
+ renderer,
238
+ isSupported,
239
+ mode,
240
+ setupControllers,
241
+ onSessionStart,
242
+ onSessionEnd,
243
+ scene,
244
+ camera,
245
+ teleportEnabled
246
+ ]);
247
+ const endSession = React.useCallback(() => {
248
+ if (!renderer) {
249
+ return;
250
+ }
251
+ const session = renderer.xr.getSession();
252
+ session?.end();
253
+ }, [renderer]);
254
+ return {
255
+ isSupported,
256
+ isSessionActive,
257
+ mode,
258
+ setMode,
259
+ startSession,
260
+ endSession,
261
+ controllers,
262
+ teleportEnabled,
263
+ setTeleportEnabled,
264
+ hitTestResult
265
+ };
281
266
  }
282
- /* ── Component ── */
283
- const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionStart, onSessionEnd, className = '', }) => {
284
- const { t } = i18n.useNiceTranslation();
285
- const { isSupported, isSessionActive, mode, setMode, startSession, endSession, controllers, teleportEnabled, setTeleportEnabled, hitTestResult, } = useXRPreview(renderer, scene, camera, onSessionStart, onSessionEnd);
286
- React.useEffect(() => {
287
- if (initialMode) {
288
- setMode(initialMode);
289
- }
290
- }, [initialMode, setMode]);
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: {
292
- flex: 1,
293
- padding: '5px 8px',
294
- borderRadius: 4,
295
- cursor: 'pointer',
296
- background: mode === 'vr' ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
297
- color: mode === 'vr' ? 'var(--nice-color-white, #fff)' : 'inherit',
298
- border: '1px solid var(--nice-border, #e2e8f0)',
299
- }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "eye", size: 14 }), " ", t('xrPreview.vr', 'VR')] }), jsxRuntime.jsxs("button", { type: "button", onClick: () => setMode('ar'), style: {
300
- flex: 1,
301
- padding: '5px 8px',
302
- borderRadius: 4,
303
- cursor: 'pointer',
304
- background: mode === 'ar' ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
305
- color: mode === 'ar' ? 'var(--nice-color-white, #fff)' : 'inherit',
306
- border: '1px solid var(--nice-border, #e2e8f0)',
307
- }, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "window", size: 14 }), " ", t('xrPreview.ar', 'AR')] })] }), jsxRuntime.jsx("button", { type: "button", onClick: isSessionActive ? endSession : startSession, style: {
308
- background: isSessionActive
309
- ? 'var(--nice-danger, #ef4444)'
310
- : 'var(--nice-success, #22c55e)',
311
- color: 'var(--nice-color-white, #fff)',
312
- border: 'none',
313
- borderRadius: 4,
314
- padding: '6px 12px',
315
- cursor: 'pointer',
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: [t('xrPreview.controllers', 'Controllers: {controllers}').replace('{controllers}', String(controllers.length)), jsxRuntime.jsx("br", {}), mode === 'ar' && hitTestResult && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: t('xrPreview.hit', 'Hit: ({x}, {y}, {z})')
317
- .replace('{x}', String(hitTestResult.x.toFixed(2)))
318
- .replace('{y}', String(hitTestResult.y.toFixed(2)))
319
- .replace('{z}', String(hitTestResult.z.toFixed(2))) }))] }))] }))] }));
267
+ const NiceXRPreview = ({
268
+ renderer,
269
+ scene,
270
+ camera,
271
+ mode: initialMode,
272
+ onSessionStart,
273
+ onSessionEnd,
274
+ className = ""
275
+ }) => {
276
+ const { t } = i18n.useNiceTranslation();
277
+ const {
278
+ isSupported,
279
+ isSessionActive,
280
+ mode,
281
+ setMode,
282
+ startSession,
283
+ endSession,
284
+ controllers,
285
+ teleportEnabled,
286
+ setTeleportEnabled,
287
+ hitTestResult
288
+ } = useXRPreview(renderer, scene, camera, onSessionStart, onSessionEnd);
289
+ React.useEffect(() => {
290
+ if (initialMode) {
291
+ setMode(initialMode);
292
+ }
293
+ }, [initialMode, setMode]);
294
+ return /* @__PURE__ */ jsxRuntime.jsxs(
295
+ "div",
296
+ {
297
+ className: `nice-xr-preview ${className}`,
298
+ style: { display: "grid", gap: 8, fontSize: 13 },
299
+ children: [
300
+ /* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
301
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "eye", size: 14 }),
302
+ " ",
303
+ t("xrPreview.xrPreview", "XR Preview")
304
+ ] }),
305
+ !isSupported && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--nice-danger, #ef4444)", fontSize: 11 }, children: t("xrPreview.webxrIsNotSupportedIn", "WebXR is not supported in this browser.") }),
306
+ isSupported && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
307
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 4 }, children: [
308
+ /* @__PURE__ */ jsxRuntime.jsxs(
309
+ "button",
310
+ {
311
+ type: "button",
312
+ onClick: () => setMode("vr"),
313
+ style: {
314
+ flex: 1,
315
+ padding: "5px 8px",
316
+ borderRadius: 4,
317
+ cursor: "pointer",
318
+ background: mode === "vr" ? "var(--nice-color-accent, #6366f1)" : "transparent",
319
+ color: mode === "vr" ? "var(--nice-color-white, #fff)" : "inherit",
320
+ border: "1px solid var(--nice-border, #e2e8f0)"
321
+ },
322
+ children: [
323
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "eye", size: 14 }),
324
+ " ",
325
+ t("xrPreview.vr", "VR")
326
+ ]
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsxRuntime.jsxs(
330
+ "button",
331
+ {
332
+ type: "button",
333
+ onClick: () => setMode("ar"),
334
+ style: {
335
+ flex: 1,
336
+ padding: "5px 8px",
337
+ borderRadius: 4,
338
+ cursor: "pointer",
339
+ background: mode === "ar" ? "var(--nice-color-accent, #6366f1)" : "transparent",
340
+ color: mode === "ar" ? "var(--nice-color-white, #fff)" : "inherit",
341
+ border: "1px solid var(--nice-border, #e2e8f0)"
342
+ },
343
+ children: [
344
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "window", size: 14 }),
345
+ " ",
346
+ t("xrPreview.ar", "AR")
347
+ ]
348
+ }
349
+ )
350
+ ] }),
351
+ /* @__PURE__ */ jsxRuntime.jsx(
352
+ "button",
353
+ {
354
+ type: "button",
355
+ onClick: isSessionActive ? endSession : startSession,
356
+ style: {
357
+ background: isSessionActive ? "var(--nice-danger, #ef4444)" : "var(--nice-success, #22c55e)",
358
+ color: "var(--nice-color-white, #fff)",
359
+ border: "none",
360
+ borderRadius: 4,
361
+ padding: "6px 12px",
362
+ cursor: "pointer"
363
+ },
364
+ children: isSessionActive ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
365
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "stop", size: 14 }),
366
+ " ",
367
+ t("xrPreview.exitXr", "Exit XR")
368
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
369
+ /* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "play", size: 14 }),
370
+ " ",
371
+ t("xrPreview.enterXr", "Enter XR")
372
+ ] })
373
+ }
374
+ ),
375
+ mode === "vr" && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
376
+ /* @__PURE__ */ jsxRuntime.jsx(
377
+ "input",
378
+ {
379
+ type: "checkbox",
380
+ checked: teleportEnabled,
381
+ onChange: (e) => setTeleportEnabled(e.target.checked)
382
+ }
383
+ ),
384
+ t("xrPreview.teleportation", "Teleportation")
385
+ ] }),
386
+ isSessionActive && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: [
387
+ t("xrPreview.controllers", "Controllers: {controllers}").replace(
388
+ "{controllers}",
389
+ String(controllers.length)
390
+ ),
391
+ /* @__PURE__ */ jsxRuntime.jsx("br", {}),
392
+ mode === "ar" && hitTestResult && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("xrPreview.hit", "Hit: ({x}, {y}, {z})").replace("{x}", String(hitTestResult.x.toFixed(2))).replace("{y}", String(hitTestResult.y.toFixed(2))).replace("{z}", String(hitTestResult.z.toFixed(2))) })
393
+ ] })
394
+ ] })
395
+ ]
396
+ }
397
+ );
320
398
  };
321
-
322
399
  exports.NiceXRPreview = NiceXRPreview;
323
400
  exports.useXRPreview = useXRPreview;