@adatechnology/game-destruction-three 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.global.js +1756 -1679
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +215 -139
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -690,6 +690,8 @@ var MU = 0.7;
|
|
|
690
690
|
var SLEEP_AFTER = 0.5;
|
|
691
691
|
var HARD_LANDING = 3;
|
|
692
692
|
var SLOP = 2e-3;
|
|
693
|
+
var NARROW = 0.3;
|
|
694
|
+
var TOPPLE = 3;
|
|
693
695
|
var PieceSystem = class {
|
|
694
696
|
constructor(THREE, o) {
|
|
695
697
|
__publicField(this, "THREE", THREE);
|
|
@@ -821,6 +823,7 @@ var PieceSystem = class {
|
|
|
821
823
|
let landPoint = [pos[0], pos[1], pos[2]];
|
|
822
824
|
let contactedLast = false;
|
|
823
825
|
let touched = false;
|
|
826
|
+
let unstable = false;
|
|
824
827
|
for (let s = 0; s < steps; s++) {
|
|
825
828
|
v[1] -= this.o.gravity * h;
|
|
826
829
|
pos[0] += v[0] * h;
|
|
@@ -909,6 +912,25 @@ var PieceSystem = class {
|
|
|
909
912
|
}
|
|
910
913
|
}
|
|
911
914
|
}
|
|
915
|
+
let x0 = Infinity, x1 = -Infinity, z0 = Infinity, z1 = -Infinity, low = 0;
|
|
916
|
+
for (const r of rs) {
|
|
917
|
+
x0 = Math.min(x0, r[0]);
|
|
918
|
+
x1 = Math.max(x1, r[0]);
|
|
919
|
+
z0 = Math.min(z0, r[2]);
|
|
920
|
+
z1 = Math.max(z1, r[2]);
|
|
921
|
+
low = Math.min(low, r[1]);
|
|
922
|
+
}
|
|
923
|
+
const height = -low;
|
|
924
|
+
const wx = x1 - x0;
|
|
925
|
+
const wz = z1 - z0;
|
|
926
|
+
unstable = Math.min(wx, wz) < NARROW * height || x0 > 0 || x1 < 0 || z0 > 0 || z1 < 0;
|
|
927
|
+
if (unstable) {
|
|
928
|
+
const dirX = x0 + x1 > 0 ? -1 : 1;
|
|
929
|
+
const dirZ = z0 + z1 > 0 ? -1 : 1;
|
|
930
|
+
const k = TOPPLE * Math.min(height, 1) * h;
|
|
931
|
+
if (wx <= wz) w[2] -= dirX * k;
|
|
932
|
+
else w[0] += dirZ * k;
|
|
933
|
+
}
|
|
912
934
|
const rd = Math.exp((rs.length >= 3 ? -3 : -1.5) * h);
|
|
913
935
|
w[0] *= rd;
|
|
914
936
|
w[1] *= rd;
|
|
@@ -926,7 +948,7 @@ var PieceSystem = class {
|
|
|
926
948
|
b.lastHit = this.time;
|
|
927
949
|
this.o.onHardLanding(b, landPoint, landing);
|
|
928
950
|
}
|
|
929
|
-
const still = touched && Math.hypot(v[0], v[1], v[2]) < 0.12 && Math.hypot(w[0], w[1], w[2]) < 0.45;
|
|
951
|
+
const still = touched && !unstable && Math.hypot(v[0], v[1], v[2]) < 0.12 && Math.hypot(w[0], w[1], w[2]) < 0.45;
|
|
930
952
|
if (still) {
|
|
931
953
|
b.still += dt;
|
|
932
954
|
if (b.still >= SLEEP_AFTER) {
|
|
@@ -986,6 +1008,166 @@ var PieceSystem = class {
|
|
|
986
1008
|
}
|
|
987
1009
|
};
|
|
988
1010
|
|
|
1011
|
+
// src/spec.ts
|
|
1012
|
+
import { guessMaterial, inferJoints } from "@adatechnology/game-destruction";
|
|
1013
|
+
function partHintOf(value) {
|
|
1014
|
+
if (typeof value === "string") return value ? { id: value } : null;
|
|
1015
|
+
if (!value || typeof value !== "object") return null;
|
|
1016
|
+
const v = value;
|
|
1017
|
+
if (typeof v.id !== "string" || !v.id) return null;
|
|
1018
|
+
const str = (x) => typeof x === "string" && x ? x : void 0;
|
|
1019
|
+
return { id: v.id, material: str(v.material), parent: str(v.parent), kind: str(v.kind) };
|
|
1020
|
+
}
|
|
1021
|
+
var METAL = /steel|iron|metal|alumin|copper|brass|bronze|tin|titan|chrome|zinc|lead/i;
|
|
1022
|
+
function sanitizeId(name) {
|
|
1023
|
+
let id = name.trim().replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1024
|
+
if (id && /^[0-9]/.test(id)) id = `p_${id}`;
|
|
1025
|
+
return id;
|
|
1026
|
+
}
|
|
1027
|
+
function isMesh(o) {
|
|
1028
|
+
return o.isMesh === true;
|
|
1029
|
+
}
|
|
1030
|
+
function sizeOf(box) {
|
|
1031
|
+
return [box.max.x - box.min.x, box.max.y - box.min.y, box.max.z - box.min.z];
|
|
1032
|
+
}
|
|
1033
|
+
function specFromObject3D(THREE, root, options = {}) {
|
|
1034
|
+
const fallback = options.fallback ?? "plastic";
|
|
1035
|
+
const minSize = options.minSize ?? 0.015;
|
|
1036
|
+
root.updateMatrixWorld(true);
|
|
1037
|
+
const inv = root.matrixWorld.clone().invert();
|
|
1038
|
+
const entries = [];
|
|
1039
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1040
|
+
const visit = (o) => {
|
|
1041
|
+
if (!o.visible || o.userData?.destructionIgnore) return;
|
|
1042
|
+
if (isMesh(o) && o.geometry) {
|
|
1043
|
+
const geom = o.geometry;
|
|
1044
|
+
if (!geom.boundingBox) geom.computeBoundingBox();
|
|
1045
|
+
if (geom.boundingBox && !geom.boundingBox.isEmpty()) {
|
|
1046
|
+
const box = geom.boundingBox.clone().applyMatrix4(new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld));
|
|
1047
|
+
const mats = Array.isArray(o.material) ? o.material : [o.material];
|
|
1048
|
+
const matName = mats[0]?.name ?? "";
|
|
1049
|
+
const ud = o.userData ?? {};
|
|
1050
|
+
const hint = partHintOf(ud.destructionPart);
|
|
1051
|
+
const material = options.materialOf?.(o) ?? (typeof ud.destructionMaterial === "string" ? ud.destructionMaterial : void 0) ?? hint?.material ?? guessMaterial(`${o.name} ${matName} ${typeof ud.material === "string" ? ud.material : ""}`) ?? fallback;
|
|
1052
|
+
const explicit = hint?.id ?? null;
|
|
1053
|
+
const hollow = ud.hollow === true || /hollow/i.test(o.name);
|
|
1054
|
+
const fill = options.fillOf?.(o, material);
|
|
1055
|
+
const tags = Array.isArray(ud.destructionTags) ? ud.destructionTags : void 0;
|
|
1056
|
+
const existing = explicit ? byKey.get(explicit) : void 0;
|
|
1057
|
+
if (existing) {
|
|
1058
|
+
const before = sizeOf(existing.box);
|
|
1059
|
+
const bv = before[0] * before[1] * before[2];
|
|
1060
|
+
const ns = sizeOf(box);
|
|
1061
|
+
existing.box.union(box);
|
|
1062
|
+
existing.meshes.push(o);
|
|
1063
|
+
if (ns[0] * ns[1] * ns[2] > bv) existing.material = material;
|
|
1064
|
+
existing.hollow = existing.hollow || hollow;
|
|
1065
|
+
if (fill !== void 0) existing.fill = fill;
|
|
1066
|
+
} else {
|
|
1067
|
+
const e = { key: explicit ?? `m${entries.length}`, meshes: [o], box, material, explicitId: explicit, fill, hollow, tags, fixedId: !!explicit, name: o.name, parent: hint?.parent, jointKind: hint?.kind };
|
|
1068
|
+
entries.push(e);
|
|
1069
|
+
if (explicit) byKey.set(explicit, e);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
for (const c of o.children) visit(c);
|
|
1074
|
+
};
|
|
1075
|
+
visit(root);
|
|
1076
|
+
const eps = 5e-3;
|
|
1077
|
+
const overlapVolume = (a, b) => {
|
|
1078
|
+
const x = Math.min(a.max.x, b.max.x) - Math.max(a.min.x, b.min.x) + eps;
|
|
1079
|
+
const y = Math.min(a.max.y, b.max.y) - Math.max(a.min.y, b.min.y) + eps;
|
|
1080
|
+
const z = Math.min(a.max.z, b.max.z) - Math.max(a.min.z, b.min.z) + eps;
|
|
1081
|
+
return x > 0 && y > 0 && z > 0 ? x * y * z : 0;
|
|
1082
|
+
};
|
|
1083
|
+
const largest = (e) => Math.max(...sizeOf(e.box));
|
|
1084
|
+
const kept = [];
|
|
1085
|
+
const tiny = [];
|
|
1086
|
+
for (const e of entries) (!e.fixedId && largest(e) < minSize ? tiny : kept).push(e);
|
|
1087
|
+
for (const t of tiny) {
|
|
1088
|
+
let best = null;
|
|
1089
|
+
let bestV = 0;
|
|
1090
|
+
for (const k of kept) {
|
|
1091
|
+
const v = overlapVolume(t.box, k.box);
|
|
1092
|
+
if (v > bestV) {
|
|
1093
|
+
bestV = v;
|
|
1094
|
+
best = k;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (!best) {
|
|
1098
|
+
let bd = 0.05;
|
|
1099
|
+
for (const k of kept) {
|
|
1100
|
+
const d = k.box.distanceToPoint(t.box.getCenter(new THREE.Vector3()));
|
|
1101
|
+
if (d < bd) {
|
|
1102
|
+
bd = d;
|
|
1103
|
+
best = k;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if (best) {
|
|
1108
|
+
best.meshes.push(...t.meshes);
|
|
1109
|
+
} else {
|
|
1110
|
+
kept.push(t);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
const used = /* @__PURE__ */ new Set();
|
|
1114
|
+
const parts = [];
|
|
1115
|
+
const meshesOf = {};
|
|
1116
|
+
let counter = 0;
|
|
1117
|
+
for (const e of kept) {
|
|
1118
|
+
counter++;
|
|
1119
|
+
let base = (e.explicitId ? sanitizeId(e.explicitId) : "") || sanitizeId(e.name) || `part_${counter}`;
|
|
1120
|
+
let id = base;
|
|
1121
|
+
for (let n = 2; used.has(id); n++) id = `${base}_${n}`;
|
|
1122
|
+
used.add(id);
|
|
1123
|
+
base = id;
|
|
1124
|
+
const size = sizeOf(e.box);
|
|
1125
|
+
const center = e.box.getCenter(new THREE.Vector3());
|
|
1126
|
+
const part = {
|
|
1127
|
+
id,
|
|
1128
|
+
material: e.material,
|
|
1129
|
+
shape: { kind: "box", size: [Math.max(size[0], 2e-3), Math.max(size[1], 2e-3), Math.max(size[2], 2e-3)] },
|
|
1130
|
+
position: [center.x, center.y, center.z]
|
|
1131
|
+
};
|
|
1132
|
+
const minSide = Math.min(...size);
|
|
1133
|
+
if (e.fill !== void 0) part.fill = e.fill;
|
|
1134
|
+
else if (e.hollow) part.fill = 0.05;
|
|
1135
|
+
if (minSide < 0.01 && METAL.test(e.material)) {
|
|
1136
|
+
part.fill = e.fill ?? 1;
|
|
1137
|
+
part.thickness = Math.max(minSide, 5e-4);
|
|
1138
|
+
}
|
|
1139
|
+
if (e.tags) part.tags = [...e.tags];
|
|
1140
|
+
const ao = options.anchorBelow ?? -1;
|
|
1141
|
+
if (ao >= 0 && e.box.min.y <= ao) part.anchored = true;
|
|
1142
|
+
parts.push(part);
|
|
1143
|
+
meshesOf[id] = e.meshes;
|
|
1144
|
+
}
|
|
1145
|
+
const spec = { parts };
|
|
1146
|
+
if (options.name !== void 0) spec.name = options.name;
|
|
1147
|
+
else if (root.name) spec.name = root.name;
|
|
1148
|
+
if (options.tags) spec.tags = [...options.tags];
|
|
1149
|
+
spec.joints = options.joints === false ? [] : inferJoints(parts);
|
|
1150
|
+
if (options.joints !== false) {
|
|
1151
|
+
const finalId = /* @__PURE__ */ new Map();
|
|
1152
|
+
kept.forEach((e, i) => finalId.set(e, parts[i].id));
|
|
1153
|
+
const idOf = /* @__PURE__ */ new Map();
|
|
1154
|
+
for (const e of kept) if (e.explicitId) idOf.set(e.explicitId, finalId.get(e));
|
|
1155
|
+
const joints = spec.joints;
|
|
1156
|
+
for (const e of kept) {
|
|
1157
|
+
if (!e.parent) continue;
|
|
1158
|
+
const id = finalId.get(e);
|
|
1159
|
+
const parent = idOf.get(e.parent) ?? (used.has(sanitizeId(e.parent)) ? sanitizeId(e.parent) : null);
|
|
1160
|
+
if (!parent || parent === id) continue;
|
|
1161
|
+
const at = joints.findIndex((j) => j.a === parent && j.b === id || j.a === id && j.b === parent);
|
|
1162
|
+
const kind = e.jointKind ?? (at >= 0 ? joints[at].kind : "resting");
|
|
1163
|
+
const joint = { id: `${parent}~${id}`, a: parent, b: id, kind };
|
|
1164
|
+
if (at >= 0) joints[at] = joint;
|
|
1165
|
+
else joints.push(joint);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return { spec, parts: meshesOf };
|
|
1169
|
+
}
|
|
1170
|
+
|
|
989
1171
|
// src/heat.ts
|
|
990
1172
|
var STOPS = [
|
|
991
1173
|
[450, 0],
|
|
@@ -1075,6 +1257,7 @@ var Visuals = class {
|
|
|
1075
1257
|
__publicField(this, "box");
|
|
1076
1258
|
/** Total live decals across meshes (kept in sync by add/remove). */
|
|
1077
1259
|
__publicField(this, "decalCount", 0);
|
|
1260
|
+
__publicField(this, "tints", /* @__PURE__ */ new WeakMap());
|
|
1078
1261
|
this.kit = new DecalKit(THREE);
|
|
1079
1262
|
this.a = tmpV(THREE);
|
|
1080
1263
|
this.b = tmpV(THREE);
|
|
@@ -1105,10 +1288,33 @@ var Visuals = class {
|
|
|
1105
1288
|
vertexCount(mv) {
|
|
1106
1289
|
return mv.mesh.geometry.getAttribute(POS_ATTR)?.count ?? 0;
|
|
1107
1290
|
}
|
|
1108
|
-
/**
|
|
1291
|
+
/**
|
|
1292
|
+
* Average vertex colour of a mesh whose material uses `vertexColors` (computed once per mesh, from its original geometry), else null. Fragments
|
|
1293
|
+
* and debris of such a mesh multiply it by `material.color`; without it they come out white.
|
|
1294
|
+
*/
|
|
1295
|
+
vertexTint(mv) {
|
|
1296
|
+
if (this.tints.has(mv.mesh)) return this.tints.get(mv.mesh) ?? null;
|
|
1297
|
+
let tint = null;
|
|
1298
|
+
const used = materialsOf(mv.origMaterial).some((m) => m.vertexColors === true);
|
|
1299
|
+
const attr = used ? mv.origGeometry.getAttribute("color") : void 0;
|
|
1300
|
+
if (attr && attr.count > 0 && attr.itemSize >= 3) {
|
|
1301
|
+
let r = 0, g = 0, b = 0;
|
|
1302
|
+
for (let i = 0; i < attr.count; i++) {
|
|
1303
|
+
r += attr.getX(i);
|
|
1304
|
+
g += attr.getY(i);
|
|
1305
|
+
b += attr.getZ(i);
|
|
1306
|
+
}
|
|
1307
|
+
tint = new this.THREE.Color(r / attr.count, g / attr.count, b / attr.count);
|
|
1308
|
+
}
|
|
1309
|
+
this.tints.set(mv.mesh, tint);
|
|
1310
|
+
return tint;
|
|
1311
|
+
}
|
|
1312
|
+
/** Colour of the original material (before any char) times the vertex tint, `0xRRGGBB`, or -1. */
|
|
1109
1313
|
baseColorHex(mv) {
|
|
1110
1314
|
const c = mv.baseColors?.[0] ?? colorOf(materialsOf(mv.origMaterial)[0]);
|
|
1111
|
-
|
|
1315
|
+
if (!c) return -1;
|
|
1316
|
+
const tint = this.vertexTint(mv);
|
|
1317
|
+
return tint ? c.clone().multiply(tint).getHex() : c.getHex();
|
|
1112
1318
|
}
|
|
1113
1319
|
/** Is the original material transparent? */
|
|
1114
1320
|
isTransparent(mv) {
|
|
@@ -1579,8 +1785,8 @@ var Renderer = class {
|
|
|
1579
1785
|
} else {
|
|
1580
1786
|
const leftovers = [];
|
|
1581
1787
|
for (const m of meshesIn(root)) {
|
|
1582
|
-
const ud = m.userData?.destructionPart;
|
|
1583
|
-
if (
|
|
1788
|
+
const ud = partHintOf(m.userData?.destructionPart)?.id;
|
|
1789
|
+
if (ud !== void 0 && partIds.has(ud)) add(ud, m);
|
|
1584
1790
|
else if (partIds.has(m.name)) add(m.name, m);
|
|
1585
1791
|
else leftovers.push(m);
|
|
1586
1792
|
}
|
|
@@ -1890,7 +2096,10 @@ var Renderer = class {
|
|
|
1890
2096
|
if (!mv) return null;
|
|
1891
2097
|
const mat = materialsOf2(mv.mesh.material)[0];
|
|
1892
2098
|
const c = mat?.color;
|
|
1893
|
-
if (c && c.isColor)
|
|
2099
|
+
if (c && c.isColor) {
|
|
2100
|
+
const tint = this.visuals.vertexTint(mv);
|
|
2101
|
+
return tint ? c.clone().multiply(tint).getHex() : c.getHex();
|
|
2102
|
+
}
|
|
1894
2103
|
return def ? fallbackColor(def.id, def) : null;
|
|
1895
2104
|
}
|
|
1896
2105
|
debrisOptions(kind, objectId, partId) {
|
|
@@ -2334,139 +2543,6 @@ var Renderer = class {
|
|
|
2334
2543
|
this.disposed = true;
|
|
2335
2544
|
}
|
|
2336
2545
|
};
|
|
2337
|
-
|
|
2338
|
-
// src/spec.ts
|
|
2339
|
-
import { guessMaterial, inferJoints } from "@adatechnology/game-destruction";
|
|
2340
|
-
var METAL = /steel|iron|metal|alumin|copper|brass|bronze|tin|titan|chrome|zinc|lead/i;
|
|
2341
|
-
function sanitizeId(name) {
|
|
2342
|
-
let id = name.trim().replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
2343
|
-
if (id && /^[0-9]/.test(id)) id = `p_${id}`;
|
|
2344
|
-
return id;
|
|
2345
|
-
}
|
|
2346
|
-
function isMesh(o) {
|
|
2347
|
-
return o.isMesh === true;
|
|
2348
|
-
}
|
|
2349
|
-
function sizeOf(box) {
|
|
2350
|
-
return [box.max.x - box.min.x, box.max.y - box.min.y, box.max.z - box.min.z];
|
|
2351
|
-
}
|
|
2352
|
-
function specFromObject3D(THREE, root, options = {}) {
|
|
2353
|
-
const fallback = options.fallback ?? "plastic";
|
|
2354
|
-
const minSize = options.minSize ?? 0.015;
|
|
2355
|
-
root.updateMatrixWorld(true);
|
|
2356
|
-
const inv = root.matrixWorld.clone().invert();
|
|
2357
|
-
const entries = [];
|
|
2358
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
2359
|
-
const visit = (o) => {
|
|
2360
|
-
if (!o.visible || o.userData?.destructionIgnore) return;
|
|
2361
|
-
if (isMesh(o) && o.geometry) {
|
|
2362
|
-
const geom = o.geometry;
|
|
2363
|
-
if (!geom.boundingBox) geom.computeBoundingBox();
|
|
2364
|
-
if (geom.boundingBox && !geom.boundingBox.isEmpty()) {
|
|
2365
|
-
const box = geom.boundingBox.clone().applyMatrix4(new THREE.Matrix4().multiplyMatrices(inv, o.matrixWorld));
|
|
2366
|
-
const mats = Array.isArray(o.material) ? o.material : [o.material];
|
|
2367
|
-
const matName = mats[0]?.name ?? "";
|
|
2368
|
-
const ud = o.userData ?? {};
|
|
2369
|
-
const material = options.materialOf?.(o) ?? (typeof ud.destructionMaterial === "string" ? ud.destructionMaterial : void 0) ?? guessMaterial(`${o.name} ${matName} ${typeof ud.material === "string" ? ud.material : ""}`) ?? fallback;
|
|
2370
|
-
const explicit = typeof ud.destructionPart === "string" && ud.destructionPart ? ud.destructionPart : null;
|
|
2371
|
-
const hollow = ud.hollow === true || /hollow/i.test(o.name);
|
|
2372
|
-
const fill = options.fillOf?.(o, material);
|
|
2373
|
-
const tags = Array.isArray(ud.destructionTags) ? ud.destructionTags : void 0;
|
|
2374
|
-
const existing = explicit ? byKey.get(explicit) : void 0;
|
|
2375
|
-
if (existing) {
|
|
2376
|
-
const before = sizeOf(existing.box);
|
|
2377
|
-
const bv = before[0] * before[1] * before[2];
|
|
2378
|
-
const ns = sizeOf(box);
|
|
2379
|
-
existing.box.union(box);
|
|
2380
|
-
existing.meshes.push(o);
|
|
2381
|
-
if (ns[0] * ns[1] * ns[2] > bv) existing.material = material;
|
|
2382
|
-
existing.hollow = existing.hollow || hollow;
|
|
2383
|
-
if (fill !== void 0) existing.fill = fill;
|
|
2384
|
-
} else {
|
|
2385
|
-
const e = { key: explicit ?? `m${entries.length}`, meshes: [o], box, material, explicitId: explicit, fill, hollow, tags, fixedId: !!explicit, name: o.name };
|
|
2386
|
-
entries.push(e);
|
|
2387
|
-
if (explicit) byKey.set(explicit, e);
|
|
2388
|
-
}
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
for (const c of o.children) visit(c);
|
|
2392
|
-
};
|
|
2393
|
-
visit(root);
|
|
2394
|
-
const eps = 5e-3;
|
|
2395
|
-
const overlapVolume = (a, b) => {
|
|
2396
|
-
const x = Math.min(a.max.x, b.max.x) - Math.max(a.min.x, b.min.x) + eps;
|
|
2397
|
-
const y = Math.min(a.max.y, b.max.y) - Math.max(a.min.y, b.min.y) + eps;
|
|
2398
|
-
const z = Math.min(a.max.z, b.max.z) - Math.max(a.min.z, b.min.z) + eps;
|
|
2399
|
-
return x > 0 && y > 0 && z > 0 ? x * y * z : 0;
|
|
2400
|
-
};
|
|
2401
|
-
const largest = (e) => Math.max(...sizeOf(e.box));
|
|
2402
|
-
const kept = [];
|
|
2403
|
-
const tiny = [];
|
|
2404
|
-
for (const e of entries) (!e.fixedId && largest(e) < minSize ? tiny : kept).push(e);
|
|
2405
|
-
for (const t of tiny) {
|
|
2406
|
-
let best = null;
|
|
2407
|
-
let bestV = 0;
|
|
2408
|
-
for (const k of kept) {
|
|
2409
|
-
const v = overlapVolume(t.box, k.box);
|
|
2410
|
-
if (v > bestV) {
|
|
2411
|
-
bestV = v;
|
|
2412
|
-
best = k;
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
if (!best) {
|
|
2416
|
-
let bd = 0.05;
|
|
2417
|
-
for (const k of kept) {
|
|
2418
|
-
const d = k.box.distanceToPoint(t.box.getCenter(new THREE.Vector3()));
|
|
2419
|
-
if (d < bd) {
|
|
2420
|
-
bd = d;
|
|
2421
|
-
best = k;
|
|
2422
|
-
}
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
if (best) {
|
|
2426
|
-
best.meshes.push(...t.meshes);
|
|
2427
|
-
} else {
|
|
2428
|
-
kept.push(t);
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
const used = /* @__PURE__ */ new Set();
|
|
2432
|
-
const parts = [];
|
|
2433
|
-
const meshesOf = {};
|
|
2434
|
-
let counter = 0;
|
|
2435
|
-
for (const e of kept) {
|
|
2436
|
-
counter++;
|
|
2437
|
-
let base = (e.explicitId ? sanitizeId(e.explicitId) : "") || sanitizeId(e.name) || `part_${counter}`;
|
|
2438
|
-
let id = base;
|
|
2439
|
-
for (let n = 2; used.has(id); n++) id = `${base}_${n}`;
|
|
2440
|
-
used.add(id);
|
|
2441
|
-
base = id;
|
|
2442
|
-
const size = sizeOf(e.box);
|
|
2443
|
-
const center = e.box.getCenter(new THREE.Vector3());
|
|
2444
|
-
const part = {
|
|
2445
|
-
id,
|
|
2446
|
-
material: e.material,
|
|
2447
|
-
shape: { kind: "box", size: [Math.max(size[0], 2e-3), Math.max(size[1], 2e-3), Math.max(size[2], 2e-3)] },
|
|
2448
|
-
position: [center.x, center.y, center.z]
|
|
2449
|
-
};
|
|
2450
|
-
const minSide = Math.min(...size);
|
|
2451
|
-
if (e.fill !== void 0) part.fill = e.fill;
|
|
2452
|
-
else if (e.hollow) part.fill = 0.05;
|
|
2453
|
-
if (minSide < 0.01 && METAL.test(e.material)) {
|
|
2454
|
-
part.fill = e.fill ?? 1;
|
|
2455
|
-
part.thickness = Math.max(minSide, 5e-4);
|
|
2456
|
-
}
|
|
2457
|
-
if (e.tags) part.tags = [...e.tags];
|
|
2458
|
-
const ao = options.anchorBelow ?? -1;
|
|
2459
|
-
if (ao >= 0 && e.box.min.y <= ao) part.anchored = true;
|
|
2460
|
-
parts.push(part);
|
|
2461
|
-
meshesOf[id] = e.meshes;
|
|
2462
|
-
}
|
|
2463
|
-
const spec = { parts };
|
|
2464
|
-
if (options.name !== void 0) spec.name = options.name;
|
|
2465
|
-
else if (root.name) spec.name = root.name;
|
|
2466
|
-
if (options.tags) spec.tags = [...options.tags];
|
|
2467
|
-
spec.joints = options.joints === false ? [] : inferJoints(parts);
|
|
2468
|
-
return { spec, parts: meshesOf };
|
|
2469
|
-
}
|
|
2470
2546
|
export {
|
|
2471
2547
|
createDestructionRenderer,
|
|
2472
2548
|
heatColor,
|