[orx-shader-phrases] Add shader phrases tweaks (#196)

This commit is contained in:
Abe Pazos
2021-10-19 11:58:08 +02:00
committed by GitHub
parent dd750e58a0
commit 328e9c94f3
6 changed files with 135 additions and 24 deletions

View File

@@ -88,6 +88,7 @@ kotlin {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion")
runtimeOnly("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion")
runtimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion")
runtimeOnly("org.slf4j:slf4j-simple:1.7.30")
implementation("org.spekframework.spek2:spek-dsl-jvm:$spekVersion")
implementation("org.amshove.kluent:kluent:$kluentVersion")
}

View File

@@ -2,6 +2,7 @@ package org.openrndr.extra.shaderphrases
import mu.KotlinLogging
import org.openrndr.draw.Shader
import org.openrndr.extra.shaderphrases.ShaderPhraseRegistry.getGLSLFunctionName
//import org.openrndr.extra.shaderphrases.phrases.phraseTbnMatrix
import org.openrndr.utils.url.textFromURL
@@ -16,16 +17,9 @@ class ShaderPhrase(val phrase: String) {
* This will likely be called by [ShaderPhraseBook]
*/
fun register(bookId: String? = null) {
val functionRex =
Regex("(float|int|[bi]?vec2|[bi]?vec3|[bi]?vec4|mat3|mat4)[ ]+([a-zA-Z0-9_]+)[ ]*\\(.*\\).*")
val defs = phrase.split("\n").filter {
functionRex.matches(it)
}.take(1).mapNotNull {
val m = functionRex.find(it)
m?.groupValues?.getOrNull(2)
}
val id = defs.firstOrNull() ?: error("no function body found in phrase")
ShaderPhraseRegistry.registerPhrase("${bookId?.let { "$it." } ?: ""}$id", this)
val id = getGLSLFunctionName(phrase)
val prefix = bookId?.let { "$it." } ?: ""
ShaderPhraseRegistry.registerPhrase("$prefix$id", this)
}
}
/**
@@ -61,6 +55,22 @@ object ShaderPhraseRegistry {
}
return phrase
}
/**
* Gets the first GLSL function name out of GLSL source code
*/
fun getGLSLFunctionName(glsl: String): String {
val functionRex =
Regex("""\s*(float|int|[bi]?vec[234]|mat[34])\s+(\w+)\s*\(.*\).*""")
val defs = glsl.split("\n").filter {
functionRex.matches(it)
}.take(1).mapNotNull {
val m = functionRex.find(it)
m?.groupValues?.getOrNull(2)
}
return defs.firstOrNull()
?: error("no function body found in phrase")
}
}
/**
@@ -74,10 +84,10 @@ fun preprocessShader(source: String, symbols: Set<String> = emptySet()): String
newSymbols.addAll(symbols)
val lines = source.split("\n")
val processed = lines.mapIndexed { index, it ->
if (it.startsWith("#pragma import")) {
val tokens = it.split(" ")
val symbol = tokens[2].trim().replace(";", "")
val funcName = Regex("""^\s*#pragma\s+import\s+([a-zA-Z0-9_.]+)""")
val processed = lines.map { line ->
if (line.contains("#pragma")) {
val symbol = funcName.find(line)?.groupValues?.get(1) ?: return@map line
val fullTokens = symbol.split(".")
val fieldName = fullTokens.last().replace(";", "").trim()
val packageClassTokens = fullTokens.dropLast(1)
@@ -90,7 +100,7 @@ fun preprocessShader(source: String, symbols: Set<String> = emptySet()): String
""
}
} else {
it
line
}
}
return processed.joinToString("\n")

View File

@@ -0,0 +1,52 @@
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.internal.assertFails
import org.openrndr.extra.shaderphrases.ShaderPhraseRegistry.getGLSLFunctionName
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
object TestFunctionNameRx : Spek({
describe("A function name") {
mapOf(
"ivec4 aaa() {" to "aaa",
"ivec3 bbb() {" to "bbb",
"ivec2 ccc() {" to "ccc",
"bvec4 ddd() {" to "ddd",
"bvec3 eee() {" to "eee",
"bvec2 fff() {" to "fff",
"vec4 ggg() {" to "ggg",
"vec3 hhh() {" to "hhh",
"vec2 iii() {" to "iii",
"mat3 jjj() {" to "jjj",
"mat4 kkk() {" to "kkk",
"float lll() {" to "lll",
" float mmm( ) { " to "mmm",
"int nnn() {" to "nnn",
"vec2 limit(vec2 a, float b) {" to "limit",
"""
vec4 white() {
return vec4(1.0);
}
""".trimMargin() to "white"
).forEach { (goodGLSL, expectedFuncName) ->
it("can be extracted from valid GLSL") {
getGLSLFunctionName(goodGLSL) `should be equal to`
expectedFuncName
}
}
}
describe("A function name") {
listOf(
"float int mat4",
"float rnd {",
).forEach { badGLSL ->
it("is not extracted if GLSL function not declared") {
assertFails {
val funcName = getGLSLFunctionName(badGLSL)
}
}
}
}
})

View File

@@ -6,10 +6,12 @@ import org.spekframework.spek2.style.specification.describe
object TestShaderPhrase : Spek({
describe("A shader phrase") {
val phrase = ShaderPhrase("""
val phrase = ShaderPhrase(
"""
|vec4 test_phrase() {
|}
""".trimMargin() )
""".trimMargin()
)
it("can be registered") {
phrase.register()
}

View File

@@ -5,19 +5,23 @@ import org.openrndr.extra.shaderphrases.ShaderPhraseRegistry
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
class TestShaderPhraseBookobject : Spek({
class TestShaderPhraseBook : Spek({
describe("A shader phrase book") {
val book = object:ShaderPhraseBook("testBook") {
val phrase = ShaderPhrase("""
|vec4 test_phrase() {
|}
""".trimMargin() )
val book = object : ShaderPhraseBook("testBook") {
val phrase = ShaderPhrase(
"""
vec4 test_phrase() {
}
""".trimMargin()
)
}
it("can be registered") {
book.register()
}
it("can be found") {
ShaderPhraseRegistry.findPhrase("testBook.test_phrase") `should be` book.phrase
ShaderPhraseRegistry.findPhrase(
"testBook.test_phrase"
) `should be` book.phrase
}
}
})

View File

@@ -0,0 +1,42 @@
import org.amshove.kluent.`should contain`
import org.amshove.kluent.`should not contain`
import org.openrndr.extra.shaderphrases.ShaderPhrase
import org.openrndr.extra.shaderphrases.ShaderPhraseBook
import org.openrndr.extra.shaderphrases.preprocessShader
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
class TestShaderPhrasePreprocessing : Spek({
describe("the glsl code") {
val book = object : ShaderPhraseBook("testBook") {
val phrase1 = ShaderPhrase("vec3 phrase1() { }")
val phrase2 = ShaderPhrase("vec4 phrase2() { }")
}
book.register()
val glsl = """
// test expected usage
#pragma import testBook.phrase1
// test odd spacing and line termination
#pragma import testBook.phrase2;
""".trimIndent()
val glslProcessed = preprocessShader(glsl)
it("should not contain phrase1 before preprocessing") {
glsl `should not contain` book.phrase1.phrase
}
it("should not contain phrase2 before preprocessing") {
glsl `should not contain` book.phrase2.phrase
}
it("should contain phrase1 after preprocessing") {
glslProcessed `should contain` book.phrase1.phrase
}
it("should contain phrase2 after preprocessing") {
glslProcessed `should contain` book.phrase2.phrase
}
}
})