Skip to content

StandardRendererDocsHarness.kt

src/jvmMain/kotlin/afield/graphics/scene/fabricated/StandardRendererDocsHarness.kt · 1421 lines

package afield.graphics.scene.fabricated

import afield.document.graph.Box
import afield.app.server.documents
import afield.graphics.render.TexturePreviewCache
import afield.graphics.vulkan.Renderer
import afield.graphics.scene.GltfSupport
import afield.document.context.fromContext
import afield.graphics.texture.LoadedTexture
import afield.graphics.vulkan.util.Window
import afield.app.diagnostics.logging.lprintln
import afield.math.linalg.Vec3
import afield.app.server.thisSessionInfo
import kotlinx.coroutines.delay
import kotlinx.coroutines.yield
import afield.math.linalg.Matrix4f
import afield.math.linalg.Vector3f
import afield.math.linalg.Vector4f
import org.lwjgl.vulkan.VK10.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
import java.awt.image.BufferedImage
import java.io.File
import java.nio.file.Files
import java.util.ArrayDeque
import java.util.Base64
import javax.imageio.ImageIO
import kotlin.math.PI
import kotlin.math.max
import kotlin.math.min

The offscreen renders behind Field's graphics documentation: deterministic StandardRenderer scenes captured to PNG, plus the HTML wrapper that puts one in a box output.

A page gets its figure by calling one of the render functions from a box, passing the returned file to htmlForImage, and letting the docs build screenshot the result through the ordinary backdoor path. The scenes take the renderer's features one at a time: metalness, shadow and ambient-occlusion comparisons, a camera and depth study, glTF loading, blended transparency, and the post-stack vignette and tone-map passes. Each takes its renderer from the box's context by default, so a box connected beneath a renderer needs to pass only an output path.

renderStackSLGallery and renderRectSLGallery, with their animation counterparts, are wrappers rather than scenes of their own: the rendering belongs to StackSLGalleryHarness and RectSLGalleryHarness, and the wrapper copies the hero image that harness produced to the path the docs fence checks.

object StandardRendererDocsHarness

Renders the graphics documentation's StandardRenderer figures offscreen, one scene per call.

object StandardRendererDocsHarness {

    private const val WAIT_FRAMES = 90
    private const val CAPTURE_MAX_DIM = 2048
    private val DATA_IMAGE_RE = Regex("base64,([^']+)")

    private fun canonicalDocsAssetsDir(language: String): File =
        File(System.getProperty("user.dir"), "public/docs/assets/$language")

    private fun generatedDocsAssetsDir(language: String): File {
        val overrideRoot = System.getenv("AFIELD_GENERATED_DOC_ASSET_ROOT")
            ?.trim()
            ?.takeIf { it.isNotEmpty() }
        return if (overrideRoot == null) canonicalDocsAssetsDir(language) else File(overrideRoot, language)
    }

suspend fun renderDefaultScene

    suspend fun renderDefaultScene(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File = renderScene(outputPath, renderer, window) { bundle, activeRenderer ->
        bundle.scene.camera.at.set(0f, 0.25f, -3.35f)
        bundle.scene.camera.target.set(0f, -0.05f, 0f)

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "left",
            model = Matrix4f().translate(-0.95f, 0f, 0f),
            material = material(color = Vector4f(0.17f, 0.98f, 0.24f, 1f)),
            slices = 48,
            stacks = 48
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "middle",
            model = Matrix4f(),
            material = material(color = Vector4f(0.98f, 0.92f, 0.22f, 1f)),
            slices = 48,
            stacks = 48
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "right",
            model = Matrix4f().translate(0.95f, 0f, 0f),
            material = material(color = Vector4f(0.98f, 0.30f, 0.32f, 1f)),
            slices = 48,
            stacks = 48
        )

        bundle.present(renderer = activeRenderer)
    }

suspend fun renderStackSLGallery

Renders the whole StackSL gallery (see StackSLGalleryHarness) inside a single graphics context, writing every stacksl-*.png into public/docs/assets/stacksl/, then copies the hero image to outputPath so the docs fence can embed one representative capture. This is the "one Field document, many renders" path: the entire gallery is produced without restarting the JVM per screenshot.

    suspend fun renderStackSLGallery(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument()
    ): File {
        val assetsDir = generatedDocsAssetsDir("stacksl")
        lprintln("info,doc", "stacksl gallery output dir: ${assetsDir.absolutePath}")
        val hero = StackSLGalleryHarness.renderGallery(assetsDir, renderer)
        val target = File(outputPath)
        target.parentFile?.mkdirs()
        target.delete()
        target.writeBytes(hero.readBytes())
        require(target.length() > 0L) { "stacksl gallery hero copy wrote an empty PNG" }
        return target
    }

suspend fun renderStackSLAnimations

Renders an mp4 per animated StackSL example (see StackSLGalleryHarness.renderAnimations) into public/docs/assets/stacksl/, then writes a still hero to outputPath for the docs fence to validate. Same single-graphics-context path as renderStackSLGallery.

    suspend fun renderStackSLAnimations(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument()
    ): File {
        val assetsDir = generatedDocsAssetsDir("stacksl")
        val mp4s = StackSLGalleryHarness.renderAnimations(assetsDir, renderer)
        lprintln("info,doc", "stacksl animations wrote ${mp4s.size} mp4(s) to ${assetsDir.absolutePath}")
        val target = File(outputPath)
        target.parentFile?.mkdirs()
        target.delete()
        // The fence only needs a valid still for visual validation; reuse a gallery frame.
        val hero = File(assetsDir, "stacksl-${StackSLGalleryHarness.loadExamples().first().name}.png")
            .takeIf { it.exists() }
            ?: assetsDir.listFiles { f -> f.name.startsWith("stacksl-") && f.name.endsWith(".png") }?.firstOrNull()
            ?: canonicalDocsAssetsDir("stacksl")
                .listFiles { f -> f.name.startsWith("stacksl-") && f.name.endsWith(".png") }
                ?.firstOrNull()
            ?: error("stacksl animations: no gallery still available for the fence hero")
        target.writeBytes(hero.readBytes())
        require(target.length() > 0L) { "stacksl animations hero copy wrote an empty PNG" }
        return target
    }

suspend fun renderRectSLGallery

Renders the whole RectSL gallery (see RectSLGalleryHarness) inside a single graphics context, writing a representative still rectsl-*.png per program into public/docs/assets/rectsl/, then copies the hero image to outputPath. The RectSL twin of renderStackSLGallery.

