Skip to content

Commit

Permalink
Plugin embed GLSL shader pairs
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed Sep 3, 2023
1 parent 3fd6f9d commit e36b878
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package indigoplugin.generators

object EmbedGLSLShaderPair {

def generate(
outDir: os.Path,
moduleName: String,
fullyQualifiedPath: String,
vertex: os.Path,
fragment: os.Path,
runValidator: Boolean
): Seq[os.Path] = {

val shaderFiles: Seq[os.Path] =
Seq(vertex, fragment)

// Bail out if the file is missing or not a file.
shaderFiles.foreach { f =>
if (!os.exists(f)) throw new Exception("Shader file not found: " + f.toString())
else if (os.isDir(f)) throw new Exception("Shader path given was a directory, not a file: " + f.toString())
else ()
}

val wd = outDir / Generators.OutputDirName

os.makeDir.all(wd)

if (runValidator) {
val glslValidatorExitCode = os.proc("glslangValidator", "-v").call(os.pwd).exitCode

if (glslValidatorExitCode == 0)
shaderFiles.foreach { f =>
val exitCode = os.proc("glslangValidator", f.toString).call(os.pwd).exitCode

if (exitCode != 0) throw new Exception("GLSL Validation Error in: " + f.toString)
}
else
throw new Exception("Validation was requested, but the GLSL Validator is not installed.")
}

val shaderDetails: Seq[ShaderDetails] =
shaderFiles.map(f => extractDetails(f))

val contents: String =
shaderDetails
.flatMap { d =>
extractShaderCode(d.shaderCode, "vertex", d.newName) ++
extractShaderCode(d.shaderCode, "fragment", d.newName) ++
extractShaderCode(d.shaderCode, "prepare", d.newName) ++
extractShaderCode(d.shaderCode, "light", d.newName) ++
extractShaderCode(d.shaderCode, "composite", d.newName)
}
.map { snippet =>
s""" val ${snippet.variableName}: String =
| ${Generators.TripleQuotes}${snippet.snippet}${Generators.TripleQuotes}
|
""".stripMargin
}
.mkString("\n")

val file: os.Path =
wd / s"$moduleName.scala"

val newContents: String =
template(moduleName, fullyQualifiedPath, contents)

os.write(file, newContents)

Seq(file)
}

def sanitiseName(name: String, ext: String): String = {
val noExt = if (ext.nonEmpty && name.endsWith(ext)) name.dropRight(ext.length) else name
noExt.replaceAll("[^A-Za-z0-9]", "-").split("-").map(_.capitalize).mkString
}

def extractDetails(file: os.Path): ShaderDetails = {
val name = file.last
val ext = file.ext
val sanitised = sanitiseName(file.last, file.ext)

ShaderDetails(sanitised, name, ext, os.read(file))
}

def template(moduleName: String, fullyQualifiedPath: String, contents: String): String =
s"""package $fullyQualifiedPath
|
|object $moduleName {
|
|$contents
|
|}
""".stripMargin

def extractShaderCode(text: String, tag: String, newName: String): List[ShaderSnippet] =
s"""//<indigo-$tag>\n((.|\n|\r)*)//</indigo-$tag>""".r
.findAllIn(text)
.toList
.map(_.toString)
.map(_.split('\n').drop(1).dropRight(1).mkString("\n"))
.map(program => ShaderSnippet(newName + tag.split("-").map(_.capitalize).mkString, program))

case class ShaderDetails(newName: String, originalName: String, ext: String, shaderCode: String)
case class ShaderSnippet(variableName: String, snippet: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ package indigoplugin.generators

object EmbedText {

val tripleQuotes: String = "\"\"\""

def generate(
outDir: os.Path,
moduleName: String,
fullyQualifiedPackage: String,
text: String
): Seq[os.Path] = {
val wd = outDir / "indigo-compile-codegen-output"
val wd = outDir / Generators.OutputDirName

os.makeDir.all(wd)

Expand All @@ -22,7 +20,7 @@ object EmbedText {
|object $moduleName:
|
| val text: String =
| $tripleQuotes$text$tripleQuotes
| ${Generators.TripleQuotes}$text${Generators.TripleQuotes}
|""".stripMargin

os.write.over(file, contents)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package indigoplugin.generators

object Generators {

val OutputDirName: String = "indigo-compile-codegen-output"
val TripleQuotes: String = "\"\"\""

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package indigoplugin.generators

class EmbedGLSLShaderPairTests extends munit.FunSuite {

test("sanitiseName") {
assertEquals(EmbedGLSLShaderPair.sanitiseName("Ha_ erm!What?...12!.vert", "vert"), "HaErmWhat12")
}

test("template") {
val actual =
EmbedGLSLShaderPair.template("MyModule", "org.example", "// code goes here.")

val expected =
s"""package org.example
|
|object MyModule {
|
|// code goes here.
|
|}
""".stripMargin

assertEquals(actual.trim, expected.trim)
}

test("extractShaderCode - vertex") {
val actual =
EmbedGLSLShaderPair.extractShaderCode(sampleVertexProgram, "vertex", "MyShader")

val expected =
List(
EmbedGLSLShaderPair.ShaderSnippet(
"MyShaderVertex",
"void vertex(){}"
)
)

assertEquals(actual, expected)
}

test("extractShaderCode - fragment") {
val actual =
EmbedGLSLShaderPair.extractShaderCode(sampleFragmentProgram, "fragment", "MyShader")

val expected =
List(
EmbedGLSLShaderPair.ShaderSnippet(
"MyShaderFragment",
"""
layout (std140) uniform IndigoBitmapData {
highp float FILLTYPE;
};
void fragment(){
// 0 = normal; 1 = stretch; 2 = tile
int fillType = int(round(FILLTYPE));
vec4 textureColor;
switch(fillType) {
case 0:
textureColor = CHANNEL_0;
break;
case 1:
vec2 stretchedUVs = CHANNEL_0_POSITION + UV * CHANNEL_0_SIZE;
textureColor = texture(SRC_CHANNEL, stretchedUVs);
break;
case 2:
vec2 tiledUVs = CHANNEL_0_POSITION + (fract(UV * (SIZE / TEXTURE_SIZE)) * CHANNEL_0_SIZE);
textureColor = texture(SRC_CHANNEL, tiledUVs);
break;
default:
textureColor = CHANNEL_0;
break;
}
COLOR = textureColor;
}
""".trim
)
)

assertEquals(actual, expected)
}

val sampleVertexProgram: String =
"""
//<indigo-vertex>
void vertex(){}
//</indigo-vertex>
"""

val sampleFragmentProgram: String =
"""
#version 300 es
precision mediump float;
uniform sampler2D SRC_CHANNEL;
vec4 CHANNEL_0;
vec4 COLOR;
vec2 UV;
vec2 TEXTURE_SIZE;
vec2 SIZE;
vec2 CHANNEL_0_POSITION;
vec2 CHANNEL_0_SIZE;
//<indigo-fragment>
layout (std140) uniform IndigoBitmapData {
highp float FILLTYPE;
};
void fragment(){
// 0 = normal; 1 = stretch; 2 = tile
int fillType = int(round(FILLTYPE));
vec4 textureColor;
switch(fillType) {
case 0:
textureColor = CHANNEL_0;
break;
case 1:
vec2 stretchedUVs = CHANNEL_0_POSITION + UV * CHANNEL_0_SIZE;
textureColor = texture(SRC_CHANNEL, stretchedUVs);
break;
case 2:
vec2 tiledUVs = CHANNEL_0_POSITION + (fract(UV * (SIZE / TEXTURE_SIZE)) * CHANNEL_0_SIZE);
textureColor = texture(SRC_CHANNEL, tiledUVs);
break;
default:
textureColor = CHANNEL_0;
break;
}
COLOR = textureColor;
}
//</indigo-fragment>
"""

}
20 changes: 20 additions & 0 deletions mill-indigo/mill-indigo/src/millindigo/MillIndigo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import indigoplugin.core.IndigoRun
import indigoplugin.core.IndigoCordova
import indigoplugin.IndigoOptions
import indigoplugin.generators.EmbedText
import indigoplugin.generators.EmbedGLSLShaderPair

trait MillIndigo extends mill.Module {

Expand All @@ -27,6 +28,25 @@ trait MillIndigo extends mill.Module {
): Seq[PathRef] =
EmbedText.generate(outDir, moduleName, fullyQualifiedPackage, text).map(p => PathRef(p))

def embedGLSLShaderPair(
outDir: os.Path,
moduleName: String,
fullyQualifiedPackage: String,
vertexShaderPath: os.Path,
fragmentShaderPath: os.Path,
validateGLSL: Boolean
): Seq[PathRef] =
EmbedGLSLShaderPair
.generate(
outDir,
moduleName,
fullyQualifiedPackage,
vertexShaderPath,
fragmentShaderPath,
validateGLSL
)
.map(p => PathRef(p))

}

/** Build a static site for your game using Scala.js's fast linking. */
Expand Down
20 changes: 20 additions & 0 deletions sbt-indigo/src/main/scala/sbtindigo/SbtIndigo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import indigoplugin.core.IndigoBuildSBT
import indigoplugin.core.IndigoCordova
import indigoplugin.core.IndigoRun
import indigoplugin.generators.EmbedText
import indigoplugin.generators.EmbedGLSLShaderPair

object SbtIndigo extends sbt.AutoPlugin {

Expand Down Expand Up @@ -53,6 +54,25 @@ object SbtIndigo extends sbt.AutoPlugin {
): Seq[File] =
EmbedText.generate(os.Path(sourceManagedDir), moduleName, fullyQualifiedPackage, text).map(_.toIO)

def embedGLSLShaderPair(
outDir: os.Path,
moduleName: String,
fullyQualifiedPackage: String,
vertexShaderPath: String,
fragmentShaderPath: String,
validateGLSL: Boolean
): Seq[File] =
EmbedGLSLShaderPair
.generate(
outDir,
moduleName,
fullyQualifiedPackage,
os.RelPath(vertexShaderPath).resolveFrom(os.pwd),
os.RelPath(fragmentShaderPath).resolveFrom(os.pwd),
validateGLSL
)
.map(_.toIO)

}

private def giveScriptBasePath(baseDir: String, scalaVersion: String, projectName: String): String = {
Expand Down

0 comments on commit e36b878

Please sign in to comment.