@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/job.d.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Blender job contract (D4).
|
|
3
|
+
*
|
|
4
|
+
* Every node produces a `BlenderJob`; the Python side consumes it and writes
|
|
5
|
+
* a `BlenderResult`. Both are versioned so the TypeScript and Python halves
|
|
6
|
+
* can drift by one version during an upgrade.
|
|
7
|
+
*
|
|
8
|
+
* The job names every file on both sides. The result never names a file: it
|
|
9
|
+
* reports which declared outputs were produced, and the host reads only the
|
|
10
|
+
* paths the job itself declared. A `produced` name the job did not declare
|
|
11
|
+
* is ignored, and a path inside `result.json` is never opened.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
/** Version both halves agree on. `run_job.py` rejects anything else. */
|
|
15
|
+
export declare const BLENDER_JOB_VERSION: 1;
|
|
16
|
+
/** A bare file name: no separator, no `..`, no leading dot. */
|
|
17
|
+
export declare const jobFileNameSchema: z.ZodString;
|
|
18
|
+
export type CameraMode = "auto" | "scene" | "orbit";
|
|
19
|
+
export type BlenderEngine = "eevee" | "cycles";
|
|
20
|
+
export type LightingPreset = "studio" | "soft" | "flat";
|
|
21
|
+
export type RenderPass = "color" | "depth" | "normal" | "mask";
|
|
22
|
+
export type DepthFormat = "png16" | "exr";
|
|
23
|
+
export type BakeMode = "none" | "ao" | "normal" | "both";
|
|
24
|
+
export type ExportFormat = "fbx" | "obj" | "usd";
|
|
25
|
+
/**
|
|
26
|
+
* Shared camera vocabulary. Reuses the `RenderToImage` prop names so a user
|
|
27
|
+
* can swap the preview node for the Blender node without relearning.
|
|
28
|
+
*/
|
|
29
|
+
export interface CameraParams {
|
|
30
|
+
/** Whose camera renders; `auto` is the scene's first camera when present. */
|
|
31
|
+
camera_mode: CameraMode;
|
|
32
|
+
azimuth: number;
|
|
33
|
+
elevation: number;
|
|
34
|
+
fov: number;
|
|
35
|
+
zoom: number;
|
|
36
|
+
lighting: LightingPreset;
|
|
37
|
+
light_intensity: number;
|
|
38
|
+
background_color: string;
|
|
39
|
+
transparent: boolean;
|
|
40
|
+
engine: BlenderEngine;
|
|
41
|
+
samples: number;
|
|
42
|
+
denoise: boolean;
|
|
43
|
+
resolution_percentage: number;
|
|
44
|
+
}
|
|
45
|
+
export interface RenderImageParams extends CameraParams {
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
}
|
|
49
|
+
export interface RenderPassesParams extends CameraParams {
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
passes: RenderPass[];
|
|
53
|
+
depth_format: DepthFormat;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One frame's camera in a sampled render (design §D6). `CameraParams` plus the
|
|
57
|
+
* two things a timeline clip's camera carries that the DTO did not: which glTF
|
|
58
|
+
* camera `scene` mode renders through, and where the orbit looks.
|
|
59
|
+
*/
|
|
60
|
+
export interface BakeCameraParams extends CameraParams {
|
|
61
|
+
/** `scene` mode: the glTF camera by name; the first one when absent. */
|
|
62
|
+
scene_camera_name?: string;
|
|
63
|
+
/** Look-at offset from the bounding-sphere center, in world units. */
|
|
64
|
+
target_offset?: [number, number, number];
|
|
65
|
+
}
|
|
66
|
+
export interface RenderAnimationParams extends CameraParams {
|
|
67
|
+
width: number;
|
|
68
|
+
height: number;
|
|
69
|
+
/** First frame in the glTF timeline (`round(t * fps)` per animation channel). */
|
|
70
|
+
frame_start: number;
|
|
71
|
+
frame_end: number;
|
|
72
|
+
fps: number;
|
|
73
|
+
/** Orbit sweep in degrees across the range when glTF has no animation. */
|
|
74
|
+
orbit_degrees: number;
|
|
75
|
+
/**
|
|
76
|
+
* Sampled mode (§D6): one model time in seconds per output frame. Present
|
|
77
|
+
* turns the op into a still-per-entry render — the frame range, the orbit
|
|
78
|
+
* sweep and the video writer are all unused — and it writes
|
|
79
|
+
* `frame_%06d.png` for the caller to mux. Absent leaves the op exactly as
|
|
80
|
+
* it was, which is what keeps `RenderAnimation` unchanged.
|
|
81
|
+
*/
|
|
82
|
+
frame_times?: number[];
|
|
83
|
+
/**
|
|
84
|
+
* Which glTF animation plays in sampled mode. Absent leaves every action
|
|
85
|
+
* playing, which is the default a `model3d` clip carries.
|
|
86
|
+
*/
|
|
87
|
+
animation_name?: string;
|
|
88
|
+
/** The camera of each `frame_times` entry. Must be the same length. */
|
|
89
|
+
cameras?: BakeCameraParams[];
|
|
90
|
+
}
|
|
91
|
+
export interface PrepareForEngineParams {
|
|
92
|
+
target_faces: number;
|
|
93
|
+
unwrap: boolean;
|
|
94
|
+
bake: BakeMode;
|
|
95
|
+
bake_resolution: number;
|
|
96
|
+
lod_count: number;
|
|
97
|
+
}
|
|
98
|
+
export interface ExportModelParams {
|
|
99
|
+
format: ExportFormat;
|
|
100
|
+
}
|
|
101
|
+
export type BlenderOp = {
|
|
102
|
+
op: "render_image";
|
|
103
|
+
params: RenderImageParams;
|
|
104
|
+
} | {
|
|
105
|
+
op: "render_passes";
|
|
106
|
+
params: RenderPassesParams;
|
|
107
|
+
} | {
|
|
108
|
+
op: "render_animation";
|
|
109
|
+
params: RenderAnimationParams;
|
|
110
|
+
} | {
|
|
111
|
+
op: "prepare_for_engine";
|
|
112
|
+
params: PrepareForEngineParams;
|
|
113
|
+
} | {
|
|
114
|
+
op: "export_model";
|
|
115
|
+
params: ExportModelParams;
|
|
116
|
+
};
|
|
117
|
+
export interface BlenderJob {
|
|
118
|
+
version: typeof BLENDER_JOB_VERSION;
|
|
119
|
+
/** Logical input name -> bare file name. The runner writes these. */
|
|
120
|
+
inputs: {
|
|
121
|
+
model: string;
|
|
122
|
+
};
|
|
123
|
+
/** Logical output name -> bare file name the op must write. */
|
|
124
|
+
outputs: Record<string, string>;
|
|
125
|
+
job: BlenderOp;
|
|
126
|
+
}
|
|
127
|
+
declare const blenderErrorCodes: readonly ["import_failed", "no_geometry", "no_camera", "unsupported_format", "render_failed", "export_failed", "bake_failed", "bad_job"];
|
|
128
|
+
export type BlenderResultErrorCode = (typeof blenderErrorCodes)[number];
|
|
129
|
+
export declare const blenderResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
130
|
+
ok: z.ZodLiteral<true>;
|
|
131
|
+
produced: z.ZodArray<z.ZodString>;
|
|
132
|
+
stats: z.ZodObject<{
|
|
133
|
+
blender_version: z.ZodString;
|
|
134
|
+
render_seconds: z.ZodNumber;
|
|
135
|
+
frames: z.ZodOptional<z.ZodNumber>;
|
|
136
|
+
objects: z.ZodOptional<z.ZodNumber>;
|
|
137
|
+
depth_near: z.ZodOptional<z.ZodNumber>;
|
|
138
|
+
depth_far: z.ZodOptional<z.ZodNumber>;
|
|
139
|
+
camera: z.ZodOptional<z.ZodString>;
|
|
140
|
+
frame_camera_locations: z.ZodOptional<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>>;
|
|
141
|
+
}, z.core.$strip>;
|
|
142
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
143
|
+
ok: z.ZodLiteral<false>;
|
|
144
|
+
error: z.ZodObject<{
|
|
145
|
+
code: z.ZodEnum<{
|
|
146
|
+
bad_job: "bad_job";
|
|
147
|
+
bake_failed: "bake_failed";
|
|
148
|
+
export_failed: "export_failed";
|
|
149
|
+
import_failed: "import_failed";
|
|
150
|
+
no_camera: "no_camera";
|
|
151
|
+
no_geometry: "no_geometry";
|
|
152
|
+
render_failed: "render_failed";
|
|
153
|
+
unsupported_format: "unsupported_format";
|
|
154
|
+
}>;
|
|
155
|
+
message: z.ZodString;
|
|
156
|
+
}, z.core.$strip>;
|
|
157
|
+
}, z.core.$strip>], "ok">;
|
|
158
|
+
export type BlenderResult = z.infer<typeof blenderResultSchema>;
|
|
159
|
+
export type BlenderResultStats = Extract<BlenderResult, {
|
|
160
|
+
ok: true;
|
|
161
|
+
}>["stats"];
|
|
162
|
+
export {};
|
|
163
|
+
//# sourceMappingURL=job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"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"}
|
package/dist/job.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Blender job contract (D4).
|
|
3
|
+
*
|
|
4
|
+
* Every node produces a `BlenderJob`; the Python side consumes it and writes
|
|
5
|
+
* a `BlenderResult`. Both are versioned so the TypeScript and Python halves
|
|
6
|
+
* can drift by one version during an upgrade.
|
|
7
|
+
*
|
|
8
|
+
* The job names every file on both sides. The result never names a file: it
|
|
9
|
+
* reports which declared outputs were produced, and the host reads only the
|
|
10
|
+
* paths the job itself declared. A `produced` name the job did not declare
|
|
11
|
+
* is ignored, and a path inside `result.json` is never opened.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
/** Version both halves agree on. `run_job.py` rejects anything else. */
|
|
15
|
+
export const BLENDER_JOB_VERSION = 1;
|
|
16
|
+
/** A bare file name: no separator, no `..`, no leading dot. */
|
|
17
|
+
export const jobFileNameSchema = z
|
|
18
|
+
.string()
|
|
19
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
|
|
20
|
+
const blenderErrorCodes = [
|
|
21
|
+
"import_failed",
|
|
22
|
+
"no_geometry",
|
|
23
|
+
"no_camera",
|
|
24
|
+
"unsupported_format",
|
|
25
|
+
"render_failed",
|
|
26
|
+
"export_failed",
|
|
27
|
+
"bake_failed",
|
|
28
|
+
"bad_job"
|
|
29
|
+
];
|
|
30
|
+
export const blenderResultSchema = z.discriminatedUnion("ok", [
|
|
31
|
+
z.object({
|
|
32
|
+
ok: z.literal(true),
|
|
33
|
+
/** Logical output names the op wrote. Must be a subset of job.outputs. */
|
|
34
|
+
produced: z.array(z.string()),
|
|
35
|
+
stats: z.object({
|
|
36
|
+
blender_version: z.string(),
|
|
37
|
+
render_seconds: z.number(),
|
|
38
|
+
frames: z.number().int().optional(),
|
|
39
|
+
objects: z.number().int().optional(),
|
|
40
|
+
/** `render_passes` with depth: min/max finite foreground depth (D4). */
|
|
41
|
+
depth_near: z.number().optional(),
|
|
42
|
+
depth_far: z.number().optional(),
|
|
43
|
+
/**
|
|
44
|
+
* Name of the camera that rendered (`render_image` only): the scene
|
|
45
|
+
* camera's name, or the orbit camera the op created. Lets the
|
|
46
|
+
* camera-mode test assert the selection without reading pixels.
|
|
47
|
+
*/
|
|
48
|
+
camera: z.string().optional(),
|
|
49
|
+
/**
|
|
50
|
+
* Sampled `render_animation` only: the world-space location the camera
|
|
51
|
+
* held for each rendered frame, in order. What a camera-move test reads
|
|
52
|
+
* instead of comparing pixels.
|
|
53
|
+
*/
|
|
54
|
+
frame_camera_locations: z
|
|
55
|
+
.array(z.tuple([z.number(), z.number(), z.number()]))
|
|
56
|
+
.optional()
|
|
57
|
+
})
|
|
58
|
+
}),
|
|
59
|
+
z.object({
|
|
60
|
+
ok: z.literal(false),
|
|
61
|
+
error: z.object({
|
|
62
|
+
code: z.enum(blenderErrorCodes),
|
|
63
|
+
message: z.string()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
]);
|
|
67
|
+
//# sourceMappingURL=job.js.map
|
package/dist/job.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job.js","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wEAAwE;AACxE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAU,CAAC;AAE9C,+DAA+D;AAC/D,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,KAAK,CAAC,8BAA8B,CAAC,CAAC;AA6GzC,MAAM,iBAAiB,GAAG;IACxB,eAAe;IACf,aAAa;IACb,WAAW;IACX,oBAAoB;IACpB,eAAe;IACf,eAAe;IACf,aAAa;IACb,SAAS;CACD,CAAC;AAIX,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE;IAC5D,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;QACnB,0EAA0E;QAC1E,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;YACd,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;YAC3B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;YAC1B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;YACnC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;YACpC,wEAAwE;YACxE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YACjC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC;;;;eAIG;YACH,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC7B;;;;eAIG;YACH,sBAAsB,EAAE,CAAC;iBACtB,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;iBACpD,QAAQ,EAAE;SACd,CAAC;KACH,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;YACd,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;YAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;SACpB,CAAC;KACH,CAAC;CACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nodetool.blender.BakeTimelineClip` — the timeline's 3D bake as a job.
|
|
3
|
+
*
|
|
4
|
+
* Hidden from the palette: nobody adds this by hand. It exists so the browser
|
|
5
|
+
* can run a bake the way it runs any other render — one inline graph on the
|
|
6
|
+
* job queue, with per-frame progress and cancellation — while the server-side
|
|
7
|
+
* `bake_model3d_clip` op calls {@link bakeModel3DClipToVideo} directly. Both
|
|
8
|
+
* reach the same producer, so a bake started from the inspector and one
|
|
9
|
+
* started by an agent are the same pixels.
|
|
10
|
+
*
|
|
11
|
+
* The sample list is computed by `@nodetool-ai/timeline`
|
|
12
|
+
* (`computeModel3DBakeSamples`) and passed in whole: the node evaluates no
|
|
13
|
+
* clip of its own, because the clip lives in a document this node never sees.
|
|
14
|
+
*/
|
|
15
|
+
import { BaseNode } from "@nodetool-ai/node-sdk";
|
|
16
|
+
import type { ProcessingContext } from "@nodetool-ai/runtime";
|
|
17
|
+
import type { ModelBytesRefLike } from "@nodetool-ai/nodes-utils";
|
|
18
|
+
type BakeTimelineClipOutputs = {
|
|
19
|
+
video: {
|
|
20
|
+
type: string;
|
|
21
|
+
uri: string;
|
|
22
|
+
asset_id: null;
|
|
23
|
+
data: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export declare class BakeTimelineClipNode extends BaseNode {
|
|
27
|
+
static readonly nodeType = "nodetool.blender.BakeTimelineClip";
|
|
28
|
+
static readonly title = "Bake 3D Timeline Clip";
|
|
29
|
+
static readonly description = "Render one timeline 3D clip to video with Blender, one still per output frame at the clip's own model time and camera.\n 3d, timeline, bake, blender, render, video";
|
|
30
|
+
static readonly hidden = true;
|
|
31
|
+
static readonly metadataOutputTypes: {
|
|
32
|
+
video: string;
|
|
33
|
+
};
|
|
34
|
+
static readonly inlineFields: never[];
|
|
35
|
+
static readonly inputFields: string[];
|
|
36
|
+
model: ModelBytesRefLike;
|
|
37
|
+
frame_times: unknown;
|
|
38
|
+
cameras: unknown;
|
|
39
|
+
animation_name: string;
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
fps: number;
|
|
43
|
+
timeout: number;
|
|
44
|
+
process(context?: ProcessingContext): Promise<BakeTimelineClipOutputs>;
|
|
45
|
+
}
|
|
46
|
+
export declare const BLENDER_BAKE_NODES: readonly [typeof BakeTimelineClipNode];
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=bake-timeline-clip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bake-timeline-clip.d.ts","sourceRoot":"","sources":["../../src/nodes/bake-timeline-clip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAOlE,KAAK,uBAAuB,GAAG;IAC7B,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE,CAAC;AASF,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,MAAM,CAAC,QAAQ,CAAC,QAAQ,uCAAa;IACrC,MAAM,CAAC,QAAQ,CAAC,KAAK,2BAA2B;IAChD,MAAM,CAAC,QAAQ,CAAC,WAAW,4KACgJ;IAC3K,MAAM,CAAC,QAAQ,CAAC,MAAM,QAAQ;IAC9B,MAAM,CAAC,QAAQ,CAAC,mBAAmB;QACjC,KAAK;MACL;IACF,MAAM,CAAC,QAAQ,CAAC,YAAY,UAAM;IAClC,MAAM,CAAC,QAAQ,CAAC,WAAW,WAAa;IAQhC,KAAK,EAAE,iBAAiB,CAAC;IASzB,WAAW,EAAE,OAAO,CAAC;IASrB,OAAO,EAAE,OAAO,CAAC;IASjB,cAAc,EAAE,MAAM,CAAC;IAGvB,KAAK,EAAE,MAAM,CAAC;IAGd,MAAM,EAAE,MAAM,CAAC;IAGf,GAAG,EAAE,MAAM,CAAC;IAGZ,OAAO,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA6E3E;CACF;AAED,eAAO,MAAM,kBAAkB,wCAAkC,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nodetool.blender.BakeTimelineClip` — the timeline's 3D bake as a job.
|
|
3
|
+
*
|
|
4
|
+
* Hidden from the palette: nobody adds this by hand. It exists so the browser
|
|
5
|
+
* can run a bake the way it runs any other render — one inline graph on the
|
|
6
|
+
* job queue, with per-frame progress and cancellation — while the server-side
|
|
7
|
+
* `bake_model3d_clip` op calls {@link bakeModel3DClipToVideo} directly. Both
|
|
8
|
+
* reach the same producer, so a bake started from the inspector and one
|
|
9
|
+
* started by an agent are the same pixels.
|
|
10
|
+
*
|
|
11
|
+
* The sample list is computed by `@nodetool-ai/timeline`
|
|
12
|
+
* (`computeModel3DBakeSamples`) and passed in whole: the node evaluates no
|
|
13
|
+
* clip of its own, because the clip lives in a document this node never sees.
|
|
14
|
+
*/
|
|
15
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
16
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
17
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
18
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
19
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
20
|
+
};
|
|
21
|
+
import { prop } from "@nodetool-ai/node-sdk";
|
|
22
|
+
import { BaseNode } from "@nodetool-ai/node-sdk";
|
|
23
|
+
import { bytesToBase64 } from "@nodetool-ai/nodes-utils";
|
|
24
|
+
import { loadMediaRefBytes } from "@nodetool-ai/runtime";
|
|
25
|
+
import { bakeModel3DClipToVideo } from "../bake.js";
|
|
26
|
+
import { DEFAULT_MODEL_3D } from "./defaults.js";
|
|
27
|
+
import { runBlenderNodeStep } from "./blender-error.js";
|
|
28
|
+
import { blenderProgressHandler } from "./progress.js";
|
|
29
|
+
const NODE_NAME = "nodetool.blender.BakeTimelineClip";
|
|
30
|
+
function timeoutMessage(timeoutMs) {
|
|
31
|
+
return (`${NODE_NAME}: the bake timed out after ${timeoutMs}ms. ` +
|
|
32
|
+
`Lower the samples, use EEVEE, shorten the clip, or raise the timeout.`);
|
|
33
|
+
}
|
|
34
|
+
export class BakeTimelineClipNode extends BaseNode {
|
|
35
|
+
static nodeType = NODE_NAME;
|
|
36
|
+
static title = "Bake 3D Timeline Clip";
|
|
37
|
+
static description = "Render one timeline 3D clip to video with Blender, one still per output frame at the clip's own model time and camera.\n 3d, timeline, bake, blender, render, video";
|
|
38
|
+
static hidden = true;
|
|
39
|
+
static metadataOutputTypes = {
|
|
40
|
+
video: "video"
|
|
41
|
+
};
|
|
42
|
+
static inlineFields = [];
|
|
43
|
+
static inputFields = ["model"];
|
|
44
|
+
async process(context) {
|
|
45
|
+
if (!context) {
|
|
46
|
+
throw new Error(`${NODE_NAME}: a bake needs a processing context with a workspace.`);
|
|
47
|
+
}
|
|
48
|
+
// `asset://` is how the browser names the clip's glTF, which the media-ref
|
|
49
|
+
// reader dereferences and `resolveModelBytes` does not.
|
|
50
|
+
// SAFETY: `model` is declared as a model3d prop, so the graph only ever
|
|
51
|
+
// binds a media ref or nothing; the reader reads these three fields and
|
|
52
|
+
// treats every one of them as optional, and an empty object yields no
|
|
53
|
+
// bytes, which the next check refuses by name.
|
|
54
|
+
const bytes = await loadMediaRefBytes((this.model ?? {}), context);
|
|
55
|
+
if (!bytes || bytes.length === 0) {
|
|
56
|
+
throw new Error(`${NODE_NAME}: model input is empty — connect the clip's glTF (GLB)`);
|
|
57
|
+
}
|
|
58
|
+
// SAFETY: guarded by the `Array.isArray` above, and every element goes
|
|
59
|
+
// through `Number()` before the finite check below, so an element of any
|
|
60
|
+
// type is either a number or refused.
|
|
61
|
+
const frameTimes = Array.isArray(this.frame_times)
|
|
62
|
+
? this.frame_times.map((value) => Number(value))
|
|
63
|
+
: [];
|
|
64
|
+
if (frameTimes.length === 0 || frameTimes.some((t) => !Number.isFinite(t))) {
|
|
65
|
+
throw new Error(`${NODE_NAME}: frame_times must be a non-empty list of finite seconds.`);
|
|
66
|
+
}
|
|
67
|
+
// SAFETY: guarded by the `Array.isArray` above. The entries are the
|
|
68
|
+
// camera DTOs `computeModel3DBakeSamples` built, and the op validates
|
|
69
|
+
// their count against `frame_times` and reads each field with a default,
|
|
70
|
+
// so a malformed entry is refused there rather than here.
|
|
71
|
+
const cameras = Array.isArray(this.cameras)
|
|
72
|
+
? this.cameras
|
|
73
|
+
: [];
|
|
74
|
+
const timeoutMs = Math.max(1, Number(this.timeout ?? 1800)) * 1000;
|
|
75
|
+
const request = {
|
|
76
|
+
frameTimes,
|
|
77
|
+
cameras,
|
|
78
|
+
width: Math.max(1, Math.round(Number(this.width ?? 1920))),
|
|
79
|
+
height: Math.max(1, Math.round(Number(this.height ?? 1080))),
|
|
80
|
+
fps: Math.max(1, Math.round(Number(this.fps ?? 30)))
|
|
81
|
+
};
|
|
82
|
+
// An empty prop is "no selection", which leaves every animation playing.
|
|
83
|
+
const animationName = String(this.animation_name ?? "").trim();
|
|
84
|
+
if (animationName)
|
|
85
|
+
request.animationName = animationName;
|
|
86
|
+
return runBlenderNodeStep({
|
|
87
|
+
nodeName: NODE_NAME,
|
|
88
|
+
timeoutMessage: timeoutMessage(timeoutMs),
|
|
89
|
+
signal: context.signal
|
|
90
|
+
}, async () => {
|
|
91
|
+
const baked = await bakeModel3DClipToVideo(context, bytes, request, {
|
|
92
|
+
timeoutMs,
|
|
93
|
+
signal: context.signal,
|
|
94
|
+
onProgress: blenderProgressHandler(context, this.__node_id)
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
video: {
|
|
98
|
+
type: "video",
|
|
99
|
+
uri: "",
|
|
100
|
+
asset_id: null,
|
|
101
|
+
data: bytesToBase64(baked.video)
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
__decorate([
|
|
108
|
+
prop({
|
|
109
|
+
type: "model_3d",
|
|
110
|
+
default: DEFAULT_MODEL_3D,
|
|
111
|
+
title: "Model",
|
|
112
|
+
description: "The glTF the clip draws (GLB or glTF with embedded buffers)"
|
|
113
|
+
})
|
|
114
|
+
], BakeTimelineClipNode.prototype, "model", void 0);
|
|
115
|
+
__decorate([
|
|
116
|
+
prop({
|
|
117
|
+
type: "list",
|
|
118
|
+
default: [],
|
|
119
|
+
title: "Frame Times",
|
|
120
|
+
description: "Model time in seconds for each output frame, already resolved for the clip's in point, speed, time remap and animation speed"
|
|
121
|
+
})
|
|
122
|
+
], BakeTimelineClipNode.prototype, "frame_times", void 0);
|
|
123
|
+
__decorate([
|
|
124
|
+
prop({
|
|
125
|
+
type: "list",
|
|
126
|
+
default: [],
|
|
127
|
+
title: "Cameras",
|
|
128
|
+
description: "The camera of each output frame, in the render_animation camera vocabulary; one entry per frame time"
|
|
129
|
+
})
|
|
130
|
+
], BakeTimelineClipNode.prototype, "cameras", void 0);
|
|
131
|
+
__decorate([
|
|
132
|
+
prop({
|
|
133
|
+
type: "str",
|
|
134
|
+
default: "",
|
|
135
|
+
title: "Animation Name",
|
|
136
|
+
description: "glTF animation to play; empty leaves every animation playing, which is what a 3D clip does by default"
|
|
137
|
+
})
|
|
138
|
+
], BakeTimelineClipNode.prototype, "animation_name", void 0);
|
|
139
|
+
__decorate([
|
|
140
|
+
prop({ type: "int", default: 1920, title: "Width", description: "Output video width in pixels", min: 16, max: 4096 })
|
|
141
|
+
], BakeTimelineClipNode.prototype, "width", void 0);
|
|
142
|
+
__decorate([
|
|
143
|
+
prop({ type: "int", default: 1080, title: "Height", description: "Output video height in pixels", min: 16, max: 4096 })
|
|
144
|
+
], BakeTimelineClipNode.prototype, "height", void 0);
|
|
145
|
+
__decorate([
|
|
146
|
+
prop({ type: "int", default: 30, title: "FPS", description: "Sequence frames per second; the bake plays at this rate", min: 1, max: 120 })
|
|
147
|
+
], BakeTimelineClipNode.prototype, "fps", void 0);
|
|
148
|
+
__decorate([
|
|
149
|
+
prop({ type: "int", default: 1800, title: "Timeout", description: "Maximum bake time in seconds", min: 1, max: 7200 })
|
|
150
|
+
], BakeTimelineClipNode.prototype, "timeout", void 0);
|
|
151
|
+
export const BLENDER_BAKE_NODES = [BakeTimelineClipNode];
|
|
152
|
+
//# sourceMappingURL=bake-timeline-clip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bake-timeline-clip.js","sourceRoot":"","sources":["../../src/nodes/bake-timeline-clip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared failure mapping for the Blender nodes.
|
|
3
|
+
*
|
|
4
|
+
* A node wraps its whole job in `runBlenderNodeStep`, which catches the
|
|
5
|
+
* failure where it happens rather than handing an unparsed value on: it
|
|
6
|
+
* rethrows with the node name prefixed, maps the wall-clock timeout onto
|
|
7
|
+
* the knobs that fix it, and passes an abort through unwrapped so the node
|
|
8
|
+
* rejects with the abort reason and no partial output. The empty-passes and
|
|
9
|
+
* unknown-format refusals already carry the node name, so they pass through
|
|
10
|
+
* unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export interface BlenderNodeStep {
|
|
13
|
+
/** Node type prefixed onto every failure this step raises. */
|
|
14
|
+
nodeName: string;
|
|
15
|
+
/** Message replacing a runner timeout, naming the knobs that fix it. */
|
|
16
|
+
timeoutMessage: string;
|
|
17
|
+
/** Cancellation passes through unwrapped when this signal aborted. */
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}
|
|
20
|
+
export declare function runBlenderNodeStep<T>(step: BlenderNodeStep, run: () => Promise<T>): Promise<T>;
|
|
21
|
+
//# sourceMappingURL=blender-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blender-error.d.ts","sourceRoot":"","sources":["../../src/nodes/blender-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACpB,OAAO,CAAC,CAAC,CAAC,CAwBZ"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared failure mapping for the Blender nodes.
|
|
3
|
+
*
|
|
4
|
+
* A node wraps its whole job in `runBlenderNodeStep`, which catches the
|
|
5
|
+
* failure where it happens rather than handing an unparsed value on: it
|
|
6
|
+
* rethrows with the node name prefixed, maps the wall-clock timeout onto
|
|
7
|
+
* the knobs that fix it, and passes an abort through unwrapped so the node
|
|
8
|
+
* rejects with the abort reason and no partial output. The empty-passes and
|
|
9
|
+
* unknown-format refusals already carry the node name, so they pass through
|
|
10
|
+
* unchanged.
|
|
11
|
+
*/
|
|
12
|
+
import { BlenderJobError } from "../runner.js";
|
|
13
|
+
export async function runBlenderNodeStep(step, run) {
|
|
14
|
+
try {
|
|
15
|
+
return await run();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
// Cancellation rejects with the abort reason: pass it through
|
|
19
|
+
// unwrapped so the node rejects with the abort reason.
|
|
20
|
+
if (step.signal?.aborted)
|
|
21
|
+
throw error;
|
|
22
|
+
if (error instanceof BlenderJobError) {
|
|
23
|
+
if (error.code === "timeout") {
|
|
24
|
+
throw new BlenderJobError("timeout", step.timeoutMessage);
|
|
25
|
+
}
|
|
26
|
+
// A refusal that already carries the node name (empty passes,
|
|
27
|
+
// unknown export format) passes through unchanged.
|
|
28
|
+
if (error.message.startsWith(step.nodeName))
|
|
29
|
+
throw error;
|
|
30
|
+
throw new BlenderJobError(error.code, `${step.nodeName}: ${error.message}`);
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof Error) {
|
|
33
|
+
throw new Error(`${step.nodeName}: ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`${step.nodeName}: ${String(error)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=blender-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blender-error.js","sourceRoot":"","sources":["../../src/nodes/blender-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAW/C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAqB,EACrB,GAAqB;IAErB,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,8DAA8D;QAC9D,uDAAuD;QACvD,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,MAAM,KAAK,CAAC;QACtC,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;YAC5D,CAAC;YACD,8DAA8D;YAC9D,mDAAmD;YACnD,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,MAAM,KAAK,CAAC;YACzD,MAAM,IAAI,eAAe,CACvB,KAAK,CAAC,IAAI,EACV,GAAG,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CACrC,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default prop values for the Blender nodes.
|
|
3
|
+
*
|
|
4
|
+
* Local copy of the `model_3d` default shape (see `DEFAULT_MODEL_3D` in
|
|
5
|
+
* `video-nodes`): this package must not depend on `video-nodes` for one
|
|
6
|
+
* constant.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_MODEL_3D: {
|
|
9
|
+
type: string;
|
|
10
|
+
uri: string;
|
|
11
|
+
asset_id: null;
|
|
12
|
+
data: null;
|
|
13
|
+
metadata: null;
|
|
14
|
+
format: null;
|
|
15
|
+
material_file: null;
|
|
16
|
+
texture_files: never[];
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/nodes/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,gBAAgB;IAC3B,IAAI;IACJ,GAAG;IACH,QAAQ;IACR,IAAI;IACJ,QAAQ;IACR,MAAM;IACN,aAAa;IACb,aAAa;CACd,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default prop values for the Blender nodes.
|
|
3
|
+
*
|
|
4
|
+
* Local copy of the `model_3d` default shape (see `DEFAULT_MODEL_3D` in
|
|
5
|
+
* `video-nodes`): this package must not depend on `video-nodes` for one
|
|
6
|
+
* constant.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_MODEL_3D = {
|
|
9
|
+
type: "model_3d",
|
|
10
|
+
uri: "",
|
|
11
|
+
asset_id: null,
|
|
12
|
+
data: null,
|
|
13
|
+
metadata: null,
|
|
14
|
+
format: null,
|
|
15
|
+
material_file: null,
|
|
16
|
+
texture_files: []
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/nodes/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,UAAU;IAChB,GAAG,EAAE,EAAE;IACP,QAAQ,EAAE,IAAI;IACd,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,IAAI;IACd,MAAM,EAAE,IAAI;IACZ,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,EAAE;CAClB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nodetool.blender.ExportModel` — glTF scene → FBX, OBJ, or USD file.
|
|
3
|
+
*
|
|
4
|
+
* Stage 3: the `export_model` op over `LocalBlenderRunner` (D8). Takes a
|
|
5
|
+
* `Model3DRef` and returns an `AssetRef`, never a `Model3DRef`: FBX, OBJ,
|
|
6
|
+
* and USD are not glTF documents, and the `model_3d` socket keeps meaning
|
|
7
|
+
* glTF everywhere. The node persists the export through
|
|
8
|
+
* `context.createAsset` and returns `{ type: "asset", uri, asset_id,
|
|
9
|
+
* metadata: { format, mime } }`. GLB is deliberately not a format here:
|
|
10
|
+
* `PrepareForEngine` and `FormatConverter` already produce a `Model3DRef`
|
|
11
|
+
* for it.
|
|
12
|
+
*
|
|
13
|
+
* Every failure rethrows with the node name prefixed. An abort through
|
|
14
|
+
* `context.signal` passes through unwrapped so the node rejects with the
|
|
15
|
+
* abort reason and no partial output.
|
|
16
|
+
*/
|
|
17
|
+
import { BaseNode } from "@nodetool-ai/node-sdk";
|
|
18
|
+
import type { ModelBytesRefLike } from "@nodetool-ai/nodes-utils";
|
|
19
|
+
import type { ProcessingContext } from "@nodetool-ai/runtime";
|
|
20
|
+
import type { ExportFormat } from "../job.js";
|
|
21
|
+
/** Output handle ExportModelNode.process() emits. */
|
|
22
|
+
type ExportModelNodeOutputs = {
|
|
23
|
+
file: {
|
|
24
|
+
type: string;
|
|
25
|
+
uri: string;
|
|
26
|
+
asset_id: string;
|
|
27
|
+
metadata: {
|
|
28
|
+
format: string;
|
|
29
|
+
mime: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export declare class ExportModelNode extends BaseNode {
|
|
34
|
+
static readonly nodeType = "nodetool.blender.ExportModel";
|
|
35
|
+
static readonly title = "Export 3D Model With Blender";
|
|
36
|
+
static readonly description = "Export a 3D model (GLB/glTF) to FBX, OBJ, or USD with Blender, persisted as a stored asset.\n 3d, mesh, model, export, fbx, obj, usd, file, blender\n\n Use cases:\n - Hand a generated mesh to a game engine as FBX\n - Export an OBJ for tools without glTF support\n - Export USD for pipelines built on it";
|
|
37
|
+
static readonly metadataOutputTypes: {
|
|
38
|
+
file: string;
|
|
39
|
+
};
|
|
40
|
+
static readonly inlineFields: never[];
|
|
41
|
+
static readonly inputFields: string[];
|
|
42
|
+
model: ModelBytesRefLike;
|
|
43
|
+
format: ExportFormat;
|
|
44
|
+
timeout: number;
|
|
45
|
+
process(context?: ProcessingContext): Promise<ExportModelNodeOutputs>;
|
|
46
|
+
}
|
|
47
|
+
export declare const BLENDER_EXPORT_NODES: readonly [typeof ExportModelNode];
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=export-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-model.d.ts","sourceRoot":"","sources":["../../src/nodes/export-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAQ,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAS9C,qDAAqD;AACrD,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KAC5C,CAAC;CACH,CAAC;AA8BF,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,MAAM,CAAC,QAAQ,CAAC,QAAQ,kCAAkC;IAC1D,MAAM,CAAC,QAAQ,CAAC,KAAK,kCAAkC;IACvD,MAAM,CAAC,QAAQ,CAAC,WAAW,mUACuS;IAClU,MAAM,CAAC,QAAQ,CAAC,mBAAmB;QACjC,IAAI;MACJ;IACF,MAAM,CAAC,QAAQ,CAAC,YAAY,UAAM;IAClC,MAAM,CAAC,QAAQ,CAAC,WAAW,WAAa;IAQhC,KAAK,EAAE,iBAAiB,CAAC;IASzB,MAAM,EAAE,YAAY,CAAC;IAGrB,OAAO,EAAE,MAAM,CAAC;IAElB,OAAO,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAwE1E;CACF;AAED,eAAO,MAAM,oBAAoB,mCAA6B,CAAC"}
|