@adatechnology/game-items-three 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.global.js +310 -73
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.global.js
CHANGED
|
@@ -2640,8 +2640,8 @@ var GameItemsThree = (() => {
|
|
|
2640
2640
|
bedHeight = roadY * 1e3 + z * 14 - (x - params.roadSlopeAnchorX) * 5;
|
|
2641
2641
|
roughness = 0.55;
|
|
2642
2642
|
}
|
|
2643
|
-
const
|
|
2644
|
-
bed[index] = bedHeight + (
|
|
2643
|
+
const noise3 = valueNoise(column * 0.35, row * 0.35) * 0.6 + valueNoise(column * 1.3, row * 1.3) * 0.4;
|
|
2644
|
+
bed[index] = bedHeight + (noise3 - 0.5) * roughness * 2;
|
|
2645
2645
|
vertical[index] = region === 0 || region === 2 ? 0.95 : 1;
|
|
2646
2646
|
pin[index] = 0.62 + 0.76 * (valueNoise(column * 0.21 + 40, row * 0.21) * 0.5 + hashCell(column, row) * 0.5) * (region === 3 ? 1.25 : 1);
|
|
2647
2647
|
}
|
|
@@ -2812,6 +2812,55 @@ var GameItemsThree = (() => {
|
|
|
2812
2812
|
ends: 22e-5
|
|
2813
2813
|
}
|
|
2814
2814
|
}
|
|
2815
|
+
},
|
|
2816
|
+
plastic_bottle: {
|
|
2817
|
+
models: [
|
|
2818
|
+
"water_bottle",
|
|
2819
|
+
"water_jug_5l",
|
|
2820
|
+
"dirty_water",
|
|
2821
|
+
"sports_drink",
|
|
2822
|
+
"coconut_water",
|
|
2823
|
+
"cooking_oil",
|
|
2824
|
+
"alcohol_70",
|
|
2825
|
+
"fuel_jerrycan"
|
|
2826
|
+
],
|
|
2827
|
+
rule: {
|
|
2828
|
+
hollow: true,
|
|
2829
|
+
welded: true,
|
|
2830
|
+
skinTags: [
|
|
2831
|
+
"paper",
|
|
2832
|
+
"food"
|
|
2833
|
+
],
|
|
2834
|
+
materials: {
|
|
2835
|
+
glass: "plastic"
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
},
|
|
2839
|
+
glass_bottle: {
|
|
2840
|
+
models: [
|
|
2841
|
+
"cachaca",
|
|
2842
|
+
"communion_wine",
|
|
2843
|
+
"honey_jar",
|
|
2844
|
+
"home_preserves"
|
|
2845
|
+
],
|
|
2846
|
+
rule: {
|
|
2847
|
+
hollow: true,
|
|
2848
|
+
welded: true,
|
|
2849
|
+
skinTags: [
|
|
2850
|
+
"paper",
|
|
2851
|
+
"food"
|
|
2852
|
+
]
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
drink_carton: {
|
|
2856
|
+
models: [
|
|
2857
|
+
"juice_box",
|
|
2858
|
+
"milk_carton"
|
|
2859
|
+
],
|
|
2860
|
+
rule: {
|
|
2861
|
+
hollow: true,
|
|
2862
|
+
welded: true
|
|
2863
|
+
}
|
|
2815
2864
|
}
|
|
2816
2865
|
},
|
|
2817
2866
|
contents: {
|
|
@@ -2827,6 +2876,67 @@ var GameItemsThree = (() => {
|
|
|
2827
2876
|
liquid: "soda",
|
|
2828
2877
|
fill: 0.95
|
|
2829
2878
|
},
|
|
2879
|
+
water_bottle: {
|
|
2880
|
+
liquid: "water",
|
|
2881
|
+
fill: 0.95
|
|
2882
|
+
},
|
|
2883
|
+
water_jug_5l: {
|
|
2884
|
+
liquid: "water",
|
|
2885
|
+
fill: 0.95,
|
|
2886
|
+
box: true
|
|
2887
|
+
},
|
|
2888
|
+
dirty_water: {
|
|
2889
|
+
liquid: "water",
|
|
2890
|
+
fill: 0.9
|
|
2891
|
+
},
|
|
2892
|
+
sports_drink: {
|
|
2893
|
+
liquid: "juice",
|
|
2894
|
+
fill: 0.95
|
|
2895
|
+
},
|
|
2896
|
+
coconut_water: {
|
|
2897
|
+
liquid: "water",
|
|
2898
|
+
fill: 0.9,
|
|
2899
|
+
box: true
|
|
2900
|
+
},
|
|
2901
|
+
juice_box: {
|
|
2902
|
+
liquid: "juice",
|
|
2903
|
+
fill: 0.95,
|
|
2904
|
+
box: true
|
|
2905
|
+
},
|
|
2906
|
+
milk_carton: {
|
|
2907
|
+
liquid: "milk",
|
|
2908
|
+
fill: 0.95,
|
|
2909
|
+
box: true
|
|
2910
|
+
},
|
|
2911
|
+
cachaca: {
|
|
2912
|
+
liquid: "spirits",
|
|
2913
|
+
fill: 0.9
|
|
2914
|
+
},
|
|
2915
|
+
communion_wine: {
|
|
2916
|
+
liquid: "wine",
|
|
2917
|
+
fill: 0.9
|
|
2918
|
+
},
|
|
2919
|
+
cooking_oil: {
|
|
2920
|
+
liquid: "cooking_oil",
|
|
2921
|
+
fill: 0.9
|
|
2922
|
+
},
|
|
2923
|
+
alcohol_70: {
|
|
2924
|
+
liquid: "spirits",
|
|
2925
|
+
fill: 0.9
|
|
2926
|
+
},
|
|
2927
|
+
honey_jar: {
|
|
2928
|
+
liquid: "honey",
|
|
2929
|
+
fill: 0.85
|
|
2930
|
+
},
|
|
2931
|
+
home_preserves: {
|
|
2932
|
+
liquid: "water",
|
|
2933
|
+
fill: 0.5
|
|
2934
|
+
},
|
|
2935
|
+
fuel_jerrycan: {
|
|
2936
|
+
liquid: "fuel",
|
|
2937
|
+
fill: 0.85,
|
|
2938
|
+
box: true
|
|
2939
|
+
},
|
|
2830
2940
|
canned_beans: {
|
|
2831
2941
|
liquid: "juice",
|
|
2832
2942
|
fill: 0.4
|
|
@@ -14207,6 +14317,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14207
14317
|
},
|
|
14208
14318
|
hitDebris: "chips",
|
|
14209
14319
|
breakDebris: "dust",
|
|
14320
|
+
visual: { innerColor: 10197652 },
|
|
14210
14321
|
thermal: {
|
|
14211
14322
|
conductivity: 0.15,
|
|
14212
14323
|
heatCapacity: 2.5
|
|
@@ -14237,6 +14348,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14237
14348
|
],
|
|
14238
14349
|
hitDebris: "chips",
|
|
14239
14350
|
breakDebris: "dust",
|
|
14351
|
+
visual: { innerColor: 12085567 },
|
|
14240
14352
|
thermal: {
|
|
14241
14353
|
conductivity: 0.15,
|
|
14242
14354
|
heatCapacity: 2.5
|
|
@@ -14267,6 +14379,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14267
14379
|
],
|
|
14268
14380
|
hitDebris: "dust",
|
|
14269
14381
|
breakDebris: "dust",
|
|
14382
|
+
visual: { innerColor: 14999507 },
|
|
14270
14383
|
thermal: {
|
|
14271
14384
|
conductivity: 0.1,
|
|
14272
14385
|
heatCapacity: 1.5
|
|
@@ -14292,6 +14405,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14292
14405
|
],
|
|
14293
14406
|
hitDebris: "chips",
|
|
14294
14407
|
breakDebris: "dust",
|
|
14408
|
+
visual: { innerColor: 9407364 },
|
|
14295
14409
|
thermal: {
|
|
14296
14410
|
conductivity: 0.2,
|
|
14297
14411
|
heatCapacity: 2.5
|
|
@@ -15049,6 +15163,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15049
15163
|
var CHIP_ENERGY = 200;
|
|
15050
15164
|
var CHIP_SPLASH = 0.5;
|
|
15051
15165
|
var BAR_BOW = 0.08;
|
|
15166
|
+
var BULLET_JOINT_SPAN = 0.3;
|
|
15052
15167
|
var CUT_SLICE = 1;
|
|
15053
15168
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
15054
15169
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -16175,7 +16290,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16175
16290
|
emit({ ...base(o, src), type: "crack", part: p.spec.id, crack, worldPoint: wp });
|
|
16176
16291
|
setStage(o, p, 1, {}, src);
|
|
16177
16292
|
};
|
|
16178
|
-
const debris = (o, p, kind, point, normal, count, speed, src) => emit({ ...base(o, src), type: "debris", part: p.spec.id, kind, point, normal, count, speed });
|
|
16293
|
+
const debris = (o, p, kind, point, normal, count, speed, src, material) => emit({ ...base(o, src), type: "debris", part: p.spec.id, kind, point, normal, count, speed, ...material && material !== p.spec.material ? { material } : {} });
|
|
16179
16294
|
const holeRate = (hole, pressurized) => (pressurized ? 0.5 : 0.1) * (hole.diameter / 9e-3);
|
|
16180
16295
|
const detachEvents = (o, opened, by, src) => {
|
|
16181
16296
|
for (const j of opened) emit({ ...base(o, src), type: "detach", joint: j.spec.id, parts: [j.spec.a, j.spec.b], by, groups: o.st.groups.map((g) => [...g]) });
|
|
@@ -16216,7 +16331,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16216
16331
|
const small = pa.dims[0] * pa.dims[1] * pa.dims[2] <= pb.dims[0] * pb.dims[1] * pb.dims[2] ? pa : pb;
|
|
16217
16332
|
return Math.max(0.06, 0.5 * Math.min(small.dims[0], small.dims[1], small.dims[2]));
|
|
16218
16333
|
};
|
|
16219
|
-
const
|
|
16334
|
+
const jointLength = (o, j) => {
|
|
16335
|
+
const A = partWorldAabb(o, o.parts[j.ia]), B = partWorldAabb(o, o.parts[j.ib]);
|
|
16336
|
+
let len = 0;
|
|
16337
|
+
for (let k = 0; k < 3; k++) len = Math.max(len, Math.min(A.max[k], B.max[k]) - Math.max(A.min[k], B.min[k]));
|
|
16338
|
+
return len;
|
|
16339
|
+
};
|
|
16340
|
+
const wearJoints = (o, p, amount, near, radius = 0.06, src, split = false, span) => {
|
|
16220
16341
|
if (amount <= 0) return;
|
|
16221
16342
|
const mine = (o.byPart.get(p.spec.id) ?? []).filter((j) => !j.st.open);
|
|
16222
16343
|
if (split && mine.length) amount /= mine.length;
|
|
@@ -16224,7 +16345,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16224
16345
|
if (j.st.open) continue;
|
|
16225
16346
|
if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
|
|
16226
16347
|
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
16227
|
-
j.st.hp -= amount;
|
|
16348
|
+
j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
16228
16349
|
if (j.st.hp <= 0) {
|
|
16229
16350
|
j.st.hp = 0;
|
|
16230
16351
|
j.st.open = true;
|
|
@@ -16253,7 +16374,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16253
16374
|
if (spalls.length > MAX_SPALLS) spalls.shift();
|
|
16254
16375
|
markDamaged(p);
|
|
16255
16376
|
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: layer.index, point: wp, area: Math.PI * radius * radius });
|
|
16256
|
-
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
16377
|
+
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src, layer.mat.id);
|
|
16257
16378
|
setStage(o, p, 1, {}, src);
|
|
16258
16379
|
};
|
|
16259
16380
|
const crater = (o, p, wp, normal, radius, src) => {
|
|
@@ -16740,7 +16861,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16740
16861
|
if (mat.fracture !== "shatter") addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16741
16862
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
16742
16863
|
debris(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
16743
|
-
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
16864
|
+
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
16744
16865
|
const absorbed = penetrated && mat.ductility >= 0.5 && mat.fracture !== "tear" ? Math.min(E, thr) : E;
|
|
16745
16866
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
16746
16867
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
@@ -27196,6 +27317,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27196
27317
|
pos.array.set(saved, v0 * 3);
|
|
27197
27318
|
pos.needsUpdate = true;
|
|
27198
27319
|
}
|
|
27320
|
+
function noise2(x, y, cell) {
|
|
27321
|
+
const h = (i2, j2) => {
|
|
27322
|
+
const s = Math.sin(i2 * 127.1 + j2 * 311.7) * 43758.5453;
|
|
27323
|
+
return s - Math.floor(s);
|
|
27324
|
+
};
|
|
27325
|
+
const fx = x / cell, fy = y / cell, i = Math.floor(fx), j = Math.floor(fy), u = fx - i, v = fy - j;
|
|
27326
|
+
const su = u * u * (3 - 2 * u), sv = v * v * (3 - 2 * v);
|
|
27327
|
+
return (h(i, j) * (1 - su) + h(i + 1, j) * su) * (1 - sv) + (h(i, j + 1) * (1 - su) + h(i + 1, j + 1) * su) * sv;
|
|
27328
|
+
}
|
|
27329
|
+
var MASK_TEXEL = 0.02;
|
|
27330
|
+
var SPALL_MIN = 0.025;
|
|
27331
|
+
var SPALL_RAGGED = 0.045;
|
|
27332
|
+
var COVER2 = 0.015;
|
|
27199
27333
|
function installDestructionBreach(K, _shared) {
|
|
27200
27334
|
const { THREE, fragmentMaterial, invalidate, invalidateShadow } = K;
|
|
27201
27335
|
function createBreachView(opts) {
|
|
@@ -27205,55 +27339,29 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27205
27339
|
const unitMaterial = new THREE.MeshLambertMaterial({ color: 16777215 });
|
|
27206
27340
|
const unitGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
27207
27341
|
const tmp = { m: new THREE.Matrix4(), q: new THREE.Quaternion(), p: new THREE.Vector3(), s: new THREE.Vector3(), c: new THREE.Color() };
|
|
27208
|
-
const wallColour = (items) => {
|
|
27209
|
-
for (const it of items) {
|
|
27210
|
-
if (!it.mesh) continue;
|
|
27211
|
-
const c = it.mesh.geometry.attributes.color;
|
|
27212
|
-
if (!c) continue;
|
|
27213
|
-
const a = c.array;
|
|
27214
|
-
tmp.c.setRGB(a[it.v0 * 3], a[it.v0 * 3 + 1], a[it.v0 * 3 + 2]);
|
|
27215
|
-
return tmp.c.getHex();
|
|
27216
|
-
}
|
|
27217
|
-
return null;
|
|
27218
|
-
};
|
|
27219
27342
|
const local = (p) => {
|
|
27220
27343
|
house.updateMatrixWorld(true);
|
|
27221
27344
|
const v = house.worldToLocal(tmp.p.set(p[0], p[1], p[2]));
|
|
27222
27345
|
return [v.x, v.y, v.z];
|
|
27223
27346
|
};
|
|
27224
|
-
function
|
|
27347
|
+
function cells(part, units) {
|
|
27225
27348
|
const [u0, v0, u1, v1] = part.aabb;
|
|
27226
27349
|
const alongU = u1 - u0 >= v1 - v0;
|
|
27227
27350
|
const len = alongU ? u1 - u0 : v1 - v0;
|
|
27228
|
-
const thick = alongU ? v1 - v0 : u1 - u0;
|
|
27229
27351
|
const [ul, uh] = units.size;
|
|
27230
27352
|
const rows = Math.max(1, Math.round((part.y1 - part.y0) / uh));
|
|
27231
27353
|
const rowH = (part.y1 - part.y0) / rows;
|
|
27232
27354
|
const per = Math.max(1, Math.round(len / ul));
|
|
27233
27355
|
const w = len / per;
|
|
27234
|
-
const
|
|
27235
|
-
const list2 = [];
|
|
27356
|
+
const out = [];
|
|
27236
27357
|
for (let r = 0; r < rows; r++) {
|
|
27237
27358
|
const shift = units.bond === "running" && r & 1 ? w / 2 : 0;
|
|
27238
|
-
|
|
27239
|
-
|
|
27240
|
-
|
|
27241
|
-
a = Math.max(0, a);
|
|
27242
|
-
b = Math.min(len, b);
|
|
27243
|
-
if (b - a < 1e-3) continue;
|
|
27244
|
-
const mid = (a + b) / 2;
|
|
27245
|
-
const x = alongU ? u0 + mid : (u0 + u1) / 2;
|
|
27246
|
-
const z = alongU ? (v0 + v1) / 2 : v0 + mid;
|
|
27247
|
-
const y = part.y0 + (r + 0.5) * rowH;
|
|
27248
|
-
const d = opening ? Math.hypot(x - opening.center[0], y - opening.center[1], z - opening.center[2]) : Infinity;
|
|
27249
|
-
if (opening && d < opening.radius) continue;
|
|
27250
|
-
const gone = (c) => Math.abs(c[1] - y) <= rowH / 2 && (alongU ? c[0] >= u0 + a && c[0] <= u0 + b : c[2] >= v0 + a && c[2] <= v0 + b);
|
|
27251
|
-
if (chips.some(gone)) continue;
|
|
27252
|
-
const near = opening && d < opening.radius + 0.25 || chips.some((c) => Math.hypot(c[0] - x, c[1] - y, c[2] - z) < 0.15);
|
|
27253
|
-
list2.push({ x, y, z, sx: alongU ? b - a : thick, sy: rowH, sz: alongU ? thick : b - a, colour: near || paint === null ? edge : paint });
|
|
27359
|
+
for (let i = 0; i < per + (shift ? 1 : 0); i++) {
|
|
27360
|
+
const a = Math.max(0, i * w - shift), b = Math.min(len, i * w - shift + w);
|
|
27361
|
+
if (b - a > 1e-3) out.push({ a, b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
|
|
27254
27362
|
}
|
|
27255
27363
|
}
|
|
27256
|
-
return
|
|
27364
|
+
return out;
|
|
27257
27365
|
}
|
|
27258
27366
|
const specOf = (id) => opts.spec()?.parts?.find((q) => q.id === id);
|
|
27259
27367
|
const same = (a, b) => a.every((x, k) => Math.abs(x - b[k]) < 1e-3);
|
|
@@ -27267,6 +27375,137 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27267
27375
|
return null;
|
|
27268
27376
|
}
|
|
27269
27377
|
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);
|
|
27378
|
+
function shell(it, mask) {
|
|
27379
|
+
const g0 = it.mesh.geometry, g = new THREE.BufferGeometry();
|
|
27380
|
+
const src = it.mesh.userData._p0 || g0.attributes.position.array;
|
|
27381
|
+
for (const [name, attr] of Object.entries(g0.attributes)) {
|
|
27382
|
+
const k = attr.itemSize;
|
|
27383
|
+
g.setAttribute(name, new THREE.BufferAttribute((name === "position" ? src : attr.array).slice(it.v0 * k, it.v1 * k), k));
|
|
27384
|
+
}
|
|
27385
|
+
if (g0.index) {
|
|
27386
|
+
const ix = g0.index.array.slice(it.i0, it.i1);
|
|
27387
|
+
for (let k = 0; k < ix.length; k++) ix[k] -= it.v0;
|
|
27388
|
+
g.setIndex(new THREE.BufferAttribute(ix, 1));
|
|
27389
|
+
}
|
|
27390
|
+
g.computeBoundingBox();
|
|
27391
|
+
g.computeBoundingSphere();
|
|
27392
|
+
const base = it.mesh.material;
|
|
27393
|
+
const material = base.clone();
|
|
27394
|
+
const toHouse = { value: new THREE.Matrix4() };
|
|
27395
|
+
material.onBeforeCompile = (sh) => {
|
|
27396
|
+
base.onBeforeCompile?.(sh);
|
|
27397
|
+
sh.uniforms.uToHouse = toHouse;
|
|
27398
|
+
sh.uniforms.uMask = { value: mask.texture };
|
|
27399
|
+
sh.uniforms.uMaskRect = { value: mask.rect };
|
|
27400
|
+
sh.uniforms.uMaskAlongX = { value: mask.alongU ? 1 : 0 };
|
|
27401
|
+
sh.vertexShader = sh.vertexShader.replace("void main() {", "uniform mat4 uToHouse;\nvarying vec3 vHousePos;\nvoid main() {").replace("#include <begin_vertex>", "#include <begin_vertex>\nvHousePos = (uToHouse * vec4(transformed, 1.0)).xyz;");
|
|
27402
|
+
sh.fragmentShader = sh.fragmentShader.replace("void main() {", "uniform sampler2D uMask;\nuniform vec4 uMaskRect;\nuniform float uMaskAlongX;\nvarying vec3 vHousePos;\nvoid main() {\n vec2 mUv = vec2(((uMaskAlongX > 0.5 ? vHousePos.x : vHousePos.z) - uMaskRect.x) / uMaskRect.z, (vHousePos.y - uMaskRect.y) / uMaskRect.w);\n if (mUv.x >= 0.0 && mUv.x <= 1.0 && mUv.y >= 0.0 && mUv.y <= 1.0 && texture2D(uMask, mUv).r > 0.5) discard;");
|
|
27403
|
+
};
|
|
27404
|
+
material.customProgramCacheKey = () => "breachMask" + (base.customProgramCacheKey?.() ?? "");
|
|
27405
|
+
const mesh2 = new THREE.Mesh(g, material);
|
|
27406
|
+
mesh2.name = "breach-shell";
|
|
27407
|
+
mesh2.userData.breachMask = mask;
|
|
27408
|
+
mesh2.position.copy(it.mesh.position);
|
|
27409
|
+
mesh2.quaternion.copy(it.mesh.quaternion);
|
|
27410
|
+
mesh2.scale.copy(it.mesh.scale);
|
|
27411
|
+
mesh2.castShadow = it.mesh.castShadow;
|
|
27412
|
+
mesh2.receiveShadow = it.mesh.receiveShadow;
|
|
27413
|
+
mesh2.renderOrder = it.mesh.renderOrder;
|
|
27414
|
+
mesh2.onBeforeRender = () => {
|
|
27415
|
+
house.updateMatrixWorld();
|
|
27416
|
+
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh2.matrixWorld);
|
|
27417
|
+
};
|
|
27418
|
+
it.mesh.parent.add(mesh2);
|
|
27419
|
+
mesh2.updateMatrixWorld(true);
|
|
27420
|
+
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh2.matrixWorld);
|
|
27421
|
+
return mesh2;
|
|
27422
|
+
}
|
|
27423
|
+
function maskOf(hostId, parts) {
|
|
27424
|
+
const host = structure.byId.get(hostId);
|
|
27425
|
+
const [u0, v0, u1, v1] = host.aabb;
|
|
27426
|
+
const alongU = u1 - u0 >= v1 - v0;
|
|
27427
|
+
const y0 = Math.min(...parts.map((id) => structure.byId.get(id).y0)), y1 = Math.max(...parts.map((id) => structure.byId.get(id).y1));
|
|
27428
|
+
const len = alongU ? u1 - u0 : v1 - v0;
|
|
27429
|
+
const w = Math.max(2, Math.min(2048, Math.ceil(len / MASK_TEXEL))), h = Math.max(2, Math.min(1024, Math.ceil((y1 - y0) / MASK_TEXEL)));
|
|
27430
|
+
const data = new Uint8Array(w * h);
|
|
27431
|
+
const texture = new THREE.DataTexture(data, w, h, THREE.RedFormat);
|
|
27432
|
+
texture.magFilter = THREE.LinearFilter;
|
|
27433
|
+
texture.minFilter = THREE.LinearFilter;
|
|
27434
|
+
texture.needsUpdate = true;
|
|
27435
|
+
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) };
|
|
27436
|
+
}
|
|
27437
|
+
const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
|
|
27438
|
+
function paint(view) {
|
|
27439
|
+
const { mask } = view;
|
|
27440
|
+
const along = (p) => (mask.alongU ? p[0] : p[2]) - mask.start;
|
|
27441
|
+
const holes = [];
|
|
27442
|
+
const discs = [];
|
|
27443
|
+
const shown = [];
|
|
27444
|
+
for (const id of view.parts) {
|
|
27445
|
+
const part = structure.byId.get(id), units2 = specOf(id).units, chips = view.chips.get(id), opening = view.openings.get(id);
|
|
27446
|
+
const all = cells(part, units2);
|
|
27447
|
+
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
27448
|
+
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
27449
|
+
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
27450
|
+
const gone = (c) => chips.some((p) => p[1] >= c.y0 && p[1] <= c.y1 && along(p) >= c.a && along(p) <= c.b) || opening && Math.hypot((c.a + c.b) / 2 - along(opening.center), (c.y0 + c.y1) / 2 - opening.center[1]) < opening.radius;
|
|
27451
|
+
const kept = [];
|
|
27452
|
+
for (const c of all) (gone(c) ? holes : kept).push(c);
|
|
27453
|
+
for (const c of kept) shown.push({ c, part, units: units2, thick, mid });
|
|
27454
|
+
}
|
|
27455
|
+
const reach = SPALL_MIN + SPALL_RAGGED;
|
|
27456
|
+
mask.data.fill(0);
|
|
27457
|
+
const stamp = (s0, s1, y0, y1, inside2) => {
|
|
27458
|
+
const i0 = Math.max(0, Math.floor((s0 - reach) / mask.len * mask.w)), i1 = Math.min(mask.w - 1, Math.ceil((s1 + reach) / mask.len * mask.w));
|
|
27459
|
+
const j0 = Math.max(0, Math.floor((y0 - reach - mask.y0) / mask.height * mask.h)), j1 = Math.min(mask.h - 1, Math.ceil((y1 + reach - mask.y0) / mask.height * mask.h));
|
|
27460
|
+
for (let j = j0; j <= j1; j++) {
|
|
27461
|
+
const y = mask.y0 + (j + 0.5) / mask.h * mask.height;
|
|
27462
|
+
for (let i = i0; i <= i1; i++) {
|
|
27463
|
+
const s = (i + 0.5) / mask.w * mask.len;
|
|
27464
|
+
const edge = SPALL_MIN + SPALL_RAGGED * (0.6 * noise2(s, y, 0.03) + 0.4 * noise2(s + 7.3, y - 3.1, 0.011));
|
|
27465
|
+
if (inside2(s, y) <= edge) mask.data[j * mask.w + i] = 255;
|
|
27466
|
+
}
|
|
27467
|
+
}
|
|
27468
|
+
};
|
|
27469
|
+
for (const c of holes) stamp(c.a, c.b, c.y0, c.y1, (s, y) => boxDistance(s, y, c.a, c.b, c.y0, c.y1));
|
|
27470
|
+
for (const d of discs) stamp(d.s - d.r, d.s + d.r, d.y - d.r, d.y + d.r, (s, y) => Math.max(0, Math.hypot(s - d.s, y - d.y) - d.r));
|
|
27471
|
+
mask.texture.needsUpdate = true;
|
|
27472
|
+
const near = (c) => holes.some((h) => boxDistance((c.a + c.b) / 2, (c.y0 + c.y1) / 2, h.a - (c.b - c.a) / 2, h.b + (c.b - c.a) / 2, h.y0 - (c.y1 - c.y0) / 2, h.y1 + (c.y1 - c.y0) / 2) <= reach) || discs.some((d) => boxDistance(d.s, d.y, c.a, c.b, c.y0, c.y1) <= d.r + reach);
|
|
27473
|
+
const list2 = [];
|
|
27474
|
+
const tone = (hex3, k) => tmp.c.setHex(hex3).multiplyScalar(k).getHex();
|
|
27475
|
+
for (const { c, part, units: units2, thick, mid } of shown) {
|
|
27476
|
+
if (!near(c)) continue;
|
|
27477
|
+
const centre = mask.start + (c.a + c.b) / 2, y = (c.y0 + c.y1) / 2, len = c.b - c.a, h = c.y1 - c.y0;
|
|
27478
|
+
const at = (s, dy, d, colour) => list2.push({ x: mask.alongU ? centre : mid, y, z: mask.alongU ? mid : centre, sx: mask.alongU ? s : d, sy: dy, sz: mask.alongU ? d : s, colour });
|
|
27479
|
+
const base = fragmentMaterial(part.material).color.getHex();
|
|
27480
|
+
const n = noise2(centre * 3.1, y * 2.7, 0.07);
|
|
27481
|
+
if (units2.mortar === "concrete") {
|
|
27482
|
+
at(len, h, thick - 2 * COVER2 - 0.03 * n, tone(base, 0.86 + 0.2 * n));
|
|
27483
|
+
} else {
|
|
27484
|
+
at(len, h, thick - 2 * COVER2 - 0.012, 10327948);
|
|
27485
|
+
at(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
|
|
27486
|
+
}
|
|
27487
|
+
}
|
|
27488
|
+
if (view.units) {
|
|
27489
|
+
view.units.removeFromParent();
|
|
27490
|
+
view.units.dispose();
|
|
27491
|
+
}
|
|
27492
|
+
const units = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
27493
|
+
units.name = "breach:" + view.host;
|
|
27494
|
+
list2.forEach((u, i) => {
|
|
27495
|
+
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
27496
|
+
units.setMatrixAt(i, tmp.m);
|
|
27497
|
+
units.setColorAt(i, tmp.c.setHex(u.colour));
|
|
27498
|
+
});
|
|
27499
|
+
units.count = list2.length;
|
|
27500
|
+
units.instanceMatrix.needsUpdate = true;
|
|
27501
|
+
if (units.instanceColor) units.instanceColor.needsUpdate = true;
|
|
27502
|
+
units.castShadow = true;
|
|
27503
|
+
units.receiveShadow = true;
|
|
27504
|
+
units.visible = lod === 0;
|
|
27505
|
+
house.add(units);
|
|
27506
|
+
view.units = units;
|
|
27507
|
+
view.gone = holes.length;
|
|
27508
|
+
}
|
|
27270
27509
|
function draw2(partId, change) {
|
|
27271
27510
|
if (lod !== 0) return false;
|
|
27272
27511
|
const hostId = hostOf(partId);
|
|
@@ -27276,37 +27515,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27276
27515
|
const items = structure.meshesOf(hostId).filter((it) => it.mesh);
|
|
27277
27516
|
if (!items.length) return false;
|
|
27278
27517
|
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
27279
|
-
|
|
27518
|
+
if (!parts.includes(partId)) return false;
|
|
27519
|
+
const mask = maskOf(hostId, parts);
|
|
27520
|
+
view = { host: hostId, parts, items, mask, shells: [], saved: null, units: null, gone: 0, chips: new Map(parts.map((id) => [id, []])), openings: /* @__PURE__ */ new Map() };
|
|
27280
27521
|
}
|
|
27281
27522
|
if (!view.chips.has(partId)) return false;
|
|
27282
27523
|
if (change.opening) {
|
|
27283
27524
|
if (view.openings.has(partId)) return false;
|
|
27284
27525
|
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
27285
|
-
if (partId === hostId) view.opening = view.openings.get(partId);
|
|
27286
27526
|
}
|
|
27287
27527
|
if (change.chip) view.chips.get(partId).push(local(change.chip));
|
|
27288
|
-
|
|
27289
|
-
|
|
27290
|
-
|
|
27291
|
-
view.mesh.removeFromParent();
|
|
27292
|
-
view.mesh.dispose();
|
|
27528
|
+
if (!view.saved) {
|
|
27529
|
+
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
27530
|
+
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
27293
27531
|
}
|
|
27294
|
-
|
|
27295
|
-
mesh2.name = "breach:" + hostId;
|
|
27296
|
-
list2.forEach((u, i) => {
|
|
27297
|
-
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
27298
|
-
mesh2.setMatrixAt(i, tmp.m);
|
|
27299
|
-
mesh2.setColorAt(i, tmp.c.setHex(u.colour));
|
|
27300
|
-
});
|
|
27301
|
-
mesh2.count = list2.length;
|
|
27302
|
-
mesh2.instanceMatrix.needsUpdate = true;
|
|
27303
|
-
if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
|
|
27304
|
-
mesh2.castShadow = true;
|
|
27305
|
-
mesh2.receiveShadow = true;
|
|
27306
|
-
house.add(mesh2);
|
|
27307
|
-
if (!view.saved) view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
27308
|
-
view.mesh = mesh2;
|
|
27309
|
-
view.units = list2.length;
|
|
27532
|
+
paint(view);
|
|
27310
27533
|
views.set(hostId, view);
|
|
27311
27534
|
invalidate();
|
|
27312
27535
|
invalidateShadow();
|
|
@@ -27318,30 +27541,40 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27318
27541
|
else s.data = collapseRange(s.it.mesh, s.v0, s.it.v1);
|
|
27319
27542
|
}
|
|
27320
27543
|
}
|
|
27544
|
+
function dispose(view) {
|
|
27545
|
+
view.units.removeFromParent();
|
|
27546
|
+
view.units.dispose();
|
|
27547
|
+
for (const m of view.shells) {
|
|
27548
|
+
m.removeFromParent();
|
|
27549
|
+
m.geometry.dispose();
|
|
27550
|
+
m.material.dispose();
|
|
27551
|
+
}
|
|
27552
|
+
view.mask.texture.dispose();
|
|
27553
|
+
}
|
|
27321
27554
|
return {
|
|
27322
|
-
/** Handles a `stage` event of the house's object. Returns true when the wall was
|
|
27555
|
+
/** Handles a `stage` event of the house's object. Returns true when the wall was cut. */
|
|
27323
27556
|
stage(event) {
|
|
27324
27557
|
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
27325
27558
|
},
|
|
27326
|
-
/** Handles a `chip` event: the unit that came out leaves a
|
|
27559
|
+
/** Handles a `chip` event: the unit that came out leaves a hole in the wall where it was. Returns true when the wall was cut. */
|
|
27327
27560
|
chip(event) {
|
|
27328
27561
|
return draw2(event.part, { chip: event.center });
|
|
27329
27562
|
},
|
|
27330
|
-
/** The part broke or fell:
|
|
27563
|
+
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
27331
27564
|
drop(partId) {
|
|
27332
27565
|
const v = views.get(partId);
|
|
27333
27566
|
if (!v) return;
|
|
27334
|
-
v
|
|
27335
|
-
v.mesh.dispose();
|
|
27567
|
+
dispose(v);
|
|
27336
27568
|
views.delete(partId);
|
|
27337
27569
|
},
|
|
27338
|
-
/** LOD 0 draws the
|
|
27570
|
+
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
27339
27571
|
setLod(level) {
|
|
27340
27572
|
const was = lod;
|
|
27341
27573
|
lod = level;
|
|
27342
27574
|
if (was === 0 === (level === 0)) return;
|
|
27343
27575
|
for (const v of views.values()) {
|
|
27344
|
-
v.
|
|
27576
|
+
v.units.visible = level === 0;
|
|
27577
|
+
for (const m of v.shells) m.visible = level === 0;
|
|
27345
27578
|
plain2(v, level !== 0);
|
|
27346
27579
|
}
|
|
27347
27580
|
invalidate();
|
|
@@ -27350,17 +27583,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
27350
27583
|
/** Everything back to what it was (after `world.reset`). */
|
|
27351
27584
|
restore() {
|
|
27352
27585
|
for (const v of views.values()) {
|
|
27353
|
-
v
|
|
27354
|
-
v.mesh.dispose();
|
|
27586
|
+
dispose(v);
|
|
27355
27587
|
if (lod === 0) plain2(v, true);
|
|
27356
27588
|
}
|
|
27357
27589
|
views.clear();
|
|
27358
27590
|
},
|
|
27359
|
-
/** The wall
|
|
27591
|
+
/** The wall has damage drawn. */
|
|
27360
27592
|
has(partId) {
|
|
27361
27593
|
return views.has(partId);
|
|
27362
27594
|
},
|
|
27363
|
-
|
|
27595
|
+
/** Walls with damage drawn, units out of them, and units laid bare at the edge of the cuts. */
|
|
27596
|
+
statistics: () => ({
|
|
27597
|
+
walls: views.size,
|
|
27598
|
+
gone: [...views.values()].reduce((n, v) => n + v.gone, 0),
|
|
27599
|
+
units: [...views.values()].reduce((n, v) => n + v.units.count, 0)
|
|
27600
|
+
})
|
|
27364
27601
|
};
|
|
27365
27602
|
}
|
|
27366
27603
|
Object.assign(K, { createBreachView });
|