Snap Studio Pro โ Capture API (headless / scripting)¶
Drive Snap Studio Pro captures from code โ your own editor scripts, or a headless -batchmode run for CI โ without opening the tool window.
It runs the exact production capture stack (RenderingManagerFactory โ PrefabPreviewManager โ CaptureEngine), the same one the golden-image regression harness uses, so output matches the interactive tool.
Covers static, animation, particle, and VFX capture (front angle). Directional (all-angle) export lands once its camera-rotation path is headless-safe โ see Roadmap.
Owns¶
- Public API surface (
SnapCapture,SnapBatchRunner,SnapJobResult,SnapRunReport) - JSON job schema (
SnapJobFileand friends), parsed with Unity'sJsonUtility - The headless stack builder (
SnapPreviewStack)
Does NOT own¶
- Capture / preview / pipeline implementation (it drives them as a black box)
- The interactive tool window
From your own editor script¶
using RevGaming.SnapStudioPro.Api;
var result = SnapCapture.RunStatic(new SnapStaticJob
{
prefabs = new[] { "Assets/Props/Barrel.prefab" },
angles = new[] { new SnapAngle { yaw = 45, pitch = 15 } },
});
Debug.Log(result.Success
? $"Wrote {result.ImageCount} images to {result.OutputDirectory}"
: $"Failed: {result.Error}");
Animation โ a prefab's clip(s) to frames (+ optional sprite sheet / GIF):
var anim = SnapCapture.RunAnimation(new SnapAnimationJob
{
prefab = "Assets/Characters/Hero.prefab",
// clips = new[] { "Run", "Idle" }, // omit for every clip
fps = 24,
spriteSheet = true,
gif = true,
});
Particle / VFX โ bake a simulated effect to frames (+ optional sprite sheet):
SnapCapture.RunParticle(new SnapParticleJob {
prefab = "Assets/FX/Explosion.prefab", duration = 2, fps = 30, spriteSheet = true });
SnapCapture.RunVfx(new SnapVfxJob { // needs SNAP_SUPPORTS_VFX
prefab = "Assets/FX/Portal.prefab", duration = 3, fps = 30 });
Or run a whole job file (any mix of job types):
SnapRunReport report = SnapBatchRunner.RunJobFile("Assets/snap-jobs/assets.json");
Headless / CI¶
Unity -batchmode -quit -projectPath <project> \
-executeMethod RevGaming.SnapStudioPro.Api.SnapBatchRunner.RunFromCommandLine \
-snapJob Assets/snap-jobs/icons.json
Exit codes: 0 all jobs ok ยท 1 a job failed ยท 2 bad/missing -snapJob argument. Output goes to the Unity log (this surface uses Debug, not the quiet-by-default MyLogger, because batchmode has no diagnostics window and the log is the result).
Job file schema (JSON)¶
JsonUtility is used, so field names are case-sensitive and must match exactly. Missing fields fall back to defaults (strings via empty = inherit, ints via 0 = inherit). See sample-job.json.
{
"defaults": {
"outputDirectory": "Assets/SnapExports", // where files go
"format": "PNG", // PNG | JPG | TGA | EXR
"width": 512, "height": 512,
"transparent": true, // false -> solid backgroundColor
"backgroundColor": "#1E4C52"
},
"staticJobs": [
{
"prefabs": ["Assets/A.prefab", "Assets/B.prefab"], // and/or:
"prefabFolder": "Assets/Props", // scanned recursively for prefabs
"angles": [ { "yaw": 0, "pitch": 0, "roll": 0 } ], // empty -> one (0,0,0) view
"outputDirectory": "", "format": "", "width": 0, "height": 0, // "" / 0 = inherit
"separateByPrefab": false, // subfolder per prefab
"separateByAngle": false, // subfolder per angle
"fitCameraToPrefab": true,
"exportNormalMap": false, "flatAlbedo": false
}
],
"animationJobs": [
{
"prefab": "Assets/Characters/Hero.prefab",
"clips": ["Run", "Idle"], // empty -> every clip on the prefab
"fps": 24, // 0 -> 12
"frameInterval": 1, // capture every Nth frame (0 -> every frame)
"startTime": 0, "endTime": 0, // seconds; endTime 0 -> clip length
"reverse": false, "applyRootMotion": false,
"outputDirectory": "", "format": "", "width": 0, "height": 0,
"spriteSheet": true, "gif": false,
"autoLayout": true, "framesPerRow": 8, "sheetPrefix": "",
"exportNormalMap": false, "flatAlbedo": false
}
],
"particleJobs": [
{
"prefab": "Assets/FX/Explosion.prefab",
"duration": 2, "fps": 30, // duration 0 -> 2, fps 0 -> 30
"warmup": 0, // seconds to pre-simulate steady-state emitters
"trimStartFrame": 0, "trimEndFrame": -1, // -1 = through the end
"alphaMode": "None", // None | Auto | Straight | Additive
"forceShaderSwap": false, "shaderMode": "AlphaTint", // only if forceShaderSwap
"outputDirectory": "", "width": 0, "height": 0,
"spriteSheet": true, "autoLayout": true, "framesPerRow": 8, "sheetPrefix": ""
}
],
"vfxJobs": [
{
"prefab": "Assets/FX/Portal.prefab", // requires SNAP_SUPPORTS_VFX
"duration": 3, "fps": 30,
"alphaMode": "Straight", // Straight | None | Additive
"spriteSheet": true
}
]
}
How it runs headless¶
The interactive tool drives capture through an editor coroutine pumped by EditorApplication.update (async โ it would never finish under -batchmode -quit). This API instead drains the same IEnumerable capture routine synchronously in a plain loop: the routine's yields exist for editor responsiveness, not correctness (render-to-texture, particle/VFX simulation, and animation sampling are all synchronous), so every step runs in-line and the process completes deterministically. Async shader compilation is disabled for the run so first-frame captures never hit placeholder shaders.
Roadmap¶
- Done: static (
RunStatic/staticJobs[]), animation (RunAnimation/animationJobs[]โ frames + sheet + GIF), particle (RunParticle/particleJobs[]), and VFX (RunVfx/vfxJobs[], underSNAP_SUPPORTS_VFX) โ all front-angle. - Next: directional / all-angle export (any path). It's UI-bound today (
IDirectionalPreviewUpdaterโSceneView.RepaintAll()+ live camera), so it needs a headless-safe camera-rotation seam before it can run under-batchmode.