diff --git a/shader-test/haxe_libraries/kiss.hxml b/shader-test/haxe_libraries/kiss.hxml index a2e4be6..3795b3e 100644 --- a/shader-test/haxe_libraries/kiss.hxml +++ b/shader-test/haxe_libraries/kiss.hxml @@ -1,12 +1,12 @@ -# @install: lix --silent download "gh://github.com/kiss-lang/kiss#62964ccb83d074ad57975183d4224838b0ec5e50" into kiss/0.0.1/github/62964ccb83d074ad57975183d4224838b0ec5e50 -# @run: haxelib run-dir kiss "${HAXE_LIBCACHE}/kiss/0.0.1/github/62964ccb83d074ad57975183d4224838b0ec5e50" +# @install: lix --silent download "gh://github.com/kiss-lang/kiss#0e99086c38b96276be3b6bd36ce40840a5988139" into kiss/0.0.1/github/0e99086c38b96276be3b6bd36ce40840a5988139 +# @run: haxelib run-dir kiss "${HAXE_LIBCACHE}/kiss/0.0.1/github/0e99086c38b96276be3b6bd36ce40840a5988139" -lib haxe-strings -lib hscript -lib tink_json -lib tink_macro -lib tink_syntaxhub -lib uuid --cp ${HAXE_LIBCACHE}/kiss/0.0.1/github/62964ccb83d074ad57975183d4224838b0ec5e50/src +-cp ${HAXE_LIBCACHE}/kiss/0.0.1/github/0e99086c38b96276be3b6bd36ce40840a5988139/src -D kiss=0.0.1 -w -WUnusedPattern --macro kiss.KissFrontend.use() \ No newline at end of file diff --git a/src/kiss_flixel/ShaderFrontend.hx b/src/kiss_flixel/ShaderFrontend.hx index bd41fe2..8c537e4 100644 --- a/src/kiss_flixel/ShaderFrontend.hx +++ b/src/kiss_flixel/ShaderFrontend.hx @@ -4,61 +4,156 @@ import tink.syntaxhub.*; import haxe.macro.Expr; import haxe.macro.Context; import haxe.macro.Expr.ImportMode; +import kiss.Stream; + using StringTools; using tink.MacroApi; class ShaderFrontend implements FrontendPlugin { - public function new() {} - - var vertexExtensions = ["v.glsl", "vert"]; - var fragmentExtensions = ["f.glsl", "frag"]; + + var vertexExtensions = ["v.glsl", "vert"]; + var fragmentExtensions = ["f.glsl", "frag"]; public function extensions() { return vertexExtensions.concat(fragmentExtensions).iterator(); } - + public function parse(file:String, context:FrontendContext):Void { - var extension = file.substr(file.indexOf(".") + 1); - trace(extension); + var extension = file.substr(file.indexOf(".") + 1); + trace(extension); final type = context.getType(); - var parentClass = 'flixel.system.FlxAssets.FlxShader'; - type.kind = TDClass(parentClass.asTypePath(), [], false, false, false); + var parentClass = 'flixel.system.FlxAssets.FlxShader'; + type.kind = TDClass(parentClass.asTypePath(), [], false, false, false); - var pos = Context.makePosition({ file: file, min: 0, max: 0 }); - - var metaName = if (vertexExtensions.contains(extension)) - ":glVertexSource" - else if (fragmentExtensions.contains(extension)) - ":glFragmentSource" - else - throw "Unknown extension"; + var pos = Context.makePosition({file: file, min: 0, max: 0}); - var meta = { - pos: pos, - name: metaName, - params: [ - { - pos: pos, - expr: EConst(CString(sys.io.File.getContent(file))) - } - ] - }; + var glslStream = Stream.fromFile(file); - type.fields.push({ - pos: pos, - name: "new", - meta: [meta], - kind: FFun({ - args: [], - expr: macro { super(); } - }), - access: [APublic] - }); + var transformedCode = ""; + function error(reason = "") { + throw 'Error transforming shader code! $reason'; + } + + // Supply ShaderToy-esque iTime + transformedCode += 'uniform float iTime = 0.0;\n'; + type.fields.push({ + pos: pos, + name: "__update", + kind: FFun({ + args: [], + expr: macro { + super.__update(); + data.iTime.value = [data.iTime.value[0] + flixel.FlxG.elapsed]; + } + }), + access: [APublic, AOverride] + }); + + // TODO Implement round for the targets that weirdly don't have it + + var delimiters = ",.(){}[] \t\n;?:|&<>/*+-'\"=".split(""); + + var colorOut = ""; + var coordIn = ""; + + + function nextToken() { + glslStream.dropWhitespace(); + return glslStream.takeUntilOneOf(delimiters); + } + + function dropNext(delim:String) { + glslStream.dropWhitespace(); + glslStream.dropString(delim); + } + + while (!glslStream.isEmpty()) { + switch (glslStream.takeWhileOneOf(delimiters)) { + case Some(codeSyntax): + transformedCode += codeSyntax; + default: + } + switch (glslStream.takeUntilOneOf(delimiters)) { + case Some("#pragma"): + switch (nextToken()) { + case Some("header"): + // We already add #pragma header at the start of everything -- a duplicate creates an error + continue; + case Some(pragma): + transformedCode += '#pragma $pragma'; + default: + } + + case Some("iResolution"): + transformedCode += "openfl_TextureSize"; + + case Some("mainImage"): + dropNext("("); + dropNext("out"); + dropNext("vec4"); + switch (nextToken()) { + case Some(symbol): + colorOut = symbol; + default: + error(); + } + dropNext(","); + dropNext("in"); + dropNext("vec2"); + switch (nextToken()) { + case Some(symbol): + coordIn = symbol; + default: + error(); + } + dropNext(")"); + transformedCode += "main() "; + + case Some(name) if (name == colorOut): + transformedCode += "gl_FragColor"; + case Some(name) if (name == coordIn): + transformedCode += "openfl_TextureCoordv * openfl_TextureSize"; + + case Some(other): + transformedCode += other; + default: + } + } + + transformedCode = "#pragma header\n" + transformedCode; + + var metaName = if (vertexExtensions.contains(extension)) ":glVertexSource" else if (fragmentExtensions.contains(extension)) ":glFragmentSource" else + throw "Unknown extension"; + + var meta = { + pos: pos, + name: metaName, + params: [ + { + pos: pos, + expr: EConst(CString(transformedCode)) + } + ] + }; + + type.fields.push({ + pos: pos, + name: "new", + meta: [meta], + kind: FFun({ + args: [], + expr: macro { + super(); + data.iTime.value = [0.0]; + } + }), + access: [APublic] + }); } static function use() { tink.SyntaxHub.frontends.whenever(new ShaderFrontend()); } -} \ No newline at end of file +}