davinci-resolve-mcp 2.33.1 → 2.33.2

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.
@@ -0,0 +1,780 @@
1
+ # Category Patterns
2
+
3
+ Every category hooks into Resolve slightly differently. This file shows the **minimum viable skeleton** for each, and what changes between them. Start with the shape that matches your target category, then modify.
4
+
5
+ ## 1. Edit Effect (clip filter)
6
+
7
+ **Role:** pixel filter applied to a single clip. Reads the clip, writes a processed version.
8
+
9
+ **Hook:** exactly one `MainInput1` connected to the internal "entry" node's `Input` parameter.
10
+
11
+ ```lua
12
+ {
13
+ Tools = ordered() {
14
+ MyEffect = GroupOperator {
15
+ Inputs = ordered() {
16
+ MainInput1 = InstanceInput {
17
+ SourceOp = "Blur1",
18
+ Source = "Input",
19
+ },
20
+ Strength = InstanceInput {
21
+ SourceOp = "Blur1",
22
+ Source = "XBlurSize",
23
+ Default = 5,
24
+ MinScale = 0,
25
+ MaxScale = 100,
26
+ },
27
+ },
28
+ Outputs = {
29
+ MainOutput1 = InstanceOutput {
30
+ SourceOp = "Blur1",
31
+ Source = "Output",
32
+ },
33
+ },
34
+ Tools = ordered() {
35
+ Blur1 = Blur {
36
+ Inputs = {
37
+ Filter = Input { Value = FuID { "Fast Gaussian" } },
38
+ XBlurSize = Input { Value = 5 },
39
+ LockXY = Input { Value = 1 },
40
+ },
41
+ },
42
+ },
43
+ },
44
+ },
45
+ ActiveTool = "MyEffect",
46
+ }
47
+ ```
48
+
49
+ **Install path:** `Templates/Edit/Effects/`.
50
+
51
+ ## 2. Edit Transition
52
+
53
+ **Role:** blends between two clips over a duration.
54
+
55
+ **Hook:** two MainInputs — `MainInput1` (Background, the outgoing clip) and `MainInput2` (Foreground, the incoming clip). Both feed a `Dissolve` or `Merge` node.
56
+
57
+ **Progress animation:** driven by a `LUTLookup` reading `FuID { "Duration" }` with an easing curve. Fusion automatically evaluates the curve across the transition length and outputs a 0→1 ramp.
58
+
59
+ ```lua
60
+ {
61
+ Tools = ordered() {
62
+ MyTransition = GroupOperator {
63
+ Inputs = ordered() {
64
+ MainInput1 = InstanceInput {
65
+ SourceOp = "Dissolve1",
66
+ Source = "Background",
67
+ },
68
+ MainInput2 = InstanceInput {
69
+ SourceOp = "Dissolve1",
70
+ Source = "Foreground",
71
+ },
72
+ Softness = InstanceInput {
73
+ SourceOp = "Dissolve1",
74
+ Source = "DFTLumaRamp.Softness",
75
+ Default = 0.05,
76
+ MinScale = 0,
77
+ MaxScale = 1,
78
+ },
79
+ },
80
+ Outputs = {
81
+ MainOutput1 = InstanceOutput {
82
+ SourceOp = "Dissolve1",
83
+ Source = "Output",
84
+ },
85
+ },
86
+ Tools = ordered() {
87
+ Dissolve1 = Dissolve {
88
+ Inputs = {
89
+ Operation = Input { Value = FuID { "DFTDissolve" } },
90
+ Mix = Input {
91
+ SourceOp = "ProgressCurve",
92
+ Source = "Value",
93
+ },
94
+ },
95
+ },
96
+ ProgressCurve = LUTLookup {
97
+ Inputs = {
98
+ Source = Input { Value = FuID { "Duration" } },
99
+ Curve = Input { Value = FuID { "Easing" } },
100
+ EaseIn = Input { Value = FuID { "Cubic" } },
101
+ EaseOut = Input { Value = FuID { "Cubic" } },
102
+ Lookup = Input {
103
+ SourceOp = "ProgressCurveLookup",
104
+ Source = "Value",
105
+ },
106
+ },
107
+ },
108
+ ProgressCurveLookup = LUTBezier {
109
+ KeyColorSplines = {
110
+ [0] = {
111
+ [0] = { 0, RH = { 0.333, 0.333 }, Flags = { Linear = true } },
112
+ [1] = { 1, LH = { 0.666, 0.666 }, Flags = { Linear = true } }
113
+ }
114
+ },
115
+ SplineColor = { Red = 255, Green = 255, Blue = 255 },
116
+ },
117
+ },
118
+ },
119
+ },
120
+ ActiveTool = "MyTransition",
121
+ }
122
+ ```
123
+
124
+ **Install path:** `Templates/Edit/Transitions/`.
125
+
126
+ **Transition operations worth knowing (for `Dissolve.Operation`):**
127
+
128
+ | FuID | Effect |
129
+ |------|--------|
130
+ | `DFTDissolve` | Straight cross-dissolve |
131
+ | `DFTLumaRamp` | Wipe driven by the `Map` input's luma — lets you shape the wipe with any image |
132
+ | `DFTAdditiveDissolve` | Additive blend |
133
+
134
+ `DFTLumaRamp` is the workhorse for wipes: pipe a gradient or shape into `Map` and you get a controllable wipe. Box Wipe and most stock transitions use this pattern.
135
+
136
+ ## 3. Edit Title
137
+
138
+ **Role:** text generator with stretched animation.
139
+
140
+ **Hooks:** no MainInput. Wraps a `TextPlus` node. Internal animation is piped through a `KeyStretcher` so it stretches to the clip's actual duration.
141
+
142
+ ```lua
143
+ {
144
+ Tools = ordered() {
145
+ MyTitle = GroupOperator {
146
+ Inputs = ordered() {
147
+ Input1 = InstanceInput { SourceOp = "Text", Source = "StyledText" },
148
+ Input2 = InstanceInput { SourceOp = "Text", Source = "Font", ControlGroup = 2 },
149
+ Input3 = InstanceInput { SourceOp = "Text", Source = "Style", ControlGroup = 2 },
150
+ Size = InstanceInput { SourceOp = "Text", Source = "Size", Default = 0.08 },
151
+ Pos = InstanceInput { SourceOp = "Text", Source = "Center", Name = "Position" },
152
+ Red = InstanceInput { SourceOp = "Text", Source = "Red", Name = "Color", ControlGroup = 10, Default = 1 },
153
+ Green = InstanceInput { SourceOp = "Text", Source = "Green", Name = "Color", ControlGroup = 10, Default = 1 },
154
+ Blue = InstanceInput { SourceOp = "Text", Source = "Blue", Name = "Color", ControlGroup = 10, Default = 1 },
155
+ Alpha = InstanceInput { SourceOp = "Text", Source = "Alpha", Name = "Color", ControlGroup = 10, Default = 1 },
156
+ },
157
+ Outputs = {
158
+ MainOutput1 = InstanceOutput {
159
+ SourceOp = "Stretcher",
160
+ Source = "Result",
161
+ },
162
+ },
163
+ Tools = ordered() {
164
+ Text = TextPlus {
165
+ Inputs = {
166
+ GlobalOut = Input { Value = 500 },
167
+ Width = Input { Value = 1920 },
168
+ Height = Input { Value = 1080 },
169
+ UseFrameFormatSettings = Input { Value = 1 },
170
+ StyledText = Input { Value = "SAMPLE" },
171
+ Font = Input { Value = "Open Sans" },
172
+ Style = Input { Value = "Bold" },
173
+ Size = Input { Value = 0.08 },
174
+ VerticalJustificationNew = Input { Value = 3 },
175
+ HorizontalJustificationNew = Input { Value = 3 },
176
+ },
177
+ },
178
+ Stretcher = KeyStretcher {
179
+ Inputs = {
180
+ Keyframes = Input {
181
+ SourceOp = "Text",
182
+ Source = "Output",
183
+ },
184
+ SourceEnd = Input { Value = 119 },
185
+ StretchStart = Input { Value = 10 },
186
+ StretchEnd = Input { Value = 100 },
187
+ },
188
+ },
189
+ },
190
+ },
191
+ },
192
+ ActiveTool = "MyTitle",
193
+ }
194
+ ```
195
+
196
+ **Install path:** `Templates/Edit/Titles/`.
197
+
198
+ Key details:
199
+
200
+ - `MainOutput1` pulls `Source = "Result"` from the `KeyStretcher` (not `"Output"` — that's the unstretched pixels).
201
+ - Animation on the `TextPlus` (via `BezierSpline` inputs for Alpha, Size, Color, etc.) gets stretched to match the clip length by `KeyStretcher`.
202
+ - Stock titles often expose *many* text controls (font, style, size, tracking, line spacing, V/H anchor, color, background color). Copy the pattern from `Edit/Titles/Background Reveal.setting` if you want the full treatment.
203
+
204
+ ## 4. Edit Generator
205
+
206
+ **Role:** pure pixel source. No input clip — just produces an image.
207
+
208
+ **Hooks:** no MainInput. One `MainOutput1`.
209
+
210
+ ```lua
211
+ {
212
+ Tools = ordered() {
213
+ MyGenerator = GroupOperator {
214
+ Inputs = ordered() {
215
+ Detail = InstanceInput {
216
+ SourceOp = "Noise1",
217
+ Source = "Detail",
218
+ Default = 5,
219
+ MinScale = 0,
220
+ MaxScale = 10,
221
+ },
222
+ Scale = InstanceInput {
223
+ SourceOp = "Noise1",
224
+ Source = "XScale",
225
+ Default = 10,
226
+ MinScale = 1,
227
+ MaxScale = 50,
228
+ },
229
+ },
230
+ Outputs = {
231
+ MainOutput1 = InstanceOutput {
232
+ SourceOp = "Noise1",
233
+ Source = "Output",
234
+ },
235
+ },
236
+ Tools = ordered() {
237
+ Noise1 = FastNoise {
238
+ Inputs = {
239
+ Width = Input { Value = 1920 },
240
+ Height = Input { Value = 1080 },
241
+ UseFrameFormatSettings = Input { Value = 1 },
242
+ Detail = Input { Value = 5 },
243
+ XScale = Input { Value = 10 },
244
+ },
245
+ },
246
+ },
247
+ },
248
+ },
249
+ ActiveTool = "MyGenerator",
250
+ }
251
+ ```
252
+
253
+ **Install path:** `Templates/Edit/Generators/`.
254
+
255
+ ## 5. Fusion Tool
256
+
257
+ **Role:** pixel filter applied on the Fusion page. Reads an upstream image, writes a processed version. Functionally identical to an Edit Effect but installed on the Fusion page.
258
+
259
+ **Hook:** `GroupOperator` with `MainInput1` wired to the first internal node's `Input`. Internal nodes can carry `UserControls` on sub-nodes — those controls surface automatically without needing to be re-declared as `InstanceInput`. The duplicate-top-level-node artifact: nodes referenced from inside the group also appear again at the outer top level of the file (a serialization quirk) — do not try to remove them.
260
+
261
+ ```lua
262
+ {
263
+ Tools = ordered() {
264
+ MyFusionTool = GroupOperator {
265
+ Inputs = ordered() {
266
+ MainInput1 = InstanceInput {
267
+ SourceOp = "Transform1",
268
+ Source = "Input",
269
+ },
270
+ Magnitude = InstanceInput {
271
+ SourceOp = "Expr1",
272
+ Source = "n1",
273
+ Name = "Overall Magnitude",
274
+ Default = 0.5,
275
+ },
276
+ },
277
+ Outputs = {
278
+ MainOutput1 = InstanceOutput {
279
+ SourceOp = "Transform1",
280
+ Source = "Output",
281
+ },
282
+ },
283
+ Tools = ordered() {
284
+ Expr1 = Custom {
285
+ -- drives transform amounts
286
+ },
287
+ Transform1 = Transform {
288
+ Inputs = {
289
+ Input = Input { SourceOp = "...", Source = "Output" },
290
+ -- ...
291
+ },
292
+ },
293
+ },
294
+ },
295
+ -- serialization artifact: inner nodes appear here too
296
+ Transform1 = Transform { -- ...
297
+ },
298
+ },
299
+ ActiveTool = "MyFusionTool",
300
+ }
301
+ ```
302
+
303
+ **Install path:** `Templates/Fusion/Tools/`.
304
+
305
+ **Key details:**
306
+
307
+ - Structure is identical to an Edit Effect — `GroupOperator` + `MainInput1` + `MainOutput1`.
308
+ - Sub-nodes can have `UserControls` blocks (e.g., `RangeControl` pairs on `Shake` nodes) that surface in the inspector without needing extra `InstanceInput` declarations.
309
+ - The top-level `Tools` block will contain duplicate entries for inner nodes — this is normal and required by Resolve's serializer; do not delete them.
310
+
311
+ ---
312
+
313
+ ## 6. Fusion Background
314
+
315
+ **Role:** pure 3D pixel source — no input clip, produces a rendered image. Backgrounds are typically full 3D scene pipelines, not 2D procedural generators.
316
+
317
+ **Hook:** two valid shapes exist in the stock library. (a) Wrapped: a `GroupOperator` with no `MainInput` and a single output (either `MainOutput1` or `Output1`) sourced from a `Renderer3D`. (b) Unwrapped: raw top-level nodes with `ActiveTool` pointing to the terminal `Renderer3D`. The canonical pipeline is `Shape3D → [Bender3D / Transform3D] → Merge3D → Camera3D → Renderer3D`.
318
+
319
+ ```lua
320
+ -- Wrapped variant (e.g., Blue Rays.setting)
321
+ {
322
+ Tools = ordered() {
323
+ MyBackground = GroupOperator {
324
+ Inputs = ordered() {
325
+ Color = InstanceInput {
326
+ SourceOp = "Shape3D1",
327
+ Source = "MtlStdInputs.Diffuse.Color.Red",
328
+ ControlGroup = 1,
329
+ Default = 0.08,
330
+ },
331
+ -- more color / glow controls ...
332
+ },
333
+ Outputs = {
334
+ MainOutput1 = InstanceOutput {
335
+ SourceOp = "Renderer3D1",
336
+ Source = "Output",
337
+ },
338
+ },
339
+ Tools = ordered() {
340
+ Shape3D1 = Shape3D { -- geometry source
341
+ Inputs = { Shape = Input { Value = FuID { "SurfaceCylinderInputs" } } },
342
+ },
343
+ Merge3D1 = Merge3D { Inputs = { SceneInput1 = Input { SourceOp = "Shape3D1", Source = "Output" } } },
344
+ Camera3D1 = Camera3D { },
345
+ Renderer3D1 = Renderer3D {
346
+ Inputs = {
347
+ SceneInput = Input { SourceOp = "Merge3D1", Source = "Output" },
348
+ Width = Input { Value = 1920 },
349
+ Height = Input { Value = 1080 },
350
+ },
351
+ },
352
+ },
353
+ },
354
+ },
355
+ ActiveTool = "MyBackground",
356
+ }
357
+
358
+ -- Unwrapped variant (e.g., Bender.setting): raw nodes at top level
359
+ -- ActiveTool = "Renderer3D1_2" -- Resolve previews this node
360
+ ```
361
+
362
+ **Install path:** `Templates/Fusion/Backgrounds/`.
363
+
364
+ **Key details:**
365
+
366
+ - No `MainInput` — these are pure sources.
367
+ - Wrapped files may use either `MainOutput1` or `Output1` for the group output. `MainOutput1` is preferred for consistency with Edit categories; `Output1` also works.
368
+ - Unwrapped files have no `GroupOperator` at all. `ActiveTool` must point to the `Renderer3D` so Resolve knows what to preview in the browser.
369
+ - The minimum 3D pipeline is `Shape3D → Merge3D → Camera3D → Renderer3D`. Lights (`LightPoint`, `AmbientLight`) are added into the `Merge3D` as additional scene inputs.
370
+
371
+ ---
372
+
373
+ ## 7. Fusion Generator
374
+
375
+ **Role:** 3D geometry generator, not a 2D pixel source. Unlike Edit Generators (which wrap `FastNoise` or similar 2D nodes), Fusion Generators produce 3D geometry and expose their `MainInput` slots as **material** inputs, not image inputs.
376
+
377
+ **Hook:** `GroupOperator` with `MainInput1`…`N` wired to `MaterialInput` on `Shape3D` nodes (one per geometry part). The output is a rendered image from a `Renderer3D`.
378
+
379
+ ```lua
380
+ {
381
+ Tools = ordered() {
382
+ MyGenerator = GroupOperator {
383
+ Inputs = ordered() {
384
+ MainInput1 = InstanceInput {
385
+ SourceOp = "Shape3D1",
386
+ Source = "MaterialInput",
387
+ Name = "Outer Material",
388
+ },
389
+ MainInput2 = InstanceInput {
390
+ SourceOp = "Shape3D2",
391
+ Source = "MaterialInput",
392
+ Name = "Inner Material",
393
+ },
394
+ -- geometry controls (radius, height, subdivision, etc.)
395
+ OutRadius = InstanceInput {
396
+ SourceOp = "Shape3D1",
397
+ Source = "SurfaceCylinderInputs.Radius",
398
+ Name = "Outer Radius",
399
+ },
400
+ },
401
+ Outputs = {
402
+ MainOutput1 = InstanceOutput {
403
+ SourceOp = "Renderer3D1",
404
+ Source = "Output",
405
+ },
406
+ },
407
+ Tools = ordered() {
408
+ Shape3D1 = Shape3D { Inputs = { Shape = Input { Value = FuID { "SurfaceCylinderInputs" } } } },
409
+ Shape3D2 = Shape3D { -- ...
410
+ },
411
+ Merge3D1 = Merge3D { Inputs = { SceneInput1 = Input { SourceOp = "Shape3D1", Source = "Output" },
412
+ SceneInput2 = Input { SourceOp = "Shape3D2", Source = "Output" } } },
413
+ Camera3D1 = Camera3D { },
414
+ Renderer3D1 = Renderer3D { Inputs = { SceneInput = Input { SourceOp = "Merge3D1", Source = "Output" } } },
415
+ },
416
+ },
417
+ },
418
+ ActiveTool = "MyGenerator",
419
+ }
420
+ ```
421
+
422
+ **Install path:** `Templates/Fusion/Generators/`.
423
+
424
+ **Key details:**
425
+
426
+ - `MainInput1`…`MainInputN` are **material** connections (wired to `Shape3D.MaterialInput`), not image inputs. Users plug in Shader presets or `MtlBlinn` materials, not video clips.
427
+ - Each geometry part that should accept a custom material needs its own `MainInputN` slot.
428
+ - The file still renders a preview image via `Renderer3D` — `MainOutput1` sources from it.
429
+ - Geometry controls (radius, height, subdivision) are surfaced as `InstanceInput` entries targeting the specific `Shape3D` sub-parameters.
430
+
431
+ ---
432
+
433
+ ## 8. Fusion How To
434
+
435
+ **Role:** pedagogical comp — illustrates a technique, not a reusable tool. Not designed to be wired into a user's timeline.
436
+
437
+ **Hook:** no standard I/O. Typically nested `GroupOperator` wrappers that output via `Output1` (not `MainOutput1`). `ActiveTool` points to an intermediate node the author wants to highlight, not the final output.
438
+
439
+ ```lua
440
+ {
441
+ Tools = ordered() {
442
+ Group2 = GroupOperator {
443
+ -- No Inputs block (no MainInput1)
444
+ Outputs = {
445
+ Output1 = InstanceOutput {
446
+ SourceOp = "Merge3D2",
447
+ Source = "Output",
448
+ },
449
+ },
450
+ ViewInfo = GroupInfo {
451
+ Flags = {
452
+ AllowPan = false,
453
+ GridSnap = true,
454
+ AutoSnap = true,
455
+ RemoveRouters = true,
456
+ },
457
+ -- ...
458
+ },
459
+ Tools = ordered() {
460
+ -- full scene graph demonstrating the technique
461
+ -- inner groups also use Output1, not MainOutput1
462
+ },
463
+ },
464
+ },
465
+ ActiveTool = "BrightnessContrast1", -- intermediate node, pedagogically interesting
466
+ }
467
+ ```
468
+
469
+ **Install path:** `Templates/Fusion/How To/`.
470
+
471
+ **Key details:**
472
+
473
+ - No `MainInput1` / `MainOutput1`. These files are not filters or sources — they are demonstrations.
474
+ - All files in this category share a single `_default.png` / `_default@2x.png` thumbnail pair (no per-file thumbnails).
475
+ - Output ports use `Output1`, not `MainOutput1`.
476
+ - `ActiveTool` is set to the node the author wants to draw attention to (e.g., a `BrightnessContrast` or `MatteControl` mid-graph), not necessarily the terminal output.
477
+ - Multiple `ActiveTool` entries can appear if the file contains nested groups that each declare one — the outermost one wins.
478
+
479
+ ---
480
+
481
+ ## 9. Fusion Lens Flare
482
+
483
+ **Role:** HotSpot-based lens flare preset. Unlike every other category, the root node is a raw `HotSpot` — not a `GroupOperator`. A single `.setting` file ships as multiple switchable preset variants stored inside `CustomData.AltSettings9`.
484
+
485
+ **The AltSettings9 preset-bank pattern:** `CustomData.AltSettings9` is a numbered `Settings` block. Each numbered entry is a complete `HotSpot` configuration. `CurrentSettings` on the outer `HotSpot` indicates which preset is currently active (1-based). This is how the Effects Library browser shows multiple "variants" for a single flare file.
486
+
487
+ ```lua
488
+ {
489
+ Tools = ordered() {
490
+ Lens_Flare_V01 = HotSpot {
491
+ CtrlWZoom = false,
492
+ NameSet = true,
493
+ CustomData = {
494
+ AltSettings9 = {
495
+ -- preset variant 1
496
+ Tools = ordered() {
497
+ HotSpot1 = HotSpot {
498
+ Inputs = {
499
+ Input = Input {
500
+ SourceOp = "Background1", -- feed a background image here
501
+ Source = "Output",
502
+ },
503
+ PrimaryCenter = Input { Value = { 0.26, 0.62 } },
504
+ HotSpotSize = Input { Value = 0.52 },
505
+ Red = Input { SourceOp = "HotSpot1Red", Source = "Value" },
506
+ Green = Input { SourceOp = "HotSpot1Green", Source = "Value" },
507
+ Blue = Input { SourceOp = "HotSpot1Blue", Source = "Value" },
508
+ -- BezierSpline nodes drive color over angle
509
+ },
510
+ },
511
+ -- BezierSpline color-curve nodes ...
512
+ },
513
+ },
514
+ -- AltSettings9[2], [3], ... for additional presets
515
+ },
516
+ -- outer HotSpot mirrors the active preset's inputs:
517
+ Inputs = {
518
+ -- same structure as HotSpot1 above
519
+ },
520
+ },
521
+ },
522
+ ActiveTool = "Lens_Flare_V01",
523
+ }
524
+ ```
525
+
526
+ **Install path:** `Templates/Fusion/Lens Flares/`.
527
+
528
+ **Key details:**
529
+
530
+ - Root node is `HotSpot`, not `GroupOperator`. There is no `MainInput1` or `MainOutput1`.
531
+ - Background image feeds via `HotSpot.Input` (not `MainInput1`).
532
+ - Preset variants live in `CustomData.AltSettings9` as numbered entries. `CurrentSettings = N` (on the outer `HotSpot`) selects the active preset.
533
+ - BezierSpline nodes inside each preset drive color tints over the flare's angular position.
534
+ - The outer `HotSpot` (at the `Tools` level) mirrors the currently-active preset's inputs — it is what Resolve renders.
535
+
536
+ ---
537
+
538
+ ## 10. Fusion Motion Graphic
539
+
540
+ **Role:** standalone animated graphic — title card, lower third, animated element. Ships as raw top-level nodes with no `GroupOperator` wrapper. Users get the full node graph dropped into their comp.
541
+
542
+ **Hook:** no wrapper. `ActiveTool` must point to the **terminal composite node** (usually a `Merge`) — Resolve uses it to determine what the browser preview renders. Thumbnails follow a distinct convention: `wide.png` / `wide@2x.png` plus `small.active.png`.
543
+
544
+ ```lua
545
+ {
546
+ Tools = ordered() {
547
+ Background1 = Background {
548
+ Inputs = {
549
+ Width = Input { Value = 1920 },
550
+ Height = Input { Value = 1080 },
551
+ UseFrameFormatSettings = Input { Value = 1 },
552
+ },
553
+ },
554
+ TextPlus1 = TextPlus {
555
+ Inputs = {
556
+ StyledText = Input { Value = "SAMPLE" },
557
+ -- animation via BezierSpline inputs ...
558
+ },
559
+ },
560
+ RectangleMask1 = RectangleMask { Inputs = { -- ...
561
+ } },
562
+ Merge10 = Merge {
563
+ Inputs = {
564
+ Background = Input { SourceOp = "Background1", Source = "Output" },
565
+ Foreground = Input { SourceOp = "TextPlus1", Source = "Output" },
566
+ },
567
+ },
568
+ -- PublishNumber, Shake, BezierSpline nodes also appear at top level
569
+ },
570
+ ActiveTool = "Merge10",
571
+ }
572
+ ```
573
+
574
+ **Install path:** `Templates/Fusion/Motion Graphics/`.
575
+
576
+ **Key details:**
577
+
578
+ - No `GroupOperator` wrapper — the entire node graph is exposed at the top level.
579
+ - `ActiveTool` must be the terminal `Merge` (or equivalent composite). Resolve uses this to render the browser thumbnail and insert the comp correctly.
580
+ - Can use any Fusion node: `PublishNumber`, `Shake`, `BezierSpline`, `TextPlus`, `Merge`, `Background`, `RectangleMask`, etc. There is no I/O contract.
581
+ - Thumbnail files use `wide.png` / `wide@2x.png` + `small.active.png` naming — different from Backgrounds (which use `large.png`).
582
+
583
+ ---
584
+
585
+ ## 11. Fusion Particle
586
+
587
+ **Role:** particle system preset. Root node is a raw `pEmitter`. Multiple preset variants (e.g., different emitter configurations) are stored inside `CustomData.Settings` on the outer `pEmitter`, with `CurrentSettings = N` selecting the active one.
588
+
589
+ **Hook:** minimum working pipeline is `pEmitter → pRender → [Merge3D with Camera3D] → Renderer3D`. The `pRender` node is required to convert the particle stream into a renderable image; omitting it results in an invisible output.
590
+
591
+ ```lua
592
+ {
593
+ Tools = ordered() {
594
+ pEmitter1 = pEmitter {
595
+ CurrentSettings = 1,
596
+ CustomData = {
597
+ Settings = {
598
+ [1] = {
599
+ Tools = ordered() {
600
+ pEmitter1 = pEmitter {
601
+ Inputs = {
602
+ Number = Input { Value = 100 },
603
+ Style = Input { Value = FuID { "ParticleStyleBitmap" } },
604
+ -- ["ParticleStyle.Size"] = Input { Value = 0.08 },
605
+ -- region, spin, color-over-life, etc.
606
+ },
607
+ },
608
+ },
609
+ },
610
+ -- [2] = { ... } additional presets
611
+ },
612
+ },
613
+ Inputs = {
614
+ -- mirrors active preset inputs
615
+ },
616
+ },
617
+ pRender1 = pRender {
618
+ Inputs = {
619
+ SceneInput = Input { SourceOp = "pEmitter1", Source = "Output" },
620
+ Width = Input { Value = 1920 },
621
+ Height = Input { Value = 1080 },
622
+ },
623
+ },
624
+ Camera3D1 = Camera3D { },
625
+ Merge3D1 = Merge3D {
626
+ Inputs = {
627
+ SceneInput1 = Input { SourceOp = "pRender1", Source = "Output" },
628
+ SceneInput2 = Input { SourceOp = "Camera3D1", Source = "Output" },
629
+ },
630
+ },
631
+ Renderer3D1 = Renderer3D {
632
+ Inputs = { SceneInput = Input { SourceOp = "Merge3D1", Source = "Output" } },
633
+ },
634
+ },
635
+ ActiveTool = "Renderer3D1",
636
+ }
637
+ ```
638
+
639
+ **Install path:** `Templates/Fusion/Particles/`.
640
+
641
+ **Key details:**
642
+
643
+ - Root node is `pEmitter`, not `GroupOperator`.
644
+ - `pRender` is mandatory — it converts the particle stream to an image that `Renderer3D` can process. Without it the output is invisible.
645
+ - Preset variants live in `CustomData.Settings` (numbered from 1). `CurrentSettings = N` selects the active one. This is the same preset-bank pattern as Lens Flares, but uses `Settings` instead of `AltSettings9`.
646
+ - `ActiveTool` points to `Renderer3D` (the terminal rendered output), not the `pEmitter`.
647
+
648
+ ---
649
+
650
+ ## 12. Fusion Shader
651
+
652
+ **Role:** 3D material (shader) preset. Designed to be plugged into a `Shape3D.MaterialInput` or used standalone with its own preview geometry. Root node is a `GroupOperator`.
653
+
654
+ **Hook:** `GroupOperator` with no `MainInput`. Output is `Output1` (not `MainOutput1`). The output can carry either (a) a rendered image (`Source = "Output"` from a `Renderer3D` inside the group — for standalone preview) or (b) a raw material (`Source = "MaterialOutput"` from a material node — for direct plug-in to another Shape3D). The `Anisotropic.setting` uses option (a): it wraps its own `Shape3D + Renderer3D` so the shader can be previewed directly. The `GroupInfo.Flags` block (`AllowPan`, `GridSnap`, etc.) is present in Shader group operators and typically absent in Edit effects.
655
+
656
+ ```lua
657
+ {
658
+ Tools = ordered() {
659
+ MyShader = GroupOperator {
660
+ CtrlWZoom = false,
661
+ NameSet = true,
662
+ Inputs = ordered() {
663
+ Comments = Input { Value = "Apply to any shape; requires lights." },
664
+ },
665
+ Outputs = {
666
+ Output1 = InstanceOutput {
667
+ SourceOp = "Renderer3D1", -- option (a): rendered preview
668
+ Source = "Output",
669
+ -- OR, for a raw material output:
670
+ -- SourceOp = "MtlBlinn1",
671
+ -- Source = "MaterialOutput",
672
+ },
673
+ },
674
+ ViewInfo = GroupInfo {
675
+ Flags = {
676
+ AllowPan = false,
677
+ GridSnap = true,
678
+ AutoSnap = true,
679
+ RemoveRouters = true,
680
+ },
681
+ -- ...
682
+ },
683
+ Tools = ordered() {
684
+ MtlBlinn1 = MtlBlinn { Inputs = { -- material properties
685
+ } },
686
+ BumpMap1 = BumpMap { Inputs = { Input = Input { SourceOp = "...", Source = "Output" } } },
687
+ Shape3D1 = Shape3D { Inputs = { MaterialInput = Input { SourceOp = "MtlBlinn1", Source = "MaterialOutput" } } },
688
+ Merge3D1 = Merge3D { Inputs = { SceneInput1 = Input { SourceOp = "Shape3D1", Source = "Output" } } },
689
+ Renderer3D1 = Renderer3D { Inputs = { SceneInput = Input { SourceOp = "Merge3D1", Source = "Output" } } },
690
+ },
691
+ },
692
+ },
693
+ ActiveTool = "MyShader",
694
+ }
695
+ ```
696
+
697
+ **Install path:** `Templates/Fusion/Shaders/`.
698
+
699
+ **Key details:**
700
+
701
+ - Output field is `Output1`, not `MainOutput1`.
702
+ - Two output modes: rendered image (`Source = "Output"` from `Renderer3D`) or raw material (`Source = "MaterialOutput"` from a material node). Choose based on how the user will consume it: standalone preview vs. plug-in to another Shape3D.
703
+ - `GroupInfo.Flags` block with `AllowPan`, `GridSnap`, `AutoSnap`, `RemoveRouters` is characteristic of Shader groups — it controls the group's internal canvas behavior.
704
+ - No `MainInput` — shaders do not accept upstream images.
705
+
706
+ ---
707
+
708
+ ## 13. Fusion Styled Text
709
+
710
+ **Role:** per-character animated text preset. Unlike Edit Titles (which use `KeyStretcher`), Styled Text presets are raw node trees with no `GroupOperator` wrapper. They are defined by two nodes that do not appear in any other category: `StyledTextFollower` and `PublishNumber`.
711
+
712
+ **Hook:** no wrapper. `ActiveTool` points to the primary `TextPlus` node (e.g., `"Text1_2"`), not the terminal `Merge`. `PublishNumber` nodes share scalar or color values across multiple `TextPlus` nodes. `StyledTextFollower` drives per-character follower animations.
713
+
714
+ ```lua
715
+ {
716
+ Tools = ordered() {
717
+ Background6 = Background {
718
+ Inputs = {
719
+ Width = Input { Value = 1920 },
720
+ Height = Input { Value = 1080 },
721
+ UseFrameFormatSettings = Input { Value = 1 },
722
+ },
723
+ },
724
+ Publish1_2 = PublishNumber { Inputs = { Value = Input { Value = 0.8 } } },
725
+ Publish2_2 = PublishNumber { Inputs = { Value = Input { Value = 0.2 } } },
726
+ Follower1_2 = StyledTextFollower {
727
+ Inputs = {
728
+ -- per-character timing / offset controls
729
+ },
730
+ },
731
+ Text1_2 = TextPlus {
732
+ Inputs = {
733
+ StyledText = Input { Value = "SAMPLE" },
734
+ Red1 = Input { SourceOp = "Publish1_2", Source = "Value" },
735
+ Green1 = Input { SourceOp = "Publish2_2", Source = "Value" },
736
+ -- animation splines, TV / SoftGlow effects piped in ...
737
+ },
738
+ },
739
+ TV1_2 = TV { Inputs = { Input = Input { SourceOp = "Text1_2", Source = "Output" } } },
740
+ SoftGlow1 = SoftGlow { Inputs = { Input = Input { SourceOp = "TV1_2", Source = "Output" } } },
741
+ Merge12 = Merge {
742
+ Inputs = {
743
+ Background = Input { SourceOp = "Background6", Source = "Output" },
744
+ Foreground = Input { SourceOp = "SoftGlow1", Source = "Output" },
745
+ },
746
+ },
747
+ },
748
+ ActiveTool = "Text1_2",
749
+ }
750
+ ```
751
+
752
+ **Install path:** `Templates/Fusion/Styled Text/`.
753
+
754
+ **Key details:**
755
+
756
+ - No `GroupOperator` — the full node tree is raw at the top level.
757
+ - `StyledTextFollower` and `PublishNumber` are the defining nodes. An author who omits them produces something that looks nothing like the stock library.
758
+ - `ActiveTool` points to the primary `TextPlus` (e.g., `"Text1_2"`), not the terminal `Merge` — this is the opposite convention from Motion Graphics.
759
+ - `PublishNumber` nodes share values (color channels, scalar parameters) across multiple `TextPlus` nodes so a single "Color" slider controls all text elements at once.
760
+ - `BezierSpline` inputs on `TextPlus` (alpha, size, tracking) provide the per-character animation timing.
761
+
762
+ ---
763
+
764
+ ## Role → Shape Cheat Sheet
765
+
766
+ | Role | Wrap in GroupOperator? | MainInput1 | MainInput2 | MainOutput1 source | Output field |
767
+ |------|-----------------------|------------|------------|---------------------|-------------|
768
+ | Edit Effect | Yes | Required → `Input` of first node | — | Terminal node | `Output` |
769
+ | Edit Transition | Yes | Required → `Background` | Required → `Foreground` | Dissolve/Merge | `Output` |
770
+ | Edit Title | Yes | — | — | KeyStretcher | `Result` |
771
+ | Edit Generator | Yes | — | — | Terminal node | `Output` |
772
+ | Fusion Tool | Yes | Required → `Input` of first node | Optional | Terminal node | `Output` |
773
+ | Fusion Background | Yes (wrapped) or No (unwrapped) | — | — | `Renderer3D` | `MainOutput1` or `Output1`; unwrapped: `ActiveTool` on `Renderer3D` |
774
+ | Fusion Generator | Yes | Material → `Shape3D.MaterialInput` | Material → `Shape3D.MaterialInput` | `Renderer3D` | `MainOutput1` |
775
+ | Fusion How To | Yes (nested groups) | — | — | Intermediate node of interest | `Output1` |
776
+ | Fusion Lens Flare | No — raw `HotSpot` at top | — | — | — | `HotSpot.Input` for background; no `MainOutput1` |
777
+ | Fusion Motion Graphic | No — raw top-level nodes | — | — | Terminal `Merge` | `ActiveTool` on `Merge` |
778
+ | Fusion Particle | No — raw `pEmitter` at top | — | — | `Renderer3D` | `ActiveTool` on `Renderer3D` |
779
+ | Fusion Shader | Yes | — | — | `Renderer3D` (preview) or material node | `Output1` |
780
+ | Fusion Styled Text | No — raw top-level nodes | — | — | Primary `TextPlus` | `ActiveTool` on `TextPlus` |