@adatechnology/game-items-three 0.3.2 → 0.4.0
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/dist/index.d.ts +13 -1
- package/dist/index.global.js +531 -54
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +64 -11
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.global.js
CHANGED
|
@@ -2761,14 +2761,19 @@ var GameItemsThree = (() => {
|
|
|
2761
2761
|
},
|
|
2762
2762
|
tessellate: {
|
|
2763
2763
|
cylinder: {
|
|
2764
|
-
heightSegments:
|
|
2765
|
-
radialSegments:
|
|
2764
|
+
heightSegments: 24,
|
|
2765
|
+
radialSegments: 40,
|
|
2766
|
+
capRings: 6
|
|
2766
2767
|
},
|
|
2767
2768
|
box: {
|
|
2768
2769
|
widthSegments: 6,
|
|
2769
2770
|
heightSegments: 1,
|
|
2770
2771
|
depthSegments: 4
|
|
2771
2772
|
}
|
|
2773
|
+
},
|
|
2774
|
+
wall: {
|
|
2775
|
+
body: 1e-4,
|
|
2776
|
+
ends: 25e-5
|
|
2772
2777
|
}
|
|
2773
2778
|
}
|
|
2774
2779
|
},
|
|
@@ -2792,14 +2797,19 @@ var GameItemsThree = (() => {
|
|
|
2792
2797
|
},
|
|
2793
2798
|
tessellate: {
|
|
2794
2799
|
cylinder: {
|
|
2795
|
-
heightSegments:
|
|
2796
|
-
radialSegments:
|
|
2800
|
+
heightSegments: 24,
|
|
2801
|
+
radialSegments: 40,
|
|
2802
|
+
capRings: 6
|
|
2797
2803
|
},
|
|
2798
2804
|
box: {
|
|
2799
2805
|
widthSegments: 6,
|
|
2800
2806
|
heightSegments: 1,
|
|
2801
2807
|
depthSegments: 4
|
|
2802
2808
|
}
|
|
2809
|
+
},
|
|
2810
|
+
wall: {
|
|
2811
|
+
body: 18e-5,
|
|
2812
|
+
ends: 22e-5
|
|
2803
2813
|
}
|
|
2804
2814
|
}
|
|
2805
2815
|
}
|
|
@@ -2893,12 +2903,53 @@ var GameItemsThree = (() => {
|
|
|
2893
2903
|
if (!box4) return 0;
|
|
2894
2904
|
return (box4.max.x - box4.min.x) * (box4.max.y - box4.min.y) * (box4.max.z - box4.min.z);
|
|
2895
2905
|
}
|
|
2896
|
-
function
|
|
2906
|
+
function ringedCylinder(T2, p, radial, maxRows, rings) {
|
|
2907
|
+
const half = p.height / 2;
|
|
2908
|
+
const column = 2 * Math.PI * Math.max(p.radiusTop, p.radiusBottom, 1e-6) / radial;
|
|
2909
|
+
const rows = Math.max(1, Math.min(maxRows, Math.round(p.height / column)));
|
|
2910
|
+
const points = [];
|
|
2911
|
+
if (!p.openEnded) for (let i = 0; i <= rings; i++) points.push(new T2.Vector2(p.radiusBottom * i / rings, -half));
|
|
2912
|
+
for (let j = 0; j <= rows; j++) points.push(new T2.Vector2(p.radiusBottom + (p.radiusTop - p.radiusBottom) * j / rows, -half + p.height * j / rows));
|
|
2913
|
+
if (!p.openEnded) for (let i = rings; i >= 0; i--) points.push(new T2.Vector2(p.radiusTop * i / rings, half));
|
|
2914
|
+
return lathed(T2, points, radial, p.thetaStart, p.thetaLength);
|
|
2915
|
+
}
|
|
2916
|
+
function lathed(T2, points, radial, phiStart, phiLength) {
|
|
2917
|
+
const geometry = new T2.LatheGeometry([...points], radial, phiStart, phiLength);
|
|
2918
|
+
geometry.computeVertexNormals();
|
|
2919
|
+
const position = geometry.getAttribute("position");
|
|
2920
|
+
const normal = geometry.getAttribute("normal");
|
|
2921
|
+
const middle = (Math.min(...points.map((point) => point.y)) + Math.max(...points.map((point) => point.y))) / 2;
|
|
2922
|
+
for (let i = 0; i < normal.count; i++) {
|
|
2923
|
+
if (Math.hypot(normal.getX(i), normal.getY(i), normal.getZ(i)) > 0.5) continue;
|
|
2924
|
+
normal.setXYZ(i, 0, position.getY(i) >= middle ? 1 : -1, 0);
|
|
2925
|
+
}
|
|
2926
|
+
return geometry;
|
|
2927
|
+
}
|
|
2928
|
+
function finerProfile(T2, points, step) {
|
|
2929
|
+
const out = [];
|
|
2930
|
+
points.forEach((point, index) => {
|
|
2931
|
+
const next = points[index + 1];
|
|
2932
|
+
out.push(point.clone());
|
|
2933
|
+
if (!next) return;
|
|
2934
|
+
const pieces = Math.max(1, Math.round(point.distanceTo(next) / step));
|
|
2935
|
+
for (let k = 1; k < pieces; k++) out.push(point.clone().lerp(next, k / pieces));
|
|
2936
|
+
});
|
|
2937
|
+
return out;
|
|
2938
|
+
}
|
|
2939
|
+
function tessellated(geometry, rule, T2) {
|
|
2897
2940
|
const Shape = geometry.constructor;
|
|
2898
2941
|
if (geometry.type === "CylinderGeometry" && rule.cylinder) {
|
|
2899
|
-
const
|
|
2942
|
+
const parameters = geometry.parameters;
|
|
2943
|
+
const { radiusTop, radiusBottom, height, openEnded, thetaStart, thetaLength } = parameters;
|
|
2944
|
+
if (T2 && rule.cylinder.capRings !== void 0) return ringedCylinder(T2, parameters, rule.cylinder.radialSegments, rule.cylinder.heightSegments, rule.cylinder.capRings);
|
|
2900
2945
|
return new Shape(radiusTop, radiusBottom, height, rule.cylinder.radialSegments, rule.cylinder.heightSegments, openEnded, thetaStart, thetaLength);
|
|
2901
2946
|
}
|
|
2947
|
+
if (geometry.type === "LatheGeometry" && rule.cylinder && T2 && rule.cylinder.capRings !== void 0) {
|
|
2948
|
+
const { points, segments, phiStart, phiLength } = geometry.parameters;
|
|
2949
|
+
if (segments >= rule.cylinder.radialSegments) return void 0;
|
|
2950
|
+
const widest = Math.max(...points.map((point) => point.x), 1e-6);
|
|
2951
|
+
return lathed(T2, finerProfile(T2, points, 2 * Math.PI * widest / rule.cylinder.radialSegments), rule.cylinder.radialSegments, phiStart, phiLength);
|
|
2952
|
+
}
|
|
2902
2953
|
if (geometry.type === "BoxGeometry" && rule.box) {
|
|
2903
2954
|
const { width, height, depth } = geometry.parameters;
|
|
2904
2955
|
return new Shape(width, height, depth, rule.box.widthSegments, rule.box.heightSegments, rule.box.depthSegments);
|
|
@@ -2906,14 +2957,14 @@ var GameItemsThree = (() => {
|
|
|
2906
2957
|
return void 0;
|
|
2907
2958
|
}
|
|
2908
2959
|
var triangleCount = (geometry) => (geometry.index ? geometry.index.count : geometry.getAttribute("position").count) / 3;
|
|
2909
|
-
function tessellateShapes(root, rule) {
|
|
2960
|
+
function tessellateShapes(root, rule, T2) {
|
|
2910
2961
|
const rebuilt = /* @__PURE__ */ new Map();
|
|
2911
2962
|
root.traverse((object2) => {
|
|
2912
2963
|
const mesh2 = object2;
|
|
2913
2964
|
if (!mesh2.isMesh) return;
|
|
2914
2965
|
let fresh = rebuilt.get(mesh2.geometry);
|
|
2915
2966
|
if (!fresh) {
|
|
2916
|
-
fresh = tessellated(mesh2.geometry, rule);
|
|
2967
|
+
fresh = tessellated(mesh2.geometry, rule, T2);
|
|
2917
2968
|
if (!fresh) return;
|
|
2918
2969
|
rebuilt.set(mesh2.geometry, fresh);
|
|
2919
2970
|
}
|
|
@@ -2927,10 +2978,10 @@ var GameItemsThree = (() => {
|
|
|
2927
2978
|
root.userData.tessellationTriangles = added;
|
|
2928
2979
|
}
|
|
2929
2980
|
function tagForDestruction(root, options = {}) {
|
|
2930
|
-
const { modelId, weight, size } = options;
|
|
2981
|
+
const { modelId, weight, size, three } = options;
|
|
2931
2982
|
if (weight !== void 0 && weight > 0) root.userData.destructionMass = weight;
|
|
2932
2983
|
const rule = modelId === void 0 ? void 0 : itemDestruction(modelId);
|
|
2933
|
-
if (rule?.tessellate) tessellateShapes(root, rule.tessellate);
|
|
2984
|
+
if (rule?.tessellate) tessellateShapes(root, rule.tessellate, three);
|
|
2934
2985
|
const solid = [];
|
|
2935
2986
|
root.traverse((object2) => {
|
|
2936
2987
|
const mesh2 = object2;
|
|
@@ -2950,6 +3001,7 @@ var GameItemsThree = (() => {
|
|
|
2950
3001
|
const anchor = solid.reduce((largest, mesh2) => volumeOf(mesh2) > volumeOf(largest) ? mesh2 : largest);
|
|
2951
3002
|
solid.forEach((mesh2, index) => {
|
|
2952
3003
|
mesh2.userData.destructionPart = mesh2 === anchor ? { id: SHELL_PART_ID } : { id: `shell_${index}`, parent: SHELL_PART_ID, kind: "weld" };
|
|
3004
|
+
if (rule.wall) mesh2.userData.destructionThickness = mesh2 === anchor ? rule.wall.body : rule.wall.ends;
|
|
2953
3005
|
});
|
|
2954
3006
|
const contents = size === void 0 ? void 0 : itemContents({ ...modelId === void 0 ? {} : { model: modelId }, size, ...weight === void 0 ? {} : { weight } });
|
|
2955
3007
|
if (contents) anchor.userData.destructionContains = { fluid: contents.liquid, liters: contents.liters };
|
|
@@ -9070,7 +9122,7 @@ var GameItemsThree = (() => {
|
|
|
9070
9122
|
const materials = kit.materials.slice(first);
|
|
9071
9123
|
if (options.wear) setWear(materials, options.wear);
|
|
9072
9124
|
root.userData.wear = options.wear ?? 0;
|
|
9073
|
-
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size });
|
|
9125
|
+
if (options.destructible) tagForDestruction(root, { ...kind === "model" && item.model !== void 0 ? { modelId: item.model } : {}, ...item.weight === void 0 ? {} : { weight: item.weight }, size: item.size, three: T2 });
|
|
9074
9126
|
markKitOrigin(root, { owner: "game-items-three", kind: "item", id: item.id, ...kind === "shape" ? { standIn: true } : {} });
|
|
9075
9127
|
return { root, kind, ...shape ? { shape } : {}, size: item.size, materials };
|
|
9076
9128
|
}
|
|
@@ -12730,7 +12782,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12730
12782
|
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
12731
12783
|
band.c[L] = (lo + hi) / 2;
|
|
12732
12784
|
band.s[L] = Math.max(hi - lo, 0.01);
|
|
12733
|
-
|
|
12785
|
+
if (inp.style === "snap") {
|
|
12786
|
+
for (const b of bsp(band, Math.max(3, Math.min(6, inp.maxFragments - out.length)), [0, 1, 2], inp.rnd)) out.push({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] });
|
|
12787
|
+
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
12734
12788
|
return out;
|
|
12735
12789
|
};
|
|
12736
12790
|
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
@@ -12761,6 +12815,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12761
12815
|
const t = sorted[0];
|
|
12762
12816
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
12763
12817
|
if (style === "snap") {
|
|
12818
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
12764
12819
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
12765
12820
|
for (const b of bsp(root, n2, [0, 1, 2], rnd)) out.push({ center: b.c, size: b.s, q: idq, shape: "slab", weight: b.s[0] * b.s[1] * b.s[2] });
|
|
12766
12821
|
return out;
|
|
@@ -12934,6 +12989,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12934
12989
|
for (const x of spread(dims[u], COVER, spacing, MAX_BARS_PER_DIRECTION)) out.push(bar(v, dims[v] / 2 - 0.02, [[u, x], [thin, 0]]));
|
|
12935
12990
|
return out;
|
|
12936
12991
|
};
|
|
12992
|
+
var clipBarsToBox = (bars, u, v, center, size) => {
|
|
12993
|
+
const out = [];
|
|
12994
|
+
for (const [a, b] of bars) {
|
|
12995
|
+
const axis = a[u] !== b[u] ? u : v;
|
|
12996
|
+
const other = axis === u ? v : u;
|
|
12997
|
+
if (Math.abs(a[other] - center[other]) > size[other] / 2) continue;
|
|
12998
|
+
const lo = Math.max(Math.min(a[axis], b[axis]), center[axis] - size[axis] / 2), hi = Math.min(Math.max(a[axis], b[axis]), center[axis] + size[axis] / 2);
|
|
12999
|
+
if (hi - lo < 1e-6) continue;
|
|
13000
|
+
const p = [a[0], a[1], a[2]], q = [b[0], b[1], b[2]];
|
|
13001
|
+
p[axis] = lo;
|
|
13002
|
+
q[axis] = hi;
|
|
13003
|
+
out.push([p, q]);
|
|
13004
|
+
}
|
|
13005
|
+
return out;
|
|
13006
|
+
};
|
|
13007
|
+
var bendBar = ([a, b], thin, bow, i) => {
|
|
13008
|
+
const length = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
13009
|
+
const m = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];
|
|
13010
|
+
m[thin] += (i % 2 === 0 ? 1 : -1) * bow * length;
|
|
13011
|
+
return [[a, m], [m, b]];
|
|
13012
|
+
};
|
|
12937
13013
|
var clipBarsToDisc = (bars, u, v, center, radius) => {
|
|
12938
13014
|
const out = [];
|
|
12939
13015
|
for (const [a, b] of bars) {
|
|
@@ -13854,14 +13930,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13854
13930
|
smoke: "grey"
|
|
13855
13931
|
},
|
|
13856
13932
|
visual: {
|
|
13857
|
-
charColor: 1709072
|
|
13933
|
+
charColor: 1709072,
|
|
13934
|
+
innerColor: 14730131
|
|
13858
13935
|
},
|
|
13859
13936
|
tags: [
|
|
13860
13937
|
"wood",
|
|
13861
13938
|
"organic",
|
|
13862
13939
|
"flammable"
|
|
13863
13940
|
],
|
|
13864
|
-
compressive: 1e5
|
|
13941
|
+
compressive: 1e5,
|
|
13942
|
+
hits: {
|
|
13943
|
+
blunt: {
|
|
13944
|
+
tear: "splinters",
|
|
13945
|
+
tearJoules: 60
|
|
13946
|
+
}
|
|
13947
|
+
}
|
|
13865
13948
|
},
|
|
13866
13949
|
{
|
|
13867
13950
|
id: "wood_hard",
|
|
@@ -13891,14 +13974,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13891
13974
|
smoke: "grey"
|
|
13892
13975
|
},
|
|
13893
13976
|
visual: {
|
|
13894
|
-
charColor: 1709072
|
|
13977
|
+
charColor: 1709072,
|
|
13978
|
+
innerColor: 13213802
|
|
13895
13979
|
},
|
|
13896
13980
|
tags: [
|
|
13897
13981
|
"wood",
|
|
13898
13982
|
"organic",
|
|
13899
13983
|
"flammable"
|
|
13900
13984
|
],
|
|
13901
|
-
compressive: 15e4
|
|
13985
|
+
compressive: 15e4,
|
|
13986
|
+
hits: {
|
|
13987
|
+
blunt: {
|
|
13988
|
+
tear: "splinters",
|
|
13989
|
+
tearJoules: 150
|
|
13990
|
+
}
|
|
13991
|
+
}
|
|
13902
13992
|
},
|
|
13903
13993
|
{
|
|
13904
13994
|
id: "plywood",
|
|
@@ -13928,14 +14018,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13928
14018
|
smoke: "grey"
|
|
13929
14019
|
},
|
|
13930
14020
|
visual: {
|
|
13931
|
-
charColor: 1709072
|
|
14021
|
+
charColor: 1709072,
|
|
14022
|
+
innerColor: 14862234
|
|
13932
14023
|
},
|
|
13933
14024
|
tags: [
|
|
13934
14025
|
"wood",
|
|
13935
14026
|
"organic",
|
|
13936
14027
|
"flammable"
|
|
13937
14028
|
],
|
|
13938
|
-
compressive: 6e4
|
|
14029
|
+
compressive: 6e4,
|
|
14030
|
+
hits: {
|
|
14031
|
+
ballistic: {
|
|
14032
|
+
tear: "splinters"
|
|
14033
|
+
},
|
|
14034
|
+
blunt: {
|
|
14035
|
+
tear: "splinters",
|
|
14036
|
+
tearJoules: 80
|
|
14037
|
+
}
|
|
14038
|
+
}
|
|
13939
14039
|
},
|
|
13940
14040
|
{
|
|
13941
14041
|
id: "mdf",
|
|
@@ -13962,14 +14062,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13962
14062
|
smoke: "grey"
|
|
13963
14063
|
},
|
|
13964
14064
|
visual: {
|
|
13965
|
-
charColor: 1709072
|
|
14065
|
+
charColor: 1709072,
|
|
14066
|
+
innerColor: 12096362
|
|
13966
14067
|
},
|
|
13967
14068
|
tags: [
|
|
13968
14069
|
"wood",
|
|
13969
14070
|
"organic",
|
|
13970
14071
|
"flammable"
|
|
13971
14072
|
],
|
|
13972
|
-
compressive: 3e4
|
|
14073
|
+
compressive: 3e4,
|
|
14074
|
+
hits: {
|
|
14075
|
+
ballistic: {
|
|
14076
|
+
tear: "lumps",
|
|
14077
|
+
tearJoules: 120
|
|
14078
|
+
},
|
|
14079
|
+
blunt: {
|
|
14080
|
+
tear: "lumps",
|
|
14081
|
+
tearJoules: 20
|
|
14082
|
+
}
|
|
14083
|
+
}
|
|
13973
14084
|
},
|
|
13974
14085
|
{
|
|
13975
14086
|
id: "glass",
|
|
@@ -14083,6 +14194,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14083
14194
|
damageScale: {
|
|
14084
14195
|
explosive: 1.2
|
|
14085
14196
|
},
|
|
14197
|
+
hits: {
|
|
14198
|
+
ballistic: {
|
|
14199
|
+
unitJoules: 500,
|
|
14200
|
+
floor: 900,
|
|
14201
|
+
crater: true
|
|
14202
|
+
},
|
|
14203
|
+
blunt: {
|
|
14204
|
+
unitJoules: 1500,
|
|
14205
|
+
floor: 60
|
|
14206
|
+
}
|
|
14207
|
+
},
|
|
14086
14208
|
hitDebris: "chips",
|
|
14087
14209
|
breakDebris: "dust",
|
|
14088
14210
|
thermal: {
|
|
@@ -14451,7 +14573,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14451
14573
|
smoke: "white"
|
|
14452
14574
|
},
|
|
14453
14575
|
visual: {
|
|
14454
|
-
charColor: 1709072
|
|
14576
|
+
charColor: 1709072,
|
|
14577
|
+
innerColor: 14207126
|
|
14455
14578
|
},
|
|
14456
14579
|
tags: [
|
|
14457
14580
|
"plant",
|
|
@@ -14459,7 +14582,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14459
14582
|
"organic",
|
|
14460
14583
|
"flammable"
|
|
14461
14584
|
],
|
|
14462
|
-
compressive: 6e4
|
|
14585
|
+
compressive: 6e4,
|
|
14586
|
+
hits: {
|
|
14587
|
+
blunt: {
|
|
14588
|
+
tear: "none"
|
|
14589
|
+
}
|
|
14590
|
+
}
|
|
14463
14591
|
},
|
|
14464
14592
|
{
|
|
14465
14593
|
id: "cactus",
|
|
@@ -14574,6 +14702,130 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14574
14702
|
"masonry"
|
|
14575
14703
|
],
|
|
14576
14704
|
compressive: 2e4
|
|
14705
|
+
},
|
|
14706
|
+
{
|
|
14707
|
+
id: "asphalt",
|
|
14708
|
+
density: 2350,
|
|
14709
|
+
hpPerKg: 10,
|
|
14710
|
+
penetration: 14,
|
|
14711
|
+
penetrationClass: "concrete",
|
|
14712
|
+
penetrationScale: 0.8,
|
|
14713
|
+
ductility: 0.1,
|
|
14714
|
+
fracture: "crumble",
|
|
14715
|
+
fragmentSize: [
|
|
14716
|
+
0.03,
|
|
14717
|
+
0.15
|
|
14718
|
+
],
|
|
14719
|
+
hitDebris: "chips",
|
|
14720
|
+
breakDebris: "dust",
|
|
14721
|
+
thermal: {
|
|
14722
|
+
conductivity: 0.1,
|
|
14723
|
+
heatCapacity: 2,
|
|
14724
|
+
ignitionC: 400,
|
|
14725
|
+
burnRate: 2e-3,
|
|
14726
|
+
smoke: "black"
|
|
14727
|
+
},
|
|
14728
|
+
tags: [
|
|
14729
|
+
"masonry",
|
|
14730
|
+
"brittle"
|
|
14731
|
+
],
|
|
14732
|
+
compressive: 3e4
|
|
14733
|
+
},
|
|
14734
|
+
{
|
|
14735
|
+
id: "sand",
|
|
14736
|
+
density: 1600,
|
|
14737
|
+
hpPerKg: 2,
|
|
14738
|
+
penetration: 6,
|
|
14739
|
+
ductility: 0.4,
|
|
14740
|
+
fracture: "crumble",
|
|
14741
|
+
fragmentSize: [
|
|
14742
|
+
0.01,
|
|
14743
|
+
0.04
|
|
14744
|
+
],
|
|
14745
|
+
hitDebris: "dust",
|
|
14746
|
+
breakDebris: "dust",
|
|
14747
|
+
thermal: {
|
|
14748
|
+
conductivity: 0.1,
|
|
14749
|
+
heatCapacity: 2
|
|
14750
|
+
},
|
|
14751
|
+
tags: [
|
|
14752
|
+
"soil"
|
|
14753
|
+
],
|
|
14754
|
+
compressive: 1e4
|
|
14755
|
+
},
|
|
14756
|
+
{
|
|
14757
|
+
id: "gravel",
|
|
14758
|
+
density: 1800,
|
|
14759
|
+
hpPerKg: 3,
|
|
14760
|
+
penetration: 8,
|
|
14761
|
+
ductility: 0.3,
|
|
14762
|
+
fracture: "crumble",
|
|
14763
|
+
fragmentSize: [
|
|
14764
|
+
0.02,
|
|
14765
|
+
0.06
|
|
14766
|
+
],
|
|
14767
|
+
hitDebris: "chips",
|
|
14768
|
+
breakDebris: "dust",
|
|
14769
|
+
thermal: {
|
|
14770
|
+
conductivity: 0.1,
|
|
14771
|
+
heatCapacity: 2
|
|
14772
|
+
},
|
|
14773
|
+
tags: [
|
|
14774
|
+
"soil",
|
|
14775
|
+
"masonry"
|
|
14776
|
+
],
|
|
14777
|
+
compressive: 2e4
|
|
14778
|
+
},
|
|
14779
|
+
{
|
|
14780
|
+
id: "turf",
|
|
14781
|
+
density: 900,
|
|
14782
|
+
hpPerKg: 3,
|
|
14783
|
+
penetration: 4,
|
|
14784
|
+
ductility: 0.5,
|
|
14785
|
+
fracture: "crumble",
|
|
14786
|
+
fragmentSize: [
|
|
14787
|
+
0.03,
|
|
14788
|
+
0.1
|
|
14789
|
+
],
|
|
14790
|
+
hitDebris: "dust",
|
|
14791
|
+
breakDebris: "dust",
|
|
14792
|
+
thermal: {
|
|
14793
|
+
conductivity: 0.1,
|
|
14794
|
+
heatCapacity: 1.5,
|
|
14795
|
+
ignitionC: 250,
|
|
14796
|
+
burnRate: 0.03,
|
|
14797
|
+
smoke: "white"
|
|
14798
|
+
},
|
|
14799
|
+
tags: [
|
|
14800
|
+
"soil",
|
|
14801
|
+
"organic"
|
|
14802
|
+
],
|
|
14803
|
+
compressive: 15e3
|
|
14804
|
+
},
|
|
14805
|
+
{
|
|
14806
|
+
id: "mortar",
|
|
14807
|
+
density: 1900,
|
|
14808
|
+
hpPerKg: 10,
|
|
14809
|
+
penetration: 12,
|
|
14810
|
+
penetrationClass: "concrete",
|
|
14811
|
+
penetrationScale: 1.2,
|
|
14812
|
+
ductility: 0.03,
|
|
14813
|
+
fracture: "crumble",
|
|
14814
|
+
fragmentSize: [
|
|
14815
|
+
0.02,
|
|
14816
|
+
0.08
|
|
14817
|
+
],
|
|
14818
|
+
hitDebris: "chips",
|
|
14819
|
+
breakDebris: "dust",
|
|
14820
|
+
thermal: {
|
|
14821
|
+
conductivity: 0.15,
|
|
14822
|
+
heatCapacity: 2.5
|
|
14823
|
+
},
|
|
14824
|
+
tags: [
|
|
14825
|
+
"masonry",
|
|
14826
|
+
"brittle"
|
|
14827
|
+
],
|
|
14828
|
+
compressive: 1e5
|
|
14577
14829
|
}
|
|
14578
14830
|
];
|
|
14579
14831
|
var material_hints_default = {
|
|
@@ -14747,6 +14999,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14747
14999
|
return (1 + 4 * (0.3 - ductility) / 0.3) * Math.min(25, Math.max(1, (0.02 / Math.max(thickness, 1e-4)) ** 2));
|
|
14748
15000
|
};
|
|
14749
15001
|
var THIN_SHEET_DISHING = 5e-3;
|
|
15002
|
+
var SWEEP_BACK = 0.01;
|
|
15003
|
+
function sweepOrigins(box4, d, spacing) {
|
|
15004
|
+
const origins = [];
|
|
15005
|
+
const line = (from, to) => {
|
|
15006
|
+
const n = Math.max(1, Math.ceil((to - from) / spacing));
|
|
15007
|
+
return Array.from({ length: n + 1 }, (_, i) => from + (to - from) * i / n);
|
|
15008
|
+
};
|
|
15009
|
+
for (const axis of [0, 1, 2]) {
|
|
15010
|
+
if (Math.abs(d[axis]) < 1e-3) continue;
|
|
15011
|
+
const lead = d[axis] > 0 ? box4.max[axis] - SWEEP_BACK : box4.min[axis] + SWEEP_BACK;
|
|
15012
|
+
const u = (axis + 1) % 3;
|
|
15013
|
+
const v = (axis + 2) % 3;
|
|
15014
|
+
for (const a of line(box4.min[u], box4.max[u])) {
|
|
15015
|
+
for (const b of line(box4.min[v], box4.max[v])) {
|
|
15016
|
+
const point = [0, 0, 0];
|
|
15017
|
+
point[axis] = lead;
|
|
15018
|
+
point[u] = a;
|
|
15019
|
+
point[v] = b;
|
|
15020
|
+
origins.push(point);
|
|
15021
|
+
}
|
|
15022
|
+
}
|
|
15023
|
+
}
|
|
15024
|
+
return origins;
|
|
15025
|
+
}
|
|
15026
|
+
var THIN_SHEET_CRUSH = 5e-4;
|
|
15027
|
+
var THIN_SHEET_CONTACT = 0.025;
|
|
14750
15028
|
var DMG_PER_J = 0.02;
|
|
14751
15029
|
var BLAST_COUPLING = 0.1;
|
|
14752
15030
|
var SHELTER_DEFAULTS = { k: 2800, brokenTransmission: 0.5, minPath: 5e-3 };
|
|
@@ -14763,11 +15041,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14763
15041
|
var SEVER_BAND = 0.1;
|
|
14764
15042
|
var SEVER_ASPECT = 3;
|
|
14765
15043
|
var SEVER_BALLISTIC = 2;
|
|
15044
|
+
var TEAR_JOULES = 150;
|
|
15045
|
+
var tears = 0;
|
|
14766
15046
|
var SUPPORT_TOLERANCE = 0.05;
|
|
14767
15047
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
14768
15048
|
var BREACH_GRID_CAP = 4096;
|
|
14769
15049
|
var CHIP_ENERGY = 200;
|
|
14770
15050
|
var CHIP_SPLASH = 0.5;
|
|
15051
|
+
var BAR_BOW = 0.08;
|
|
14771
15052
|
var CUT_SLICE = 1;
|
|
14772
15053
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
14773
15054
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -14777,8 +15058,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14777
15058
|
return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
|
|
14778
15059
|
};
|
|
14779
15060
|
var capacityScale = (p) => {
|
|
14780
|
-
|
|
14781
|
-
|
|
15061
|
+
const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
|
|
15062
|
+
if (p.st.stripped) return residual;
|
|
15063
|
+
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, 1 - sectionLoss(p));
|
|
15064
|
+
return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
|
|
15065
|
+
};
|
|
15066
|
+
var sectionLoss = (p) => {
|
|
15067
|
+
const cells = p.st.chipped;
|
|
15068
|
+
if (!cells?.length) return 0;
|
|
15069
|
+
const [u, v] = planeAxes(p.thin);
|
|
15070
|
+
const along = p.dims[u] >= p.dims[v] ? u : v;
|
|
15071
|
+
const across2 = along === u ? v : u;
|
|
15072
|
+
let worst = 0;
|
|
15073
|
+
for (const c of cells) {
|
|
15074
|
+
let gone = 0;
|
|
15075
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) gone += d.size[across2];
|
|
15076
|
+
worst = Math.max(worst, gone / Math.max(p.dims[across2], 1e-6));
|
|
15077
|
+
}
|
|
15078
|
+
return Math.min(1, worst);
|
|
14782
15079
|
};
|
|
14783
15080
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
14784
15081
|
var clone = (v) => structuredClone(v);
|
|
@@ -15959,6 +16256,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15959
16256
|
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
15960
16257
|
setStage(o, p, 1, {}, src);
|
|
15961
16258
|
};
|
|
16259
|
+
const crater = (o, p, wp, normal, radius, src) => {
|
|
16260
|
+
markDamaged(p);
|
|
16261
|
+
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: -1, point: wp, area: Math.PI * radius * radius });
|
|
16262
|
+
debris(o, p, p.mat.breakDebris, wp, normal, 6, 1.5, src);
|
|
16263
|
+
setStage(o, p, 1, {}, src);
|
|
16264
|
+
};
|
|
15962
16265
|
const insideOpening = (o, p, objectPoint) => {
|
|
15963
16266
|
const op = p.st.opening;
|
|
15964
16267
|
if (!op) return false;
|
|
@@ -16035,9 +16338,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16035
16338
|
const lp = toLocal(p.pos, p.q, objectPoint);
|
|
16036
16339
|
return cells.some((c) => Math.abs(lp[u] - c.center[u]) <= c.size[u] / 2 && Math.abs(lp[v] - c.center[v]) <= c.size[v] / 2);
|
|
16037
16340
|
};
|
|
16038
|
-
const chipUnits = (o, p, wp,
|
|
16341
|
+
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
16039
16342
|
var _a, _b;
|
|
16040
|
-
if (!p.spec.units ||
|
|
16343
|
+
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
16344
|
+
const rule = p.mat.hits?.[kind];
|
|
16345
|
+
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
16346
|
+
const energy = hitEnergy - (rule?.floor ?? 0);
|
|
16347
|
+
if (perKg <= 0 || energy <= 0) return;
|
|
16041
16348
|
const [u, v] = planeAxes(p.thin);
|
|
16042
16349
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
16043
16350
|
const grid = gridOf(p);
|
|
@@ -16051,7 +16358,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16051
16358
|
const share = gap === 0 ? 1 : CHIP_SPLASH * (1 - gap / radius);
|
|
16052
16359
|
const total = (worn[i] ?? 0) + energy * share;
|
|
16053
16360
|
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
16054
|
-
if (total <
|
|
16361
|
+
if (total < perKg * mass) {
|
|
16055
16362
|
worn[i] = total;
|
|
16056
16363
|
continue;
|
|
16057
16364
|
}
|
|
@@ -16076,7 +16383,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16076
16383
|
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
16077
16384
|
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
16078
16385
|
}
|
|
16386
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
16079
16387
|
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
16388
|
+
if (p.structural && !isLost(p)) {
|
|
16389
|
+
solveStructure(o);
|
|
16390
|
+
collapseCheck(o, src);
|
|
16391
|
+
}
|
|
16392
|
+
};
|
|
16393
|
+
const exposeRebar = (o, p, src) => {
|
|
16394
|
+
const r = p.spec.reinforcement;
|
|
16395
|
+
if (!r) return;
|
|
16396
|
+
const [u, v] = planeAxes(p.thin);
|
|
16397
|
+
const all = rebarBars(p.dims, p.thin, r.spacing);
|
|
16398
|
+
let bars;
|
|
16399
|
+
if (p.st.stripped) bars = all;
|
|
16400
|
+
else {
|
|
16401
|
+
bars = [];
|
|
16402
|
+
for (const cell of p.st.chipped ?? []) bars.push(...clipBarsToBox(all, u, v, cell.center, cell.size));
|
|
16403
|
+
const op = p.st.opening;
|
|
16404
|
+
if (op) {
|
|
16405
|
+
const lc = toLocal(p.pos, p.q, op.center);
|
|
16406
|
+
bars.push(...clipBarsToDisc(all, u, v, [lc[u], lc[v]], op.radius + 0.05));
|
|
16407
|
+
}
|
|
16408
|
+
}
|
|
16409
|
+
const bow = clamp2(((p.st.stress ?? 0) - 1) / 0.5, 0, 1);
|
|
16410
|
+
if (bow > 0) bars = bars.flatMap((bar2, i) => bendBar(bar2, p.thin, bow * BAR_BOW, i));
|
|
16411
|
+
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
16080
16412
|
};
|
|
16081
16413
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
16082
16414
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -16104,10 +16436,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16104
16436
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
16105
16437
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
16106
16438
|
const reinforcement = p.spec.reinforcement;
|
|
16107
|
-
if (reinforcement)
|
|
16108
|
-
const bars = worldBars(o, p, clipBarsToDisc(rebarBars(p.dims, p.thin, reinforcement.spacing), u, v, [lc[u], lc[v]], radius + 0.05));
|
|
16109
|
-
if (bars.length > 0) emit({ ...base(o, src), type: "expose", part: p.spec.id, bars });
|
|
16110
|
-
}
|
|
16439
|
+
if (reinforcement) exposeRebar(o, p, src);
|
|
16111
16440
|
if (p.structural) solveStructure(o);
|
|
16112
16441
|
};
|
|
16113
16442
|
const stripPart = (o, p, reinforcement, src) => {
|
|
@@ -16119,8 +16448,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16119
16448
|
p.st.burning = false;
|
|
16120
16449
|
p.rt.jet = false;
|
|
16121
16450
|
markDamaged(p);
|
|
16122
|
-
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
16123
16451
|
solveStructure(o);
|
|
16452
|
+
exposeRebar(o, p, src);
|
|
16124
16453
|
collapseCheck(o, src);
|
|
16125
16454
|
};
|
|
16126
16455
|
const unitFragmentPlan = (p, cap) => {
|
|
@@ -16174,7 +16503,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16174
16503
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
16175
16504
|
const hitLocal = toLocal(c, q, hitW);
|
|
16176
16505
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
16177
|
-
const axis = style === "splinter" ? severAxis(p) : -1;
|
|
16506
|
+
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
16178
16507
|
const cutAt2 = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
16179
16508
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
16180
16509
|
style,
|
|
@@ -16246,7 +16575,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16246
16575
|
};
|
|
16247
16576
|
const CHAIN_TRIGGER_OF = { ballistic: "shot", explosive: "explosion" };
|
|
16248
16577
|
const severAxis = (p) => {
|
|
16249
|
-
if (p.mat.fracture !== "splinter" || p.spec.shape.kind !== "box") return -1;
|
|
16578
|
+
if (p.mat.fracture !== "splinter" && p.mat.fracture !== "snap" || p.spec.shape.kind !== "box") return -1;
|
|
16250
16579
|
const d = p.dims;
|
|
16251
16580
|
const order = [0, 1, 2].sort((a, b) => d[b] - d[a]);
|
|
16252
16581
|
return d[order[0]] >= SEVER_ASPECT * d[order[1]] && d[order[0]] >= 3 * SEVER_BAND ? order[0] : -1;
|
|
@@ -16307,6 +16636,49 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16307
16636
|
}
|
|
16308
16637
|
}
|
|
16309
16638
|
};
|
|
16639
|
+
const tearOf = (mat, kind) => {
|
|
16640
|
+
const h = mat.hits?.[kind];
|
|
16641
|
+
const tear = h?.tear ?? (kind === "ballistic" && mat.penetrationClass === "wood" ? "splinters" : "none");
|
|
16642
|
+
return { tear, joules: h?.tearJoules ?? TEAR_JOULES };
|
|
16643
|
+
};
|
|
16644
|
+
const tearAt = (o, p, kind, entry2, exit, d, E, scale, src) => {
|
|
16645
|
+
const { tear, joules } = tearOf(p.mat, kind);
|
|
16646
|
+
if (tear === "none") return;
|
|
16647
|
+
const shot = kind === "ballistic";
|
|
16648
|
+
const n = shot ? exit ? clamp2(Math.round(E / joules), 1, 7) : 1 : clamp2(Math.floor(E / joules), 0, 5);
|
|
16649
|
+
if (n === 0) return;
|
|
16650
|
+
const fibre = tear === "lumps";
|
|
16651
|
+
const { q } = partXf(o, p);
|
|
16652
|
+
const along = (p.spec.grain ? AXIS[p.spec.grain] : void 0) ?? [0, 1, 2].sort((a, b) => p.dims[b] - p.dims[a])[0];
|
|
16653
|
+
const fragments = [];
|
|
16654
|
+
for (let i = 0; i < n; i++) {
|
|
16655
|
+
const back = !exit || i % 3 === 2;
|
|
16656
|
+
const len = scale * (fibre ? 1.2 + 1.2 * rnd() : 3 + 4 * rnd()), w = scale * (0.8 + 0.8 * rnd()), t = scale * (fibre ? 0.7 + 0.6 * rnd() : 0.4 + 0.4 * rnd());
|
|
16657
|
+
const size = [w, w, w];
|
|
16658
|
+
size[along] = len;
|
|
16659
|
+
size[(along + 1) % 3] = w;
|
|
16660
|
+
size[(along + 2) % 3] = t;
|
|
16661
|
+
const spread3 = [(rnd() - 0.5) * 0.8, (rnd() - 0.2) * 0.8, (rnd() - 0.5) * 0.8];
|
|
16662
|
+
const speed = shot ? back ? 1.5 + 2 * rnd() : 2 + 4 * rnd() : 0.8 + 1.2 * rnd();
|
|
16663
|
+
const v = vec.scale(vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3)), speed);
|
|
16664
|
+
const mass = size[0] * size[1] * size[2] * p.mat.density;
|
|
16665
|
+
fragments.push({
|
|
16666
|
+
id: `${p.spec.id}#t${tears++}-${i}`,
|
|
16667
|
+
part: p.spec.id,
|
|
16668
|
+
material: p.spec.material,
|
|
16669
|
+
shape: fibre ? "chunk" : "splinter",
|
|
16670
|
+
center: back || !exit ? vec.madd(entry2, d, -0.01) : vec.madd(exit, d, 0.01),
|
|
16671
|
+
size,
|
|
16672
|
+
rotation: q,
|
|
16673
|
+
velocity: v,
|
|
16674
|
+
inner: true,
|
|
16675
|
+
angularVelocity: [(rnd() - 0.5) * 30, (rnd() - 0.5) * 30, (rnd() - 0.5) * 30],
|
|
16676
|
+
mass
|
|
16677
|
+
});
|
|
16678
|
+
}
|
|
16679
|
+
const patch = [scale * 4, scale * 4, scale * 4];
|
|
16680
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments });
|
|
16681
|
+
};
|
|
16310
16682
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round3 = {}) => {
|
|
16311
16683
|
const mat = p.mat;
|
|
16312
16684
|
const piercing = round3.piercing ?? 1;
|
|
@@ -16335,6 +16707,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16335
16707
|
return { penetrated: false, exitEnergy: 0, damage: dmg2 };
|
|
16336
16708
|
}
|
|
16337
16709
|
if (p.layers.length > 0) spallOuter(o, p, entry2, face, n, Math.max(0.03, caliber / 1e3 * 4), src);
|
|
16710
|
+
else if (mat.hits?.ballistic?.crater) crater(o, p, entry2, n, clamp2(0.02 + Math.sqrt(E) * 15e-4, 0.02, 0.15), src);
|
|
16338
16711
|
const brittle = mat.ductility < 0.3;
|
|
16339
16712
|
const ratio = Ep / Math.max(thr, 1e-6);
|
|
16340
16713
|
const crackLen = clamp2(0.03 + ratio * 0.2, 0.03, 0.8);
|
|
@@ -16368,8 +16741,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16368
16741
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16369
16742
|
debris(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
16370
16743
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
16371
|
-
const
|
|
16372
|
-
|
|
16744
|
+
const absorbed = penetrated && mat.ductility >= 0.5 && mat.fracture !== "tear" ? Math.min(E, thr) : E;
|
|
16745
|
+
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
16746
|
+
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
16747
|
+
chipUnits(o, p, "ballistic", entry2, penetrated ? Math.min(E, Math.max(thr, E * 0.5)) : E, d, Math.max(0.05, caliber / 1e3 * 8), src);
|
|
16373
16748
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
16374
16749
|
};
|
|
16375
16750
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -16377,14 +16752,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16377
16752
|
const thrMm = mat.penetration * p.thickness * 1e3;
|
|
16378
16753
|
const fragile = kind === "blunt" ? fragility(mat.ductility, p.thickness) : 1;
|
|
16379
16754
|
const Ec = E * fragile;
|
|
16755
|
+
const sheet = mat.ductility >= 0.5 && p.thickness <= THIN_SHEET_CRUSH;
|
|
16756
|
+
const fold = sheet ? Math.min(4, Math.sqrt(1e-3 / p.thickness)) : 1;
|
|
16757
|
+
const patch = sheet ? Math.min(contactR, THIN_SHEET_CONTACT) : contactR;
|
|
16380
16758
|
if (E > 0) {
|
|
16381
16759
|
if (kind === "blunt") {
|
|
16382
|
-
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(
|
|
16760
|
+
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(patch, 0.015), clamp2(E * 1e-4 * fold, 1e-3, sheet ? Math.min(0.06, Math.min(...p.dims) * 0.45) : 0.06), src);
|
|
16383
16761
|
else if (mat.ductility < 0.3) {
|
|
16384
16762
|
if (Ec >= 5) addCrack(o, p, point, n, clamp2(0.04 + Math.min(Ec, E * 6) * 8e-4, 0.04, 0.6), src);
|
|
16385
16763
|
} else if (E >= thrMm * 0.2) addDent(o, p, point, n, Math.max(contactR, 0.015), clamp2(E * 6e-5, 1e-3, 0.04), src);
|
|
16386
16764
|
} else if (mat.fracture === "tear") {
|
|
16387
16765
|
addHole(o, p, point, n, clamp2(0.02 + E * 5e-4, 0.02, 0.3), true, void 0, src);
|
|
16766
|
+
} else if (mat.ductility >= 0.3 && E >= thrMm) {
|
|
16767
|
+
addHole(o, p, point, n, clamp2(contactR, 0.01, 0.08), true, void 0, src);
|
|
16768
|
+
if (sheet) addDent(o, p, point, n, Math.max(patch * 0.6, 0.01), clamp2(E * 4e-5 * fold, 5e-4, 0.02), src);
|
|
16388
16769
|
} else if (mat.ductility >= 0.5) {
|
|
16389
16770
|
addDent(o, p, point, n, Math.max(contactR, 0.01), clamp2(E * 4e-5, 5e-4, 0.02), src);
|
|
16390
16771
|
}
|
|
@@ -16397,7 +16778,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16397
16778
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
16398
16779
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
16399
16780
|
addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src);
|
|
16400
|
-
if (kind === "blunt") chipUnits(o, p, point, E, d, Math.max(contactR, 0.03), src);
|
|
16781
|
+
if (kind === "blunt") chipUnits(o, p, "blunt", point, E, d, Math.max(contactR, 0.03), src);
|
|
16782
|
+
if (E > 0 && !isLost(p)) tearAt(o, p, kind, point, void 0, d, E, clamp2(contactR * 0.2, 6e-3, 0.02), src);
|
|
16401
16783
|
return done;
|
|
16402
16784
|
};
|
|
16403
16785
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -16976,6 +17358,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16976
17358
|
if (p.rt.stressT <= 0) {
|
|
16977
17359
|
p.rt.stressT = 0.5;
|
|
16978
17360
|
emit({ ...base(o), type: "stress", part: p.spec.id, stress, load: p.st.load ?? 0 });
|
|
17361
|
+
if (p.spec.reinforcement && (p.st.stripped || p.st.chipped?.length || p.st.opening)) exposeRebar(o, p);
|
|
16979
17362
|
}
|
|
16980
17363
|
applyDamage(o, p, overloadRate(stress) * p.st.maxHp * dt, "blunt", null, null, 5, 3);
|
|
16981
17364
|
} else if (stress > 0.8 && p.mat.ductility < 0.3 && p.rt.stressT <= 0 && p.st.cracks.length < 6) {
|
|
@@ -17192,6 +17575,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17192
17575
|
const r = raycastInternal(origin, direction, maxDistance);
|
|
17193
17576
|
return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null;
|
|
17194
17577
|
},
|
|
17578
|
+
sweepBox(box4, direction, distance2, options2 = {}) {
|
|
17579
|
+
const d = vec.normalize(direction);
|
|
17580
|
+
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
17581
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : void 0;
|
|
17582
|
+
let best = null;
|
|
17583
|
+
for (const origin of sweepOrigins(box4, d, options2.spacing ?? 0.2)) {
|
|
17584
|
+
const r = raycastInternal(origin, d, distance2 + SWEEP_BACK, ignore);
|
|
17585
|
+
if (!r || best && r.distance >= best.distance) continue;
|
|
17586
|
+
best = { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: Math.max(0, r.distance - SWEEP_BACK) };
|
|
17587
|
+
}
|
|
17588
|
+
return best;
|
|
17589
|
+
},
|
|
17195
17590
|
shoot(origin, direction, energy, caliber, source, round3) {
|
|
17196
17591
|
const extras = typeof round3 === "number" ? { piercing: round3 } : round3 ?? {};
|
|
17197
17592
|
return withCollapses(run(() => {
|
|
@@ -18763,7 +19158,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
18763
19158
|
}
|
|
18764
19159
|
return spec;
|
|
18765
19160
|
};
|
|
18766
|
-
var
|
|
19161
|
+
var MEMBER_CAGE = { spacing: 0.1, diameter: 8e-3, unit: 0.1 };
|
|
19162
|
+
var CAGED_ROLES = /* @__PURE__ */ new Set(["beam", "column"]);
|
|
19163
|
+
var caged = (spec, part, rebar2) => {
|
|
19164
|
+
if (!rebar2 || part.material !== "concrete" || !CAGED_ROLES.has(part.role) || spec.reinforcement) return spec;
|
|
19165
|
+
const size = spec.shape.kind === "box" ? spec.shape.size : null;
|
|
19166
|
+
if (!size) return spec;
|
|
19167
|
+
const thin = Math.min(...size);
|
|
19168
|
+
return {
|
|
19169
|
+
...spec,
|
|
19170
|
+
reinforcement: { material: rebar2, spacing: MEMBER_CAGE.spacing, diameter: MEMBER_CAGE.diameter },
|
|
19171
|
+
units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, thin], bond: "stack", mortar: "concrete" }
|
|
19172
|
+
};
|
|
19173
|
+
};
|
|
19174
|
+
var partSpecOf = (part, capacity, detailed, rebar2) => {
|
|
19175
|
+
if (detailed && part.assembly) return assemblySpec(part, capacity);
|
|
19176
|
+
const spec = boxSpec(part, capacity);
|
|
19177
|
+
return detailed ? caged(spec, part, rebar2) : spec;
|
|
19178
|
+
};
|
|
18767
19179
|
function toDestructibleSpec(structure, options = {}) {
|
|
18768
19180
|
const { safety = 0.65, redundancy = 0.97, safetyOthers = 0.45 } = options;
|
|
18769
19181
|
const kept = structure.parts.filter((part) => !options.filter || options.filter(part));
|
|
@@ -18780,7 +19192,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
18780
19192
|
const sizing = { safety, redundancy, safetyOthers };
|
|
18781
19193
|
const sized = { ...panels, capacities: options.sized === false ? /* @__PURE__ */ new Map() : sizeCapacities(panels.parts, panels.supports, sizing) };
|
|
18782
19194
|
const { parts, supports, capacities } = detail === "units" ? applyUnits(sized, structure) : sized;
|
|
18783
|
-
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse"));
|
|
19195
|
+
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse", structure.construction.frame.rebar));
|
|
18784
19196
|
const margin = options.terrainMargin ?? TERRAIN_MARGIN;
|
|
18785
19197
|
const x0 = Math.min(...parts.map((part) => part.aabb[0]));
|
|
18786
19198
|
const z0 = Math.min(...parts.map((part) => part.aabb[1]));
|
|
@@ -26745,7 +27157,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26745
27157
|
if (!pc || pc.unit) return sp;
|
|
26746
27158
|
if (pc.role !== "wall" && pc.role !== "partition") {
|
|
26747
27159
|
const def = getAssembly(pc.assembly || (pc.material === "concrete" && ["slab", "beam", "column", "stair"].includes(pc.role) ? "reinforced_concrete" : ""));
|
|
26748
|
-
|
|
27160
|
+
const out = def && def.reinforcement && !sp.reinforcement ? { ...sp, reinforcement: { ...def.reinforcement } } : sp;
|
|
27161
|
+
if (out.reinforcement && !out.units && pc.material === "concrete" && (pc.role === "beam" || pc.role === "column") && sp.shape.kind === "box") {
|
|
27162
|
+
return { ...out, units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, Math.min(...sp.shape.size)], bond: "stack", mortar: "concrete" } };
|
|
27163
|
+
}
|
|
27164
|
+
return out;
|
|
26749
27165
|
}
|
|
26750
27166
|
if (!pc.assembly) return sp;
|
|
26751
27167
|
const [u0, v0, u1, v1] = pc.aabb, alongU = u1 - u0 >= v1 - v0;
|
|
@@ -26839,29 +27255,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26839
27255
|
}
|
|
26840
27256
|
return list2;
|
|
26841
27257
|
}
|
|
27258
|
+
const specOf = (id) => opts.spec()?.parts?.find((q) => q.id === id);
|
|
27259
|
+
const same = (a, b) => a.every((x, k) => Math.abs(x - b[k]) < 1e-3);
|
|
27260
|
+
function hostOf(partId) {
|
|
27261
|
+
if (structure.meshesOf(partId).some((it) => it.mesh)) return partId;
|
|
27262
|
+
const part = structure.byId.get(partId);
|
|
27263
|
+
if (!part) return null;
|
|
27264
|
+
for (const q of structure.parts) {
|
|
27265
|
+
if (q.id !== partId && same(q.aabb, part.aabb) && Math.abs(q.y1 - part.y0) < 0.01 && structure.meshesOf(q.id).some((it) => it.mesh)) return q.id;
|
|
27266
|
+
}
|
|
27267
|
+
return null;
|
|
27268
|
+
}
|
|
27269
|
+
const riders = (hostId) => structure.parts.filter((q) => q.id !== hostId && specOf(q.id)?.units && hostOf(q.id) === hostId && !structure.meshesOf(q.id).some((it) => it.mesh)).map((q) => q.id);
|
|
26842
27270
|
function draw2(partId, change) {
|
|
26843
27271
|
if (lod !== 0) return false;
|
|
26844
|
-
const
|
|
26845
|
-
|
|
26846
|
-
|
|
26847
|
-
let view = views.get(partId);
|
|
27272
|
+
const hostId = hostOf(partId);
|
|
27273
|
+
if (!hostId || !specOf(partId)?.units) return false;
|
|
27274
|
+
let view = views.get(hostId);
|
|
26848
27275
|
if (!view) {
|
|
26849
|
-
const items = structure.meshesOf(
|
|
27276
|
+
const items = structure.meshesOf(hostId).filter((it) => it.mesh);
|
|
26850
27277
|
if (!items.length) return false;
|
|
26851
|
-
|
|
27278
|
+
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
27279
|
+
view = { mesh: null, saved: null, items, paint: wallColour(items), units: 0, opening: null, chips: new Map(parts.map((id) => [id, []])), openings: /* @__PURE__ */ new Map() };
|
|
26852
27280
|
}
|
|
27281
|
+
if (!view.chips.has(partId)) return false;
|
|
26853
27282
|
if (change.opening) {
|
|
26854
|
-
if (view.
|
|
26855
|
-
view.
|
|
27283
|
+
if (view.openings.has(partId)) return false;
|
|
27284
|
+
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
27285
|
+
if (partId === hostId) view.opening = view.openings.get(partId);
|
|
26856
27286
|
}
|
|
26857
|
-
if (change.chip) view.chips.push(local(change.chip));
|
|
26858
|
-
const list2 =
|
|
27287
|
+
if (change.chip) view.chips.get(partId).push(local(change.chip));
|
|
27288
|
+
const list2 = [];
|
|
27289
|
+
for (const [id, chips] of view.chips) list2.push(...courses(structure.byId.get(id), specOf(id).units, view.openings.get(id) ?? null, chips, view.paint));
|
|
26859
27290
|
if (view.mesh) {
|
|
26860
27291
|
view.mesh.removeFromParent();
|
|
26861
27292
|
view.mesh.dispose();
|
|
26862
27293
|
}
|
|
26863
27294
|
const mesh2 = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
26864
|
-
mesh2.name = "breach:" +
|
|
27295
|
+
mesh2.name = "breach:" + hostId;
|
|
26865
27296
|
list2.forEach((u, i) => {
|
|
26866
27297
|
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
26867
27298
|
mesh2.setMatrixAt(i, tmp.m);
|
|
@@ -26876,7 +27307,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26876
27307
|
if (!view.saved) view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
26877
27308
|
view.mesh = mesh2;
|
|
26878
27309
|
view.units = list2.length;
|
|
26879
|
-
views.set(
|
|
27310
|
+
views.set(hostId, view);
|
|
26880
27311
|
invalidate();
|
|
26881
27312
|
invalidateShadow();
|
|
26882
27313
|
return true;
|
|
@@ -27929,6 +28360,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27929
28360
|
return buildApi(K, shared);
|
|
27930
28361
|
}
|
|
27931
28362
|
var WOOD_THIN = 0.035;
|
|
28363
|
+
var BOARD_TOP_THICK = 0.07;
|
|
28364
|
+
var BOARD_TOP_WIDTH = 0.5;
|
|
28365
|
+
var BOARD_WIDTH = 0.16;
|
|
27932
28366
|
var SHELF_SPAN = 1.5;
|
|
27933
28367
|
var SHELVING = /shelf|bookcase|rack/;
|
|
27934
28368
|
function carcass(parts, drawn) {
|
|
@@ -27943,6 +28377,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27943
28377
|
} else if (y <= x && y <= z) part.structure = { role: "beam", maxSpan: SHELF_SPAN * Math.max(x, z) };
|
|
27944
28378
|
}
|
|
27945
28379
|
}
|
|
28380
|
+
function splitTopsIntoBoards(drawn, materialOf, carcassed) {
|
|
28381
|
+
const tops = /* @__PURE__ */ new Map();
|
|
28382
|
+
if (carcassed) return tops;
|
|
28383
|
+
for (let i = drawn.length - 1; i >= 0; i--) {
|
|
28384
|
+
const p = drawn[i];
|
|
28385
|
+
if (p.key !== "wood" || materialOf.wood !== "wood_hard" || p.shape.kind !== "box" || p.rotation) continue;
|
|
28386
|
+
const [sx, sy, sz] = p.shape.size;
|
|
28387
|
+
const across2 = sx < sz ? 0 : 2;
|
|
28388
|
+
const width = Math.min(sx, sz);
|
|
28389
|
+
if (sy < WOOD_THIN || sy > BOARD_TOP_THICK || sy >= width || width < BOARD_TOP_WIDTH) continue;
|
|
28390
|
+
const count = Math.round(width / BOARD_WIDTH);
|
|
28391
|
+
const boards = [];
|
|
28392
|
+
for (let b = 0; b < count; b++) {
|
|
28393
|
+
const size = [sx, sy, sz];
|
|
28394
|
+
size[across2] = width / count;
|
|
28395
|
+
const position = [...p.position];
|
|
28396
|
+
position[across2] += (b + 0.5) * size[across2] - width / 2;
|
|
28397
|
+
const min = [...p.min];
|
|
28398
|
+
const max = [...p.max];
|
|
28399
|
+
min[across2] = position[across2] - size[across2] / 2;
|
|
28400
|
+
max[across2] = position[across2] + size[across2] / 2;
|
|
28401
|
+
boards.push({ ...p, id: `${p.id}_b${b}`, shape: { kind: "box", size }, position, min, max, color: shadeBoard(p.color, b) });
|
|
28402
|
+
}
|
|
28403
|
+
drawn.splice(i, 1, ...boards);
|
|
28404
|
+
tops.set(p.id, boards);
|
|
28405
|
+
}
|
|
28406
|
+
return tops;
|
|
28407
|
+
}
|
|
28408
|
+
function shadeBoard(color2, board) {
|
|
28409
|
+
const k = [1, 0.94, 1.05, 0.97, 1.03][board % 5];
|
|
28410
|
+
const channel = (shift) => Math.min(255, Math.round((color2 >> shift & 255) * k)) << shift;
|
|
28411
|
+
return channel(16) | channel(8) | channel(0);
|
|
28412
|
+
}
|
|
27946
28413
|
var FLOOR_TOUCH = 5e-3;
|
|
27947
28414
|
var LOOSE = /* @__PURE__ */ new Set(["paper"]);
|
|
27948
28415
|
var BASE_MATERIAL = {
|
|
@@ -28020,10 +28487,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
28020
28487
|
};
|
|
28021
28488
|
run(A);
|
|
28022
28489
|
if (!drawn.length) throw new Error(`game-floorplan-three: the builder of "${name}" drew no box or cylinder`);
|
|
28490
|
+
const boardsOf = splitTopsIntoBoards(drawn, materialOf, Boolean(opts.carcass));
|
|
28023
28491
|
const parts = drawn.map((p) => {
|
|
28024
28492
|
const thin = Math.min(...p.shape.kind === "box" ? p.shape.size : p.shape.kind === "cylinder" ? [p.shape.radius * 2, p.shape.height] : [p.shape.radius * 2]);
|
|
28025
28493
|
let material = materialOf[p.key] ?? "plastic";
|
|
28026
|
-
if (p.key === "wood" && material === "wood_hard" &&
|
|
28494
|
+
if (p.key === "wood" && material === "wood_hard" && opts.carcass && !opts.materials?.wood) material = "mdf";
|
|
28495
|
+
else if (p.key === "wood" && material === "wood_hard" && thin < WOOD_THIN) material = "wood_soft";
|
|
28027
28496
|
const part = { id: p.id, material, shape: p.shape, position: p.position };
|
|
28028
28497
|
if (p.rotation) part.rotation = p.rotation;
|
|
28029
28498
|
if (!LOOSE.has(material) && p.min[1] <= FLOOR_TOUCH) part.anchored = true;
|
|
@@ -28058,6 +28527,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
28058
28527
|
if (area3 < 0.6 * footprint || p.max[1] < 0.2) continue;
|
|
28059
28528
|
if (!top || p.max[1] > top.y + 1e-6) top = { part: p.id, y: p.max[1] };
|
|
28060
28529
|
}
|
|
28530
|
+
for (const boards of boardsOf.values()) {
|
|
28531
|
+
if (top && boards[0].max[1] <= top.y - 1e-6) continue;
|
|
28532
|
+
const mid = boards[boards.length >> 1];
|
|
28533
|
+
const min = [Math.min(...boards.map((b) => b.min[0])), Math.min(...boards.map((b) => b.min[2]))];
|
|
28534
|
+
const max = [Math.max(...boards.map((b) => b.max[0])), Math.max(...boards.map((b) => b.max[2]))];
|
|
28535
|
+
if ((max[0] - min[0]) * (max[1] - min[1]) >= 0.6 * footprint && mid.max[1] >= 0.2) top = { part: mid.id, y: mid.max[1], parts: boards.map((b) => b.id) };
|
|
28536
|
+
}
|
|
28061
28537
|
if (opts.carcass) carcass(parts, byId);
|
|
28062
28538
|
const spec = { name, parts, joints, tags: ["furniture"] };
|
|
28063
28539
|
const colors = new Map(drawn.map((p) => [p.id, p.color]));
|
|
@@ -29047,6 +29523,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
29047
29523
|
const module = {
|
|
29048
29524
|
id: "items_area",
|
|
29049
29525
|
name: text.name,
|
|
29526
|
+
ground: { surface: "tile_floor" },
|
|
29050
29527
|
group: text.group,
|
|
29051
29528
|
state,
|
|
29052
29529
|
placed: () => entries2.map((entry2) => ({ id: entry2.id, itemId: entry2.itemId, group: entry2.group, kind: entry2.kind, size: entry2.size, root: entry2.root })),
|