@adatechnology/game-floorplan-three 0.5.0 → 0.6.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/README.md +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.global.js +468 -43
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +80 -15
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.global.js
CHANGED
|
@@ -11268,7 +11268,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
11268
11268
|
const band = { c: [root.c[0], root.c[1], root.c[2]], s: [dims[0], dims[1], dims[2]] };
|
|
11269
11269
|
band.c[L] = (lo + hi) / 2;
|
|
11270
11270
|
band.s[L] = Math.max(hi - lo, 0.01);
|
|
11271
|
-
|
|
11271
|
+
if (inp.style === "snap") {
|
|
11272
|
+
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] });
|
|
11273
|
+
} else for (const p of splinterBox(inp, band, Math.max(3, inp.maxFragments - out.length))) out.push(p);
|
|
11272
11274
|
return out;
|
|
11273
11275
|
};
|
|
11274
11276
|
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
@@ -11299,6 +11301,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
11299
11301
|
const t = sorted[0];
|
|
11300
11302
|
const [u, v] = [sorted[1], sorted[2]].sort((a, b) => a - b);
|
|
11301
11303
|
if (style === "snap") {
|
|
11304
|
+
if (inp.cut) return severAt(inp, root, inp.cut.at, inp.cut.gap);
|
|
11302
11305
|
const n2 = Math.min(maxFragments, 2 + Math.floor(rnd() * 3));
|
|
11303
11306
|
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] });
|
|
11304
11307
|
return out;
|
|
@@ -11472,6 +11475,27 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
11472
11475
|
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]]));
|
|
11473
11476
|
return out;
|
|
11474
11477
|
};
|
|
11478
|
+
var clipBarsToBox = (bars, u, v, center, size) => {
|
|
11479
|
+
const out = [];
|
|
11480
|
+
for (const [a, b] of bars) {
|
|
11481
|
+
const axis = a[u] !== b[u] ? u : v;
|
|
11482
|
+
const other = axis === u ? v : u;
|
|
11483
|
+
if (Math.abs(a[other] - center[other]) > size[other] / 2) continue;
|
|
11484
|
+
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);
|
|
11485
|
+
if (hi - lo < 1e-6) continue;
|
|
11486
|
+
const p = [a[0], a[1], a[2]], q = [b[0], b[1], b[2]];
|
|
11487
|
+
p[axis] = lo;
|
|
11488
|
+
q[axis] = hi;
|
|
11489
|
+
out.push([p, q]);
|
|
11490
|
+
}
|
|
11491
|
+
return out;
|
|
11492
|
+
};
|
|
11493
|
+
var bendBar = ([a, b], thin, bow, i) => {
|
|
11494
|
+
const length = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
11495
|
+
const m = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];
|
|
11496
|
+
m[thin] += (i % 2 === 0 ? 1 : -1) * bow * length;
|
|
11497
|
+
return [[a, m], [m, b]];
|
|
11498
|
+
};
|
|
11475
11499
|
var clipBarsToDisc = (bars, u, v, center, radius) => {
|
|
11476
11500
|
const out = [];
|
|
11477
11501
|
for (const [a, b] of bars) {
|
|
@@ -12392,14 +12416,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12392
12416
|
smoke: "grey"
|
|
12393
12417
|
},
|
|
12394
12418
|
visual: {
|
|
12395
|
-
charColor: 1709072
|
|
12419
|
+
charColor: 1709072,
|
|
12420
|
+
innerColor: 14730131
|
|
12396
12421
|
},
|
|
12397
12422
|
tags: [
|
|
12398
12423
|
"wood",
|
|
12399
12424
|
"organic",
|
|
12400
12425
|
"flammable"
|
|
12401
12426
|
],
|
|
12402
|
-
compressive: 1e5
|
|
12427
|
+
compressive: 1e5,
|
|
12428
|
+
hits: {
|
|
12429
|
+
blunt: {
|
|
12430
|
+
tear: "splinters",
|
|
12431
|
+
tearJoules: 60
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12403
12434
|
},
|
|
12404
12435
|
{
|
|
12405
12436
|
id: "wood_hard",
|
|
@@ -12429,14 +12460,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12429
12460
|
smoke: "grey"
|
|
12430
12461
|
},
|
|
12431
12462
|
visual: {
|
|
12432
|
-
charColor: 1709072
|
|
12463
|
+
charColor: 1709072,
|
|
12464
|
+
innerColor: 13213802
|
|
12433
12465
|
},
|
|
12434
12466
|
tags: [
|
|
12435
12467
|
"wood",
|
|
12436
12468
|
"organic",
|
|
12437
12469
|
"flammable"
|
|
12438
12470
|
],
|
|
12439
|
-
compressive: 15e4
|
|
12471
|
+
compressive: 15e4,
|
|
12472
|
+
hits: {
|
|
12473
|
+
blunt: {
|
|
12474
|
+
tear: "splinters",
|
|
12475
|
+
tearJoules: 150
|
|
12476
|
+
}
|
|
12477
|
+
}
|
|
12440
12478
|
},
|
|
12441
12479
|
{
|
|
12442
12480
|
id: "plywood",
|
|
@@ -12466,14 +12504,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12466
12504
|
smoke: "grey"
|
|
12467
12505
|
},
|
|
12468
12506
|
visual: {
|
|
12469
|
-
charColor: 1709072
|
|
12507
|
+
charColor: 1709072,
|
|
12508
|
+
innerColor: 14862234
|
|
12470
12509
|
},
|
|
12471
12510
|
tags: [
|
|
12472
12511
|
"wood",
|
|
12473
12512
|
"organic",
|
|
12474
12513
|
"flammable"
|
|
12475
12514
|
],
|
|
12476
|
-
compressive: 6e4
|
|
12515
|
+
compressive: 6e4,
|
|
12516
|
+
hits: {
|
|
12517
|
+
ballistic: {
|
|
12518
|
+
tear: "splinters"
|
|
12519
|
+
},
|
|
12520
|
+
blunt: {
|
|
12521
|
+
tear: "splinters",
|
|
12522
|
+
tearJoules: 80
|
|
12523
|
+
}
|
|
12524
|
+
}
|
|
12477
12525
|
},
|
|
12478
12526
|
{
|
|
12479
12527
|
id: "mdf",
|
|
@@ -12500,14 +12548,25 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12500
12548
|
smoke: "grey"
|
|
12501
12549
|
},
|
|
12502
12550
|
visual: {
|
|
12503
|
-
charColor: 1709072
|
|
12551
|
+
charColor: 1709072,
|
|
12552
|
+
innerColor: 12096362
|
|
12504
12553
|
},
|
|
12505
12554
|
tags: [
|
|
12506
12555
|
"wood",
|
|
12507
12556
|
"organic",
|
|
12508
12557
|
"flammable"
|
|
12509
12558
|
],
|
|
12510
|
-
compressive: 3e4
|
|
12559
|
+
compressive: 3e4,
|
|
12560
|
+
hits: {
|
|
12561
|
+
ballistic: {
|
|
12562
|
+
tear: "lumps",
|
|
12563
|
+
tearJoules: 120
|
|
12564
|
+
},
|
|
12565
|
+
blunt: {
|
|
12566
|
+
tear: "lumps",
|
|
12567
|
+
tearJoules: 20
|
|
12568
|
+
}
|
|
12569
|
+
}
|
|
12511
12570
|
},
|
|
12512
12571
|
{
|
|
12513
12572
|
id: "glass",
|
|
@@ -12621,6 +12680,17 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12621
12680
|
damageScale: {
|
|
12622
12681
|
explosive: 1.2
|
|
12623
12682
|
},
|
|
12683
|
+
hits: {
|
|
12684
|
+
ballistic: {
|
|
12685
|
+
unitJoules: 500,
|
|
12686
|
+
floor: 900,
|
|
12687
|
+
crater: true
|
|
12688
|
+
},
|
|
12689
|
+
blunt: {
|
|
12690
|
+
unitJoules: 1500,
|
|
12691
|
+
floor: 60
|
|
12692
|
+
}
|
|
12693
|
+
},
|
|
12624
12694
|
hitDebris: "chips",
|
|
12625
12695
|
breakDebris: "dust",
|
|
12626
12696
|
thermal: {
|
|
@@ -12989,7 +13059,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12989
13059
|
smoke: "white"
|
|
12990
13060
|
},
|
|
12991
13061
|
visual: {
|
|
12992
|
-
charColor: 1709072
|
|
13062
|
+
charColor: 1709072,
|
|
13063
|
+
innerColor: 14207126
|
|
12993
13064
|
},
|
|
12994
13065
|
tags: [
|
|
12995
13066
|
"plant",
|
|
@@ -12997,7 +13068,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12997
13068
|
"organic",
|
|
12998
13069
|
"flammable"
|
|
12999
13070
|
],
|
|
13000
|
-
compressive: 6e4
|
|
13071
|
+
compressive: 6e4,
|
|
13072
|
+
hits: {
|
|
13073
|
+
blunt: {
|
|
13074
|
+
tear: "none"
|
|
13075
|
+
}
|
|
13076
|
+
}
|
|
13001
13077
|
},
|
|
13002
13078
|
{
|
|
13003
13079
|
id: "cactus",
|
|
@@ -13112,6 +13188,130 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13112
13188
|
"masonry"
|
|
13113
13189
|
],
|
|
13114
13190
|
compressive: 2e4
|
|
13191
|
+
},
|
|
13192
|
+
{
|
|
13193
|
+
id: "asphalt",
|
|
13194
|
+
density: 2350,
|
|
13195
|
+
hpPerKg: 10,
|
|
13196
|
+
penetration: 14,
|
|
13197
|
+
penetrationClass: "concrete",
|
|
13198
|
+
penetrationScale: 0.8,
|
|
13199
|
+
ductility: 0.1,
|
|
13200
|
+
fracture: "crumble",
|
|
13201
|
+
fragmentSize: [
|
|
13202
|
+
0.03,
|
|
13203
|
+
0.15
|
|
13204
|
+
],
|
|
13205
|
+
hitDebris: "chips",
|
|
13206
|
+
breakDebris: "dust",
|
|
13207
|
+
thermal: {
|
|
13208
|
+
conductivity: 0.1,
|
|
13209
|
+
heatCapacity: 2,
|
|
13210
|
+
ignitionC: 400,
|
|
13211
|
+
burnRate: 2e-3,
|
|
13212
|
+
smoke: "black"
|
|
13213
|
+
},
|
|
13214
|
+
tags: [
|
|
13215
|
+
"masonry",
|
|
13216
|
+
"brittle"
|
|
13217
|
+
],
|
|
13218
|
+
compressive: 3e4
|
|
13219
|
+
},
|
|
13220
|
+
{
|
|
13221
|
+
id: "sand",
|
|
13222
|
+
density: 1600,
|
|
13223
|
+
hpPerKg: 2,
|
|
13224
|
+
penetration: 6,
|
|
13225
|
+
ductility: 0.4,
|
|
13226
|
+
fracture: "crumble",
|
|
13227
|
+
fragmentSize: [
|
|
13228
|
+
0.01,
|
|
13229
|
+
0.04
|
|
13230
|
+
],
|
|
13231
|
+
hitDebris: "dust",
|
|
13232
|
+
breakDebris: "dust",
|
|
13233
|
+
thermal: {
|
|
13234
|
+
conductivity: 0.1,
|
|
13235
|
+
heatCapacity: 2
|
|
13236
|
+
},
|
|
13237
|
+
tags: [
|
|
13238
|
+
"soil"
|
|
13239
|
+
],
|
|
13240
|
+
compressive: 1e4
|
|
13241
|
+
},
|
|
13242
|
+
{
|
|
13243
|
+
id: "gravel",
|
|
13244
|
+
density: 1800,
|
|
13245
|
+
hpPerKg: 3,
|
|
13246
|
+
penetration: 8,
|
|
13247
|
+
ductility: 0.3,
|
|
13248
|
+
fracture: "crumble",
|
|
13249
|
+
fragmentSize: [
|
|
13250
|
+
0.02,
|
|
13251
|
+
0.06
|
|
13252
|
+
],
|
|
13253
|
+
hitDebris: "chips",
|
|
13254
|
+
breakDebris: "dust",
|
|
13255
|
+
thermal: {
|
|
13256
|
+
conductivity: 0.1,
|
|
13257
|
+
heatCapacity: 2
|
|
13258
|
+
},
|
|
13259
|
+
tags: [
|
|
13260
|
+
"soil",
|
|
13261
|
+
"masonry"
|
|
13262
|
+
],
|
|
13263
|
+
compressive: 2e4
|
|
13264
|
+
},
|
|
13265
|
+
{
|
|
13266
|
+
id: "turf",
|
|
13267
|
+
density: 900,
|
|
13268
|
+
hpPerKg: 3,
|
|
13269
|
+
penetration: 4,
|
|
13270
|
+
ductility: 0.5,
|
|
13271
|
+
fracture: "crumble",
|
|
13272
|
+
fragmentSize: [
|
|
13273
|
+
0.03,
|
|
13274
|
+
0.1
|
|
13275
|
+
],
|
|
13276
|
+
hitDebris: "dust",
|
|
13277
|
+
breakDebris: "dust",
|
|
13278
|
+
thermal: {
|
|
13279
|
+
conductivity: 0.1,
|
|
13280
|
+
heatCapacity: 1.5,
|
|
13281
|
+
ignitionC: 250,
|
|
13282
|
+
burnRate: 0.03,
|
|
13283
|
+
smoke: "white"
|
|
13284
|
+
},
|
|
13285
|
+
tags: [
|
|
13286
|
+
"soil",
|
|
13287
|
+
"organic"
|
|
13288
|
+
],
|
|
13289
|
+
compressive: 15e3
|
|
13290
|
+
},
|
|
13291
|
+
{
|
|
13292
|
+
id: "mortar",
|
|
13293
|
+
density: 1900,
|
|
13294
|
+
hpPerKg: 10,
|
|
13295
|
+
penetration: 12,
|
|
13296
|
+
penetrationClass: "concrete",
|
|
13297
|
+
penetrationScale: 1.2,
|
|
13298
|
+
ductility: 0.03,
|
|
13299
|
+
fracture: "crumble",
|
|
13300
|
+
fragmentSize: [
|
|
13301
|
+
0.02,
|
|
13302
|
+
0.08
|
|
13303
|
+
],
|
|
13304
|
+
hitDebris: "chips",
|
|
13305
|
+
breakDebris: "dust",
|
|
13306
|
+
thermal: {
|
|
13307
|
+
conductivity: 0.15,
|
|
13308
|
+
heatCapacity: 2.5
|
|
13309
|
+
},
|
|
13310
|
+
tags: [
|
|
13311
|
+
"masonry",
|
|
13312
|
+
"brittle"
|
|
13313
|
+
],
|
|
13314
|
+
compressive: 1e5
|
|
13115
13315
|
}
|
|
13116
13316
|
];
|
|
13117
13317
|
var material_hints_default = {
|
|
@@ -13285,6 +13485,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13285
13485
|
return (1 + 4 * (0.3 - ductility) / 0.3) * Math.min(25, Math.max(1, (0.02 / Math.max(thickness, 1e-4)) ** 2));
|
|
13286
13486
|
};
|
|
13287
13487
|
var THIN_SHEET_DISHING = 5e-3;
|
|
13488
|
+
var SWEEP_BACK = 0.01;
|
|
13489
|
+
function sweepOrigins(box3, d, spacing) {
|
|
13490
|
+
const origins = [];
|
|
13491
|
+
const line = (from, to) => {
|
|
13492
|
+
const n = Math.max(1, Math.ceil((to - from) / spacing));
|
|
13493
|
+
return Array.from({ length: n + 1 }, (_, i) => from + (to - from) * i / n);
|
|
13494
|
+
};
|
|
13495
|
+
for (const axis of [0, 1, 2]) {
|
|
13496
|
+
if (Math.abs(d[axis]) < 1e-3) continue;
|
|
13497
|
+
const lead = d[axis] > 0 ? box3.max[axis] - SWEEP_BACK : box3.min[axis] + SWEEP_BACK;
|
|
13498
|
+
const u = (axis + 1) % 3;
|
|
13499
|
+
const v = (axis + 2) % 3;
|
|
13500
|
+
for (const a of line(box3.min[u], box3.max[u])) {
|
|
13501
|
+
for (const b of line(box3.min[v], box3.max[v])) {
|
|
13502
|
+
const point = [0, 0, 0];
|
|
13503
|
+
point[axis] = lead;
|
|
13504
|
+
point[u] = a;
|
|
13505
|
+
point[v] = b;
|
|
13506
|
+
origins.push(point);
|
|
13507
|
+
}
|
|
13508
|
+
}
|
|
13509
|
+
}
|
|
13510
|
+
return origins;
|
|
13511
|
+
}
|
|
13512
|
+
var THIN_SHEET_CRUSH = 5e-4;
|
|
13513
|
+
var THIN_SHEET_CONTACT = 0.025;
|
|
13288
13514
|
var DMG_PER_J = 0.02;
|
|
13289
13515
|
var BLAST_COUPLING = 0.1;
|
|
13290
13516
|
var SHELTER_DEFAULTS = { k: 2800, brokenTransmission: 0.5, minPath: 5e-3 };
|
|
@@ -13301,11 +13527,14 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13301
13527
|
var SEVER_BAND = 0.1;
|
|
13302
13528
|
var SEVER_ASPECT = 3;
|
|
13303
13529
|
var SEVER_BALLISTIC = 2;
|
|
13530
|
+
var TEAR_JOULES = 150;
|
|
13531
|
+
var tears = 0;
|
|
13304
13532
|
var SUPPORT_TOLERANCE = 0.05;
|
|
13305
13533
|
var UNIT_FRAGMENT_FACTOR = 4;
|
|
13306
13534
|
var BREACH_GRID_CAP = 4096;
|
|
13307
13535
|
var CHIP_ENERGY = 200;
|
|
13308
13536
|
var CHIP_SPLASH = 0.5;
|
|
13537
|
+
var BAR_BOW = 0.08;
|
|
13309
13538
|
var CUT_SLICE = 1;
|
|
13310
13539
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
13311
13540
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -13315,8 +13544,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13315
13544
|
return v === void 0 ? void 0 : v * (mat.penetrationScale ?? 1);
|
|
13316
13545
|
};
|
|
13317
13546
|
var capacityScale = (p) => {
|
|
13318
|
-
|
|
13319
|
-
|
|
13547
|
+
const residual = p.spec.reinforcement?.residualCapacity ?? RESIDUAL_CAPACITY;
|
|
13548
|
+
if (p.st.stripped) return residual;
|
|
13549
|
+
const kept = Math.min(p.rt.removed ? 1 - p.rt.removed / p.mass0 : 1, 1 - sectionLoss(p));
|
|
13550
|
+
return Math.max(p.spec.reinforcement ? residual : 0.05, kept);
|
|
13551
|
+
};
|
|
13552
|
+
var sectionLoss = (p) => {
|
|
13553
|
+
const cells = p.st.chipped;
|
|
13554
|
+
if (!cells?.length) return 0;
|
|
13555
|
+
const [u, v] = planeAxes(p.thin);
|
|
13556
|
+
const along = p.dims[u] >= p.dims[v] ? u : v;
|
|
13557
|
+
const across = along === u ? v : u;
|
|
13558
|
+
let worst = 0;
|
|
13559
|
+
for (const c of cells) {
|
|
13560
|
+
let gone = 0;
|
|
13561
|
+
for (const d of cells) if (Math.abs(d.center[along] - c.center[along]) < d.size[along] / 2 + 1e-6) gone += d.size[across];
|
|
13562
|
+
worst = Math.max(worst, gone / Math.max(p.dims[across], 1e-6));
|
|
13563
|
+
}
|
|
13564
|
+
return Math.min(1, worst);
|
|
13320
13565
|
};
|
|
13321
13566
|
var isLost = (p) => p.st.status === "broken" || p.st.status === "melted" || p.st.status === "ashes";
|
|
13322
13567
|
var clone = (v) => structuredClone(v);
|
|
@@ -14497,6 +14742,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14497
14742
|
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
14498
14743
|
setStage(o, p, 1, {}, src);
|
|
14499
14744
|
};
|
|
14745
|
+
const crater = (o, p, wp, normal, radius, src) => {
|
|
14746
|
+
markDamaged(p);
|
|
14747
|
+
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: -1, point: wp, area: Math.PI * radius * radius });
|
|
14748
|
+
debris(o, p, p.mat.breakDebris, wp, normal, 6, 1.5, src);
|
|
14749
|
+
setStage(o, p, 1, {}, src);
|
|
14750
|
+
};
|
|
14500
14751
|
const insideOpening = (o, p, objectPoint) => {
|
|
14501
14752
|
const op = p.st.opening;
|
|
14502
14753
|
if (!op) return false;
|
|
@@ -14573,9 +14824,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14573
14824
|
const lp = toLocal(p.pos, p.q, objectPoint);
|
|
14574
14825
|
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);
|
|
14575
14826
|
};
|
|
14576
|
-
const chipUnits = (o, p, wp,
|
|
14827
|
+
const chipUnits = (o, p, kind, wp, hitEnergy, dir, radius, src) => {
|
|
14577
14828
|
var _a, _b;
|
|
14578
|
-
if (!p.spec.units ||
|
|
14829
|
+
if (!p.spec.units || !p.staged || chipEnergy <= 0 || p.st.stripped || isLost(p) || (p.st.stage ?? 0) >= 2) return;
|
|
14830
|
+
const rule = p.mat.hits?.[kind];
|
|
14831
|
+
const perKg = rule?.unitJoules ?? chipEnergy;
|
|
14832
|
+
const energy = hitEnergy - (rule?.floor ?? 0);
|
|
14833
|
+
if (perKg <= 0 || energy <= 0) return;
|
|
14579
14834
|
const [u, v] = planeAxes(p.thin);
|
|
14580
14835
|
const lp = toLocal(p.pos, p.q, objLocal(o, wp));
|
|
14581
14836
|
const grid = gridOf(p);
|
|
@@ -14589,7 +14844,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14589
14844
|
const share = gap === 0 ? 1 : CHIP_SPLASH * (1 - gap / radius);
|
|
14590
14845
|
const total = (worn[i] ?? 0) + energy * share;
|
|
14591
14846
|
const mass = p.mat.density * cell.size[0] * cell.size[1] * cell.size[2];
|
|
14592
|
-
if (total <
|
|
14847
|
+
if (total < perKg * mass) {
|
|
14593
14848
|
worn[i] = total;
|
|
14594
14849
|
continue;
|
|
14595
14850
|
}
|
|
@@ -14614,7 +14869,32 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14614
14869
|
emit({ ...base(o, src), type: "chip", part: p.spec.id, center, size: [cell.size[0], cell.size[1], cell.size[2]], rotation: q, fragments });
|
|
14615
14870
|
debris(o, p, p.mat.breakDebris, center, vec.scale(dir ?? [0, 1, 0], -1), 6, 2, src);
|
|
14616
14871
|
}
|
|
14872
|
+
if (p.spec.reinforcement) exposeRebar(o, p, src);
|
|
14617
14873
|
for (const cell of out) addBreach(o, p, toWorld(c, q, cell.center), cell.size[u] * cell.size[v], energy, dir, src);
|
|
14874
|
+
if (p.structural && !isLost(p)) {
|
|
14875
|
+
solveStructure(o);
|
|
14876
|
+
collapseCheck(o, src);
|
|
14877
|
+
}
|
|
14878
|
+
};
|
|
14879
|
+
const exposeRebar = (o, p, src) => {
|
|
14880
|
+
const r = p.spec.reinforcement;
|
|
14881
|
+
if (!r) return;
|
|
14882
|
+
const [u, v] = planeAxes(p.thin);
|
|
14883
|
+
const all = rebarBars(p.dims, p.thin, r.spacing);
|
|
14884
|
+
let bars;
|
|
14885
|
+
if (p.st.stripped) bars = all;
|
|
14886
|
+
else {
|
|
14887
|
+
bars = [];
|
|
14888
|
+
for (const cell of p.st.chipped ?? []) bars.push(...clipBarsToBox(all, u, v, cell.center, cell.size));
|
|
14889
|
+
const op = p.st.opening;
|
|
14890
|
+
if (op) {
|
|
14891
|
+
const lc = toLocal(p.pos, p.q, op.center);
|
|
14892
|
+
bars.push(...clipBarsToDisc(all, u, v, [lc[u], lc[v]], op.radius + 0.05));
|
|
14893
|
+
}
|
|
14894
|
+
}
|
|
14895
|
+
const bow = clamp2(((p.st.stress ?? 0) - 1) / 0.5, 0, 1);
|
|
14896
|
+
if (bow > 0) bars = bars.flatMap((bar2, i) => bendBar(bar2, p.thin, bow * BAR_BOW, i));
|
|
14897
|
+
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
14618
14898
|
};
|
|
14619
14899
|
const openBreach = (o, p, center, total, energy, dir, src) => {
|
|
14620
14900
|
const [u, v] = planeAxes(p.thin);
|
|
@@ -14642,10 +14922,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14642
14922
|
setStage(o, p, 2, { opening: { center: worldCenter, radius }, fragments }, src);
|
|
14643
14923
|
debris(o, p, p.mat.breakDebris, worldCenter, vec.scale(dir ?? [0, 1, 0], -1), 14, 3, src);
|
|
14644
14924
|
const reinforcement = p.spec.reinforcement;
|
|
14645
|
-
if (reinforcement)
|
|
14646
|
-
const bars = worldBars(o, p, clipBarsToDisc(rebarBars(p.dims, p.thin, reinforcement.spacing), u, v, [lc[u], lc[v]], radius + 0.05));
|
|
14647
|
-
if (bars.length > 0) emit({ ...base(o, src), type: "expose", part: p.spec.id, bars });
|
|
14648
|
-
}
|
|
14925
|
+
if (reinforcement) exposeRebar(o, p, src);
|
|
14649
14926
|
if (p.structural) solveStructure(o);
|
|
14650
14927
|
};
|
|
14651
14928
|
const stripPart = (o, p, reinforcement, src) => {
|
|
@@ -14657,8 +14934,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14657
14934
|
p.st.burning = false;
|
|
14658
14935
|
p.rt.jet = false;
|
|
14659
14936
|
markDamaged(p);
|
|
14660
|
-
emit({ ...base(o, src), type: "expose", part: p.spec.id, bars: worldBars(o, p, bars) });
|
|
14661
14937
|
solveStructure(o);
|
|
14938
|
+
exposeRebar(o, p, src);
|
|
14662
14939
|
collapseCheck(o, src);
|
|
14663
14940
|
};
|
|
14664
14941
|
const unitFragmentPlan = (p, cap) => {
|
|
@@ -14712,7 +14989,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14712
14989
|
const grain = p.spec.grain ? AXIS[p.spec.grain] : void 0;
|
|
14713
14990
|
const hitLocal = toLocal(c, q, hitW);
|
|
14714
14991
|
const unitPlan = p.spec.units ? unitFragmentPlan(p, maxFragments * UNIT_FRAGMENT_FACTOR) : [];
|
|
14715
|
-
const axis = style === "splinter" ? severAxis(p) : -1;
|
|
14992
|
+
const axis = style === "splinter" || style === "snap" ? severAxis(p) : -1;
|
|
14716
14993
|
const cutAt2 = axis < 0 ? void 0 : p.rt.cutAt ?? (origin ? hitLocal[axis] : void 0);
|
|
14717
14994
|
const plan = unitPlan.length > 0 ? unitPlan : planFragments({
|
|
14718
14995
|
style,
|
|
@@ -14784,7 +15061,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14784
15061
|
};
|
|
14785
15062
|
const CHAIN_TRIGGER_OF = { ballistic: "shot", explosive: "explosion" };
|
|
14786
15063
|
const severAxis = (p) => {
|
|
14787
|
-
if (p.mat.fracture !== "splinter" || p.spec.shape.kind !== "box") return -1;
|
|
15064
|
+
if (p.mat.fracture !== "splinter" && p.mat.fracture !== "snap" || p.spec.shape.kind !== "box") return -1;
|
|
14788
15065
|
const d = p.dims;
|
|
14789
15066
|
const order = [0, 1, 2].sort((a, b) => d[b] - d[a]);
|
|
14790
15067
|
return d[order[0]] >= SEVER_ASPECT * d[order[1]] && d[order[0]] >= 3 * SEVER_BAND ? order[0] : -1;
|
|
@@ -14845,6 +15122,49 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14845
15122
|
}
|
|
14846
15123
|
}
|
|
14847
15124
|
};
|
|
15125
|
+
const tearOf = (mat, kind) => {
|
|
15126
|
+
const h = mat.hits?.[kind];
|
|
15127
|
+
const tear = h?.tear ?? (kind === "ballistic" && mat.penetrationClass === "wood" ? "splinters" : "none");
|
|
15128
|
+
return { tear, joules: h?.tearJoules ?? TEAR_JOULES };
|
|
15129
|
+
};
|
|
15130
|
+
const tearAt = (o, p, kind, entry2, exit, d, E, scale, src) => {
|
|
15131
|
+
const { tear, joules } = tearOf(p.mat, kind);
|
|
15132
|
+
if (tear === "none") return;
|
|
15133
|
+
const shot = kind === "ballistic";
|
|
15134
|
+
const n = shot ? exit ? clamp2(Math.round(E / joules), 1, 7) : 1 : clamp2(Math.floor(E / joules), 0, 5);
|
|
15135
|
+
if (n === 0) return;
|
|
15136
|
+
const fibre = tear === "lumps";
|
|
15137
|
+
const { q } = partXf(o, p);
|
|
15138
|
+
const along = (p.spec.grain ? AXIS[p.spec.grain] : void 0) ?? [0, 1, 2].sort((a, b) => p.dims[b] - p.dims[a])[0];
|
|
15139
|
+
const fragments = [];
|
|
15140
|
+
for (let i = 0; i < n; i++) {
|
|
15141
|
+
const back = !exit || i % 3 === 2;
|
|
15142
|
+
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());
|
|
15143
|
+
const size = [w, w, w];
|
|
15144
|
+
size[along] = len;
|
|
15145
|
+
size[(along + 1) % 3] = w;
|
|
15146
|
+
size[(along + 2) % 3] = t;
|
|
15147
|
+
const spread3 = [(rnd() - 0.5) * 0.8, (rnd() - 0.2) * 0.8, (rnd() - 0.5) * 0.8];
|
|
15148
|
+
const speed = shot ? back ? 1.5 + 2 * rnd() : 2 + 4 * rnd() : 0.8 + 1.2 * rnd();
|
|
15149
|
+
const v = vec.scale(vec.normalize(vec.add(back ? vec.scale(d, -1) : d, spread3)), speed);
|
|
15150
|
+
const mass = size[0] * size[1] * size[2] * p.mat.density;
|
|
15151
|
+
fragments.push({
|
|
15152
|
+
id: `${p.spec.id}#t${tears++}-${i}`,
|
|
15153
|
+
part: p.spec.id,
|
|
15154
|
+
material: p.spec.material,
|
|
15155
|
+
shape: fibre ? "chunk" : "splinter",
|
|
15156
|
+
center: back || !exit ? vec.madd(entry2, d, -0.01) : vec.madd(exit, d, 0.01),
|
|
15157
|
+
size,
|
|
15158
|
+
rotation: q,
|
|
15159
|
+
velocity: v,
|
|
15160
|
+
inner: true,
|
|
15161
|
+
angularVelocity: [(rnd() - 0.5) * 30, (rnd() - 0.5) * 30, (rnd() - 0.5) * 30],
|
|
15162
|
+
mass
|
|
15163
|
+
});
|
|
15164
|
+
}
|
|
15165
|
+
const patch = [scale * 4, scale * 4, scale * 4];
|
|
15166
|
+
emit({ ...base(o, src), type: "chip", part: p.spec.id, center: exit ?? entry2, size: patch, rotation: q, fragments });
|
|
15167
|
+
};
|
|
14848
15168
|
const ballistic = (o, p, entry2, n, d, E, caliber, pathLen, src, round3 = {}) => {
|
|
14849
15169
|
const mat = p.mat;
|
|
14850
15170
|
const piercing = round3.piercing ?? 1;
|
|
@@ -14873,6 +15193,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14873
15193
|
return { penetrated: false, exitEnergy: 0, damage: dmg2 };
|
|
14874
15194
|
}
|
|
14875
15195
|
if (p.layers.length > 0) spallOuter(o, p, entry2, face, n, Math.max(0.03, caliber / 1e3 * 4), src);
|
|
15196
|
+
else if (mat.hits?.ballistic?.crater) crater(o, p, entry2, n, clamp2(0.02 + Math.sqrt(E) * 15e-4, 0.02, 0.15), src);
|
|
14876
15197
|
const brittle = mat.ductility < 0.3;
|
|
14877
15198
|
const ratio = Ep / Math.max(thr, 1e-6);
|
|
14878
15199
|
const crackLen = clamp2(0.03 + ratio * 0.2, 0.03, 0.8);
|
|
@@ -14906,8 +15227,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14906
15227
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
14907
15228
|
debris(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
14908
15229
|
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
14909
|
-
const
|
|
14910
|
-
|
|
15230
|
+
const absorbed = penetrated && mat.ductility >= 0.5 && mat.fracture !== "tear" ? Math.min(E, thr) : E;
|
|
15231
|
+
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
15232
|
+
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
15233
|
+
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);
|
|
14911
15234
|
return { penetrated, exitEnergy: penetrated ? penMm !== void 0 ? E * (1 - thr / Ep) : E - thr / (hard ? piercing : 1) : 0, exitPoint, damage: dmg };
|
|
14912
15235
|
};
|
|
14913
15236
|
const melee = (o, p, kind, E, point, n, d, contactR, src) => {
|
|
@@ -14915,14 +15238,20 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14915
15238
|
const thrMm = mat.penetration * p.thickness * 1e3;
|
|
14916
15239
|
const fragile = kind === "blunt" ? fragility(mat.ductility, p.thickness) : 1;
|
|
14917
15240
|
const Ec = E * fragile;
|
|
15241
|
+
const sheet = mat.ductility >= 0.5 && p.thickness <= THIN_SHEET_CRUSH;
|
|
15242
|
+
const fold = sheet ? Math.min(4, Math.sqrt(1e-3 / p.thickness)) : 1;
|
|
15243
|
+
const patch = sheet ? Math.min(contactR, THIN_SHEET_CONTACT) : contactR;
|
|
14918
15244
|
if (E > 0) {
|
|
14919
15245
|
if (kind === "blunt") {
|
|
14920
|
-
if (mat.ductility >= 0.5) addDent(o, p, point, n, Math.max(
|
|
15246
|
+
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);
|
|
14921
15247
|
else if (mat.ductility < 0.3) {
|
|
14922
15248
|
if (Ec >= 5) addCrack(o, p, point, n, clamp2(0.04 + Math.min(Ec, E * 6) * 8e-4, 0.04, 0.6), src);
|
|
14923
15249
|
} 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);
|
|
14924
15250
|
} else if (mat.fracture === "tear") {
|
|
14925
15251
|
addHole(o, p, point, n, clamp2(0.02 + E * 5e-4, 0.02, 0.3), true, void 0, src);
|
|
15252
|
+
} else if (mat.ductility >= 0.3 && E >= thrMm) {
|
|
15253
|
+
addHole(o, p, point, n, clamp2(contactR, 0.01, 0.08), true, void 0, src);
|
|
15254
|
+
if (sheet) addDent(o, p, point, n, Math.max(patch * 0.6, 0.01), clamp2(E * 4e-5 * fold, 5e-4, 0.02), src);
|
|
14926
15255
|
} else if (mat.ductility >= 0.5) {
|
|
14927
15256
|
addDent(o, p, point, n, Math.max(contactR, 0.01), clamp2(E * 4e-5, 5e-4, 0.02), src);
|
|
14928
15257
|
}
|
|
@@ -14935,7 +15264,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14935
15264
|
const scale = mat.damageScale?.[kind] ?? 1;
|
|
14936
15265
|
const done = applyDamage(o, p, Ec * DMG_PER_J * scale * (kind === "slash" ? p.cutFactor : 1), kind, point, d, E * 0.1, 12, src);
|
|
14937
15266
|
addBreach(o, p, point, breachAreaOf(p, done, false), E, d, src);
|
|
14938
|
-
if (kind === "blunt") chipUnits(o, p, point, E, d, Math.max(contactR, 0.03), src);
|
|
15267
|
+
if (kind === "blunt") chipUnits(o, p, "blunt", point, E, d, Math.max(contactR, 0.03), src);
|
|
15268
|
+
if (E > 0 && !isLost(p)) tearAt(o, p, kind, point, void 0, d, E, clamp2(contactR * 0.2, 6e-3, 0.02), src);
|
|
14939
15269
|
return done;
|
|
14940
15270
|
};
|
|
14941
15271
|
const blastPart = (o, p, point, energy, radius, src, res, reachAt) => {
|
|
@@ -15514,6 +15844,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15514
15844
|
if (p.rt.stressT <= 0) {
|
|
15515
15845
|
p.rt.stressT = 0.5;
|
|
15516
15846
|
emit({ ...base(o), type: "stress", part: p.spec.id, stress, load: p.st.load ?? 0 });
|
|
15847
|
+
if (p.spec.reinforcement && (p.st.stripped || p.st.chipped?.length || p.st.opening)) exposeRebar(o, p);
|
|
15517
15848
|
}
|
|
15518
15849
|
applyDamage(o, p, overloadRate(stress) * p.st.maxHp * dt, "blunt", null, null, 5, 3);
|
|
15519
15850
|
} else if (stress > 0.8 && p.mat.ductility < 0.3 && p.rt.stressT <= 0 && p.st.cracks.length < 6) {
|
|
@@ -15730,6 +16061,18 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15730
16061
|
const r = raycastInternal(origin, direction, maxDistance);
|
|
15731
16062
|
return r ? { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: r.distance } : null;
|
|
15732
16063
|
},
|
|
16064
|
+
sweepBox(box3, direction, distance2, options2 = {}) {
|
|
16065
|
+
const d = vec.normalize(direction);
|
|
16066
|
+
const ignored = options2.ignore === void 0 ? void 0 : objects.get(options2.ignore);
|
|
16067
|
+
const ignore = ignored ? new Set(ignored.parts.map((p) => `${ignored.id}\0${p.spec.id}`)) : void 0;
|
|
16068
|
+
let best = null;
|
|
16069
|
+
for (const origin of sweepOrigins(box3, d, options2.spacing ?? 0.2)) {
|
|
16070
|
+
const r = raycastInternal(origin, d, distance2 + SWEEP_BACK, ignore);
|
|
16071
|
+
if (!r || best && r.distance >= best.distance) continue;
|
|
16072
|
+
best = { object: r.object, part: r.part, point: r.point, normal: r.normal, distance: Math.max(0, r.distance - SWEEP_BACK) };
|
|
16073
|
+
}
|
|
16074
|
+
return best;
|
|
16075
|
+
},
|
|
15733
16076
|
shoot(origin, direction, energy, caliber, source, round3) {
|
|
15734
16077
|
const extras = typeof round3 === "number" ? { piercing: round3 } : round3 ?? {};
|
|
15735
16078
|
return withCollapses(run(() => {
|
|
@@ -18295,7 +18638,24 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
18295
18638
|
}
|
|
18296
18639
|
return spec;
|
|
18297
18640
|
};
|
|
18298
|
-
var
|
|
18641
|
+
var MEMBER_CAGE = { spacing: 0.1, diameter: 8e-3, unit: 0.1 };
|
|
18642
|
+
var CAGED_ROLES = /* @__PURE__ */ new Set(["beam", "column"]);
|
|
18643
|
+
var caged = (spec, part, rebar) => {
|
|
18644
|
+
if (!rebar || part.material !== "concrete" || !CAGED_ROLES.has(part.role) || spec.reinforcement) return spec;
|
|
18645
|
+
const size = spec.shape.kind === "box" ? spec.shape.size : null;
|
|
18646
|
+
if (!size) return spec;
|
|
18647
|
+
const thin = Math.min(...size);
|
|
18648
|
+
return {
|
|
18649
|
+
...spec,
|
|
18650
|
+
reinforcement: { material: rebar, spacing: MEMBER_CAGE.spacing, diameter: MEMBER_CAGE.diameter },
|
|
18651
|
+
units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, thin], bond: "stack", mortar: "concrete" }
|
|
18652
|
+
};
|
|
18653
|
+
};
|
|
18654
|
+
var partSpecOf = (part, capacity, detailed, rebar) => {
|
|
18655
|
+
if (detailed && part.assembly) return assemblySpec(part, capacity);
|
|
18656
|
+
const spec = boxSpec(part, capacity);
|
|
18657
|
+
return detailed ? caged(spec, part, rebar) : spec;
|
|
18658
|
+
};
|
|
18299
18659
|
function toDestructibleSpec(structure, options = {}) {
|
|
18300
18660
|
const { safety = 0.65, redundancy = 0.97, safetyOthers = 0.45 } = options;
|
|
18301
18661
|
const kept = structure.parts.filter((part) => !options.filter || options.filter(part));
|
|
@@ -18312,7 +18672,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
18312
18672
|
const sizing = { safety, redundancy, safetyOthers };
|
|
18313
18673
|
const sized = { ...panels, capacities: options.sized === false ? /* @__PURE__ */ new Map() : sizeCapacities(panels.parts, panels.supports, sizing) };
|
|
18314
18674
|
const { parts, supports, capacities } = detail === "units" ? applyUnits(sized, structure) : sized;
|
|
18315
|
-
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse"));
|
|
18675
|
+
const specs = parts.map((part) => partSpecOf(part, capacities.get(part.id), detail !== "coarse", structure.construction.frame.rebar));
|
|
18316
18676
|
const margin = options.terrainMargin ?? TERRAIN_MARGIN;
|
|
18317
18677
|
const x0 = Math.min(...parts.map((part) => part.aabb[0]));
|
|
18318
18678
|
const z0 = Math.min(...parts.map((part) => part.aabb[1]));
|
|
@@ -19192,7 +19552,11 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19192
19552
|
if (!pc || pc.unit) return sp;
|
|
19193
19553
|
if (pc.role !== "wall" && pc.role !== "partition") {
|
|
19194
19554
|
const def = getAssembly(pc.assembly || (pc.material === "concrete" && ["slab", "beam", "column", "stair"].includes(pc.role) ? "reinforced_concrete" : ""));
|
|
19195
|
-
|
|
19555
|
+
const out = def && def.reinforcement && !sp.reinforcement ? { ...sp, reinforcement: { ...def.reinforcement } } : sp;
|
|
19556
|
+
if (out.reinforcement && !out.units && pc.material === "concrete" && (pc.role === "beam" || pc.role === "column") && sp.shape.kind === "box") {
|
|
19557
|
+
return { ...out, units: { size: [MEMBER_CAGE.unit, MEMBER_CAGE.unit, Math.min(...sp.shape.size)], bond: "stack", mortar: "concrete" } };
|
|
19558
|
+
}
|
|
19559
|
+
return out;
|
|
19196
19560
|
}
|
|
19197
19561
|
if (!pc.assembly) return sp;
|
|
19198
19562
|
const [u0, v0, u1, v1] = pc.aabb, alongU = u1 - u0 >= v1 - v0;
|
|
@@ -19288,29 +19652,44 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19288
19652
|
}
|
|
19289
19653
|
return list2;
|
|
19290
19654
|
}
|
|
19655
|
+
const specOf = (id) => opts.spec()?.parts?.find((q) => q.id === id);
|
|
19656
|
+
const same = (a, b) => a.every((x, k) => Math.abs(x - b[k]) < 1e-3);
|
|
19657
|
+
function hostOf(partId) {
|
|
19658
|
+
if (structure.meshesOf(partId).some((it) => it.mesh)) return partId;
|
|
19659
|
+
const part = structure.byId.get(partId);
|
|
19660
|
+
if (!part) return null;
|
|
19661
|
+
for (const q of structure.parts) {
|
|
19662
|
+
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;
|
|
19663
|
+
}
|
|
19664
|
+
return null;
|
|
19665
|
+
}
|
|
19666
|
+
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);
|
|
19291
19667
|
function draw2(partId, change) {
|
|
19292
19668
|
if (lod !== 0) return false;
|
|
19293
|
-
const
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
let view = views.get(partId);
|
|
19669
|
+
const hostId = hostOf(partId);
|
|
19670
|
+
if (!hostId || !specOf(partId)?.units) return false;
|
|
19671
|
+
let view = views.get(hostId);
|
|
19297
19672
|
if (!view) {
|
|
19298
|
-
const items = structure.meshesOf(
|
|
19673
|
+
const items = structure.meshesOf(hostId).filter((it) => it.mesh);
|
|
19299
19674
|
if (!items.length) return false;
|
|
19300
|
-
|
|
19675
|
+
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
19676
|
+
view = { mesh: null, saved: null, items, paint: wallColour(items), units: 0, opening: null, chips: new Map(parts.map((id) => [id, []])), openings: /* @__PURE__ */ new Map() };
|
|
19301
19677
|
}
|
|
19678
|
+
if (!view.chips.has(partId)) return false;
|
|
19302
19679
|
if (change.opening) {
|
|
19303
|
-
if (view.
|
|
19304
|
-
view.
|
|
19680
|
+
if (view.openings.has(partId)) return false;
|
|
19681
|
+
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
19682
|
+
if (partId === hostId) view.opening = view.openings.get(partId);
|
|
19305
19683
|
}
|
|
19306
|
-
if (change.chip) view.chips.push(local(change.chip));
|
|
19307
|
-
const list2 =
|
|
19684
|
+
if (change.chip) view.chips.get(partId).push(local(change.chip));
|
|
19685
|
+
const list2 = [];
|
|
19686
|
+
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));
|
|
19308
19687
|
if (view.mesh) {
|
|
19309
19688
|
view.mesh.removeFromParent();
|
|
19310
19689
|
view.mesh.dispose();
|
|
19311
19690
|
}
|
|
19312
19691
|
const mesh2 = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
19313
|
-
mesh2.name = "breach:" +
|
|
19692
|
+
mesh2.name = "breach:" + hostId;
|
|
19314
19693
|
list2.forEach((u, i) => {
|
|
19315
19694
|
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
19316
19695
|
mesh2.setMatrixAt(i, tmp.m);
|
|
@@ -19325,7 +19704,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19325
19704
|
if (!view.saved) view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
19326
19705
|
view.mesh = mesh2;
|
|
19327
19706
|
view.units = list2.length;
|
|
19328
|
-
views.set(
|
|
19707
|
+
views.set(hostId, view);
|
|
19329
19708
|
invalidate();
|
|
19330
19709
|
invalidateShadow();
|
|
19331
19710
|
return true;
|
|
@@ -21495,6 +21874,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21495
21874
|
return {
|
|
21496
21875
|
id: FLOORPLAN_AREA_ID,
|
|
21497
21876
|
name: text.name,
|
|
21877
|
+
ground: { surface: "lawn" },
|
|
21498
21878
|
group: text.group,
|
|
21499
21879
|
get houses() {
|
|
21500
21880
|
return placed;
|
|
@@ -22779,6 +23159,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22779
23159
|
|
|
22780
23160
|
// src/furniture/spec.ts
|
|
22781
23161
|
var WOOD_THIN = 0.035;
|
|
23162
|
+
var BOARD_TOP_THICK = 0.07;
|
|
23163
|
+
var BOARD_TOP_WIDTH = 0.5;
|
|
23164
|
+
var BOARD_WIDTH = 0.16;
|
|
22782
23165
|
var SHELF_SPAN = 1.5;
|
|
22783
23166
|
var SHELVING = /shelf|bookcase|rack/;
|
|
22784
23167
|
function carcass(parts, drawn) {
|
|
@@ -22793,6 +23176,39 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22793
23176
|
} else if (y <= x && y <= z) part.structure = { role: "beam", maxSpan: SHELF_SPAN * Math.max(x, z) };
|
|
22794
23177
|
}
|
|
22795
23178
|
}
|
|
23179
|
+
function splitTopsIntoBoards(drawn, materialOf2, carcassed) {
|
|
23180
|
+
const tops = /* @__PURE__ */ new Map();
|
|
23181
|
+
if (carcassed) return tops;
|
|
23182
|
+
for (let i = drawn.length - 1; i >= 0; i--) {
|
|
23183
|
+
const p = drawn[i];
|
|
23184
|
+
if (p.key !== "wood" || materialOf2.wood !== "wood_hard" || p.shape.kind !== "box" || p.rotation) continue;
|
|
23185
|
+
const [sx, sy, sz] = p.shape.size;
|
|
23186
|
+
const across = sx < sz ? 0 : 2;
|
|
23187
|
+
const width = Math.min(sx, sz);
|
|
23188
|
+
if (sy < WOOD_THIN || sy > BOARD_TOP_THICK || sy >= width || width < BOARD_TOP_WIDTH) continue;
|
|
23189
|
+
const count = Math.round(width / BOARD_WIDTH);
|
|
23190
|
+
const boards = [];
|
|
23191
|
+
for (let b = 0; b < count; b++) {
|
|
23192
|
+
const size = [sx, sy, sz];
|
|
23193
|
+
size[across] = width / count;
|
|
23194
|
+
const position = [...p.position];
|
|
23195
|
+
position[across] += (b + 0.5) * size[across] - width / 2;
|
|
23196
|
+
const min = [...p.min];
|
|
23197
|
+
const max = [...p.max];
|
|
23198
|
+
min[across] = position[across] - size[across] / 2;
|
|
23199
|
+
max[across] = position[across] + size[across] / 2;
|
|
23200
|
+
boards.push({ ...p, id: `${p.id}_b${b}`, shape: { kind: "box", size }, position, min, max, color: shadeBoard(p.color, b) });
|
|
23201
|
+
}
|
|
23202
|
+
drawn.splice(i, 1, ...boards);
|
|
23203
|
+
tops.set(p.id, boards);
|
|
23204
|
+
}
|
|
23205
|
+
return tops;
|
|
23206
|
+
}
|
|
23207
|
+
function shadeBoard(color2, board) {
|
|
23208
|
+
const k = [1, 0.94, 1.05, 0.97, 1.03][board % 5];
|
|
23209
|
+
const channel = (shift) => Math.min(255, Math.round((color2 >> shift & 255) * k)) << shift;
|
|
23210
|
+
return channel(16) | channel(8) | channel(0);
|
|
23211
|
+
}
|
|
22796
23212
|
var FLOOR_TOUCH = 5e-3;
|
|
22797
23213
|
var LOOSE = /* @__PURE__ */ new Set(["paper"]);
|
|
22798
23214
|
var BASE_MATERIAL = {
|
|
@@ -22870,10 +23286,12 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22870
23286
|
};
|
|
22871
23287
|
run(A);
|
|
22872
23288
|
if (!drawn.length) throw new Error(`game-floorplan-three: the builder of "${name}" drew no box or cylinder`);
|
|
23289
|
+
const boardsOf = splitTopsIntoBoards(drawn, materialOf2, Boolean(opts.carcass));
|
|
22873
23290
|
const parts = drawn.map((p) => {
|
|
22874
23291
|
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]);
|
|
22875
23292
|
let material = materialOf2[p.key] ?? "plastic";
|
|
22876
|
-
if (p.key === "wood" && material === "wood_hard" &&
|
|
23293
|
+
if (p.key === "wood" && material === "wood_hard" && opts.carcass && !opts.materials?.wood) material = "mdf";
|
|
23294
|
+
else if (p.key === "wood" && material === "wood_hard" && thin < WOOD_THIN) material = "wood_soft";
|
|
22877
23295
|
const part = { id: p.id, material, shape: p.shape, position: p.position };
|
|
22878
23296
|
if (p.rotation) part.rotation = p.rotation;
|
|
22879
23297
|
if (!LOOSE.has(material) && p.min[1] <= FLOOR_TOUCH) part.anchored = true;
|
|
@@ -22908,6 +23326,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
22908
23326
|
if (area3 < 0.6 * footprint || p.max[1] < 0.2) continue;
|
|
22909
23327
|
if (!top || p.max[1] > top.y + 1e-6) top = { part: p.id, y: p.max[1] };
|
|
22910
23328
|
}
|
|
23329
|
+
for (const boards of boardsOf.values()) {
|
|
23330
|
+
if (top && boards[0].max[1] <= top.y - 1e-6) continue;
|
|
23331
|
+
const mid = boards[boards.length >> 1];
|
|
23332
|
+
const min = [Math.min(...boards.map((b) => b.min[0])), Math.min(...boards.map((b) => b.min[2]))];
|
|
23333
|
+
const max = [Math.max(...boards.map((b) => b.max[0])), Math.max(...boards.map((b) => b.max[2]))];
|
|
23334
|
+
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) };
|
|
23335
|
+
}
|
|
22911
23336
|
if (opts.carcass) carcass(parts, byId);
|
|
22912
23337
|
const spec = { name, parts, joints, tags: ["furniture"] };
|
|
22913
23338
|
const colors = new Map(drawn.map((p) => [p.id, p.color]));
|