@ikijs/engine 0.1.0 → 0.2.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.mjs CHANGED
@@ -417,6 +417,10 @@ var IkiPlayer = class {
417
417
  textures = [];
418
418
  /** Bumped by every `load` and by `destroy`; lets a stale async load bail. */
419
419
  loadGeneration = 0;
420
+ /** True from the moment `load()` is entered until it resolves or throws. */
421
+ loadPending = false;
422
+ /** Latches the un-awaited-load report, so a repeat caller says it once. */
423
+ warnedLoadUnfinished = false;
420
424
  destroyed = false;
421
425
  /**
422
426
  * Engine-internal mesh buffers, keyed by the part's INDEX in `this.parts`
@@ -444,8 +448,21 @@ var IkiPlayer = class {
444
448
  * Mesh buffer allocation failure IS fatal (unlike per-texture skip) because
445
449
  * textures have an `IkiLoadResult.failedTextures` reporting surface and mesh
446
450
  * buffers have none — there is no partial-mesh concept in the format.
451
+ *
452
+ * AWAIT THIS before reading {@link getParameters}. The swap happens after
453
+ * texture decoding, so an un-awaited `load()` leaves the parameter store
454
+ * empty for the rest of the tick; `getParameters` reports that case rather
455
+ * than letting a host conclude the model drives nothing.
447
456
  */
448
457
  async load(model) {
458
+ this.loadPending = true;
459
+ try {
460
+ return await this.adoptModel(model);
461
+ } finally {
462
+ this.loadPending = false;
463
+ }
464
+ }
465
+ async adoptModel(model) {
449
466
  const { gl } = this;
450
467
  const generation = ++this.loadGeneration;
451
468
  const sources = model.textures ?? [];
@@ -657,8 +674,24 @@ var IkiPlayer = class {
657
674
  getParameter(id) {
658
675
  return this.params.get(id);
659
676
  }
660
- /** The model's parameter descriptors, for building UI or host wiring. */
677
+ /**
678
+ * The model's parameter descriptors, for building UI or host wiring.
679
+ *
680
+ * Empty until the first {@link load} resolves. Reaching it through an
681
+ * un-awaited `load()` is the one mistake in this class that produces no
682
+ * error and no motion: the caller gets `[]`, concludes the model has no
683
+ * parameters, and drives nothing — so that case is reported instead of
684
+ * being indistinguishable from a model that really declares none. A
685
+ * reload is deliberately NOT reported: those parameters are stale rather
686
+ * than absent, and warning there would fire on legitimate concurrent reads.
687
+ */
661
688
  getParameters() {
689
+ if (this.loadPending && this.model === void 0 && !this.warnedLoadUnfinished) {
690
+ this.warnedLoadUnfinished = true;
691
+ console.error(
692
+ "Iki: getParameters() ran before load() finished, so it returned an empty list \u2014 await load() before reading parameters."
693
+ );
694
+ }
662
695
  return this.params.list();
663
696
  }
664
697
  destroy() {
@@ -1016,6 +1049,8 @@ var SWAY_X_AMP_B_DEG = 1.3;
1016
1049
  var SWAY_X_PERIOD_B_MS = 9700;
1017
1050
  var SWAY_Y_AMP_DEG = 1.6;
1018
1051
  var SWAY_Y_PERIOD_MS = 7300;
1052
+ var SWAY_Z_AMP_DEG = 1.1;
1053
+ var SWAY_Z_PERIOD_MS = 11300;
1019
1054
  function lerp(a, b, t) {
1020
1055
  return a + (b - a) * t;
1021
1056
  }
@@ -1089,6 +1124,7 @@ var IdleMotion = class {
1089
1124
  this.sink(StandardParameter.EyeballY, this.gazeCurrentY);
1090
1125
  this.sink(StandardParameter.AngleX, this.swayX());
1091
1126
  this.sink(StandardParameter.AngleY, this.swayY());
1127
+ this.sink(StandardParameter.AngleZ, this.swayZ());
1092
1128
  }
1093
1129
  // ---------------------------------------------------------------------------
1094
1130
  // Private helpers
@@ -1101,6 +1137,7 @@ var IdleMotion = class {
1101
1137
  this.sink(StandardParameter.EyeballY, 0);
1102
1138
  this.sink(StandardParameter.AngleX, 0);
1103
1139
  this.sink(StandardParameter.AngleY, 0);
1140
+ this.sink(StandardParameter.AngleZ, 0);
1104
1141
  }
1105
1142
  /** Returns the current eye-open value (0..1) and advances blink state. */
1106
1143
  advanceBlink() {
@@ -1132,6 +1169,10 @@ var IdleMotion = class {
1132
1169
  swayY() {
1133
1170
  return SWAY_Y_AMP_DEG * Math.sin(2 * Math.PI * this.clockMs / SWAY_Y_PERIOD_MS);
1134
1171
  }
1172
+ /** Head roll sway in degrees, pure function of the internal clock. */
1173
+ swayZ() {
1174
+ return SWAY_Z_AMP_DEG * Math.sin(2 * Math.PI * this.clockMs / SWAY_Z_PERIOD_MS);
1175
+ }
1135
1176
  /** Ease gaze current toward target; pick a new target on the internal clock. */
1136
1177
  advanceGaze(dt) {
1137
1178
  if (this.clockMs >= this.nextGazeRetargetMs) {