@adatechnology/game-items-three 0.7.2 → 0.7.3
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.global.js +616 -72
- package/dist/index.global.js.map +1 -1
- package/package.json +8 -8
package/dist/index.global.js
CHANGED
|
@@ -2731,6 +2731,19 @@ var GameItemsThree = (() => {
|
|
|
2731
2731
|
"drink",
|
|
2732
2732
|
"sticky"
|
|
2733
2733
|
]
|
|
2734
|
+
},
|
|
2735
|
+
{
|
|
2736
|
+
id: "mud",
|
|
2737
|
+
color: 4008988,
|
|
2738
|
+
opacity: 0.95,
|
|
2739
|
+
stainColor: 3811866,
|
|
2740
|
+
viscosity: 40,
|
|
2741
|
+
density: 1600,
|
|
2742
|
+
dryTime: 900,
|
|
2743
|
+
gloss: 0.4,
|
|
2744
|
+
tags: [
|
|
2745
|
+
"dirty"
|
|
2746
|
+
]
|
|
2734
2747
|
}
|
|
2735
2748
|
];
|
|
2736
2749
|
var LIQUIDS = liquids_default;
|
|
@@ -13254,6 +13267,22 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13254
13267
|
return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
|
|
13255
13268
|
};
|
|
13256
13269
|
var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
13270
|
+
var splitAlong = (box4, count, axis, rnd) => {
|
|
13271
|
+
const shares = Array.from({ length: count }, () => 0.25 + 1.5 * rnd());
|
|
13272
|
+
const total = shares.reduce((sum, share) => sum + share, 0);
|
|
13273
|
+
const out = [];
|
|
13274
|
+
let from = box4.c[axis] - box4.s[axis] / 2;
|
|
13275
|
+
for (const share of shares) {
|
|
13276
|
+
const length = box4.s[axis] * share / total;
|
|
13277
|
+
const c = [box4.c[0], box4.c[1], box4.c[2]];
|
|
13278
|
+
const size = [box4.s[0], box4.s[1], box4.s[2]];
|
|
13279
|
+
c[axis] = from + length / 2;
|
|
13280
|
+
size[axis] = length;
|
|
13281
|
+
out.push({ c, s: size });
|
|
13282
|
+
from += length;
|
|
13283
|
+
}
|
|
13284
|
+
return out;
|
|
13285
|
+
};
|
|
13257
13286
|
var splinterBox = (inp, box4, limit = inp.maxFragments) => {
|
|
13258
13287
|
const { rnd } = inp;
|
|
13259
13288
|
const dims = box4.s;
|
|
@@ -13263,35 +13292,81 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13263
13292
|
const w = Math.max(fmin * 1.5, 0.01);
|
|
13264
13293
|
const c = clamp(Math.round(dims[a] * dims[b] / (w * w)), 3, Math.max(3, Math.floor(limit / 2)));
|
|
13265
13294
|
const maxLen = fmax * 4;
|
|
13266
|
-
const cells =
|
|
13295
|
+
const cells = [];
|
|
13296
|
+
for (const cell of bsp(box4, c, [a, b], rnd)) {
|
|
13297
|
+
const wide = cell.s[a] >= cell.s[b] ? a : b;
|
|
13298
|
+
if (rnd() < 0.4 && cell.s[wide] > fmin * 0.5) for (const part of splitAlong(cell, 2, wide, rnd)) cells.push(part);
|
|
13299
|
+
else cells.push(cell);
|
|
13300
|
+
}
|
|
13267
13301
|
const pieces = [];
|
|
13268
13302
|
for (const cell of cells) {
|
|
13269
|
-
const extra = rnd()
|
|
13303
|
+
const extra = Math.floor(rnd() * 3);
|
|
13270
13304
|
const k = Math.max(1, Math.ceil(dims[g] / maxLen)) + extra;
|
|
13271
|
-
for (const p of
|
|
13305
|
+
for (const p of splitAlong(cell, k, g, rnd)) pieces.push(p);
|
|
13272
13306
|
}
|
|
13273
13307
|
return pieces.slice(0, limit).map((p) => ({ center: p.c, size: [p.s[g], p.s[a], p.s[b]], q: frame(g, a, b), shape: "splinter", weight: p.s[0] * p.s[1] * p.s[2] }));
|
|
13274
13308
|
};
|
|
13309
|
+
var PLY = 3e-3;
|
|
13310
|
+
var PLY_GAP = 0.6;
|
|
13311
|
+
var PLY_SHEET = 0.012;
|
|
13312
|
+
var plyBreak = (inp, root, limit) => {
|
|
13313
|
+
const { rnd } = inp;
|
|
13314
|
+
const dims = root.s;
|
|
13315
|
+
const sorted = [0, 1, 2].sort((x, y) => dims[x] - dims[y]);
|
|
13316
|
+
const t = sorted[0];
|
|
13317
|
+
const [u, v] = [sorted[1], sorted[2]].sort((x, y) => x - y);
|
|
13318
|
+
const along = dims[u] >= dims[v] ? u : v;
|
|
13319
|
+
const across2 = along === u ? v : u;
|
|
13320
|
+
const layers = clamp(Math.round(dims[t] / PLY / 2), 2, 3);
|
|
13321
|
+
const pitch = dims[t] / layers;
|
|
13322
|
+
const th = Math.min(pitch, PLY_SHEET);
|
|
13323
|
+
const per = Math.max(2, Math.floor(limit / layers));
|
|
13324
|
+
const out = [];
|
|
13325
|
+
for (let k = 0; k < layers; k++) {
|
|
13326
|
+
const layer = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
13327
|
+
layer.c[t] = root.c[t] + (-dims[t] / 2 + (k + 0.5) * pitch) * (pitch > PLY_SHEET ? 1 : 1 + PLY_GAP);
|
|
13328
|
+
layer.s[t] = th;
|
|
13329
|
+
for (const cell of bsp(layer, per, [u, v], rnd)) {
|
|
13330
|
+
if (out.length >= limit) return out;
|
|
13331
|
+
if (rnd() < 0.35) {
|
|
13332
|
+
const strand = cell.s[across2] * (0.15 + 0.2 * rnd());
|
|
13333
|
+
out.push({ center: cell.c, size: [cell.s[along], strand, th * 0.7], q: frame(along, across2, t), shape: "splinter", weight: cell.s[along] * strand * th });
|
|
13334
|
+
} else {
|
|
13335
|
+
out.push({ center: cell.c, size: [cell.s[u], cell.s[v], th], q: frame(u, v, t, (rnd() - 0.5) * 0.5), shape: "scrap", weight: cell.s[u] * cell.s[v] * th });
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
}
|
|
13339
|
+
return out;
|
|
13340
|
+
};
|
|
13341
|
+
var granules = (inp, box4, count) => bsp(box4, count, [0, 1, 2], inp.rnd).map((b) => ({ center: b.c, size: b.s, q: quat.identity(), shape: "chunk", weight: b.s[0] * b.s[1] * b.s[2] }));
|
|
13342
|
+
var CRUMBS = 6;
|
|
13275
13343
|
var severAt = (inp, root, at2, gap) => {
|
|
13276
13344
|
const dims = root.s;
|
|
13277
13345
|
const L = [0, 1, 2].sort((x, y) => dims[y] - dims[x])[0];
|
|
13278
13346
|
const half = dims[L] / 2;
|
|
13279
13347
|
const lo = clamp(at2 - gap / 2, -half, half), hi = clamp(at2 + gap / 2, -half, half);
|
|
13280
13348
|
const out = [];
|
|
13281
|
-
const
|
|
13349
|
+
const fibrous = inp.form === "slivers" || inp.form === "plies";
|
|
13350
|
+
const [a, b] = [0, 1, 2].filter((i) => i !== L);
|
|
13351
|
+
const stub = (from, to, breakAtEnd) => {
|
|
13282
13352
|
if (to - from < 0.02) return;
|
|
13283
13353
|
const c = [root.c[0], root.c[1], root.c[2]], sz = [dims[0], dims[1], dims[2]];
|
|
13284
13354
|
c[L] = (from + to) / 2;
|
|
13285
13355
|
sz[L] = to - from;
|
|
13286
|
-
|
|
13356
|
+
const weight = sz[0] * sz[1] * sz[2];
|
|
13357
|
+
if (fibrous) out.push({ center: c, size: [sz[L], sz[a], sz[b]], q: frame(L, a, b, breakAtEnd ? 0 : Math.PI), shape: "slab", weight });
|
|
13358
|
+
else out.push({ center: c, size: sz, q: quat.identity(), shape: "slab", weight });
|
|
13287
13359
|
};
|
|
13288
|
-
stub(-half, lo);
|
|
13289
|
-
stub(hi, half);
|
|
13360
|
+
stub(-half, lo, true);
|
|
13361
|
+
stub(hi, half, false);
|
|
13290
13362
|
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
13291
13363
|
band.c[L] = (lo + hi) / 2;
|
|
13292
13364
|
band.s[L] = Math.max(hi - lo, 0.01);
|
|
13293
|
-
|
|
13294
|
-
|
|
13365
|
+
const room = inp.maxFragments - out.length;
|
|
13366
|
+
if (inp.style === "snap" && inp.form === "plies") for (const p of plyBreak(inp, band, Math.max(3, room))) out.push(p);
|
|
13367
|
+
else if (inp.style === "snap" && inp.form === "granules") for (const p of granules(inp, band, clamp(room, 3, 12))) out.push(p);
|
|
13368
|
+
else if (inp.style === "snap") {
|
|
13369
|
+
for (const b2 of bsp(band, Math.max(3, Math.min(6, room)), [0, 1, 2], inp.rnd)) out.push({ center: b2.c, size: b2.s, q: quat.identity(), shape: "chunk", weight: b2.s[0] * b2.s[1] * b2.s[2] });
|
|
13295
13370
|
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
13296
13371
|
return out;
|
|
13297
13372
|
};
|
|
@@ -13324,8 +13399,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13324
13399
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
13325
13400
|
if (style === "snap") {
|
|
13326
13401
|
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
13402
|
+
if (inp.form === "plies") return plyBreak(inp, root, maxFragments);
|
|
13327
13403
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
13328
13404
|
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] });
|
|
13405
|
+
if (inp.form !== "granules") return out;
|
|
13406
|
+
const crumbs = Math.min(CRUMBS, maxFragments - out.length);
|
|
13407
|
+
if (crumbs <= 0) return out;
|
|
13408
|
+
const band = { c: [0, 0, 0], s: [0, 0, 0] };
|
|
13409
|
+
for (let i = 0; i < 3; i++) {
|
|
13410
|
+
band.s[i] = Math.min(dims[i], Math.max(fmin, dims[i] * 0.2));
|
|
13411
|
+
band.c[i] = clamp(inp.hit[i], -dims[i] / 2 + band.s[i] / 2, dims[i] / 2 - band.s[i] / 2);
|
|
13412
|
+
}
|
|
13413
|
+
for (const p of granules(inp, band, crumbs)) out.push({ ...p, weight: p.weight * 0.5 });
|
|
13329
13414
|
return out;
|
|
13330
13415
|
}
|
|
13331
13416
|
if (style === "crumble" || style === "none") {
|
|
@@ -13432,6 +13517,88 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13432
13517
|
}
|
|
13433
13518
|
return marks;
|
|
13434
13519
|
};
|
|
13520
|
+
var JOINT_GAP = 1e-4;
|
|
13521
|
+
var MIN_BEARING_SHARE = 0.25;
|
|
13522
|
+
var unsupportedCells = (cells, thin, dims, standing) => {
|
|
13523
|
+
if (thin === 1) return [];
|
|
13524
|
+
const [u, v] = planeAxes(thin);
|
|
13525
|
+
const bottom = -dims[v] / 2;
|
|
13526
|
+
const rowOf = (y) => Math.round((y - bottom) * 1e4);
|
|
13527
|
+
const rows = /* @__PURE__ */ new Map();
|
|
13528
|
+
for (let i = 0; i < cells.length; i++) {
|
|
13529
|
+
if (!standing(i)) continue;
|
|
13530
|
+
const cell = cells[i];
|
|
13531
|
+
const key2 = rowOf(cell.center[v] - cell.size[v] / 2);
|
|
13532
|
+
const row = rows.get(key2);
|
|
13533
|
+
if (row) row.push(i);
|
|
13534
|
+
else rows.set(key2, [i]);
|
|
13535
|
+
}
|
|
13536
|
+
const held = /* @__PURE__ */ new Set();
|
|
13537
|
+
const queue = [];
|
|
13538
|
+
for (const i of rows.get(0) ?? []) {
|
|
13539
|
+
held.add(i);
|
|
13540
|
+
queue.push(i);
|
|
13541
|
+
}
|
|
13542
|
+
const overlap = (a, b) => Math.min(a.center[u] + a.size[u] / 2, b.center[u] + b.size[u] / 2) - Math.max(a.center[u] - a.size[u] / 2, b.center[u] - b.size[u] / 2);
|
|
13543
|
+
const climb = () => {
|
|
13544
|
+
while (queue.length > 0) {
|
|
13545
|
+
const cell = cells[queue.pop()];
|
|
13546
|
+
for (const j of rows.get(rowOf(cell.center[v] + cell.size[v] / 2)) ?? []) {
|
|
13547
|
+
const other = cells[j];
|
|
13548
|
+
if (held.has(j) || overlap(cell, other) <= Math.max(JOINT_GAP, MIN_BEARING_SHARE * Math.min(cell.size[u], other.size[u]))) continue;
|
|
13549
|
+
held.add(j);
|
|
13550
|
+
queue.push(j);
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
};
|
|
13554
|
+
for (const row of rows.values()) row.sort((a, b) => cells[a].center[u] - cells[b].center[u]);
|
|
13555
|
+
const touching = (a, b) => Math.abs(a.center[u] + a.size[u] / 2 - (b.center[u] - b.size[u] / 2)) <= 1e-3;
|
|
13556
|
+
for (let wedged = true; wedged; ) {
|
|
13557
|
+
climb();
|
|
13558
|
+
wedged = false;
|
|
13559
|
+
for (const row of rows.values()) {
|
|
13560
|
+
for (let k = 1; k < row.length - 1; k++) {
|
|
13561
|
+
const i = row[k];
|
|
13562
|
+
if (held.has(i) || !held.has(row[k - 1]) || !held.has(row[k + 1])) continue;
|
|
13563
|
+
if (!touching(cells[row[k - 1]], cells[i]) || !touching(cells[i], cells[row[k + 1]])) continue;
|
|
13564
|
+
held.add(i);
|
|
13565
|
+
queue.push(i);
|
|
13566
|
+
wedged = true;
|
|
13567
|
+
}
|
|
13568
|
+
}
|
|
13569
|
+
}
|
|
13570
|
+
const loose = [];
|
|
13571
|
+
for (const row of rows.values()) for (const i of row) if (!held.has(i)) loose.push(i);
|
|
13572
|
+
return loose;
|
|
13573
|
+
};
|
|
13574
|
+
var clumpCells = (cells, thin, cap) => {
|
|
13575
|
+
if (cells.length <= cap) return [...cells];
|
|
13576
|
+
const [u, v] = planeAxes(thin);
|
|
13577
|
+
let lo0 = Infinity, lo1 = Infinity, hi0 = -Infinity, hi1 = -Infinity;
|
|
13578
|
+
for (const cell of cells) {
|
|
13579
|
+
lo0 = Math.min(lo0, cell.center[u] - cell.size[u] / 2);
|
|
13580
|
+
hi0 = Math.max(hi0, cell.center[u] + cell.size[u] / 2);
|
|
13581
|
+
lo1 = Math.min(lo1, cell.center[v] - cell.size[v] / 2);
|
|
13582
|
+
hi1 = Math.max(hi1, cell.center[v] + cell.size[v] / 2);
|
|
13583
|
+
}
|
|
13584
|
+
let side = Math.sqrt((hi0 - lo0) * (hi1 - lo1) / cap);
|
|
13585
|
+
for (; ; ) {
|
|
13586
|
+
const bins = /* @__PURE__ */ new Map();
|
|
13587
|
+
for (const cell of cells) {
|
|
13588
|
+
const key2 = `${Math.floor((cell.center[u] - lo0) / side)}:${Math.floor((cell.center[v] - lo1) / side)}`;
|
|
13589
|
+
const bin = bins.get(key2) ?? { lo: [Infinity, Infinity, Infinity], hi: [-Infinity, -Infinity, -Infinity] };
|
|
13590
|
+
for (let k = 0; k < 3; k++) {
|
|
13591
|
+
bin.lo[k] = Math.min(bin.lo[k], cell.center[k] - cell.size[k] / 2);
|
|
13592
|
+
bin.hi[k] = Math.max(bin.hi[k], cell.center[k] + cell.size[k] / 2);
|
|
13593
|
+
}
|
|
13594
|
+
bins.set(key2, bin);
|
|
13595
|
+
}
|
|
13596
|
+
if (bins.size <= cap) {
|
|
13597
|
+
return [...bins.values()].map(({ lo, hi }) => ({ center: [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2], size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]] }));
|
|
13598
|
+
}
|
|
13599
|
+
side *= 1.25;
|
|
13600
|
+
}
|
|
13601
|
+
};
|
|
13435
13602
|
var MAX_SPALLS = 64;
|
|
13436
13603
|
var thinAxisOf = (dims) => dims[0] <= dims[1] && dims[0] <= dims[2] ? 0 : dims[1] <= dims[2] ? 1 : 2;
|
|
13437
13604
|
var resolveLayer = (def, index, mat) => ({ index, def, mat, energy: mat.penetration * def.thickness * 1e3 });
|
|
@@ -14411,6 +14578,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14411
14578
|
},
|
|
14412
14579
|
{
|
|
14413
14580
|
id: "wood_soft",
|
|
14581
|
+
splitForm: "slivers",
|
|
14414
14582
|
density: 500,
|
|
14415
14583
|
hpPerKg: 90,
|
|
14416
14584
|
penetration: 9,
|
|
@@ -14456,6 +14624,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14456
14624
|
},
|
|
14457
14625
|
{
|
|
14458
14626
|
id: "wood_hard",
|
|
14627
|
+
splitForm: "slivers",
|
|
14459
14628
|
density: 750,
|
|
14460
14629
|
hpPerKg: 140,
|
|
14461
14630
|
penetration: 14,
|
|
@@ -14500,6 +14669,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14500
14669
|
},
|
|
14501
14670
|
{
|
|
14502
14671
|
id: "plywood",
|
|
14672
|
+
splitForm: "plies",
|
|
14503
14673
|
density: 600,
|
|
14504
14674
|
hpPerKg: 70,
|
|
14505
14675
|
penetration: 7,
|
|
@@ -14547,6 +14717,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14547
14717
|
},
|
|
14548
14718
|
{
|
|
14549
14719
|
id: "mdf",
|
|
14720
|
+
splitForm: "granules",
|
|
14550
14721
|
density: 750,
|
|
14551
14722
|
hpPerKg: 45,
|
|
14552
14723
|
penetration: 6,
|
|
@@ -15058,6 +15229,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15058
15229
|
},
|
|
15059
15230
|
{
|
|
15060
15231
|
id: "green_wood",
|
|
15232
|
+
splitForm: "slivers",
|
|
15061
15233
|
density: 650,
|
|
15062
15234
|
hpPerKg: 5,
|
|
15063
15235
|
penetration: 10,
|
|
@@ -15338,6 +15510,125 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15338
15510
|
"brittle"
|
|
15339
15511
|
],
|
|
15340
15512
|
compressive: 1e5
|
|
15513
|
+
},
|
|
15514
|
+
{
|
|
15515
|
+
id: "mud",
|
|
15516
|
+
density: 1700,
|
|
15517
|
+
hpPerKg: 1,
|
|
15518
|
+
penetration: 5,
|
|
15519
|
+
ductility: 0.8,
|
|
15520
|
+
fracture: "crumble",
|
|
15521
|
+
fragmentSize: [
|
|
15522
|
+
0.02,
|
|
15523
|
+
0.08
|
|
15524
|
+
],
|
|
15525
|
+
hitDebris: "dust",
|
|
15526
|
+
breakDebris: "dust",
|
|
15527
|
+
thermal: {
|
|
15528
|
+
conductivity: 0.5,
|
|
15529
|
+
heatCapacity: 3.2
|
|
15530
|
+
},
|
|
15531
|
+
tags: [
|
|
15532
|
+
"soil",
|
|
15533
|
+
"wet"
|
|
15534
|
+
],
|
|
15535
|
+
compressive: 5e3
|
|
15536
|
+
},
|
|
15537
|
+
{
|
|
15538
|
+
id: "clay",
|
|
15539
|
+
density: 1800,
|
|
15540
|
+
hpPerKg: 5,
|
|
15541
|
+
penetration: 7,
|
|
15542
|
+
ductility: 0.05,
|
|
15543
|
+
fracture: "crumble",
|
|
15544
|
+
fragmentSize: [
|
|
15545
|
+
0.02,
|
|
15546
|
+
0.1
|
|
15547
|
+
],
|
|
15548
|
+
hitDebris: "chips",
|
|
15549
|
+
breakDebris: "dust",
|
|
15550
|
+
thermal: {
|
|
15551
|
+
conductivity: 0.15,
|
|
15552
|
+
heatCapacity: 2.2
|
|
15553
|
+
},
|
|
15554
|
+
tags: [
|
|
15555
|
+
"soil",
|
|
15556
|
+
"brittle"
|
|
15557
|
+
],
|
|
15558
|
+
compressive: 6e4
|
|
15559
|
+
},
|
|
15560
|
+
{
|
|
15561
|
+
id: "snow",
|
|
15562
|
+
density: 300,
|
|
15563
|
+
hpPerKg: 1,
|
|
15564
|
+
penetration: 3,
|
|
15565
|
+
ductility: 0.6,
|
|
15566
|
+
fracture: "crumble",
|
|
15567
|
+
fragmentSize: [
|
|
15568
|
+
0.01,
|
|
15569
|
+
0.05
|
|
15570
|
+
],
|
|
15571
|
+
hitDebris: "dust",
|
|
15572
|
+
breakDebris: "dust",
|
|
15573
|
+
thermal: {
|
|
15574
|
+
conductivity: 0.1,
|
|
15575
|
+
heatCapacity: 2.1
|
|
15576
|
+
},
|
|
15577
|
+
tags: [
|
|
15578
|
+
"soil",
|
|
15579
|
+
"frozen"
|
|
15580
|
+
],
|
|
15581
|
+
compressive: 1e3
|
|
15582
|
+
},
|
|
15583
|
+
{
|
|
15584
|
+
id: "cobble",
|
|
15585
|
+
density: 2650,
|
|
15586
|
+
hpPerKg: 25,
|
|
15587
|
+
penetration: 25,
|
|
15588
|
+
penetrationClass: "concrete",
|
|
15589
|
+
penetrationScale: 0.9,
|
|
15590
|
+
ductility: 0.03,
|
|
15591
|
+
fracture: "crumble",
|
|
15592
|
+
fragmentSize: [
|
|
15593
|
+
0.05,
|
|
15594
|
+
0.15
|
|
15595
|
+
],
|
|
15596
|
+
hitDebris: "chips",
|
|
15597
|
+
breakDebris: "chips",
|
|
15598
|
+
thermal: {
|
|
15599
|
+
conductivity: 0.2,
|
|
15600
|
+
heatCapacity: 2.5
|
|
15601
|
+
},
|
|
15602
|
+
tags: [
|
|
15603
|
+
"masonry",
|
|
15604
|
+
"brittle"
|
|
15605
|
+
],
|
|
15606
|
+
compressive: 3e5
|
|
15607
|
+
},
|
|
15608
|
+
{
|
|
15609
|
+
id: "paver",
|
|
15610
|
+
density: 2300,
|
|
15611
|
+
hpPerKg: 14,
|
|
15612
|
+
penetration: 18,
|
|
15613
|
+
penetrationClass: "concrete",
|
|
15614
|
+
penetrationScale: 1,
|
|
15615
|
+
ductility: 0.03,
|
|
15616
|
+
fracture: "crumble",
|
|
15617
|
+
fragmentSize: [
|
|
15618
|
+
0.04,
|
|
15619
|
+
0.12
|
|
15620
|
+
],
|
|
15621
|
+
hitDebris: "chips",
|
|
15622
|
+
breakDebris: "chips",
|
|
15623
|
+
thermal: {
|
|
15624
|
+
conductivity: 0.15,
|
|
15625
|
+
heatCapacity: 2.5
|
|
15626
|
+
},
|
|
15627
|
+
tags: [
|
|
15628
|
+
"masonry",
|
|
15629
|
+
"brittle"
|
|
15630
|
+
],
|
|
15631
|
+
compressive: 35e4
|
|
15341
15632
|
}
|
|
15342
15633
|
];
|
|
15343
15634
|
var material_hints_default = {
|
|
@@ -15550,6 +15841,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15550
15841
|
var BREACH_BLAST_FACTOR = 6;
|
|
15551
15842
|
var BREACH_AREA = 0.25;
|
|
15552
15843
|
var BREACH_BLAST_REACH = 0.12;
|
|
15844
|
+
var MEMBER_CUT_REACH = 0.5;
|
|
15845
|
+
var BLAST_REFERENCE_RESISTANCE = 200 * 1900 * 0.13;
|
|
15846
|
+
var BLAST_EDGE_RAGGED = 0.3;
|
|
15847
|
+
var BLAST_EDGE_LOOSE = 1.35;
|
|
15848
|
+
var BLAST_EDGE_WEAR = 0.6;
|
|
15553
15849
|
var RESIDUAL_CAPACITY = 0.15;
|
|
15554
15850
|
var SEVER_BAND = 0.1;
|
|
15555
15851
|
var SEVER_ASPECT = 3;
|
|
@@ -16802,7 +17098,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16802
17098
|
if (j.st.open) continue;
|
|
16803
17099
|
if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
|
|
16804
17100
|
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
16805
|
-
j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
17101
|
+
j.st.hp -= span === void 0 ? amount : Math.min(amount, j.st.maxHp) * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
16806
17102
|
if (j.st.hp <= 0) {
|
|
16807
17103
|
j.st.hp = 0;
|
|
16808
17104
|
j.st.open = true;
|
|
@@ -16930,6 +17226,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16930
17226
|
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
16931
17227
|
const cut = (_b = p.rt).craters ?? (_b.craters = {});
|
|
16932
17228
|
const out = [];
|
|
17229
|
+
const wholeBefore = Object.values(worn).filter((w) => w === -1).length;
|
|
16933
17230
|
const shot = kind === "ballistic";
|
|
16934
17231
|
for (let i = 0; i < grid.length; i++) {
|
|
16935
17232
|
const cell = grid[i];
|
|
@@ -16988,6 +17285,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16988
17285
|
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
16989
17286
|
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
16990
17287
|
}
|
|
17288
|
+
if (Object.values(worn).filter((w) => w === -1).length > wholeBefore) dropUnsupported(o, p, src);
|
|
16991
17289
|
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
16992
17290
|
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
16993
17291
|
if (p.structural && !isLost(p)) {
|
|
@@ -17091,10 +17389,122 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17091
17389
|
setStage(o, p, 1, {}, src);
|
|
17092
17390
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
17093
17391
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
17392
|
+
dropUnsupported(o, p, src);
|
|
17094
17393
|
const reinforcement = p.spec.reinforcement;
|
|
17095
17394
|
if (reinforcement) exposeRebar(o, p, src);
|
|
17096
17395
|
if (p.structural) solveStructure(o);
|
|
17097
17396
|
};
|
|
17397
|
+
const blastReachOf = (p, energy) => {
|
|
17398
|
+
const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
|
|
17399
|
+
const resistance = Math.max(1, perKg * p.mat.density * p.thickness);
|
|
17400
|
+
return BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3) * clamp2(Math.cbrt(BLAST_REFERENCE_RESISTANCE / resistance), 0.3, 2);
|
|
17401
|
+
};
|
|
17402
|
+
const blastUnits = (o, p, wp, energy, dist, src) => {
|
|
17403
|
+
var _a, _b;
|
|
17404
|
+
if (!p.staged || p.st.stripped || isLost(p) || chipEnergy <= 0) return false;
|
|
17405
|
+
const reach = blastReachOf(p, energy);
|
|
17406
|
+
if (dist >= reach) return false;
|
|
17407
|
+
const radius = Math.sqrt(reach * reach - dist * dist);
|
|
17408
|
+
const [u, v] = planeAxes(p.thin);
|
|
17409
|
+
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
17410
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
17411
|
+
const grid = gridOf(p);
|
|
17412
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
17413
|
+
const perKg = p.mat.hits?.blunt?.unitJoules ?? chipEnergy;
|
|
17414
|
+
const lobes = [rnd() * 2 * Math.PI, rnd() * 2 * Math.PI];
|
|
17415
|
+
const out = [];
|
|
17416
|
+
for (let i = 0; i < grid.length; i++) {
|
|
17417
|
+
const cell = grid[i];
|
|
17418
|
+
if (worn[i] === -1) continue;
|
|
17419
|
+
if (op && p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius) continue;
|
|
17420
|
+
const du = cell.center[u] - lp[u], dv = cell.center[v] - lp[v];
|
|
17421
|
+
const angle = Math.atan2(dv, du);
|
|
17422
|
+
const edge = radius * (1 + BLAST_EDGE_RAGGED * (0.5 * Math.sin(3 * angle + lobes[0]) + 0.3 * Math.sin(5 * angle + lobes[1]) + 0.4 * (rnd() - 0.5)));
|
|
17423
|
+
const d = Math.hypot(du, dv);
|
|
17424
|
+
if (d <= edge) {
|
|
17425
|
+
worn[i] = -1;
|
|
17426
|
+
out.push(i);
|
|
17427
|
+
} else if (d <= edge * BLAST_EDGE_LOOSE) {
|
|
17428
|
+
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
17429
|
+
worn[i] = Math.max(worn[i] ?? 0, perKg * mass * BLAST_EDGE_WEAR);
|
|
17430
|
+
}
|
|
17431
|
+
}
|
|
17432
|
+
if (out.length === 0) return false;
|
|
17433
|
+
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !(op && p.st.opening && Math.hypot(grid[i].center[u] - op[u], grid[i].center[v] - op[v]) <= p.st.opening.radius));
|
|
17434
|
+
for (const i of loose) worn[i] = -1;
|
|
17435
|
+
const blown = out.map((i) => grid[i]), fallen = loose.map((i) => grid[i]);
|
|
17436
|
+
const volume = (cells2) => cells2.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0);
|
|
17437
|
+
const blownMass = Math.min(p.st.mass, p.mat.density * volume(blown));
|
|
17438
|
+
const fallenMass = Math.min(p.st.mass - blownMass, p.mat.density * volume(fallen));
|
|
17439
|
+
const speed = clamp2(Math.sqrt(2 * energy * 0.02 / Math.max(blownMass, 0.05)), 1, 12);
|
|
17440
|
+
const { c, q } = partXf(o, p);
|
|
17441
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
17442
|
+
const tag = `x${chipped.length}_`;
|
|
17443
|
+
const fragments = [
|
|
17444
|
+
...cellFragments(o, p, clumpCells(blown, p.thin, maxFragments), wp, null, speed, blownMass, tag, true),
|
|
17445
|
+
...fallen.length > 0 ? cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), wp, null, 0.3, fallenMass, `${tag}f`, true) : []
|
|
17446
|
+
];
|
|
17447
|
+
const cells = [...blown, ...fallen];
|
|
17448
|
+
const holes = localHolesOf(p);
|
|
17449
|
+
p.st.holes = p.st.holes.filter((_, k) => !cells.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
|
|
17450
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
17451
|
+
for (const cell of cells) {
|
|
17452
|
+
chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
17453
|
+
for (let k = 0; k < 3; k++) {
|
|
17454
|
+
lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
|
|
17455
|
+
hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
|
|
17456
|
+
}
|
|
17457
|
+
}
|
|
17458
|
+
p.st.mass = Math.max(0, p.st.mass - blownMass - fallenMass);
|
|
17459
|
+
p.rt.removed = (p.rt.removed ?? 0) + blownMass + fallenMass;
|
|
17460
|
+
markDamaged(p);
|
|
17461
|
+
setStage(o, p, 1, {}, src);
|
|
17462
|
+
const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
|
|
17463
|
+
const units = cells.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
|
|
17464
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
|
|
17465
|
+
debris(o, p, p.mat.breakDebris, center, vec.normalize(vec.sub(center, wp), [0, 1, 0]), 14, 3, src);
|
|
17466
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
17467
|
+
if (p.structural && !isLost(p)) {
|
|
17468
|
+
solveStructure(o);
|
|
17469
|
+
collapseCheck(o, src);
|
|
17470
|
+
}
|
|
17471
|
+
return true;
|
|
17472
|
+
};
|
|
17473
|
+
const dropUnsupported = (o, p, src) => {
|
|
17474
|
+
var _a, _b;
|
|
17475
|
+
if (!p.spec.units || p.spec.units.mortar === p.spec.material || p.st.stripped || isLost(p)) return 0;
|
|
17476
|
+
const [u, v] = planeAxes(p.thin);
|
|
17477
|
+
const grid = gridOf(p);
|
|
17478
|
+
const worn = (_a = p.rt).chips ?? (_a.chips = {});
|
|
17479
|
+
const op = p.st.opening ? toLocal(p.pos, p.q, p.st.opening.center) : null;
|
|
17480
|
+
const opened = (cell) => !!op && !!p.st.opening && Math.hypot(cell.center[u] - op[u], cell.center[v] - op[v]) <= p.st.opening.radius;
|
|
17481
|
+
const loose = unsupportedCells(grid, p.thin, p.dims, (i) => worn[i] !== -1 && !opened(grid[i]));
|
|
17482
|
+
if (loose.length === 0) return 0;
|
|
17483
|
+
for (const i of loose) worn[i] = -1;
|
|
17484
|
+
const fallen = loose.map((i) => grid[i]);
|
|
17485
|
+
const mass = Math.min(p.st.mass, p.mat.density * fallen.reduce((sum, cell) => sum + cell.size[0] * cell.size[1] * cell.size[2], 0));
|
|
17486
|
+
const { c, q } = partXf(o, p);
|
|
17487
|
+
const chipped = (_b = p.st).chipped ?? (_b.chipped = []);
|
|
17488
|
+
const lo = [Infinity, Infinity, Infinity], hi = [-Infinity, -Infinity, -Infinity];
|
|
17489
|
+
for (const cell of fallen) {
|
|
17490
|
+
for (let k = 0; k < 3; k++) {
|
|
17491
|
+
lo[k] = Math.min(lo[k], cell.center[k] - cell.size[k] / 2);
|
|
17492
|
+
hi[k] = Math.max(hi[k], cell.center[k] + cell.size[k] / 2);
|
|
17493
|
+
}
|
|
17494
|
+
}
|
|
17495
|
+
const center = toWorld(c, q, [(lo[0] + hi[0]) / 2, (lo[1] + hi[1]) / 2, (lo[2] + hi[2]) / 2]);
|
|
17496
|
+
const fragments = cellFragments(o, p, clumpCells(fallen, p.thin, Math.max(2, Math.round(maxFragments / 4))), vec.add(center, [0, 1, 0]), null, 0.3, mass, `u${chipped.length}_`, true);
|
|
17497
|
+
const holes = localHolesOf(p);
|
|
17498
|
+
p.st.holes = p.st.holes.filter((_, k) => !fallen.some((cell) => marksInBox(cell.center, cell.size, void 0, [holes[k]]).length > 0));
|
|
17499
|
+
for (const cell of fallen) chipped.push({ center: [cell.center[0], cell.center[1], cell.center[2]], size: [cell.size[0], cell.size[1], cell.size[2]] });
|
|
17500
|
+
p.st.mass = Math.max(0, p.st.mass - mass);
|
|
17501
|
+
p.rt.removed = (p.rt.removed ?? 0) + mass;
|
|
17502
|
+
markDamaged(p);
|
|
17503
|
+
const units = fallen.map((cell) => ({ center: toWorld(c, q, cell.center), size: [cell.size[0], cell.size[1], cell.size[2]] }));
|
|
17504
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]], rotation: q, fragments, units });
|
|
17505
|
+
debris(o, p, p.mat.breakDebris, center, [0, -1, 0], 6, 1, src);
|
|
17506
|
+
return loose.length;
|
|
17507
|
+
};
|
|
17098
17508
|
const stripPart = (o, p, reinforcement, src) => {
|
|
17099
17509
|
const bars = rebarBars(p.dims, p.thin, reinforcement.spacing);
|
|
17100
17510
|
p.st.stripped = true;
|
|
@@ -17161,6 +17571,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17161
17571
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
17162
17572
|
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
17163
17573
|
const cutAt2 = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
17574
|
+
const planRnd = p.mat.splitForm ? splitStream(o.id, p.spec.id, hitLocal) : rnd;
|
|
17164
17575
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
17165
17576
|
style,
|
|
17166
17577
|
dims: p.dims,
|
|
@@ -17169,8 +17580,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17169
17580
|
hit: hitLocal,
|
|
17170
17581
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
17171
17582
|
maxFragments,
|
|
17172
|
-
rnd,
|
|
17583
|
+
rnd: planRnd,
|
|
17173
17584
|
sheet: p.mat.sheet,
|
|
17585
|
+
...p.mat.splitForm ? { form: p.mat.splitForm } : {},
|
|
17174
17586
|
...cutAt2 !== void 0 ? { cut: { at: cutAt2, gap: SEVER_BAND } } : {}
|
|
17175
17587
|
});
|
|
17176
17588
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
@@ -17309,7 +17721,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17309
17721
|
const fragments = [];
|
|
17310
17722
|
for (let i = 0; i < n; i++) {
|
|
17311
17723
|
const back = !exit || i % 3 === 2;
|
|
17312
|
-
const
|
|
17724
|
+
const plies = p.mat.splitForm === "plies";
|
|
17725
|
+
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() : plies ? 0.15 + 0.15 * rnd() : 0.4 + 0.4 * rnd());
|
|
17313
17726
|
const size = [w, w, w];
|
|
17314
17727
|
size[along] = len;
|
|
17315
17728
|
size[(along + 1) % 3] = w;
|
|
@@ -17470,7 +17883,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17470
17883
|
checkIgnite(o, p, src);
|
|
17471
17884
|
}
|
|
17472
17885
|
}
|
|
17473
|
-
wearJoints(o, p, Ein * 0.5, void 0, 0, src);
|
|
17886
|
+
wearJoints(o, p, Ein * 0.5, void 0, 0, src, false, 2 * BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3));
|
|
17474
17887
|
const amount = Ein * DMG_PER_J * (mat.damageScale?.explosive ?? 1);
|
|
17475
17888
|
if (blastFrontSpeed > 0 && dist > 0 && mat.tags?.includes("glass")) {
|
|
17476
17889
|
frontQueue.push({ o, p, due: time + dist / blastFrontSpeed, amount, fragEnergy: Ein * 0.3, point, ...src !== void 0 ? { src } : {} });
|
|
@@ -17480,7 +17893,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17480
17893
|
const dealt = applyDamage(o, p, amount, "explosive", point, null, Ein * 0.3, 40, src);
|
|
17481
17894
|
res.damage += dealt;
|
|
17482
17895
|
if (p.bend && flexure.enabled) bendByBlast(o, p, point, energy, radius, reach, src);
|
|
17483
|
-
|
|
17896
|
+
const breachReach = BREACH_BLAST_REACH * Math.cbrt(Math.max(0, energy) / 1e3);
|
|
17897
|
+
if (p.spec.units && p.spec.units.mortar !== p.spec.material) blastUnits(o, p, wp, energy, dist, src);
|
|
17898
|
+
else if (p.spec.reinforcement && p.spec.units && dist > MEMBER_CUT_REACH * breachReach) blastUnits(o, p, wp, energy, dist, src);
|
|
17899
|
+
else if (dist <= breachReach) addBreach(o, p, wp, breachAreaOf(p, dealt, true), Ein, null, src);
|
|
17484
17900
|
return reach;
|
|
17485
17901
|
};
|
|
17486
17902
|
const blastProbe = { partsTested: 0, partsNear: 0 };
|
|
@@ -17726,6 +18142,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
17726
18142
|
}
|
|
17727
18143
|
return h | 0;
|
|
17728
18144
|
};
|
|
18145
|
+
const splitStream = (objectId, partId, hit) => {
|
|
18146
|
+
const codes = Array.from(`${objectId}|${partId}`, (char) => char.charCodeAt(0) / 1e3);
|
|
18147
|
+
const stream = createRng(seedOf2(rng3.state() / 1e3, ...codes, hit[0], hit[1], hit[2]));
|
|
18148
|
+
return () => stream.next();
|
|
18149
|
+
};
|
|
17729
18150
|
const throwFragment = (frag, objectId, from, g, centre, index, src, shields, results, reports, mult) => {
|
|
17730
18151
|
const v = frag.velocity;
|
|
17731
18152
|
const speed = Math.hypot(v[0], v[1], v[2]);
|
|
@@ -19021,7 +19442,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19021
19442
|
var DEFAULT_DENSITY = 500;
|
|
19022
19443
|
var densityOf = (material) => DENSITY[material] ?? DEFAULT_DENSITY;
|
|
19023
19444
|
var OPEN_ROOM_TYPES = ["balcony", "terrace", "porch"];
|
|
19445
|
+
var WALL_PILLAR_SIDE = 0.2;
|
|
19446
|
+
var HALF = WALL_PILLAR_SIDE / 2;
|
|
19024
19447
|
var CLOSED_ONLY = [...OPEN_ROOM_TYPES, "void"];
|
|
19448
|
+
var HALF_PILLAR = WALL_PILLAR_SIDE / 2;
|
|
19025
19449
|
var entry = (itemId, min, max, group, pickable, weight) => ({ itemId, min, max, group, pickable, weight });
|
|
19026
19450
|
var FRIDGE = [
|
|
19027
19451
|
entry("water_bottle", 1, 3, "water", true, 3),
|
|
@@ -19756,6 +20180,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19756
20180
|
}
|
|
19757
20181
|
var LINTEL_DEPTH2 = 0.2;
|
|
19758
20182
|
var LINTEL_BEARING2 = 0.2;
|
|
20183
|
+
var SILL_BEAM_DEPTH = 0.1;
|
|
20184
|
+
var LINTEL_SPAN_RATIO = 10;
|
|
19759
20185
|
var MIN_PIECE = 0.05;
|
|
19760
20186
|
var frameOf = (wall) => {
|
|
19761
20187
|
const alongU = wall.aabb[2] - wall.aabb[0] >= wall.aabb[3] - wall.aabb[1];
|
|
@@ -19769,6 +20195,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19769
20195
|
const drafts = [];
|
|
19770
20196
|
const piers = [];
|
|
19771
20197
|
const lintels = [];
|
|
20198
|
+
const sillBeams = [];
|
|
19772
20199
|
const spandrels = [];
|
|
19773
20200
|
const height = wall.y1 - wall.y0;
|
|
19774
20201
|
let cursor = frame2.from;
|
|
@@ -19779,23 +20206,26 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19779
20206
|
};
|
|
19780
20207
|
own.forEach((opening2, k) => {
|
|
19781
20208
|
addPier(Math.max(cursor, opening2.from));
|
|
19782
|
-
const
|
|
20209
|
+
const span = opening2.to - opening2.from;
|
|
20210
|
+
const lintelTop = Math.min(wall.y1, opening2.y1 + Math.max(LINTEL_DEPTH2, style.lintelDepth?.(span) ?? 0, (span + LINTEL_BEARING2) / LINTEL_SPAN_RATIO));
|
|
19783
20211
|
const lintelFrom = Math.max(frame2.from, opening2.from - LINTEL_BEARING2);
|
|
19784
20212
|
const lintelTo = Math.min(frame2.to, opening2.to + LINTEL_BEARING2);
|
|
19785
20213
|
const lintelHeight = lintelTop - opening2.y1;
|
|
19786
|
-
|
|
20214
|
+
const sillTop = style.sillBeam && opening2.y0 - wall.y0 > SILL_BEAM_DEPTH + MIN_PIECE ? opening2.y0 - SILL_BEAM_DEPTH : opening2.y0;
|
|
20215
|
+
if (sillTop - wall.y0 > MIN_PIECE) drafts.push({ id: `${wall.id}_sill_${k}`, role: "partition", material: wall.material, from: opening2.from, to: opening2.to, y0: wall.y0, y1: sillTop, volume: (opening2.to - opening2.from) * (sillTop - wall.y0) });
|
|
20216
|
+
if (sillTop < opening2.y0) sillBeams.push({ id: `${wall.id}_sillbeam_${k}`, role: "beam", material: style.lintelMaterial, from: lintelFrom, to: lintelTo, y0: sillTop, y1: opening2.y0, volume: (lintelTo - lintelFrom) * SILL_BEAM_DEPTH });
|
|
19787
20217
|
lintels.push({ id: `${wall.id}_lintel_${k}`, role: "beam", material: style.lintelMaterial, from: lintelFrom, to: lintelTo, y0: opening2.y1, y1: lintelTop, volume: (lintelTo - lintelFrom) * lintelHeight });
|
|
19788
20218
|
spandrels.push(wall.y1 - lintelTop > MIN_PIECE ? { id: `${wall.id}_spandrel_${k}`, role: roleOf2, material: wall.material, from: opening2.from, to: opening2.to, y0: lintelTop, y1: wall.y1, volume: (opening2.to - opening2.from) * (wall.y1 - lintelTop) } : void 0);
|
|
19789
20219
|
cursor = opening2.to;
|
|
19790
20220
|
});
|
|
19791
20221
|
addPier(frame2.to);
|
|
19792
20222
|
for (const pier of piers) {
|
|
19793
|
-
for (const lintel of lintels) {
|
|
20223
|
+
for (const lintel of [...lintels, ...sillBeams]) {
|
|
19794
20224
|
const overlap = Math.max(0, Math.min(pier.to, lintel.to) - Math.max(pier.from, lintel.from));
|
|
19795
20225
|
pier.volume -= overlap * (lintel.y1 - lintel.y0);
|
|
19796
20226
|
}
|
|
19797
20227
|
}
|
|
19798
|
-
const all = [...piers, ...drafts, ...lintels, ...spandrels.filter((d) => d !== void 0)];
|
|
20228
|
+
const all = [...piers, ...drafts, ...sillBeams, ...lintels, ...spandrels.filter((d) => d !== void 0)];
|
|
19799
20229
|
const total = all.reduce((sum, d) => sum + d.volume, 0);
|
|
19800
20230
|
const pieces = all.map((d) => ({
|
|
19801
20231
|
id: d.id,
|
|
@@ -19810,6 +20240,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19810
20240
|
...d.role === "beam" ? style.lintelAssembly ? { assembly: style.lintelAssembly } : {} : wall.assembly ? { assembly: wall.assembly } : {}
|
|
19811
20241
|
}));
|
|
19812
20242
|
const internal = [];
|
|
20243
|
+
for (const beam of sillBeams) {
|
|
20244
|
+
const sill = drafts.find((d) => d.id === beam.id.replace("_sillbeam_", "_sill_"));
|
|
20245
|
+
if (sill) internal.push({ part: beam.id, on: sill.id, joint: style.lintelJoint });
|
|
20246
|
+
for (const pier of piers) if (Math.min(pier.to, beam.to) - Math.max(pier.from, beam.from) > 0) internal.push({ part: beam.id, on: pier.id, joint: style.lintelJoint });
|
|
20247
|
+
}
|
|
19813
20248
|
lintels.forEach((lintel, k) => {
|
|
19814
20249
|
for (const pier of piers) if (Math.min(pier.to, lintel.to) - Math.max(pier.from, lintel.from) > 0) internal.push({ part: lintel.id, on: pier.id, joint: style.lintelJoint });
|
|
19815
20250
|
const spandrel = spandrels[k];
|
|
@@ -19839,7 +20274,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19839
20274
|
...system.timber ? {} : { lintelAssembly: "reinforced_concrete" },
|
|
19840
20275
|
lintelJoint: system.joints.beam,
|
|
19841
20276
|
wallJoint: system.joints.wall,
|
|
19842
|
-
...lintelDepth && !system.timber ? { lintelDepth } : {}
|
|
20277
|
+
...lintelDepth && !system.timber ? { lintelDepth } : {},
|
|
20278
|
+
...construction.system === "masonry" ? { sillBeam: true } : {}
|
|
19843
20279
|
});
|
|
19844
20280
|
if (split.pieces.length > 1) splits.set(part.id, split);
|
|
19845
20281
|
}
|
|
@@ -19847,7 +20283,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19847
20283
|
}
|
|
19848
20284
|
function applyPanels(input, structure, detail, lintelDepth) {
|
|
19849
20285
|
if (detail === "coarse") return input;
|
|
19850
|
-
return applySplits(input, panelSplits(input, structure, lintelDepth));
|
|
20286
|
+
return onJambPillars(applySplits(input, panelSplits(input, structure, lintelDepth)));
|
|
20287
|
+
}
|
|
20288
|
+
var OPENING_BEAM = /_(lintel|sillbeam)_\d+$/;
|
|
20289
|
+
var overlaps2 = (a, b) => a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1];
|
|
20290
|
+
function onJambPillars(split) {
|
|
20291
|
+
const pillars = split.parts.filter((part) => part.role === "column" && part.material === "concrete");
|
|
20292
|
+
const extra = [];
|
|
20293
|
+
for (const beam of split.parts) {
|
|
20294
|
+
if (beam.role !== "beam" || !OPENING_BEAM.test(beam.id)) continue;
|
|
20295
|
+
for (const pillar of pillars) if (pillar.floor === beam.floor && overlaps2(pillar.aabb, beam.aabb) && pillar.y0 <= beam.y0 && pillar.y1 >= beam.y1 - 0.01) extra.push({ part: beam.id, on: pillar.id, joint: "rebar" });
|
|
20296
|
+
}
|
|
20297
|
+
return extra.length ? { parts: split.parts, supports: [...split.supports, ...extra] } : split;
|
|
19851
20298
|
}
|
|
19852
20299
|
function applyUnits(input, structure) {
|
|
19853
20300
|
const splits = /* @__PURE__ */ new Map();
|
|
@@ -19909,21 +20356,35 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19909
20356
|
};
|
|
19910
20357
|
var MEMBER_CAGE = { spacing: 0.1, diameter: 8e-3, unit: 0.1 };
|
|
19911
20358
|
var CAGED_ROLES = /* @__PURE__ */ new Set(["beam", "column"]);
|
|
20359
|
+
var CAGE_COVER = 0.03;
|
|
20360
|
+
var CAGE_BUCKLING_SHARE = 0.5;
|
|
20361
|
+
var DEFAULT_RESIDUAL = 0.15;
|
|
20362
|
+
function cageResidualCapacity(size, reinforcement, capacity) {
|
|
20363
|
+
const [a = 0, b = 0] = [...size].sort((x, y) => x - y);
|
|
20364
|
+
const perSide = (w) => Math.max(2, Math.floor(Math.max(0, w - 2 * CAGE_COVER) / reinforcement.spacing) + 1);
|
|
20365
|
+
const bars = 2 * perSide(a) + 2 * perSide(b) - 4;
|
|
20366
|
+
const steel = MATERIALS2.find((m) => m.id === reinforcement.material)?.compressive ?? 0;
|
|
20367
|
+
const carries2 = bars * Math.PI * (reinforcement.diameter / 2) ** 2 * steel * CAGE_BUCKLING_SHARE;
|
|
20368
|
+
if (!capacity || capacity <= 0 || carries2 <= 0) return DEFAULT_RESIDUAL;
|
|
20369
|
+
return Math.min(1, Math.max(DEFAULT_RESIDUAL, carries2 / capacity));
|
|
20370
|
+
}
|
|
19912
20371
|
var caged = (spec, part, rebar2) => {
|
|
19913
20372
|
if (!rebar2 || part.material !== "concrete" || !CAGED_ROLES.has(part.role) || spec.reinforcement) return spec;
|
|
19914
20373
|
const size = spec.shape.kind === "box" ? spec.shape.size : null;
|
|
19915
20374
|
if (!size) return spec;
|
|
19916
20375
|
const thin = Math.min(...size);
|
|
20376
|
+
const reinforcement = { material: rebar2, spacing: MEMBER_CAGE.spacing, diameter: MEMBER_CAGE.diameter };
|
|
19917
20377
|
return {
|
|
19918
20378
|
...spec,
|
|
19919
|
-
reinforcement: {
|
|
20379
|
+
reinforcement: part.role === "column" ? { ...reinforcement, residualCapacity: cageResidualCapacity(size, reinforcement, spec.structure?.capacity) } : reinforcement,
|
|
19920
20380
|
units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, thin], bond: "stack", mortar: "concrete" }
|
|
19921
20381
|
};
|
|
19922
20382
|
};
|
|
19923
20383
|
var partSpecOf = (part, capacity, detailed, rebar2) => {
|
|
19924
20384
|
if (detailed && part.assembly) return assemblySpec(part, capacity);
|
|
19925
20385
|
const spec = boxSpec(part, capacity);
|
|
19926
|
-
|
|
20386
|
+
const out = detailed ? caged(spec, part, rebar2) : spec;
|
|
20387
|
+
return part.beamKind === "ground_beam" ? { ...out, anchored: true, structure: { role: "foundation" } } : out;
|
|
19927
20388
|
};
|
|
19928
20389
|
function toDestructibleSpec(structure, options = {}) {
|
|
19929
20390
|
const { safety = 0.65, redundancy = 0.97, safetyOthers = 0.45 } = options;
|
|
@@ -19939,7 +20400,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19939
20400
|
const walled = applyPanels(slabbed, structure, detail, slabOptions && structure.construction.frame.rebar ? (span) => beamDepthFor(span, slabOptions) : void 0);
|
|
19940
20401
|
const panels = slabOptions && detail !== "coarse" ? deepenBondBeams(walled, structure, slabOptions) : walled;
|
|
19941
20402
|
const sizing = { safety, redundancy, safetyOthers };
|
|
19942
|
-
const
|
|
20403
|
+
const foundation = new Set(panels.parts.filter((part) => part.beamKind === "ground_beam").map((part) => part.id));
|
|
20404
|
+
const onFoundation = foundation.size ? panels.supports.filter((s) => !foundation.has(s.part)).map((s) => foundation.has(s.on) ? { ...s, on: GROUND } : s) : panels.supports;
|
|
20405
|
+
const sized = { ...panels, capacities: options.sized === false ? /* @__PURE__ */ new Map() : sizeCapacities(panels.parts.filter((part) => !foundation.has(part.id)), onFoundation, sizing) };
|
|
19943
20406
|
const { parts, supports, capacities } = detail === "units" ? applyUnits(sized, structure) : sized;
|
|
19944
20407
|
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse", structure.construction.frame.rebar));
|
|
19945
20408
|
const margin = options.terrainMargin ?? TERRAIN_MARGIN;
|
|
@@ -20004,6 +20467,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
20004
20467
|
out.parts = out.parts.map((p, i) => i === ground ? raft2 : p);
|
|
20005
20468
|
return out;
|
|
20006
20469
|
}
|
|
20470
|
+
var LOW_WINDOW = SILL_BEAM_DEPTH + 0.05;
|
|
20007
20471
|
var slabId = (g, f) => f === g.floors - 1 && g.roof ? "roof" : `slab_${f}`;
|
|
20008
20472
|
var storeyBase = (g, f) => f * (g.height + g.slab);
|
|
20009
20473
|
var slabHalf = (g) => [(g.spanX + g.columnSize + 0.2) / 2, (g.spanZ + g.columnSize + 0.2) / 2];
|
|
@@ -20162,7 +20626,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
20162
20626
|
parts.push({ id, material: c.material, shape: box3(size), position, structure: { role: "partition" }, tags: ["infill"] });
|
|
20163
20627
|
for (const o of on) rests(joints, id, o, "mortar");
|
|
20164
20628
|
};
|
|
20165
|
-
const bay = (side, k, along,
|
|
20629
|
+
const bay = (side, k, along, centre2, fixed, length, c1, c2) => {
|
|
20166
20630
|
const clear = length - g.columnSize;
|
|
20167
20631
|
const win = clear * c.window;
|
|
20168
20632
|
const pier = (clear - win) / 2;
|
|
@@ -20173,11 +20637,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
20173
20637
|
wall(`infill_${f}_${side}_${k}_${name}`, size(u, h), [p[0], y, p[2]], on);
|
|
20174
20638
|
};
|
|
20175
20639
|
const id = (name) => `infill_${f}_${side}_${k}_${name}`;
|
|
20176
|
-
put("sill", clear, sill, yb + sill / 2,
|
|
20177
|
-
put("pl", pier, head - sill, yb + sill + (head - sill) / 2,
|
|
20178
|
-
put("pr", pier, head - sill, yb + sill + (head - sill) / 2,
|
|
20179
|
-
put("head", clear, room - head, yb + head + (room - head) / 2,
|
|
20180
|
-
const gp = at2(
|
|
20640
|
+
put("sill", clear, sill, yb + sill / 2, centre2, [c1, c2]);
|
|
20641
|
+
put("pl", pier, head - sill, yb + sill + (head - sill) / 2, centre2 - clear / 2 + pier / 2, [c1, id("sill")]);
|
|
20642
|
+
put("pr", pier, head - sill, yb + sill + (head - sill) / 2, centre2 + clear / 2 - pier / 2, [c2, id("sill")]);
|
|
20643
|
+
put("head", clear, room - head, yb + head + (room - head) / 2, centre2, [c1, c2]);
|
|
20644
|
+
const gp = at2(centre2);
|
|
20181
20645
|
const gid = `glass_${f}_${side}_${k}`;
|
|
20182
20646
|
parts.push({
|
|
20183
20647
|
id: gid,
|
|
@@ -20644,7 +21108,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
20644
21108
|
}
|
|
20645
21109
|
var towerLocales = { en: { labels: en_default.labels }, "pt-BR": { labels: pt_BR_default.labels } };
|
|
20646
21110
|
|
|
20647
|
-
// ../game-floorplan-three/dist/chunk-
|
|
21111
|
+
// ../game-floorplan-three/dist/chunk-7XVCZF6H.js
|
|
20648
21112
|
var en2 = {
|
|
20649
21113
|
"prompt.open": "E: open {what}",
|
|
20650
21114
|
"prompt.close": "E: close {what}",
|
|
@@ -21386,6 +21850,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21386
21850
|
}
|
|
21387
21851
|
function installHouseWalls(K, shared2) {
|
|
21388
21852
|
const { EPS: EPS3, Y0, boxC, bx, cursor, effectiveHeight, pieceRecord, structurePiece, wallColor } = K;
|
|
21853
|
+
const sh = (color2, factor) => K.sh(color2, factor);
|
|
21389
21854
|
function wallBox(alongU, L, a, b, ya, yb, th, cL, cH, mL, mH) {
|
|
21390
21855
|
if (yb - ya < 1e-4 || b - a < 1e-4) return;
|
|
21391
21856
|
if (alongU) {
|
|
@@ -21429,6 +21894,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21429
21894
|
function walls() {
|
|
21430
21895
|
const R = shared2.ctx.r, cut = shared2.ctx.cut, nA = R.floors.length, vMin = Math.min(...R.walls.map((w) => w.axis === "u" ? w.v0 : Math.min(w.v0, w.v1)));
|
|
21431
21896
|
const corners = /* @__PURE__ */ new Map();
|
|
21897
|
+
const looks = /* @__PURE__ */ new Map();
|
|
21432
21898
|
for (const w of R.walls) {
|
|
21433
21899
|
const f = w.floor;
|
|
21434
21900
|
cursor.group = "f" + f;
|
|
@@ -21438,6 +21904,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21438
21904
|
const hh = cut ? Math.min(h, 1.2) : h, L = alongU ? w.v0 : w.u0, s0 = Math.min(alongU ? w.u0 : w.v0, alongU ? w.u1 : w.v1), s1 = Math.max(alongU ? w.u0 : w.v0, alongU ? w.u1 : w.v1);
|
|
21439
21905
|
const front = alongU && Math.abs(L - vMin) < 0.05;
|
|
21440
21906
|
const sideL = sideOf(w.sides[0], front), sideH = sideOf(w.sides[1], front);
|
|
21907
|
+
if (w.id) {
|
|
21908
|
+
const face = sideL.exterior ? sideL : sideH;
|
|
21909
|
+
looks.set(w.id, { mat: face.mat, color: face.color, top: y0 + hh });
|
|
21910
|
+
}
|
|
21441
21911
|
shared2.ctx.colliders.push({ type: "wall", piece: w.id, f, x0: alongU ? s0 : L - th / 2, z0: alongU ? L - th / 2 : s0, x1: alongU ? s1 : L + th / 2, z1: alongU ? L + th / 2 : s1 });
|
|
21442
21912
|
if (w.type === "railing") {
|
|
21443
21913
|
const c0 = shared2.ctx.colors.metal != null ? shared2.ctx.colors.metal : 9080466;
|
|
@@ -21484,8 +21954,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21484
21954
|
cursor.group = "f" + e.f;
|
|
21485
21955
|
bx(e.color.mat, e.pu - e.th / 2, e.y0, e.pv - e.th / 2, e.pu + e.th / 2, e.y0 + e.hh, e.pv + e.th / 2, e.color.color);
|
|
21486
21956
|
}
|
|
21957
|
+
if (shared2.ctx.showPillars) wallPillars(looks);
|
|
21487
21958
|
}
|
|
21488
|
-
|
|
21959
|
+
const PILLAR_IN_WALL = /^(wall_column_|column_\d|jamb_column_)/;
|
|
21960
|
+
const wallOf = (pillar, parts) => {
|
|
21961
|
+
const u = (pillar.aabb[0] + pillar.aabb[2]) / 2, v = (pillar.aabb[1] + pillar.aabb[3]) / 2;
|
|
21962
|
+
return parts.find((q) => (q.role === "wall" || q.role === "partition") && q.assembly !== void 0 && q.floor === pillar.floor && u >= q.aabb[0] - 1e-3 && u <= q.aabb[2] + 1e-3 && v >= q.aabb[1] - 1e-3 && v <= q.aabb[3] + 1e-3);
|
|
21963
|
+
};
|
|
21964
|
+
function wallPillars(looks) {
|
|
21965
|
+
const parts = Object.values(shared2.ctx.structureIndex);
|
|
21966
|
+
for (const e of parts) {
|
|
21967
|
+
if (e.role !== "column" || !PILLAR_IN_WALL.test(e.id)) continue;
|
|
21968
|
+
const host = wallOf(e, parts);
|
|
21969
|
+
const look = host && looks.get(host.id);
|
|
21970
|
+
if (!host || !look) continue;
|
|
21971
|
+
cursor.group = "f" + e.floor;
|
|
21972
|
+
cursor.pc = structurePiece(host.id);
|
|
21973
|
+
bx(look.mat, e.aabb[0], e.y0, e.aabb[1], e.aabb[2], Math.min(e.y1, look.top), e.aabb[3], sh(look.color != null ? look.color : 15262420, 0.93));
|
|
21974
|
+
}
|
|
21975
|
+
cursor.pc = null;
|
|
21976
|
+
}
|
|
21977
|
+
Object.assign(K, { wallBox, sideOf, pieceOf, nearestWall, walls, wallPillars });
|
|
21489
21978
|
}
|
|
21490
21979
|
function installHouseDoors(K, shared2) {
|
|
21491
21980
|
const seedKey = (id) => K.seedKey(id);
|
|
@@ -26857,7 +27346,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26857
27346
|
if (!pc || pc.unit) return sp;
|
|
26858
27347
|
if (pc.role !== "wall" && pc.role !== "partition") {
|
|
26859
27348
|
const def = getAssembly(pc.assembly || (pc.material === "concrete" && ["slab", "beam", "column", "stair"].includes(pc.role) ? "reinforced_concrete" : ""));
|
|
26860
|
-
const
|
|
27349
|
+
const cage = def && def.reinforcement ? pc.role === "column" && sp.shape.kind === "box" ? { ...def.reinforcement, residualCapacity: cageResidualCapacity(sp.shape.size, def.reinforcement, sp.structure?.capacity) } : { ...def.reinforcement } : null;
|
|
27350
|
+
const out = cage && !sp.reinforcement ? { ...sp, reinforcement: cage } : sp;
|
|
26861
27351
|
if (out.reinforcement && !out.units && pc.material === "concrete" && (pc.role === "beam" || pc.role === "column") && sp.shape.kind === "box") {
|
|
26862
27352
|
return { ...out, units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, Math.min(...sp.shape.size)], bond: "stack", mortar: "concrete" } };
|
|
26863
27353
|
}
|
|
@@ -26883,6 +27373,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26883
27373
|
const a = pos.array;
|
|
26884
27374
|
const u = mesh2.userData;
|
|
26885
27375
|
if (!u._p0) u._p0 = a.slice();
|
|
27376
|
+
if (!u.plainPositions) Object.defineProperty(u, "plainPositions", { value: u._p0, enumerable: false, configurable: true, writable: true });
|
|
26886
27377
|
const list2 = u.collapsedRanges ?? (u.collapsedRanges = []);
|
|
26887
27378
|
if (!list2.some((r) => r[0] === v0 && r[1] === v1)) list2.push([v0, v1]);
|
|
26888
27379
|
const saved = a.slice(v0 * 3, v1 * 3);
|
|
@@ -26966,10 +27457,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26966
27457
|
const v = house.worldToLocal(tmp.p.set(p[0], p[1], p[2]));
|
|
26967
27458
|
return [v.x, v.y, v.z];
|
|
26968
27459
|
};
|
|
26969
|
-
function cells(part, units) {
|
|
27460
|
+
function cells(part, units, mask) {
|
|
26970
27461
|
const [u0, v0, u1, v1] = part.aabb;
|
|
26971
|
-
const alongU =
|
|
27462
|
+
const alongU = mask.alongU;
|
|
26972
27463
|
const len = alongU ? u1 - u0 : v1 - v0;
|
|
27464
|
+
const offset = (alongU ? u0 : v0) - mask.start;
|
|
26973
27465
|
const [ul, uh] = units.size;
|
|
26974
27466
|
const rows = Math.max(1, Math.round((part.y1 - part.y0) / uh));
|
|
26975
27467
|
const rowH = (part.y1 - part.y0) / rows;
|
|
@@ -26980,7 +27472,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26980
27472
|
const shift = units.bond === "running" && r & 1 ? w / 2 : 0;
|
|
26981
27473
|
for (let i = 0; i < per + (shift ? 1 : 0); i++) {
|
|
26982
27474
|
const a = Math.max(0, i * w - shift), b = Math.min(len, i * w - shift + w);
|
|
26983
|
-
if (b - a > 1e-3) out.push({ a, b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
|
|
27475
|
+
if (b - a > 1e-3) out.push({ a: offset + a, b: offset + b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
|
|
26984
27476
|
}
|
|
26985
27477
|
}
|
|
26986
27478
|
return out;
|
|
@@ -26994,8 +27486,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
26994
27486
|
for (const q of structure.parts) {
|
|
26995
27487
|
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;
|
|
26996
27488
|
}
|
|
27489
|
+
return part.role === "column" ? wallAround(part) : null;
|
|
27490
|
+
}
|
|
27491
|
+
function wallAround(part) {
|
|
27492
|
+
const cu = (part.aabb[0] + part.aabb[2]) / 2, cv = (part.aabb[1] + part.aabb[3]) / 2;
|
|
27493
|
+
for (const q of structure.parts) {
|
|
27494
|
+
if (q.id === part.id || q.role !== "wall" && q.role !== "partition" || !structure.meshesOf(q.id).some((it) => it.mesh)) continue;
|
|
27495
|
+
const inside2 = cu >= q.aabb[0] - 1e-3 && cu <= q.aabb[2] + 1e-3 && cv >= q.aabb[1] - 1e-3 && cv <= q.aabb[3] + 1e-3;
|
|
27496
|
+
if (inside2 && Math.min(q.y1, part.y1) - Math.max(q.y0, part.y0) > 0.5) return q.id;
|
|
27497
|
+
}
|
|
26997
27498
|
return null;
|
|
26998
27499
|
}
|
|
27500
|
+
const pillarSpans = (view) => structure.parts.filter((q) => q.role === "column" && wallAround(q) === view.host).map((q) => {
|
|
27501
|
+
const a = (view.mask.alongU ? q.aabb[0] : q.aabb[1]) - view.mask.start, b = (view.mask.alongU ? q.aabb[2] : q.aabb[3]) - view.mask.start;
|
|
27502
|
+
return { a, b, y0: q.y0, y1: q.y1 };
|
|
27503
|
+
});
|
|
26999
27504
|
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);
|
|
27000
27505
|
function shell(it, mask) {
|
|
27001
27506
|
const g0 = it.mesh.geometry, g = new THREE.BufferGeometry();
|
|
@@ -27084,6 +27589,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27084
27589
|
texture.needsUpdate = true;
|
|
27085
27590
|
return { data, texture, w, h, alongU, start: alongU ? u0 : v0, len, y0, height: y1 - y0, rect: new THREE.Vector4(alongU ? u0 : v0, y0, len, y1 - y0) };
|
|
27086
27591
|
}
|
|
27592
|
+
function besidePillars(c, pillars) {
|
|
27593
|
+
let { a, b } = c;
|
|
27594
|
+
for (const k of pillars) {
|
|
27595
|
+
if (c.y1 <= k.y0 || c.y0 >= k.y1 || b <= k.a || a >= k.b) continue;
|
|
27596
|
+
const centre = (a + b) / 2;
|
|
27597
|
+
if (centre > k.a && centre < k.b) return [];
|
|
27598
|
+
if (centre <= k.a) b = Math.min(b, k.a);
|
|
27599
|
+
else a = Math.max(a, k.b);
|
|
27600
|
+
}
|
|
27601
|
+
return b - a > 1e-3 ? [{ ...c, a, b }] : [];
|
|
27602
|
+
}
|
|
27087
27603
|
const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
|
|
27088
27604
|
const ellipseDistance = (s, y, cs, cy, rs, ry) => (Math.hypot((s - cs) / Math.max(rs, 1e-4), (y - cy) / Math.max(ry, 1e-4)) - 1) * Math.min(rs, ry);
|
|
27089
27605
|
function covered(c, boxes) {
|
|
@@ -27106,9 +27622,15 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27106
27622
|
const discs = [];
|
|
27107
27623
|
const flakes = [];
|
|
27108
27624
|
const shown = [];
|
|
27625
|
+
const pillars = pillarSpans(view);
|
|
27109
27626
|
for (const id of view.parts) {
|
|
27110
27627
|
const part = structure.byId.get(id), units2 = specOf(id).units, chips = view.chips.get(id), opening = view.openings.get(id), spalls = view.spalls.get(id);
|
|
27111
|
-
const
|
|
27628
|
+
const own = cells(part, units2, mask);
|
|
27629
|
+
const all = id === view.host ? own.flatMap((c) => besidePillars(c, pillars)) : own;
|
|
27630
|
+
if (view.fallen.has(id)) {
|
|
27631
|
+
holes.push(...all);
|
|
27632
|
+
continue;
|
|
27633
|
+
}
|
|
27112
27634
|
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
27113
27635
|
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
27114
27636
|
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
@@ -27204,52 +27726,55 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27204
27726
|
}
|
|
27205
27727
|
return { center: local(chip.center), along: a, up: u };
|
|
27206
27728
|
}
|
|
27729
|
+
function newView(hostId) {
|
|
27730
|
+
const items = structure.meshesOf(hostId).filter((it) => it.mesh);
|
|
27731
|
+
if (!items.length) return null;
|
|
27732
|
+
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
27733
|
+
const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
|
|
27734
|
+
return {
|
|
27735
|
+
host: hostId,
|
|
27736
|
+
parts,
|
|
27737
|
+
items,
|
|
27738
|
+
mask,
|
|
27739
|
+
core,
|
|
27740
|
+
unitMaterial: unitsMaterial(core),
|
|
27741
|
+
shells: [],
|
|
27742
|
+
saved: null,
|
|
27743
|
+
units: null,
|
|
27744
|
+
gone: 0,
|
|
27745
|
+
pits: 0,
|
|
27746
|
+
bounds: drawnBounds(items),
|
|
27747
|
+
chips: new Map(parts.map((id) => [id, []])),
|
|
27748
|
+
spalls: new Map(parts.map((id) => [id, []])),
|
|
27749
|
+
openings: /* @__PURE__ */ new Map(),
|
|
27750
|
+
flakes: 0,
|
|
27751
|
+
fallen: /* @__PURE__ */ new Set()
|
|
27752
|
+
};
|
|
27753
|
+
}
|
|
27754
|
+
function cut(view) {
|
|
27755
|
+
if (view.saved) return;
|
|
27756
|
+
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
27757
|
+
for (const m of view.shells) m.userData.behind = () => view.units;
|
|
27758
|
+
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
27759
|
+
}
|
|
27207
27760
|
function draw(partId, change) {
|
|
27208
27761
|
if (lod !== 0) return false;
|
|
27209
27762
|
const hostId = hostOf(partId);
|
|
27210
27763
|
if (!hostId || !specOf(partId)?.units) return false;
|
|
27211
|
-
|
|
27212
|
-
if (!view)
|
|
27213
|
-
const items = structure.meshesOf(hostId).filter((it) => it.mesh);
|
|
27214
|
-
if (!items.length) return false;
|
|
27215
|
-
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
27216
|
-
if (!parts.includes(partId)) return false;
|
|
27217
|
-
const mask = maskOf(hostId, parts), core = maskOf(hostId, parts);
|
|
27218
|
-
view = {
|
|
27219
|
-
host: hostId,
|
|
27220
|
-
parts,
|
|
27221
|
-
items,
|
|
27222
|
-
mask,
|
|
27223
|
-
core,
|
|
27224
|
-
unitMaterial: unitsMaterial(core),
|
|
27225
|
-
shells: [],
|
|
27226
|
-
saved: null,
|
|
27227
|
-
units: null,
|
|
27228
|
-
gone: 0,
|
|
27229
|
-
pits: 0,
|
|
27230
|
-
bounds: drawnBounds(items),
|
|
27231
|
-
chips: new Map(parts.map((id) => [id, []])),
|
|
27232
|
-
spalls: new Map(parts.map((id) => [id, []])),
|
|
27233
|
-
openings: /* @__PURE__ */ new Map(),
|
|
27234
|
-
flakes: 0
|
|
27235
|
-
};
|
|
27236
|
-
}
|
|
27237
|
-
if (!view.chips.has(partId)) return false;
|
|
27764
|
+
const view = views.get(hostId) ?? newView(hostId);
|
|
27765
|
+
if (!view || !view.chips.has(partId)) return false;
|
|
27238
27766
|
if (change.opening) {
|
|
27239
27767
|
if (view.openings.has(partId)) return false;
|
|
27240
27768
|
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
27241
27769
|
}
|
|
27242
27770
|
if (change.chip) view.chips.get(partId).push(chipBox(change.chip, view.mask.alongU));
|
|
27771
|
+
for (const chip of change.chips ?? []) view.chips.get(partId).push(chipBox(chip, view.mask.alongU));
|
|
27243
27772
|
if (change.spall) {
|
|
27244
27773
|
const list2 = view.spalls.get(partId);
|
|
27245
27774
|
list2.push({ center: local(change.spall.center), radius: change.spall.radius });
|
|
27246
27775
|
if (list2.length > MAX_FLAKES) list2.shift();
|
|
27247
27776
|
}
|
|
27248
|
-
|
|
27249
|
-
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
27250
|
-
for (const m of view.shells) m.userData.behind = () => view.units;
|
|
27251
|
-
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
27252
|
-
}
|
|
27777
|
+
cut(view);
|
|
27253
27778
|
views.set(hostId, view);
|
|
27254
27779
|
if (opts.deferPaint && change.spall) dirty.add(view);
|
|
27255
27780
|
else {
|
|
@@ -27360,14 +27885,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27360
27885
|
*/
|
|
27361
27886
|
chip(event) {
|
|
27362
27887
|
if (event.torn) return false;
|
|
27363
|
-
|
|
27888
|
+
const rotation = event.rotation ?? [0, 0, 0, 1];
|
|
27889
|
+
if (event.units?.length) return draw(event.part, { chips: event.units.map((unit) => ({ center: unit.center, size: unit.size, rotation })) });
|
|
27890
|
+
return draw(event.part, { chip: { center: event.center, size: event.size ?? [0, 0, 0], rotation } });
|
|
27364
27891
|
},
|
|
27365
27892
|
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
27366
27893
|
drop(partId) {
|
|
27367
|
-
|
|
27368
|
-
if (!
|
|
27894
|
+
let host = views.get(partId) ?? [...views.values()].find((view) => view.parts.includes(partId));
|
|
27895
|
+
if (!host && lod === 0 && riders(partId).length > 0) {
|
|
27896
|
+
host = newView(partId);
|
|
27897
|
+
if (host) {
|
|
27898
|
+
cut(host);
|
|
27899
|
+
views.set(partId, host);
|
|
27900
|
+
}
|
|
27901
|
+
}
|
|
27902
|
+
if (!host) return;
|
|
27903
|
+
host.fallen.add(partId);
|
|
27904
|
+
if (host.parts.some((id) => !host.fallen.has(id))) {
|
|
27905
|
+
dirty.delete(host);
|
|
27906
|
+
paint(host);
|
|
27907
|
+
invalidate();
|
|
27908
|
+
invalidateShadow();
|
|
27909
|
+
return;
|
|
27910
|
+
}
|
|
27911
|
+
const v = host;
|
|
27369
27912
|
dispose(v);
|
|
27370
|
-
views.delete(
|
|
27913
|
+
views.delete(v.host);
|
|
27371
27914
|
if (v.saved) dropped.push(v);
|
|
27372
27915
|
},
|
|
27373
27916
|
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
@@ -27842,6 +28385,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27842
28385
|
shared2.ctx.noPiece = false;
|
|
27843
28386
|
cursor.separate = opts.pieces === "meshes";
|
|
27844
28387
|
cursor.pc = null;
|
|
28388
|
+
shared2.ctx.showPillars = opts.pillars !== "hidden";
|
|
27845
28389
|
shared2.ctx.apartment = r.template === "apartment_modern";
|
|
27846
28390
|
shared2.ctx.pa = Math.max(3, Math.min(20, pr.floors != null ? pr.floors : 5));
|
|
27847
28391
|
shared2.ctx.pf = Math.max(1, Math.min(shared2.ctx.pa, pr.floor != null ? pr.floor : 3));
|