@adatechnology/game-floorplan-three 0.6.0 → 0.7.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 +198 -71
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +182 -66
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.global.js
CHANGED
|
@@ -12693,6 +12693,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12693
12693
|
},
|
|
12694
12694
|
hitDebris: "chips",
|
|
12695
12695
|
breakDebris: "dust",
|
|
12696
|
+
visual: { innerColor: 10197652 },
|
|
12696
12697
|
thermal: {
|
|
12697
12698
|
conductivity: 0.15,
|
|
12698
12699
|
heatCapacity: 2.5
|
|
@@ -12723,6 +12724,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12723
12724
|
],
|
|
12724
12725
|
hitDebris: "chips",
|
|
12725
12726
|
breakDebris: "dust",
|
|
12727
|
+
visual: { innerColor: 12085567 },
|
|
12726
12728
|
thermal: {
|
|
12727
12729
|
conductivity: 0.15,
|
|
12728
12730
|
heatCapacity: 2.5
|
|
@@ -12753,6 +12755,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12753
12755
|
],
|
|
12754
12756
|
hitDebris: "dust",
|
|
12755
12757
|
breakDebris: "dust",
|
|
12758
|
+
visual: { innerColor: 14999507 },
|
|
12756
12759
|
thermal: {
|
|
12757
12760
|
conductivity: 0.1,
|
|
12758
12761
|
heatCapacity: 1.5
|
|
@@ -12778,6 +12781,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
12778
12781
|
],
|
|
12779
12782
|
hitDebris: "chips",
|
|
12780
12783
|
breakDebris: "dust",
|
|
12784
|
+
visual: { innerColor: 9407364 },
|
|
12781
12785
|
thermal: {
|
|
12782
12786
|
conductivity: 0.2,
|
|
12783
12787
|
heatCapacity: 2.5
|
|
@@ -13535,6 +13539,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
13535
13539
|
var CHIP_ENERGY = 200;
|
|
13536
13540
|
var CHIP_SPLASH = 0.5;
|
|
13537
13541
|
var BAR_BOW = 0.08;
|
|
13542
|
+
var BULLET_JOINT_SPAN = 0.3;
|
|
13538
13543
|
var CUT_SLICE = 1;
|
|
13539
13544
|
var cutFactorOf = (shape) => clamp2(Math.max(...shapeDims(shape)) / CUT_SLICE, 1, 50);
|
|
13540
13545
|
var clamp2 = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
@@ -14661,7 +14666,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14661
14666
|
emit({ ...base(o, src), type: "crack", part: p.spec.id, crack, worldPoint: wp });
|
|
14662
14667
|
setStage(o, p, 1, {}, src);
|
|
14663
14668
|
};
|
|
14664
|
-
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 });
|
|
14669
|
+
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 } : {} });
|
|
14665
14670
|
const holeRate = (hole, pressurized) => (pressurized ? 0.5 : 0.1) * (hole.diameter / 9e-3);
|
|
14666
14671
|
const detachEvents = (o, opened, by, src) => {
|
|
14667
14672
|
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]) });
|
|
@@ -14702,7 +14707,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14702
14707
|
const small = pa.dims[0] * pa.dims[1] * pa.dims[2] <= pb.dims[0] * pb.dims[1] * pb.dims[2] ? pa : pb;
|
|
14703
14708
|
return Math.max(0.06, 0.5 * Math.min(small.dims[0], small.dims[1], small.dims[2]));
|
|
14704
14709
|
};
|
|
14705
|
-
const
|
|
14710
|
+
const jointLength = (o, j) => {
|
|
14711
|
+
const A = partWorldAabb(o, o.parts[j.ia]), B = partWorldAabb(o, o.parts[j.ib]);
|
|
14712
|
+
let len = 0;
|
|
14713
|
+
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]));
|
|
14714
|
+
return len;
|
|
14715
|
+
};
|
|
14716
|
+
const wearJoints = (o, p, amount, near, radius = 0.06, src, split = false, span) => {
|
|
14706
14717
|
if (amount <= 0) return;
|
|
14707
14718
|
const mine = (o.byPart.get(p.spec.id) ?? []).filter((j) => !j.st.open);
|
|
14708
14719
|
if (split && mine.length) amount /= mine.length;
|
|
@@ -14710,7 +14721,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14710
14721
|
if (j.st.open) continue;
|
|
14711
14722
|
if (j.spec.kind === "rebar" && (isReinforced(o.parts[j.ia].spec) || isReinforced(o.parts[j.ib].spec))) continue;
|
|
14712
14723
|
if (near && jointReach(o, j, near) > Math.max(radius, jointReachLimit(o, j))) continue;
|
|
14713
|
-
j.st.hp -= amount;
|
|
14724
|
+
j.st.hp -= span === void 0 ? amount : amount * Math.min(1, span / Math.max(jointLength(o, j), 1e-6));
|
|
14714
14725
|
if (j.st.hp <= 0) {
|
|
14715
14726
|
j.st.hp = 0;
|
|
14716
14727
|
j.st.open = true;
|
|
@@ -14739,7 +14750,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
14739
14750
|
if (spalls.length > MAX_SPALLS) spalls.shift();
|
|
14740
14751
|
markDamaged(p);
|
|
14741
14752
|
emit({ ...base(o, src), type: "spall", part: p.spec.id, layer: layer.index, point: wp, area: Math.PI * radius * radius });
|
|
14742
|
-
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src);
|
|
14753
|
+
debris(o, p, layer.mat.hitDebris, wp, normal, 5, 2, src, layer.mat.id);
|
|
14743
14754
|
setStage(o, p, 1, {}, src);
|
|
14744
14755
|
};
|
|
14745
14756
|
const crater = (o, p, wp, normal, radius, src) => {
|
|
@@ -15226,7 +15237,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
15226
15237
|
if (mat.fracture !== "shatter") addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
15227
15238
|
} else addHole(o, p, entry2, n, caliber / 1e3, false, void 0, src);
|
|
15228
15239
|
debris(o, p, mat.hitDebris, entry2, n, clamp2(Math.round(E / 60) + 2, 2, 14), clamp2(Math.sqrt(E) / 6, 1, 8), src);
|
|
15229
|
-
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true);
|
|
15240
|
+
wearJoints(o, p, (penetrated ? thr : E) * 0.02, void 0, 0, src, true, BULLET_JOINT_SPAN);
|
|
15230
15241
|
const absorbed = penetrated && mat.ductility >= 0.5 && mat.fracture !== "tear" ? Math.min(E, thr) : E;
|
|
15231
15242
|
const dmg = applyDamage(o, p, absorbed * DMG_PER_J * scale, "ballistic", entry2, d, absorbed * 0.1, 12, src);
|
|
15232
15243
|
if (!isLost(p)) tearAt(o, p, "ballistic", entry2, penetrated ? exitPoint : void 0, d, E, caliber / 1e3, src);
|
|
@@ -19593,6 +19604,19 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19593
19604
|
pos.array.set(saved, v0 * 3);
|
|
19594
19605
|
pos.needsUpdate = true;
|
|
19595
19606
|
}
|
|
19607
|
+
function noise2(x, y, cell) {
|
|
19608
|
+
const h = (i2, j2) => {
|
|
19609
|
+
const s = Math.sin(i2 * 127.1 + j2 * 311.7) * 43758.5453;
|
|
19610
|
+
return s - Math.floor(s);
|
|
19611
|
+
};
|
|
19612
|
+
const fx = x / cell, fy = y / cell, i = Math.floor(fx), j = Math.floor(fy), u = fx - i, v = fy - j;
|
|
19613
|
+
const su = u * u * (3 - 2 * u), sv = v * v * (3 - 2 * v);
|
|
19614
|
+
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;
|
|
19615
|
+
}
|
|
19616
|
+
var MASK_TEXEL = 0.02;
|
|
19617
|
+
var SPALL_MIN = 0.025;
|
|
19618
|
+
var SPALL_RAGGED = 0.045;
|
|
19619
|
+
var COVER2 = 0.015;
|
|
19596
19620
|
function installDestructionBreach(K, _shared) {
|
|
19597
19621
|
const { THREE, fragmentMaterial, invalidate, invalidateShadow } = K;
|
|
19598
19622
|
function createBreachView(opts) {
|
|
@@ -19602,55 +19626,29 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19602
19626
|
const unitMaterial = new THREE.MeshLambertMaterial({ color: 16777215 });
|
|
19603
19627
|
const unitGeometry = new THREE.BoxGeometry(1, 1, 1);
|
|
19604
19628
|
const tmp = { m: new THREE.Matrix4(), q: new THREE.Quaternion(), p: new THREE.Vector3(), s: new THREE.Vector3(), c: new THREE.Color() };
|
|
19605
|
-
const wallColour = (items) => {
|
|
19606
|
-
for (const it of items) {
|
|
19607
|
-
if (!it.mesh) continue;
|
|
19608
|
-
const c = it.mesh.geometry.attributes.color;
|
|
19609
|
-
if (!c) continue;
|
|
19610
|
-
const a = c.array;
|
|
19611
|
-
tmp.c.setRGB(a[it.v0 * 3], a[it.v0 * 3 + 1], a[it.v0 * 3 + 2]);
|
|
19612
|
-
return tmp.c.getHex();
|
|
19613
|
-
}
|
|
19614
|
-
return null;
|
|
19615
|
-
};
|
|
19616
19629
|
const local = (p) => {
|
|
19617
19630
|
house.updateMatrixWorld(true);
|
|
19618
19631
|
const v = house.worldToLocal(tmp.p.set(p[0], p[1], p[2]));
|
|
19619
19632
|
return [v.x, v.y, v.z];
|
|
19620
19633
|
};
|
|
19621
|
-
function
|
|
19634
|
+
function cells(part, units) {
|
|
19622
19635
|
const [u0, v0, u1, v1] = part.aabb;
|
|
19623
19636
|
const alongU = u1 - u0 >= v1 - v0;
|
|
19624
19637
|
const len = alongU ? u1 - u0 : v1 - v0;
|
|
19625
|
-
const thick = alongU ? v1 - v0 : u1 - u0;
|
|
19626
19638
|
const [ul, uh] = units.size;
|
|
19627
19639
|
const rows = Math.max(1, Math.round((part.y1 - part.y0) / uh));
|
|
19628
19640
|
const rowH = (part.y1 - part.y0) / rows;
|
|
19629
19641
|
const per = Math.max(1, Math.round(len / ul));
|
|
19630
19642
|
const w = len / per;
|
|
19631
|
-
const
|
|
19632
|
-
const list2 = [];
|
|
19643
|
+
const out = [];
|
|
19633
19644
|
for (let r = 0; r < rows; r++) {
|
|
19634
19645
|
const shift = units.bond === "running" && r & 1 ? w / 2 : 0;
|
|
19635
|
-
|
|
19636
|
-
|
|
19637
|
-
|
|
19638
|
-
a = Math.max(0, a);
|
|
19639
|
-
b = Math.min(len, b);
|
|
19640
|
-
if (b - a < 1e-3) continue;
|
|
19641
|
-
const mid = (a + b) / 2;
|
|
19642
|
-
const x = alongU ? u0 + mid : (u0 + u1) / 2;
|
|
19643
|
-
const z = alongU ? (v0 + v1) / 2 : v0 + mid;
|
|
19644
|
-
const y = part.y0 + (r + 0.5) * rowH;
|
|
19645
|
-
const d = opening ? Math.hypot(x - opening.center[0], y - opening.center[1], z - opening.center[2]) : Infinity;
|
|
19646
|
-
if (opening && d < opening.radius) continue;
|
|
19647
|
-
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);
|
|
19648
|
-
if (chips.some(gone)) continue;
|
|
19649
|
-
const near = opening && d < opening.radius + 0.25 || chips.some((c) => Math.hypot(c[0] - x, c[1] - y, c[2] - z) < 0.15);
|
|
19650
|
-
list2.push({ x, y, z, sx: alongU ? b - a : thick, sy: rowH, sz: alongU ? thick : b - a, colour: near || paint === null ? edge : paint });
|
|
19646
|
+
for (let i = 0; i < per + (shift ? 1 : 0); i++) {
|
|
19647
|
+
const a = Math.max(0, i * w - shift), b = Math.min(len, i * w - shift + w);
|
|
19648
|
+
if (b - a > 1e-3) out.push({ a, b, y0: part.y0 + r * rowH, y1: part.y0 + (r + 1) * rowH, r, i });
|
|
19651
19649
|
}
|
|
19652
19650
|
}
|
|
19653
|
-
return
|
|
19651
|
+
return out;
|
|
19654
19652
|
}
|
|
19655
19653
|
const specOf = (id) => opts.spec()?.parts?.find((q) => q.id === id);
|
|
19656
19654
|
const same = (a, b) => a.every((x, k) => Math.abs(x - b[k]) < 1e-3);
|
|
@@ -19664,6 +19662,137 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19664
19662
|
return null;
|
|
19665
19663
|
}
|
|
19666
19664
|
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);
|
|
19665
|
+
function shell(it, mask) {
|
|
19666
|
+
const g0 = it.mesh.geometry, g = new THREE.BufferGeometry();
|
|
19667
|
+
const src = it.mesh.userData._p0 || g0.attributes.position.array;
|
|
19668
|
+
for (const [name, attr] of Object.entries(g0.attributes)) {
|
|
19669
|
+
const k = attr.itemSize;
|
|
19670
|
+
g.setAttribute(name, new THREE.BufferAttribute((name === "position" ? src : attr.array).slice(it.v0 * k, it.v1 * k), k));
|
|
19671
|
+
}
|
|
19672
|
+
if (g0.index) {
|
|
19673
|
+
const ix = g0.index.array.slice(it.i0, it.i1);
|
|
19674
|
+
for (let k = 0; k < ix.length; k++) ix[k] -= it.v0;
|
|
19675
|
+
g.setIndex(new THREE.BufferAttribute(ix, 1));
|
|
19676
|
+
}
|
|
19677
|
+
g.computeBoundingBox();
|
|
19678
|
+
g.computeBoundingSphere();
|
|
19679
|
+
const base = it.mesh.material;
|
|
19680
|
+
const material = base.clone();
|
|
19681
|
+
const toHouse = { value: new THREE.Matrix4() };
|
|
19682
|
+
material.onBeforeCompile = (sh) => {
|
|
19683
|
+
base.onBeforeCompile?.(sh);
|
|
19684
|
+
sh.uniforms.uToHouse = toHouse;
|
|
19685
|
+
sh.uniforms.uMask = { value: mask.texture };
|
|
19686
|
+
sh.uniforms.uMaskRect = { value: mask.rect };
|
|
19687
|
+
sh.uniforms.uMaskAlongX = { value: mask.alongU ? 1 : 0 };
|
|
19688
|
+
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;");
|
|
19689
|
+
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;");
|
|
19690
|
+
};
|
|
19691
|
+
material.customProgramCacheKey = () => "breachMask" + (base.customProgramCacheKey?.() ?? "");
|
|
19692
|
+
const mesh2 = new THREE.Mesh(g, material);
|
|
19693
|
+
mesh2.name = "breach-shell";
|
|
19694
|
+
mesh2.userData.breachMask = mask;
|
|
19695
|
+
mesh2.position.copy(it.mesh.position);
|
|
19696
|
+
mesh2.quaternion.copy(it.mesh.quaternion);
|
|
19697
|
+
mesh2.scale.copy(it.mesh.scale);
|
|
19698
|
+
mesh2.castShadow = it.mesh.castShadow;
|
|
19699
|
+
mesh2.receiveShadow = it.mesh.receiveShadow;
|
|
19700
|
+
mesh2.renderOrder = it.mesh.renderOrder;
|
|
19701
|
+
mesh2.onBeforeRender = () => {
|
|
19702
|
+
house.updateMatrixWorld();
|
|
19703
|
+
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh2.matrixWorld);
|
|
19704
|
+
};
|
|
19705
|
+
it.mesh.parent.add(mesh2);
|
|
19706
|
+
mesh2.updateMatrixWorld(true);
|
|
19707
|
+
toHouse.value.copy(house.matrixWorld).invert().multiply(mesh2.matrixWorld);
|
|
19708
|
+
return mesh2;
|
|
19709
|
+
}
|
|
19710
|
+
function maskOf(hostId, parts) {
|
|
19711
|
+
const host = structure.byId.get(hostId);
|
|
19712
|
+
const [u0, v0, u1, v1] = host.aabb;
|
|
19713
|
+
const alongU = u1 - u0 >= v1 - v0;
|
|
19714
|
+
const y0 = Math.min(...parts.map((id) => structure.byId.get(id).y0)), y1 = Math.max(...parts.map((id) => structure.byId.get(id).y1));
|
|
19715
|
+
const len = alongU ? u1 - u0 : v1 - v0;
|
|
19716
|
+
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)));
|
|
19717
|
+
const data = new Uint8Array(w * h);
|
|
19718
|
+
const texture = new THREE.DataTexture(data, w, h, THREE.RedFormat);
|
|
19719
|
+
texture.magFilter = THREE.LinearFilter;
|
|
19720
|
+
texture.minFilter = THREE.LinearFilter;
|
|
19721
|
+
texture.needsUpdate = true;
|
|
19722
|
+
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) };
|
|
19723
|
+
}
|
|
19724
|
+
const boxDistance = (s, y, a, b, c, d) => Math.hypot(Math.max(a - s, 0, s - b), Math.max(c - y, 0, y - d));
|
|
19725
|
+
function paint(view) {
|
|
19726
|
+
const { mask } = view;
|
|
19727
|
+
const along = (p) => (mask.alongU ? p[0] : p[2]) - mask.start;
|
|
19728
|
+
const holes = [];
|
|
19729
|
+
const discs = [];
|
|
19730
|
+
const shown = [];
|
|
19731
|
+
for (const id of view.parts) {
|
|
19732
|
+
const part = structure.byId.get(id), units2 = specOf(id).units, chips = view.chips.get(id), opening = view.openings.get(id);
|
|
19733
|
+
const all = cells(part, units2);
|
|
19734
|
+
const thick = mask.alongU ? part.aabb[3] - part.aabb[1] : part.aabb[2] - part.aabb[0];
|
|
19735
|
+
const mid = mask.alongU ? (part.aabb[1] + part.aabb[3]) / 2 : (part.aabb[0] + part.aabb[2]) / 2;
|
|
19736
|
+
if (opening) discs.push({ s: along(opening.center), y: opening.center[1], r: opening.radius });
|
|
19737
|
+
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;
|
|
19738
|
+
const kept = [];
|
|
19739
|
+
for (const c of all) (gone(c) ? holes : kept).push(c);
|
|
19740
|
+
for (const c of kept) shown.push({ c, part, units: units2, thick, mid });
|
|
19741
|
+
}
|
|
19742
|
+
const reach = SPALL_MIN + SPALL_RAGGED;
|
|
19743
|
+
mask.data.fill(0);
|
|
19744
|
+
const stamp = (s0, s1, y0, y1, inside2) => {
|
|
19745
|
+
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));
|
|
19746
|
+
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));
|
|
19747
|
+
for (let j = j0; j <= j1; j++) {
|
|
19748
|
+
const y = mask.y0 + (j + 0.5) / mask.h * mask.height;
|
|
19749
|
+
for (let i = i0; i <= i1; i++) {
|
|
19750
|
+
const s = (i + 0.5) / mask.w * mask.len;
|
|
19751
|
+
const edge = SPALL_MIN + SPALL_RAGGED * (0.6 * noise2(s, y, 0.03) + 0.4 * noise2(s + 7.3, y - 3.1, 0.011));
|
|
19752
|
+
if (inside2(s, y) <= edge) mask.data[j * mask.w + i] = 255;
|
|
19753
|
+
}
|
|
19754
|
+
}
|
|
19755
|
+
};
|
|
19756
|
+
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));
|
|
19757
|
+
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));
|
|
19758
|
+
mask.texture.needsUpdate = true;
|
|
19759
|
+
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);
|
|
19760
|
+
const list2 = [];
|
|
19761
|
+
const tone = (hex2, k) => tmp.c.setHex(hex2).multiplyScalar(k).getHex();
|
|
19762
|
+
for (const { c, part, units: units2, thick, mid } of shown) {
|
|
19763
|
+
if (!near(c)) continue;
|
|
19764
|
+
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;
|
|
19765
|
+
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 });
|
|
19766
|
+
const base = fragmentMaterial(part.material).color.getHex();
|
|
19767
|
+
const n = noise2(centre * 3.1, y * 2.7, 0.07);
|
|
19768
|
+
if (units2.mortar === "concrete") {
|
|
19769
|
+
at(len, h, thick - 2 * COVER2 - 0.03 * n, tone(base, 0.86 + 0.2 * n));
|
|
19770
|
+
} else {
|
|
19771
|
+
at(len, h, thick - 2 * COVER2 - 0.012, 10327948);
|
|
19772
|
+
at(len - 0.012, h - 0.012, thick - 2 * COVER2, tone(base, 0.8 + 0.35 * n));
|
|
19773
|
+
}
|
|
19774
|
+
}
|
|
19775
|
+
if (view.units) {
|
|
19776
|
+
view.units.removeFromParent();
|
|
19777
|
+
view.units.dispose();
|
|
19778
|
+
}
|
|
19779
|
+
const units = new THREE.InstancedMesh(unitGeometry, unitMaterial, Math.max(1, list2.length));
|
|
19780
|
+
units.name = "breach:" + view.host;
|
|
19781
|
+
list2.forEach((u, i) => {
|
|
19782
|
+
tmp.m.compose(tmp.p.set(u.x, u.y, u.z), tmp.q.identity(), tmp.s.set(u.sx, u.sy, u.sz));
|
|
19783
|
+
units.setMatrixAt(i, tmp.m);
|
|
19784
|
+
units.setColorAt(i, tmp.c.setHex(u.colour));
|
|
19785
|
+
});
|
|
19786
|
+
units.count = list2.length;
|
|
19787
|
+
units.instanceMatrix.needsUpdate = true;
|
|
19788
|
+
if (units.instanceColor) units.instanceColor.needsUpdate = true;
|
|
19789
|
+
units.castShadow = true;
|
|
19790
|
+
units.receiveShadow = true;
|
|
19791
|
+
units.visible = lod === 0;
|
|
19792
|
+
house.add(units);
|
|
19793
|
+
view.units = units;
|
|
19794
|
+
view.gone = holes.length;
|
|
19795
|
+
}
|
|
19667
19796
|
function draw2(partId, change) {
|
|
19668
19797
|
if (lod !== 0) return false;
|
|
19669
19798
|
const hostId = hostOf(partId);
|
|
@@ -19673,37 +19802,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19673
19802
|
const items = structure.meshesOf(hostId).filter((it) => it.mesh);
|
|
19674
19803
|
if (!items.length) return false;
|
|
19675
19804
|
const parts = [hostId, ...riders(hostId)].filter((id) => structure.byId.get(id) && specOf(id)?.units);
|
|
19676
|
-
|
|
19805
|
+
if (!parts.includes(partId)) return false;
|
|
19806
|
+
const mask = maskOf(hostId, parts);
|
|
19807
|
+
view = { host: hostId, parts, items, mask, shells: [], saved: null, units: null, gone: 0, chips: new Map(parts.map((id) => [id, []])), openings: /* @__PURE__ */ new Map() };
|
|
19677
19808
|
}
|
|
19678
19809
|
if (!view.chips.has(partId)) return false;
|
|
19679
19810
|
if (change.opening) {
|
|
19680
19811
|
if (view.openings.has(partId)) return false;
|
|
19681
19812
|
view.openings.set(partId, { center: local(change.opening.center), radius: change.opening.radius });
|
|
19682
|
-
if (partId === hostId) view.opening = view.openings.get(partId);
|
|
19683
19813
|
}
|
|
19684
19814
|
if (change.chip) view.chips.get(partId).push(local(change.chip));
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19688
|
-
view.mesh.removeFromParent();
|
|
19689
|
-
view.mesh.dispose();
|
|
19815
|
+
if (!view.saved) {
|
|
19816
|
+
view.shells = view.items.map((it) => shell(it, view.mask));
|
|
19817
|
+
view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
19690
19818
|
}
|
|
19691
|
-
|
|
19692
|
-
mesh2.name = "breach:" + hostId;
|
|
19693
|
-
list2.forEach((u, i) => {
|
|
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));
|
|
19695
|
-
mesh2.setMatrixAt(i, tmp.m);
|
|
19696
|
-
mesh2.setColorAt(i, tmp.c.setHex(u.colour));
|
|
19697
|
-
});
|
|
19698
|
-
mesh2.count = list2.length;
|
|
19699
|
-
mesh2.instanceMatrix.needsUpdate = true;
|
|
19700
|
-
if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
|
|
19701
|
-
mesh2.castShadow = true;
|
|
19702
|
-
mesh2.receiveShadow = true;
|
|
19703
|
-
house.add(mesh2);
|
|
19704
|
-
if (!view.saved) view.saved = view.items.map((it) => ({ it, v0: it.v0, data: collapseRange(it.mesh, it.v0, it.v1) }));
|
|
19705
|
-
view.mesh = mesh2;
|
|
19706
|
-
view.units = list2.length;
|
|
19819
|
+
paint(view);
|
|
19707
19820
|
views.set(hostId, view);
|
|
19708
19821
|
invalidate();
|
|
19709
19822
|
invalidateShadow();
|
|
@@ -19715,30 +19828,40 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19715
19828
|
else s.data = collapseRange(s.it.mesh, s.v0, s.it.v1);
|
|
19716
19829
|
}
|
|
19717
19830
|
}
|
|
19831
|
+
function dispose(view) {
|
|
19832
|
+
view.units.removeFromParent();
|
|
19833
|
+
view.units.dispose();
|
|
19834
|
+
for (const m of view.shells) {
|
|
19835
|
+
m.removeFromParent();
|
|
19836
|
+
m.geometry.dispose();
|
|
19837
|
+
m.material.dispose();
|
|
19838
|
+
}
|
|
19839
|
+
view.mask.texture.dispose();
|
|
19840
|
+
}
|
|
19718
19841
|
return {
|
|
19719
|
-
/** Handles a `stage` event of the house's object. Returns true when the wall was
|
|
19842
|
+
/** Handles a `stage` event of the house's object. Returns true when the wall was cut. */
|
|
19720
19843
|
stage(event) {
|
|
19721
19844
|
return event.stage === 2 && !!event.opening ? draw2(event.part, { opening: event.opening }) : false;
|
|
19722
19845
|
},
|
|
19723
|
-
/** Handles a `chip` event: the unit that came out leaves a
|
|
19846
|
+
/** 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. */
|
|
19724
19847
|
chip(event) {
|
|
19725
19848
|
return draw2(event.part, { chip: event.center });
|
|
19726
19849
|
},
|
|
19727
|
-
/** The part broke or fell:
|
|
19850
|
+
/** The part broke or fell: its damage goes (the plain range stays collapsed, the renderer hides the part). */
|
|
19728
19851
|
drop(partId) {
|
|
19729
19852
|
const v = views.get(partId);
|
|
19730
19853
|
if (!v) return;
|
|
19731
|
-
v
|
|
19732
|
-
v.mesh.dispose();
|
|
19854
|
+
dispose(v);
|
|
19733
19855
|
views.delete(partId);
|
|
19734
19856
|
},
|
|
19735
|
-
/** LOD 0 draws the
|
|
19857
|
+
/** LOD 0 draws the cut wall; LOD 1 and 2 give the wall its box back. */
|
|
19736
19858
|
setLod(level) {
|
|
19737
19859
|
const was = lod;
|
|
19738
19860
|
lod = level;
|
|
19739
19861
|
if (was === 0 === (level === 0)) return;
|
|
19740
19862
|
for (const v of views.values()) {
|
|
19741
|
-
v.
|
|
19863
|
+
v.units.visible = level === 0;
|
|
19864
|
+
for (const m of v.shells) m.visible = level === 0;
|
|
19742
19865
|
plain2(v, level !== 0);
|
|
19743
19866
|
}
|
|
19744
19867
|
invalidate();
|
|
@@ -19747,17 +19870,21 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
19747
19870
|
/** Everything back to what it was (after `world.reset`). */
|
|
19748
19871
|
restore() {
|
|
19749
19872
|
for (const v of views.values()) {
|
|
19750
|
-
v
|
|
19751
|
-
v.mesh.dispose();
|
|
19873
|
+
dispose(v);
|
|
19752
19874
|
if (lod === 0) plain2(v, true);
|
|
19753
19875
|
}
|
|
19754
19876
|
views.clear();
|
|
19755
19877
|
},
|
|
19756
|
-
/** The wall
|
|
19878
|
+
/** The wall has damage drawn. */
|
|
19757
19879
|
has(partId) {
|
|
19758
19880
|
return views.has(partId);
|
|
19759
19881
|
},
|
|
19760
|
-
|
|
19882
|
+
/** Walls with damage drawn, units out of them, and units laid bare at the edge of the cuts. */
|
|
19883
|
+
statistics: () => ({
|
|
19884
|
+
walls: views.size,
|
|
19885
|
+
gone: [...views.values()].reduce((n, v) => n + v.gone, 0),
|
|
19886
|
+
units: [...views.values()].reduce((n, v) => n + v.units.count, 0)
|
|
19887
|
+
})
|
|
19761
19888
|
};
|
|
19762
19889
|
}
|
|
19763
19890
|
Object.assign(K, { createBreachView });
|