From 5f41d4cded56121af66b18c232c7f557ab803f94 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sun, 27 Sep 2026 12:06:00 +0530 Subject: Add headless screenshot + fix scaling for antialiasing for vacation --- build.gradle.kts | 16 ++- justfile | 6 + screenshot/vacation.VacationProgram.png | Bin 0 -> 2135153 bytes src/main/kotlin/org/openrndr/Preload.kt | 7 +- src/main/kotlin/phenax/extensions/CameraManager.kt | 6 +- .../kotlin/phenax/extensions/HeadlessScreenshot.kt | 30 +++++ src/main/kotlin/phenax/utils/program.kt | 7 +- src/main/kotlin/seigaiha/SeigaihaProgram.kt | 2 +- src/main/kotlin/vacation/VacationProgram.kt | 16 ++- src/main/resources/shaders/vacation.glsl | 133 +++++++++++---------- video/.empty | 1 - 11 files changed, 147 insertions(+), 77 deletions(-) create mode 100644 screenshot/vacation.VacationProgram.png create mode 100644 src/main/kotlin/phenax/extensions/HeadlessScreenshot.kt delete mode 100644 video/.empty diff --git a/build.gradle.kts b/build.gradle.kts index 694f23f..6fb1cb9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -339,10 +339,18 @@ tasks.register("record") { classpath = runTask.classpath args = runTask.args ?: emptyList() - environment("RECORD", "1") - // doFirst { - // jvmArgs = (runTask.jvmArgs ?: emptyList()) + listOf("-Dorg.openrndr.application=EGL") - // } + environment("PHENAX_RECORD", "1") +} + +tasks.register("screenshot") { + group = "application" + val runTask = tasks.named("run").get() + + mainClass.set(runTask.mainClass) + classpath = runTask.classpath + args = runTask.args ?: emptyList() + + environment("PHENAX_SCREENSHOT", "1") } if (properties["openrndr.tasks"] == "true") { diff --git a/justfile b/justfile index e4b4c36..2989070 100644 --- a/justfile +++ b/justfile @@ -7,6 +7,12 @@ record name: record-windowed name: ./gradlew record -Popenrndr.application={{name}}.$(echo "{{name}}" | sed -E 's/^(\w)/\U\1/')ProgramKt +screenshot name: + xvfb-run --server-args="-screen 0 1920x1080x24" just screenshot-windowed "{{name}}" + +screenshot-windowed name: + ./gradlew screenshot -Popenrndr.application={{name}}.$(echo "{{name}}" | sed -E 's/^(\w)/\U\1/')ProgramKt + update: ./gradlew dependencyUpydates diff --git a/screenshot/vacation.VacationProgram.png b/screenshot/vacation.VacationProgram.png new file mode 100644 index 0000000..528ef09 Binary files /dev/null and b/screenshot/vacation.VacationProgram.png differ diff --git a/src/main/kotlin/org/openrndr/Preload.kt b/src/main/kotlin/org/openrndr/Preload.kt index 1bbd673..9664416 100644 --- a/src/main/kotlin/org/openrndr/Preload.kt +++ b/src/main/kotlin/org/openrndr/Preload.kt @@ -1,14 +1,19 @@ package org.openrndr import org.openrndr.draw.* +import org.openrndr.extensions.Screenshots +import org.openrndr.extra.olive.* import phenax.extensions.HeadlessRecorder -import phenax.utils.recordingEnabled +import phenax.extensions.HeadlessScreenshot +import phenax.utils.* class Preload : ApplicationPreload() { override fun onProgramSetup(program: Program) { println("BACKEND(${program.name}): ${program.application::class.qualifiedName}") if (recordingEnabled) { program.extend(HeadlessRecorder()) + } else if (screenshotEnabled) { + program.extend(HeadlessScreenshot()) } } } diff --git a/src/main/kotlin/phenax/extensions/CameraManager.kt b/src/main/kotlin/phenax/extensions/CameraManager.kt index d14205f..5112332 100644 --- a/src/main/kotlin/phenax/extensions/CameraManager.kt +++ b/src/main/kotlin/phenax/extensions/CameraManager.kt @@ -7,7 +7,7 @@ import org.openrndr.draw.* import org.openrndr.math.Quaternion import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 -import phenax.utils.recordingEnabled +import phenax.utils.* enum class ActionType { PAN, @@ -35,7 +35,7 @@ class CameraManager(public var cameraPosition: Vector3, public var cameraRotatio } override fun setup(program: Program) { - if (recordingEnabled) return + if (recordingEnabled || screenshotEnabled) return cameraQuaternion = Quaternion.fromAngles(cameraRotation.x, cameraRotation.y, cameraRotation.z) @@ -99,7 +99,7 @@ class CameraManager(public var cameraPosition: Vector3, public var cameraRotatio override fun beforeDraw(drawer: Drawer, program: Program) {} override fun afterDraw(drawer: Drawer, program: Program) { - if (recordingEnabled) return + if (recordingEnabled || screenshotEnabled) return drawer.fontMap = font drawer.fill = ColorRGBa.WHITE diff --git a/src/main/kotlin/phenax/extensions/HeadlessScreenshot.kt b/src/main/kotlin/phenax/extensions/HeadlessScreenshot.kt new file mode 100644 index 0000000..35de05d --- /dev/null +++ b/src/main/kotlin/phenax/extensions/HeadlessScreenshot.kt @@ -0,0 +1,30 @@ +package phenax.extensions + +import org.openrndr.* +import org.openrndr.draw.* +import org.openrndr.extensions.SingleScreenshot +import org.openrndr.ffmpeg.VideoWriter + +class HeadlessScreenshot : Extension { + override var enabled: Boolean = true + + var screenshots: SingleScreenshot? = null + var videoWriter: VideoWriter? = null + var maxFrameCount: Int? = null + + override fun setup(program: Program) { + val scr = SingleScreenshot() + screenshots = scr + program.extend(scr) { + outputFile = "screenshot/${program.name}.png" + } + } + + override fun beforeDraw(drawer: Drawer, program: Program) {} + + override fun afterDraw(drawer: Drawer, program: Program) { + screenshots?.trigger() + + // program.application.exit() + } +} diff --git a/src/main/kotlin/phenax/utils/program.kt b/src/main/kotlin/phenax/utils/program.kt index fe48351..cfbe9c9 100644 --- a/src/main/kotlin/phenax/utils/program.kt +++ b/src/main/kotlin/phenax/utils/program.kt @@ -19,7 +19,8 @@ fun recordDuration(millis: Int) { fun recordDuration(): Int? = System.getProperty("phenax.record.frameCount")?.toIntOrNull() -val recordingEnabled: Boolean by lazy { System.getenv("RECORD") != null } +val recordingEnabled: Boolean by lazy { System.getenv("PHENAX_RECORD") != null } +val screenshotEnabled: Boolean by lazy { System.getenv("PHENAX_SCREENSHOT") != null } // fun classDir(): String = // "src/main/kotlin/${stackRootClassName(sanitize = @@ -30,7 +31,7 @@ val recordingEnabled: Boolean by lazy { System.getenv("RECORD") != null } // Have to keep the same name (oliveProgram) to get update on save to work fun ApplicationBuilder.oliveProgram(init: Program.() -> Unit) { - if (recordingEnabled) { + if (recordingEnabled || screenshotEnabled) { program(init) } else { // TODO: Make watching shader files work @@ -88,7 +89,7 @@ fun Program.liveComputeShader(name: String): ReadOnlyProperty