From 98baf2a4c30dd5348d459eee6e3287cffde1a597 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Fri, 23 Aug 2019 16:46:24 +0200 Subject: [PATCH] Added WIP shader-phrases, annotations and tooling for shader phrases resolving is done using the JVM class loader --- build.gradle | 11 +++---- orx-noise/build.gradle | 3 ++ .../src/main/kotlin/phrases/NoisePhrases.kt | 32 ++++++++++++++++++ .../src/main/kotlin/ShaderPreprocessor.kt | 33 +++++++++++++++++++ .../main/kotlin/annotations/ShaderPhrase.kt | 15 +++++++++ .../src/main/kotlin/phrases/Dummy.kt | 24 ++++++++++++++ .../src/test/kotlin/TestPreprocessShader.kt | 19 +++++++++++ settings.gradle | 4 ++- 8 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 orx-noise/build.gradle create mode 100644 orx-noise/src/main/kotlin/phrases/NoisePhrases.kt create mode 100644 orx-shader-phrases/src/main/kotlin/ShaderPreprocessor.kt create mode 100644 orx-shader-phrases/src/main/kotlin/annotations/ShaderPhrase.kt create mode 100644 orx-shader-phrases/src/main/kotlin/phrases/Dummy.kt create mode 100644 orx-shader-phrases/src/test/kotlin/TestPreprocessShader.kt diff --git a/build.gradle b/build.gradle index 02bbe74c..e21fec0f 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { plugins { // plugin dependencies, load without applying them - id 'nebula.kotlin' version '1.3.41' apply false + id 'nebula.kotlin' version '1.3.50' apply false id 'com.jfrog.artifactory' version '4.6.2' apply false id 'nebula.contacts' version '4.1.1' apply false @@ -38,7 +38,7 @@ plugins { project.ext { openrndrVersion = "0.3.35" - kotlinVersion = "1.3.41" + kotlinVersion = "1.3.50" spekVersion = "2.0.6" } @@ -65,16 +65,14 @@ allprojects { repositories { mavenCentral() + jcenter() maven { url = "https://dl.bintray.com/openrndr/openrndr" } - maven { url "https://dl.bintray.com/spekframework/spek" } - - } dependencies { @@ -82,11 +80,12 @@ allprojects { compile "org.openrndr:openrndr-filter:$openrndrVersion" compile "org.openrndr:openrndr-shape:$openrndrVersion" compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.3.0-RC2' - testImplementation "org.spekframework.spek2:spek-dsl-jvm:$spekVersion" + testImplementation "org.amshove.kluent:kluent:1.53" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" testRuntimeOnly "org.spekframework.spek2:spek-runner-junit5:$spekVersion" testRuntimeOnly "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" + runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" } contacts { diff --git a/orx-noise/build.gradle b/orx-noise/build.gradle new file mode 100644 index 00000000..3cc46fa0 --- /dev/null +++ b/orx-noise/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation project(":orx-shader-phrases") +} \ No newline at end of file diff --git a/orx-noise/src/main/kotlin/phrases/NoisePhrases.kt b/orx-noise/src/main/kotlin/phrases/NoisePhrases.kt new file mode 100644 index 00000000..d92129c6 --- /dev/null +++ b/orx-noise/src/main/kotlin/phrases/NoisePhrases.kt @@ -0,0 +1,32 @@ +@file:ShaderPhrases(exports = ["hash22","hash21","valueNoise21"]) +package org.openrndr.extra.noise.phrases + +import org.openrndr.extra.shaderphrases.annotations.ShaderPhrase +import org.openrndr.extra.shaderphrases.annotations.ShaderPhrases + +@ShaderPhrase(exports = ["hash22"]) +val phraseHash22 = """vec2 hash22(vec2 p) { + float n = sin(dot(p, vec2(41, 289))); + return fract(vec2(262144, 32768)*n); +} +""" + +@ShaderPhrase(exports = ["hash21"]) +val phraseHash21 = "float hash21(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }" + +@ShaderPhrase(exports = ["valueNoise21"], imports = ["hash21"]) +val phraseValueNoise21 = """ + +float noise(vec2 x) { + vec2 i = floor(x); + vec2 f = fract(x); + + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + + vec2 u = f * f * (3.0 - 2.0 * f); + return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; +} +""".trimIndent() diff --git a/orx-shader-phrases/src/main/kotlin/ShaderPreprocessor.kt b/orx-shader-phrases/src/main/kotlin/ShaderPreprocessor.kt new file mode 100644 index 00000000..ab1380be --- /dev/null +++ b/orx-shader-phrases/src/main/kotlin/ShaderPreprocessor.kt @@ -0,0 +1,33 @@ +package org.openrndr.extra.shaderphrases + +import org.openrndr.extra.shaderphrases.annotations.ShaderPhrases + +fun preprocessShader(shader: String): String { + val lines = shader.split("\n") + val processed = lines.map { + if (it.startsWith("import ")) { + val tokens = it.split(" ") + val full = tokens[1] + val fullTokens = full.split(".") + val fieldName = fullTokens.last().replace(";","") + val packageClassTokens = fullTokens.dropLast(1) + val packageClass = packageClassTokens.joinToString(".") + + val c = Class.forName(packageClass) + if (c.annotations.any { it.annotationClass == ShaderPhrases::class }) { + if (fieldName == "*") { + c.declaredFields.filter { println(it.type); it.type.name =="java.lang.String" }.map { + it.get(null) + }.joinToString("\n") + } else { + c.getDeclaredField(fieldName).get(null) + } + } else { + throw IllegalArgumentException("class $packageClass has no ShaderPhrases annotation") + } + } else { + it + } + } + return processed.joinToString("\n") +} \ No newline at end of file diff --git a/orx-shader-phrases/src/main/kotlin/annotations/ShaderPhrase.kt b/orx-shader-phrases/src/main/kotlin/annotations/ShaderPhrase.kt new file mode 100644 index 00000000..369e2a69 --- /dev/null +++ b/orx-shader-phrases/src/main/kotlin/annotations/ShaderPhrase.kt @@ -0,0 +1,15 @@ +package org.openrndr.extra.shaderphrases.annotations + +enum class ShaderPhraseLanguage { + GLSL +} + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FILE, AnnotationTarget.CLASS, AnnotationTarget.FIELD) +annotation class ShaderPhrase(val exports: Array, + val imports: Array = emptyArray(), + val language: ShaderPhraseLanguage = ShaderPhraseLanguage.GLSL) + + +@Target(AnnotationTarget.FILE) +annotation class ShaderPhrases(val exports: Array) \ No newline at end of file diff --git a/orx-shader-phrases/src/main/kotlin/phrases/Dummy.kt b/orx-shader-phrases/src/main/kotlin/phrases/Dummy.kt new file mode 100644 index 00000000..25bb86af --- /dev/null +++ b/orx-shader-phrases/src/main/kotlin/phrases/Dummy.kt @@ -0,0 +1,24 @@ +@file:JvmName("Dummy") +@file:ShaderPhrases(["dummy"]) + +package org.openrndr.extra.shaderphrases.phrases +import org.openrndr.extra.shaderphrases.annotations.ShaderPhrase +import org.openrndr.extra.shaderphrases.annotations.ShaderPhrases + +@ShaderPhrase(["dummy"]) +const val phraseDummy = """ +float dummy() { + return 0.0; +} +""" + + +fun main() { + val c = Class.forName("org.openrndr.extra.shaderphrases.phrases.Dummy") + + if (c.annotations.any { it.annotationClass == ShaderPhrases::class }) { + println(c.getDeclaredField("phraseDummy").get(null)) + + + } +} \ No newline at end of file diff --git a/orx-shader-phrases/src/test/kotlin/TestPreprocessShader.kt b/orx-shader-phrases/src/test/kotlin/TestPreprocessShader.kt new file mode 100644 index 00000000..c6cc5f0f --- /dev/null +++ b/orx-shader-phrases/src/test/kotlin/TestPreprocessShader.kt @@ -0,0 +1,19 @@ +import org.openrndr.extra.shaderphrases.preprocessShader +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.specification.describe + +object TestPreprocessShader:Spek({ + + describe("A shader with import statements") { + val shader = """ +#version 330 +import org.openrndr.extra.shaderphrases.phrases.Dummy.* + + +""".trimIndent() + describe("when preprocessed") { + val processed = preprocessShader(shader) + println(processed) + } + } +}) \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e9def1be..0e8d838a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,4 +14,6 @@ include 'orx-camera', 'orx-no-clear', 'orx-noise', 'orx-obj-loader', - 'orx-olive' \ No newline at end of file + 'orx-olive', + 'orx-shader-phrases', + 'orx-shader-phrases-processor' \ No newline at end of file