    suspend fun renderRectSLGallery(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument()
    ): File {
        val assetsDir = generatedDocsAssetsDir("rectsl")
        lprintln("info,doc", "rectsl gallery output dir: ${assetsDir.absolutePath}")
        val hero = RectSLGalleryHarness.renderGallery(assetsDir, renderer)
        val target = File(outputPath)
        target.parentFile?.mkdirs()
        target.delete()
        target.writeBytes(hero.readBytes())
        require(target.length() > 0L) { "rectsl gallery hero copy wrote an empty PNG" }
        return target
    }

suspend fun renderRectSLAnimations

Renders an mp4 per animated RectSL example (see RectSLGalleryHarness.renderAnimations) into public/docs/assets/rectsl/, then writes a still hero to outputPath for the docs fence to validate. Same single-graphics-context path as renderRectSLGallery.

    suspend fun renderRectSLAnimations(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument()
    ): File {
        val assetsDir = generatedDocsAssetsDir("rectsl")
        val mp4s = RectSLGalleryHarness.renderAnimations(assetsDir, renderer)
        lprintln("info,doc", "rectsl animations wrote ${mp4s.size} mp4(s) to ${assetsDir.absolutePath}")
        val target = File(outputPath)
        target.parentFile?.mkdirs()
        target.delete()
        // The fence only needs a valid still for visual validation; reuse a gallery frame.
        val hero = File(assetsDir, "rectsl-${RectSLGalleryHarness.loadExamples().first().name}.png")
            .takeIf { it.exists() }
            ?: assetsDir.listFiles { f -> f.name.startsWith("rectsl-") && f.name.endsWith(".png") }?.firstOrNull()
            ?: canonicalDocsAssetsDir("rectsl")
                .listFiles { f -> f.name.startsWith("rectsl-") && f.name.endsWith(".png") }
                ?.firstOrNull()
            ?: error("rectsl animations: no gallery still available for the fence hero")
        target.writeBytes(hero.readBytes())
        require(target.length() > 0L) { "rectsl animations hero copy wrote an empty PNG" }
        return target
    }

suspend fun renderRectSLFit

Renders the "evolving a distribution" (#5) still — the host fits a curve to a target and RectSL lays out the array from it (see RectSLGalleryHarness.renderFitToTarget) — to rectsl-fit-to-target.png, then copies it to outputPath for the docs fence to validate.

    suspend fun renderRectSLFit(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument()
    ): File {
        val assetsDir = generatedDocsAssetsDir("rectsl")
        val hero = RectSLGalleryHarness.renderFitToTarget(assetsDir, renderer)
        val target = File(outputPath)
        target.parentFile?.mkdirs()
        target.delete()
        target.writeBytes(hero.readBytes())
        require(target.length() > 0L) { "rectsl fit hero copy wrote an empty PNG" }
        return target
    }

suspend fun renderRectSLSdf

Renders a RectSL scene as a signed distance field on the GPU (see RectSLGalleryHarness.renderSdf): one still per look preset, a motion-blur clip, the raster-vs-SDF counterfactual, and the GPU↔CPU differential — which throws before any image is written if the compiled shader and the CPU sampler disagree. Everything lands in public/docs/assets/rectsl/; the hero is copied to outputPath for the fence to validate.

    suspend fun renderRectSLSdf(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument()
    ): File {
        val assetsDir = generatedDocsAssetsDir("rectsl")
        val hero = RectSLGalleryHarness.renderSdf(assetsDir, renderer)
        val target = File(outputPath)
        target.parentFile?.mkdirs()
        target.delete()
        target.writeBytes(hero.readBytes())
        require(target.length() > 0L) { "rectsl sdf hero copy wrote an empty PNG" }
        return target
    }

suspend fun renderMetalnessComparison

    suspend fun renderMetalnessComparison(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File = renderScene(outputPath, renderer, window) { bundle, activeRenderer ->
        bundle.scene.camera.at.set(0f, 0.2f, -3.25f)
        bundle.scene.camera.target.set(0f, 0f, 0f)

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "dielectric",
            model = Matrix4f().translate(-1.05f, 0f, 0f),
            material = material(
                color = Vector4f(0.78f, 0.80f, 0.84f, 1f),
                metallic = 0.0f,
                roughness = 0.16f
            ),
            slices = 56,
            stacks = 56
        )

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "metal",
            model = Matrix4f().translate(1.05f, 0f, 0f),
            material = material(
                color = Vector4f(0.78f, 0.80f, 0.84f, 1f),
                metallic = 1.0f,
                roughness = 0.16f
            ),
            slices = 56,
            stacks = 56
        )

        bundle.present(renderer = activeRenderer)
    }

suspend fun renderShadowComparison

