package phenax.utils import java.io.File import kotlin.properties.ReadOnlyProperty import org.openrndr.ApplicationBuilder import org.openrndr.Program import org.openrndr.draw.* import org.openrndr.extra.filewatcher.watchingFile import org.openrndr.extra.olive.oliveProgram as oliveProgramOriginal fun Int.seconds(): Int = this * 1000 fun Int.minutes(): Int = this.seconds() * 60 fun recordDuration(millis: Int) { System.setProperty("phenax.record.frameCount", (60 * millis / 1000).toInt().toString()) } fun recordDuration(): Int? = System.getProperty("phenax.record.frameCount")?.toIntOrNull() val recordingEnabled: Boolean by lazy { System.getenv("RECORD") != null } // fun classDir(): String = // "src/main/kotlin/${stackRootClassName(sanitize = // true).split(".").dropLast(1).joinToString("/")}"; // fun localComputeShader(name: String): ComputeShader = // ComputeShader.fromCode(File("src/main/resources/shaders/${name}.cs").readText(), name) // Have to keep the same name (oliveProgram) to get update on save to work fun ApplicationBuilder.oliveProgram(init: Program.() -> Unit) { if (recordingEnabled) { program(init) } else { // TODO: Make watching shader files work oliveProgramOriginal(init = init) } } fun preprocessShader(shaderCode: String): String { val pattern = Regex("#include\\s+<([A-Za-z0-9-_/.]+)>") val includes = pattern.findAll(shaderCode) var parts: MutableList = mutableListOf() var lastIndex = 0 includes.forEach { parts.add(shaderCode.slice(lastIndex..(it.range.first - 1))) lastIndex = it.range.last + 1 val modulePath = it.groupValues[1] val moduleContents = File("src/main/resources/shaders/modules/${modulePath}.glsl").readText() parts.add("/* Module ${modulePath} */") parts.add(moduleContents) parts.add("/* End module ${modulePath} */") } parts.add(shaderCode.slice(lastIndex..(shaderCode.length - 1))) return parts.joinToString(separator = "\n") } val defaultShaderCode: String = "" fun Program.liveComputeShader(name: String): ReadOnlyProperty { val shaderCode by watchingFile(File("src/main/resources/shaders/${name}.glsl")) { file -> println("change") preprocessShader(file.readText().toString()) } var cs: ComputeShader = ComputeShader.fromCode(shaderCode, name) if (!recordingEnabled) { var lastCompiledCode: String = shaderCode program.extend { if (shaderCode != lastCompiledCode) { cs.destroy() try { cs = ComputeShader.fromCode(shaderCode, name) } catch (e: Exception) { println("Shader compile failed: ${e.message}") // Restore old shader if new code has error or default shader try { cs = ComputeShader.fromCode(lastCompiledCode, name) } catch (e: Exception) { println("Falling back to default shader") cs = ComputeShader.fromCode(defaultShaderCode, name) } } lastCompiledCode = shaderCode } } } return ReadOnlyProperty { _, _ -> cs } }