@melite/formats 0.0.0-stage → 0.1.5-pre-alpha
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/LICENSE +557 -0
- package/README.md +37 -2
- package/lib/b64.d.ts +2 -0
- package/lib/b64.js +7 -0
- package/lib/bodyPresets.d.ts +40 -0
- package/lib/bodyPresets.js +77 -0
- package/lib/captionSplitter.d.ts +49 -0
- package/lib/captionSplitter.js +172 -0
- package/lib/cardMutations.d.ts +52 -0
- package/lib/cardMutations.js +67 -0
- package/lib/downloads.d.ts +18 -0
- package/lib/downloads.js +64 -0
- package/lib/extractVideoFrame.d.ts +23 -0
- package/lib/extractVideoFrame.js +114 -0
- package/lib/glbLoad.d.ts +54 -0
- package/lib/glbLoad.js +96 -0
- package/lib/glbSkin.d.ts +14 -0
- package/lib/glbSkin.js +124 -0
- package/lib/handPose.d.ts +46 -0
- package/lib/handPose.js +122 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.js +25 -0
- package/lib/keyframeExtract.d.ts +46 -0
- package/lib/keyframeExtract.js +89 -0
- package/lib/maskPrep.d.ts +88 -0
- package/lib/maskPrep.js +386 -0
- package/lib/motionConstraints.d.ts +237 -0
- package/lib/motionConstraints.js +369 -0
- package/lib/motionEnvelope.d.ts +56 -0
- package/lib/motionEnvelope.js +59 -0
- package/lib/motionNpz.d.ts +120 -0
- package/lib/motionNpz.js +497 -0
- package/lib/npz.d.ts +24 -0
- package/lib/npz.js +158 -0
- package/lib/openPoseCanvas.d.ts +17 -0
- package/lib/openPoseCanvas.js +90 -0
- package/lib/pngMeta.d.ts +42 -0
- package/lib/pngMeta.js +212 -0
- package/lib/pngMetaInfo.d.ts +14 -0
- package/lib/pngMetaInfo.js +127 -0
- package/lib/poseCodec.d.ts +20 -0
- package/lib/poseCodec.js +61 -0
- package/lib/poseTransforms.d.ts +66 -0
- package/lib/poseTransforms.js +227 -0
- package/lib/poserSkin.d.ts +75 -0
- package/lib/poserSkin.js +141 -0
- package/lib/prompt-tags.json +132 -0
- package/lib/promptTags.d.ts +42 -0
- package/lib/promptTags.js +116 -0
- package/lib/psdExport.d.ts +28 -0
- package/lib/psdExport.js +272 -0
- package/lib/readFile.d.ts +9 -0
- package/lib/readFile.js +16 -0
- package/lib/renderSnapshot.d.ts +34 -0
- package/lib/renderSnapshot.js +55 -0
- package/lib/somaSkin.d.ts +82 -0
- package/lib/somaSkin.js +267 -0
- package/lib/textToPose.d.ts +394 -0
- package/lib/textToPose.js +607 -0
- package/lib/uploadToEstate.d.ts +25 -0
- package/lib/uploadToEstate.js +97 -0
- package/package.json +149 -4
package/lib/somaSkin.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* somaSkin — neutral SOMA-77 mesh skinning, a direct port of the kimodo demo's
|
|
3
|
+
* vendor/kimodo/kimodo/viz/soma_skin.py SOMASkin.skin()+lbs().
|
|
4
|
+
*
|
|
5
|
+
* The demo (and only the demo) renders Kimodo motions on a NEUTRAL body via
|
|
6
|
+
* LBS weights stored in skin_standard.npz (bind_vertices + faces +
|
|
7
|
+
* bind_rig_transform + lbs_indices + lbs_weights). That is the "base SOMAX
|
|
8
|
+
* mesh" that actually deforms — distinct from the poser backend's `soma`
|
|
9
|
+
* engine (a static mesh with no LBS weights) and from `anny`/`mhr` (parametric
|
|
10
|
+
* bodies with their own proportions). This module reproduces the demo's math
|
|
11
|
+
* client-side so the asset preview shows a Kimodo motion on the same neutral
|
|
12
|
+
* mesh the demo uses, with no parametric body shape imposed.
|
|
13
|
+
*
|
|
14
|
+
* Flow per frame:
|
|
15
|
+
* fk[j] = [[global_rot[j], global_pos[j]], [0,0,0,1]] (4×4, per joint)
|
|
16
|
+
* affine[j] = (fk[j] · inv(bind_rig[j]))[:3, :] (3×4, per joint)
|
|
17
|
+
* v' = Σ_w lbs_weights[v,w] · (affine[lbs_indices[v,w]] · [bind_v, 1])
|
|
18
|
+
*
|
|
19
|
+
* Units: meters throughout (Kimodo's native unit). No cm↔m conversion — that
|
|
20
|
+
* was a poser-route artifact that doesn't apply here.
|
|
21
|
+
*/
|
|
22
|
+
import { parseNpz } from "./npz.js";
|
|
23
|
+
import { apiFetch } from "@melite/catalog";
|
|
24
|
+
// The skin is a DECLARED provision on the kimodo family (models/
|
|
25
|
+
// motion/kimodo/model.py _SOMA_SKIN) — the provision door streams it
|
|
26
|
+
// from the weights mount and ACQUIRES it on demand (fetched direct
|
|
27
|
+
// from the DECL's pinned distribution, DECL-sha-gated before it
|
|
28
|
+
// lands) when missing. NEVER a vite-public
|
|
29
|
+
// blob: the old /editor/soma-skin-standard.npz path 404'd whenever
|
|
30
|
+
|
|
31
|
+
// Exported: the editor test wall pins the door shape.
|
|
32
|
+
export const SKIN_URL = "/v1/models/kimodo/provisions/skin_standard.npz";
|
|
33
|
+
const J = 77; // SOMA-77 joints
|
|
34
|
+
const W = 8; // max influencing joints per vertex in skin_standard.npz
|
|
35
|
+
let _skinPromise = null;
|
|
36
|
+
/** Invert a 4×4 matrix (column-major-agnostic: stored row-major as 16 floats). */
|
|
37
|
+
export function invert4(m) {
|
|
38
|
+
// Gauss-Jordan with partial pivoting. m is row-major (J*16 + col).
|
|
39
|
+
const a = Array.from(m);
|
|
40
|
+
const inv = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
41
|
+
for (let col = 0; col < 4; col++) {
|
|
42
|
+
// pivot
|
|
43
|
+
let piv = col, max = Math.abs(a[col * 4 + col]);
|
|
44
|
+
for (let r = col + 1; r < 4; r++) {
|
|
45
|
+
const v = Math.abs(a[r * 4 + col]);
|
|
46
|
+
if (v > max) {
|
|
47
|
+
max = v;
|
|
48
|
+
piv = r;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (max < 1e-12)
|
|
52
|
+
continue; // singular column — leave as-is
|
|
53
|
+
if (piv !== col) {
|
|
54
|
+
for (let c = 0; c < 4; c++) {
|
|
55
|
+
;
|
|
56
|
+
[a[col * 4 + c], a[piv * 4 + c]] = [a[piv * 4 + c], a[col * 4 + c]];
|
|
57
|
+
[inv[col * 4 + c], inv[piv * 4 + c]] = [inv[piv * 4 + c], inv[col * 4 + c]];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const diag = a[col * 4 + col];
|
|
61
|
+
for (let c = 0; c < 4; c++) {
|
|
62
|
+
a[col * 4 + c] /= diag;
|
|
63
|
+
inv[col * 4 + c] /= diag;
|
|
64
|
+
}
|
|
65
|
+
for (let r = 0; r < 4; r++) {
|
|
66
|
+
if (r === col)
|
|
67
|
+
continue;
|
|
68
|
+
const factor = a[r * 4 + col];
|
|
69
|
+
if (factor === 0)
|
|
70
|
+
continue;
|
|
71
|
+
for (let c = 0; c < 4; c++) {
|
|
72
|
+
a[r * 4 + c] -= factor * a[col * 4 + c];
|
|
73
|
+
inv[r * 4 + c] -= factor * inv[col * 4 + c];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return Float32Array.from(inv);
|
|
78
|
+
}
|
|
79
|
+
function asFloat32(arr) {
|
|
80
|
+
return arr instanceof Float32Array ? arr : new Float32Array(arr);
|
|
81
|
+
}
|
|
82
|
+
/** Extract the rest pose from row-major FORWARD bind transforms (J×16):
|
|
83
|
+
* translation column (tx, ty, tz) = indices 3, 7, 11 of each 4×4, and the
|
|
84
|
+
* 3×3 rotation block = 0,1,2 / 4,5,6 / 8,9,10. This is the TS equivalent
|
|
85
|
+
* of Kimodo's neutral_joints — the motion card's pre-inference pose (see
|
|
86
|
+
* SomaSkin.restJoints doc). Shared by both loaders: the npz carries the
|
|
87
|
+
* forward binds directly; the GLB adapter inverts glTF boneInverses into
|
|
88
|
+
* the same row-major forward shape first. */
|
|
89
|
+
export function restPoseFromBind(bindRig, jointCount) {
|
|
90
|
+
const restJoints = new Float32Array(jointCount * 3);
|
|
91
|
+
const restRots = new Float32Array(jointCount * 9);
|
|
92
|
+
for (let j = 0; j < jointCount; j++) {
|
|
93
|
+
const b = j * 16;
|
|
94
|
+
restJoints[j * 3] = bindRig[b + 3];
|
|
95
|
+
restJoints[j * 3 + 1] = bindRig[b + 7];
|
|
96
|
+
restJoints[j * 3 + 2] = bindRig[b + 11];
|
|
97
|
+
restRots[j * 9] = bindRig[b + 0];
|
|
98
|
+
restRots[j * 9 + 1] = bindRig[b + 1];
|
|
99
|
+
restRots[j * 9 + 2] = bindRig[b + 2];
|
|
100
|
+
restRots[j * 9 + 3] = bindRig[b + 4];
|
|
101
|
+
restRots[j * 9 + 4] = bindRig[b + 5];
|
|
102
|
+
restRots[j * 9 + 5] = bindRig[b + 6];
|
|
103
|
+
restRots[j * 9 + 6] = bindRig[b + 8];
|
|
104
|
+
restRots[j * 9 + 7] = bindRig[b + 9];
|
|
105
|
+
restRots[j * 9 + 8] = bindRig[b + 10];
|
|
106
|
+
}
|
|
107
|
+
return { restJoints, restRots };
|
|
108
|
+
}
|
|
109
|
+
/** Load + cache the neutral SOMA skin (one fetch per session). */
|
|
110
|
+
export async function loadSomaSkin() {
|
|
111
|
+
if (_skinPromise)
|
|
112
|
+
return _skinPromise;
|
|
113
|
+
_skinPromise = (async () => {
|
|
114
|
+
const res = await apiFetch(SKIN_URL);
|
|
115
|
+
if (!res.ok)
|
|
116
|
+
throw new Error(`Failed to fetch SOMA skin: ${res.status}`);
|
|
117
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
118
|
+
const arrays = await parseNpz(bytes);
|
|
119
|
+
const bv = arrays.get("bind_vertices");
|
|
120
|
+
const fc = arrays.get("faces");
|
|
121
|
+
const brt = arrays.get("bind_rig_transform");
|
|
122
|
+
const li = arrays.get("lbs_indices");
|
|
123
|
+
const lw = arrays.get("lbs_weights");
|
|
124
|
+
if (!bv || !fc || !brt || !li || !lw) {
|
|
125
|
+
throw new Error(`SOMA skin missing keys (have: ${[...arrays.keys()].join(",")})`);
|
|
126
|
+
}
|
|
127
|
+
const bindVertices = asFloat32(bv.data);
|
|
128
|
+
const bindRig = asFloat32(brt.data); // (77, 4, 4) row-major
|
|
129
|
+
// Precompute per-joint inverse bind transforms.
|
|
130
|
+
const bindRigInv = new Float32Array(J * 16);
|
|
131
|
+
for (let j = 0; j < J; j++) {
|
|
132
|
+
const m = bindRig.subarray(j * 16, j * 16 + 16);
|
|
133
|
+
bindRigInv.set(invert4(m), j * 16);
|
|
134
|
+
}
|
|
135
|
+
// Neutral / rest pose — extracted from the row-major forward binds
|
|
136
|
+
// (shared helper; see restPoseFromBind).
|
|
137
|
+
const { restJoints, restRots } = restPoseFromBind(bindRig, J);
|
|
138
|
+
// faces (int64) and lbs_indices (int32) arrive as BigInt64Array / Uint32Array
|
|
139
|
+
const faceSrc = fc.data instanceof BigInt64Array
|
|
140
|
+
? Array.from(fc.data, (x) => Number(x))
|
|
141
|
+
: Array.from(fc.data);
|
|
142
|
+
const faces = Uint32Array.from(faceSrc);
|
|
143
|
+
const lbsIndices = li.data instanceof Uint32Array
|
|
144
|
+
? li.data
|
|
145
|
+
: li.data instanceof BigInt64Array
|
|
146
|
+
? Uint32Array.from(li.data, (x) => Number(x))
|
|
147
|
+
: Uint32Array.from(li.data);
|
|
148
|
+
const lbsWeights = asFloat32(lw.data);
|
|
149
|
+
return {
|
|
150
|
+
bindVertices,
|
|
151
|
+
faces,
|
|
152
|
+
lbsIndices,
|
|
153
|
+
lbsWeights,
|
|
154
|
+
bindRigInv,
|
|
155
|
+
restJoints,
|
|
156
|
+
restRots,
|
|
157
|
+
vertexCount: bindVertices.length / 3,
|
|
158
|
+
faceCount: faces.length / 3,
|
|
159
|
+
jointCount: J,
|
|
160
|
+
influences: W,
|
|
161
|
+
};
|
|
162
|
+
})();
|
|
163
|
+
try {
|
|
164
|
+
return await _skinPromise;
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
// A failed load must not poison the session cache: the next call
|
|
168
|
+
// re-fetches (a transient gateway blip during the first preview must
|
|
169
|
+
// not brick every later motion preview). Successes stay cached.
|
|
170
|
+
_skinPromise = null;
|
|
171
|
+
throw e;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Build per-joint (3,4) affine matrices for one frame:
|
|
176
|
+
* affine[j] = (fk[j] · bindRigInv[j])[:3, :]
|
|
177
|
+
* fk[j] is the global posed transform (global_rot → rotation, global_pos →
|
|
178
|
+
* translation). Output is `affines` length J*12 (row-major 3×4 per joint).
|
|
179
|
+
*/
|
|
180
|
+
function buildAffines(globalRot, // (J, 9) row-major 3×3 per joint
|
|
181
|
+
globalPos, // (J, 3)
|
|
182
|
+
bindRigInv, // (J, 16)
|
|
183
|
+
out, // (J, 12)
|
|
184
|
+
jointCount) {
|
|
185
|
+
for (let j = 0; j < jointCount; j++) {
|
|
186
|
+
const r = j * 9;
|
|
187
|
+
const p = j * 3;
|
|
188
|
+
const bi = j * 16;
|
|
189
|
+
// fk[j] row-major 4×4 (rotation block + translation; last row 0,0,0,1)
|
|
190
|
+
// fk @ bindRigInv: compute the 3×4 top rows only (we discard row 3).
|
|
191
|
+
const a0 = bindRigInv[bi + 0], a1 = bindRigInv[bi + 1], a2 = bindRigInv[bi + 2], a3 = bindRigInv[bi + 3];
|
|
192
|
+
const a4 = bindRigInv[bi + 4], a5 = bindRigInv[bi + 5], a6 = bindRigInv[bi + 6], a7 = bindRigInv[bi + 7];
|
|
193
|
+
const a8 = bindRigInv[bi + 8], a9 = bindRigInv[bi + 9], a10 = bindRigInv[bi + 10], a11 = bindRigInv[bi + 11];
|
|
194
|
+
// (Not needed for top 3 rows:) a12,a13,a14,a15 — last row of inv, multiplied
|
|
195
|
+
// by fk's last row [0,0,0,1], contributes fk doesn't add from row 4.
|
|
196
|
+
// fk rotation block:
|
|
197
|
+
const fr0 = globalRot[r], fr1 = globalRot[r + 1], fr2 = globalRot[r + 2];
|
|
198
|
+
const fr3 = globalRot[r + 3], fr4 = globalRot[r + 4], fr5 = globalRot[r + 5];
|
|
199
|
+
const fr6 = globalRot[r + 6], fr7 = globalRot[r + 7], fr8 = globalRot[r + 8];
|
|
200
|
+
// fk @ inv : out_row_i = fk_row_i · inv_col
|
|
201
|
+
// Row 0 of result (3 cols × 4):
|
|
202
|
+
const o0 = fr0 * a0 + fr1 * a4 + fr2 * a8;
|
|
203
|
+
const o1 = fr0 * a1 + fr1 * a5 + fr2 * a9;
|
|
204
|
+
const o2 = fr0 * a2 + fr1 * a6 + fr2 * a10;
|
|
205
|
+
const o3 = fr0 * a3 + fr1 * a7 + fr2 * a11 + globalPos[p];
|
|
206
|
+
const o4 = fr3 * a0 + fr4 * a4 + fr5 * a8;
|
|
207
|
+
const o5 = fr3 * a1 + fr4 * a5 + fr5 * a9;
|
|
208
|
+
const o6 = fr3 * a2 + fr4 * a6 + fr5 * a10;
|
|
209
|
+
const o7 = fr3 * a3 + fr4 * a7 + fr5 * a11 + globalPos[p + 1];
|
|
210
|
+
const o8 = fr6 * a0 + fr7 * a4 + fr8 * a8;
|
|
211
|
+
const o9 = fr6 * a1 + fr7 * a5 + fr8 * a9;
|
|
212
|
+
const o10 = fr6 * a2 + fr7 * a6 + fr8 * a10;
|
|
213
|
+
const o11 = fr6 * a3 + fr7 * a7 + fr8 * a11 + globalPos[p + 2];
|
|
214
|
+
const o = j * 12;
|
|
215
|
+
out[o] = o0;
|
|
216
|
+
out[o + 1] = o1;
|
|
217
|
+
out[o + 2] = o2;
|
|
218
|
+
out[o + 3] = o3;
|
|
219
|
+
out[o + 4] = o4;
|
|
220
|
+
out[o + 5] = o5;
|
|
221
|
+
out[o + 6] = o6;
|
|
222
|
+
out[o + 7] = o7;
|
|
223
|
+
out[o + 8] = o8;
|
|
224
|
+
out[o + 9] = o9;
|
|
225
|
+
out[o + 10] = o10;
|
|
226
|
+
out[o + 11] = o11;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
export function deformFrame(globalRot, globalPos, skin, out) {
|
|
242
|
+
const J_ = skin.jointCount;
|
|
243
|
+
const W_ = skin.influences;
|
|
244
|
+
const affines = new Float32Array(J_ * 12);
|
|
245
|
+
buildAffines(globalRot, globalPos, skin.bindRigInv, affines, J_);
|
|
246
|
+
const { bindVertices, lbsIndices, lbsWeights } = skin;
|
|
247
|
+
const V = skin.vertexCount;
|
|
248
|
+
for (let v = 0; v < V; v++) {
|
|
249
|
+
const vi = v * 3;
|
|
250
|
+
const bx = bindVertices[vi], by = bindVertices[vi + 1], bz = bindVertices[vi + 2];
|
|
251
|
+
let ox = 0, oy = 0, oz = 0;
|
|
252
|
+
const wi = v * W_;
|
|
253
|
+
for (let widx = 0; widx < W_; widx++) {
|
|
254
|
+
const weight = lbsWeights[wi + widx];
|
|
255
|
+
if (weight === 0)
|
|
256
|
+
continue;
|
|
257
|
+
const ai = lbsIndices[wi + widx] * 12;
|
|
258
|
+
ox += weight * (affines[ai] * bx + affines[ai + 1] * by + affines[ai + 2] * bz + affines[ai + 3]);
|
|
259
|
+
oy += weight * (affines[ai + 4] * bx + affines[ai + 5] * by + affines[ai + 6] * bz + affines[ai + 7]);
|
|
260
|
+
oz += weight * (affines[ai + 8] * bx + affines[ai + 9] * by + affines[ai + 10] * bz + affines[ai + 11]);
|
|
261
|
+
}
|
|
262
|
+
out[vi] = ox;
|
|
263
|
+
out[vi + 1] = oy;
|
|
264
|
+
out[vi + 2] = oz;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=somaSkin.js.map
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text-to-pose client — submit text prompts, poll for results, manage cache.
|
|
3
|
+
*
|
|
4
|
+
* Backend: custom_nodes/melite-poser-nodes/server.py (proxied via melite-head)
|
|
5
|
+
* Endpoints:
|
|
6
|
+
* POST /poser/text-to-pose Submit generation request
|
|
7
|
+
* GET /poser/text-to-pose/jobs/{job_id} Poll job status
|
|
8
|
+
* DELETE /poser/text-to-pose/jobs/{job_id} Cancel running job
|
|
9
|
+
* GET /poser/text-to-pose/cache List cached entries
|
|
10
|
+
* DELETE /poser/text-to-pose/cache/{hash} Delete cache entry
|
|
11
|
+
* POST /poser/text-to-pose/cache/clear Clear all cache entries
|
|
12
|
+
*/
|
|
13
|
+
/** Slice a single frame's vertices out of a multi-frame base64 vertex buffer.
|
|
14
|
+
*
|
|
15
|
+
* Async because decoding the (potentially huge) multi-frame buffer is offloaded
|
|
16
|
+
* to the browser's native base64 decoder. Returns a base64 string containing
|
|
17
|
+
* just the one frame's float32 positions.
|
|
18
|
+
*
|
|
19
|
+
* @param multiB64 base64-encoded buffer of shape (T, V, 3) float32
|
|
20
|
+
* @param frameIdx which frame to slice (clamped to [0, T-1])
|
|
21
|
+
* @param vertexCount V — defaults to the SOMA-77 canonical count
|
|
22
|
+
* @returns base64-encoded single-frame buffer, or null on decode failure */
|
|
23
|
+
export declare function sliceFrameAsB64(multiB64: string, frameIdx: number, vertexCount?: number): Promise<string | null>;
|
|
24
|
+
export type CameraView = "front" | "side" | "three-quarter" | "demo";
|
|
25
|
+
export type CfgType = "nocfg" | "regular" | "separated";
|
|
26
|
+
export type FrameStrategy = "auto_peak" | "auto_mid" | "manual" | "all";
|
|
27
|
+
export type JobStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
|
|
28
|
+
/** A prompt segment for the multi-stack timeline. */
|
|
29
|
+
export interface PromptSegmentInput {
|
|
30
|
+
text: string;
|
|
31
|
+
duration_sec: number;
|
|
32
|
+
}
|
|
33
|
+
/** A prompt segment with resolved frame ranges — returned by the backend. */
|
|
34
|
+
export interface PromptSegmentOutput {
|
|
35
|
+
text: string;
|
|
36
|
+
start_frame: number;
|
|
37
|
+
end_frame: number;
|
|
38
|
+
frame_count: number;
|
|
39
|
+
}
|
|
40
|
+
export interface TextToPoseRequest {
|
|
41
|
+
prompt: string;
|
|
42
|
+
seed: number | null;
|
|
43
|
+
duration_sec: number;
|
|
44
|
+
diffusion_steps?: number;
|
|
45
|
+
camera_view?: CameraView;
|
|
46
|
+
frame_strategy?: FrameStrategy;
|
|
47
|
+
cfg_type?: CfgType;
|
|
48
|
+
cfg_weight?: number[] | null;
|
|
49
|
+
num_samples?: number;
|
|
50
|
+
num_transition_frames?: number;
|
|
51
|
+
use_cache?: boolean;
|
|
52
|
+
confidence?: number;
|
|
53
|
+
ear_confidence?: number;
|
|
54
|
+
width?: number;
|
|
55
|
+
height?: number;
|
|
56
|
+
frame_indices?: number[] | null;
|
|
57
|
+
/** Per-segment prompts with explicit durations — "timeline command stacking".
|
|
58
|
+
* When present, the backend gives each segment its own frame budget instead
|
|
59
|
+
* of splitting a single prompt evenly. */
|
|
60
|
+
prompt_segments?: PromptSegmentInput[];
|
|
61
|
+
|
|
62
|
+
post_processing?: boolean;
|
|
63
|
+
/** Root margin for post-processing (meters). Lower = tighter root pinning. */
|
|
64
|
+
root_margin?: number;
|
|
65
|
+
}
|
|
66
|
+
/** One frame of COCO-18 keypoints: [x0, y0, c0, x1, y1, c1, ...] (54 floats). */
|
|
67
|
+
export type FlatKeypoints = number[];
|
|
68
|
+
/** One frame of COCO-18 3D keypoints: [[x, y, z], ...] (18 joints). */
|
|
69
|
+
export type Coco18_3D = [number, number, number][];
|
|
70
|
+
export interface PoseMotion {
|
|
71
|
+
fps: number;
|
|
72
|
+
frame_count: number;
|
|
73
|
+
duration_sec: number;
|
|
74
|
+
skeleton: string;
|
|
75
|
+
model: string;
|
|
76
|
+
camera_view: CameraView;
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
peak_frame_idx: number;
|
|
80
|
+
/** Prompt segments with resolved frame ranges — for accurate timeline display. */
|
|
81
|
+
prompt_segments?: PromptSegmentOutput[];
|
|
82
|
+
/** Flat 2D keypoints per frame — ready for /poser/render. */
|
|
83
|
+
frames_flat: FlatKeypoints[];
|
|
84
|
+
/** 3D keypoints per frame — for 3D preview / rotation. */
|
|
85
|
+
frames_coco18_3d: Coco18_3D[];
|
|
86
|
+
/** Full SOMA-77 3D keypoints per frame (T, 77, 3) — lossless, for /poser/skin/deform_soma. */
|
|
87
|
+
frames_soma77_3d?: [number, number, number][][];
|
|
88
|
+
/** Full SOMA-77 global rotation matrices per frame (T, 77, 3, 3) — for correct LBS skinning. */
|
|
89
|
+
frames_soma77_rotmat?: [number, number, number][][][];
|
|
90
|
+
/** Pre-computed deformed mesh vertices per frame — base64-encoded float32 binary (T, V, 3).
|
|
91
|
+
* Decode via: atob() → ArrayBuffer → Float32Array, then reshape [T * V * 3] → [T][V][3].
|
|
92
|
+
* NOT a JSON array — see deformed_vertices_b64 instead. */
|
|
93
|
+
deformed_vertices_b64?: string;
|
|
94
|
+
/** Base64-encoded .npz of SOMA-77 joints — for round-tripping to other tools. */
|
|
95
|
+
soma_npz_b64?: string;
|
|
96
|
+
/** Foot-contact probabilities per frame (T, 4): [L_heel, R_heel, L_toe, R_toe] in [0,1]. */
|
|
97
|
+
foot_contacts?: number[][];
|
|
98
|
+
/** Number of samples actually generated (may be > 1 when num_samples > 1). */
|
|
99
|
+
num_samples?: number;
|
|
100
|
+
/** Alternate samples when num_samples > 1. Each entry is a full SOMA-77 motion
|
|
101
|
+
* with its own deformed verts, so the UI can render clickable alternates. */
|
|
102
|
+
alt_samples?: AltSample[];
|
|
103
|
+
/** Identity model the deformed_vertices were skinned to. Echoed by the
|
|
104
|
+
* backend so the frontend can detect a mismatch if the user switched
|
|
105
|
+
* identity after generating (verts/topo would be wrong). Missing = unknown
|
|
106
|
+
* = do NOT drop (the historical default before this field existed). */
|
|
107
|
+
identity_model?: string;
|
|
108
|
+
/** Identity coefficients used for the skinning pass (echoed so capture-
|
|
109
|
+
* reference can build a matching ReferenceMeshSpec without round-tripping
|
|
110
|
+
* through the bind/mesh endpoint). Missing = defaults were used. */
|
|
111
|
+
identity_coeffs?: number[] | null;
|
|
112
|
+
/** Vertex count of the deformed mesh (V, not V*3). Informational. */
|
|
113
|
+
vertex_count?: number;
|
|
114
|
+
}
|
|
115
|
+
export interface AltSample {
|
|
116
|
+
sample_idx: number;
|
|
117
|
+
frames_soma77_3d: [number, number, number][][];
|
|
118
|
+
frames_soma77_rotmat: [number, number, number][][][];
|
|
119
|
+
deformed_vertices_b64: string;
|
|
120
|
+
}
|
|
121
|
+
export interface TextToPoseResult {
|
|
122
|
+
status: "success";
|
|
123
|
+
cache_hit: boolean;
|
|
124
|
+
inference_time_sec: number;
|
|
125
|
+
motion: PoseMotion;
|
|
126
|
+
}
|
|
127
|
+
export interface SubmitResponse {
|
|
128
|
+
job_id: string;
|
|
129
|
+
status: "pending";
|
|
130
|
+
poll_url: string;
|
|
131
|
+
}
|
|
132
|
+
export interface JobStatusResponse {
|
|
133
|
+
job_id: string;
|
|
134
|
+
status: JobStatus;
|
|
135
|
+
result?: TextToPoseResult;
|
|
136
|
+
error?: string;
|
|
137
|
+
error_class?: string;
|
|
138
|
+
}
|
|
139
|
+
export interface CacheEntry {
|
|
140
|
+
hash: string;
|
|
141
|
+
prompt: string;
|
|
142
|
+
seed: number | null;
|
|
143
|
+
duration_sec: number;
|
|
144
|
+
model_name: string;
|
|
145
|
+
created_at: number;
|
|
146
|
+
accessed_at: number;
|
|
147
|
+
size_bytes: number;
|
|
148
|
+
frame_count: number;
|
|
149
|
+
peak_frame_idx: number;
|
|
150
|
+
}
|
|
151
|
+
export interface CacheListResponse {
|
|
152
|
+
entries: CacheEntry[];
|
|
153
|
+
total_size_bytes: number;
|
|
154
|
+
entry_count: number;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Submit a text-to-pose generation request.
|
|
158
|
+
* Returns immediately with a job_id — poll via pollJob() or waitForJob().
|
|
159
|
+
*/
|
|
160
|
+
export declare function submitTextToPose(req: TextToPoseRequest): Promise<SubmitResponse>;
|
|
161
|
+
/**
|
|
162
|
+
* Poll job status once. Returns the current status + result if completed.
|
|
163
|
+
*/
|
|
164
|
+
export declare function pollJob(jobId: string, signal?: AbortSignal): Promise<JobStatusResponse>;
|
|
165
|
+
/**
|
|
166
|
+
* Wait for a job to reach a terminal state (completed/failed/cancelled).
|
|
167
|
+
* Polls every `intervalMs` until `timeoutMs`.
|
|
168
|
+
*
|
|
169
|
+
* Calls `onUpdate` on each poll if provided — useful for progress UI.
|
|
170
|
+
*/
|
|
171
|
+
export declare function waitForJob(jobId: string, opts?: {
|
|
172
|
+
intervalMs?: number;
|
|
173
|
+
timeoutMs?: number;
|
|
174
|
+
onUpdate?: (status: JobStatusResponse) => void;
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
signal?: AbortSignal;
|
|
180
|
+
}): Promise<TextToPoseResult>;
|
|
181
|
+
export declare function cancelJob(jobId: string): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Submit a text-to-pose request and wait for the result.
|
|
184
|
+
* This is the simplest call for UI components that just want the final motion.
|
|
185
|
+
*/
|
|
186
|
+
export declare function generatePose(req: TextToPoseRequest, opts?: {
|
|
187
|
+
intervalMs?: number;
|
|
188
|
+
timeoutMs?: number;
|
|
189
|
+
onUpdate?: (status: JobStatusResponse) => void;
|
|
190
|
+
signal?: AbortSignal;
|
|
191
|
+
}): Promise<TextToPoseResult>;
|
|
192
|
+
export declare function listPoseCache(): Promise<CacheListResponse>;
|
|
193
|
+
export declare function deletePoseCacheEntry(hash: string): Promise<void>;
|
|
194
|
+
export declare function clearPoseCache(): Promise<number>;
|
|
195
|
+
export interface ModelInfo {
|
|
196
|
+
id: string;
|
|
197
|
+
label: string;
|
|
198
|
+
/** True if the model checkpoint is available locally. */
|
|
199
|
+
local: boolean;
|
|
200
|
+
is_default: boolean;
|
|
201
|
+
}
|
|
202
|
+
export declare function listModels(): Promise<{
|
|
203
|
+
models: ModelInfo[];
|
|
204
|
+
default: string;
|
|
205
|
+
}>;
|
|
206
|
+
export interface RegistryVersion {
|
|
207
|
+
display: string;
|
|
208
|
+
short_key: string;
|
|
209
|
+
local: boolean;
|
|
210
|
+
is_g1: boolean;
|
|
211
|
+
}
|
|
212
|
+
export interface RegistrySkeleton {
|
|
213
|
+
label: string;
|
|
214
|
+
key: string;
|
|
215
|
+
versions: RegistryVersion[];
|
|
216
|
+
}
|
|
217
|
+
export interface RegistryTree {
|
|
218
|
+
datasets: string[];
|
|
219
|
+
tree: Record<string, {
|
|
220
|
+
skeletons: RegistrySkeleton[];
|
|
221
|
+
}>;
|
|
222
|
+
default_dataset: string | null;
|
|
223
|
+
default_skeleton: string | null;
|
|
224
|
+
default_version: string;
|
|
225
|
+
}
|
|
226
|
+
export declare function listRegistry(): Promise<RegistryTree>;
|
|
227
|
+
export interface KimodoExampleMeta {
|
|
228
|
+
text?: string;
|
|
229
|
+
texts?: string[];
|
|
230
|
+
duration?: number;
|
|
231
|
+
num_samples?: number;
|
|
232
|
+
seed?: number;
|
|
233
|
+
diffusion_steps?: number;
|
|
234
|
+
cfg?: {
|
|
235
|
+
enabled?: boolean;
|
|
236
|
+
text_weight?: number;
|
|
237
|
+
constraint_weight?: number;
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
export interface KimodoExample {
|
|
241
|
+
id: string;
|
|
242
|
+
name: string;
|
|
243
|
+
model: string;
|
|
244
|
+
has_motion: boolean;
|
|
245
|
+
has_constraints: boolean;
|
|
246
|
+
meta: KimodoExampleMeta;
|
|
247
|
+
}
|
|
248
|
+
export declare function listExamples(model?: string): Promise<{
|
|
249
|
+
examples: KimodoExample[];
|
|
250
|
+
}>;
|
|
251
|
+
export declare function loadExample(model: string, exampleId: string, loadGt?: boolean): Promise<{
|
|
252
|
+
id: string;
|
|
253
|
+
model: string;
|
|
254
|
+
meta: KimodoExampleMeta;
|
|
255
|
+
motion_b64?: string;
|
|
256
|
+
motion_size?: number;
|
|
257
|
+
constraints?: unknown[];
|
|
258
|
+
}>;
|
|
259
|
+
export declare function exportBVH(framesSoma77: number[][][], framesRotmat: number[][][][], fps: number): Promise<{
|
|
260
|
+
bvh: string;
|
|
261
|
+
frames: number;
|
|
262
|
+
joints: number;
|
|
263
|
+
}>;
|
|
264
|
+
/** Download a BVH file from SOMA-77 motion data. */
|
|
265
|
+
export declare function downloadBVH(motion: PoseMotion, standardTpose?: boolean): Promise<void>;
|
|
266
|
+
/** Export motion as AMASS NPZ (SMPL-X compatible). */
|
|
267
|
+
export declare function downloadAMASSNpz(motion: PoseMotion): Promise<void>;
|
|
268
|
+
|
|
269
|
+
export declare function downloadG1Csv(motion: PoseMotion): Promise<void>;
|
|
270
|
+
/**
|
|
271
|
+
* Export motion as a rigged FBX file (Mixamo-compatible joint names).
|
|
272
|
+
*
|
|
273
|
+
* The backend renders an FBX 7.4.0 ASCII skeleton with the full 77-joint
|
|
274
|
+
* hierarchy and baked animation — importable by Blender, Maya, Unity, and
|
|
275
|
+
* Unreal. Mixamo naming enables automatic retargeting onto Mixamo rigs.
|
|
276
|
+
*/
|
|
277
|
+
export declare function downloadFBX(motion: PoseMotion, mixamo?: boolean): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* Render a 2D pose video server-side via matplotlib + imageio.
|
|
280
|
+
* Higher-quality and browser-independent than client-side MediaRecorder.
|
|
281
|
+
*/
|
|
282
|
+
export declare function renderServerVideo(motion: PoseMotion, opts?: {
|
|
283
|
+
width?: number;
|
|
284
|
+
height?: number;
|
|
285
|
+
background?: string;
|
|
286
|
+
}): Promise<Blob>;
|
|
287
|
+
/** Render and download a server-side 2D pose video. */
|
|
288
|
+
export declare function downloadServerVideo(motion: PoseMotion, opts?: {
|
|
289
|
+
width?: number;
|
|
290
|
+
height?: number;
|
|
291
|
+
background?: string;
|
|
292
|
+
}): Promise<void>;
|
|
293
|
+
export interface ConstraintEntry {
|
|
294
|
+
name: string;
|
|
295
|
+
filename: string;
|
|
296
|
+
saved_at: number;
|
|
297
|
+
}
|
|
298
|
+
export declare function listConstraints(): Promise<ConstraintEntry[]>;
|
|
299
|
+
export declare function saveConstraints(name: string, constraints: unknown): Promise<void>;
|
|
300
|
+
export declare function loadConstraints(filename: string): Promise<unknown>;
|
|
301
|
+
export declare function deleteConstraints(filename: string): Promise<void>;
|
|
302
|
+
export interface SceneEntry {
|
|
303
|
+
name: string;
|
|
304
|
+
filename: string;
|
|
305
|
+
saved_at: number;
|
|
306
|
+
}
|
|
307
|
+
export declare function listScenes(): Promise<SceneEntry[]>;
|
|
308
|
+
export declare function saveScene(name: string, scene: unknown): Promise<void>;
|
|
309
|
+
export declare function loadScene(filename: string): Promise<unknown>;
|
|
310
|
+
export declare function deleteScene(filename: string): Promise<void>;
|
|
311
|
+
/**
|
|
312
|
+
* Pick a specific frame from a PoseMotion's flat keypoints.
|
|
313
|
+
* Clamps to valid range.
|
|
314
|
+
*/
|
|
315
|
+
export declare function pickFrame(motion: PoseMotion, idx: number): FlatKeypoints;
|
|
316
|
+
/**
|
|
317
|
+
* Fetch the 77-joint SOMA-71 rest-pose (bind-pose) positions for the
|
|
318
|
+
* requested identity model. Used by Pose Studio's "Load Model" button to
|
|
319
|
+
* enter Edit Mode WITHOUT generating a motion first — the user can grab
|
|
320
|
+
* joints and pose the character manually.
|
|
321
|
+
*
|
|
322
|
+
* The endpoint accepts both GET (returns the neutral-default joints) and
|
|
323
|
+
* POST (applies the supplied identity morphs first, returns joints that
|
|
324
|
+
* match the shaped body). The POST path is the fully-dynamic one: Anny's
|
|
325
|
+
* height/proportions sliders reshape BOTH the mesh AND the skeleton, so
|
|
326
|
+
* the edit-mode wires always match the body the user sees. Without it the
|
|
327
|
+
* skeleton would stay one fixed size (the static SOMA-77 reference) while
|
|
328
|
+
* the mesh grew/shrank underneath it.
|
|
329
|
+
*
|
|
330
|
+
* Calls GET /poser/skin/bind_joints?identity_model=… when no identity spec
|
|
331
|
+
* is supplied, POST /poser/skin/bind_joints with a JSON body otherwise.
|
|
332
|
+
*/
|
|
333
|
+
export declare function fetchBindJoints(identityModel?: string,
|
|
334
|
+
/**
|
|
335
|
+
* Optional identity spec — when ANY of these is supplied, the call
|
|
336
|
+
* switches to POST and the backend applies them via
|
|
337
|
+
* `SomaXSkin.set_identity(coeffs, local_changes=, custom_targets=)`
|
|
338
|
+
* before reading joints, so the returned positions match the shaped
|
|
339
|
+
* body. Mirrors the deformSomaJoints / shaped-mesh contract.
|
|
340
|
+
*/
|
|
341
|
+
identityCoeffs?: number[] | null, localChanges?: Record<string, number> | null, customTargets?: Record<string, number> | null): Promise<{
|
|
342
|
+
joints: [number, number, number][];
|
|
343
|
+
identity_model: string;
|
|
344
|
+
}>;
|
|
345
|
+
/**
|
|
346
|
+
* Preload (warm up) a model on the GPU so the first Generate is fast.
|
|
347
|
+
*
|
|
348
|
+
* NOTE: The old forge /admin/load endpoint was removed in the 3-container
|
|
349
|
+
* architecture cleanup. The KimodoPoseService singleton inside ComfyUI
|
|
350
|
+
* now loads lazily on first inference and stays resident, so explicit
|
|
351
|
+
* preloading is unnecessary. This is kept as a no-op so the frontend's
|
|
352
|
+
* "Load Model" button doesn't error.
|
|
353
|
+
*/
|
|
354
|
+
export declare function preloadForgeService(_service: string, _model?: string): Promise<{
|
|
355
|
+
status: string;
|
|
356
|
+
service: string;
|
|
357
|
+
}>;
|
|
358
|
+
/**
|
|
359
|
+
* Re-skin the mesh for edited SOMA-77 joint positions.
|
|
360
|
+
* Calls /poser/skin/deform_soma with binary response for speed.
|
|
361
|
+
* Used during interactive pose editing — debounced by the caller.
|
|
362
|
+
*
|
|
363
|
+
* @param joints Float32Array of 77 x 3 joint positions
|
|
364
|
+
* @param identityModel Which mesh to skin ("soma" | "mhr" | "anny"). Must
|
|
365
|
+
* match the mesh the viewer is rendering, otherwise the deformed vertices
|
|
366
|
+
* won't match the bind-mesh topology and the pose will look wrong.
|
|
367
|
+
* @param jointsRot Optional Float32Array of 77 x 9 global rotation matrices
|
|
368
|
+
* (row-major 3x3 per joint). When provided, the backend skins directly with
|
|
369
|
+
* these exact rotations (most accurate). When omitted, the backend derives
|
|
370
|
+
* approximate rotations from bone directions.
|
|
371
|
+
* @returns base64-encoded float32 deformed vertices (V x 3) for one frame
|
|
372
|
+
*/
|
|
373
|
+
export declare function deformSomaJoints(joints: Float32Array, identityModel?: string, jointsRot?: Float32Array | null,
|
|
374
|
+
/**
|
|
375
|
+
* Identity spec — when supplied, the backend applies them via
|
|
376
|
+
* `SomaXSkin.set_identity(coeffs, local_changes=, custom_targets=)` before
|
|
377
|
+
* skinning so the re-skinned verts match the loaded character (not the
|
|
378
|
+
* default soma bind pose). Without these, edit-mode re-skin would snap the
|
|
379
|
+
* mesh back to the un-morphed body on the first joint drag. All three are
|
|
380
|
+
* optional and model-specific (Anny uses all three; MHR ignores
|
|
381
|
+
* local_changes/custom_targets; SOMA ignores all three).
|
|
382
|
+
*/
|
|
383
|
+
identityCoeffs?: number[] | null, localChanges?: Record<string, number> | null, customTargets?: Record<string, number> | null,
|
|
384
|
+
/**
|
|
385
|
+
* Pre-edit global rotation matrices (77×9, row-major 3×3 per joint) captured
|
|
386
|
+
* BEFORE the user dragged any joint. When supplied (and `jointsRot` is null),
|
|
387
|
+
* the backend recomputes the swing (bone direction) from `joints` while
|
|
388
|
+
* preserving the twist (roll around the bone axis) from these — so dragging
|
|
389
|
+
* a joint updates the bone direction without erasing forearm pronation,
|
|
390
|
+
* neck yaw, etc. This is the edit-mode drag path; `jointsRot` (explicit
|
|
391
|
+
* rotations) takes precedence when both are set.
|
|
392
|
+
*/
|
|
393
|
+
baseRotmats?: Float32Array | null): Promise<string>;
|
|
394
|
+
//# sourceMappingURL=textToPose.d.ts.map
|