    suspend fun renderShadowComparison(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File {
        val bundle = StandardRenderer.build(renderer)
        configureGroundedTorusScene(bundle, renderer)
        bundle.scene.aoStrength = 0f
        bundle.scene.ambientColor.set(0.015f, 0.015f, 0.018f)
        bundle.scene.lightIntensity = 1.35f
        bundle.present(renderer = renderer)
        bundle.applyScenePlan()
        bundle.attachTo(renderer)

        val shadowOff = captureScene(tempPng("fabricate-shadow-off"), bundle, renderer) {
            bundle.scene.shadowStrength = 0f
        }
        val shadowOn = captureScene(tempPng("fabricate-shadow-on"), bundle, renderer) {
            bundle.scene.shadowStrength = 1f
        }
        return try {
            composeHorizontal(shadowOff, shadowOn, File(outputPath))
        } finally {
            shadowOff.delete()
            shadowOn.delete()
        }
    }

suspend fun renderAoComparison

    suspend fun renderAoComparison(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File {
        val bundle = StandardRenderer.build(renderer)
        configureAoContactScene(bundle, renderer)
        bundle.present(renderer = renderer)
        bundle.applyScenePlan()
        bundle.attachTo(renderer)
        val originalAoStrength = bundle.scene.aoStrength

        val aoOff = captureScene(tempPng("fabricate-ao-off"), bundle, renderer) {
            bundle.scene.aoStrength = 0f
        }
        val aoOn = captureScene(tempPng("fabricate-ao-on"), bundle, renderer) {
            bundle.scene.aoStrength = originalAoStrength
        }
        return try {
            composeHorizontal(aoOff, aoOn, File(outputPath))
        } finally {
            aoOff.delete()
            aoOn.delete()
        }
    }

suspend fun renderShinyToriCheckerboard

body — 52 lines, from line 340
    suspend fun renderShinyToriCheckerboard(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File = renderScene(outputPath, renderer, window) { bundle, activeRenderer ->
        bundle.scene.camera.at.set(0f, 0.78f, -3.75f)
        bundle.scene.camera.target.set(0f, -0.25f, 0f)
        bundle.scene.lightDirection.set(Vector3f(-0.55f, -1.0f, -0.20f).normalize())
        bundle.scene.lightIntensity = 1.55f
        bundle.scene.shadowStrength = 1f
        bundle.scene.aoStrength = 1f
        bundle.scene.shadowExtent = 8.5f
        bundle.scene.shadowBias = 0.0022f

        addCheckerboardFloor(
            bundle = bundle,
            renderer = activeRenderer,
            tilesPerSide = 10,
            tileSize = 0.52f,
            y = -1.0f
        )

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "torus-a",
            model = Matrix4f().translate(-1.25f, -0.25f, 0.6f).rotateY(0.35f),
            material = material(Vector4f(0.92f, 0.35f, 0.33f, 1f), metallic = 1.0f, roughness = 0.08f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "torus-b",
            model = Matrix4f().translate(0f, -0.16f, 0f).rotateX(0.24f).rotateY(-0.25f),
            material = material(Vector4f(0.28f, 0.86f, 0.96f, 1f), metallic = 1.0f, roughness = 0.07f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "torus-c",
            model = Matrix4f().translate(1.25f, -0.25f, -0.65f).rotateY(-0.45f),
            material = material(Vector4f(0.95f, 0.78f, 0.29f, 1f), metallic = 1.0f, roughness = 0.09f),
            slices = 72,
            stacks = 72
        )

        bundle.present(renderer = activeRenderer)
    }

suspend fun renderShaderGeneratedCheckerboard

body — 55 lines, from line 393
    suspend fun renderShaderGeneratedCheckerboard(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File = renderScene(outputPath, renderer, window) { bundle, activeRenderer ->
        bundle.scene.camera.at.set(0f, 0.78f, -3.65f)
        bundle.scene.camera.target.set(0f, -0.2f, 0f)
        bundle.scene.lightDirection.set(Vector3f(-0.52f, -1.0f, -0.22f).normalize())
        bundle.scene.lightIntensity = 1.5f
        bundle.scene.shadowStrength = 1f
        bundle.scene.aoStrength = 1f
        bundle.scene.shadowExtent = 8.0f
        bundle.scene.shadowBias = 0.0022f

        val floor = registerCubeNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "shader-floor",
            model = Matrix4f()
                .translate(0f, -1.0f, 0f)
                .scale(3.2f, 0.02f, 3.2f),
            material = material(
                color = Vector4f(0.58f, 0.58f, 0.62f, 1f),
                metallic = 0.03f,
                roughness = 0.9f
            )
        )
        checker(
            floor,
            scale = 3.0f,
            color = Vector4f(0.20f, 0.20f, 0.22f, 1f),
            mix = 1f
        )

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "shader-torus-left",
            model = Matrix4f().translate(-0.95f, -0.22f, 0.48f).rotateY(0.32f),
            material = material(Vector4f(0.90f, 0.39f, 0.33f, 1f), metallic = 1f, roughness = 0.1f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "shader-torus-right",
            model = Matrix4f().translate(0.95f, -0.22f, -0.52f).rotateY(-0.35f),
            material = material(Vector4f(0.30f, 0.84f, 0.95f, 1f), metallic = 1f, roughness = 0.08f),
            slices = 72,
            stacks = 72
        )

        bundle.present(renderer = activeRenderer)
    }

suspend fun renderCheckerboardCameraDepthStudy

body — 62 lines, from line 449
    suspend fun renderCheckerboardCameraDepthStudy(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File {
        val bundle = StandardRenderer.build(renderer)
        bundle.scene.lightDirection.set(Vector3f(-0.48f, -1.0f, -0.26f).normalize())
        bundle.scene.lightIntensity = 1.45f
        bundle.scene.shadowStrength = 1f
        bundle.scene.aoStrength = 1f
        bundle.scene.shadowExtent = 9.0f
        bundle.scene.shadowBias = 0.0024f
        addCheckerboardFloor(bundle, renderer, tilesPerSide = 12, tileSize = 0.48f, y = -1.05f)

        registerTorusNode(
            bundle = bundle,
            renderer = renderer,
            name = "near-torus",
            model = Matrix4f().translate(-1.0f, -0.15f, 1.0f).rotateY(0.35f),
            material = material(Vector4f(0.89f, 0.44f, 0.30f, 1f), metallic = 1f, roughness = 0.12f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = renderer,
            name = "mid-torus",
            model = Matrix4f().translate(0f, -0.12f, -0.1f).rotateY(-0.20f),
            material = material(Vector4f(0.34f, 0.82f, 0.95f, 1f), metallic = 1f, roughness = 0.10f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = renderer,
            name = "far-torus",
            model = Matrix4f().translate(1.0f, -0.08f, -1.35f).rotateY(0.45f),
            material = material(Vector4f(0.96f, 0.85f, 0.34f, 1f), metallic = 1f, roughness = 0.10f),
            slices = 72,
            stacks = 72
        )

        bundle.present(renderer = renderer)
        bundle.applyScenePlan()
        bundle.attachTo(renderer)

        val nearCamera = captureScene(tempPng("fabricate-depth-camera-near"), bundle, renderer) {
            bundle.scene.camera.at.set(-1.55f, 0.72f, -1.75f)
            bundle.scene.camera.target.set(-0.95f, -0.2f, 0.95f)
        }
        val farCamera = captureScene(tempPng("fabricate-depth-camera-far"), bundle, renderer) {
            bundle.scene.camera.at.set(1.45f, 0.78f, -3.35f)
            bundle.scene.camera.target.set(0.95f, -0.15f, -1.35f)
        }

        return try {
            composeHorizontal(nearCamera, farCamera, File(outputPath))
        } finally {
            nearCamera.delete()
            farCamera.delete()
        }
    }

suspend fun renderBlendedTransparency

body — 51 lines, from line 512
    suspend fun renderBlendedTransparency(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File = renderScene(outputPath, renderer, window) { bundle, activeRenderer ->
        bundle.scene.camera.at.set(0f, 0.72f, -3.25f)
        bundle.scene.camera.target.set(0f, -0.18f, 0f)
        bundle.scene.lightDirection.set(Vector3f(-0.45f, -1.0f, -0.22f).normalize())
        bundle.scene.lightIntensity = 1.45f
        bundle.scene.shadowStrength = 1f
        bundle.scene.aoStrength = 1f
        bundle.scene.shadowExtent = 7.0f

        addCheckerboardFloor(bundle, activeRenderer, tilesPerSide = 8, tileSize = 0.55f, y = -1.02f)

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "behind-glass",
            model = Matrix4f().translate(-0.75f, -0.18f, 0.35f).rotateY(0.30f),
            material = material(Vector4f(0.95f, 0.40f, 0.28f, 1f), metallic = 1f, roughness = 0.10f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "front-metal",
            model = Matrix4f().translate(0.78f, -0.12f, -0.40f).rotateY(-0.35f),
            material = material(Vector4f(0.28f, 0.85f, 0.95f, 1f), metallic = 1f, roughness = 0.08f),
            slices = 72,
            stacks = 72
        )

        transparentSphere(
            bundle = bundle,
            renderer = activeRenderer,
            name = "far-glass",
            model = Matrix4f().translate(-0.52f, -0.32f, 0.85f).scale(0.72f),
            color = Vector4f(0.45f, 0.70f, 1.0f, 0.30f)
        )
        transparentSphere(
            bundle = bundle,
            renderer = activeRenderer,
            name = "near-glass",
            model = Matrix4f().translate(0.42f, -0.28f, -0.38f).scale(0.66f),
            color = Vector4f(1.0f, 0.78f, 0.38f, 0.38f)
        )
        bundle.sortTransparentMeshes()
        bundle.present(renderer = activeRenderer)
    }

suspend fun renderPostStackVignette

    suspend fun renderPostStackVignette(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window
    ): File = renderScene(outputPath, renderer, window) { bundle, activeRenderer ->
        bundle.scene.camera.at.set(0.15f, 0.62f, -3.05f)
        bundle.scene.camera.target.set(0f, -0.18f, 0.05f)
        bundle.scene.lightDirection.set(Vector3f(-0.55f, -1.0f, -0.20f).normalize())
        bundle.scene.lightIntensity = 1.55f
        bundle.scene.shadowStrength = 1f
        bundle.scene.aoStrength = 1f
        addCheckerboardFloor(bundle, activeRenderer, tilesPerSide = 9, tileSize = 0.52f, y = -1.0f)

        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "post-left",
            model = Matrix4f().translate(-0.72f, -0.20f, 0.18f).rotateY(0.32f),
            material = material(Vector4f(0.96f, 0.28f, 0.68f, 1f), metallic = 1f, roughness = 0.09f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = activeRenderer,
            name = "post-right",
            model = Matrix4f().translate(0.72f, -0.16f, -0.32f).rotateY(-0.35f),
            material = material(Vector4f(0.18f, 0.92f, 0.70f, 1f), metallic = 1f, roughness = 0.08f),
            slices = 72,
            stacks = 72
        )

        bundle.addShaderEffect("vignette-warmth", vignetteWarmthFragment, renderer = activeRenderer)
        bundle.present(renderer = activeRenderer)
    }

suspend fun renderToyCarGltf

body — 99 lines, from line 600
    suspend fun renderToyCarGltf(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window,
        gltfPath: String = File("public/docs/assets/fabricate_demo/ToyCar.glb").absolutePath,
        environmentPath: String = File("public/docs/assets/fabricate_demo/venice_sunset_1k.exr").absolutePath
    ): File {
        val environment = if (environmentPath.endsWith(".exr", ignoreCase = true)) {
            LoadedTexture.fromExrFile(
                environmentPath,
                renderer,
                requestedMipLevels = 0,
                prefilterEnvironment = true
            )
        } else {
            LoadedTexture.fromFile(
                environmentPath,
                renderer,
                requestedMipLevels = 0,
                generateMipmaps = true
            )
        }.also { texture ->
            texture.setDefaultSampler()
            texture.transferNow()
        }
        return renderScene(
            outputPath,
            renderer,
            window,
            StandardRenderer.Config(
                environmentTexture = environment,
                environmentStrength = 1.35f,
                environmentRotation = 0.42f,
                backgroundColor = srgbGray(0.095f)
            )
        ) { bundle, activeRenderer ->
            bundle.scene.projection.fov = 50.0 * PI / 180.0
            // Keep the same side-on direction as the comparison view, but leave enough distance
            // for the normalized 4.45-unit model. The old eye point sat inside ToyCar after the
            // Field-matrix TRS decomposition was corrected.
            // Camera lowered to match the Don McCurdy / three.js reference framing:
            // a flatter, slightly-below-roofline vantage rather than looking down on the car.
            bundle.scene.camera.at.set(1.8375f, 0.82f, -2.4375f)
            bundle.scene.camera.target.set(0f, 0.26f, 0f)
            bundle.scene.lightDirection.set(Vector3f(-0.36f, -0.58f, 0.72f).normalize())
            bundle.scene.lightIntensity = 2.45f
            bundle.scene.ambientColor.set(0.055f, 0.055f, 0.06f)
            bundle.scene.shadowStrength = 0.34f
            bundle.scene.aoStrength = 0.18f
            bundle.scene.shadowExtent = 6.2f
            bundle.scene.shadowBias = 0.0028f

            val model = GltfSupport.loadGltfBasic(gltfPath, GltfSupport.GltfLoadConfig(loadTextures = true))
            val loaded = bundle.registerGltf(model, namePrefix = "toy-car", renderer = activeRenderer)
            val bounds = gltfWorldBounds(model)
            val scale = 4.45f / bounds.longestAxis.coerceAtLeast(0.0001f)
            val root = bundle.sceneGraph.node("toy-car-root") {
                transform.set(
                    Matrix4f()
                        .translate(0f, -0.18f, 0f)
                        .rotateY(PI.toFloat())
                        .scale(scale)
                        .translate(-bounds.center.x, -bounds.center.y, -bounds.center.z)
                )
            }
            model.meshes.zip(loaded.nodes).forEachIndexed { index, (instance, node) ->
                root.add(node)
                // Freeze the docs frame to the imported bind pose; the parent node owns screenshot fit.
                node.animationSamples = null
                node.renderable?.boundsRadius = 2.2f
                node.renderable?.material?.let { importedMaterial ->
                    when (instance.name) {
                                "ToyCar" -> {
                                    importedMaterial.metallic = 1.0f
                                    importedMaterial.roughness = 1.0f
                                    importedMaterial.emissiveFactor.set(0f, 0f, 0f)
                                }
                        "Fabric" -> {
                            importedMaterial.baseColor.set(0.42f, 0.42f, 0.42f, 1f)
                            importedMaterial.metallic = 0f
                            importedMaterial.roughness = 1.0f
                            importedMaterial.receiveAo = false
                        }
                        "Glass" -> {
                            importedMaterial.baseColor.set(0.58f, 0.91f, 0.58f, 0.34f)
                            importedMaterial.metallic = 0f
                            importedMaterial.roughness = 0.0f
                            importedMaterial.alphaMode = FabricateAlphaMode.BLEND
                            importedMaterial.receiveShadow = false
                            importedMaterial.receiveAo = false
                        }
                    }
                    loaded.meshBindings.getOrNull(index)?.applyMaterial(importedMaterial)
                }
            }
            bundle.sceneGraph.updateWorldTransforms()
            bundle.present(renderer = activeRenderer)
        }
    }

suspend fun renderMultiLightSkybox

body — 83 lines, from line 700
    suspend fun renderMultiLightSkybox(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window,
        environmentPath: String = File("public/docs/assets/fabricate_demo/venice_sunset_1k.exr").absolutePath
    ): File {
        val environment = LoadedTexture.fromExrFile(
            environmentPath,
            renderer,
            requestedMipLevels = 0,
            prefilterEnvironment = true
        ).also { texture ->
            texture.setDefaultSampler()
            texture.transferNow()
        }
        return renderScene(
            outputPath,
            renderer,
            window,
            StandardRenderer.Config(
                environmentTexture = environment,
                environmentStrength = 0.85f,
                environmentRotation = 0.35f,
                backgroundColor = srgbGray(0.06f)
            )
        ) { bundle, activeRenderer ->
            bundle.scene.camera.at.set(0f, 1.05f, -4.1f)
            bundle.scene.camera.target.set(0f, -0.15f, 0f)
            bundle.scene.aoStrength = 0.6f
            bundle.scene.shadowStrength = 0.7f
            bundle.scene.shadowExtent = 6.5f
            bundle.scene.shadowBias = 0.0024f

            // Show the loaded environment as the scene backdrop (Three.js scene.background = env).
            bundle.showEnvironmentBackground(true, intensity = 1.0f)

            addCheckerboardFloor(bundle, activeRenderer, tilesPerSide = 10, tileSize = 0.55f, y = -1.0f)

            registerTorusNode(
                bundle = bundle,
                renderer = activeRenderer,
                name = "ml-left",
                model = Matrix4f().translate(-1.35f, -0.2f, 0.5f).rotateY(0.35f),
                material = material(Vector4f(0.85f, 0.85f, 0.88f, 1f), metallic = 1f, roughness = 0.14f),
                slices = 72,
                stacks = 72
            )
            registerTorusNode(
                bundle = bundle,
                renderer = activeRenderer,
                name = "ml-mid",
                model = Matrix4f().translate(0f, -0.16f, 0f).rotateX(0.22f).rotateY(-0.24f),
                material = material(Vector4f(0.85f, 0.85f, 0.88f, 1f), metallic = 1f, roughness = 0.1f),
                slices = 72,
                stacks = 72
            )
            registerTorusNode(
                bundle = bundle,
                renderer = activeRenderer,
                name = "ml-right",
                model = Matrix4f().translate(1.35f, -0.2f, -0.5f).rotateY(-0.4f),
                material = material(Vector4f(0.85f, 0.85f, 0.88f, 1f), metallic = 1f, roughness = 0.18f),
                slices = 72,
                stacks = 72
            )

            // One shadow-casting key light plus three coloured point lights that pool on the floor.
            bundle.addLight(
                FabricateLight.directional(
                    direction = Vector3f(-0.4f, -1f, -0.3f),
                    color = Vector3f(1f, 0.97f, 0.9f),
                    intensity = 1.7f,
                    castShadow = true
                )
            )
            bundle.addLight(FabricateLight.point(Vector3f(-1.7f, 0.15f, -1.2f), color = Vector3f(1f, 0.22f, 0.25f), intensity = 10f, range = 5.5f))
            bundle.addLight(FabricateLight.point(Vector3f(1.7f, 0.1f, -1.0f), color = Vector3f(0.25f, 0.5f, 1f), intensity = 10f, range = 5.5f))
            bundle.addLight(FabricateLight.point(Vector3f(0f, 0.55f, 1.7f), color = Vector3f(0.32f, 1f, 0.45f), intensity = 9f, range = 5.5f))
            bundle.syncLights()

            bundle.present(renderer = activeRenderer)
        }
    }

suspend fun renderToneMapBloom

body — 70 lines, from line 784
    suspend fun renderToneMapBloom(
        outputPath: String,
        renderer: Renderer = rendererFromContextOrDocument(),
        window: Window = renderer.window,
        environmentPath: String = File("public/docs/assets/fabricate_demo/venice_sunset_1k.exr").absolutePath
    ): File {
        val environment = LoadedTexture.fromExrFile(
            environmentPath,
            renderer,
            requestedMipLevels = 0,
            prefilterEnvironment = true
        ).also { texture ->
            texture.setDefaultSampler()
            texture.transferNow()
        }
        val bundle = StandardRenderer.build(
            renderer,
            StandardRenderer.Config(
                environmentTexture = environment,
                environmentStrength = 3.0f,
                environmentRotation = 0.3f,
                backgroundColor = srgbGray(0.05f)
            )
        )
        bundle.scene.camera.at.set(0f, 0.8f, -3.6f)
        bundle.scene.camera.target.set(0f, -0.15f, 0f)
        bundle.scene.lightIntensity = 24.0f
        bundle.scene.shadowStrength = 0.8f
        bundle.scene.aoStrength = 0.7f
        bundle.scene.shadowExtent = 6.0f
        bundle.scene.shadowBias = 0.0024f

        addCheckerboardFloor(bundle, renderer, tilesPerSide = 9, tileSize = 0.52f, y = -1.0f)
        registerTorusNode(
            bundle = bundle,
            renderer = renderer,
            name = "tb-left",
            model = Matrix4f().translate(-1.05f, -0.18f, 0.35f).rotateY(0.32f),
            material = material(Vector4f(0.95f, 0.35f, 0.30f, 1f), metallic = 1f, roughness = 0.08f),
            slices = 72,
            stacks = 72
        )
        registerTorusNode(
            bundle = bundle,
            renderer = renderer,
            name = "tb-right",
            model = Matrix4f().translate(1.05f, -0.16f, -0.35f).rotateY(-0.35f),
            material = material(Vector4f(0.30f, 0.70f, 0.98f, 1f), metallic = 1f, roughness = 0.07f),
            slices = 72,
            stacks = 72
        )

        bundle.present(renderer = renderer)
        bundle.applyScenePlan()
        bundle.attachTo(renderer)

        // Left: raw HDR (highlights clip to white). Right: bloom + ACES tone mapping.
        val raw = captureScene(tempPng("fabricate-tonemap-off"), bundle, renderer) {}
        bundle.addBloom(threshold = 1.0f, intensity = 1.8f, radius = 4.0f, renderer = renderer)
        bundle.setToneMapping(FabricateToneMap.ACES, exposure = 1.0f, renderer = renderer)
        bundle.attachTo(renderer)
        val graded = captureScene(tempPng("fabricate-tonemap-on"), bundle, renderer) {}

        return try {
            composeHorizontal(raw, graded, File(outputPath))
        } finally {
            raw.delete()
            graded.delete()
        }
    }

fun htmlForImage(imageFile: File, caption: String? = null): String

body — 141 lines, from line 855
    fun htmlForImage(imageFile: File, caption: String? = null): String {
        val encoded = Base64.getEncoder().encodeToString(imageFile.readBytes())
        val captionHtml = caption?.takeIf { it.isNotBlank() }?.let {
            """<div style="margin-top:10px;color:#b8b8b8;font-size:13px;">$it</div>"""
        } ?: ""
        return """
            <div style="padding:16px;background:#111;border:1px solid #2f2f2f;">
                <img
                    src="data:image/png;base64,$encoded"
                    alt="StandardRenderer capture"
                    style="display:block;width:100%;height:auto;background:#111;"
                />
                $captionHtml
            </div>
        """.trimIndent()
    }

    private suspend fun renderScene(
        outputPath: String,
        renderer: Renderer,
        window: Window,
        config: StandardRenderer.Config = StandardRenderer.Config(),
        buildScene: (StandardRenderer.Bundle, Renderer) -> Unit
    ): File {
        val targetFile = File(outputPath)
        targetFile.parentFile?.mkdirs()
        if (targetFile.exists()) {
            targetFile.delete()
        }

        val bundle = StandardRenderer.build(renderer, config)
        buildScene(bundle, renderer)
        bundle.applyScenePlan()
        bundle.attachTo(renderer)

        return captureFinalImage(bundle, targetFile, renderer)
    }

    private suspend fun captureScene(
        targetFile: File,
        bundle: StandardRenderer.Bundle,
        renderer: Renderer,
        beforeCapture: () -> Unit
    ): File {
        targetFile.parentFile?.mkdirs()
        if (targetFile.exists()) {
            targetFile.delete()
        }
        beforeCapture()
        bundle.applyScenePlan()
        return captureFinalImage(bundle, targetFile, renderer)
    }

    private suspend fun captureFinalImage(
        bundle: StandardRenderer.Bundle,
        targetFile: File,
        renderer: Renderer
    ): File {
        waitForRendererFrames(renderer)
        val output = bundle.postStack.output()
        val html = TexturePreviewCache.capture(
            renderer = renderer,
            vkImage = output.colorImageHandles[0],
            format = output.colorFormats[0],
            srcWidth = output.width,
            srcHeight = output.height,
            currentLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
            maxDim = CAPTURE_MAX_DIM
        ) ?: error("could not read back StandardRenderer final image")
        val encoded = DATA_IMAGE_RE.find(html)?.groupValues?.get(1)
            ?: error("could not parse StandardRenderer final image data")
        targetFile.writeBytes(Base64.getDecoder().decode(encoded))
        require(targetFile.length() > 0L) { "StandardRenderer final image readback wrote an empty PNG" }
        return targetFile
    }

    private suspend fun waitForRendererFrames(renderer: Renderer) {
        val startFrame = renderer.frameCounter
        val targetFrame = startFrame + WAIT_FRAMES
        val deadline = System.nanoTime() + 15_000_000_000L
        var lastLoggedFrame = Long.MIN_VALUE
        while (renderer.frameCounter < targetFrame) {
            if (renderer.frameCounter != lastLoggedFrame) {
                lastLoggedFrame = renderer.frameCounter
                lprintln(
                    "info,doc",
                    " -- waiting for renderer texture capture frame ${renderer.frameCounter}/$targetFrame --"
                )
            }
            if (System.nanoTime() > deadline) {
                error("timed out waiting for renderer frames: start=$startFrame current=${renderer.frameCounter} target=$targetFrame")
            }
            delay(16)
            yield()
        }
    }

    private fun configureGroundedTorusScene(bundle: StandardRenderer.Bundle, renderer: Renderer) {
        bundle.scene.camera.at.set(0f, 0.62f, -3.35f)
        bundle.scene.camera.target.set(0f, -0.35f, 0f)
        bundle.scene.lightDirection.set(Vector3f(-0.42f, -1.0f, -0.25f).normalize())
        bundle.scene.shadowExtent = 5.5f
        bundle.scene.shadowBias = 0.0025f

        val floor = bundle.registerPullingMesh(name = "floor", maxVertices = 4096, renderer = renderer)
        val floorWrite = StandardShapes.disk(
            positions = floor.gBuffer.position,
            normals = floor.gBuffer.normal,
            radius = 3.2f,
            slices = 96,
            stacks = 1,
            config = StandardShapes.WriteConfig(append = false)
        )
        floor.setVertexCount(floorWrite.writtenVertices)
        bindFabricateMesh(
            bundle = bundle,
            name = "floor",
            mesh = floor,
            model = Matrix4f().translate(0f, -0.85f, 0f).rotateX((-PI * 0.5).toFloat()),
            material = material(
                color = Vector4f(0.74f, 0.74f, 0.78f, 1f),
                metallic = 0.0f,
                roughness = 0.92f
            ),
            boundsRadius = 3.2f
        )

        registerTorusNode(
            bundle = bundle,
            renderer = renderer,
            name = "torus",
            model = Matrix4f().translate(0f, -0.18f, 0f),
            material = material(
                color = Vector4f(0.83f, 0.58f, 0.28f, 1f),
                metallic = 0.18f,
                roughness = 0.36f
            ),
            slices = 64,
            stacks = 64
        )
    }

A tight cluster of bright matte spheres resting on the floor. AO in this renderer only modulates the small ambient floor (direct light is never occluded), so it is visible only where a surface is starved of direct light — a deep, shadowed crevice. Touching spheres create exactly that: the contact valleys are self-shadowed and ambient-dominated, so toggling AO swings them from a soft grey to a firm dark seam. Bright, non-metallic, rough material keeps the ambient term (and thus the AO difference) as large as the shader allows.

source — 415 lines, from line 1006
    private fun configureAoContactScene(bundle: StandardRenderer.Bundle, renderer: Renderer) {
        bundle.scene.camera.at.set(0f, 1.45f, -3.05f)
        bundle.scene.camera.target.set(0f, -0.18f, 0.15f)
        // A steep, near-overhead key. AO here only modulates the small ambient term, so it
        // is visible only where direct light is scarce; a top-down key keeps the sphere
        // tops bright while leaving the contact seams direct-light-starved and thus
        // ambient- (hence AO-) dominated.
        bundle.scene.lightDirection.set(Vector3f(-0.16f, -1.0f, -0.12f).normalize())
        // Knock the key well down from the default 4.0 so the ambient floor is a large
        // share of the shading — otherwise the ~0.09 ambient (the only thing AO touches)
        // is swamped and AO on/off is indistinguishable.
        bundle.scene.lightIntensity = 1.4f
        // Max AO and a wider gather so the touching-sphere valleys occlude deeply.
        bundle.scene.aoStrength = 1.0f
        bundle.scene.aoRadius = 0.55f
        bundle.scene.shadowExtent = 5.5f
        bundle.scene.shadowBias = 0.0025f

        val floor = bundle.registerPullingMesh(name = "floor", maxVertices = 4096, renderer = renderer)
        val floorWrite = StandardShapes.disk(
            positions = floor.gBuffer.position,
            normals = floor.gBuffer.normal,
            radius = 3.4f,
            slices = 96,
            stacks = 1,
            config = StandardShapes.WriteConfig(append = false)
        )
        floor.setVertexCount(floorWrite.writtenVertices)
        bindFabricateMesh(
            bundle = bundle,
            name = "floor",
            mesh = floor,
            model = Matrix4f().translate(0f, -0.85f, 0f).rotateX((-PI * 0.5).toFloat()),
            material = material(color = Vector4f(0.78f, 0.78f, 0.82f, 1f), metallic = 0.0f, roughness = 0.94f),
            boundsRadius = 3.4f
        )

        // Bright matte pebbles. radius R, centers 2R apart so neighbours just touch and
        // leave a cusp-deep valley between them. Front row of three along X, back row of
        // two hex-nestled into the front gaps to close the valleys off from the sky.
        val r = 0.62f
        val cy = -0.85f + r // rest on the floor
        val ballMaterial = material(color = Vector4f(0.86f, 0.86f, 0.89f, 1f), metallic = 0.0f, roughness = 0.95f)
        val backZ = -0.35f + 2f * r * 0.866f
        val centers = listOf(
            Triple(-2f * r, cy, -0.35f),
            Triple(0f, cy, -0.35f),
            Triple(2f * r, cy, -0.35f),
            Triple(-r, cy, backZ),
            Triple(r, cy, backZ)
        )
        centers.forEachIndexed { index, (x, y, z) ->
            registerSphereNode(
                bundle = bundle,
                renderer = renderer,
                name = "ao-ball-$index",
                center = Vector3f(x, y, z),
                radius = r,
                material = ballMaterial
            )
        }
    }

    private fun registerSphereNode(
        bundle: StandardRenderer.Bundle,
        renderer: Renderer,
        name: String,
        center: Vector3f,
        radius: Float,
        material: FabricateMaterial,
        slices: Int = 44,
        stacks: Int = 44
    ): StandardRenderer.Bundle.PullingMeshPair {
        val mesh = bundle.registerPullingMesh(name = name, maxVertices = slices * stacks * 6 + 256, renderer = renderer)
        val write = StandardShapes.sphere(
            positions = mesh.gBuffer.position,
            normals = mesh.gBuffer.normal,
            radius = radius,
            slices = slices,
            stacks = stacks,
            config = StandardShapes.WriteConfig(append = false)
        )
        mesh.setVertexCount(write.writtenVertices)
        return bindFabricateMesh(
            bundle = bundle,
            name = name,
            mesh = mesh,
            model = Matrix4f().translate(center.x, center.y, center.z),
            material = material,
            boundsRadius = radius * 1.05f
        )
    }

    private fun addCheckerboardFloor(
        bundle: StandardRenderer.Bundle,
        renderer: Renderer,
        tilesPerSide: Int,
        tileSize: Float,
        y: Float
    ) {
        val verticesPerCube = 36
        val tileGridStep = 2f // pre-scale spacing; model scale (tileSize * 0.5f) maps this to tileSize world spacing
        val tileCount = tilesPerSide * tilesPerSide
        val gridHalfSpan = (tilesPerSide - 1).toFloat()
        val model = Matrix4f()
            .translate(0f, y, 0f)
            .scale(tileSize * 0.5f, 0.02f, tileSize * 0.5f)

        fun createParityMesh(
            name: String,
            parity: Int,
            material: FabricateMaterial
        ): StandardRenderer.Bundle.PullingMeshPair {
            val tilesInParity = (0 until tileCount).count { index ->
                val row = index / tilesPerSide
                val col = index % tilesPerSide
                (row + col) % 2 == parity
            }
            val mesh = bundle.registerPullingMesh(
                name = name,
                maxVertices = tilesInParity * verticesPerCube,
                renderer = renderer
            )
            var append = false
            var vertexCount = 0
            for (row in 0 until tilesPerSide) {
                for (col in 0 until tilesPerSide) {
                    if ((row + col) % 2 != parity) continue
                    val write = StandardShapes.cube(
                        positions = mesh.gBuffer.position,
                        normals = mesh.gBuffer.normal,
                        size = 1f,
                        center = Vec3(
                            -gridHalfSpan + col * tileGridStep,
                            0f,
                            -gridHalfSpan + row * tileGridStep
                        ),
                        config = StandardShapes.WriteConfig(append = append)
                    )
                    append = true
                    vertexCount += write.writtenVertices
                }
            }
            mesh.setVertexCount(vertexCount)
            return bindFabricateMesh(
                bundle = bundle,
                name = name,
                mesh = mesh,
                model = Matrix4f(model),
                material = material,
                boundsRadius = tileSize * tilesPerSide * 0.75f
            )
        }

        createParityMesh(
            name = "checkerboard-light",
            parity = 0,
            material = material(
                color = Vector4f(0.57f, 0.57f, 0.61f, 1f),
                metallic = 0.035f,
                roughness = 0.88f
            )
        )
        createParityMesh(
            name = "checkerboard-dark",
            parity = 1,
            material = material(
                color = Vector4f(0.22f, 0.22f, 0.24f, 1f),
                metallic = 0.035f,
                roughness = 0.92f
            )
        )
    }

    private fun material(
        color: Vector4f,
        metallic: Float = 0.08f,
        roughness: Float = 0.42f
    ): FabricateMaterial =
        FabricateMaterial(baseColor = Vector4f(color), metallic = metallic, roughness = roughness)

    private fun srgbGray(value: Float): Vector3f {
        val linear = if (value <= 0.04045f) {
            value / 12.92f
        } else {
            Math.pow(((value + 0.055f) / 1.055f).toDouble(), 2.4).toFloat()
        }
        return Vector3f(linear, linear, linear)
    }

    private fun registerTorusNode(
        bundle: StandardRenderer.Bundle,
        renderer: Renderer,
        name: String,
        model: Matrix4f,
        material: FabricateMaterial,
        slices: Int,
        stacks: Int,
        tubeRadius: Float = 0.25f,
        majorRadius: Float = 1f
    ): StandardRenderer.Bundle.PullingMeshPair {
        val mesh = bundle.registerTestTorus(
            name = name,
            slices = slices,
            stacks = stacks,
            tubeRadius = tubeRadius,
            majorRadius = majorRadius,
            renderer = renderer
        )
        return bindFabricateMesh(
            bundle = bundle,
            name = name,
            mesh = mesh,
            model = model,
            material = material,
            boundsRadius = majorRadius + tubeRadius
        )
    }

    private fun registerCubeNode(
        bundle: StandardRenderer.Bundle,
        renderer: Renderer,
        name: String,
        model: Matrix4f,
        material: FabricateMaterial,
        size: Float = 1f
    ): StandardRenderer.Bundle.PullingMeshPair {
        val mesh = bundle.registerCube(name = name, size = size, renderer = renderer)
        return bindFabricateMesh(
            bundle = bundle,
            name = name,
            mesh = mesh,
            model = model,
            material = material,
            boundsRadius = size * 0.9f
        )
    }

    private data class GltfBounds(
        val min: Vector3f,
        val max: Vector3f
    ) {
        val center: Vector3f
            get() = Vector3f(min).add(max).mul(0.5f)
        val longestAxis: Float
            get() = max(max.x - min.x, max(max.y - min.y, max.z - min.z))
    }

    private fun gltfWorldBounds(model: GltfSupport.GltfModel): GltfBounds {
        val minPoint = Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
        val maxPoint = Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY)
        val point = Vector3f()
        model.meshes.forEach { instance ->
            // bindMatrix, not bindPose.toMatrix(): a TRS cannot carry a mirror, so a bounds computed
            // from the decomposed pose would frame a mirrored node in the wrong place. See
            // GltfSupport.MeshInstance.bindMatrix.
            val matrix = instance.bindMatrix ?: instance.bindPose.toMatrix()
            val floats = instance.buffers.vertexData.asFloatBuffer()
            for (i in 0 until instance.buffers.vertexCount) {
                val base = i * 12
                point.set(floats.get(base), floats.get(base + 1), floats.get(base + 2))
                matrix.transformPosition(point)
                minPoint.set(min(minPoint.x, point.x), min(minPoint.y, point.y), min(minPoint.z, point.z))
                maxPoint.set(max(maxPoint.x, point.x), max(maxPoint.y, point.y), max(maxPoint.z, point.z))
            }
        }
        require(minPoint.x.isFinite() && maxPoint.x.isFinite()) { "GLTF model contained no vertices" }
        return GltfBounds(minPoint, maxPoint)
    }

    private fun bindFabricateMesh(
        bundle: StandardRenderer.Bundle,
        name: String,
        mesh: StandardRenderer.Bundle.PullingMeshPair,
        model: Matrix4f,
        material: FabricateMaterial,
        boundsRadius: Float = 1f
    ): StandardRenderer.Bundle.PullingMeshPair {
        val node = bundle.sceneGraph.mesh(name = name, material = material) {
            transform.set(model)
            renderable?.userData = name
            renderable?.boundsRadius = boundsRadius
        }
        mesh.bindNode(node)
        mesh.applyFabricateMaterial(material)
        return mesh
    }

    private fun checker(
        mesh: StandardRenderer.Bundle.PullingMeshPair,
        scale: Float,
        color: Vector4f,
        mix: Float = 1f
    ) {
        mesh.gBuffer.mesh.push["checkerScale"] = scale
        mesh.gBuffer.mesh.push["checkerColor"] = color
        mesh.gBuffer.mesh.push["checkerMix"] = mix
    }

    private fun transparentSphere(
        bundle: StandardRenderer.Bundle,
        renderer: Renderer,
        name: String,
        model: Matrix4f,
        color: Vector4f
    ) {
        val node = bundle.sceneGraph.mesh(
            name = name,
            material = FabricateMaterial(baseColor = Vector4f(color)).transparent(color.w)
        ) {
            transform.set(model)
            renderable?.userData = name
        }
        bundle.sceneGraph.updateWorldTransforms()

        val mesh = bundle.registerTransparentPullingMesh(name = name, maxVertices = 32 * 32 * 6, renderer = renderer)
        val write = StandardShapes.sphere(
            positions = mesh.position,
            normals = mesh.normal,
            radius = 1f,
            slices = 32,
            stacks = 32,
            config = StandardShapes.WriteConfig(append = false)
        )
        mesh.setVertexCount(write.writtenVertices)
        mesh.bindModel { Matrix4f(node.worldMatrix) }
        mesh.applyFabricateMaterial(node.renderable?.material ?: FabricateMaterial(baseColor = Vector4f(color)).transparent(color.w))
    }

    private fun composeHorizontal(left: File, right: File, output: File): File {
        val leftImage = ImageIO.read(left)
        val rightImage = ImageIO.read(right)
        val width = leftImage.width + rightImage.width
        val height = max(leftImage.height, rightImage.height)
        val combined = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
        val graphics = combined.createGraphics()
        try {
            graphics.drawImage(leftImage, 0, 0, null)
            graphics.drawImage(rightImage, leftImage.width, 0, null)
        } finally {
            graphics.dispose()
        }
        output.parentFile?.mkdirs()
        ImageIO.write(combined, "png", output)
        return output
    }

    private fun rendererFromContextOrDocument(): Renderer {
        runCatching { fromContext<Renderer>() }.getOrNull()?.let { return it }

        runCatching { Box.context.get() }.getOrNull()?.let { start ->
            rendererNear(start)?.let { return it }
        }

        documents.values.forEach { session ->
            session.boxes["agent_graphics_context"]?.let { rendererIn(it) }?.let { return it }
            session.boxes.values.firstOrNull { it.name == "renderer" }?.let { rendererIn(it) }?.let { return it }
        }

        runCatching { thisSessionInfo.get() }.getOrNull()?.let { session ->
            session.boxes.values.forEach { box ->
                rendererIn(box)?.let { return it }
            }
            rendererIn(session.root)?.let { return it }
        }

        documents.values.forEach { session ->
            session.boxes.values.forEach { box ->
                rendererIn(box)?.let { return it }
            }
            rendererIn(session.root)?.let { return it }
        }

        throw IllegalArgumentException("must pass in a value for context parameter ${Renderer::class.java.name}")
    }

    private fun rendererNear(start: Box): Renderer? {
        val seen = mutableSetOf<Box>()
        val queue = ArrayDeque<Box>()
        queue.add(start)
        while (queue.isNotEmpty()) {
            val box = queue.removeFirst()
            if (!seen.add(box)) continue
            rendererIn(box)?.let { return it }
            box.parents.forEach { queue.add(it) }
            box.children.forEach { queue.add(it) }
        }
        return null
    }

    private fun rendererIn(box: Box): Renderer? {
        (box.getPropertyRaw("renderer") as? Renderer)?.let { return it }
        box.properties.values.firstOrNull { it is Renderer }?.let { return it as Renderer }
        return box.ki.context.vars.values.firstOrNull { it.value is Renderer }?.value as? Renderer
    }

    private fun tempPng(prefix: String): File = Files.createTempFile(prefix, ".png").toFile()

    private val vignetteWarmthFragment = """
        #version 450
        layout(location = 0) in vec2 vUv;
        layout(set = 0, binding = 0) uniform sampler2D sourceTex;
        layout(location = 0) out vec4 outColor;

        void main() {
            vec4 c = texture(sourceTex, vUv);
            vec2 p = vUv * 2.0 - 1.0;
            float vignette = 1.0 - smoothstep(0.18, 1.05, dot(p, p));
            vec3 warm = c.rgb * vec3(1.18, 1.04, 0.84);
            vec3 lifted = pow(max(warm, vec3(0.0)), vec3(0.88));
            vec3 graded = mix(c.rgb, lifted, 0.72) * mix(0.38, 1.22, vignette);
            outColor = vec4(graded, c.a);
        }
    """.trimIndent()
}