cozyclay 1.2.0 → 1.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/CHANGELOG.md +85 -0
- package/README.md +31 -0
- package/THIRD_PARTY_NOTICES.md +37 -1
- package/bin/cozyclay.mjs +51 -2
- package/dist/ai-camera-control/index.html +406 -0
- package/dist/app/index.html +5 -5
- package/dist/assets/app-B3U5aut1.js +4811 -0
- package/dist/assets/app-BrRF0wso.css +1 -0
- package/dist/assets/vision_bundle-jFkh-fIS.js +41 -0
- package/dist/fonts/InstrumentSerif-OFL.txt +93 -0
- package/dist/fonts/Inter-OFL.txt +92 -0
- package/dist/fonts/README.md +15 -0
- package/dist/index.html +60 -16
- package/dist/sitemap.xml +7 -1
- package/mcp/LIVE-PROTOCOL.md +63 -0
- package/mcp/README.md +142 -0
- package/mcp/ardy-prompts.mjs +170 -0
- package/mcp/live-hub.mjs +105 -0
- package/mcp/package.json +24 -0
- package/mcp/server.mjs +1394 -0
- package/package.json +122 -90
- package/src/App.jsx +2957 -514
- package/src/ardy/cskel27.js +7 -2
- package/src/ardy/ik.js +25 -15
- package/src/ardy/npz.js +64 -3
- package/src/ardy/playback.js +31 -1
- package/src/ardy/prompt-clips.js +7 -2
- package/src/ardy/retime.js +211 -0
- package/src/ardy/timeline-coordinates.js +13 -0
- package/src/ardy/timeline.jsx +124 -5
- package/src/ardy/to-cskel27.js +34 -12
- package/src/ardy/trim.js +33 -0
- package/src/asset-pane.jsx +36 -0
- package/src/dualview.jsx +14 -8
- package/src/hierarchy-model.js +95 -13
- package/src/hierarchy-panel.jsx +139 -6
- package/src/live-control.js +122 -0
- package/src/matte-editor.js +543 -0
- package/src/matte.js +503 -0
- package/src/multimodel-ingest.js +344 -0
- package/src/object-gizmo.jsx +43 -15
- package/src/planview.jsx +43 -32
- package/src/pose-extract/detector.js +75 -0
- package/src/pose-extract/index.js +3 -0
- package/src/pose-extract/take.js +87 -0
- package/src/pose-extract/video-frames.js +91 -0
- package/src/pose-thumbs.js +152 -0
- package/src/posestudio.jsx +361 -11
- package/src/project-browser.jsx +135 -0
- package/src/project.js +289 -0
- package/src/props.jsx +69 -3
- package/src/room.jsx +14 -35
- package/src/scene-asset-cache.js +125 -0
- package/src/scene-assets.js +288 -0
- package/src/scene-objects.js +245 -7
- package/src/scenes.js +207 -26
- package/src/shot-authoring.js +55 -13
- package/src/styles.css +1296 -129
- package/tools/ardy/BRIDGE.md +3 -2
- package/tools/ardy/README.md +9 -5
- package/tools/ardy/bridge.mjs +57 -1
- package/tools/ardy/bvh-cskel27.mjs +1209 -0
- package/tools/ardy/cclay_constrained_generate.py +123 -11
- package/tools/ardy/cclay_sequence_generate.py +49 -0
- package/tools/ardy/extract.mjs +367 -0
- package/tools/ardy/footage.mjs +462 -0
- package/tools/ardy/npz.mjs +74 -9
- package/tools/ardy/run-on-box.sh +25 -0
- package/tools/ardy/run-sequence-on-box.sh +15 -0
- package/tools/ardy/runners/remote.mjs +8 -2
- package/dist/assets/app-Cgpk2hwX.js +0 -4803
- package/dist/assets/app-DgZvaAE1.css +0 -1
package/tools/ardy/BRIDGE.md
CHANGED
|
@@ -158,8 +158,9 @@ ascending and are converted independently with the floor-aligned canonical
|
|
|
158
158
|
CoreSkeleton27 reference. No user-selected motion supplies root height.
|
|
159
159
|
`run-on-box.sh` passes every entry as a repeated
|
|
160
160
|
`--pose-from <npz> 0 <frame>` constraint. The remote
|
|
161
|
-
|
|
162
|
-
frame.
|
|
161
|
+
the CozyClay full-body constraint pins joint positions, authored joint
|
|
162
|
+
rotations, root X/Y/Z, and heading at that frame. The pose npz carries the
|
|
163
|
+
authored rotation indices so identity-filled Core joints are not overconstrained.
|
|
163
164
|
|
|
164
165
|
`segments` is optional. When present it must contain 2..64 contiguous,
|
|
165
166
|
non-overlapping ranges covering frame 0 through `duration * 20`; each range
|
package/tools/ardy/README.md
CHANGED
|
@@ -107,7 +107,10 @@ per-joint local rotations, built from the CozyClay basis quaternions via
|
|
|
107
107
|
armature-space rest rotation — see CozyClay `motion_retarget.py` /
|
|
108
108
|
`motion_constraints.py`). The src-frame is range-checked against the npz by
|
|
109
109
|
the generator remotely, so an out-of-range `--src-frame` dies on the box
|
|
110
|
-
with a clear message rather than silently.
|
|
110
|
+
with a clear message rather than silently. CozyClay-authored pose files also
|
|
111
|
+
carry `rotation_constraint_indices`: only those rotations are observed by the
|
|
112
|
+
model, while the identity-filled Core joints remain free. An ordinary motion
|
|
113
|
+
npz without that member constrains every joint rotation.
|
|
111
114
|
|
|
112
115
|
## The generation grammar
|
|
113
116
|
|
|
@@ -117,10 +120,11 @@ The raw generator flag for a full-body pose constraint is:
|
|
|
117
120
|
--pose-from <src-npz> <src-frame> <dst-frame>
|
|
118
121
|
```
|
|
119
122
|
|
|
120
|
-
It
|
|
121
|
-
|
|
122
|
-
and works for poses no end-effector constraint can express
|
|
123
|
-
reaching). The clip is `int(duration * 20)` frames at
|
|
123
|
+
It pins every joint position plus the selected joint rotations from
|
|
124
|
+
`<src-npz>` at `<src-frame>` onto `<dst-frame>` of the new clip. It is
|
|
125
|
+
repeatable and works for poses no end-effector constraint can express
|
|
126
|
+
(sitting, lying, reaching). The clip is `int(duration * 20)` frames at
|
|
127
|
+
ARDY's 20 fps, and
|
|
124
128
|
`dst-frame` must satisfy `0 <= dst-frame < duration * 20`; the generator
|
|
125
129
|
also rejects clips under 3 frames.
|
|
126
130
|
|
package/tools/ardy/bridge.mjs
CHANGED
|
@@ -43,6 +43,8 @@ import { decodeMotionNpz } from "../../src/ardy/npz.js";
|
|
|
43
43
|
import { motionArraysToNpzMembers, replaceMotionSegment, writeNpz } from "./npz.mjs";
|
|
44
44
|
import { globalChildren, killGroup, runStreaming, track } from "./runners/proc.mjs";
|
|
45
45
|
import { createRunner } from "./runners/index.mjs";
|
|
46
|
+
import { footagePath, handleFootage, serveFootage } from "./footage.mjs";
|
|
47
|
+
import { handleExtract } from "./extract.mjs";
|
|
46
48
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
47
49
|
const REPO = resolve(HERE, "../..");
|
|
48
50
|
const OUT_DIR = join(HERE, "out");
|
|
@@ -187,6 +189,13 @@ function validateGenerate(body) {
|
|
|
187
189
|
if (body.prompt.length > PROMPT_MAX_CHARS) return `field 'prompt' is ${body.prompt.length} chars; the cap is ${PROMPT_MAX_CHARS}`;
|
|
188
190
|
if (typeof body.duration !== "number" || !Number.isFinite(body.duration)) return `field 'duration' must be a finite number`;
|
|
189
191
|
if (body.duration < DURATION_MIN || body.duration > DURATION_MAX) return `field 'duration' must be in ${DURATION_MIN}..${DURATION_MAX} seconds`;
|
|
192
|
+
// Optional post-processing knobs, forwarded to the box generators
|
|
193
|
+
// (ARDY defaults: root margin 0.04 m, contact threshold 0.5).
|
|
194
|
+
if (body.rootMargin !== undefined && (typeof body.rootMargin !== "number" || !Number.isFinite(body.rootMargin) || body.rootMargin < 0 || body.rootMargin > 1)) return `field 'rootMargin' must be a number in 0..1 (meters)`;
|
|
195
|
+
if (body.contactThreshold !== undefined && (typeof body.contactThreshold !== "number" || !Number.isFinite(body.contactThreshold) || body.contactThreshold < 0 || body.contactThreshold > 1)) return `field 'contactThreshold' must be a number in 0..1`;
|
|
196
|
+
// Optional history crop (per autoregressive step), forwarded to the box
|
|
197
|
+
// generators; the box enforces its own token-size multiple.
|
|
198
|
+
if (body.historyFrames !== undefined && (!Number.isInteger(body.historyFrames) || body.historyFrames <= 0 || body.historyFrames > 400)) return `field 'historyFrames' must be an integer in 1..400`;
|
|
190
199
|
const clipFrames = Math.floor(body.duration * FPS);
|
|
191
200
|
if (clipFrames < 3) return `field 'duration' yields fewer than 3 frames`;
|
|
192
201
|
|
|
@@ -772,6 +781,9 @@ async function handleGenerate(req, res) {
|
|
|
772
781
|
waypoints: body.waypoints,
|
|
773
782
|
seed: body.seed,
|
|
774
783
|
cpu: body.cpu,
|
|
784
|
+
rootMargin: body.rootMargin,
|
|
785
|
+
contactThreshold: body.contactThreshold,
|
|
786
|
+
historyFrames: body.historyFrames,
|
|
775
787
|
output: outNpzPath,
|
|
776
788
|
});
|
|
777
789
|
sendStatus(
|
|
@@ -838,6 +850,9 @@ async function handleGenerate(req, res) {
|
|
|
838
850
|
seed: body.seed,
|
|
839
851
|
cpu: body.cpu,
|
|
840
852
|
waypoints: body.waypoints,
|
|
853
|
+
rootMargin: body.rootMargin,
|
|
854
|
+
contactThreshold: body.contactThreshold,
|
|
855
|
+
historyFrames: body.historyFrames,
|
|
841
856
|
output: outputPath,
|
|
842
857
|
});
|
|
843
858
|
sendStatus(`[bridge] generating frames ${segment.startFrame}..${segment.endFrame - 1}`);
|
|
@@ -1084,11 +1099,52 @@ const server = createServer((req, res) => {
|
|
|
1084
1099
|
log(serveMotion(req, res, pathname));
|
|
1085
1100
|
return;
|
|
1086
1101
|
}
|
|
1102
|
+
if (pathname === "/ardy/footage" && req.method === "POST") {
|
|
1103
|
+
handleFootage(req, res, (request) => readBody(request, MAX_BODY_BYTES)).catch((err) => {
|
|
1104
|
+
if (!res.headersSent) sendJson(res, 500, { ok: false, reason: `internal error: ${err.message}` });
|
|
1105
|
+
else {
|
|
1106
|
+
try {
|
|
1107
|
+
res.end();
|
|
1108
|
+
} catch {
|
|
1109
|
+
/* socket gone */
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
console.error(`[bridge] ${req.method} ${pathname} threw: ${err.stack || err}`);
|
|
1113
|
+
log(500);
|
|
1114
|
+
});
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
if (/^\/ardy\/footage\//.test(pathname) && req.method === "GET") {
|
|
1118
|
+
log(serveFootage(req, res, pathname));
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
if (pathname === "/ardy/extract" && req.method === "POST") {
|
|
1122
|
+
handleExtract(req, res, {
|
|
1123
|
+
readBody: (request) => readBody(request, MAX_BODY_BYTES),
|
|
1124
|
+
footagePath,
|
|
1125
|
+
registerMotion,
|
|
1126
|
+
}).catch((err) => {
|
|
1127
|
+
if (!res.headersSent) sendJson(res, 500, { ok: false, reason: `internal error: ${err.message}` });
|
|
1128
|
+
else {
|
|
1129
|
+
try {
|
|
1130
|
+
res.end();
|
|
1131
|
+
} catch {
|
|
1132
|
+
/* socket gone */
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
console.error(`[bridge] ${req.method} ${pathname} threw: ${err.stack || err}`);
|
|
1136
|
+
log(500);
|
|
1137
|
+
});
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1087
1140
|
if (
|
|
1088
1141
|
pathname === "/ardy/health" ||
|
|
1089
1142
|
pathname === "/ardy/bases" ||
|
|
1090
1143
|
pathname === "/ardy/generate" ||
|
|
1091
|
-
|
|
1144
|
+
pathname === "/ardy/footage" ||
|
|
1145
|
+
pathname === "/ardy/extract" ||
|
|
1146
|
+
/^\/ardy\/motions\//.test(pathname) ||
|
|
1147
|
+
/^\/ardy\/footage\//.test(pathname)
|
|
1092
1148
|
) {
|
|
1093
1149
|
sendJson(res, 405, { ok: false, reason: `method ${req.method} not allowed on ${pathname}` });
|
|
1094
1150
|
log(405);
|