@adatechnology/game-vfx-three 0.2.1 → 0.3.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.js CHANGED
@@ -531,6 +531,81 @@ function createMarkLayer(THREE, options) {
531
531
  };
532
532
  }
533
533
 
534
+ // src/mesh-layer.ts
535
+ var SPHERE_DEFAULT_WIDTH = 8;
536
+ var SPHERE_DEFAULT_HEIGHT = 6;
537
+ function geometryOf(THREE, particle) {
538
+ switch (particle.shape) {
539
+ case "cone":
540
+ return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
541
+ case "cone_open":
542
+ return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
543
+ case "sphere":
544
+ return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
545
+ case "icosahedron":
546
+ return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
547
+ }
548
+ }
549
+ function materialOf(THREE, particle) {
550
+ if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
551
+ return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
552
+ }
553
+ function createMeshLayerProbes(THREE) {
554
+ const geometry = new THREE.BoxGeometry(1e-3, 1e-3, 1e-3);
555
+ return [{ lit: true, additive: false }, { lit: false, additive: false }, { lit: false, additive: true }].map(
556
+ (kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
557
+ );
558
+ }
559
+ function createMeshLayer(THREE, pool) {
560
+ const group = new THREE.Group();
561
+ group.name = "workshop-meshes";
562
+ const drawn = /* @__PURE__ */ new Map();
563
+ const frame = new THREE.Quaternion();
564
+ const euler = new THREE.Euler();
565
+ const turn = new THREE.Quaternion();
566
+ const offset = new THREE.Vector3();
567
+ function release(mesh) {
568
+ mesh.removeFromParent();
569
+ mesh.geometry.dispose();
570
+ mesh.material.dispose();
571
+ }
572
+ return {
573
+ group,
574
+ sync() {
575
+ const live = /* @__PURE__ */ new Set();
576
+ for (const particle of pool.particles) {
577
+ live.add(particle.id);
578
+ let mesh = drawn.get(particle.id);
579
+ if (!mesh) {
580
+ mesh = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
581
+ mesh.frustumCulled = false;
582
+ mesh.renderOrder = particle.additive ? 5 : 3;
583
+ drawn.set(particle.id, mesh);
584
+ group.add(mesh);
585
+ }
586
+ frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
587
+ offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
588
+ mesh.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
589
+ turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
590
+ mesh.quaternion.copy(frame).multiply(turn);
591
+ mesh.scale.setScalar(particle.scale);
592
+ mesh.material.opacity = particle.opacity;
593
+ }
594
+ for (const [id, mesh] of drawn) {
595
+ if (live.has(id)) continue;
596
+ release(mesh);
597
+ drawn.delete(id);
598
+ }
599
+ return pool.count;
600
+ },
601
+ dispose() {
602
+ for (const mesh of drawn.values()) release(mesh);
603
+ drawn.clear();
604
+ group.removeFromParent();
605
+ }
606
+ };
607
+ }
608
+
534
609
  // src/screen-overlay.ts
535
610
  var clamp = (value, min, max) => value < min ? min : value > max ? max : value;
536
611
  function layer(container, name, blend) {
@@ -710,75 +785,6 @@ function createSpriteLayer(THREE, system, pointScale) {
710
785
  };
711
786
  }
712
787
 
713
- // src/mesh-layer.ts
714
- var SPHERE_DEFAULT_WIDTH = 8;
715
- var SPHERE_DEFAULT_HEIGHT = 6;
716
- function geometryOf(THREE, particle) {
717
- switch (particle.shape) {
718
- case "cone":
719
- return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
720
- case "cone_open":
721
- return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
722
- case "sphere":
723
- return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
724
- case "icosahedron":
725
- return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
726
- }
727
- }
728
- function materialOf(THREE, particle) {
729
- if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
730
- return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
731
- }
732
- function createMeshLayer(THREE, pool) {
733
- const group = new THREE.Group();
734
- group.name = "workshop-meshes";
735
- const drawn = /* @__PURE__ */ new Map();
736
- const frame = new THREE.Quaternion();
737
- const euler = new THREE.Euler();
738
- const turn = new THREE.Quaternion();
739
- const offset = new THREE.Vector3();
740
- function release(mesh) {
741
- mesh.removeFromParent();
742
- mesh.geometry.dispose();
743
- mesh.material.dispose();
744
- }
745
- return {
746
- group,
747
- sync() {
748
- const live = /* @__PURE__ */ new Set();
749
- for (const particle of pool.particles) {
750
- live.add(particle.id);
751
- let mesh = drawn.get(particle.id);
752
- if (!mesh) {
753
- mesh = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
754
- mesh.frustumCulled = false;
755
- mesh.renderOrder = particle.additive ? 5 : 3;
756
- drawn.set(particle.id, mesh);
757
- group.add(mesh);
758
- }
759
- frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
760
- offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
761
- mesh.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
762
- turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
763
- mesh.quaternion.copy(frame).multiply(turn);
764
- mesh.scale.setScalar(particle.scale);
765
- mesh.material.opacity = particle.opacity;
766
- }
767
- for (const [id, mesh] of drawn) {
768
- if (live.has(id)) continue;
769
- release(mesh);
770
- drawn.delete(id);
771
- }
772
- return pool.count;
773
- },
774
- dispose() {
775
- for (const mesh of drawn.values()) release(mesh);
776
- drawn.clear();
777
- group.removeFromParent();
778
- }
779
- };
780
- }
781
-
782
788
  // src/shaders/workshop-sprite.ts
783
789
  var WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;
784
790
  void main(){vC=pcol;vA=alpha;vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=clamp(size*uScale/-mv.z,1.,512.);gl_Position=projectionMatrix*mv;}`;
@@ -1149,6 +1155,13 @@ function createVfxRenderer(THREE, options) {
1149
1155
  const requested = options.vfx;
1150
1156
  const world = isWorld(requested) ? requested : createVfx({ profile: options.quality ?? "medium", reducedMotion, ...requested });
1151
1157
  const pools = world.pools();
1158
+ let anchors;
1159
+ const releaseAnchors = () => {
1160
+ if (!anchors) return;
1161
+ anchors.meshes[0]?.geometry.dispose();
1162
+ for (const mesh of anchors.meshes) mesh.material.dispose();
1163
+ anchors = void 0;
1164
+ };
1152
1165
  const root = new THREE.Group();
1153
1166
  options.scene.add(root);
1154
1167
  const direction = new THREE.Vector3(...options.lightDirection ?? LAB_LIGHT_DIRECTION).normalize();
@@ -1265,6 +1278,16 @@ function createVfxRenderer(THREE, options) {
1265
1278
  syncAll();
1266
1279
  overlay?.update(screenNow());
1267
1280
  },
1281
+ warmUpProbes() {
1282
+ releaseAnchors();
1283
+ const meshes = createMeshLayerProbes(THREE);
1284
+ for (const mesh of meshes) mesh.frustumCulled = false;
1285
+ const group = new THREE.Group();
1286
+ group.add(...meshes);
1287
+ anchors = { meshes, group };
1288
+ return { group, dispose: () => {
1289
+ } };
1290
+ },
1268
1291
  applyCamera() {
1269
1292
  const shake = screenNow().shake;
1270
1293
  if (reducedMotion || !(shake > SHAKE_FLOOR)) return () => {
@@ -1283,6 +1306,7 @@ function createVfxRenderer(THREE, options) {
1283
1306
  },
1284
1307
  screen: screenNow,
1285
1308
  dispose() {
1309
+ releaseAnchors();
1286
1310
  stopMarks();
1287
1311
  overlay?.dispose();
1288
1312
  workshopLayers?.dispose();
@@ -1556,6 +1580,11 @@ function createVfxTestModule(options = {}) {
1556
1580
  controls,
1557
1581
  layers,
1558
1582
  metrics,
1583
+ // opening the effects area puts the camera at the bench view (the initial distance does not move it: that would take the player's view on load)
1584
+ area: () => ({
1585
+ placements: [],
1586
+ view: { eye: eyeFor(DEFAULT_DISTANCE), target: [0, 1.2, 0] }
1587
+ }),
1559
1588
  attach(host) {
1560
1589
  const bench = host.bench;
1561
1590
  let own;
@@ -1575,6 +1604,7 @@ function createVfxTestModule(options = {}) {
1575
1604
  let phase = 0;
1576
1605
  let handle = null;
1577
1606
  let sinceRepeat = 0;
1607
+ let live = false;
1578
1608
  read = () => {
1579
1609
  const def = explosions[effect];
1580
1610
  const recipe = recipes[effect];
@@ -1613,7 +1643,7 @@ function createVfxTestModule(options = {}) {
1613
1643
  }
1614
1644
  );
1615
1645
  bench?.sheet?.(sheetFor(id));
1616
- replay();
1646
+ if (live) replay();
1617
1647
  }
1618
1648
  function look() {
1619
1649
  const eye = eyeFor(distance);
@@ -1630,7 +1660,7 @@ function createVfxTestModule(options = {}) {
1630
1660
  choose(change.value);
1631
1661
  else if (change.id === "variant" && typeof change.value === "string") {
1632
1662
  variant = change.value;
1633
- if (recipes[effect]?.params) replay();
1663
+ if (live && recipes[effect]?.params) replay();
1634
1664
  } else if (change.id === "auto_repeat") auto = change.value === true;
1635
1665
  else if (change.id === "distance" && typeof change.value === "number") {
1636
1666
  distance = change.value;
@@ -1640,6 +1670,7 @@ function createVfxTestModule(options = {}) {
1640
1670
  };
1641
1671
  const offChange = bench?.onChange(apply);
1642
1672
  const offFrame = host.onFrame((dt) => {
1673
+ live = true;
1643
1674
  phase += dt;
1644
1675
  sinceRepeat += dt;
1645
1676
  own?.update(dt);