@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.
- package/CHANGELOG.md +13 -0
- package/dist/cjs/collaborative/collaborativeScene.js +160 -206
- package/dist/cjs/core/editorAsk.js +51 -41
- package/dist/cjs/core/i18n.js +13 -14
- package/dist/cjs/core/threeCompat.js +29 -42
- package/dist/cjs/dance/DanceBridge.js +148 -177
- package/dist/cjs/dance/DanceScoreEngine.js +209 -221
- package/dist/cjs/dance/PoseDetector.js +302 -275
- package/dist/cjs/index.js +110 -113
- package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
- package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
- package/dist/cjs/material/materialEditorTypes.js +35 -49
- package/dist/cjs/material/materialEditorUtils.js +684 -747
- package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
- package/dist/cjs/model/ModelEditor.js +90 -40
- package/dist/cjs/model/ModelEditor.module.css.js +257 -5
- package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
- package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
- package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
- package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
- package/dist/cjs/model/ModelEditorTimeline.js +232 -34
- package/dist/cjs/model/ModelEditorToolbar.js +170 -14
- package/dist/cjs/model/ModelEditorViewport.js +231 -39
- package/dist/cjs/model/ModelViewer.js +388 -63
- package/dist/cjs/model/ModelViewer.module.css.js +77 -5
- package/dist/cjs/model/NiceArmatureEditor.js +473 -274
- package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
- package/dist/cjs/model/NiceOctree.js +372 -320
- package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
- package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
- package/dist/cjs/model/NiceTerrainEditor.js +595 -385
- package/dist/cjs/model/NiceWeightPainter.js +384 -261
- package/dist/cjs/model/NiceXRPreview.js +385 -308
- package/dist/cjs/model/cadModeUtils.js +103 -106
- package/dist/cjs/model/editorShortcuts.js +171 -194
- package/dist/cjs/model/modelEditorTypes.js +100 -118
- package/dist/cjs/model/modelEditorUtils.js +1061 -1146
- package/dist/cjs/model/simsModeUtils.js +1023 -1053
- package/dist/cjs/model/useModelEditor.js +1977 -1933
- package/dist/cjs/model/useModelViewer.js +633 -665
- package/dist/cjs/nice2dev-ui-3d.css +1 -1
- package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
- package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
- package/dist/cjs/particle/particleEditorTypes.js +62 -70
- package/dist/cjs/particle/particleEditorUtils.js +998 -1013
- package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
- package/dist/cjs/rendering/NiceRenderExport.js +424 -337
- package/dist/cjs/rendering/NiceSSAO.js +454 -337
- package/dist/cjs/rendering/NiceSSR.js +387 -261
- package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
- package/dist/cjs/uv/NiceUVEditor.js +787 -550
- package/dist/cjs/uv/UVEditor.module.css.js +53 -5
- package/dist/cjs/uv/uvEditorTypes.js +59 -76
- package/dist/cjs/uv/uvEditorUtils.js +501 -575
- package/dist/esm/collaborative/collaborativeScene.js +163 -205
- package/dist/esm/core/editorAsk.js +53 -40
- package/dist/esm/core/i18n.js +15 -13
- package/dist/esm/core/threeCompat.js +18 -26
- package/dist/esm/dance/DanceBridge.js +151 -176
- package/dist/esm/dance/DanceScoreEngine.js +211 -220
- package/dist/esm/dance/PoseDetector.js +304 -274
- package/dist/esm/index.js +258 -36
- package/dist/esm/material/MaterialEditor.module.css.js +242 -3
- package/dist/esm/material/NiceMaterialEditor.js +1215 -756
- package/dist/esm/material/materialEditorTypes.js +45 -48
- package/dist/esm/material/materialEditorUtils.js +713 -746
- package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
- package/dist/esm/model/ModelEditor.js +91 -39
- package/dist/esm/model/ModelEditor.module.css.js +257 -3
- package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
- package/dist/esm/model/ModelEditorMenuBar.js +359 -30
- package/dist/esm/model/ModelEditorRightPanel.js +634 -114
- package/dist/esm/model/ModelEditorSubComponents.js +265 -94
- package/dist/esm/model/ModelEditorTimeline.js +232 -32
- package/dist/esm/model/ModelEditorToolbar.js +170 -12
- package/dist/esm/model/ModelEditorViewport.js +231 -37
- package/dist/esm/model/ModelViewer.js +389 -62
- package/dist/esm/model/ModelViewer.module.css.js +77 -3
- package/dist/esm/model/NiceArmatureEditor.js +462 -258
- package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
- package/dist/esm/model/NiceOctree.js +361 -304
- package/dist/esm/model/NicePhysicsSimulation.js +517 -282
- package/dist/esm/model/NiceProceduralGeometry.js +667 -284
- package/dist/esm/model/NiceTerrainEditor.js +584 -369
- package/dist/esm/model/NiceWeightPainter.js +373 -245
- package/dist/esm/model/NiceXRPreview.js +374 -292
- package/dist/esm/model/cadModeUtils.js +95 -88
- package/dist/esm/model/editorShortcuts.js +172 -193
- package/dist/esm/model/modelEditorTypes.js +90 -100
- package/dist/esm/model/modelEditorUtils.js +1056 -1128
- package/dist/esm/model/simsModeUtils.js +1021 -1035
- package/dist/esm/model/useModelEditor.js +1946 -1920
- package/dist/esm/model/useModelViewer.js +619 -647
- package/dist/esm/nice2dev-ui-3d.css +1 -1
- package/dist/esm/particle/NiceParticleEditor.js +1188 -570
- package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
- package/dist/esm/particle/particleEditorTypes.js +69 -69
- package/dist/esm/particle/particleEditorUtils.js +1027 -1012
- package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
- package/dist/esm/rendering/NiceRenderExport.js +413 -321
- package/dist/esm/rendering/NiceSSAO.js +450 -328
- package/dist/esm/rendering/NiceSSR.js +383 -252
- package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
- package/dist/esm/uv/NiceUVEditor.js +788 -549
- package/dist/esm/uv/UVEditor.module.css.js +53 -3
- package/dist/esm/uv/uvEditorTypes.js +68 -75
- package/dist/esm/uv/uvEditorUtils.js +515 -557
- package/package.json +7 -12
|
@@ -1,370 +1,422 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
23
|
-
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
|
|
27
|
-
|
|
28
|
-
/* ── Octree implementation ── */
|
|
24
|
+
const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
|
|
29
25
|
const DEFAULT_CONFIG = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
maxDepth: 8,
|
|
27
|
+
maxObjectsPerLeaf: 8,
|
|
28
|
+
looseness: 1
|
|
33
29
|
};
|
|
34
30
|
function createNode(bounds, depth) {
|
|
35
|
-
|
|
31
|
+
return { bounds, children: null, objects: [], depth };
|
|
36
32
|
}
|
|
37
33
|
function splitNode(node, config) {
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
if (node.depth >= config.maxDepth) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const center = new THREE__namespace.Vector3();
|
|
38
|
+
node.bounds.getCenter(center);
|
|
39
|
+
const min = node.bounds.min;
|
|
40
|
+
const max = node.bounds.max;
|
|
41
|
+
node.children = [];
|
|
42
|
+
for (let i = 0; i < 8; i++) {
|
|
43
|
+
const childMin = new THREE__namespace.Vector3(
|
|
44
|
+
i & 1 ? center.x : min.x,
|
|
45
|
+
i & 2 ? center.y : min.y,
|
|
46
|
+
i & 4 ? center.z : min.z
|
|
47
|
+
);
|
|
48
|
+
const childMax = new THREE__namespace.Vector3(
|
|
49
|
+
i & 1 ? max.x : center.x,
|
|
50
|
+
i & 2 ? max.y : center.y,
|
|
51
|
+
i & 4 ? max.z : center.z
|
|
52
|
+
);
|
|
53
|
+
if (config.looseness > 1) {
|
|
54
|
+
const size = new THREE__namespace.Vector3().subVectors(childMax, childMin);
|
|
55
|
+
const expansion = size.clone().multiplyScalar((config.looseness - 1) * 0.5);
|
|
56
|
+
childMin.sub(expansion);
|
|
57
|
+
childMax.add(expansion);
|
|
40
58
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
childMin.sub(expansion);
|
|
54
|
-
childMax.add(expansion);
|
|
55
|
-
}
|
|
56
|
-
node.children.push(createNode(new THREE__namespace.Box3(childMin, childMax), node.depth + 1));
|
|
59
|
+
node.children.push(createNode(new THREE__namespace.Box3(childMin, childMax), node.depth + 1));
|
|
60
|
+
}
|
|
61
|
+
const orphans = [];
|
|
62
|
+
for (const obj of node.objects) {
|
|
63
|
+
const objBounds = new THREE__namespace.Box3().setFromObject(obj);
|
|
64
|
+
let placed = false;
|
|
65
|
+
for (const child of node.children) {
|
|
66
|
+
if (child.bounds.containsBox(objBounds)) {
|
|
67
|
+
child.objects.push(obj);
|
|
68
|
+
placed = true;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
57
71
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
for (const obj of node.objects) {
|
|
61
|
-
const objBounds = new THREE__namespace.Box3().setFromObject(obj);
|
|
62
|
-
let placed = false;
|
|
63
|
-
for (const child of node.children) {
|
|
64
|
-
if (child.bounds.containsBox(objBounds)) {
|
|
65
|
-
child.objects.push(obj);
|
|
66
|
-
placed = true;
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (!placed) {
|
|
71
|
-
orphans.push(obj);
|
|
72
|
-
}
|
|
72
|
+
if (!placed) {
|
|
73
|
+
orphans.push(obj);
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
}
|
|
76
|
+
node.objects = orphans;
|
|
75
77
|
}
|
|
76
78
|
function insertObject(node, object, config) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Doesn't fit in any child — store in this node
|
|
89
|
-
node.objects.push(object);
|
|
79
|
+
const objBounds = new THREE__namespace.Box3().setFromObject(object);
|
|
80
|
+
if (!node.bounds.intersectsBox(objBounds)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (node.children) {
|
|
84
|
+
for (const child of node.children) {
|
|
85
|
+
if (child.bounds.containsBox(objBounds)) {
|
|
86
|
+
insertObject(child, object, config);
|
|
90
87
|
return;
|
|
88
|
+
}
|
|
91
89
|
}
|
|
92
90
|
node.objects.push(object);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
node.objects.push(object);
|
|
94
|
+
if (node.objects.length > config.maxObjectsPerLeaf && node.depth < config.maxDepth) {
|
|
95
|
+
splitNode(node, config);
|
|
96
|
+
}
|
|
96
97
|
}
|
|
97
98
|
function removeObject(node, object) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
const idx = node.objects.indexOf(object);
|
|
100
|
+
if (idx !== -1) {
|
|
101
|
+
node.objects.splice(idx, 1);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
if (node.children) {
|
|
105
|
+
for (const child of node.children) {
|
|
106
|
+
if (removeObject(child, object)) {
|
|
101
107
|
return true;
|
|
108
|
+
}
|
|
102
109
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (removeObject(child, object)) {
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return false;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
111
112
|
}
|
|
112
113
|
function raycastNode(node, raycaster, results) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
114
|
+
if (!raycaster.ray.intersectsBox(node.bounds)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
for (const obj of node.objects) {
|
|
118
|
+
if (obj instanceof THREE__namespace.Mesh) {
|
|
119
|
+
const intersects = raycaster.intersectObject(obj, false);
|
|
120
|
+
for (const hit of intersects) {
|
|
121
|
+
results.push({
|
|
122
|
+
object: obj,
|
|
123
|
+
point: hit.point,
|
|
124
|
+
distance: hit.distance,
|
|
125
|
+
face: hit.face ?? null
|
|
126
|
+
});
|
|
127
|
+
}
|
|
130
128
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
129
|
+
}
|
|
130
|
+
if (node.children) {
|
|
131
|
+
for (const child of node.children) {
|
|
132
|
+
raycastNode(child, raycaster, results);
|
|
136
133
|
}
|
|
134
|
+
}
|
|
137
135
|
}
|
|
138
136
|
function frustumCullNode(node, frustum, results) {
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
if (!frustum.intersectsBox(node.bounds)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
for (const obj of node.objects) {
|
|
141
|
+
const objBounds = new THREE__namespace.Box3().setFromObject(obj);
|
|
142
|
+
if (frustum.intersectsBox(objBounds)) {
|
|
143
|
+
results.push(obj);
|
|
141
144
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (node.children) {
|
|
149
|
-
for (const child of node.children) {
|
|
150
|
-
frustumCullNode(child, frustum, results);
|
|
151
|
-
}
|
|
145
|
+
}
|
|
146
|
+
if (node.children) {
|
|
147
|
+
for (const child of node.children) {
|
|
148
|
+
frustumCullNode(child, frustum, results);
|
|
152
149
|
}
|
|
150
|
+
}
|
|
153
151
|
}
|
|
154
152
|
function nearestInNode(node, point, maxDist, best) {
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
if (!node.bounds.distanceToPoint(point)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (node.bounds.distanceToPoint(point) > best.dist) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
for (const obj of node.objects) {
|
|
160
|
+
const pos = new THREE__namespace.Vector3();
|
|
161
|
+
obj.getWorldPosition(pos);
|
|
162
|
+
const dist = pos.distanceTo(point);
|
|
163
|
+
if (dist < best.dist && dist <= maxDist) {
|
|
164
|
+
best.dist = dist;
|
|
165
|
+
best.object = obj;
|
|
157
166
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
best.object = obj;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
if (node.children) {
|
|
171
|
-
// Sort children by distance to point for early termination
|
|
172
|
-
const sorted = [...node.children].sort((a, b) => {
|
|
173
|
-
const da = a.bounds.distanceToPoint(point);
|
|
174
|
-
const db = b.bounds.distanceToPoint(point);
|
|
175
|
-
return da - db;
|
|
176
|
-
});
|
|
177
|
-
for (const child of sorted) {
|
|
178
|
-
nearestInNode(child, point, maxDist, best);
|
|
179
|
-
}
|
|
167
|
+
}
|
|
168
|
+
if (node.children) {
|
|
169
|
+
const sorted = [...node.children].sort((a, b) => {
|
|
170
|
+
const da = a.bounds.distanceToPoint(point);
|
|
171
|
+
const db = b.bounds.distanceToPoint(point);
|
|
172
|
+
return da - db;
|
|
173
|
+
});
|
|
174
|
+
for (const child of sorted) {
|
|
175
|
+
nearestInNode(child, point, maxDist, best);
|
|
180
176
|
}
|
|
177
|
+
}
|
|
181
178
|
}
|
|
182
179
|
function countNodes(node) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
180
|
+
let count = 1;
|
|
181
|
+
if (node.children) {
|
|
182
|
+
for (const child of node.children) {
|
|
183
|
+
count += countNodes(child);
|
|
188
184
|
}
|
|
189
|
-
|
|
185
|
+
}
|
|
186
|
+
return count;
|
|
190
187
|
}
|
|
191
188
|
function countObjects(node) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
189
|
+
let count = node.objects.length;
|
|
190
|
+
if (node.children) {
|
|
191
|
+
for (const child of node.children) {
|
|
192
|
+
count += countObjects(child);
|
|
197
193
|
}
|
|
198
|
-
|
|
194
|
+
}
|
|
195
|
+
return count;
|
|
199
196
|
}
|
|
200
197
|
function buildDebugVisualization(node, group, maxDepth) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
198
|
+
const size = new THREE__namespace.Vector3();
|
|
199
|
+
node.bounds.getSize(size);
|
|
200
|
+
const center = new THREE__namespace.Vector3();
|
|
201
|
+
node.bounds.getCenter(center);
|
|
202
|
+
const hue = node.depth / maxDepth;
|
|
203
|
+
const color = new THREE__namespace.Color().setHSL(hue, 0.8, 0.5);
|
|
204
|
+
const geo = new THREE__namespace.BoxGeometry(size.x, size.y, size.z);
|
|
205
|
+
const wireframe = new THREE__namespace.LineSegments(
|
|
206
|
+
new THREE__namespace.WireframeGeometry(geo),
|
|
207
|
+
new THREE__namespace.LineBasicMaterial({ color, opacity: 0.3, transparent: true })
|
|
208
|
+
);
|
|
209
|
+
wireframe.position.copy(center);
|
|
210
|
+
group.add(wireframe);
|
|
211
|
+
if (node.children) {
|
|
212
|
+
for (const child of node.children) {
|
|
213
|
+
if (child.objects.length > 0 || child.children) {
|
|
214
|
+
buildDebugVisualization(child, group, maxDepth);
|
|
215
|
+
}
|
|
217
216
|
}
|
|
217
|
+
}
|
|
218
218
|
}
|
|
219
|
-
/* ── Hook ── */
|
|
220
219
|
function useOctree(scene, configOverride) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
};
|
|
238
|
-
}, [scene, showDebug]);
|
|
239
|
-
const refreshDebug = React.useCallback((rootNode) => {
|
|
240
|
-
// Clear existing debug
|
|
241
|
-
while (debugGroup.current.children.length) {
|
|
242
|
-
const child = debugGroup.current.children[0];
|
|
243
|
-
debugGroup.current.remove(child);
|
|
244
|
-
}
|
|
245
|
-
if (showDebug) {
|
|
246
|
-
buildDebugVisualization(rootNode, debugGroup.current, config.maxDepth);
|
|
247
|
-
}
|
|
248
|
-
}, [showDebug, config.maxDepth]);
|
|
249
|
-
const build = React.useCallback(() => {
|
|
250
|
-
if (!scene) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
// Compute scene bounds
|
|
254
|
-
const sceneBounds = new THREE__namespace.Box3();
|
|
255
|
-
scene.traverse((child) => {
|
|
256
|
-
if (child instanceof THREE__namespace.Mesh && !child.name.startsWith('__')) {
|
|
257
|
-
sceneBounds.expandByObject(child);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
if (sceneBounds.isEmpty()) {
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
// Make bounds cubic
|
|
264
|
-
const size = new THREE__namespace.Vector3();
|
|
265
|
-
sceneBounds.getSize(size);
|
|
266
|
-
const maxDim = Math.max(size.x, size.y, size.z) * 1.1;
|
|
267
|
-
const center = new THREE__namespace.Vector3();
|
|
268
|
-
sceneBounds.getCenter(center);
|
|
269
|
-
const halfSize = maxDim / 2;
|
|
270
|
-
const cubicBounds = new THREE__namespace.Box3(new THREE__namespace.Vector3(center.x - halfSize, center.y - halfSize, center.z - halfSize), new THREE__namespace.Vector3(center.x + halfSize, center.y + halfSize, center.z + halfSize));
|
|
271
|
-
const rootNode = createNode(cubicBounds, 0);
|
|
272
|
-
scene.traverse((child) => {
|
|
273
|
-
if (child instanceof THREE__namespace.Mesh && !child.name.startsWith('__')) {
|
|
274
|
-
insertObject(rootNode, child, config);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
setRoot(rootNode);
|
|
278
|
-
setStats({ nodeCount: countNodes(rootNode), objectCount: countObjects(rootNode) });
|
|
279
|
-
refreshDebug(rootNode);
|
|
280
|
-
}, [scene, config, refreshDebug]);
|
|
281
|
-
const insert = React.useCallback((object) => {
|
|
282
|
-
if (!root) {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
insertObject(root, object, config);
|
|
286
|
-
setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
|
|
287
|
-
refreshDebug(root);
|
|
288
|
-
}, [root, config, refreshDebug]);
|
|
289
|
-
const remove = React.useCallback((object) => {
|
|
290
|
-
if (!root) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
removeObject(root, object);
|
|
294
|
-
setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
|
|
295
|
-
}, [root]);
|
|
296
|
-
const update = React.useCallback((object) => {
|
|
297
|
-
if (!root) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
removeObject(root, object);
|
|
301
|
-
insertObject(root, object, config);
|
|
302
|
-
}, [root, config]);
|
|
303
|
-
const raycast = React.useCallback((raycaster) => {
|
|
304
|
-
if (!root) {
|
|
305
|
-
return [];
|
|
306
|
-
}
|
|
307
|
-
const results = [];
|
|
308
|
-
raycastNode(root, raycaster, results);
|
|
309
|
-
results.sort((a, b) => a.distance - b.distance);
|
|
310
|
-
return results;
|
|
311
|
-
}, [root]);
|
|
312
|
-
const frustumCull = React.useCallback((camera) => {
|
|
313
|
-
if (!root) {
|
|
314
|
-
return [];
|
|
315
|
-
}
|
|
316
|
-
const frustum = new THREE__namespace.Frustum();
|
|
317
|
-
const projScreenMatrix = new THREE__namespace.Matrix4();
|
|
318
|
-
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
319
|
-
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
320
|
-
const results = [];
|
|
321
|
-
frustumCullNode(root, frustum, results);
|
|
322
|
-
return results;
|
|
323
|
-
}, [root]);
|
|
324
|
-
const nearestNeighbor = React.useCallback((point, maxDistance = Infinity) => {
|
|
325
|
-
if (!root) {
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
const best = { object: null, dist: maxDistance };
|
|
329
|
-
nearestInNode(root, point, maxDistance, best);
|
|
330
|
-
return best.object;
|
|
331
|
-
}, [root]);
|
|
332
|
-
return {
|
|
333
|
-
build,
|
|
334
|
-
insert,
|
|
335
|
-
remove,
|
|
336
|
-
update,
|
|
337
|
-
raycast,
|
|
338
|
-
frustumCull,
|
|
339
|
-
nearestNeighbor,
|
|
340
|
-
nodeCount: stats.nodeCount,
|
|
341
|
-
objectCount: stats.objectCount,
|
|
342
|
-
showDebug,
|
|
343
|
-
setShowDebug,
|
|
344
|
-
root,
|
|
220
|
+
const config = React.useMemo(() => ({ ...DEFAULT_CONFIG, ...configOverride }), [configOverride]);
|
|
221
|
+
const [root, setRoot] = React.useState(null);
|
|
222
|
+
const [showDebug, setShowDebug] = React.useState(false);
|
|
223
|
+
const [stats, setStats] = React.useState({ nodeCount: 0, objectCount: 0 });
|
|
224
|
+
const debugGroup = React.useRef(new THREE__namespace.Group());
|
|
225
|
+
React.useEffect(() => {
|
|
226
|
+
if (!scene) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const group = debugGroup.current;
|
|
230
|
+
group.name = "__octree_debug__";
|
|
231
|
+
if (showDebug) {
|
|
232
|
+
scene.add(group);
|
|
233
|
+
}
|
|
234
|
+
return () => {
|
|
235
|
+
scene.remove(group);
|
|
345
236
|
};
|
|
237
|
+
}, [scene, showDebug]);
|
|
238
|
+
const refreshDebug = React.useCallback(
|
|
239
|
+
(rootNode) => {
|
|
240
|
+
while (debugGroup.current.children.length) {
|
|
241
|
+
const child = debugGroup.current.children[0];
|
|
242
|
+
debugGroup.current.remove(child);
|
|
243
|
+
}
|
|
244
|
+
if (showDebug) {
|
|
245
|
+
buildDebugVisualization(rootNode, debugGroup.current, config.maxDepth);
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
[showDebug, config.maxDepth]
|
|
249
|
+
);
|
|
250
|
+
const build = React.useCallback(() => {
|
|
251
|
+
if (!scene) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const sceneBounds = new THREE__namespace.Box3();
|
|
255
|
+
scene.traverse((child) => {
|
|
256
|
+
if (child instanceof THREE__namespace.Mesh && !child.name.startsWith("__")) {
|
|
257
|
+
sceneBounds.expandByObject(child);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
if (sceneBounds.isEmpty()) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const size = new THREE__namespace.Vector3();
|
|
264
|
+
sceneBounds.getSize(size);
|
|
265
|
+
const maxDim = Math.max(size.x, size.y, size.z) * 1.1;
|
|
266
|
+
const center = new THREE__namespace.Vector3();
|
|
267
|
+
sceneBounds.getCenter(center);
|
|
268
|
+
const halfSize = maxDim / 2;
|
|
269
|
+
const cubicBounds = new THREE__namespace.Box3(
|
|
270
|
+
new THREE__namespace.Vector3(center.x - halfSize, center.y - halfSize, center.z - halfSize),
|
|
271
|
+
new THREE__namespace.Vector3(center.x + halfSize, center.y + halfSize, center.z + halfSize)
|
|
272
|
+
);
|
|
273
|
+
const rootNode = createNode(cubicBounds, 0);
|
|
274
|
+
scene.traverse((child) => {
|
|
275
|
+
if (child instanceof THREE__namespace.Mesh && !child.name.startsWith("__")) {
|
|
276
|
+
insertObject(rootNode, child, config);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
setRoot(rootNode);
|
|
280
|
+
setStats({ nodeCount: countNodes(rootNode), objectCount: countObjects(rootNode) });
|
|
281
|
+
refreshDebug(rootNode);
|
|
282
|
+
}, [scene, config, refreshDebug]);
|
|
283
|
+
const insert = React.useCallback(
|
|
284
|
+
(object) => {
|
|
285
|
+
if (!root) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
insertObject(root, object, config);
|
|
289
|
+
setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
|
|
290
|
+
refreshDebug(root);
|
|
291
|
+
},
|
|
292
|
+
[root, config, refreshDebug]
|
|
293
|
+
);
|
|
294
|
+
const remove = React.useCallback(
|
|
295
|
+
(object) => {
|
|
296
|
+
if (!root) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
removeObject(root, object);
|
|
300
|
+
setStats({ nodeCount: countNodes(root), objectCount: countObjects(root) });
|
|
301
|
+
},
|
|
302
|
+
[root]
|
|
303
|
+
);
|
|
304
|
+
const update = React.useCallback(
|
|
305
|
+
(object) => {
|
|
306
|
+
if (!root) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
removeObject(root, object);
|
|
310
|
+
insertObject(root, object, config);
|
|
311
|
+
},
|
|
312
|
+
[root, config]
|
|
313
|
+
);
|
|
314
|
+
const raycast = React.useCallback(
|
|
315
|
+
(raycaster) => {
|
|
316
|
+
if (!root) {
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
const results = [];
|
|
320
|
+
raycastNode(root, raycaster, results);
|
|
321
|
+
results.sort((a, b) => a.distance - b.distance);
|
|
322
|
+
return results;
|
|
323
|
+
},
|
|
324
|
+
[root]
|
|
325
|
+
);
|
|
326
|
+
const frustumCull = React.useCallback(
|
|
327
|
+
(camera) => {
|
|
328
|
+
if (!root) {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
const frustum = new THREE__namespace.Frustum();
|
|
332
|
+
const projScreenMatrix = new THREE__namespace.Matrix4();
|
|
333
|
+
projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
334
|
+
frustum.setFromProjectionMatrix(projScreenMatrix);
|
|
335
|
+
const results = [];
|
|
336
|
+
frustumCullNode(root, frustum, results);
|
|
337
|
+
return results;
|
|
338
|
+
},
|
|
339
|
+
[root]
|
|
340
|
+
);
|
|
341
|
+
const nearestNeighbor = React.useCallback(
|
|
342
|
+
(point, maxDistance = Infinity) => {
|
|
343
|
+
if (!root) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
const best = { object: null, dist: maxDistance };
|
|
347
|
+
nearestInNode(root, point, maxDistance, best);
|
|
348
|
+
return best.object;
|
|
349
|
+
},
|
|
350
|
+
[root]
|
|
351
|
+
);
|
|
352
|
+
return {
|
|
353
|
+
build,
|
|
354
|
+
insert,
|
|
355
|
+
remove,
|
|
356
|
+
update,
|
|
357
|
+
raycast,
|
|
358
|
+
frustumCull,
|
|
359
|
+
nearestNeighbor,
|
|
360
|
+
nodeCount: stats.nodeCount,
|
|
361
|
+
objectCount: stats.objectCount,
|
|
362
|
+
showDebug,
|
|
363
|
+
setShowDebug,
|
|
364
|
+
root
|
|
365
|
+
};
|
|
346
366
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
367
|
+
const NiceOctree = ({
|
|
368
|
+
scene,
|
|
369
|
+
config,
|
|
370
|
+
showDebug: initialShowDebug,
|
|
371
|
+
className = ""
|
|
372
|
+
}) => {
|
|
373
|
+
const { t } = i18n.useNiceTranslation();
|
|
374
|
+
const octree = useOctree(scene, config);
|
|
375
|
+
const { setShowDebug } = octree;
|
|
376
|
+
React.useEffect(() => {
|
|
377
|
+
if (initialShowDebug !== void 0) {
|
|
378
|
+
setShowDebug(initialShowDebug);
|
|
379
|
+
}
|
|
380
|
+
}, [initialShowDebug, setShowDebug]);
|
|
381
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `nice-octree ${className}`, style: { display: "grid", gap: 8, fontSize: 13 }, children: [
|
|
382
|
+
/* @__PURE__ */ jsxRuntime.jsxs("strong", { children: [
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "tree", size: 14 }),
|
|
384
|
+
" ",
|
|
385
|
+
t("octree.spatialIndexOctree", "Spatial Index (Octree)")
|
|
386
|
+
] }),
|
|
387
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
388
|
+
"button",
|
|
389
|
+
{
|
|
390
|
+
type: "button",
|
|
391
|
+
onClick: octree.build,
|
|
392
|
+
style: {
|
|
393
|
+
background: "var(--nice-color-accent, #6366f1)",
|
|
394
|
+
color: "var(--nice-text-on-primary, #fff)",
|
|
395
|
+
border: "none",
|
|
396
|
+
borderRadius: 4,
|
|
397
|
+
padding: "6px 12px",
|
|
398
|
+
cursor: "pointer"
|
|
399
|
+
},
|
|
400
|
+
children: [
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }),
|
|
402
|
+
" ",
|
|
403
|
+
t("octree.buildOctree", "Build Octree")
|
|
404
|
+
]
|
|
405
|
+
}
|
|
406
|
+
),
|
|
407
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: t("octree.nodesObjects", "Nodes: {nodeCount} · Objects: {objectCount}").replace("{nodeCount}", String(octree.nodeCount)).replace("{objectCount}", String(octree.objectCount)) }),
|
|
408
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
409
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
410
|
+
"input",
|
|
411
|
+
{
|
|
412
|
+
type: "checkbox",
|
|
413
|
+
checked: octree.showDebug,
|
|
414
|
+
onChange: (e) => octree.setShowDebug(e.target.checked)
|
|
355
415
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
border: 'none',
|
|
361
|
-
borderRadius: 4,
|
|
362
|
-
padding: '6px 12px',
|
|
363
|
-
cursor: 'pointer',
|
|
364
|
-
}, children: [jsxRuntime.jsx(ui.NiceIcon, { name: "zap", size: 14 }), " ", t('octree.buildOctree', 'Build Octree')] }), jsxRuntime.jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: t('octree.nodesObjects', 'Nodes: {nodeCount} · Objects: {objectCount}')
|
|
365
|
-
.replace('{nodeCount}', String(octree.nodeCount))
|
|
366
|
-
.replace('{objectCount}', String(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')] })] }));
|
|
416
|
+
),
|
|
417
|
+
t("octree.showDebugWireframes", "Show debug wireframes")
|
|
418
|
+
] })
|
|
419
|
+
] });
|
|
367
420
|
};
|
|
368
|
-
|
|
369
421
|
exports.NiceOctree = NiceOctree;
|
|
370
422
|
exports.useOctree = useOctree;
|