├── .gitignore ├── LICENSE ├── ProgramExample ├── build.gradle └── src │ └── main │ └── kotlin │ └── example.kt ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md ├── settings.gradle └── src └── main ├── kotlin └── com │ └── salakheev │ └── shaderbuilderkt │ ├── ProgramProcessor.kt │ ├── ShaderBuilderProcessor.kt │ ├── annotations │ ├── ShaderIntRange.kt │ └── ShaderProgram.kt │ ├── builder │ ├── ShaderBuilder.kt │ ├── delegates │ │ ├── AttributeDelegate.kt │ │ ├── BuiltinVarDelegate.kt │ │ ├── ComponentDelegate.kt │ │ ├── ConstructorDelegate.kt │ │ ├── UniformArrayDelegate.kt │ │ ├── UniformDelegate.kt │ │ └── VaryingDelegate.kt │ └── types │ │ ├── BoolResult.kt │ │ ├── Variable.kt │ │ ├── mat │ │ ├── MatrixColumnDelegate.kt │ │ ├── mat2.kt │ │ ├── mat3.kt │ │ └── mat4.kt │ │ ├── sampler │ │ ├── Sampler2D.kt │ │ └── Sampler2DArray.kt │ │ ├── scalar │ │ ├── float.kt │ │ └── int.kt │ │ └── vec │ │ ├── arrays.kt │ │ ├── vec2.kt │ │ ├── vec3.kt │ │ └── vec4.kt │ ├── examples │ ├── SimpleFragmentShader.kt │ ├── SimpleVertexShader.kt │ └── components │ │ └── ShadowReceiveComponent.kt │ └── sources │ └── ShaderSourceProvider.kt └── resources ├── META-INF └── services │ └── javax.annotation.processing.Processor └── themes └── program.chtml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea/ 4 | !.idea/runConfigurations.xml 5 | /local.properties 6 | .DS_Store 7 | build 8 | /captures 9 | .externalNativeBuild 10 | build/ 11 | release/ 12 | out/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [Danil Salakheev] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /ProgramExample/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.2.70' 3 | 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 9 | } 10 | } 11 | 12 | group 'com.salakheev' 13 | version '1.0' 14 | 15 | apply plugin: 'kotlin' 16 | apply plugin: 'kotlin-kapt' 17 | 18 | repositories { 19 | mavenCentral() 20 | } 21 | 22 | dependencies { 23 | compile rootProject 24 | kapt rootProject 25 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 26 | } 27 | 28 | compileKotlin { 29 | kotlinOptions.jvmTarget = "1.8" 30 | } 31 | compileTestKotlin { 32 | kotlinOptions.jvmTarget = "1.8" 33 | } -------------------------------------------------------------------------------- /ProgramExample/src/main/kotlin/example.kt: -------------------------------------------------------------------------------- 1 | import com.salakheev.shaderbuilderkt.annotations.ShaderProgram 2 | import com.salakheev.shaderbuilderkt.examples.SimpleFragmentShader 3 | import com.salakheev.shaderbuilderkt.examples.SimpleVertexShader 4 | 5 | /** 6 | * Here we use [ShaderProgram] annotation to generate [SimpleShaderProgramSources] 7 | * using gradle kaptKotlin task. 8 | * 9 | * @see [ShaderProgram] 10 | */ 11 | @ShaderProgram(SimpleVertexShader::class, SimpleFragmentShader::class) 12 | class SimpleShaderProgram(receiveShadow: Boolean, alphaTest: Boolean) 13 | 14 | /** 15 | * Before running use gradle build. 16 | */ 17 | fun main(args: Array) { 18 | /** 19 | * [SimpleShaderProgramSources] was generated with all possible shader variants during gradle build. 20 | */ 21 | println(SimpleShaderProgramSources.get(true, false).vertex) 22 | 23 | /** 24 | * Or you can get GLSL source directly from [ShaderBuilder] instance 25 | */ 26 | println(SimpleVertexShader(true).getSource()) 27 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.2.70' 3 | 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 9 | } 10 | } 11 | 12 | group 'com.salakheev' 13 | version '1.0' 14 | 15 | apply plugin: 'kotlin' 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | compile "com.x5dev:chunk-templates:3.3.1" 23 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 24 | } 25 | 26 | compileKotlin { 27 | kotlinOptions.jvmTarget = "1.8" 28 | } 29 | compileTestKotlin { 30 | kotlinOptions.jvmTarget = "1.8" 31 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dananas/kotlin-glsl/7ba77ca59a1f5542314b42cf3f53f066ca442956/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 21 01:39:00 YEKT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # GLSL shaders development in Kotlin 2 | 3 | The sources provide a way of creating GLSL-like code in Kotlin. 4 | 5 | Shader source in Kotlin: 6 | ```kotlin 7 | class FragmentShader(useAlphaTest: Boolean) : ShaderBuilder() { 8 | private val alphaTestThreshold by uniform(::GLFloat) 9 | private val texture by uniform(::Sampler2D) 10 | private val uv by varying(::Vec2) 11 | init { 12 | var color by vec4() 13 | color = texture2D(texture, uv) 14 | // static branching 15 | if (useAlphaTest) { 16 | // dynamic branching 17 | If(color.w lt alphaTestThreshold) { 18 | discard() 19 | } 20 | } 21 | gl_FragColor = color 22 | } 23 | } 24 | ``` 25 | You can get your GLSL source in two ways: 26 | #### 1. Runtime generation 27 | ```kotlin 28 | FragmentShader(useAlphaTest = true).getSource() 29 | ``` 30 | Pros: easy to generate sources. 31 | 32 | Cons: GLSL swizzling functionality may affect performance, as it creates lots of objects at runtime. 33 | 34 | #### 2. Generation using ```@ShaderProgram``` annotation 35 | This functionality can be used as follows: 36 | ```kotlin 37 | @ShaderProgram(VertexShader::class, FragmentShader::class) 38 | class ShaderProgramName(alphaTest: Boolean) 39 | ``` 40 | 41 | Annotation processor generates new sources during ```gradle kaptKotlin``` task which look as follows: 42 | ```kotlin 43 | class ShaderProgramNameSources { 44 | enum class Sources(vertex: String, fragment: String): ShaderProgramSources { 45 | Source0("", "") 46 | ... 47 | } 48 | fun get(alphaTest: Boolean) { 49 | if (alphaTest) return Source0 50 | else return Source1 51 | } 52 | } 53 | ``` 54 | After generation you can get both vertex and fragment sources as follows: 55 | ```kotlin 56 | val sources = ShaderProgramNameSources.get(replaceAlpha = true) 57 | println(sources.vertex) 58 | println(sources.fragment) 59 | ``` 60 | Pros: no need to generate sources at runtime, as you will have direct access to them. 61 | 62 | Cons: shader source classes have to be accessible for AP to load them, thus can't be stored just anywhere in the project. 63 | 64 | #### Generated GLSL 65 | Both methods produce the same result: 66 | ```glsl 67 | uniform sampler2D texture; 68 | uniform float alphaTestThreshold; 69 | varying vec2 uv; 70 | void main(void) { 71 | vec4 color; 72 | color = texture2D(texture, uv); 73 | if ((color.w < alphaTestThreshold)) { 74 | discard; 75 | } 76 | gl_FragColor = color; 77 | } 78 | ``` 79 | 80 | ## How to use 81 | You will need gradle to build sources. 82 | 83 | You have two options here. If you do not need ```@ShaderProgram``` annotation functionality in the above example, then you can simply import the KotlinGlsl project (and may remove ProgramExample module). 84 | 85 | If you do want to use ```@ShaderProgram```, then you will have to store your Kotlin shader sources either directly in the KotlinGlsl or in a project accessible for it during annotations processing stage, as AP will try to load classes provided with ```@ShaderProgram```. 86 | 87 | ## License 88 | MIT 89 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'KotlinGlsl' 2 | include 'ProgramExample' 3 | 4 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/ProgramProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt 2 | 3 | import com.salakheev.shaderbuilderkt.annotations.ShaderIntRange 4 | import com.salakheev.shaderbuilderkt.annotations.ShaderProgram 5 | import com.salakheev.shaderbuilderkt.sources.ShaderSourceProvider 6 | import com.x5.template.Chunk 7 | import java.lang.IllegalArgumentException 8 | import javax.activation.UnsupportedDataTypeException 9 | import javax.annotation.processing.ProcessingEnvironment 10 | import javax.lang.model.element.ElementKind 11 | import javax.lang.model.element.ExecutableElement 12 | import javax.lang.model.element.TypeElement 13 | import javax.lang.model.element.VariableElement 14 | import javax.lang.model.type.MirroredTypeException 15 | import javax.lang.model.type.TypeMirror 16 | 17 | /** 18 | * Processes constructor of a [ShaderProgram] annotated class. 19 | * Collects all possible values for the constructor and generates shader program sources 20 | * using 'resources/themes/program/program.chtml' template. 21 | */ 22 | class ProgramProcessor(private val env: ProcessingEnvironment, private val programType: TypeElement, private val chunk: Chunk) { 23 | private val sourceClassName = programType.simpleName 24 | private val generatedClassName = "${sourceClassName}Sources" 25 | private val vertex: TypeElement 26 | private val fragment: TypeElement 27 | 28 | init { 29 | val packageName = programType.getPackageName() 30 | if (packageName.isNotEmpty()) { 31 | chunk.set("package", packageName) 32 | } 33 | chunk.set("sourceClass", programType.qualifiedName) 34 | chunk.set("className", generatedClassName) 35 | chunk.set("sourceName", sourceClassName) 36 | 37 | val annotation = programType.getAnnotation(ShaderProgram::class.java) 38 | vertex = getAnnotationValue(annotation) { a -> a.vertex } 39 | fragment = getAnnotationValue(annotation) { a -> a.fragment } 40 | 41 | val programConstructor = getTypeConstructor(programType) 42 | processConstructor(programConstructor) 43 | } 44 | 45 | fun getPackageName() = programType.getPackageName() 46 | fun getClassName() = generatedClassName 47 | fun getResult() = chunk.toString() 48 | 49 | private fun getTypeConstructor(type: TypeElement): ExecutableElement { 50 | return type.enclosedElements.find { elem -> elem.kind == ElementKind.CONSTRUCTOR } as ExecutableElement 51 | } 52 | 53 | private fun processConstructor(constructor: ExecutableElement) { 54 | val parameters = constructor.parameters.map { parameter -> parseParameter(parameter) } 55 | val list = parameters.map { "${it.name}: ${it.type}" } 56 | if (list.isNotEmpty()) { 57 | chunk.set("paramsWithTypes", list) 58 | } 59 | chunk.set("resultBranches", getBranches(parameters)) 60 | } 61 | 62 | private fun convertToKotlinType(typeName: String): String { 63 | return when (typeName) { 64 | "boolean" -> "Boolean" 65 | "int" -> "Int" 66 | else -> typeName 67 | } 68 | } 69 | 70 | private fun parseParameter(param: VariableElement): Parameter { 71 | val typeName = param.asType().toString() 72 | return Parameter(convertToKotlinType(typeName), param.simpleName.toString(), when (typeName) { 73 | "boolean" -> BooleanParams().values() 74 | "int" -> getPossibleIntValues(param) 75 | else -> throw UnsupportedDataTypeException("Type '$typeName' is not supported as @ShaderProgram parameter type") 76 | }) 77 | } 78 | 79 | private fun getPossibleIntValues(param: VariableElement): Array<*> { 80 | val annotation = param.getAnnotation(ShaderIntRange::class.java) 81 | return when (annotation) { 82 | null -> throw UnsupportedDataTypeException("@ShaderProgram type parameter with '${param.asType()}' type must have @ShaderIntRange annotation") 83 | else -> { 84 | with (annotation) { 85 | if (from < 1) throw IllegalArgumentException("@ShaderIntRange: 'from' < 1") 86 | if (from > to) throw IllegalArgumentException("@ShaderIntRange: 'from' > 'to'") 87 | if (to - from > 63) throw IllegalArgumentException("@ShaderIntRange: provided range is too big! Maximum supported is 64 elements") 88 | IntParameterParams(from, to).values() 89 | } 90 | } 91 | } 92 | } 93 | 94 | private fun getBranches(parameters: List): List { 95 | val result = mutableListOf() 96 | collectBranches(parameters, emptyMap(), result) 97 | return result 98 | } 99 | 100 | private fun collectBranches(parameters: List, paramValues: Map, result: MutableCollection) { 101 | if (parameters.isEmpty()) { 102 | val sources = getShaderSources(paramValues) 103 | result.add(Branch(paramValues.entries.joinToString(" and ") { "(${it.key} == ${it.value})" }, result.size, sources.first, sources.second)) 104 | } else { 105 | val parameter = parameters.first() 106 | parameter.possibleValues.forEach { value -> 107 | collectBranches(parameters.drop(1), paramValues + (parameter.name to value!!), result) 108 | } 109 | } 110 | } 111 | 112 | private fun getShaderSources(paramValues: Map): Pair { 113 | val classLoader = javaClass.classLoader 114 | val vertexClass = classLoader.loadClass(vertex.qualifiedName.toString()) 115 | val fragmentClass = classLoader.loadClass(fragment.qualifiedName.toString()) 116 | val vertexSource = prepareShaderSource(vertex, vertexClass, paramValues) 117 | val fragmentSource = prepareShaderSource(fragment, fragmentClass, paramValues) 118 | return Pair(vertexSource, fragmentSource) 119 | } 120 | 121 | private fun prepareShaderSource(typeElement: TypeElement, shaderClass: Class<*>, paramValues: Map): String { 122 | val element = getTypeConstructor(typeElement) 123 | val programConstructor = getTypeConstructor(programType) 124 | val shaderClassName = typeElement.qualifiedName.toString() 125 | 126 | checkProgramHasShaderParams(programConstructor, element, shaderClassName) 127 | 128 | if (shaderClass.constructors.isEmpty()) { 129 | throw IllegalAccessException("Class '$shaderClassName' doesn't have constructor") 130 | } 131 | val constructor = shaderClass.constructors[0] 132 | val params = element.parameters.map { paramValues.getValue(it.simpleName.toString()) }.toTypedArray() 133 | val instance = constructor.newInstance(*params) 134 | val provider = instance as ShaderSourceProvider 135 | return provider.getSource() 136 | } 137 | 138 | private fun checkProgramHasShaderParams(programConstructor: ExecutableElement, shaderConstructor: ExecutableElement, shaderClassName: String) { 139 | shaderConstructor.parameters.forEach { shaderParam -> 140 | val shaderParamName = shaderParam.simpleName.toString() 141 | val programParam = programConstructor.parameters.find { it.simpleName.toString() == shaderParamName } 142 | ?: throw IllegalArgumentException("Shader program '$sourceClassName' doesn't have parameter '$shaderParamName' provided in '$shaderClassName'") 143 | if (programParam.asType().toString() != shaderParam.asType().toString()) { 144 | throw IllegalArgumentException("Shader program '$sourceClassName' and '$shaderClassName' have different types of '$shaderParamName'") 145 | } 146 | } 147 | } 148 | 149 | private fun asTypeElement(typeMirror: TypeMirror) = env.typeUtils.asElement(typeMirror) as TypeElement 150 | 151 | private fun getAnnotationValue(annotation: T, processor: (T) -> Unit): TypeElement { 152 | try { 153 | processor(annotation) 154 | } catch (e: MirroredTypeException) { 155 | return asTypeElement(e.typeMirror) 156 | } 157 | throw Error() 158 | } 159 | 160 | private fun TypeElement.getPackageName(): String { 161 | if (qualifiedName.contains(".")) { 162 | return qualifiedName.substring(0, qualifiedName.lastIndexOf('.')) 163 | } 164 | return "" 165 | } 166 | 167 | private interface ParameterValues { 168 | fun values(): Array<*> 169 | } 170 | 171 | private class IntParameterParams(val from: Int, val to: Int): ParameterValues { 172 | override fun values(): Array { 173 | return Array(to - from + 1) { i -> 174 | i + from 175 | } 176 | } 177 | } 178 | private class BooleanParams: ParameterValues { 179 | override fun values(): Array { 180 | return arrayOf(false, true) 181 | } 182 | } 183 | 184 | internal class Branch(val params: String, val index: Int, val vertex: String, val fragment: String) 185 | private class Parameter(val type: String, val name: String, val possibleValues: Array<*>) 186 | } 187 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/ShaderBuilderProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt 2 | 3 | import com.salakheev.shaderbuilderkt.annotations.ShaderProgram 4 | import com.x5.template.Theme 5 | import java.io.File 6 | import javax.annotation.processing.* 7 | import javax.lang.model.element.TypeElement 8 | import javax.lang.model.util.ElementFilter 9 | import javax.lang.model.SourceVersion 10 | 11 | /** 12 | * Searches for Classes annotated with [ShaderProgram] and generates sources named Sources 13 | * with the same package and module a source is from. 14 | * 15 | * During the generation process it will try to find constructor parameters of classes [vertex], [fragment] 16 | * and associate them with constructor parameters of an annotated Class. 17 | * 18 | * Classes [ShaderProgram.vertex] and [ShaderProgram.fragment] must be accessible 19 | * for [ShaderBuilderProcessor] to instantiate them, thus have to be either 20 | * stored along with it or in a dependent module. 21 | */ 22 | @SupportedAnnotationTypes("com.salakheev.shaderbuilderkt.annotations.ShaderProgram") 23 | @SupportedSourceVersion(SourceVersion.RELEASE_8) 24 | class ShaderBuilderProcessor : AbstractProcessor() { 25 | override fun process(annotations: MutableSet, env: RoundEnvironment): Boolean { 26 | val elements = env.getElementsAnnotatedWith(ShaderProgram::class.java) 27 | val shaderPrograms = ElementFilter.typesIn(elements) 28 | shaderPrograms.forEach { generateProgram(it) } 29 | return shaderPrograms.isEmpty() 30 | } 31 | 32 | private fun generateProgram(type: TypeElement) { 33 | val theme = Theme("/resources/themes", "") 34 | val chunk = theme.makeChunk("program") 35 | val processor = ProgramProcessor(processingEnv, type, chunk) 36 | writeSourceFile(processor.getPackageName(), processor.getClassName(), processor.getResult()) 37 | } 38 | 39 | private fun writeSourceFile(classPackage: String, className: String, text: String) { 40 | val filePath = processingEnv.options.getValue(KAPT_KOTLIN_GENERATED) 41 | File("$filePath/$classPackage", "$className.kt").apply { 42 | parentFile.mkdirs() 43 | writeText(text) 44 | } 45 | } 46 | 47 | private companion object { 48 | const val KAPT_KOTLIN_GENERATED = "kapt.kotlin.generated" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/annotations/ShaderIntRange.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.annotations 2 | 3 | /** 4 | * Annotation is used to limit the generation range of Int type parameters. 5 | * [ShaderIntRange] must be provided with all [Int] type parameters. 6 | * 7 | * As a result all values in [from]..[to] range will be used as parameter values 8 | * @Example: 9 | * class ShaderProgram(param0: Boolean, @ShaderIntRange(to = 2)param1: Int) 10 | */ 11 | @Target(AnnotationTarget.VALUE_PARAMETER) 12 | @Retention(AnnotationRetention.SOURCE) 13 | annotation class ShaderIntRange(val from: Int = 1, val to: Int = 1) 14 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/annotations/ShaderProgram.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.annotations 2 | 3 | import com.salakheev.shaderbuilderkt.sources.ShaderSourceProvider 4 | import kotlin.reflect.KClass 5 | 6 | /** 7 | * Annotation is used to automatically generate shader program sources 8 | * with all possible parameter values during gradle kaptKotlin task. 9 | * Class annotated with [ShaderProgram] must have one constructor 10 | * with all parameters provided in [vertex] and [fragment] constructors. 11 | * Only [Boolean] and [Int] (with [ShaderIntRange] annotation) are currently supported 12 | * as constructor parameter types. 13 | * 14 | * Example: 15 | * For a com.example.Program class annotated with ShaderProgram 16 | * a com.example.ProgramSources class will be created 17 | */ 18 | @Target(AnnotationTarget.CLASS) 19 | @Retention(AnnotationRetention.SOURCE) 20 | annotation class ShaderProgram(val vertex: KClass, val fragment: KClass) 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/ShaderBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder 2 | 3 | import com.salakheev.shaderbuilderkt.builder.InstructionType.* 4 | import com.salakheev.shaderbuilderkt.builder.types.BoolResult 5 | import com.salakheev.shaderbuilderkt.builder.types.GenType 6 | import com.salakheev.shaderbuilderkt.builder.types.Variable 7 | import com.salakheev.shaderbuilderkt.builder.types.mat.Mat3 8 | import com.salakheev.shaderbuilderkt.builder.types.mat.Mat4 9 | import com.salakheev.shaderbuilderkt.builder.types.sampler.Sampler2D 10 | import com.salakheev.shaderbuilderkt.builder.types.sampler.Sampler2DArray 11 | import com.salakheev.shaderbuilderkt.builder.types.sampler.ShadowTexture2D 12 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLFloat 13 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLInt 14 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec2 15 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec3 16 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec4 17 | import com.salakheev.shaderbuilderkt.builder.delegates.AttributeDelegate 18 | import com.salakheev.shaderbuilderkt.builder.delegates.BuiltinVarDelegate 19 | import com.salakheev.shaderbuilderkt.builder.delegates.ConstructorDelegate 20 | import com.salakheev.shaderbuilderkt.builder.delegates.UniformArrayDelegate 21 | import com.salakheev.shaderbuilderkt.builder.delegates.UniformDelegate 22 | import com.salakheev.shaderbuilderkt.builder.delegates.VaryingDelegate 23 | import com.salakheev.shaderbuilderkt.sources.ShaderSourceProvider 24 | import java.util.* 25 | 26 | enum class InstructionType { 27 | DEFINE, 28 | ASSIGN, 29 | IF, 30 | ELSEIF, 31 | ELSE, 32 | ENDIF, 33 | DISCARD 34 | } 35 | 36 | data class Instruction(val type: InstructionType, var result: String = "") { 37 | companion object { 38 | fun assign(left: String?, right: String?): Instruction { 39 | return Instruction(ASSIGN, "$left = $right") 40 | } 41 | } 42 | } 43 | 44 | @Suppress("PropertyName", "FunctionName", "unused") 45 | abstract class ShaderBuilder : ShaderSourceProvider { 46 | val uniforms = HashSet() 47 | val attributes = HashSet() 48 | val varyings = HashSet() 49 | val instructions = ArrayList() 50 | 51 | protected var gl_Position by BuiltinVarDelegate() 52 | protected var gl_FragCoord by BuiltinVarDelegate() 53 | protected var gl_FragColor by BuiltinVarDelegate() 54 | 55 | override fun getSource(): String { 56 | removeUnusedDefinitions() 57 | 58 | val sb = StringBuilder() 59 | uniforms.forEach { 60 | sb.appendln("uniform $it;") 61 | } 62 | attributes.forEach { 63 | sb.appendln("attribute $it;") 64 | } 65 | varyings.forEach { 66 | sb.appendln("\nvarying $it;") 67 | } 68 | 69 | sb.appendln("void main(void) {") 70 | instructions.forEach { 71 | val instructionString = when (it.type) { 72 | DEFINE, ASSIGN -> "${it.result};" 73 | IF -> { 74 | "if (${it.result}) {" 75 | } 76 | ELSEIF -> { 77 | "else if (${it.result}) {" 78 | } 79 | ELSE -> { 80 | "else {" 81 | } 82 | ENDIF -> "}" 83 | DISCARD -> "discard;" 84 | } 85 | sb.appendln(instructionString) 86 | } 87 | sb.appendln("}") 88 | return sb.toString() 89 | } 90 | 91 | fun appendComponent(builder: ShaderBuilderComponent) { 92 | uniforms.addAll(builder.uniforms) 93 | attributes.addAll(builder.attributes) 94 | varyings.addAll(builder.varyings) 95 | instructions.addAll(builder.instructions) 96 | } 97 | 98 | private fun removeUnusedDefinitions() { 99 | instructions.removeAll { it.result.contains("{def}") } 100 | } 101 | 102 | protected fun varying(factory: (ShaderBuilder) -> T) = VaryingDelegate(factory) 103 | protected fun attribute(factory: (ShaderBuilder) -> T) = AttributeDelegate(factory) 104 | protected fun uniform(factory: (ShaderBuilder) -> T) = UniformDelegate(factory) 105 | protected fun uniformArray(size: Int, init: (builder: ShaderBuilder) -> T) = UniformArrayDelegate(size, init) 106 | protected fun samplersArray(size: Int) = UniformArrayDelegate(size, ::Sampler2DArray) 107 | 108 | protected fun discard() = instructions.add(Instruction(DISCARD)) 109 | 110 | protected fun If(condition: BoolResult, body: () -> Unit) { 111 | instructions.add(Instruction(IF, condition.value)) 112 | body() 113 | instructions.add(Instruction(ENDIF)) 114 | } 115 | 116 | protected fun ElseIf(condition: BoolResult, body: () -> Unit) { 117 | instructions.add(Instruction(ELSEIF, condition.value)) 118 | body() 119 | instructions.add(Instruction(ENDIF)) 120 | } 121 | 122 | protected fun Else(body: () -> Unit) { 123 | instructions.add(Instruction(ELSE)) 124 | body() 125 | instructions.add(Instruction(ENDIF)) 126 | } 127 | 128 | protected fun castMat3(m: Mat4) = Mat3(this, "mat3(${m.value})") 129 | protected fun int(v: GLFloat) = GLInt(this, "int(${v.value})") 130 | 131 | protected fun radians(v: GLFloat) = GLFloat(this, "radians(${v.value})") 132 | protected fun radians(v: Vec2) = Vec2(this, "radians(${v.value})") 133 | protected fun radians(v: Vec3) = Vec3(this, "radians(${v.value})") 134 | protected fun radians(v: Vec4) = Vec4(this, "radians(${v.value})") 135 | 136 | protected fun degrees(v: GLFloat) = GLFloat(this, "degrees(${v.value})") 137 | protected fun degrees(v: Vec2) = Vec2(this, "degrees(${v.value})") 138 | protected fun degrees(v: Vec3) = Vec3(this, "degrees(${v.value})") 139 | protected fun degrees(v: Vec4) = Vec4(this, "degrees(${v.value})") 140 | 141 | protected fun sin(v: GLFloat) = GLFloat(this, "sin(${v.value})") 142 | protected fun sin(v: Vec2) = Vec2(this, "sin(${v.value})") 143 | protected fun sin(v: Vec3) = Vec3(this, "sin(${v.value})") 144 | protected fun sin(v: Vec4) = Vec4(this, "sin(${v.value})") 145 | 146 | protected fun cos(v: GLFloat) = GLFloat(this, "cos(${v.value})") 147 | protected fun cos(v: Vec2) = Vec2(this, "cos(${v.value})") 148 | protected fun cos(v: Vec3) = Vec3(this, "cos(${v.value})") 149 | protected fun cos(v: Vec4) = Vec4(this, "cos(${v.value})") 150 | 151 | protected fun tan(v: GLFloat) = GLFloat(this, "tan(${v.value})") 152 | protected fun tan(v: Vec2) = Vec2(this, "tan(${v.value})") 153 | protected fun tan(v: Vec3) = Vec3(this, "tan(${v.value})") 154 | protected fun tan(v: Vec4) = Vec4(this, "tan(${v.value})") 155 | 156 | protected fun acos(v: GLFloat) = GLFloat(this, "acos(${v.value})") 157 | protected fun acos(v: Vec2) = Vec2(this, "acos(${v.value})") 158 | protected fun acos(v: Vec3) = Vec3(this, "acos(${v.value})") 159 | protected fun acos(v: Vec4) = Vec4(this, "acos(${v.value})") 160 | 161 | protected fun atan(v: GLFloat) = GLFloat(this, "atan(${v.value})") 162 | protected fun atan(v: Vec2) = Vec2(this, "atan(${v.value})") 163 | protected fun atan(v: Vec3) = Vec3(this, "atan(${v.value})") 164 | protected fun atan(v: Vec4) = Vec4(this, "atan(${v.value})") 165 | 166 | protected fun exp(v: GLFloat) = GLFloat(this, "exp(${v.value})") 167 | protected fun exp(v: Vec2) = Vec2(this, "exp(${v.value})") 168 | protected fun exp(v: Vec3) = Vec3(this, "exp(${v.value})") 169 | protected fun exp(v: Vec4) = Vec4(this, "exp(${v.value})") 170 | 171 | protected fun log(v: GLFloat) = GLFloat(this, "log(${v.value})") 172 | protected fun log(v: Vec2) = Vec2(this, "log(${v.value})") 173 | protected fun log(v: Vec3) = Vec3(this, "log(${v.value})") 174 | protected fun log(v: Vec4) = Vec4(this, "log(${v.value})") 175 | 176 | protected fun exp2(v: GLFloat) = GLFloat(this, "exp2(${v.value})") 177 | protected fun exp2(v: Vec2) = Vec2(this, "exp2(${v.value})") 178 | protected fun exp2(v: Vec3) = Vec3(this, "exp2(${v.value})") 179 | protected fun exp2(v: Vec4) = Vec4(this, "exp2(${v.value})") 180 | 181 | protected fun log2(v: GLFloat) = GLFloat(this, "log2(${v.value})") 182 | protected fun log2(v: Vec2) = Vec2(this, "log2(${v.value})") 183 | protected fun log2(v: Vec3) = Vec3(this, "log2(${v.value})") 184 | protected fun log2(v: Vec4) = Vec4(this, "log2(${v.value})") 185 | 186 | protected fun sqrt(v: GLFloat) = GLFloat(this, "sqrt(${v.value})") 187 | protected fun sqrt(v: Vec2) = Vec2(this, "sqrt(${v.value})") 188 | protected fun sqrt(v: Vec3) = Vec3(this, "sqrt(${v.value})") 189 | protected fun sqrt(v: Vec4) = Vec4(this, "sqrt(${v.value})") 190 | 191 | protected fun inversesqrt(v: GLFloat) = GLFloat(this, "inversesqrt(${v.value})") 192 | protected fun inversesqrt(v: Vec2) = Vec2(this, "inversesqrt(${v.value})") 193 | protected fun inversesqrt(v: Vec3) = Vec3(this, "inversesqrt(${v.value})") 194 | protected fun inversesqrt(v: Vec4) = Vec4(this, "inversesqrt(${v.value})") 195 | 196 | protected fun abs(v: GLFloat) = GLFloat(this, "abs(${v.value})") 197 | protected fun abs(v: Vec2) = Vec2(this, "abs(${v.value})") 198 | protected fun abs(v: Vec3) = Vec3(this, "abs(${v.value})") 199 | protected fun abs(v: Vec4) = Vec4(this, "abs(${v.value})") 200 | 201 | protected fun sign(v: GLFloat) = GLFloat(this, "sign(${v.value})") 202 | protected fun sign(v: Vec2) = Vec2(this, "sign(${v.value})") 203 | protected fun sign(v: Vec3) = Vec3(this, "sign(${v.value})") 204 | protected fun sign(v: Vec4) = Vec4(this, "sign(${v.value})") 205 | 206 | protected fun floor(v: GLFloat) = GLFloat(this, "floor(${v.value})") 207 | protected fun floor(v: Vec2) = Vec2(this, "floor(${v.value})") 208 | protected fun floor(v: Vec3) = Vec3(this, "floor(${v.value})") 209 | protected fun floor(v: Vec4) = Vec4(this, "floor(${v.value})") 210 | 211 | protected fun ceil(v: GLFloat) = GLFloat(this, "ceil(${v.value})") 212 | protected fun ceil(v: Vec2) = Vec2(this, "ceil(${v.value})") 213 | protected fun ceil(v: Vec3) = Vec3(this, "ceil(${v.value})") 214 | protected fun ceil(v: Vec4) = Vec4(this, "ceil(${v.value})") 215 | 216 | protected fun fract(v: GLFloat) = GLFloat(this, "fract(${v.value})") 217 | protected fun fract(v: Vec2) = Vec2(this, "fract(${v.value})") 218 | protected fun fract(v: Vec3) = Vec3(this, "fract(${v.value})") 219 | protected fun fract(v: Vec4) = Vec4(this, "fract(${v.value})") 220 | 221 | protected fun mod(v: GLFloat, base: GLFloat) = GLFloat(this, "mod(${v.value}, ${base.value})") 222 | protected fun mod(v: Vec2, base: Vec2) = Vec2(this, "mod(${v.value}, ${base.value})") 223 | protected fun mod(v: Vec3, base: Vec3) = Vec3(this, "mod(${v.value}, ${base.value})") 224 | protected fun mod(v: Vec4, base: Vec4) = Vec4(this, "mod(${v.value}, ${base.value})") 225 | protected fun mod(v: Vec2, base: GLFloat) = Vec2(this, "mod(${v.value}, ${base.value})") 226 | protected fun mod(v: Vec3, base: GLFloat) = Vec3(this, "mod(${v.value}, ${base.value})") 227 | protected fun mod(v: Vec4, base: GLFloat) = Vec4(this, "mod(${v.value}, ${base.value})") 228 | 229 | protected fun min(v: GLFloat, base: GLFloat) = GLFloat(this, "min(${v.value}, ${base.value})") 230 | protected fun min(v: Vec2, base: Vec2) = Vec2(this, "min(${v.value}, ${base.value})") 231 | protected fun min(v: Vec3, base: Vec3) = Vec3(this, "min(${v.value}, ${base.value})") 232 | protected fun min(v: Vec4, base: Vec4) = Vec4(this, "min(${v.value}, ${base.value})") 233 | protected fun min(v: Vec2, base: GLFloat) = Vec2(this, "min(${v.value}, ${base.value})") 234 | protected fun min(v: Vec3, base: GLFloat) = Vec3(this, "min(${v.value}, ${base.value})") 235 | protected fun min(v: Vec4, base: GLFloat) = Vec4(this, "min(${v.value}, ${base.value})") 236 | 237 | protected fun max(v: GLFloat, v2: Float) = GLFloat(this, "max(${v.value}, ${v2.str()})") 238 | protected fun max(v: GLFloat, base: GLFloat) = GLFloat(this, "max(${v.value}, ${base.value})") 239 | protected fun max(v: Vec2, base: Vec2) = Vec2(this, "max(${v.value}, ${base.value})") 240 | protected fun max(v: Vec3, base: Vec3) = Vec3(this, "max(${v.value}, ${base.value})") 241 | protected fun max(v: Vec4, base: Vec4) = Vec4(this, "max(${v.value}, ${base.value})") 242 | protected fun max(v: Vec2, base: GLFloat) = Vec2(this, "max(${v.value}, ${base.value})") 243 | protected fun max(v: Vec3, base: GLFloat) = Vec3(this, "max(${v.value}, ${base.value})") 244 | protected fun max(v: Vec4, base: GLFloat) = Vec4(this, "max(${v.value}, ${base.value})") 245 | 246 | protected fun clamp(v: GLFloat, min: GLFloat, max: GLFloat) = GLFloat(this, "clamp(${v.value}, ${min.value}, ${max.value})") 247 | protected fun clamp(v: Vec2, min: Vec2, max: Vec2) = Vec2(this, "clamp(${v.value}, ${min.value}, ${max.value})") 248 | protected fun clamp(v: Vec3, min: Vec3, max: Vec3) = Vec3(this, "clamp(${v.value}, ${min.value}, ${max.value})") 249 | protected fun clamp(v: Vec4, min: Vec4, max: Vec4) = Vec4(this, "clamp(${v.value}, ${min.value}, ${max.value})") 250 | protected fun clamp(v: Vec2, min: GLFloat, max: GLFloat) = Vec2(this, "clamp(${v.value}, ${min.value}, ${max.value})") 251 | protected fun clamp(v: Vec3, min: GLFloat, max: GLFloat) = Vec3(this, "clamp(${v.value}, ${min.value}, ${max.value})") 252 | protected fun clamp(v: Vec4, min: GLFloat, max: GLFloat) = Vec4(this, "clamp(${v.value}, ${min.value}, ${max.value})") 253 | 254 | protected fun clamp(v: GLFloat, min: Float, max: Float) = GLFloat(this, "clamp(${v.value}, ${min.str()}, ${max.str()})") 255 | protected fun clamp(v: GLFloat, min: GLFloat, max: Float) = GLFloat(this, "clamp(${v.value}, ${min.value}, ${max.str()})") 256 | protected fun clamp(v: Vec2, min: Float, max: Float) = Vec2(this, "clamp(${v.value}, ${min.str()}, ${max.str()})") 257 | protected fun clamp(v: Vec3, min: Float, max: Float) = Vec3(this, "clamp(${v.value}, ${min.str()}, ${max.str()})") 258 | protected fun clamp(v: Vec4, min: Float, max: Float) = Vec4(this, "clamp(${v.value}, ${min.str()}, ${max.str()})") 259 | 260 | protected fun mix(v: GLFloat, y: GLFloat, a: GLFloat) = GLFloat(this, "mix(${v.value}, ${y.value}, ${a.value})") 261 | protected fun mix(v: Vec2, y: Vec2, a: Vec2) = Vec2(this, "mix(${v.value}, ${y.value}, ${a.value})") 262 | protected fun mix(v: Vec3, y: Vec3, a: Vec3) = Vec3(this, "mix(${v.value}, ${y.value}, ${a.value})") 263 | protected fun mix(v: Vec4, y: Vec4, a: Vec4) = Vec4(this, "mix(${v.value}, ${y.value}, ${a.value})") 264 | protected fun mix(v: Vec2, y: Vec2, a: GLFloat) = Vec2(this, "mix(${v.value}, ${y.value}, ${a.value})") 265 | protected fun mix(v: Vec3, y: Vec3, a: GLFloat) = Vec3(this, "mix(${v.value}, ${y.value}, ${a.value})") 266 | protected fun mix(v: Vec4, y: Vec4, a: GLFloat) = Vec4(this, "mix(${v.value}, ${y.value}, ${a.value})") 267 | 268 | protected fun step(v: GLFloat, x: GLFloat) = GLFloat(this, "step(${v.value}, ${x.value})") 269 | protected fun step(v: Vec2, x: Vec2) = Vec2(this, "step(${v.value}, ${x.value})") 270 | protected fun step(v: Vec3, x: Vec3) = Vec3(this, "step(${v.value}, ${x.value})") 271 | protected fun step(v: Vec4, x: Vec4) = Vec4(this, "step(${v.value}, ${x.value})") 272 | protected fun step(v: Vec2, x: GLFloat) = Vec2(this, "step(${v.value}, ${x.value})") 273 | protected fun step(v: Vec3, x: GLFloat) = Vec3(this, "step(${v.value}, ${x.value})") 274 | protected fun step(v: Vec4, x: GLFloat) = Vec4(this, "step(${v.value}, ${x.value})") 275 | 276 | protected fun pow(v: GLFloat, x: Float) = GLFloat(this, "pow(${v.value}, ${x.str()})") 277 | 278 | protected fun smoothstep(v: GLFloat, u: GLFloat, x: GLFloat) = GLFloat(this, "smoothstep(${v.value}, ${u.value}, ${x.value})") 279 | protected fun smoothstep(v: Vec2, u: Vec2, x: Vec2) = Vec2(this, "smoothstep(${v.value}, ${u.value}, ${x.value})") 280 | protected fun smoothstep(v: Vec3, u: Vec3, x: Vec3) = Vec3(this, "smoothstep(${v.value}, ${u.value}, ${x.value})") 281 | protected fun smoothstep(v: Vec4, u: Vec4, x: Vec4) = Vec4(this, "smoothstep(${v.value}, ${u.value}, ${x.value})") 282 | 283 | protected fun length(v: GenType) = GLFloat(this, "length(${v.value})") 284 | protected fun distance(a: GenType, b: GenType) = GLFloat(this, "distance(${a.value}, ${b.value})") 285 | protected fun dot(a: GenType, b: GenType) = GLFloat(this, "dot(${a.value}, ${b.value})") 286 | protected fun cross(a: Vec3, b: Vec3) = Vec3(this, "dot(${a.value}, ${b.value})") 287 | protected fun normalize(v: GLFloat) = GLFloat(this, "normalize(${v.value})") 288 | protected fun normalize(v: Vec3) = Vec3(this, "normalize(${v.value})") 289 | protected fun normalize(v: Vec4) = Vec4(this, "normalize(${v.value})") 290 | protected fun reflect(i: GenType, n: GenType) = Vec3(this, "reflect(${i.value}, ${n.value})") 291 | protected fun refract(i: GenType, n: GenType, eta: GLFloat) = Vec3(this, "refract(${i.value}, ${n.value}, ${eta.value})") 292 | protected fun shadow2D(sampler: ShadowTexture2D, v: Vec2) = Vec4(this, "shadow2D(${sampler.value}, ${v.value})") 293 | protected fun texture2D(sampler: Sampler2D, v: Vec2) = Vec4(this, "texture2D(${sampler.value}, ${v.value})") 294 | 295 | protected fun float() = ConstructorDelegate(GLFloat(this)) 296 | protected fun float(x: Float) = ConstructorDelegate(GLFloat(this), x.str()) 297 | protected fun float(x: GLFloat) = ConstructorDelegate(GLFloat(this), x.value) 298 | 299 | protected fun intVal() = ConstructorDelegate(GLInt(this)) 300 | protected fun intVal(x: GLInt) = ConstructorDelegate(GLInt(this), x.value) 301 | protected fun intVal(x: Int) = ConstructorDelegate(GLInt(this), "$x") 302 | 303 | protected fun vec2() = ConstructorDelegate(Vec2(this)) 304 | protected fun vec2(x: Vec2) = ConstructorDelegate(Vec2(this), "${x.value}") 305 | protected fun vec2(x: Float, y: Float) = ConstructorDelegate(Vec2(this), "vec2(${x.str()}, ${y.str()})") 306 | protected fun vec2(x: GLFloat, y: Float) = ConstructorDelegate(Vec2(this), "vec2(${x.value}, ${y.str()})") 307 | protected fun vec2(x: Float, y: GLFloat) = ConstructorDelegate(Vec2(this), "vec2(${x.str()}, ${y.value})") 308 | protected fun vec2(x: GLFloat, y: GLFloat) = ConstructorDelegate(Vec2(this), "vec2(${x.value}, ${y.value})") 309 | 310 | protected fun vec3() = ConstructorDelegate(Vec3(this)) 311 | protected fun vec3(x: GLFloat, y: GLFloat, z: GLFloat) = ConstructorDelegate(Vec3(this), ("vec3(${x.value}, ${y.value}, ${z.value})")) 312 | protected fun vec3(x: GLFloat, y: GLFloat, z: Float) = ConstructorDelegate(Vec3(this), ("vec3(${x.value}, ${y.value}, ${z.str()})")) 313 | protected fun vec3(x: GLFloat, y: Float, z: GLFloat) = ConstructorDelegate(Vec3(this), ("vec3(${x.value}, ${y.str()}, ${z.value})")) 314 | protected fun vec3(x: GLFloat, y: Float, z: Float) = ConstructorDelegate(Vec3(this), ("vec3(${x.value}, ${y.str()}, ${z.str()})")) 315 | protected fun vec3(x: Float, y: GLFloat, z: GLFloat) = ConstructorDelegate(Vec3(this), ("vec3(${x.str()}, ${y.value}, ${z.value})")) 316 | protected fun vec3(x: Float, y: GLFloat, z: Float) = ConstructorDelegate(Vec3(this), ("vec3(${x.str()}, ${y.value}, ${z.str()})")) 317 | protected fun vec3(x: Float, y: Float, z: GLFloat) = ConstructorDelegate(Vec3(this), ("vec3(${x.str()}, ${y.str()}, ${z.value})")) 318 | protected fun vec3(x: Float, y: Float, z: Float) = ConstructorDelegate(Vec3(this), ("vec3(${x.str()}, ${y.str()}, ${z.str()})")) 319 | 320 | protected fun vec3(v2: Vec2, z: Float) = ConstructorDelegate(Vec3(this), ("vec3(${v2.value}, ${z.str()})")) 321 | protected fun vec3(v2: Vec2, z: GLFloat) = ConstructorDelegate(Vec3(this), ("vec3(${v2.value}, ${z.value})")) 322 | protected fun vec3(x: Float, v2: Vec2) = ConstructorDelegate(Vec3(this), ("vec3(${x.str()}, ${v2.value})")) 323 | protected fun vec3(x: GLFloat, v2: Vec2) = ConstructorDelegate(Vec3(this), ("vec3(${x.value}, ${v2.value})")) 324 | 325 | protected fun vec4() = ConstructorDelegate(Vec4(this)) 326 | protected fun vec4(vec3: Vec3, w: Float) = ConstructorDelegate(Vec4(this), ("vec4(${vec3.value}, ${w.str()})")) 327 | protected fun vec4(vec3: Vec3, w: GLFloat) = ConstructorDelegate(Vec4(this), ("vec4(${vec3.value}, ${w.value})")) 328 | protected fun vec4(vec2: Vec2, z: Float, w: Float) = ConstructorDelegate(Vec4(this), ("vec4(${vec2.value}, ${z.str()}, ${w.str()})")) 329 | protected fun vec4(x: GLFloat, y: GLFloat, zw: Vec2) = ConstructorDelegate(Vec4(this), ("vec4(${x.value}, ${y.value}, ${zw.value})")) 330 | 331 | protected fun vec4(x: Float, y: Float, z: Float, w: Float) = ConstructorDelegate(Vec4(this), ("vec4(${x.str()}, ${y.str()}, ${z.str()}, ${w.str()})")) 332 | protected fun vec4(x: Float, y: Float, z: Float, w: GLFloat) = ConstructorDelegate(Vec4(this), ("vec4(${x.str()}, ${y.str()}, ${z.str()}, ${w.value})")) 333 | protected fun vec4(x: GLFloat, y: GLFloat, z: GLFloat, w: GLFloat) = ConstructorDelegate(Vec4(this), ("vec4(${x.value}, ${y.value}, ${z.value}, ${w.value})")) 334 | protected fun vec4(x: GLFloat, y: GLFloat, z: GLFloat, w: Float) = ConstructorDelegate(Vec4(this), ("vec4(${x.value}, ${y.value}, ${z.value}, ${w.str()})")) 335 | protected fun vec4(x: GLFloat, y: GLFloat, z: Float, w: Float) = ConstructorDelegate(Vec4(this), ("vec4(${x.value}, ${y.value}, ${z.str()}, ${w.str()})")) 336 | 337 | protected fun mat3() = ConstructorDelegate(Mat3(this)) 338 | 339 | operator fun Float.minus(a: GLFloat) = GLFloat(a.builder, "(${this.str()} - ${a.value})") 340 | operator fun Float.plus(a: GLFloat) = GLFloat(a.builder, "(${this.str()} + ${a.value})") 341 | } 342 | 343 | fun Float.str(): String { 344 | val r = "$this" 345 | return if (r.contains(".")) r else "$r.0" 346 | } 347 | 348 | abstract class ShaderBuilderComponent : ShaderBuilder() 349 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/AttributeDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.Variable 5 | import kotlin.reflect.KProperty 6 | 7 | class AttributeDelegate(private val factory: (ShaderBuilder) -> T) { 8 | private lateinit var v: T 9 | 10 | operator fun provideDelegate(thisRef: ShaderBuilder, 11 | property: KProperty<*>): AttributeDelegate { 12 | v = factory(thisRef) 13 | v.value = property.name 14 | return this 15 | } 16 | 17 | operator fun getValue(thisRef: ShaderBuilder, property: KProperty<*>): T { 18 | thisRef.attributes.add("${v.typeName} ${property.name}") 19 | return v 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/BuiltinVarDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.Instruction 4 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 5 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec4 6 | import kotlin.reflect.KProperty 7 | 8 | class BuiltinVarDelegate { 9 | private lateinit var v: Vec4 10 | 11 | operator fun provideDelegate(thisRef: ShaderBuilder, 12 | property: KProperty<*>): BuiltinVarDelegate { 13 | v = Vec4(thisRef, property.name) 14 | return this 15 | } 16 | 17 | operator fun getValue(thisRef: ShaderBuilder, property: KProperty<*>): Vec4 { 18 | return v 19 | } 20 | 21 | operator fun setValue(thisRef: ShaderBuilder, property: KProperty<*>, value: Vec4) { 22 | thisRef.instructions.add(Instruction.assign(property.name, value.value)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/ComponentDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.Instruction 4 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 5 | import com.salakheev.shaderbuilderkt.builder.types.Variable 6 | import kotlin.reflect.KProperty 7 | 8 | class ComponentDelegate(private val factory: (ShaderBuilder) -> T) { 9 | private lateinit var v: T 10 | 11 | operator fun provideDelegate(thisRef: Variable, property: KProperty<*>): ComponentDelegate { 12 | v = factory(thisRef.builder) 13 | return this 14 | } 15 | 16 | operator fun getValue(thisRef: Variable, property: KProperty<*>): T { 17 | if (v.value == null) { 18 | v.value = "${thisRef.value}.${property.name}" 19 | } 20 | return v 21 | } 22 | 23 | operator fun setValue(thisRef: Variable, property: KProperty<*>, value: T) { 24 | if (v.value == null) { 25 | v.value = "${thisRef.value}.${property.name}" 26 | } 27 | thisRef.builder.instructions.add(Instruction.assign(v.value, value.value)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/ConstructorDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.Instruction 4 | import com.salakheev.shaderbuilderkt.builder.InstructionType 5 | import com.salakheev.shaderbuilderkt.builder.types.Variable 6 | import kotlin.reflect.KProperty 7 | 8 | class ConstructorDelegate(private val v: T, initialValue: String? = null) { 9 | private var define: Instruction 10 | private var defined: Boolean = false 11 | 12 | init { 13 | val definitionString = "${v.typeName} {def} ${getInitializerExpr(initialValue)}" 14 | define = Instruction(InstructionType.DEFINE, definitionString) 15 | v.builder.instructions.add(define) 16 | } 17 | 18 | operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ConstructorDelegate { 19 | v.value = property.name 20 | return this 21 | } 22 | 23 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T { 24 | if (!defined) { 25 | define.result = define.result.replace("{def}", property.name) 26 | defined = true 27 | } 28 | return v 29 | } 30 | 31 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { 32 | if (!defined) { 33 | define.result = define.result.replace("{def}", property.name) 34 | defined = true 35 | } 36 | v.builder.instructions.add(Instruction.assign(property.name, value.value)) 37 | } 38 | 39 | private fun getInitializerExpr(initialValue: String?): String { 40 | return if (initialValue == null) "" else " = $initialValue" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/UniformArrayDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.Variable 5 | import kotlin.reflect.KProperty 6 | 7 | class UniformArrayDelegate( 8 | val size: Int, 9 | private val factory: (builder: ShaderBuilder) -> T 10 | ) { 11 | private lateinit var v: T 12 | 13 | operator fun provideDelegate(thisRef: ShaderBuilder, 14 | property: KProperty<*>): UniformArrayDelegate { 15 | v = factory(thisRef) 16 | v.value = property.name 17 | return this 18 | } 19 | 20 | operator fun getValue(thisRef: ShaderBuilder, property: KProperty<*>): T { 21 | thisRef.uniforms.add("${v.typeName} ${property.name}[$size]") 22 | return v 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/UniformDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.Variable 5 | import kotlin.reflect.KProperty 6 | 7 | class UniformDelegate(private val factory: (ShaderBuilder) -> T) { 8 | private lateinit var v: T 9 | 10 | operator fun provideDelegate(thisRef: ShaderBuilder, 11 | property: KProperty<*>): UniformDelegate { 12 | v = factory(thisRef) 13 | v.value = property.name 14 | return this 15 | } 16 | 17 | operator fun getValue(thisRef: ShaderBuilder, property: KProperty<*>): T { 18 | thisRef.uniforms.add("${v.typeName} ${property.name}") 19 | return v 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/delegates/VaryingDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.delegates 2 | 3 | import com.salakheev.shaderbuilderkt.builder.Instruction 4 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 5 | import com.salakheev.shaderbuilderkt.builder.types.Variable 6 | import kotlin.reflect.KProperty 7 | 8 | class VaryingDelegate(private val factory: (ShaderBuilder) -> T) { 9 | 10 | private lateinit var v: T 11 | 12 | operator fun provideDelegate(thisRef: ShaderBuilder, 13 | property: KProperty<*>): VaryingDelegate { 14 | v = factory(thisRef) 15 | v.value = property.name 16 | return this 17 | } 18 | 19 | operator fun getValue(thisRef: ShaderBuilder, property: KProperty<*>): T { 20 | thisRef.varyings.add("${v.typeName} ${property.name}") 21 | return v 22 | } 23 | 24 | operator fun setValue(thisRef: ShaderBuilder, property: KProperty<*>, value: T) { 25 | thisRef.varyings.add("${v.typeName} ${property.name}") 26 | thisRef.instructions.add(Instruction.assign(property.name, value.value)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/BoolResult.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types 2 | 3 | data class BoolResult(val value: String) { 4 | infix fun or(a: BoolResult): BoolResult = BoolResult("(${this.value} || ${a.value})") 5 | infix fun and(a: BoolResult): BoolResult = BoolResult("(${this.value} && ${a.value})") 6 | } 7 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/Variable.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | 5 | interface Variable { 6 | val builder: ShaderBuilder 7 | val typeName: String 8 | var value: String? 9 | } 10 | 11 | interface GenType : Variable 12 | interface Vector : GenType 13 | interface Matrix : Variable 14 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/mat/MatrixColumnDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.mat 2 | 3 | import com.salakheev.shaderbuilderkt.builder.Instruction 4 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 5 | import com.salakheev.shaderbuilderkt.builder.types.Variable 6 | import kotlin.reflect.KProperty 7 | 8 | class MatrixColumnDelegate( 9 | private val index: Int, 10 | private val factory: (builder: ShaderBuilder) -> T 11 | ) { 12 | private lateinit var v: T 13 | 14 | operator fun provideDelegate(thisRef: Variable, 15 | property: KProperty<*>): MatrixColumnDelegate { 16 | v = factory(thisRef.builder) 17 | return this 18 | } 19 | 20 | operator fun getValue(thisRef: Variable, property: KProperty<*>): T { 21 | if (v.value == null) { 22 | v.value = "${thisRef.value}[$index]" 23 | } 24 | return v 25 | } 26 | 27 | operator fun setValue(thisRef: Variable, property: KProperty<*>, value: T) { 28 | if (v.value == null) { 29 | v.value = "${thisRef.value}[$index]" 30 | } 31 | thisRef.builder.instructions.add(Instruction.assign(v.value, value.value)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/mat/mat2.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.mat 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.str 5 | import com.salakheev.shaderbuilderkt.builder.types.Matrix 6 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec2 7 | 8 | class Mat2(override val builder: ShaderBuilder) : Matrix { 9 | 10 | override val typeName: String = "mat2" 11 | override var value: String? = null 12 | 13 | private var column1 by MatrixColumnDelegate(0, ::Vec2) 14 | private var column2 by MatrixColumnDelegate(1, ::Vec2) 15 | 16 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 17 | this.value = value 18 | } 19 | 20 | operator fun get(i: Int): Vec2 { 21 | return when (i) { 22 | 0 -> column1 23 | 1 -> column2 24 | else -> throw Error("Column index $i out of range [0..1]") 25 | } 26 | } 27 | 28 | operator fun times(a: Float) = Mat2(builder, "(${this.value} * ${a.str()})") 29 | operator fun div(a: Float) = Mat2(builder, "(${this.value} / ${a.str()})") 30 | 31 | operator fun times(a: Vec2) = Vec2(builder, "(${this.value} * ${a.value})") 32 | operator fun div(a: Vec2) = Vec2(builder, "(${this.value} / ${a.value})") 33 | 34 | operator fun times(a: Mat2) = Mat2(builder, "(${this.value} * ${a.value})") 35 | operator fun div(a: Mat2) = Mat2(builder, "(${this.value} / ${a.value})") 36 | operator fun plus(a: Mat2) = Mat2(builder, "(${this.value} + ${a.value})") 37 | operator fun minus(a: Mat2) = Mat2(builder, "(${this.value} - ${a.value})") 38 | 39 | operator fun unaryMinus() = Mat2(builder, "-(${this.value})") 40 | } 41 | 42 | operator fun Float.times(a: Mat2) = Mat2(a.builder, "(${this.str()}} * ${a.value})") 43 | operator fun Float.div(a: Mat2) = Mat2(a.builder, "(${this.str()}} / ${a.value})") 44 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/mat/mat3.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.mat 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.str 5 | import com.salakheev.shaderbuilderkt.builder.types.Matrix 6 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec3 7 | 8 | class Mat3(override val builder: ShaderBuilder) : Matrix { 9 | 10 | override val typeName: String = "mat3" 11 | override var value: String? = null 12 | 13 | private var column1 by MatrixColumnDelegate(0, ::Vec3) 14 | private var column2 by MatrixColumnDelegate(1, ::Vec3) 15 | 16 | private var column3 by MatrixColumnDelegate(2, ::Vec3) 17 | 18 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 19 | this.value = value 20 | } 21 | 22 | operator fun Mat3.get(i: Int): Vec3 { 23 | return when (i) { 24 | 0 -> column1 25 | 1 -> column2 26 | 2 -> column3 27 | else -> throw Error("Column index $i out of range [0..2]") 28 | } 29 | } 30 | 31 | operator fun times(a: Float) = Mat3(builder, "(${this.value} * ${a.str()})") 32 | operator fun div(a: Float) = Mat3(builder, "(${this.value} / ${a.str()})") 33 | 34 | operator fun times(a: Vec3) = Vec3(builder, "(${this.value} * ${a.value})") 35 | operator fun div(a: Vec3) = Vec3(builder, "(${this.value} / ${a.value})") 36 | 37 | operator fun times(a: Mat3) = Mat3(builder, "(${this.value} * ${a.value})") 38 | operator fun div(a: Mat3) = Mat3(builder, "(${this.value} / ${a.value})") 39 | operator fun plus(a: Mat3) = Mat3(builder, "(${this.value} + ${a.value})") 40 | operator fun minus(a: Mat3) = Mat3(builder, "(${this.value} - ${a.value})") 41 | 42 | operator fun unaryMinus() = Mat3(builder, "-(${this.value})") 43 | } 44 | 45 | operator fun Float.times(a: Mat3) = Mat3(a.builder, "(${this.str()} * ${a.value})") 46 | operator fun Float.div(a: Mat3) = Mat3(a.builder, "(${this.str()} / ${a.value})") 47 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/mat/mat4.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.mat 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.str 5 | import com.salakheev.shaderbuilderkt.builder.types.Matrix 6 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec4 7 | 8 | class Mat4(override val builder: ShaderBuilder) : Matrix { 9 | 10 | override val typeName: String = "mat4" 11 | override var value: String? = null 12 | 13 | private var column1 by MatrixColumnDelegate(0, ::Vec4) 14 | private var column2 by MatrixColumnDelegate(1, ::Vec4) 15 | private var column3 by MatrixColumnDelegate(2, ::Vec4) 16 | private var column4 by MatrixColumnDelegate(3, ::Vec4) 17 | 18 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 19 | this.value = value 20 | } 21 | 22 | operator fun get(i: Int): Vec4 { 23 | return when (i) { 24 | 0 -> column1 25 | 1 -> column2 26 | 2 -> column3 27 | 3 -> column4 28 | else -> throw Error("Column index $i out of range [0..3]") 29 | } 30 | } 31 | 32 | operator fun times(a: Float) = Mat4(builder, "(${this.value} * ${a.str()})") 33 | operator fun div(a: Float) = Mat4(builder, "(${this.value} / ${a.str()})") 34 | 35 | operator fun times(a: Vec4) = Vec4(builder, "(${this.value} * ${a.value})") 36 | operator fun div(a: Vec4) = Vec4(builder, "(${this.value} / ${a.value})") 37 | 38 | operator fun times(a: Mat4) = Mat4(builder, "(${this.value} * ${a.value})") 39 | operator fun div(a: Mat4) = Mat4(builder, "(${this.value} / ${a.value})") 40 | operator fun plus(a: Mat4) = Mat4(builder, "(${this.value} + ${a.value})") 41 | operator fun minus(a: Mat4) = Mat4(builder, "(${this.value} - ${a.value})") 42 | 43 | operator fun unaryMinus() = Mat4(builder, "-(${this.value})") 44 | } 45 | 46 | operator fun Float.times(a: Mat4) = Mat4(a.builder, "(${this.str()} * ${a.value})") 47 | operator fun Float.div(a: Mat4) = Mat4(a.builder, "(${this.str()} / ${a.value})") 48 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/sampler/Sampler2D.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.sampler 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.Variable 5 | 6 | class Sampler2D(override val builder: ShaderBuilder) : Variable { 7 | override val typeName: String = "sampler2D" 8 | override var value: String? = null 9 | } 10 | 11 | class ShadowTexture2D(override val builder: ShaderBuilder) : Variable { 12 | override val typeName: String = "sampler2D" 13 | override var value: String? = null 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/sampler/Sampler2DArray.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.sampler 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.Variable 5 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLInt 6 | 7 | class Sampler2DArray(override val builder: ShaderBuilder) : Variable { 8 | override val typeName: String = "sampler2D" 9 | override var value: String? = null 10 | 11 | operator fun get(i: GLInt): Sampler2D { 12 | val result = Sampler2D(builder) 13 | result.value = "$value[${i.value}]" 14 | return result 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/scalar/float.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.scalar 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.BoolResult 5 | import com.salakheev.shaderbuilderkt.builder.str 6 | import com.salakheev.shaderbuilderkt.builder.types.GenType 7 | import com.salakheev.shaderbuilderkt.builder.delegates.ComponentDelegate 8 | 9 | class GLFloat(override val builder: ShaderBuilder) : GenType { 10 | 11 | override val typeName: String = "float" 12 | override var value: String? = null 13 | 14 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 15 | this.value = value 16 | } 17 | 18 | operator fun times(a: Float) = GLFloat(builder, "(${this.value} * ${a.str()})") 19 | operator fun div(a: Float) = GLFloat(builder, "(${this.value} / ${a.str()})") 20 | operator fun plus(a: Float) = GLFloat(builder, "(${this.value} + ${a.str()})") 21 | operator fun minus(a: Float) = GLFloat(builder, "(${this.value} - ${a.str()})") 22 | 23 | operator fun times(a: GLFloat) = GLFloat(builder, "(${this.value} * ${a.value})") 24 | operator fun div(a: GLFloat) = GLFloat(builder, "(${this.value} / ${a.value})") 25 | operator fun plus(a: GLFloat) = GLFloat(builder, "(${this.value} + ${a.value})") 26 | operator fun minus(a: GLFloat) = GLFloat(builder, "(${this.value} - ${a.value})") 27 | 28 | operator fun unaryMinus() = GLFloat(builder, "-(${this.value})") 29 | 30 | infix fun eq(a: GLFloat) = BoolResult("(${this.value} == ${a.value})") 31 | infix fun gte(a: GLFloat) = BoolResult("(${this.value} >= ${a.value})") 32 | infix fun gt(a: GLFloat) = BoolResult("(${this.value} > ${a.value})") 33 | infix fun lte(a: GLFloat) = BoolResult("(${this.value} <= ${a.value})") 34 | infix fun lt(a: GLFloat) = BoolResult("(${this.value} < ${a.value})") 35 | 36 | infix fun lt(a: Float) = BoolResult("(${this.value} < ${a.str()})") 37 | infix fun gt(a: Float) = BoolResult("(${this.value} > ${a.str()})") 38 | } 39 | 40 | fun floatComponent() = ComponentDelegate(::GLFloat) 41 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/scalar/int.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.scalar 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.GenType 5 | 6 | class GLInt(override val builder: ShaderBuilder) : GenType { 7 | override val typeName: String = "int" 8 | override var value: String? = null 9 | 10 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 11 | this.value = value 12 | } 13 | 14 | operator fun plus(a: GLInt) = GLInt(builder, "(${this.value} + ${a.value})") 15 | operator fun plus(a: Int) = GLInt(builder, "(${this.value} + $a)") 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/vec/arrays.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.vec 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.types.Variable 5 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLInt 6 | 7 | class Vec2Array(override val builder: ShaderBuilder) : Variable { 8 | override val typeName: String = "vec2" 9 | override var value: String? = null 10 | 11 | operator fun get(i: GLInt): Vec2 { 12 | val result = Vec2(builder) 13 | result.value = "$value[${i.value}]" 14 | return result 15 | } 16 | } 17 | 18 | class Vec3Array(override val builder: ShaderBuilder) : Variable { 19 | override val typeName: String = "vec3" 20 | override var value: String? = null 21 | 22 | operator fun get(i: GLInt): Vec3 { 23 | val result = Vec3(builder) 24 | result.value = "$value[${i.value}]" 25 | return result 26 | } 27 | } 28 | 29 | class Vec4Array(override val builder: ShaderBuilder) : Variable { 30 | override val typeName: String = "vec4" 31 | override var value: String? = null 32 | 33 | operator fun get(i: GLInt): Vec4 { 34 | val result = Vec4(builder) 35 | result.value = "$value[${i.value}]" 36 | return result 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/vec/vec2.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.vec 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.str 5 | import com.salakheev.shaderbuilderkt.builder.types.Vector 6 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLFloat 7 | import com.salakheev.shaderbuilderkt.builder.types.scalar.floatComponent 8 | import com.salakheev.shaderbuilderkt.builder.delegates.ComponentDelegate 9 | 10 | class Vec2(override val builder: ShaderBuilder) : Vector { 11 | override val typeName: String = "vec2" 12 | override var value: String? = null 13 | 14 | var x by floatComponent() 15 | var y by floatComponent() 16 | 17 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 18 | this.value = value 19 | } 20 | 21 | operator fun times(a: Float) = Vec2(builder, "(${this.value} * ${a.str()})") 22 | operator fun div(a: Float) = Vec2(builder, "(${this.value} / ${a.str()})") 23 | 24 | operator fun times(a: GLFloat) = Vec2(builder, "(${this.value} * ${a.value})") 25 | operator fun div(a: GLFloat) = Vec2(builder, "(${this.value} / ${a.value})") 26 | 27 | operator fun times(a: Vec2) = Vec2(builder, "(${this.value} * ${a.value})") 28 | operator fun div(a: Vec2) = Vec2(builder, "(${this.value} / ${a.value})") 29 | operator fun plus(a: Vec2) = Vec2(builder, "(${this.value} + ${a.value})") 30 | operator fun plus(a: Float) = Vec2(builder, "(${this.value} + ${a.str()})") 31 | operator fun minus(a: Vec2) = Vec2(builder, "(${this.value} - ${a.value})") 32 | 33 | operator fun unaryMinus() = Vec2(builder, "-(${this.value})") 34 | } 35 | 36 | operator fun Float.times(a: Vec2) = Vec2(a.builder, "(${this.str()} * ${a.value})") 37 | operator fun Float.div(a: Vec2) = Vec2(a.builder, "(${this.str()} / ${a.value})") 38 | 39 | fun vec2Component() = ComponentDelegate(::Vec2) 40 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/vec/vec3.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.vec 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.str 5 | import com.salakheev.shaderbuilderkt.builder.types.Vector 6 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLFloat 7 | import com.salakheev.shaderbuilderkt.builder.types.scalar.floatComponent 8 | import com.salakheev.shaderbuilderkt.builder.delegates.ComponentDelegate 9 | 10 | @Suppress("unused") 11 | class Vec3(override val builder: ShaderBuilder) : Vector { 12 | 13 | override val typeName: String = "vec3" 14 | override var value: String? = null 15 | 16 | var x by floatComponent() 17 | var y by floatComponent() 18 | var z by floatComponent() 19 | 20 | var xx by vec2Component() 21 | var xy by vec2Component() 22 | var xz by vec2Component() 23 | var yx by vec2Component() 24 | var yy by vec2Component() 25 | var yz by vec2Component() 26 | var zx by vec2Component() 27 | var zy by vec2Component() 28 | var zz by vec2Component() 29 | 30 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 31 | this.value = value 32 | } 33 | 34 | operator fun times(a: Float) = Vec3(builder, "(${this.value} * ${a.str()})") 35 | operator fun div(a: Float) = Vec3(builder, "(${this.value} / ${a.str()})") 36 | 37 | operator fun times(a: GLFloat) = Vec3(builder, "(${this.value} * ${a.value})") 38 | operator fun div(a: GLFloat) = Vec3(builder, "(${this.value} / ${a.value})") 39 | 40 | operator fun times(a: Vec3) = Vec3(builder, "(${this.value} * ${a.value})") 41 | operator fun div(a: Vec3) = Vec3(builder, "(${this.value} / ${a.value})") 42 | operator fun plus(a: Vec3) = Vec3(builder, "(${this.value} + ${a.value})") 43 | operator fun minus(a: Vec3) = Vec3(builder, "(${this.value} - ${a.value})") 44 | 45 | operator fun unaryMinus() = Vec3(builder, "-(${this.value})") 46 | } 47 | 48 | operator fun Float.times(a: Vec3) = Vec3(a.builder, "(${this.str()} * ${a.value})") 49 | operator fun Float.div(a: Vec3) = Vec3(a.builder, "(${this.str()} / ${a.value})") 50 | operator fun GLFloat.times(a: Vec3) = Vec3(a.builder, "(${this.value} * ${a.value})") 51 | 52 | fun vec3Component() = ComponentDelegate(::Vec3) 53 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/builder/types/vec/vec4.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.builder.types.vec 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.str 5 | import com.salakheev.shaderbuilderkt.builder.types.Vector 6 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLFloat 7 | import com.salakheev.shaderbuilderkt.builder.types.scalar.floatComponent 8 | 9 | @Suppress("unused") 10 | class Vec4(override val builder: ShaderBuilder) : Vector { 11 | override val typeName: String = "vec4" 12 | override var value: String? = null 13 | 14 | var x by floatComponent() 15 | var y by floatComponent() 16 | var z by floatComponent() 17 | var w by floatComponent() 18 | 19 | var xx by vec2Component() 20 | var xy by vec2Component() 21 | var xz by vec2Component() 22 | var xw by vec2Component() 23 | var yx by vec2Component() 24 | var yy by vec2Component() 25 | var yz by vec2Component() 26 | var yw by vec2Component() 27 | var zx by vec2Component() 28 | var zy by vec2Component() 29 | var zz by vec2Component() 30 | var zw by vec2Component() 31 | var wx by vec2Component() 32 | var wy by vec2Component() 33 | var wz by vec2Component() 34 | var ww by vec2Component() 35 | 36 | var xxx by vec3Component() 37 | var xxy by vec3Component() 38 | var xxz by vec3Component() 39 | var xyx by vec3Component() 40 | var xyy by vec3Component() 41 | var xyz by vec3Component() 42 | var xzx by vec3Component() 43 | var xzy by vec3Component() 44 | var xzz by vec3Component() 45 | 46 | var yxx by vec3Component() 47 | var yxy by vec3Component() 48 | var yxz by vec3Component() 49 | var yyx by vec3Component() 50 | var yyy by vec3Component() 51 | var yyz by vec3Component() 52 | var yzx by vec3Component() 53 | var yzy by vec3Component() 54 | var yzz by vec3Component() 55 | var yzw by vec3Component() 56 | 57 | var zxx by vec3Component() 58 | var zxy by vec3Component() 59 | var zxz by vec3Component() 60 | var zyx by vec3Component() 61 | var zyy by vec3Component() 62 | var zyz by vec3Component() 63 | var zzx by vec3Component() 64 | var zzy by vec3Component() 65 | var zzz by vec3Component() 66 | 67 | var www by vec3Component() 68 | 69 | constructor(builder: ShaderBuilder, value: String) : this(builder) { 70 | this.value = value 71 | } 72 | 73 | operator fun times(a: GLFloat) = Vec4(builder, "(${this.value} * ${a.value})") 74 | operator fun div(a: GLFloat) = Vec4(builder, "(${this.value} / ${a.value})") 75 | 76 | operator fun times(a: Float) = Vec4(builder, "(${this.value} * ${a.str()})") 77 | operator fun div(a: Float) = Vec4(builder, "(${this.value} / ${a.str()})") 78 | 79 | operator fun times(a: Vec4) = Vec4(builder, "(${this.value} * ${a.value})") 80 | operator fun div(a: Vec4) = Vec4(builder, "(${this.value} / ${a.value})") 81 | operator fun plus(a: Vec4) = Vec4(builder, "(${this.value} + ${a.value})") 82 | operator fun minus(a: Vec4) = Vec4(builder, "(${this.value} - ${a.value})") 83 | 84 | operator fun unaryMinus() = Vec4(builder, "-(${this.value})") 85 | } 86 | 87 | operator fun Float.times(a: Vec4) = Vec4(a.builder, "(${this.str()} * ${a.value})") 88 | operator fun Float.div(a: Vec4) = Vec4(a.builder, "(${this.str()} / ${a.value})") 89 | operator fun GLFloat.times(a: Vec4) = Vec4(a.builder, "(${this.value} * ${a.value})") 90 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/examples/SimpleFragmentShader.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.examples 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.examples.components.ShadowReceiveComponent 5 | import com.salakheev.shaderbuilderkt.builder.types.sampler.Sampler2D 6 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLFloat 7 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec2 8 | 9 | class SimpleFragmentShader(alphaTest: Boolean, receiveShadow: Boolean) : ShaderBuilder() { 10 | private val alphaTestThreshold by uniform(::GLFloat) 11 | private val texture by uniform(::Sampler2D) 12 | 13 | private val vUV by varying(::Vec2) 14 | 15 | init { 16 | var color by vec4() 17 | color = texture2D(texture, vUV) 18 | if (alphaTest) { 19 | If(color.w lt alphaTestThreshold) { 20 | discard() 21 | } 22 | } 23 | var brightness by float(1.0f) 24 | if (receiveShadow) { 25 | val shadowReceiveComponent = ShadowReceiveComponent() 26 | brightness = shadowReceiveComponent.fragment(this, brightness) 27 | } 28 | brightness = clamp(brightness, 0.5f, 1.0f) 29 | color.xyz = color.xyz * brightness 30 | gl_FragColor = color 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/examples/SimpleVertexShader.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.examples 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.examples.components.ShadowReceiveComponent 5 | import com.salakheev.shaderbuilderkt.builder.types.mat.Mat4 6 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec2 7 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec3 8 | 9 | class SimpleVertexShader(receiveShadow: Boolean) : ShaderBuilder() { 10 | private val mvp by uniform(::Mat4) 11 | 12 | private val vertex by attribute(::Vec3) 13 | private val uv by attribute(::Vec2) 14 | 15 | private var vUV by varying(::Vec2) 16 | 17 | init { 18 | val inp by vec4(vertex, 1.0f) 19 | vUV = uv 20 | if (receiveShadow) { 21 | val shadowReceiveComponent = ShadowReceiveComponent() 22 | shadowReceiveComponent.vertex(this, inp) 23 | } 24 | gl_Position = mvp * inp 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/examples/components/ShadowReceiveComponent.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.examples.components 2 | 3 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilder 4 | import com.salakheev.shaderbuilderkt.builder.ShaderBuilderComponent 5 | import com.salakheev.shaderbuilderkt.builder.types.mat.Mat4 6 | import com.salakheev.shaderbuilderkt.builder.types.sampler.Sampler2D 7 | import com.salakheev.shaderbuilderkt.builder.types.scalar.GLFloat 8 | import com.salakheev.shaderbuilderkt.builder.types.vec.Vec4 9 | 10 | /** 11 | * An example of [ShaderBuilderComponent] 12 | */ 13 | class ShadowReceiveComponent : ShaderBuilderComponent() { 14 | private val shadowMVP by uniform(::Mat4) 15 | private val shadowTexture by uniform(::Sampler2D) 16 | 17 | private var vShadowCoord by varying(::Vec4) 18 | 19 | fun vertex(parent: ShaderBuilder, inp: Vec4) { 20 | vShadowCoord = shadowMVP * inp 21 | vShadowCoord.y = -vShadowCoord.y 22 | val offset by vec2(1f, 1f) 23 | vShadowCoord.xy = (vShadowCoord.xy + offset) / 2f 24 | parent.appendComponent(this) 25 | } 26 | 27 | fun fragment(parent: ShaderBuilder, brightness: GLFloat): GLFloat { 28 | val bias by float(0.0035f) 29 | val shadowStep by float(0.4f) 30 | 31 | var newBrightness by float(brightness) 32 | 33 | var pixel by float() 34 | pixel = texture2D(shadowTexture, vShadowCoord.xy).x 35 | If(pixel lt (vShadowCoord.z + 1.0f) / 2.0f - bias) { 36 | newBrightness = newBrightness - shadowStep 37 | } 38 | parent.appendComponent(this) 39 | return newBrightness 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/kotlin/com/salakheev/shaderbuilderkt/sources/ShaderSourceProvider.kt: -------------------------------------------------------------------------------- 1 | package com.salakheev.shaderbuilderkt.sources 2 | 3 | interface ShaderSourceProvider { 4 | fun getSource(): String 5 | } 6 | 7 | interface ShaderProgramSources { 8 | val vertex: String 9 | val fragment: String 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | com.salakheev.shaderbuilderkt.ShaderBuilderProcessor 2 | -------------------------------------------------------------------------------- /src/main/resources/themes/program.chtml: -------------------------------------------------------------------------------- 1 | {% if $package %} 2 | package {$package}; 3 | {% endif %} 4 | 5 | import {$sourceClass}; 6 | import com.salakheev.shaderbuilderkt.sources.ShaderProgramSources; 7 | 8 | class {$className} { 9 | enum class Sources(override val vertex: String, override val fragment: String) : ShaderProgramSources { 10 | {% loop in $resultBranches as $branch %} 11 | SOURCE{$branch.index}("""{$branch.vertex}""","""{$branch.fragment}""") 12 | {% onEmpty %} 13 | {% divider %},{% enddivider %} 14 | {% endloop %}; 15 | } 16 | 17 | companion object { 18 | {% if ($paramsWithTypes) %} 19 | fun get({$paramsWithTypes|join(, )}): ShaderProgramSources { 20 | {% loop in $resultBranches as $branch %} 21 | if ({$branch.params}) { 22 | return Sources.SOURCE{$branch.index}; 23 | } else 24 | {% onEmpty %} 25 | {% endloop %} 26 | throw IllegalArgumentException(); 27 | } 28 | {% else %} 29 | fun get(): ShaderProgramSources { 30 | {% loop in $resultBranches as $branch %} 31 | return Sources.SOURCE{$branch.index}; 32 | {% onEmpty %} 33 | {% endloop %} 34 | } 35 | {% endif %} 36 | } 37 | } 38 | --------------------------------------------------------------------------------