@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/README.md +25 -31
- package/dist/index.d.mts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +81 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -3
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,
|