@ikijs/engine 0.1.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.mjs CHANGED
@@ -1049,6 +1049,8 @@ var SWAY_X_AMP_B_DEG = 1.3;
1049
1049
  var SWAY_X_PERIOD_B_MS = 9700;
1050
1050
  var SWAY_Y_AMP_DEG = 1.6;
1051
1051
  var SWAY_Y_PERIOD_MS = 7300;
1052
+ var SWAY_Z_AMP_DEG = 1.1;
1053
+ var SWAY_Z_PERIOD_MS = 11300;
1052
1054
  function lerp(a, b, t) {
1053
1055
  return a + (b - a) * t;
1054
1056
  }
@@ -1061,6 +1063,22 @@ function randomInDisk(rng, radius) {
1061
1063
  return [r * Math.cos(theta), r * Math.sin(theta)];
1062
1064
  }
1063
1065
  var IdleMotion = class {
1066
+ /**
1067
+ * Every parameter id {@link update} and `emitRestingPose` write, in write
1068
+ * order. Published so a host (or IkiMotion) can restore them after it
1069
+ * stops driving — keep in step with those two methods; the test suite
1070
+ * pins this list to the actual emissions.
1071
+ */
1072
+ drivenParameterIds = [
1073
+ StandardParameter.EyeOpenLeft,
1074
+ StandardParameter.EyeOpenRight,
1075
+ StandardParameter.Breath,
1076
+ StandardParameter.EyeballX,
1077
+ StandardParameter.EyeballY,
1078
+ StandardParameter.AngleX,
1079
+ StandardParameter.AngleY,
1080
+ StandardParameter.AngleZ
1081
+ ];
1064
1082
  sink;
1065
1083
  rng;
1066
1084
  // Internal clock: advances by clamped dt, NOT by raw wall-clock jumps.
@@ -1122,6 +1140,7 @@ var IdleMotion = class {
1122
1140
  this.sink(StandardParameter.EyeballY, this.gazeCurrentY);
1123
1141
  this.sink(StandardParameter.AngleX, this.swayX());
1124
1142
  this.sink(StandardParameter.AngleY, this.swayY());
1143
+ this.sink(StandardParameter.AngleZ, this.swayZ());
1125
1144
  }
1126
1145
  // ---------------------------------------------------------------------------
1127
1146
  // Private helpers
@@ -1134,6 +1153,7 @@ var IdleMotion = class {
1134
1153
  this.sink(StandardParameter.EyeballY, 0);
1135
1154
  this.sink(StandardParameter.AngleX, 0);
1136
1155
  this.sink(StandardParameter.AngleY, 0);
1156
+ this.sink(StandardParameter.AngleZ, 0);
1137
1157
  }
1138
1158
  /** Returns the current eye-open value (0..1) and advances blink state. */
1139
1159
  advanceBlink() {
@@ -1165,6 +1185,10 @@ var IdleMotion = class {
1165
1185
  swayY() {
1166
1186
  return SWAY_Y_AMP_DEG * Math.sin(2 * Math.PI * this.clockMs / SWAY_Y_PERIOD_MS);
1167
1187
  }
1188
+ /** Head roll sway in degrees, pure function of the internal clock. */
1189
+ swayZ() {
1190
+ return SWAY_Z_AMP_DEG * Math.sin(2 * Math.PI * this.clockMs / SWAY_Z_PERIOD_MS);
1191
+ }
1168
1192
  /** Ease gaze current toward target; pick a new target on the internal clock. */
1169
1193
  advanceGaze(dt) {
1170
1194
  if (this.clockMs >= this.nextGazeRetargetMs) {
@@ -1187,6 +1211,8 @@ function signedNormalized(value, param) {
1187
1211
  return clamp((value - rest) / den, -1, 1);
1188
1212
  }
1189
1213
  var PhysicsMotion = class {
1214
+ /** The output parameter of every rig, in rig order — what `emit` writes. */
1215
+ drivenParameterIds;
1190
1216
  rigs;
1191
1217
  read;
1192
1218
  sink;
@@ -1200,6 +1226,7 @@ var PhysicsMotion = class {
1200
1226
  this.sink = sink;
1201
1227
  this.params = new Map(params.map((p) => [p.id, p]));
1202
1228
  this.state = rigs.map(() => ({ x: 0, v: 0 }));
1229
+ this.drivenParameterIds = rigs.map((rig) => rig.output.parameter);
1203
1230
  }
1204
1231
  /**
1205
1232
  * Advance every rig to the given wall-clock timestamp (milliseconds).
@@ -1270,6 +1297,8 @@ var PhysicsMotion = class {
1270
1297
  var DEG2RAD = Math.PI / 180;
1271
1298
  var RAD2DEG = 180 / Math.PI;
1272
1299
  var HairChainMotion = class {
1300
+ /** Every segment's output, chain by chain — what `emitSegment` writes. */
1301
+ drivenParameterIds;
1273
1302
  chainData;
1274
1303
  params;
1275
1304
  deformers;
@@ -1290,6 +1319,9 @@ var HairChainMotion = class {
1290
1319
  ),
1291
1320
  state: chain.segments.map(() => ({ angle: 0, angularVelocity: 0 }))
1292
1321
  }));
1322
+ this.drivenParameterIds = chains.flatMap(
1323
+ (c) => c.segments.map((s) => s.output.parameter)
1324
+ );
1293
1325
  }
1294
1326
  /**
1295
1327
  * Advance every chain to the given wall-clock timestamp (milliseconds).
@@ -1402,9 +1434,58 @@ var HairChainMotion = class {
1402
1434
  );
1403
1435
  }
1404
1436
  };
1437
+
1438
+ // src/iki-motion.ts
1439
+ var IkiMotion = class {
1440
+ /**
1441
+ * Idle ids, then rig outputs, then chain-segment outputs, deduplicated and
1442
+ * insertion-ordered. May name ids the model lacks (the player silently
1443
+ * drops writes to unknown ids) — intersect with the model's parameters if
1444
+ * you mirror into your own store.
1445
+ */
1446
+ drivenParameterIds;
1447
+ idle;
1448
+ physics;
1449
+ chains;
1450
+ constructor(model, read, sink) {
1451
+ const rigs = model.physics ?? [];
1452
+ const chains = model.physicsChains ?? [];
1453
+ this.idle = new IdleMotion(sink);
1454
+ this.physics = new PhysicsMotion(rigs, model.parameters, read, sink);
1455
+ this.chains = new HairChainMotion(
1456
+ chains,
1457
+ model.parameters,
1458
+ model.deformers ?? [],
1459
+ read,
1460
+ sink
1461
+ );
1462
+ this.drivenParameterIds = [
1463
+ .../* @__PURE__ */ new Set([
1464
+ ...this.idle.drivenParameterIds,
1465
+ ...this.physics.drivenParameterIds,
1466
+ ...this.chains.drivenParameterIds
1467
+ ])
1468
+ ];
1469
+ }
1470
+ /**
1471
+ * Advance idle, then physics, then chains to the same wall-clock timestamp
1472
+ * (milliseconds). Order is load-bearing: `PhysicsMotion` reads its input
1473
+ * parameters (typically `ParamAngleX/Z`) through `read`, and the head sway
1474
+ * idle wrote THIS frame is what the springs must lag behind; `HairChainMotion`
1475
+ * then resolves its anchor deformer's world rotation from that same
1476
+ * just-written pose (which may include a physics output). One timestamp for
1477
+ * all three keeps their dt in lockstep.
1478
+ */
1479
+ update(nowMs) {
1480
+ this.idle.update(nowMs);
1481
+ this.physics.update(nowMs);
1482
+ this.chains.update(nowMs);
1483
+ }
1484
+ };
1405
1485
  export {
1406
1486
  HairChainMotion,
1407
1487
  IdleMotion,
1488
+ IkiMotion,
1408
1489
  IkiPlayer,
1409
1490
  ParameterStore,
1410
1491
  PhysicsMotion,