@nodetool-ai/blender-nodes 0.8.1
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 +36 -0
- package/blender_ops/depth.py +133 -0
- package/blender_ops/errors.py +46 -0
- package/blender_ops/exr.py +205 -0
- package/blender_ops/framing.py +75 -0
- package/blender_ops/ops/__init__.py +24 -0
- package/blender_ops/ops/common.py +310 -0
- package/blender_ops/ops/export_model.py +95 -0
- package/blender_ops/ops/prepare_for_engine.py +352 -0
- package/blender_ops/ops/render_animation.py +382 -0
- package/blender_ops/ops/render_image.py +41 -0
- package/blender_ops/ops/render_passes.py +363 -0
- package/blender_ops/run_job.py +110 -0
- package/blender_ops/tests/test_camera_lens.py +107 -0
- package/blender_ops/tests/test_depth.py +158 -0
- package/blender_ops/tests/test_exr.py +218 -0
- package/blender_ops/tests/test_framing.py +50 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/bake.d.ts +107 -0
- package/dist/bake.d.ts.map +1 -0
- package/dist/bake.js +209 -0
- package/dist/bake.js.map +1 -0
- package/dist/blender-binary.d.ts +38 -0
- package/dist/blender-binary.d.ts.map +1 -0
- package/dist/blender-binary.js +169 -0
- package/dist/blender-binary.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/job.d.ts +163 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +67 -0
- package/dist/job.js.map +1 -0
- package/dist/nodes/bake-timeline-clip.d.ts +48 -0
- package/dist/nodes/bake-timeline-clip.d.ts.map +1 -0
- package/dist/nodes/bake-timeline-clip.js +152 -0
- package/dist/nodes/bake-timeline-clip.js.map +1 -0
- package/dist/nodes/blender-error.d.ts +21 -0
- package/dist/nodes/blender-error.d.ts.map +1 -0
- package/dist/nodes/blender-error.js +38 -0
- package/dist/nodes/blender-error.js.map +1 -0
- package/dist/nodes/defaults.d.ts +18 -0
- package/dist/nodes/defaults.d.ts.map +1 -0
- package/dist/nodes/defaults.js +18 -0
- package/dist/nodes/defaults.js.map +1 -0
- package/dist/nodes/export-model.d.ts +49 -0
- package/dist/nodes/export-model.d.ts.map +1 -0
- package/dist/nodes/export-model.js +127 -0
- package/dist/nodes/export-model.js.map +1 -0
- package/dist/nodes/prepare-for-engine.d.ts +52 -0
- package/dist/nodes/prepare-for-engine.d.ts.map +1 -0
- package/dist/nodes/prepare-for-engine.js +127 -0
- package/dist/nodes/prepare-for-engine.js.map +1 -0
- package/dist/nodes/progress.d.ts +11 -0
- package/dist/nodes/progress.d.ts.map +1 -0
- package/dist/nodes/progress.js +21 -0
- package/dist/nodes/progress.js.map +1 -0
- package/dist/nodes/render-animation.d.ts +52 -0
- package/dist/nodes/render-animation.d.ts.map +1 -0
- package/dist/nodes/render-animation.js +136 -0
- package/dist/nodes/render-animation.js.map +1 -0
- package/dist/nodes/render-base.d.ts +35 -0
- package/dist/nodes/render-base.d.ts.map +1 -0
- package/dist/nodes/render-base.js +95 -0
- package/dist/nodes/render-base.js.map +1 -0
- package/dist/nodes/render-image.d.ts +41 -0
- package/dist/nodes/render-image.d.ts.map +1 -0
- package/dist/nodes/render-image.js +98 -0
- package/dist/nodes/render-image.js.map +1 -0
- package/dist/nodes/render-passes.d.ts +54 -0
- package/dist/nodes/render-passes.d.ts.map +1 -0
- package/dist/nodes/render-passes.js +167 -0
- package/dist/nodes/render-passes.js.map +1 -0
- package/dist/run-job.d.ts +34 -0
- package/dist/run-job.d.ts.map +1 -0
- package/dist/run-job.js +137 -0
- package/dist/run-job.js.map +1 -0
- package/dist/runner.d.ts +124 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +521 -0
- package/dist/runner.js.map +1 -0
- package/package.json +73 -0
package/dist/bake.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The timeline's 3D bake: a sampled Blender render muxed into one video
|
|
3
|
+
* (design §D6).
|
|
4
|
+
*
|
|
5
|
+
* The caller hands over one model time and one camera per output frame — the
|
|
6
|
+
* evaluated clip, with its in point, speed, time remap, animation speed and
|
|
7
|
+
* camera curves already applied — and gets back an MP4 the clip can play as
|
|
8
|
+
* an ordinary video layer. Blender renders a still per entry rather than a
|
|
9
|
+
* frame range, because a trimmed, sped-up, reversed or looped clip is not a
|
|
10
|
+
* run of consecutive scene frames.
|
|
11
|
+
*
|
|
12
|
+
* The mux is ffmpeg and not Blender's own writer, which is what
|
|
13
|
+
* `render_animation`'s video mode uses. Two reasons: one sequence feeds both
|
|
14
|
+
* bake formats, and Blender cannot write VP9 with an alpha channel. ffmpeg is
|
|
15
|
+
* therefore a host binary this path needs and the video mode does not; a
|
|
16
|
+
* machine without it fails here with the binary named.
|
|
17
|
+
*/
|
|
18
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { runHostBinary } from "@nodetool-ai/runtime";
|
|
21
|
+
import { runBlenderJob } from "./run-job.js";
|
|
22
|
+
import { BlenderJobError } from "./runner.js";
|
|
23
|
+
/**
|
|
24
|
+
* Ceiling on the frames one bake asks for. Every frame is a declared output
|
|
25
|
+
* the runner reads into memory, so this is the bound on that, not a limit on
|
|
26
|
+
* clip length in the abstract: two minutes at 30 fps.
|
|
27
|
+
*/
|
|
28
|
+
export const MAX_BAKE_FRAMES = 3600;
|
|
29
|
+
/**
|
|
30
|
+
* How a bake is encoded: opaque to MP4/H.264 `yuv420p`, transparent to WebM
|
|
31
|
+
* VP9 `yuva420p` (design §D6).
|
|
32
|
+
*
|
|
33
|
+
* The alpha pair is the one `resolveTimelineOutput({ format: "webm", alpha:
|
|
34
|
+
* true })` already declares for a timeline render, copied rather than imported
|
|
35
|
+
* because `@nodetool-ai/blender-nodes` does not depend on
|
|
36
|
+
* `@nodetool-ai/video-nodes` and a bake needs four strings from it, not a
|
|
37
|
+
* package. `packages/agents/tests/timeline-bake-format.test.ts` depends on both
|
|
38
|
+
* and fails when the two drift.
|
|
39
|
+
*/
|
|
40
|
+
export function bakeVideoFormat(alpha) {
|
|
41
|
+
if (alpha) {
|
|
42
|
+
return {
|
|
43
|
+
extension: "webm",
|
|
44
|
+
mimeType: "video/webm",
|
|
45
|
+
// VP9 is the only WebM video codec with an alpha plane, which is why the
|
|
46
|
+
// format pins the codec instead of taking an override.
|
|
47
|
+
encoderArgs: ["-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p"]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
extension: "mp4",
|
|
52
|
+
mimeType: "video/mp4",
|
|
53
|
+
encoderArgs: ["-c:v", "libx264", "-pix_fmt", "yuv420p"]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Six-digit frame names, so the outputs sort the way they play. */
|
|
57
|
+
function frameName(index) {
|
|
58
|
+
return `frame_${String(index + 1).padStart(6, "0")}`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The `frame_*` outputs a sampled job declares: one per entry, in play order.
|
|
62
|
+
* The op writes the file each name carries and reports the names back.
|
|
63
|
+
*/
|
|
64
|
+
export function bakeFrameOutputs(count) {
|
|
65
|
+
const outputs = {};
|
|
66
|
+
for (let i = 0; i < count; i += 1) {
|
|
67
|
+
outputs[frameName(i)] = `${frameName(i)}.png`;
|
|
68
|
+
}
|
|
69
|
+
return outputs;
|
|
70
|
+
}
|
|
71
|
+
function assertRequest(request) {
|
|
72
|
+
const count = request.frameTimes.length;
|
|
73
|
+
if (count === 0) {
|
|
74
|
+
throw new BlenderJobError("bad_job", "A 3D bake needs at least one frame; the clip resolved to none.");
|
|
75
|
+
}
|
|
76
|
+
if (count > MAX_BAKE_FRAMES) {
|
|
77
|
+
throw new BlenderJobError("bad_job", `A 3D bake of ${count} frames is above the ${MAX_BAKE_FRAMES}-frame cap. ` +
|
|
78
|
+
`Shorten the clip or lower the sequence fps.`);
|
|
79
|
+
}
|
|
80
|
+
if (request.cameras.length !== count) {
|
|
81
|
+
throw new BlenderJobError("bad_job", `A 3D bake carries ${request.cameras.length} cameras for ${count} frames.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** The sampled `render_animation` params for one bake. */
|
|
85
|
+
export function bakeRenderAnimationParams(request) {
|
|
86
|
+
const first = request.cameras[0];
|
|
87
|
+
if (!first) {
|
|
88
|
+
throw new BlenderJobError("bad_job", "A 3D bake carries no camera.");
|
|
89
|
+
}
|
|
90
|
+
const params = {
|
|
91
|
+
...first,
|
|
92
|
+
width: Math.max(1, Math.round(request.width)),
|
|
93
|
+
height: Math.max(1, Math.round(request.height)),
|
|
94
|
+
// The sampled path renders stills and never reads the range, but the
|
|
95
|
+
// runner's progress arithmetic does: `Fra:n` counts entries, so the range
|
|
96
|
+
// is the entry count.
|
|
97
|
+
frame_start: 1,
|
|
98
|
+
frame_end: request.frameTimes.length,
|
|
99
|
+
fps: Math.max(1, Math.round(request.fps)),
|
|
100
|
+
orbit_degrees: 0,
|
|
101
|
+
frame_times: [...request.frameTimes],
|
|
102
|
+
cameras: [...request.cameras]
|
|
103
|
+
};
|
|
104
|
+
if (request.animationName !== undefined) {
|
|
105
|
+
params.animation_name = request.animationName;
|
|
106
|
+
}
|
|
107
|
+
return params;
|
|
108
|
+
}
|
|
109
|
+
/** Render the sequence. Frames come back in play order. */
|
|
110
|
+
export async function renderModel3DBakeFrames(context, modelBytes, request, options) {
|
|
111
|
+
assertRequest(request);
|
|
112
|
+
const count = request.frameTimes.length;
|
|
113
|
+
const outputs = bakeFrameOutputs(count);
|
|
114
|
+
const result = await runBlenderJob(context, modelBytes, { op: "render_animation", params: bakeRenderAnimationParams(request) }, outputs, {
|
|
115
|
+
timeoutMs: options.timeoutMs,
|
|
116
|
+
signal: options.signal,
|
|
117
|
+
onProgress: options.onProgress,
|
|
118
|
+
// Every frame is one declared output, so the default ceiling on
|
|
119
|
+
// declared outputs is the ceiling on a bake's length. The frame cap
|
|
120
|
+
// above is the bound that matters here.
|
|
121
|
+
maxOutputCount: MAX_BAKE_FRAMES
|
|
122
|
+
});
|
|
123
|
+
const frames = [];
|
|
124
|
+
for (let i = 0; i < count; i += 1) {
|
|
125
|
+
const bytes = result.outputs[frameName(i)];
|
|
126
|
+
if (!bytes || bytes.length === 0) {
|
|
127
|
+
throw new BlenderJobError("missing_output", `Blender produced no pixels for bake frame ${i + 1} of ${count}.`);
|
|
128
|
+
}
|
|
129
|
+
frames.push(bytes);
|
|
130
|
+
}
|
|
131
|
+
return { frames, stats: result.stats };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The ffmpeg call that turns the numbered PNGs into one video.
|
|
135
|
+
*
|
|
136
|
+
* Pure, because the arguments are the whole difference between the two bakes
|
|
137
|
+
* and a test should not need ffmpeg to read them.
|
|
138
|
+
*/
|
|
139
|
+
export function bakeMuxArgs(format, fps, output) {
|
|
140
|
+
const rate = String(Math.max(1, Math.round(fps)));
|
|
141
|
+
return [
|
|
142
|
+
"-y",
|
|
143
|
+
"-framerate",
|
|
144
|
+
rate,
|
|
145
|
+
"-start_number",
|
|
146
|
+
"1",
|
|
147
|
+
"-i",
|
|
148
|
+
"frame_%06d.png",
|
|
149
|
+
...format.encoderArgs,
|
|
150
|
+
// Every timeline decoder seeks this video, and a seek lands on the
|
|
151
|
+
// preceding keyframe: one per second keeps a scrub inside a frame or two
|
|
152
|
+
// of where it was asked for.
|
|
153
|
+
"-g",
|
|
154
|
+
rate,
|
|
155
|
+
// MP4 keeps its index at the front so a decoder can seek before the whole
|
|
156
|
+
// file has arrived; WebM has no such flag and needs none.
|
|
157
|
+
...(format.extension === "mp4" ? ["-movflags", "+faststart"] : []),
|
|
158
|
+
output
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Mux a PNG sequence into one video through ffmpeg's image2 demuxer.
|
|
163
|
+
*
|
|
164
|
+
* The frames are written under the workspace's scratch directory rather than
|
|
165
|
+
* piped: image2 reads a numbered pattern, and the same files reach either
|
|
166
|
+
* encoder unchanged — an alpha bake differs only in the arguments
|
|
167
|
+
* {@link bakeVideoFormat} names, because the PNGs already carry the channel.
|
|
168
|
+
*/
|
|
169
|
+
export async function muxPngSequenceToVideo(context, frames, fps, format, options) {
|
|
170
|
+
const workspace = context.workspace;
|
|
171
|
+
if (!workspace) {
|
|
172
|
+
throw new BlenderJobError("bad_job", "A 3D bake needs a processing context with a workspace to mux through.");
|
|
173
|
+
}
|
|
174
|
+
const cwd = await mkdtemp(path.join(await workspace.scratchDir(), "nodetool-bake-"));
|
|
175
|
+
try {
|
|
176
|
+
for (const [index, bytes] of frames.entries()) {
|
|
177
|
+
await writeFile(path.join(cwd, `${frameName(index)}.png`), bytes);
|
|
178
|
+
}
|
|
179
|
+
const output = `bake.${format.extension}`;
|
|
180
|
+
const argv = bakeMuxArgs(format, fps, output);
|
|
181
|
+
const result = await runHostBinary("ffmpeg", argv, {
|
|
182
|
+
cwd,
|
|
183
|
+
timeoutMs: options.timeoutMs,
|
|
184
|
+
signal: options.signal,
|
|
185
|
+
artifactPath: output
|
|
186
|
+
});
|
|
187
|
+
if (result.exitCode !== 0) {
|
|
188
|
+
throw new BlenderJobError("render_failed", `ffmpeg could not mux the bake (exit ${result.exitCode}): ${result.stderr.slice(-2000)}`);
|
|
189
|
+
}
|
|
190
|
+
return new Uint8Array(await readFile(path.join(cwd, output)));
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
await rm(cwd, { recursive: true, force: true });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Render the sampled sequence and mux it: the whole bake (§D6).
|
|
198
|
+
*
|
|
199
|
+
* Whether it carries alpha is read off the frames' own camera rather than
|
|
200
|
+
* taken as a second field: `transparent` is what Blender rendered the film
|
|
201
|
+
* under, so a separate flag could only ever disagree with the pixels.
|
|
202
|
+
*/
|
|
203
|
+
export async function bakeModel3DClipToVideo(context, modelBytes, request, options) {
|
|
204
|
+
const format = bakeVideoFormat(request.cameras[0]?.transparent === true);
|
|
205
|
+
const { frames, stats } = await renderModel3DBakeFrames(context, modelBytes, request, options);
|
|
206
|
+
const video = await muxPngSequenceToVideo(context, frames, request.fps, format, options);
|
|
207
|
+
return { video, format, stats };
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=bake.js.map
|
package/dist/bake.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bake.js","sourceRoot":"","sources":["../src/bake.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blender executable discovery (D3).
|
|
3
|
+
*
|
|
4
|
+
* Order: `BLENDER_PATH`, then `blender` on PATH, then the well-known
|
|
5
|
+
* locations for macOS, Linux, and Windows. The first candidate whose
|
|
6
|
+
* `--version` runs wins. Below the 5.2 floor throws `BlenderVersionError`
|
|
7
|
+
* naming both versions; nothing found throws `HostBinaryMissingError`.
|
|
8
|
+
*
|
|
9
|
+
* The result is cached per process and invalidated when `BLENDER_PATH`
|
|
10
|
+
* changes, so tests pointing `BLENDER_PATH` at a fake re-resolve.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Minimum supported Blender version: 5.2 LTS. The ops use
|
|
14
|
+
* `scene.compositing_node_group` and `image_settings.media_type`, neither of
|
|
15
|
+
* which exists on 4.2, so the 4.2 floor the design first named was never
|
|
16
|
+
* runnable. See the Revision note under A1 in
|
|
17
|
+
* `docs/blender-headless-integration-design.md`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const BLENDER_MIN_VERSION: readonly [number, number, number];
|
|
20
|
+
export interface BlenderBinary {
|
|
21
|
+
path: string;
|
|
22
|
+
version: [number, number, number];
|
|
23
|
+
}
|
|
24
|
+
export declare class BlenderVersionError extends Error {
|
|
25
|
+
readonly found: string;
|
|
26
|
+
readonly minimum: string;
|
|
27
|
+
constructor(found: string);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the Blender executable per D3. Cached per process; the cache is
|
|
31
|
+
* invalidated when `BLENDER_PATH` changes.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveBlenderBinary(options?: {
|
|
34
|
+
configuredPath?: string | null;
|
|
35
|
+
}): Promise<BlenderBinary>;
|
|
36
|
+
/** Forget the cached resolution. Tests use this after mutating the environment. */
|
|
37
|
+
export declare function resetBlenderBinaryCache(): void;
|
|
38
|
+
//# sourceMappingURL=blender-binary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blender-binary.d.ts","sourceRoot":"","sources":["../src/blender-binary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAa,CAAC;AAEhF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,YAAY,KAAK,EAAE,MAAM,EASxB;CACF;AAoHD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,GAAE;IAClD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,OAAO,CAAC,aAAa,CAAC,CAoB9B;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blender executable discovery (D3).
|
|
3
|
+
*
|
|
4
|
+
* Order: `BLENDER_PATH`, then `blender` on PATH, then the well-known
|
|
5
|
+
* locations for macOS, Linux, and Windows. The first candidate whose
|
|
6
|
+
* `--version` runs wins. Below the 5.2 floor throws `BlenderVersionError`
|
|
7
|
+
* naming both versions; nothing found throws `HostBinaryMissingError`.
|
|
8
|
+
*
|
|
9
|
+
* The result is cached per process and invalidated when `BLENDER_PATH`
|
|
10
|
+
* changes, so tests pointing `BLENDER_PATH` at a fake re-resolve.
|
|
11
|
+
*/
|
|
12
|
+
import { execFile } from "node:child_process";
|
|
13
|
+
import { readdir } from "node:fs/promises";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { promisify } from "node:util";
|
|
16
|
+
import { HostBinaryMissingError } from "@nodetool-ai/runtime";
|
|
17
|
+
const execFileAsync = promisify(execFile);
|
|
18
|
+
/**
|
|
19
|
+
* Minimum supported Blender version: 5.2 LTS. The ops use
|
|
20
|
+
* `scene.compositing_node_group` and `image_settings.media_type`, neither of
|
|
21
|
+
* which exists on 4.2, so the 4.2 floor the design first named was never
|
|
22
|
+
* runnable. See the Revision note under A1 in
|
|
23
|
+
* `docs/blender-headless-integration-design.md`.
|
|
24
|
+
*/
|
|
25
|
+
export const BLENDER_MIN_VERSION = [5, 2, 0];
|
|
26
|
+
export class BlenderVersionError extends Error {
|
|
27
|
+
found;
|
|
28
|
+
minimum;
|
|
29
|
+
constructor(found) {
|
|
30
|
+
const minimum = BLENDER_MIN_VERSION.join(".");
|
|
31
|
+
super(`Blender ${found} is too old: NodeTool needs Blender ${minimum} or newer. ` +
|
|
32
|
+
`Install a newer Blender or point BLENDER_PATH at one.`);
|
|
33
|
+
this.name = "BlenderVersionError";
|
|
34
|
+
this.found = found;
|
|
35
|
+
this.minimum = minimum;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isAtLeast(version, floor) {
|
|
39
|
+
for (let i = 0; i < 3; i++) {
|
|
40
|
+
if (version[i] > floor[i])
|
|
41
|
+
return true;
|
|
42
|
+
if (version[i] < floor[i])
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function parseVersion(output) {
|
|
48
|
+
const match = /Blender\s+(\d+)\.(\d+)\.(\d+)/.exec(output);
|
|
49
|
+
if (!match)
|
|
50
|
+
return null;
|
|
51
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
52
|
+
}
|
|
53
|
+
async function probe(candidate) {
|
|
54
|
+
try {
|
|
55
|
+
const { stdout, stderr } = await execFileAsync(candidate, ["--version"], {
|
|
56
|
+
timeout: 15000,
|
|
57
|
+
windowsHide: true
|
|
58
|
+
});
|
|
59
|
+
const version = parseVersion(`${stdout}\n${stderr}`);
|
|
60
|
+
if (!version) {
|
|
61
|
+
return {
|
|
62
|
+
binary: null,
|
|
63
|
+
failure: {
|
|
64
|
+
candidate,
|
|
65
|
+
detail: firstLine(`${stdout}\n${stderr}`)
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return { binary: { path: candidate, version }, failure: null };
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return {
|
|
73
|
+
binary: null,
|
|
74
|
+
failure: {
|
|
75
|
+
candidate,
|
|
76
|
+
detail: firstLine(err instanceof Error ? (err.message ?? String(err)) : String(err))
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function firstLine(text) {
|
|
82
|
+
const line = text.split("\n", 1)[0] ?? "";
|
|
83
|
+
return line.length > 200 ? `${line.slice(0, 200)}…` : line;
|
|
84
|
+
}
|
|
85
|
+
async function windowsCandidates() {
|
|
86
|
+
const roots = [
|
|
87
|
+
process.env["ProgramFiles"],
|
|
88
|
+
process.env["ProgramFiles(x86)"]
|
|
89
|
+
].filter((root) => root !== undefined && root !== "");
|
|
90
|
+
const found = [];
|
|
91
|
+
for (const root of roots) {
|
|
92
|
+
const base = path.join(root, "Blender Foundation");
|
|
93
|
+
let entries;
|
|
94
|
+
try {
|
|
95
|
+
entries = await readdir(base);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// No Blender install under this root.
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const entry of entries.sort().reverse()) {
|
|
102
|
+
if (!entry.startsWith("Blender"))
|
|
103
|
+
continue;
|
|
104
|
+
found.push(path.join(base, entry, "blender.exe"));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
async function candidates() {
|
|
110
|
+
const list = [];
|
|
111
|
+
const envPath = process.env["BLENDER_PATH"];
|
|
112
|
+
if (envPath !== undefined && envPath !== "")
|
|
113
|
+
list.push(envPath);
|
|
114
|
+
list.push("blender");
|
|
115
|
+
if (process.platform === "darwin") {
|
|
116
|
+
list.push("/Applications/Blender.app/Contents/MacOS/Blender");
|
|
117
|
+
}
|
|
118
|
+
else if (process.platform === "win32") {
|
|
119
|
+
list.push(...(await windowsCandidates()));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
list.push("/usr/bin/blender", "/snap/bin/blender");
|
|
123
|
+
}
|
|
124
|
+
return list;
|
|
125
|
+
}
|
|
126
|
+
let cached = null;
|
|
127
|
+
function missingError(failures, configuredPath) {
|
|
128
|
+
const err = new HostBinaryMissingError("blender");
|
|
129
|
+
const envPath = process.env["BLENDER_PATH"];
|
|
130
|
+
const probeNote = failures
|
|
131
|
+
.filter((f) => f.candidate === envPath || f.candidate === configuredPath)
|
|
132
|
+
.map((f) => ` BLENDER_PATH=${JSON.stringify(f.candidate)} failed: ${f.detail}`)
|
|
133
|
+
.join("");
|
|
134
|
+
err.message =
|
|
135
|
+
`blender was not found. Install Blender 5.2 or newer and add it to PATH, ` +
|
|
136
|
+
`or set BLENDER_PATH to the Blender executable.${probeNote}`;
|
|
137
|
+
return err;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Resolve the Blender executable per D3. Cached per process; the cache is
|
|
141
|
+
* invalidated when `BLENDER_PATH` changes.
|
|
142
|
+
*/
|
|
143
|
+
export async function resolveBlenderBinary(options = {}) {
|
|
144
|
+
const blenderPathEnv = options.configuredPath ?? process.env["BLENDER_PATH"];
|
|
145
|
+
if (cached && cached.blenderPathEnv === blenderPathEnv)
|
|
146
|
+
return cached.binary;
|
|
147
|
+
const failures = [];
|
|
148
|
+
const candidateList = options.configuredPath
|
|
149
|
+
? [options.configuredPath, ...(await candidates())]
|
|
150
|
+
: await candidates();
|
|
151
|
+
for (const candidate of candidateList) {
|
|
152
|
+
const { binary, failure } = await probe(candidate);
|
|
153
|
+
if (binary) {
|
|
154
|
+
if (!isAtLeast(binary.version, BLENDER_MIN_VERSION)) {
|
|
155
|
+
throw new BlenderVersionError(binary.version.join("."));
|
|
156
|
+
}
|
|
157
|
+
cached = { blenderPathEnv, binary };
|
|
158
|
+
return binary;
|
|
159
|
+
}
|
|
160
|
+
if (failure)
|
|
161
|
+
failures.push(failure);
|
|
162
|
+
}
|
|
163
|
+
throw missingError(failures, options.configuredPath);
|
|
164
|
+
}
|
|
165
|
+
/** Forget the cached resolution. Tests use this after mutating the environment. */
|
|
166
|
+
export function resetBlenderBinaryCache() {
|
|
167
|
+
cached = null;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=blender-binary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blender-binary.js","sourceRoot":"","sources":["../src/blender-binary.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nodetool-ai/blender-nodes` — Blender headless integration (Stage 3).
|
|
3
|
+
*
|
|
4
|
+
* Binary discovery, the versioned job contract, the runner interface with
|
|
5
|
+
* its local implementation, the thin `runBlenderJob` entry point, and the
|
|
6
|
+
* `nodetool.blender.*` nodes. `BLENDER_NODES` is registered in
|
|
7
|
+
* `@nodetool-ai/base-nodes` alongside the other node arrays.
|
|
8
|
+
*/
|
|
9
|
+
export { BLENDER_RENDER_NODES } from "./nodes/render-image.js";
|
|
10
|
+
export { RenderImageNode } from "./nodes/render-image.js";
|
|
11
|
+
export { BLENDER_PASSES_NODES } from "./nodes/render-passes.js";
|
|
12
|
+
export { RenderPassesNode } from "./nodes/render-passes.js";
|
|
13
|
+
export { BLENDER_ANIMATION_NODES } from "./nodes/render-animation.js";
|
|
14
|
+
export { BLENDER_BAKE_NODES } from "./nodes/bake-timeline-clip.js";
|
|
15
|
+
export { BakeTimelineClipNode } from "./nodes/bake-timeline-clip.js";
|
|
16
|
+
export { RenderAnimationNode } from "./nodes/render-animation.js";
|
|
17
|
+
export { BLENDER_PREPARE_NODES } from "./nodes/prepare-for-engine.js";
|
|
18
|
+
export { PrepareForEngineNode } from "./nodes/prepare-for-engine.js";
|
|
19
|
+
export { BLENDER_EXPORT_NODES } from "./nodes/export-model.js";
|
|
20
|
+
export { ExportModelNode } from "./nodes/export-model.js";
|
|
21
|
+
export type { BlenderBinary } from "./blender-binary.js";
|
|
22
|
+
export { BlenderVersionError, resolveBlenderBinary, resetBlenderBinaryCache, BLENDER_MIN_VERSION } from "./blender-binary.js";
|
|
23
|
+
export { BLENDER_JOB_VERSION, jobFileNameSchema, blenderResultSchema } from "./job.js";
|
|
24
|
+
export type { BakeCameraParams, BakeMode, BlenderEngine, BlenderJob, BlenderOp, BlenderResult, BlenderResultStats, CameraMode, CameraParams, DepthFormat, ExportFormat, ExportModelParams, LightingPreset, PrepareForEngineParams, RenderAnimationParams, RenderImageParams, RenderPass, RenderPassesParams } from "./job.js";
|
|
25
|
+
export { BlenderJobError, LocalBlenderRunner, WorkerBlenderRunner, resolveOpScriptDir, MAX_OUTPUT_BYTES, MAX_TOTAL_OUTPUT_BYTES, MAX_OUTPUT_COUNT } from "./runner.js";
|
|
26
|
+
export type { BlenderJobErrorCode, WorkerBlenderRunnerOptions, BlenderRunner, BlenderRunOptions, BlenderRunResult } from "./runner.js";
|
|
27
|
+
export { bakeFrameOutputs, bakeModel3DClipToVideo, bakeMuxArgs, bakeRenderAnimationParams, bakeVideoFormat, muxPngSequenceToVideo, renderModel3DBakeFrames, MAX_BAKE_FRAMES } from "./bake.js";
|
|
28
|
+
export type { BakeVideoFormat, Model3DBakeOptions, Model3DBakeRequest, Model3DBakeResult } from "./bake.js";
|
|
29
|
+
export { resolveBlenderRunner, runBlenderJob, __setBlenderRunnerForTesting, BLENDER_MODEL_INPUT_FILE } from "./run-job.js";
|
|
30
|
+
export declare const BLENDER_NODES: (typeof import("./nodes/render-image.js").RenderImageNode | typeof import("./nodes/render-passes.js").RenderPassesNode | typeof import("./nodes/render-animation.js").RenderAnimationNode | typeof import("./nodes/bake-timeline-clip.js").BakeTimelineClipNode | typeof import("./nodes/prepare-for-engine.js").PrepareForEngineNode | typeof import("./nodes/export-model.js").ExportModelNode)[];
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EAChB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,4BAA4B,EAC5B,wBAAwB,EACzB,MAAM,cAAc,CAAC;AAStB,eAAO,MAAM,aAAa,qYAOzB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nodetool-ai/blender-nodes` — Blender headless integration (Stage 3).
|
|
3
|
+
*
|
|
4
|
+
* Binary discovery, the versioned job contract, the runner interface with
|
|
5
|
+
* its local implementation, the thin `runBlenderJob` entry point, and the
|
|
6
|
+
* `nodetool.blender.*` nodes. `BLENDER_NODES` is registered in
|
|
7
|
+
* `@nodetool-ai/base-nodes` alongside the other node arrays.
|
|
8
|
+
*/
|
|
9
|
+
export { BLENDER_RENDER_NODES } from "./nodes/render-image.js";
|
|
10
|
+
export { RenderImageNode } from "./nodes/render-image.js";
|
|
11
|
+
export { BLENDER_PASSES_NODES } from "./nodes/render-passes.js";
|
|
12
|
+
export { RenderPassesNode } from "./nodes/render-passes.js";
|
|
13
|
+
export { BLENDER_ANIMATION_NODES } from "./nodes/render-animation.js";
|
|
14
|
+
export { BLENDER_BAKE_NODES } from "./nodes/bake-timeline-clip.js";
|
|
15
|
+
export { BakeTimelineClipNode } from "./nodes/bake-timeline-clip.js";
|
|
16
|
+
export { RenderAnimationNode } from "./nodes/render-animation.js";
|
|
17
|
+
export { BLENDER_PREPARE_NODES } from "./nodes/prepare-for-engine.js";
|
|
18
|
+
export { PrepareForEngineNode } from "./nodes/prepare-for-engine.js";
|
|
19
|
+
export { BLENDER_EXPORT_NODES } from "./nodes/export-model.js";
|
|
20
|
+
export { ExportModelNode } from "./nodes/export-model.js";
|
|
21
|
+
export { BlenderVersionError, resolveBlenderBinary, resetBlenderBinaryCache, BLENDER_MIN_VERSION } from "./blender-binary.js";
|
|
22
|
+
export { BLENDER_JOB_VERSION, jobFileNameSchema, blenderResultSchema } from "./job.js";
|
|
23
|
+
export { BlenderJobError, LocalBlenderRunner, WorkerBlenderRunner, resolveOpScriptDir, MAX_OUTPUT_BYTES, MAX_TOTAL_OUTPUT_BYTES, MAX_OUTPUT_COUNT } from "./runner.js";
|
|
24
|
+
export { bakeFrameOutputs, bakeModel3DClipToVideo, bakeMuxArgs, bakeRenderAnimationParams, bakeVideoFormat, muxPngSequenceToVideo, renderModel3DBakeFrames, MAX_BAKE_FRAMES } from "./bake.js";
|
|
25
|
+
export { resolveBlenderRunner, runBlenderJob, __setBlenderRunnerForTesting, BLENDER_MODEL_INPUT_FILE } from "./run-job.js";
|
|
26
|
+
import { BLENDER_RENDER_NODES } from "./nodes/render-image.js";
|
|
27
|
+
import { BLENDER_PASSES_NODES } from "./nodes/render-passes.js";
|
|
28
|
+
import { BLENDER_ANIMATION_NODES } from "./nodes/render-animation.js";
|
|
29
|
+
import { BLENDER_BAKE_NODES } from "./nodes/bake-timeline-clip.js";
|
|
30
|
+
import { BLENDER_PREPARE_NODES } from "./nodes/prepare-for-engine.js";
|
|
31
|
+
import { BLENDER_EXPORT_NODES } from "./nodes/export-model.js";
|
|
32
|
+
export const BLENDER_NODES = [
|
|
33
|
+
...BLENDER_RENDER_NODES,
|
|
34
|
+
...BLENDER_PASSES_NODES,
|
|
35
|
+
...BLENDER_ANIMATION_NODES,
|
|
36
|
+
...BLENDER_BAKE_NODES,
|
|
37
|
+
...BLENDER_PREPARE_NODES,
|
|
38
|
+
...BLENDER_EXPORT_NODES
|
|
39
|
+
];
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAqBlB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAQrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EAChB,MAAM,WAAW,CAAC;AAOnB,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,4BAA4B,EAC5B,wBAAwB,EACzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,GAAG,oBAAoB;IACvB,GAAG,oBAAoB;IACvB,GAAG,uBAAuB;IAC1B,GAAG,kBAAkB;IACrB,GAAG,qBAAqB;IACxB,GAAG,oBAAoB;CACxB,CAAC"}
|