@adatechnology/game-vfx-three 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +15 -1
- package/dist/index.global.js +187 -88
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +186 -87
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,12 @@ interface VfxRendererOptions {
|
|
|
49
49
|
pointScale?: number;
|
|
50
50
|
/** Multiplies the blast and fire lights. Default `Math.PI`: three.js r155+ lights are physical, the Lab's were not (r128). */
|
|
51
51
|
lightScale?: number;
|
|
52
|
+
/** Caps on the marks kept on the ground; once one is full the oldest is reused for the next, so nothing grows and nothing is allocated. Defaults: 64 scorch and dirt, 512 rocks, 256 blood. */
|
|
53
|
+
markLimits?: {
|
|
54
|
+
readonly maxGroundMarks?: number;
|
|
55
|
+
readonly maxRocks?: number;
|
|
56
|
+
readonly maxBlood?: number;
|
|
57
|
+
};
|
|
52
58
|
}
|
|
53
59
|
/** The world's counters flattened (`lastBlast` becomes `last_puffs`, `last_sparks`, `last_chunks`, `last_lights`), plus `drawCalls`. */
|
|
54
60
|
type VfxRendererStats = Readonly<Record<string, number>>;
|
|
@@ -146,7 +152,9 @@ interface MarkLayer {
|
|
|
146
152
|
/** The shared rock and plate shapes, so the debris and the rim rocks use the same geometry. */
|
|
147
153
|
handle(event: MarkEvent): void;
|
|
148
154
|
update(dt: number): void;
|
|
149
|
-
/**
|
|
155
|
+
/** Puts the instanced meshes in the scene now, so their shader programs link while loading instead of at the first mark. */
|
|
156
|
+
prepare(): void;
|
|
157
|
+
/** The draw calls the marks cost right now. */
|
|
150
158
|
count(): number;
|
|
151
159
|
dispose(): void;
|
|
152
160
|
}
|
|
@@ -155,6 +163,12 @@ interface MarkLayerOptions {
|
|
|
155
163
|
readonly createCanvas: (() => CanvasLike) | undefined;
|
|
156
164
|
readonly rockGeometry: THREE_NS.BufferGeometry;
|
|
157
165
|
readonly plateGeometry: THREE_NS.BufferGeometry;
|
|
166
|
+
/** Scorch and dirt meshes alive at once; the oldest is reused for the next. Default 64. */
|
|
167
|
+
readonly maxGroundMarks?: number;
|
|
168
|
+
/** Rim rocks and plates alive at once (each shape has its own instanced mesh of this size); the oldest is overwritten. Default 512. */
|
|
169
|
+
readonly maxRocks?: number;
|
|
170
|
+
/** Blood pools alive at once; the oldest is overwritten. Default 256. */
|
|
171
|
+
readonly maxBlood?: number;
|
|
158
172
|
}
|
|
159
173
|
/**
|
|
160
174
|
* The scorch, dirt, rim rocks and blood of the Lab as meshes (L443-476). A scorch or dirt mesh follows the ground as it was the moment the mark was
|
package/dist/index.global.js
CHANGED
|
@@ -8523,6 +8523,11 @@ void main(){
|
|
|
8523
8523
|
var MAX_CONFORM_STEPS = 48;
|
|
8524
8524
|
var MIN_CONFORM_STEPS = 16;
|
|
8525
8525
|
var BLOOD_GROW_RATE = 1.2;
|
|
8526
|
+
var BLOOD_START_GROWTH = 0.05;
|
|
8527
|
+
var DEFAULT_MAX_GROUND_MARKS = 64;
|
|
8528
|
+
var DEFAULT_MAX_ROCKS = 512;
|
|
8529
|
+
var DEFAULT_MAX_BLOOD = 256;
|
|
8530
|
+
var MAX_SPARE_GROUND_GEOMETRIES = 16;
|
|
8526
8531
|
function createMarkLayer(THREE, options) {
|
|
8527
8532
|
const group = new THREE.Group();
|
|
8528
8533
|
const textures = createMarkTextures(THREE, options.createCanvas);
|
|
@@ -8546,92 +8551,146 @@ void main(){
|
|
|
8546
8551
|
const bloodMaterial = new THREE.MeshStandardMaterial({ color: 6031882, roughness: 0.15, metalness: 0.1, polygonOffset: true, polygonOffsetFactor: -3, polygonOffsetUnits: -3 });
|
|
8547
8552
|
const bloodGeometry = new THREE.CircleGeometry(0.5, 9).rotateX(-Math.PI / 2);
|
|
8548
8553
|
owned.push(bloodMaterial, bloodGeometry);
|
|
8549
|
-
const
|
|
8550
|
-
const
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
material = new THREE.MeshStandardMaterial({ color, roughness: 0.9, metalness: 0, flatShading: true });
|
|
8556
|
-
rockMaterials.set(color, material);
|
|
8557
|
-
owned.push(material);
|
|
8554
|
+
const maxGroundMarks = options.maxGroundMarks ?? DEFAULT_MAX_GROUND_MARKS;
|
|
8555
|
+
const instanced = {};
|
|
8556
|
+
function rockMaterial() {
|
|
8557
|
+
if (!instanced.rockMaterial) {
|
|
8558
|
+
instanced.rockMaterial = new THREE.MeshStandardMaterial({ color: 16777215, roughness: 0.9, metalness: 0, flatShading: true });
|
|
8559
|
+
owned.push(instanced.rockMaterial);
|
|
8558
8560
|
}
|
|
8559
|
-
return
|
|
8561
|
+
return instanced.rockMaterial;
|
|
8562
|
+
}
|
|
8563
|
+
function bloodMesh() {
|
|
8564
|
+
return instanced.blood ?? (instanced.blood = attach(createInstanced(bloodGeometry, bloodMaterial, options.maxBlood ?? DEFAULT_MAX_BLOOD)));
|
|
8565
|
+
}
|
|
8566
|
+
function rockMesh() {
|
|
8567
|
+
return instanced.rock ?? (instanced.rock = attach(createInstanced(options.rockGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
8568
|
+
}
|
|
8569
|
+
function plateMesh() {
|
|
8570
|
+
return instanced.plate ?? (instanced.plate = attach(createInstanced(options.plateGeometry, rockMaterial(), options.maxRocks ?? DEFAULT_MAX_ROCKS), true));
|
|
8571
|
+
}
|
|
8572
|
+
function attach(mesh2, castShadow = false) {
|
|
8573
|
+
mesh2.castShadow = castShadow;
|
|
8574
|
+
group.add(mesh2);
|
|
8575
|
+
return mesh2;
|
|
8576
|
+
}
|
|
8577
|
+
const groundMarks = [];
|
|
8578
|
+
const spareGeometries = [];
|
|
8579
|
+
const blood = [];
|
|
8580
|
+
const placed = { blood: 0, rock: 0, plate: 0 };
|
|
8581
|
+
const matrix = new THREE.Matrix4();
|
|
8582
|
+
const position = new THREE.Vector3();
|
|
8583
|
+
const quaternion = new THREE.Quaternion();
|
|
8584
|
+
const euler = new THREE.Euler();
|
|
8585
|
+
const scale = new THREE.Vector3();
|
|
8586
|
+
const color = new THREE.Color();
|
|
8587
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
8588
|
+
function createInstanced(geometry, material, capacity) {
|
|
8589
|
+
const mesh2 = new THREE.InstancedMesh(geometry, material, capacity);
|
|
8590
|
+
mesh2.count = 0;
|
|
8591
|
+
mesh2.frustumCulled = false;
|
|
8592
|
+
mesh2.receiveShadow = true;
|
|
8593
|
+
return mesh2;
|
|
8594
|
+
}
|
|
8595
|
+
function takeSpare(vertexCount) {
|
|
8596
|
+
const found = spareGeometries.findIndex((geometry) => geometry.getAttribute("position").count === vertexCount);
|
|
8597
|
+
return found < 0 ? void 0 : spareGeometries.splice(found, 1)[0];
|
|
8598
|
+
}
|
|
8599
|
+
function evictOldestGroundMark() {
|
|
8600
|
+
const oldest = groundMarks.shift();
|
|
8601
|
+
group.remove(oldest.mesh);
|
|
8602
|
+
spareGeometries.push(oldest.mesh.geometry);
|
|
8603
|
+
if (spareGeometries.length > MAX_SPARE_GROUND_GEOMETRIES) spareGeometries.shift().dispose();
|
|
8560
8604
|
}
|
|
8561
8605
|
function conform(mark, material) {
|
|
8562
8606
|
const radius = mark.radius;
|
|
8563
8607
|
const steps = Math.max(MIN_CONFORM_STEPS, Math.min(MAX_CONFORM_STEPS, Math.round(radius * 8)));
|
|
8564
|
-
|
|
8565
|
-
const
|
|
8608
|
+
if (groundMarks.length >= maxGroundMarks) evictOldestGroundMark();
|
|
8609
|
+
const geometry = takeSpare((steps + 1) * (steps + 1)) ?? new THREE.PlaneGeometry(2, 2, steps, steps).rotateX(-Math.PI / 2);
|
|
8610
|
+
const grid = geometry.attributes.position;
|
|
8611
|
+
const cell = 2 / steps;
|
|
8566
8612
|
const cos2 = Math.cos(mark.rotation);
|
|
8567
8613
|
const sin2 = Math.sin(mark.rotation);
|
|
8568
|
-
for (let index = 0; index <
|
|
8569
|
-
const localX =
|
|
8570
|
-
const localZ =
|
|
8614
|
+
for (let index = 0; index < grid.count; index++) {
|
|
8615
|
+
const localX = Math.fround(index % (steps + 1) * cell - 1) * radius;
|
|
8616
|
+
const localZ = Math.fround(Math.floor(index / (steps + 1)) * cell - 1) * radius;
|
|
8571
8617
|
const worldX = mark.x + localX * cos2 - localZ * sin2;
|
|
8572
8618
|
const worldZ = mark.z + localX * sin2 + localZ * cos2;
|
|
8573
|
-
|
|
8619
|
+
grid.setXYZ(index, worldX, (options.ground?.heightAt(worldX, worldZ) ?? 0) + mark.lift, worldZ);
|
|
8574
8620
|
}
|
|
8621
|
+
grid.needsUpdate = true;
|
|
8575
8622
|
geometry.computeVertexNormals();
|
|
8623
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
8624
|
+
geometry.boundingSphere = null;
|
|
8625
|
+
geometry.boundingBox = null;
|
|
8576
8626
|
const mesh2 = new THREE.Mesh(geometry, material);
|
|
8577
8627
|
mesh2.receiveShadow = true;
|
|
8578
|
-
|
|
8628
|
+
group.add(mesh2);
|
|
8629
|
+
groundMarks.push({ mesh: mesh2, steps });
|
|
8579
8630
|
}
|
|
8580
8631
|
function addRock(mark) {
|
|
8581
|
-
const mesh2 =
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
mesh2.
|
|
8586
|
-
mesh2.
|
|
8587
|
-
|
|
8632
|
+
const mesh2 = mark.plate ? plateMesh() : rockMesh();
|
|
8633
|
+
const slot = mark.plate ? "plate" : "rock";
|
|
8634
|
+
const index = placed[slot]++ % mesh2.instanceMatrix.count;
|
|
8635
|
+
quaternion.setFromEuler(euler.set(mark.rotation[0], mark.rotation[1], mark.rotation[2]));
|
|
8636
|
+
mesh2.setMatrixAt(index, matrix.compose(position.set(mark.x, mark.y, mark.z), quaternion, scale.set(mark.scale[0], mark.scale[1], mark.scale[2])));
|
|
8637
|
+
mesh2.setColorAt(index, color.set(mark.color));
|
|
8638
|
+
mesh2.count = Math.min(placed[slot], mesh2.instanceMatrix.count);
|
|
8639
|
+
mesh2.instanceMatrix.needsUpdate = true;
|
|
8640
|
+
mesh2.instanceColor.needsUpdate = true;
|
|
8641
|
+
}
|
|
8642
|
+
function writeBlood(index, drawn, growth) {
|
|
8643
|
+
quaternion.setFromAxisAngle(up, drawn.rotation);
|
|
8644
|
+
const mesh2 = bloodMesh();
|
|
8645
|
+
mesh2.setMatrixAt(index, matrix.compose(position.set(drawn.position[0], drawn.position[1], drawn.position[2]), quaternion, scale.set(drawn.scaleX * growth, 1, drawn.scaleZ * growth)));
|
|
8646
|
+
mesh2.instanceMatrix.needsUpdate = true;
|
|
8588
8647
|
}
|
|
8589
8648
|
function addBlood(mark) {
|
|
8590
|
-
const
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
mesh2.scale.multiplyScalar(0.05);
|
|
8596
|
-
blood.push({ mesh: mesh2, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 });
|
|
8597
|
-
return mesh2;
|
|
8649
|
+
const index = placed.blood++ % bloodMesh().instanceMatrix.count;
|
|
8650
|
+
const drawn = { position: [mark.x, mark.y, mark.z], rotation: mark.rotation, scaleX: mark.scaleX, scaleZ: mark.scaleZ, age: 0 };
|
|
8651
|
+
blood[index] = drawn;
|
|
8652
|
+
writeBlood(index, drawn, BLOOD_START_GROWTH);
|
|
8653
|
+
bloodMesh().count = Math.min(placed.blood, bloodMesh().instanceMatrix.count);
|
|
8598
8654
|
}
|
|
8599
8655
|
function build(mark) {
|
|
8600
|
-
if (mark.kind === "scorch")
|
|
8601
|
-
if (mark.kind === "dirt")
|
|
8602
|
-
if (mark.kind === "rock")
|
|
8603
|
-
|
|
8604
|
-
}
|
|
8605
|
-
function
|
|
8606
|
-
for (const
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8656
|
+
if (mark.kind === "scorch") conform(mark, scorchMaterials[mark.variant]);
|
|
8657
|
+
else if (mark.kind === "dirt") conform(mark, dirtMaterial);
|
|
8658
|
+
else if (mark.kind === "rock") addRock(mark);
|
|
8659
|
+
else addBlood(mark);
|
|
8660
|
+
}
|
|
8661
|
+
function clearMarks() {
|
|
8662
|
+
for (const drawn of groundMarks) group.remove(drawn.mesh);
|
|
8663
|
+
for (const drawn of groundMarks) drawn.mesh.geometry.dispose();
|
|
8664
|
+
groundMarks.length = 0;
|
|
8665
|
+
for (const geometry of spareGeometries) geometry.dispose();
|
|
8666
|
+
spareGeometries.length = 0;
|
|
8611
8667
|
blood.length = 0;
|
|
8612
|
-
|
|
8668
|
+
placed.blood = placed.rock = placed.plate = 0;
|
|
8669
|
+
for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) if (mesh2) mesh2.count = 0;
|
|
8613
8670
|
}
|
|
8614
8671
|
return {
|
|
8615
8672
|
group,
|
|
8616
8673
|
handle(event) {
|
|
8617
|
-
if (event.type === "clear")
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8674
|
+
if (event.type === "clear") clearMarks();
|
|
8675
|
+
else build(event.mark);
|
|
8676
|
+
},
|
|
8677
|
+
prepare() {
|
|
8678
|
+
bloodMesh();
|
|
8679
|
+
rockMesh();
|
|
8680
|
+
plateMesh();
|
|
8623
8681
|
},
|
|
8624
8682
|
update(dt) {
|
|
8625
|
-
for (
|
|
8626
|
-
|
|
8683
|
+
for (let index = 0; index < blood.length; index++) {
|
|
8684
|
+
const drawn = blood[index];
|
|
8685
|
+
if (!drawn || drawn.age > 1) continue;
|
|
8627
8686
|
drawn.age += dt * BLOOD_GROW_RATE;
|
|
8628
|
-
|
|
8629
|
-
drawn.mesh.scale.set(drawn.scaleX * Math.max(0.05, ease3), 1, drawn.scaleZ * Math.max(0.05, ease3));
|
|
8687
|
+
writeBlood(index, drawn, Math.max(BLOOD_START_GROWTH, 1 - Math.pow(1 - Math.min(1, drawn.age), 2.5)));
|
|
8630
8688
|
}
|
|
8631
8689
|
},
|
|
8632
|
-
count: () =>
|
|
8690
|
+
count: () => groundMarks.length + [instanced.blood, instanced.rock, instanced.plate].filter((mesh2) => mesh2 && mesh2.count > 0).length,
|
|
8633
8691
|
dispose() {
|
|
8634
|
-
|
|
8692
|
+
clearMarks();
|
|
8693
|
+
for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) mesh2?.dispose();
|
|
8635
8694
|
for (const item of owned) item.dispose();
|
|
8636
8695
|
}
|
|
8637
8696
|
};
|
|
@@ -8640,18 +8699,22 @@ void main(){
|
|
|
8640
8699
|
// src/mesh-layer.ts
|
|
8641
8700
|
var SPHERE_DEFAULT_WIDTH = 8;
|
|
8642
8701
|
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
8702
|
+
var MAX_PARKED_MESHES_PER_KIND = 256;
|
|
8643
8703
|
function geometryOf(THREE, particle) {
|
|
8644
8704
|
switch (particle.shape) {
|
|
8645
8705
|
case "cone":
|
|
8646
|
-
return new THREE.ConeGeometry(
|
|
8706
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, false);
|
|
8647
8707
|
case "cone_open":
|
|
8648
|
-
return new THREE.ConeGeometry(
|
|
8708
|
+
return new THREE.ConeGeometry(1, 1, particle.segments || void 0, particle.segments2 || 1, true);
|
|
8649
8709
|
case "sphere":
|
|
8650
|
-
return new THREE.SphereGeometry(
|
|
8710
|
+
return new THREE.SphereGeometry(1, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
8651
8711
|
case "icosahedron":
|
|
8652
|
-
return new THREE.IcosahedronGeometry(
|
|
8712
|
+
return new THREE.IcosahedronGeometry(1, particle.segments);
|
|
8653
8713
|
}
|
|
8654
8714
|
}
|
|
8715
|
+
function isCone(particle) {
|
|
8716
|
+
return particle.shape === "cone" || particle.shape === "cone_open";
|
|
8717
|
+
}
|
|
8655
8718
|
function materialOf(THREE, particle) {
|
|
8656
8719
|
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
8657
8720
|
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
@@ -8662,51 +8725,82 @@ void main(){
|
|
|
8662
8725
|
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
8663
8726
|
);
|
|
8664
8727
|
}
|
|
8728
|
+
function geometryKeyOf(particle) {
|
|
8729
|
+
return `${particle.shape}:${particle.segments}:${particle.segments2}`;
|
|
8730
|
+
}
|
|
8665
8731
|
function createMeshLayer(THREE, pool) {
|
|
8666
8732
|
const group = new THREE.Group();
|
|
8667
8733
|
group.name = "workshop-meshes";
|
|
8668
8734
|
const drawn = /* @__PURE__ */ new Map();
|
|
8735
|
+
const geometries = /* @__PURE__ */ new Map();
|
|
8736
|
+
const parked = /* @__PURE__ */ new Map();
|
|
8669
8737
|
const frame = new THREE.Quaternion();
|
|
8670
8738
|
const euler = new THREE.Euler();
|
|
8671
8739
|
const turn = new THREE.Quaternion();
|
|
8672
8740
|
const offset = new THREE.Vector3();
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8741
|
+
let stamp = 0;
|
|
8742
|
+
function geometryFor(particle) {
|
|
8743
|
+
const key = geometryKeyOf(particle);
|
|
8744
|
+
let geometry = geometries.get(key);
|
|
8745
|
+
if (!geometry) {
|
|
8746
|
+
geometry = geometryOf(THREE, particle);
|
|
8747
|
+
geometries.set(key, geometry);
|
|
8748
|
+
}
|
|
8749
|
+
return geometry;
|
|
8750
|
+
}
|
|
8751
|
+
function acquire(particle) {
|
|
8752
|
+
const parkingKey = `${geometryKeyOf(particle)}|${particle.lit}|${particle.additive}`;
|
|
8753
|
+
const mesh2 = parked.get(parkingKey)?.pop() ?? new THREE.Mesh(geometryFor(particle), materialOf(THREE, particle));
|
|
8754
|
+
mesh2.material.color.set(particle.color);
|
|
8755
|
+
mesh2.frustumCulled = false;
|
|
8756
|
+
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
8757
|
+
group.add(mesh2);
|
|
8758
|
+
return { mesh: mesh2, parkingKey, stamp };
|
|
8759
|
+
}
|
|
8760
|
+
function park(entry) {
|
|
8761
|
+
entry.mesh.removeFromParent();
|
|
8762
|
+
let free = parked.get(entry.parkingKey);
|
|
8763
|
+
if (!free) {
|
|
8764
|
+
free = [];
|
|
8765
|
+
parked.set(entry.parkingKey, free);
|
|
8766
|
+
}
|
|
8767
|
+
if (free.length < MAX_PARKED_MESHES_PER_KIND) free.push(entry.mesh);
|
|
8768
|
+
else entry.mesh.material.dispose();
|
|
8677
8769
|
}
|
|
8678
8770
|
return {
|
|
8679
8771
|
group,
|
|
8680
8772
|
sync() {
|
|
8681
|
-
|
|
8773
|
+
stamp++;
|
|
8682
8774
|
for (const particle of pool.particles) {
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
mesh2.frustumCulled = false;
|
|
8688
|
-
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
8689
|
-
drawn.set(particle.id, mesh2);
|
|
8690
|
-
group.add(mesh2);
|
|
8775
|
+
let entry = drawn.get(particle.id);
|
|
8776
|
+
if (!entry) {
|
|
8777
|
+
entry = acquire(particle);
|
|
8778
|
+
drawn.set(particle.id, entry);
|
|
8691
8779
|
}
|
|
8780
|
+
entry.stamp = stamp;
|
|
8781
|
+
const mesh2 = entry.mesh;
|
|
8692
8782
|
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
8693
8783
|
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
8694
8784
|
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
8695
8785
|
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
8696
8786
|
mesh2.quaternion.copy(frame).multiply(turn);
|
|
8697
|
-
mesh2.scale.
|
|
8787
|
+
mesh2.scale.set(particle.radius * particle.scale, (isCone(particle) ? particle.height : particle.radius) * particle.scale, particle.radius * particle.scale);
|
|
8698
8788
|
mesh2.material.opacity = particle.opacity;
|
|
8699
8789
|
}
|
|
8700
|
-
for (const [id,
|
|
8701
|
-
if (
|
|
8702
|
-
|
|
8790
|
+
for (const [id, entry] of drawn) {
|
|
8791
|
+
if (entry.stamp === stamp) continue;
|
|
8792
|
+
park(entry);
|
|
8703
8793
|
drawn.delete(id);
|
|
8704
8794
|
}
|
|
8705
8795
|
return pool.count;
|
|
8706
8796
|
},
|
|
8707
8797
|
dispose() {
|
|
8708
|
-
for (const
|
|
8798
|
+
for (const entry of drawn.values()) park(entry);
|
|
8709
8799
|
drawn.clear();
|
|
8800
|
+
for (const free of parked.values()) for (const mesh2 of free) mesh2.material.dispose();
|
|
8801
|
+
parked.clear();
|
|
8802
|
+
for (const geometry of geometries.values()) geometry.dispose();
|
|
8803
|
+
geometries.clear();
|
|
8710
8804
|
group.removeFromParent();
|
|
8711
8805
|
}
|
|
8712
8806
|
};
|
|
@@ -9291,7 +9385,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9291
9385
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
9292
9386
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
9293
9387
|
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
9294
|
-
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry });
|
|
9388
|
+
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
9295
9389
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
9296
9390
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
9297
9391
|
const overlay = options.overlay ? createScreenOverlay(options.overlay) : void 0;
|
|
@@ -9317,6 +9411,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9317
9411
|
const position = options.camera.position;
|
|
9318
9412
|
world.setCamera([position.x, position.y, position.z]);
|
|
9319
9413
|
}
|
|
9414
|
+
function attachWorkshopLayers() {
|
|
9415
|
+
const engine = world.peekWorkshop();
|
|
9416
|
+
if (!engine || engine === workshopEngine) return;
|
|
9417
|
+
workshopLayers?.dispose();
|
|
9418
|
+
if (workshopLayers) root.remove(workshopLayers.group);
|
|
9419
|
+
workshopEngine = engine;
|
|
9420
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
9421
|
+
root.add(workshopLayers.group);
|
|
9422
|
+
applyLayers();
|
|
9423
|
+
}
|
|
9320
9424
|
function syncAll() {
|
|
9321
9425
|
lastDrawn.puffs = puffs.sync();
|
|
9322
9426
|
lastDrawn.sparks = sparks.sync();
|
|
@@ -9325,15 +9429,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9325
9429
|
lastDrawn.shocks = shocks.sync();
|
|
9326
9430
|
lastDrawn.sprites = sprites.sync();
|
|
9327
9431
|
lights.sync();
|
|
9328
|
-
|
|
9329
|
-
if (engine && engine !== workshopEngine) {
|
|
9330
|
-
workshopLayers?.dispose();
|
|
9331
|
-
if (workshopLayers) root.remove(workshopLayers.group);
|
|
9332
|
-
workshopEngine = engine;
|
|
9333
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
9334
|
-
root.add(workshopLayers.group);
|
|
9335
|
-
applyLayers();
|
|
9336
|
-
}
|
|
9432
|
+
attachWorkshopLayers();
|
|
9337
9433
|
const workshopCalls = workshopLayers?.sync() ?? 0;
|
|
9338
9434
|
const glow = pools.lights.glow;
|
|
9339
9435
|
uniforms.uGlowPos.value.set(glow.x, glow.y, glow.z);
|
|
@@ -9386,6 +9482,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9386
9482
|
},
|
|
9387
9483
|
warmUpProbes() {
|
|
9388
9484
|
releaseAnchors();
|
|
9485
|
+
marks.prepare();
|
|
9486
|
+
world.workshop();
|
|
9487
|
+
attachWorkshopLayers();
|
|
9389
9488
|
const meshes = createMeshLayerProbes(THREE);
|
|
9390
9489
|
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
9391
9490
|
const group = new THREE.Group();
|